Version 4.3.0

This commit is contained in:
Gary Scavone
2009-03-24 23:02:15 -04:00
committed by Stephen Sinclair
parent 2cbce2d8bd
commit 27d9b79dc7
271 changed files with 22219 additions and 8834 deletions

View File

@@ -3,62 +3,124 @@
\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. Because this class
makes use of RtAudio's blocking output routines, its performance
is less robust on systems where the audio API is callback-based
(Macintosh CoreAudio and Windows ASIO).
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() methods, which output single
samples to all channels in a sample frame, from the tickFrame()
method, which take a pointer or reference to multi-channel sample
frame data.
RtWvOut supports multi-channel data in both interleaved and
non-interleaved formats. It is important to distinguish the
tick() methods, which output single samples to all channels in a
sample frame, from the tickFrame() method, which take a pointer or
reference to multi-channel sample frame data.
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
*/
/***************************************************/
#include "RtWvOut.h"
// 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;
}
framesFilled_ -= frameCount;
if ( framesFilled_ < 0 ) {
framesFilled_ = 0;
// writeIndex_ = readIndex_;
errorString_ << "RtWvOut: audio buffer underrun!";
handleError( StkError::WARNING );
}
return 0;
}
RtWvOut :: RtWvOut( unsigned int nChannels, StkFloat sampleRate, int device, int bufferFrames, int nBuffers )
: stopped_( true ), nChannels_(nChannels), bufferIndex_( 0 ), iBuffer_( 0 )
: stopped_( true ), readIndex_( 0 ), writeIndex_( 0 ), framesFilled_( 0 ), status_(0)
{
// We'll let RtAudio deal with channel and sample rate limitations.
int size = bufferFrames;
RtAudio::StreamParameters parameters;
parameters.deviceId = device;
parameters.nChannels = nChannels;
unsigned int size = bufferFrames;
RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
dac_ = 0;
// Open a stream and set the callback function.
try {
dac_ = new RtAudio();
dac_.openStream( &parameters, NULL, format, (unsigned int)Stk::sampleRate(), &size, &write, (void *)this );
}
catch (RtError &error) {
handleError( error.getMessageString(), StkError::AUDIO_SYSTEM );
catch ( RtError &error ) {
handleError( error.what(), StkError::AUDIO_SYSTEM );
}
// Now open a stream and get the buffer pointer.
try {
dac_->openStream( device, (int)nChannels, 0, 0, format,
(int)sampleRate, &size, nBuffers );
buffer_ = (StkFloat *) dac_->getStreamBuffer();
}
catch (RtError &error) {
handleError( error.getMessageString(), StkError::AUDIO_SYSTEM );
}
bufferFrames_ = size;
data_.resize( size * nBuffers, nChannels );
unsigned int offset = (unsigned int ) (data_.size() / 2.0);
writeIndex_ = offset; // start writing half-way into buffer
framesFilled_ = offset;
}
RtWvOut :: ~RtWvOut()
{
if ( !stopped_ ) dac_->stopStream();
dac_->closeStream();
delete dac_;
// 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()
{
if ( stopped_ ) {
dac_->startStream();
dac_.startStream();
stopped_ = false;
}
}
@@ -66,77 +128,81 @@ void RtWvOut :: start()
void RtWvOut :: stop()
{
if ( !stopped_ ) {
dac_->stopStream();
dac_.stopStream();
stopped_ = true;
}
}
void RtWvOut :: incrementFrame( void )
{
frameCounter_++;
bufferIndex_++;
if ( bufferIndex_ == bufferFrames_ ) {
try {
dac_->tickStream();
}
catch (RtError &error) {
handleError( error.getMessageString(), StkError::AUDIO_SYSTEM );
}
bufferIndex_ = 0;
iBuffer_ = 0;
}
}
void RtWvOut :: computeSample( const StkFloat sample )
{
if ( stopped_ ) start();
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 );
for ( unsigned int j=0; j<nChannels_; j++ )
buffer_[iBuffer_++] = input;
unsigned long index = writeIndex_ * nChannels;
for ( unsigned int j=0; j<nChannels; j++ )
data_[index++] = input;
this->incrementFrame();
framesFilled_++;
frameCounter_++;
writeIndex_++;
if ( writeIndex_ == data_.frames() )
writeIndex_ = 0;
}
void RtWvOut :: computeFrames( const StkFrames& frames )
{
if ( stopped_ ) start();
if ( frames.channels() != nChannels_ ) {
if ( data_.channels() != frames.channels() ) {
errorString_ << "RtWvOut::computeFrames(): incompatible channel value in StkFrames argument!";
handleError( StkError::FUNCTION_ARGUMENT );
}
unsigned int j;
if ( nChannels_ == 1 || frames.interleaved() ) {
if ( stopped_ ) this->start();
unsigned int iFrames = 0;
// Block until we have room for the frames of output data.
while ( data_.frames() - framesFilled_ < frames.frames() ) Stk::sleep( 1 );
unsigned int j, nChannels = data_.channels();
if ( nChannels == 1 || frames.interleaved() ) {
unsigned int index, iFrames = 0;
for ( unsigned int i=0; i<frames.frames(); i++ ) {
for ( j=0; j<nChannels_; j++ ) {
buffer_[iBuffer_] = frames[iFrames++];
clipTest( buffer_[iBuffer_++] );
}
index = writeIndex_ * nChannels;
for ( j=0; j<nChannels; j++ )
data_[index] = frames[iFrames++];
clipTest( data_[index++] );
this->incrementFrame();
framesFilled_++;
frameCounter_++;
writeIndex_++;
if ( writeIndex_ == data_.frames() )
writeIndex_ = 0;
}
}
else { // non-interleaved frames
unsigned long hop = frames.frames();
unsigned int index;
unsigned int index, iFrame;
for ( unsigned int i=0; i<frames.frames(); i++ ) {
index = i;
for ( j=0; j<nChannels_; j++ ) {
buffer_[iBuffer_] = frames[index];
clipTest( buffer_[iBuffer_++] );
index += hop;
iFrame = i;
index = writeIndex_ * nChannels;
for ( j=0; j<nChannels; j++ ) {
data_[index] = frames[iFrame];
clipTest( data_[index++] );
iFrame += hop;
}
this->incrementFrame();
framesFilled_++;
frameCounter_++;
writeIndex_++;
if ( writeIndex_ == data_.frames() )
writeIndex_ = 0;
}
}
}