annotate 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
rev   line source
f@0 1 /***************************************************/
f@0 2 /*! \class RtWvOut
f@0 3 \brief STK realtime audio (blocking) output class.
f@0 4
f@0 5 This class provides a simplified interface to RtAudio for realtime
f@0 6 audio output. It is a subclass of WvOut. This class makes use of
f@0 7 RtAudio's callback functionality by creating a large ring-buffer
f@0 8 into which data is written. This class should not be used when
f@0 9 low-latency is desired.
f@0 10
f@0 11 RtWvOut supports multi-channel data in interleaved format. It is
f@0 12 important to distinguish the tick() method that outputs a single
f@0 13 sample to all channels in a sample frame from the overloaded one
f@0 14 that takes a reference to an StkFrames object for multi-channel
f@0 15 and/or multi-frame data.
f@0 16
f@0 17 by Perry R. Cook and Gary P. Scavone, 1995--2014.
f@0 18 */
f@0 19 /***************************************************/
f@0 20 #pragma once
f@0 21
f@0 22 #include "../include/RtWvOut.h"
f@0 23 #include <cstring>
f@0 24
f@0 25 namespace stk {
f@0 26
f@0 27 // Streaming status states.
f@0 28 enum { RUNNING, EMPTYING, FINISHED };
f@0 29
f@0 30 // This function is automatically called by RtAudio to get audio data for output.
f@0 31 int write( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
f@0 32 double streamTime, RtAudioStreamStatus status, void *dataPointer )
f@0 33 {
f@0 34 return ( (RtWvOut *) dataPointer )->readBuffer( outputBuffer, nBufferFrames );
f@0 35 }
f@0 36
f@0 37 // This function does not block. If the user does not write output
f@0 38 // data to the buffer fast enough, previous data will be re-output
f@0 39 // (data underrun).
f@0 40 int RtWvOut :: readBuffer( void *buffer, unsigned int frameCount )
f@0 41 {
f@0 42 unsigned int nSamples, nChannels = data_.channels();
f@0 43 unsigned int nFrames = frameCount;
f@0 44 StkFloat *input = (StkFloat *) &data_[ readIndex_ * nChannels ];
f@0 45 StkFloat *output = (StkFloat *) buffer;
f@0 46 long counter;
f@0 47
f@0 48 while ( nFrames > 0 ) {
f@0 49
f@0 50 // I'm assuming that both the RtAudio and StkFrames buffers
f@0 51 // contain interleaved data.
f@0 52 counter = nFrames;
f@0 53
f@0 54 // Pre-increment read pointer and check bounds.
f@0 55 readIndex_ += nFrames;
f@0 56 if ( readIndex_ >= data_.frames() ) {
f@0 57 counter -= readIndex_ - data_.frames();
f@0 58 readIndex_ = 0;
f@0 59 }
f@0 60
f@0 61 // Copy data from the StkFrames container.
f@0 62 if ( status_ == EMPTYING && framesFilled_ <= counter ) {
f@0 63 nSamples = framesFilled_ * nChannels;
f@0 64 unsigned int i;
f@0 65 for ( i=0; i<nSamples; i++ ) *output++ = *input++;
f@0 66 nSamples = (counter - framesFilled_) * nChannels;
f@0 67 for ( i=0; i<nSamples; i++ ) *output++ = 0.0;
f@0 68 status_ = FINISHED;
f@0 69 return 1;
f@0 70 }
f@0 71 else {
f@0 72 nSamples = counter * nChannels;
f@0 73 for ( unsigned int i=0; i<nSamples; i++ )
f@0 74 *output++ = *input++;
f@0 75 }
f@0 76
f@0 77 nFrames -= counter;
f@0 78 }
f@0 79
f@0 80 mutex_.lock();
f@0 81 framesFilled_ -= frameCount;
f@0 82 mutex_.unlock();
f@0 83 if ( framesFilled_ < 0 ) {
f@0 84 framesFilled_ = 0;
f@0 85 // writeIndex_ = readIndex_;
f@0 86 oStream_ << "RtWvOut: audio buffer underrun!";
f@0 87 handleError( StkError::WARNING );
f@0 88 }
f@0 89
f@0 90 return 0;
f@0 91 }
f@0 92
f@0 93
f@0 94 RtWvOut :: RtWvOut( unsigned int nChannels, StkFloat sampleRate, int device, int bufferFrames, int nBuffers )
f@0 95 : stopped_( true ), readIndex_( 0 ), writeIndex_( 0 ), framesFilled_( 0 ), status_(0)
f@0 96 {
f@0 97 // We'll let RtAudio deal with channel and sample rate limitations.
f@0 98 RtAudio::StreamParameters parameters;
f@0 99 if ( device == 0 )
f@0 100 parameters.deviceId = dac_.getDefaultOutputDevice();
f@0 101 else
f@0 102 parameters.deviceId = device - 1;
f@0 103 parameters.nChannels = nChannels;
f@0 104 unsigned int size = bufferFrames;
f@0 105 RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
f@0 106
f@0 107 // Open a stream and set the callback function.
f@0 108 try {
f@0 109 dac_.openStream( &parameters, NULL, format, (unsigned int)Stk::sampleRate(), &size, &write, (void *)this );
f@0 110 }
f@0 111 catch ( RtAudioError &error ) {
f@0 112 handleError( error.what(), StkError::AUDIO_SYSTEM );
f@0 113 }
f@0 114
f@0 115 data_.resize( size * nBuffers, nChannels );
f@0 116
f@0 117 // Start writing half-way into buffer.
f@0 118 writeIndex_ = (unsigned int ) (data_.frames() / 2.0);
f@0 119 framesFilled_ = writeIndex_;
f@0 120 }
f@0 121
f@0 122 RtWvOut :: ~RtWvOut( void )
f@0 123 {
f@0 124 // Change status flag to signal callback to clear the buffer and close.
f@0 125 status_ = EMPTYING;
f@0 126 while ( status_ != FINISHED || dac_.isStreamRunning() == true ) Stk::sleep( 100 );
f@0 127 dac_.closeStream();
f@0 128 }
f@0 129
f@0 130 void RtWvOut :: start( void )
f@0 131 {
f@0 132 if ( stopped_ ) {
f@0 133 dac_.startStream();
f@0 134 stopped_ = false;
f@0 135 }
f@0 136 }
f@0 137
f@0 138 void RtWvOut :: stop( void )
f@0 139 {
f@0 140 if ( !stopped_ ) {
f@0 141 dac_.stopStream();
f@0 142 stopped_ = true;
f@0 143 }
f@0 144 }
f@0 145
f@0 146 void RtWvOut :: tick( const StkFloat sample )
f@0 147 {
f@0 148 if ( stopped_ ) this->start();
f@0 149
f@0 150 // Block until we have room for at least one frame of output data.
f@0 151 while ( framesFilled_ == (long) data_.frames() ) Stk::sleep( 1 );
f@0 152
f@0 153 unsigned int nChannels = data_.channels();
f@0 154 StkFloat input = sample;
f@0 155 clipTest( input );
f@0 156 unsigned long index = writeIndex_ * nChannels;
f@0 157 for ( unsigned int j=0; j<nChannels; j++ )
f@0 158 data_[index++] = input;
f@0 159
f@0 160 mutex_.lock();
f@0 161 framesFilled_++;
f@0 162 mutex_.unlock();
f@0 163 frameCounter_++;
f@0 164 writeIndex_++;
f@0 165 if ( writeIndex_ == data_.frames() )
f@0 166 writeIndex_ = 0;
f@0 167 }
f@0 168
f@0 169 void RtWvOut :: tick( const StkFrames& frames )
f@0 170 {
f@0 171 #if defined(_STK_DEBUG_)
f@0 172 if ( data_.channels() != frames.channels() ) {
f@0 173 oStream_ << "RtWvOut::tick(): incompatible channel value in StkFrames argument!";
f@0 174 handleError( StkError::FUNCTION_ARGUMENT );
f@0 175 }
f@0 176 #endif
f@0 177
f@0 178 if ( stopped_ ) this->start();
f@0 179
f@0 180 // See how much space we have and fill as much as we can ... if we
f@0 181 // still have samples left in the frames object, then wait and
f@0 182 // repeat.
f@0 183 unsigned int framesEmpty, nFrames, bytes, framesWritten = 0;
f@0 184 unsigned int nChannels = data_.channels();
f@0 185 while ( framesWritten < frames.frames() ) {
f@0 186
f@0 187 // Block until we have some room for output data.
f@0 188 while ( framesFilled_ == (long) data_.frames() ) Stk::sleep( 1 );
f@0 189 framesEmpty = data_.frames() - framesFilled_;
f@0 190
f@0 191 // Copy data in one chunk up to the end of the data buffer.
f@0 192 nFrames = framesEmpty;
f@0 193 if ( writeIndex_ + nFrames > data_.frames() )
f@0 194 nFrames = data_.frames() - writeIndex_;
f@0 195 if ( nFrames > frames.frames() - framesWritten )
f@0 196 nFrames = frames.frames() - framesWritten;
f@0 197 bytes = nFrames * nChannels * sizeof( StkFloat );
f@0 198 StkFloat *samples = &data_[writeIndex_ * nChannels];
f@0 199 StkFrames *ins = (StkFrames *) &frames;
f@0 200 memcpy( samples, &(*ins)[framesWritten * nChannels], bytes );
f@0 201 for ( unsigned int i=0; i<nFrames * nChannels; i++ ) clipTest( *samples++ );
f@0 202
f@0 203 writeIndex_ += nFrames;
f@0 204 if ( writeIndex_ == data_.frames() ) writeIndex_ = 0;
f@0 205
f@0 206 framesWritten += nFrames;
f@0 207 mutex_.lock();
f@0 208 framesFilled_ += nFrames;
f@0 209 mutex_.unlock();
f@0 210 frameCounter_ += nFrames;
f@0 211 }
f@0 212 }
f@0 213
f@0 214 } // stk namespace