/***************************************************/ /*! \class RtWvOut \brief STK realtime audio (blocking) output class. This class provides a simplified interface to RtAudio for realtime audio output. It is a subclass of WvOut. This class makes use of RtAudio's callback functionality by creating a large ring-buffer into which data is written. This class should not be used when low-latency is desired. RtWvOut supports multi-channel data in interleaved format. It is important to distinguish the tick() method that outputs a single sample to all channels in a sample frame from the overloaded one that takes a reference to an StkFrames object for multi-channel and/or multi-frame data. by Perry R. Cook and Gary P. Scavone, 1995--2017. */ /***************************************************/ #include "RtWvOut.h" #include namespace stk { // Streaming status states. enum { RUNNING, EMPTYING, FINISHED }; // This function is automatically called by RtAudio to get audio data for output. int write( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *dataPointer ) { return ( (RtWvOut *) dataPointer )->readBuffer( outputBuffer, nBufferFrames ); } // This function does not block. If the user does not write output // data to the buffer fast enough, previous data will be re-output // (data underrun). int RtWvOut :: readBuffer( void *buffer, unsigned int frameCount ) { unsigned int nSamples, nChannels = data_.channels(); unsigned int nFrames = frameCount; StkFloat *input = (StkFloat *) &data_[ readIndex_ * nChannels ]; StkFloat *output = (StkFloat *) buffer; long counter; while ( nFrames > 0 ) { // I'm assuming that both the RtAudio and StkFrames buffers // contain interleaved data. counter = nFrames; // Pre-increment read pointer and check bounds. readIndex_ += nFrames; if ( readIndex_ >= data_.frames() ) { counter -= readIndex_ - data_.frames(); readIndex_ = 0; } // Copy data from the StkFrames container. if ( status_ == EMPTYING && framesFilled_ <= counter ) { nSamples = framesFilled_ * nChannels; unsigned int i; for ( i=0; istart(); // Block until we have room for at least one frame of output data. while ( framesFilled_ == (long) data_.frames() ) Stk::sleep( 1 ); unsigned int nChannels = data_.channels(); StkFloat input = sample; clipTest( input ); unsigned long index = writeIndex_ * nChannels; for ( unsigned int j=0; jstart(); // See how much space we have and fill as much as we can ... if we // still have samples left in the frames object, then wait and // repeat. unsigned int framesEmpty, nFrames, bytes, framesWritten = 0; unsigned int nChannels = data_.channels(); while ( framesWritten < frames.frames() ) { // Block until we have some room for output data. while ( framesFilled_ == (long) data_.frames() ) Stk::sleep( 1 ); framesEmpty = data_.frames() - framesFilled_; // Copy data in one chunk up to the end of the data buffer. nFrames = framesEmpty; if ( writeIndex_ + nFrames > data_.frames() ) nFrames = data_.frames() - writeIndex_; if ( nFrames > frames.frames() - framesWritten ) nFrames = frames.frames() - framesWritten; bytes = nFrames * nChannels * sizeof( StkFloat ); StkFloat *samples = &data_[writeIndex_ * nChannels]; StkFrames *ins = (StkFrames *) &frames; memcpy( samples, &(*ins)[framesWritten * nChannels], bytes ); for ( unsigned int i=0; i