-   Programming (
-   -   wxWidgets - Having problem with audio seeking a little before from the specified position. (

apoorv569 09-21-2021 09:52 AM

wxWidgets - Having problem with audio seeking a little before from the specified position.
I have a application that I'm currently developing, and I'm having a problem, that I'm not able to figure out. The application is for managing and auditioning audio samples, and I'm using wxWidgets as the GUI toolkit. Recently I was trying to implement looping a selected region. I have it working for the most part. The only problem I'm facing is that, say I have 2 points set, A and B, A is at 500 and B at 700 in milliseconds, I am doing a little match to calculate these points to pixel position on the screen to show the playhead and selected region points, the problem is that when my playhead reaches point B, and its time go back to A and play the sample again, to essentially loop, it goes back a little before point A i.e 450 or something for example, and I'm pretty sure my calculations are correct, don't know what is causing this issue. Here is a little GIF showing the problem hopefully this explains.

Here is some of the code related to the problem,

Here I calculate the position for the loop points A and B from pixel position to milliseconds, this is a custom event that I defined to send this information from one class to another.

void WaveformViewer::SendLoopPoints()
    wxLogDebug("%s Called", __FUNCTION__);

    SampleHive::SH_LoopPointsEvent event(SampleHive::SH_EVT_LOOP_POINTS_UPDATED, this->GetId());

    Database db(m_InfoBar);

    int selected_row = m_Library.GetSelectedRow();

    if (selected_row < 0)

    wxString selected = m_Library.GetTextValue(selected_row, 1);
    std::string path = db.GetSamplePathByFilename(m_DatabaseFilepath, selected.BeforeLast('.').ToStdString());

    Tags tags(path);

    int length = tags.GetAudioInfo().length;

    int panel_width = this->GetSize().GetWidth();

    int a = m_AnchorPoint.x, b = m_CurrentPoint.x;

    double loopA = ((double)a / panel_width) * length;
    double loopB = ((double)b / panel_width) * length;

    event.SetLoopPoints({ loopA, loopB });


    wxLogDebug("%s processed event, sending loop points..", __FUNCTION__);

And I receive this custom event in my MainFrame class, and use these points to seek the audio sample from point A to B to loop between these points.

void MainFrame::OnRecieveLoopPoints(SampleHive::SH_LoopPointsEvent& event)
    wxLogDebug("%s called and recieved loop points", __FUNCTION__);

    std::pair<double, double> loop_points = event.GetLoopPoints();

    m_LoopA = wxLongLong(loop_points.first);
    m_LoopB = wxLongLong(loop_points.second);

    int loopA_min = static_cast<int>((m_LoopA / 60000).GetValue());
    int loopA_sec = static_cast<int>(((m_LoopA % 60000) / 1000).GetValue());
    int loopB_min = static_cast<int>((m_LoopB / 60000).GetValue());
    int loopB_sec = static_cast<int>(((m_LoopB % 60000) / 1000).GetValue());

    wxLogDebug(wxString::Format("LoopA: %2i:%02i, LoopB: %2i:%02i",
                                loopA_min, loopA_sec, loopB_min, loopB_sec));

    m_LoopPointAText->SetValue(wxString::Format("%2i:%02i", loopA_min, loopA_sec));
    m_LoopPointBText->SetValue(wxString::Format("%2i:%02i", loopB_min, loopB_sec));

    wxLogDebug("%s Event processed successfully..", __FUNCTION__);

Here I start the loop process, this is one of the methods to play the audio sample,

void MainFrame::OnClickPlay(wxCommandEvent& event)
    bStopped = false;

    int selected_row = m_Library->GetSelectedRow();

    if (selected_row < 0)

    wxString selection = m_Library->GetTextValue(selected_row, 1);

    wxString sample_path = GetFilenamePathAndExtension(selection).Path;

    if (bLoop && m_LoopPointAButton->GetValue() && m_LoopPointBButton->GetValue())
        PlaySample(sample_path.ToStdString(), selection.ToStdString(), true, m_LoopA.ToDouble(), wxFromStart);
        PlaySample(sample_path.ToStdString(), selection.ToStdString());

PlaySample() is a function I defined so I can have one place where the wxTimer starts,

void MainFrame::PlaySample(const std::string& filepath, const std::string& sample, bool seek, wxFileOffset where, wxSeekMode mode)
    wxLogDebug("TIMER STARTING FROM %s", __FUNCTION__);

    if (m_MediaCtrl->Load(filepath))
        if (seek)
            m_MediaCtrl->Seek(where, mode);

        if (!m_MediaCtrl->Play())
            wxLogDebug(_("Error! Cannot play sample."));

        PushStatusText(wxString::Format(_("Now playing: %s"), sample), 1);

        if (!m_Timer->IsRunning())
            m_Timer->Start(20, wxTIMER_CONTINUOUS);
        wxLogDebug(_("Error! Cannot load sample."));

And here I check if playhead is at point B, if it is go back to point A and play again, this is wxTimer event handler,

void MainFrame::UpdateElapsedTime(wxTimerEvent& event)
    wxLogDebug("TIMER IS RUNNING..");

    wxString duration, position;
    wxLongLong llLength, llTell;

    llLength = m_MediaCtrl->Length();
    int total_min = static_cast<int>((llLength / 60000).GetValue());
    int total_sec = static_cast<int>(((llLength % 60000) / 1000).GetValue());

    llTell = m_MediaCtrl->Tell();
    int current_min = static_cast<int>((llTell / 60000).GetValue());
    int current_sec = static_cast<int>(((llTell % 60000) / 1000).GetValue());

    duration.Printf(wxT("%2i:%02i"), total_min, total_sec);
    position.Printf(wxT("%2i:%02i"), current_min, current_sec);

    m_SamplePosition->SetLabel(wxString::Format(wxT("%s/%s"), position.c_str(), duration.c_str()));


    if (bLoop && m_LoopPointAButton->GetValue() && m_LoopPointBButton->GetValue())
        if (static_cast<double>(m_MediaCtrl->Tell()) >= m_LoopB.ToDouble())
            m_MediaCtrl->Seek(m_LoopA.ToDouble(), wxFromStart);

Here is the link to full project if needed- SampleHive

This specific feature that I'm asking for help right now, is currently in the experimental-draw-waveform branch only.

astrogeek 09-28-2021 04:11 PM

As no one has replied I can only offer my own thoughts after having read your post several times...

First, I think your question may seem too much like asking for others to develop the application for you as opposed to asking for help solving a specific programming problem. I have read your post to try to understand what may not be working in your code, but always end up asking myself what it should be doing, so it is impossible to then say why it is not doing that! It seems that you are not asking a very specific question.

You may find it helpful to review the Site FAQ for guidance in asking well formed questions. Especially visit the link from that page, How to Ask Questions the Smart Way for discussion of things to consider when asking others for help.

The better you can refine your own understanding of your project code the better you may be able to reduce it to a specific, minimal example to provide when asking others for help.

Hope this helps!

All times are GMT -5. The time now is 01:16 PM.