将 OpenGL 与 RtAudio(或端口音频)同步
Synchronizing OpenGL with RtAudio (or Port Audio)
我需要使用节拍器将一些绘图与 OpenGL 同步。节拍器是使用 libPD 构建的,并使用 RtAudio 播放。
这两件事都很好(分别),但我需要用节拍器的脉冲移动一个物体(一个三角形)。应用程序也必须播放咔哒声。这两个动作必须平行进行(演奏和绘画)。我也应该添加一个 midi 记录。我的应用程序是在 C++ 中。
我尝试 运行 使用一个线程,但它不起作用。
我试着遵循这个解释:
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。声音管理器 class 的播放功能如下:
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( ¶meters, 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() 的 EXC_BAD_Acces 或来自 RtAudio 的任何其他功能。这只发生在线程上下文中。
最好用 Port Audio 来做?很高兴知道我做错了什么,或者是否有其他方法可以解决此问题
我找到了解决办法。问题出在 wxwidgets 主循环与 openGL.The 之间的交互中 openGL.The 解决方案是通过以下方式创建一个空闲事件 int:
//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);
}
}
}
synchronsazion 现在完美运行。
我需要使用节拍器将一些绘图与 OpenGL 同步。节拍器是使用 libPD 构建的,并使用 RtAudio 播放。
这两件事都很好(分别),但我需要用节拍器的脉冲移动一个物体(一个三角形)。应用程序也必须播放咔哒声。这两个动作必须平行进行(演奏和绘画)。我也应该添加一个 midi 记录。我的应用程序是在 C++ 中。
我尝试 运行 使用一个线程,但它不起作用。
我试着遵循这个解释:
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。声音管理器 class 的播放功能如下:
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( ¶meters, 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() 的 EXC_BAD_Acces 或来自 RtAudio 的任何其他功能。这只发生在线程上下文中。 最好用 Port Audio 来做?很高兴知道我做错了什么,或者是否有其他方法可以解决此问题
我找到了解决办法。问题出在 wxwidgets 主循环与 openGL.The 之间的交互中 openGL.The 解决方案是通过以下方式创建一个空闲事件 int:
//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);
}
}
}
synchronsazion 现在完美运行。