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