Question

I'm trying to use the STK from Stanford to do some realtime wavetable synthesis. I'm using the FMVoices instrument class https://ccrma.stanford.edu/software/stk/classstk_1_1FMVoices.html and trying to use it in a callback routine defined below.

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;
}

The issue, I think, is with the type of that last parameter. I'm getting a runtime error : "0xC0000005: Access violation executing location 0x000000001." Now, this is the way that the callback is supposed to be written for other STK instruments like Clarinet or even the FileLoop class, but there's something funky about FMVoices. The object is passed to openStream (which handles platform specific realtime output) as a pointer to void. The callback is called automatically when the system's audio buffer is full. A code snippet that implements this and DOES work for other instruments is shown below:

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;

}

The size of nFrames does not seem to have an effect. It just seemed to me that these types of errors usually come from referencing a pointer to void.

Was it helpful?

Solution

The problem is you are taking the address of a pointer, and passing it into 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)

The quickest solution is to just get rid of the & in that line.


Now some comments on C++, and some more fixes to your code. The code looks like a mixture of C and Java, and opens up a lot of pitfalls to fall into, one of which led to your problem.

  • There is no need for dynamically allocating FMVoices . Use the stack just like you did for RtAudio dac.
    • No need to worry about pointers, and deleteing the memory you allocated
    • Therefore no memory leaks.
    • Just write FMVoices instrument_FM;
  • There is no need to do try/catch in most cases for cleanup since C++ has destructors that trigger at the end of scope, and propagate the error.
    • If you only use the stack, there is no need to worry about delete and having cleanup operations
  • Don't ever use goto in C++, it's really not needed. (unlike in C, where it could be used for cleanup).
    • There are destructors and RAII for that.
  • Use C++ casts which are more fine-grained, such as static_cast<> and reinterpret_cast<>, instead of C-style casts

Here's the revised code:

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();
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top