Version 4.1

This commit is contained in:
Gary Scavone
2009-03-24 23:02:12 -04:00
committed by Stephen Sinclair
parent 81475b04c5
commit 2f09fcd019
279 changed files with 36223 additions and 25364 deletions

View File

@@ -1,190 +1,190 @@
/***************************************************/
/*! \class ADSR
\brief STK ADSR envelope class.
This Envelope subclass implements a
traditional ADSR (Attack, Decay,
Sustain, Release) envelope. It
responds to simple keyOn and keyOff
messages, keeping track of its state.
The \e state = ADSR::DONE after the
envelope value reaches 0.0 in the
ADSR::RELEASE state.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "ADSR.h"
#include <stdio.h>
ADSR :: ADSR() : Envelope()
{
target = (MY_FLOAT) 0.0;
value = (MY_FLOAT) 0.0;
attackRate = (MY_FLOAT) 0.001;
decayRate = (MY_FLOAT) 0.001;
sustainLevel = (MY_FLOAT) 0.5;
releaseRate = (MY_FLOAT) 0.01;
state = ATTACK;
}
ADSR :: ~ADSR()
{
}
void ADSR :: keyOn()
{
target = (MY_FLOAT) 1.0;
rate = attackRate;
state = ATTACK;
}
void ADSR :: keyOff()
{
target = (MY_FLOAT) 0.0;
rate = releaseRate;
state = RELEASE;
}
void ADSR :: setAttackRate(MY_FLOAT aRate)
{
if (aRate < 0.0) {
printf("ADSR: negative rates not allowed ... correcting!\n");
attackRate = -aRate;
}
else attackRate = aRate;
}
void ADSR :: setDecayRate(MY_FLOAT aRate)
{
if (aRate < 0.0) {
printf("ADSR: negative rates not allowed ... correcting!\n");
decayRate = -aRate;
}
else decayRate = aRate;
}
void ADSR :: setSustainLevel(MY_FLOAT aLevel)
{
if (aLevel < 0.0 ) {
printf("ADSR: sustain level out of range ... correcting!\n");
sustainLevel = (MY_FLOAT) 0.0;
}
else sustainLevel = aLevel;
}
void ADSR :: setReleaseRate(MY_FLOAT aRate)
{
if (aRate < 0.0) {
printf("ADSR: negative rates not allowed ... correcting!\n");
releaseRate = -aRate;
}
else releaseRate = aRate;
}
void ADSR :: setAttackTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("ADSR: negative rates not allowed ... correcting!\n");
attackRate = 1.0 / ( -aTime * Stk::sampleRate() );
}
else attackRate = 1.0 / ( aTime * Stk::sampleRate() );
}
void ADSR :: setDecayTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("ADSR: negative times not allowed ... correcting!\n");
decayRate = 1.0 / ( -aTime * Stk::sampleRate() );
}
else decayRate = 1.0 / ( aTime * Stk::sampleRate() );
}
void ADSR :: setReleaseTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("ADSR: negative times not allowed ... correcting!\n");
releaseRate = 1.0 / ( -aTime * Stk::sampleRate() );
}
else releaseRate = 1.0 / ( aTime * Stk::sampleRate() );
}
void ADSR :: setAllTimes(MY_FLOAT aTime, MY_FLOAT dTime, MY_FLOAT sLevel, MY_FLOAT rTime)
{
this->setAttackTime(aTime);
this->setDecayTime(dTime);
this->setSustainLevel(sLevel);
this->setReleaseTime(rTime);
}
void ADSR :: setTarget(MY_FLOAT aTarget)
{
target = aTarget;
if (value < target) {
state = ATTACK;
this->setSustainLevel(target);
rate = attackRate;
}
if (value > target) {
this->setSustainLevel(target);
state = DECAY;
rate = decayRate;
}
}
void ADSR :: setValue(MY_FLOAT aValue)
{
state = SUSTAIN;
target = aValue;
value = aValue;
this->setSustainLevel(aValue);
rate = (MY_FLOAT) 0.0;
}
int ADSR :: getState(void) const
{
return state;
}
MY_FLOAT ADSR :: tick()
{
switch (state) {
case ATTACK:
value += rate;
if (value >= target) {
value = target;
rate = decayRate;
target = sustainLevel;
state = DECAY;
}
break;
case DECAY:
value -= decayRate;
if (value <= sustainLevel) {
value = sustainLevel;
rate = (MY_FLOAT) 0.0;
state = SUSTAIN;
}
break;
case RELEASE:
value -= releaseRate;
if (value <= 0.0) {
value = (MY_FLOAT) 0.0;
state = DONE;
}
}
return value;
}
MY_FLOAT *ADSR :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick();
return vector;
}
/***************************************************/
/*! \class ADSR
\brief STK ADSR envelope class.
This Envelope subclass implements a
traditional ADSR (Attack, Decay,
Sustain, Release) envelope. It
responds to simple keyOn and keyOff
messages, keeping track of its state.
The \e state = ADSR::DONE after the
envelope value reaches 0.0 in the
ADSR::RELEASE state.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "ADSR.h"
#include <stdio.h>
ADSR :: ADSR() : Envelope()
{
target = (MY_FLOAT) 0.0;
value = (MY_FLOAT) 0.0;
attackRate = (MY_FLOAT) 0.001;
decayRate = (MY_FLOAT) 0.001;
sustainLevel = (MY_FLOAT) 0.5;
releaseRate = (MY_FLOAT) 0.01;
state = ATTACK;
}
ADSR :: ~ADSR()
{
}
void ADSR :: keyOn()
{
target = (MY_FLOAT) 1.0;
rate = attackRate;
state = ATTACK;
}
void ADSR :: keyOff()
{
target = (MY_FLOAT) 0.0;
rate = releaseRate;
state = RELEASE;
}
void ADSR :: setAttackRate(MY_FLOAT aRate)
{
if (aRate < 0.0) {
printf("ADSR: negative rates not allowed ... correcting!\n");
attackRate = -aRate;
}
else attackRate = aRate;
}
void ADSR :: setDecayRate(MY_FLOAT aRate)
{
if (aRate < 0.0) {
printf("ADSR: negative rates not allowed ... correcting!\n");
decayRate = -aRate;
}
else decayRate = aRate;
}
void ADSR :: setSustainLevel(MY_FLOAT aLevel)
{
if (aLevel < 0.0 ) {
printf("ADSR: sustain level out of range ... correcting!\n");
sustainLevel = (MY_FLOAT) 0.0;
}
else sustainLevel = aLevel;
}
void ADSR :: setReleaseRate(MY_FLOAT aRate)
{
if (aRate < 0.0) {
printf("ADSR: negative rates not allowed ... correcting!\n");
releaseRate = -aRate;
}
else releaseRate = aRate;
}
void ADSR :: setAttackTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("ADSR: negative rates not allowed ... correcting!\n");
attackRate = 1.0 / ( -aTime * Stk::sampleRate() );
}
else attackRate = 1.0 / ( aTime * Stk::sampleRate() );
}
void ADSR :: setDecayTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("ADSR: negative times not allowed ... correcting!\n");
decayRate = 1.0 / ( -aTime * Stk::sampleRate() );
}
else decayRate = 1.0 / ( aTime * Stk::sampleRate() );
}
void ADSR :: setReleaseTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("ADSR: negative times not allowed ... correcting!\n");
releaseRate = 1.0 / ( -aTime * Stk::sampleRate() );
}
else releaseRate = 1.0 / ( aTime * Stk::sampleRate() );
}
void ADSR :: setAllTimes(MY_FLOAT aTime, MY_FLOAT dTime, MY_FLOAT sLevel, MY_FLOAT rTime)
{
this->setAttackTime(aTime);
this->setDecayTime(dTime);
this->setSustainLevel(sLevel);
this->setReleaseTime(rTime);
}
void ADSR :: setTarget(MY_FLOAT aTarget)
{
target = aTarget;
if (value < target) {
state = ATTACK;
this->setSustainLevel(target);
rate = attackRate;
}
if (value > target) {
this->setSustainLevel(target);
state = DECAY;
rate = decayRate;
}
}
void ADSR :: setValue(MY_FLOAT aValue)
{
state = SUSTAIN;
target = aValue;
value = aValue;
this->setSustainLevel(aValue);
rate = (MY_FLOAT) 0.0;
}
int ADSR :: getState(void) const
{
return state;
}
MY_FLOAT ADSR :: tick()
{
switch (state) {
case ATTACK:
value += rate;
if (value >= target) {
value = target;
rate = decayRate;
target = sustainLevel;
state = DECAY;
}
break;
case DECAY:
value -= decayRate;
if (value <= sustainLevel) {
value = sustainLevel;
rate = (MY_FLOAT) 0.0;
state = SUSTAIN;
}
break;
case RELEASE:
value -= releaseRate;
if (value <= 0.0) {
value = (MY_FLOAT) 0.0;
state = DONE;
}
}
return value;
}
MY_FLOAT *ADSR :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick();
return vector;
}

View File

@@ -38,7 +38,7 @@ BandedWG :: BandedWG()
{
doPluck = true;
delay = new Delay[MAX_BANDED_MODES];
delay = new DelayL[MAX_BANDED_MODES];
bandpass = new BiQuad[MAX_BANDED_MODES];
bowTabl = new BowTabl;
@@ -58,6 +58,8 @@ BandedWG :: BandedWG()
bowVelocity = 0.0;
bowTarget = 0.0;
strikeAmp = 0.0;
}
BandedWG :: ~BandedWG()
@@ -88,8 +90,10 @@ void BandedWG :: setPreset(int preset)
modes[2] = (MY_FLOAT) 10.7184986595;
modes[3] = (MY_FLOAT) 18.0697050938;
for (i=0; i<presetModes; i++)
gains[i] = (MY_FLOAT) pow(0.999,(double) i);
for (i=0; i<presetModes; i++) {
basegains[i] = (MY_FLOAT) pow(0.999,(double) i+1);
excitation[i] = 1.0;
}
break;
@@ -102,40 +106,58 @@ void BandedWG :: setPreset(int preset)
modes[4] = (MY_FLOAT) 9.38;
// modes[5] = (MY_FLOAT) 12.22;
for (i=0; i<presetModes; i++)
gains[i] = (MY_FLOAT) pow(0.999,(double) i);
for (i=0; i<presetModes; i++) {
basegains[i] = (MY_FLOAT) pow(0.999,(double) i+1);
excitation[i] = 1.0;
}
/*
baseGain = (MY_FLOAT) 0.99999;
for (i=0; i<presetModes; i++)
baseGain = (MY_FLOAT) 0.99999;
for (i=0; i<presetModes; i++)
gains[i]= (MY_FLOAT) pow(baseGain, delay[i].getDelay()+i);
*/
break;
case 3: // Tibetan Prayer Bowl (ICMC'02)
presetModes = 12;
modes[0]=0.996108344;
basegains[0]=0.999925960128219;
excitation[0]=11.900357/10.0;
modes[1]=1.0038916562;
basegains[1]=0.999925960128219;
excitation[1]=11.900357/10.;
modes[2]=2.979178;
basegains[2]=0.999982774366897;
excitation[2]=10.914886/10.;
modes[3]=2.99329767;
basegains[3]=0.999982774366897;
excitation[3]=10.914886/10.;
modes[4]=5.704452;
basegains[4]=1.0; //0.999999999999999999987356406352;
excitation[4]=42.995041/10.;
modes[5]=5.704452;
basegains[5]=1.0; //0.999999999999999999987356406352;
excitation[5]=42.995041/10.;
modes[6]=8.9982;
basegains[6]=1.0; //0.999999999999999999996995497558225;
excitation[6]=40.063034/10.;
modes[7]=9.01549726;
basegains[7]=1.0; //0.999999999999999999996995497558225;
excitation[7]=40.063034/10.;
modes[8]=12.83303;
basegains[8]=0.999965497558225;
excitation[8]=7.063034/10.;
modes[9]=12.807382;
basegains[9]=0.999965497558225;
excitation[9]=7.063034/10.;
modes[10]=17.2808219;
basegains[10]=0.9999999999999999999965497558225;
excitation[10]=57.063034/10.;
modes[11]=21.97602739726;
basegains[11]=0.999999999999999965497558225;
excitation[11]=57.063034/10.;
case 3: // Tibetan Prayer Bowl
presetModes = 17;
modes[0] = (MY_FLOAT) 1.0;
modes[1] = (MY_FLOAT) 1.135;
modes[2] = (MY_FLOAT) 2.329;
modes[3] = (MY_FLOAT) 3.210;
modes[4] = (MY_FLOAT) 6.046;
modes[5] = (MY_FLOAT) 6.106;
modes[6] = (MY_FLOAT) 6.419;
modes[7] = (MY_FLOAT) 9.689;
modes[8] = (MY_FLOAT) 12.212;
modes[9] = (MY_FLOAT) 13.869;
modes[10] = (MY_FLOAT) 15.735;
modes[11] = (MY_FLOAT) 15.795;
modes[12] = (MY_FLOAT) 18.601;
modes[13] = (MY_FLOAT) 18.661;
modes[14] = (MY_FLOAT) 19.363;
modes[15] = (MY_FLOAT) 23.901;
modes[16] = (MY_FLOAT) 32.470;
for (i=0; i<presetModes; i++)
gains[i]=0.9995;
break;
break;
default: // Uniform Bar
presetModes = 4;
@@ -144,8 +166,10 @@ void BandedWG :: setPreset(int preset)
modes[2] = (MY_FLOAT) 5.404;
modes[3] = (MY_FLOAT) 8.933;
for (i=0; i<presetModes; i++)
gains[i] = (MY_FLOAT) pow(0.9,(double) i);
for (i=0; i<presetModes; i++) {
basegains[i] = (MY_FLOAT) pow(0.9,(double) i+1);
excitation[i] = 1.0;
}
break;
}
@@ -165,19 +189,24 @@ void BandedWG :: setFrequency(MY_FLOAT frequency)
MY_FLOAT radius;
MY_FLOAT base = Stk::sampleRate() / freakency;
int length;
MY_FLOAT length;
for (int i=0; i<presetModes; i++) {
// Calculate the delay line lengths for each mode.
length = (int) (base / modes[i]);
if ( length > 2)
length = (int)(base / modes[i]);
if ( length > 2.0) {
delay[i].setDelay( length );
gains[i]=basegains[i];
// gains[i]=(MY_FLOAT) pow(basegains[i], 1/((MY_FLOAT)delay[i].getDelay()));
// cerr << gains[i];
}
else {
nModes = i;
break;
}
// cerr << endl;
// Set the bandpass filter resonances
radius = 1.0 - PI * freakency * modes[i] / Stk::sampleRate();
radius = 1.0 - PI * 32 / Stk::sampleRate(); //freakency * modes[i] / Stk::sampleRate()/32;
if ( radius < 0.0 ) radius = 0.0;
bandpass[i].setResonance(freakency * modes[i], radius, true);
@@ -191,7 +220,7 @@ void BandedWG :: setFrequency(MY_FLOAT frequency)
void BandedWG :: setStrikePosition(MY_FLOAT position)
{
strikePosition = (int)(delay[0].getDelay() * position / 2);
strikePosition = (int)(delay[0].getDelay() * position / 2.0);
}
void BandedWG :: startBowing(MY_FLOAT amplitude, MY_FLOAT rate)
@@ -209,8 +238,13 @@ void BandedWG :: stopBowing(MY_FLOAT rate)
void BandedWG :: pluck(MY_FLOAT amplitude)
{
int j;
MY_FLOAT min_len = delay[nModes-1].getDelay();
for (int i=0; i<nModes; i++)
delay[i].tick( amplitude / nModes );
for(j=0; j<(int)(delay[i].getDelay()/min_len); j++)
delay[i].tick( excitation[i]*amplitude / nModes /*/ (delay[i].getDelay()/min_len)*/);
/* strikeAmp += amplitude;*/
}
void BandedWG :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
@@ -240,11 +274,13 @@ void BandedWG :: noteOff(MY_FLOAT amplitude)
MY_FLOAT BandedWG :: tick()
{
int k;
MY_FLOAT velocityInput = 0.0;
MY_FLOAT input = 0.0;
if ( doPluck )
if ( doPluck ) {
input = 0.0;
// input = strikeAmp/nModes;
// strikeAmp = 0.0;
}
else {
if (integrationConstant == 0.0)
velocityInput = 0.0;
@@ -315,9 +351,12 @@ void BandedWG :: controlChange(int number, MY_FLOAT value)
adsr->setTarget(norm);
}
else if (number == __SK_ModWheel_) { // 1
baseGain = 0.9989999999 + (0.001 * norm );
// baseGain = 0.9989999999 + (0.001 * norm );
baseGain = 0.8999999999999999 + (0.1 * norm);
// cerr << "Yuck!" << endl;
for (int i=0; i<nModes; i++)
gains[i]=(MY_FLOAT) pow(baseGain, delay[i].getDelay()+i);
gains[i]=(MY_FLOAT) basegains[i]*baseGain;
// gains[i]=(MY_FLOAT) pow(baseGain, (int)((MY_FLOAT)delay[i].getDelay()+i));
}
else if (number == __SK_ModFrequency_) // 11
integrationConstant = norm;
@@ -330,7 +369,7 @@ void BandedWG :: controlChange(int number, MY_FLOAT value)
else trackVelocity = true;
}
else if (number == __SK_ProphesyRibbon_) // 16
this->setPreset((int) value);
this->setPreset((int) value);
else
cerr << "BandedWG: Undefined Control Number (" << number << ")!!" << endl;

View File

@@ -45,10 +45,10 @@ BeeThree :: BeeThree()
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
strcat(files[0], "sinewave.raw");
strcat(files[1], "sinewave.raw");
strcat(files[2], "sinewave.raw");
strcat(files[3], "fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );

View File

@@ -1,120 +1,120 @@
/***************************************************/
/*! \class BiQuad
\brief STK biquad (two-pole, two-zero) filter class.
This protected Filter subclass implements a
two-pole, two-zero digital filter. A method
is provided for creating a resonance in the
frequency response while maintaining a constant
filter gain.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "BiQuad.h"
#include <math.h>
BiQuad :: BiQuad() : Filter()
{
MY_FLOAT B[3] = {1.0, 0.0, 0.0};
MY_FLOAT A[3] = {1.0, 0.0, 0.0};
Filter::setCoefficients( 3, B, 3, A );
}
BiQuad :: ~BiQuad()
{
}
void BiQuad :: clear(void)
{
Filter::clear();
}
void BiQuad :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void BiQuad :: setB1(MY_FLOAT b1)
{
b[1] = b1;
}
void BiQuad :: setB2(MY_FLOAT b2)
{
b[2] = b2;
}
void BiQuad :: setA1(MY_FLOAT a1)
{
a[1] = a1;
}
void BiQuad :: setA2(MY_FLOAT a2)
{
a[2] = a2;
}
void BiQuad :: setResonance(MY_FLOAT frequency, MY_FLOAT radius, bool normalize)
{
a[2] = radius * radius;
a[1] = -2.0 * radius * cos(TWO_PI * frequency / Stk::sampleRate());
if ( normalize ) {
// Use zeros at +- 1 and normalize the filter peak gain.
b[0] = 0.5 - 0.5 * a[2];
b[1] = 0.0;
b[2] = -b[0];
}
}
void BiQuad :: setNotch(MY_FLOAT frequency, MY_FLOAT radius)
{
// This method does not attempt to normalize the filter gain.
b[2] = radius * radius;
b[1] = (MY_FLOAT) -2.0 * radius * cos(TWO_PI * (double) frequency / Stk::sampleRate());
}
void BiQuad :: setEqualGainZeroes()
{
b[0] = 1.0;
b[1] = 0.0;
b[2] = -1.0;
}
void BiQuad :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT BiQuad :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT BiQuad :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT BiQuad :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
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];
}
MY_FLOAT *BiQuad :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class BiQuad
\brief STK biquad (two-pole, two-zero) filter class.
This protected Filter subclass implements a
two-pole, two-zero digital filter. A method
is provided for creating a resonance in the
frequency response while maintaining a constant
filter gain.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "BiQuad.h"
#include <math.h>
BiQuad :: BiQuad() : Filter()
{
MY_FLOAT B[3] = {1.0, 0.0, 0.0};
MY_FLOAT A[3] = {1.0, 0.0, 0.0};
Filter::setCoefficients( 3, B, 3, A );
}
BiQuad :: ~BiQuad()
{
}
void BiQuad :: clear(void)
{
Filter::clear();
}
void BiQuad :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void BiQuad :: setB1(MY_FLOAT b1)
{
b[1] = b1;
}
void BiQuad :: setB2(MY_FLOAT b2)
{
b[2] = b2;
}
void BiQuad :: setA1(MY_FLOAT a1)
{
a[1] = a1;
}
void BiQuad :: setA2(MY_FLOAT a2)
{
a[2] = a2;
}
void BiQuad :: setResonance(MY_FLOAT frequency, MY_FLOAT radius, bool normalize)
{
a[2] = radius * radius;
a[1] = -2.0 * radius * cos(TWO_PI * frequency / Stk::sampleRate());
if ( normalize ) {
// Use zeros at +- 1 and normalize the filter peak gain.
b[0] = 0.5 - 0.5 * a[2];
b[1] = 0.0;
b[2] = -b[0];
}
}
void BiQuad :: setNotch(MY_FLOAT frequency, MY_FLOAT radius)
{
// This method does not attempt to normalize the filter gain.
b[2] = radius * radius;
b[1] = (MY_FLOAT) -2.0 * radius * cos(TWO_PI * (double) frequency / Stk::sampleRate());
}
void BiQuad :: setEqualGainZeroes()
{
b[0] = 1.0;
b[1] = 0.0;
b[2] = -1.0;
}
void BiQuad :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT BiQuad :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT BiQuad :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT BiQuad :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
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];
}
MY_FLOAT *BiQuad :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

@@ -1,158 +1,158 @@
/***************************************************/
/*! \class BlowBotl
\brief STK blown bottle instrument class.
This class implements a helmholtz resonator
(biquad filter) with a polynomial jet
excitation (a la Cook).
Control Change Numbers:
- Noise Gain = 4
- Vibrato Frequency = 11
- Vibrato Gain = 1
- Volume = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "BlowBotl.h"
#include "SKINI.msg"
#include <string.h>
#define __BOTTLE_RADIUS_ 0.999
BlowBotl :: BlowBotl()
{
jetTable = new JetTabl();
dcBlock = new PoleZero();
dcBlock->setBlockZero();
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato->setFrequency( 5.925 );
vibratoGain = 0.0;
resonator = new BiQuad();
resonator->setResonance(500.0, __BOTTLE_RADIUS_, true);
adsr = new ADSR();
adsr->setAllTimes( 0.005, 0.01, 0.8, 0.010);
noise = new Noise();
noiseGain = 20.0;
maxPressure = (MY_FLOAT) 0.0;
}
BlowBotl :: ~BlowBotl()
{
delete jetTable;
delete resonator;
delete dcBlock;
delete noise;
delete adsr;
delete vibrato;
}
void BlowBotl :: clear()
{
resonator->clear();
}
void BlowBotl :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "BlowBotl: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
resonator->setResonance( freakency, __BOTTLE_RADIUS_, true );
}
void BlowBotl :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
adsr->setAttackRate(rate);
maxPressure = amplitude;
adsr->keyOn();
}
void BlowBotl :: stopBlowing(MY_FLOAT rate)
{
adsr->setReleaseRate(rate);
adsr->keyOff();
}
void BlowBotl :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
setFrequency(frequency);
startBlowing( 1.1 + (amplitude * 0.20), amplitude * 0.02);
outputGain = amplitude + 0.001;
#if defined(_STK_DEBUG_)
cerr << "BlowBotl: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void BlowBotl :: noteOff(MY_FLOAT amplitude)
{
this->stopBlowing(amplitude * 0.02);
#if defined(_STK_DEBUG_)
cerr << "BlowBotl: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT BlowBotl :: tick()
{
MY_FLOAT breathPressure;
MY_FLOAT randPressure;
MY_FLOAT pressureDiff;
// Calculate the breath pressure (envelope + vibrato)
breathPressure = maxPressure * adsr->tick();
breathPressure += vibratoGain * vibrato->tick();
pressureDiff = breathPressure - resonator->lastOut();
randPressure = noiseGain * noise->tick();
randPressure *= breathPressure;
randPressure *= (1.0 + pressureDiff);
resonator->tick( breathPressure + randPressure - ( jetTable->tick( pressureDiff ) * pressureDiff ) );
lastOutput = 0.2 * outputGain * dcBlock->tick( pressureDiff );
return lastOutput;
}
void BlowBotl :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "BlowBotl: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "BlowBotl: Control value greater than 128.0!" << endl;
}
if (number == __SK_NoiseLevel_) // 4
noiseGain = norm * 30.0;
else if (number == __SK_ModFrequency_) // 11
vibrato->setFrequency( norm * 12.0 );
else if (number == __SK_ModWheel_) // 1
vibratoGain = norm * 0.4;
else if (number == __SK_AfterTouch_Cont_) // 128
adsr->setTarget( norm );
else
cerr << "BlowBotl: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "BlowBotl: controlChange number = " << number << ", value = " << value << endl;
#endif
}
/***************************************************/
/*! \class BlowBotl
\brief STK blown bottle instrument class.
This class implements a helmholtz resonator
(biquad filter) with a polynomial jet
excitation (a la Cook).
Control Change Numbers:
- Noise Gain = 4
- Vibrato Frequency = 11
- Vibrato Gain = 1
- Volume = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "BlowBotl.h"
#include "SKINI.msg"
#include <string.h>
#define __BOTTLE_RADIUS_ 0.999
BlowBotl :: BlowBotl()
{
jetTable = new JetTabl();
dcBlock = new PoleZero();
dcBlock->setBlockZero();
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE );
vibrato->setFrequency( 5.925 );
vibratoGain = 0.0;
resonator = new BiQuad();
resonator->setResonance(500.0, __BOTTLE_RADIUS_, true);
adsr = new ADSR();
adsr->setAllTimes( 0.005, 0.01, 0.8, 0.010);
noise = new Noise();
noiseGain = 20.0;
maxPressure = (MY_FLOAT) 0.0;
}
BlowBotl :: ~BlowBotl()
{
delete jetTable;
delete resonator;
delete dcBlock;
delete noise;
delete adsr;
delete vibrato;
}
void BlowBotl :: clear()
{
resonator->clear();
}
void BlowBotl :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "BlowBotl: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
resonator->setResonance( freakency, __BOTTLE_RADIUS_, true );
}
void BlowBotl :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
adsr->setAttackRate(rate);
maxPressure = amplitude;
adsr->keyOn();
}
void BlowBotl :: stopBlowing(MY_FLOAT rate)
{
adsr->setReleaseRate(rate);
adsr->keyOff();
}
void BlowBotl :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
setFrequency(frequency);
startBlowing( 1.1 + (amplitude * 0.20), amplitude * 0.02);
outputGain = amplitude + 0.001;
#if defined(_STK_DEBUG_)
cerr << "BlowBotl: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void BlowBotl :: noteOff(MY_FLOAT amplitude)
{
this->stopBlowing(amplitude * 0.02);
#if defined(_STK_DEBUG_)
cerr << "BlowBotl: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT BlowBotl :: tick()
{
MY_FLOAT breathPressure;
MY_FLOAT randPressure;
MY_FLOAT pressureDiff;
// Calculate the breath pressure (envelope + vibrato)
breathPressure = maxPressure * adsr->tick();
breathPressure += vibratoGain * vibrato->tick();
pressureDiff = breathPressure - resonator->lastOut();
randPressure = noiseGain * noise->tick();
randPressure *= breathPressure;
randPressure *= (1.0 + pressureDiff);
resonator->tick( breathPressure + randPressure - ( jetTable->tick( pressureDiff ) * pressureDiff ) );
lastOutput = 0.2 * outputGain * dcBlock->tick( pressureDiff );
return lastOutput;
}
void BlowBotl :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "BlowBotl: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "BlowBotl: Control value greater than 128.0!" << endl;
}
if (number == __SK_NoiseLevel_) // 4
noiseGain = norm * 30.0;
else if (number == __SK_ModFrequency_) // 11
vibrato->setFrequency( norm * 12.0 );
else if (number == __SK_ModWheel_) // 1
vibratoGain = norm * 0.4;
else if (number == __SK_AfterTouch_Cont_) // 128
adsr->setTarget( norm );
else
cerr << "BlowBotl: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "BlowBotl: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

@@ -86,7 +86,7 @@ BlowHole :: BlowHole(MY_FLOAT lowestFrequency)
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE );
vibrato->setFrequency((MY_FLOAT) 5.735);
outputGain = (MY_FLOAT) 1.0;
noiseGain = (MY_FLOAT) 0.2;
@@ -234,11 +234,11 @@ void BlowHole :: controlChange(int number, MY_FLOAT value)
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Clarinet: Control value less than zero!" << endl;
cerr << "BlowHole: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Clarinet: Control value greater than 128.0!" << endl;
cerr << "BlowHole: Control value greater than 128.0!" << endl;
}
if (number == __SK_ReedStiffness_) // 2

View File

@@ -1,63 +1,63 @@
/***************************************************/
/*! \class BowTabl
\brief STK bowed string table class.
This class implements a simple bowed string
non-linear function, as described by Smith (1986).
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "BowTabl.h"
#include <math.h>
BowTabl :: BowTabl()
{
offSet = (MY_FLOAT) 0.0;
slope = (MY_FLOAT) 0.1;
}
BowTabl :: ~BowTabl()
{
}
void BowTabl :: setOffset(MY_FLOAT aValue)
{
offSet = aValue;
}
void BowTabl :: setSlope(MY_FLOAT aValue)
{
slope = aValue;
}
MY_FLOAT BowTabl :: lastOut() const
{
return lastOutput;
}
MY_FLOAT BowTabl :: tick(MY_FLOAT input)
{
// The input represents differential string vs. bow velocity.
MY_FLOAT sample;
sample = input + offSet; // add bias to input
sample *= slope; // then scale it
lastOutput = (MY_FLOAT) fabs((double) sample) + (MY_FLOAT) 0.75;
lastOutput = (MY_FLOAT) pow( lastOutput,(MY_FLOAT) -4.0 );
// Set minimum friction to 0.0
//if (lastOutput < 0.0 ) lastOutput = 0.0;
// Set maximum friction to 1.0.
if (lastOutput > 1.0 ) lastOutput = (MY_FLOAT) 1.0;
return lastOutput;
}
MY_FLOAT *BowTabl :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class BowTabl
\brief STK bowed string table class.
This class implements a simple bowed string
non-linear function, as described by Smith (1986).
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "BowTabl.h"
#include <math.h>
BowTabl :: BowTabl()
{
offSet = (MY_FLOAT) 0.0;
slope = (MY_FLOAT) 0.1;
}
BowTabl :: ~BowTabl()
{
}
void BowTabl :: setOffset(MY_FLOAT aValue)
{
offSet = aValue;
}
void BowTabl :: setSlope(MY_FLOAT aValue)
{
slope = aValue;
}
MY_FLOAT BowTabl :: lastOut() const
{
return lastOutput;
}
MY_FLOAT BowTabl :: tick(MY_FLOAT input)
{
// The input represents differential string vs. bow velocity.
MY_FLOAT sample;
sample = input + offSet; // add bias to input
sample *= slope; // then scale it
lastOutput = (MY_FLOAT) fabs((double) sample) + (MY_FLOAT) 0.75;
lastOutput = (MY_FLOAT) pow( lastOutput,(MY_FLOAT) -4.0 );
// Set minimum friction to 0.0
//if (lastOutput < 0.0 ) lastOutput = 0.0;
// Set maximum friction to 1.0.
if (lastOutput > 1.0 ) lastOutput = (MY_FLOAT) 1.0;
return lastOutput;
}
MY_FLOAT *BowTabl :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

@@ -39,7 +39,7 @@ Bowed :: Bowed(MY_FLOAT lowestFrequency)
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE );
vibrato->setFrequency((MY_FLOAT) 6.12723);
vibratoGain = (MY_FLOAT) 0.0;
@@ -55,6 +55,9 @@ Bowed :: Bowed(MY_FLOAT lowestFrequency)
adsr->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.005,(MY_FLOAT) 0.9,(MY_FLOAT) 0.01);
betaRatio = (MY_FLOAT) 0.127236;
// Necessary to initialize internal variables.
setFrequency( 220.0 );
}
Bowed :: ~Bowed()

View File

@@ -41,13 +41,16 @@ Brass :: Brass(MY_FLOAT lowestFrequency)
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE );
vibrato->setFrequency( 6.137 );
vibratoGain = 0.0;
this->clear();
maxPressure = (MY_FLOAT) 0.0;
lipTarget = 0.0;
// Necessary to initialize variables.
setFrequency( 220.0 );
}
Brass :: ~Brass()

View File

@@ -21,9 +21,9 @@ Chorus :: Chorus(MY_FLOAT baseDelay)
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char path[128];
strcpy(path, RAWWAVE_PATH);
mods[0] = new WaveLoop( strcat(path,"rawwaves/sinewave.raw"), TRUE );
mods[0] = new WaveLoop( strcat(path,"sinewave.raw"), TRUE );
strcpy(path, RAWWAVE_PATH);
mods[1] = new WaveLoop( strcat(path,"rawwaves/sinewave.raw"), TRUE );
mods[1] = new WaveLoop( strcat(path,"sinewave.raw"), TRUE );
mods[0]->setFrequency(0.2);
mods[1]->setFrequency(0.222222);
modDepth = 0.05;

View File

@@ -1,166 +1,166 @@
/***************************************************/
/*! \class Clarinet
\brief STK clarinet physical model class.
This class implements a simple clarinet
physical model, as discussed by Smith (1986),
McIntyre, Schumacher, Woodhouse (1983), and
others.
This is a digital waveguide model, making its
use possibly subject to patents held by Stanford
University, Yamaha, and others.
Control Change Numbers:
- Reed Stiffness = 2
- Noise Gain = 4
- Vibrato Frequency = 11
- Vibrato Gain = 1
- Breath Pressure = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Clarinet.h"
#include "SKINI.msg"
#include <string.h>
Clarinet :: Clarinet(MY_FLOAT lowestFrequency)
{
length = (long) (Stk::sampleRate() / lowestFrequency + 1);
delayLine = new DelayL( (MY_FLOAT)(length / 2.0), length);
reedTable = new ReedTabl();
reedTable->setOffset((MY_FLOAT) 0.7);
reedTable->setSlope((MY_FLOAT) -0.3);
filter = new OneZero;
envelope = new Envelope;
noise = new Noise;
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char path[128];
strcpy(path, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(path,"rawwaves/sinewave.raw"), TRUE );
vibrato->setFrequency((MY_FLOAT) 5.735);
outputGain = (MY_FLOAT) 1.0;
noiseGain = (MY_FLOAT) 0.2;
vibratoGain = (MY_FLOAT) 0.1;
}
Clarinet :: ~Clarinet()
{
delete delayLine;
delete reedTable;
delete filter;
delete envelope;
delete noise;
delete vibrato;
}
void Clarinet :: clear()
{
delayLine->clear();
filter->tick((MY_FLOAT) 0.0);
}
void Clarinet :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "Clarinet: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
// Delay = length - approximate filter delay.
MY_FLOAT delay = (Stk::sampleRate() / freakency) * 0.5 - 1.5;
if (delay <= 0.0) delay = 0.3;
else if (delay > length) delay = length;
delayLine->setDelay(delay);
}
void Clarinet :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
envelope->setRate(rate);
envelope->setTarget(amplitude);
}
void Clarinet :: stopBlowing(MY_FLOAT rate)
{
envelope->setRate(rate);
envelope->setTarget((MY_FLOAT) 0.0);
}
void Clarinet :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
this->setFrequency(frequency);
this->startBlowing((MY_FLOAT) 0.55 + (amplitude * (MY_FLOAT) 0.30), amplitude * (MY_FLOAT) 0.005);
outputGain = amplitude + (MY_FLOAT) 0.001;
#if defined(_STK_DEBUG_)
cerr << "Clarinet: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Clarinet :: noteOff(MY_FLOAT amplitude)
{
this->stopBlowing(amplitude * (MY_FLOAT) 0.01);
#if defined(_STK_DEBUG_)
cerr << "Clarinet: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT Clarinet :: tick()
{
MY_FLOAT pressureDiff;
MY_FLOAT breathPressure;
// Calculate the breath pressure (envelope + noise + vibrato)
breathPressure = envelope->tick();
breathPressure += breathPressure * noiseGain * noise->tick();
breathPressure += breathPressure * vibratoGain * vibrato->tick();
// Perform commuted loss filtering.
pressureDiff = -0.95 * filter->tick(delayLine->lastOut());
// Calculate pressure difference of reflected and mouthpiece pressures.
pressureDiff = pressureDiff - breathPressure;
// Perform non-linear scattering using pressure difference in reed function.
lastOutput = delayLine->tick(breathPressure + pressureDiff * reedTable->tick(pressureDiff));
// Apply output gain.
lastOutput *= outputGain;
return lastOutput;
}
void Clarinet :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Clarinet: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Clarinet: Control value greater than 128.0!" << endl;
}
if (number == __SK_ReedStiffness_) // 2
reedTable->setSlope((MY_FLOAT) -0.44 + ( (MY_FLOAT) 0.26 * norm ));
else if (number == __SK_NoiseLevel_) // 4
noiseGain = (norm * (MY_FLOAT) 0.4);
else if (number == __SK_ModFrequency_) // 11
vibrato->setFrequency((norm * (MY_FLOAT) 12.0));
else if (number == __SK_ModWheel_) // 1
vibratoGain = (norm * (MY_FLOAT) 0.5);
else if (number == __SK_AfterTouch_Cont_) // 128
envelope->setValue(norm);
else
cerr << "Clarinet: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Clarinet: controlChange number = " << number << ", value = " << value << endl;
#endif
}
/***************************************************/
/*! \class Clarinet
\brief STK clarinet physical model class.
This class implements a simple clarinet
physical model, as discussed by Smith (1986),
McIntyre, Schumacher, Woodhouse (1983), and
others.
This is a digital waveguide model, making its
use possibly subject to patents held by Stanford
University, Yamaha, and others.
Control Change Numbers:
- Reed Stiffness = 2
- Noise Gain = 4
- Vibrato Frequency = 11
- Vibrato Gain = 1
- Breath Pressure = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Clarinet.h"
#include "SKINI.msg"
#include <string.h>
Clarinet :: Clarinet(MY_FLOAT lowestFrequency)
{
length = (long) (Stk::sampleRate() / lowestFrequency + 1);
delayLine = new DelayL( (MY_FLOAT)(length / 2.0), length);
reedTable = new ReedTabl();
reedTable->setOffset((MY_FLOAT) 0.7);
reedTable->setSlope((MY_FLOAT) -0.3);
filter = new OneZero;
envelope = new Envelope;
noise = new Noise;
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char path[128];
strcpy(path, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(path,"sinewave.raw"), TRUE );
vibrato->setFrequency((MY_FLOAT) 5.735);
outputGain = (MY_FLOAT) 1.0;
noiseGain = (MY_FLOAT) 0.2;
vibratoGain = (MY_FLOAT) 0.1;
}
Clarinet :: ~Clarinet()
{
delete delayLine;
delete reedTable;
delete filter;
delete envelope;
delete noise;
delete vibrato;
}
void Clarinet :: clear()
{
delayLine->clear();
filter->tick((MY_FLOAT) 0.0);
}
void Clarinet :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "Clarinet: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
// Delay = length - approximate filter delay.
MY_FLOAT delay = (Stk::sampleRate() / freakency) * 0.5 - 1.5;
if (delay <= 0.0) delay = 0.3;
else if (delay > length) delay = length;
delayLine->setDelay(delay);
}
void Clarinet :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
envelope->setRate(rate);
envelope->setTarget(amplitude);
}
void Clarinet :: stopBlowing(MY_FLOAT rate)
{
envelope->setRate(rate);
envelope->setTarget((MY_FLOAT) 0.0);
}
void Clarinet :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
this->setFrequency(frequency);
this->startBlowing((MY_FLOAT) 0.55 + (amplitude * (MY_FLOAT) 0.30), amplitude * (MY_FLOAT) 0.005);
outputGain = amplitude + (MY_FLOAT) 0.001;
#if defined(_STK_DEBUG_)
cerr << "Clarinet: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Clarinet :: noteOff(MY_FLOAT amplitude)
{
this->stopBlowing(amplitude * (MY_FLOAT) 0.01);
#if defined(_STK_DEBUG_)
cerr << "Clarinet: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT Clarinet :: tick()
{
MY_FLOAT pressureDiff;
MY_FLOAT breathPressure;
// Calculate the breath pressure (envelope + noise + vibrato)
breathPressure = envelope->tick();
breathPressure += breathPressure * noiseGain * noise->tick();
breathPressure += breathPressure * vibratoGain * vibrato->tick();
// Perform commuted loss filtering.
pressureDiff = -0.95 * filter->tick(delayLine->lastOut());
// Calculate pressure difference of reflected and mouthpiece pressures.
pressureDiff = pressureDiff - breathPressure;
// Perform non-linear scattering using pressure difference in reed function.
lastOutput = delayLine->tick(breathPressure + pressureDiff * reedTable->tick(pressureDiff));
// Apply output gain.
lastOutput *= outputGain;
return lastOutput;
}
void Clarinet :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Clarinet: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Clarinet: Control value greater than 128.0!" << endl;
}
if (number == __SK_ReedStiffness_) // 2
reedTable->setSlope((MY_FLOAT) -0.44 + ( (MY_FLOAT) 0.26 * norm ));
else if (number == __SK_NoiseLevel_) // 4
noiseGain = (norm * (MY_FLOAT) 0.4);
else if (number == __SK_ModFrequency_) // 11
vibrato->setFrequency((norm * (MY_FLOAT) 12.0));
else if (number == __SK_ModWheel_) // 1
vibratoGain = (norm * (MY_FLOAT) 0.5);
else if (number == __SK_AfterTouch_Cont_) // 128
envelope->setValue(norm);
else
cerr << "Clarinet: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Clarinet: controlChange number = " << number << ", value = " << value << endl;
#endif
}

0
src/Debug/.placeholder Normal file
View File

View File

@@ -1,159 +1,164 @@
/***************************************************/
/*! \class Delay
\brief STK non-interpolating delay line class.
This protected Filter subclass implements
a non-interpolating digital delay-line.
A fixed maximum length of 4095 and a delay
of zero is set using the default constructor.
Alternatively, the delay and maximum length
can be set during instantiation with an
overloaded constructor.
A non-interpolating delay line is typically
used in fixed delay-length applications, such
as for reverberation.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Delay.h"
#include <iostream.h>
Delay :: Delay()
{
// Default max delay length set to 4095.
length = 4096;
delete [] inputs;
inputs = new MY_FLOAT[length];
this->clear();
inPoint = 0;
outPoint = 0;
delay = 0;
}
Delay :: Delay(long theDelay, long maxDelay)
{
// Writing before reading allows delays from 0 to length-1.
// If we want to allow a delay of maxDelay, we need a
// delay-line of length = maxDelay+1.
length = maxDelay+1;
// We need to delete the previously allocated inputs.
delete [] inputs;
inputs = new MY_FLOAT[length];
this->clear();
inPoint = 0;
this->setDelay(theDelay);
}
Delay :: ~Delay()
{
}
void Delay :: clear(void)
{
long i;
for (i=0;i<length;i++) inputs[i] = 0.0;
outputs[0] = 0.0;
}
void Delay :: setDelay(long theDelay)
{
if (theDelay > length-1) { // The value is too big.
cerr << "Delay: setDelay(" << theDelay << ") too big!" << endl;
// Force delay to maxLength.
outPoint = inPoint + 1;
delay = length - 1;
}
else if (theDelay < 0 ) {
cerr << "Delay: setDelay(" << theDelay << ") less than zero!" << endl;
outPoint = inPoint;
delay = 0;
}
else {
outPoint = inPoint - (long) theDelay; // read chases write
delay = theDelay;
}
while (outPoint < 0) outPoint += length; // modulo maximum length
}
long Delay :: getDelay(void) const
{
return (long)delay;
}
MY_FLOAT Delay :: energy(void) const
{
int i;
register MY_FLOAT e = 0;
if (inPoint >= outPoint) {
for (i=outPoint; i<inPoint; i++) {
register MY_FLOAT t = inputs[i];
e += t*t;
}
} else {
for (i=outPoint; i<length; i++) {
register MY_FLOAT t = inputs[i];
e += t*t;
}
for (i=0; i<inPoint; i++) {
register MY_FLOAT t = inputs[i];
e += t*t;
}
}
return e;
}
MY_FLOAT Delay :: contentsAt(long tapDelay) const
{
long i = tapDelay;
if (i < 1) {
cerr << "Delay: contentsAt(" << tapDelay << ") too small!" << endl;
i=1;
}
else if (i > delay) {
cerr << "Delay: contentsAt(" << tapDelay << ") too big!" << endl;
i = (long) delay;
}
long tap = inPoint - tapDelay;
if (tap < 0) // Check for wraparound.
tap += length;
return inputs[tap];
}
MY_FLOAT Delay :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT Delay :: tick(MY_FLOAT sample)
{
inputs[inPoint++] = sample;
// Check for end condition
if (inPoint == length)
inPoint -= length;
// Read out next value
outputs[0] = inputs[outPoint++];
if (outPoint>=length)
outPoint -= length;
return outputs[0];
}
MY_FLOAT *Delay :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class Delay
\brief STK non-interpolating delay line class.
This protected Filter subclass implements
a non-interpolating digital delay-line.
A fixed maximum length of 4095 and a delay
of zero is set using the default constructor.
Alternatively, the delay and maximum length
can be set during instantiation with an
overloaded constructor.
A non-interpolating delay line is typically
used in fixed delay-length applications, such
as for reverberation.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Delay.h"
#include <iostream.h>
Delay :: Delay()
{
// Default max delay length set to 4095.
length = 4096;
delete [] inputs;
inputs = new MY_FLOAT[length];
this->clear();
inPoint = 0;
outPoint = 0;
delay = 0;
}
Delay :: Delay(long theDelay, long maxDelay)
{
// Writing before reading allows delays from 0 to length-1.
// If we want to allow a delay of maxDelay, we need a
// delay-line of length = maxDelay+1.
length = maxDelay+1;
// We need to delete the previously allocated inputs.
delete [] inputs;
inputs = new MY_FLOAT[length];
this->clear();
inPoint = 0;
this->setDelay(theDelay);
}
Delay :: ~Delay()
{
}
void Delay :: clear(void)
{
long i;
for (i=0;i<length;i++) inputs[i] = 0.0;
outputs[0] = 0.0;
}
void Delay :: setDelay(long theDelay)
{
if (theDelay > length-1) { // The value is too big.
cerr << "Delay: setDelay(" << theDelay << ") too big!" << endl;
// Force delay to maxLength.
outPoint = inPoint + 1;
delay = length - 1;
}
else if (theDelay < 0 ) {
cerr << "Delay: setDelay(" << theDelay << ") less than zero!" << endl;
outPoint = inPoint;
delay = 0;
}
else {
outPoint = inPoint - (long) theDelay; // read chases write
delay = theDelay;
}
while (outPoint < 0) outPoint += length; // modulo maximum length
}
long Delay :: getDelay(void) const
{
return (long)delay;
}
MY_FLOAT Delay :: energy(void) const
{
int i;
register MY_FLOAT e = 0;
if (inPoint >= outPoint) {
for (i=outPoint; i<inPoint; i++) {
register MY_FLOAT t = inputs[i];
e += t*t;
}
} else {
for (i=outPoint; i<length; i++) {
register MY_FLOAT t = inputs[i];
e += t*t;
}
for (i=0; i<inPoint; i++) {
register MY_FLOAT t = inputs[i];
e += t*t;
}
}
return e;
}
MY_FLOAT Delay :: contentsAt(long tapDelay) const
{
long i = tapDelay;
if (i < 0) {
cerr << "Delay: contentsAt(" << tapDelay << ") too small!" << endl;
i = 0;
}
else if (i > delay) {
cerr << "Delay: contentsAt(" << tapDelay << ") too big!" << endl;
i = (long) delay;
}
long tap = inPoint - i;
if (tap < 0) // Check for wraparound.
tap += length;
return inputs[tap];
}
MY_FLOAT Delay :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT Delay :: nextOut(void) const
{
return inputs[outPoint];
}
MY_FLOAT Delay :: tick(MY_FLOAT sample)
{
inputs[inPoint++] = sample;
// Check for end condition
if (inPoint == length)
inPoint -= length;
// Read out next value
outputs[0] = inputs[outPoint++];
if (outPoint>=length)
outPoint -= length;
return outputs[0];
}
MY_FLOAT *Delay :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

@@ -1,117 +1,129 @@
/***************************************************/
/*! \class DelayA
\brief STK allpass interpolating delay line class.
This Delay subclass implements a fractional-
length digital delay-line using a first-order
allpass filter. A fixed maximum length
of 4095 and a delay of 0.5 is set using the
default constructor. Alternatively, the
delay and maximum length can be set during
instantiation with an overloaded constructor.
An allpass filter has unity magnitude gain but
variable phase delay properties, making it useful
in achieving fractional delays without affecting
a signal's frequency magnitude response. In
order to achieve a maximally flat phase delay
response, the minimum delay possible in this
implementation is limited to a value of 0.5.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "DelayA.h"
#include <iostream.h>
DelayA :: DelayA()
{
this->setDelay( 0.5 );
apInput = 0.0;
}
DelayA :: DelayA(MY_FLOAT theDelay, long maxDelay)
{
// Writing before reading allows delays from 0 to length-1.
length = maxDelay+1;
if ( length > 4096 ) {
// We need to delete the previously allocated inputs.
delete [] inputs;
inputs = new MY_FLOAT[length];
this->clear();
}
inPoint = 0;
this->setDelay(theDelay);
}
DelayA :: ~DelayA()
{
}
void DelayA :: clear()
{
Delay::clear();
apInput = 0.0;
}
void DelayA :: setDelay(MY_FLOAT theDelay)
{
MY_FLOAT outPointer;
if (theDelay > length-1) {
cerr << "DelayA: setDelay(" << theDelay << ") too big!" << endl;
// Force delay to maxLength
outPointer = inPoint + 1.0;
delay = length - 1;
}
else if (theDelay < 0.5) {
cerr << "DelayA: setDelay(" << theDelay << ") less than 0.5 not possible!" << endl;
outPointer = inPoint + 0.4999999999;
delay = 0.5;
}
else {
outPointer = inPoint - theDelay + 1.0; // outPoint chases inpoint
delay = theDelay;
}
if (outPointer < 0)
outPointer += length; // modulo maximum length
outPoint = (long) outPointer; // integer part
alpha = 1.0 + outPoint - outPointer; // fractional part
if (alpha < 0.5) {
// The optimal range for alpha is about 0.5 - 1.5 in order to
// achieve the flattest phase delay response.
outPoint += 1;
if (outPoint >= length) outPoint -= length;
alpha += (MY_FLOAT) 1.0;
}
coeff = ((MY_FLOAT) 1.0 - alpha) /
((MY_FLOAT) 1.0 + alpha); // coefficient for all pass
}
MY_FLOAT DelayA :: tick(MY_FLOAT sample)
{
inputs[inPoint++] = sample;
// Increment input pointer modulo length.
if (inPoint == length)
inPoint -= length;
// Take delay-line output and increment modulo length.
MY_FLOAT temp = inputs[outPoint++];
if (outPoint == length)
outPoint -= length;
// Do allpass interpolation delay.
outputs[0] = -coeff * outputs[0];
outputs[0] += apInput + (coeff * temp);
apInput = temp;
return outputs[0];
}
/***************************************************/
/*! \class DelayA
\brief STK allpass interpolating delay line class.
This Delay subclass implements a fractional-
length digital delay-line using a first-order
allpass filter. A fixed maximum length
of 4095 and a delay of 0.5 is set using the
default constructor. Alternatively, the
delay and maximum length can be set during
instantiation with an overloaded constructor.
An allpass filter has unity magnitude gain but
variable phase delay properties, making it useful
in achieving fractional delays without affecting
a signal's frequency magnitude response. In
order to achieve a maximally flat phase delay
response, the minimum delay possible in this
implementation is limited to a value of 0.5.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "DelayA.h"
#include <iostream.h>
DelayA :: DelayA()
{
this->setDelay( 0.5 );
apInput = 0.0;
doNextOut = true;
}
DelayA :: DelayA(MY_FLOAT theDelay, long maxDelay)
{
// Writing before reading allows delays from 0 to length-1.
length = maxDelay+1;
if ( length > 4096 ) {
// We need to delete the previously allocated inputs.
delete [] inputs;
inputs = new MY_FLOAT[length];
this->clear();
}
inPoint = 0;
this->setDelay(theDelay);
doNextOut = true;
}
DelayA :: ~DelayA()
{
}
void DelayA :: clear()
{
Delay::clear();
apInput = 0.0;
}
void DelayA :: setDelay(MY_FLOAT theDelay)
{
MY_FLOAT outPointer;
if (theDelay > length-1) {
cerr << "DelayA: setDelay(" << theDelay << ") too big!" << endl;
// Force delay to maxLength
outPointer = inPoint + 1.0;
delay = length - 1;
}
else if (theDelay < 0.5) {
cerr << "DelayA: setDelay(" << theDelay << ") less than 0.5 not possible!" << endl;
outPointer = inPoint + 0.4999999999;
delay = 0.5;
}
else {
outPointer = inPoint - theDelay + 1.0; // outPoint chases inpoint
delay = theDelay;
}
if (outPointer < 0)
outPointer += length; // modulo maximum length
outPoint = (long) outPointer; // integer part
alpha = 1.0 + outPoint - outPointer; // fractional part
if (alpha < 0.5) {
// The optimal range for alpha is about 0.5 - 1.5 in order to
// achieve the flattest phase delay response.
outPoint += 1;
if (outPoint >= length) outPoint -= length;
alpha += (MY_FLOAT) 1.0;
}
coeff = ((MY_FLOAT) 1.0 - alpha) /
((MY_FLOAT) 1.0 + alpha); // coefficient for all pass
}
MY_FLOAT DelayA :: nextOut(void)
{
if ( doNextOut ) {
// Do allpass interpolation delay.
nextOutput = -coeff * outputs[0];
nextOutput += apInput + (coeff * inputs[outPoint]);
doNextOut = false;
}
return nextOutput;
}
MY_FLOAT DelayA :: tick(MY_FLOAT sample)
{
inputs[inPoint++] = sample;
// Increment input pointer modulo length.
if (inPoint == length)
inPoint -= length;
outputs[0] = nextOut();
doNextOut = true;
// Save the allpass input and increment modulo length.
apInput = inputs[outPoint++];
if (outPoint == length)
outPoint -= length;
return outputs[0];
}

View File

@@ -1,107 +1,119 @@
/***************************************************/
/*! \class DelayL
\brief STK linear interpolating delay line class.
This Delay subclass implements a fractional-
length digital delay-line using first-order
linear interpolation. A fixed maximum length
of 4095 and a delay of zero is set using the
default constructor. Alternatively, the
delay and maximum length can be set during
instantiation with an overloaded constructor.
Linear interpolation is an efficient technique
for achieving fractional delay lengths, though
it does introduce high-frequency signal
attenuation to varying degrees depending on the
fractional delay setting. The use of higher
order Lagrange interpolators can typically
improve (minimize) this attenuation characteristic.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "DelayL.h"
#include <iostream.h>
DelayL :: DelayL()
{
}
DelayL :: DelayL(MY_FLOAT theDelay, long maxDelay)
{
// Writing before reading allows delays from 0 to length-1.
length = maxDelay+1;
if ( length > 4096 ) {
// We need to delete the previously allocated inputs.
delete [] inputs;
inputs = new MY_FLOAT[length];
this->clear();
}
inPoint = 0;
this->setDelay(theDelay);
}
DelayL :: ~DelayL()
{
}
void DelayL :: setDelay(MY_FLOAT theDelay)
{
MY_FLOAT outPointer;
if (theDelay > length-1) {
cerr << "DelayL: setDelay(" << theDelay << ") too big!" << endl;
// Force delay to maxLength
outPointer = inPoint + 1.0;
delay = length - 1;
}
else if (theDelay < 0 ) {
cerr << "DelayL: setDelay(" << theDelay << ") less than zero!" << endl;
outPointer = inPoint;
delay = 0;
}
else {
outPointer = inPoint - theDelay; // read chases write
delay = theDelay;
}
while (outPointer < 0)
outPointer += length; // modulo maximum length
outPoint = (long) outPointer; // integer part
alpha = outPointer - outPoint; // fractional part
omAlpha = (MY_FLOAT) 1.0 - alpha;
}
MY_FLOAT DelayL :: getDelay(void) const
{
return delay;
}
MY_FLOAT DelayL :: tick(MY_FLOAT sample)
{
inputs[inPoint++] = sample;
// Check for end condition
if (inPoint == length)
inPoint -= length;
// First 1/2 of interpolation
outputs[0] = inputs[outPoint++] * omAlpha;
// Check for end condition
if (outPoint < length) {
// Second 1/2 of interpolation
outputs[0] += inputs[outPoint] * alpha;
}
else { // if at end ...
// Second 1/2 of interpolation
outputs[0] += inputs[0] * alpha;
outPoint -= length;
}
return outputs[0];
}
/***************************************************/
/*! \class DelayL
\brief STK linear interpolating delay line class.
This Delay subclass implements a fractional-
length digital delay-line using first-order
linear interpolation. A fixed maximum length
of 4095 and a delay of zero is set using the
default constructor. Alternatively, the
delay and maximum length can be set during
instantiation with an overloaded constructor.
Linear interpolation is an efficient technique
for achieving fractional delay lengths, though
it does introduce high-frequency signal
attenuation to varying degrees depending on the
fractional delay setting. The use of higher
order Lagrange interpolators can typically
improve (minimize) this attenuation characteristic.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "DelayL.h"
#include <iostream.h>
DelayL :: DelayL()
{
doNextOut = true;
}
DelayL :: DelayL(MY_FLOAT theDelay, long maxDelay)
{
// Writing before reading allows delays from 0 to length-1.
length = maxDelay+1;
if ( length > 4096 ) {
// We need to delete the previously allocated inputs.
delete [] inputs;
inputs = new MY_FLOAT[length];
this->clear();
}
inPoint = 0;
this->setDelay(theDelay);
doNextOut = true;
}
DelayL :: ~DelayL()
{
}
void DelayL :: setDelay(MY_FLOAT theDelay)
{
MY_FLOAT outPointer;
if (theDelay > length-1) {
cerr << "DelayL: setDelay(" << theDelay << ") too big!" << endl;
// Force delay to maxLength
outPointer = inPoint + 1.0;
delay = length - 1;
}
else if (theDelay < 0 ) {
cerr << "DelayL: setDelay(" << theDelay << ") less than zero!" << endl;
outPointer = inPoint;
delay = 0;
}
else {
outPointer = inPoint - theDelay; // read chases write
delay = theDelay;
}
while (outPointer < 0)
outPointer += length; // modulo maximum length
outPoint = (long) outPointer; // integer part
alpha = outPointer - outPoint; // fractional part
omAlpha = (MY_FLOAT) 1.0 - alpha;
}
MY_FLOAT DelayL :: getDelay(void) const
{
return delay;
}
MY_FLOAT DelayL :: nextOut(void)
{
if ( doNextOut ) {
// First 1/2 of interpolation
nextOutput = inputs[outPoint] * omAlpha;
// Second 1/2 of interpolation
if (outPoint+1 < length)
nextOutput += inputs[outPoint+1] * alpha;
else
nextOutput += inputs[0] * alpha;
doNextOut = false;
}
return nextOutput;
}
MY_FLOAT DelayL :: tick(MY_FLOAT sample)
{
inputs[inPoint++] = sample;
// Increment input pointer modulo length.
if (inPoint == length)
inPoint -= length;
outputs[0] = nextOut();
doNextOut = true;
// Increment output pointer modulo length.
if (++outPoint >= length)
outPoint -= length;
return outputs[0];
}

View File

@@ -125,7 +125,6 @@ void Drummer :: noteOn(MY_FLOAT instrument, MY_FLOAT amplitude)
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char path[128];
strcpy(path, RAWWAVE_PATH);
strcat(path, "rawwaves/");
strcat(path, waveNames[genMIDIMap[noteNum]]);
waves[nSounding-1] = new WvIn(path, TRUE);
if (Stk::sampleRate() != 22050.0)

View File

@@ -1,114 +1,114 @@
/***************************************************/
/*! \class Envelope
\brief STK envelope base class.
This class implements a simple envelope
generator which is capable of ramping to
a target value by a specified \e rate.
It also responds to simple \e keyOn and
\e keyOff messages, ramping to 1.0 on
keyOn and to 0.0 on keyOff.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Envelope.h"
#include <stdio.h>
Envelope :: Envelope(void) : Stk()
{
target = (MY_FLOAT) 0.0;
value = (MY_FLOAT) 0.0;
rate = (MY_FLOAT) 0.001;
state = 0;
}
Envelope :: ~Envelope(void)
{
}
void Envelope :: keyOn(void)
{
target = (MY_FLOAT) 1.0;
if (value != target) state = 1;
}
void Envelope :: keyOff(void)
{
target = (MY_FLOAT) 0.0;
if (value != target) state = 1;
}
void Envelope :: setRate(MY_FLOAT aRate)
{
if (aRate < 0.0) {
printf("Envelope: negative rates not allowed ... correcting!\n");
rate = -aRate;
}
else
rate = aRate;
}
void Envelope :: setTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("Envelope: negative times not allowed ... correcting!\n");
rate = 1.0 / (-aTime * Stk::sampleRate());
}
else
rate = 1.0 / (aTime * Stk::sampleRate());
}
void Envelope :: setTarget(MY_FLOAT aTarget)
{
target = aTarget;
if (value != target) state = 1;
}
void Envelope :: setValue(MY_FLOAT aValue)
{
state = 0;
target = aValue;
value = aValue;
}
int Envelope :: getState(void) const
{
return state;
}
MY_FLOAT Envelope :: tick(void)
{
if (state) {
if (target > value) {
value += rate;
if (value >= target) {
value = target;
state = 0;
}
}
else {
value -= rate;
if (value <= target) {
value = target;
state = 0;
}
}
}
return value;
}
MY_FLOAT *Envelope :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick();
return vector;
}
MY_FLOAT Envelope :: lastOut(void) const
{
return value;
}
/***************************************************/
/*! \class Envelope
\brief STK envelope base class.
This class implements a simple envelope
generator which is capable of ramping to
a target value by a specified \e rate.
It also responds to simple \e keyOn and
\e keyOff messages, ramping to 1.0 on
keyOn and to 0.0 on keyOff.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Envelope.h"
#include <stdio.h>
Envelope :: Envelope(void) : Stk()
{
target = (MY_FLOAT) 0.0;
value = (MY_FLOAT) 0.0;
rate = (MY_FLOAT) 0.001;
state = 0;
}
Envelope :: ~Envelope(void)
{
}
void Envelope :: keyOn(void)
{
target = (MY_FLOAT) 1.0;
if (value != target) state = 1;
}
void Envelope :: keyOff(void)
{
target = (MY_FLOAT) 0.0;
if (value != target) state = 1;
}
void Envelope :: setRate(MY_FLOAT aRate)
{
if (aRate < 0.0) {
printf("Envelope: negative rates not allowed ... correcting!\n");
rate = -aRate;
}
else
rate = aRate;
}
void Envelope :: setTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("Envelope: negative times not allowed ... correcting!\n");
rate = 1.0 / (-aTime * Stk::sampleRate());
}
else
rate = 1.0 / (aTime * Stk::sampleRate());
}
void Envelope :: setTarget(MY_FLOAT aTarget)
{
target = aTarget;
if (value != target) state = 1;
}
void Envelope :: setValue(MY_FLOAT aValue)
{
state = 0;
target = aValue;
value = aValue;
}
int Envelope :: getState(void) const
{
return state;
}
MY_FLOAT Envelope :: tick(void)
{
if (state) {
if (target > value) {
value += rate;
if (value >= target) {
value = target;
state = 0;
}
}
else {
value -= rate;
if (value <= target) {
value = target;
state = 0;
}
}
}
return value;
}
MY_FLOAT *Envelope :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick();
return vector;
}
MY_FLOAT Envelope :: lastOut(void) const
{
return value;
}

View File

@@ -44,7 +44,7 @@ FM :: FM(int operators)
// Concatenate the STK RAWWAVE_PATH to the rawwave file.
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE );
vibrato->setFrequency(6.0);
int i;

View File

@@ -32,7 +32,7 @@
#include "FMVoices.h"
#include "SKINI.msg"
#include "phontabl.tbl"
#include "Phonemes.h"
#include <string.h>
FMVoices :: FMVoices()
@@ -45,10 +45,10 @@ FMVoices :: FMVoices()
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
strcat(files[0], "sinewave.raw");
strcat(files[1], "sinewave.raw");
strcat(files[2], "sinewave.raw");
strcat(files[3], "fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );
@@ -82,40 +82,37 @@ FMVoices :: ~FMVoices()
{
}
extern double phonGains[32][2];
extern double phonParams[32][4][3];
extern char phonemes[32][4];
void FMVoices :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT temp, temp2 = 0.0;
int tempi, tempi2 = 0;
int tempi = 0;
unsigned int i = 0;
if (currentVowel < 32) {
tempi2 = currentVowel;
i = currentVowel;
temp2 = (MY_FLOAT) 0.9;
}
else if (currentVowel < 64) {
tempi2 = currentVowel - 32;
i = currentVowel - 32;
temp2 = (MY_FLOAT) 1.0;
}
else if (currentVowel < 96) {
tempi2 = currentVowel - 64;
i = currentVowel - 64;
temp2 = (MY_FLOAT) 1.1;
}
else if (currentVowel <= 128) {
tempi2 = currentVowel - 96;
i = currentVowel - 96;
temp2 = (MY_FLOAT) 1.2;
}
baseFrequency = frequency;
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][0][0] / baseFrequency) + 0.5;
temp = (temp2 * Phonemes::formantFrequency(i, 0) / baseFrequency) + 0.5;
tempi = (int) temp;
this->setRatio(0,(MY_FLOAT) tempi);
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][1][0] / baseFrequency) + 0.5;
temp = (temp2 * Phonemes::formantFrequency(i, 1) / baseFrequency) + 0.5;
tempi = (int) temp;
this->setRatio(1,(MY_FLOAT) tempi);
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][2][0] / baseFrequency) + 0.5;
temp = (temp2 * Phonemes::formantFrequency(i, 2) / baseFrequency) + 0.5;
tempi = (int) temp;
this->setRatio(2, (MY_FLOAT) tempi);
gains[0] = 1.0;
@@ -174,7 +171,7 @@ void FMVoices :: controlChange(int number, MY_FLOAT value)
if (number == __SK_Breath_) // 2
gains[3] = __FM_gains[(int) ( norm * 100.0 )];
gains[3] = __FM_gains[(int) ( norm * 99.9 )];
else if (number == __SK_FootControl_) { // 4
currentVowel = (int) (norm * 128.0);
this->setFrequency(baseFrequency);

View File

@@ -1,244 +1,244 @@
/***************************************************/
/*! \class Filter
\brief STK filter class.
This class implements a generic structure which
can be used to create a wide range of filters.
It can function independently or be subclassed
to provide more specific controls based on a
particular filter type.
In particular, this class implements the standard
difference equation:
a[0]*y[n] = b[0]*x[n] + ... + b[nb]*x[n-nb] -
a[1]*y[n-1] - ... - a[na]*y[n-na]
If a[0] is not equal to 1, the filter coeffcients
are normalized by a[0].
The \e gain parameter is applied at the filter
input and does not affect the coefficient values.
The default gain value is 1.0. This structure
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 - 2002.
*/
/***************************************************/
#include "Filter.h"
#include <stdio.h>
Filter :: Filter()
{
// The default constructor should setup for pass-through.
gain = 1.0;
nB = 1;
nA = 1;
b = new MY_FLOAT[nB];
b[0] = 1.0;
a = new MY_FLOAT[nA];
a[0] = 1.0;
inputs = new MY_FLOAT[nB];
outputs = new MY_FLOAT[nA];
this->clear();
}
Filter :: Filter(int nb, MY_FLOAT *bCoefficients, int na, MY_FLOAT *aCoefficients)
{
char message[256];
// Check the arguments.
if ( nb < 1 || na < 1 ) {
sprintf(message, "Filter: nb (%d) and na (%d) must be >= 1!", nb, na);
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if ( aCoefficients[0] == 0.0 ) {
sprintf(message, "Filter: a[0] coefficient cannot == 0!");
handleError( message, StkError::FUNCTION_ARGUMENT );
}
gain = 1.0;
nB = nb;
nA = na;
b = new MY_FLOAT[nB];
a = new MY_FLOAT[nA];
inputs = new MY_FLOAT[nB];
outputs = new MY_FLOAT[nA];
this->clear();
this->setCoefficients(nB, bCoefficients, nA, aCoefficients);
}
Filter :: ~Filter()
{
delete [] b;
delete [] a;
delete [] inputs;
delete [] outputs;
}
void Filter :: clear(void)
{
int i;
for (i=0; i<nB; i++)
inputs[i] = 0.0;
for (i=0; i<nA; i++)
outputs[i] = 0.0;
}
void Filter :: setCoefficients(int nb, MY_FLOAT *bCoefficients, int na, MY_FLOAT *aCoefficients)
{
int i;
char message[256];
// Check the arguments.
if ( nb < 1 || na < 1 ) {
sprintf(message, "Filter: nb (%d) and na (%d) must be >= 1!", nb, na);
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if ( aCoefficients[0] == 0.0 ) {
sprintf(message, "Filter: a[0] coefficient cannot == 0!");
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if (nb != nB) {
delete [] b;
delete [] inputs;
nB = nb;
b = new MY_FLOAT[nB];
inputs = new MY_FLOAT[nB];
for (i=0; i<nB; i++) inputs[i] = 0.0;
}
if (na != nA) {
delete [] a;
delete [] outputs;
nA = na;
a = new MY_FLOAT[nA];
outputs = new MY_FLOAT[nA];
for (i=0; i<nA; i++) outputs[i] = 0.0;
}
for (i=0; i<nB; i++)
b[i] = bCoefficients[i];
for (i=0; i<nA; i++)
a[i] = aCoefficients[i];
// scale coefficients by a[0] if necessary
if (a[0] != 1.0) {
for (i=0; i<nB; i++)
b[i] /= a[0];
for (i=0; i<nA; i++)
a[i] /= a[0];
}
}
void Filter :: setNumerator(int nb, MY_FLOAT *bCoefficients)
{
int i;
char message[256];
// Check the arguments.
if ( nb < 1 ) {
sprintf(message, "Filter: nb (%d) must be >= 1!", nb);
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if (nb != nB) {
delete [] b;
delete [] inputs;
nB = nb;
b = new MY_FLOAT[nB];
inputs = new MY_FLOAT[nB];
for (i=0; i<nB; i++) inputs[i] = 0.0;
}
for (i=0; i<nB; i++)
b[i] = bCoefficients[i];
}
void Filter :: setDenominator(int na, MY_FLOAT *aCoefficients)
{
int i;
char message[256];
// Check the arguments.
if ( na < 1 ) {
sprintf(message, "Filter: na (%d) must be >= 1!", na);
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if ( aCoefficients[0] == 0.0 ) {
sprintf(message, "Filter: a[0] coefficient cannot == 0!");
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if (na != nA) {
delete [] a;
delete [] outputs;
nA = na;
a = new MY_FLOAT[nA];
outputs = new MY_FLOAT[nA];
for (i=0; i<nA; i++) outputs[i] = 0.0;
}
for (i=0; i<nA; i++)
a[i] = aCoefficients[i];
// scale coefficients by a[0] if necessary
if (a[0] != 1.0) {
for (i=0; i<nB; i++)
b[i] /= a[0];
for (i=0; i<nA; i++)
a[i] /= a[0];
}
}
void Filter :: setGain(MY_FLOAT theGain)
{
gain = theGain;
}
MY_FLOAT Filter :: getGain(void) const
{
return gain;
}
MY_FLOAT Filter :: lastOut(void) const
{
return outputs[0];
}
MY_FLOAT Filter :: tick(MY_FLOAT sample)
{
int i;
outputs[0] = 0.0;
inputs[0] = gain * sample;
for (i=nB-1; i>0; i--) {
outputs[0] += b[i] * inputs[i];
inputs[i] = inputs[i-1];
}
outputs[0] += b[0] * inputs[0];
for (i=nA-1; i>0; i--) {
outputs[0] += -a[i] * outputs[i];
outputs[i] = outputs[i-1];
}
return outputs[0];
}
MY_FLOAT *Filter :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class Filter
\brief STK filter class.
This class implements a generic structure which
can be used to create a wide range of filters.
It can function independently or be subclassed
to provide more specific controls based on a
particular filter type.
In particular, this class implements the standard
difference equation:
a[0]*y[n] = b[0]*x[n] + ... + b[nb]*x[n-nb] -
a[1]*y[n-1] - ... - a[na]*y[n-na]
If a[0] is not equal to 1, the filter coeffcients
are normalized by a[0].
The \e gain parameter is applied at the filter
input and does not affect the coefficient values.
The default gain value is 1.0. This structure
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 - 2002.
*/
/***************************************************/
#include "Filter.h"
#include <stdio.h>
Filter :: Filter()
{
// The default constructor should setup for pass-through.
gain = 1.0;
nB = 1;
nA = 1;
b = new MY_FLOAT[nB];
b[0] = 1.0;
a = new MY_FLOAT[nA];
a[0] = 1.0;
inputs = new MY_FLOAT[nB];
outputs = new MY_FLOAT[nA];
this->clear();
}
Filter :: Filter(int nb, MY_FLOAT *bCoefficients, int na, MY_FLOAT *aCoefficients)
{
char message[256];
// Check the arguments.
if ( nb < 1 || na < 1 ) {
sprintf(message, "Filter: nb (%d) and na (%d) must be >= 1!", nb, na);
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if ( aCoefficients[0] == 0.0 ) {
sprintf(message, "Filter: a[0] coefficient cannot == 0!");
handleError( message, StkError::FUNCTION_ARGUMENT );
}
gain = 1.0;
nB = nb;
nA = na;
b = new MY_FLOAT[nB];
a = new MY_FLOAT[nA];
inputs = new MY_FLOAT[nB];
outputs = new MY_FLOAT[nA];
this->clear();
this->setCoefficients(nB, bCoefficients, nA, aCoefficients);
}
Filter :: ~Filter()
{
delete [] b;
delete [] a;
delete [] inputs;
delete [] outputs;
}
void Filter :: clear(void)
{
int i;
for (i=0; i<nB; i++)
inputs[i] = 0.0;
for (i=0; i<nA; i++)
outputs[i] = 0.0;
}
void Filter :: setCoefficients(int nb, MY_FLOAT *bCoefficients, int na, MY_FLOAT *aCoefficients)
{
int i;
char message[256];
// Check the arguments.
if ( nb < 1 || na < 1 ) {
sprintf(message, "Filter: nb (%d) and na (%d) must be >= 1!", nb, na);
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if ( aCoefficients[0] == 0.0 ) {
sprintf(message, "Filter: a[0] coefficient cannot == 0!");
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if (nb != nB) {
delete [] b;
delete [] inputs;
nB = nb;
b = new MY_FLOAT[nB];
inputs = new MY_FLOAT[nB];
for (i=0; i<nB; i++) inputs[i] = 0.0;
}
if (na != nA) {
delete [] a;
delete [] outputs;
nA = na;
a = new MY_FLOAT[nA];
outputs = new MY_FLOAT[nA];
for (i=0; i<nA; i++) outputs[i] = 0.0;
}
for (i=0; i<nB; i++)
b[i] = bCoefficients[i];
for (i=0; i<nA; i++)
a[i] = aCoefficients[i];
// scale coefficients by a[0] if necessary
if (a[0] != 1.0) {
for (i=0; i<nB; i++)
b[i] /= a[0];
for (i=0; i<nA; i++)
a[i] /= a[0];
}
}
void Filter :: setNumerator(int nb, MY_FLOAT *bCoefficients)
{
int i;
char message[256];
// Check the arguments.
if ( nb < 1 ) {
sprintf(message, "Filter: nb (%d) must be >= 1!", nb);
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if (nb != nB) {
delete [] b;
delete [] inputs;
nB = nb;
b = new MY_FLOAT[nB];
inputs = new MY_FLOAT[nB];
for (i=0; i<nB; i++) inputs[i] = 0.0;
}
for (i=0; i<nB; i++)
b[i] = bCoefficients[i];
}
void Filter :: setDenominator(int na, MY_FLOAT *aCoefficients)
{
int i;
char message[256];
// Check the arguments.
if ( na < 1 ) {
sprintf(message, "Filter: na (%d) must be >= 1!", na);
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if ( aCoefficients[0] == 0.0 ) {
sprintf(message, "Filter: a[0] coefficient cannot == 0!");
handleError( message, StkError::FUNCTION_ARGUMENT );
}
if (na != nA) {
delete [] a;
delete [] outputs;
nA = na;
a = new MY_FLOAT[nA];
outputs = new MY_FLOAT[nA];
for (i=0; i<nA; i++) outputs[i] = 0.0;
}
for (i=0; i<nA; i++)
a[i] = aCoefficients[i];
// scale coefficients by a[0] if necessary
if (a[0] != 1.0) {
for (i=0; i<nB; i++)
b[i] /= a[0];
for (i=0; i<nA; i++)
a[i] /= a[0];
}
}
void Filter :: setGain(MY_FLOAT theGain)
{
gain = theGain;
}
MY_FLOAT Filter :: getGain(void) const
{
return gain;
}
MY_FLOAT Filter :: lastOut(void) const
{
return outputs[0];
}
MY_FLOAT Filter :: tick(MY_FLOAT sample)
{
int i;
outputs[0] = 0.0;
inputs[0] = gain * sample;
for (i=nB-1; i>0; i--) {
outputs[0] += b[i] * inputs[i];
inputs[i] = inputs[i-1];
}
outputs[0] += b[0] * inputs[0];
for (i=nA-1; i>0; i--) {
outputs[0] += -a[i] * outputs[i];
outputs[i] = outputs[i-1];
}
return outputs[0];
}
MY_FLOAT *Filter :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

@@ -42,7 +42,7 @@ Flute :: Flute(MY_FLOAT lowestFrequency)
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE );
vibrato->setFrequency( 5.925 );
this->clear();
@@ -57,6 +57,7 @@ Flute :: Flute(MY_FLOAT lowestFrequency)
jetRatio = (MY_FLOAT) 0.32;
maxPressure = (MY_FLOAT) 0.0;
lastFrequency = 220.0;
}
Flute :: ~Flute()
@@ -184,7 +185,7 @@ void Flute :: controlChange(int number, MY_FLOAT value)
}
if (number == __SK_JetDelay_) // 2
this->setJetDelay( 0.08 + (0.48 * norm) );
this->setJetDelay( (MY_FLOAT) (0.08 + (0.48 * norm)) );
else if (number == __SK_NoiseLevel_) // 4
noiseGain = ( norm * 0.4);
else if (number == __SK_ModFrequency_) // 11

View File

@@ -1,118 +1,118 @@
/***************************************************/
/*! \class FormSwep
\brief STK sweepable formant filter class.
This public BiQuad filter subclass implements
a formant (resonance) which can be "swept"
over time from one frequency setting to another.
It provides methods for controlling the sweep
rate and target frequency.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "FormSwep.h"
FormSwep :: FormSwep() : BiQuad()
{
frequency = (MY_FLOAT) 0.0;
radius = (MY_FLOAT) 0.0;
targetGain = (MY_FLOAT) 1.0;
targetFrequency = (MY_FLOAT) 0.0;
targetRadius = (MY_FLOAT) 0.0;
deltaGain = (MY_FLOAT) 0.0;
deltaFrequency = (MY_FLOAT) 0.0;
deltaRadius = (MY_FLOAT) 0.0;
sweepState = (MY_FLOAT) 0.0;
sweepRate = (MY_FLOAT) 0.002;
dirty = false;
this->clear();
}
FormSwep :: ~FormSwep()
{
}
void FormSwep :: setResonance(MY_FLOAT aFrequency, MY_FLOAT aRadius)
{
dirty = false;
radius = aRadius;
frequency = aFrequency;
BiQuad::setResonance( frequency, radius, true );
}
void FormSwep :: setStates(MY_FLOAT aFrequency, MY_FLOAT aRadius, MY_FLOAT aGain)
{
dirty = false;
if ( frequency != aFrequency || radius != aRadius )
BiQuad::setResonance( aFrequency, aRadius, true );
frequency = aFrequency;
radius = aRadius;
gain = aGain;
targetFrequency = aFrequency;
targetRadius = aRadius;
targetGain = aGain;
}
void FormSwep :: setTargets(MY_FLOAT aFrequency, MY_FLOAT aRadius, MY_FLOAT aGain)
{
dirty = true;
startFrequency = frequency;
startRadius = radius;
startGain = gain;
targetFrequency = aFrequency;
targetRadius = aRadius;
targetGain = aGain;
deltaFrequency = aFrequency - frequency;
deltaRadius = aRadius - radius;
deltaGain = aGain - gain;
sweepState = (MY_FLOAT) 0.0;
}
void FormSwep :: setSweepRate(MY_FLOAT aRate)
{
sweepRate = aRate;
if ( sweepRate > 1.0 ) sweepRate = 1.0;
if ( sweepRate < 0.0 ) sweepRate = 0.0;
}
void FormSwep :: setSweepTime(MY_FLOAT aTime)
{
sweepRate = 1.0 / ( aTime * Stk::sampleRate() );
if ( sweepRate > 1.0 ) sweepRate = 1.0;
if ( sweepRate < 0.0 ) sweepRate = 0.0;
}
MY_FLOAT FormSwep :: tick(MY_FLOAT sample)
{
if (dirty) {
sweepState += sweepRate;
if ( sweepState >= 1.0 ) {
sweepState = (MY_FLOAT) 1.0;
dirty = false;
radius = targetRadius;
frequency = targetFrequency;
gain = targetGain;
}
else {
radius = startRadius + (deltaRadius * sweepState);
frequency = startFrequency + (deltaFrequency * sweepState);
gain = startGain + (deltaGain * sweepState);
}
BiQuad::setResonance( frequency, radius, true );
}
return BiQuad::tick( sample );
}
MY_FLOAT *FormSwep :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class FormSwep
\brief STK sweepable formant filter class.
This public BiQuad filter subclass implements
a formant (resonance) which can be "swept"
over time from one frequency setting to another.
It provides methods for controlling the sweep
rate and target frequency.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "FormSwep.h"
FormSwep :: FormSwep() : BiQuad()
{
frequency = (MY_FLOAT) 0.0;
radius = (MY_FLOAT) 0.0;
targetGain = (MY_FLOAT) 1.0;
targetFrequency = (MY_FLOAT) 0.0;
targetRadius = (MY_FLOAT) 0.0;
deltaGain = (MY_FLOAT) 0.0;
deltaFrequency = (MY_FLOAT) 0.0;
deltaRadius = (MY_FLOAT) 0.0;
sweepState = (MY_FLOAT) 0.0;
sweepRate = (MY_FLOAT) 0.002;
dirty = false;
this->clear();
}
FormSwep :: ~FormSwep()
{
}
void FormSwep :: setResonance(MY_FLOAT aFrequency, MY_FLOAT aRadius)
{
dirty = false;
radius = aRadius;
frequency = aFrequency;
BiQuad::setResonance( frequency, radius, true );
}
void FormSwep :: setStates(MY_FLOAT aFrequency, MY_FLOAT aRadius, MY_FLOAT aGain)
{
dirty = false;
if ( frequency != aFrequency || radius != aRadius )
BiQuad::setResonance( aFrequency, aRadius, true );
frequency = aFrequency;
radius = aRadius;
gain = aGain;
targetFrequency = aFrequency;
targetRadius = aRadius;
targetGain = aGain;
}
void FormSwep :: setTargets(MY_FLOAT aFrequency, MY_FLOAT aRadius, MY_FLOAT aGain)
{
dirty = true;
startFrequency = frequency;
startRadius = radius;
startGain = gain;
targetFrequency = aFrequency;
targetRadius = aRadius;
targetGain = aGain;
deltaFrequency = aFrequency - frequency;
deltaRadius = aRadius - radius;
deltaGain = aGain - gain;
sweepState = (MY_FLOAT) 0.0;
}
void FormSwep :: setSweepRate(MY_FLOAT aRate)
{
sweepRate = aRate;
if ( sweepRate > 1.0 ) sweepRate = 1.0;
if ( sweepRate < 0.0 ) sweepRate = 0.0;
}
void FormSwep :: setSweepTime(MY_FLOAT aTime)
{
sweepRate = 1.0 / ( aTime * Stk::sampleRate() );
if ( sweepRate > 1.0 ) sweepRate = 1.0;
if ( sweepRate < 0.0 ) sweepRate = 0.0;
}
MY_FLOAT FormSwep :: tick(MY_FLOAT sample)
{
if (dirty) {
sweepState += sweepRate;
if ( sweepState >= 1.0 ) {
sweepState = (MY_FLOAT) 1.0;
dirty = false;
radius = targetRadius;
frequency = targetFrequency;
gain = targetGain;
}
else {
radius = startRadius + (deltaRadius * sweepState);
frequency = startFrequency + (deltaFrequency * sweepState);
gain = startGain + (deltaGain * sweepState);
}
BiQuad::setResonance( frequency, radius, true );
}
return BiQuad::tick( sample );
}
MY_FLOAT *FormSwep :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

@@ -39,10 +39,10 @@ HevyMetl :: HevyMetl()
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
strcat(files[0], "sinewave.raw");
strcat(files[1], "sinewave.raw");
strcat(files[2], "sinewave.raw");
strcat(files[3], "fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );

View File

@@ -1,42 +1,42 @@
/***************************************************/
/*! \class Instrmnt
\brief STK instrument abstract base class.
This class provides a common interface for
all STK instruments.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Instrmnt.h"
Instrmnt :: Instrmnt()
{
}
Instrmnt :: ~Instrmnt()
{
}
void Instrmnt :: setFrequency(MY_FLOAT frequency)
{
cerr << "Instrmnt: virtual setFrequency function call!" << endl;
}
MY_FLOAT Instrmnt :: lastOut() const
{
return lastOutput;
}
MY_FLOAT *Instrmnt :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick();
return vector;
}
void Instrmnt :: controlChange(int number, MY_FLOAT value)
{
}
/***************************************************/
/*! \class Instrmnt
\brief STK instrument abstract base class.
This class provides a common interface for
all STK instruments.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Instrmnt.h"
Instrmnt :: Instrmnt()
{
}
Instrmnt :: ~Instrmnt()
{
}
void Instrmnt :: setFrequency(MY_FLOAT frequency)
{
cerr << "Instrmnt: virtual setFrequency function call!" << endl;
}
MY_FLOAT Instrmnt :: lastOut() const
{
return lastOutput;
}
MY_FLOAT *Instrmnt :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick();
return vector;
}
void Instrmnt :: controlChange(int number, MY_FLOAT value)
{
}

View File

@@ -1,53 +1,53 @@
/***************************************************/
/*! \class JetTabl
\brief STK jet table class.
This class implements a flue jet non-linear
function, computed by a polynomial calculation.
Contrary to the name, this is not a "table".
Consult Fletcher and Rossing, Karjalainen,
Cook, and others for more information.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "JetTabl.h"
JetTabl :: JetTabl()
{
lastOutput = (MY_FLOAT) 0.0;
}
JetTabl :: ~JetTabl()
{
}
MY_FLOAT JetTabl :: lastOut() const
{
return lastOutput;
}
MY_FLOAT JetTabl :: tick( MY_FLOAT input )
{
// Perform "table lookup" using a polynomial
// calculation (x^3 - x), which approximates
// the jet sigmoid behavior.
lastOutput = input * (input * input - (MY_FLOAT) 1.0);
// Saturate at +/- 1.0.
if (lastOutput > 1.0)
lastOutput = (MY_FLOAT) 1.0;
if (lastOutput < -1.0)
lastOutput = (MY_FLOAT) -1.0;
return lastOutput;
}
MY_FLOAT *JetTabl :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class JetTabl
\brief STK jet table class.
This class implements a flue jet non-linear
function, computed by a polynomial calculation.
Contrary to the name, this is not a "table".
Consult Fletcher and Rossing, Karjalainen,
Cook, and others for more information.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "JetTabl.h"
JetTabl :: JetTabl()
{
lastOutput = (MY_FLOAT) 0.0;
}
JetTabl :: ~JetTabl()
{
}
MY_FLOAT JetTabl :: lastOut() const
{
return lastOutput;
}
MY_FLOAT JetTabl :: tick( MY_FLOAT input )
{
// Perform "table lookup" using a polynomial
// calculation (x^3 - x), which approximates
// the jet sigmoid behavior.
lastOutput = input * (input * input - (MY_FLOAT) 1.0);
// Saturate at +/- 1.0.
if (lastOutput > 1.0)
lastOutput = (MY_FLOAT) 1.0;
if (lastOutput < -1.0)
lastOutput = (MY_FLOAT) -1.0;
return lastOutput;
}
MY_FLOAT *JetTabl :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

@@ -1,53 +0,0 @@
# libstk Makefile - for Linux or SGI with GNU Makefile utilities
LIBRARY = libstk.a
AR = ar -qsc
RM = /bin/rm
INCLUDE = -I../include
OS = $(shell uname)
O_FILES = Stk.o Noise.o SubNoise.o Envelope.o ADSR.o \
Filter.o OneZero.o OnePole.o PoleZero.o \
TwoZero.o TwoPole.o BiQuad.o FormSwep.o \
Delay.o DelayL.o DelayA.o \
Reverb.o PRCRev.o JCRev.o NRev.o \
Table.o JetTabl.o ReedTabl.o BowTabl.o Modulate.o \
WvIn.o WaveLoop.o WvOut.o \
Chorus.o Echo.o PitShift.o \
RtAudio.o RtWvOut.o RtWvIn.o RtDuplex.o \
Socket.o Thread.o TcpWvOut.o TcpWvIn.o \
\
Instrmnt.o Clarinet.o BlowHole.o Saxofony.o Flute.o Brass.o BlowBotl.o \
Bowed.o Plucked.o StifKarp.o Sitar.o PluckTwo.o Mandolin.o \
FM.o Rhodey.o Wurley.o TubeBell.o HevyMetl.o PercFlut.o BeeThree.o FMVoices.o \
Sampler.o Moog.o Simple.o Drummer.o Shakers.o Modal.o ModalBar.o BandedWG.o \
Mesh2D.o Resonate.o SKINI.o Messager.o RtMidi.o
ifeq ($(OS),IRIX) # These are for SGI
CC = CC -D__OS_IRIX__
endif
ifeq ($(OS),Linux) # These are for Linux
ifeq ($(strip $(SOUNDDRIVER)),)
SOUNDDRIVER = __LINUX_OSS__
endif
ifeq ($(strip $(RAWWAVES)),)
RAWWAVES = "../../"
endif
CC = g++ -O3 -Wall -D__LITTLE_ENDIAN__ -D$(SOUNDDRIVER) -DRAWWAVE_PATH=$(RAWWAVES)
endif
%.o : %.cpp
$(CC) $(INCLUDE) -c $(<) -o $@
debug all: $(LIBRARY)
$(LIBRARY): $(O_FILES)
/bin/rm -f $(LIBRARY)
$(AR) $(LIBRARY) $(O_FILES)
ranlib -t $(LIBRARY)
clean :
rm *.o
rm $(LIBRARY)

70
src/Makefile.in Normal file
View File

@@ -0,0 +1,70 @@
### libstk Makefile - for various flavors of unix
LIBRARY = libstk.a
AR = ar -qc
RM = /bin/rm
OBJECT_PATH = @object_path@
vpath %.o $(OBJECT_PATH)
OBJECTS = Stk.o Noise.o SubNoise.o Envelope.o ADSR.o \
WvIn.o WaveLoop.o WvOut.o \
Filter.o OneZero.o OnePole.o PoleZero.o TwoZero.o TwoPole.o \
BiQuad.o FormSwep.o Delay.o DelayL.o DelayA.o \
Reverb.o PRCRev.o JCRev.o NRev.o \
Chorus.o Echo.o PitShift.o \
Table.o ReedTabl.o JetTabl.o BowTabl.o \
Modulate.o SingWave.o Voicer.o \
Vector3D.o Sphere.o \
\
Instrmnt.o Clarinet.o BlowHole.o Saxofony.o Flute.o Brass.o BlowBotl.o \
Bowed.o Plucked.o StifKarp.o Sitar.o PluckTwo.o Mandolin.o Mesh2D.o \
FM.o Rhodey.o Wurley.o TubeBell.o HevyMetl.o PercFlut.o BeeThree.o FMVoices.o \
Sampler.o Moog.o Simple.o Drummer.o Shakers.o \
Modal.o ModalBar.o BandedWG.o Resonate.o VoicForm.o Phonemes.o Whistle.o \
\
Messager.o SKINI.o
INCLUDE = @include@
ifeq ($(strip $(INCLUDE)),)
vpath %.h ../include
INCLUDE = -I../include
else
vpath %.h ../include $(INCLUDE)
INCLUDE = -I../include -I@include@
endif
CC = @CXX@
DEFS = @byte_order@
DEFS += @debug@
CFLAGS = @cflags@
CFLAGS += @warn@ $(INCLUDE)
REALTIME = @realtime@
ifeq ($(REALTIME),yes)
OBJECTS += RtMidi.o RtAudio.o RtWvOut.o RtWvIn.o TcpWvOut.o TcpWvIn.o Thread.o Socket.o
DEFS += @sound_api@
DEFS += @midiator@
endif
RAWWAVES = @rawwaves@
ifeq ($(strip $(RAWWAVES)), )
RAWWAVES = ../rawwaves/
endif
DEFS += -DRAWWAVE_PATH=\"$(RAWWAVES)\"
%.o : %.cpp
$(CC) $(CFLAGS) $(DEFS) -c $(<) -o $(OBJECT_PATH)/$@
all : $(LIBRARY)
$(LIBRARY) : $(OBJECTS)
/bin/rm -f $(LIBRARY)
$(AR) $(LIBRARY) $(OBJECT_PATH)/*.o
ranlib $(LIBRARY)
$(OBJECTS) : Stk.h
clean :
-rm $(OBJECT_PATH)/*.o
-rm $(LIBRARY)

View File

@@ -38,29 +38,29 @@ Mandolin :: Mandolin(MY_FLOAT lowestFrequency)
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char temp[128];
strcpy(temp, RAWWAVE_PATH);
soundfile[0] = new WvIn( strcat(temp,"rawwaves/mand1.raw"), TRUE );
soundfile[0] = new WvIn( strcat(temp,"mand1.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[1] = new WvIn( strcat(temp,"rawwaves/mand2.raw"), TRUE );
soundfile[1] = new WvIn( strcat(temp,"mand2.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[2] = new WvIn( strcat(temp,"rawwaves/mand3.raw"), TRUE );
soundfile[2] = new WvIn( strcat(temp,"mand3.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[3] = new WvIn( strcat(temp,"rawwaves/mand4.raw"), TRUE );
soundfile[3] = new WvIn( strcat(temp,"mand4.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[4] = new WvIn( strcat(temp,"rawwaves/mand5.raw"), TRUE );
soundfile[4] = new WvIn( strcat(temp,"mand5.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[5] = new WvIn( strcat(temp,"rawwaves/mand6.raw"), TRUE );
soundfile[5] = new WvIn( strcat(temp,"mand6.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[6] = new WvIn( strcat(temp,"rawwaves/mand7.raw"), TRUE );
soundfile[6] = new WvIn( strcat(temp,"mand7.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[7] = new WvIn( strcat(temp,"rawwaves/mand8.raw"), TRUE );
soundfile[7] = new WvIn( strcat(temp,"mand8.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[8] = new WvIn( strcat(temp,"rawwaves/mand9.raw"), TRUE );
soundfile[8] = new WvIn( strcat(temp,"mand9.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[9] = new WvIn( strcat(temp,"rawwaves/mand10.raw"), TRUE );
soundfile[9] = new WvIn( strcat(temp,"mand10.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[10] = new WvIn( strcat(temp,"rawwaves/mand11.raw"), TRUE );
soundfile[10] = new WvIn( strcat(temp,"mand11.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[11] = new WvIn( strcat(temp,"rawwaves/mand12.raw"), TRUE );
soundfile[11] = new WvIn( strcat(temp,"mand12.raw"), TRUE );
directBody = 1.0;
mic = 0;
dampTime = 0;

View File

@@ -1,385 +1,394 @@
/***************************************************/
/*! \class Messager
\brief STK input control message parser.
This class reads and parses control messages
from a variety of sources, such as a MIDI
port, scorefile, socket connection, or pipe.
MIDI messages are retrieved using the RtMidi
class. All other input sources (scorefile,
socket, or pipe) are assumed to provide SKINI
formatted messages.
For each call to nextMessage(), the active
input sources are queried to see if a new
control message is available.
This class is primarily for use in STK main()
event loops.
One of the original goals in creating this
class was to simplify the message acquisition
process by removing all threads. If the
windoze select() function behaved just like
the unix one, that would have been possible.
Since it does not (it can't be used to poll
STDIN), I am using a thread to acquire
messages from STDIN, which sends these
messages via a socket connection to the
message socket server. Perhaps in the future,
it will be possible to simplify things.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Messager.h"
#include <string.h>
#include <iostream.h>
#define STK_MIDI 0x0001
#define STK_PIPE 0x0002
#define STK_SOCKET 0x0004
#define STK_SOCKET_PORT 2001
Messager :: Messager(int inputMask)
{
sources = inputMask;
rtDelta = RT_BUFFER_SIZE;
messageIndex = 0;
nMessages = 0;
skini = new SKINI();
#if defined(__STK_REALTIME__)
// If no input source is specified, we assume the input is coming
// from a SKINI scorefile. If any input source is specified, we
// will always check STDIN, even if STK_PIPE is not specified. This
// provides a means to exit cleanly when reading MIDI or in case a
// socket connection cannot be made after STK_SOCKET has been
// specified. The current means of polling STDIN is via a thread,
// which sends its data via a socket connection to the socket
// server.
if ( sources ) {
if ( sources & STK_MIDI )
midi = new RtMidi();
// If STK_PIPE is not specified, let the users know they can exit
// the program via the console if necessary.
if ( !(sources & STK_PIPE) && sources )
cout << "\nType `Exit<cr>' to quit.\n" << endl;
sources |= STK_SOCKET;
soket = new Socket(STK_SOCKET_PORT);
if (inputMask & STK_SOCKET)
printf("\nSocket server listening for connection(s) on port %d ...\n\n", STK_SOCKET_PORT);
nSockets = 0;
maxfd = 0;
FD_ZERO(&mask);
int d = soket->socket();
FD_SET(d, &mask);
if (d > maxfd) maxfd = d;
// The fd array is used to hold the file descriptors for all
// connected sockets. This saves some time incrementing through
// file descriptors when using select().
for (int i=0; i<16; i++)
fd[i] = 0;
// Start the stdin input thread.
thread = new Thread();
if ( !thread->start( (THREAD_FUNCTION)&stdinHandler, NULL ) ) {
sprintf(error, "Messager: Unable to start stdin input thread!");
handleError( error, StkError::PROCESS_THREAD );
}
}
#endif // __STK_REALTIME__
}
Messager :: ~Messager()
{
delete skini;
#if defined(__STK_REALTIME__)
if ( sources & STK_MIDI )
delete midi;
if ( sources & STK_SOCKET ) {
delete soket;
delete thread;
}
#endif // __STK_REALTIME__
}
long Messager :: getType() const
{
return type;
}
MY_FLOAT Messager :: getByteTwo() const
{
return byte2;
}
MY_FLOAT Messager :: getByteThree() const
{
return byte3;
}
long Messager :: getChannel() const
{
return channel;
}
void Messager :: setRtDelta(long nSamples)
{
if ( nSamples > 0 )
rtDelta = nSamples;
else
cerr << "Messager: setRtDelta(" << nSamples << ") less than or equal to zero!" << endl;
}
long Messager :: getDelta() const
{
return delta;
}
long Messager :: nextMessage()
{
if (nMessages > 0 ) nMessages--;
type = 0;
if ( !sources ) {
// No realtime flags ... assuming scorefile input.
memset(message[messageIndex], 0, MESSAGE_LENGTH);
if ( fgets(message[messageIndex], MESSAGE_LENGTH, stdin) == 0 ) {
delta = 0;
return -1; // end of file
}
nMessages++;
}
#if defined(__STK_REALTIME__)
else if (nMessages == 0) {
if ( midiMessage() ) return type;
if ( !socketMessage() ) return type;
}
#endif
skini->parseThis(message[messageIndex++]);
if (messageIndex >= MAX_MESSAGES) messageIndex = 0;
type = skini->getType();
if (type <= 0) {
// Don't tick for comments or improperly formatted messages.
nMessages--;
delta = 0;
type = 0;
return type;
}
channel = skini->getChannel();
byte2 = skini->getByteTwo();
byte3 = skini->getByteThree();
MY_FLOAT temp = skini->getDelta();
if ( temp >= 0.0 )
delta = (long) (temp * Stk::sampleRate());
else
// Ignore negative delta times (absolute time).
delta = rtDelta;
return type;
}
#if defined(__STK_REALTIME__)
bool Messager :: midiMessage( void )
{
if (sources & STK_MIDI) {
if ( midi->nextMessage() > 0 ) {
// get MIDI message info
type = midi->getType();
channel = midi->getChannel();
byte2 = midi->getByteTwo();
byte3 = midi->getByteThree();
nMessages++;
delta = rtDelta;
return true;
}
}
return false;
}
bool Messager :: socketMessage()
{
register fd_set rmask;
static struct timeval timeout = {0, 0};
rmask = mask;
if ( select(maxfd+1, &rmask, (fd_set *)0, (fd_set *)0, &timeout) ) {
// A file descriptor is set.
// Check if there's a new socket connection available.
if ( FD_ISSET(soket->socket(), &rmask) ) {
// Accept and service new connection.
int newfd = soket->accept();
if ( newfd < 0 ) {
sprintf(error, "Messager: Couldn't accept connection request!");
handleError(error, StkError::WARNING);
}
// We assume the first connection will occur for the stdin
// thread socket. Since this connection is "hidden" from
// the user, only print connected messages for subsequent
// connections.
if (nSockets == 0)
pipefd = newfd;
else
cout << "New socket connection made.\n" << endl;
// Set the socket to non-blocking mode.
Socket::setBlocking( newfd, false );
// Save the descriptor and update the masks.
fd[nSockets++] = newfd;
FD_SET(newfd, &mask);
if ( newfd > maxfd) maxfd = newfd;
FD_CLR(soket->socket(), &rmask);
}
// Check client socket connections.
unsigned int client = 0;
while ( client < nSockets ) {
if ( !FD_ISSET(fd[client], &rmask) )
client++;
else {
// This connection has data.
if ( !readSocket( fd[client] ) ) {
// The socket connection closed.
nSockets--;
if ( nSockets == 0 ) {
type = -1;
return false;
}
if ( nSockets == 1 && FD_ISSET(pipefd, &mask) ) {
// The "piping" socket is still running.
if (sources & STK_MIDI) {
cout << "MIDI input still running ... type 'Exit<cr>' to quit.\n" << endl;
}
else if (!(sources & STK_PIPE) ) {
// The user didn't specify this connection, so quit now.
type = -1;
return false;
}
}
if (client < nSockets) {
// Move descriptors down in the list.
for (unsigned int j=client; j<nSockets; j++)
fd[j] = fd[j+1];
}
delta = 0;
return false;
}
if ( !strncmp(message[messageIndex], "Exit", 4) || !strncmp(message[messageIndex], "exit", 4) ) {
// We have an "Exit" message ... don't try to parse it.
messageIndex++;
nMessages--;
delta = 0;
return false;
}
// Not an "Exit" message ... parse it.
return true;
}
}
}
// If we get here, we checked all devices but found no messages.
delta = rtDelta;
return false;
}
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
#include <errno.h>
#endif
bool Messager :: readSocket(int fd)
{
// This method will read all data available from a socket
// connection, filling the message buffer. This is necessary
// because the select() function triggers on socket activity, not on
// the presence of (buffered) data. So, whenever activity is
// indicated, we need to grab all available data.
char buffer[MESSAGE_LENGTH];
int index = 0, m = 0, bufferSize = 0;
int nextMessage;
nextMessage = (messageIndex + nMessages) % MAX_MESSAGES;
memset(message[nextMessage], 0, MESSAGE_LENGTH);
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
errno = 0;
while (bufferSize != -1 && errno != EAGAIN) {
#elif defined(__OS_WINDOWS__)
while (bufferSize != SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
#endif
while (index < bufferSize) {
message[nextMessage][m++] = buffer[index];
if (buffer[index++] == '\n') {
m = 0;
nMessages++;
nextMessage = (messageIndex + nMessages) % MAX_MESSAGES;
memset(message[nextMessage], 0, MESSAGE_LENGTH);
}
}
index = 0;
// Receive a new socket buffer.
memset(buffer, 0, MESSAGE_LENGTH);
bufferSize = Socket::readBuffer(fd, buffer, MESSAGE_LENGTH, 0);
if (bufferSize == 0) {
FD_CLR(fd, &mask);
Socket::close( fd );
return false;
}
}
return true;
}
THREAD_RETURN THREAD_TYPE stdinHandler(void *)
{
char message[MESSAGE_LENGTH];
Socket *s;
try {
s = new Socket( STK_SOCKET_PORT, "localhost" );
}
catch (StkError &) {
fprintf(stderr, "Messager: Couldn't create stdin input thread!\n");
return NULL;
}
for (;;) {
memset(message, 0, MESSAGE_LENGTH);
if ( fgets(message, MESSAGE_LENGTH, stdin) == 0 )
break;
// Check for an "Exit" message.
if ( !strncmp(message, "Exit", 4) || !strncmp(message, "exit", 4) )
break;
if ( s->writeBuffer( (void *)message, strlen(message), 0) < 0 ) {
fprintf(stderr, "Messager: stdin thread connection to socket server failed!\n");
break;
}
}
delete s;
return NULL;
}
#endif // __STK_REALTIME__
/***************************************************/
/*! \class Messager
\brief STK input control message parser.
This class reads and parses control messages
from a variety of sources, such as a MIDI
port, scorefile, socket connection, or pipe.
MIDI messages are retrieved using the RtMidi
class. All other input sources (scorefile,
socket, or pipe) are assumed to provide SKINI
formatted messages.
For each call to nextMessage(), the active
input sources are queried to see if a new
control message is available.
This class is primarily for use in STK main()
event loops.
One of the original goals in creating this
class was to simplify the message acquisition
process by removing all threads. If the
windoze select() function behaved just like
the unix one, that would have been possible.
Since it does not (it can't be used to poll
STDIN), I am using a thread to acquire
messages from STDIN, which sends these
messages via a socket connection to the
message socket server. Perhaps in the future,
it will be possible to simplify things.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Messager.h"
#include <string.h>
#include <iostream.h>
int socket_port = 2001;
Messager :: Messager(int inputMask, int port)
{
sources = inputMask;
rtDelta = RT_BUFFER_SIZE;
messageIndex = 0;
nMessages = 0;
skini = new SKINI();
#if defined(__STK_REALTIME__)
// If no input source is specified, we assume the input is coming
// from a SKINI scorefile. If any input source is specified, we
// will always check STDIN, even if STK_PIPE is not specified. This
// provides a means to exit cleanly when reading MIDI or in case a
// socket connection cannot be made after STK_SOCKET has been
// specified. The current means of polling STDIN is via a thread,
// which sends its data via a socket connection to the socket
// server.
if ( sources ) {
if ( sources & STK_MIDI ) {
// Attempt to open a MIDI device, but don't throw an exception
// if other input sources are specified.
try {
midi = new RtMidi();
}
catch (StkError &exception) {
if ( sources == STK_MIDI ) {
throw exception;
}
// Disable the MIDI input and keep going.
sources &= ~STK_MIDI;
}
}
// If STK_PIPE is not specified, let the users know they can exit
// the program via the console if necessary.
if ( !(sources & STK_PIPE) && sources )
cout << "\nType `Exit<cr>' to quit.\n" << endl;
sources |= STK_SOCKET;
socket_port = port;
soket = new Socket(port);
if (inputMask & STK_SOCKET)
printf("\nSocket server listening for connection(s) on port %d ...\n\n", port);
nSockets = 0;
maxfd = 0;
FD_ZERO(&mask);
int d = soket->socket();
FD_SET(d, &mask);
if (d > maxfd) maxfd = d;
// The fd array is used to hold the file descriptors for all
// connected sockets. This saves some time incrementing through
// file descriptors when using select().
for (int i=0; i<16; i++)
fd[i] = 0;
// Start the stdin input thread.
thread = new Thread();
if ( !thread->start( (THREAD_FUNCTION)&stdinHandler, NULL ) ) {
sprintf(error, "Messager: Unable to start stdin input thread!");
handleError( error, StkError::PROCESS_THREAD );
}
}
#endif // __STK_REALTIME__
}
Messager :: ~Messager()
{
delete skini;
#if defined(__STK_REALTIME__)
if ( sources & STK_MIDI )
delete midi;
if ( sources & STK_SOCKET ) {
delete soket;
delete thread;
}
#endif // __STK_REALTIME__
}
long Messager :: getType() const
{
return type;
}
MY_FLOAT Messager :: getByteTwo() const
{
return byte2;
}
MY_FLOAT Messager :: getByteThree() const
{
return byte3;
}
long Messager :: getChannel() const
{
return channel;
}
void Messager :: setRtDelta(long nSamples)
{
if ( nSamples > 0 )
rtDelta = nSamples;
else
cerr << "Messager: setRtDelta(" << nSamples << ") less than or equal to zero!" << endl;
}
long Messager :: getDelta() const
{
return delta;
}
long Messager :: nextMessage()
{
if (nMessages > 0 ) nMessages--;
type = 0;
if ( !sources ) {
// No realtime flags ... assuming scorefile input.
memset(message[messageIndex], 0, MESSAGE_LENGTH);
if ( fgets(message[messageIndex], MESSAGE_LENGTH, stdin) == 0 ) {
delta = 0;
return -1; // end of file
}
nMessages++;
}
#if defined(__STK_REALTIME__)
else if (nMessages == 0) {
if ( midiMessage() ) return type;
if ( !socketMessage() ) return type;
}
#endif
skini->parseThis(message[messageIndex++]);
if (messageIndex >= MAX_MESSAGES) messageIndex = 0;
type = skini->getType();
if (type <= 0) {
// Don't tick for comments or improperly formatted messages.
nMessages--;
delta = 0;
type = 0;
return type;
}
channel = skini->getChannel();
byte2 = skini->getByteTwo();
byte3 = skini->getByteThree();
MY_FLOAT temp = skini->getDelta();
if ( temp >= 0.0 )
delta = (long) (temp * Stk::sampleRate());
else
// Ignore negative delta times (absolute time).
delta = rtDelta;
return type;
}
#if defined(__STK_REALTIME__)
bool Messager :: midiMessage( void )
{
if (sources & STK_MIDI) {
if ( midi->nextMessage() > 0 ) {
// get MIDI message info
type = midi->getType();
channel = midi->getChannel();
byte2 = midi->getByteTwo();
byte3 = midi->getByteThree();
nMessages++;
delta = rtDelta;
return true;
}
}
return false;
}
bool Messager :: socketMessage()
{
register fd_set rmask;
static struct timeval timeout = {0, 0};
rmask = mask;
if ( select(maxfd+1, &rmask, (fd_set *)0, (fd_set *)0, &timeout) ) {
// A file descriptor is set.
// Check if there's a new socket connection available.
if ( FD_ISSET(soket->socket(), &rmask) ) {
// Accept and service new connection.
int newfd = soket->accept();
if ( newfd < 0 ) {
sprintf(error, "Messager: Couldn't accept connection request!");
handleError(error, StkError::WARNING);
}
// We assume the first connection will occur for the stdin
// thread socket. Since this connection is "hidden" from
// the user, only print connected messages for subsequent
// connections.
if (nSockets == 0)
pipefd = newfd;
else
cout << "New socket connection made.\n" << endl;
// Set the socket to non-blocking mode.
Socket::setBlocking( newfd, false );
// Save the descriptor and update the masks.
fd[nSockets++] = newfd;
FD_SET(newfd, &mask);
if ( newfd > maxfd) maxfd = newfd;
FD_CLR(soket->socket(), &rmask);
}
// Check client socket connections.
unsigned int client = 0;
while ( client < nSockets ) {
if ( !FD_ISSET(fd[client], &rmask) )
client++;
else {
// This connection has data.
if ( !readSocket( fd[client] ) ) {
// The socket connection closed.
nSockets--;
if ( nSockets == 0 ) {
type = -1;
return false;
}
if ( nSockets == 1 && FD_ISSET(pipefd, &mask) ) {
// The "piping" socket is still running.
if (sources & STK_MIDI) {
cout << "MIDI input still running ... type 'Exit<cr>' to quit.\n" << endl;
}
else if (!(sources & STK_PIPE) ) {
// The user didn't specify this connection, so quit now.
type = -1;
return false;
}
}
if (client < nSockets) {
// Move descriptors down in the list.
for (unsigned int j=client; j<nSockets; j++)
fd[j] = fd[j+1];
}
delta = 0;
return false;
}
if ( !strncmp(message[messageIndex], "Exit", 4) || !strncmp(message[messageIndex], "exit", 4) ) {
// We have an "Exit" message ... don't try to parse it.
messageIndex++;
nMessages--;
delta = 0;
return false;
}
// Not an "Exit" message ... parse it.
return true;
}
}
}
// If we get here, we checked all devices but found no messages.
delta = rtDelta;
return false;
}
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
#include <errno.h>
#endif
bool Messager :: readSocket(int fd)
{
// This method will read all data available from a socket
// connection, filling the message buffer. This is necessary
// because the select() function triggers on socket activity, not on
// the presence of (buffered) data. So, whenever activity is
// indicated, we need to grab all available data.
char buffer[MESSAGE_LENGTH];
int index = 0, m = 0, bufferSize = 0;
int nextMessage;
nextMessage = (messageIndex + nMessages) % MAX_MESSAGES;
memset(message[nextMessage], 0, MESSAGE_LENGTH);
#if ( defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__) )
errno = 0;
while (bufferSize != -1 && errno != EAGAIN) {
#elif defined(__OS_WINDOWS__)
while (bufferSize != SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
#endif
while (index < bufferSize) {
message[nextMessage][m++] = buffer[index];
if (buffer[index++] == '\n') {
m = 0;
nMessages++;
nextMessage = (messageIndex + nMessages) % MAX_MESSAGES;
memset(message[nextMessage], 0, MESSAGE_LENGTH);
}
}
index = 0;
// Receive a new socket buffer.
memset(buffer, 0, MESSAGE_LENGTH);
bufferSize = Socket::readBuffer(fd, buffer, MESSAGE_LENGTH, 0);
if (bufferSize == 0) {
FD_CLR(fd, &mask);
Socket::close( fd );
return false;
}
}
return true;
}
THREAD_RETURN THREAD_TYPE stdinHandler(void *)
{
char message[MESSAGE_LENGTH];
Socket *s;
try {
s = new Socket( socket_port, "localhost" );
}
catch (StkError &) {
fprintf(stderr, "Messager: Couldn't create stdin input thread!\n");
return NULL;
}
for (;;) {
memset(message, 0, MESSAGE_LENGTH);
if ( fgets(message, MESSAGE_LENGTH, stdin) == 0 )
break;
// Check for an "Exit" message.
if ( !strncmp(message, "Exit", 4) || !strncmp(message, "exit", 4) )
break;
if ( s->writeBuffer( (void *)message, strlen(message), 0) < 0 ) {
fprintf(stderr, "Messager: stdin thread connection to socket server failed!\n");
break;
}
}
delete s;
return NULL;
}
#endif // __STK_REALTIME__

View File

@@ -41,7 +41,7 @@ Modal :: Modal(int modes)
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE);
vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE);
// Set some default values.
vibrato->setFrequency( 6.0 );

View File

@@ -39,7 +39,7 @@ ModalBar :: ModalBar()
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
wave = new WvIn( strcat(file,"rawwaves/marmstk1.raw"), TRUE );
wave = new WvIn( strcat(file,"marmstk1.raw"), TRUE );
wave->setRate((MY_FLOAT) 0.5 * 22050.0 / Stk::sampleRate() );
// Set the resonances for preset 0 (marimba).

View File

@@ -18,7 +18,7 @@ Modulate :: Modulate()
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato = new WaveLoop( strcat(file,"sinewave.raw"), TRUE );
vibrato->setFrequency( 6.0 );
vibratoGain = 0.04;
@@ -57,7 +57,7 @@ void Modulate :: setRandomGain(MY_FLOAT aGain)
filter->setGain( randomGain );
}
MY_FLOAT Modulate :: tick()
MY_FLOAT Modulate :: tick()
{
// Compute periodic and random modulations.
lastOutput = vibratoGain * vibrato->tick();
@@ -65,6 +65,14 @@ MY_FLOAT Modulate :: tick()
return lastOutput;
}
MY_FLOAT *Modulate :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick();
return vector;
}
MY_FLOAT Modulate :: lastOut() const
{
return lastOutput;

View File

@@ -30,11 +30,11 @@ Moog :: Moog()
char file[128];
strcpy(temp, RAWWAVE_PATH);
strcpy(file,temp);
attacks[0] = new WvIn( strcat(file,"rawwaves/mandpluk.raw"), TRUE );
attacks[0] = new WvIn( strcat(file,"mandpluk.raw"), TRUE );
strcpy(file,temp);
loops[0] = new WaveLoop( strcat(file,"rawwaves/impuls20.raw"), TRUE );
loops[0] = new WaveLoop( strcat(file,"impuls20.raw"), TRUE );
strcpy(file,temp);
loops[1] = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE ); // vibrato
loops[1] = new WaveLoop( strcat(file,"sinewave.raw"), TRUE ); // vibrato
loops[1]->setFrequency((MY_FLOAT) 6.122);
filters[0] = new FormSwep();

View File

@@ -1,44 +1,44 @@
/***************************************************/
/*! \class Noise
\brief STK noise generator.
Generic random number generation 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 - 2002.
*/
/***************************************************/
#include "Noise.h"
#include <stdlib.h>
Noise :: Noise() : Stk()
{
lastOutput = (MY_FLOAT) 0.0;
}
Noise :: ~Noise()
{
}
MY_FLOAT Noise :: tick()
{
lastOutput = (MY_FLOAT) (2.0 * rand() / (RAND_MAX + 1.0) );
lastOutput -= 1.0;
return lastOutput;
}
MY_FLOAT *Noise :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick();
return vector;
}
MY_FLOAT Noise :: lastOut() const
{
return lastOutput;
}
/***************************************************/
/*! \class Noise
\brief STK noise generator.
Generic random number generation 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 - 2002.
*/
/***************************************************/
#include "Noise.h"
#include <stdlib.h>
Noise :: Noise() : Stk()
{
lastOutput = (MY_FLOAT) 0.0;
}
Noise :: ~Noise()
{
}
MY_FLOAT Noise :: tick()
{
lastOutput = (MY_FLOAT) (2.0 * rand() / (RAND_MAX + 1.0) );
lastOutput -= 1.0;
return lastOutput;
}
MY_FLOAT *Noise :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick();
return vector;
}
MY_FLOAT Noise :: lastOut() const
{
return lastOutput;
}

View File

@@ -1,99 +1,99 @@
/***************************************************/
/*! \class OnePole
\brief STK one-pole filter class.
This protected Filter subclass implements
a one-pole digital filter. A method is
provided for setting the pole position along
the real axis of the z-plane while maintaining
a constant peak filter gain.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "OnePole.h"
OnePole :: OnePole() : Filter()
{
MY_FLOAT B = 0.1;
MY_FLOAT A[2] = {1.0, -0.9};
Filter::setCoefficients( 1, &B, 2, A );
}
OnePole :: OnePole(MY_FLOAT thePole) : Filter()
{
MY_FLOAT B;
MY_FLOAT A[2] = {1.0, -0.9};
// Normalize coefficients for peak unity gain.
if (thePole > 0.0)
B = (MY_FLOAT) (1.0 - thePole);
else
B = (MY_FLOAT) (1.0 + thePole);
A[1] = -thePole;
Filter::setCoefficients( 1, &B, 2, A );
}
OnePole :: ~OnePole()
{
}
void OnePole :: clear(void)
{
Filter::clear();
}
void OnePole :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void OnePole :: setA1(MY_FLOAT a1)
{
a[1] = a1;
}
void OnePole :: setPole(MY_FLOAT thePole)
{
// Normalize coefficients for peak unity gain.
if (thePole > 0.0)
b[0] = (MY_FLOAT) (1.0 - thePole);
else
b[0] = (MY_FLOAT) (1.0 + thePole);
a[1] = -thePole;
}
void OnePole :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT OnePole :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT OnePole :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT OnePole :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
outputs[0] = b[0] * inputs[0] - a[1] * outputs[1];
outputs[1] = outputs[0];
return outputs[0];
}
MY_FLOAT *OnePole :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class OnePole
\brief STK one-pole filter class.
This protected Filter subclass implements
a one-pole digital filter. A method is
provided for setting the pole position along
the real axis of the z-plane while maintaining
a constant peak filter gain.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "OnePole.h"
OnePole :: OnePole() : Filter()
{
MY_FLOAT B = 0.1;
MY_FLOAT A[2] = {1.0, -0.9};
Filter::setCoefficients( 1, &B, 2, A );
}
OnePole :: OnePole(MY_FLOAT thePole) : Filter()
{
MY_FLOAT B;
MY_FLOAT A[2] = {1.0, -0.9};
// Normalize coefficients for peak unity gain.
if (thePole > 0.0)
B = (MY_FLOAT) (1.0 - thePole);
else
B = (MY_FLOAT) (1.0 + thePole);
A[1] = -thePole;
Filter::setCoefficients( 1, &B, 2, A );
}
OnePole :: ~OnePole()
{
}
void OnePole :: clear(void)
{
Filter::clear();
}
void OnePole :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void OnePole :: setA1(MY_FLOAT a1)
{
a[1] = a1;
}
void OnePole :: setPole(MY_FLOAT thePole)
{
// Normalize coefficients for peak unity gain.
if (thePole > 0.0)
b[0] = (MY_FLOAT) (1.0 - thePole);
else
b[0] = (MY_FLOAT) (1.0 + thePole);
a[1] = -thePole;
}
void OnePole :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT OnePole :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT OnePole :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT OnePole :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
outputs[0] = b[0] * inputs[0] - a[1] * outputs[1];
outputs[1] = outputs[0];
return outputs[0];
}
MY_FLOAT *OnePole :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

@@ -1,99 +1,99 @@
/***************************************************/
/*! \class OneZero
\brief STK one-zero filter class.
This protected Filter subclass implements
a one-zero digital filter. A method is
provided for setting the zero position
along the real axis of the z-plane while
maintaining a constant filter gain.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "OneZero.h"
OneZero :: OneZero() : Filter()
{
MY_FLOAT B[2] = {0.5, 0.5};
MY_FLOAT A = 1.0;
Filter::setCoefficients( 2, B, 1, &A );
}
OneZero :: OneZero(MY_FLOAT theZero) : Filter()
{
MY_FLOAT B[2];
MY_FLOAT A = 1.0;
// Normalize coefficients for unity gain.
if (theZero > 0.0)
B[0] = 1.0 / ((MY_FLOAT) 1.0 + theZero);
else
B[0] = 1.0 / ((MY_FLOAT) 1.0 - theZero);
B[1] = -theZero * B[0];
Filter::setCoefficients( 2, B, 1, &A );
}
OneZero :: ~OneZero(void)
{
}
void OneZero :: clear(void)
{
Filter::clear();
}
void OneZero :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void OneZero :: setB1(MY_FLOAT b1)
{
b[1] = b1;
}
void OneZero :: setZero(MY_FLOAT theZero)
{
// Normalize coefficients for unity gain.
if (theZero > 0.0)
b[0] = 1.0 / ((MY_FLOAT) 1.0 + theZero);
else
b[0] = 1.0 / ((MY_FLOAT) 1.0 - theZero);
b[1] = -theZero * b[0];
}
void OneZero :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT OneZero :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT OneZero :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT OneZero :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
outputs[0] = b[1] * inputs[1] + b[0] * inputs[0];
inputs[1] = inputs[0];
return outputs[0];
}
MY_FLOAT *OneZero :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class OneZero
\brief STK one-zero filter class.
This protected Filter subclass implements
a one-zero digital filter. A method is
provided for setting the zero position
along the real axis of the z-plane while
maintaining a constant filter gain.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "OneZero.h"
OneZero :: OneZero() : Filter()
{
MY_FLOAT B[2] = {0.5, 0.5};
MY_FLOAT A = 1.0;
Filter::setCoefficients( 2, B, 1, &A );
}
OneZero :: OneZero(MY_FLOAT theZero) : Filter()
{
MY_FLOAT B[2];
MY_FLOAT A = 1.0;
// Normalize coefficients for unity gain.
if (theZero > 0.0)
B[0] = 1.0 / ((MY_FLOAT) 1.0 + theZero);
else
B[0] = 1.0 / ((MY_FLOAT) 1.0 - theZero);
B[1] = -theZero * B[0];
Filter::setCoefficients( 2, B, 1, &A );
}
OneZero :: ~OneZero(void)
{
}
void OneZero :: clear(void)
{
Filter::clear();
}
void OneZero :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void OneZero :: setB1(MY_FLOAT b1)
{
b[1] = b1;
}
void OneZero :: setZero(MY_FLOAT theZero)
{
// Normalize coefficients for unity gain.
if (theZero > 0.0)
b[0] = 1.0 / ((MY_FLOAT) 1.0 + theZero);
else
b[0] = 1.0 / ((MY_FLOAT) 1.0 - theZero);
b[1] = -theZero * b[0];
}
void OneZero :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT OneZero :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT OneZero :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT OneZero :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
outputs[0] = b[1] * inputs[1] + b[0] * inputs[0];
inputs[1] = inputs[0];
return outputs[0];
}
MY_FLOAT *OneZero :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

@@ -39,10 +39,10 @@ PercFlut :: PercFlut()
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
strcat(files[0], "sinewave.raw");
strcat(files[1], "sinewave.raw");
strcat(files[2], "sinewave.raw");
strcat(files[3], "fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );

279
src/Phonemes.cpp Normal file
View File

@@ -0,0 +1,279 @@
/***************************************************/
/*! \class Phonemes
\brief STK phonemes table.
This class does nothing other than declare a
set of 32 static phoneme formant parameters
and provide access to those values.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Phonemes.h"
#include <iostream.h>
const char Phonemes :: phonemeNames[32][4] =
{"eee", "ihh", "ehh", "aaa",
"ahh", "aww", "ohh", "uhh",
"uuu", "ooo", "rrr", "lll",
"mmm", "nnn", "nng", "ngg",
"fff", "sss", "thh", "shh",
"xxx", "hee", "hoo", "hah",
"bbb", "ddd", "jjj", "ggg",
"vvv", "zzz", "thz", "zhh"
};
const MY_FLOAT Phonemes :: phonemeGains[32][2] =
{{1.0, 0.0}, // eee
{1.0, 0.0}, // ihh
{1.0, 0.0}, // ehh
{1.0, 0.0}, // aaa
{1.0, 0.0}, // ahh
{1.0, 0.0}, // aww
{1.0, 0.0}, // ohh
{1.0, 0.0}, // uhh
{1.0, 0.0}, // uuu
{1.0, 0.0}, // ooo
{1.0, 0.0}, // rrr
{1.0, 0.0}, // lll
{1.0, 0.0}, // mmm
{1.0, 0.0}, // nnn
{1.0, 0.0}, // nng
{1.0, 0.0}, // ngg
{0.0, 0.7}, // fff
{0.0, 0.7}, // sss
{0.0, 0.7}, // thh
{0.0, 0.7}, // shh
{0.0, 0.7}, // xxx
{0.0, 0.1}, // hee
{0.0, 0.1}, // hoo
{0.0, 0.1}, // hah
{1.0, 0.1}, // bbb
{1.0, 0.1}, // ddd
{1.0, 0.1}, // jjj
{1.0, 0.1}, // ggg
{1.0, 1.0}, // vvv
{1.0, 1.0}, // zzz
{1.0, 1.0}, // thz
{1.0, 1.0} // zhh
};
const MY_FLOAT Phonemes :: phonemeParameters[32][4][3] =
{{ { 273, 0.996, 10}, // eee (beet)
{2086, 0.945, -16},
{2754, 0.979, -12},
{3270, 0.440, -17}},
{ { 385, 0.987, 10}, // ihh (bit)
{2056, 0.930, -20},
{2587, 0.890, -20},
{3150, 0.400, -20}},
{ { 515, 0.977, 10}, // ehh (bet)
{1805, 0.810, -10},
{2526, 0.875, -10},
{3103, 0.400, -13}},
{ { 773, 0.950, 10}, // aaa (bat)
{1676, 0.830, -6},
{2380, 0.880, -20},
{3027, 0.600, -20}},
{ { 770, 0.950, 0}, // ahh (father)
{1153, 0.970, -9},
{2450, 0.780, -29},
{3140, 0.800, -39}},
{ { 637, 0.910, 0}, // aww (bought)
{ 895, 0.900, -3},
{2556, 0.950, -17},
{3070, 0.910, -20}},
{ { 637, 0.910, 0}, // ohh (bone) NOTE:: same as aww (bought)
{ 895, 0.900, -3},
{2556, 0.950, -17},
{3070, 0.910, -20}},
{ { 561, 0.965, 0}, // uhh (but)
{1084, 0.930, -10},
{2541, 0.930, -15},
{3345, 0.900, -20}},
{ { 515, 0.976, 0}, // uuu (foot)
{1031, 0.950, -3},
{2572, 0.960, -11},
{3345, 0.960, -20}},
{ { 349, 0.986, -10}, // ooo (boot)
{ 918, 0.940, -20},
{2350, 0.960, -27},
{2731, 0.950, -33}},
{ { 394, 0.959, -10}, // rrr (bird)
{1297, 0.780, -16},
{1441, 0.980, -16},
{2754, 0.950, -40}},
{ { 462, 0.990, +5}, // lll (lull)
{1200, 0.640, -10},
{2500, 0.200, -20},
{3000, 0.100, -30}},
{ { 265, 0.987, -10}, // mmm (mom)
{1176, 0.940, -22},
{2352, 0.970, -20},
{3277, 0.940, -31}},
{ { 204, 0.980, -10}, // nnn (nun)
{1570, 0.940, -15},
{2481, 0.980, -12},
{3133, 0.800, -30}},
{ { 204, 0.980, -10}, // nng (sang) NOTE:: same as nnn
{1570, 0.940, -15},
{2481, 0.980, -12},
{3133, 0.800, -30}},
{ { 204, 0.980, -10}, // ngg (bong) NOTE:: same as nnn
{1570, 0.940, -15},
{2481, 0.980, -12},
{3133, 0.800, -30}},
{ {1000, 0.300, 0}, // fff
{2800, 0.860, -10},
{7425, 0.740, 0},
{8140, 0.860, 0}},
{ {0, 0.000, 0}, // sss
{2000, 0.700, -15},
{5257, 0.750, -3},
{7171, 0.840, 0}},
{ { 100, 0.900, 0}, // thh
{4000, 0.500, -20},
{5500, 0.500, -15},
{8000, 0.400, -20}},
{ {2693, 0.940, 0}, // shh
{4000, 0.720, -10},
{6123, 0.870, -10},
{7755, 0.750, -18}},
{ {1000, 0.300, -10}, // xxx NOTE:: Not Really Done Yet
{2800, 0.860, -10},
{7425, 0.740, 0},
{8140, 0.860, 0}},
{ { 273, 0.996, -40}, // hee (beet) (noisy eee)
{2086, 0.945, -16},
{2754, 0.979, -12},
{3270, 0.440, -17}},
{ { 349, 0.986, -40}, // hoo (boot) (noisy ooo)
{ 918, 0.940, -10},
{2350, 0.960, -17},
{2731, 0.950, -23}},
{ { 770, 0.950, -40}, // hah (father) (noisy ahh)
{1153, 0.970, -3},
{2450, 0.780, -20},
{3140, 0.800, -32}},
{ {2000, 0.700, -20}, // bbb NOTE:: Not Really Done Yet
{5257, 0.750, -15},
{7171, 0.840, -3},
{9000, 0.900, 0}},
{ { 100, 0.900, 0}, // ddd NOTE:: Not Really Done Yet
{4000, 0.500, -20},
{5500, 0.500, -15},
{8000, 0.400, -20}},
{ {2693, 0.940, 0}, // jjj NOTE:: Not Really Done Yet
{4000, 0.720, -10},
{6123, 0.870, -10},
{7755, 0.750, -18}},
{ {2693, 0.940, 0}, // ggg NOTE:: Not Really Done Yet
{4000, 0.720, -10},
{6123, 0.870, -10},
{7755, 0.750, -18}},
{ {2000, 0.700, -20}, // vvv NOTE:: Not Really Done Yet
{5257, 0.750, -15},
{7171, 0.840, -3},
{9000, 0.900, 0}},
{ { 100, 0.900, 0}, // zzz NOTE:: Not Really Done Yet
{4000, 0.500, -20},
{5500, 0.500, -15},
{8000, 0.400, -20}},
{ {2693, 0.940, 0}, // thz NOTE:: Not Really Done Yet
{4000, 0.720, -10},
{6123, 0.870, -10},
{7755, 0.750, -18}},
{ {2693, 0.940, 0}, // zhh NOTE:: Not Really Done Yet
{4000, 0.720, -10},
{6123, 0.870, -10},
{7755, 0.750, -18}}
};
Phonemes :: Phonemes(void)
{
}
Phonemes :: ~Phonemes(void)
{
}
const char *Phonemes :: name( unsigned int index )
{
if ( index > 31 ) {
cerr << "Phonemes: name index is greater than 31!" << endl;
return 0;
}
return phonemeNames[index];
}
MY_FLOAT Phonemes :: voiceGain( unsigned int index )
{
if ( index > 31 ) {
cerr << "Phonemes: voiceGain index is greater than 31!" << endl;
return 0.0;
}
return phonemeGains[index][0];
}
MY_FLOAT Phonemes :: noiseGain( unsigned int index )
{
if ( index > 31 ) {
cerr << "Phonemes: noiseGain index is greater than 31!" << endl;
return 0.0;
}
return phonemeGains[index][1];
}
MY_FLOAT Phonemes :: formantFrequency( unsigned int index, unsigned int partial )
{
if ( index > 31 ) {
cerr << "Phonemes: formantFrequency index is greater than 31!" << endl;
return 0.0;
}
if ( partial > 3 ) {
cerr << "Phonemes: formantFrequency partial is greater than 3!" << endl;
return 0.0;
}
return phonemeParameters[index][partial][0];
}
MY_FLOAT Phonemes :: formantRadius( unsigned int index, unsigned int partial )
{
if ( index > 31 ) {
cerr << "Phonemes: formantRadius index is greater than 31!" << endl;
return 0.0;
}
if ( partial > 3 ) {
cerr << "Phonemes: formantRadius partial is greater than 3!" << endl;
return 0.0;
}
return phonemeParameters[index][partial][1];
}
MY_FLOAT Phonemes :: formantGain( unsigned int index, unsigned int partial )
{
if ( index > 31 ) {
cerr << "Phonemes: formantGain index is greater than 31!" << endl;
return 0.0;
}
if ( partial > 3 ) {
cerr << "Phonemes: formantGain partial is greater than 3!" << endl;
return 0.0;
}
return phonemeParameters[index][partial][2];
}

View File

@@ -29,6 +29,13 @@ PitShift :: ~PitShift()
delete delayLine[1];
}
void PitShift :: clear()
{
delayLine[0]->clear();
delayLine[1]->clear();
lastOutput = 0.0;
}
void PitShift :: setEffectMix(MY_FLOAT mix)
{
effectMix = mix;

View File

@@ -1,97 +1,97 @@
/***************************************************/
/*! \class PoleZero
\brief STK one-pole, one-zero filter class.
This protected Filter subclass implements
a one-pole, one-zero digital filter. A
method is provided for creating an allpass
filter with a given coefficient. Another
method is provided to create a DC blocking filter.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "PoleZero.h"
PoleZero :: PoleZero() : Filter()
{
// Default setting for pass-through.
MY_FLOAT B[2] = {1.0, 0.0};
MY_FLOAT A[2] = {1.0, 0.0};
Filter::setCoefficients( 2, B, 2, A );
}
PoleZero :: ~PoleZero()
{
}
void PoleZero :: clear(void)
{
Filter::clear();
}
void PoleZero :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void PoleZero :: setB1(MY_FLOAT b1)
{
b[1] = b1;
}
void PoleZero :: setA1(MY_FLOAT a1)
{
a[1] = a1;
}
void PoleZero :: setAllpass(MY_FLOAT coefficient)
{
b[0] = coefficient;
b[1] = 1.0;
a[0] = 1.0; // just in case
a[1] = coefficient;
}
void PoleZero :: setBlockZero(MY_FLOAT thePole)
{
b[0] = 1.0;
b[1] = -1.0;
a[0] = 1.0; // just in case
a[1] = -thePole;
}
void PoleZero :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT PoleZero :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT PoleZero :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT PoleZero :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
outputs[0] = b[0] * inputs[0] + b[1] * inputs[1] - a[1] * outputs[1];
inputs[1] = inputs[0];
outputs[1] = outputs[0];
return outputs[0];
}
MY_FLOAT *PoleZero :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class PoleZero
\brief STK one-pole, one-zero filter class.
This protected Filter subclass implements
a one-pole, one-zero digital filter. A
method is provided for creating an allpass
filter with a given coefficient. Another
method is provided to create a DC blocking filter.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "PoleZero.h"
PoleZero :: PoleZero() : Filter()
{
// Default setting for pass-through.
MY_FLOAT B[2] = {1.0, 0.0};
MY_FLOAT A[2] = {1.0, 0.0};
Filter::setCoefficients( 2, B, 2, A );
}
PoleZero :: ~PoleZero()
{
}
void PoleZero :: clear(void)
{
Filter::clear();
}
void PoleZero :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void PoleZero :: setB1(MY_FLOAT b1)
{
b[1] = b1;
}
void PoleZero :: setA1(MY_FLOAT a1)
{
a[1] = a1;
}
void PoleZero :: setAllpass(MY_FLOAT coefficient)
{
b[0] = coefficient;
b[1] = 1.0;
a[0] = 1.0; // just in case
a[1] = coefficient;
}
void PoleZero :: setBlockZero(MY_FLOAT thePole)
{
b[0] = 1.0;
b[1] = -1.0;
a[0] = 1.0; // just in case
a[1] = -thePole;
}
void PoleZero :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT PoleZero :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT PoleZero :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT PoleZero :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
outputs[0] = b[0] * inputs[0] + b[1] * inputs[1] - a[1] * outputs[1];
inputs[1] = inputs[0];
outputs[1] = outputs[0];
return outputs[0];
}
MY_FLOAT *PoleZero :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

@@ -1,71 +1,71 @@
/***************************************************/
/*! \class ReedTabl
\brief STK reed table class.
This class implements a simple one breakpoint,
non-linear reed function, as described by
Smith (1986). This function is based on a
memoryless non-linear spring model of the reed
(the reed mass is ignored) which saturates when
the reed collides with the mouthpiece facing.
See McIntyre, Schumacher, & Woodhouse (1983),
Smith (1986), Hirschman, Cook, Scavone, and
others for more information.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "ReedTabl.h"
ReedTabl :: ReedTabl()
{
offSet = (MY_FLOAT) 0.6; // Offset is a bias, related to reed rest position.
slope = (MY_FLOAT) -0.8; // Slope corresponds loosely to reed stiffness.
}
ReedTabl :: ~ReedTabl()
{
}
void ReedTabl :: setOffset(MY_FLOAT aValue)
{
offSet = aValue;
}
void ReedTabl :: setSlope(MY_FLOAT aValue)
{
slope = aValue;
}
MY_FLOAT ReedTabl :: lastOut() const
{
return lastOutput;
}
MY_FLOAT ReedTabl :: tick(MY_FLOAT input)
{
// The input is differential pressure across the reed.
lastOutput = offSet + (slope * input);
// If output is > 1, the reed has slammed shut and the
// reflection function value saturates at 1.0.
if (lastOutput > 1.0) lastOutput = (MY_FLOAT) 1.0;
// This is nearly impossible in a physical system, but
// a reflection function value of -1.0 corresponds to
// an open end (and no discontinuity in bore profile).
if (lastOutput < -1.0) lastOutput = (MY_FLOAT) -1.0;
return lastOutput;
}
MY_FLOAT *ReedTabl :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class ReedTabl
\brief STK reed table class.
This class implements a simple one breakpoint,
non-linear reed function, as described by
Smith (1986). This function is based on a
memoryless non-linear spring model of the reed
(the reed mass is ignored) which saturates when
the reed collides with the mouthpiece facing.
See McIntyre, Schumacher, & Woodhouse (1983),
Smith (1986), Hirschman, Cook, Scavone, and
others for more information.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "ReedTabl.h"
ReedTabl :: ReedTabl()
{
offSet = (MY_FLOAT) 0.6; // Offset is a bias, related to reed rest position.
slope = (MY_FLOAT) -0.8; // Slope corresponds loosely to reed stiffness.
}
ReedTabl :: ~ReedTabl()
{
}
void ReedTabl :: setOffset(MY_FLOAT aValue)
{
offSet = aValue;
}
void ReedTabl :: setSlope(MY_FLOAT aValue)
{
slope = aValue;
}
MY_FLOAT ReedTabl :: lastOut() const
{
return lastOutput;
}
MY_FLOAT ReedTabl :: tick(MY_FLOAT input)
{
// The input is differential pressure across the reed.
lastOutput = offSet + (slope * input);
// If output is > 1, the reed has slammed shut and the
// reflection function value saturates at 1.0.
if (lastOutput > 1.0) lastOutput = (MY_FLOAT) 1.0;
// This is nearly impossible in a physical system, but
// a reflection function value of -1.0 corresponds to
// an open end (and no discontinuity in bore profile).
if (lastOutput < -1.0) lastOutput = (MY_FLOAT) -1.0;
return lastOutput;
}
MY_FLOAT *ReedTabl :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

0
src/Release/.placeholder Normal file
View File

View File

@@ -43,10 +43,10 @@ Rhodey :: Rhodey()
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
strcat(files[0], "sinewave.raw");
strcat(files[1], "sinewave.raw");
strcat(files[2], "sinewave.raw");
strcat(files[3], "fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );

File diff suppressed because it is too large Load Diff

View File

@@ -1,153 +1,153 @@
/***************************************************/
/*! \class RtDuplex
\brief STK realtime audio input/output class.
This class provides a simplified interface to
RtAudio for realtime audio input/output. It
is also possible to achieve duplex operation
using separate RtWvIn and RtWvOut classes, but
this class ensures better input/output
syncronization.
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 - 2002.
*/
/***************************************************/
#include "RtDuplex.h"
RtDuplex :: RtDuplex(int nChannels, MY_FLOAT sampleRate, int device, int bufferFrames, int nBuffers )
{
channels = nChannels;
bufferSize = bufferFrames;
RtAudio::RTAUDIO_FORMAT format = ( sizeof(MY_FLOAT) == 8 ) ? RtAudio::RTAUDIO_FLOAT64 : RtAudio::RTAUDIO_FLOAT32;
try {
audio = new RtAudio(&stream, device, channels, device, channels, format,
(int)sampleRate, &bufferSize, nBuffers);
data = (MY_FLOAT *) audio->getStreamBuffer(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
for (unsigned int i=0; i<channels; i++) lastOutput[i] = 0.0;
counter = 0;
stopped = true;
}
RtDuplex :: ~RtDuplex()
{
if ( !stopped )
audio->stopStream(stream);
delete audio;
delete [] lastOutput;
data = 0; // RtAudio deletes the buffer itself.
}
void RtDuplex :: start()
{
if ( stopped ) {
audio->startStream(stream);
stopped = false;
}
}
void RtDuplex :: stop()
{
if ( !stopped ) {
audio->stopStream(stream);
stopped = true;
}
}
MY_FLOAT RtDuplex :: lastOut(void) const
{
if ( channels == 1 )
return *lastOutput;
MY_FLOAT output = 0.0;
for (unsigned int i=0; i<channels; i++ ) {
output += lastOutput[i];
}
return output / channels;
}
MY_FLOAT RtDuplex :: tick(const MY_FLOAT sample)
{
if ( stopped )
start();
if (counter == 0) {
try {
audio->tickStream(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), 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();
}
MY_FLOAT *RtDuplex :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for ( unsigned int i=0; i<vectorSize; i++ )
vector[i] = tick(vector[i]);
return vector;
}
const MY_FLOAT *RtDuplex :: lastFrame() const
{
return lastOutput;
}
MY_FLOAT *RtDuplex :: tickFrame(MY_FLOAT *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(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), 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;
}
/***************************************************/
/*! \class RtDuplex
\brief STK realtime audio input/output class.
This class provides a simplified interface to
RtAudio for realtime audio input/output. It
is also possible to achieve duplex operation
using separate RtWvIn and RtWvOut classes, but
this class ensures better input/output
syncronization.
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 - 2002.
*/
/***************************************************/
#include "RtDuplex.h"
RtDuplex :: RtDuplex(int nChannels, MY_FLOAT sampleRate, int device, int bufferFrames, int nBuffers )
{
channels = nChannels;
bufferSize = bufferFrames;
RtAudio::RTAUDIO_FORMAT format = ( sizeof(MY_FLOAT) == 8 ) ? RtAudio::RTAUDIO_FLOAT64 : RtAudio::RTAUDIO_FLOAT32;
try {
audio = new RtAudio(&stream, device, channels, device, channels, format,
(int)sampleRate, &bufferSize, nBuffers);
data = (MY_FLOAT *) audio->getStreamBuffer(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
for (unsigned int i=0; i<channels; i++) lastOutput[i] = 0.0;
counter = 0;
stopped = true;
}
RtDuplex :: ~RtDuplex()
{
if ( !stopped )
audio->stopStream(stream);
delete audio;
delete [] lastOutput;
data = 0; // RtAudio deletes the buffer itself.
}
void RtDuplex :: start()
{
if ( stopped ) {
audio->startStream(stream);
stopped = false;
}
}
void RtDuplex :: stop()
{
if ( !stopped ) {
audio->stopStream(stream);
stopped = true;
}
}
MY_FLOAT RtDuplex :: lastOut(void) const
{
if ( channels == 1 )
return *lastOutput;
MY_FLOAT output = 0.0;
for (unsigned int i=0; i<channels; i++ ) {
output += lastOutput[i];
}
return output / channels;
}
MY_FLOAT RtDuplex :: tick(const MY_FLOAT sample)
{
if ( stopped )
start();
if (counter == 0) {
try {
audio->tickStream(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), 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();
}
MY_FLOAT *RtDuplex :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for ( unsigned int i=0; i<vectorSize; i++ )
vector[i] = tick(vector[i]);
return vector;
}
const MY_FLOAT *RtDuplex :: lastFrame() const
{
return lastOutput;
}
MY_FLOAT *RtDuplex :: tickFrame(MY_FLOAT *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(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), 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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,121 +1,121 @@
/***************************************************/
/*! \class RtWvIn
\brief STK realtime audio input class.
This class provides a simplified interface to
RtAudio for realtime audio input. It is a
protected subclass of WvIn.
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 pointers to multi-channel sample frames.
For single-channel data, these methods return
equivalent values.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "RtWvIn.h"
RtWvIn :: RtWvIn(int nChannels, MY_FLOAT sampleRate, int device, int bufferFrames, int nBuffers )
{
channels = nChannels;
int size = bufferFrames;
RtAudio::RTAUDIO_FORMAT format = ( sizeof(MY_FLOAT) == 8 ) ? RtAudio::RTAUDIO_FLOAT64 : RtAudio::RTAUDIO_FLOAT32;
try {
audio = new RtAudio(&stream, 0, 0, device, channels, format,
(int)sampleRate, &size, nBuffers);
data = (MY_FLOAT *) audio->getStreamBuffer(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
bufferSize = size;
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
for (unsigned int i=0; i<channels; i++) lastOutput[i] = 0.0;
counter = 0;
stopped = true;
}
RtWvIn :: ~RtWvIn()
{
if ( !stopped )
audio->stopStream(stream);
delete audio;
data = 0; // RtAudio deletes the buffer itself.
}
void RtWvIn :: start()
{
if ( stopped ) {
audio->startStream(stream);
stopped = false;
}
}
void RtWvIn :: stop()
{
if ( !stopped ) {
audio->stopStream(stream);
stopped = true;
}
}
MY_FLOAT RtWvIn :: lastOut(void) const
{
return WvIn::lastOut();
}
MY_FLOAT RtWvIn :: tick(void)
{
tickFrame();
return lastOut();
}
MY_FLOAT *RtWvIn :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for ( unsigned int i=0; i<vectorSize; i++ )
vector[i] = tick();
return vector;
}
const MY_FLOAT *RtWvIn :: lastFrame() const
{
return lastOutput;
}
const MY_FLOAT *RtWvIn :: tickFrame(void)
{
if ( stopped )
start();
if (counter == 0) {
try {
audio->tickStream(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
}
long temp = counter * channels;
for (unsigned int i=0; i<channels; i++)
lastOutput[i] = data[temp++];
counter++;
if (counter >= (long) bufferSize)
counter = 0;
return lastOutput;
}
MY_FLOAT *RtWvIn :: tickFrame(MY_FLOAT *frameVector, unsigned int frames)
{
return WvIn::tickFrame( frameVector, frames );
}
/***************************************************/
/*! \class RtWvIn
\brief STK realtime audio input class.
This class provides a simplified interface to
RtAudio for realtime audio input. It is a
protected subclass of WvIn.
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 pointers to multi-channel sample frames.
For single-channel data, these methods return
equivalent values.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "RtWvIn.h"
RtWvIn :: RtWvIn(int nChannels, MY_FLOAT sampleRate, int device, int bufferFrames, int nBuffers )
{
channels = nChannels;
int size = bufferFrames;
RtAudio::RTAUDIO_FORMAT format = ( sizeof(MY_FLOAT) == 8 ) ? RtAudio::RTAUDIO_FLOAT64 : RtAudio::RTAUDIO_FLOAT32;
try {
audio = new RtAudio(&stream, 0, 0, device, channels, format,
(int)sampleRate, &size, nBuffers);
data = (MY_FLOAT *) audio->getStreamBuffer(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
bufferSize = size;
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
for (unsigned int i=0; i<channels; i++) lastOutput[i] = 0.0;
counter = 0;
stopped = true;
}
RtWvIn :: ~RtWvIn()
{
if ( !stopped )
audio->stopStream(stream);
delete audio;
data = 0; // RtAudio deletes the buffer itself.
}
void RtWvIn :: start()
{
if ( stopped ) {
audio->startStream(stream);
stopped = false;
}
}
void RtWvIn :: stop()
{
if ( !stopped ) {
audio->stopStream(stream);
stopped = true;
}
}
MY_FLOAT RtWvIn :: lastOut(void) const
{
return WvIn::lastOut();
}
MY_FLOAT RtWvIn :: tick(void)
{
tickFrame();
return lastOut();
}
MY_FLOAT *RtWvIn :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for ( unsigned int i=0; i<vectorSize; i++ )
vector[i] = tick();
return vector;
}
const MY_FLOAT *RtWvIn :: lastFrame() const
{
return lastOutput;
}
const MY_FLOAT *RtWvIn :: tickFrame(void)
{
if ( stopped )
start();
if (counter == 0) {
try {
audio->tickStream(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
}
long temp = counter * channels;
for (unsigned int i=0; i<channels; i++)
lastOutput[i] = data[temp++];
counter++;
if (counter >= (long) bufferSize)
counter = 0;
return lastOutput;
}
MY_FLOAT *RtWvIn :: tickFrame(MY_FLOAT *frameVector, unsigned int frames)
{
return WvIn::tickFrame( frameVector, frames );
}

View File

@@ -1,125 +1,125 @@
/***************************************************/
/*! \class RtWvOut
\brief STK realtime audio output class.
This class provides a simplified interface to
RtAudio for realtime audio output. It is a
protected subclass of WvOut.
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
takes a pointer to multi-channel sample
frame data.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "RtWvOut.h"
#include <stdio.h>
RtWvOut :: RtWvOut(unsigned int nChannels, MY_FLOAT sampleRate, int device, int bufferFrames, int nBuffers )
{
// We'll let RtAudio deal with channel and srate limitations.
channels = nChannels;
bufferSize = bufferFrames;
RtAudio::RTAUDIO_FORMAT format = ( sizeof(MY_FLOAT) == 8 ) ? RtAudio::RTAUDIO_FLOAT64 : RtAudio::RTAUDIO_FLOAT32;
try {
audio = new RtAudio(&stream, device, (int)channels, 0, 0, format,
(int)sampleRate, &bufferSize, nBuffers);
data = (MY_FLOAT *) audio->getStreamBuffer(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
stopped = true;
}
RtWvOut :: ~RtWvOut()
{
if ( !stopped )
audio->stopStream(stream);
delete audio;
data = 0; // RtAudio deletes the buffer itself.
}
void RtWvOut :: start()
{
if ( stopped ) {
audio->startStream(stream);
stopped = false;
}
}
void RtWvOut :: stop()
{
if ( !stopped ) {
audio->stopStream(stream);
stopped = true;
}
}
unsigned long RtWvOut :: getFrames( void ) const
{
return totalCount;
}
MY_FLOAT RtWvOut :: getTime( void ) const
{
return (MY_FLOAT) totalCount / Stk::sampleRate();
}
void RtWvOut :: tick(const MY_FLOAT sample)
{
if ( stopped )
start();
for ( unsigned int j=0; j<channels; j++ )
data[counter*channels+j] = sample;
counter++;
totalCount++;
if ( counter >= (unsigned int )bufferSize ) {
try {
audio->tickStream(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
counter = 0;
}
}
void RtWvOut :: tick(const MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
tick( vector[i] );
}
void RtWvOut :: tickFrame(const MY_FLOAT *frameVector, unsigned int frames)
{
if ( stopped )
start();
for ( unsigned int i=0; i<frames; i++ ) {
for ( unsigned int j=0; j<channels; j++ ) {
data[counter*channels+j] = frameVector[i*channels+j];
}
counter++;
if ( counter >= (unsigned int)bufferSize ) {
try {
audio->tickStream(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
counter = 0;
}
}
}
/***************************************************/
/*! \class RtWvOut
\brief STK realtime audio output class.
This class provides a simplified interface to
RtAudio for realtime audio output. It is a
protected subclass of WvOut.
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
takes a pointer to multi-channel sample
frame data.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "RtWvOut.h"
#include <stdio.h>
RtWvOut :: RtWvOut(unsigned int nChannels, MY_FLOAT sampleRate, int device, int bufferFrames, int nBuffers )
{
// We'll let RtAudio deal with channel and srate limitations.
channels = nChannels;
bufferSize = bufferFrames;
RtAudio::RTAUDIO_FORMAT format = ( sizeof(MY_FLOAT) == 8 ) ? RtAudio::RTAUDIO_FLOAT64 : RtAudio::RTAUDIO_FLOAT32;
try {
audio = new RtAudio(&stream, device, (int)channels, 0, 0, format,
(int)sampleRate, &bufferSize, nBuffers);
data = (MY_FLOAT *) audio->getStreamBuffer(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
stopped = true;
}
RtWvOut :: ~RtWvOut()
{
if ( !stopped )
audio->stopStream(stream);
delete audio;
data = 0; // RtAudio deletes the buffer itself.
}
void RtWvOut :: start()
{
if ( stopped ) {
audio->startStream(stream);
stopped = false;
}
}
void RtWvOut :: stop()
{
if ( !stopped ) {
audio->stopStream(stream);
stopped = true;
}
}
unsigned long RtWvOut :: getFrames( void ) const
{
return totalCount;
}
MY_FLOAT RtWvOut :: getTime( void ) const
{
return (MY_FLOAT) totalCount / Stk::sampleRate();
}
void RtWvOut :: tick(const MY_FLOAT sample)
{
if ( stopped )
start();
for ( unsigned int j=0; j<channels; j++ )
data[counter*channels+j] = sample;
counter++;
totalCount++;
if ( counter >= (unsigned int )bufferSize ) {
try {
audio->tickStream(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
counter = 0;
}
}
void RtWvOut :: tick(const MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
tick( vector[i] );
}
void RtWvOut :: tickFrame(const MY_FLOAT *frameVector, unsigned int frames)
{
if ( stopped )
start();
for ( unsigned int i=0; i<frames; i++ ) {
for ( unsigned int j=0; j<channels; j++ ) {
data[counter*channels+j] = frameVector[i*channels+j];
}
counter++;
if ( counter >= (unsigned int)bufferSize ) {
try {
audio->tickStream(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
counter = 0;
}
}
}

View File

@@ -1,350 +1,350 @@
/***************************************************/
/*! \class SKINI
\brief STK SKINI parsing class
This class parses SKINI formatted text
messages. It can be used to parse individual
messages or it can be passed an entire file.
The file specification is Perry's and his
alone, but it's all text so it shouldn't be to
hard to figure out.
SKINI (Synthesis toolKit Instrument Network
Interface) is like MIDI, but allows for
floating-point control changes, note numbers,
etc. The following example causes a sharp
middle C to be played with a velocity of 111.132:
noteOn 60.01 111.13
See also SKINI.txt.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "SKINI.h"
#include <string.h>
#include <stdlib.h>
// Constructor for use when parsing SKINI strings (coming over socket
// for example. Use parseThis() method with string pointer.
SKINI :: SKINI()
{
}
// Constructor for reading SKINI files ... use nextMessage() method.
SKINI :: SKINI(char *fileName)
{
char msg[256];
myFile = fopen(fileName,"r");
if ((int) myFile < 0) {
sprintf(msg, "SKINI: Could not open or find file (%s).", fileName);
handleError(msg, StkError::FILE_NOT_FOUND);
}
this->nextMessage();
}
SKINI :: ~SKINI()
{
}
/***************** SOME HANDY ROUTINES *******************/
#include "SKINI.tbl"
#define __SK_MAX_FIELDS_ 5
#define __SK_MAX_SIZE_ 32
short ignore(char aChar)
{
short ignoreIt = 0;
if (aChar == 0) ignoreIt = 1; // Null String Termination
if (aChar == '\n') ignoreIt = 1; // Carraige Return???
if (aChar == '/') ignoreIt = 2; // Comment Line
return ignoreIt;
}
short delimit(char aChar)
{
if (aChar == ' ' || // Space
aChar == ',' || // Or Comma
aChar == '\t') // Or Tab
return 1;
else
return 0;
}
short nextChar(char* aString)
{
int i;
for (i=0;i<__SK_MAX_SIZE_;i++) {
if ( aString[i] != ' ' && // Space
aString[i] != ',' && // Or Comma
aString[i] != '\t' ) // Or Tab
return i;
}
return 1024;
}
int subStrings(char *aString,
char someStrings[__SK_MAX_FIELDS_][__SK_MAX_SIZE_],
int somePointrs[__SK_MAX_FIELDS_],
char *remainderString)
{
int notDone,howMany,point,temp;
notDone = 1;
howMany = 0;
point = 0;
temp = nextChar(aString);
if (temp >= __SK_MAX_SIZE_) {
notDone = 0;
printf("Confusion here: Ignoring this line\n");
printf("%s\n",aString);
return howMany;
}
point = temp;
somePointrs[howMany] = point;
temp = 0;
while (notDone) {
if (aString[point] == '\n') {
notDone = 0;
}
else {
someStrings[howMany][temp++] = aString[point++];
if (temp >= __SK_MAX_SIZE_) {
howMany = 0;
return howMany;
}
if (delimit(aString[point]) || aString[point] == '\n') {
someStrings[howMany][temp] = 0;
howMany += 1;
if (howMany < __SK_MAX_FIELDS_) {
temp = nextChar(&aString[point]);
point += temp;
somePointrs[howMany-1] = point;
temp = 0;
}
else {
temp = 0;
somePointrs[howMany-1] = point;
while(aString[point] != '\n')
remainderString[temp++] = aString[point++];
remainderString[temp] = aString[point];
}
}
}
}
// printf("Got: %i Strings:\n",howMany);
// for (temp=0;temp<howMany;temp++)
// printf("%s\n",someStrings[temp]);
return howMany;
}
/**************** THE ENCHILLADA !!!! **********************/
/*** This function parses a single string (if it can) ****/
/*** of SKINI message, setting the appropriate variables ***/
/*************************************************************/
long SKINI :: parseThis(char* aString)
{
int which,aField;
int temp,temp2;
char someStrings[__SK_MAX_FIELDS_][__SK_MAX_SIZE_];
int somePointrs[__SK_MAX_FIELDS_];
temp = nextChar(aString);
if ((which = ignore(aString[temp]))) {
if (which == 2) printf("// CommentLine: %s\n",aString);
messageType = 0;
return messageType;
}
else {
temp = subStrings(aString,someStrings,somePointrs,remainderString);
if (temp > 0)
which = 0;
aField = 0;
strcpy(msgTypeString,someStrings[aField]);
while ((which < __SK_MaxMsgTypes_) &&
(strcmp(msgTypeString,
skini_msgs[which].messageString))) {
which += 1;
}
if (which >= __SK_MaxMsgTypes_) {
messageType = 0;
printf("Couldn't parse this message field: =%s\n %s\n",
msgTypeString,aString);
return messageType;
}
else {
messageType = skini_msgs[which].type;
// printf("Message Token = %s type = %i\n", msgTypeString,messageType);
}
aField += 1;
if (someStrings[aField][0] == '=') {
deltaTime = (MY_FLOAT) atof(&someStrings[aField][1]);
deltaTime = -deltaTime;
}
else {
deltaTime = (MY_FLOAT) atof(someStrings[aField]);
}
// printf("DeltaTime = %f\n",deltaTime);
aField += 1;
channel = atoi(someStrings[aField]);
// printf("Channel = %i\n",channel);
aField += 1;
if (skini_msgs[which].data2 != NOPE) {
if (skini_msgs[which].data2 == SK_INT) {
byteTwoInt = atoi(someStrings[aField]);
byteTwo = (MY_FLOAT) byteTwoInt;
}
else if (skini_msgs[which].data2 == SK_DBL) {
byteTwo = (MY_FLOAT) atof(someStrings[aField]);
byteTwoInt = (long) byteTwo;
}
else if (skini_msgs[which].data2 == SK_STR) {
temp = somePointrs[aField-1]; /* Hack Danger Here, Why -1??? */
temp2 = 0;
while (aString[temp] != '\n') {
remainderString[temp2++] = aString[temp++];
}
remainderString[temp2] = 0;
}
else {
byteTwoInt = skini_msgs[which].data2;
byteTwo = (MY_FLOAT) byteTwoInt;
aField -= 1;
}
aField += 1;
if (skini_msgs[which].data3 != NOPE) {
if (skini_msgs[which].data3 == SK_INT) {
byteThreeInt = atoi(someStrings[aField]);
byteThree = (MY_FLOAT) byteThreeInt;
}
else if (skini_msgs[which].data3 == SK_DBL) {
byteThree = (MY_FLOAT) atof(someStrings[aField]);
byteThreeInt = (long) byteThree;
}
else if (skini_msgs[which].data3 == SK_STR) {
temp = somePointrs[aField-1]; /* Hack Danger Here, Why -1??? */
temp2 = 0;
while (aString[temp] != '\n') {
remainderString[temp2++] = aString[temp++];
}
remainderString[temp2] = 0;
}
else {
byteThreeInt = skini_msgs[which].data3;
byteThree = (MY_FLOAT) byteThreeInt;
}
}
else {
byteThreeInt = byteTwoInt;
byteThree = byteTwo;
}
}
}
return messageType;
}
long SKINI :: nextMessage()
{
int notDone;
char inputString[1024];
notDone = 1;
while (notDone) {
notDone = 0;
if (!fgets(inputString,1024,myFile)) {
printf("// End of Score. Thanks for using SKINI!!\n");
messageType = -1;
return messageType;
}
else if (parseThis(inputString) == 0) {
notDone = 1;
}
}
return messageType;
}
long SKINI :: getType() const
{
return messageType;
}
long SKINI :: getChannel() const
{
return channel;
}
MY_FLOAT SKINI :: getDelta() const
{
return deltaTime;
}
MY_FLOAT SKINI :: getByteTwo() const
{
return byteTwo;
}
long SKINI :: getByteTwoInt() const
{
return byteTwoInt;
}
MY_FLOAT SKINI :: getByteThree() const
{
return byteThree;
}
long SKINI :: getByteThreeInt() const
{
return byteThreeInt;
}
const char* SKINI :: getRemainderString()
{
return remainderString;
}
const char* SKINI :: getMessageTypeString()
{
return msgTypeString;
}
const char* SKINI :: whatsThisType(long type)
{
int i = 0;
whatString[0] = 0;
for ( i=0; i<__SK_MaxMsgTypes_; i++ ) {
if ( type == skini_msgs[i].type ) {
strcat(whatString, skini_msgs[i].messageString);
strcat(whatString, ",");
}
}
return whatString;
}
const char* SKINI :: whatsThisController(long contNum)
{
int i = 0;
whatString[0] = 0;
for ( i=0; i<__SK_MaxMsgTypes_; i++) {
if ( skini_msgs[i].type == __SK_ControlChange_
&& contNum == skini_msgs[i].data2) {
strcat(whatString, skini_msgs[i].messageString);
strcat(whatString, ",");
}
}
return whatString;
}
/***************************************************/
/*! \class SKINI
\brief STK SKINI parsing class
This class parses SKINI formatted text
messages. It can be used to parse individual
messages or it can be passed an entire file.
The file specification is Perry's and his
alone, but it's all text so it shouldn't be to
hard to figure out.
SKINI (Synthesis toolKit Instrument Network
Interface) is like MIDI, but allows for
floating-point control changes, note numbers,
etc. The following example causes a sharp
middle C to be played with a velocity of 111.132:
noteOn 60.01 111.13
See also SKINI.txt.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "SKINI.h"
#include <string.h>
#include <stdlib.h>
// Constructor for use when parsing SKINI strings (coming over socket
// for example. Use parseThis() method with string pointer.
SKINI :: SKINI()
{
}
// Constructor for reading SKINI files ... use nextMessage() method.
SKINI :: SKINI(char *fileName)
{
char msg[256];
myFile = fopen(fileName,"r");
if ((int) myFile < 0) {
sprintf(msg, "SKINI: Could not open or find file (%s).", fileName);
handleError(msg, StkError::FILE_NOT_FOUND);
}
this->nextMessage();
}
SKINI :: ~SKINI()
{
}
/***************** SOME HANDY ROUTINES *******************/
#include "SKINI.tbl"
#define __SK_MAX_FIELDS_ 5
#define __SK_MAX_SIZE_ 32
short ignore(char aChar)
{
short ignoreIt = 0;
if (aChar == 0) ignoreIt = 1; // Null String Termination
if (aChar == '\n') ignoreIt = 1; // Carraige Return???
if (aChar == '/') ignoreIt = 2; // Comment Line
return ignoreIt;
}
short delimit(char aChar)
{
if (aChar == ' ' || // Space
aChar == ',' || // Or Comma
aChar == '\t') // Or Tab
return 1;
else
return 0;
}
short nextChar(char* aString)
{
int i;
for (i=0;i<__SK_MAX_SIZE_;i++) {
if ( aString[i] != ' ' && // Space
aString[i] != ',' && // Or Comma
aString[i] != '\t' ) // Or Tab
return i;
}
return 1024;
}
int subStrings(char *aString,
char someStrings[__SK_MAX_FIELDS_][__SK_MAX_SIZE_],
int somePointrs[__SK_MAX_FIELDS_],
char *remainderString)
{
int notDone,howMany,point,temp;
notDone = 1;
howMany = 0;
point = 0;
temp = nextChar(aString);
if (temp >= __SK_MAX_SIZE_) {
notDone = 0;
printf("Confusion here: Ignoring this line\n");
printf("%s\n",aString);
return howMany;
}
point = temp;
somePointrs[howMany] = point;
temp = 0;
while (notDone) {
if (aString[point] == '\n') {
notDone = 0;
}
else {
someStrings[howMany][temp++] = aString[point++];
if (temp >= __SK_MAX_SIZE_) {
howMany = 0;
return howMany;
}
if (delimit(aString[point]) || aString[point] == '\n') {
someStrings[howMany][temp] = 0;
howMany += 1;
if (howMany < __SK_MAX_FIELDS_) {
temp = nextChar(&aString[point]);
point += temp;
somePointrs[howMany-1] = point;
temp = 0;
}
else {
temp = 0;
somePointrs[howMany-1] = point;
while(aString[point] != '\n')
remainderString[temp++] = aString[point++];
remainderString[temp] = aString[point];
}
}
}
}
// printf("Got: %i Strings:\n",howMany);
// for (temp=0;temp<howMany;temp++)
// printf("%s\n",someStrings[temp]);
return howMany;
}
/**************** THE ENCHILLADA !!!! **********************/
/*** This function parses a single string (if it can) ****/
/*** of SKINI message, setting the appropriate variables ***/
/*************************************************************/
long SKINI :: parseThis(char* aString)
{
int which,aField;
int temp,temp2;
char someStrings[__SK_MAX_FIELDS_][__SK_MAX_SIZE_];
int somePointrs[__SK_MAX_FIELDS_];
temp = nextChar(aString);
if ((which = ignore(aString[temp]))) {
if (which == 2) printf("// CommentLine: %s\n",aString);
messageType = 0;
return messageType;
}
else {
temp = subStrings(aString,someStrings,somePointrs,remainderString);
if (temp > 0)
which = 0;
aField = 0;
strcpy(msgTypeString,someStrings[aField]);
while ((which < __SK_MaxMsgTypes_) &&
(strcmp(msgTypeString,
skini_msgs[which].messageString))) {
which += 1;
}
if (which >= __SK_MaxMsgTypes_) {
messageType = 0;
printf("Couldn't parse this message field: =%s\n %s\n",
msgTypeString,aString);
return messageType;
}
else {
messageType = skini_msgs[which].type;
// printf("Message Token = %s type = %i\n", msgTypeString,messageType);
}
aField += 1;
if (someStrings[aField][0] == '=') {
deltaTime = (MY_FLOAT) atof(&someStrings[aField][1]);
deltaTime = -deltaTime;
}
else {
deltaTime = (MY_FLOAT) atof(someStrings[aField]);
}
// printf("DeltaTime = %f\n",deltaTime);
aField += 1;
channel = atoi(someStrings[aField]);
// printf("Channel = %i\n",channel);
aField += 1;
if (skini_msgs[which].data2 != NOPE) {
if (skini_msgs[which].data2 == SK_INT) {
byteTwoInt = atoi(someStrings[aField]);
byteTwo = (MY_FLOAT) byteTwoInt;
}
else if (skini_msgs[which].data2 == SK_DBL) {
byteTwo = (MY_FLOAT) atof(someStrings[aField]);
byteTwoInt = (long) byteTwo;
}
else if (skini_msgs[which].data2 == SK_STR) {
temp = somePointrs[aField-1]; /* Hack Danger Here, Why -1??? */
temp2 = 0;
while (aString[temp] != '\n') {
remainderString[temp2++] = aString[temp++];
}
remainderString[temp2] = 0;
}
else {
byteTwoInt = skini_msgs[which].data2;
byteTwo = (MY_FLOAT) byteTwoInt;
aField -= 1;
}
aField += 1;
if (skini_msgs[which].data3 != NOPE) {
if (skini_msgs[which].data3 == SK_INT) {
byteThreeInt = atoi(someStrings[aField]);
byteThree = (MY_FLOAT) byteThreeInt;
}
else if (skini_msgs[which].data3 == SK_DBL) {
byteThree = (MY_FLOAT) atof(someStrings[aField]);
byteThreeInt = (long) byteThree;
}
else if (skini_msgs[which].data3 == SK_STR) {
temp = somePointrs[aField-1]; /* Hack Danger Here, Why -1??? */
temp2 = 0;
while (aString[temp] != '\n') {
remainderString[temp2++] = aString[temp++];
}
remainderString[temp2] = 0;
}
else {
byteThreeInt = skini_msgs[which].data3;
byteThree = (MY_FLOAT) byteThreeInt;
}
}
else {
byteThreeInt = byteTwoInt;
byteThree = byteTwo;
}
}
}
return messageType;
}
long SKINI :: nextMessage()
{
int notDone;
char inputString[1024];
notDone = 1;
while (notDone) {
notDone = 0;
if (!fgets(inputString,1024,myFile)) {
printf("// End of Score. Thanks for using SKINI!!\n");
messageType = -1;
return messageType;
}
else if (parseThis(inputString) == 0) {
notDone = 1;
}
}
return messageType;
}
long SKINI :: getType() const
{
return messageType;
}
long SKINI :: getChannel() const
{
return channel;
}
MY_FLOAT SKINI :: getDelta() const
{
return deltaTime;
}
MY_FLOAT SKINI :: getByteTwo() const
{
return byteTwo;
}
long SKINI :: getByteTwoInt() const
{
return byteTwoInt;
}
MY_FLOAT SKINI :: getByteThree() const
{
return byteThree;
}
long SKINI :: getByteThreeInt() const
{
return byteThreeInt;
}
const char* SKINI :: getRemainderString()
{
return remainderString;
}
const char* SKINI :: getMessageTypeString()
{
return msgTypeString;
}
const char* SKINI :: whatsThisType(long type)
{
int i = 0;
whatString[0] = 0;
for ( i=0; i<__SK_MaxMsgTypes_; i++ ) {
if ( type == skini_msgs[i].type ) {
strcat(whatString, skini_msgs[i].messageString);
strcat(whatString, ",");
}
}
return whatString;
}
const char* SKINI :: whatsThisController(long contNum)
{
int i = 0;
whatString[0] = 0;
for ( i=0; i<__SK_MaxMsgTypes_; i++) {
if ( skini_msgs[i].type == __SK_ControlChange_
&& contNum == skini_msgs[i].data2) {
strcat(whatString, skini_msgs[i].messageString);
strcat(whatString, ",");
}
}
return whatString;
}

View File

@@ -57,7 +57,7 @@ Saxofony :: Saxofony(MY_FLOAT lowestFrequency)
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char path[128];
strcpy(path, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(path,"rawwaves/sinewave.raw"), TRUE );
vibrato = new WaveLoop( strcat(path,"sinewave.raw"), TRUE );
vibrato->setFrequency((MY_FLOAT) 5.735);
outputGain = (MY_FLOAT) 0.3;

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,7 @@ Simple :: Simple()
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
loop = new WaveLoop( strcat(file,"rawwaves/impuls10.raw"), TRUE );
loop = new WaveLoop( strcat(file,"impuls10.raw"), TRUE );
filter = new OnePole(0.5);
noise = new Noise;

128
src/SingWave.cpp Normal file
View File

@@ -0,0 +1,128 @@
/***************************************************/
/*! \class SingWave
\brief STK "singing" looped soundfile class.
This class contains all that is needed to make
a pitched musical sound, like a simple voice
or violin. In general, it will not be used
alone because of munchkinification effects
from pitch shifting. It will be used as an
excitation source for other instruments.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "SingWave.h"
SingWave :: SingWave(const char *fileName, bool raw)
{
// An exception could be thrown here.
wave = new WaveLoop( fileName, raw );
rate = 1.0;
sweepRate = 0.001;
modulator = new Modulate();
modulator->setVibratoRate( 6.0 );
modulator->setVibratoGain( 0.04 );
modulator->setRandomGain( 0.005 );
envelope = new Envelope;
pitchEnvelope = new Envelope;
setFrequency( 75.0 );
pitchEnvelope->setRate( 1.0 );
this->tick();
this->tick();
pitchEnvelope->setRate( sweepRate * rate );
}
SingWave :: ~SingWave()
{
delete wave;
delete modulator;
delete envelope;
delete pitchEnvelope;
}
void SingWave :: reset()
{
wave->reset();
lastOutput = 0.0;
}
void SingWave :: normalize()
{
wave->normalize();
}
void SingWave :: normalize(MY_FLOAT newPeak)
{
wave->normalize( newPeak );
}
void SingWave :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT temp = rate;
rate = wave->getSize() * frequency / Stk::sampleRate();
temp -= rate;
if ( temp < 0) temp = -temp;
pitchEnvelope->setTarget( rate );
pitchEnvelope->setRate( sweepRate * temp );
}
void SingWave :: setVibratoRate(MY_FLOAT aRate)
{
modulator->setVibratoRate( aRate );
}
void SingWave :: setVibratoGain(MY_FLOAT gain)
{
modulator->setVibratoGain(gain);
}
void SingWave :: setRandomGain(MY_FLOAT gain)
{
modulator->setRandomGain(gain);
}
void SingWave :: setSweepRate(MY_FLOAT aRate)
{
sweepRate = aRate;
}
void SingWave :: setGainRate(MY_FLOAT aRate)
{
envelope->setRate(aRate);
}
void SingWave :: setGainTarget(MY_FLOAT target)
{
envelope->setTarget(target);
}
void SingWave :: noteOn()
{
envelope->keyOn();
}
void SingWave :: noteOff()
{
envelope->keyOff();
}
MY_FLOAT SingWave :: tick()
{
// Set the wave rate.
MY_FLOAT newRate = pitchEnvelope->tick();
newRate += newRate * modulator->tick();
wave->setRate( newRate );
lastOutput = wave->tick();
lastOutput *= envelope->tick();
return lastOutput;
}
MY_FLOAT SingWave :: lastOut()
{
return lastOutput;
}

View File

@@ -1,11 +1,21 @@
/******************************************/
/* Karplus-Strong Sitar1 string model */
/* by Perry Cook, 1995-96 */
/* */
/* There exist at least two patents, */
/* assigned to Stanford, bearing the */
/* names of Karplus and/or Strong. */
/******************************************/
/***************************************************/
/*! \class Sitar
\brief STK sitar string model class.
This class implements a sitar plucked string
physical model based on the Karplus-Strong
algorithm.
This is a digital waveguide model, making its
use possibly subject to patents held by
Stanford University, Yamaha, and others.
There exist at least two patents, assigned to
Stanford, bearing the names of Karplus and/or
Strong.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Sitar.h"
#include <math.h>

View File

@@ -1,240 +1,241 @@
/***************************************************/
/*! \class Socket
\brief STK TCP socket client/server class.
This class provides a uniform cross-platform
TCP socket client or socket server interface.
Methods are provided for reading or writing
data buffers to/from connections. This class
also provides a number of static functions for
use with external socket descriptors.
The user is responsible for checking the values
returned by the read/write methods. Values
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 - 2002.
*/
/***************************************************/
#include "Socket.h"
#include <stdio.h>
#include <string.h>
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#elif defined(__OS_WINDOWS__)
#include <winsock.h>
#endif
Socket :: Socket( int port )
{
soket = -1;
server = true;
poort = port;
// Create a socket server.
#if defined(__OS_WINDOWS__) // windoze-only stuff
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1,1);
WSAStartup(wVersionRequested, &wsaData);
if (wsaData.wVersion != wVersionRequested) {
sprintf( msg, "Socket: Incompatible Windows socket library version!" );
handleError( msg, StkError::PROCESS_SOCKET );
}
#endif
// Create the server-side socket
soket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (soket < 0) {
sprintf(msg, "Socket: Couldn't create socket server!");
handleError( msg, StkError::PROCESS_SOCKET );
}
struct sockaddr_in mysocket;
mysocket.sin_family=AF_INET;
mysocket.sin_addr.s_addr=INADDR_ANY;
mysocket.sin_port=htons( port );
// Bind socket to the appropriate port and interface (INADDR_ANY)
if (bind(soket, (struct sockaddr *)&mysocket, sizeof(mysocket)) < 0) {
sprintf(msg, "Socket: Couldn't bind socket!");
handleError( msg, StkError::PROCESS_SOCKET );
}
// Listen for incoming connection(s)
if ( listen(soket, 1) < 0 ) {
sprintf(msg, "Socket: Couldn't start server listening!");
handleError( msg, StkError::PROCESS_SOCKET );
}
}
Socket :: Socket(int port, const char *hostname )
{
soket = -1;
server = false;
poort = port;
#if defined(__OS_WINDOWS__) // windoze-only stuff
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1,1);
WSAStartup(wVersionRequested, &wsaData);
if (wsaData.wVersion != wVersionRequested) {
sprintf( msg, "Socket: Incompatible Windows socket library version!" );
handleError( msg, StkError::PROCESS_SOCKET );
}
#endif
// Create a socket client connection.
connect( port, hostname );
}
Socket :: ~Socket()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
::close( soket );
#elif defined(__OS_WINDOWS__)
::closesocket( soket );
WSACleanup();
#endif
}
int Socket :: connect( int port, const char *hostname )
{
// This method is for client connections only!
if ( server == true ) return -1;
// Close an existing connection if it exists.
if ( isValid( soket ) ) this->close();
// Create the client-side socket
soket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (soket < 0) {
sprintf(msg, "Socket: Couldn't create socket client!");
handleError( msg, StkError::PROCESS_SOCKET );
}
struct hostent *hostp;
if ( (hostp = gethostbyname(hostname)) == 0 ) {
sprintf(msg, "Socket: unknown host (%s)!", hostname);
handleError( msg, StkError::PROCESS_SOCKET_IPADDR );
}
// Fill in the address structure
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
memcpy((void *)&server_address.sin_addr, hostp->h_addr, hostp->h_length);
server_address.sin_port = htons(port);
// Connect to the server
if ( ::connect(soket, (struct sockaddr *)&server_address,
sizeof(server_address) ) < 0) {
sprintf(msg, "Socket: Couldn't connect to socket server!");
handleError( msg, StkError::PROCESS_SOCKET );
}
return soket;
}
int Socket :: socket( void ) const
{
return soket;
}
int Socket :: port( void ) const
{
return poort;
}
int Socket :: accept( void )
{
if ( server )
return ::accept( soket, NULL, NULL );
else
return -1;
}
bool Socket :: isValid( int socket )
{
return socket != -1;
}
void Socket :: setBlocking( int socket, bool enable )
{
if ( !isValid( socket ) ) return;
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
int tmp = ::fcntl(socket, F_GETFL, 0);
if ( tmp >= 0 )
tmp = ::fcntl( socket, F_SETFL, enable ? (tmp &~ O_NONBLOCK) : (tmp | O_NONBLOCK) );
#elif defined(__OS_WINDOWS__)
unsigned long non_block = !enable;
ioctlsocket( socket, FIONBIO, &non_block );
#endif
}
void Socket :: close( void )
{
if ( !isValid( soket ) ) return;
this->close( soket );
soket = -1;
}
void Socket :: close( int socket )
{
if ( !isValid( socket ) ) return;
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
::close( socket );
#elif defined(__OS_WINDOWS__)
::closesocket( socket );
#endif
}
int Socket :: writeBuffer(const void *buffer, long bufferSize, int flags )
{
if ( !isValid( soket ) ) return -1;
return send( soket, (const char *)buffer, bufferSize, flags );
}
int Socket :: writeBuffer(int socket, const void *buffer, long bufferSize, int flags )
{
if ( !isValid( socket ) ) return -1;
return send( socket, (const char *)buffer, bufferSize, flags );
}
int Socket :: readBuffer(void *buffer, long bufferSize, int flags )
{
if ( !isValid( soket ) ) return -1;
return recv( soket, (char *)buffer, bufferSize, flags );
}
int Socket :: readBuffer(int socket, void *buffer, long bufferSize, int flags )
{
if ( !isValid( socket ) ) return -1;
return recv( socket, (char *)buffer, bufferSize, flags );
}
/***************************************************/
/*! \class Socket
\brief STK TCP socket client/server class.
This class provides a uniform cross-platform
TCP socket client or socket server interface.
Methods are provided for reading or writing
data buffers to/from connections. This class
also provides a number of static functions for
use with external socket descriptors.
The user is responsible for checking the values
returned by the read/write methods. Values
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 - 2002.
*/
/***************************************************/
#include "Socket.h"
#include <stdio.h>
#include <string.h>
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#elif defined(__OS_WINDOWS__)
#include <winsock.h>
#endif
Socket :: Socket( int port )
{
soket = -1;
server = true;
poort = port;
// Create a socket server.
#if defined(__OS_WINDOWS__) // windoze-only stuff
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1,1);
WSAStartup(wVersionRequested, &wsaData);
if (wsaData.wVersion != wVersionRequested) {
sprintf( msg, "Socket: Incompatible Windows socket library version!" );
handleError( msg, StkError::PROCESS_SOCKET );
}
#endif
// Create the server-side socket
soket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (soket < 0) {
sprintf(msg, "Socket: Couldn't create socket server!");
handleError( msg, StkError::PROCESS_SOCKET );
}
struct sockaddr_in mysocket;
mysocket.sin_family=AF_INET;
mysocket.sin_addr.s_addr=INADDR_ANY;
mysocket.sin_port=htons( port );
// Bind socket to the appropriate port and interface (INADDR_ANY)
if (bind(soket, (struct sockaddr *)&mysocket, sizeof(mysocket)) < 0) {
sprintf(msg, "Socket: Couldn't bind socket!");
handleError( msg, StkError::PROCESS_SOCKET );
}
// Listen for incoming connection(s)
if ( listen(soket, 1) < 0 ) {
sprintf(msg, "Socket: Couldn't start server listening!");
handleError( msg, StkError::PROCESS_SOCKET );
}
}
Socket :: Socket(int port, const char *hostname )
{
soket = -1;
server = false;
poort = port;
#if defined(__OS_WINDOWS__) // windoze-only stuff
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1,1);
WSAStartup(wVersionRequested, &wsaData);
if (wsaData.wVersion != wVersionRequested) {
sprintf( msg, "Socket: Incompatible Windows socket library version!" );
handleError( msg, StkError::PROCESS_SOCKET );
}
#endif
// Create a socket client connection.
connect( port, hostname );
}
Socket :: ~Socket()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
::close( soket );
#elif defined(__OS_WINDOWS__)
::closesocket( soket );
WSACleanup();
#endif
}
int Socket :: connect( int port, const char *hostname )
{
// This method is for client connections only!
if ( server == true ) return -1;
// Close an existing connection if it exists.
if ( isValid( soket ) ) this->close();
// Create the client-side socket
soket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (soket < 0) {
sprintf(msg, "Socket: Couldn't create socket client!");
handleError( msg, StkError::PROCESS_SOCKET );
}
struct hostent *hostp;
if ( (hostp = gethostbyname(hostname)) == 0 ) {
sprintf(msg, "Socket: unknown host (%s)!", hostname);
handleError( msg, StkError::PROCESS_SOCKET_IPADDR );
}
// Fill in the address structure
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
memcpy((void *)&server_address.sin_addr, hostp->h_addr, hostp->h_length);
server_address.sin_port = htons(port);
// Connect to the server
if ( ::connect(soket, (struct sockaddr *)&server_address,
sizeof(server_address) ) < 0) {
sprintf(msg, "Socket: Couldn't connect to socket server!");
handleError( msg, StkError::PROCESS_SOCKET );
}
return soket;
}
int Socket :: socket( void ) const
{
return soket;
}
int Socket :: port( void ) const
{
return poort;
}
int Socket :: accept( void )
{
if ( server )
return ::accept( soket, NULL, NULL );
else
return -1;
}
bool Socket :: isValid( int socket )
{
return socket != -1;
}
void Socket :: setBlocking( int socket, bool enable )
{
if ( !isValid( socket ) ) return;
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
int tmp = ::fcntl(socket, F_GETFL, 0);
if ( tmp >= 0 )
tmp = ::fcntl( socket, F_SETFL, enable ? (tmp &~ O_NONBLOCK) : (tmp | O_NONBLOCK) );
#elif defined(__OS_WINDOWS__)
unsigned long non_block = !enable;
ioctlsocket( socket, FIONBIO, &non_block );
#endif
}
void Socket :: close( void )
{
if ( !isValid( soket ) ) return;
this->close( soket );
soket = -1;
}
void Socket :: close( int socket )
{
if ( !isValid( socket ) ) return;
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
::close( socket );
#elif defined(__OS_WINDOWS__)
::closesocket( socket );
#endif
}
int Socket :: writeBuffer(const void *buffer, long bufferSize, int flags )
{
if ( !isValid( soket ) ) return -1;
return send( soket, (const char *)buffer, bufferSize, flags );
}
int Socket :: writeBuffer(int socket, const void *buffer, long bufferSize, int flags )
{
if ( !isValid( socket ) ) return -1;
return send( socket, (const char *)buffer, bufferSize, flags );
}
int Socket :: readBuffer(void *buffer, long bufferSize, int flags )
{
if ( !isValid( soket ) ) return -1;
return recv( soket, (char *)buffer, bufferSize, flags );
}
int Socket :: readBuffer(int socket, void *buffer, long bufferSize, int flags )
{
if ( !isValid( socket ) ) return -1;
return recv( socket, (char *)buffer, bufferSize, flags );
}

104
src/Sphere.cpp Normal file
View File

@@ -0,0 +1,104 @@
/***************************************************/
/*! \class Sphere
\brief STK sphere class.
This class implements a spherical ball with
radius, mass, position, and velocity parameters.
by Perry R. Cook, 1995 - 2002.
*/
/***************************************************/
#include "Sphere.h"
#include <stdio.h>
#include <math.h>
Sphere::Sphere(double initRadius)
{
myRadius = initRadius;
myMass = 1.0;
myPosition = new Vector3D(0, 0, 0);
myVelocity = new Vector3D(0, 0, 0);
};
Sphere::~Sphere()
{
delete myPosition;
delete myVelocity;
}
void Sphere::setPosition(double anX, double aY, double aZ)
{
myPosition->setXYZ(anX, aY, aZ);
};
void Sphere::setVelocity(double anX, double aY, double aZ)
{
myVelocity->setXYZ(anX, aY, aZ);
};
void Sphere::setRadius(double aRadius)
{
myRadius = aRadius;
};
void Sphere::setMass(double aMass)
{
myMass = aMass;
};
Vector3D* Sphere::getPosition()
{
return myPosition;
};
Vector3D* Sphere::getRelativePosition(Vector3D* aPosition)
{
workingVector.setXYZ(aPosition->getX() - myPosition->getX(),
aPosition->getY() - myPosition->getY(),
aPosition->getZ() - myPosition->getZ());
return &workingVector;
};
double Sphere::getVelocity(Vector3D* aVelocity)
{
aVelocity->setXYZ(myVelocity->getX(), myVelocity->getY(), myVelocity->getZ());
return myVelocity->getLength();
};
double Sphere::isInside(Vector3D *aPosition)
{
// Return directed distance from aPosition to spherical boundary ( <
// 0 if inside).
double distance;
Vector3D *tempVector;
tempVector = this->getRelativePosition(aPosition);
distance = tempVector->getLength();
return distance - myRadius;
};
double Sphere::getRadius()
{
return myRadius;
};
double Sphere::getMass()
{
return myMass;
};
void Sphere::addVelocity(double anX, double aY, double aZ)
{
myVelocity->setX(myVelocity->getX() + anX);
myVelocity->setY(myVelocity->getY() + aY);
myVelocity->setZ(myVelocity->getZ() + aZ);
}
void Sphere::tick(double timeIncrement)
{
myPosition->setX(myPosition->getX() + (timeIncrement * myVelocity->getX()));
myPosition->setY(myPosition->getY() + (timeIncrement * myVelocity->getY()));
myPosition->setZ(myPosition->getZ() + (timeIncrement * myVelocity->getZ()));
};

View File

@@ -1,143 +1,143 @@
/***************************************************/
/*! \class Stk
\brief STK base class
Nearly all STK classes inherit from this class.
The global sample rate can be queried and
modified via Stk. In addition, this class
provides error handling and byte-swapping
functions.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Stk.h"
#include <stdio.h>
#include <string.h>
MY_FLOAT Stk :: srate = (MY_FLOAT) SRATE;
const Stk::STK_FORMAT Stk :: STK_SINT8 = 1;
const Stk::STK_FORMAT Stk :: STK_SINT16 = 2;
const Stk::STK_FORMAT Stk :: STK_SINT32 = 8;
const Stk::STK_FORMAT Stk :: STK_FLOAT32 = 16;
const Stk::STK_FORMAT Stk :: STK_FLOAT64 = 32;
Stk :: Stk(void)
{
}
Stk :: ~Stk(void)
{
}
MY_FLOAT Stk :: sampleRate(void)
{
return srate;
}
void Stk :: setSampleRate(MY_FLOAT newRate)
{
if (newRate > 0)
srate = newRate;
}
void Stk :: swap16(unsigned char *ptr)
{
register unsigned char val;
// Swap 1st and 2nd bytes
val = *(ptr);
*(ptr) = *(ptr+1);
*(ptr+1) = val;
}
void Stk :: swap32(unsigned char *ptr)
{
register unsigned char val;
// Swap 1st and 4th bytes
val = *(ptr);
*(ptr) = *(ptr+3);
*(ptr+3) = val;
//Swap 2nd and 3rd bytes
ptr += 1;
val = *(ptr);
*(ptr) = *(ptr+1);
*(ptr+1) = val;
}
void Stk :: swap64(unsigned char *ptr)
{
register unsigned char val;
// Swap 1st and 8th bytes
val = *(ptr);
*(ptr) = *(ptr+7);
*(ptr+7) = val;
// Swap 2nd and 7th bytes
ptr += 1;
val = *(ptr);
*(ptr) = *(ptr+5);
*(ptr+5) = val;
// Swap 3rd and 6th bytes
ptr += 1;
val = *(ptr);
*(ptr) = *(ptr+3);
*(ptr+3) = val;
// Swap 4th and 5th bytes
ptr += 1;
val = *(ptr);
*(ptr) = *(ptr+1);
*(ptr+1) = val;
}
#if defined(__OS_IRIX__) || defined(__OS_LINUX__)
#include <unistd.h>
#elif defined(__OS_WINDOWS__)
#include <windows.h>
#endif
void Stk :: sleep(unsigned long milliseconds)
{
#if defined(__OS_WINDOWS__)
Sleep((DWORD) milliseconds);
#elif defined(__OS_IRIX__) || defined(__OS_LINUX__)
usleep( (unsigned long) (milliseconds * 1000.0) );
#endif
}
void Stk :: handleError( const char *message, StkError::TYPE type )
{
if (type == StkError::WARNING)
fprintf(stderr, "\n%s\n\n", message);
else if (type == StkError::DEBUG_WARNING) {
#if defined(_STK_DEBUG_)
fprintf(stderr, "\n%s\n\n", message);
#endif
}
else {
// Print error message before throwing.
fprintf(stderr, "\n%s\n\n", message);
throw StkError(message, type);
}
}
StkError :: StkError(const char *p, TYPE tipe)
: type(tipe)
{
strncpy(message, p, 256);
}
StkError :: ~StkError(void)
{
}
void StkError :: printMessage(void)
{
printf("\n%s\n\n", message);
}
/***************************************************/
/*! \class Stk
\brief STK base class
Nearly all STK classes inherit from this class.
The global sample rate can be queried and
modified via Stk. In addition, this class
provides error handling and byte-swapping
functions.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Stk.h"
#include <stdio.h>
#include <string.h>
MY_FLOAT Stk :: srate = (MY_FLOAT) SRATE;
const Stk::STK_FORMAT Stk :: STK_SINT8 = 1;
const Stk::STK_FORMAT Stk :: STK_SINT16 = 2;
const Stk::STK_FORMAT Stk :: STK_SINT32 = 8;
const Stk::STK_FORMAT Stk :: STK_FLOAT32 = 16;
const Stk::STK_FORMAT Stk :: STK_FLOAT64 = 32;
Stk :: Stk(void)
{
}
Stk :: ~Stk(void)
{
}
MY_FLOAT Stk :: sampleRate(void)
{
return srate;
}
void Stk :: setSampleRate(MY_FLOAT newRate)
{
if (newRate > 0)
srate = newRate;
}
void Stk :: swap16(unsigned char *ptr)
{
register unsigned char val;
// Swap 1st and 2nd bytes
val = *(ptr);
*(ptr) = *(ptr+1);
*(ptr+1) = val;
}
void Stk :: swap32(unsigned char *ptr)
{
register unsigned char val;
// Swap 1st and 4th bytes
val = *(ptr);
*(ptr) = *(ptr+3);
*(ptr+3) = val;
//Swap 2nd and 3rd bytes
ptr += 1;
val = *(ptr);
*(ptr) = *(ptr+1);
*(ptr+1) = val;
}
void Stk :: swap64(unsigned char *ptr)
{
register unsigned char val;
// Swap 1st and 8th bytes
val = *(ptr);
*(ptr) = *(ptr+7);
*(ptr+7) = val;
// Swap 2nd and 7th bytes
ptr += 1;
val = *(ptr);
*(ptr) = *(ptr+5);
*(ptr+5) = val;
// Swap 3rd and 6th bytes
ptr += 1;
val = *(ptr);
*(ptr) = *(ptr+3);
*(ptr+3) = val;
// Swap 4th and 5th bytes
ptr += 1;
val = *(ptr);
*(ptr) = *(ptr+1);
*(ptr+1) = val;
}
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
#include <unistd.h>
#elif defined(__OS_WINDOWS__)
#include <windows.h>
#endif
void Stk :: sleep(unsigned long milliseconds)
{
#if defined(__OS_WINDOWS__)
Sleep((DWORD) milliseconds);
#elif (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
usleep( (unsigned long) (milliseconds * 1000.0) );
#endif
}
void Stk :: handleError( const char *message, StkError::TYPE type )
{
if (type == StkError::WARNING)
fprintf(stderr, "\n%s\n\n", message);
else if (type == StkError::DEBUG_WARNING) {
#if defined(_STK_DEBUG_)
fprintf(stderr, "\n%s\n\n", message);
#endif
}
else {
// Print error message before throwing.
fprintf(stderr, "\n%s\n\n", message);
throw StkError(message, type);
}
}
StkError :: StkError(const char *p, TYPE tipe)
: type(tipe)
{
strncpy(message, p, 256);
}
StkError :: ~StkError(void)
{
}
void StkError :: printMessage(void)
{
printf("\n%s\n\n", message);
}

View File

@@ -1,43 +1,43 @@
/***************************************************/
/*! \class SubNoise
\brief STK sub-sampled noise generator.
Generates a new random number every "rate" ticks
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 - 2002.
*/
/***************************************************/
#include "SubNoise.h"
SubNoise :: SubNoise(int subRate) : Noise()
{
rate = subRate;
counter = rate;
}
SubNoise :: ~SubNoise()
{
}
int SubNoise :: subRate(void) const
{
return rate;
}
void SubNoise :: setRate(int subRate)
{
if (subRate > 0)
rate = subRate;
}
MY_FLOAT SubNoise :: tick()
{
if ( ++counter > rate ) {
Noise::tick();
counter = 1;
}
return lastOutput;
}
/***************************************************/
/*! \class SubNoise
\brief STK sub-sampled noise generator.
Generates a new random number every "rate" ticks
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 - 2002.
*/
/***************************************************/
#include "SubNoise.h"
SubNoise :: SubNoise(int subRate) : Noise()
{
rate = subRate;
counter = rate;
}
SubNoise :: ~SubNoise()
{
}
int SubNoise :: subRate(void) const
{
return rate;
}
void SubNoise :: setRate(int subRate)
{
if (subRate > 0)
rate = subRate;
}
MY_FLOAT SubNoise :: tick()
{
if ( ++counter > rate ) {
Noise::tick();
counter = 1;
}
return lastOutput;
}

View File

@@ -1,106 +1,106 @@
/***************************************************/
/*! \class Table
\brief STK table lookup class.
This class loads a table of floating-point
doubles, which are assumed to be in big-endian
format. Linear interpolation is performed for
fractional lookup indexes.
An StkError will be thrown if the table file
is not found.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Table.h"
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <iostream.h>
Table :: Table(char *fileName)
{
char message[256];
// Use the system call "stat" to determine the file length
struct stat filestat;
if ( stat(fileName, &filestat) == -1 ) {
sprintf(message, "Table: Couldn't stat or find file (%s).", fileName);
handleError( message, StkError::FILE_NOT_FOUND );
}
length = (long) filestat.st_size / 8; // length in 8-byte samples
// Open the file and read samples into data[]
FILE *fd;
fd = fopen(fileName,"rb");
if (!fd) {
sprintf(message, "Table: Couldn't open or find file (%s).", fileName);
handleError( message, StkError::FILE_NOT_FOUND );
}
data = (MY_FLOAT *) new MY_FLOAT[length];
// Read samples into data[]
long i = 0;
double temp;
while ( fread(&temp, 8, 1, fd) ) {
#ifdef __LITTLE_ENDIAN__
swap64((unsigned char *)&temp);
#endif
data[i++] = (MY_FLOAT) temp;
}
fclose(fd);
lastOutput = 0.0;
}
Table :: ~Table()
{
delete [ ] data;
}
long Table :: getLength() const
{
return length;
}
MY_FLOAT Table :: lastOut() const
{
return lastOutput;
}
MY_FLOAT Table :: tick(MY_FLOAT index)
{
MY_FLOAT alpha;
long temp;
if (index > length-1) {
cerr << "Table: Index (" << index << ") exceeds table length ... sticking at end!" << endl;
index = length-1;
}
else if (index < 0.0) {
cerr << "Table: Index (" << index << ") is less than zero ... setting to zero!" << endl;
index = 0.0;
}
// Index in range 0 to length-1
temp = (long) index; // Integer part of index
alpha = index - (MY_FLOAT) temp; // Fractional part of index
if (alpha > 0.0) { // Do linear interpolation
lastOutput = data[temp];
lastOutput += (alpha*(data[temp+1] - lastOutput));
}
else lastOutput = data[temp];
return lastOutput;
}
MY_FLOAT *Table :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class Table
\brief STK table lookup class.
This class loads a table of floating-point
doubles, which are assumed to be in big-endian
format. Linear interpolation is performed for
fractional lookup indexes.
An StkError will be thrown if the table file
is not found.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Table.h"
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <iostream.h>
Table :: Table(char *fileName)
{
char message[256];
// Use the system call "stat" to determine the file length
struct stat filestat;
if ( stat(fileName, &filestat) == -1 ) {
sprintf(message, "Table: Couldn't stat or find file (%s).", fileName);
handleError( message, StkError::FILE_NOT_FOUND );
}
length = (long) filestat.st_size / 8; // length in 8-byte samples
// Open the file and read samples into data[]
FILE *fd;
fd = fopen(fileName,"rb");
if (!fd) {
sprintf(message, "Table: Couldn't open or find file (%s).", fileName);
handleError( message, StkError::FILE_NOT_FOUND );
}
data = (MY_FLOAT *) new MY_FLOAT[length];
// Read samples into data[]
long i = 0;
double temp;
while ( fread(&temp, 8, 1, fd) ) {
#ifdef __LITTLE_ENDIAN__
swap64((unsigned char *)&temp);
#endif
data[i++] = (MY_FLOAT) temp;
}
fclose(fd);
lastOutput = 0.0;
}
Table :: ~Table()
{
delete [ ] data;
}
long Table :: getLength() const
{
return length;
}
MY_FLOAT Table :: lastOut() const
{
return lastOutput;
}
MY_FLOAT Table :: tick(MY_FLOAT index)
{
MY_FLOAT alpha;
long temp;
if (index > length-1) {
cerr << "Table: Index (" << index << ") exceeds table length ... sticking at end!" << endl;
index = length-1;
}
else if (index < 0.0) {
cerr << "Table: Index (" << index << ") is less than zero ... setting to zero!" << endl;
index = 0.0;
}
// Index in range 0 to length-1
temp = (long) index; // Integer part of index
alpha = index - (MY_FLOAT) temp; // Fractional part of index
if (alpha > 0.0) { // Do linear interpolation
lastOutput = data[temp];
lastOutput += (alpha*(data[temp+1] - lastOutput));
}
else lastOutput = data[temp];
return lastOutput;
}
MY_FLOAT *Table :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

@@ -1,323 +1,325 @@
/***************************************************/
/*! \class TcpWvIn
\brief STK internet streaming input class.
This protected Wvin subclass can read streamed
data over a network via a TCP socket connection.
The data is assumed in big-endian, or network,
byte order.
TcpWvIn 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 pointers to multi-channel sample frames.
For single-channel data, these methods return
equivalent values.
This class starts a socket server, which waits
for a single remote connection. The default
data type for the incoming stream is signed
16-bit integers, though any of the defined
STK_FORMATs are permissible.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "TcpWvIn.h"
#define N_BUFFERS 10
// Do OS dependent includes
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
#include <sys/types.h>
#endif
extern "C" THREAD_RETURN THREAD_TYPE inputThread(void * ptr)
{
thread_info *info = (thread_info *)ptr;
while ( !info->finished ) {
Thread::test();
((TcpWvIn *) info->object)->receive();
}
return 0;
}
TcpWvIn :: TcpWvIn( int port )
{
init( port );
}
TcpWvIn :: ~TcpWvIn()
{
// Close down the thread.
connected = false;
threadInfo.finished = true;
thread->wait( -1 );
delete thread;
delete soket;
if (buffer)
delete [] buffer;
}
void TcpWvIn :: init( int port )
{
buffer = 0;
bufferBytes = 0;
connected = false;
// Start socket server ... an error can be thrown from the Socket class.
soket = new Socket( port );
thread = new Thread();
threadInfo.finished = false;
threadInfo.object = (void *) this;
// Start the input thread.
if ( !thread->start( &inputThread, &threadInfo ) ) {
sprintf(msg, "TcpWvIn: Unable to start input thread!");
handleError( msg, StkError::PROCESS_THREAD );
}
}
void TcpWvIn :: listen(unsigned int nChannels, Stk::STK_FORMAT format)
{
mutex.lock();
if ( connected ) {
soket->close(fd);
}
if (nChannels < 1) {
sprintf(msg, "TcpWvOut: the channel argument (%d) must be greater than zero.", nChannels);
handleError( msg, StkError::FUNCTION_ARGUMENT );
}
unsigned int lastChannels = channels;
channels = nChannels;
if ( format == STK_SINT16 ) dataSize = 2;
else if ( format == STK_SINT32 || format == STK_FLOAT32 ) dataSize = 4;
else if ( format == STK_FLOAT64 ) dataSize = 8;
else if ( format == STK_SINT8 ) dataSize = 1;
else {
sprintf( msg, "TcpWvIn: Unknown data type specified (%ld).", format );
handleError(msg, StkError::FUNCTION_ARGUMENT);
}
dataType = format;
int lastBufferBytes = bufferBytes;
bufferBytes = CHUNK_SIZE * N_BUFFERS * channels * dataSize;
// Allocate new memory if necessary.
if ( lastBufferBytes < bufferBytes ) {
if ( buffer) delete [] buffer;
buffer = (char *) new char[bufferBytes];
}
if ( lastChannels < channels ) {
if ( data ) delete [] data;
data = (MY_FLOAT *) new MY_FLOAT[CHUNK_SIZE*channels];
if ( lastOutput ) delete [] lastOutput;
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
}
WvIn::reset();
counter = 0;
writePoint = 0;
bytesFilled = 0;
// Accept a connection.
printf("Listening for connection on port %d ... ", soket->port());
fd = soket->accept();
if ( fd < 0) {
sprintf( msg, "TcpWvIn: Couldn't accept connection request!");
handleError( msg, StkError::PROCESS_SOCKET );
}
printf(" connection made!\n\n");
// Start input thread.
connected = true;
mutex.unlock();
}
void TcpWvIn :: receive( void )
{
if ( !connected ) {
Stk::sleep(100);
return;
}
fd_set mask;
FD_ZERO(&mask);
FD_SET(fd, &mask);
// The select function will block until data is available for reading.
select(fd+1, &mask, (fd_set *)0, (fd_set *)0, NULL);
if (FD_ISSET(fd, &mask)) {
mutex.lock();
long unfilled = bufferBytes - bytesFilled;
if ( unfilled > 0 ) {
// There's room in our buffer for more data.
long endPoint = writePoint + unfilled;
if ( endPoint > bufferBytes ) unfilled -= endPoint - bufferBytes;
int i = Socket::readBuffer(fd, (void *)&buffer[writePoint], unfilled, 0);
if ( i <= 0 ) {
printf("The remote TcpWvIn socket connection has closed.\n\n");
connected = false;
mutex.unlock();
return;
}
bytesFilled += i;
writePoint += i;
if (writePoint == bufferBytes)
writePoint = 0;
mutex.unlock();
}
else {
// Sleep 10 milliseconds AFTER unlocking mutex.
mutex.unlock();
Stk::sleep( 10 );
}
}
}
int TcpWvIn :: readData( void )
{
// We have two potential courses of action should this method
// be called and the input buffer isn't sufficiently filled.
// One solution is to fill the data buffer with zeros and return.
// The other solution is to wait until the necessary data exists.
// I chose the latter, as it works for both streamed files
// (non-realtime data transport) and realtime playback (given
// adequate network bandwidth and speed).
// Wait until data is ready.
long bytes = CHUNK_SIZE * channels * dataSize;
while ( connected && bytesFilled < bytes )
Stk::sleep( 10 );
if ( !connected && bytesFilled == 0 ) return 0;
bytes = ( bytesFilled < bytes ) ? bytesFilled : bytes;
// Copy samples from buffer to data.
long samples = bytes / dataSize;
mutex.lock();
if ( dataType == STK_SINT16 ) {
gain = 1.0 / 32767.0;
SINT16 *buf = (SINT16 *) (buffer+readPoint);
for (int i=0; i<samples; i++ ) {
#ifdef __LITTLE_ENDIAN__
swap16((unsigned char *) buf);
#endif
data[i] = (MY_FLOAT) *buf++;
data[i] *= gain;
}
}
else if ( dataType == STK_SINT32 ) {
gain = 1.0 / 2147483647.0;
SINT32 *buf = (SINT32 *) (buffer+readPoint);
for (int i=0; i<samples; i++ ) {
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *) buf);
#endif
data[i] = (MY_FLOAT) *buf++;
data[i] *= gain;
}
}
else if ( dataType == STK_FLOAT32 ) {
FLOAT32 *buf = (FLOAT32 *) (buffer+readPoint);
for (int i=0; i<samples; i++ ) {
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *) buf);
#endif
data[i] = (MY_FLOAT) *buf++;
}
}
else if ( dataType == STK_FLOAT64 ) {
FLOAT64 *buf = (FLOAT64 *) (buffer+readPoint);
for (int i=0; i<samples; i++ ) {
#ifdef __LITTLE_ENDIAN__
swap64((unsigned char *) buf);
#endif
data[i] = (MY_FLOAT) *buf++;
}
}
else if ( dataType == STK_SINT8 ) {
gain = 1.0 / 127.0;
signed char *buf = (signed char *) (buffer+readPoint);
for (int i=0; i<samples; i++ ) {
data[i] = (MY_FLOAT) *buf++;
data[i] *= gain;
}
}
readPoint += bytes;
if ( readPoint == bufferBytes )
readPoint = 0;
bytesFilled -= bytes;
if ( bytesFilled < 0 )
bytesFilled = 0;
mutex.unlock();
return samples / channels;
}
bool TcpWvIn :: isConnected(void)
{
if ( bytesFilled > 0 || counter > 0 )
return true;
else
return connected;
}
const MY_FLOAT *TcpWvIn :: lastFrame(void) const
{
return lastOutput;
}
MY_FLOAT TcpWvIn :: lastOut(void) const
{
return WvIn::lastOut();
}
MY_FLOAT TcpWvIn :: tick(void)
{
return WvIn::tick();
}
MY_FLOAT *TcpWvIn :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
return WvIn::tick( vector, vectorSize );
}
const MY_FLOAT *TcpWvIn :: tickFrame(void)
{
// If no connection and we've output all samples in the queue, return.
if ( !connected && bytesFilled == 0 && counter == 0 ) return lastOutput;
if (counter == 0)
counter = readData();
long temp = (CHUNK_SIZE - counter) * channels;
for (unsigned int i=0; i<channels; i++)
lastOutput[i] = data[temp++];
counter--;
if ( counter < 0 )
counter = 0;
return lastOutput;
}
MY_FLOAT *TcpWvIn :: tickFrame(MY_FLOAT *frameVector, unsigned int frames)
{
return WvIn::tickFrame( frameVector, frames );
}
/***************************************************/
/*! \class TcpWvIn
\brief STK internet streaming input class.
This protected Wvin subclass can read streamed
data over a network via a TCP socket connection.
The data is assumed in big-endian, or network,
byte order.
TcpWvIn 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 pointers to multi-channel sample frames.
For single-channel data, these methods return
equivalent values.
This class starts a socket server, which waits
for a single remote connection. The default
data type for the incoming stream is signed
16-bit integers, though any of the defined
STK_FORMATs are permissible.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "TcpWvIn.h"
#define N_BUFFERS 10
// Do OS dependent includes
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#endif
extern "C" THREAD_RETURN THREAD_TYPE inputThread(void * ptr)
{
thread_info *info = (thread_info *)ptr;
while ( !info->finished ) {
Thread::test();
((TcpWvIn *) info->object)->receive();
}
return 0;
}
TcpWvIn :: TcpWvIn( int port )
{
init( port );
}
TcpWvIn :: ~TcpWvIn()
{
// Close down the thread.
connected = false;
threadInfo.finished = true;
thread->wait( -1 );
delete thread;
delete soket;
if (buffer)
delete [] buffer;
}
void TcpWvIn :: init( int port )
{
buffer = 0;
bufferBytes = 0;
connected = false;
// Start socket server ... an error can be thrown from the Socket class.
soket = new Socket( port );
thread = new Thread();
threadInfo.finished = false;
threadInfo.object = (void *) this;
// Start the input thread.
if ( !thread->start( &inputThread, &threadInfo ) ) {
sprintf(msg, "TcpWvIn: Unable to start input thread!");
handleError( msg, StkError::PROCESS_THREAD );
}
}
void TcpWvIn :: listen(unsigned int nChannels, Stk::STK_FORMAT format)
{
mutex.lock();
if ( connected ) {
soket->close(fd);
}
if (nChannels < 1) {
sprintf(msg, "TcpWvOut: the channel argument (%d) must be greater than zero.", nChannels);
handleError( msg, StkError::FUNCTION_ARGUMENT );
}
unsigned int lastChannels = channels;
channels = nChannels;
if ( format == STK_SINT16 ) dataSize = 2;
else if ( format == STK_SINT32 || format == STK_FLOAT32 ) dataSize = 4;
else if ( format == STK_FLOAT64 ) dataSize = 8;
else if ( format == STK_SINT8 ) dataSize = 1;
else {
sprintf( msg, "TcpWvIn: Unknown data type specified (%ld).", format );
handleError(msg, StkError::FUNCTION_ARGUMENT);
}
dataType = format;
int lastBufferBytes = bufferBytes;
bufferBytes = CHUNK_SIZE * N_BUFFERS * channels * dataSize;
// Allocate new memory if necessary.
if ( lastBufferBytes < bufferBytes ) {
if ( buffer) delete [] buffer;
buffer = (char *) new char[bufferBytes];
}
if ( lastChannels < channels ) {
if ( data ) delete [] data;
data = (MY_FLOAT *) new MY_FLOAT[CHUNK_SIZE*channels];
if ( lastOutput ) delete [] lastOutput;
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
}
WvIn::reset();
counter = 0;
writePoint = 0;
bytesFilled = 0;
// Accept a connection.
printf("Listening for connection on port %d ... ", soket->port());
fd = soket->accept();
if ( fd < 0) {
sprintf( msg, "TcpWvIn: Couldn't accept connection request!");
handleError( msg, StkError::PROCESS_SOCKET );
}
printf(" connection made!\n\n");
// Start input thread.
connected = true;
mutex.unlock();
}
void TcpWvIn :: receive( void )
{
if ( !connected ) {
Stk::sleep(100);
return;
}
fd_set mask;
FD_ZERO(&mask);
FD_SET(fd, &mask);
// The select function will block until data is available for reading.
select(fd+1, &mask, (fd_set *)0, (fd_set *)0, NULL);
if (FD_ISSET(fd, &mask)) {
mutex.lock();
long unfilled = bufferBytes - bytesFilled;
if ( unfilled > 0 ) {
// There's room in our buffer for more data.
long endPoint = writePoint + unfilled;
if ( endPoint > bufferBytes ) unfilled -= endPoint - bufferBytes;
int i = Socket::readBuffer(fd, (void *)&buffer[writePoint], unfilled, 0);
if ( i <= 0 ) {
printf("The remote TcpWvIn socket connection has closed.\n\n");
connected = false;
mutex.unlock();
return;
}
bytesFilled += i;
writePoint += i;
if (writePoint == bufferBytes)
writePoint = 0;
mutex.unlock();
}
else {
// Sleep 10 milliseconds AFTER unlocking mutex.
mutex.unlock();
Stk::sleep( 10 );
}
}
}
int TcpWvIn :: readData( void )
{
// We have two potential courses of action should this method
// be called and the input buffer isn't sufficiently filled.
// One solution is to fill the data buffer with zeros and return.
// The other solution is to wait until the necessary data exists.
// I chose the latter, as it works for both streamed files
// (non-realtime data transport) and realtime playback (given
// adequate network bandwidth and speed).
// Wait until data is ready.
long bytes = CHUNK_SIZE * channels * dataSize;
while ( connected && bytesFilled < bytes )
Stk::sleep( 10 );
if ( !connected && bytesFilled == 0 ) return 0;
bytes = ( bytesFilled < bytes ) ? bytesFilled : bytes;
// Copy samples from buffer to data.
long samples = bytes / dataSize;
mutex.lock();
if ( dataType == STK_SINT16 ) {
gain = 1.0 / 32767.0;
SINT16 *buf = (SINT16 *) (buffer+readPoint);
for (int i=0; i<samples; i++ ) {
#ifdef __LITTLE_ENDIAN__
swap16((unsigned char *) buf);
#endif
data[i] = (MY_FLOAT) *buf++;
data[i] *= gain;
}
}
else if ( dataType == STK_SINT32 ) {
gain = 1.0 / 2147483647.0;
SINT32 *buf = (SINT32 *) (buffer+readPoint);
for (int i=0; i<samples; i++ ) {
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *) buf);
#endif
data[i] = (MY_FLOAT) *buf++;
data[i] *= gain;
}
}
else if ( dataType == STK_FLOAT32 ) {
FLOAT32 *buf = (FLOAT32 *) (buffer+readPoint);
for (int i=0; i<samples; i++ ) {
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *) buf);
#endif
data[i] = (MY_FLOAT) *buf++;
}
}
else if ( dataType == STK_FLOAT64 ) {
FLOAT64 *buf = (FLOAT64 *) (buffer+readPoint);
for (int i=0; i<samples; i++ ) {
#ifdef __LITTLE_ENDIAN__
swap64((unsigned char *) buf);
#endif
data[i] = (MY_FLOAT) *buf++;
}
}
else if ( dataType == STK_SINT8 ) {
gain = 1.0 / 127.0;
signed char *buf = (signed char *) (buffer+readPoint);
for (int i=0; i<samples; i++ ) {
data[i] = (MY_FLOAT) *buf++;
data[i] *= gain;
}
}
readPoint += bytes;
if ( readPoint == bufferBytes )
readPoint = 0;
bytesFilled -= bytes;
if ( bytesFilled < 0 )
bytesFilled = 0;
mutex.unlock();
return samples / channels;
}
bool TcpWvIn :: isConnected(void)
{
if ( bytesFilled > 0 || counter > 0 )
return true;
else
return connected;
}
const MY_FLOAT *TcpWvIn :: lastFrame(void) const
{
return lastOutput;
}
MY_FLOAT TcpWvIn :: lastOut(void) const
{
return WvIn::lastOut();
}
MY_FLOAT TcpWvIn :: tick(void)
{
return WvIn::tick();
}
MY_FLOAT *TcpWvIn :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
return WvIn::tick( vector, vectorSize );
}
const MY_FLOAT *TcpWvIn :: tickFrame(void)
{
// If no connection and we've output all samples in the queue, return.
if ( !connected && bytesFilled == 0 && counter == 0 ) return lastOutput;
if (counter == 0)
counter = readData();
long temp = (CHUNK_SIZE - counter) * channels;
for (unsigned int i=0; i<channels; i++)
lastOutput[i] = data[temp++];
counter--;
if ( counter < 0 )
counter = 0;
return lastOutput;
}
MY_FLOAT *TcpWvIn :: tickFrame(MY_FLOAT *frameVector, unsigned int frames)
{
return WvIn::tickFrame( frameVector, frames );
}

View File

@@ -1,204 +1,204 @@
/***************************************************/
/*! \class TcpWvOut
\brief STK internet streaming output class.
This protected WvOut subclass can stream
data over a network via a TCP socket connection.
The data is converted to big-endian byte order,
if necessary, before being transmitted.
TcpWvOut 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
takes a pointer to multi-channel sample
frame data.
This class connects to a socket server, the
port and IP address of which must be specified
as constructor arguments. The default data
type is signed 16-bit integers but any of the
defined STK_FORMATs are permissible.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "TcpWvOut.h"
#include <string.h>
TcpWvOut :: TcpWvOut()
{
buffer = 0;
soket = 0;
}
TcpWvOut :: TcpWvOut(int port, const char *hostname, unsigned int nChannels, Stk::STK_FORMAT format)
{
buffer = 0;
soket = 0;
connect( port, hostname, nChannels, format );
}
TcpWvOut :: ~TcpWvOut()
{
disconnect();
delete soket;
delete [] buffer;
}
void TcpWvOut :: connect(int port, const char *hostname, unsigned int nChannels, Stk::STK_FORMAT format)
{
if ( soket && soket->isValid( soket->socket() ) )
disconnect();
if (nChannels < 1) {
sprintf(msg, "TcpWvOut: the channel argument (%d) must be greater than zero.", nChannels);
handleError( msg, StkError::FUNCTION_ARGUMENT );
}
unsigned int lastChannels = channels;
channels = nChannels;
if ( format == STK_SINT8 ) dataSize = 1;
else if ( format == STK_SINT16 ) dataSize = 2;
else if ( format == STK_SINT32 || format == STK_FLOAT32 ) dataSize = 4;
else if ( format == STK_FLOAT64 ) dataSize = 8;
else {
sprintf( msg, "TcpWvOut: Unknown data type specified (%ld).", format );
handleError(msg, StkError::FUNCTION_ARGUMENT);
}
dataType = format;
if ( !soket )
soket = new Socket( port, hostname );
else
soket->connect( port, hostname );
// Allocate new memory if necessary.
if ( lastChannels < channels ) {
if ( data ) delete [] data;
data = (MY_FLOAT *) new MY_FLOAT[BUFFER_SIZE*channels];
if ( buffer) delete [] buffer;
long bytes = dataSize * BUFFER_SIZE * channels;
buffer = (char *) new char[bytes];
}
counter = 0;
}
void TcpWvOut :: disconnect(void)
{
if ( soket ) {
writeData( counter );
soket->close();
}
}
void TcpWvOut :: writeData( long frames )
{
if ( dataType == STK_SINT8 ) {
signed char *ptr = (signed char *) buffer;
for ( unsigned long k=0; k<frames*channels; k++ )
*ptr++ = (signed char) (data[k] * 127.0);
}
else if ( dataType == STK_SINT16 ) {
SINT16 *ptr = (SINT16 *) buffer;
for ( unsigned long k=0; k<frames*channels; k++ ) {
*ptr = (SINT16) (data[k] * 32767.0);
#ifdef __LITTLE_ENDIAN__
swap16 ((unsigned char *)ptr);
#endif
ptr++;
}
}
else if ( dataType == STK_SINT32 ) {
SINT32 *ptr = (SINT32 *) buffer;
for ( unsigned long k=0; k<frames*channels; k++ ) {
*ptr = (SINT32) (data[k] * 2147483647.0);
#ifdef __LITTLE_ENDIAN__
swap32 ((unsigned char *)ptr);
#endif
ptr++;
}
}
else if ( dataType == STK_FLOAT32 ) {
FLOAT32 *ptr = (FLOAT32 *) buffer;
for ( unsigned long k=0; k<frames*channels; k++ ) {
*ptr = (FLOAT32) data[k];
#ifdef __LITTLE_ENDIAN__
swap32 ((unsigned char *)ptr);
#endif
ptr++;
}
}
else if ( dataType == STK_FLOAT64 ) {
FLOAT64 *ptr = (FLOAT64 *) buffer;
for ( unsigned long k=0; k<frames*channels; k++ ) {
*ptr = (FLOAT64) data[k];
#ifdef __LITTLE_ENDIAN__
swap64 ((unsigned char *)ptr);
#endif
ptr++;
}
}
long bytes = dataSize * frames * channels;
if ( soket->writeBuffer( (const void *)buffer, bytes, 0 ) < 0 ) {
sprintf(msg, "TcpWvOut: connection to socket server failed!");
handleError( msg, StkError::PROCESS_SOCKET );
}
}
unsigned long TcpWvOut :: getFrames( void ) const
{
return totalCount;
}
MY_FLOAT TcpWvOut :: getTime( void ) const
{
return (MY_FLOAT) totalCount / Stk::sampleRate();
}
void TcpWvOut :: tick(MY_FLOAT sample)
{
if ( !soket || !soket->isValid( soket->socket() ) ) return;
for ( unsigned int j=0; j<channels; j++ )
data[counter*channels+j] = sample;
counter++;
totalCount++;
if ( counter == BUFFER_SIZE ) {
writeData( BUFFER_SIZE );
counter = 0;
}
}
void TcpWvOut :: tick(const MY_FLOAT *vector, unsigned int vectorSize)
{
if ( !soket || !soket->isValid( soket->socket() ) ) return;
for (unsigned int i=0; i<vectorSize; i++)
tick( vector[i] );
}
void TcpWvOut :: tickFrame(const MY_FLOAT *frameVector, unsigned int frames)
{
if ( !soket || !soket->isValid( soket->socket() ) ) return;
unsigned int j;
for ( unsigned int i=0; i<frames; i++ ) {
for ( j=0; j<channels; j++ ) {
data[counter*channels+j] = frameVector[i*channels+j];
}
counter++;
totalCount++;
if ( counter == BUFFER_SIZE ) {
writeData( BUFFER_SIZE );
counter = 0;
}
}
}
/***************************************************/
/*! \class TcpWvOut
\brief STK internet streaming output class.
This protected WvOut subclass can stream
data over a network via a TCP socket connection.
The data is converted to big-endian byte order,
if necessary, before being transmitted.
TcpWvOut 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
takes a pointer to multi-channel sample
frame data.
This class connects to a socket server, the
port and IP address of which must be specified
as constructor arguments. The default data
type is signed 16-bit integers but any of the
defined STK_FORMATs are permissible.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "TcpWvOut.h"
#include <string.h>
TcpWvOut :: TcpWvOut()
{
buffer = 0;
soket = 0;
}
TcpWvOut :: TcpWvOut(int port, const char *hostname, unsigned int nChannels, Stk::STK_FORMAT format)
{
buffer = 0;
soket = 0;
connect( port, hostname, nChannels, format );
}
TcpWvOut :: ~TcpWvOut()
{
disconnect();
delete soket;
delete [] buffer;
}
void TcpWvOut :: connect(int port, const char *hostname, unsigned int nChannels, Stk::STK_FORMAT format)
{
if ( soket && soket->isValid( soket->socket() ) )
disconnect();
if (nChannels < 1) {
sprintf(msg, "TcpWvOut: the channel argument (%d) must be greater than zero.", nChannels);
handleError( msg, StkError::FUNCTION_ARGUMENT );
}
unsigned int lastChannels = channels;
channels = nChannels;
if ( format == STK_SINT8 ) dataSize = 1;
else if ( format == STK_SINT16 ) dataSize = 2;
else if ( format == STK_SINT32 || format == STK_FLOAT32 ) dataSize = 4;
else if ( format == STK_FLOAT64 ) dataSize = 8;
else {
sprintf( msg, "TcpWvOut: Unknown data type specified (%ld).", format );
handleError(msg, StkError::FUNCTION_ARGUMENT);
}
dataType = format;
if ( !soket )
soket = new Socket( port, hostname );
else
soket->connect( port, hostname );
// Allocate new memory if necessary.
if ( lastChannels < channels ) {
if ( data ) delete [] data;
data = (MY_FLOAT *) new MY_FLOAT[BUFFER_SIZE*channels];
if ( buffer) delete [] buffer;
long bytes = dataSize * BUFFER_SIZE * channels;
buffer = (char *) new char[bytes];
}
counter = 0;
}
void TcpWvOut :: disconnect(void)
{
if ( soket ) {
writeData( counter );
soket->close();
}
}
void TcpWvOut :: writeData( long frames )
{
if ( dataType == STK_SINT8 ) {
signed char *ptr = (signed char *) buffer;
for ( unsigned long k=0; k<frames*channels; k++ )
*ptr++ = (signed char) (data[k] * 127.0);
}
else if ( dataType == STK_SINT16 ) {
SINT16 *ptr = (SINT16 *) buffer;
for ( unsigned long k=0; k<frames*channels; k++ ) {
*ptr = (SINT16) (data[k] * 32767.0);
#ifdef __LITTLE_ENDIAN__
swap16 ((unsigned char *)ptr);
#endif
ptr++;
}
}
else if ( dataType == STK_SINT32 ) {
SINT32 *ptr = (SINT32 *) buffer;
for ( unsigned long k=0; k<frames*channels; k++ ) {
*ptr = (SINT32) (data[k] * 2147483647.0);
#ifdef __LITTLE_ENDIAN__
swap32 ((unsigned char *)ptr);
#endif
ptr++;
}
}
else if ( dataType == STK_FLOAT32 ) {
FLOAT32 *ptr = (FLOAT32 *) buffer;
for ( unsigned long k=0; k<frames*channels; k++ ) {
*ptr = (FLOAT32) data[k];
#ifdef __LITTLE_ENDIAN__
swap32 ((unsigned char *)ptr);
#endif
ptr++;
}
}
else if ( dataType == STK_FLOAT64 ) {
FLOAT64 *ptr = (FLOAT64 *) buffer;
for ( unsigned long k=0; k<frames*channels; k++ ) {
*ptr = (FLOAT64) data[k];
#ifdef __LITTLE_ENDIAN__
swap64 ((unsigned char *)ptr);
#endif
ptr++;
}
}
long bytes = dataSize * frames * channels;
if ( soket->writeBuffer( (const void *)buffer, bytes, 0 ) < 0 ) {
sprintf(msg, "TcpWvOut: connection to socket server failed!");
handleError( msg, StkError::PROCESS_SOCKET );
}
}
unsigned long TcpWvOut :: getFrames( void ) const
{
return totalCount;
}
MY_FLOAT TcpWvOut :: getTime( void ) const
{
return (MY_FLOAT) totalCount / Stk::sampleRate();
}
void TcpWvOut :: tick(MY_FLOAT sample)
{
if ( !soket || !soket->isValid( soket->socket() ) ) return;
for ( unsigned int j=0; j<channels; j++ )
data[counter*channels+j] = sample;
counter++;
totalCount++;
if ( counter == BUFFER_SIZE ) {
writeData( BUFFER_SIZE );
counter = 0;
}
}
void TcpWvOut :: tick(const MY_FLOAT *vector, unsigned int vectorSize)
{
if ( !soket || !soket->isValid( soket->socket() ) ) return;
for (unsigned int i=0; i<vectorSize; i++)
tick( vector[i] );
}
void TcpWvOut :: tickFrame(const MY_FLOAT *frameVector, unsigned int frames)
{
if ( !soket || !soket->isValid( soket->socket() ) ) return;
unsigned int j;
for ( unsigned int i=0; i<frames; i++ ) {
for ( j=0; j<channels; j++ ) {
data[counter*channels+j] = frameVector[i*channels+j];
}
counter++;
totalCount++;
if ( counter == BUFFER_SIZE ) {
writeData( BUFFER_SIZE );
counter = 0;
}
}
}

View File

@@ -1,139 +1,139 @@
/***************************************************/
/*! \class Thread
\brief STK thread class.
This class provides a uniform interface for
cross-platform thread use. On Linux and IRIX
systems, the pthread library is used. Under Windows,
the Windows thread library is used.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Thread.h"
#include<iostream.h>
Thread :: Thread()
{
thread = 0;
}
Thread :: ~Thread()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
pthread_cancel(thread);
pthread_join(thread, NULL);
#elif defined(__OS_WINDOWS__)
if ( thread )
TerminateThread((HANDLE)thread, 0);
#endif
}
bool Thread :: start( THREAD_FUNCTION routine, void * ptr )
{
bool result = false;
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
if ( pthread_create(&thread, NULL, *routine, ptr) == 0 )
result = true;
#elif defined(__OS_WINDOWS__)
unsigned thread_id;
thread = _beginthreadex(NULL, 0, routine, ptr, 0, &thread_id);
if ( thread ) result = true;
#endif
return result;
}
bool Thread :: wait( long milliseconds )
{
bool result = false;
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
pthread_cancel(thread);
pthread_join(thread, NULL);
#elif defined(__OS_WINDOWS__)
DWORD timeout, retval;
if ( milliseconds < 0 ) timeout = INFINITE;
else timeout = milliseconds;
retval = WaitForSingleObject( (HANDLE)thread, timeout );
if ( retval == WAIT_OBJECT_0 ) {
CloseHandle( (HANDLE)thread );
thread = 0;
result = true;
}
#endif
return result;
}
void Thread :: test(void)
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
pthread_testcancel();
#endif
}
Mutex :: Mutex()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
pthread_mutex_init(&mutex, NULL);
#elif defined(__OS_WINDOWS__)
InitializeCriticalSection(&mutex);
#endif
}
Mutex :: ~Mutex()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
pthread_mutex_destroy(&mutex);
#elif defined(__OS_WINDOWS__)
DeleteCriticalSection(&mutex);
#endif
}
void Mutex :: lock()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
pthread_mutex_lock(&mutex);
#elif defined(__OS_WINDOWS__)
EnterCriticalSection(&mutex);
#endif
}
void Mutex :: unlock()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__))
pthread_mutex_unlock(&mutex);
#elif defined(__OS_WINDOWS__)
LeaveCriticalSection(&mutex);
#endif
}
/***************************************************/
/*! \class Thread
\brief STK thread class.
This class provides a uniform interface for
cross-platform thread use. On Linux and IRIX
systems, the pthread library is used. Under Windows,
the Windows thread library is used.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Thread.h"
#include<iostream.h>
Thread :: Thread()
{
thread = 0;
}
Thread :: ~Thread()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
pthread_cancel(thread);
pthread_join(thread, NULL);
#elif defined(__OS_WINDOWS__)
if ( thread )
TerminateThread((HANDLE)thread, 0);
#endif
}
bool Thread :: start( THREAD_FUNCTION routine, void * ptr )
{
bool result = false;
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
if ( pthread_create(&thread, NULL, *routine, ptr) == 0 )
result = true;
#elif defined(__OS_WINDOWS__)
unsigned thread_id;
thread = _beginthreadex(NULL, 0, routine, ptr, 0, &thread_id);
if ( thread ) result = true;
#endif
return result;
}
bool Thread :: wait( long milliseconds )
{
bool result = false;
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
pthread_cancel(thread);
pthread_join(thread, NULL);
#elif defined(__OS_WINDOWS__)
DWORD timeout, retval;
if ( milliseconds < 0 ) timeout = INFINITE;
else timeout = milliseconds;
retval = WaitForSingleObject( (HANDLE)thread, timeout );
if ( retval == WAIT_OBJECT_0 ) {
CloseHandle( (HANDLE)thread );
thread = 0;
result = true;
}
#endif
return result;
}
void Thread :: test(void)
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
pthread_testcancel();
#endif
}
Mutex :: Mutex()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
pthread_mutex_init(&mutex, NULL);
#elif defined(__OS_WINDOWS__)
InitializeCriticalSection(&mutex);
#endif
}
Mutex :: ~Mutex()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
pthread_mutex_destroy(&mutex);
#elif defined(__OS_WINDOWS__)
DeleteCriticalSection(&mutex);
#endif
}
void Mutex :: lock()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
pthread_mutex_lock(&mutex);
#elif defined(__OS_WINDOWS__)
EnterCriticalSection(&mutex);
#endif
}
void Mutex :: unlock()
{
#if (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__))
pthread_mutex_unlock(&mutex);
#elif defined(__OS_WINDOWS__)
LeaveCriticalSection(&mutex);
#endif
}

View File

@@ -43,10 +43,10 @@ TubeBell :: TubeBell()
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
strcat(files[0], "sinewave.raw");
strcat(files[1], "sinewave.raw");
strcat(files[2], "sinewave.raw");
strcat(files[3], "fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );

View File

@@ -1,93 +1,93 @@
/***************************************************/
/*! \class TwoPole
\brief STK two-pole filter class.
This protected Filter subclass implements
a two-pole digital filter. A method is
provided for creating a resonance in the
frequency response while maintaining a nearly
constant filter gain.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "TwoPole.h"
#include <math.h>
TwoPole :: TwoPole() : Filter()
{
MY_FLOAT B = 1.0;
MY_FLOAT A[3] = {1.0, 0.0, 0.0};
Filter::setCoefficients( 1, &B, 3, A );
}
TwoPole :: ~TwoPole()
{
}
void TwoPole :: clear(void)
{
Filter::clear();
}
void TwoPole :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void TwoPole :: setA1(MY_FLOAT a1)
{
a[1] = a1;
}
void TwoPole :: setA2(MY_FLOAT a2)
{
a[2] = a2;
}
void TwoPole :: setResonance(MY_FLOAT frequency, MY_FLOAT radius, bool normalize)
{
a[2] = radius * radius;
a[1] = (MY_FLOAT) -2.0 * radius * cos(TWO_PI * frequency / Stk::sampleRate());
if ( normalize ) {
// Normalize the filter gain ... not terribly efficient.
MY_FLOAT real = 1 - radius + (a[2] - radius) * cos(TWO_PI * 2 * frequency / Stk::sampleRate());
MY_FLOAT imag = (a[2] - radius) * sin(TWO_PI * 2 * frequency / Stk::sampleRate());
b[0] = sqrt( pow(real, 2) + pow(imag, 2) );
}
}
void TwoPole :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT TwoPole :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT TwoPole :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT TwoPole :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
outputs[0] = b[0] * inputs[0] - a[2] * outputs[2] - a[1] * outputs[1];
outputs[2] = outputs[1];
outputs[1] = outputs[0];
return outputs[0];
}
MY_FLOAT *TwoPole :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class TwoPole
\brief STK two-pole filter class.
This protected Filter subclass implements
a two-pole digital filter. A method is
provided for creating a resonance in the
frequency response while maintaining a nearly
constant filter gain.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "TwoPole.h"
#include <math.h>
TwoPole :: TwoPole() : Filter()
{
MY_FLOAT B = 1.0;
MY_FLOAT A[3] = {1.0, 0.0, 0.0};
Filter::setCoefficients( 1, &B, 3, A );
}
TwoPole :: ~TwoPole()
{
}
void TwoPole :: clear(void)
{
Filter::clear();
}
void TwoPole :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void TwoPole :: setA1(MY_FLOAT a1)
{
a[1] = a1;
}
void TwoPole :: setA2(MY_FLOAT a2)
{
a[2] = a2;
}
void TwoPole :: setResonance(MY_FLOAT frequency, MY_FLOAT radius, bool normalize)
{
a[2] = radius * radius;
a[1] = (MY_FLOAT) -2.0 * radius * cos(TWO_PI * frequency / Stk::sampleRate());
if ( normalize ) {
// Normalize the filter gain ... not terribly efficient.
MY_FLOAT real = 1 - radius + (a[2] - radius) * cos(TWO_PI * 2 * frequency / Stk::sampleRate());
MY_FLOAT imag = (a[2] - radius) * sin(TWO_PI * 2 * frequency / Stk::sampleRate());
b[0] = sqrt( pow(real, 2) + pow(imag, 2) );
}
}
void TwoPole :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT TwoPole :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT TwoPole :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT TwoPole :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
outputs[0] = b[0] * inputs[0] - a[2] * outputs[2] - a[1] * outputs[1];
outputs[2] = outputs[1];
outputs[1] = outputs[0];
return outputs[0];
}
MY_FLOAT *TwoPole :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

@@ -1,94 +1,94 @@
/***************************************************/
/*! \class TwoZero
\brief STK two-zero filter class.
This protected Filter subclass implements
a two-zero digital filter. A method is
provided for creating a "notch" in the
frequency response while maintaining a
constant filter gain.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "TwoZero.h"
#include <math.h>
TwoZero :: TwoZero() : Filter()
{
MY_FLOAT B[3] = {1.0, 0.0, 0.0};
MY_FLOAT A = 1.0;
Filter::setCoefficients( 3, B, 1, &A );
}
TwoZero :: ~TwoZero()
{
}
void TwoZero :: clear(void)
{
Filter::clear();
}
void TwoZero :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void TwoZero :: setB1(MY_FLOAT b1)
{
b[1] = b1;
}
void TwoZero :: setB2(MY_FLOAT b2)
{
b[2] = b2;
}
void TwoZero :: setNotch(MY_FLOAT frequency, MY_FLOAT radius)
{
b[2] = radius * radius;
b[1] = (MY_FLOAT) -2.0 * radius * cos(TWO_PI * (double) frequency / Stk::sampleRate());
// Normalize the filter gain.
if (b[1] > 0.0) // Maximum at z = 0.
b[0] = 1.0 / (1.0+b[1]+b[2]);
else // Maximum at z = -1.
b[0] = 1.0 / (1.0-b[1]+b[2]);
b[1] *= b[0];
b[2] *= b[0];
}
void TwoZero :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT TwoZero :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT TwoZero :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT TwoZero :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
outputs[0] = b[2] * inputs[2] + b[1] * inputs[1] + b[0] * inputs[0];
inputs[2] = inputs[1];
inputs[1] = inputs[0];
return outputs[0];
}
MY_FLOAT *TwoZero :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
/***************************************************/
/*! \class TwoZero
\brief STK two-zero filter class.
This protected Filter subclass implements
a two-zero digital filter. A method is
provided for creating a "notch" in the
frequency response while maintaining a
constant filter gain.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "TwoZero.h"
#include <math.h>
TwoZero :: TwoZero() : Filter()
{
MY_FLOAT B[3] = {1.0, 0.0, 0.0};
MY_FLOAT A = 1.0;
Filter::setCoefficients( 3, B, 1, &A );
}
TwoZero :: ~TwoZero()
{
}
void TwoZero :: clear(void)
{
Filter::clear();
}
void TwoZero :: setB0(MY_FLOAT b0)
{
b[0] = b0;
}
void TwoZero :: setB1(MY_FLOAT b1)
{
b[1] = b1;
}
void TwoZero :: setB2(MY_FLOAT b2)
{
b[2] = b2;
}
void TwoZero :: setNotch(MY_FLOAT frequency, MY_FLOAT radius)
{
b[2] = radius * radius;
b[1] = (MY_FLOAT) -2.0 * radius * cos(TWO_PI * (double) frequency / Stk::sampleRate());
// Normalize the filter gain.
if (b[1] > 0.0) // Maximum at z = 0.
b[0] = 1.0 / (1.0+b[1]+b[2]);
else // Maximum at z = -1.
b[0] = 1.0 / (1.0-b[1]+b[2]);
b[1] *= b[0];
b[2] *= b[0];
}
void TwoZero :: setGain(MY_FLOAT theGain)
{
Filter::setGain(theGain);
}
MY_FLOAT TwoZero :: getGain(void) const
{
return Filter::getGain();
}
MY_FLOAT TwoZero :: lastOut(void) const
{
return Filter::lastOut();
}
MY_FLOAT TwoZero :: tick(MY_FLOAT sample)
{
inputs[0] = gain * sample;
outputs[0] = b[2] * inputs[2] + b[1] * inputs[1] + b[0] * inputs[0];
inputs[2] = inputs[1];
inputs[1] = inputs[0];
return outputs[0];
}
MY_FLOAT *TwoZero :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

72
src/Vector3D.cpp Normal file
View File

@@ -0,0 +1,72 @@
/***************************************************/
/*! \class Vector3D
\brief STK 3D vector class.
This class implements a three-dimensional vector.
by Perry R. Cook, 1995 - 2002.
*/
/***************************************************/
#include "Vector3D.h"
#include <math.h>
Vector3D :: Vector3D(double initX, double initY, double initZ)
{
myX = initX;
myY = initY;
myZ = initZ;
}
Vector3D :: ~Vector3D()
{
}
double Vector3D :: getX()
{
return myX;
}
double Vector3D :: getY()
{
return myY;
}
double Vector3D :: getZ()
{
return myZ;
}
double Vector3D :: getLength()
{
double temp;
temp = myX * myX;
temp += myY * myY;
temp += myZ * myZ;
temp = sqrt(temp);
return temp;
}
void Vector3D :: setXYZ(double anX, double aY, double aZ)
{
myX = anX;
myY = aY;
myZ = aZ;
};
void Vector3D :: setX(double aval)
{
myX = aval;
}
void Vector3D :: setY(double aval)
{
myY = aval;
}
void Vector3D :: setZ(double aval)
{
myZ = aval;
}

248
src/VoicForm.cpp Normal file
View File

@@ -0,0 +1,248 @@
/***************************************************/
/*! \class VoicForm
\brief Four formant synthesis instrument.
This instrument contains an excitation singing
wavetable (looping wave with random and
periodic vibrato, smoothing on frequency,
etc.), excitation noise, and four sweepable
complex resonances.
Measured formant data is included, and enough
data is there to support either parallel or
cascade synthesis. In the floating point case
cascade synthesis is the most natural so
that's what you'll find here.
Control Change Numbers:
- Voiced/Unvoiced Mix = 2
- Vowel/Phoneme Selection = 4
- Vibrato Frequency = 11
- Vibrato Gain = 1
- Loudness (Spectral Tilt) = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "VoicForm.h"
#include "Phonemes.h"
#include "SKINI.msg"
#include <math.h>
#include <string.h>
VoicForm :: VoicForm() : Instrmnt()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
voiced = new SingWave( strcat(file,"impuls20.raw"), TRUE );
voiced->setGainRate( 0.001 );
voiced->setGainTarget( 0.0 );
noise = new Noise;
for ( int i=0; i<4; i++ ) {
filters[i] = new FormSwep;
filters[i]->setSweepRate( 0.001 );
}
onezero = new OneZero;
onezero->setZero( -0.9 );
onepole = new OnePole;
onepole->setPole( 0.9 );
noiseEnv = new Envelope;
noiseEnv->setRate( 0.001 );
noiseEnv->setTarget( 0.0 );
this->setPhoneme( "eee" );
this->clear();
}
VoicForm :: ~VoicForm()
{
delete voiced;
delete noise;
delete onezero;
delete onepole;
delete noiseEnv;
for ( int i=0; i<4; i++ ) {
delete filters[i];
}
}
void VoicForm :: clear()
{
onezero->clear();
onepole->clear();
for ( int i=0; i<4; i++ ) {
filters[i]->clear();
}
}
void VoicForm :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "VoicForm: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
voiced->setFrequency( freakency );
}
bool VoicForm :: setPhoneme(const char *phoneme )
{
bool found = false;
unsigned int i = 0;
while( i < 32 && !found ) {
if ( !strcmp( Phonemes::name(i), phoneme ) ) {
found = true;
filters[0]->setTargets( Phonemes::formantFrequency(i, 0), Phonemes::formantRadius(i, 0), pow(10.0, Phonemes::formantGain(i, 0 ) / 20.0) );
filters[1]->setTargets( Phonemes::formantFrequency(i, 1), Phonemes::formantRadius(i, 1), pow(10.0, Phonemes::formantGain(i, 1 ) / 20.0) );
filters[2]->setTargets( Phonemes::formantFrequency(i, 2), Phonemes::formantRadius(i, 2), pow(10.0, Phonemes::formantGain(i, 2 ) / 20.0) );
filters[3]->setTargets( Phonemes::formantFrequency(i, 3), Phonemes::formantRadius(i, 3), pow(10.0, Phonemes::formantGain(i, 3 ) / 20.0) );
setVoiced( Phonemes::voiceGain( i ) );
setUnVoiced( Phonemes::noiseGain( i ) );
#if defined(_STK_DEBUG_)
cout << "VoicForm: found formant " << phoneme << " (number " << i << ")" << endl;
#endif
}
i++;
}
if ( !found )
cerr << "VoicForm: phoneme " << phoneme << " not found!" << endl;
return found;
}
void VoicForm :: setVoiced(MY_FLOAT vGain)
{
voiced->setGainTarget(vGain);
}
void VoicForm :: setUnVoiced(MY_FLOAT nGain)
{
noiseEnv->setTarget(nGain);
}
void VoicForm :: setFilterSweepRate(int whichOne, MY_FLOAT rate)
{
if ( whichOne < 0 || whichOne > 3 ) {
cerr << "VoicForm: setFilterSweepRate filter argument outside range 0-3!" << endl;
return;
}
filters[whichOne]->setSweepRate(rate);
}
void VoicForm :: setPitchSweepRate(MY_FLOAT rate)
{
voiced->setSweepRate(rate);
}
void VoicForm :: speak()
{
voiced->noteOn();
}
void VoicForm :: quiet()
{
voiced->noteOff();
noiseEnv->setTarget( 0.0 );
}
void VoicForm :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
setFrequency(frequency);
voiced->setGainTarget(amplitude);
onepole->setPole( 0.97 - (amplitude * 0.2) );
}
void VoicForm :: noteOff(MY_FLOAT amplitude)
{
this->quiet();
}
MY_FLOAT VoicForm :: tick()
{
MY_FLOAT temp;
temp = onepole->tick( onezero->tick( voiced->tick() ) );
temp += noiseEnv->tick() * noise->tick();
lastOutput = filters[0]->tick(temp);
lastOutput += filters[1]->tick(temp);
lastOutput += filters[2]->tick(temp);
lastOutput += filters[3]->tick(temp);
/*
temp += noiseEnv->tick() * noise->tick();
lastOutput = filters[0]->tick(temp);
lastOutput = filters[1]->tick(lastOutput);
lastOutput = filters[2]->tick(lastOutput);
lastOutput = filters[3]->tick(lastOutput);
*/
return lastOutput;
}
void VoicForm :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "VoicForm: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "VoicForm: Control value greater than 128.0!" << endl;
}
if (number == __SK_Breath_) { // 2
this->setVoiced( 1.0 - norm );
this->setUnVoiced( 0.01 * norm );
}
else if (number == __SK_FootControl_) { // 4
MY_FLOAT temp = 0.0;
unsigned int i = (int) value;
if (i < 32) {
temp = 0.9;
}
else if (i < 64) {
i -= 32;
temp = 1.0;
}
else if (i < 96) {
i -= 64;
temp = 1.1;
}
else if (i < 128) {
i -= 96;
temp = 1.2;
}
else if (i == 128) {
i = 0;
temp = 1.4;
}
filters[0]->setTargets( temp * Phonemes::formantFrequency(i, 0), Phonemes::formantRadius(i, 0), pow(10.0, Phonemes::formantGain(i, 0 ) / 20.0) );
filters[1]->setTargets( temp * Phonemes::formantFrequency(i, 1), Phonemes::formantRadius(i, 1), pow(10.0, Phonemes::formantGain(i, 1 ) / 20.0) );
filters[2]->setTargets( temp * Phonemes::formantFrequency(i, 2), Phonemes::formantRadius(i, 2), pow(10.0, Phonemes::formantGain(i, 2 ) / 20.0) );
filters[3]->setTargets( temp * Phonemes::formantFrequency(i, 3), Phonemes::formantRadius(i, 3), pow(10.0, Phonemes::formantGain(i, 3 ) / 20.0) );
setVoiced( Phonemes::voiceGain( i ) );
setUnVoiced( Phonemes::noiseGain( i ) );
}
else if (number == __SK_ModFrequency_) // 11
voiced->setVibratoRate( norm * 12.0); // 0 to 12 Hz
else if (number == __SK_ModWheel_) // 1
voiced->setVibratoGain( norm * 0.2);
else if (number == __SK_AfterTouch_Cont_) { // 128
setVoiced( norm );
onepole->setPole( 0.97 - ( norm * 0.2) );
}
else
cerr << "VoicForm: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "VoicForm: controlChange number = " << number << ", value = " << value << endl;
#endif
}

254
src/Voicer.cpp Normal file
View File

@@ -0,0 +1,254 @@
/***************************************************/
/*! \class Voicer
\brief STK voice manager class.
This class can be used to manage a group of
STK instrument classes. Individual voices can
be controlled via unique note tags.
Instrument groups can be controlled by channel
number.
A previously constructed STK instrument class
is linked with a voice manager using the
addInstrument() function. An optional channel
number argument can be specified to the
addInstrument() function as well (default
channel = 0). The voice manager does not
delete any instrument instances ... it is the
responsibility of the user to allocate and
deallocate all instruments.
The tick() function returns the mix of all
sounding voices. Each noteOn returns a unique
tag (credits to the NeXT MusicKit), so you can
send control changes to specific voices within
an ensemble. Alternately, control changes can
be sent to all voices on a given channel.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Voicer.h"
#include <stdlib.h>
#include <math.h>
Voicer :: Voicer( int maxInstruments, MY_FLOAT decayTime )
{
nVoices = 0;
maxVoices = maxInstruments;
voices = (Voice *) new Voice[maxVoices];
tags = 23456;
muteTime = (int) ( decayTime * Stk::sampleRate() );
}
Voicer :: ~Voicer()
{
delete [] voices;
//free( voices );
}
void Voicer :: addInstrument( Instrmnt *instrument, int channel )
{
//voices = (Voice *) realloc( (void *) voices, nVoices+1 * sizeof( Voice ) );
if ( nVoices == maxVoices ) {
cerr << "Voicer: Maximum number of voices already added!!" << endl;
return;
}
voices[nVoices].instrument = instrument;
voices[nVoices].tag = 0;
voices[nVoices].channel = channel;
voices[nVoices].noteNumber = -1;
voices[nVoices].frequency = 0.0;
voices[nVoices].sounding = 0;
nVoices++;
}
void Voicer :: removeInstrument( Instrmnt *instrument )
{
bool found = false;
for ( int i=0; i<nVoices; i++ ) {
if ( voices[i].instrument == instrument ) found = true;
if ( found && i+1 < nVoices ) {
voices[i].instrument = voices[i+1].instrument;
voices[i].tag = voices[i+1].tag;
voices[i].noteNumber = voices[i+1].noteNumber;
voices[i].frequency = voices[i+1].frequency;
voices[i].sounding = voices[i+1].sounding;
voices[i].channel = voices[i+1].channel;
}
}
if ( found )
nVoices--;
//voices = (Voice *) realloc( voices, --nVoices * sizeof( Voice ) );
}
long Voicer :: noteOn(MY_FLOAT noteNumber, MY_FLOAT amplitude, int channel )
{
int i;
MY_FLOAT frequency = (MY_FLOAT) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 );
for ( i=0; i<nVoices; i++ ) {
if (voices[i].noteNumber < 0 && voices[i].channel == channel) {
voices[i].tag = tags++;
voices[i].channel = channel;
voices[i].noteNumber = noteNumber;
voices[i].frequency = frequency;
voices[i].instrument->noteOn( frequency, amplitude * ONE_OVER_128 );
voices[i].sounding = 1;
return voices[i].tag;
}
}
// All voices are sounding, so interrupt the oldest voice.
int voice = -1;
for ( i=0; i<nVoices; i++ ) {
if ( voices[i].channel == channel ) {
if ( voice == -1 ) voice = i;
else if ( voices[i].tag < voices[voice].tag ) voice = i;
}
}
if ( voice >= 0 ) {
voices[voice].tag = tags++;
voices[voice].channel = channel;
voices[voice].noteNumber = noteNumber;
voices[voice].frequency = frequency;
voices[voice].instrument->noteOn( frequency, amplitude * ONE_OVER_128 );
voices[voice].sounding = 1;
return voices[voice].tag;
}
return -1;
}
void Voicer :: noteOff( MY_FLOAT noteNumber, MY_FLOAT amplitude, int channel )
{
for ( int i=0; i<nVoices; i++ ) {
if ( voices[i].noteNumber == noteNumber && voices[i].channel == channel ) {
voices[i].instrument->noteOff( amplitude * ONE_OVER_128 );
voices[i].sounding = -muteTime;
}
}
}
void Voicer :: noteOff( long tag, MY_FLOAT amplitude )
{
for ( int i=0; i<nVoices; i++ ) {
if ( voices[i].tag == tag ) {
voices[i].instrument->noteOff( amplitude * ONE_OVER_128 );
voices[i].sounding = -muteTime;
break;
}
}
}
void Voicer :: setFrequency( MY_FLOAT noteNumber, int channel )
{
MY_FLOAT frequency = (MY_FLOAT) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 );
for ( int i=0; i<nVoices; i++ ) {
if ( voices[i].channel == channel ) {
voices[i].noteNumber = noteNumber;
voices[i].frequency = frequency;
voices[i].instrument->setFrequency( frequency );
}
}
}
void Voicer :: setFrequency( long tag, MY_FLOAT noteNumber )
{
MY_FLOAT frequency = (MY_FLOAT) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 );
for ( int i=0; i<nVoices; i++ ) {
if ( voices[i].tag == tag ) {
voices[i].noteNumber = noteNumber;
voices[i].frequency = frequency;
voices[i].instrument->setFrequency( frequency );
break;
}
}
}
void Voicer :: pitchBend( MY_FLOAT value, int channel )
{
MY_FLOAT pitchScaler;
if ( value < 64.0 )
pitchScaler = pow(0.5, (64.0-value)/64.0);
else
pitchScaler = pow(2.0, (value-64.0)/64.0);
for ( int i=0; i<nVoices; i++ ) {
if ( voices[i].channel == channel )
voices[i].instrument->setFrequency( (MY_FLOAT) (voices[i].frequency * pitchScaler) );
}
}
void Voicer :: pitchBend( long tag, MY_FLOAT value )
{
MY_FLOAT pitchScaler;
if ( value < 64.0 )
pitchScaler = pow(0.5, (64.0-value)/64.0);
else
pitchScaler = pow(2.0, (value-64.0)/64.0);
for ( int i=0; i<nVoices; i++ ) {
if ( voices[i].tag == tag ) {
voices[i].instrument->setFrequency( (MY_FLOAT) (voices[i].frequency * pitchScaler) );
break;
}
}
}
void Voicer :: controlChange( int number, MY_FLOAT value, int channel )
{
for ( int i=0; i<nVoices; i++ ) {
if ( voices[i].channel == channel )
voices[i].instrument->controlChange( number, value );
}
}
void Voicer :: controlChange( long tag, int number, MY_FLOAT value )
{
for ( int i=0; i<nVoices; i++ ) {
if ( voices[i].tag == tag ) {
voices[i].instrument->controlChange( number, value );
break;
}
}
}
void Voicer :: silence( void )
{
for ( int i=0; i<nVoices; i++ ) {
if ( voices[i].sounding > 0 )
voices[i].instrument->noteOff( 0.5 );
}
}
MY_FLOAT Voicer :: tick()
{
lastOutput = 0.0;
for ( int i=0; i<nVoices; i++ ) {
if ( voices[i].sounding != 0 )
lastOutput += voices[i].instrument->tick();
if ( voices[i].sounding < 0 ) {
voices[i].sounding++;
if ( voices[i].sounding == 0 )
voices[i].noteNumber = -1;
}
}
return lastOutput / nVoices;
}
MY_FLOAT *Voicer :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick();
return vector;
}
MY_FLOAT Voicer :: lastOut() const
{
return lastOutput;
}

View File

@@ -1,130 +1,130 @@
/***************************************************/
/*! \class WaveLoop
\brief STK waveform oscillator class.
This class inherits from WvIn and provides
audio file looping functionality.
WaveLoop 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 pointers to multi-channel sample frames.
For single-channel data, these methods return
equivalent values.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "WaveLoop.h"
#include <math.h>
WaveLoop :: WaveLoop( const char *fileName, bool raw )
: WvIn( fileName, raw ), phaseOffset(0.0)
{
}
WaveLoop :: ~WaveLoop()
{
}
void WaveLoop :: readData( unsigned long index )
{
WvIn::readData( index );
// If at end of file, redo extra sample frame for looping.
if (chunkPointer+bufferSize == fileSize) {
for (unsigned int j=0; j<channels; j++)
data[bufferSize*channels+j] = data[j];
}
}
void WaveLoop :: setFrequency(MY_FLOAT aFrequency)
{
// This is a looping frequency.
rate = fileSize * aFrequency / sampleRate();
}
void WaveLoop :: addTime(MY_FLOAT aTime)
{
// Add an absolute time in samples
time += aTime;
while (time < 0.0)
time += fileSize;
while (time >= fileSize)
time -= fileSize;
}
void WaveLoop :: addPhase(MY_FLOAT anAngle)
{
// Add a time in cycles (one cycle = fileSize).
time += fileSize * anAngle;
while (time < 0.0)
time += fileSize;
while (time >= fileSize)
time -= fileSize;
}
void WaveLoop :: addPhaseOffset(MY_FLOAT anAngle)
{
// Add a phase offset in cycles, where 1.0 = fileSize.
phaseOffset = fileSize * anAngle;
}
const MY_FLOAT *WaveLoop :: tickFrame(void)
{
register MY_FLOAT tyme, alpha;
register unsigned long i, index;
// Check limits of time address ... if necessary, recalculate modulo fileSize.
while (time < 0.0)
time += fileSize;
while (time >= fileSize)
time -= fileSize;
if (phaseOffset) {
tyme = time + phaseOffset;
while (tyme < 0.0)
tyme += fileSize;
while (tyme >= fileSize)
tyme -= fileSize;
}
else {
tyme = time;
}
if (chunking) {
// Check the time address vs. our current buffer limits.
if ( (tyme < chunkPointer) || (tyme >= chunkPointer+bufferSize) )
this->readData((long) tyme);
// Adjust index for the current buffer.
tyme -= chunkPointer;
}
// Always do linear interpolation here ... integer part of time address.
index = (unsigned long) tyme;
// Fractional part of time address.
alpha = tyme - (MY_FLOAT) index;
index *= channels;
for (i=0; i<channels; i++) {
lastOutput[i] = data[index];
lastOutput[i] += (alpha * (data[index+channels] - lastOutput[i]));
index++;
}
if (chunking) {
// Scale outputs by gain.
for (i=0; i<channels; i++) lastOutput[i] *= gain;
}
// Increment time, which can be negative.
time += rate;
return lastOutput;
}
/***************************************************/
/*! \class WaveLoop
\brief STK waveform oscillator class.
This class inherits from WvIn and provides
audio file looping functionality.
WaveLoop 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 pointers to multi-channel sample frames.
For single-channel data, these methods return
equivalent values.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "WaveLoop.h"
#include <math.h>
WaveLoop :: WaveLoop( const char *fileName, bool raw )
: WvIn( fileName, raw ), phaseOffset(0.0)
{
}
WaveLoop :: ~WaveLoop()
{
}
void WaveLoop :: readData( unsigned long index )
{
WvIn::readData( index );
// If at end of file, redo extra sample frame for looping.
if (chunkPointer+bufferSize == fileSize) {
for (unsigned int j=0; j<channels; j++)
data[bufferSize*channels+j] = data[j];
}
}
void WaveLoop :: setFrequency(MY_FLOAT aFrequency)
{
// This is a looping frequency.
rate = fileSize * aFrequency / sampleRate();
}
void WaveLoop :: addTime(MY_FLOAT aTime)
{
// Add an absolute time in samples
time += aTime;
while (time < 0.0)
time += fileSize;
while (time >= fileSize)
time -= fileSize;
}
void WaveLoop :: addPhase(MY_FLOAT anAngle)
{
// Add a time in cycles (one cycle = fileSize).
time += fileSize * anAngle;
while (time < 0.0)
time += fileSize;
while (time >= fileSize)
time -= fileSize;
}
void WaveLoop :: addPhaseOffset(MY_FLOAT anAngle)
{
// Add a phase offset in cycles, where 1.0 = fileSize.
phaseOffset = fileSize * anAngle;
}
const MY_FLOAT *WaveLoop :: tickFrame(void)
{
register MY_FLOAT tyme, alpha;
register unsigned long i, index;
// Check limits of time address ... if necessary, recalculate modulo fileSize.
while (time < 0.0)
time += fileSize;
while (time >= fileSize)
time -= fileSize;
if (phaseOffset) {
tyme = time + phaseOffset;
while (tyme < 0.0)
tyme += fileSize;
while (tyme >= fileSize)
tyme -= fileSize;
}
else {
tyme = time;
}
if (chunking) {
// Check the time address vs. our current buffer limits.
if ( (tyme < chunkPointer) || (tyme >= chunkPointer+bufferSize) )
this->readData((long) tyme);
// Adjust index for the current buffer.
tyme -= chunkPointer;
}
// Always do linear interpolation here ... integer part of time address.
index = (unsigned long) tyme;
// Fractional part of time address.
alpha = tyme - (MY_FLOAT) index;
index *= channels;
for (i=0; i<channels; i++) {
lastOutput[i] = data[index];
lastOutput[i] += (alpha * (data[index+channels] - lastOutput[i]));
index++;
}
if (chunking) {
// Scale outputs by gain.
for (i=0; i<channels; i++) lastOutput[i] *= gain;
}
// Increment time, which can be negative.
time += rate;
return lastOutput;
}

259
src/Whistle.cpp Normal file
View File

@@ -0,0 +1,259 @@
/***************************************************/
/*! \class Whistle
\brief STK police/referee whistle instrument class.
This class implements a hybrid physical/spectral
model of a police whistle (a la Cook).
Control Change Numbers:
- Noise Gain = 4
- Fipple Modulation Frequency = 11
- Fipple Modulation Gain = 1
- Blowing Frequency Modulation = 2
- Volume = 128
by Perry R. Cook 1996 - 2002.
*/
/***************************************************/
#include "Whistle.h"
#include "SKINI.msg"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define CAN_RADIUS 100
#define PEA_RADIUS 30
#define BUMP_RADIUS 5
#define NORM_CAN_LOSS 0.97
#define SLOW_CAN_LOSS 0.90
#define GRAVITY 20.0
// GRAVITY WAS 6.0
#define NORM_TICK_SIZE 0.004
#define SLOW_TICK_SIZE 0.0001
#define ENV_RATE 0.001
Whistle :: Whistle()
{
tempVector = new Vector3D(0,0,0);
can = new Sphere(CAN_RADIUS);
pea = new Sphere(PEA_RADIUS);
bumper = new Sphere(BUMP_RADIUS);
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char path[128];
strcpy(path, RAWWAVE_PATH);
sine = new WaveLoop( strcat(path,"sinewave.raw"), TRUE );
sine->setFrequency(2800.0);
can->setPosition(0, 0, 0); // set can location
can->setVelocity(0, 0, 0); // and the velocity
onepole.setPole(0.95); // 0.99
bumper->setPosition(0.0, CAN_RADIUS-BUMP_RADIUS, 0);
bumper->setPosition(0.0, CAN_RADIUS-BUMP_RADIUS, 0);
pea->setPosition(0, CAN_RADIUS/2, 0);
pea->setVelocity(35, 15, 0);
envelope.setRate(ENV_RATE);
envelope.keyOn();
fippleFreqMod = 0.5;
fippleGainMod = 0.5;
blowFreqMod = 0.25;
noiseGain = 0.125;
maxPressure = (MY_FLOAT) 0.0;
baseFrequency = 2000;
tickSize = NORM_TICK_SIZE;
canLoss = NORM_CAN_LOSS;
subSample = 1;
subSampCount = subSample;
}
Whistle :: ~Whistle()
{
delete tempVector;
delete can;
delete pea;
delete bumper;
delete sine;
}
void Whistle :: clear()
{
}
void Whistle :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT freakency = frequency * 4; // the whistle is a transposing instrument
if ( frequency <= 0.0 ) {
cerr << "Whistle: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
baseFrequency = freakency;
}
void Whistle :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
envelope.setRate(ENV_RATE);
envelope.setTarget(amplitude);
}
void Whistle :: stopBlowing(MY_FLOAT rate)
{
envelope.setRate(rate);
envelope.keyOff();
}
void Whistle :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
setFrequency(frequency);
startBlowing(amplitude*2.0 ,amplitude * 0.2);
#if defined(_STK_DEBUG_)
cerr << "Whistle: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Whistle :: noteOff(MY_FLOAT amplitude)
{
this->stopBlowing(amplitude * 0.02);
#if defined(_STK_DEBUG_)
cerr << "Whistle: NoteOff amplitude = " << amplitude << endl;
#endif
}
int frameCount = 0;
MY_FLOAT Whistle :: tick()
{
MY_FLOAT soundMix, tempFreq;
double envOut = 0, temp, temp1, temp2, tempX, tempY;
double phi, cosphi, sinphi;
double gain = 0.5, mod = 0.0;
if (--subSampCount <= 0) {
tempVectorP = pea->getPosition();
subSampCount = subSample;
temp = bumper->isInside(tempVectorP);
#ifdef WHISTLE_ANIMATION
frameCount += 1;
if (frameCount >= (1470 / subSample)) {
frameCount = 0;
printf("%f %f %f\n",tempVectorP->getX(),tempVectorP->getY(),envOut);
fflush(stdout);
}
#endif
envOut = envelope.tick();
if (temp < (BUMP_RADIUS + PEA_RADIUS)) {
tempX = envOut * tickSize * 2000 * noise.tick();
tempY = -envOut * tickSize * 1000 * (1.0 + noise.tick());
pea->addVelocity(tempX,tempY,0);
pea->tick(tickSize);
}
mod = exp(-temp * 0.01); // exp. distance falloff of fipple/pea effect
temp = onepole.tick(mod); // smooth it a little
gain = (1.0 - (fippleGainMod*0.5)) + (2.0 * fippleGainMod * temp);
gain *= gain; // squared distance/gain
// tempFreq = 1.0 // Normalized Base Freq
// + (fippleFreqMod * 0.25) - (fippleFreqMod * temp) // fippleModulation
// - (blowFreqMod) + (blowFreqMod * envOut); // blowingModulation
// short form of above
tempFreq = 1.0 + fippleFreqMod*(0.25-temp) + blowFreqMod*(envOut-1.0);
tempFreq *= baseFrequency;
sine->setFrequency(tempFreq);
tempVectorP = pea->getPosition();
temp = can->isInside(tempVectorP);
temp = -temp; // We know (hope) it's inside, just how much??
if (temp < (PEA_RADIUS * 1.25)) {
pea->getVelocity(tempVector); // This is the can/pea collision
tempX = tempVectorP->getX(); // calculation. Could probably
tempY = tempVectorP->getY(); // simplify using tables, etc.
phi = -atan2(tempY,tempX);
cosphi = cos(phi);
sinphi = sin(phi);
temp1 = (cosphi*tempVector->getX()) - (sinphi*tempVector->getY());
temp2 = (sinphi*tempVector->getX()) + (cosphi*tempVector->getY());
temp1 = -temp1;
tempX = (cosphi*temp1) + (sinphi*temp2);
tempY = (-sinphi*temp1) + (cosphi*temp2);
pea->setVelocity(tempX, tempY, 0);
pea->tick(tickSize);
pea->setVelocity(tempX*canLoss, tempY*canLoss, 0);
pea->tick(tickSize);
}
temp = tempVectorP->getLength();
if (temp > 0.01) {
tempX = tempVectorP->getX();
tempY = tempVectorP->getY();
phi = atan2(tempY,tempX);
phi += 0.3 * temp / CAN_RADIUS;
cosphi = cos(phi);
sinphi = sin(phi);
tempX = 3.0 * temp * cosphi;
tempY = 3.0 * temp * sinphi;
}
else {
tempX = 0.0;
tempY = 0.0;
}
temp = (0.9 + 0.1*subSample*noise.tick()) * envOut * 0.6 * tickSize;
pea->addVelocity(temp * tempX,
(temp*tempY) - (GRAVITY*tickSize),0);
pea->tick(tickSize);
// bumper->tick(0.0);
}
temp = envOut * envOut * gain / 2;
soundMix = temp * (sine->tick() + (noiseGain*noise.tick()));
lastOutput = 0.25 * soundMix; // should probably do one-zero filter here
return lastOutput;
}
void Whistle :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Whistle: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Whistle: Control value greater than 128.0!" << endl;
}
if (number == __SK_NoiseLevel_) // 4
noiseGain = 0.25 * norm;
else if (number == __SK_ModFrequency_) // 11
fippleFreqMod = norm;
else if (number == __SK_ModWheel_) // 1
fippleGainMod = norm;
else if (number == __SK_AfterTouch_Cont_) // 128
envelope.setTarget( norm * 2.0 );
else if (number == __SK_Breath_) // 2
blowFreqMod = norm * 0.5;
else if (number == __SK_Sustain_) // 64
if (value < 1.0) subSample = 1;
else
cerr << "Whistle: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Whistle: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

@@ -43,10 +43,10 @@ Wurley :: Wurley()
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
strcat(files[0], "sinewave.raw");
strcat(files[1], "sinewave.raw");
strcat(files[2], "sinewave.raw");
strcat(files[3], "fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

257
src/asio/asio.cpp Normal file
View File

@@ -0,0 +1,257 @@
/*
Steinberg Audio Stream I/O API
(c) 1996, Steinberg Soft- und Hardware GmbH
asio.cpp
asio functions entries which translate the
asio interface to the asiodrvr class methods
*/
#include <string.h>
#include "asiosys.h" // platform definition
#include "asio.h"
#if MAC
#include "asiodrvr.h"
#pragma export on
AsioDriver *theAsioDriver = 0;
extern "C"
{
long main()
{
return 'ASIO';
}
#elif WINDOWS
#include "windows.h"
#include "iasiodrv.h"
#include "asiodrivers.h"
IASIO *theAsioDriver = 0;
extern AsioDrivers *asioDrivers;
#elif SGI || SUN || BEOS || LINUX
#include "asiodrvr.h"
static AsioDriver *theAsioDriver = 0;
#endif
//-----------------------------------------------------------------------------------------------------
ASIOError ASIOInit(ASIODriverInfo *info)
{
#if MAC || SGI || SUN || BEOS || LINUX
if(theAsioDriver)
{
delete theAsioDriver;
theAsioDriver = 0;
}
info->driverVersion = 0;
strcpy(info->name, "No ASIO Driver");
theAsioDriver = getDriver();
if(!theAsioDriver)
{
strcpy(info->errorMessage, "Not enough memory for the ASIO driver!");
return ASE_NotPresent;
}
if(!theAsioDriver->init(info->sysRef))
{
theAsioDriver->getErrorMessage(info->errorMessage);
delete theAsioDriver;
theAsioDriver = 0;
return ASE_NotPresent;
}
strcpy(info->errorMessage, "No ASIO Driver Error");
theAsioDriver->getDriverName(info->name);
info->driverVersion = theAsioDriver->getDriverVersion();
return ASE_OK;
#else
info->driverVersion = 0;
strcpy(info->name, "No ASIO Driver");
if(theAsioDriver) // must be loaded!
{
if(!theAsioDriver->init(info->sysRef))
{
theAsioDriver->getErrorMessage(info->errorMessage);
theAsioDriver = 0;
return ASE_NotPresent;
}
strcpy(info->errorMessage, "No ASIO Driver Error");
theAsioDriver->getDriverName(info->name);
info->driverVersion = theAsioDriver->getDriverVersion();
return ASE_OK;
}
return ASE_NotPresent;
#endif // !MAC
}
ASIOError ASIOExit(void)
{
if(theAsioDriver)
{
#if WINDOWS
asioDrivers->removeCurrentDriver();
#else
delete theAsioDriver;
#endif
}
theAsioDriver = 0;
return ASE_OK;
}
ASIOError ASIOStart(void)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->start();
}
ASIOError ASIOStop(void)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->stop();
}
ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels)
{
if(!theAsioDriver)
{
*numInputChannels = *numOutputChannels = 0;
return ASE_NotPresent;
}
return theAsioDriver->getChannels(numInputChannels, numOutputChannels);
}
ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency)
{
if(!theAsioDriver)
{
*inputLatency = *outputLatency = 0;
return ASE_NotPresent;
}
return theAsioDriver->getLatencies(inputLatency, outputLatency);
}
ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity)
{
if(!theAsioDriver)
{
*minSize = *maxSize = *preferredSize = *granularity = 0;
return ASE_NotPresent;
}
return theAsioDriver->getBufferSize(minSize, maxSize, preferredSize, granularity);
}
ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->canSampleRate(sampleRate);
}
ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->getSampleRate(currentRate);
}
ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->setSampleRate(sampleRate);
}
ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources)
{
if(!theAsioDriver)
{
*numSources = 0;
return ASE_NotPresent;
}
return theAsioDriver->getClockSources(clocks, numSources);
}
ASIOError ASIOSetClockSource(long reference)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->setClockSource(reference);
}
ASIOError ASIOGetSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->getSamplePosition(sPos, tStamp);
}
ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info)
{
if(!theAsioDriver)
{
info->channelGroup = -1;
info->type = ASIOSTInt16MSB;
strcpy(info->name, "None");
return ASE_NotPresent;
}
return theAsioDriver->getChannelInfo(info);
}
ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels,
long bufferSize, ASIOCallbacks *callbacks)
{
if(!theAsioDriver)
{
ASIOBufferInfo *info = bufferInfos;
for(long i = 0; i < numChannels; i++, info++)
info->buffers[0] = info->buffers[1] = 0;
return ASE_NotPresent;
}
return theAsioDriver->createBuffers(bufferInfos, numChannels, bufferSize, callbacks);
}
ASIOError ASIODisposeBuffers(void)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->disposeBuffers();
}
ASIOError ASIOControlPanel(void)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->controlPanel();
}
ASIOError ASIOFuture(long selector, void *opt)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->future(selector, opt);
}
ASIOError ASIOOutputReady(void)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->outputReady();
}
#if MAC
} // extern "C"
#pragma export off
#endif

955
src/asio/asio.h Normal file
View File

@@ -0,0 +1,955 @@
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
/*
Steinberg Audio Stream I/O API
(c) 1997 - 1999, Steinberg Soft- und Hardware GmbH
ASIO Interface Specification v 2.0
basic concept is an i/o synchronous double-buffer scheme:
on bufferSwitch(index == 0), host will read/write:
after ASIOStart(), the
read first input buffer A (index 0)
| will be invalid (empty)
* ------------------------
|------------------------|-----------------------|
| | |
| Input Buffer A (0) | Input Buffer B (1) |
| | |
|------------------------|-----------------------|
| | |
| Output Buffer A (0) | Output Buffer B (1) |
| | |
|------------------------|-----------------------|
* -------------------------
| before calling ASIOStart(),
write host will have filled output
buffer B (index 1) already
*please* take special care of proper statement of input
and output latencies (see ASIOGetLatencies()), these
control sequencer sync accuracy
*/
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
/*
prototypes summary:
ASIOError ASIOInit(ASIODriverInfo *info);
ASIOError ASIOExit(void);
ASIOError ASIOStart(void);
ASIOError ASIOStop(void);
ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels);
ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency);
ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);
ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate);
ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate);
ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate);
ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources);
ASIOError ASIOSetClockSource(long reference);
ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp);
ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info);
ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels,
long bufferSize, ASIOCallbacks *callbacks);
ASIOError ASIODisposeBuffers(void);
ASIOError ASIOControlPanel(void);
void *ASIOFuture(long selector, void *params);
ASIOError ASIOOutputReady(void);
*/
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
#ifndef __ASIO_H
#define __ASIO_H
// force 4 byte alignment
#if defined(_MSC_VER) && !defined(__MWERKS__)
#pragma pack(push,4)
#elif PRAGMA_ALIGN_SUPPORTED
#pragma options align = native
#endif
//- - - - - - - - - - - - - - - - - - - - - - - - -
// Type definitions
//- - - - - - - - - - - - - - - - - - - - - - - - -
// number of samples data type is 64 bit integer
#if NATIVE_INT64
typedef long long int ASIOSamples;
#else
typedef struct ASIOSamples {
unsigned long hi;
unsigned long lo;
} ASIOSamples;
#endif
// Timestamp data type is 64 bit integer,
// Time format is Nanoseconds.
#if NATIVE_INT64
typedef long long int ASIOTimeStamp ;
#else
typedef struct ASIOTimeStamp {
unsigned long hi;
unsigned long lo;
} ASIOTimeStamp;
#endif
// Samplerates are expressed in IEEE 754 64 bit double float,
// native format as host computer
#if IEEE754_64FLOAT
typedef double ASIOSampleRate;
#else
typedef struct ASIOSampleRate {
char ieee[8];
} ASIOSampleRate;
#endif
// Boolean values are expressed as long
typedef long ASIOBool;
enum {
ASIOFalse = 0,
ASIOTrue = 1
};
// Sample Types are expressed as long
typedef long ASIOSampleType;
enum {
ASIOSTInt16MSB = 0,
ASIOSTInt24MSB = 1, // used for 20 bits as well
ASIOSTInt32MSB = 2,
ASIOSTFloat32MSB = 3, // IEEE 754 32 bit float
ASIOSTFloat64MSB = 4, // IEEE 754 64 bit double float
// 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
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
ASIOSTInt16LSB = 16,
ASIOSTInt24LSB = 17, // used for 20 bits as well
ASIOSTInt32LSB = 18,
ASIOSTFloat32LSB = 19, // IEEE 754 32 bit float, as found on Intel x86 architecture
ASIOSTFloat64LSB = 20, // IEEE 754 64 bit double float, as found on Intel x86 architecture
// these are used for 32 bit data buffer, with different alignment of the data inside
// 32 bit PCI bus systems can more easily used with these
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
};
//- - - - - - - - - - - - - - - - - - - - - - - - -
// Error codes
//- - - - - - - - - - - - - - - - - - - - - - - - -
typedef long ASIOError;
enum {
ASE_OK = 0, // This value will be returned whenever the call succeeded
ASE_SUCCESS = 0x3f4847a0, // unique success return value for ASIOFuture calls
ASE_NotPresent = -1000, // hardware input or output is not present or available
ASE_HWMalfunction, // hardware is malfunctioning (can be returned by any ASIO function)
ASE_InvalidParameter, // input parameter invalid
ASE_InvalidMode, // hardware is in a bad mode or used in a bad mode
ASE_SPNotAdvancing, // hardware is not running when sample position is inquired
ASE_NoClock, // sample clock or rate cannot be determined or is not present
ASE_NoMemory // not enough memory for completing the request
};
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
//- - - - - - - - - - - - - - - - - - - - - - - - -
// Time Info support
//- - - - - - - - - - - - - - - - - - - - - - - - -
typedef struct ASIOTimeCode
{
double speed; // speed relation (fraction of nominal speed)
// optional; set to 0. or 1. if not supported
ASIOSamples timeCodeSamples; // time in samples
unsigned long flags; // some information flags (see below)
char future[64];
} ASIOTimeCode;
typedef enum ASIOTimeCodeFlags
{
kTcValid = 1,
kTcRunning = 1 << 1,
kTcReverse = 1 << 2,
kTcOnspeed = 1 << 3,
kTcStill = 1 << 4,
kTcSpeedValid = 1 << 8
} ASIOTimeCodeFlags;
typedef struct AsioTimeInfo
{
double speed; // absolute speed (1. = nominal)
ASIOTimeStamp systemTime; // system time related to samplePosition, in nanoseconds
// on mac, must be derived from Microseconds() (not UpTime()!)
// on windows, must be derived from timeGetTime()
ASIOSamples samplePosition;
ASIOSampleRate sampleRate; // current rate
unsigned long flags; // (see below)
char reserved[12];
} AsioTimeInfo;
typedef enum AsioTimeInfoFlags
{
kSystemTimeValid = 1, // must always be valid
kSamplePositionValid = 1 << 1, // must always be valid
kSampleRateValid = 1 << 2,
kSpeedValid = 1 << 3,
kSampleRateChanged = 1 << 4,
kClockSourceChanged = 1 << 5
} AsioTimeInfoFlags;
typedef struct ASIOTime // both input/output
{
long reserved[4]; // must be 0
struct AsioTimeInfo timeInfo; // required
struct ASIOTimeCode timeCode; // optional, evaluated if (timeCode.flags & kTcValid)
} ASIOTime;
/*
using time info:
it is recommended to use the new method with time info even if the asio
device does not support timecode; continuous calls to ASIOGetSamplePosition
and ASIOGetSampleRate are avoided, and there is a more defined relationship
between callback time and the time info.
see the example below.
to initiate time info mode, after you have received the callbacks pointer in
ASIOCreateBuffers, you will call the asioMessage callback with kAsioSupportsTimeInfo
as the argument. if this returns 1, host has accepted time info mode.
now host expects the new callback bufferSwitchTimeInfo to be used instead
of the old bufferSwitch method. the ASIOTime structure is assumed to be valid
and accessible until the callback returns.
using time code:
if the device supports reading time code, it will call host's asioMessage callback
with kAsioSupportsTimeCode as the selector. it may then fill the according
fields and set the kTcValid flag.
host will call the future method with the kAsioEnableTimeCodeRead selector when
it wants to enable or disable tc reading by the device. you should also support
the kAsioCanTimeInfo and kAsioCanTimeCode selectors in ASIOFuture (see example).
note:
the AsioTimeInfo/ASIOTimeCode pair is supposed to work in both directions.
as a matter of convention, the relationship between the sample
position counter and the time code at buffer switch time is
(ignoring offset between tc and sample pos when tc is running):
on input: sample 0 -> input buffer sample 0 -> time code 0
on output: sample 0 -> output buffer sample 0 -> time code 0
this means that for 'real' calculations, one has to take into account
the according latencies.
example:
ASIOTime asioTime;
in createBuffers()
{
memset(&asioTime, 0, sizeof(ASIOTime));
AsioTimeInfo* ti = &asioTime.timeInfo;
ti->sampleRate = theSampleRate;
ASIOTimeCode* tc = &asioTime.timeCode;
tc->speed = 1.;
timeInfoMode = false;
canTimeCode = false;
if(callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0) == 1)
{
timeInfoMode = true;
#if kCanTimeCode
if(callbacks->asioMessage(kAsioSupportsTimeCode, 0, 0, 0) == 1)
canTimeCode = true;
#endif
}
}
void switchBuffers(long doubleBufferIndex, bool processNow)
{
if(timeInfoMode)
{
AsioTimeInfo* ti = &asioTime.timeInfo;
ti->flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid;
ti->systemTime = theNanoSeconds;
ti->samplePosition = theSamplePosition;
if(ti->sampleRate != theSampleRate)
ti->flags |= kSampleRateChanged;
ti->sampleRate = theSampleRate;
#if kCanTimeCode
if(canTimeCode && timeCodeEnabled)
{
ASIOTimeCode* tc = &asioTime.timeCode;
tc->timeCodeSamples = tcSamples; // tc in samples
tc->flags = kTcValid | kTcRunning | kTcOnspeed; // if so...
}
ASIOTime* bb = callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse);
#else
callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse);
#endif
}
else
callbacks->bufferSwitch(doubleBufferIndex, ASIOFalse);
}
ASIOError ASIOFuture(long selector, void *params)
{
switch(selector)
{
case kAsioEnableTimeCodeRead:
timeCodeEnabled = true;
return ASE_SUCCESS;
case kAsioDisableTimeCodeRead:
timeCodeEnabled = false;
return ASE_SUCCESS;
case kAsioCanTimeInfo:
return ASE_SUCCESS;
#if kCanTimeCode
case kAsioCanTimeCode:
return ASE_SUCCESS;
#endif
}
return ASE_NotPresent;
};
*/
//- - - - - - - - - - - - - - - - - - - - - - - - -
// application's audio stream handler callbacks
//- - - - - - - - - - - - - - - - - - - - - - - - -
typedef struct ASIOCallbacks
{
void (*bufferSwitch) (long doubleBufferIndex, ASIOBool directProcess);
// bufferSwitch indicates that both input and output are to be processed.
// the current buffer half index (0 for A, 1 for B) determines
// - the output buffer that the host should start to fill. the other buffer
// will be passed to output hardware regardless of whether it got filled
// in time or not.
// - the input buffer that is now filled with incoming data. Note that
// because of the synchronicity of i/o, the input always has at
// least one buffer latency in relation to the output.
// directProcess suggests to the host whether it should immedeately
// start processing (directProcess == ASIOTrue), or whether its process
// should be deferred because the call comes from a very low level
// (for instance, a high level priority interrupt), and direct processing
// would cause timing instabilities for the rest of the system. If in doubt,
// directProcess should be set to ASIOFalse.
// Note: bufferSwitch may be called at interrupt time for highest efficiency.
void (*sampleRateDidChange) (ASIOSampleRate sRate);
// gets called when the AudioStreamIO detects a sample rate change
// If sample rate is unknown, 0 is passed (for instance, clock loss
// when externally synchronized).
long (*asioMessage) (long selector, long value, void* message, double* opt);
// generic callback for various purposes, see selectors below.
// note this is only present if the asio version is 2 or higher
ASIOTime* (*bufferSwitchTimeInfo) (ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess);
// new callback with time info. makes ASIOGetSamplePosition() and various
// calls to ASIOGetSampleRate obsolete,
// and allows for timecode sync etc. to be preferred; will be used if
// the driver calls asioMessage with selector kAsioSupportsTimeInfo.
} ASIOCallbacks;
// asioMessage selectors
enum
{
kAsioSelectorSupported = 1, // selector in <value>, returns 1L if supported,
// 0 otherwise
kAsioEngineVersion, // returns engine (host) asio implementation version,
// 2 or higher
kAsioResetRequest, // request driver reset. if accepted, this
// will close the driver (ASIO_Exit() ) and
// re-open it again (ASIO_Init() etc). some
// drivers need to reconfigure for instance
// when the sample rate changes, or some basic
// changes have been made in ASIO_ControlPanel().
// returns 1L; note the request is merely passed
// to the application, there is no way to determine
// if it gets accepted at this time (but it usually
// will be).
kAsioBufferSizeChange, // not yet supported, will currently always return 0L.
// for now, use kAsioResetRequest instead.
// once implemented, the new buffer size is expected
// in <value>, and on success returns 1L
kAsioResyncRequest, // the driver went out of sync, such that
// the timestamp is no longer valid. this
// is a request to re-start the engine and
// slave devices (sequencer). returns 1 for ok,
// 0 if not supported.
kAsioLatenciesChanged, // the drivers latencies have changed. The engine
// will refetch the latencies.
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
kAsioNumMessageSelectors
};
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
//- - - - - - - - - - - - - - - - - - - - - - - - -
// (De-)Construction
//- - - - - - - - - - - - - - - - - - - - - - - - -
typedef struct ASIODriverInfo
{
long asioVersion; // currently, 2
long driverVersion; // driver specific
char name[32];
char errorMessage[124];
void *sysRef; // on input: system reference
// (Windows: application main window handle, Mac & SGI: 0)
} ASIODriverInfo;
ASIOError ASIOInit(ASIODriverInfo *info);
/* Purpose:
Initialize the AudioStreamIO.
Parameter:
info: pointer to an ASIODriver structure:
- asioVersion:
- on input, the host version. *** Note *** this is 0 for earlier asio
implementations, and the asioMessage callback is implemeted
only if asioVersion is 2 or greater. sorry but due to a design fault
the driver doesn't have access to the host version in ASIOInit :-(
added selector for host (engine) version in the asioMessage callback
so we're ok from now on.
- on return, asio implementation version.
older versions are 1
if you support this version (namely, ASIO_outputReady() )
this should be 2 or higher. also see the note in
ASIO_getTimeStamp() !
- version: on return, the driver version (format is driver specific)
- name: on return, a null-terminated string containing the driver's name
- error message: on return, should contain a user message describing
the type of error that occured during ASIOInit(), if any.
- sysRef: platform specific
Returns:
If neither input nor output is present ASE_NotPresent
will be returned.
ASE_NoMemory, ASE_HWMalfunction are other possible error conditions
*/
ASIOError ASIOExit(void);
/* Purpose:
Terminates the AudioStreamIO.
Parameter:
None.
Returns:
If neither input nor output is present ASE_NotPresent
will be returned.
Notes: this implies ASIOStop() and ASIODisposeBuffers(),
meaning that no host callbacks must be accessed after ASIOExit().
*/
//- - - - - - - - - - - - - - - - - - - - - - - - -
// Start/Stop
//- - - - - - - - - - - - - - - - - - - - - - - - -
ASIOError ASIOStart(void);
/* Purpose:
Start input and output processing synchronously.
This will
- reset the sample counter to zero
- start the hardware (both input and output)
The first call to the hosts' bufferSwitch(index == 0) then tells
the host to read from input buffer A (index 0), and start
processing to output buffer A while output buffer B (which
has been filled by the host prior to calling ASIOStart())
is possibly sounding (see also ASIOGetLatencies())
Parameter:
None.
Returns:
If neither input nor output is present, ASE_NotPresent
will be returned.
If the hardware fails to start, ASE_HWMalfunction will be returned.
Notes:
There is no restriction on the time that ASIOStart() takes
to perform (that is, it is not considered a realtime trigger).
*/
ASIOError ASIOStop(void);
/* Purpose:
Stops input and output processing altogether.
Parameter:
None.
Returns:
If neither input nor output is present ASE_NotPresent
will be returned.
Notes:
On return from ASIOStop(), the driver must in no
case call the hosts' bufferSwitch() routine.
*/
//- - - - - - - - - - - - - - - - - - - - - - - - -
// Inquiry methods and sample rate
//- - - - - - - - - - - - - - - - - - - - - - - - -
ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels);
/* Purpose:
Returns number of individual input/output channels.
Parameter:
numInputChannels will hold the number of available input channels
numOutputChannels will hold the number of available output channels
Returns:
If no input/output is present ASE_NotPresent will be returned.
If only inputs, or only outputs are available, the according
other parameter will be zero, and ASE_OK is returned.
*/
ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency);
/* Purpose:
Returns the input and output latencies. This includes
device specific delays, like FIFOs etc.
Parameter:
inputLatency will hold the 'age' of the first sample frame
in the input buffer when the hosts reads it in bufferSwitch()
(this is theoretical, meaning it does not include the overhead
and delay between the actual physical switch, and the time
when bufferSitch() enters).
This will usually be the size of one block in sample frames, plus
device specific latencies.
outputLatency will specify the time between the buffer switch,
and the time when the next play buffer will start to sound.
The next play buffer is defined as the one the host starts
processing after (or at) bufferSwitch(), indicated by the
index parameter (0 for buffer A, 1 for buffer B).
It will usually be either one block, if the host writes directly
to a dma buffer, or two or more blocks if the buffer is 'latched' by
the driver. As an example, on ASIOStart(), the host will have filled
the play buffer at index 1 already; when it gets the callback (with
the parameter index == 0), this tells it to read from the input
buffer 0, and start to fill the play buffer 0 (assuming that now
play buffer 1 is already sounding). In this case, the output
latency is one block. If the driver decides to copy buffer 1
at that time, and pass it to the hardware at the next slot (which
is most commonly done, but should be avoided), the output latency
becomes two blocks instead, resulting in a total i/o latency of at least
3 blocks. As memory access is the main bottleneck in native dsp processing,
and to acheive less latency, it is highly recommended to try to avoid
copying (this is also why the driver is the owner of the buffers). To
summarize, the minimum i/o latency can be acheived if the input buffer
is processed by the host into the output buffer which will physically
start to sound on the next time slice. Also note that the host expects
the bufferSwitch() callback to be accessed for each time slice in order
to retain sync, possibly recursively; if it fails to process a block in
time, it will suspend its operation for some time in order to recover.
Returns:
If no input/output is present ASE_NotPresent will be returned.
*/
ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);
/* Purpose:
Returns min, max, and preferred buffer sizes for input/output
Parameter:
minSize will hold the minimum buffer size
maxSize will hold the maxium possible buffer size
preferredSize will hold the preferred buffer size (a size which
best fits performance and hardware requirements)
granularity will hold the granularity at which buffer sizes
may differ. Usually, the buffer size will be a power of 2;
in this case, granularity will hold -1 on return, signalling
possible buffer sizes starting from minSize, increased in
powers of 2 up to maxSize.
Returns:
If no input/output is present ASE_NotPresent will be returned.
Notes:
When minimum and maximum buffer size are equal,
the preferred buffer size has to be the same value as well; granularity
should be 0 in this case.
*/
ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate);
/* Purpose:
Inquires the hardware for the available sample rates.
Parameter:
sampleRate is the rate in question.
Returns:
If the inquired sample rate is not supported, ASE_NoClock will be returned.
If no input/output is present ASE_NotPresent will be returned.
*/
ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate);
/* Purpose:
Get the current sample Rate.
Parameter:
currentRate will hold the current sample rate on return.
Returns:
If sample rate is unknown, sampleRate will be 0 and ASE_NoClock will be returned.
If no input/output is present ASE_NotPresent will be returned.
Notes:
*/
ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate);
/* Purpose:
Set the hardware to the requested sample Rate. If sampleRate == 0,
enable external sync.
Parameter:
sampleRate: on input, the requested rate
Returns:
If sampleRate is unknown ASE_NoClock will be returned.
If the current clock is external, and sampleRate is != 0,
ASE_InvalidMode will be returned
If no input/output is present ASE_NotPresent will be returned.
Notes:
*/
typedef struct ASIOClockSource
{
long index; // as used for ASIOSetClockSource()
long associatedChannel; // for instance, S/PDIF or AES/EBU
long associatedGroup; // see channel groups (ASIOGetChannelInfo())
ASIOBool isCurrentSource; // ASIOTrue if this is the current clock source
char name[32]; // for user selection
} ASIOClockSource;
ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources);
/* Purpose:
Get the available external audio clock sources
Parameter:
clocks points to an array of ASIOClockSource structures:
- index: this is used to identify the clock source
when ASIOSetClockSource() is accessed, should be
an index counting from zero
- associatedInputChannel: the first channel of an associated
input group, if any.
- associatedGroup: the group index of that channel.
groups of channels are defined to seperate for
instance analog, S/PDIF, AES/EBU, ADAT connectors etc,
when present simultaniously. Note that associated channel
is enumerated according to numInputs/numOutputs, means it
is independant from a group (see also ASIOGetChannelInfo())
inputs are associated to a clock if the physical connection
transfers both data and clock (like S/PDIF, AES/EBU, or
ADAT inputs). if there is no input channel associated with
the clock source (like Word Clock, or internal oscillator), both
associatedChannel and associatedGroup should be set to -1.
- isCurrentSource: on exit, ASIOTrue if this is the current clock
source, ASIOFalse else
- name: a null-terminated string for user selection of the available sources.
numSources:
on input: the number of allocated array members
on output: the number of available clock sources, at least
1 (internal clock generator).
Returns:
If no input/output is present ASE_NotPresent will be returned.
Notes:
*/
ASIOError ASIOSetClockSource(long index);
/* Purpose:
Set the audio clock source
Parameter:
index as obtained from an inquiry to ASIOGetClockSources()
Returns:
If no input/output is present ASE_NotPresent will be returned.
If the clock can not be selected because an input channel which
carries the current clock source is active, ASE_InvalidMode
*may* be returned (this depends on the properties of the driver
and/or hardware).
Notes:
Should *not* return ASE_NoClock if there is no clock signal present
at the selected source; this will be inquired via ASIOGetSampleRate().
It should call the host callback procedure sampleRateHasChanged(),
if the switch causes a sample rate change, or if no external clock
is present at the selected source.
*/
ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp);
/* Purpose:
Inquires the sample position/time stamp pair.
Parameter:
sPos will hold the sample position on return. The sample
position is reset to zero when ASIOStart() gets called.
tStamp will hold the system time when the sample position
was latched.
Returns:
If no input/output is present, ASE_NotPresent will be returned.
If there is no clock, ASE_SPNotAdvancing will be returned.
Notes:
in order to be able to synchronise properly,
the sample position / time stamp pair must refer to the current block,
that is, the engine will call ASIOGetSamplePosition() in its bufferSwitch()
callback and expect the time for the current block. thus, when requested
in the very first bufferSwitch after ASIO_Start(), the sample position
should be zero, and the time stamp should refer to the very time where
the stream was started. it also means that the sample position must be
block aligned. the driver must ensure proper interpolation if the system
time can not be determined for the block position. the driver is responsible
for precise time stamps as it usually has most direct access to lower
level resources. proper behaviour of ASIO_GetSamplePosition() and ASIO_GetLatencies()
are essential for precise media synchronization!
*/
typedef struct ASIOChannelInfo
{
long channel; // on input, channel index
ASIOBool isInput; // on input
ASIOBool isActive; // on exit
long channelGroup; // dto
ASIOSampleType type; // dto
char name[32]; // dto
} ASIOChannelInfo;
ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info);
/* Purpose:
retreive information about the nature of a channel
Parameter:
info: pointer to a ASIOChannelInfo structure with
- channel: on input, the channel index of the channel in question.
- isInput: on input, ASIOTrue if info for an input channel is
requested, else output
- channelGroup: on return, the channel group that the channel
belongs to. For drivers which support different types of
channels, like analog, S/PDIF, AES/EBU, ADAT etc interfaces,
there should be a reasonable grouping of these types. Groups
are always independant form a channel index, that is, a channel
index always counts from 0 to numInputs/numOutputs regardless
of the group it may belong to.
There will always be at least one group (group 0). Please
also note that by default, the host may decide to activate
channels 0 and 1; thus, these should belong to the most
useful type (analog i/o, if present).
- type: on return, contains the sample type of the channel
- isActive: on return, ASIOTrue if channel is active as it was
installed by ASIOCreateBuffers(), ASIOFalse else
- name: describing the type of channel in question. Used to allow
for user selection, and enabling of specific channels. examples:
"Analog In", "SPDIF Out" etc
Returns:
If no input/output is present ASE_NotPresent will be returned.
Notes:
If possible, the string should be organised such that the first
characters are most significantly describing the nature of the
port, to allow for identification even if the view showing the
port name is too small to display more than 8 characters, for
instance.
*/
//- - - - - - - - - - - - - - - - - - - - - - - - -
// Buffer preparation
//- - - - - - - - - - - - - - - - - - - - - - - - -
typedef struct ASIOBufferInfo
{
ASIOBool isInput; // on input: ASIOTrue: input, else output
long channelNum; // on input: channel index
void *buffers[2]; // on output: double buffer addresses
} ASIOBufferInfo;
ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels,
long bufferSize, ASIOCallbacks *callbacks);
/* Purpose:
Allocates input/output buffers for all input and output channels to be activated.
Parameter:
bufferInfos is a pointer to an array of ASIOBufferInfo structures:
- isInput: on input, ASIOTrue if the buffer is to be allocated
for an input, output buffer else
- channelNum: on input, the index of the channel in question
(counting from 0)
- buffers: on exit, 2 pointers to the halves of the channels' double-buffer.
the size of the buffer(s) of course depend on both the ASIOSampleType
as obtained from ASIOGetChannelInfo(), and bufferSize
numChannels is the sum of all input and output channels to be created;
thus bufferInfos is a pointer to an array of numChannels ASIOBufferInfo
structures.
bufferSize selects one of the possible buffer sizes as obtained from
ASIOGetBufferSizes().
callbacks is a pointer to an ASIOCallbacks structure.
Returns:
If not enough memory is available ASE_NoMemory will be returned.
If no input/output is present ASE_NotPresent will be returned.
If bufferSize is not supported, or one or more of the bufferInfos elements
contain invalid settings, ASE_InvalidMode will be returned.
Notes:
If individual channel selection is not possible but requested,
the driver has to handle this. namely, bufferSwitch() will only
have filled buffers of enabled outputs. If possible, processing
and buss activities overhead should be avoided for channels which
were not enabled here.
*/
ASIOError ASIODisposeBuffers(void);
/* Purpose:
Releases all buffers for the device.
Parameter:
None.
Returns:
If no buffer were ever prepared, ASE_InvalidMode will be returned.
If no input/output is present ASE_NotPresent will be returned.
Notes:
This implies ASIOStop().
*/
ASIOError ASIOControlPanel(void);
/* Purpose:
request the driver to start a control panel component
for device specific user settings. This will not be
accessed on some platforms (where the component is accessed
instead).
Parameter:
None.
Returns:
If no panel is available ASE_NotPresent will be returned.
Actually, the return code is ignored.
Notes:
if the user applied settings which require a re-configuration
of parts or all of the enigine and/or driver (such as a change of
the block size), the asioMessage callback can be used (see
ASIO_Callbacks).
*/
ASIOError ASIOFuture(long selector, void *params);
/* Purpose:
various
Parameter:
selector: operation Code as to be defined. zero is reserved for
testing purposes.
params: depends on the selector; usually pointer to a structure
for passing and retreiving any type and amount of parameters.
Returns:
the return value is also selector dependant. if the selector
is unknown, ASE_InvalidParameter should be returned to prevent
further calls with this selector. on success, ASE_SUCCESS
must be returned (note: ASE_OK is *not* sufficient!)
Notes:
see selectors defined below.
*/
enum
{
kAsioEnableTimeCodeRead = 1, // no arguments
kAsioDisableTimeCodeRead, // no arguments
kAsioSetInputMonitor, // ASIOInputMonitor* in params
kAsioTransport, // ASIOTransportParameters* in params
kAsioSetInputGain, // ASIOChannelControls* in params, apply gain
kAsioGetInputMeter, // ASIOChannelControls* in params, fill meter
kAsioSetOutputGain, // ASIOChannelControls* in params, apply gain
kAsioGetOutputMeter, // ASIOChannelControls* in params, fill meter
kAsioCanInputMonitor, // no arguments for kAsioCanXXX selectors
kAsioCanTimeInfo,
kAsioCanTimeCode,
kAsioCanTransport,
kAsioCanInputGain,
kAsioCanInputMeter,
kAsioCanOutputGain,
kAsioCanOutputMeter
};
typedef struct ASIOInputMonitor
{
long input; // this input was set to monitor (or off), -1: all
long output; // suggested output for monitoring the input (if so)
long gain; // suggested gain, ranging 0 - 0x7fffffffL (-inf to +12 dB)
ASIOBool state; // ASIOTrue => on, ASIOFalse => off
long pan; // suggested pan, 0 => all left, 0x7fffffff => right
} ASIOInputMonitor;
typedef struct ASIOChannelControls
{
long channel; // on input, channel index
ASIOBool isInput; // on input
long gain; // on input, ranges 0 thru 0x7fffffff
long meter; // on return, ranges 0 thru 0x7fffffff
char future[32];
} ASIOChannelControls;
typedef struct ASIOTransportParameters
{
long command; // see enum below
ASIOSamples samplePosition;
long track;
long trackSwitches[16]; // 512 tracks on/off
char future[64];
} ASIOTransportParameters;
enum
{
kTransStart = 1,
kTransStop,
kTransLocate, // to samplePosition
kTransPunchIn,
kTransPunchOut,
kTransArmOn, // track
kTransArmOff, // track
kTransMonitorOn, // track
kTransMonitorOff, // track
kTransArm, // trackSwitches
kTransMonitor // trackSwitches
};
ASIOError ASIOOutputReady(void);
/* Purpose:
this tells the driver that the host has completed processing
the output buffers. if the data format required by the hardware
differs from the supported asio formats, but the hardware
buffers are DMA buffers, the driver will have to convert
the audio stream data; as the bufferSwitch callback is
usually issued at dma block switch time, the driver will
have to convert the *previous* host buffer, which increases
the output latency by one block.
when the host finds out that ASIOOutputReady() returns
true, it will issue this call whenever it completed
output processing. then the driver can convert the
host data directly to the dma buffer to be played next,
reducing output latency by one block.
another way to look at it is, that the buffer switch is called
in order to pass the *input* stream to the host, so that it can
process the input into the output, and the output stream is passed
to the driver when the host has completed its process.
Parameter:
None
Returns:
only if the above mentioned scenario is given, and a reduction
of output latency can be acheived by this mechanism, should
ASE_OK be returned. otherwise (and usually), ASE_NotPresent
should be returned in order to prevent further calls to this
function. note that the host may want to determine if it is
to use this when the system is not yet fully initialized, so
ASE_OK should always be returned if the mechanism makes sense.
Notes:
please remeber to adjust ASIOGetLatencies() according to
whether ASIOOutputReady() was ever called or not, if your
driver supports this scenario.
also note that the engine may fail to call ASIO_OutputReady()
in time in overload cases. as already mentioned, bufferSwitch
should be called for every block regardless of whether a block
could be processed in time.
*/
// restore old alignment
#if defined(_MSC_VER) && !defined(__MWERKS__)
#pragma pack(pop)
#elif PRAGMA_ALIGN_SUPPORTED
#pragma options align = reset
#endif
#endif

186
src/asio/asiodrivers.cpp Normal file
View File

@@ -0,0 +1,186 @@
#include <string.h>
#include "asiodrivers.h"
AsioDrivers* asioDrivers = 0;
bool loadAsioDriver(char *name);
bool loadAsioDriver(char *name)
{
if(!asioDrivers)
asioDrivers = new AsioDrivers();
if(asioDrivers)
return asioDrivers->loadDriver(name);
return false;
}
//------------------------------------------------------------------------------------
#if MAC
bool resolveASIO(unsigned long aconnID);
AsioDrivers::AsioDrivers() : CodeFragments("ASIO Drivers", 'AsDr', 'Asio')
{
connID = -1;
curIndex = -1;
}
AsioDrivers::~AsioDrivers()
{
removeCurrentDriver();
}
bool AsioDrivers::getCurrentDriverName(char *name)
{
if(curIndex >= 0)
return getName(curIndex, name);
return false;
}
long AsioDrivers::getDriverNames(char **names, long maxDrivers)
{
for(long i = 0; i < getNumFragments() && i < maxDrivers; i++)
getName(i, names[i]);
return getNumFragments() < maxDrivers ? getNumFragments() : maxDrivers;
}
bool AsioDrivers::loadDriver(char *name)
{
char dname[64];
unsigned long newID;
for(long i = 0; i < getNumFragments(); i++)
{
if(getName(i, dname) && !strcmp(name, dname))
{
if(newInstance(i, &newID))
{
if(resolveASIO(newID))
{
if(connID != -1)
removeInstance(curIndex, connID);
curIndex = i;
connID = newID;
return true;
}
}
break;
}
}
return false;
}
void AsioDrivers::removeCurrentDriver()
{
if(connID != -1)
removeInstance(curIndex, connID);
connID = -1;
curIndex = -1;
}
//------------------------------------------------------------------------------------
#elif WINDOWS
#include "iasiodrv.h"
extern IASIO* theAsioDriver;
AsioDrivers::AsioDrivers() : AsioDriverList()
{
curIndex = -1;
}
AsioDrivers::~AsioDrivers()
{
}
bool AsioDrivers::getCurrentDriverName(char *name)
{
if(curIndex >= 0)
return asioGetDriverName(curIndex, name, 32) == 0 ? true : false;
name[0] = 0;
return false;
}
long AsioDrivers::getDriverNames(char **names, long maxDrivers)
{
for(long i = 0; i < asioGetNumDev() && i < maxDrivers; i++)
asioGetDriverName(i, names[i], 32);
return asioGetNumDev() < maxDrivers ? asioGetNumDev() : maxDrivers;
}
bool AsioDrivers::loadDriver(char *name)
{
char dname[64];
char curName[64];
for(long i = 0; i < asioGetNumDev(); i++)
{
if(!asioGetDriverName(i, dname, 32) && !strcmp(name, dname))
{
curName[0] = 0;
getCurrentDriverName(curName); // in case we fail...
removeCurrentDriver();
if(!asioOpenDriver(i, (void **)&theAsioDriver))
{
curIndex = i;
return true;
}
else
{
theAsioDriver = 0;
if(curName[0] && strcmp(dname, curName))
loadDriver(curName); // try restore
}
break;
}
}
return false;
}
void AsioDrivers::removeCurrentDriver()
{
if(curIndex != -1)
asioCloseDriver(curIndex);
curIndex = -1;
}
#elif SGI || BEOS
#include "asiolist.h"
AsioDrivers::AsioDrivers()
: AsioDriverList()
{
curIndex = -1;
}
AsioDrivers::~AsioDrivers()
{
}
bool AsioDrivers::getCurrentDriverName(char *name)
{
return false;
}
long AsioDrivers::getDriverNames(char **names, long maxDrivers)
{
return 0;
}
bool AsioDrivers::loadDriver(char *name)
{
return false;
}
void AsioDrivers::removeCurrentDriver()
{
}
#else
#error implement me
#endif

41
src/asio/asiodrivers.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef __AsioDrivers__
#define __AsioDrivers__
#include "ginclude.h"
#if MAC
#include "CodeFragments.hpp"
class AsioDrivers : public CodeFragments
#elif WINDOWS
#include <windows.h>
#include "asiolist.h"
class AsioDrivers : public AsioDriverList
#elif SGI || BEOS
#include "asiolist.h"
class AsioDrivers : public AsioDriverList
#else
#error implement me
#endif
{
public:
AsioDrivers();
~AsioDrivers();
bool getCurrentDriverName(char *name);
long getDriverNames(char **names, long maxDrivers);
bool loadDriver(char *name);
void removeCurrentDriver();
long getCurrentDriverIndex() {return curIndex;}
protected:
unsigned long connID;
long curIndex;
};
#endif

76
src/asio/asiodrvr.h Normal file
View File

@@ -0,0 +1,76 @@
/*
Steinberg Audio Stream I/O API
(c) 1996, Steinberg Soft- und Hardware GmbH
charlie (May 1996)
asiodrvr.h
c++ superclass to implement asio functionality. from this,
you can derive whatever required
*/
#ifndef _asiodrvr_
#define _asiodrvr_
// cpu and os system we are running on
#include "asiosys.h"
// basic "C" interface
#include "asio.h"
class AsioDriver;
extern AsioDriver *getDriver(); // for generic constructor
#if WINDOWS
#include <windows.h>
#include "combase.h"
#include "iasiodrv.h"
class AsioDriver : public IASIO ,public CUnknown
{
public:
AsioDriver(LPUNKNOWN pUnk, HRESULT *phr);
DECLARE_IUNKNOWN
// Factory method
static CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
// IUnknown
virtual HRESULT STDMETHODCALLTYPE NonDelegatingQueryInterface(REFIID riid,void **ppvObject);
#else
class AsioDriver
{
public:
AsioDriver();
#endif
virtual ~AsioDriver();
virtual ASIOBool init(void* sysRef);
virtual void getDriverName(char *name); // max 32 bytes incl. terminating zero
virtual long getDriverVersion();
virtual void getErrorMessage(char *string); // max 124 bytes incl.
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();
};
#endif

268
src/asio/asiolist.cpp Normal file
View File

@@ -0,0 +1,268 @@
#include <windows.h>
#include "iasiodrv.h"
#include "asiolist.h"
#define ASIODRV_DESC "description"
#define INPROC_SERVER "InprocServer32"
#define ASIO_PATH "software\\asio"
#define COM_CLSID "clsid"
// ******************************************************************
// Local Functions
// ******************************************************************
static LONG findDrvPath (char *clsidstr,char *dllpath,int dllpathsize)
{
HKEY hkEnum,hksub,hkpath;
char databuf[512];
LONG cr,rc = -1;
DWORD datatype,datasize;
DWORD index;
OFSTRUCT ofs;
HFILE hfile;
BOOL found = FALSE;
CharLowerBuff(clsidstr,strlen(clsidstr));
if ((cr = RegOpenKey(HKEY_CLASSES_ROOT,COM_CLSID,&hkEnum)) == ERROR_SUCCESS) {
index = 0;
while (cr == ERROR_SUCCESS && !found) {
cr = RegEnumKey(hkEnum,index++,(LPTSTR)databuf,512);
if (cr == ERROR_SUCCESS) {
CharLowerBuff(databuf,strlen(databuf));
if (!(strcmp(databuf,clsidstr))) {
if ((cr = RegOpenKeyEx(hkEnum,(LPCTSTR)databuf,0,KEY_READ,&hksub)) == ERROR_SUCCESS) {
if ((cr = RegOpenKeyEx(hksub,(LPCTSTR)INPROC_SERVER,0,KEY_READ,&hkpath)) == ERROR_SUCCESS) {
datatype = REG_SZ; datasize = (DWORD)dllpathsize;
cr = RegQueryValueEx(hkpath,0,0,&datatype,(LPBYTE)dllpath,&datasize);
if (cr == ERROR_SUCCESS) {
memset(&ofs,0,sizeof(OFSTRUCT));
ofs.cBytes = sizeof(OFSTRUCT);
hfile = OpenFile(dllpath,&ofs,OF_EXIST);
if (hfile) rc = 0;
}
RegCloseKey(hkpath);
}
RegCloseKey(hksub);
}
found = TRUE; // break out
}
}
}
RegCloseKey(hkEnum);
}
return rc;
}
static LPASIODRVSTRUCT newDrvStruct (HKEY hkey,char *keyname,int drvID,LPASIODRVSTRUCT lpdrv)
{
HKEY hksub;
char databuf[256];
char dllpath[MAXPATHLEN];
WORD wData[100];
CLSID clsid;
DWORD datatype,datasize;
LONG cr,rc;
if (!lpdrv) {
if ((cr = RegOpenKeyEx(hkey,(LPCTSTR)keyname,0,KEY_READ,&hksub)) == ERROR_SUCCESS) {
datatype = REG_SZ; datasize = 256;
cr = RegQueryValueEx(hksub,COM_CLSID,0,&datatype,(LPBYTE)databuf,&datasize);
if (cr == ERROR_SUCCESS) {
rc = findDrvPath (databuf,dllpath,MAXPATHLEN);
if (rc == 0) {
lpdrv = new ASIODRVSTRUCT[1];
if (lpdrv) {
memset(lpdrv,0,sizeof(ASIODRVSTRUCT));
lpdrv->drvID = drvID;
MultiByteToWideChar(CP_ACP,0,(LPCSTR)databuf,-1,(LPWSTR)wData,100);
if ((cr = CLSIDFromString((LPOLESTR)wData,(LPCLSID)&clsid)) == S_OK) {
memcpy(&lpdrv->clsid,&clsid,sizeof(CLSID));
}
datatype = REG_SZ; datasize = 256;
cr = RegQueryValueEx(hksub,ASIODRV_DESC,0,&datatype,(LPBYTE)databuf,&datasize);
if (cr == ERROR_SUCCESS) {
strcpy(lpdrv->drvname,databuf);
}
else strcpy(lpdrv->drvname,keyname);
}
}
}
RegCloseKey(hksub);
}
}
else lpdrv->next = newDrvStruct(hkey,keyname,drvID+1,lpdrv->next);
return lpdrv;
}
static void deleteDrvStruct (LPASIODRVSTRUCT lpdrv)
{
IASIO *iasio;
if (lpdrv != 0) {
deleteDrvStruct(lpdrv->next);
if (lpdrv->asiodrv) {
iasio = (IASIO *)lpdrv->asiodrv;
iasio->Release();
}
delete lpdrv;
}
}
static LPASIODRVSTRUCT getDrvStruct (int drvID,LPASIODRVSTRUCT lpdrv)
{
while (lpdrv) {
if (lpdrv->drvID == drvID) return lpdrv;
lpdrv = lpdrv->next;
}
return 0;
}
// ******************************************************************
// ******************************************************************
// AsioDriverList
// ******************************************************************
AsioDriverList::AsioDriverList ()
{
HKEY hkEnum = 0;
char keyname[MAXDRVNAMELEN];
LPASIODRVSTRUCT pdl;
LONG cr;
DWORD index = 0;
BOOL fin = FALSE;
numdrv = 0;
lpdrvlist = 0;
cr = RegOpenKey(HKEY_LOCAL_MACHINE,ASIO_PATH,&hkEnum);
while (cr == ERROR_SUCCESS) {
if ((cr = RegEnumKey(hkEnum,index++,(LPTSTR)keyname,MAXDRVNAMELEN))== ERROR_SUCCESS) {
lpdrvlist = newDrvStruct (hkEnum,keyname,0,lpdrvlist);
}
else fin = TRUE;
}
if (hkEnum) RegCloseKey(hkEnum);
pdl = lpdrvlist;
while (pdl) {
numdrv++;
pdl = pdl->next;
}
if (numdrv) CoInitialize(0); // initialize COM
}
AsioDriverList::~AsioDriverList ()
{
if (numdrv) {
deleteDrvStruct(lpdrvlist);
CoUninitialize();
}
}
LONG AsioDriverList::asioGetNumDev (VOID)
{
return (LONG)numdrv;
}
LONG AsioDriverList::asioOpenDriver (int drvID,LPVOID *asiodrv)
{
LPASIODRVSTRUCT lpdrv = 0;
long rc;
if (!asiodrv) return DRVERR_INVALID_PARAM;
if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
if (!lpdrv->asiodrv) {
rc = CoCreateInstance(lpdrv->clsid,0,CLSCTX_INPROC_SERVER,lpdrv->clsid,asiodrv);
if (rc == S_OK) {
lpdrv->asiodrv = *asiodrv;
return 0;
}
// else if (rc == REGDB_E_CLASSNOTREG)
// strcpy (info->messageText, "Driver not registered in the Registration Database!");
}
else rc = DRVERR_DEVICE_ALREADY_OPEN;
}
else rc = DRVERR_DEVICE_NOT_FOUND;
return rc;
}
LONG AsioDriverList::asioCloseDriver (int drvID)
{
LPASIODRVSTRUCT lpdrv = 0;
IASIO *iasio;
if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
if (lpdrv->asiodrv) {
iasio = (IASIO *)lpdrv->asiodrv;
iasio->Release();
lpdrv->asiodrv = 0;
}
}
return 0;
}
LONG AsioDriverList::asioGetDriverName (int drvID,char *drvname,int drvnamesize)
{
LPASIODRVSTRUCT lpdrv = 0;
if (!drvname) return DRVERR_INVALID_PARAM;
if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
if (strlen(lpdrv->drvname) < (unsigned int)drvnamesize) {
strcpy(drvname,lpdrv->drvname);
}
else {
memcpy(drvname,lpdrv->drvname,drvnamesize-4);
drvname[drvnamesize-4] = '.';
drvname[drvnamesize-3] = '.';
drvname[drvnamesize-2] = '.';
drvname[drvnamesize-1] = 0;
}
return 0;
}
return DRVERR_DEVICE_NOT_FOUND;
}
LONG AsioDriverList::asioGetDriverPath (int drvID,char *dllpath,int dllpathsize)
{
LPASIODRVSTRUCT lpdrv = 0;
if (!dllpath) return DRVERR_INVALID_PARAM;
if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
if (strlen(lpdrv->dllpath) < (unsigned int)dllpathsize) {
strcpy(dllpath,lpdrv->dllpath);
return 0;
}
dllpath[0] = 0;
return DRVERR_INVALID_PARAM;
}
return DRVERR_DEVICE_NOT_FOUND;
}
LONG AsioDriverList::asioGetDriverCLSID (int drvID,CLSID *clsid)
{
LPASIODRVSTRUCT lpdrv = 0;
if (!clsid) return DRVERR_INVALID_PARAM;
if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
memcpy(clsid,&lpdrv->clsid,sizeof(CLSID));
return 0;
}
return DRVERR_DEVICE_NOT_FOUND;
}

46
src/asio/asiolist.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef __asiolist__
#define __asiolist__
#define DRVERR -5000
#define DRVERR_INVALID_PARAM DRVERR-1
#define DRVERR_DEVICE_ALREADY_OPEN DRVERR-2
#define DRVERR_DEVICE_NOT_FOUND DRVERR-3
#define MAXPATHLEN 512
#define MAXDRVNAMELEN 128
struct asiodrvstruct
{
int drvID;
CLSID clsid;
char dllpath[MAXPATHLEN];
char drvname[MAXDRVNAMELEN];
LPVOID asiodrv;
struct asiodrvstruct *next;
};
typedef struct asiodrvstruct ASIODRVSTRUCT;
typedef ASIODRVSTRUCT *LPASIODRVSTRUCT;
class AsioDriverList {
public:
AsioDriverList();
~AsioDriverList();
LONG asioOpenDriver (int,VOID **);
LONG asioCloseDriver (int);
// nice to have
LONG asioGetNumDev (VOID);
LONG asioGetDriverName (int,char *,int);
LONG asioGetDriverPath (int,char *,int);
LONG asioGetDriverCLSID (int,CLSID *);
// or use directly access
LPASIODRVSTRUCT lpdrvlist;
int numdrv;
};
typedef class AsioDriverList *LPASIODRIVERLIST;
#endif

82
src/asio/asiosys.h Normal file
View File

@@ -0,0 +1,82 @@
#ifndef __asiosys__
#define __asiosys__
#ifdef WIN32
#undef MAC
#define PPC 0
#define WINDOWS 1
#define SGI 0
#define SUN 0
#define LINUX 0
#define BEOS 0
#define NATIVE_INT64 0
#define IEEE754_64FLOAT 1
#elif BEOS
#define MAC 0
#define PPC 0
#define WINDOWS 0
#define PC 0
#define SGI 0
#define SUN 0
#define LINUX 0
#define NATIVE_INT64 0
#define IEEE754_64FLOAT 1
#ifndef DEBUG
#define DEBUG 0
#if DEBUG
void DEBUGGERMESSAGE(char *string);
#else
#define DEBUGGERMESSAGE(a)
#endif
#endif
#elif SGI
#define MAC 0
#define PPC 0
#define WINDOWS 0
#define PC 0
#define SUN 0
#define LINUX 0
#define BEOS 0
#define NATIVE_INT64 0
#define IEEE754_64FLOAT 1
#ifndef DEBUG
#define DEBUG 0
#if DEBUG
void DEBUGGERMESSAGE(char *string);
#else
#define DEBUGGERMESSAGE(a)
#endif
#endif
#else // MAC
#define MAC 1
#define PPC 1
#define WINDOWS 0
#define PC 0
#define SGI 0
#define SUN 0
#define LINUX 0
#define BEOS 0
#define NATIVE_INT64 0
#define IEEE754_64FLOAT 1
#ifndef DEBUG
#define DEBUG 0
#if DEBUG
void DEBUGGERMESSAGE(char *string);
#else
#define DEBUGGERMESSAGE(a)
#endif
#endif
#endif
#endif

38
src/asio/ginclude.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef __gInclude__
#define __gInclude__
#if SGI
#undef BEOS
#undef MAC
#undef WINDOWS
//
#define ASIO_BIG_ENDIAN 1
#define ASIO_CPU_MIPS 1
#elif defined WIN32
#undef BEOS
#undef MAC
#undef SGI
#define WINDOWS 1
#define ASIO_LITTLE_ENDIAN 1
#define ASIO_CPU_X86 1
#elif BEOS
#undef MAC
#undef SGI
#undef WINDOWS
#define ASIO_LITTLE_ENDIAN 1
#define ASIO_CPU_X86 1
//
#else
#define MAC 1
#undef BEOS
#undef WINDOWS
#undef SGI
#define ASIO_BIG_ENDIAN 1
#define ASIO_CPU_PPC 1
#endif
// always
#define NATIVE_INT64 0
#define IEEE754_64FLOAT 1
#endif // __gInclude__

37
src/asio/iasiodrv.h Normal file
View File

@@ -0,0 +1,37 @@
#include "asiosys.h"
#include "asio.h"
/* Forward Declarations */
#ifndef __ASIODRIVER_FWD_DEFINED__
#define __ASIODRIVER_FWD_DEFINED__
typedef interface IASIO IASIO;
#endif /* __ASIODRIVER_FWD_DEFINED__ */
interface IASIO : public IUnknown
{
virtual ASIOBool init(void *sysHandle) = 0;
virtual void getDriverName(char *name) = 0;
virtual long getDriverVersion() = 0;
virtual void getErrorMessage(char *string) = 0;
virtual ASIOError start() = 0;
virtual ASIOError stop() = 0;
virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0;
virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0;
virtual ASIOError getBufferSize(long *minSize, long *maxSize,
long *preferredSize, long *granularity) = 0;
virtual ASIOError canSampleRate(ASIOSampleRate sampleRate) = 0;
virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate) = 0;
virtual ASIOError setSampleRate(ASIOSampleRate sampleRate) = 0;
virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0;
virtual ASIOError setClockSource(long reference) = 0;
virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;
virtual ASIOError getChannelInfo(ASIOChannelInfo *info) = 0;
virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels,
long bufferSize, ASIOCallbacks *callbacks) = 0;
virtual ASIOError disposeBuffers() = 0;
virtual ASIOError controlPanel() = 0;
virtual ASIOError future(long selector,void *opt) = 0;
virtual ASIOError outputReady() = 0;
};