с помощью openFrameworks + CoreAudio

мои навыки C++ довольно легкие, но мне удается делать вещи - пока я не упираюсь в стену. Текущая стена:

Я использую CoreAudio, чтобы делать некоторые вещи с воспроизведением Midi. У меня много работы, но я застрял на простой вещи. (Я не знаю правильную терминологию С++, так что терпите меня...).

Я использую функцию обратного вызова рендеринга в CoreAudio, чтобы указать, когда событие миди-ноты визуализируется AU. Если я определяю ее как неклассовую функцию и вставляю main.cpp (или testApp.cpp, если уж на то пошло), она работает — я получаю события. Проблема в том, что мне нужно, чтобы экземпляр testApp получал эти события.

Итак... есть ли способ получить экземпляр testApp из main.cpp, чтобы я мог вызвать нужный мне метод testApp?

ИЛИ есть ли какое-то вуду С++, чтобы иметь неклассовую функцию, находящуюся внутри класса, вызывающую метод экземпляра? Например, если функция ниже находится в моем классе, как она может вызвать метод для экземпляра класса...

    OSStatus renderCallback(void *inRefCon,
                                              AudioUnitRenderActionFlags *  ioActionFlags,
                                              const AudioTimeStamp *        inTimeStamp,
                                              UInt32                inBusNumber,
                                              UInt32                inNumberFrames,
                                              AudioBufferList *     ioData)
{

           someClassMethod(); // doesn't work
           this.someClassMethod(); // doesn't work
           self.someClassMethod(); // doesn't work

}

Я не знаю точно, но я думаю, что материал CoreAudio не принимает методы экземпляра в качестве обратных вызовов - по крайней мере, это то, что я почерпнул из сообщения об ошибке (ниже). Я в порядке, если это работает.

спасибо за любые советы!

ошибка: аргумент типа OSStatus (testApp::)(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32, UInt32, AudioBufferList*)' не соответствует 'OSStatus ()(void, AudioUnitRenderActionFlags*, константа AudioTimeStamp*, UInt32, UInt32, AudioBufferList*)


person spring    schedule 14.07.2011    source источник


Ответы (4)


Вам не нужно возиться со статическими экземплярами, чтобы сделать это. Когда вы добавляете обратный вызов рендеринга, передайте свой объект C++ в качестве refcon, а затем приведите refcon к вашему объекту в обратном вызове:

// The actual callback is defined as a static function
static OSStatus
myAURenderCallback(void *inRefCon, 
                   AudioUnitRenderActionFlags *ioActionFlags,
                   const AudioTimeStamp *inTimeStamp,
                   UInt32 inBusNumber,
                   UInt32 inNumberFrames,
                   AudioBufferList *ioData)
{
    MyClass *object = static_cast<MyClass *>(inRefCon);
    return object->Render(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
}


// When adding the render callback pass this as the context
AURenderCallbackStruct cbs = { myAURenderCallback, this };
OSStatus result = AUGraphSetNodeInputCallback(graph, node, 0, &cbs);


// The callback will look like
OSStatus MyClass::Render(AudioUnitRenderActionFlags *ioActionFlags,
                         const AudioTimeStamp *inTimeStamp,
                         UInt32 inBusNumber,
                         UInt32 inNumberFrames,
                         AudioBufferList *ioData)
{
  // Do something
}
person sbooth    schedule 14.07.2011
comment
Это отличный трюк с API, кто-то явно думал о том, как люди могут его использовать. Я предположил, что в моем ответе спрашивающий вызывает обычную библиотеку c. - person LukeN; 14.07.2011
comment
Да, Apple четко понимает, как обычно будет использоваться этот API. Я также, кажется, помню этот шаблон еще во времена программирования Toolbox на Pascal, поэтому я думаю, что он существовал уже некоторое время. - person sbooth; 15.07.2011
comment
спасибо всем за ответы. Я смог заставить его работать с подходом выше. CoreAudio сначала сбил меня с толку всеми указателями, но я разобрался. - person spring; 16.07.2011

Вы правы в том, что здесь нельзя использовать указатель на метод экземпляра (нет возможности предоставить неявный указатель this на функцию-член, поскольку обратный вызов на самом деле не вызывается через объект).

Для этого вы можете использовать статическую функцию-член (тогда вам понадобится какой-то способ предоставить этой функции указатель на экземпляр вашего testApp (глобальная переменная, статический экземпляр и т. д.)).

person Riley Adams    schedule 14.07.2011

Если вы можете установить boost, boost::bind сделает это за вас.

person fileoffset    schedule 14.07.2011

ответ от sbooth является ответом здесь, поскольку дизайнеры API предоставили вам хороший трюк. Однако, если вы передавали обратный вызов в менее хорошо спроектированную библиотеку c, ответ ниже может помочь.

Вам нужно перейти от указателя на функцию-член к указателю на функцию.

ЧаВо по c++ lite здесь и здесь есть ваш ответ

Вы также можете:

1. Сделайте someMethod() статическим и сохраните статический экземпляр testApp (если вам нужно изменить данные экземпляра) 2. Сохраните указатель testApp глобально и используйте его из глобального функция, которую вы уже используете

Вот пример метода 1, который я бы рекомендовал

class Test
{
public:
    Test();
    void InstanceCallback();
    static void StaticCallbackMethod();

private:
    static Test* instance;
};


// Normally in cpp
Test* Test::instance = 0;

Test::Test()
{
    instance = this;
}

void Test::InstanceCallback()
{
    // do stuff;
}

void Test::StaticCallbackMethod()
{
    if (instance)
    {
        instance->InstanceCallback();
    }
}

int main()
{
    Test* t = new Test();

    void (*mf)();
    mf = &Test::StaticCallbackMethod;   
    mf();

    return 0;
}

Учитывая, где вы находитесь со своими знаниями C++, я бы настоятельно рекомендовал прочитать все часто задаваемые вопросы lite, а затем fqa. Хотел бы я, чтобы кто-нибудь порекомендовал мне это, когда я начинал.

person LukeN    schedule 14.07.2011