/***************************************************/ /*! \class RtWvIn \brief STK realtime audio (blocking) input class. This class provides a simplified interface to RtAudio for realtime audio input. It is a subclass of WvIn. This class makes use of RtAudio's callback functionality by creating a large ring-buffer from which data is read. This class should not be used when low-latency is desired. RtWvIn supports multi-channel data in both interleaved and non-interleaved formats. It is important to distinguish the tick() method that computes a single frame (and returns only the specified sample of a multi-channel frame) from the overloaded one that takes an StkFrames object for multi-channel and/or multi-frame data. by Perry R. Cook and Gary P. Scavone, 1995-2011. */ /***************************************************/ #include "RtWvIn.h" #include namespace stk { // This function is automatically called by RtAudio to supply input audio data. int read( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *dataPointer ) { ( (RtWvIn *) dataPointer )->fillBuffer( inputBuffer, nBufferFrames ); return 0; } // This function does not block. If the user does not read the buffer // data fast enough, unread data will be overwritten (data overrun). void RtWvIn :: fillBuffer( void *buffer, unsigned int nFrames ) { StkFloat *samples = (StkFloat *) buffer; unsigned int counter, iStart, nSamples = nFrames * data_.channels(); while ( nSamples > 0 ) { // I'm assuming that both the RtAudio and StkFrames buffers // contain interleaved data. iStart = writeIndex_ * data_.channels(); counter = nSamples; // Pre-increment write pointer and check bounds. writeIndex_ += nSamples / data_.channels(); if ( writeIndex_ >= data_.frames() ) { writeIndex_ = 0; counter = data_.size() - iStart; } // Copy data to the StkFrames container. for ( unsigned int i=0; i data_.frames() ) { framesFilled_ = data_.frames(); oStream_ << "RtWvIn: audio buffer overrun!"; handleError( StkError::WARNING ); } } RtWvIn :: RtWvIn( unsigned int nChannels, StkFloat sampleRate, int device, int bufferFrames, int nBuffers ) : stopped_( true ), readIndex_( 0 ), writeIndex_( 0 ), framesFilled_( 0 ) { // We'll let RtAudio deal with channel and sample rate limitations. RtAudio::StreamParameters parameters; if ( device == 0 ) parameters.deviceId = adc_.getDefaultInputDevice(); else parameters.deviceId = device - 1; parameters.nChannels = nChannels; unsigned int size = bufferFrames; RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; try { adc_.openStream( NULL, ¶meters, format, (unsigned int)Stk::sampleRate(), &size, &read, (void *)this ); } catch ( RtError &error ) { handleError( error.what(), StkError::AUDIO_SYSTEM ); } data_.resize( size * nBuffers, nChannels ); lastFrame_.resize( 1, nChannels ); } RtWvIn :: ~RtWvIn() { if ( !stopped_ ) adc_.stopStream(); adc_.closeStream(); } void RtWvIn :: start() { if ( stopped_ ) { adc_.startStream(); stopped_ = false; } } void RtWvIn :: stop() { if ( !stopped_ ) { adc_.stopStream(); stopped_ = true; for ( unsigned int i=0; i= data_.channels() ) { oStream_ << "RtWvIn::tick(): channel argument is incompatible with streamed channels!"; handleError( StkError::FUNCTION_ARGUMENT ); } #endif if ( stopped_ ) this->start(); // Block until at least one frame is available. while ( framesFilled_ == 0 ) Stk::sleep( 1 ); unsigned long index = readIndex_ * lastFrame_.channels(); for ( unsigned int i=0; i= data_.frames() ) readIndex_ = 0; return lastFrame_[channel]; } StkFrames& RtWvIn :: tick( StkFrames& frames ) { unsigned int nChannels = lastFrame_.channels(); #if defined(_STK_DEBUG_) if ( nChannels != frames.channels() ) { oStream_ << "RtWvIn::tick(): StkFrames argument is incompatible with adc channels!"; 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 space left in the frames object, then wait and repeat. unsigned int nFrames, bytes, framesRead = 0; while ( framesRead < frames.frames() ) { // Block until we have some input data. while ( framesFilled_ == 0 ) Stk::sleep( 1 ); // Copy data in one chunk up to the end of the data buffer. nFrames = framesFilled_; if ( readIndex_ + nFrames > data_.frames() ) nFrames = data_.frames() - readIndex_; if ( nFrames > frames.frames() - framesRead ) nFrames = frames.frames() - framesRead; bytes = nFrames * nChannels * sizeof( StkFloat ); StkFloat *samples = &data_[readIndex_ * nChannels]; memcpy( &frames[framesRead * nChannels], samples, bytes ); readIndex_ += nFrames; if ( readIndex_ == data_.frames() ) readIndex_ = 0; framesRead += nFrames; mutex_.lock(); framesFilled_ -= nFrames; mutex_.unlock(); } unsigned long index = (frames.frames() - 1) * nChannels; for ( unsigned int i=0; i