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