Ошибка доступа к памяти при использовании 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( &parameters, 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( &parameters, /* 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( &parameters, NULL, format, static_cast<unsigned int>(Stk::sampleRate()), &bufferFrames, &tick, reinterpret_cast<void*>(&instrument_FM));
    }
    catch ( RtError& error ) {
        error.printMessage();
    }
}
Другие вопросы по тегам