Synchronizing OpenGL with RtAudio (or Port Audio)

490 Views Asked by At

I need to synchronize some draws with OpenGL with a Metronome. The Metronome is build with libPD, and played with RtAudio. Both things are working well (separately), but i need to move an object (a triangle) with the pulse a metronome. The application must play the clicks too. Both actions must be done parallel (playing and drawing). I should add a midi record too. My Application is in C++. I tried to run that with one thread, but it doesn't work. I tried to follow this explanation: How to make my metronome play at the same time as recording in my program? The gui Library is WxWidgets. The threads are done with Poco::Runnable in this way:

class MyThread : public Poco::Runnable {
public:
    MyThread(BasicGLPane *pane, std::shared_ptr<SoundManager> man);

    virtual void run();
private:
    BasicGLPane *_pane;
    std::shared_ptr<SoundManager> _man;
};

MyThread::MyThread(BasicGLPane *pane, std::shared_ptr<SoundManager> man) {
    _pane = pane;
    _man = man;
}


void MyThread::run() {
    _man->play();
    _pane->startAnimation();    
}

BasicGLpane is a wxGLCanvas. THe play function of the Sound Manager class is the following:

void SoundManager::play() {
    // Init pd
    if(!lpd->init(0, 2, sampleRate)) {
        std::cerr << "Could not init pd" << std::endl;
        exit(1);
    }    
    // Receive messages from pd
    lpd->setReceiver(object.get());
    lpd->subscribe("metro-bang");
    lpd->subscribe("switch");

    // send DSP 1 message to pd
    lpd->computeAudio(true);

    // load the patch
    open_patch("metro-main.pd");
    std::cout << patch << std::endl;    

    // Use the RtAudio API to connect to the default audio device.
    if(audio->getDeviceCount()==0){
        std::cout << "There are no available sound devices." << std::endl;
        exit(1);
    }

    RtAudio::StreamParameters parameters;
    parameters.deviceId = audio->getDefaultOutputDevice();
    parameters.nChannels = 2;

    RtAudio::StreamOptions options;
    options.streamName = "Pd Metronome";
    options.flags = RTAUDIO_SCHEDULE_REALTIME;
    if ( audio->getCurrentApi() != RtAudio::MACOSX_CORE ) {
        options.flags |= RTAUDIO_MINIMIZE_LATENCY; // CoreAudio doesn't seem to like this
    }
    try {
        if(audio->isStreamOpen()) {
            audio->closeStream();
        }
        else {
            audio->openStream( &parameters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, lpd.get(), &options );
            audio->startStream();
        }
    }
    catch ( RtAudioError& e ) {
        std::cerr << e.getMessage() << std::endl;
        exit(1);
    }  
}

The OpenGL drawing methode are the following:

void BasicGLPane::startAnimation() {
    std::cout<<"Start Animation"<<std::endl;
    triangle_1(p1, p2, p3);    
    Refresh();
}

void BasicGLPane::triangle_1(std::shared_ptr<vertex2f> _p1, std::shared_ptr<vertex2f> _p2, std::shared_ptr<vertex2f> _p3) {
    CGLContextObj ctx = CGLGetCurrentContext(); //enable multithreading (only apple) 
    CGLError err = CGLEnable( ctx, kCGLCEMPEngine);
    if (err != kCGLNoError ) {
        glEnable(GL_MULTISAMPLE);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0, getWidth(), getHeight(),0 , -1, 1);
        glShadeModel(GL_SMOOTH);
        glBegin(GL_POLYGON);                      // Drawing Using Triangles
        glColor3f (157.0/255.0, 44.0/255.0, 44.0/255.0);
        glVertex3f( p1->x, p1->y, 0.0f);              // Top left
        glVertex3f(p2->x,p2->y, 0.0f);              // Top Right
        glVertex3f( p3->x,p3->y, 0.0f);               //Bottom
        glEnd();
        glMatrixMode(GL_MODELVIEW);
        glEnable (GL_BLEND);
        glLoadIdentity();
        glDisable(GL_MULTISAMPLE);
    }
}

And the thread is calling with the following funcion:

void BasicGLPane::startThread() {
    while (_object->getCounter()<10) { //this is only to test the functionality
        thread.start(work);
    }
    thread.join();
    manager->stop();
}

And after that, this funtion is called in Reder:

void BasicGLPane::render( wxPaintEvent& evt ) {
    //some code here, not important....
    startThread();
    SwapBuffers();
}

Maybe I'm going to change this object, that is not important now, my problem is the synchronisation. I think RtAudio is making problems, because i become a EXC_BAD_Acces to getDeviceCount() or with any other function from RtAudio. That occurrs only in the thread context. It would be better to do that with Port Audio?. It would be nice to know what I'm doing wrong, or if there is another way to resolve this problem

1

There are 1 best solutions below

0
On BEST ANSWER

I found a solution. The problem was in the interaction between the wxwidgets main loop and openGL.The solution is to create an Idle event int the following way:

//on wxApp
void MyApp::activateRenderLoop(bool on) {
    if(on && !render_loop_on) {
        Connect(wxID_ANY, wxEVT_IDLE, wxIdleEventHandler(MyApp::onIdle));
        render_loop_on = true;
    }
    else if (!on && render_loop_on) {
        Disconnect(wxEVT_IDLE, wxIdleEventHandler(MyApp::onIdle));
        render_loop_on = false;
    }
}

void MyApp::onIdle(wxIdleEvent &evt) {
    activateRenderLoop(glPane->render_on);
    if(render_loop_on) {
        std::cout<<"MyApp on Idle, render_loop_on"<<std::endl;
        glPane->paint_now();
        evt.RequestMore();
    }
}

//on event table:
EVT_PAINT(BasicGLPane::paint_rt)

void BasicGLPane::rightClick(wxMouseEvent& event) {
    render_on = true;
    manager->init();
    SLEEP(2000);
    manager->play();
    wxGetApp().activateRenderLoop(true);   
}

void BasicGLPane::paint_rt(wxPaintEvent &evt) {
    wxPaintDC dc(this);
    render_rt(dc);
}

void BasicGLPane::paint_now(){
    wxClientDC dc(this);
    std::cout<<"paint now() "<<std::endl;
    render_rt(dc);
}

void BasicGLPane::render_rt(wxDC &dc) {
    wxGLCanvas::SetCurrent(*m_context);
    if(_object->getCounter()>=10) {
        wxGetApp().activateRenderLoop(false);
        manager->stop();
        render_on = false;
    }
    else {
        ctx = CGLGetCurrentContext(); //OSx only
        err =  CGLEnable( ctx, kCGLCEMPEngine); //OSX only
        std::cout<<"render_rt CGLError: "<<err<<std::endl;
        if (err==0) {
            glTranslatef(p3->x, p3->y, 0);
            Refresh(false);
        }

    }
}

The synchronsazion works perfectly now.