【问题标题】:Synchronizing OpenGL with RtAudio (or Port Audio)将 OpenGL 与 RtAudio(或端口音频)同步
【发布时间】:2016-02-16 04:42:12
【问题描述】:

我需要使用节拍器将一些绘图与 OpenGL 同步。节拍器是用 libPD 构建的,并用 RtAudio 播放。 这两件事都运行良好(分别),但我需要用节拍器移动一个物体(一个三角形)。应用程序也必须播放点击。这两个动作必须并行进行(玩和画)。我也应该添加一个midi记录。我的应用程序是 C++。 我试图用一个线程运行它,但它不起作用。 我试图遵循这个解释:How to make my metronome play at the same time as recording in my program? gui 库是 WxWidgets。线程是通过 Poco::Runnable 以这种方式完成的:

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 是一个 wxGLCanvas。 SoundManager类的播放函数如下:

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);
    }  
}

OpenGL的绘制方法如下:

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);
    }
}

线程正在调用以下函数:

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

然后,这个函数在 Reder 中被调用:

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

也许我要改变这个对象,现在这并不重要,我的问题是同步。我认为 RtAudio 正在制造问题,因为我成为了 getDeviceCount() 或 RtAudio 的任何其他函数的 EXC_BAD_Acces。这只发生在线程上下文中。 使用 Port Audio 会更好吗?很高兴知道我做错了什么,或者是否有其他方法可以解决此问题

【问题讨论】:

    标签: c++ opengl wxwidgets portaudio rtaudio


    【解决方案1】:

    我找到了解决方案。问题出在 wxwidgets 主循环和 openGL 的交互上。解决方法是通过以下方式创建 Idle 事件:

    //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);
            }
    
        }
    }
    

    同步现在完美运行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-13
      • 2017-11-05
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多