Mercurial > hg > apm2s
diff stk/src/RtWvOut.cpp @ 0:4606bd505630 tip
first import
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Sat, 13 Jun 2015 15:08:10 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stk/src/RtWvOut.cpp Sat Jun 13 15:08:10 2015 +0100 @@ -0,0 +1,214 @@ +/***************************************************/ +/*! \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--2014. +*/ +/***************************************************/ +#pragma once + +#include "../include/RtWvOut.h" +#include <cstring> + +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; i<nSamples; i++ ) *output++ = *input++; + nSamples = (counter - framesFilled_) * nChannels; + for ( i=0; i<nSamples; i++ ) *output++ = 0.0; + status_ = FINISHED; + return 1; + } + else { + nSamples = counter * nChannels; + for ( unsigned int i=0; i<nSamples; i++ ) + *output++ = *input++; + } + + nFrames -= counter; + } + + mutex_.lock(); + framesFilled_ -= frameCount; + mutex_.unlock(); + if ( framesFilled_ < 0 ) { + framesFilled_ = 0; + // writeIndex_ = readIndex_; + oStream_ << "RtWvOut: audio buffer underrun!"; + handleError( StkError::WARNING ); + } + + return 0; +} + + +RtWvOut :: RtWvOut( unsigned int nChannels, StkFloat sampleRate, int device, int bufferFrames, int nBuffers ) + : stopped_( true ), readIndex_( 0 ), writeIndex_( 0 ), framesFilled_( 0 ), status_(0) +{ + // We'll let RtAudio deal with channel and sample rate limitations. + RtAudio::StreamParameters parameters; + if ( device == 0 ) + parameters.deviceId = dac_.getDefaultOutputDevice(); + else + parameters.deviceId = device - 1; + parameters.nChannels = nChannels; + unsigned int size = bufferFrames; + RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; + + // Open a stream and set the callback function. + try { + dac_.openStream( ¶meters, NULL, format, (unsigned int)Stk::sampleRate(), &size, &write, (void *)this ); + } + catch ( RtAudioError &error ) { + handleError( error.what(), StkError::AUDIO_SYSTEM ); + } + + data_.resize( size * nBuffers, nChannels ); + + // Start writing half-way into buffer. + writeIndex_ = (unsigned int ) (data_.frames() / 2.0); + framesFilled_ = writeIndex_; +} + +RtWvOut :: ~RtWvOut( void ) +{ + // Change status flag to signal callback to clear the buffer and close. + status_ = EMPTYING; + while ( status_ != FINISHED || dac_.isStreamRunning() == true ) Stk::sleep( 100 ); + dac_.closeStream(); +} + +void RtWvOut :: start( void ) +{ + if ( stopped_ ) { + dac_.startStream(); + stopped_ = false; + } +} + +void RtWvOut :: stop( void ) +{ + if ( !stopped_ ) { + dac_.stopStream(); + stopped_ = true; + } +} + +void RtWvOut :: tick( const StkFloat sample ) +{ + if ( stopped_ ) this->start(); + + // 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; j<nChannels; j++ ) + data_[index++] = input; + + mutex_.lock(); + framesFilled_++; + mutex_.unlock(); + frameCounter_++; + writeIndex_++; + if ( writeIndex_ == data_.frames() ) + writeIndex_ = 0; +} + +void RtWvOut :: tick( const StkFrames& frames ) +{ +#if defined(_STK_DEBUG_) + if ( data_.channels() != frames.channels() ) { + oStream_ << "RtWvOut::tick(): incompatible channel value in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + if ( stopped_ ) this->start(); + + // 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<nFrames * nChannels; i++ ) clipTest( *samples++ ); + + writeIndex_ += nFrames; + if ( writeIndex_ == data_.frames() ) writeIndex_ = 0; + + framesWritten += nFrames; + mutex_.lock(); + framesFilled_ += nFrames; + mutex_.unlock(); + frameCounter_ += nFrames; + } +} + +} // stk namespace