Mercurial > hg > apm2s
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( ¶meters, 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 |