mirror of
https://github.com/thestk/stk
synced 2026-01-16 22:31:52 +00:00
Version 4.3.0
This commit is contained in:
committed by
Stephen Sinclair
parent
2cbce2d8bd
commit
27d9b79dc7
11
src/ADSR.cpp
11
src/ADSR.cpp
@@ -11,7 +11,7 @@
|
||||
envelope value reaches 0.0 in the
|
||||
ADSR::RELEASE state.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -32,6 +32,15 @@ ADSR :: ~ADSR()
|
||||
{
|
||||
}
|
||||
|
||||
void ADSR :: sampleRateChanged( StkFloat newRate, StkFloat oldRate )
|
||||
{
|
||||
if ( !ignoreSampleRateChange_ ) {
|
||||
attackRate_ = oldRate * attackRate_ / newRate;
|
||||
decayRate_ = oldRate * decayRate_ / newRate;
|
||||
releaseRate_ = oldRate * releaseRate_ / newRate;
|
||||
}
|
||||
}
|
||||
|
||||
void ADSR :: keyOn()
|
||||
{
|
||||
target_ = 1.0;
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
to \e keyOn and \e keyOff messages by ramping to
|
||||
1.0 on keyOn and to 0.0 on keyOff.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
#include "Asymp.h"
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
Asymp :: Asymp(void) : Envelope()
|
||||
{
|
||||
@@ -36,6 +36,14 @@ Asymp :: ~Asymp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void Asymp :: sampleRateChanged( StkFloat newRate, StkFloat oldRate )
|
||||
{
|
||||
if ( !ignoreSampleRateChange_ ) {
|
||||
StkFloat tau = -1.0 / ( std::log( factor_ ) * oldRate );
|
||||
factor_ = std::exp( -1.0 / ( tau * newRate ) );
|
||||
}
|
||||
}
|
||||
|
||||
void Asymp :: keyOn(void)
|
||||
{
|
||||
Envelope::keyOn();
|
||||
@@ -56,7 +64,7 @@ void Asymp :: setTau(StkFloat tau)
|
||||
return;
|
||||
}
|
||||
|
||||
factor_ = exp( -1.0 / ( tau * Stk::sampleRate() ) );
|
||||
factor_ = std::exp( -1.0 / ( tau * Stk::sampleRate() ) );
|
||||
constant_ = ( 1.0 - factor_ ) * target_;
|
||||
}
|
||||
|
||||
@@ -68,8 +76,8 @@ void Asymp :: setTime(StkFloat time)
|
||||
return;
|
||||
}
|
||||
|
||||
StkFloat tau = -time / log( TARGET_THRESHOLD );
|
||||
factor_ = exp( -1.0 / ( tau * Stk::sampleRate() ) );
|
||||
StkFloat tau = -time / std::log( TARGET_THRESHOLD );
|
||||
factor_ = std::exp( -1.0 / ( tau * Stk::sampleRate() ) );
|
||||
constant_ = ( 1.0 - factor_ ) * target_;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
type who should worry about this (making
|
||||
money) worry away.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
frequency response while maintaining a constant
|
||||
filter gain.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -22,10 +22,20 @@ BiQuad :: BiQuad() : Filter()
|
||||
b[0] = 1.0;
|
||||
a[0] = 1.0;
|
||||
Filter::setCoefficients( b, a );
|
||||
Stk::addSampleRateAlert( this );
|
||||
}
|
||||
|
||||
BiQuad :: ~BiQuad()
|
||||
{
|
||||
Stk::removeSampleRateAlert( this );
|
||||
}
|
||||
|
||||
void BiQuad :: sampleRateChanged( StkFloat newRate, StkFloat oldRate )
|
||||
{
|
||||
if ( !ignoreSampleRateChange_ ) {
|
||||
errorString_ << "BiQuad::sampleRateChanged: you may need to recompute filter coefficients!";
|
||||
handleError( StkError::WARNING );
|
||||
}
|
||||
}
|
||||
|
||||
void BiQuad :: clear(void)
|
||||
@@ -99,26 +109,3 @@ StkFloat BiQuad :: lastOut(void) const
|
||||
{
|
||||
return Filter::lastOut();
|
||||
}
|
||||
|
||||
StkFloat BiQuad :: computeSample( StkFloat input )
|
||||
{
|
||||
inputs_[0] = gain_ * input;
|
||||
outputs_[0] = b_[0] * inputs_[0] + b_[1] * inputs_[1] + b_[2] * inputs_[2];
|
||||
outputs_[0] -= a_[2] * outputs_[2] + a_[1] * outputs_[1];
|
||||
inputs_[2] = inputs_[1];
|
||||
inputs_[1] = inputs_[0];
|
||||
outputs_[2] = outputs_[1];
|
||||
outputs_[1] = outputs_[0];
|
||||
|
||||
return outputs_[0];
|
||||
}
|
||||
|
||||
StkFloat BiQuad :: tick( StkFloat input )
|
||||
{
|
||||
return this->computeSample( input );
|
||||
}
|
||||
|
||||
StkFrames& BiQuad :: tick( StkFrames& frames, unsigned int channel )
|
||||
{
|
||||
return Filter::tick( frames, channel );
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
The algorithm implemented in this class uses a SincM function with
|
||||
an even M value to achieve a bipolar bandlimited impulse train.
|
||||
This signal is then integrated to achieve a square waveform. The
|
||||
integration process has an associated DC offset but that is
|
||||
subtracted off the output signal.
|
||||
integration process has an associated DC offset so a DC blocking
|
||||
filter is applied at the output.
|
||||
|
||||
The user can specify both the fundamental frequency of the
|
||||
waveform and the number of harmonics contained in the resulting
|
||||
@@ -19,17 +19,21 @@
|
||||
to half the sample rate. Note, however, that this setting may
|
||||
produce aliasing in the signal when the frequency is changing (no
|
||||
automatic modification of the number of harmonics is performed by
|
||||
the setFrequency() function).
|
||||
the setFrequency() function). Also note that the harmonics of a
|
||||
square wave fall at odd integer multiples of the fundamental, so
|
||||
aliasing will happen with a lower fundamental than with the other
|
||||
Blit waveforms. This class is not guaranteed to be well behaved
|
||||
in the presence of significant aliasing.
|
||||
|
||||
Based on initial code of Robin Davies, 2005.
|
||||
Modified algorithm code by Gary Scavone, 2005.
|
||||
Modified algorithm code by Gary Scavone, 2005 - 2006.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
#include "BlitSquare.h"
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
|
||||
BlitSquare:: BlitSquare( StkFloat frequency )
|
||||
{
|
||||
nHarmonics_ = 0;
|
||||
@@ -45,6 +49,8 @@ void BlitSquare :: reset()
|
||||
{
|
||||
phase_ = 0.0;
|
||||
lastOutput_ = 0;
|
||||
dcbState_ = 0.0;
|
||||
lastBlitOutput_ = 0;
|
||||
}
|
||||
|
||||
void BlitSquare :: setFrequency( StkFloat frequency )
|
||||
@@ -56,8 +62,8 @@ void BlitSquare :: setFrequency( StkFloat frequency )
|
||||
|
||||
// By using an even value of the parameter M, we get a bipolar blit
|
||||
// waveform at half the blit frequency. Thus, we need to scale the
|
||||
// frequency value here by 2.0. (GPS, 2005).
|
||||
p_ = 2.0 * Stk::sampleRate() / frequency;
|
||||
// frequency value here by 0.5. (GPS, 2006).
|
||||
p_ = 0.5 * Stk::sampleRate() / frequency;
|
||||
rate_ = PI / p_;
|
||||
this->updateHarmonics();
|
||||
}
|
||||
@@ -73,13 +79,12 @@ void BlitSquare :: updateHarmonics( void )
|
||||
// Make sure we end up with an even value of the parameter M here.
|
||||
if ( nHarmonics_ <= 0 ) {
|
||||
unsigned int maxHarmonics = (unsigned int) floor( 0.5 * p_ );
|
||||
m_ = 2 * maxHarmonics;
|
||||
m_ = 2 * (maxHarmonics + 1);
|
||||
}
|
||||
else
|
||||
m_ = 2 * nHarmonics_;
|
||||
m_ = 2 * (nHarmonics_ + 1);
|
||||
|
||||
// This offset value was derived empirically. (GPS, 2005)
|
||||
offset_ = 1.0 - 0.5 * m_ / p_;
|
||||
a_ = m_ / p_;
|
||||
|
||||
#if defined(_STK_DEBUG_)
|
||||
errorString_ << "BlitSquare::updateHarmonics: nHarmonics_ = " << nHarmonics_ << ", m_ = " << m_ << '.';
|
||||
@@ -89,7 +94,7 @@ void BlitSquare :: updateHarmonics( void )
|
||||
|
||||
StkFloat BlitSquare :: computeSample( void )
|
||||
{
|
||||
StkFloat temp = lastOutput_;
|
||||
StkFloat temp = lastBlitOutput_;
|
||||
|
||||
// A fully optimized version of this would replace the two sin calls
|
||||
// with a pair of fast sin oscillators, for which stable fast
|
||||
@@ -103,20 +108,24 @@ StkFloat BlitSquare :: computeSample( void )
|
||||
if ( fabs( denominator ) < std::numeric_limits<StkFloat>::epsilon() ) {
|
||||
// Inexact comparison safely distinguishes betwen *close to zero*, and *close to PI*.
|
||||
if ( phase_ < 0.1f || phase_ > TWO_PI - 0.1f )
|
||||
lastOutput_ = 1.0;
|
||||
lastBlitOutput_ = a_;
|
||||
else
|
||||
lastOutput_ = -1.0;
|
||||
lastBlitOutput_ = -a_;
|
||||
}
|
||||
else {
|
||||
lastOutput_ = sin( m_ * phase_ );
|
||||
lastOutput_ /= p_ * denominator;
|
||||
lastBlitOutput_ = sin( m_ * phase_ );
|
||||
lastBlitOutput_ /= p_ * denominator;
|
||||
}
|
||||
|
||||
lastOutput_ += temp;
|
||||
lastBlitOutput_ += temp;
|
||||
|
||||
// Now apply DC blocker.
|
||||
lastOutput_ = lastBlitOutput_ - dcbState_ + 0.999 * lastOutput_;
|
||||
dcbState_ = lastBlitOutput_;
|
||||
|
||||
phase_ += rate_;
|
||||
if ( phase_ >= TWO_PI ) phase_ -= TWO_PI;
|
||||
|
||||
return lastOutput_ - offset_;
|
||||
|
||||
return lastOutput_;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
- Vibrato Gain = 1
|
||||
- Volume = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
- Register State = 1
|
||||
- Breath Pressure = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
This class implements a simple bowed string
|
||||
non-linear function, as described by Smith (1986).
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
- Vibrato Gain = 1
|
||||
- Volume = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
- Vibrato Gain = 1
|
||||
- Volume = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
This class implements a chorus effect.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
- Vibrato Gain = 1
|
||||
- Breath Pressure = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -46,7 +46,7 @@ Clarinet :: ~Clarinet()
|
||||
void Clarinet :: clear()
|
||||
{
|
||||
delayLine_.clear();
|
||||
filter_.tick((StkFloat) 0.0);
|
||||
filter_.tick( 0.0 );
|
||||
}
|
||||
|
||||
void Clarinet :: setFrequency(StkFloat frequency)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
used in fixed delay-length applications, such
|
||||
as for reverberation.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
minimum delay possible in this implementation is limited to a
|
||||
value of 0.5.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
order Lagrange interpolators can typically
|
||||
improve (minimize) this attenuation characteristic.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
of simultaneous voices) via a #define in the
|
||||
Drummer.h.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
This class implements an echo effect.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
This class provides common functionality for
|
||||
STK effects subclasses.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
\e keyOff messages, ramping to 1.0 on
|
||||
keyOn and to 0.0 on keyOff.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -21,6 +21,7 @@ Envelope :: Envelope(void) : Generator()
|
||||
value_ = 0.0;
|
||||
rate_ = 0.001;
|
||||
state_ = 0;
|
||||
Stk::addSampleRateAlert( this );
|
||||
}
|
||||
|
||||
Envelope :: Envelope ( const Envelope& e )
|
||||
@@ -29,10 +30,12 @@ Envelope :: Envelope ( const Envelope& e )
|
||||
value_ = 0.0;
|
||||
rate_ = 0.001;
|
||||
state_ = 0;
|
||||
Stk::addSampleRateAlert( this );
|
||||
}
|
||||
|
||||
Envelope :: ~Envelope(void)
|
||||
{
|
||||
Stk::removeSampleRateAlert( this );
|
||||
}
|
||||
|
||||
Envelope& Envelope :: operator= ( const Envelope& e )
|
||||
@@ -47,6 +50,12 @@ Envelope& Envelope :: operator= ( const Envelope& e )
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Envelope :: sampleRateChanged( StkFloat newRate, StkFloat oldRate )
|
||||
{
|
||||
if ( !ignoreSampleRateChange_ )
|
||||
rate_ = oldRate * rate_ / newRate;
|
||||
}
|
||||
|
||||
void Envelope :: keyOn(void)
|
||||
{
|
||||
target_ = 1.0;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
type who should worry about this (making
|
||||
money) worry away.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
type who should worry about this (making
|
||||
money) worry away.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
filling a matrix row. The sample rate for
|
||||
MAT-files is assumed to be 44100 Hz.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -469,11 +469,12 @@ bool FileRead :: getMatInfo( const char *fileName )
|
||||
// http://www.mathworks.com/access/helpdesk/help/pdf_doc/matlab/matfile_format.pdf
|
||||
|
||||
// Verify this is a version 5 MAT-file format.
|
||||
char head[4];
|
||||
char head[5];
|
||||
if ( fseek(fd_, 0, SEEK_SET) == -1 ) goto error;
|
||||
if ( fread(&head, 4, 1, fd_) != 1 ) goto error;
|
||||
// If any of the first 4 characters of the header = 0, then this is
|
||||
// a Version 4 MAT-file.
|
||||
head[4] = '\0';
|
||||
if ( strstr(head, "0") ) {
|
||||
errorString_ << "FileRead: " << fileName << " appears to be a Version 4 MAT-file, which is not currently supported.";
|
||||
return false;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
type, the data type will automatically be modified. Compressed
|
||||
data types are not supported.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
See the FileRead class for a description of the supported audio
|
||||
file formats.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -37,6 +37,7 @@ FileWvIn :: FileWvIn( unsigned long chunkThreshold, unsigned long chunkSize )
|
||||
: finished_(true), interpolate_(false), time_(0.0),
|
||||
chunkThreshold_(chunkThreshold), chunkSize_(chunkSize)
|
||||
{
|
||||
Stk::addSampleRateAlert( this );
|
||||
}
|
||||
|
||||
FileWvIn :: FileWvIn( std::string fileName, bool raw, bool doNormalize,
|
||||
@@ -45,11 +46,19 @@ FileWvIn :: FileWvIn( std::string fileName, bool raw, bool doNormalize,
|
||||
chunkThreshold_(chunkThreshold), chunkSize_(chunkSize)
|
||||
{
|
||||
openFile( fileName, raw, doNormalize );
|
||||
Stk::addSampleRateAlert( this );
|
||||
}
|
||||
|
||||
FileWvIn :: ~FileWvIn()
|
||||
{
|
||||
this->closeFile();
|
||||
Stk::removeSampleRateAlert( this );
|
||||
}
|
||||
|
||||
void FileWvIn :: sampleRateChanged( StkFloat newRate, StkFloat oldRate )
|
||||
{
|
||||
if ( !ignoreSampleRateChange_ )
|
||||
this->setRate( oldRate * rate_ / newRate );
|
||||
}
|
||||
|
||||
void FileWvIn :: closeFile( void )
|
||||
@@ -130,6 +139,11 @@ void FileWvIn :: normalize( StkFloat peak )
|
||||
|
||||
void FileWvIn :: setRate( StkFloat rate )
|
||||
{
|
||||
#if defined(_STK_DEBUG_)
|
||||
errorString_ << "FileWvIn::setRate: changing file read rate from " << rate_ << " to " << rate << '.';
|
||||
handleError( StkError::DEBUG_WARNING );
|
||||
#endif
|
||||
|
||||
rate_ = rate;
|
||||
|
||||
// If negative rate and at beginning of sound, move pointer to end
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
Currently, FileWvOut is non-interpolating and the output rate is
|
||||
always Stk::sampleRate().
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
results in one extra multiply per computed sample,
|
||||
but allows easy control of the overall filter gain.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
- Vibrato Gain = 1
|
||||
- Breath Pressure = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -31,7 +31,6 @@ Flute :: Flute(StkFloat lowestFrequency)
|
||||
boreDelay_.setMaximumDelay( length_ );
|
||||
boreDelay_.setDelay( 100.0 );
|
||||
|
||||
length_ >>= 1;
|
||||
jetDelay_.setMaximumDelay( length_ );
|
||||
jetDelay_.setDelay( 49.0 );
|
||||
|
||||
@@ -133,7 +132,7 @@ void Flute :: setEndReflection(StkFloat coefficient)
|
||||
endReflection_ = coefficient;
|
||||
}
|
||||
|
||||
void Flute :: setJetDelay(StkFloat aRatio)
|
||||
void Flute :: setJetDelay( StkFloat aRatio )
|
||||
{
|
||||
// Delay = length - approximate filter delay.
|
||||
StkFloat temp = Stk::sampleRate() / lastFrequency_ - (StkFloat) 2.0;
|
||||
@@ -149,7 +148,6 @@ StkFloat Flute :: computeSample()
|
||||
// Calculate the breath pressure (envelope + noise + vibrato)
|
||||
breathPressure = maxPressure_ * adsr_.tick();
|
||||
breathPressure += breathPressure * ( noiseGain_ * noise_.tick() + vibratoGain_ * vibrato_.tick() );
|
||||
//breathPressure += breathPressure * vibratoGain_ * vibrato_.tick();
|
||||
|
||||
StkFloat temp = filter_.tick( boreDelay_.lastOut() );
|
||||
temp = dcBlock_.tick( temp ); // Block DC on reflection.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
It provides methods for controlling the sweep
|
||||
rate and target frequency.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
implement tables or other types of input to output function
|
||||
mappings.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
This class provides common functionality for
|
||||
STK unit generator sample-source subclasses.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "Granulate.h"
|
||||
#include "FileRead.h"
|
||||
#include <cmath>
|
||||
|
||||
Granulate :: Granulate( void )
|
||||
{
|
||||
@@ -120,7 +121,7 @@ void Granulate :: reset()
|
||||
void Granulate :: setVoices( unsigned int nVoices )
|
||||
{
|
||||
#if defined(_STK_DEBUG_)
|
||||
errorString_ << "Granulate::setGrains: nGrains = " << nGrains << ", existing grains = " << grains_.size() << '.';
|
||||
errorString_ << "Granulate::setVoices: nVoices = " << nVoices << ", existing voices = " << grains_.size() << '.';
|
||||
handleError( StkError::DEBUG_WARNING );
|
||||
#endif
|
||||
|
||||
@@ -133,6 +134,7 @@ void Granulate :: setVoices( unsigned int nVoices )
|
||||
grains_[i].repeats = 0;
|
||||
count = (unsigned int ) ( i * gDuration_ * 0.001 * Stk::sampleRate() / nVoices );
|
||||
grains_[i].counter = count;
|
||||
grains_[i].pointer = gPointer_;
|
||||
grains_[i].state = GRAIN_STOPPED;
|
||||
}
|
||||
|
||||
@@ -186,9 +188,13 @@ void Granulate :: calculateGrain( Granulate::Grain& grain )
|
||||
|
||||
// Calculate offset parameter.
|
||||
seconds = gOffset_ * 0.001;
|
||||
seconds += ( seconds * gRandomFactor_ * noise.tick() );
|
||||
seconds += ( seconds * gRandomFactor_ * std::abs( noise.tick() ) );
|
||||
int offset = (int) ( seconds * Stk::sampleRate() );
|
||||
grain.pointer = gPointer_ + offset;
|
||||
|
||||
// Add some randomization to the pointer start position.
|
||||
seconds = gDuration_ * 0.001 * gRandomFactor_ * noise.tick();
|
||||
offset += (int) ( seconds * Stk::sampleRate() );
|
||||
grain.pointer += offset;
|
||||
while ( grain.pointer >= data_.frames() ) grain.pointer -= data_.frames();
|
||||
if ( grain.pointer < 0 ) grain.pointer = 0;
|
||||
grain.startPointer = grain.pointer;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
type who should worry about this (making
|
||||
money) worry away.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
data type for the incoming stream is signed 16-bit integers,
|
||||
though any of the defined StkFormats are permissible.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
data type is signed 16-bit integers but any of the defined
|
||||
StkFormats are permissible.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
This class provides a common interface for
|
||||
all STK instruments.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
filters, and two decorrelation delay lines in
|
||||
parallel at the output.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
Consult Fletcher and Rossing, Karjalainen,
|
||||
Cook, and others for more information.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ CFLAGS += @warn@ $(INCLUDE)
|
||||
|
||||
REALTIME = @realtime@
|
||||
ifeq ($(REALTIME),yes)
|
||||
OBJECTS += RtMidi.o RtAudio.o RtWvOut.o RtWvIn.o RtDuplex.o InetWvOut.o InetWvIn.o Thread.o Mutex.o Socket.o TcpClient.o TcpServer.o UdpSocket.o
|
||||
OBJECTS += RtMidi.o RtAudio.o RtWvOut.o RtWvIn.o InetWvOut.o InetWvIn.o Thread.o Mutex.o Socket.o TcpClient.o TcpServer.o UdpSocket.o
|
||||
DEFS += @audio_apis@
|
||||
endif
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
- String Detuning = 1
|
||||
- Microphone Position = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -58,7 +58,7 @@ Mandolin :: ~Mandolin()
|
||||
delete soundfile_[i];
|
||||
}
|
||||
|
||||
void Mandolin :: pluck(StkFloat amplitude)
|
||||
void Mandolin :: pluck( StkFloat amplitude )
|
||||
{
|
||||
// This function gets interesting, because pluck
|
||||
// may be longer than string length, so we just
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
This class is primarily for use in STK example programs but it is
|
||||
generic enough to work in many other contexts.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -298,7 +298,7 @@ unsigned long MidiFileIn :: getNextEvent( std::vector<unsigned char> *event, uns
|
||||
// Update track counter and check the tempo map.
|
||||
trackCounters_[track] += ticks;
|
||||
TempoChange tempoEvent = tempoEvents_[ trackTempoIndex_[track] ];
|
||||
if ( trackCounters_[track] >= tempoEvent.count ) {
|
||||
if ( trackCounters_[track] >= tempoEvent.count && trackTempoIndex_[track] < tempoEvents_.size() - 1 ) {
|
||||
trackTempoIndex_[track]++;
|
||||
tickSeconds_[track] = tempoEvent.tickSeconds;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
(non-sweeping BiQuad filters), where N is set
|
||||
during instantiation.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
- Two Fixed = 7
|
||||
- Clump = 8
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
modulations to give a nice, natural human
|
||||
modulation function.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
- Vibrato Gain = 1
|
||||
- Gain = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
systems, the pthread library is used. Under
|
||||
Windows, critical sections are used.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
filters in parallel with corresponding right
|
||||
and left outputs.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
C rand() function. The quality of the rand()
|
||||
function varies from one OS to another.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
the real axis of the z-plane while maintaining
|
||||
a constant peak filter gain.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
along the real axis of the z-plane while
|
||||
maintaining a constant filter gain.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
two series allpass units and two parallel comb
|
||||
filters.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
type who should worry about this (making
|
||||
money) worry away.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
set of 32 static phoneme formant parameters
|
||||
and provide access to those values.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
This class implements a simple pitch shifter
|
||||
using delay lines.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
use possibly subject to patents held by
|
||||
Stanford University, Yamaha, and others.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
Stanford, bearing the names of Karplus and/or
|
||||
Strong.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
#include "Plucked.h"
|
||||
|
||||
Plucked :: Plucked(StkFloat lowestFrequency)
|
||||
Plucked :: Plucked( StkFloat lowestFrequency )
|
||||
{
|
||||
length_ = (unsigned long) (Stk::sampleRate() / lowestFrequency + 1);
|
||||
loopGain_ = 0.999;
|
||||
@@ -39,7 +39,7 @@ void Plucked :: clear()
|
||||
pickFilter_.clear();
|
||||
}
|
||||
|
||||
void Plucked :: setFrequency(StkFloat frequency)
|
||||
void Plucked :: setFrequency( StkFloat frequency )
|
||||
{
|
||||
StkFloat freakency = frequency;
|
||||
if ( frequency <= 0.0 ) {
|
||||
@@ -60,7 +60,7 @@ void Plucked :: setFrequency(StkFloat frequency)
|
||||
if ( loopGain_ >= 1.0 ) loopGain_ = 0.99999;
|
||||
}
|
||||
|
||||
void Plucked :: pluck(StkFloat amplitude)
|
||||
void Plucked :: pluck( StkFloat amplitude )
|
||||
{
|
||||
StkFloat gain = amplitude;
|
||||
if ( gain > 1.0 ) {
|
||||
@@ -81,7 +81,7 @@ void Plucked :: pluck(StkFloat amplitude)
|
||||
delayLine_.tick( 0.6 * delayLine_.lastOut() + pickFilter_.tick( noise_.tick() ) );
|
||||
}
|
||||
|
||||
void Plucked :: noteOn(StkFloat frequency, StkFloat amplitude)
|
||||
void Plucked :: noteOn( StkFloat frequency, StkFloat amplitude )
|
||||
{
|
||||
this->setFrequency( frequency );
|
||||
this->pluck( amplitude );
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
filter with a given coefficient. Another
|
||||
method is provided to create a DC blocking filter.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
Smith (1986), Hirschman, Cook, Scavone, and
|
||||
others for more information.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
- Zero Radii = 1
|
||||
- Envelope Gain = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
type who should worry about this (making
|
||||
money) worry away.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
11744
src/RtAudio.cpp
11744
src/RtAudio.cpp
File diff suppressed because it is too large
Load Diff
245
src/RtDuplex.cpp
245
src/RtDuplex.cpp
@@ -1,245 +0,0 @@
|
||||
/***************************************************/
|
||||
/*! \class RtDuplex
|
||||
\brief STK realtime audio (blocking) input/output class.
|
||||
|
||||
This class provides a simplified interface to
|
||||
RtAudio for realtime audio input/output. It
|
||||
may also be possible to achieve duplex
|
||||
operation using separate RtWvIn and RtWvOut
|
||||
classes, but this class ensures better
|
||||
input/output synchronization.
|
||||
|
||||
Because this class makes use of RtAudio's
|
||||
blocking input/output routines, its
|
||||
performance is less robust on systems where
|
||||
the audio API is callback-based (Macintosh
|
||||
CoreAudio and Windows ASIO).
|
||||
|
||||
RtDuplex 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 and return samples produced by averaging
|
||||
across sample frames, from the tickFrame()
|
||||
methods, which take/return pointers to
|
||||
multi-channel sample frames.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
#include "RtDuplex.h"
|
||||
|
||||
RtDuplex :: RtDuplex(int nChannels, StkFloat sampleRate, int device, int bufferFrames, int nBuffers )
|
||||
{
|
||||
channels_ = nChannels;
|
||||
bufferSize_ = bufferFrames;
|
||||
RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
|
||||
|
||||
audio_ = 0;
|
||||
try {
|
||||
audio_ = new RtAudio();
|
||||
}
|
||||
catch (RtError &error) {
|
||||
handleError( error.getMessageString(), StkError::AUDIO_SYSTEM );
|
||||
}
|
||||
|
||||
// Now open a stream and get the buffer pointer.
|
||||
try {
|
||||
audio_->openStream(device, channels_, device, channels_, format,
|
||||
(int)sampleRate, &bufferSize_, nBuffers);
|
||||
data_ = (StkFloat *) audio_->getStreamBuffer();
|
||||
}
|
||||
catch (RtError &error) {
|
||||
handleError( error.getMessageString(), StkError::AUDIO_SYSTEM );
|
||||
}
|
||||
|
||||
lastOutput_ = (StkFloat *) new StkFloat[channels_];
|
||||
for (unsigned int i=0; i<channels_; i++) lastOutput_[i] = 0.0;
|
||||
counter_ = 0;
|
||||
stopped_ = true;
|
||||
}
|
||||
|
||||
RtDuplex :: ~RtDuplex()
|
||||
{
|
||||
if ( !stopped_ )
|
||||
audio_->stopStream();
|
||||
delete audio_;
|
||||
delete [] lastOutput_;
|
||||
data_ = 0; // RtAudio deletes the buffer itself.
|
||||
}
|
||||
|
||||
void RtDuplex :: start()
|
||||
{
|
||||
if ( stopped_ ) {
|
||||
audio_->startStream();
|
||||
stopped_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void RtDuplex :: stop()
|
||||
{
|
||||
if ( !stopped_ ) {
|
||||
audio_->stopStream();
|
||||
stopped_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
StkFloat RtDuplex :: lastOut(void) const
|
||||
{
|
||||
if ( channels_ == 1 )
|
||||
return *lastOutput_;
|
||||
|
||||
StkFloat output = 0.0;
|
||||
for (unsigned int i=0; i<channels_; i++ ) {
|
||||
output += lastOutput_[i];
|
||||
}
|
||||
return output / channels_;
|
||||
}
|
||||
|
||||
StkFloat RtDuplex :: tick(const StkFloat sample)
|
||||
{
|
||||
if ( stopped_ )
|
||||
start();
|
||||
|
||||
if (counter_ == 0) {
|
||||
try {
|
||||
audio_->tickStream();
|
||||
}
|
||||
catch (RtError &error) {
|
||||
handleError( error.getMessageString(), StkError::AUDIO_SYSTEM );
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long temp = counter_ * channels_;
|
||||
for (unsigned int i=0; i<channels_; i++) {
|
||||
lastOutput_[i] = data_[temp];
|
||||
data_[temp++] = sample;
|
||||
}
|
||||
|
||||
counter_++;
|
||||
if (counter_ >= (long) bufferSize_)
|
||||
counter_ = 0;
|
||||
|
||||
return lastOut();
|
||||
}
|
||||
|
||||
StkFloat *RtDuplex :: tick(StkFloat *vector, unsigned int vectorSize)
|
||||
{
|
||||
for ( unsigned int i=0; i<vectorSize; i++ )
|
||||
vector[i] = tick(vector[i]);
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
StkFrames& RtDuplex :: tick( StkFrames& frames, unsigned int channel )
|
||||
{
|
||||
if ( channel == 0 || frames.channels() < channel ) {
|
||||
errorString_ << "RtDuplex::tick(): channel argument (" << channel << ") is zero or > channels in StkFrames argument!";
|
||||
handleError( StkError::FUNCTION_ARGUMENT );
|
||||
}
|
||||
|
||||
if ( frames.channels() == 1 ) {
|
||||
for ( unsigned int i=0; i<frames.frames(); i++ )
|
||||
frames[i] = tick( frames[i] );
|
||||
}
|
||||
else if ( frames.interleaved() ) {
|
||||
unsigned int hop = frames.channels();
|
||||
unsigned int index = channel - 1;
|
||||
for ( unsigned int i=0; i<frames.frames(); i++ ) {
|
||||
frames[index] = tick( frames[index] );
|
||||
index += hop;
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsigned int iStart = (channel - 1) * frames.frames();
|
||||
for ( unsigned int i=0; i<frames.frames(); i++ )
|
||||
frames[iStart + i] = tick( frames[iStart + i] );
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
const StkFloat *RtDuplex :: lastFrame() const
|
||||
{
|
||||
return lastOutput_;
|
||||
}
|
||||
|
||||
StkFloat *RtDuplex :: tickFrame(StkFloat *frameVector, unsigned int frames)
|
||||
{
|
||||
if ( stopped_ )
|
||||
start();
|
||||
|
||||
unsigned int i;
|
||||
unsigned long temp;
|
||||
for (unsigned int j=0; j<frames; j++ ) {
|
||||
if (counter_ == 0) {
|
||||
try {
|
||||
audio_->tickStream();
|
||||
}
|
||||
catch (RtError &error) {
|
||||
handleError( error.getMessageString(), StkError::AUDIO_SYSTEM );
|
||||
}
|
||||
}
|
||||
|
||||
temp = counter_ * channels_;
|
||||
for (i=0; i<channels_; i++) {
|
||||
lastOutput_[i] = data_[temp];
|
||||
data_[temp++] = frameVector[j*channels_+i];
|
||||
frameVector[j*channels_+i] = lastOutput_[i];
|
||||
}
|
||||
|
||||
counter_++;
|
||||
if (counter_ >= (long) bufferSize_)
|
||||
counter_ = 0;
|
||||
}
|
||||
|
||||
return frameVector;
|
||||
}
|
||||
|
||||
StkFrames& RtDuplex :: tickFrame( StkFrames& frames )
|
||||
{
|
||||
if ( channels_ != frames.channels() ) {
|
||||
errorString_ << "RtDuplex::tickFrame(): incompatible channel value in StkFrames argument!";
|
||||
handleError( StkError::FUNCTION_ARGUMENT );
|
||||
}
|
||||
|
||||
if ( stopped_ )
|
||||
start();
|
||||
|
||||
unsigned long j, index = 0;
|
||||
unsigned long k, hop = frames.frames();
|
||||
for (unsigned int i=0; i<frames.frames(); i++ ) {
|
||||
if (counter_ == 0) {
|
||||
try {
|
||||
audio_->tickStream();
|
||||
}
|
||||
catch (RtError &error) {
|
||||
handleError( error.getMessageString(), StkError::AUDIO_SYSTEM );
|
||||
}
|
||||
}
|
||||
|
||||
if ( channels_ > 1 && frames.interleaved() == false ) {
|
||||
k = i;
|
||||
for (j=0; j<channels_; j++) {
|
||||
lastOutput_[j] = data_[index];
|
||||
data_[index++] = frames[k];
|
||||
frames[k] = lastOutput_[j];
|
||||
k += hop;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (j=0; j<channels_; j++) {
|
||||
lastOutput_[j] = data_[index];
|
||||
data_[index] = frames[index];
|
||||
frames[index++] = lastOutput_[j];
|
||||
}
|
||||
}
|
||||
|
||||
counter_++;
|
||||
if (counter_ >= (long) bufferSize_)
|
||||
counter_ = 0;
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
@@ -35,7 +35,7 @@
|
||||
*/
|
||||
/**********************************************************************/
|
||||
|
||||
// RtMidi: Version 1.0.4, 14 October 2005
|
||||
// RtMidi: Version 1.0.5, in development
|
||||
|
||||
#include "RtMidi.h"
|
||||
#include <sstream>
|
||||
@@ -383,13 +383,15 @@ void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
connected_ = true;
|
||||
}
|
||||
|
||||
void RtMidiIn :: openVirtualPort()
|
||||
void RtMidiIn :: openVirtualPort( const std::string portName )
|
||||
{
|
||||
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
||||
|
||||
// Create a virtual MIDI input destination.
|
||||
MIDIEndpointRef endpoint;
|
||||
OSStatus result = MIDIDestinationCreate( data->client, CFSTR("RtMidi Input"), midiInputCallback, (void *)&inputData_, &endpoint );
|
||||
OSStatus result = MIDIDestinationCreate( data->client,
|
||||
CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
|
||||
midiInputCallback, (void *)&inputData_, &endpoint );
|
||||
if ( result != noErr ) {
|
||||
errorString_ = "RtMidiIn::openVirtualPort: error creating virtual OS-X MIDI destination.";
|
||||
error( RtError::DRIVER_ERROR );
|
||||
@@ -548,7 +550,7 @@ void RtMidiOut :: closePort( void )
|
||||
}
|
||||
}
|
||||
|
||||
void RtMidiOut :: openVirtualPort()
|
||||
void RtMidiOut :: openVirtualPort( std::string portName )
|
||||
{
|
||||
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
||||
|
||||
@@ -560,7 +562,9 @@ void RtMidiOut :: openVirtualPort()
|
||||
|
||||
// Create a virtual MIDI output source.
|
||||
MIDIEndpointRef endpoint;
|
||||
OSStatus result = MIDISourceCreate( data->client, CFSTR("RtMidi Output"), &endpoint );
|
||||
OSStatus result = MIDISourceCreate( data->client,
|
||||
CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
|
||||
&endpoint );
|
||||
if ( result != noErr ) {
|
||||
errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source.";
|
||||
error( RtError::DRIVER_ERROR );
|
||||
@@ -957,7 +961,7 @@ void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
connected_ = true;
|
||||
}
|
||||
|
||||
void RtMidiIn :: openVirtualPort()
|
||||
void RtMidiIn :: openVirtualPort( std::string portName )
|
||||
{
|
||||
AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
||||
if ( data->vport < 0 ) {
|
||||
@@ -973,7 +977,7 @@ void RtMidiIn :: openVirtualPort()
|
||||
snd_seq_port_info_set_timestamping(pinfo, 1);
|
||||
snd_seq_port_info_set_timestamp_real(pinfo, 1);
|
||||
snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
|
||||
snd_seq_port_info_set_name(pinfo, "RtMidi Input");
|
||||
snd_seq_port_info_set_name(pinfo, portName.c_str());
|
||||
data->vport = snd_seq_create_port(data->seq, pinfo);
|
||||
|
||||
if ( data->vport < 0 ) {
|
||||
@@ -1195,11 +1199,11 @@ void RtMidiOut :: closePort( void )
|
||||
}
|
||||
}
|
||||
|
||||
void RtMidiOut :: openVirtualPort()
|
||||
void RtMidiOut :: openVirtualPort( std::string portName )
|
||||
{
|
||||
AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
||||
if ( data->vport < 0 ) {
|
||||
data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output",
|
||||
data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
|
||||
SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
|
||||
SND_SEQ_PORT_TYPE_MIDI_GENERIC );
|
||||
|
||||
@@ -1484,7 +1488,7 @@ void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
connected_ = true;
|
||||
}
|
||||
|
||||
void RtMidiIn :: openVirtualPort()
|
||||
void RtMidiIn :: openVirtualPort( std::string portName )
|
||||
{
|
||||
// This function cannot be implemented for the Irix MIDI API.
|
||||
errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Irix MIDI API!";
|
||||
@@ -1617,7 +1621,7 @@ void RtMidiOut :: closePort( void )
|
||||
}
|
||||
}
|
||||
|
||||
void RtMidiOut :: openVirtualPort()
|
||||
void RtMidiOut :: openVirtualPort( std::string portName )
|
||||
{
|
||||
// This function cannot be implemented for the Irix MIDI API.
|
||||
errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Irix MIDI API!";
|
||||
@@ -1676,6 +1680,8 @@ void RtMidiOut :: sendMessage( std::vector<unsigned char> *message )
|
||||
// API information deciphered from:
|
||||
// - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp
|
||||
|
||||
// Thanks to Jean-Baptiste Berruchon for the sysex code.
|
||||
|
||||
#if defined(__WINDOWS_MM__)
|
||||
|
||||
// The Windows MM API is based on the use of a callback function for
|
||||
@@ -1693,8 +1699,11 @@ struct WinMidiData {
|
||||
HMIDIOUT outHandle; // Handle to Midi Output Device
|
||||
DWORD lastTime;
|
||||
RtMidiIn::MidiMessage message;
|
||||
LPMIDIHDR sysexBuffer;
|
||||
};
|
||||
|
||||
#define RT_SYSEX_BUFFER_SIZE 1024
|
||||
|
||||
//*********************************************************************//
|
||||
// API: Windows MM
|
||||
// Class Definitions: RtMidiIn
|
||||
@@ -1748,11 +1757,21 @@ static void CALLBACK midiInputCallback( HMIDIOUT hmin,
|
||||
unsigned char *ptr = (unsigned char *) &midiMessage;
|
||||
for ( int i=0; i<nBytes; i++ ) apiData->message.bytes.push_back( *ptr++ );
|
||||
}
|
||||
else { // Sysex message
|
||||
else if ( !(data->ignoreFlags & 0x01) ) {
|
||||
// Sysex message and we're not ignoring it
|
||||
MIDIHDR *sysex = ( MIDIHDR *) midiMessage;
|
||||
for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ )
|
||||
apiData->message.bytes.push_back( sysex->lpData[i] );
|
||||
if ( apiData->message.bytes.back() != 0xF7 ) return;
|
||||
|
||||
// When the callback has to be unaffected (application closes),
|
||||
// it seems WinMM calls it with an empty sysex to de-queue the buffer
|
||||
// If the buffer is requeued afer that message, the PC suddenly reboots
|
||||
// after one or two minutes (JB).
|
||||
if ( apiData->sysexBuffer->dwBytesRecorded > 0 ) {
|
||||
MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer, sizeof(MIDIHDR) );
|
||||
if ( result != MMSYSERR_NOERROR )
|
||||
std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ( data->usingCallback ) {
|
||||
@@ -1822,6 +1841,27 @@ void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
error( RtError::DRIVER_ERROR );
|
||||
}
|
||||
|
||||
// Allocate and init the sysex buffer.
|
||||
data->sysexBuffer = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
|
||||
data->sysexBuffer->lpData = new char[1024];
|
||||
data->sysexBuffer->dwBufferLength = 1024;
|
||||
data->sysexBuffer->dwFlags = 0;
|
||||
|
||||
result = midiInPrepareHeader( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) );
|
||||
if ( result != MMSYSERR_NOERROR ) {
|
||||
midiInClose( data->inHandle );
|
||||
errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port (PrepareHeader).";
|
||||
error( RtError::DRIVER_ERROR );
|
||||
}
|
||||
|
||||
// Register the buffer.
|
||||
result = midiInAddBuffer( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) );
|
||||
if ( result != MMSYSERR_NOERROR ) {
|
||||
midiInClose( data->inHandle );
|
||||
errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port (AddBuffer).";
|
||||
error( RtError::DRIVER_ERROR );
|
||||
}
|
||||
|
||||
result = midiInStart( data->inHandle );
|
||||
if ( result != MMSYSERR_NOERROR ) {
|
||||
midiInClose( data->inHandle );
|
||||
@@ -1832,7 +1872,7 @@ void RtMidiIn :: openPort( unsigned int portNumber )
|
||||
connected_ = true;
|
||||
}
|
||||
|
||||
void RtMidiIn :: openVirtualPort()
|
||||
void RtMidiIn :: openVirtualPort( std::string portName )
|
||||
{
|
||||
// This function cannot be implemented for the Windows MM MIDI API.
|
||||
errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
|
||||
@@ -1845,6 +1885,16 @@ void RtMidiIn :: closePort( void )
|
||||
WinMidiData *data = static_cast<WinMidiData *> (apiData_);
|
||||
midiInReset( data->inHandle );
|
||||
midiInStop( data->inHandle );
|
||||
|
||||
int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer, sizeof(MIDIHDR));
|
||||
delete [] data->sysexBuffer->lpData;
|
||||
delete [] data->sysexBuffer;
|
||||
if ( result != MMSYSERR_NOERROR ) {
|
||||
midiInClose( data->inHandle );
|
||||
errorString_ = "RtMidiIn::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader).";
|
||||
error( RtError::DRIVER_ERROR );
|
||||
}
|
||||
|
||||
midiInClose( data->inHandle );
|
||||
connected_ = false;
|
||||
}
|
||||
@@ -1878,7 +1928,14 @@ std::string RtMidiIn :: getPortName( unsigned int portNumber )
|
||||
MIDIINCAPS deviceCaps;
|
||||
MMRESULT result = midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS));
|
||||
|
||||
std::string stringName = std::string( deviceCaps.szPname );
|
||||
// For some reason, we need to copy character by character with
|
||||
// UNICODE (thanks to Eduardo Coutinho!).
|
||||
//std::string stringName = std::string( deviceCaps.szPname );
|
||||
char nameString[MAXPNAMELEN];
|
||||
for( int i=0; i<MAXPNAMELEN; i++ )
|
||||
nameString[i] = (char)( deviceCaps.szPname[i] );
|
||||
|
||||
std::string stringName( nameString );
|
||||
return stringName;
|
||||
}
|
||||
|
||||
@@ -1905,7 +1962,14 @@ std::string RtMidiOut :: getPortName( unsigned int portNumber )
|
||||
MIDIOUTCAPS deviceCaps;
|
||||
MMRESULT result = midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS));
|
||||
|
||||
std::string stringName = std::string( deviceCaps.szPname );
|
||||
// For some reason, we need to copy character by character with
|
||||
// UNICODE (thanks to Eduardo Coutinho!).
|
||||
//std::string stringName = std::string( deviceCaps.szPname );
|
||||
char nameString[MAXPNAMELEN];
|
||||
for( int i=0; i<MAXPNAMELEN; i++ )
|
||||
nameString[i] = (char)( deviceCaps.szPname[i] );
|
||||
|
||||
std::string stringName( nameString );
|
||||
return stringName;
|
||||
}
|
||||
|
||||
@@ -1969,7 +2033,7 @@ void RtMidiOut :: closePort( void )
|
||||
}
|
||||
}
|
||||
|
||||
void RtMidiOut :: openVirtualPort()
|
||||
void RtMidiOut :: openVirtualPort( std::string portName )
|
||||
{
|
||||
// This function cannot be implemented for the Windows MM MIDI API.
|
||||
errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
|
||||
|
||||
125
src/RtWvIn.cpp
125
src/RtWvIn.cpp
@@ -3,62 +3,98 @@
|
||||
\brief STK realtime audio (blocking) input class.
|
||||
|
||||
This class provides a simplified interface to RtAudio for realtime
|
||||
audio input. It is a protected subclass of WvIn. 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 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 interleaved format. It is
|
||||
important to distinguish the tick() methods, which return samples
|
||||
produced by averaging across sample frames, from the tickFrame()
|
||||
methods, which return references or pointers to multi-channel
|
||||
sample frames.
|
||||
RtWvIn supports multi-channel data in both interleaved and
|
||||
non-interleaved formats. It is important to distinguish the
|
||||
tick() methods, which return samples produced by averaging across
|
||||
sample frames, from the tickFrame() methods, which return
|
||||
references or pointers to multi-channel sample frames.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
#include "RtWvIn.h"
|
||||
|
||||
RtWvIn :: RtWvIn( unsigned int nChannels, StkFloat sampleRate, int device, int bufferFrames, int nBuffers )
|
||||
: stopped_( true ), bufferIndex_( 0 )
|
||||
// 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 )
|
||||
{
|
||||
int size = bufferFrames;
|
||||
( (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<counter; i++ )
|
||||
data_[iStart++] = *samples++;
|
||||
|
||||
nSamples -= counter;
|
||||
}
|
||||
|
||||
framesFilled_ += nFrames;
|
||||
if ( framesFilled_ > data_.frames() ) {
|
||||
framesFilled_ = data_.frames();
|
||||
errorString_ << "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;
|
||||
parameters.deviceId = device;
|
||||
parameters.nChannels = nChannels;
|
||||
unsigned int size = bufferFrames;
|
||||
RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
|
||||
|
||||
adc_ = 0;
|
||||
try {
|
||||
adc_ = new RtAudio();
|
||||
adc_.openStream( NULL, ¶meters, format, (unsigned int)Stk::sampleRate(), &size, &read, (void *)this );
|
||||
}
|
||||
catch (RtError &error) {
|
||||
handleError( error.getMessageString(), StkError::AUDIO_SYSTEM );
|
||||
catch ( RtError &error ) {
|
||||
handleError( error.what(), StkError::AUDIO_SYSTEM );
|
||||
}
|
||||
|
||||
// Allocate the adc and get the buffer pointer.
|
||||
try {
|
||||
adc_->openStream( 0, 0, device, (int)nChannels, format,
|
||||
(int)sampleRate, &size, nBuffers );
|
||||
buffer_ = (StkFloat *) adc_->getStreamBuffer();
|
||||
}
|
||||
catch (RtError &error) {
|
||||
handleError( error.getMessageString(), StkError::AUDIO_SYSTEM );
|
||||
}
|
||||
|
||||
bufferFrames_ = size;
|
||||
data_.resize( size * nBuffers, nChannels );
|
||||
lastOutputs_.resize( 1, nChannels );
|
||||
}
|
||||
|
||||
RtWvIn :: ~RtWvIn()
|
||||
{
|
||||
if ( !stopped_ ) adc_->stopStream();
|
||||
adc_->closeStream();
|
||||
delete adc_;
|
||||
if ( !stopped_ ) adc_.stopStream();
|
||||
adc_.closeStream();
|
||||
}
|
||||
|
||||
void RtWvIn :: start()
|
||||
{
|
||||
if ( stopped_ ) {
|
||||
adc_->startStream();
|
||||
adc_.startStream();
|
||||
stopped_ = false;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +102,7 @@ void RtWvIn :: start()
|
||||
void RtWvIn :: stop()
|
||||
{
|
||||
if ( !stopped_ ) {
|
||||
adc_->stopStream();
|
||||
adc_.stopStream();
|
||||
stopped_ = true;
|
||||
}
|
||||
}
|
||||
@@ -75,20 +111,15 @@ void RtWvIn :: computeFrame( void )
|
||||
{
|
||||
if ( stopped_ ) this->start();
|
||||
|
||||
if ( bufferIndex_ == 0) {
|
||||
try {
|
||||
adc_->tickStream();
|
||||
}
|
||||
catch (RtError &error) {
|
||||
handleError( error.getMessageString(), StkError::AUDIO_SYSTEM );
|
||||
}
|
||||
}
|
||||
// Block until at least one frame is available.
|
||||
while ( framesFilled_ == 0 )
|
||||
Stk::sleep( 1 );
|
||||
|
||||
long iBuffer = bufferIndex_ * lastOutputs_.size();
|
||||
for (unsigned int i=0; i<lastOutputs_.size(); i++)
|
||||
lastOutputs_[i] = buffer_[iBuffer++];
|
||||
for ( unsigned int i=0; i<lastOutputs_.size(); i++ )
|
||||
lastOutputs_[i] = data_( readIndex_, i );
|
||||
|
||||
bufferIndex_++;
|
||||
if ( bufferIndex_ >= bufferFrames_ )
|
||||
bufferIndex_ = 0;
|
||||
framesFilled_--;
|
||||
readIndex_++;
|
||||
if ( readIndex_ >= data_.frames() )
|
||||
readIndex_ = 0;
|
||||
}
|
||||
|
||||
208
src/RtWvOut.cpp
208
src/RtWvOut.cpp
@@ -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( ¶meters, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
This instrument provides an ADSR envelope, a one-pole filter, and
|
||||
structures for an arbitrary number of attack and loop waves.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
- Vibrato Gain = 1
|
||||
- Breath Pressure = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
- Envelope Rate = 11
|
||||
- Gain = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
The "table" length, set in SineWave.h, is 2048 samples by default.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -27,10 +27,19 @@ SineWave :: SineWave( void )
|
||||
for ( unsigned long i=0; i<=TABLE_SIZE; i++ )
|
||||
table_[i] = sin( TWO_PI * i * temp );
|
||||
}
|
||||
|
||||
Stk::addSampleRateAlert( this );
|
||||
}
|
||||
|
||||
SineWave :: ~SineWave()
|
||||
{
|
||||
Stk::removeSampleRateAlert( this );
|
||||
}
|
||||
|
||||
void SineWave :: sampleRateChanged( StkFloat newRate, StkFloat oldRate )
|
||||
{
|
||||
if ( !ignoreSampleRateChange_ )
|
||||
this->setRate( oldRate * rate_ / newRate );
|
||||
}
|
||||
|
||||
void SineWave :: reset(void)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
from pitch shifting. It will be used as an
|
||||
excitation source for other instruments.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
Stanford, bearing the names of Karplus and/or
|
||||
Strong.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
See also SKINI.txt.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
This class provides common functionality for TCP and UDP internet
|
||||
socket server and client subclasses.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
- String Sustain = 11
|
||||
- String Stretch = 1
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
72
src/Stk.cpp
72
src/Stk.cpp
@@ -8,7 +8,31 @@
|
||||
provides error handling and byte-swapping
|
||||
functions.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
asked to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. This is,
|
||||
however, not a binding provision of this license.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -22,17 +46,55 @@ const Stk::StkFormat Stk :: STK_SINT24 = 0x4;
|
||||
const Stk::StkFormat Stk :: STK_SINT32 = 0x8;
|
||||
const Stk::StkFormat Stk :: STK_FLOAT32 = 0x10;
|
||||
const Stk::StkFormat Stk :: STK_FLOAT64 = 0x20;
|
||||
bool Stk :: showWarnings_ = false;
|
||||
bool Stk :: showWarnings_ = true;
|
||||
bool Stk :: printErrors_ = true;
|
||||
std::vector<Stk *> Stk :: alertList_;
|
||||
|
||||
Stk :: Stk(void)
|
||||
Stk :: Stk( void )
|
||||
: ignoreSampleRateChange_(false)
|
||||
{
|
||||
}
|
||||
|
||||
Stk :: ~Stk(void)
|
||||
Stk :: ~Stk( void )
|
||||
{
|
||||
}
|
||||
|
||||
void Stk :: setSampleRate( StkFloat rate )
|
||||
{
|
||||
if ( rate > 0.0 && rate != srate_ ) {
|
||||
StkFloat oldRate = srate_;
|
||||
srate_ = rate;
|
||||
|
||||
for ( unsigned int i=0; i<alertList_.size(); i++ )
|
||||
alertList_[i]->sampleRateChanged( srate_, oldRate );
|
||||
}
|
||||
}
|
||||
|
||||
void Stk :: sampleRateChanged( StkFloat newRate, StkFloat oldRate )
|
||||
{
|
||||
// This function should be reimplemented in classes that need to
|
||||
// make internal variable adjustments in response to a global sample
|
||||
// rate change.
|
||||
}
|
||||
|
||||
void Stk :: addSampleRateAlert( Stk *ptr )
|
||||
{
|
||||
for ( unsigned int i=0; i<alertList_.size(); i++ )
|
||||
if ( alertList_[i] == ptr ) return;
|
||||
|
||||
alertList_.push_back( ptr );
|
||||
}
|
||||
|
||||
void Stk :: removeSampleRateAlert( Stk *ptr )
|
||||
{
|
||||
for ( unsigned int i=0; i<alertList_.size(); i++ ) {
|
||||
if ( alertList_[i] == ptr ) {
|
||||
alertList_.erase( alertList_.begin() + i );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stk :: setRawwavePath( std::string path )
|
||||
{
|
||||
if ( !path.empty() )
|
||||
@@ -304,7 +366,7 @@ StkFloat StkFrames :: interpolate( StkFloat frame, unsigned int channel ) const
|
||||
else {
|
||||
iIndex += channel * nFrames_;
|
||||
output = data_[ iIndex ];
|
||||
output += ( alpha * ( data_[ iIndex++ ] - output ) );
|
||||
output += ( alpha * ( data_[ ++iIndex ] - output ) );
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
using the C rand() function. The quality of the
|
||||
rand() function varies from one OS to another.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
less than or equal to zero indicate a closed
|
||||
or lost connection or the occurence of an error.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
less than or equal to zero indicate a closed
|
||||
or lost connection or the occurence of an error.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
systems, the pthread library is used. Under Windows,
|
||||
the Windows thread library is used.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
type who should worry about this (making
|
||||
money) worry away.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
frequency response while maintaining a nearly
|
||||
constant filter gain.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -21,10 +21,20 @@ TwoPole :: TwoPole() : Filter()
|
||||
std::vector<StkFloat> a(3, 0.0);
|
||||
a[0] = 1.0;
|
||||
Filter::setCoefficients( b, a );
|
||||
Stk::addSampleRateAlert( this );
|
||||
}
|
||||
|
||||
TwoPole :: ~TwoPole()
|
||||
{
|
||||
Stk::removeSampleRateAlert( this );
|
||||
}
|
||||
|
||||
void TwoPole :: sampleRateChanged( StkFloat newRate, StkFloat oldRate )
|
||||
{
|
||||
if ( !ignoreSampleRateChange_ ) {
|
||||
errorString_ << "TwoPole::sampleRateChanged: you may need to recompute filter coefficients!";
|
||||
handleError( StkError::WARNING );
|
||||
}
|
||||
}
|
||||
|
||||
void TwoPole :: clear(void)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
frequency response while maintaining a
|
||||
constant filter gain.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -21,10 +21,20 @@ TwoZero :: TwoZero() : Filter()
|
||||
b[0] = 1.0;
|
||||
std::vector<StkFloat> a(1, 1.0);
|
||||
Filter::setCoefficients( b, a );
|
||||
Stk::addSampleRateAlert( this );
|
||||
}
|
||||
|
||||
TwoZero :: ~TwoZero()
|
||||
{
|
||||
Stk::removeSampleRateAlert( this );
|
||||
}
|
||||
|
||||
void TwoZero :: sampleRateChanged( StkFloat newRate, StkFloat oldRate )
|
||||
{
|
||||
if ( !ignoreSampleRateChange_ ) {
|
||||
errorString_ << "TwoZero::sampleRateChanged: you may need to recompute filter coefficients!";
|
||||
handleError( StkError::WARNING );
|
||||
}
|
||||
}
|
||||
|
||||
void TwoZero :: clear(void)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
read/write methods. Values less than or equal to zero indicate
|
||||
the occurence of an error.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
- Vibrato Gain = 1
|
||||
- Loudness (Spectral Tilt) = 128
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
an ensemble. Alternately, control changes can
|
||||
be sent to all voices on a given channel.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
which return references or pointers to multi-channel sample
|
||||
frames.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
WaveLoop :: WaveLoop( unsigned long chunkThreshold, unsigned long chunkSize )
|
||||
: FileWvIn( chunkThreshold, chunkSize ), phaseOffset_(0.0)
|
||||
{
|
||||
Stk::addSampleRateAlert( this );
|
||||
}
|
||||
|
||||
WaveLoop :: WaveLoop( std::string fileName, bool raw, bool doNormalize,
|
||||
@@ -29,10 +30,18 @@ WaveLoop :: WaveLoop( std::string fileName, bool raw, bool doNormalize,
|
||||
: FileWvIn( chunkThreshold, chunkSize ), phaseOffset_(0.0)
|
||||
{
|
||||
this->openFile( fileName, raw, doNormalize );
|
||||
Stk::addSampleRateAlert( this );
|
||||
}
|
||||
|
||||
WaveLoop :: ~WaveLoop()
|
||||
{
|
||||
Stk::removeSampleRateAlert( this );
|
||||
}
|
||||
|
||||
void WaveLoop :: sampleRateChanged( StkFloat newRate, StkFloat oldRate )
|
||||
{
|
||||
if ( !ignoreSampleRateChange_ )
|
||||
this->setRate( oldRate * rate_ / newRate );
|
||||
}
|
||||
|
||||
void WaveLoop :: openFile( std::string fileName, bool raw, bool doNormalize )
|
||||
|
||||
@@ -139,7 +139,7 @@ StkFloat Whistle :: computeSample()
|
||||
frameCount += 1;
|
||||
if ( frameCount >= (1470 / subSample_) ) {
|
||||
frameCount = 0;
|
||||
printf("%f %f %f\n",tempVectorP->getX(),tempVectorP->getY(),envOut);
|
||||
printf("%f %f %f\n",tempVectorP_->getX(),tempVectorP_->getY(),envOut);
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
type who should worry about this (making
|
||||
money) worry away.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
Both interleaved and non-interleaved data is supported via the use
|
||||
of StkFrames objects.
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
Currently, WvOut is non-interpolating and the output rate is
|
||||
always Stk::sampleRate().
|
||||
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2005.
|
||||
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
|
||||
115
src/asio/asio.h
115
src/asio/asio.h
@@ -3,9 +3,12 @@
|
||||
|
||||
/*
|
||||
Steinberg Audio Stream I/O API
|
||||
(c) 1997 - 1999, Steinberg Soft- und Hardware GmbH
|
||||
(c) 1997 - 2005, Steinberg Media Technologies GmbH
|
||||
|
||||
ASIO Interface Specification v 2.1
|
||||
|
||||
2005 - Added support for DSD sample data (in cooperation with Sony)
|
||||
|
||||
ASIO Interface Specification v 2.0
|
||||
|
||||
basic concept is an i/o synchronous double-buffer scheme:
|
||||
|
||||
@@ -131,7 +134,7 @@ enum {
|
||||
|
||||
// these are used for 32 bit data buffer, with different alignment of the data inside
|
||||
// 32 bit PCI bus systems can be more easily used with these
|
||||
ASIOSTInt32MSB16 = 8, // 32 bit data with 18 bit alignment
|
||||
ASIOSTInt32MSB16 = 8, // 32 bit data with 16 bit alignment
|
||||
ASIOSTInt32MSB18 = 9, // 32 bit data with 18 bit alignment
|
||||
ASIOSTInt32MSB20 = 10, // 32 bit data with 20 bit alignment
|
||||
ASIOSTInt32MSB24 = 11, // 32 bit data with 24 bit alignment
|
||||
@@ -147,9 +150,56 @@ enum {
|
||||
ASIOSTInt32LSB16 = 24, // 32 bit data with 18 bit alignment
|
||||
ASIOSTInt32LSB18 = 25, // 32 bit data with 18 bit alignment
|
||||
ASIOSTInt32LSB20 = 26, // 32 bit data with 20 bit alignment
|
||||
ASIOSTInt32LSB24 = 27 // 32 bit data with 24 bit alignment
|
||||
ASIOSTInt32LSB24 = 27, // 32 bit data with 24 bit alignment
|
||||
|
||||
// ASIO DSD format.
|
||||
ASIOSTDSDInt8LSB1 = 32, // DSD 1 bit data, 8 samples per byte. First sample in Least significant bit.
|
||||
ASIOSTDSDInt8MSB1 = 33, // DSD 1 bit data, 8 samples per byte. First sample in Most significant bit.
|
||||
ASIOSTDSDInt8NER8 = 40, // DSD 8 bit data, 1 sample per byte. No Endianness required.
|
||||
|
||||
ASIOSTLastEntry
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
// DSD operation and buffer layout
|
||||
// Definition by Steinberg/Sony Oxford.
|
||||
//
|
||||
// We have tried to treat DSD as PCM and so keep a consistant structure across
|
||||
// the ASIO interface.
|
||||
//
|
||||
// DSD's sample rate is normally referenced as a multiple of 44.1Khz, so
|
||||
// the standard sample rate is refered to as 64Fs (or 2.8224Mhz). We looked
|
||||
// at making a special case for DSD and adding a field to the ASIOFuture that
|
||||
// would allow the user to select the Over Sampleing Rate (OSR) as a seperate
|
||||
// entity but decided in the end just to treat it as a simple value of
|
||||
// 2.8224Mhz and use the standard interface to set it.
|
||||
//
|
||||
// The second problem was the "word" size, in PCM the word size is always a
|
||||
// greater than or equal to 8 bits (a byte). This makes life easy as we can
|
||||
// then pack the samples into the "natural" size for the machine.
|
||||
// In DSD the "word" size is 1 bit. This is not a major problem and can easily
|
||||
// be dealt with if we ensure that we always deal with a multiple of 8 samples.
|
||||
//
|
||||
// DSD brings with it another twist to the Endianness religion. How are the
|
||||
// samples packed into the byte. It would be nice to just say the most significant
|
||||
// bit is always the first sample, however there would then be a performance hit
|
||||
// on little endian machines. Looking at how some of the processing goes...
|
||||
// Little endian machines like the first sample to be in the Least Significant Bit,
|
||||
// this is because when you write it to memory the data is in the correct format
|
||||
// to be shifted in and out of the words.
|
||||
// Big endian machine prefer the first sample to be in the Most Significant Bit,
|
||||
// again for the same reasion.
|
||||
//
|
||||
// And just when things were looking really muddy there is a proposed extension to
|
||||
// DSD that uses 8 bit word sizes. It does not care what endianness you use.
|
||||
//
|
||||
// Switching the driver between DSD and PCM mode
|
||||
// ASIOFuture allows for extending the ASIO API quite transparently.
|
||||
// See kAsioSetIoFormat, kAsioGetIoFormat, kAsioCanDoIoFormat
|
||||
//
|
||||
//-----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Error codes
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@@ -403,9 +453,14 @@ enum
|
||||
kAsioSupportsTimeInfo, // if host returns true here, it will expect the
|
||||
// callback bufferSwitchTimeInfo to be called instead
|
||||
// of bufferSwitch
|
||||
kAsioSupportsTimeCode, // supports time code reading/writing
|
||||
|
||||
kAsioSupportsInputMonitor, // supports input monitoring
|
||||
kAsioSupportsTimeCode, //
|
||||
kAsioMMCCommand, // unused - value: number of commands, message points to mmc commands
|
||||
kAsioSupportsInputMonitor, // kAsioSupportsXXX return 1 if host supports this
|
||||
kAsioSupportsInputGain, // unused and undefined
|
||||
kAsioSupportsInputMeter, // unused and undefined
|
||||
kAsioSupportsOutputGain, // unused and undefined
|
||||
kAsioSupportsOutputMeter, // unused and undefined
|
||||
kAsioOverload, // driver detected an overload
|
||||
|
||||
kAsioNumMessageSelectors
|
||||
};
|
||||
@@ -860,7 +915,14 @@ enum
|
||||
kAsioCanInputGain,
|
||||
kAsioCanInputMeter,
|
||||
kAsioCanOutputGain,
|
||||
kAsioCanOutputMeter
|
||||
kAsioCanOutputMeter,
|
||||
|
||||
// DSD support
|
||||
// The following extensions are required to allow switching
|
||||
// and control of the DSD subsystem.
|
||||
kAsioSetIoFormat = 0x23111961, /* ASIOIoFormat * in params. */
|
||||
kAsioGetIoFormat = 0x23111983, /* ASIOIoFormat * in params. */
|
||||
kAsioCanDoIoFormat = 0x23112004, /* ASIOIoFormat * in params. */
|
||||
};
|
||||
|
||||
typedef struct ASIOInputMonitor
|
||||
@@ -905,6 +967,43 @@ enum
|
||||
kTransMonitor // trackSwitches
|
||||
};
|
||||
|
||||
/*
|
||||
// DSD support
|
||||
// Some notes on how to use ASIOIoFormatType.
|
||||
//
|
||||
// The caller will fill the format with the request types.
|
||||
// If the board can do the request then it will leave the
|
||||
// values unchanged. If the board does not support the
|
||||
// request then it will change that entry to Invalid (-1)
|
||||
//
|
||||
// So to request DSD then
|
||||
//
|
||||
// ASIOIoFormat NeedThis={kASIODSDFormat};
|
||||
//
|
||||
// if(ASE_SUCCESS != ASIOFuture(kAsioSetIoFormat,&NeedThis) ){
|
||||
// // If the board did not accept one of the parameters then the
|
||||
// // whole call will fail and the failing parameter will
|
||||
// // have had its value changes to -1.
|
||||
// }
|
||||
//
|
||||
// Note: Switching between the formats need to be done before the "prepared"
|
||||
// state (see ASIO 2 documentation) is entered.
|
||||
*/
|
||||
typedef long int ASIOIoFormatType;
|
||||
enum ASIOIoFormatType_e
|
||||
{
|
||||
kASIOFormatInvalid = -1,
|
||||
kASIOPCMFormat = 0,
|
||||
kASIODSDFormat = 1,
|
||||
};
|
||||
|
||||
typedef struct ASIOIoFormat_s
|
||||
{
|
||||
ASIOIoFormatType FormatType;
|
||||
char future[512-sizeof(ASIOIoFormatType)];
|
||||
} ASIOIoFormat;
|
||||
|
||||
|
||||
ASIOError ASIOOutputReady(void);
|
||||
/* Purpose:
|
||||
this tells the driver that the host has completed processing
|
||||
|
||||
@@ -19,7 +19,7 @@ static LONG findDrvPath (char *clsidstr,char *dllpath,int dllpathsize)
|
||||
DWORD index;
|
||||
OFSTRUCT ofs;
|
||||
HFILE hfile;
|
||||
BOOL found = false;
|
||||
BOOL found = FALSE;
|
||||
|
||||
CharLowerBuff(clsidstr,strlen(clsidstr));
|
||||
if ((cr = RegOpenKey(HKEY_CLASSES_ROOT,COM_CLSID,&hkEnum)) == ERROR_SUCCESS) {
|
||||
@@ -44,7 +44,7 @@ static LONG findDrvPath (char *clsidstr,char *dllpath,int dllpathsize)
|
||||
}
|
||||
RegCloseKey(hksub);
|
||||
}
|
||||
found = true; // break out
|
||||
found = TRUE; // break out
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ AsioDriverList::AsioDriverList ()
|
||||
LPASIODRVSTRUCT pdl;
|
||||
LONG cr;
|
||||
DWORD index = 0;
|
||||
BOOL fin = false;
|
||||
BOOL fin = FALSE;
|
||||
|
||||
numdrv = 0;
|
||||
lpdrvlist = 0;
|
||||
@@ -144,7 +144,7 @@ AsioDriverList::AsioDriverList ()
|
||||
if ((cr = RegEnumKey(hkEnum,index++,(LPTSTR)keyname,MAXDRVNAMELEN))== ERROR_SUCCESS) {
|
||||
lpdrvlist = newDrvStruct (hkEnum,keyname,0,lpdrvlist);
|
||||
}
|
||||
else fin = true;
|
||||
else fin = TRUE;
|
||||
}
|
||||
if (hkEnum) RegCloseKey(hkEnum);
|
||||
|
||||
|
||||
563
src/asio/iasiothiscallresolver.cpp
Normal file
563
src/asio/iasiothiscallresolver.cpp
Normal file
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
IASIOThiscallResolver.cpp see the comments in iasiothiscallresolver.h for
|
||||
the top level description - this comment describes the technical details of
|
||||
the implementation.
|
||||
|
||||
The latest version of this file is available from:
|
||||
http://www.audiomulch.com/~rossb/code/calliasio
|
||||
|
||||
please email comments to Ross Bencina <rossb@audiomulch.com>
|
||||
|
||||
BACKGROUND
|
||||
|
||||
The IASIO interface declared in the Steinberg ASIO 2 SDK declares
|
||||
functions with no explicit calling convention. This causes MSVC++ to default
|
||||
to using the thiscall convention, which is a proprietary convention not
|
||||
implemented by some non-microsoft compilers - notably borland BCC,
|
||||
C++Builder, and gcc. MSVC++ is the defacto standard compiler used by
|
||||
Steinberg. As a result of this situation, the ASIO sdk will compile with
|
||||
any compiler, however attempting to execute the compiled code will cause a
|
||||
crash due to different default calling conventions on non-Microsoft
|
||||
compilers.
|
||||
|
||||
IASIOThiscallResolver solves the problem by providing an adapter class that
|
||||
delegates to the IASIO interface using the correct calling convention
|
||||
(thiscall). Due to the lack of support for thiscall in the Borland and GCC
|
||||
compilers, the calls have been implemented in assembly language.
|
||||
|
||||
A number of macros are defined for thiscall function calls with different
|
||||
numbers of parameters, with and without return values - it may be possible
|
||||
to modify the format of these macros to make them work with other inline
|
||||
assemblers.
|
||||
|
||||
|
||||
THISCALL DEFINITION
|
||||
|
||||
A number of definitions of the thiscall calling convention are floating
|
||||
around the internet. The following definition has been validated against
|
||||
output from the MSVC++ compiler:
|
||||
|
||||
For non-vararg functions, thiscall works as follows: the object (this)
|
||||
pointer is passed in ECX. All arguments are passed on the stack in
|
||||
right to left order. The return value is placed in EAX. The callee
|
||||
clears the passed arguments from the stack.
|
||||
|
||||
|
||||
FINDING FUNCTION POINTERS FROM AN IASIO POINTER
|
||||
|
||||
The first field of a COM object is a pointer to its vtble. Thus a pointer
|
||||
to an object implementing the IASIO interface also points to a pointer to
|
||||
that object's vtbl. The vtble is a table of function pointers for all of
|
||||
the virtual functions exposed by the implemented interfaces.
|
||||
|
||||
If we consider a variable declared as a pointer to IASO:
|
||||
|
||||
IASIO *theAsioDriver
|
||||
|
||||
theAsioDriver points to:
|
||||
|
||||
object implementing IASIO
|
||||
{
|
||||
IASIOvtbl *vtbl
|
||||
other data
|
||||
}
|
||||
|
||||
in other words, theAsioDriver points to a pointer to an IASIOvtbl
|
||||
|
||||
vtbl points to a table of function pointers:
|
||||
|
||||
IASIOvtbl ( interface IASIO : public IUnknown )
|
||||
{
|
||||
(IUnknown functions)
|
||||
0 virtual HRESULT STDMETHODCALLTYPE (*QueryInterface)(REFIID riid, void **ppv) = 0;
|
||||
4 virtual ULONG STDMETHODCALLTYPE (*AddRef)() = 0;
|
||||
8 virtual ULONG STDMETHODCALLTYPE (*Release)() = 0;
|
||||
|
||||
(IASIO functions)
|
||||
12 virtual ASIOBool (*init)(void *sysHandle) = 0;
|
||||
16 virtual void (*getDriverName)(char *name) = 0;
|
||||
20 virtual long (*getDriverVersion)() = 0;
|
||||
24 virtual void (*getErrorMessage)(char *string) = 0;
|
||||
28 virtual ASIOError (*start)() = 0;
|
||||
32 virtual ASIOError (*stop)() = 0;
|
||||
36 virtual ASIOError (*getChannels)(long *numInputChannels, long *numOutputChannels) = 0;
|
||||
40 virtual ASIOError (*getLatencies)(long *inputLatency, long *outputLatency) = 0;
|
||||
44 virtual ASIOError (*getBufferSize)(long *minSize, long *maxSize,
|
||||
long *preferredSize, long *granularity) = 0;
|
||||
48 virtual ASIOError (*canSampleRate)(ASIOSampleRate sampleRate) = 0;
|
||||
52 virtual ASIOError (*getSampleRate)(ASIOSampleRate *sampleRate) = 0;
|
||||
56 virtual ASIOError (*setSampleRate)(ASIOSampleRate sampleRate) = 0;
|
||||
60 virtual ASIOError (*getClockSources)(ASIOClockSource *clocks, long *numSources) = 0;
|
||||
64 virtual ASIOError (*setClockSource)(long reference) = 0;
|
||||
68 virtual ASIOError (*getSamplePosition)(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;
|
||||
72 virtual ASIOError (*getChannelInfo)(ASIOChannelInfo *info) = 0;
|
||||
76 virtual ASIOError (*createBuffers)(ASIOBufferInfo *bufferInfos, long numChannels,
|
||||
long bufferSize, ASIOCallbacks *callbacks) = 0;
|
||||
80 virtual ASIOError (*disposeBuffers)() = 0;
|
||||
84 virtual ASIOError (*controlPanel)() = 0;
|
||||
88 virtual ASIOError (*future)(long selector,void *opt) = 0;
|
||||
92 virtual ASIOError (*outputReady)() = 0;
|
||||
};
|
||||
|
||||
The numbers in the left column show the byte offset of each function ptr
|
||||
from the beginning of the vtbl. These numbers are used in the code below
|
||||
to select different functions.
|
||||
|
||||
In order to find the address of a particular function, theAsioDriver
|
||||
must first be dereferenced to find the value of the vtbl pointer:
|
||||
|
||||
mov eax, theAsioDriver
|
||||
mov edx, [theAsioDriver] // edx now points to vtbl[0]
|
||||
|
||||
Then an offset must be added to the vtbl pointer to select a
|
||||
particular function, for example vtbl+44 points to the slot containing
|
||||
a pointer to the getBufferSize function.
|
||||
|
||||
Finally vtbl+x must be dereferenced to obtain the value of the function
|
||||
pointer stored in that address:
|
||||
|
||||
call [edx+44] // call the function pointed to by
|
||||
// the value in the getBufferSize field of the vtbl
|
||||
|
||||
|
||||
SEE ALSO
|
||||
|
||||
Martin Fay's OpenASIO DLL at http://www.martinfay.com solves the same
|
||||
problem by providing a new COM interface which wraps IASIO with an
|
||||
interface that uses portable calling conventions. OpenASIO must be compiled
|
||||
with MSVC, and requires that you ship the OpenASIO DLL with your
|
||||
application.
|
||||
|
||||
|
||||
ACKNOWLEDGEMENTS
|
||||
|
||||
Ross Bencina: worked out the thiscall details above, wrote the original
|
||||
Borland asm macros, and a patch for asio.cpp (which is no longer needed).
|
||||
Thanks to Martin Fay for introducing me to the issues discussed here,
|
||||
and to Rene G. Ceballos for assisting with asm dumps from MSVC++.
|
||||
|
||||
Antti Silvast: converted the original calliasio to work with gcc and NASM
|
||||
by implementing the asm code in a separate file.
|
||||
|
||||
Fraser Adams: modified the original calliasio containing the Borland inline
|
||||
asm to add inline asm for gcc i.e. Intel syntax for Borland and AT&T syntax
|
||||
for gcc. This seems a neater approach for gcc than to have a separate .asm
|
||||
file and it means that we only need one version of the thiscall patch.
|
||||
|
||||
Fraser Adams: rewrote the original calliasio patch in the form of the
|
||||
IASIOThiscallResolver class in order to avoid modifications to files from
|
||||
the Steinberg SDK, which may have had potential licence issues.
|
||||
|
||||
Andrew Baldwin: contributed fixes for compatibility problems with more
|
||||
recent versions of the gcc assembler.
|
||||
*/
|
||||
|
||||
|
||||
// We only need IASIOThiscallResolver at all if we are on Win32. For other
|
||||
// platforms we simply bypass the IASIOThiscallResolver definition to allow us
|
||||
// to be safely #include'd whatever the platform to keep client code portable
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
|
||||
|
||||
|
||||
// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver
|
||||
// is not used.
|
||||
#if !defined(_MSC_VER)
|
||||
|
||||
|
||||
#include <new>
|
||||
#include <assert.h>
|
||||
|
||||
// We have a mechanism in iasiothiscallresolver.h to ensure that asio.h is
|
||||
// #include'd before it in client code, we do NOT want to do this test here.
|
||||
#define iasiothiscallresolver_sourcefile 1
|
||||
#include "iasiothiscallresolver.h"
|
||||
#undef iasiothiscallresolver_sourcefile
|
||||
|
||||
// iasiothiscallresolver.h redefines ASIOInit for clients, but we don't want
|
||||
// this macro defined in this translation unit.
|
||||
#undef ASIOInit
|
||||
|
||||
|
||||
// theAsioDriver is a global pointer to the current IASIO instance which the
|
||||
// ASIO SDK uses to perform all actions on the IASIO interface. We substitute
|
||||
// our own forwarding interface into this pointer.
|
||||
extern IASIO* theAsioDriver;
|
||||
|
||||
|
||||
// The following macros define the inline assembler for BORLAND first then gcc
|
||||
|
||||
#if defined(__BCPLUSPLUS__) || defined(__BORLANDC__)
|
||||
|
||||
|
||||
#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )\
|
||||
void *this_ = (thisPtr); \
|
||||
__asm { \
|
||||
mov ecx, this_ ; \
|
||||
mov eax, [ecx] ; \
|
||||
call [eax+funcOffset] ; \
|
||||
mov resultName, eax ; \
|
||||
}
|
||||
|
||||
|
||||
#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )\
|
||||
void *this_ = (thisPtr); \
|
||||
__asm { \
|
||||
mov eax, param1 ; \
|
||||
push eax ; \
|
||||
mov ecx, this_ ; \
|
||||
mov eax, [ecx] ; \
|
||||
call [eax+funcOffset] ; \
|
||||
}
|
||||
|
||||
|
||||
#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )\
|
||||
void *this_ = (thisPtr); \
|
||||
__asm { \
|
||||
mov eax, param1 ; \
|
||||
push eax ; \
|
||||
mov ecx, this_ ; \
|
||||
mov eax, [ecx] ; \
|
||||
call [eax+funcOffset] ; \
|
||||
mov resultName, eax ; \
|
||||
}
|
||||
|
||||
|
||||
#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )\
|
||||
void *this_ = (thisPtr); \
|
||||
void *doubleParamPtr_ (¶m1); \
|
||||
__asm { \
|
||||
mov eax, doubleParamPtr_ ; \
|
||||
push [eax+4] ; \
|
||||
push [eax] ; \
|
||||
mov ecx, this_ ; \
|
||||
mov eax, [ecx] ; \
|
||||
call [eax+funcOffset] ; \
|
||||
mov resultName, eax ; \
|
||||
}
|
||||
|
||||
|
||||
#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )\
|
||||
void *this_ = (thisPtr); \
|
||||
__asm { \
|
||||
mov eax, param2 ; \
|
||||
push eax ; \
|
||||
mov eax, param1 ; \
|
||||
push eax ; \
|
||||
mov ecx, this_ ; \
|
||||
mov eax, [ecx] ; \
|
||||
call [eax+funcOffset] ; \
|
||||
mov resultName, eax ; \
|
||||
}
|
||||
|
||||
|
||||
#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
|
||||
void *this_ = (thisPtr); \
|
||||
__asm { \
|
||||
mov eax, param4 ; \
|
||||
push eax ; \
|
||||
mov eax, param3 ; \
|
||||
push eax ; \
|
||||
mov eax, param2 ; \
|
||||
push eax ; \
|
||||
mov eax, param1 ; \
|
||||
push eax ; \
|
||||
mov ecx, this_ ; \
|
||||
mov eax, [ecx] ; \
|
||||
call [eax+funcOffset] ; \
|
||||
mov resultName, eax ; \
|
||||
}
|
||||
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
|
||||
#define CALL_THISCALL_0( resultName, thisPtr, funcOffset ) \
|
||||
__asm__ __volatile__ ("movl (%1), %%edx\n\t" \
|
||||
"call *"#funcOffset"(%%edx)\n\t" \
|
||||
:"=a"(resultName) /* Output Operands */ \
|
||||
:"c"(thisPtr) /* Input Operands */ \
|
||||
); \
|
||||
|
||||
|
||||
#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 ) \
|
||||
__asm__ __volatile__ ("pushl %0\n\t" \
|
||||
"movl (%1), %%edx\n\t" \
|
||||
"call *"#funcOffset"(%%edx)\n\t" \
|
||||
: /* Output Operands */ \
|
||||
:"r"(param1), /* Input Operands */ \
|
||||
"c"(thisPtr) \
|
||||
); \
|
||||
|
||||
|
||||
#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 ) \
|
||||
__asm__ __volatile__ ("pushl %1\n\t" \
|
||||
"movl (%2), %%edx\n\t" \
|
||||
"call *"#funcOffset"(%%edx)\n\t" \
|
||||
:"=a"(resultName) /* Output Operands */ \
|
||||
:"r"(param1), /* Input Operands */ \
|
||||
"c"(thisPtr) \
|
||||
); \
|
||||
|
||||
|
||||
#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 ) \
|
||||
__asm__ __volatile__ ("pushl 4(%1)\n\t" \
|
||||
"pushl (%1)\n\t" \
|
||||
"movl (%2), %%edx\n\t" \
|
||||
"call *"#funcOffset"(%%edx);\n\t" \
|
||||
:"=a"(resultName) /* Output Operands */ \
|
||||
:"a"(¶m1), /* Input Operands */ \
|
||||
/* Note: Using "r" above instead of "a" fails */ \
|
||||
/* when using GCC 3.3.3, and maybe later versions*/\
|
||||
"c"(thisPtr) \
|
||||
); \
|
||||
|
||||
|
||||
#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 ) \
|
||||
__asm__ __volatile__ ("pushl %1\n\t" \
|
||||
"pushl %2\n\t" \
|
||||
"movl (%3), %%edx\n\t" \
|
||||
"call *"#funcOffset"(%%edx)\n\t" \
|
||||
:"=a"(resultName) /* Output Operands */ \
|
||||
:"r"(param2), /* Input Operands */ \
|
||||
"r"(param1), \
|
||||
"c"(thisPtr) \
|
||||
); \
|
||||
|
||||
|
||||
#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
|
||||
__asm__ __volatile__ ("pushl %1\n\t" \
|
||||
"pushl %2\n\t" \
|
||||
"pushl %3\n\t" \
|
||||
"pushl %4\n\t" \
|
||||
"movl (%5), %%edx\n\t" \
|
||||
"call *"#funcOffset"(%%edx)\n\t" \
|
||||
:"=a"(resultName) /* Output Operands */ \
|
||||
:"r"(param4), /* Input Operands */ \
|
||||
"r"(param3), \
|
||||
"r"(param2), \
|
||||
"r"(param1), \
|
||||
"c"(thisPtr) \
|
||||
); \
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Our static singleton instance.
|
||||
IASIOThiscallResolver IASIOThiscallResolver::instance;
|
||||
|
||||
// Constructor called to initialize static Singleton instance above. Note that
|
||||
// it is important not to clear that_ incase it has already been set by the call
|
||||
// to placement new in ASIOInit().
|
||||
IASIOThiscallResolver::IASIOThiscallResolver()
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor called from ASIOInit() below
|
||||
IASIOThiscallResolver::IASIOThiscallResolver(IASIO* that)
|
||||
: that_( that )
|
||||
{
|
||||
}
|
||||
|
||||
// Implement IUnknown methods as assert(false). IASIOThiscallResolver is not
|
||||
// really a COM object, just a wrapper which will work with the ASIO SDK.
|
||||
// If you wanted to use ASIO without the SDK you might want to implement COM
|
||||
// aggregation in these methods.
|
||||
HRESULT STDMETHODCALLTYPE IASIOThiscallResolver::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
(void)riid; // suppress unused variable warning
|
||||
|
||||
assert( false ); // this function should never be called by the ASIO SDK.
|
||||
|
||||
*ppv = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE IASIOThiscallResolver::AddRef()
|
||||
{
|
||||
assert( false ); // this function should never be called by the ASIO SDK.
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE IASIOThiscallResolver::Release()
|
||||
{
|
||||
assert( false ); // this function should never be called by the ASIO SDK.
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Implement the IASIO interface methods by performing the vptr manipulation
|
||||
// described above then delegating to the real implementation.
|
||||
ASIOBool IASIOThiscallResolver::init(void *sysHandle)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_1( result, that_, 12, sysHandle );
|
||||
return result;
|
||||
}
|
||||
|
||||
void IASIOThiscallResolver::getDriverName(char *name)
|
||||
{
|
||||
CALL_VOID_THISCALL_1( that_, 16, name );
|
||||
}
|
||||
|
||||
long IASIOThiscallResolver::getDriverVersion()
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_0( result, that_, 20 );
|
||||
return result;
|
||||
}
|
||||
|
||||
void IASIOThiscallResolver::getErrorMessage(char *string)
|
||||
{
|
||||
CALL_VOID_THISCALL_1( that_, 24, string );
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::start()
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_0( result, that_, 28 );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::stop()
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_0( result, that_, 32 );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::getChannels(long *numInputChannels, long *numOutputChannels)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::getLatencies(long *inputLatency, long *outputLatency)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::getBufferSize(long *minSize, long *maxSize,
|
||||
long *preferredSize, long *granularity)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::canSampleRate(ASIOSampleRate sampleRate)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::getSampleRate(ASIOSampleRate *sampleRate)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_1( result, that_, 52, sampleRate );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::setSampleRate(ASIOSampleRate sampleRate)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::getClockSources(ASIOClockSource *clocks, long *numSources)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_2( result, that_, 60, clocks, numSources );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::setClockSource(long reference)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_1( result, that_, 64, reference );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_2( result, that_, 68, sPos, tStamp );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::getChannelInfo(ASIOChannelInfo *info)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_1( result, that_, 72, info );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::createBuffers(ASIOBufferInfo *bufferInfos,
|
||||
long numChannels, long bufferSize, ASIOCallbacks *callbacks)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::disposeBuffers()
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_0( result, that_, 80 );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::controlPanel()
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_0( result, that_, 84 );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::future(long selector,void *opt)
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_2( result, that_, 88, selector, opt );
|
||||
return result;
|
||||
}
|
||||
|
||||
ASIOError IASIOThiscallResolver::outputReady()
|
||||
{
|
||||
ASIOBool result;
|
||||
CALL_THISCALL_0( result, that_, 92 );
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Implement our substitute ASIOInit() method
|
||||
ASIOError IASIOThiscallResolver::ASIOInit(ASIODriverInfo *info)
|
||||
{
|
||||
// To ensure that our instance's vptr is correctly constructed, even if
|
||||
// ASIOInit is called prior to main(), we explicitly call its constructor
|
||||
// (potentially over the top of an existing instance). Note that this is
|
||||
// pretty ugly, and is only safe because IASIOThiscallResolver has no
|
||||
// destructor and contains no objects with destructors.
|
||||
new((void*)&instance) IASIOThiscallResolver( theAsioDriver );
|
||||
|
||||
// Interpose between ASIO client code and the real driver.
|
||||
theAsioDriver = &instance;
|
||||
|
||||
// Note that we never need to switch theAsioDriver back to point to the
|
||||
// real driver because theAsioDriver is reset to zero in ASIOExit().
|
||||
|
||||
// Delegate to the real ASIOInit
|
||||
return ::ASIOInit(info);
|
||||
}
|
||||
|
||||
|
||||
#endif /* !defined(_MSC_VER) */
|
||||
|
||||
#endif /* Win32 */
|
||||
|
||||
201
src/asio/iasiothiscallresolver.h
Normal file
201
src/asio/iasiothiscallresolver.h
Normal file
@@ -0,0 +1,201 @@
|
||||
// ****************************************************************************
|
||||
//
|
||||
// Changed: I have modified this file slightly (includes) to work with
|
||||
// RtAudio. RtAudio.cpp must include this file after asio.h.
|
||||
//
|
||||
// File: IASIOThiscallResolver.h
|
||||
// Description: The IASIOThiscallResolver class implements the IASIO
|
||||
// interface and acts as a proxy to the real IASIO interface by
|
||||
// calling through its vptr table using the thiscall calling
|
||||
// convention. To put it another way, we interpose
|
||||
// IASIOThiscallResolver between ASIO SDK code and the driver.
|
||||
// This is necessary because most non-Microsoft compilers don't
|
||||
// implement the thiscall calling convention used by IASIO.
|
||||
//
|
||||
// iasiothiscallresolver.cpp contains the background of this
|
||||
// problem plus a technical description of the vptr
|
||||
// manipulations.
|
||||
//
|
||||
// In order to use this mechanism one simply has to add
|
||||
// iasiothiscallresolver.cpp to the list of files to compile
|
||||
// and #include <iasiothiscallresolver.h>
|
||||
//
|
||||
// Note that this #include must come after the other ASIO SDK
|
||||
// #includes, for example:
|
||||
//
|
||||
// #include <windows.h>
|
||||
// #include <asiosys.h>
|
||||
// #include <asio.h>
|
||||
// #include <asiodrivers.h>
|
||||
// #include <iasiothiscallresolver.h>
|
||||
//
|
||||
// Actually the important thing is to #include
|
||||
// <iasiothiscallresolver.h> after <asio.h>. We have
|
||||
// incorporated a test to enforce this ordering.
|
||||
//
|
||||
// The code transparently takes care of the interposition by
|
||||
// using macro substitution to intercept calls to ASIOInit()
|
||||
// and ASIOExit(). We save the original ASIO global
|
||||
// "theAsioDriver" in our "that" variable, and then set
|
||||
// "theAsioDriver" to equal our IASIOThiscallResolver instance.
|
||||
//
|
||||
// Whilst this method of resolving the thiscall problem requires
|
||||
// the addition of #include <iasiothiscallresolver.h> to client
|
||||
// code it has the advantage that it does not break the terms
|
||||
// of the ASIO licence by publishing it. We are NOT modifying
|
||||
// any Steinberg code here, we are merely implementing the IASIO
|
||||
// interface in the same way that we would need to do if we
|
||||
// wished to provide an open source ASIO driver.
|
||||
//
|
||||
// For compilation with MinGW -lole32 needs to be added to the
|
||||
// linker options. For BORLAND, linking with Import32.lib is
|
||||
// sufficient.
|
||||
//
|
||||
// The dependencies are with: CoInitialize, CoUninitialize,
|
||||
// CoCreateInstance, CLSIDFromString - used by asiolist.cpp
|
||||
// and are required on Windows whether ThiscallResolver is used
|
||||
// or not.
|
||||
//
|
||||
// Searching for the above strings in the root library path
|
||||
// of your compiler should enable the correct libraries to be
|
||||
// identified if they aren't immediately obvious.
|
||||
//
|
||||
// Note that the current implementation of IASIOThiscallResolver
|
||||
// is not COM compliant - it does not correctly implement the
|
||||
// IUnknown interface. Implementing it is not necessary because
|
||||
// it is not called by parts of the ASIO SDK which call through
|
||||
// theAsioDriver ptr. The IUnknown methods are implemented as
|
||||
// assert(false) to ensure that the code fails if they are
|
||||
// ever called.
|
||||
// Restrictions: None. Public Domain & Open Source distribute freely
|
||||
// You may use IASIOThiscallResolver commercially as well as
|
||||
// privately.
|
||||
// You the user assume the responsibility for the use of the
|
||||
// files, binary or text, and there is no guarantee or warranty,
|
||||
// expressed or implied, including but not limited to the
|
||||
// implied warranties of merchantability and fitness for a
|
||||
// particular purpose. You assume all responsibility and agree
|
||||
// to hold no entity, copyright holder or distributors liable
|
||||
// for any loss of data or inaccurate representations of data
|
||||
// as a result of using IASIOThiscallResolver.
|
||||
// Version: 1.4 Added separate macro CALL_THISCALL_1_DOUBLE from
|
||||
// Andrew Baldwin, and volatile for whole gcc asm blocks,
|
||||
// both for compatibility with newer gcc versions. Cleaned up
|
||||
// Borland asm to use one less register.
|
||||
// 1.3 Switched to including assert.h for better compatibility.
|
||||
// Wrapped entire .h and .cpp contents with a check for
|
||||
// _MSC_VER to provide better compatibility with MS compilers.
|
||||
// Changed Singleton implementation to use static instance
|
||||
// instead of freestore allocated instance. Removed ASIOExit
|
||||
// macro as it is no longer needed.
|
||||
// 1.2 Removed semicolons from ASIOInit and ASIOExit macros to
|
||||
// allow them to be embedded in expressions (if statements).
|
||||
// Cleaned up some comments. Removed combase.c dependency (it
|
||||
// doesn't compile with BCB anyway) by stubbing IUnknown.
|
||||
// 1.1 Incorporated comments from Ross Bencina including things
|
||||
// such as changing name from ThiscallResolver to
|
||||
// IASIOThiscallResolver, tidying up the constructor, fixing
|
||||
// a bug in IASIOThiscallResolver::ASIOExit() and improving
|
||||
// portability through the use of conditional compilation
|
||||
// 1.0 Initial working version.
|
||||
// Created: 6/09/2003
|
||||
// Authors: Fraser Adams
|
||||
// Ross Bencina
|
||||
// Rene G. Ceballos
|
||||
// Martin Fay
|
||||
// Antti Silvast
|
||||
// Andrew Baldwin
|
||||
//
|
||||
// ****************************************************************************
|
||||
|
||||
|
||||
#ifndef included_iasiothiscallresolver_h
|
||||
#define included_iasiothiscallresolver_h
|
||||
|
||||
// We only need IASIOThiscallResolver at all if we are on Win32. For other
|
||||
// platforms we simply bypass the IASIOThiscallResolver definition to allow us
|
||||
// to be safely #include'd whatever the platform to keep client code portable
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
|
||||
|
||||
|
||||
// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver
|
||||
// is not used.
|
||||
#if !defined(_MSC_VER)
|
||||
|
||||
|
||||
// The following is in order to ensure that this header is only included after
|
||||
// the other ASIO headers (except for the case of iasiothiscallresolver.cpp).
|
||||
// We need to do this because IASIOThiscallResolver works by eclipsing the
|
||||
// original definition of ASIOInit() with a macro (see below).
|
||||
#if !defined(iasiothiscallresolver_sourcefile)
|
||||
#if !defined(__ASIO_H)
|
||||
#error iasiothiscallresolver.h must be included AFTER asio.h
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include "iasiodrv.h" /* From ASIO SDK */
|
||||
|
||||
|
||||
class IASIOThiscallResolver : public IASIO {
|
||||
private:
|
||||
IASIO* that_; // Points to the real IASIO
|
||||
|
||||
static IASIOThiscallResolver instance; // Singleton instance
|
||||
|
||||
// Constructors - declared private so construction is limited to
|
||||
// our Singleton instance
|
||||
IASIOThiscallResolver();
|
||||
IASIOThiscallResolver(IASIO* that);
|
||||
public:
|
||||
|
||||
// Methods from the IUnknown interface. We don't fully implement IUnknown
|
||||
// because the ASIO SDK never calls these methods through theAsioDriver ptr.
|
||||
// These methods are implemented as assert(false).
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv);
|
||||
virtual ULONG STDMETHODCALLTYPE AddRef();
|
||||
virtual ULONG STDMETHODCALLTYPE Release();
|
||||
|
||||
// Methods from the IASIO interface, implemented as forwarning calls to that.
|
||||
virtual ASIOBool init(void *sysHandle);
|
||||
virtual void getDriverName(char *name);
|
||||
virtual long getDriverVersion();
|
||||
virtual void getErrorMessage(char *string);
|
||||
virtual ASIOError start();
|
||||
virtual ASIOError stop();
|
||||
virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels);
|
||||
virtual ASIOError getLatencies(long *inputLatency, long *outputLatency);
|
||||
virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);
|
||||
virtual ASIOError canSampleRate(ASIOSampleRate sampleRate);
|
||||
virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate);
|
||||
virtual ASIOError setSampleRate(ASIOSampleRate sampleRate);
|
||||
virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources);
|
||||
virtual ASIOError setClockSource(long reference);
|
||||
virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp);
|
||||
virtual ASIOError getChannelInfo(ASIOChannelInfo *info);
|
||||
virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks);
|
||||
virtual ASIOError disposeBuffers();
|
||||
virtual ASIOError controlPanel();
|
||||
virtual ASIOError future(long selector,void *opt);
|
||||
virtual ASIOError outputReady();
|
||||
|
||||
// Class method, see ASIOInit() macro below.
|
||||
static ASIOError ASIOInit(ASIODriverInfo *info); // Delegates to ::ASIOInit
|
||||
};
|
||||
|
||||
|
||||
// Replace calls to ASIOInit with our interposing version.
|
||||
// This macro enables us to perform thiscall resolution simply by #including
|
||||
// <iasiothiscallresolver.h> after the asio #includes (this file _must_ be
|
||||
// included _after_ the asio #includes)
|
||||
|
||||
#define ASIOInit(name) IASIOThiscallResolver::ASIOInit((name))
|
||||
|
||||
|
||||
#endif /* !defined(_MSC_VER) */
|
||||
|
||||
#endif /* Win32 */
|
||||
|
||||
#endif /* included_iasiothiscallresolver_h */
|
||||
|
||||
|
||||
2061
src/oss/soundcard.h
Normal file
2061
src/oss/soundcard.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user