Ошибка доступа к памяти при использовании STK и класса FMVoices
Я пытаюсь использовать STK из Стэнфорда, чтобы сделать синтез в реальном времени. Я использую класс инструментов FMVoices https://ccrma.stanford.edu/software/stk/classstk_1_1FMVoices.html и пытаюсь использовать его в процедуре обратного вызова, определенной ниже.
int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
double streamTime, RtAudioStreamStatus status, void *dataPointer )
{
FMVoices *FM = (FMVoices *) dataPointer;
register StkFloat *samples = (StkFloat *) outputBuffer;
for ( unsigned int i=0; i<nBufferFrames; i++ )
*samples++ = FM->tick();
return 0;
}
Проблема, я думаю, связана с типом этого последнего параметра. Я получаю сообщение об ошибке во время выполнения: "0xC0000005: нарушение прав доступа, местоположение выполнения 0x000000001". Теперь, это способ, которым обратный вызов должен быть написан для других инструментов STK, таких как Clarinet или даже класс FileLoop, но в FMVoices есть что-то прикольное. Объект передается openStream (который обрабатывает специфичные для платформы выходные данные в реальном времени) как указатель на void. Обратный вызов вызывается автоматически, когда звуковой буфер системы заполнен. Фрагмент кода, который реализует это и работает для других инструментов, показан ниже:
int main()
{
// Set the global sample rate before creating class instances.
Stk::setSampleRate( 44100.0 );
RtAudio dac;
Instrmnt * instrument_FM;
int nFrames = 10000;
try {
instrument_FM = new FMVoices;
}
catch ( StkError & ) {
goto cleanup;
}
instrument_FM->setFrequency(440.0);
// Figure out how many bytes in an StkFloat and setup the RtAudio stream.
RtAudio::StreamParameters parameters;
parameters.deviceId = dac.getDefaultOutputDevice();
parameters.nChannels = 1;
RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
unsigned int bufferFrames = RT_BUFFER_SIZE;
try {
dac.openStream( ¶meters, NULL, format, (unsigned int)Stk::sampleRate(), &bufferFrames, &tick, (void *)&instrument_FM);
}
catch ( RtError &error ) {
error.printMessage();
Sleep(1000);
goto cleanup;
}
Размер nFrames, похоже, не влияет. Мне просто показалось, что эти типы ошибок обычно происходят из-за ссылки на void.
1 ответ
Проблема в том, что вы берете адрес указателя и передаете его в openStream
,
// pointer to instrument
Instrmnt * instrument_FM;
// snip ...
// &instrument_FM is a pointer to a pointer! i.e. Instrmnt **
dac.openStream( ¶meters, /* other params */, (void *)&instrument_FM)
Самое быстрое решение - просто избавиться от &
в этой строке.
Теперь несколько комментариев о C++ и еще несколько исправлений в вашем коде. Код выглядит как смесь C и Java и открывает множество подводных камней, одна из которых привела к вашей проблеме.
- Нет необходимости динамически распределять
FMVoices
, Используйте стек так же, как дляRtAudio dac
,- Не нужно беспокоиться об указателях, и
delete
память, которую вы выделили - Поэтому нет утечек памяти.
- Просто пиши
FMVoices instrument_FM;
- Не нужно беспокоиться об указателях, и
- Там нет необходимости делать
try
/catch
в большинстве случаев для очистки, поскольку C++ имеет деструкторы, которые срабатывают в конце области и распространяют ошибку.- Если вы используете только стек, вам не нужно беспокоиться о
delete
и имея операции по очистке
- Если вы используете только стек, вам не нужно беспокоиться о
- Никогда не используйте
goto
в C++ это действительно не нужно. (в отличие от C, где он может быть использован для очистки).- Для этого есть деструкторы и RAII.
- Используйте C++ приведения, которые более мелкозернистые, такие как
static_cast<>
а такжеreinterpret_cast<>
вместо бросков в стиле C- Смотрите эту статью для объяснения.
Вот пересмотренный код:
int main()
{
// Set the global sample rate before creating class instances.
Stk::setSampleRate( 44100.0 );
RtAudio dac;
FMVoices instrument_FM;
instrument_FM.setFrequency(440.0);
// Figure out how many bytes in an StkFloat and setup the RtAudio stream.
RtAudio::StreamParameters parameters;
parameters.deviceId = dac.getDefaultOutputDevice();
parameters.nChannels = 1;
RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
unsigned int bufferFrames = RT_BUFFER_SIZE;
// didn't get rid of this try since you want to print the error message.
try {
// note here i need the ampersand &, because instrument_FM is on the stack
dac.openStream( ¶meters, NULL, format, static_cast<unsigned int>(Stk::sampleRate()), &bufferFrames, &tick, reinterpret_cast<void*>(&instrument_FM));
}
catch ( RtError& error ) {
error.printMessage();
}
}