Я пишу программу, которая использует SDL_Audio и динамически воспроизводит звук, но я обнаружил, что моя реализация не очень отзывчива с задержкой от 0,3 до 0,5 секунды.
Я подозреваю, что я произвожу звук задолго до того, как звуковая карта вызовет данные. Мое решение состоит в том, чтобы инициировать производство звука с первым запросом на аудио. Для этого я добавил указатель на функцию, изначально установленный на основную функцию, которая
Итак, по сути, у меня есть статическая функция-член, вызывающая функцию-член через указатель функции-члена, и у меня возникают проблемы с компиляцией.
Поиск правильного синтаксиса для вызова функции-члена через указатель функции.
У меня есть следующая тестовая программа, которая воспроизводит мою проблему.
/*
* testcallback.cpp
*
* Created on: Apr 20, 2023
* Author: phil
*/
#include <SDL2/SDL.h>
#include <fstream>
#include <iostream>
class AudioCallbackTest
{
public:
AudioCallbackTest()
{
SDL_Init(SDL_INIT_AUDIO);
SDL_AudioSpec desiredSpec = {0};
desiredSpec.format = AUDIO_S16SYS;
desiredSpec.channels = 1;
desiredSpec.freq = 44100;
desiredSpec.samples = 256;
desiredSpec.callback = audioCallbackWrap;
desiredSpec.userdata = this;
m_hwDevice = SDL_OpenAudioDevice(nullptr, false, &desiredSpec, NULL, 0);
if (m_hwDevice == 0)
{
std::cout << "SDL_OpenAudioDevice failed: SDL error [" << SDL_GetError() << "]" << std::endl;
return;
}
}
static void audioCallbackWrap(void *_instance, Uint8* _stream, int _length)
{
// ((AudioCallbackTest*)_instance)->m_audioCallback(_stream, _length);
((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);
}
void audioCallback(Uint8* stream, int len)
{
std::cout << "audio callback" << std::endl;
}
void primeAudioCallback(Uint8* stream, int len)
{
std::cout << "prime callback" << std::endl;
m_audioCallback = &AudioCallbackTest::audioCallback;
}
private:
SDL_AudioDeviceID m_hwDevice;
void (AudioCallbackTest::*m_audioCallback)(uint8_t *stream, int len) = &AudioCallbackTest::primeAudioCallback;
};
Обратите внимание на закомментированную строку в статической функции. В первой форме я получаю ошибку
g++ -O0 -g3 -Wall -c -fmessage-length=0 -o testcallback.o ../testcallback.cpp
../testcallback.cpp: In static member function ‘static void AudioCallbackTest::audioCallbackWrap(void*, Uint8*, int)’:
../testcallback.cpp:35:65: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘((AudioCallbackTest*)_instance)->AudioCallbackTest::m_audioCallback (...)’, e.g. ‘(... ->* ((AudioCallbackTest*)_instance)->AudioCallbackTest::m_audioCallback) (...)’
35 | ((AudioCallbackTest*)_instance)->m_audioCallback(_stream, _length);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
Со второй формой я получаю следующую ошибку
g++ -O0 -g3 -Wall -c -fmessage-length=0 -o testcallback.o ../testcallback.cpp
../testcallback.cpp: In static member function ‘static void AudioCallbackTest::audioCallbackWrap(void*, Uint8*, int)’:
../testcallback.cpp:37:51: error: invalid use of member ‘AudioCallbackTest::m_audioCallback’ in static member function
37 | ((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);
| ^~~~~~~~~~~~~~~
../testcallback.cpp:51:35: note: declared here
51 | void (AudioCallbackTest::*m_audioCallback)(uint8_t *stream, int len) = &AudioCallbackTest::primeAudioCallback;
| ^~~~~~~~~~~~~~~
версия компилятора GNU C++17 (Ubuntu 11.3.0-1ubuntu1~22.04) версия 11.3.0 (x86_64-linux-gnu) Версия IDE: 2023-03 (4.27.0) Версия CDT: 11.1.0.202212091724
Я пробовал различные синтаксис
This->m_audioCallback(_stream, _length);
This->*m_audioCallback(_stream, _length);
(This->*m_audioCallback)(_stream, _length);
This->*m_audioCallback(_stream, _length);
(*This).*m_audioCallback(_stream, _length);
Я погуглил сообщение об ошибке.
Я гуглил «статический член, вызывающий член через указатель функции-члена», и получил информацию о том, как вызывать функции-члены, и множество примеров указателей на функции, но ничего, что попало в цель.
Я предполагаю, что для этого есть правильный синтаксис.
Сообщение об ошибке во втором примере говорит вам, что не так:
../testcallback.cpp:37:51: error: invalid use of member ‘AudioCallbackTest::m_audioCallback’ in static member function
37 | ((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);
| ^~~~~~~~~~~~~~~
m_audioCallback
— это переменная экземпляра, поэтому вы не можете просто
((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);
Вам нужно сообщить компилятору, чей m_audioCallback
использовать:
((AudioCallbackTest*)_instance)->*((AudioCallbackTest*)_instance)->m_audioCallback(_stream, _length);
Странно, я тоже не могу заставить это работать. clang сообщает, что тип объекта 'void (AudioCallbackTest::)(uint8_t, int)' не является функцией или указателем на функцию, но void (AudioCallbackTest::*)(uint8_t *, int)
, черт возьми, выглядит как указатель на функцию. g++ жалуется на ошибку: необходимо использовать '.' или '->' для вызова функции указателя на член, что я почти уверен, что делаю.
Но std::invoke не совершает той ошибки, которую совершаю я.
#include <iostream>
#include <functional>
using Uint8 = uint8_t;
class AudioCallbackTest
{
public:
AudioCallbackTest()
{
}
static void audioCallbackWrap(void *_instance, Uint8* _stream, int _length)
{
AudioCallbackTest* instance = (AudioCallbackTest*)_instance; // to simplify following
std::invoke(instance->m_audioCallback, *instance, _stream, _length);
// ^ Is an instance variable. Needs to be accessed through an instance
// ^ and here's the instance
//dunno what I have wrong in the syntax here.
// void (AudioCallbackTest::*)(uint8_t *, int) sure looks like a method pointer to me.
//instance->*(instance->m_audioCallback)(_stream, _length);
}
void audioCallback(Uint8* , int )
{
std::cout << "audio callback" << std::endl;
}
void primeAudioCallback(Uint8* , int )
{
std::cout << "prime callback" << std::endl;
m_audioCallback = &AudioCallbackTest::audioCallback;
}
private:
void (AudioCallbackTest::*m_audioCallback)(uint8_t *stream, int len) = &AudioCallbackTest::primeAudioCallback;
};
int main()
{
AudioCallbackTest test;
Uint8 stream = 1;
AudioCallbackTest::audioCallbackWrap (&test, &stream, 2);
AudioCallbackTest::audioCallbackWrap (&test, &stream, 2);
}
Выводит ожидаемый
prime callback
audio callback
Поскольку работа std::invoke
заключается в упрощении такого синтаксиса, используйте его.
Но std::invoke не делает той ошибки, которую делаю я. Все, что вам не хватает, это дополнительный набор скобок, похоже, но я обнаружил, что разбиение его на 3 строки значительно упрощает жизнь.
Ну что ж, сукин сын, @PaulSanders. (instance->*(instance->m_audioCallback))(_stream, _length);
Не могу поверить, что я этого не пробовал.
Рад, что я не единственный, кто спотыкается о такие вещи.
Я попытался упростить ваш код и, согласно этому ответу, я попытался преобразовать указатель, но это не сработало.
class A
{
void (A::* m_func_ptr)(int len) = &A::foo2;
static void foo(void* obj, int k) {
//want call foo1
A* p = static_cast<A*>(obj);
p->m_func_ptr(k);
}
void foo1(int len) { cout << "foo1" << endl; }
void foo2(int len) {
cout << "foo2" << endl;
m_func_ptr = &A::foo1;
}
};
Возможно, вы можете попробовать синглтон в том же ответе выше, объявив статический указатель в классе:
class A
{
public:
static A* myInstance;
//static A* getInstance() {
// if (!myInstance) {
// myInstance = new A;
// }
// return myInstance;
//}
static void foo(void* obj, int k) {
//call foo1
A* p = static_cast<A*>(obj);
p->foo1(k);
}
void foo1(int len) { cout << "foo1" << endl; }
void foo2(int len) {
cout << "foo2" << endl;
}
};
Я не знаю, как вы будете вызывать функцию, поэтому я написал только ее часть, не запуская ее на самом деле. Надеюсь это поможет.
В качестве альтернативы ответу @ user4581301 (хотя я думаю, что на самом деле я предпочитаю его метод), вот волшебный соус для прямого вызова функции:
static void audioCallbackWrap(void *_instance, Uint8* _stream, int _length)
{
auto *inst = (AudioCallbackTest*) _instance;
auto call_me = inst->m_audioCallback;
(inst->*call_me) (_stream, _length);
}
Обратите внимание на дополнительный набор квадратных скобок и необходимость использовать _instance
дважды: один раз для получения указателя на функцию-член и один раз для ее вызова.
Никогда раньше не использовал std::invoke, поэтому я сначала попробовал это, просто чтобы понять, и он отлично работает.
Также попробовал второй вариант (instance->*(instance->m_audioCallback))(_stream, _length); и это тоже работает.
Большое спасибо за помощь.
Но, в конце концов, у меня все еще есть задержка в 200 мс.
Почти уверен, что вы хотите
(((AudioCallbackTest*)_instance)->*m_audioCallback)(_stream, _length);
.