Version 3.2

This commit is contained in:
Gary Scavone
2013-09-25 14:47:10 +02:00
committed by Stephen Sinclair
parent 4b6500d3de
commit 3f126af4e5
443 changed files with 11772 additions and 8060 deletions

197
src/ADSR.cpp Normal file
View File

@@ -0,0 +1,197 @@
/*******************************************/
/* ADSR Subclass of the Envelope Class, */
/* by Perry R. Cook, 1995-96 */
/* This is the traditional ADSR (Attack */
/* Decay, Sustain, Release) ADSR. */
/* It responds to simple KeyOn and KeyOff */
/* messages, keeping track of it's state. */
/* There are two tick (update value) */
/* methods, one returns the value, and */
/* other returns the state (0 = A, 1 = D, */
/* 2 = S, 3 = R) */
/*******************************************/
#include "ADSR.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 = 0;
}
ADSR :: ~ADSR()
{
/* Nothing to do here */
}
void ADSR :: keyOn()
{
target = (MY_FLOAT) 1.0;
rate = attackRate;
state = 0;
}
void ADSR :: keyOff()
{
target = (MY_FLOAT) 0.0;
rate = releaseRate;
state = 3;
}
void ADSR :: setAttackRate(MY_FLOAT aRate)
{
if (aRate < 0.0) {
printf("negative rates not allowed!!, correcting\n");
attackRate = -aRate;
}
else attackRate = aRate;
}
void ADSR :: setDecayRate(MY_FLOAT aRate)
{
if (aRate < 0.0) {
printf("negative rates not allowed!!, correcting\n");
decayRate = -aRate;
}
else decayRate = aRate;
}
void ADSR :: setSustainLevel(MY_FLOAT aLevel)
{
if (aLevel < 0.0 ) {
printf("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("negative rates not allowed!!, correcting\n");
releaseRate = -aRate;
}
else releaseRate = aRate;
}
void ADSR :: setAttackTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("negative times not allowed!!, correcting\n");
attackRate = ONE_OVER_SRATE / -aTime;
}
else attackRate = ONE_OVER_SRATE / aTime;
}
void ADSR :: setDecayTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("negative times not allowed!!, correcting\n");
decayRate = ONE_OVER_SRATE / -aTime;
}
else decayRate = ONE_OVER_SRATE / aTime;
}
void ADSR :: setReleaseTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("negative times not allowed!!, correcting\n");
releaseRate = ONE_OVER_SRATE / -aTime;
}
else releaseRate = ONE_OVER_SRATE / aTime;
}
void ADSR :: setAllTimes(MY_FLOAT attTime, MY_FLOAT decTime, MY_FLOAT susLevel, MY_FLOAT relTime)
{
this->setAttackTime(attTime);
this->setDecayTime(decTime);
this->setSustainLevel(susLevel);
this->setReleaseTime(relTime);
}
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;
}
MY_FLOAT ADSR :: tick()
{
if (state==ATTACK) {
value += rate;
if (value >= target) {
value = target;
rate = decayRate;
target = sustainLevel;
state = DECAY;
}
}
else if (state==DECAY) {
value -= decayRate;
if (value <= sustainLevel) {
value = sustainLevel;
rate = (MY_FLOAT) 0.0;
state = SUSTAIN;
}
}
else if (state==RELEASE) {
value -= releaseRate;
if (value <= 0.0) {
value = (MY_FLOAT) 0.0;
state = 4;
}
}
return value;
}
int ADSR :: informTick()
{
this->tick();
return state;
}
MY_FLOAT ADSR :: lastOut()
{
return value;
}
/************ Test Main ************************/
/*
void main()
{
long i;
ADSR test;
test.setAttackRate(0.15);
test.keyOn();
while(test.informTick()==ATTACK) printf("%lf\n",test.tick());
test.setDecayRate(0.1);
while (test.informTick()==DECAY) printf("%lf\n",test.lastOut());
test.setReleaseRate(0.05);
test.keyOff();
while(test.informTick()==RELEASE) printf("%lf\n",test.lastOut());
}
*/

209
src/AifWvIn.cpp Normal file
View File

@@ -0,0 +1,209 @@
/*******************************************/
/*
AifWvIn Input Class,
by Gary P. Scavone, 2000
This object inherits from WvIn and is
used to open Audio Interchange File
Format files with 16-bit data (signed
integer) for playback.
.aif files are always bif-endian.
*/
/*******************************************/
#include "AifWvIn.h"
#ifdef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
AifWvIn :: AifWvIn(char *fileName, const char *mode)
{
char msg[256];
// check mode string
if ( strcmp(mode,"oneshot") && strcmp(mode,"looping") ) {
sprintf(msg, "AifWvIn: constructor parameter 'mode' must be oneshot or looping only.\n");
throw StkError(msg, StkError::FUNCTION_SYNTAX);
}
// Open the file and get header info
fd = fopen(fileName,"rb");
if (!fd) {
sprintf(msg, "AifWvIn: Couldn't open or find file (%s).\n", fileName);
throw StkError(msg, StkError::FILE_NOT_FOUND);
}
// Make sure this is an .aif format file
char id[4];
fseek(fd,8,SEEK_SET); // Locate form type
fread(&id,4,1,fd);
if (strncmp(id,"AIFF",4)) {
fclose(fd);
sprintf(msg, "AifWvIn: %s doesn't appear to be an AIFF file.\n", fileName);
throw StkError(msg, StkError::FILE_ERROR);
}
// Find "common" chunk
INT32 chunkSize;
fread(&id,4,1,fd);
while (strncmp(id,"COMM",4)) {
fread(&chunkSize,4,1,fd);
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&chunkSize);
#endif
fseek(fd,chunkSize,SEEK_CUR);
fread(&id,4,1,fd);
}
// Get number of channels from the header
INT16 temp;
fseek(fd,4,SEEK_CUR); // Jump over chunk size
fread(&temp,2,1,fd);
#ifdef __LITTLE_ENDIAN__
swap16((unsigned char *)&temp);
#endif
channels = temp;
// Get length of data from the header
INT32 frames;
fread(&frames,4,1,fd);
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&frames);
#endif
// length is the number of sample frames
fileSize = frames;
bufferSize = fileSize;
// Verify that the data is 16 bits per sample
fread(&temp,2,1,fd);
#ifdef __LITTLE_ENDIAN__
swap16((unsigned char *)&temp);
#endif
if (temp != 16) {
fclose(fd);
sprintf(msg, "AifWvIn: STK does not currently support data formats other than 16 bit signed integer.\n");
throw StkError(msg, StkError::FILE_ERROR);
}
/* Get file sample rate from the header and set the default rate.
* For AIFF files, this value is stored in a 10-byte, IEEE Standard
* 754 floating point number, so we need to convert it first.
*/
unsigned char srate[10];
unsigned char exp;
unsigned long mantissa;
unsigned long last = 0;;
fread(&srate,10,1,fd);
mantissa = (unsigned long) *(unsigned long *)(srate+2);
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&mantissa);
#endif
exp = 30 - *(srate+1);
while (exp--) {
last = mantissa;
mantissa >>= 1;
}
if (last & 0x00000001) mantissa++;
rate = (MY_FLOAT) (mantissa/SRATE); // set default rate based on file sampling rate
// Find "data" chunk
fread(&id,4,1,fd);
while (strncmp(id,"SSND",4)) {
fread(&chunkSize,4,1,fd);
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&chunkSize);
#endif
fseek(fd,chunkSize,SEEK_CUR);
fread(&id,4,1,fd);
}
// Skip over chunk size, offset, and blocksize fields
fseek(fd,12,SEEK_CUR);
if ((fileSize*channels) > MAX_FILE_LOAD_SIZE) {
printf("\nAifWvIn: The .AIF file (%s) has more than %d samples and\n",
fileName, MAX_FILE_LOAD_SIZE);
printf("will be loaded incrementally from disk. Normalization will be disabled.\n");
chunking = 1;
bufferSize = LOAD_BUFFER_SIZE;
}
// Setup for looping or one-shot playback
if (!strcmp(mode,"looping"))
looping = 1;
else // default = oneshot
looping = 0;
data = (MY_FLOAT *) new MY_FLOAT[(bufferSize+1)*channels];
dataOffset = ftell(fd);
this->getData(0); // Read samples into data[]
if (fmod(rate, 1.0) != 0.0) interpolate = 1;
else interpolate = 0;
phaseOffset = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
this->reset();
// finally, let's normalize the data by default
this->normalize();
}
AifWvIn :: ~AifWvIn()
{
}
void AifWvIn :: getData(long index)
{
/* Compare index to current readPointer and modify as needed.
* The following while() loops will only execute on calls subsequent
* to class instantiation ... and thus, only when "chunking".
*/
while (index < readPointer) {
readPointer -= LOAD_BUFFER_SIZE;
bufferSize = LOAD_BUFFER_SIZE;
if (readPointer < 0) {
bufferSize += readPointer;
readPointer = 0;
}
}
while (index >= readPointer+bufferSize) {
readPointer += LOAD_BUFFER_SIZE;
bufferSize = LOAD_BUFFER_SIZE;
if (readPointer+LOAD_BUFFER_SIZE >= fileSize) {
bufferSize = fileSize - readPointer;
}
}
fseek(fd, dataOffset+(long)(readPointer*channels*2), SEEK_SET);
long length = bufferSize;
int end_of_file = (readPointer+bufferSize == fileSize);
if (!end_of_file) length += 1;
// Read samples into data[]. Use MY _FLOAT data structure to store INT16 samples
INT16 *buf = (INT16 *)data;
fread(buf, 2, length*channels, fd);
// Convert in place (unpack) to MY_FLOAT from the end of the array
for (int i=length*channels-1; i>=0; i--) {
#ifdef __LITTLE_ENDIAN__
swap16((unsigned char *)(buf+i));
#endif
data[i] = buf[i];
if (chunking) data[i] *= 0.00003051;
}
// fill in the extra sample frame for interpolation
if (end_of_file) {
for (int j=0; j<channels; j++)
if (looping)
data[bufferSize*channels+j] = data[j];
else
data[bufferSize*channels+j] = data[(bufferSize-1)*channels+j];
}
if (!chunking) {
fclose(fd);
fd = 0;
}
}

192
src/AifWvOut.cpp Normal file
View File

@@ -0,0 +1,192 @@
/*******************************************/
/*
AifWvOut Output Class,
by Gary P. Scavone, 2000
This object inherits from WvOut and is
used to write Audio Interchange File
Format files with 16-bit data (signed
integer).
.aif files are always bif-endian.
*/
/*******************************************/
#include "AifWvOut.h"
#ifdef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
/******** Aiff Soundfile Header Struct *******/
struct aiffhdr {
char form[4]; // "FORM"
INT32 form_size; // in bytes
char aiff[4]; // "AIFF"
char comm[4]; // "COMM"
INT32 comm_size; // "COMM" chunk size (should be 18)
INT16 num_chans; // number of channels
unsigned long sample_frames; // sample frames of audio data
INT16 sample_size; // always 16 for STK
unsigned char srate[10]; // IEEE 754 floating point format
char ssnd[4]; // "SSND"
INT32 ssnd_size; // "SSND" chunk size
unsigned long offset; // data offset in data block (should be 0)
unsigned long block_size; // not used by STK (should be 0)
};
FILE *openAifFile(int chans, char *fileName) {
struct aiffhdr hdr = {"FOR",46,"AIF","COM",18,0,0,16,"0","SSN",8,0,0};
char tempName[128];
FILE *fd;
char msg[256];
INT16 i;
unsigned long exp;
unsigned long rate = (unsigned long) SRATE;
hdr.form[3] = 'M';
hdr.aiff[3] = 'F';
hdr.comm[3] = 'M';
hdr.ssnd[3] = 'D';
hdr.num_chans = chans;
/*
* For AIFF files, the sample reate is stored in a 10-byte, IEEE Standard
* 754 floating point number, so we need to convert to that.
*/
memset(hdr.srate, 0, 10);
exp = rate;
for (i=0; i<32; i++) {
exp >>= 1;
if (!exp) break;
}
i += 16383;
#ifdef __LITTLE_ENDIAN__
swap16((unsigned char *)&i);
#endif
*(INT16 *)(hdr.srate) = (INT16) i;
for (i=32; i; i--) {
if (rate & 0x80000000) break;
rate <<= 1;
}
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&rate);
#endif
*(unsigned long *)(hdr.srate+2) = (unsigned long) rate;
strcpy(tempName,fileName);
if (strstr(tempName,".aif") == NULL) strcat(tempName,".aif");
fd = fopen(tempName,"wb");
if (!fd) {
sprintf(msg, "AifWvOut: Could not create soundfile: %s\n", tempName);
throw StkError(msg, StkError::FILE_ERROR);
}
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&hdr.form_size);
swap32((unsigned char *)&hdr.comm_size);
swap16((unsigned char *)&hdr.num_chans);
swap16((unsigned char *)&hdr.sample_size);
swap32((unsigned char *)&hdr.ssnd_size);
swap32((unsigned char *)&hdr.offset);
swap32((unsigned char *)&hdr.block_size);
#endif
printf("\nCreating soundfile: %s\n", tempName);
/* I found it necessary to break the fwrite() calls as
* follows ... a single write of 54 bytes didn't work.
*/
fwrite(&hdr,4,5,fd);
fwrite(&hdr.num_chans,2,1,fd);
fwrite(&hdr.sample_frames,4,1,fd);
fwrite(&hdr.sample_size,2,1,fd);
fwrite(&hdr.srate,10,1,fd);
fwrite(&hdr.ssnd,4,4,fd);
return fd;
}
AifWvOut :: AifWvOut(char *fileName, int chans)
{
char msg[256];
if (chans < 1) {
sprintf(msg, "AifWvOut: number of channels = %d not supported!\n", chans);
throw StkError(msg, StkError::FUNCTION_SYNTAX);
}
channels = chans;
fd = openAifFile(chans,fileName);
data_length = FILE_BUFFER_SIZE*channels;
data = (INT16 *) new INT16[data_length];
}
AifWvOut :: ~AifWvOut()
{
MY_FLOAT time;
unsigned long bytes;
unsigned long frames;
fwrite(data,2,counter,fd);
time = (double) totalCount * ONE_OVER_SRATE;
printf("%f Seconds Computed\n\n", time);
frames = (unsigned long) totalCount;
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&frames);
#endif
fseek(fd,22,SEEK_SET); // jump to "COMM" sample_frames
fwrite(&frames,4,1,fd);
bytes = totalCount*2*channels + 46;
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&bytes);
#endif
fseek(fd,4,SEEK_SET); // jump to file size
fwrite(&bytes,4,1,fd);
bytes = totalCount*2*channels + 8;
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&bytes);
#endif
fseek(fd,42,SEEK_SET); // jump to "SSND" chunk size
fwrite(&bytes,4,1,fd);
fclose(fd);
}
void AifWvOut :: tick(MY_FLOAT sample)
{
INT16 isample;
isample = (INT16) (sample * 32000.0);
#ifdef __LITTLE_ENDIAN__
swap16((unsigned char *)&isample);
#endif
for (int i=0;i<channels;i++)
data[counter++] = isample;
totalCount++;
if (counter == data_length) {
fwrite(data,2,data_length,fd);
counter = 0;
}
}
void AifWvOut :: mtick(MY_MULTI samples)
{
for (int i=0;i<channels;i++) {
data[counter] = (INT16) (*samples++ * 32000.0);
#ifdef __LITTLE_ENDIAN__
swap16 ((unsigned char *)&data[counter]);
#endif
counter++;
}
totalCount++;
if (counter == data_length) {
fwrite(data,2,data_length,fd);
counter = 0;
}
}

81
src/BeeThree.cpp Normal file
View File

@@ -0,0 +1,81 @@
/******************************************/
/* Hammond(OID) Organ Subclass */
/* of Algorithm 8 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/******************************************/
#include "BeeThree.h"
BeeThree :: BeeThree() : FM4Alg8()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file1[128];
char file2[128];
char file3[128];
char file4[128];
strcpy(file1, RAWWAVE_PATH);
strcpy(file2, RAWWAVE_PATH);
strcpy(file3, RAWWAVE_PATH);
strcpy(file4, RAWWAVE_PATH);
this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"),
strcat(file2,"rawwaves/sinewave.raw"),
strcat(file3,"rawwaves/sinewave.raw"),
strcat(file4,"rawwaves/fwavblnk.raw"));
this->setRatio(0,(MY_FLOAT) 0.999);
this->setRatio(1,(MY_FLOAT) 1.997);
this->setRatio(2,(MY_FLOAT) 3.006);
this->setRatio(3,(MY_FLOAT) 6.009);
gains[0] = __FM4Op_gains[95];
gains[1] = __FM4Op_gains[95];
gains[2] = __FM4Op_gains[99];
gains[3] = __FM4Op_gains[95];
adsr[0]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.003,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01);
adsr[1]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.003,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01);
adsr[2]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.003,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01);
adsr[3]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 0.001,(MY_FLOAT) 0.4,(MY_FLOAT) 0.03);
twozero->setGain((MY_FLOAT) 0.1);
}
BeeThree :: ~BeeThree()
{
}
void BeeThree :: setFreq(MY_FLOAT frequency)
{
baseFreq = frequency;
waves[0]->setFreq(baseFreq * ratios[0]);
waves[1]->setFreq(baseFreq * ratios[1]);
waves[2]->setFreq(baseFreq * ratios[2]);
waves[3]->setFreq(baseFreq * ratios[3]);
}
MY_FLOAT BeeThree :: tick()
{
MY_FLOAT temp;
if (modDepth > 0.0) {
temp = (MY_FLOAT) 1.0 + (modDepth * vibWave->tick() * (MY_FLOAT) 0.1);
waves[0]->setFreq(baseFreq * ratios[0] * temp);
waves[1]->setFreq(baseFreq * ratios[1] * temp);
waves[2]->setFreq(baseFreq * ratios[2] * temp);
waves[3]->setFreq(baseFreq * ratios[3] * temp);
}
lastOutput = FM4Alg8 :: tick();
return lastOutput;
}
void BeeThree :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
gains[0] = amp * __FM4Op_gains[95];
gains[1] = amp * __FM4Op_gains[95];
gains[2] = amp * __FM4Op_gains[99];
gains[3] = amp * __FM4Op_gains[95];
this->setFreq(freq);
this->keyOn();
#if defined(_debug_)
printf("BeeThree : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}

112
src/BiQuad.cpp Normal file
View File

@@ -0,0 +1,112 @@
/*******************************************/
/*
BiQuad (2-pole, 2-zero) Filter Class,
by Perry R. Cook, 1995-96.
Modified by Julius Smith, 2000:
setA1,setA2,setB1,setB2
See books on filters to understand
more about how this works. Nothing
out of the ordinary in this version.
*/
/*******************************************/
#include "BiQuad.h"
BiQuad :: BiQuad() : Filter()
{
inputs = (MY_FLOAT *) malloc(2 * sizeof(MY_FLOAT));
zeroCoeffs[0] = (MY_FLOAT) 0.0;
zeroCoeffs[1] = (MY_FLOAT) 0.0;
poleCoeffs[0] = (MY_FLOAT) 0.0;
poleCoeffs[1] = (MY_FLOAT) 0.0;
gain = (MY_FLOAT) 1.0;
this->clear();
}
BiQuad :: ~BiQuad()
{
free(inputs);
}
void BiQuad :: clear()
{
inputs[0] = (MY_FLOAT) 0.0;
inputs[1] = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0.0;
}
void BiQuad :: setA1(MY_FLOAT a1)
{
poleCoeffs[0] = -a1;
}
void BiQuad :: setA2(MY_FLOAT a2)
{
poleCoeffs[1] = -a2;
}
void BiQuad :: setB1(MY_FLOAT b1)
{
zeroCoeffs[0] = b1;
}
void BiQuad :: setB2(MY_FLOAT b2)
{
zeroCoeffs[1] = b2;
}
void BiQuad :: setPoleCoeffs(MY_FLOAT *coeffs)
{
poleCoeffs[0] = coeffs[0];
poleCoeffs[1] = coeffs[1];
}
void BiQuad :: setZeroCoeffs(MY_FLOAT *coeffs)
{
zeroCoeffs[0] = coeffs[0];
zeroCoeffs[1] = coeffs[1];
}
void BiQuad :: setFreqAndReson(MY_FLOAT freq, MY_FLOAT reson)
{
poleCoeffs[1] = - (reson * reson);
poleCoeffs[0] = (MY_FLOAT) 2.0 * reson * (MY_FLOAT) cos(TWO_PI * freq / SRATE);
}
void BiQuad :: setEqualGainZeroes()
{
zeroCoeffs[1] = (MY_FLOAT) -1.0;
zeroCoeffs[0] = (MY_FLOAT) 0.0;
}
void BiQuad :: setGain(MY_FLOAT aValue)
{
gain = aValue;
}
/* Perform Filter Operation */
MY_FLOAT BiQuad :: tick(MY_FLOAT sample)
{
/*
Biquad is two pole, two zero filter. Look it up
in your favorite DSP text. This version implements
only 2 state variables. It takes 5 multiplies,
4 adds, and 3 moves.
*/
MY_FLOAT temp;
temp = sample * gain;
temp += inputs[0] * poleCoeffs[0];
temp += inputs[1] * poleCoeffs[1];
lastOutput = temp;
lastOutput += (inputs[0] * zeroCoeffs[0]);
lastOutput += (inputs[1] * zeroCoeffs[1]);
inputs[1] = inputs[0];
inputs[0] = temp;
return lastOutput;
}

209
src/BlowHole.cpp Normal file
View File

@@ -0,0 +1,209 @@
/***********************************************/
/*
Waveguide "reed" instrument model with a
register hole and one tonehole
by Gary P. Scavone, 2000.
*/
/***********************************************/
#include "BlowHole.h"
#include "SKINI11.msg"
BlowHole :: BlowHole(MY_FLOAT lowestFreq)
{
length = (long) (SRATE / lowestFreq + 1);
delays = (DLineL *) new DLineL[3];
// delays[0] is the delay line between the reed and the register vent
delays[0].setDelay(5.0);
// delays[1] is the delay line between the register vent and the tonehole
delays[1].setDelay(length >> 1);
// delays[2] is the delay line between the tonehole and the end of the bore
delays[2].setDelay(4.0);
reedTable = new ReedTabl;
reedTable->setOffset((MY_FLOAT) 0.7);
reedTable->setSlope((MY_FLOAT) -0.3);
filter = new OneZero;
envelope = new Envelope;
noise = new Noise;
// Calculate the initial tonehole three-port scattering coefficient
double r_b = 0.0075; /* main bore radius */
r_th = 0.003; /* tonehole radius */
scatter = -pow(r_th,2) / ( pow(r_th,2) + 2*pow(r_b,2) );
// Calculate tonehole coefficients
MY_FLOAT te = 1.4 * r_th; /* effective length of the open hole */
th_coeff = (te*2*SRATE - 347.23) / (te*2*SRATE + 347.23);
tonehole = new PoleZero;
// Start with tonehole open
tonehole->setA1(-th_coeff);
tonehole->setB0(th_coeff);
tonehole->setB1(-1.0);
// Calculate register hole filter coefficients
double r_rh = 0.0015; /* register vent radius */
te = 1.4 * r_rh; /* effective length of the open hole */
double xi = 0.0; /* series resistance term */
double zeta = 347.23 + 2*PI*pow(r_b,2)*xi/1.1769;
double psi = 2*PI*pow(r_b,2)*te / (PI*pow(r_rh,2));
rh_coeff = (zeta - 2*SRATE*psi) / (zeta + 2*SRATE*psi);
rh_gain = -347.23 / (zeta + 2*SRATE*psi);
vent = new PoleZero;
vent->setA1(rh_coeff);
vent->setB0(1.0);
vent->setB1(1.0);
// Start with register vent closed
vent->setGain(0.0);
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibr = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping");
vibr->setFreq((MY_FLOAT) 5.735);
outputGain = (MY_FLOAT) 1.0;
noiseGain = (MY_FLOAT) 0.2;
vibrGain = (MY_FLOAT) 0.01;
}
BlowHole :: ~BlowHole()
{
delete [] delays;
delete reedTable;
delete filter;
delete tonehole;
delete vent;
delete envelope;
delete noise;
delete vibr;
}
void BlowHole :: clear()
{
delays[0].clear();
delays[1].clear();
filter->tick((MY_FLOAT) 0.0);
tonehole->tick((MY_FLOAT) 0.0);
vent->tick((MY_FLOAT) 0.0);
}
void BlowHole :: setFreq(MY_FLOAT frequency)
{
MY_FLOAT new_length = (SRATE / frequency) * (MY_FLOAT) 0.5 - (MY_FLOAT) 1.5;
new_length -= 9;
if (new_length <= 1.0) new_length = 1.0;
else if (new_length >= length) new_length = length;
delays[1].setDelay(new_length);
}
void BlowHole :: setVent(MY_FLOAT newValue)
{
/*
This method allows setting of the register vent "open-ness" at
any point between "Open" (newValue = 1) and "Closed"
(newValue = 0).
*/
MY_FLOAT gain;
if (newValue <= 0.0) gain = 0.0;
else if (newValue >= 1.0) gain = rh_gain;
else gain = newValue * rh_gain;
vent->setGain(gain);
}
void BlowHole :: setTonehole(MY_FLOAT newValue)
{
/*
This method allows setting of the tonehole "open-ness" at
any point between "Open" (newValue = 1) and "Closed"
(newValue = 0).
*/
MY_FLOAT new_coeff;
if (newValue <= 0.0) new_coeff = 0.9995;
else if (newValue >= 1.0) new_coeff = th_coeff;
else new_coeff = (newValue * (th_coeff - 0.9995)) + 0.9995;
tonehole->setA1(-new_coeff);
tonehole->setB0(new_coeff);
}
void BlowHole :: startBlowing(MY_FLOAT amplitude,MY_FLOAT rate)
{
envelope->setRate(rate);
envelope->setTarget(amplitude);
}
void BlowHole :: stopBlowing(MY_FLOAT rate)
{
envelope->setRate(rate);
envelope->setTarget((MY_FLOAT) 0.0);
}
void BlowHole :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
this->setFreq(freq);
this->startBlowing((MY_FLOAT) 0.55 + (amp * (MY_FLOAT) 0.30),amp * (MY_FLOAT) 0.005);
outputGain = amp + (MY_FLOAT) 0.001;
}
void BlowHole :: noteOff(MY_FLOAT amp)
{
this->stopBlowing(amp * (MY_FLOAT) 0.01);
}
MY_FLOAT BlowHole :: tick()
{
MY_FLOAT pressureDiff;
MY_FLOAT breathPressure;
MY_FLOAT temp;
// Calculate the breath pressure (envelope + noise + vibrator)
breathPressure = envelope->tick();
breathPressure += breathPressure * noiseGain * noise->tick();
breathPressure += breathPressure * vibrGain * vibr->tick();
// Calculate the differential pressure = reflected - mouthpiece pressures
pressureDiff = delays[0].lastOut() - breathPressure;
// Do two-port junction scattering for register vent
MY_FLOAT pa = breathPressure + pressureDiff * reedTable->lookup(pressureDiff);
MY_FLOAT pb = delays[1].lastOut();
vent->tick(pa+pb);
lastOutput = delays[0].tick(vent->lastOut()+pb);
lastOutput *= outputGain;
// Do three-port junction scattering (under tonehole)
pa += vent->lastOut();
pb = delays[2].lastOut();
MY_FLOAT pth = tonehole->lastOut();
temp = scatter * (pa + pb - 2 * pth);
delays[2].tick(filter->tick(pa + temp) * -0.95);
delays[1].tick(pb + temp);
tonehole->tick(pa + pb - pth + temp);
return lastOutput;
}
void BlowHole :: controlChange(int number, MY_FLOAT value)
{
if (number == __SK_ReedStiffness_)
reedTable->setSlope((MY_FLOAT) -0.44 + ((MY_FLOAT) 0.26 * value * NORM_7));
else if (number == __SK_NoiseLevel_)
noiseGain = (value * NORM_7 * (MY_FLOAT) 0.4);
else if (number == __SK_ModFrequency_)
//vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0));
this->setTonehole(value * NORM_7);
else if (number == __SK_ModWheel_)
//vibrGain = (value * NORM_7 * (MY_FLOAT) 0.5);
this->setVent(value * NORM_7);
else if (number == __SK_AfterTouch_Cont_) {
envelope->setValue(value * NORM_7);
}
else {
printf("BlowHole : Undefined Control Number!!\n");
}
}

52
src/BowTabl.cpp Normal file
View File

@@ -0,0 +1,52 @@
/***********************************************/
/* Simple Bow Table Object, after Smith */
/* by Perry R. Cook, 1995-96 */
/***********************************************/
#include "BowTabl.h"
BowTabl :: BowTabl()
{
/* offset is a bias, really not needed unless */
/* friction is different in each direction */
offSet = (MY_FLOAT) 0.0;
slope = (MY_FLOAT) 0.1; /* controls width of friction pulse, */
/* related to bowForce */
}
BowTabl :: ~BowTabl()
{
}
void BowTabl :: setOffset(MY_FLOAT aValue)
{
offSet = aValue;
}
void BowTabl :: setSlope(MY_FLOAT aValue)
{
slope = aValue;
}
MY_FLOAT BowTabl :: lookup(MY_FLOAT sample)
{
return this->tick(sample);
}
MY_FLOAT BowTabl :: tick(MY_FLOAT sample) /* Perform Table Lookup */
{ /* sample is differential */
/* string vs. bow velocity */
MY_FLOAT input;
input = sample + offSet; /* add bias to sample */
input *= slope; /* scale it */
lastOutput = (MY_FLOAT) fabs((double) input) + (MY_FLOAT) 0.75; /* below min delta, friction = 1 */
lastOutput = (MY_FLOAT) pow(lastOutput,(MY_FLOAT) -4.0);
// if (lastOutput < 0.0 ) lastOutput = 0.0; /* minimum friction is 0.0 */
if (lastOutput > 1.0 ) lastOutput = (MY_FLOAT) 1.0; /* maximum friction is 1.0 */
return lastOutput;
}
MY_FLOAT BowTabl :: lastOut()
{
return lastOutput;
}

165
src/Bowed.cpp Normal file
View File

@@ -0,0 +1,165 @@
/******************************************/
/* Bowed String model ala Smith */
/* after McIntyre, Schumacher, Woodhouse */
/* by Perry Cook, 1995-96 */
/* */
/* This is a waveguide model, and thus */
/* relates to various Stanford Univ. */
/* and possibly Yamaha and other patents.*/
/* */
/* Controls: CONTROL1 = bowPressure */
/* CONTROL2 = bowPosition */
/* CONTROL3 = vibrFreq */
/* MOD_WHEEL= vibrGain */
/* */
/******************************************/
#include "Bowed.h"
#include "SKINI11.msg"
Bowed :: Bowed(MY_FLOAT lowestFreq)
{
long length;
length = (long) (SRATE / lowestFreq + 1);
neckDelay = new DLineL(length);
length >>= 1;
bridgeDelay = new DLineL(length);
bowTabl = new BowTabl;
reflFilt = new OnePole;
bodyFilt = new BiQuad;
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibr = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping");
adsr = new ADSR;
vibrGain = (MY_FLOAT) 0.0;
neckDelay->setDelay((MY_FLOAT) 100.0);
bridgeDelay->setDelay((MY_FLOAT) 29.0);
bowTabl->setSlope((MY_FLOAT) 3.0);
reflFilt->setPole((MY_FLOAT) (0.6 - (0.1 * 22050.0 / SRATE)));
reflFilt->setGain((MY_FLOAT) 0.95);
bodyFilt->setFreqAndReson((MY_FLOAT) 500.0, (MY_FLOAT) 0.85);
bodyFilt->setEqualGainZeroes();
bodyFilt->setGain((MY_FLOAT) 0.2);
vibr->setFreq((MY_FLOAT) 6.12723);
adsr->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.005,(MY_FLOAT) 0.9,(MY_FLOAT) 0.01);
betaRatio = (MY_FLOAT) 0.127236;
}
Bowed :: ~Bowed()
{
delete neckDelay;
delete bridgeDelay;
delete bowTabl;
delete reflFilt;
delete bodyFilt;
delete vibr;
delete adsr;
}
void Bowed :: clear()
{
neckDelay->clear();
bridgeDelay->clear();
}
void Bowed :: setFreq(MY_FLOAT frequency)
{
baseDelay = SRATE / frequency - (MY_FLOAT) 4.0; /* delay - approx. filter delay */
bridgeDelay->setDelay(baseDelay * betaRatio); /* bow to bridge length */
neckDelay->setDelay(baseDelay * ((MY_FLOAT) 1.0 - betaRatio)); /* bow to nut (finger) length */
}
void Bowed :: startBowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
adsr->setRate(rate);
adsr->keyOn();
maxVelocity = (MY_FLOAT) 0.03 + ((MY_FLOAT) 0.2 * amplitude);
}
void Bowed :: stopBowing(MY_FLOAT rate)
{
adsr->setRate(rate);
adsr->keyOff();
}
void Bowed :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
this->startBowing(amp,amp * (MY_FLOAT) 0.001);
this->setFreq(freq);
#if defined(_debug_)
printf("Bowed : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}
void Bowed :: noteOff(MY_FLOAT amp)
{
this->stopBowing(((MY_FLOAT) 1.0 - amp) * (MY_FLOAT) 0.005);
#if defined(_debug_)
printf("Bowed : NoteOff: Amp=%lf\n",amp);
#endif
}
void Bowed :: setVibrato(MY_FLOAT amount)
{
vibrGain = amount;
}
MY_FLOAT Bowed :: tick()
{
MY_FLOAT bowVelocity;
MY_FLOAT bridgeRefl=(MY_FLOAT) 0,nutRefl=(MY_FLOAT) 0;
MY_FLOAT newVel=(MY_FLOAT) 0,velDiff=(MY_FLOAT) 0,stringVel=(MY_FLOAT) 0;
bowVelocity = maxVelocity * adsr->tick();
bridgeRefl = -reflFilt->tick(
bridgeDelay->lastOut()); /* Bridge Reflection */
nutRefl = -neckDelay->lastOut(); /* Nut Reflection */
stringVel = bridgeRefl + nutRefl; /* Sum is String Velocity */
velDiff = bowVelocity - stringVel; /* Differential Velocity */
newVel = velDiff * bowTabl->lookup(velDiff); /* Non-Lin Bow Function */
neckDelay->tick(bridgeRefl + newVel); /* Do string */
bridgeDelay->tick(nutRefl + newVel); /* propagations */
if (vibrGain > 0.0) {
neckDelay->setDelay((baseDelay * ((MY_FLOAT) 1.0 - betaRatio)) +
(baseDelay * vibrGain*vibr->tick()));
}
lastOutput = bodyFilt->tick(bridgeDelay->lastOut());
return lastOutput;
}
void Bowed :: controlChange(int number, MY_FLOAT value)
{
#if defined(_debug_)
printf("Bowed : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_BowPressure_)
bowTabl->setSlope((MY_FLOAT) 5.0 - ((MY_FLOAT) 4.0 * value * NORM_7));
else if (number == __SK_BowPosition_) {
betaRatio = (MY_FLOAT) 0.027236 + ((MY_FLOAT) 0.2 * value * NORM_7);
bridgeDelay->setDelay(baseDelay * betaRatio); /* bow to bridge length */
neckDelay->setDelay(baseDelay * ((MY_FLOAT) 1.0 - betaRatio)); /* bow to nut (finger) length */
}
else if (number == __SK_ModFrequency_)
vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0));
else if (number == __SK_ModWheel_)
vibrGain = (value * NORM_7 * (MY_FLOAT) 0.4);
else if (number == __SK_AfterTouch_Cont_)
adsr->setTarget(value * NORM_7);
else {
printf("Bowed : Undefined Control Number!!\n");
}
}

314
src/BowedBar.cpp Normal file
View File

@@ -0,0 +1,314 @@
/*********************************************/
/* Bowed Bar model */
/* by Georg Essl, 1999 */
/* For details refer to: */
/* G.Essl, P.R.Cook: "Banded Waveguides: */
/* Towards Physical Modelling of Bar */
/* Percussion Instruments", ICMC'99 */
/*********************************************/
#include "BowedBar.h"
#include "SKINI11.msg"
#include "Noise.h"
/* Contructor */
BowedBar :: BowedBar()
{
long i;
doPluck = 1;
/* Number of banded waveguide modes */
NR_MODES = 4;
modes[0] = (MY_FLOAT) 1.0;
modes[1] = (MY_FLOAT) 2.756;
modes[2] = (MY_FLOAT) 5.404;
modes[3] = (MY_FLOAT) 8.933;
for (i=0;i<4;i++) {
gains[i] = (MY_FLOAT) pow(0.9,(double) i);
}
bowTabl = new BowTabl;
adsr = new ADSR;
bandpass = new BiQuad[NR_MODES];
bowTabl->setSlope((MY_FLOAT) 3.0);
adsr->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.005,(MY_FLOAT) 0.9,(MY_FLOAT) 0.01);
freq = SRATE / 100;
length = 100;
bowPos = 0;
lastBowPos = 0;
for(i = 0; i<NR_MODES; i++) {
delay[i].setDelay((int)(length/modes[i]));
delay[i].clear();
bandpass[i].clear();
Zs[i][1] = 0.0;
Zs[i][2] = 0.0;
filtOut[i] = 0.0;
filtIn[i] = 0.0;
}
R = (MY_FLOAT) 0.97;
GAIN = (MY_FLOAT) 0.999;
slope = (MY_FLOAT) 3.0;
this->tuneBandPasses();
}
BowedBar :: ~BowedBar()
{
delete bowTabl;
delete adsr;
delete [] bandpass;
}
void BowedBar :: clear()
{
long i;
for(i = 0; i<NR_MODES; i++) {
delay[i].clear();
bandpass[i].clear();
Zs[i][1] = 0.0;
Zs[i][2] = 0.0;
filtOut[i] = 0.0;
filtIn[i] = 0.0;
}
}
void BowedBar :: setFreq(MY_FLOAT frequency)
{
int i;
freq = frequency;
if(freq > 1568.0) freq = 1568.0;
length = (int)(SRATE/freq);
NR_MODES = 4;
for(i = 0; i<NR_MODES; i++) {
if((int)(length/modes[i]) > 4)
delay[i].setDelay((int)(length/modes[i]));
else {
NR_MODES = i;
break;
}
/* FIX THIS BETTER!!!!! */
delay[i].clear();
bandpass[i].clear();
Zs[i][1] = 0.0;
Zs[i][2] = 0.0;
filtOut[i] = 0.0;
filtIn[i] = 0.0;
}
tuneBandPasses();
}
void BowedBar :: setStrikePosition(MY_FLOAT position)
{
MY_FLOAT temp2;
temp2 = position * PI;
gains[0] = fabs(sin(temp2 / 2) * pow(0.9,0));
gains[1] = fabs(sin(temp2) * pow(0.9,1));
gains[2] = fabs(sin(temp2 * 3 / 2) * pow(0.9,2));
gains[3] = fabs(sin(temp2 * 2) * pow(0.9,3));
}
void BowedBar :: tuneBandPasses()
{
long i;
for(i=0; i<NR_MODES; i++) {
R = 1 - 6.28318530718 * freq * modes[i] / SRATE / 2.0;
bandpass[i].setFreqAndReson(freq * modes[i], R);
bandpass[i].setEqualGainZeroes();
bandpass[i].setGain((1.0-R*R)/2.0);
filtGain[i] = (MY_FLOAT) (1.0 - (R*R))/2.0;
coeffs[i][1] = -R * R;
coeffs[i][0] = 2.0 * R * cos(6.28318530718 * freq * modes[i] / SRATE);
delay[i].clear(); //(rand()) - 16384;
}
}
void BowedBar :: startBowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
adsr->setRate(rate);
adsr->keyOn();
maxVelocity = (MY_FLOAT) 0.03 + ((MY_FLOAT) 0.2 * amplitude);
maxVelocity = (MY_FLOAT) 0.03 + ((MY_FLOAT) 0.5 * amplitude);
}
void BowedBar :: stopBowing(MY_FLOAT rate)
{
adsr->setRate(rate);
adsr->keyOff();
}
void BowedBar :: pluck(MY_FLOAT amplitude)
{
long i,j;
int pluckLen;
MY_FLOAT temp;
Noise noise;
pluckLen = (int)(length/modes[NR_MODES-1]);
for (j=1;j<pluckLen/2;j++) {
temp = amplitude*2.0*noise.tick();
for(i=0; i<NR_MODES; i++)
delay[i].tick(temp*j/pluckLen*gains[i]);
}
for (j=pluckLen/2;j>0;j--) {
temp = amplitude*2.0*noise.tick();
for(i=0; i<NR_MODES; i++)
delay[i].tick(temp*j/pluckLen*gains[i]);
}
}
void BowedBar :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
if(!doPluck) {
for(int i=0; i<NR_MODES ; i++)
bandpass[i].clear();
this->startBowing(amp,amp * (MY_FLOAT) 0.001);
this->setFreq(freq);
doPluck = 0;
#if defined(_debug_)
printf("BowedBar : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}
else {
for(int i=0; i<NR_MODES ; i++)
bandpass[i].clear();
this->setFreq(freq);
this->pluck(amp);
doPluck = 1;
}
}
void BowedBar :: noteOff(MY_FLOAT amp)
{
if(!doPluck) {
this->stopBowing(((MY_FLOAT) 1.0 - amp) * (MY_FLOAT) 0.005);
}
#if defined(_debug_)
printf("BowedBar : NoteOff: Amp=%lf\n",amp);
#endif
}
MY_FLOAT BowedBar :: tick()
{
long k;
MY_FLOAT input;
MY_FLOAT data;
data = 0.0;
input = 0.0;
if(integration_const == 0.0)
velinput = 0.0;
else
velinput = integration_const * velinput;
for(k=0; k<NR_MODES; k++)
{
velinput += GAIN * delay[k].lastOut();
}
if(trackVel) {
bowvel *= 0.9995;
bowvel += bowTarg;
bowTarg *= 0.995;
}
else
{
bowvel = adsr->tick()*maxVelocity;
}
if(doPluck)
{
input = 0.0;
}
else
{
input = bowvel - velinput;
input = input * bowTabl->lookup(input);
input = input/(MY_FLOAT)NR_MODES;
}
for(k=0; k<NR_MODES; k++)
{
bandpass[k].tick(input*gains[k] + GAIN * delay[k].lastOut());
delay[k].tick(bandpass[k].lastOut());
data += bandpass[k].lastOut();
}
lastOutput = data * 4.0;
return lastOutput;
}
void BowedBar :: controlChange(int number, MY_FLOAT value)
{
#if defined(_debug_)
printf("BowedBar : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_BowPressure_)
{
bowTabl->setSlope((MY_FLOAT) 10.0 - ((MY_FLOAT) 9.0 * value * NORM_7));
slope = (MY_FLOAT) 10.0 - ((MY_FLOAT) 9.0 * value * NORM_7);
}
else if (number == __SK_BowPosition_)
{
this->setStrikePosition(value*NORM_7);
}
else if (number == __SK_Balance_) {
bowPos = value * NORM_7;
bowTarg += 0.02 * (bowPos - lastBowPos);
lastBowPos = bowPos;
adsr->setTarget(bowPos);
}
else if (number == __SK_AfterTouch_Cont_)
{
bowPos = value * NORM_7;
bowTarg += 0.02 * (bowPos - lastBowPos);
lastBowPos = bowPos;
adsr->setTarget(bowPos);
}
else if (number == __SK_ModWheel_)
{
GAIN = 0.809 + (0.2 * (value * NORM_7));
}
else if(number == __SK_ModFrequency_)
{
integration_const = value * NORM_7;
}
else if(number == __SK_Sustain_) {
if(value < 65)
doPluck = 1;
else
doPluck = 0;
}
else if(number == __SK_Portamento_) {
if(value < 65)
trackVel = 0;
else
trackVel = 1;
}
else {
printf("BowedBar : Undefined Control Number!!\n");
}
}

134
src/Brass.cpp Normal file
View File

@@ -0,0 +1,134 @@
/******************************************/
/* Waveguide Brass Instrument Model ala */
/* Cook (TBone, HosePlayer) */
/* by Perry R. Cook, 1995-96 */
/* */
/* This is a waveguide model, and thus */
/* relates to various Stanford Univ. */
/* and possibly Yamaha and other patents.*/
/* */
/* Controls: CONTROL1 = lipTension */
/* CONTROL2 = slideLength */
/* CONTROL3 = vibFreq */
/* MOD_WHEEL= vibAmt */
/******************************************/
#include "Brass.h"
#include "SKINI11.msg"
Brass :: Brass(MY_FLOAT lowestFreq)
{
length = (long) (SRATE / lowestFreq + 1);
delayLine = new DLineA(length);
lipFilter = new LipFilt;
dcBlock = new DCBlock;
adsr = new ADSR;
adsr->setAllTimes((MY_FLOAT) 0.005, (MY_FLOAT) 0.001, (MY_FLOAT) 1.0, (MY_FLOAT) 0.010);
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibr = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping");
this->clear();
vibr->setFreq((MY_FLOAT) 6.137);
vibrGain = (MY_FLOAT) 0.05; /* breath periodic vibrato component */
maxPressure = (MY_FLOAT) 0.0;
}
Brass :: ~Brass()
{
delete delayLine;
delete lipFilter;
delete dcBlock;
delete adsr;
delete vibr;
}
void Brass :: clear()
{
delayLine->clear();
lipFilter->clear();
dcBlock->clear();
}
void Brass :: setFreq(MY_FLOAT frequency)
{
slideTarget = (SRATE / frequency * (MY_FLOAT) 2.0) + (MY_FLOAT) 3.0;
/* fudge correction for filter delays */
delayLine->setDelay(slideTarget); /* we'll play a harmonic */
lipTarget = frequency;
lipFilter->setFreq(frequency);
}
void Brass :: setLip(MY_FLOAT frequency)
{
lipFilter->setFreq(frequency);
}
void Brass :: startBlowing(MY_FLOAT amplitude,MY_FLOAT rate)
{
adsr->setAttackRate(rate);
maxPressure = amplitude;
adsr->keyOn();
}
void Brass :: stopBlowing(MY_FLOAT rate)
{
adsr->setReleaseRate(rate);
adsr->keyOff();
}
void Brass :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
this->setFreq(freq);
this->startBlowing(amp, amp * (MY_FLOAT) 0.001);
#if defined(_debug_)
printf("Brass : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}
void Brass :: noteOff(MY_FLOAT amp)
{
this->stopBlowing(amp * (MY_FLOAT) 0.005);
#if defined(_debug_)
printf("Brass : NoteOff: Amp=%lf\n",amp);
#endif
}
MY_FLOAT Brass :: tick()
{
MY_FLOAT breathPressure;
breathPressure = maxPressure * adsr->tick();
breathPressure += vibrGain * vibr->tick();
lastOutput = delayLine->tick( /* bore delay */
dcBlock->tick( /* block DC */
lipFilter->tick((MY_FLOAT) 0.3 * breathPressure, /* mouth input */
(MY_FLOAT) 0.85 * delayLine->lastOut()))); /* and bore reflection */
return lastOutput;
}
void Brass :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT temp;
#if defined(_debug_)
printf("Brass : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_LipTension_) {
temp = lipTarget * (MY_FLOAT) pow(4.0,(2.0*value*NORM_7) - 1.0);
this->setLip(temp);
}
else if (number == __SK_SlideLength_)
delayLine->setDelay(slideTarget * ((MY_FLOAT) 0.5 + (value * NORM_7)));
else if (number == __SK_ModFrequency_)
vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0));
else if (number == __SK_ModWheel_ )
vibrGain = (value * NORM_7 * (MY_FLOAT) 0.4);
else if (number == __SK_AfterTouch_Cont_)
adsr->setTarget(value * NORM_7);
else {
printf("Brass : Undefined Control Number!!\n");
}
}

55
src/ByteSwap.cpp Normal file
View File

@@ -0,0 +1,55 @@
#include "ByteSwap.h"
void swap16(unsigned char *ptr)
{
register unsigned char val;
/* Swap 1st and 2nd bytes */
val = *(ptr);
*(ptr) = *(ptr+1);
*(ptr+1) = val;
}
void 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 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;
}

129
src/Clarinet.cpp Normal file
View File

@@ -0,0 +1,129 @@
/******************************************/
/* Waveguide Clarinet model ala Smith */
/* after McIntyre, Schumacher, Woodhouse */
/* by Perry Cook, 1995-96 */
/* */
/* This is a waveguide model, and thus */
/* relates to various Stanford Univ. */
/* and possibly Yamaha and other patents.*/
/* */
/* Controls: CONTROL1 = reedStiffns */
/* CONTROL2 = noiseGain */
/* CONTROL3 = vibFreq */
/* MOD_WHEEL= vibAmt */
/******************************************/
#include "Clarinet.h"
#include "SKINI11.msg"
Clarinet :: Clarinet(MY_FLOAT lowestFreq)
{
length = (long) (SRATE / lowestFreq + 1);
delayLine = new DLineL(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 file[128];
strcpy(file, RAWWAVE_PATH);
vibr = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping");
vibr->setFreq((MY_FLOAT) 5.735);
outputGain = (MY_FLOAT) 1.0;
noiseGain = (MY_FLOAT) 0.2;
vibrGain = (MY_FLOAT) 0.1;
}
Clarinet :: ~Clarinet()
{
delete delayLine;
delete reedTable;
delete filter;
delete envelope;
delete noise;
delete vibr;
}
void Clarinet :: clear()
{
delayLine->clear();
filter->tick((MY_FLOAT) 0.0);
}
void Clarinet :: setFreq(MY_FLOAT frequency)
{
delayLine->setDelay /* length - approx filter delay */
((SRATE / frequency) * (MY_FLOAT) 0.5 - (MY_FLOAT) 1.5);
}
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 freq, MY_FLOAT amp)
{
this->setFreq(freq);
this->startBlowing((MY_FLOAT) 0.55 + (amp * (MY_FLOAT) 0.30),amp * (MY_FLOAT) 0.005);
outputGain = amp + (MY_FLOAT) 0.001;
#if defined(_debug_)
printf("Clarinet : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}
void Clarinet :: noteOff(MY_FLOAT amp)
{
this->stopBlowing(amp * (MY_FLOAT) 0.01);
#if defined(_debug_)
printf("Clarinet : NoteOff: Amp=%lf\n",amp);
#endif
}
MY_FLOAT Clarinet :: tick()
{
MY_FLOAT pressureDiff;
MY_FLOAT breathPressure;
breathPressure = envelope->tick();
breathPressure += breathPressure *
noiseGain * noise->tick();
breathPressure += breathPressure *
vibrGain * vibr->tick();
pressureDiff = filter->tick(delayLine->lastOut()); /* differential pressure */
pressureDiff = (pressureDiff * (MY_FLOAT) -0.95) - breathPressure; /* of reflected and mouth */
lastOutput = delayLine->tick(breathPressure + /* perform scattering */
pressureDiff * reedTable->lookup(pressureDiff)); /* in economical way */
lastOutput *= outputGain;
return lastOutput;
}
void Clarinet :: controlChange(int number, MY_FLOAT value)
{
#if defined(_debug_)
printf("Clarinet : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_ReedStiffness_)
reedTable->setSlope((MY_FLOAT) -0.44 + ((MY_FLOAT) 0.26 * value * NORM_7));
else if (number == __SK_NoiseLevel_)
noiseGain = (value * NORM_7 * (MY_FLOAT) 0.4);
else if (number == __SK_ModFrequency_)
vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0));
else if (number == __SK_ModWheel_)
vibrGain = (value * NORM_7 * (MY_FLOAT) 0.5);
else if (number == __SK_AfterTouch_Cont_) {
envelope->setValue(value * NORM_7);
}
else {
printf("Clarinet : Undefined Control Number!!\n");
}
}

490
src/Controller.cpp Normal file
View File

@@ -0,0 +1,490 @@
/******************************************/
/*
Controller Class,
by Gary P. Scavone, 2000
This object will accept control messages
from a variety of sources, such as a MIDI
port, scorefile, socket connection, or
pipe. MIDI messages are retrieved with
the RtMidi class. All other input sources
(scorefile, socket, or pipe) are assumed
to provide SKINI formatted messages.
For each call of getNextMessage(), the
active input devices are queried to see
if a new control message is available.
Only one message per call is returned, so
a subsequent call begins querying the
next available device "after" the previously
handled one.
This class is primarily for use in STK
main() event loops.
One of my 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 are
then sent via a socket connection to the message
socket server. Perhaps in the future, I will be
able to simplify things.
*/
/******************************************/
#include "Controller.h"
Controller :: Controller(int inputMask)
{
source = inputMask;
default_ticks = RT_BUFFER_SIZE;
msg_index = 0;
num_messages = 0;
score = new SKINI11();
#if defined(__STK_REALTIME_)
maxfd = 0;
char msg[256];
struct sockaddr_in mysocket;
FD_ZERO(&mask);
/*
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;
if ( source & STK_MIDI )
midi_input = new RtMidi();
/*
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 in 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.
*/
if (source) {
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
if (pthread_create(&stdin_thread, NULL, stdinHandler, NULL)) {
#elif defined(__OS_Win_)
stdin_thread = _beginthread(stdinHandler, 0, NULL);
if (stdin_thread == 0) {
#endif
sprintf(msg, "Controller: unable to create stdin socket thread!\n");
throw StkError(msg, StkError::PROCESS_THREAD);
}
source |= STK_SOCKET;
}
/* Let the user know that they can exit the program via the console
if necessary.
*/
if ( !(source & STK_PIPE) && inputMask )
printf("\nType `Exit<cr>' to quit.\n\n");
if ( source & STK_SOCKET ) {
// Set up the socket server to accept remote connections
#if defined(__OS_Win_) // Windoze-only stuff
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1,1);
WSAStartup(wVersionRequested, &wsaData);
if (wsaData.wVersion != wVersionRequested) {
sprintf(msg, "Controller: Wrong Windoze socket library version!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
#endif
// Create the server-side socket
local_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (local_socket < 0) {
sprintf(msg, "Controller: Couldn't create socket!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
mysocket.sin_family=AF_INET;
mysocket.sin_addr.s_addr=INADDR_ANY;
mysocket.sin_port=htons(STK_SOCKET_PORT);
// Bind socket to the appropriate port and interface (INADDR_ANY)
if (bind(local_socket, (struct sockaddr *)&mysocket, sizeof(mysocket)) < 0) {
sprintf(msg, "Controller: Couldn't bind socket!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
// Listen for one incoming connection
if (listen(local_socket, 1) < 0) {
sprintf(msg, "Controller: Couldn't set up listen on socket!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
if (inputMask & STK_SOCKET)
// Only print the message if STK_SOCKET was initially specified
printf("Listening for a connection on port %d\n\n", STK_SOCKET_PORT);
FD_SET(local_socket, &mask);
if (local_socket > maxfd) maxfd = local_socket;
}
#endif // __STK_REALTIME
}
Controller :: ~Controller()
{
delete score;
#if defined(__STK_REALTIME_)
if ( source & STK_MIDI )
delete midi_input;
if ( source & STK_SOCKET ) {
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
shutdown(local_socket,0);
pthread_cancel(stdin_thread);
pthread_join(stdin_thread, NULL);
#elif defined(__OS_Win_)
TerminateThread((HANDLE)stdin_thread,0);
closesocket(local_socket);
WSACleanup();
#endif
}
#endif // __STK_REALTIME
}
int Controller :: getType()
{
return type;
}
MY_FLOAT Controller :: getByte2()
{
return byte2;
}
MY_FLOAT Controller :: getByte3()
{
return byte3;
}
MY_FLOAT Controller :: getChannel()
{
return channel;
}
void Controller :: setDefaultTicks(long nSamples)
{
default_ticks = nSamples;
}
int Controller :: getNextMessage()
{
#if defined(__STK_REALTIME_)
static fd_set rmask;
static struct timeval timeout = {0, 0};
static int device = 0;
static int nSockets = 0;
static int fd_thread = 0;
int checked = 0, i;
char msg[256];
#endif
// reset message type
type = 0;
if (!source) {
// no realtime flags ... assuming scorefile input
memset(message[msg_index], 0, MESSAGE_LENGTH);
if ( fgets(message[msg_index], MESSAGE_LENGTH, stdin) == 0 )
return -1;
goto have_message;
}
#if defined(__STK_REALTIME_)
if (num_messages > 0)
goto have_message;
while (checked < nSockets+1) {
if ( source & STK_SOCKET ) {
rmask = mask;
if (select(maxfd+1, &rmask, (fd_set *)0, (fd_set *)0, &timeout)) {
// there is a file descriptor set
// check whether there's a new socket connection available
if ( FD_ISSET(local_socket, &rmask) ) {
// accept and service new connection
int remote_socket=accept(local_socket, NULL, NULL);
if (remote_socket < 0) {
sprintf(msg, "Controller: Couldn't accept connection request!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
if (nSockets != 0)
// print message for connections subsequent to "piping" socket
printf("New socket connection made.\n\n");
// set the socket non-blocking
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
fcntl(remote_socket, F_SETFL, O_NONBLOCK);
#elif defined(__OS_Win_)
unsigned long non_block = 1;
ioctlsocket(remote_socket, FIONBIO, &non_block);
#endif
fd[nSockets] = remote_socket;
if (nSockets == 0) fd_thread = remote_socket;
nSockets++;
FD_SET(remote_socket, &mask);
if ( remote_socket > maxfd) maxfd = remote_socket;
FD_CLR(local_socket, &rmask);
}
// check socket connections
while (device < nSockets) {
if (FD_ISSET(fd[device], &rmask)) {
// this socket has data
i = parseSocketData(fd[device]);
if (i == 0) {
// the socket connection closed
nSockets--;
if ( nSockets == 0 )
return -1;
if ( nSockets == 1 && FD_ISSET(fd_thread, &mask) ) {
// the "piping" socket is still running
if (source & STK_MIDI) {
printf("MIDI input still running ... type 'Exit<cr>' to quit.\n\n");
return 0;
}
else if (!(source & STK_PIPE) )
return -1;
}
if (device < nSockets) {
// move descriptors down in the list
for (int j=device; j<nSockets; j++)
fd[j] = fd[j+1];
}
device++;
return 0;
}
device++;
if ( !strncmp(message[msg_index], "Exit", 4) || !strncmp(message[msg_index], "exit", 4) ) {
// we have an "Exit" message ... ignore it
msg_index++;
return 0;
}
// not an "Exit" message ... parse it
goto have_message;
}
device++;
if (++checked >= nSockets+1) break;
}
}
else { // no file descriptors were set
device += nSockets;
checked += nSockets;
}
}
// check MIDI
if (device >= nSockets) {
//printf("got here, nSockets = %d, checked = %d, device = %d\n", nSockets, checked, device);
device = 0;
if (source & STK_MIDI) {
if ( midi_input->nextMessage() > 0 ) {
// get MIDI message info
type = midi_input->getType();
channel = midi_input->getChannel();
byte2 = midi_input->getByteTwo();
byte3 = midi_input->getByteThree();
return default_ticks;
}
}
if (++checked >= nSockets+1) break;
}
}
// if we get here, we checked all devices but found no messages
return default_ticks;
#endif // __STK_REALTIME
have_message:
//printf("%s", message[msg_index]);
long ticks;
score->parseThis(message[msg_index++]);
num_messages--;
if (msg_index >= MAX_MESSAGES) msg_index = 0;
type = score->getType();
if (type > 0) {
MY_FLOAT temp = score->getDelta();
if ( temp >= 0.0 )
ticks = (long) (temp * SRATE);
else
// negative delta times specify realtime messages
ticks = default_ticks;
channel = score->getChannel();
byte2 = score->getByteTwo();
byte3 = score->getByteThree();
}
else {
// Don't tick for comments or improperly formatted messages
ticks = 0;
}
return ticks;
}
#if defined(__STK_REALTIME_)
int Controller :: parseSocketData(int fd)
{
/*
Parsing the socket data buffer is complicated by the fact that
multiple and sometimes incomplete socket messages can be returned
from a single recv() call, especially during high socket activity.
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.
*/
static char socket_buffer[MESSAGE_LENGTH];
int index = 0, m = 0, bufsize = 0;
int fill_msg;
fill_msg = (msg_index + num_messages) % MAX_MESSAGES;
memset(message[fill_msg], 0, MESSAGE_LENGTH);
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
errno = 0;
while (bufsize != -1 && errno != EAGAIN) {
#elif defined(__OS_Win_)
while (bufsize != SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
#endif
while (index < bufsize) {
message[fill_msg][m++] = socket_buffer[index];
if (socket_buffer[index++] == '\n') {
m = 0;
num_messages++;
fill_msg = (msg_index + num_messages) % MAX_MESSAGES;
memset(message[fill_msg], 0, MESSAGE_LENGTH);
}
}
index = 0;
// receive a new socket buffer
memset(socket_buffer, 0, MESSAGE_LENGTH);
bufsize = recv(fd, socket_buffer, MESSAGE_LENGTH, 0);
if (bufsize == 0) {
FD_CLR(fd, &mask);
#if defined(__OS_Win_)
closesocket(fd);
#else
close(fd);
#endif
return 0;
}
}
return 1;
}
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
void *stdinHandler(void *)
#elif defined(__OS_Win_)
void stdinHandler(void *)
#endif
{
char message[MESSAGE_LENGTH];
char msg[256];
int local_socket;
struct sockaddr_in server_address;
#if defined(__OS_Win_) // Windoze-only stuff
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1,1);
WSAStartup(wVersionRequested, &wsaData);
if (wsaData.wVersion != wVersionRequested) {
sprintf(msg, "Controller: Wrong Windoze socket library version!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
#endif
// Create the client-side socket
local_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (local_socket < 0) {
sprintf(msg, "Controller: Couldn't create socket!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
struct hostent *hostp;
hostp = gethostbyname("localhost");
// Fill in the address structure
server_address.sin_family = AF_INET;
memcpy((void *)&server_address.sin_addr, hostp->h_addr, hostp->h_length);
server_address.sin_port = htons(STK_SOCKET_PORT);
// Connect to the server
if (connect(local_socket, (struct sockaddr *)&server_address,
sizeof(server_address) ) < 0) {
#if defined(__OS_Win_)
closesocket(local_socket);
WSACleanup();
#else
close(local_socket);
#endif
sprintf(msg, "Controller: Couldn't connect stdin socket thread to server!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
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 (send(local_socket, (const char *)message, strlen(message), 0) < 0) {
#if defined(__OS_Win_)
closesocket(local_socket);
WSACleanup();
#else
close(local_socket);
#endif
sprintf(msg, "Controller: connection to socket server failed!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
}
#if defined(__OS_Win_)
closesocket(local_socket);
_endthread();
#else
close(local_socket);
return NULL;
#endif
}
#endif // __STK_REALTIME

40
src/DCBlock.cpp Normal file
View File

@@ -0,0 +1,40 @@
/*******************************************/
/* DC Blocking Filter */
/* by Perry R. Cook, 1995-96 */
/* This guy is very helpful in, uh, */
/* blocking DC. Needed because a simple */
/* low-pass reflection filter allows DC */
/* to build up inside recursive */
/* structures. */
/*******************************************/
#include "DCBlock.h"
DCBlock :: DCBlock()
{
inputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT));
outputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT));
this->clear();
}
DCBlock :: ~DCBlock()
{
free(inputs);
free(outputs);
}
void DCBlock :: clear()
{
outputs[0] = (MY_FLOAT) 0.0;
inputs[0] = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0.0;
}
MY_FLOAT DCBlock :: tick(MY_FLOAT sample)
{
outputs[0] = sample - inputs[0] + ((MY_FLOAT) 0.99 * outputs[0]);
inputs[0] = sample;
lastOutput = outputs[0];
return lastOutput;
}

107
src/DLineA.cpp Normal file
View File

@@ -0,0 +1,107 @@
/*******************************************/
/* AllPass Interpolating Delay Line */
/* Object by Perry R. Cook 1995-96. */
/* Revised by Gary P. Scavone, 1999. */
/* */
/* This one uses a delay line of maximum */
/* length specified on creation, and */
/* interpolates fractional length using */
/* an all-pass filter. This version is */
/* more efficient for computing static */
/* length delay lines (alpha and coeff */
/* are computed only when the length */
/* is set, there probably is a more */
/* efficient computational form if alpha */
/* is changed often (each sample)). */
/* */
/*******************************************/
#include "DLineA.h"
DLineA :: DLineA()
{
long i;
// Default max delay length set to 2047.
length = 2048;
inputs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT));
for (i=0;i<length;i++) inputs[i] = (MY_FLOAT) 0;
this->clear();
inPoint = 0;
outPoint = length >> 1;
}
DLineA :: DLineA(long max_length)
{
long i;
length = max_length;
inputs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT));
for (i=0;i<length;i++) inputs[i] = (MY_FLOAT) 0;
this->clear();
inPoint = 0;
outPoint = length >> 1;
}
DLineA :: ~DLineA()
{
free(inputs);
}
void DLineA :: clear()
{
long i;
for (i=0;i<length;i++) inputs[i] = (MY_FLOAT) 0.0;
lastIn = (MY_FLOAT) 0;
lastOutput = (MY_FLOAT) 0;
}
void DLineA :: setDelay(MY_FLOAT lag)
{
MY_FLOAT outPointer;
if (lag > length-1) { // if delay is too big,
printf("DLineA: Delay length too big.\n");
printf("Setting to maximum length of %ld.\n",length-1);
outPointer = inPoint + 1.0; // force delay to max_length
}
else if (lag < 0.1) {
printf("DLineA: Delays < 0.1 not possible with current structure.\n");
printf("Setting delay length to 0.1.\n");
outPointer = inPoint + 0.8999999999;
}
else
outPointer = inPoint - lag + 1.0; // outPoint chases inpoint
if (outPointer < 0)
outPointer += length; // modulo table length
outPoint = (long) outPointer; // Integer part of delay
alpha = 1.0 + outPoint - outPointer; // fractional part of delay
if (alpha == 0.0) { // exact integer delay
outPoint -= 1;
if (outPoint < 0) outPoint += length;
}
if (alpha<0.1) { // Hack to avoid pole/zero
outPoint += 1; // cancellation. Keeps allpass
if (outPoint >= length) outPoint -= length;
alpha += (MY_FLOAT) 1.0; // delay in range of .1 to 1.1
}
coeff = ((MY_FLOAT) 1.0 - alpha) /
((MY_FLOAT) 1.0 + alpha); // coefficient for all pass
}
MY_FLOAT DLineA :: tick(MY_FLOAT sample) // Take sample, yield sample
{
MY_FLOAT temp;
inputs[inPoint++] = sample; // Write input sample
if (inPoint == length) // Increment input pointer
inPoint -= length; // modulo length
temp = inputs[outPoint++]; // filter input
if (outPoint == length) // Increment output pointer
outPoint -= length; // modulo length
lastOutput = -coeff * lastOutput; // delayed output
lastOutput += lastIn + (coeff * temp); // input + delayed Input
lastIn = temp;
return lastOutput; // save output and return
}

172
src/DLineL.cpp Normal file
View File

@@ -0,0 +1,172 @@
/*******************************************/
/*
Linearly Interpolating Delay Line
Object by Perry R. Cook 1995-96.
Added methods by Julius Smith, 2000.
This one uses a delay line of maximum
length specified on creation, and
linearly interpolates fractional
length. It is designed to be more
efficient if the delay length is not
changed very often.
*/
/*******************************************/
#include "DLineL.h"
DLineL :: DLineL()
{
// Default max delay length set to 2047.
length = 2048;
inputs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT));
this->clear();
inPoint = 0;
outPoint = length >> 1;
currentDelay = outPoint;
}
DLineL :: DLineL(long max_length)
{
length = max_length;
inputs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT));
this->clear();
inPoint = 0;
outPoint = length >> 1;
currentDelay = outPoint;
}
DLineL :: ~DLineL()
{
free(inputs);
}
void DLineL :: clear()
{
long i;
for (i=0;i<length;i++) inputs[i] = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0;
}
void DLineL :: setDelay(MY_FLOAT lag)
{
MY_FLOAT outPointer;
if (lag > length-1) {
// delay is too big,
printf("DLineL: Delay length too big.\n");
printf("Setting to maximum length of %ld.\n",length-1);
// force delay to max_length
outPointer = inPoint + 1;
currentDelay = length - 1;
}
else {
// read chases write
outPointer = inPoint - lag;
currentDelay = lag;
}
while (outPointer<0)
// modulo maximum length
outPointer += length;
// integer part
outPoint = (long) outPointer;
// fractional part
alpha = outPointer - outPoint;
omAlpha = (MY_FLOAT) 1.0 - alpha;
}
MY_FLOAT DLineL :: delay(void)
{
return currentDelay;
}
MY_FLOAT DLineL :: energy(void)
{
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;
}
long DLineL :: currentInPoint(void)
{
return inPoint;
}
long DLineL :: currentOutPoint(void)
{
return outPoint;
}
MY_FLOAT DLineL :: contentsAt(int n)
{
int i = n;
if (i<0) i=0;
if (i>= length) i = length-1;
if (i != n) {
fprintf(stderr,
"DLineL: contentsAt(%d) overflows length %ld delay line\n",
n, length);
}
return inputs[i];
}
MY_FLOAT DLineL :: contentsAtNowMinus(int n)
{
/* "Now" is always where inPoint points which is not yet written. */
/* outPoint points to "now - delay". Thus, valid values for n are 1 to delay. */
int i = n;
if (i<1) i=1;
if (i>length) i = length;
if (i != n) {
fprintf(stderr,
"DLineL: contentsAtNowMinus(%d) overflows length %ld delay line\n"
"Clipped\n", n,length);
}
int ndx = inPoint-i;
if (ndx < 0) { /* Check for wraparound */
ndx += length;
if (ndx < 0 || ndx >= length)
fprintf(stderr,"DLineL: contentsAtNowMinus(): can't happen\n");
}
return inputs[ndx];
}
MY_FLOAT DLineL :: tick(MY_FLOAT sample)
{
inputs[inPoint++] = sample;
// Check for end condition
if (inPoint == length)
inPoint -= length;
// First 1/2 of interpolation
lastOutput = inputs[outPoint++] * omAlpha;
// Check for end condition
if (outPoint<length) {
// Second 1/2 of interpolation
lastOutput += inputs[outPoint] * alpha;
}
else { // if at end ...
// Second 1/2 of interpolation
lastOutput += inputs[0] * alpha;
outPoint -= length;
}
return lastOutput;
}

158
src/DLineN.cpp Normal file
View File

@@ -0,0 +1,158 @@
/*******************************************/
/*
Non-Interpolating Delay Line
Object by Perry R. Cook 1995-96.
Revised by Gary Scavone, 1999.
Added methods by Julius Smith, 2000.
This one uses either a delay line of
maximum length specified on creation
or a default length of 2047 samples.
A non-interpolating delay line is
typically used in non-time varying
(reverb) applications.
*/
/*******************************************/
#include "DLineN.h"
DLineN :: DLineN()
{
// Default max delay length set to 2047.
length = 2048;
inputs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT));
this->clear();
inPoint = 0;
outPoint = length >> 1;
currentDelay = outPoint;
}
DLineN :: DLineN(long max_length)
{
// Writing before reading allows delays from 0 to length-1.
// Thus, if we want to allow a delay of max_length, we need
// a delay-line of length = max_length+1.
length = max_length+1;
inputs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT));
this->clear();
inPoint = 0;
outPoint = length >> 1;
currentDelay = outPoint;
}
DLineN :: ~DLineN()
{
free(inputs);
}
void DLineN :: clear(void)
{
long i;
for (i=0;i<length;i++) inputs[i] = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0;
}
void DLineN :: setDelay(MY_FLOAT lag)
{
if (lag > length-1) {
// delay is too big
printf("DLineN: Delay length too big ... setting to maximum length of %ld.\n",length-1);
// force delay to max_length
outPoint = inPoint + 1;
currentDelay = length - 1;
}
else {
outPoint = inPoint - (long) lag; // read chases write
currentDelay = lag;
}
while (outPoint<0) outPoint += length; // modulo maximum length
}
MY_FLOAT DLineN :: delay(void)
{
return currentDelay;
}
MY_FLOAT DLineN :: energy(void)
{
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;
}
long DLineN :: currentInPoint(void)
{
return inPoint;
}
long DLineN :: currentOutPoint(void)
{
return outPoint;
}
MY_FLOAT DLineN :: contentsAt(int n)
{
int i = n;
if (i<0) i=0;
if (i>= length) i = length-1;
if (i != n) {
fprintf(stderr,
"DLineN: contentsAt(%d) overflows length %ld delay line\n",
n, length);
}
return inputs[i];
}
MY_FLOAT DLineN :: contentsAtNowMinus(int n)
{
/* "Now" is always where inPoint points which is not yet written. */
/* outPoint points to "now - delay". Thus, valid values for n are 1 to delay. */
int i = n;
if (i<1) i=1;
if (i>length) i = length;
if (i != n) {
fprintf(stderr,
"DLineN: contentsAtNowMinus(%d) overflows length %ld delay line\n"
"Clipped\n", n, length);
}
int ndx = inPoint-i;
if (ndx < 0) { /* Check for wraparound */
ndx += length;
if (ndx < 0 || ndx >= length)
fprintf(stderr,"DLineN: contentsAtNowMinus(): can't happen\n");
}
return inputs[ndx];
}
MY_FLOAT DLineN :: tick(MY_FLOAT sample)
{
inputs[inPoint++] = sample;
// Check for end condition
if (inPoint == length)
inPoint -= length;
// Read out next value
lastOutput = inputs[outPoint++];
if (outPoint>=length)
outPoint -= length;
return lastOutput;
}

183
src/DrumSynt.cpp Normal file
View File

@@ -0,0 +1,183 @@
/*******************************************/
/* Master Class for Drum Synthesizer */
/* by Perry R. Cook, 1995-96 */
/* */
/* This instrument contains a bunch of */
/* RawWvIn objects, run through a bunch */
/* of one-pole filters. All the */
/* corresponding rawwave files have been */
/* sampled at 22050 Hz. Thus, if the */
/* compile-time SRATE = 22050, then */
/* no interpolation is used. Otherwise, */
/* the rawwave data is appropriately */
/* interpolated for the current SRATE. */
/* You can specify the maximum Polyphony */
/* (maximum number of simultaneous voices)*/
/* in a #define in the .h file. */
/* */
/* Modified for RawWvIn class */
/* by Gary P. Scavone (4/99) */
/*******************************************/
#include "DrumSynt.h"
#include <string.h>
/* Not really General MIDI yet. Coming soon. */
unsigned char genMIDIMap[128] = { 0,0,0,0,0,0,0,0, // 0-7
0,0,0,0,0,0,0,0, // 8-15
0,0,0,0,0,0,0,0, // 16-23
0,0,0,0,0,0,0,0, // 24-31
0,0,0,0,1,0,2,0, // 32-39
2,3,6,3,6,4,7,4, // 40-47
5,8,5,0,0,0,10,0, // 48-55
9,0,0,0,0,0,0,0, // 56-63
0,0,0,0,0,0,0,0, // 64-71
0,0,0,0,0,0,0,0, // 72-79
0,0,0,0,0,0,0,0, // 80-87
0,0,0,0,0,0,0,0, // 88-95
0,0,0,0,0,0,0,0, // 96-103
0,0,0,0,0,0,0,0, // 104-111
0,0,0,0,0,0,0,0, // 112-119
0,0,0,0,0,0,0,0}; // 120-127
char waveNames[DRUM_NUMWAVES][16] = {
"dope.raw",
"bassdrum.raw",
"snardrum.raw",
"tomlowdr.raw",
"tommiddr.raw",
"tomhidrm.raw",
"hihatcym.raw",
"ridecymb.raw",
"crashcym.raw",
"cowbell1.raw",
"tambourn.raw"
};
DrumSynt :: DrumSynt() : Instrmnt()
{
int i;
for (i=0;i<DRUM_POLYPHONY;i++) {
filters[i] = new OnePole;
sounding[i] = -1;
}
/* This counts the number of sounding voices */
numSounding = 0;
/* Print warning about aliasing if SRATE < 22050 */
if (SRATE < 22050) {
printf("\nWarning: DrumSynt is designed for sampling rates of\n");
printf("22050 Hz or greater. You will likely encounter aliasing\n");
printf("at the current sampling rate of %.0f Hz.\n\n", SRATE);
}
}
DrumSynt :: ~DrumSynt()
{
int i;
for ( i=0; i<numSounding-1; i++ ) delete waves[i];
for ( i=0; i<DRUM_POLYPHONY; i++ ) delete filters[i];
}
void DrumSynt :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
int i, notDone;
int noteNum;
int vel;
char tempString[64];
RawWvIn *tempWv;
OnePole *tempFilt;
/* Yes I know, this is tres kludgey */
noteNum = (int) ((12*log(freq/220)/log(2.0)) + 57.01);
vel = (int) (amp * 127);
#if defined(_debug_)
printf("NoteOn: %s vel=%i\n",waveNames[genMIDIMap[noteNum]],vel);
#endif
notDone = -1;
for (i=0;i<DRUM_POLYPHONY;i++) { /* Check first to see */
if (sounding[i] == noteNum) notDone = i; /* if there's already */
} /* one like this sounding */
if (notDone<0) { /* If not, then */
if (numSounding == DRUM_POLYPHONY) { /* If we're already */
delete waves[0]; /* at max polyphony, */
filters[0]->clear(); /* then */
tempWv = waves[0];
tempFilt = filters[0];
for (i=0;i<DRUM_POLYPHONY-1;i++) { /* preempt oldest */
waves[i] = waves[i+1]; /* voice and */
filters[i] = filters[i+1]; /* ripple all down */
}
waves[DRUM_POLYPHONY-1] = tempWv;
filters[DRUM_POLYPHONY-1] = tempFilt;
} else {
numSounding += 1; /* otherwise just add one */
}
sounding[numSounding-1] = noteNum; /* allocate new wave */
// Concatenate the STK RAWWAVE_PATH to the rawwave file
strcpy(tempString, RAWWAVE_PATH);
strcat(tempString,"rawwaves/");
strcat(tempString,waveNames[genMIDIMap[noteNum]]);
waves[numSounding-1] = new RawWvIn(tempString, "oneshot");
if (SRATE != 22050) {
waves[numSounding-1]->setRate((MY_FLOAT) (22050.0/SRATE));
}
filters[numSounding-1]->setPole((MY_FLOAT) 0.999 - ((MY_FLOAT) vel * NORM_7 * 0.6));
filters[numSounding-1]->setGain(vel / (MY_FLOAT) 128.0);
}
else {
waves[notDone]->reset();
filters[notDone]->setPole((MY_FLOAT) 0.999 - ((MY_FLOAT) vel * NORM_7 * 0.6));
filters[notDone]->setGain(vel / (MY_FLOAT) 128.0);
}
#if defined(_debug_)
printf("Number Sounding = %i\n",numSounding);
for (i=0;i<numSounding;i++) printf(" %i ",sounding[i]);
printf("\n");
#endif
}
void DrumSynt :: noteOff(MY_FLOAT amp)
{ /* Set all sounding wave filter gains low */
int i = 0;
while(i<numSounding) {
filters[i]->setGain(amp*0.01);
i++;
}
}
MY_FLOAT DrumSynt :: tick()
{
int j, i = 0;
MY_FLOAT output = 0.0;
OnePole *tempFilt;
while (i < numSounding) {
output += filters[i]->tick(waves[i]->lastOut());
if (waves[i]->informTick() == 1) {
delete waves[i];
tempFilt = filters[i];
for (j=i;j<numSounding-1;j++) {
sounding[j] = sounding[j+1];
waves[j] = waves[j+1];
filters[j] = filters[j+1];
}
filters[j] = tempFilt;
filters[j]->clear();
sounding[j] = -1;
numSounding -= 1;
i -= 1;
}
i++;
}
return output;
}

122
src/Envelope.cpp Normal file
View File

@@ -0,0 +1,122 @@
/*******************************************/
/* Envelope Class, Perry R. Cook, 1995-96 */
/* */
/* This is the base class for envelopes. */
/* This one is capable of ramping state */
/* from where it is to a target value by */
/* a rate. It also responds to simple */
/* KeyOn and KeyOff messages, ramping to */
/* 1.0 on keyon and to 0.0 on keyoff. */
/* There are two tick (update value) */
/* methods, one returns the value, and */
/* other returns 0 if the envelope is at */
/* the target value (the state bit). */
/*******************************************/
#include "Envelope.h"
Envelope :: Envelope() : Object()
{
target = (MY_FLOAT) 0.0;
value = (MY_FLOAT) 0.0;
rate = (MY_FLOAT) 0.001;
state = 0;
}
Envelope :: ~Envelope()
{
}
void Envelope :: keyOn()
{
target = (MY_FLOAT) 1.0;
if (value != target) state = 1;
}
void Envelope :: keyOff()
{
target = (MY_FLOAT) 0.0;
if (value != target) state = 1;
}
void Envelope :: setRate(MY_FLOAT aRate)
{
if (aRate < 0.0) {
printf("negative rates not allowed!!, correcting\n");
rate = -aRate;
}
else rate = aRate;
}
void Envelope :: setTime(MY_FLOAT aTime)
{
if (aTime < 0.0) {
printf("negative times not allowed!!, correcting\n");
rate = ONE_OVER_SRATE / -aTime ;
}
else rate = ONE_OVER_SRATE / aTime ;
}
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;
}
MY_FLOAT Envelope :: tick()
{
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;
}
int Envelope :: informTick()
{
this->tick();
return state;
}
MY_FLOAT Envelope :: lastOut()
{
return value;
}
/************ Test Main ************************/
/*
void main()
{
long i;
Envelope test;
test.setRate(0.15);
test.keyOn();
for (i=0;i<10;i++) printf("%lf\n",test.tick());
test.setRate(0.1);
test.setTarget(0.5);
while (test.informTick()) printf("%lf\n",test.lastOut());
test.setRate(0.05);
test.keyOff();
while(test.informTick()) printf("%lf\n",test.lastOut());
}
*/

67
src/FIR.cpp Normal file
View File

@@ -0,0 +1,67 @@
/********************************************/
/*
General Finite-Impulse-Response (FIR)
Digital Filter Class
by Julius Smith, 1997
*/
/********************************************/
#include "FIR.h"
FIR :: FIR(int theLength) : Object()
{
length = theLength;
coeffs = (MY_FLOAT *) malloc(length * sizeof(MY_FLOAT));
pastInputs = (MY_FLOAT *) calloc(2*length, sizeof(MY_FLOAT));
piOffset = length;
}
FIR :: ~FIR()
{
free(pastInputs);
free(coeffs);
}
void FIR :: clear()
{
int i;
for (i=0; i < 2*length; i++) {
pastInputs[i] = 0;
}
piOffset = length;
}
void FIR :: setCoeffs(MY_FLOAT *theCoeffs)
{
int i;
for (i=0; i < length; i++) {
coeffs[i] = theCoeffs[i];
}
}
MY_FLOAT FIR :: tick(MY_FLOAT input) {
int i;
lastOutput = input*coeffs[0];
for (i=1; i<length; i++)
lastOutput += coeffs[i] * pastInputs[piOffset-i]; // sample 0 unused
pastInputs[piOffset++] = input;
if (piOffset >= 2*length) { // sample 2*length-1 unused
piOffset = length;
for (i=0; i<length; i++)
pastInputs[i] = pastInputs[length+i];
}
return lastOutput;
}
MY_FLOAT FIR :: getDelay(MY_FLOAT freq)
{
return delay;
}
int FIR :: getLength(void)
{
return length;
}

56
src/FM4Alg3.cpp Normal file
View File

@@ -0,0 +1,56 @@
/******************************************/
/* Algorithm 3 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/* */
/* Alg 3 is : 4--\ */
/* 3-->2-- + -->1-->Out */
/* */
/* Controls: control1 = total mod index */
/* control2 = crossfade of two */
/* modulators */
/* control3 = LFO speed */
/* modWheel = LFO amount */
/* */
/******************************************/
#include "FM4Alg3.h"
FM4Alg3 :: FM4Alg3() : FM4Op()
{
/* We still don't make the waves here yet, because */
/* we still don't know what they will be. */
}
FM4Alg3 :: ~FM4Alg3()
{
}
MY_FLOAT FM4Alg3 :: tick()
{
MY_FLOAT temp;
temp = vibWave->tick() * modDepth * (MY_FLOAT) 0.2;
waves[0]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp) * ratios[0]);
waves[1]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp) * ratios[1]);
waves[2]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp) * ratios[2]);
waves[3]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp) * ratios[3]);
temp = gains[2] * adsr[2]->tick() * waves[2]->tick();
waves[1]->addPhaseOffset(temp);
waves[3]->addPhaseOffset(twozero->lastOut());
temp = ((MY_FLOAT) 1.0 - (control2 * (MY_FLOAT) 0.5)) *
gains[3] * adsr[3]->tick() * waves[3]->tick();
twozero->tick(temp);
temp += control2 * (MY_FLOAT) 0.5 * gains[1] * adsr[1]->tick() * waves[1]->tick();
temp = temp * control1;
waves[0]->addPhaseOffset(temp);
temp = gains[0] * adsr[0]->tick() * waves[0]->tick();
lastOutput = temp * (MY_FLOAT) 0.5;
return lastOutput;
}

53
src/FM4Alg4.cpp Normal file
View File

@@ -0,0 +1,53 @@
/******************************************/
/* Algorithm 4 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/* */
/* Alg 4 is : 4->3--\ */
/* 2-- + -->1-->Out */
/* */
/* Controls: control1 = total mod index */
/* control2 = crossfade of two */
/* modulators */
/* control3 = LFO speed */
/* modWheel = LFO amount */
/* */
/******************************************/
#include "FM4Alg4.h"
FM4Alg4 :: FM4Alg4() : FM4Op()
{
/* We still don't make the waves here yet, because */
/* we still don't know what they will be. */
}
FM4Alg4 :: ~FM4Alg4()
{
}
MY_FLOAT FM4Alg4 :: tick()
{
MY_FLOAT temp;
temp = vibWave->tick() * modDepth * (MY_FLOAT) 0.2;
waves[0]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp) * ratios[0]);
waves[1]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp) * ratios[1]);
waves[2]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp) * ratios[2]);
waves[3]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp) * ratios[3]);
waves[3]->addPhaseOffset(twozero->lastOut());
temp = gains[3] * adsr[3]->tick() * waves[3]->tick();
twozero->tick(temp);
waves[2]->addPhaseOffset(temp);
temp = ((MY_FLOAT) 1.0 - (control2 * (MY_FLOAT) 0.5)) *
gains[2] * adsr[2]->tick() * waves[2]->tick();
temp += control2 * (MY_FLOAT) 0.5 * gains[1] * adsr[1]->tick() * waves[1]->tick();
temp = temp * control1;
waves[0]->addPhaseOffset(temp);
temp = gains[0] * adsr[0]->tick() * waves[0]->tick();
lastOutput = temp * (MY_FLOAT) 0.5;
return lastOutput;
}

53
src/FM4Alg5.cpp Normal file
View File

@@ -0,0 +1,53 @@
/******************************************/
/* Algorithm 5 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/* This connection topology is 2 simple */
/* FM Pairs summed together, like: */
/* */
/* Alg 5 is : 4->3--\ */
/* + --> Out */
/* 2->1--/ */
/* */
/* Controls: control1 = mod index 1 */
/* control2 = crossfade of two */
/* outputs */
/* control3 = LFO speed */
/* modWheel = LFO amount */
/* */
/******************************************/
#include "FM4Alg5.h"
FM4Alg5 :: FM4Alg5() : FM4Op()
{
/* We still don't make the waves here yet, because */
/* we still don't know what they will be. */
}
FM4Alg5 :: ~FM4Alg5()
{
}
MY_FLOAT FM4Alg5 :: tick()
{
MY_FLOAT temp,temp2;
temp = gains[1] * adsr[1]->tick() * waves[1]->tick();
temp = temp * control1;
waves[0]->addPhaseOffset(temp);
waves[3]->addPhaseOffset(twozero->lastOut());
temp = gains[3] * adsr[3]->tick() * waves[3]->tick();
twozero->tick(temp);
waves[2]->addPhaseOffset(temp);
temp = ((MY_FLOAT) 1.0 - (control2 * (MY_FLOAT) 0.5)) *
gains[0] * adsr[0]->tick() * waves[0]->tick();
temp += control2 * (MY_FLOAT) 0.5 * gains[2] * adsr[2]->tick() * waves[2]->tick();
temp2 = vibWave->tick() * modDepth; /* Calculate amplitude mod */
temp = temp * ((MY_FLOAT) 1.0 + temp2); /* and apply it to output */
lastOutput = temp * (MY_FLOAT) 0.5;
return lastOutput;
}

58
src/FM4Alg6.cpp Normal file
View File

@@ -0,0 +1,58 @@
/******************************************/
/* Algorithm 6 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/* This connection topology is three */
/* Carriers and a common Modulator */
/* */
/* /->1 -\ */
/* 4-|-->2 - +-> Out */
/* \->3 -/ */
/* */
/* Controls: control1 = vowel */
/* control2 = spectral tilt */
/* control3 = LFO speed */
/* modWheel = LFO amount */
/* */
/******************************************/
#include "FM4Alg6.h"
FM4Alg6 :: FM4Alg6() : FM4Op()
{
/* We still don't make the waves here yet, because */
/* we still don't know what they will be. */
}
FM4Alg6 :: ~FM4Alg6()
{
}
MY_FLOAT FM4Alg6 :: tick()
{
MY_FLOAT temp,temp2;
temp = gains[3] * adsr[3]->tick() * waves[3]->tick();
temp2 = vibWave->tick() * modDepth * (MY_FLOAT) 0.1; /* Calculate frequency mod */
waves[0]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp2) * ratios[0]);
waves[1]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp2) * ratios[1]);
waves[2]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp2) * ratios[2]);
waves[3]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp2) * ratios[3]);
waves[0]->addPhaseOffset(temp * mods[0]);
waves[1]->addPhaseOffset(temp * mods[1]);
waves[2]->addPhaseOffset(temp * mods[2]);
waves[3]->addPhaseOffset(twozero->lastOut());
twozero->tick(temp);
temp = gains[0] * tilt[0] * adsr[0]->tick() * waves[0]->tick();
temp += gains[1] * tilt[1] * adsr[1]->tick() * waves[1]->tick();
temp += gains[2] * tilt[2] * adsr[2]->tick() * waves[2]->tick();
return temp * (MY_FLOAT) 0.33;
}
void FM4Alg6 :: controlChange(int number, MY_FLOAT value)
{
}

47
src/FM4Alg8.cpp Normal file
View File

@@ -0,0 +1,47 @@
/******************************************/
/* Algorithm 8 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/* This connection topology is simple */
/* Additive Synthesis, like: */
/* */
/* 1 --. */
/* 2 -\| */
/* +-> Out */
/* 3 -/| */
/* 4 -- */
/* */
/* Controls: control1 = op4 (fb) gain */
/* control2 = op3 gain */
/* control3 = LFO speed */
/* modWheel = LFO amount */
/* */
/******************************************/
#include "FM4Alg8.h"
FM4Alg8 :: FM4Alg8() : FM4Op()
{
/* We still don't make the waves here yet, because */
/* we still don't know what they will be. */
}
FM4Alg8 :: ~FM4Alg8()
{
}
MY_FLOAT FM4Alg8 :: tick()
{
MY_FLOAT temp;
waves[3]->addPhaseOffset(twozero->lastOut());
temp = control1 * (MY_FLOAT) 2.0 * gains[3] * adsr[3]->tick() * waves[3]->tick();
twozero->tick(temp);
temp += control2 * (MY_FLOAT) 2.0 * gains[2] * adsr[2]->tick() * waves[2]->tick();
temp += gains[1] * adsr[1]->tick() * waves[1]->tick();
temp += gains[0] * adsr[0]->tick() * waves[0]->tick();
lastOutput = temp * (MY_FLOAT) 0.125;
return lastOutput;
}

179
src/FM4Op.cpp Normal file
View File

@@ -0,0 +1,179 @@
/*******************************************/
/* Master Class for 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/* This instrument contains 4 waves, */
/* 4 adsr, and various state vars. */
/* */
/* The basic Chowning/Stanford FM patent */
/* expired April 1995, but there exist */
/* follow-on patents, mostly assigned to */
/* Yamaha. If you are of the type who */
/* should worry about this (making money) */
/* worry away. */
/* */
/*******************************************/
#include "FM4Op.h"
#include "SKINI11.msg"
FM4Op :: FM4Op()
{
int i;
MY_FLOAT temp;
MY_FLOAT tempCoeffs[2] = {(MY_FLOAT) 0.0, (MY_FLOAT) -1.0};
adsr[0] = new ADSR;
adsr[1] = new ADSR;
adsr[2] = new ADSR;
adsr[3] = new ADSR;
twozero = new TwoZero;
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibWave = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping");
vibWave->setFreq((MY_FLOAT) 6.0); /* should make this random?? */
modDepth = (MY_FLOAT) 0.0;
/* We don't make the waves here yet, because */
/* we don't know what they will be. */
baseFreq = (MY_FLOAT) 440.0;
ratios[0] = (MY_FLOAT) 1.0;
ratios[1] = (MY_FLOAT) 1.0;
ratios[2] = (MY_FLOAT) 1.0;
ratios[3] = (MY_FLOAT) 1.0;
gains[0] = (MY_FLOAT) 1.0;
gains[1] = (MY_FLOAT) 1.0;
gains[2] = (MY_FLOAT) 1.0;
gains[3] = (MY_FLOAT) 1.0;
twozero->setZeroCoeffs(tempCoeffs);
twozero->setGain((MY_FLOAT) 0.0);
control1 = (MY_FLOAT) 1.0;
control2 = (MY_FLOAT) 1.0;
temp = (MY_FLOAT) 1.0;
for (i=99;i>=0;i--) {
__FM4Op_gains[i] = temp;
temp *= (MY_FLOAT) 0.933033;
}
temp = (MY_FLOAT) 1.0;
for (i=15;i>=0;i--) {
__FM4Op_susLevels[i] = temp;
temp *= (MY_FLOAT) 0.707101;
}
temp = (MY_FLOAT) 8.498186;
for (i=0;i<32;i++) {
__FM4Op_attTimes[i] = temp;
temp *= (MY_FLOAT) 0.707101;
}
}
FM4Op :: ~FM4Op()
{
delete adsr[0];
delete adsr[1];
delete adsr[2];
delete adsr[3];
delete waves[0];
delete waves[1];
delete waves[2];
delete waves[3];
delete vibWave;
delete twozero;
}
void FM4Op :: loadWaves(char* wave1, char* wave2, char* wave3, char* wave4)
{
waves[0] = new RawWvIn(wave1,"looping");
waves[1] = new RawWvIn(wave2,"looping");
waves[2] = new RawWvIn(wave3,"looping");
waves[3] = new RawWvIn(wave4,"looping");
}
void FM4Op :: setFreq(MY_FLOAT frequency)
{
baseFreq = frequency;
waves[0]->setFreq(baseFreq * ratios[0]);
waves[1]->setFreq(baseFreq * ratios[1]);
waves[2]->setFreq(baseFreq * ratios[2]);
waves[3]->setFreq(baseFreq * ratios[3]);
}
void FM4Op :: setRatio(int whichOne, MY_FLOAT ratio)
{
ratios[whichOne] = ratio;
if (ratio>0.0)
waves[whichOne]->setFreq(baseFreq * ratio);
else
waves[whichOne]->setFreq(ratio);
}
void FM4Op :: setGain(int whichOne, MY_FLOAT gain)
{
gains[whichOne]=gain;
}
void FM4Op :: keyOn()
{
adsr[0]->keyOn();
adsr[1]->keyOn();
adsr[2]->keyOn();
adsr[3]->keyOn();
}
void FM4Op :: keyOff()
{
adsr[0]->keyOff();
adsr[1]->keyOff();
adsr[2]->keyOff();
adsr[3]->keyOff();
}
void FM4Op :: noteOff(MY_FLOAT amp)
{
this->keyOff();
#if defined(_debug_)
printf("FM4Op : NoteOff: Amp=%lf\n",amp);
#endif
}
void FM4Op :: setModulationSpeed(MY_FLOAT mSpeed)
{
vibWave->setFreq(mSpeed);
}
void FM4Op :: setModulationDepth(MY_FLOAT mDepth)
{
modDepth = mDepth;
}
void FM4Op :: setControl1(MY_FLOAT cVal)
{
control1 = cVal * (MY_FLOAT) 2.0;
}
void FM4Op :: setControl2(MY_FLOAT cVal)
{
control2 = cVal * (MY_FLOAT) 2.0;
}
void FM4Op :: controlChange(int number, MY_FLOAT value)
{
#if defined(_debug_)
printf("FM4Op : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_Breath_) /* Control Change 2 */
this->setControl1(value * NORM_7);
else if (number == __SK_FootControl_) /* Control Change 4 */
this->setControl2(value * NORM_7);
else if (number == __SK_ModFrequency_) /* Control Change 11 */
this->setModulationSpeed(value * NORM_7 * (MY_FLOAT) 12.0); /* 0 to 12 Hz */
else if (number == __SK_ModWheel_) /* Control Change 1 */
this->setModulationDepth(value * NORM_7);
else if (number == __SK_AfterTouch_Cont_) {
//adsr[0]->setTarget(value * NORM_7);
adsr[1]->setTarget(value * NORM_7);
//adsr[2]->setTarget(value * NORM_7);
adsr[3]->setTarget(value * NORM_7);
}
else {
printf("FM4Op : Undefined Control Number!!\n");
}
}

136
src/FMVoices.cpp Normal file
View File

@@ -0,0 +1,136 @@
/******************************************/
/* Singing Voice Synthesis Subclass */
/* of Algorithm 6 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1996 */
/******************************************/
#include "FMVoices.h"
#include "SKINI11.msg"
FMVoices :: FMVoices() : FM4Alg6()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file1[128];
char file2[128];
char file3[128];
char file4[128];
strcpy(file1, RAWWAVE_PATH);
strcpy(file2, RAWWAVE_PATH);
strcpy(file3, RAWWAVE_PATH);
strcpy(file4, RAWWAVE_PATH);
this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"),
strcat(file2,"rawwaves/sinewave.raw"),
strcat(file3,"rawwaves/sinewave.raw"),
strcat(file4,"rawwaves/fwavblnk.raw"));
this->setRatio(0,(MY_FLOAT) 2.00);
this->setRatio(1,(MY_FLOAT) 4.00);
this->setRatio(2,(MY_FLOAT) 12.0);
this->setRatio(3,(MY_FLOAT) 1.00);
gains[3] = __FM4Op_gains[80];
adsr[0]->setAllTimes((MY_FLOAT) 0.050,(MY_FLOAT) 0.050,
__FM4Op_susLevels[15],(MY_FLOAT) 0.050);
adsr[1]->setAllTimes((MY_FLOAT) 0.050,(MY_FLOAT) 0.050,
__FM4Op_susLevels[15],(MY_FLOAT) 0.050);
adsr[2]->setAllTimes((MY_FLOAT) 0.050,(MY_FLOAT) 0.050,
__FM4Op_susLevels[15],(MY_FLOAT) 0.050);
adsr[3]->setAllTimes((MY_FLOAT) 0.010,(MY_FLOAT) 0.010,
__FM4Op_susLevels[15],(MY_FLOAT) 0.500);
twozero->setGain((MY_FLOAT) 0.0);
modDepth = (MY_FLOAT) 0.005;
currentVowel = 0;
tilt[0] = (MY_FLOAT) 1.0;
tilt[1] = (MY_FLOAT) 0.5;
tilt[2] = (MY_FLOAT) 0.2;
mods[0] = (MY_FLOAT) 1.0;
mods[1] = (MY_FLOAT) 1.1;
mods[2] = (MY_FLOAT) 1.1;
baseFreq = (MY_FLOAT) 110.0;
this->setFreq((MY_FLOAT) 110.0);
}
/* #include "phonTabl.h" */
extern double phonGains[32][2];
extern double phonParams[32][4][3];
extern char phonemes[32][4];
void FMVoices :: setFreq(MY_FLOAT frequency)
{
MY_FLOAT temp, temp2 = 0.0;
int tempi, tempi2 = 0;
if (currentVowel < 32) {
tempi2 = currentVowel;
temp2 = (MY_FLOAT) 0.9;
}
else if (currentVowel < 64) {
tempi2 = currentVowel - 32;
temp2 = (MY_FLOAT) 1.0;
}
else if (currentVowel < 96) {
tempi2 = currentVowel - 64;
temp2 = (MY_FLOAT) 1.1;
}
else if (currentVowel <= 128) {
tempi2 = currentVowel - 96;
temp2 = (MY_FLOAT) 1.2;
}
baseFreq = frequency;
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][0][0] / baseFreq) + (MY_FLOAT) 0.5;
tempi = (int) temp;
this->setRatio(0,(MY_FLOAT) tempi);
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][1][0] / baseFreq) + (MY_FLOAT) 0.5;
tempi = (int) temp;
this->setRatio(1,(MY_FLOAT) tempi);
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][2][0] / baseFreq) + (MY_FLOAT) 0.5;
tempi = (int) temp;
this->setRatio(2,(MY_FLOAT) tempi);
gains[0] = (MY_FLOAT) 1.0; // pow(10.0,phonParams[tempi2][0][2] * 0.05);
gains[1] = (MY_FLOAT) 1.0; // pow(10.0,phonParams[tempi2][1][2] * 0.05);
gains[2] = (MY_FLOAT) 1.0; // pow(10.0,phonParams[tempi2][2][2] * 0.05);
}
void FMVoices :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
this->setFreq(freq);
tilt[0] = amp;
tilt[1] = amp * amp;
tilt[2] = amp * amp * amp;
this->keyOn();
#if defined(_debug_)
printf("FMVoices : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}
void FMVoices :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT temp;
int tempi;
#if defined(_debug_)
printf("FM4Op : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_Breath_)
gains[3] = __FM4Op_gains[(int) (value * 0.78125)];
else if (number == __SK_FootControl_) {
tempi = (int) value;
currentVowel = tempi;
this->setFreq(baseFreq);
}
else if (number == __SK_ModFrequency_)
this->setModulationSpeed(value * NORM_7 * (MY_FLOAT) 12.0); /* 0 to 12 Hz */
else if (number == __SK_ModWheel_)
this->setModulationDepth(value * NORM_7);
else if (number == __SK_AfterTouch_Cont_) {
temp = value * NORM_7;
tilt[0] = temp;
tilt[1] = temp * temp;
tilt[2] = temp * temp * temp;
}
else {
printf("FM4Op : Undefined Control Number!!\n");
}
}

24
src/Filter.cpp Normal file
View File

@@ -0,0 +1,24 @@
/*******************************************/
/* Filter Class, by Perry R. Cook, 1995-96*/
/* This is the base class for all filters.*/
/* To me, most anything is a filter, but */
/* I'll be a little less general here, and*/
/* define a filter as something which has */
/* input(s), output(s), and gain. */
/*******************************************/
#include "Filter.h"
Filter :: Filter() : Object()
{
}
Filter :: ~Filter()
{
}
MY_FLOAT Filter :: lastOut()
{
return lastOutput;
}

177
src/Flute.cpp Normal file
View File

@@ -0,0 +1,177 @@
/******************************************/
/* WaveGuide Flute ala Karjalainen, */
/* Smith, Waryznyk, etc. */
/* with polynomial Jet ala Cook */
/* by Perry Cook, 1995-96 */
/* */
/* This is a waveguide model, and thus */
/* relates to various Stanford Univ. */
/* and possibly Yamaha and other patents.*/
/* */
/* Controls: CONTROL1 = jetDelay */
/* CONTROL2 = noiseGain */
/* CONTROL3 = vibFreq */
/* MOD_WHEEL= vibAmt */
/******************************************/
#include "Flute.h"
#include "SKINI11.msg"
Flute :: Flute(MY_FLOAT lowestFreq)
{
long length;
length = (long) (SRATE / lowestFreq + 1);
boreDelay = new DLineL(length);
length >>= 1;
jetDelay = new DLineL(length);
jetTable = new JetTabl;
filter = new OnePole;
dcBlock = new DCBlock;
noise = new Noise;
adsr = new ADSR;
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibr = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping");
this->clear();
boreDelay->setDelay((MY_FLOAT) 100.0);
jetDelay->setDelay((MY_FLOAT) 49.0);
filter->setPole((MY_FLOAT) 0.7 - ((MY_FLOAT) 0.1 * (MY_FLOAT) 22050.0 / SRATE));
filter->setGain((MY_FLOAT) -1.0);
vibr->setFreq((MY_FLOAT) 5.925);
adsr->setAllTimes((MY_FLOAT) 0.005, (MY_FLOAT) 0.01, (MY_FLOAT) 0.8, (MY_FLOAT) 0.010);
endRefl = (MY_FLOAT) 0.5;
jetRefl = (MY_FLOAT) 0.5;
noiseGain = (MY_FLOAT) 0.15; /* Breath pressure random component */
vibrGain = (MY_FLOAT) 0.05; /* breath periodic vibrato component */
jetRatio = (MY_FLOAT) 0.32;
maxPressure = (MY_FLOAT) 0.0;
}
Flute :: ~Flute()
{
delete jetDelay;
delete boreDelay;
delete jetTable;
delete filter;
delete dcBlock;
delete noise;
delete adsr;
delete vibr;
}
void Flute :: clear()
{
jetDelay->clear();
boreDelay->clear();
filter->clear();
dcBlock->clear();
}
void Flute :: setFreq(MY_FLOAT frequency)
{
MY_FLOAT temp;
lastFreq = frequency * (MY_FLOAT) 0.66666; /* we're overblowing here */
temp = SRATE / lastFreq - (MY_FLOAT) 2.0; /* Length - approx. filter delay */
boreDelay->setDelay(temp); /* Length of bore tube */
jetDelay->setDelay(temp * jetRatio); /* jet delay shorter */
}
void Flute :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
adsr->setAttackRate(rate);
maxPressure = amplitude / (MY_FLOAT) 0.8;
adsr->keyOn();
}
void Flute :: stopBlowing(MY_FLOAT rate)
{
adsr->setReleaseRate(rate);
adsr->keyOff();
}
void Flute :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
this->setFreq(freq);
this->startBlowing((MY_FLOAT) 1.1 + (amp * (MY_FLOAT) 0.20),amp * (MY_FLOAT) 0.02);
outputGain = amp + (MY_FLOAT) 0.001;
#if defined(_debug_)
printf("Flute : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}
void Flute :: noteOff(MY_FLOAT amp)
{
this->stopBlowing(amp * (MY_FLOAT) 0.02);
#if defined(_debug_)
printf("Flute : NoteOff: Amp=%lf\n",amp);
#endif
}
void Flute :: setJetRefl(MY_FLOAT refl)
{
jetRefl = refl;
}
void Flute :: setEndRefl(MY_FLOAT refl)
{
endRefl = refl;
}
void Flute :: setJetDelay(MY_FLOAT aRatio)
{
MY_FLOAT temp;
temp = SRATE / lastFreq - (MY_FLOAT) 2.0; /* Length - approx. filter delay */
jetRatio = aRatio;
jetDelay->setDelay(temp * aRatio); /* Scaled by ratio */
}
MY_FLOAT Flute :: tick()
{
MY_FLOAT temp;
MY_FLOAT pressureDiff;
MY_FLOAT randPressure;
MY_FLOAT breathPressure;
breathPressure = maxPressure * adsr->tick(); /* Breath Pressure */
randPressure = noiseGain * noise->tick(); /* Random Deviation */
randPressure += vibrGain * vibr->tick(); /* + breath vibrato */
randPressure *= breathPressure; /* All scaled by Breath Pressure */
temp = filter->tick(boreDelay->lastOut());
temp = dcBlock->tick(temp); /* Block DC on reflection */
pressureDiff = breathPressure + randPressure - /* Breath Pressure */
(jetRefl * temp); /* - reflected */
pressureDiff = jetDelay->tick(pressureDiff); /* Jet Delay Line */
pressureDiff = jetTable->lookup(pressureDiff) /* Non-Lin Jet + reflected */
+ (endRefl * temp);
lastOutput = (MY_FLOAT) 0.3 * boreDelay->tick(pressureDiff); /* Bore Delay and "bell" filter */
lastOutput *= outputGain;
return lastOutput;
}
void Flute :: controlChange(int number, MY_FLOAT value)
{
#if defined(_debug_)
printf("Flute : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_JetDelay_)
this->setJetDelay((MY_FLOAT) 0.08 + ((MY_FLOAT) 0.48 * value * NORM_7));
else if (number == __SK_NoiseLevel_)
noiseGain = (value * NORM_7 * (MY_FLOAT) 0.4);
else if (number == __SK_ModFrequency_)
vibr->setFreq((value * NORM_7 * (MY_FLOAT) 12.0));
else if (number == __SK_ModWheel_)
vibrGain = (value * NORM_7 * (MY_FLOAT) 0.4);
else if (number == __SK_AfterTouch_Cont_)
adsr->setTarget(value * NORM_7);
else {
printf("Flute : Undefined Control Number!!\n");
}
}

139
src/FormSwep.cpp Normal file
View File

@@ -0,0 +1,139 @@
/*******************************************/
/* Sweepable Formant (2-pole) */
/* Filter Class, by Perry R. Cook, 1995-96*/
/* See books on filters to understand */
/* more about how this works. This drives*/
/* to a target at speed set by rate. */
/*******************************************/
#include "FormSwep.h"
FormSwep :: FormSwep() : Filter()
{
outputs = (MY_FLOAT *) malloc(2 * sizeof(MY_FLOAT));
poleCoeffs[0] = (MY_FLOAT) 0.0;
poleCoeffs[1] = (MY_FLOAT) 0.0;
gain = (MY_FLOAT) 1.0;
freq = (MY_FLOAT) 0.0;
reson = (MY_FLOAT) 0.0;
currentGain = (MY_FLOAT) 1.0;
currentFreq = (MY_FLOAT) 0.0;
currentReson = (MY_FLOAT) 0.0;
targetGain = (MY_FLOAT) 1.0;
targetFreq = (MY_FLOAT) 0.0;
targetReson = (MY_FLOAT) 0.0;
deltaGain = (MY_FLOAT) 0.0;
deltaFreq = (MY_FLOAT) 0.0;
deltaReson = (MY_FLOAT) 0.0;
sweepState = (MY_FLOAT) 0.0;
sweepRate = (MY_FLOAT) 0.002;
dirty = 0;
this->clear();
}
FormSwep :: ~FormSwep()
{
free(outputs);
}
void FormSwep :: clear()
{
outputs[0] = (MY_FLOAT) 0.0;
outputs[1] = (MY_FLOAT) 0.0;
}
void FormSwep :: setPoleCoeffs(MY_FLOAT *coeffs)
{
dirty = 0;
poleCoeffs[0] = coeffs[0];
poleCoeffs[1] = coeffs[1];
}
void FormSwep :: setFreqAndReson(MY_FLOAT aFreq, MY_FLOAT aReson)
{
dirty = 0;
reson = aReson;
freq = aFreq;
currentReson = aReson;
currentFreq = aFreq;
poleCoeffs[1] = - (reson * reson);
poleCoeffs[0] = (MY_FLOAT) 2.0 * reson * (MY_FLOAT) cos(TWO_PI * freq / SRATE);
}
void FormSwep :: setStates(MY_FLOAT aFreq, MY_FLOAT aReson, MY_FLOAT aGain)
{
dirty = 0;
freq = aFreq;
reson = aReson;
gain = aGain;
targetFreq = aFreq;
targetReson = aReson;
targetGain = aGain;
currentFreq = aFreq;
currentReson = aReson;
currentGain = aGain;
}
void FormSwep :: setTargets(MY_FLOAT aFreq, MY_FLOAT aReson, MY_FLOAT aGain)
{
dirty = 1;
targetFreq = aFreq;
targetReson = aReson;
targetGain = aGain;
deltaFreq = aFreq - currentFreq;
deltaReson = aReson - currentReson;
deltaGain = aGain - currentGain;
sweepState = (MY_FLOAT) 0.0;
}
void FormSwep :: setSweepRate(MY_FLOAT aRate)
{
sweepRate = aRate;
}
void FormSwep :: setSweepTime(MY_FLOAT aTime)
{
sweepRate = ONE_OVER_SRATE / aTime;
}
void FormSwep :: setGain(MY_FLOAT aValue)
{
gain = aValue;
}
MY_FLOAT FormSwep :: tick(MY_FLOAT sample) // Perform Filter Operation
{
MY_FLOAT temp;
if (dirty) {
sweepState += sweepRate;
if (sweepState>= 1.0) {
sweepState = (MY_FLOAT) 1.0;
dirty = 0;
currentReson = targetReson;
reson = targetReson;
currentFreq = targetFreq;
freq = targetFreq;
currentGain = targetGain;
gain = targetGain;
}
else {
currentReson = reson + (deltaReson * sweepState);
currentFreq = freq + (deltaFreq * sweepState);
currentGain = gain + (deltaGain * sweepState);
}
poleCoeffs[1] = - (currentReson * currentReson);
poleCoeffs[0] = (MY_FLOAT) 2.0 * currentReson *
(MY_FLOAT) cos(TWO_PI * currentFreq / SRATE);
}
temp = currentGain * sample;
temp += poleCoeffs[0] * outputs[0];
temp += poleCoeffs[1] * outputs[1];
outputs[1] = outputs[0];
outputs[0] = temp;
lastOutput = outputs[0];
return lastOutput;
}

68
src/HeavyMtl.cpp Normal file
View File

@@ -0,0 +1,68 @@
/******************************************/
/* Heavy Metal Synth Subclass */
/* of Algorithm 3 (TX81Z) Subclass of */
/* 3 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/******************************************/
#include "HeavyMtl.h"
HeavyMtl :: HeavyMtl() : FM4Alg3()
{
char file1[128];
char file2[128];
char file3[128];
char file4[128];
strcpy(file1, RAWWAVE_PATH);
strcpy(file2, RAWWAVE_PATH);
strcpy(file3, RAWWAVE_PATH);
strcpy(file4, RAWWAVE_PATH);
this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"),
strcat(file2,"rawwaves/sinewave.raw"),
strcat(file3,"rawwaves/sinewave.raw"),
strcat(file4,"rawwaves/fwavblnk.raw"));
this->setRatio(0,(MY_FLOAT) (1.00 * 1.000));
this->setRatio(1,(MY_FLOAT) (4.00 * 0.999));
this->setRatio(2,(MY_FLOAT) (3.00 * 1.001));
this->setRatio(3,(MY_FLOAT) (0.50 * 1.002));
gains[0] = __FM4Op_gains[92];
gains[1] = __FM4Op_gains[76];
gains[2] = __FM4Op_gains[91];
gains[3] = __FM4Op_gains[68];
adsr[0]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.001,(MY_FLOAT) 1.0,(MY_FLOAT) 0.01);
adsr[1]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.010,(MY_FLOAT) 1.0,(MY_FLOAT) 0.50);
adsr[2]->setAllTimes((MY_FLOAT) 0.010,(MY_FLOAT) 0.005,(MY_FLOAT) 1.0,(MY_FLOAT) 0.20);
adsr[3]->setAllTimes((MY_FLOAT) 0.030,(MY_FLOAT) 0.010,(MY_FLOAT) 0.2,(MY_FLOAT) 0.20);
twozero->setGain((MY_FLOAT) 2.0);
vibWave->setFreq((MY_FLOAT) 5.5);
modDepth = (MY_FLOAT) 0.00;
}
HeavyMtl :: ~HeavyMtl()
{
}
void HeavyMtl :: setFreq(MY_FLOAT frequency)
{
baseFreq = frequency;
waves[0]->setFreq(baseFreq * ratios[0]);
waves[1]->setFreq(baseFreq * ratios[1]);
waves[2]->setFreq(baseFreq * ratios[2]);
waves[3]->setFreq(baseFreq * ratios[3]);
}
void HeavyMtl :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
gains[0] = amp * __FM4Op_gains[92];
gains[1] = amp * __FM4Op_gains[76];
gains[2] = amp * __FM4Op_gains[91];
gains[3] = amp * __FM4Op_gains[68];
this->setFreq(freq);
this->keyOn();
#if defined(_debug_)
printf("HeavyMtl : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}

45
src/Instrmnt.cpp Normal file
View File

@@ -0,0 +1,45 @@
/******************************************/
/* Instrument SuperClass for Toolkit96 */
/* Perry R. Cook, Princeton University */
/******************************************/
#include "Instrmnt.h"
Instrmnt :: Instrmnt()
{
}
Instrmnt :: ~Instrmnt()
{
}
void Instrmnt :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
printf("Warning!! Instrument Class noteOn here!! %f %f\n",freq,amp);
}
void Instrmnt :: noteOff(MY_FLOAT amp)
{
printf("Warning!! Instrument Class noteOff here!! %f\n",amp);
}
void Instrmnt :: setFreq(MY_FLOAT freq)
{
printf("Warning!! Instrument Class setFreq here!! %f\n",freq);
}
MY_FLOAT Instrmnt :: tick()
{
printf("Warning!! Instrument Class tick here!!\n");
return lastOutput;
}
MY_FLOAT Instrmnt :: lastOut()
{
return lastOutput;
}
void Instrmnt :: controlChange(int number, MY_FLOAT value)
{
printf("Warning!! Instrument Class Control Change here!! %i %f\n",number,value);
}

166
src/JCRev.cpp Normal file
View File

@@ -0,0 +1,166 @@
/*******************************************/
/* JVRev Reverb Subclass */
/* by Tim Stilson, 1998 */
/* based on CLM JCRev */
/* Integrated into STK by Gary Scavone */
/* */
/* This is based on some of the famous */
/* Stanford CCRMA reverbs (NRev, KipRev) */
/* all based on the Chowning/Moorer/ */
/* Schroeder reverberators, which use */
/* networks of simple allpass and comb */
/* delay filters. This particular */
/* arrangement consists of 3 allpass */
/* filters in series, followed by 4 comb */
/* filters in parallel, an optional */
/* lowpass filter, and two decorrelation */
/* delay lines in parallel at the output. */
/*******************************************/
#include "JCRev.h"
//#define LOWPASS
JCRev :: JCRev(MY_FLOAT T60)
{
/* These are the values from CLM's JCRev.ins ... I found that the
impulse response sounded better with the shorter delay lengths.
--Gary Scavone, 2/1998
int lens[9] = {4799,4999,5399,5801,1051,337,113,573,487};
*/
int lens[9] = {1777,1847,1993,2137,389,127,43,211,179};
int val, i;
if (SRATE < 44100.0) {
double srscale = SRATE / 44100.0;
for (i=0; i<9; i++) {
val = (int) floor(srscale * lens[i]);
if ((val & 1) == 0) val++;
while (!this->isprime(val)) val += 2;
lens[i] = val;
}
}
for (i=0; i<3; i++)
{
APdelayLine[i] = new DLineN(lens[i+4] + 2);
APdelayLine[i]->setDelay(lens[i+4]);
}
for (i=0; i<4; i++)
{
CdelayLine[i] = new DLineN(lens[i] + 2);
CdelayLine[i]->setDelay(lens[i]);
combCoeff[i] = pow(10,(-3 * lens[i] / (T60 * SRATE)));
// printf("combCoeff[%d] = %f\n", i, combCoeff[i]);
}
outLdelayLine = new DLineN(lens[7] + 2);
outLdelayLine->setDelay(lens[7]);
outRdelayLine = new DLineN(lens[8] + 2);
outRdelayLine->setDelay(lens[8]);
allPassCoeff = 0.7;
effectMix = 0.3;
this->clear();
}
JCRev :: ~JCRev()
{
delete APdelayLine[0];
delete APdelayLine[1];
delete APdelayLine[2];
delete CdelayLine[0];
delete CdelayLine[1];
delete CdelayLine[2];
delete CdelayLine[3];
delete outLdelayLine;
delete outRdelayLine;
}
void JCRev :: clear()
{
APdelayLine[0]->clear();
APdelayLine[1]->clear();
APdelayLine[2]->clear();
CdelayLine[0]->clear();
CdelayLine[1]->clear();
CdelayLine[2]->clear();
CdelayLine[3]->clear();
outRdelayLine->clear();
outLdelayLine->clear();
lastOutL = 0.0;
lastOutR = 0.0;
combsum1=0.0;
combsum2=0.0;
combsum=0.0;
}
void JCRev :: setEffectMix(MY_FLOAT mix)
{
effectMix = mix;
}
MY_FLOAT JCRev :: lastOutput()
{
return (lastOutL + lastOutR) * 0.5;
}
MY_FLOAT JCRev :: lastOutputL()
{
return lastOutL;
}
MY_FLOAT JCRev :: lastOutputR()
{
return lastOutR;
}
MY_FLOAT JCRev :: tick(MY_FLOAT input)
{
MY_FLOAT temp,temp0,temp1,temp2,temp3,temp4,temp5,temp6;
MY_FLOAT filtout;
temp = APdelayLine[0]->lastOut();
temp0 = allPassCoeff * temp;
temp0 += input;
APdelayLine[0]->tick(temp0);
temp0 = -(allPassCoeff * temp0) + temp;
temp = APdelayLine[1]->lastOut();
temp1 = allPassCoeff * temp;
temp1 += temp0;
APdelayLine[1]->tick(temp1);
temp1 = -(allPassCoeff * temp1) + temp;
temp = APdelayLine[2]->lastOut();
temp2 = allPassCoeff * temp;
temp2 += temp1;
APdelayLine[2]->tick(temp2);
temp2 = -(allPassCoeff * temp2) + temp;
temp3 = temp2 + (combCoeff[0] * CdelayLine[0]->lastOut());
temp4 = temp2 + (combCoeff[1] * CdelayLine[1]->lastOut());
temp5 = temp2 + (combCoeff[2] * CdelayLine[2]->lastOut());
temp6 = temp2 + (combCoeff[3] * CdelayLine[3]->lastOut());
CdelayLine[0]->tick(temp3);
CdelayLine[1]->tick(temp4);
CdelayLine[2]->tick(temp5);
CdelayLine[3]->tick(temp6);
#ifdef LOWPASS
combsum2=combsum1;
combsum1=combsum;
combsum = temp3+temp4+temp5+temp6;
filtout= 0.5*combsum1+0.25*(combsum+combsum2);
#else
filtout = temp3+temp4+temp5+temp6;
#endif
lastOutL = effectMix * (outLdelayLine->tick(filtout));
lastOutR = effectMix * (outRdelayLine->tick(filtout));
temp = (1.0 - effectMix) * input;
lastOutL += temp;
lastOutR += temp;
return (lastOutL + lastOutR) * 0.5;
}

43
src/JetTabl.cpp Normal file
View File

@@ -0,0 +1,43 @@
/**********************************************/
/* Jet Table Object by Perry R. Cook, 1995-96 */
/* Consult Fletcher and Rossing, Karjalainen, */
/* Cook, more, for information. */
/* This, as with many other of my "tables", */
/* is not a table, but is computed by poly- */
/* nomial calculation. */
/**********************************************/
#include "JetTabl.h"
JetTabl :: JetTabl()
{
lastOutput = (MY_FLOAT) 0.0;
}
JetTabl :: ~JetTabl()
{
}
MY_FLOAT JetTabl :: lookup(MY_FLOAT sample)
{
return this->tick(sample);
}
MY_FLOAT JetTabl :: tick(MY_FLOAT sample)
// Perform "Table Lookup"
// By Polynomial Calculation
{
// (x^3 - x) approximates sigmoid of jet
lastOutput = sample * (sample*sample - (MY_FLOAT) 1.0);
if (lastOutput > 1.0)
lastOutput = (MY_FLOAT) 1.0; // Saturation at +/- 1.0
if (lastOutput < -1.0)
lastOutput = (MY_FLOAT) -1.0;
return lastOutput;
}
MY_FLOAT JetTabl :: lastOut()
{
return lastOutput;
}

65
src/LipFilt.cpp Normal file
View File

@@ -0,0 +1,65 @@
/************************************************/
/* Lip Filter Object by Perry R. Cook, 1995-96 */
/* The lip of the brass player has dynamics */
/* which are controlled by the mass, spring */
/* constant, and damping of the lip. This */
/* filter simulates that behavior and the */
/* transmission/reflection properties as */
/* well. See Cook TBone and HosePlayer */
/* instruments and articles. */
/************************************************/
#include "LipFilt.h"
LipFilt :: LipFilt()
{
MY_FLOAT coeffs[2];
filter = new BiQuad;
coeffs[0] = (MY_FLOAT) 0.0;
coeffs[1] = (MY_FLOAT) 0.0;
filter->setZeroCoeffs(coeffs);
this->clear();
}
LipFilt :: ~LipFilt()
{
delete filter;
}
void LipFilt :: clear()
{
filter->clear();
lastOutput = (MY_FLOAT) 0.0;
}
void LipFilt :: setFreq(MY_FLOAT frequency)
{
MY_FLOAT coeffs[2];
coeffs[0] = (MY_FLOAT) 2.0 * (MY_FLOAT) 0.997 *
(MY_FLOAT) cos(TWO_PI * frequency / SRATE); /* damping should change with */
coeffs[1] = (MY_FLOAT) (-0.997 * 0.997); /* lip parameters, but not yet.*/
filter->setPoleCoeffs(coeffs);
filter->setGain((MY_FLOAT) 0.03);
}
/* NOTE: Here we should add lip tension */
/* settings based on Mass/Spring/Damping */
MY_FLOAT LipFilt :: tick(MY_FLOAT mouthSample,MY_FLOAT boreSample)
/* Perform "Table Lookup" By Polynomial Calculation */
{
MY_FLOAT temp;
temp = mouthSample - boreSample; /* Differential pressure */
temp = filter->tick(temp); /* Force -> position */
temp = temp*temp; /* Simple position to area mapping */
if (temp > 1.0) temp = (MY_FLOAT) 1.0; /* Saturation at + 1.0 */
lastOutput = temp * mouthSample; /* Assume mouth input = area */
lastOutput += ((MY_FLOAT) 1.0 - temp) * boreSample; /* and Bore reflection is compliment. */
return lastOutput;
}
MY_FLOAT LipFilt :: lastOut()
{
return lastOutput;
}

410
src/MD2SKINI.cpp Normal file
View File

@@ -0,0 +1,410 @@
/*******************************************/
/* Simple Realtime MIDI to SKINI Parser */
/* Gary P. Scavone, February 1998. */
/* Revised for sockets, May & June 1998. */
/* SKINI/MIDI merge added August 1999. */
/* */
/* This object takes MIDI from the input */
/* stream (via the MIDIIO class), */
/* parses it, and turns it into SKINI */
/* messages. */
/*******************************************/
#include "RtMidi.h"
#include "SKINI11.msg"
#if defined(__STK_REALTIME_)
int outAHere = 0;
// Do OS dependent declarations and includes
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <pthread.h>
pthread_t exit_thread;
#elif defined(__OS_Win_)
#include <process.h>
#include <winsock.h>
unsigned long exit_thread;
#endif
// The thread function protocols are slightly different
// under Windoze ... but of course!
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
void *monitorStdin(void *)
#elif defined(__OS_Win_)
void monitorStdin(void *)
#endif
{
char inputString[128];
printf("Type 'Exit<cr>' to quit.\n");
while (!outAHere) {
fgets(inputString, 128, stdin);
if (inputString[3] == 't' && inputString[1] == 'x'
&& inputString[2] == 'i' && inputString[0] == 'E') {
outAHere = 1;
}
else {
printf(inputString);
fflush(stdout);
}
}
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
pthread_exit(NULL);
return NULL;
#elif defined(__OS_Win_)
_endthread();
#endif
}
void usage(void) {
printf("useage: MD2SKINI <flag(s)>\n\n");
printf(" With no arguments, MD2SKINI converts MIDI input to SKINI\n");
printf(" format and sends the output directly to stdout.\n");
printf(" With flag = -s <hostname>, the output is sent over a socket\n");
printf(" connection to the optional hostname (default = localhost).\n");
printf(" With flag = -f <filename>, the output stream is simultaneously\n");
printf(" written to the file specified by the optional <filename>\n");
printf(" (default = test.ski).\n\n");
exit(0);
}
int main(int argc,char *argv[])
{
long j, i = 1;
MY_FLOAT byte2, byte3;
int channel;
int firstMessage = 1;
int writeFileOut = 0;
FILE *fileOut = NULL;
RtMidi *controller;
char hostName[256];
char fileName[256];
int useSocket = 0;
int theSocket = 0;
struct sockaddr_in saServer;
static struct timeval timeout = {0, 10000}; // ten millisecond
if (argc>5) {
usage();
}
// Parse the command-line arguments.
while (i < argc) {
if (argv[i][0] == '-') {
switch(argv[i][1]) {
case 's':
if ((i+1 < argc) && argv[i+1][0] != '-') {
i++;
strcpy(hostName,argv[i]);
}
else strcpy(hostName,"localhost");
useSocket = 1;
break;
case 'f':
if ((i+1 < argc) && argv[i+1][0] != '-') {
i++;
strcpy(fileName,argv[i]);
if (strstr(fileName,".ski") == NULL) strcat(fileName,".ski");
}
else strcpy(fileName,"test.ski");
fileOut = fopen(fileName,"wb");
writeFileOut = 1;
break;
default:
usage();
break;
}
}
else usage();
i++;
}
MY_FLOAT dt=0.0;
try {
controller = new RtMidi();
}
catch (StkError& m) {
m.printMessage();
exit(0);
}
// If using sockets, setup the client socket
if (useSocket) {
#if defined(__OS_Win_) // Stupid Windoze only stuff
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1,1);
WSAStartup(wVersionRequested, &wsaData);
if (wsaData.wVersion != wVersionRequested) {
fprintf(stderr,"\n Wrong Windoze socket library version!\n");
exit(0);
}
#endif
// Create the client-side socket
theSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (theSocket < 0) {
fprintf(stderr,"Couldn't create socket ... aborting!\n");
exit(0);
}
struct hostent *hostp;
if ((hostp = gethostbyname(hostName)) == 0) {
fprintf(stderr,"%s: unknown host ... aborting!\n", hostName);
exit(0);
}
// Fill in the address structure
saServer.sin_family = AF_INET;
memcpy((void *)&saServer.sin_addr, hostp->h_addr, hostp->h_length);
saServer.sin_port = htons(2001); // Port number
// Connect to the server
if(connect(theSocket, (struct sockaddr *)&saServer, sizeof(saServer)) < 0) {
fprintf(stderr,"Socket connect failed ... aborting!\n");
#if defined(__OS_Win_)
closesocket(theSocket);
WSACleanup();
#else
close(theSocket);
#endif
exit(0);
}
}
// Setup the exit thread.
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
if (pthread_create(&exit_thread, NULL, monitorStdin, NULL)) {
fprintf(stderr, "Unable to create exit thread ... aborting.\n");
exit(0);
}
#elif defined(__OS_Win_)
exit_thread = _beginthread(monitorStdin, 0, NULL);
if (exit_thread == -1) {
fprintf(stderr, "Unable to create exit thread ... aborting.\n");
exit(0);
}
#endif
/* Write SKINI messages to buffer 's'. This is the easiest way
to allow this single executable to work for both socketing
and printf's to stdout.
*/
char s[128];
while (!outAHere) {
if (controller->nextMessage() > 0) {
byte3 = controller->getByteThree();
byte2 = controller->getByteTwo();
channel = controller->getChannel();
if (writeFileOut) dt = controller->getDeltaTime();
if (firstMessage) { /* first MIDI message time stamp is meaningless */
dt = 0.0;
firstMessage = 0;
}
switch(controller->getType()) {
case __SK_NoteOn_:
if (byte3 < 1.0) {
sprintf(s,"NoteOff\t\t%.3f %d %.1f %.1f\n",0.0,channel,byte2,64.0);
if (writeFileOut) {
fprintf(fileOut,"NoteOff\t\t%.3f %d %.1f %.1f\n",dt,channel,byte2,64.0);
}
} else {
sprintf(s,"NoteOn\t\t%.3f %d %.1f %.1f\n",0.0,channel,byte2,byte3);
if (writeFileOut) {
fprintf(fileOut,"NoteOn\t\t%.3f %d %.1f %.1f\n",dt,channel,byte2,byte3);
}
}
break;
case __SK_NoteOff_:
if (byte3 < 2.0) byte3 = 64.0;
sprintf(s,"NoteOff\t\t%.3f %d %.1f %.1f\n",0.0,channel,byte2,byte3);
if (writeFileOut) {
fprintf(fileOut,"NoteOff\t\t%.3f %d %.1f %.1f\n",dt,channel,byte2,byte3);
}
break;
case __SK_PolyPressure_:
sprintf(s,"PolyPressure\t%.3f %d %.1f %.1f\n",0.0,channel,byte2,byte3);
if (writeFileOut) {
fprintf(fileOut,"PolyPressure\t%.3f %d %.1f %.1f\n",dt,channel,byte2,byte3);
}
break;
case __SK_ControlChange_:
j = (int) byte2;
switch(j) {
case __SK_Volume_:
sprintf(s,"Volume\t%.3f %d %.1f\n",0.0,channel,byte3);
if (writeFileOut) {
fprintf(fileOut,"Volume\t%.3f %d %.1f\n",dt,channel,byte3);
}
break;
case __SK_ModWheel_:
sprintf(s,"ModWheel\t%.3f %d %.1f\n",0.0,channel,byte3);
if (writeFileOut) {
fprintf(fileOut,"ModWheel\t%.3f %d %.1f\n",dt,channel,byte3);
}
break;
case __SK_Breath_:
sprintf(s,"Breath\t\t%.3f %d %.1f\n",0.0,channel,byte3);
if (writeFileOut) {
fprintf(fileOut,"Breath\t\t%.3f %d %.1f\n",dt,channel,byte3);
}
break;
case __SK_FootControl_:
sprintf(s,"FootControl\t%.3f %d %.1f\n",0.0,channel,byte3);
if (writeFileOut) {
fprintf(fileOut,"FootControl\t%.3f %d %.1f\n",dt,channel,byte3);
}
break;
case __SK_Portamento_:
sprintf(s,"Portamento\t%.3f %d %.1f\n",0.0,channel,byte3);
if (writeFileOut) {
fprintf(fileOut,"Portamento\t%.3f %d %.1f\n",dt,channel,byte3);
}
break;
case __SK_Balance_:
sprintf(s,"Balance\t%.3f %d %.1f\n",0.0,channel,byte3);
if (writeFileOut) {
fprintf(fileOut,"Balance\t%.3f %d %.1f\n",dt,channel,byte3);
}
break;
case __SK_Pan_:
sprintf(s,"Pan\t\t%.3f %d %.1f\n",0.0,channel,byte3);
if (writeFileOut) {
fprintf(fileOut,"Pan\t\t%.3f %d %.1f\n",dt,channel,byte3);
}
break;
case __SK_Sustain_:
sprintf(s,"Sustain\t%.3f %d %.1f\n",0.0,channel,byte3);
if (writeFileOut) {
fprintf(fileOut,"Sustain\t%.3f %d %.1f\n",dt,channel,byte3);
}
break;
case __SK_Expression_:
sprintf(s,"Expression\t%.3f %d %.1f\n",0.0,channel,byte3);
if (writeFileOut) {
fprintf(fileOut,"Expression\t%.3f %d %.1f\n",dt,channel,byte3);
}
break;
default:
sprintf(s,"ControlChange\t%.3f %d %ld %.1f\n",0.0,channel,j,byte3);
if (writeFileOut) {
fprintf(fileOut,"ControlChange\t%.3f %d %ld %.1f\n",dt,channel,j,byte3);
}
break;
}
break;
case __SK_ProgramChange_:
j = (int) byte2;
sprintf(s,"ProgramChange\t%.3f %d %ld\n",0.0,channel,j);
if (writeFileOut) {
fprintf(fileOut,"ProgramChange\t%.3f %d %ld\n",dt,channel,j);
}
break;
case __SK_ChannelPressure_:
sprintf(s,"ChannelPressure\t%.3f %d %.1f\n",0.0,channel,byte2);
if (writeFileOut) {
fprintf(fileOut,"ChannelPressure\t%.3f %d %.1f\n",dt,channel,byte2);
}
break;
case __SK_PitchBend_:
sprintf(s,"PitchBend\t%.3f %d %f\n",0.0,channel,byte2);
if (writeFileOut) {
fprintf(fileOut,"PitchBend\t%.3f %d %f\n",dt,channel,byte2);
}
break;
default:
sprintf(s,"// Unknown\t%.3f %d %f %f\n",0.0,channel,byte2,byte3);
if (writeFileOut) {
fprintf(fileOut,"// Unknown\t\t%.3f %d %f %f\n",dt,channel,byte2,byte3);
}
break;
}
if (useSocket) {
if (send(theSocket, s, strlen(s), 0) < 0) {
fprintf(stderr,"Socket connection failed ... aborting.\n");
#if defined(__OS_Win_)
closesocket(theSocket);
WSACleanup();
#else
close(theSocket);
#endif
outAHere = 1;
exit(0);
}
}
else {
printf("%s", s);
fflush(stdout);
}
memset(s, 0, sizeof(s));
} else {
// With Irix 5.3, you can no longer use the usleep()
// function. And in Windoze, you can't use the select()
// function to do timings. I love supporting multiple
// platforms!
#if defined(__OS_Win_)
Sleep ( (DWORD) 5);
#else
timeout.tv_sec = 0;
timeout.tv_usec = 10000; // 0.01 seconds
select(0, NULL, NULL, NULL, &timeout);
#endif
}
}
sprintf(s,"Exiting MD2SKINI process ... bye!\n");
if (useSocket) {
send(theSocket, s, strlen(s), 0);
#if defined(__OS_Win_)
closesocket(theSocket);
WSACleanup();
#else
close(theSocket);
#endif
}
else {
printf("%s", s);
fflush(stdout);
}
if (writeFileOut) {
printf("Wrote SKINI output to file %s.\n", fileName);
fclose(fileOut);
}
delete controller;
return 0;
}
#endif

52
src/Makefile Normal file
View File

@@ -0,0 +1,52 @@
# stklib Makefile - for Linux or SGI with GNU Makefile utilities
LIBRARY = stklib.a
AR = ar -qsc
RM = /bin/rm
INCLUDE = -I../include
OS = $(shell uname)
O_FILES = Object.o Envelope.o ADSR.o Noise.o SubNoise.o \
Filter.o OneZero.o OnePole.o PoleZero.o DCBlock.o FIR.o \
TwoZero.o TwoPole.o BiQuad.o DLineA.o DLineL.o DLineN.o \
BowTabl.o JetTabl.o ReedTabl.o LipFilt.o TablLook.o \
\
Instrmnt.o Modal4.o ModalBar.o Shakers.o \
Plucked.o Plucked2.o Mandolin.o Bowed.o Clarinet.o \
Flute.o Brass.o BlowHole.o BowedBar.o \
FM4Op.o FM4Alg3.o FM4Alg4.o FM4Alg5.o FM4Alg6.o FM4Alg8.o \
Rhodey.o Wurley.o TubeBell.o HeavyMtl.o \
PercFlut.o BeeThree.o DrumSynt.o Moog1.o \
Sampler.o SamplFlt.o Simple.o SingWave.o \
VoicForm.o FMVoices.o FormSwep.o Modulatr.o VoicMang.o \
\
WvOut.o SndWvOut.o WavWvOut.o MatWvOut.o RawWvOut.o AifWvOut.o \
WvIn.o SndWvIn.o WavWvIn.o MatWvIn.o RawWvIn.o AifWvIn.o \
StrmWvIn.o StrmWvOut.o \
RtWvOut.o RtAudio.o RtWvIn.o RtMidi.o RtDuplex.o \
Reverb.o PRCRev.o JCRev.o NRev.o \
\
SKINI11.o Controller.o ByteSwap.o StkError.o
ifeq ($(OS),IRIX) # These are for SGI
CC = CC -D__OS_IRIX_
endif
ifeq ($(OS),Linux) # These are for Linux
CC = g++ -O3 -Wall -D__OS_Linux_
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)

152
src/Mandolin.cpp Normal file
View File

@@ -0,0 +1,152 @@
/********************************************/
/* Commuted Mandolin Subclass of enhanced */
/* dual plucked-string model */
/* by Perry Cook, 1995-96 */
/* */
/* Controls: CONTROL1 = bodySize */
/* CONTROL2 = pluckPosition */
/* CONTROL3 = loopGain */
/* MOD_WHEEL= deTuning */
/* */
/* Note: Commuted Synthesis, as with many */
/* other WaveGuide techniques, is covered */
/* by patents, granted, pending, and/or */
/* applied-for. Many are assigned to the */
/* Board of Trustees, Stanford University. */
/* For information, contact the Office of */
/* Technology Licensing, Stanford U. */
/********************************************/
#include "Mandolin.h"
#include "SKINI11.msg"
Mandolin :: Mandolin(MY_FLOAT lowestFreq) : Plucked2(lowestFreq)
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char temp[128];
strcpy(temp, RAWWAVE_PATH);
soundfile[0] = new RawWvIn(strcat(temp,"rawwaves/mand1.raw"),"oneshot");
strcpy(temp, RAWWAVE_PATH);
soundfile[1] = new RawWvIn(strcat(temp,"rawwaves/mand2.raw"),"oneshot");
strcpy(temp, RAWWAVE_PATH);
soundfile[2] = new RawWvIn(strcat(temp,"rawwaves/mand3.raw"),"oneshot");
strcpy(temp, RAWWAVE_PATH);
soundfile[3] = new RawWvIn(strcat(temp,"rawwaves/mand4.raw"),"oneshot");
strcpy(temp, RAWWAVE_PATH);
soundfile[4] = new RawWvIn(strcat(temp,"rawwaves/mand5.raw"),"oneshot");
strcpy(temp, RAWWAVE_PATH);
soundfile[5] = new RawWvIn(strcat(temp,"rawwaves/mand6.raw"),"oneshot");
strcpy(temp, RAWWAVE_PATH);
soundfile[6] = new RawWvIn(strcat(temp,"rawwaves/mand7.raw"),"oneshot");
strcpy(temp, RAWWAVE_PATH);
soundfile[7] = new RawWvIn(strcat(temp,"rawwaves/mand8.raw"),"oneshot");
strcpy(temp, RAWWAVE_PATH);
soundfile[8] = new RawWvIn(strcat(temp,"rawwaves/mand9.raw"),"oneshot");
strcpy(temp, RAWWAVE_PATH);
soundfile[9] = new RawWvIn(strcat(temp,"rawwaves/mand10.raw"),"oneshot");
strcpy(temp, RAWWAVE_PATH);
soundfile[10] = new RawWvIn(strcat(temp,"rawwaves/mand11.raw"),"oneshot");
strcpy(temp, RAWWAVE_PATH);
soundfile[11] = new RawWvIn(strcat(temp,"rawwaves/mand12.raw"),"oneshot");
directBody = 1.0;
mic = 0;
dampTime = 0;
waveDone = 1;
}
Mandolin :: ~Mandolin()
{
}
void Mandolin :: pluck(MY_FLOAT amplitude)
{ /* this function gets interesting here, */
/* because pluck may be longer than */
/* string length, so we just reset the */
/* soundfile and add in the pluck in */
/* the tick method. */
soundfile[mic]->reset();
pluckAmp = amplitude;
/* Set Pick Position which puts zeroes at pos*length */
combDelay->setDelay((MY_FLOAT) 0.5 * pluckPos * lastLength);
dampTime = (long) lastLength; /* See tick method below */
waveDone = 0;
}
void Mandolin :: pluck(MY_FLOAT amplitude, MY_FLOAT position)
{
pluckPos = position; /* pluck position is zeroes at pos*length */
this->pluck(amplitude);
}
void Mandolin :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
this->setFreq(freq);
this->pluck(amp);
#if defined(_debug_)
printf("Mandolin : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}
void Mandolin :: setBodySize(MY_FLOAT size)
{
int i;
for (i=0;i<12;i++) {
soundfile[i]->setRate(size);
}
}
MY_FLOAT Mandolin :: tick()
{
MY_FLOAT temp = (MY_FLOAT) 0;
if (!waveDone) {
waveDone = soundfile[mic]->informTick(); /* as long as it goes . . . */
temp = soundfile[mic]->lastOut() * pluckAmp; /* scaled pluck excitation */
temp = temp - combDelay->tick(temp); /* with comb filtering */
}
if (dampTime>=0) { /* Damping hack to help avoid */
dampTime -= 1; /* overflow on replucking */
lastOutput = delayLine->tick( /* Calculate 1st delay */
filter->tick( /* filterered reflection */
temp + /* plus pluck excitation */
(delayLine->lastOut() * (MY_FLOAT) 0.7)));
lastOutput += delayLine2->tick( /* and 2nd delay */
filter2->tick( /* just like the 1st */
temp +
(delayLine2->lastOut() * (MY_FLOAT) 0.7)));
}
else { /* No damping hack after 1 period */
lastOutput = delayLine->tick( /* Calculate 1st delay */
filter->tick( /* filtered reflection */
temp + /* plus pluck excitation */
(delayLine->lastOut() * loopGain)));
lastOutput += delayLine2->tick( /* and 2nd delay */
filter2->tick( /* just like the 1st */
temp +
(delayLine2->lastOut() * loopGain)));
}
lastOutput *= (MY_FLOAT) 0.3;
return lastOutput;
}
void Mandolin :: controlChange(int number, MY_FLOAT value)
{
#if defined(_debug_)
printf("Mandolin : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_BodySize_)
this->setBodySize(value * (MY_FLOAT) NORM_7 * (MY_FLOAT) 2.0);
else if (number == __SK_PickPosition_)
this->setPluckPos(value * (MY_FLOAT) NORM_7);
else if (number == __SK_StringDamping_)
this->setBaseLoopGain((MY_FLOAT) 0.97 + (value * (MY_FLOAT) NORM_7 * (MY_FLOAT) 0.03));
else if (number == __SK_StringDetune_)
this->setDetune((MY_FLOAT) 1.0 - (value * (MY_FLOAT) NORM_7 * (MY_FLOAT) 0.1));
else if (number == __SK_AfterTouch_)
this->pluck(value * (MY_FLOAT) NORM_7);
else if (number == 411) {
mic = (int) value % 12;
}
else {
printf("Mandolin : Undefined Control Number!! %i\n",number);
}
}

235
src/MatWvIn.cpp Normal file
View File

@@ -0,0 +1,235 @@
/*******************************************/
/* MatWvIn Input Class, */
/* by Gary P. Scavone, 1999 */
/* */
/* This object inherits from WvIn and is */
/* used to open Matlab MAT-file data */
/* (doubles) files for playback. In */
/* order for this class to work, the */
/* MAT-file must contain a single array */
/* (matrix) of double-precision floating */
/* point values (can be multi-channel). */
/* It does not work for any other data */
/* formats. */
/* */
/* MAT-file data is either big- or */
/* little-endian, which can be determined */
/* from the header. */
/*******************************************/
#include "MatWvIn.h"
#include "ByteSwap.h"
MatWvIn :: MatWvIn(char *fileName, char *mode)
{
char msg[256];
// check mode string
if ( strcmp(mode,"oneshot") && strcmp(mode,"looping") ) {
sprintf(msg, "MatWvIn: constructor parameter 'mode' must be oneshot or looping only.\n");
throw StkError(msg, StkError::FUNCTION_SYNTAX);
}
// Open the file and get header info
fd = fopen(fileName,"rb");
if (!fd) {
sprintf(msg, "MatWvIn: Couldn't open or find MAT-file (%s).\n", fileName);
throw StkError(msg, StkError::FILE_NOT_FOUND);
}
// Make sure this is a version 5 MAT-file format and find its endian-ness
char head[4];
fseek(fd,0,SEEK_SET);
fread(&head,4,1,fd); // If any of the first 4 characters of the header = 0,
if (strstr(head,"0")) { // then this is a Version 4 MAT-file.
fclose(fd);
sprintf(msg, "MatWvIn: %s appears to be a Version 4 \nMAT-file, which is not currently supported.\n",
fileName);
throw StkError(msg, StkError::FILE_ERROR);
}
char mi[2];
doSwap = 0;
fseek(fd,126,SEEK_SET); // Locate "M" and "I" characters in header
fread(&mi,2,1,fd);
#ifdef __LITTLE_ENDIAN__
if (!strncmp(mi,"MI",2)) {
doSwap = 1;
} else if (strncmp(mi,"IM",2)) {
fclose(fd);
sprintf(msg, "MatWvIn: %s doesn't appear to be a MAT-file.\n", fileName);
throw StkError(msg, StkError::FILE_ERROR);
}
#else
if (!strncmp(mi,"IM",2)) {
doSwap = 1;
} else if (strncmp(mi,"MI",2)) {
fclose(fd);
sprintf(msg, "MatWvIn: %s doesn't appear to be a MAT-file.\n", fileName);
throw StkError(msg, StkError::FILE_ERROR);
}
#endif
// Check the data element type
INT32 datatype;
fread(&datatype,4,1,fd);
if (doSwap) swap32((unsigned char *)&datatype);
if (datatype != 14) {
fclose(fd);
sprintf(msg, "MatWvIn: I'm expecting a single array (or matrix) data element.\n");
throw StkError(msg, StkError::FILE_ERROR);
}
// Check the array data type
INT32 tmp;
INT32 size;
fseek(fd,168,SEEK_SET);
fread(&tmp,4,1,fd);
if (doSwap) swap32((unsigned char *)&tmp);
if (tmp == 1) { // array name > 4 characters
fread(&tmp,4,1,fd); // get array name length
if (doSwap) swap32((unsigned char *)&tmp);
size = (INT32) ceil((float)tmp/8);
fseek(fd,size*8,SEEK_CUR); // jump over array name
}
else { // array name <= 4 characters, compressed data element
fseek(fd,4,SEEK_CUR);
}
fread(&tmp,4,1,fd);
if (doSwap) swap32((unsigned char *)&tmp);
if (tmp != 9) {
fclose(fd);
sprintf(msg, "MatWvIn: I'm expecting the array data to be in double precision floating-point format.\n");
throw StkError(msg, StkError::FILE_ERROR);
}
// Get number of rows from the header
INT32 rows;
fseek(fd,160,SEEK_SET);
fread(&rows,4,1,fd);
if (doSwap) swap32((unsigned char *)&rows);
// Get number of columns from the header
INT32 columns;
fread(&columns,4,1,fd); // columns
if (doSwap) swap32((unsigned char *)&columns);
// Make channels = smaller of rows or columns
if (rows < columns) {
channels = rows;
fileSize = columns;
interleaved = 1;
}
else {
channels = columns;
fileSize = rows;
interleaved = 0;
}
bufferSize = fileSize;
if ((fileSize*channels) > MAX_FILE_LOAD_SIZE) {
printf("\nMatWvIn: The MAT-file (%s) has more than %d samples and\n",
fileName, MAX_FILE_LOAD_SIZE);
printf("will be loaded incrementally from disk. Normalization will be disabled.\n");
chunking = 1;
bufferSize = LOAD_BUFFER_SIZE;
}
// Setup for looping or one-shot playback
if (!strcmp(mode,"looping"))
looping = 1;
else // default = oneshot
looping = 0;
data = (MY_FLOAT *) new MY_FLOAT[(bufferSize+1)*channels];
// Move read pointer to the data in the file
INT32 headsize;
fseek(fd,132,SEEK_SET);
fread(&headsize,4,1,fd); // file size from 132nd byte
if (doSwap) swap32((unsigned char *)&headsize);
headsize -= fileSize * 8 * channels;
fseek(fd,headsize,SEEK_CUR);
dataOffset = ftell(fd);
this->getData(0); // Read samples into data[]
rate = (MY_FLOAT) 1.0;
interpolate = 0;
phaseOffset = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
this->reset();
}
MatWvIn :: ~MatWvIn()
{
}
void MatWvIn :: getData(long index)
{
/* Compare index to current readPointer and modify as needed.
* The following while() loops will only execute on calls subsequent
* to class instantiation ... and thus, only when "chunking".
*/
while (index < readPointer) {
readPointer -= LOAD_BUFFER_SIZE;
bufferSize = LOAD_BUFFER_SIZE;
if (readPointer < 0) {
bufferSize += readPointer;
readPointer = 0;
}
}
while (index >= readPointer+bufferSize) {
readPointer += LOAD_BUFFER_SIZE;
bufferSize = LOAD_BUFFER_SIZE;
if (readPointer+LOAD_BUFFER_SIZE >= fileSize) {
bufferSize = fileSize - readPointer;
}
}
long length = bufferSize;
int end_of_file = (readPointer+bufferSize == fileSize);
if (!end_of_file) length += 1;
// Read samples into data[]. Use MY _FLOAT data structure to store doubles
double *buf = (double *)data;
if (interleaved) {
fseek(fd, dataOffset+(long)(readPointer*channels*8), SEEK_SET);
fread(data, 8, length*channels, fd);
for (int i=length*channels-1; i>=0; i--) {
if (doSwap)
swap64((unsigned char *)(buf+i));
data[i] = buf[i];
}
}
else {
long i = 0;
long j = 0;
double temp;
fseek(fd, dataOffset+(long)(readPointer*8), SEEK_SET);
while (j < channels) {
fread(&temp,8,1,fd);
if (doSwap) swap64((unsigned char *)&temp);
data[channels*i+j] = (MY_FLOAT) temp;
i++;
if (i>=length) {
i = 0;
j++;
fseek(fd, dataOffset+(long)(((j*fileSize)+readPointer)*8), SEEK_SET);
}
}
}
// fill in the extra sample frame for interpolation
if (end_of_file) {
for (int j=0; j<channels; j++)
if (looping)
data[bufferSize*channels+j] = data[j];
else
data[bufferSize*channels+j] = data[(bufferSize-1)*channels+j];
}
if (!chunking) {
fclose(fd);
fd = 0;
}
}

196
src/MatWvOut.cpp Normal file
View File

@@ -0,0 +1,196 @@
/*******************************************/
/* Matlab MAT-file Output Class, */
/* by Gary P. Scavone, 1999. */
/* This object creates a Matlab MAT-file */
/* structure with a numeric array */
/* subelement and fills it with buffers */
/* of samples (doubles). */
/* */
/* When originally created, the Matlab */
/* MAT-file format was not public and I */
/* had to reverse-engineer the format. */
/* Matlab finally released the format in */
/* the Spring of 1999, and this class was */
/* updated to more closely adhere to */
/* specifications. */
/*******************************************/
#include "MatWvOut.h"
/******** Matlab Matfile Header Struct *******/
struct matheaderform {
char heading[124]; // Header text field
INT16 hff[2]; // Header flag fields
INT32 adf[11]; // Array data format fields
// There's more, but it's of variable length
};
FILE *openMatFile(int chans,char *fileName) {
struct matheaderform hdr;
FILE *fd;
char tempName[128];
char arrayName[128];
char msg[256];
int i;
INT32 tmp, headsize, namelen;
strcpy(hdr.heading,"MATLAB 5.0 MAT-file, Generated using the Synthesis ToolKit (STK). By Gary P. Scavone, CCRMA, Stanford University, 1999.");
for (i=strlen(hdr.heading);i<124;i++) hdr.heading[i] = ' ';
// Header Flag Fields
hdr.hff[0] = (INT16) 0x0100; // Version field
hdr.hff[1] = (INT16) 'M'; // Endian indicator field ("MI")
hdr.hff[1] <<= 8;
hdr.hff[1] += 'I';
hdr.adf[0] = (INT32) 14; // Matlab array data type value
hdr.adf[1] = (INT32) 0; // Size of file after this point to end (in bytes)
// Don't know size yet.
// Numeric Array Subelements (4):
// 1. Array Flags
hdr.adf[2] = (INT32) 6; // Matlab 32-bit unsigned integer data type value
hdr.adf[3] = (INT32) 8; // 8 bytes of data to follow
hdr.adf[4] = (INT32) 6; // Double-precision array, no array flags set
hdr.adf[5] = (INT32) 0; // 4 bytes undefined
// 2. Array Dimensions
hdr.adf[6] = (INT32) 5; // Matlab 32-bit signed integer data type value
hdr.adf[7] = (INT32) 8; // 8 bytes of data to follow (2D array)
hdr.adf[8] = (INT32) chans; // This is the number of rows
hdr.adf[9] = (INT32) 0; // This is the number of columns
// 3. Array Name
// We'll use fileName for the matlab array name (as well as the file name).
// If fileName is 4 characters or less, we have to use a compressed data element
// format for the array name data element. Otherwise, the array name must
// be formatted in 8-byte increments (up to 31 characters + NULL).
namelen = strlen(fileName);
if (strstr(fileName,".mat")) namelen -= 4;
if (namelen > 31) { // Check length of array name
namelen = 31;
printf("File name too long for MATLAB array name.\n");
printf("Using first 31 characters only.\n");
}
strncpy(arrayName,fileName,namelen);
if (namelen > 4) {
hdr.adf[10] = 1; // Matlab 8-bit signed integer data type value
}
else { // Compressed data element format
hdr.adf[10] = namelen;
hdr.adf[10] <<=16;
hdr.adf[10] += 1;
}
headsize = 40; // Number of bytes in data element so far
// Format file name
strcpy(tempName,fileName);
if (strstr(tempName,".mat")==NULL) strcat(tempName,".mat");
// Open file and write fixed header information
fd = fopen(tempName,"w+b");
if (!fd) {
sprintf(msg, "MatWvOut: Could not create soundfile: %s\n", tempName);
throw StkError(msg, StkError::FILE_ERROR);
}
arrayName[namelen] = '\0';
printf("\nCreating MAT-file (%s) with MATLAB array: %s\n", tempName, arrayName);
fwrite(&hdr,sizeof(char),172,fd); // Write the fixed portion of the header
// Write MATLAB array name
if (namelen > 4) {
fwrite(&namelen,sizeof(INT32),1,fd); // Size of array name in bytes (chars)
fwrite(arrayName,sizeof(char),namelen,fd);
tmp = (INT32) ceil((float)namelen/8);
fseek(fd,tmp*8-namelen,SEEK_CUR);
headsize += tmp*8;
}
else { // Compressed data element format
fwrite(arrayName,sizeof(char),namelen,fd);
tmp = 4-namelen;
fseek(fd,tmp,SEEK_CUR);
}
// Finish writing known header information
tmp = 9; // Matlab IEEE 754 double data type
fwrite(&tmp,sizeof(INT32),1,fd);
tmp = 0; // Size of real part subelement in bytes (8 per sample)
fwrite(&tmp,sizeof(INT32),1,fd);
headsize += 8; // Total number of bytes in data element so far
fseek(fd,132,SEEK_SET);
fwrite(&headsize,sizeof(INT32),1,fd); // Write header size ... will update at end
fseek(fd,0,SEEK_END);
return fd;
}
MatWvOut :: MatWvOut(char *fileName, int chans)
{
char msg[256];
if (chans < 1) {
sprintf(msg, "MatWvOut: number of channels = %d not supported!\n", chans);
throw StkError(msg, StkError::FUNCTION_SYNTAX);
}
channels = chans;
fd = openMatFile(channels,fileName);
data_length = FILE_BUFFER_SIZE*channels;
matdata = (double *) new double[data_length];
}
MatWvOut :: ~MatWvOut()
{
double temp;
INT32 headsize, temp1;
fwrite(matdata,sizeof(double),counter,fd);
temp = (double) totalCount * ONE_OVER_SRATE;
printf("%f Seconds Computed\n\n", temp);
fseek(fd,164,SEEK_SET); // jump to number of columns
fwrite(&totalCount,sizeof(INT32),1,fd);
fseek(fd,132,SEEK_SET); // jump to header size
fread(&headsize,sizeof(INT32),1,fd);
temp1 = headsize;
headsize += (INT32) (totalCount * 8 * channels);
fseek(fd,132,SEEK_SET);
// Write file size (minus some header info)
fwrite(&headsize,sizeof(INT32),1,fd);
fseek(fd,temp1+132,SEEK_SET); // jumpt to data size (in bytes)
temp1 = totalCount * 8 * channels;
fwrite(&temp1,sizeof(INT32),1,fd);
fclose(fd);
if (matdata) {
delete [ ] matdata;
matdata = 0;
}
}
void MatWvOut :: tick(MY_FLOAT sample)
{
for (int i=0;i<channels;i++)
matdata[counter++] = (double) (sample);
totalCount++;
if (counter == data_length) {
fwrite(matdata,sizeof(double),data_length,fd);
counter = 0;
}
}
void MatWvOut :: mtick(MY_MULTI samples)
{
for (int i=0;i<channels;i++)
matdata[counter++] = (double) *samples++;
totalCount++;
if (counter == data_length) {
fwrite(matdata,sizeof(double),data_length,fd);
counter = 0;
}
}

202
src/Modal4.cpp Normal file
View File

@@ -0,0 +1,202 @@
/*******************************************/
/*
Four Resonance Modal Synthesis Instrument
by Perry R. Cook, 1995-2000
This instrument contains an excitation
wavetable, an envelope, an oscillator,
and four resonances (Non-Sweeping BiQuad
Filters).
*/
/*******************************************/
#include "Modal4.h"
Modal4 :: Modal4()
{
envelope = new Envelope;
// We don't make the excitation wave here yet,
// because we don't know what it's going to be.
filters[0] = new BiQuad;
filters[1] = new BiQuad;
filters[2] = new BiQuad;
filters[3] = new BiQuad;
onepole = new OnePole;
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibr = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping");
vibr->setFreq((MY_FLOAT) 6.0);
vibrGain = (MY_FLOAT) 0.0; // zero gain by default
directGain = (MY_FLOAT) 0.0;
masterGain = (MY_FLOAT) 1.0;
baseFreq = (MY_FLOAT) 440.0;
this->setRatioAndReson(0,(MY_FLOAT) 1.00,(MY_FLOAT) 0.9997); /* Set some */
this->setRatioAndReson(1,(MY_FLOAT) 1.30,(MY_FLOAT) 0.9997); /* silly */
this->setRatioAndReson(2,(MY_FLOAT) 1.77,(MY_FLOAT) 0.9997); /* default */
this->setRatioAndReson(3,(MY_FLOAT) 2.37,(MY_FLOAT) 0.9997); /* values here */
this->setFiltGain(0,(MY_FLOAT) 0.01);
this->setFiltGain(1,(MY_FLOAT) 0.01);
this->setFiltGain(2,(MY_FLOAT) 0.01);
this->setFiltGain(3,(MY_FLOAT) 0.01);
this->clear();
filters[0]->setEqualGainZeroes();
filters[1]->setEqualGainZeroes();
filters[2]->setEqualGainZeroes();
filters[3]->setEqualGainZeroes();
stickHardness = (MY_FLOAT) 0.5;
strikePosition = (MY_FLOAT) 0.561;
}
Modal4 :: ~Modal4()
{
delete envelope;
delete filters[0];
delete filters[1];
delete filters[2];
delete filters[3];
delete onepole;
delete vibr;
}
void Modal4 :: clear()
{
onepole->clear();
filters[0]->clear();
filters[1]->clear();
filters[2]->clear();
filters[3]->clear();
}
void Modal4 :: setFreq(MY_FLOAT frequency)
{
baseFreq = frequency;
this->setRatioAndReson(0,ratios[0],resons[0]);
this->setRatioAndReson(1,ratios[1],resons[1]);
this->setRatioAndReson(2,ratios[2],resons[2]);
this->setRatioAndReson(3,ratios[3],resons[3]);
}
#include <stdio.h>
void Modal4 :: setRatioAndReson(int whichOne, MY_FLOAT ratio,MY_FLOAT reson)
{
MY_FLOAT temp;
if (ratio*baseFreq < SRATE_OVER_TWO) {
ratios[whichOne] = ratio;
}
else {
temp = ratio;
while (temp*baseFreq > SRATE_OVER_TWO) temp *= (MY_FLOAT) 0.5;
ratios[whichOne] = temp;
#if defined(_debug_)
printf("Modal4 : Aliasing would occur here, correcting.\n");
#endif
}
resons[whichOne] = reson;
if (ratio < 0)
temp = -ratio;
else
temp = ratio*baseFreq;
filters[whichOne]->setFreqAndReson(temp,reson);
}
void Modal4 :: setMasterGain(MY_FLOAT aGain)
{
masterGain = aGain;
}
void Modal4 :: setDirectGain(MY_FLOAT aGain)
{
directGain = aGain;
}
void Modal4 :: setFiltGain(int whichOne, MY_FLOAT gain)
{
filters[whichOne]->setGain(gain);
}
void Modal4 :: strike(MY_FLOAT amplitude)
{
int i;
MY_FLOAT temp;
envelope->setRate((MY_FLOAT) 1.0);
envelope->setTarget(amplitude);
onepole->setPole((MY_FLOAT) 1.0 - amplitude);
envelope->tick();
wave->reset();
for (i=0; i<4; i++) {
if (ratios[i] < 0)
temp = -ratios[i];
else
temp = ratios[i] * baseFreq;
filters[i]->setFreqAndReson(temp,resons[i]);
}
}
void Modal4 :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
this->strike(amp);
this->setFreq(freq);
#if defined(_debug_)
printf("Modal4 : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}
/* This calls damp, but inverts the meaning of amplitude
* (high amplitude means fast damping).
*/
void Modal4 :: noteOff(MY_FLOAT amp)
{
this->damp((MY_FLOAT) 1.0 - (amp * (MY_FLOAT) 0.03));
#if defined(_debug_)
printf("Modal4 : NoteOff: Amp=%lf\n",amp);
#endif
}
void Modal4 :: damp(MY_FLOAT amplitude)
{
int i;
MY_FLOAT temp;
for (i=0; i<4; i++) {
if (ratios[i] < 0)
temp = -ratios[i];
else
temp = ratios[i] * baseFreq;
filters[i]->setFreqAndReson(temp,resons[i]*amplitude);
}
}
void Modal4 :: controlChange(int number, MY_FLOAT value)
{
}
MY_FLOAT Modal4 :: tick()
{
MY_FLOAT temp,temp2;
temp = masterGain * onepole->tick(wave->tick() * envelope->tick());
temp2 = filters[0]->tick(temp);
temp2 += filters[1]->tick(temp);
temp2 += filters[2]->tick(temp);
temp2 += filters[3]->tick(temp);
temp2 = temp2 - (temp2 * directGain);
temp2 += directGain * temp;
if (vibrGain != 0.0) {
// Calculate AM and apply to master out
temp = (MY_FLOAT) 1.0 + (vibr->tick() * vibrGain);
temp2 = temp * temp2;
}
lastOutput = temp2 * (MY_FLOAT) 2.0;
return lastOutput;
}

149
src/ModalBar.cpp Normal file
View File

@@ -0,0 +1,149 @@
/*******************************************/
/*
ModalBar SubClass of Modal4 Instrument
by Perry R. Cook, 1999-2000
Controls: CONTROL1 = stickHardness
CONTROL2 = strikePosition
CONTROL3 = Mode Presets
*/
/*******************************************/
#include "ModalBar.h"
#include "SKINI11.msg"
ModalBar :: ModalBar() : Modal4()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
wave = new RawWvIn(strcat(file,"rawwaves/marmstk1.raw"),"oneshot");
wave->setRate((MY_FLOAT) 0.5); /* normal stick */
this->setRatioAndReson(0, (MY_FLOAT) 1.00,(MY_FLOAT) 0.9996); /* Set all 132.0 */
this->setRatioAndReson(1, (MY_FLOAT) 3.99,(MY_FLOAT) 0.9994); /* of our 523.0 */
this->setRatioAndReson(2,(MY_FLOAT) 10.65,(MY_FLOAT) 0.9994); /* default 1405.0 */
this->setRatioAndReson(3,-(MY_FLOAT) 2443.0,(MY_FLOAT) 0.999); /* resonances 2443.0 */
this->setFiltGain(0,(MY_FLOAT) 0.04); /* and */
this->setFiltGain(1,(MY_FLOAT) 0.01); /* gains */
this->setFiltGain(2,(MY_FLOAT) 0.01); /* for each */
this->setFiltGain(3,(MY_FLOAT) 0.008); /* resonance */
directGain = (MY_FLOAT) 0.1;
}
ModalBar :: ~ModalBar()
{
delete wave;
}
void ModalBar :: setStickHardness(MY_FLOAT hardness)
{
stickHardness = hardness;
wave->setRate((MY_FLOAT) (0.25 * (MY_FLOAT) pow(4.0,stickHardness)));
masterGain = (MY_FLOAT) 0.1 + ((MY_FLOAT) 1.8 * stickHardness);
}
void ModalBar :: setStrikePosition(MY_FLOAT position)
{
MY_FLOAT temp,temp2;
temp2 = position * PI;
strikePosition = position; /* Hack only first three modes */
temp = (MY_FLOAT) sin(temp2);
this->setFiltGain(0,(MY_FLOAT) 0.12 * temp); /* 1st mode function of pos. */
temp = (MY_FLOAT) sin(0.05 + (3.9 * temp2));
this->setFiltGain(1,(MY_FLOAT) -0.03 * temp); /* 2nd mode function of pos. */
temp = (MY_FLOAT) sin(-0.05 + (11 * temp2));
this->setFiltGain(2,(MY_FLOAT) 0.11 * temp); /* 3rd mode function of pos. */
}
void ModalBar :: setModalPreset(int which)
{
/* presets:
* first line: relative modal frequencies (negative number is
* a fixed mode that doesn't scale with frequency
* second line: resonances of the modes
* third line: mode volumes
* fourth line: stickHardness, strikePosition, and direct stick
* gain (mixed directly into the output
*/
int i, temp;
MY_FLOAT presets[9][4][4] = {
{{1.0, 3.99, 10.65, -2443}, // Marimba
{0.9996, 0.9994, 0.9994, 0.999},
{0.04, 0.01, 0.01, 0.008},
{0.429688, 0.445312, 0.093750}},
{{1.0, 2.01, 3.9, 14.37}, // Vibraphone
{0.99995, 0.99991, 0.99992, 0.9999},
{0.025, 0.015, 0.015, 0.015 },
{0.390625,0.570312,0.078125}},
{{1.0, 4.08, 6.669, -3725.0}, // Agogo
{0.999, 0.999, 0.999, 0.999},
{0.06, 0.05, 0.03, 0.02},
{0.609375,0.359375,0.140625}},
{{1.0, 2.777, 7.378, 15.377}, // Wood1
{0.996, 0.994, 0.994, 0.99},
{0.04, 0.01, 0.01, 0.008},
{0.460938,0.375000,0.046875}},
{{1.0, 2.777, 7.378, 15.377}, // Reso
{0.99996, 0.99994, 0.99994, 0.9999},
{0.02, 0.005, 0.005, 0.004},
{0.453125,0.250000,0.101562}},
{{1.0, 1.777, 2.378, 3.377}, // Wood2
{0.996, 0.994, 0.994, 0.99},
{0.04, 0.01, 0.01, 0.008},
{0.312500,0.445312,0.109375}},
{{1.0, 1.004, 1.013, 2.377}, // Beats
{0.9999, 0.9999, 0.9999, 0.999},
{0.02, 0.005, 0.005, 0.004},
{0.398438,0.296875,0.070312}},
{{1.0, 4.0, -1320.0, -3960.0}, // 2Fix
{0.9996, 0.999, 0.9994, 0.999},
{0.04, 0.01, 0.01, 0.008},
{0.453125,0.453125,0.070312}},
{{1.0, 1.217, 1.475, 1.729}, // Clump
{0.999, 0.999, 0.999, 0.999},
{0.03, 0.03, 0.03, 0.03 },
{0.390625,0.570312,0.078125}},
};
temp = (which % 9);
for (i=0; i<4; i++) {
this->setRatioAndReson(i, presets[temp][0][i], presets[temp][1][i]);
this->setFiltGain(i,presets[temp][2][i]);
}
this->setStickHardness(presets[temp][3][0]);
this->setStrikePosition(presets[temp][3][1]);
directGain = presets[temp][3][2];
if (temp == 1) // vibraphone
vibrGain = 0.2;
else
vibrGain = 0.0;
}
void ModalBar :: controlChange(int number, MY_FLOAT value)
{
#if defined(_debug_)
printf("ModalBar : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_StickHardness_)
this->setStickHardness(value * NORM_7);
else if (number == __SK_StrikePosition_)
this->setStrikePosition(value * NORM_7);
else if (number == __SK_ProphesyRibbon_)
this->setModalPreset((int) value);
else if (number == __SK_ModWheel_)
directGain = value * NORM_7;
else if (number == __SK_AfterTouch_Cont_)
envelope->setTarget(value * NORM_7);
else if (number == __SK_ModFrequency_)
vibr->setFreq(value * NORM_7 * 12.0);
else if (number == 1024) { // HACKED Poop message
printf("StickHard=%f StrikePos=%f directGain=%f\n",
stickHardness, strikePosition, directGain);
}
else {
printf("ModalBar : Undefined Control Number!!\n");
}
}

85
src/Modulatr.cpp Normal file
View File

@@ -0,0 +1,85 @@
/*******************************************/
/* Modulator Class, Perry R. Cook, 1995-96*/
/* This Object combines random and */
/* periodic modulations to give a nice */
/* natural human modulation function. */
/*******************************************/
#define POLE_POS (MY_FLOAT) 0.999
#define RND_SCALE (MY_FLOAT) 10.0
#include "Modulatr.h"
Modulatr :: Modulatr()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibwave = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping");
vibwave->setFreq((MY_FLOAT) 6.0);
vibAmt = (MY_FLOAT) 0.04;
noise = new SubNoise(330);
rndAmt = (MY_FLOAT) 0.005;
onepole = new OnePole;
onepole->setPole(POLE_POS);
onepole->setGain(rndAmt * RND_SCALE);
}
Modulatr :: ~Modulatr()
{
delete vibwave;
delete noise;
delete onepole;
}
void Modulatr :: reset()
{
lastOutput = (MY_FLOAT) 0.0;
}
void Modulatr :: setVibFreq(MY_FLOAT vibFreq)
{
vibwave->setFreq(vibFreq);
}
void Modulatr :: setVibAmt(MY_FLOAT vibAmount)
{
vibAmt = vibAmount;
}
void Modulatr :: setRndAmt(MY_FLOAT rndAmount)
{
rndAmt = rndAmount;
onepole->setGain(RND_SCALE * rndAmt);
}
MY_FLOAT Modulatr :: tick()
{
lastOutput = vibAmt * vibwave->tick(); /* Compute periodic and */
lastOutput += onepole->tick(noise->tick()); /* random modulations */
return lastOutput;
}
MY_FLOAT Modulatr :: lastOut()
{
return lastOutput;
}
/************ Test Main Program *****************/
/*
void main()
{
Modulatr testMod;
FILE *fd;
short data;
long i;
fd = fopen("test.raw","wb");
for (i=0;i<20000;i++) {
data = testMod.tick() * 32000.0;
fwrite(&data,2,1,fd);
}
fclose(fd);
}
*/

111
src/Moog1.cpp Normal file
View File

@@ -0,0 +1,111 @@
/******************************************/
/* Test Sampler Subclass of */
/* Sampling Synthesizer Class */
/* by Perry R. Cook, 1995-96 */
/* */
/* Controls: CONTROL1 = filterQ */
/* CONTROL2 = filterRate */
/* CONTROL3 = vibFreq */
/* MOD_WHEEL= vibAmt */
/******************************************/
#include "Moog1.h"
#include "SKINI11.msg"
Moog1 :: Moog1() : SamplFlt()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char temp[128];
char file[128];
strcpy(temp, RAWWAVE_PATH);
strcpy(file,temp);
attacks[0] = new RawWvIn(strcat(file,"rawwaves/mandpluk.raw"),"oneshot");
strcpy(file,temp);
loops[0] = new RawWvIn(strcat(file,"rawwaves/impuls20.raw"),"looping");
strcpy(file,temp);
loops[1] = new RawWvIn(strcat(file,"rawwaves/sinewave.raw"),"looping"); /* Steal one for vibrato */
loops[1]->setFreq((MY_FLOAT) 6.122);
adsr->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.5,(MY_FLOAT) 0.6,(MY_FLOAT) 0.250);
filterQ = (MY_FLOAT) 0.85;
filterRate = (MY_FLOAT) 0.0001;
modDepth = (MY_FLOAT) 0.0;
}
Moog1 :: ~Moog1()
{
delete attacks[0];
delete loops[0];
delete loops[1];
}
void Moog1 :: setFreq(MY_FLOAT frequency)
{
baseFreq = frequency;
attacks[0]->setFreq(baseFreq * (MY_FLOAT) 0.01);
loops[0]->setFreq(baseFreq);
}
void Moog1 :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
MY_FLOAT temp;
this->setFreq(freq);
this->keyOn();
attackGain = amp * (MY_FLOAT) 0.5;
loopGain = amp;
temp = filterQ + (MY_FLOAT) 0.05;
filters[0]->setStates((MY_FLOAT) 2000.0,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp));
filters[1]->setStates((MY_FLOAT) 2000.0,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp));
temp = filterQ + (MY_FLOAT) 0.099;
filters[0]->setTargets((MY_FLOAT) freq,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp));
filters[1]->setTargets((MY_FLOAT) freq,temp,(MY_FLOAT) 2.0 * ((MY_FLOAT) 1.0 - temp));
filters[0]->setSweepRate(filterRate * (MY_FLOAT) 22050.0 / SRATE);
filters[1]->setSweepRate(filterRate * (MY_FLOAT) 22050.0 / SRATE);
#if defined(_debug_)
printf("Moog1 : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}
void Moog1 :: setModulationSpeed(MY_FLOAT mSpeed)
{
loops[1]->setFreq(mSpeed);
}
void Moog1 :: setModulationDepth(MY_FLOAT mDepth)
{
modDepth = mDepth * (MY_FLOAT) 0.5;
}
void Moog1 :: controlChange(int number, MY_FLOAT value)
{
#if defined(_debug_)
printf("Moog1 : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_FilterQ_)
filterQ = (MY_FLOAT) 0.80 + ((MY_FLOAT) 0.1 * value * NORM_7);
else if (number == __SK_FilterSweepRate_)
filterRate = (value * NORM_7 * (MY_FLOAT) 0.0002);
else if (number == __SK_ModFrequency_)
this->setModulationSpeed(value * NORM_7 * (MY_FLOAT) 12.0);
else if (number == __SK_ModWheel_)
this->setModulationDepth(value * NORM_7);
else if (number == __SK_AfterTouch_Cont_)
adsr->setTarget(value * NORM_7);
else {
printf("Moog1 : Undefined Control Number!!\n");
}
}
MY_FLOAT Moog1 :: tick()
{
MY_FLOAT temp;
if (modDepth!=0.0) {
temp = loops[1]->tick() * modDepth;
loops[0]->setFreq(baseFreq * ((MY_FLOAT) 1.0 + temp));
}
lastOutput = SamplFlt :: tick();
return lastOutput;
}

140
src/NRev.cpp Normal file
View File

@@ -0,0 +1,140 @@
/******************************************/
/* NRev Reverb Subclass */
/* by Tim Stilson, 1998 */
/* based on CLM NRev */
/* Integrated into STK by Gary Scavone */
/* */
/* This is based on some of the famous */
/* Stanford CCRMA reverbs (NRev, KipRev) */
/* all based on the the Chowning/Moorer/ */
/* Schroeder reverberators, which use */
/* networks of simple allpass and comb */
/* delay filters. This particular */
/* arrangement consists of 6 comb */
/* filters in parallel, followed by 3 */
/* allpass filters, a lowpass filter, */
/* and another allpass in series, */
/* followed by two allpass filters in */
/* parallel with corresponding right and */
/* left outputs. */
/******************************************/
#include "NRev.h"
NRev :: NRev(MY_FLOAT T60)
{
int lens[15]={1433,1601,1867,2053,2251,2399,347,113,37,59,53,43,37,29,19};
double srscale= SRATE / 25641.0;
int val;
int i;
for (i=0; i<15; i++)
{
val = (int)floor(srscale*lens[i]);
if ((val & 1) == 0) val++;
while (!this->isprime(val)) val+=2;
lens[i]=val;
}
for (i=0; i<6; i++)
{
CdelayLine[i] = new DLineN((long) (lens[i]) + 2);
CdelayLine[i]->setDelay((long) (lens[i]));
combCoef[i] = pow(10,(-3 * lens[i] / (T60 * SRATE)));
}
for (i=0; i<8; i++)
{
APdelayLine[i] = new DLineN((long) (lens[i+6]) + 2);
APdelayLine[i]->setDelay((long) (lens[i+6]));
}
allPassCoeff = 0.7;
effectMix = 0.3;
this->clear();
}
NRev :: ~NRev()
{
int i;
for (i=0; i<6; i++) delete CdelayLine[i];
for (i=0; i<8; i++) delete APdelayLine[i];
}
void NRev :: clear()
{
int i;
for (i=0; i<6; i++) CdelayLine[i]->clear();
for (i=0; i<8; i++) APdelayLine[i]->clear();
lastOutL = 0.0;
lastOutR = 0.0;
lpLastout = 0.0;
}
void NRev :: setEffectMix(MY_FLOAT mix)
{
effectMix = mix;
}
MY_FLOAT NRev :: lastOutput()
{
return (lastOutL + lastOutR) * 0.5;
}
MY_FLOAT NRev :: lastOutputL()
{
return lastOutL;
}
MY_FLOAT NRev :: lastOutputR()
{
return lastOutR;
}
MY_FLOAT NRev :: tick(MY_FLOAT input)
{
// FPU underflow checks seem to make things much
// worse here, so I won't do them.
MY_FLOAT temp,temp0,temp1,temp2,temp3;
int i;
temp0 = 0.0;
for (i=0; i<6; i++)
{
temp = input + (combCoef[i] * CdelayLine[i]->lastOut());
temp0 += CdelayLine[i]->tick(temp);
}
for (i=0; i<3; i++)
{
temp = APdelayLine[i]->lastOut();
temp1 = allPassCoeff * temp;
temp1 += temp0;
APdelayLine[i]->tick(temp1);
temp0 = -(allPassCoeff * temp1) + temp;
}
lpLastout = 0.7*lpLastout + 0.3*temp0; // onepole LP filter
temp = APdelayLine[3]->lastOut();
temp1 = allPassCoeff * temp;
temp1 += lpLastout;
APdelayLine[3]->tick(temp1);
temp1 = -(allPassCoeff * temp1) + temp;
temp = APdelayLine[4]->lastOut();
temp2 = allPassCoeff * temp;
temp2 += temp1;
APdelayLine[4]->tick(temp2);
lastOutL = effectMix*(-(allPassCoeff * temp2) + temp);
temp = APdelayLine[5]->lastOut();
temp3 = allPassCoeff * temp;
temp3 += temp1;
APdelayLine[5]->tick(temp3);
lastOutR = effectMix*(-(allPassCoeff * temp3) + temp);
temp = (1.0 - effectMix) * input;
lastOutL += temp;
lastOutR += temp;
return (lastOutL + lastOutR) * 0.5;
}

37
src/Noise.cpp Normal file
View File

@@ -0,0 +1,37 @@
/*******************************************/
/* Noise Generator Class, */
/* by Perry R. Cook, 1995-96 */
/* White noise as often as you like. */
/*******************************************/
#include "Noise.h"
#ifdef __OS_NeXT_
#include <libc.h>
#endif
Noise :: Noise() : Object()
{
lastOutput = (MY_FLOAT) 0.0;
}
Noise :: ~Noise()
{
}
MY_FLOAT Noise :: tick()
{
#if defined(__OS_Win_) /* For Windoze */
lastOutput = (MY_FLOAT) (rand() - (int)RANDLIMIT_OVER_TWO);
#else /* This is for Linux, NeXT and SGI */
lastOutput = (MY_FLOAT) (random() - (int)RANDLIMIT_OVER_TWO);
#endif
lastOutput *= (MY_FLOAT) ONE_OVER_RANDLIMIT;
return lastOutput;
}
MY_FLOAT Noise :: lastOut()
{
return lastOutput;
}

24
src/Object.cpp Normal file
View File

@@ -0,0 +1,24 @@
/*********************************************/
/* Object Class, by Perry R. Cook, 1995-99 */
/* */
/* This is mostly here for compatibility */
/* with Objective C. We'll also stick */
/* global defines here, so everyone will */
/* see them. */
/*********************************************/
#include "Object.h"
/* This is just here for compatibility and convenience,
so there's no need to do any real calculations.
I do set up some redefinable variables in Object.h.
*/
Object :: Object()
{
}
Object :: ~Object()
{
}

101
src/OnePole.cpp Normal file
View File

@@ -0,0 +1,101 @@
/*******************************************/
/*
One Pole Filter Class,
by Perry R. Cook, 1995-96.
Added methods by Julius Smith, 2000.
The parameter gain is an additional
gain parameter applied to the filter
on top of the normalization that takes
place automatically. So the net max
gain through the system equals the
value of gain. sgain is the combina-
tion of gain and the normalization
parameter, so if you set the poleCoeff
to alpha, sgain is always set to
gain * (1.0 - fabs(alpha)).
*/
/*******************************************/
#include "OnePole.h"
OnePole :: OnePole() : Filter()
{
poleCoeff = (MY_FLOAT) 0.9;
gain = (MY_FLOAT) 1.0;
sgain = (MY_FLOAT) 0.1;
outputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT));
outputs[0] = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0.0;
}
OnePole :: OnePole(MY_FLOAT thePole) : Filter()
{
poleCoeff = thePole;
gain = (MY_FLOAT) 1.0;
sgain = (MY_FLOAT) 1.0 - fabs(thePole);
outputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT));
outputs[0] = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0.0;
}
OnePole :: ~OnePole()
{
free(outputs);
}
void OnePole :: clear()
{
outputs[0] = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0.0;
}
void OnePole :: setB0(MY_FLOAT aValue)
{
sgain = aValue;
}
void OnePole :: setNum(MY_FLOAT *values)
{
sgain = values[0];
}
void OnePole :: setA1(MY_FLOAT aValue)
{
poleCoeff = -aValue;
}
void OnePole :: setDen(MY_FLOAT *values)
{
poleCoeff = -values[0];
}
void OnePole :: setPole(MY_FLOAT aValue)
{
poleCoeff = aValue;
// Normalize gain to 1.0 max
if (poleCoeff > (MY_FLOAT) 0.0)
sgain = gain * ((MY_FLOAT) 1.0 - poleCoeff);
else
sgain = gain * ((MY_FLOAT) 1.0 + poleCoeff);
}
void OnePole :: setGain(MY_FLOAT aValue)
{
gain = aValue;
// Normalize gain to 1.0 max
if (poleCoeff > (MY_FLOAT) 0.0)
sgain = gain * ((MY_FLOAT) 1.0 - poleCoeff);
else
sgain = gain * ((MY_FLOAT) 1.0 + poleCoeff);
}
// Perform Filter Operation
MY_FLOAT OnePole :: tick(MY_FLOAT sample)
{
outputs[0] = (sgain * sample) + (poleCoeff * outputs[0]);
lastOutput = outputs[0];
return lastOutput;
}

64
src/OneZero.cpp Normal file
View File

@@ -0,0 +1,64 @@
/*******************************************/
/* One Zero Filter Class, */
/* by Perry R. Cook, 1995-96 */
/* The parameter gain is an additional */
/* gain parameter applied to the filter */
/* on top of the normalization that takes */
/* place automatically. So the net max */
/* gain through the system equals the */
/* value of gain. sgain is the combina- */
/* tion of gain and the normalization */
/* parameter, so if you set the poleCoeff */
/* to alpha, sgain is always set to */
/* gain / (1.0 - fabs(alpha)). */
/*******************************************/
#include "OneZero.h"
OneZero :: OneZero()
{
gain = (MY_FLOAT) 1.0;
zeroCoeff = (MY_FLOAT) 1.0;
sgain = (MY_FLOAT) 0.5;
inputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT));
this->clear();
}
OneZero :: ~OneZero()
{
free(inputs);
}
void OneZero :: clear()
{
inputs[0] = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0.0;
}
void OneZero :: setGain(MY_FLOAT aValue)
{
gain = aValue;
if (zeroCoeff > 0.0) // Normalize gain to 1.0 max
sgain = gain / ((MY_FLOAT) 1.0 + zeroCoeff);
else
sgain = gain / ((MY_FLOAT) 1.0 - zeroCoeff);
}
void OneZero :: setCoeff(MY_FLOAT aValue)
{
zeroCoeff = aValue;
if (zeroCoeff > 0.0) // Normalize gain to 1.0 max
sgain = gain / ((MY_FLOAT) 1.0 + zeroCoeff);
else
sgain = gain / ((MY_FLOAT) 1.0 - zeroCoeff);
}
MY_FLOAT OneZero :: tick(MY_FLOAT sample) // Perform Filter Operation
{
MY_FLOAT temp;
temp = sgain * sample;
lastOutput = (inputs[0] * zeroCoeff) + temp;
inputs[0] = temp;
return lastOutput;
}

113
src/PRCRev.cpp Normal file
View File

@@ -0,0 +1,113 @@
/*******************************************/
/* PRCRev, a simple reverb unit */
/* by Perry Cook, 1996. */
/* Incorporated into the Reverb superclass */
/* by Gary Scavone, 1998. */
/* */
/* This is based on some of the famous */
/* Stanford CCRMA reverbs (NRev, KipRev) */
/* all based on the the Chowning/Moorer/ */
/* Schroeder reverberators, which use */
/* networks of simple allpass and comb */
/* delay filters. This particular */
/* structure consists of 2 allpass units */
/* in series followed by 2 comb filters in */
/* parallel. */
/*******************************************/
#include "PRCRev.h"
PRCRev :: PRCRev(MY_FLOAT T60)
{
int lens[4]={353,1097,1777,2137};
double srscale = SRATE / 44100.0;
int val, i;
if (SRATE < 44100.0) {
for (i=0; i<4; i++) {
val = (int) floor(srscale * lens[i]);
if ((val & 1) == 0) val++;
while (!this->isprime(val)) val += 2;
lens[i] = val;
}
}
for (i=0; i<2; i++)
{
APdelayLine[i] = new DLineN(lens[i] + 2);
APdelayLine[i]->setDelay(lens[i]);
CdelayLine[i] = new DLineN(lens[i+2] + 2);
CdelayLine[i]->setDelay(lens[i+2]);
combCoeff[i] = pow(10,(-3 * lens[i+2] / (T60 * SRATE)));
}
allPassCoeff = (MY_FLOAT) 0.7;
effectMix = (MY_FLOAT) 0.5;
this->clear();
}
PRCRev :: ~PRCRev()
{
delete APdelayLine[0];
delete APdelayLine[1];
delete CdelayLine[0];
delete CdelayLine[1];
}
void PRCRev :: clear()
{
APdelayLine[0]->clear();
APdelayLine[1]->clear();
CdelayLine[0]->clear();
CdelayLine[1]->clear();
lastOutL = (MY_FLOAT) 0.0;
lastOutR = (MY_FLOAT) 0.0;
}
void PRCRev :: setEffectMix(MY_FLOAT mix)
{
effectMix = mix;
}
MY_FLOAT PRCRev :: lastOutput()
{
return (lastOutL + lastOutR) * (MY_FLOAT) 0.5;
}
MY_FLOAT PRCRev :: lastOutputL()
{
return lastOutL;
}
MY_FLOAT PRCRev :: lastOutputR()
{
return lastOutR;
}
MY_FLOAT PRCRev :: tick(MY_FLOAT input)
{
MY_FLOAT temp,temp0,temp1,temp2,temp3;
temp = APdelayLine[0]->lastOut();
temp0 = allPassCoeff * temp;
temp0 += input;
APdelayLine[0]->tick(temp0);
temp0 = -(allPassCoeff * temp0) + temp;
temp = APdelayLine[1]->lastOut();
temp1 = allPassCoeff * temp;
temp1 += temp0;
APdelayLine[1]->tick(temp1);
temp1 = -(allPassCoeff * temp1) + temp;
temp2 = temp1 + (combCoeff[0] * CdelayLine[0]->lastOut());
temp3 = temp1 + (combCoeff[1] * CdelayLine[1]->lastOut());
lastOutL = effectMix * (CdelayLine[0]->tick(temp2));
lastOutR = effectMix * (CdelayLine[1]->tick(temp3));
temp = (MY_FLOAT) (1.0 - effectMix) * input;
lastOutL += temp;
lastOutR += temp;
return (lastOutL + lastOutR) * (MY_FLOAT) 0.5;
}

63
src/PercFlut.cpp Normal file
View File

@@ -0,0 +1,63 @@
/******************************************/
/* Percussive Flute Subclass */
/* of Algorithm 4 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/******************************************/
#include "PercFlut.h"
PercFlut :: PercFlut() : FM4Alg4()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file1[128];
char file2[128];
char file3[128];
char file4[128];
strcpy(file1, RAWWAVE_PATH);
strcpy(file2, RAWWAVE_PATH);
strcpy(file3, RAWWAVE_PATH);
strcpy(file4, RAWWAVE_PATH);
this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"),
strcat(file2,"rawwaves/sinewave.raw"),
strcat(file3,"rawwaves/sinewave.raw"),
strcat(file4,"rawwaves/fwavblnk.raw"));
this->setRatio(0,(MY_FLOAT) (1.50 * 1.000));
this->setRatio(1,(MY_FLOAT) (3.00 * 0.995));
this->setRatio(2,(MY_FLOAT) (2.99 * 1.005));
this->setRatio(3,(MY_FLOAT) (6.00 * 0.997));
gains[0] = __FM4Op_gains[99];
gains[1] = __FM4Op_gains[71];
gains[2] = __FM4Op_gains[93];
gains[3] = __FM4Op_gains[85];
adsr[0]->setAllTimes((MY_FLOAT) 0.05,(MY_FLOAT) 0.05,
__FM4Op_susLevels[14],(MY_FLOAT) 0.05);
adsr[1]->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.50,
__FM4Op_susLevels[13],(MY_FLOAT) 0.5);
adsr[2]->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.30,
__FM4Op_susLevels[11],(MY_FLOAT) 0.05);
adsr[3]->setAllTimes((MY_FLOAT) 0.02,(MY_FLOAT) 0.05,
__FM4Op_susLevels[13],(MY_FLOAT) 0.01);
twozero->setGain((MY_FLOAT) 0.0);
modDepth = (MY_FLOAT) 0.005;
}
void PercFlut :: setFreq(MY_FLOAT frequency)
{
baseFreq = frequency;
}
void PercFlut :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
gains[0] = amp * __FM4Op_gains[99] * 0.5;
gains[1] = amp * __FM4Op_gains[71] * 0.5;
gains[2] = amp * __FM4Op_gains[93] * 0.5;
gains[3] = amp * __FM4Op_gains[85] * 0.5;
this->setFreq(freq);
this->keyOn();
#if defined(_debug_)
printf("PercFlut : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}

84
src/Plucked.cpp Normal file
View File

@@ -0,0 +1,84 @@
/******************************************/
/* Karplus-Strong plucked string model */
/* by Perry Cook, 1995-96 */
/* */
/* There exist at least two patents, */
/* assigned to Stanford, bearing the */
/* names of Karplus and/or Strong. */
/******************************************/
#include "Plucked.h"
Plucked :: Plucked(MY_FLOAT lowestFreq)
{
length = (long) (SRATE / lowestFreq + 1);
loopGain = (MY_FLOAT) 0.999;
delayLine = new DLineA(length);
loopFilt = new OneZero;
pickFilt = new OnePole;
noise = new Noise;
this->clear();
}
Plucked :: ~Plucked()
{
delete delayLine;
delete loopFilt;
delete pickFilt;
delete noise;
}
void Plucked :: clear()
{
delayLine->clear();
loopFilt->clear();
pickFilt->clear();
}
void Plucked :: setFreq(MY_FLOAT frequency)
{
MY_FLOAT delay;
delay = (SRATE / frequency) - (MY_FLOAT) 0.5; /* length - delays */
delayLine->setDelay(delay);
loopGain = (MY_FLOAT) 0.995 + (frequency * (MY_FLOAT) 0.000005);
if (loopGain>1.0) loopGain = (MY_FLOAT) 0.99999;
}
void Plucked :: pluck(MY_FLOAT amplitude)
{
long i;
pickFilt->setPole((MY_FLOAT) 0.999 - (amplitude * (MY_FLOAT) 0.15));
pickFilt->setGain(amplitude * (MY_FLOAT) 0.5);
for (i=0;i<length;i++)
// fill delay with noise additively with current contents
delayLine->tick(delayLine->lastOut() * (MY_FLOAT) 0.6
+ pickFilt->tick(noise->tick()));
}
void Plucked :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
this->setFreq(freq);
this->pluck(amp);
#if defined(_debug_)
printf("Plucked : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}
void Plucked :: noteOff(MY_FLOAT amp)
{
loopGain = (MY_FLOAT) 1.0 - amp;
#if defined(_debug_)
printf("Plucked : NoteOff: Amp=%lf\n",amp);
#endif
}
MY_FLOAT Plucked :: tick()
{
/* check this out */
/* here's the whole inner loop of the instrument!! */
lastOutput = delayLine->tick(loopFilt->tick(delayLine->lastOut() * loopGain));
lastOutput *= (MY_FLOAT) 3.0;
return lastOutput;
}

90
src/Plucked2.cpp Normal file
View File

@@ -0,0 +1,90 @@
/******************************************/
/* Enhanced (Jaffe-Smith, Smith, others) */
/* Karplus-Strong plucked model */
/* by Perry Cook, 1995-96 */
/* This is the super-class, with no */
/* excitation specified. So this one by */
/* itself doesn't make any sound. */
/******************************************/
#include "Plucked2.h"
Plucked2 :: Plucked2(MY_FLOAT lowestFreq)
{
length = (long) (SRATE / lowestFreq + 1);
baseLoopGain = (MY_FLOAT) 0.995;
loopGain = (MY_FLOAT) 0.999;
delayLine = new DLineA(length);
delayLine2 = new DLineA(length);
combDelay = new DLineL(length);
filter = new OneZero;
filter2 = new OneZero;
pluckAmp = (MY_FLOAT) 0.3;
pluckPos = (MY_FLOAT) 0.4;
detuning = (MY_FLOAT) 0.995;
lastFreq = lowestFreq * (MY_FLOAT) 2.0;
lastLength = length * (MY_FLOAT) 0.5;
}
Plucked2 :: ~Plucked2()
{
delete delayLine;
delete delayLine2;
delete combDelay;
delete filter;
delete filter2;
}
void Plucked2 :: clear()
{
delayLine->clear();
delayLine2->clear();
combDelay->clear();
filter->clear();
filter2->clear();
}
void Plucked2 :: setFreq(MY_FLOAT frequency)
{
lastFreq = frequency;
lastLength = ((MY_FLOAT) SRATE / lastFreq); /* length - delays */
delayLine->setDelay((lastLength / detuning) - (MY_FLOAT) 0.5);
delayLine2->setDelay((lastLength * detuning) - (MY_FLOAT) 0.5);
loopGain = baseLoopGain + (frequency * (MY_FLOAT) 0.000005);
if (loopGain>1.0) loopGain = (MY_FLOAT) 0.99999;
}
void Plucked2 :: setDetune(MY_FLOAT detune)
{
detuning = detune;
delayLine->setDelay((lastLength / detuning) - (MY_FLOAT) 0.5);
delayLine2->setDelay((lastLength * detuning) - (MY_FLOAT) 0.5);
}
void Plucked2 :: setFreqAndDetune(MY_FLOAT frequency,MY_FLOAT detune)
{
lastFreq = frequency;
detuning = detune;
this->setFreq(frequency);
}
void Plucked2 :: setPluckPos(MY_FLOAT position)
{
pluckPos = position;
}
void Plucked2 :: setBaseLoopGain(MY_FLOAT aGain)
{
baseLoopGain = aGain;
loopGain = baseLoopGain + (lastFreq * (MY_FLOAT) 0.000005);
if (loopGain>1.0) loopGain = (MY_FLOAT) 0.99999;
}
void Plucked2 :: noteOff(MY_FLOAT amp)
{
loopGain = ((MY_FLOAT) 1.0 - amp) * (MY_FLOAT) 0.5;
#if defined(_debug_)
printf("Plucked2 : NoteOff: Amp=%lf\n",amp);
#endif
}

68
src/PoleZero.cpp Normal file
View File

@@ -0,0 +1,68 @@
/*******************************************/
/* PoleZero (1-pole, 1-zero) Filter Class */
/* by Gary P. Scavone, 1999 */
/* */
/* See books on filters to understand */
/* more about how this works. Nothing */
/* out of the ordinary in this version. */
/*******************************************/
#include "PoleZero.h"
PoleZero :: PoleZero() : Filter()
{
inputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT));
outputs = (MY_FLOAT *) malloc(sizeof(MY_FLOAT));
b0Coeff = (MY_FLOAT) 1.0;
b1Coeff = (MY_FLOAT) 0.0;
a1Coeff = (MY_FLOAT) 0.0;
gain = (MY_FLOAT) 1.0;
this->clear();
}
PoleZero :: ~PoleZero()
{
free(inputs);
free(outputs);
}
void PoleZero :: clear()
{
inputs[0] = (MY_FLOAT) 0.0;
outputs[0] = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0.0;
}
void PoleZero :: setA1(MY_FLOAT coeff)
{
a1Coeff = coeff;
}
void PoleZero :: setB0(MY_FLOAT coeff)
{
b0Coeff = coeff;
}
void PoleZero :: setB1(MY_FLOAT coeff)
{
b1Coeff = coeff;
}
void PoleZero :: setGain(MY_FLOAT aValue)
{
gain = aValue;
}
// PoleZero is one pole, one zero filter
// Look it up in your favorite DSP text
MY_FLOAT PoleZero :: tick(MY_FLOAT sample)
{
MY_FLOAT in_sample = gain*sample;
lastOutput = b0Coeff*in_sample + b1Coeff*inputs[0] - a1Coeff*outputs[0];
inputs[0] = in_sample;
outputs[0] = lastOutput;
return lastOutput;
}

134
src/RawWvIn.cpp Normal file
View File

@@ -0,0 +1,134 @@
/*******************************************/
/* RawWvIn Input Class, */
/* by Gary P. Scavone, 2000 */
/* */
/* This object inherits from WvIn and is */
/* used to open raw 16-bit data (signed */
/* integer) files for playback. */
/* */
/* STK RawWave files are assumed to be */
/* monaural and big-endian. */
/*******************************************/
#include "RawWvIn.h"
#include <sys/stat.h>
#include <sys/types.h>
#ifdef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
RawWvIn :: RawWvIn(char *fileName, const char *mode)
{
char msg[256];
// check mode string
if ( strcmp(mode,"oneshot") && strcmp(mode,"looping") ) {
sprintf(msg, "RawWvIn: constructor parameter 'mode' must be oneshot or looping only.\n");
throw StkError(msg, StkError::FUNCTION_SYNTAX);
}
// Use the system call "stat" to determine the file length
struct stat filestat;
if (stat(fileName, &filestat) == -1) {
// Opening file failed
sprintf(msg, "RawWvIn: Couldn't stat or find file (%s).\n", fileName);
throw StkError(msg, StkError::FILE_NOT_FOUND);
}
fileSize = (long) filestat.st_size / 2; // length in 2-byte samples
bufferSize = fileSize;
if (fileSize > MAX_FILE_LOAD_SIZE) {
printf("\nRawWvIn: The .WAV file (%s) has more than %d samples and\n",
fileName, MAX_FILE_LOAD_SIZE);
printf("will be loaded incrementally from disk. Normalization will be disabled.\n");
chunking = 1;
bufferSize = LOAD_BUFFER_SIZE;
}
// Open the file and read samples into data[]
fd = fopen(fileName,"rb");
if (!fd) {
sprintf(msg, "RawWvIn: Couldn't open or find file (%s).\n", fileName);
throw StkError(msg, StkError::FILE_NOT_FOUND);
}
// Setup for looping or one-shot playback
if (!strcmp(mode,"looping"))
looping = 1;
else // default = oneshot
looping = 0;
channels = 1; // All STK rawwave files are mono
data = (MY_FLOAT *) new MY_FLOAT[(bufferSize+1)*channels];
fseek(fd,0,SEEK_SET);
dataOffset = 0;
this->getData(0); // Read samples into data[]
phaseOffset = (MY_FLOAT) 0.0;
rate = (MY_FLOAT) 1.0;
interpolate = 0;
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
this->reset();
// finally, let's normalize the data by default
this->normalize();
}
RawWvIn :: ~RawWvIn()
{
}
void RawWvIn :: getData(long index)
{
/* Compare index to current readPointer and modify as needed.
* The following while() loops will only execute on calls subsequent
* to class instantiation ... and thus, only when "chunking".
*/
while (index < readPointer) {
readPointer -= LOAD_BUFFER_SIZE;
bufferSize = LOAD_BUFFER_SIZE;
if (readPointer < 0) {
bufferSize += readPointer;
readPointer = 0;
}
}
while (index >= readPointer+bufferSize) {
readPointer += LOAD_BUFFER_SIZE;
bufferSize = LOAD_BUFFER_SIZE;
if (readPointer+LOAD_BUFFER_SIZE >= fileSize) {
bufferSize = fileSize - readPointer;
}
}
fseek(fd, (long)(readPointer*2), SEEK_SET);
long length = bufferSize;
int end_of_file = (readPointer+bufferSize == fileSize);
if (!end_of_file) length += 1;
// Read samples into data[]. Use MY _FLOAT data structure to store INT16 samples
INT16 *buf = (INT16 *)data;
fread(buf, length, 2, fd);
// Convert in place (unpack) to MY_FLOAT from the end of the array
for (int i=length-1; i>=0; i--) {
#ifdef __LITTLE_ENDIAN__
swap16((unsigned char *)(buf+i));
#endif
data[i] = buf[i];
if (chunking) data[i] *= 0.00003051;
}
// fill in the extra sample frame for interpolation
if (end_of_file) {
if (looping)
data[bufferSize] = data[0];
else
data[bufferSize] = data[(bufferSize-1)];
}
if (!chunking) {
fclose(fd);
fd = 0;
}
}

77
src/RawWvOut.cpp Normal file
View File

@@ -0,0 +1,77 @@
/*******************************************/
/* RawWvOut Output Class */
/* by Gary P. Scavone, 1999 */
/* */
/* This object spits samples into a raw */
/* 16-bit data (signed integer) file. */
/* */
/* STK RawWave files are assumed to be */
/* monaural and big-endian. */
/*******************************************/
#include "RawWvOut.h"
#ifdef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
RawWvOut :: RawWvOut(char *fileName, int chans)
{
char tempName[128];
char msg[128];
if (chans != 1) {
sprintf(msg, "RawWvOut: STK rawwave files are always monaural (channels = %d not supported)!\n", chans);
throw StkError(msg, StkError::FUNCTION_SYNTAX);
}
channels = chans;
strcpy(tempName,fileName);
if (strstr(tempName,".raw") == NULL) strcat(tempName,".raw");
fd = fopen(tempName,"wb");
if (!fd) {
sprintf(msg, "RawWvOut: Could not create soundfile: %s\n", tempName);
throw StkError(msg, StkError::FILE_ERROR);
}
printf("\nCreating soundfile: %s\n", tempName);
data_length = FILE_BUFFER_SIZE*channels;
data = (INT16 *) new INT16[data_length];
}
RawWvOut :: ~RawWvOut()
{
double temp;
fwrite(data,2,counter,fd);
temp = (double) totalCount * ONE_OVER_SRATE;
printf("%f Seconds Computed\n\n", temp);
fclose(fd);
}
void RawWvOut :: tick(MY_FLOAT sample)
{
data[counter] = (INT16) (sample * 32000.0);
#ifdef __LITTLE_ENDIAN__
swap16 ((unsigned char *)&data[counter]);
#endif
counter++;
totalCount++;
if (counter == data_length) {
fwrite(data,2,data_length,fd);
counter = 0;
}
}
void RawWvOut :: mtick(MY_MULTI samples)
{
data[counter] = (INT16) (*samples * 32000.0);
#ifdef __LITTLE_ENDIAN__
swap16 ((unsigned char *)&data[counter]);
#endif
counter++;
totalCount++;
if (counter == data_length) {
fwrite(data,2,data_length,fd);
counter = 0;
}
}

51
src/ReedTabl.cpp Normal file
View File

@@ -0,0 +1,51 @@
/**********************************************/
/* One break point linear reed table object */
/* by Perry R. Cook, 1995-96 */
/* Consult McIntyre, Schumacher, & Woodhouse */
/* Smith, Hirschman, Cook, Scavone, */
/* more for information. */
/**********************************************/
#include "ReedTabl.h"
ReedTabl :: ReedTabl() : Object()
{
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; /* Offset is a bias, related to reed rest position */
}
void ReedTabl :: setSlope(MY_FLOAT aValue)
{
slope = aValue; /* Slope corresponds loosely to reed stiffness */
}
MY_FLOAT ReedTabl :: lookup(MY_FLOAT deltaP)
{
return this->tick(deltaP);
}
MY_FLOAT ReedTabl :: tick(MY_FLOAT deltaP)
/* Perform "Table Lookup" by direct clipped */
/* linear function calculation */
{ /* deltaP is differential reed pressure */
lastOutput = offSet + (slope * deltaP); /* compute basic non-linearity */
if (lastOutput > 1.0) lastOutput = (MY_FLOAT) 1.0; /* if other way, reed slams shut */
if (lastOutput < -1.0) lastOutput = (MY_FLOAT) -1.0; /* if all the way open, acts like open end */
return lastOutput;
}
MY_FLOAT ReedTabl :: lastOut()
{
return lastOutput;
}

43
src/Reverb.cpp Normal file
View File

@@ -0,0 +1,43 @@
/********************************************/
/* Reverb Abstract Class, */
/* by Tim Stilson, 1998 */
/* */
/* Integrated into STK by Gary Scavone */
/* with T60 argument. */
/********************************************/
#include "Reverb.h"
Reverb :: Reverb()
{
}
Reverb :: ~Reverb()
{
}
MY_FLOAT Reverb :: tick(MY_FLOAT sample)
{
printf("Warning: Using virtual function Reverb :: tick()\n");
return 0;
}
void Reverb :: setEffectMix(MY_FLOAT mix)
{
}
int Reverb :: isprime(int val)
{
int i;
if (val == 2) return 1;
if (val & 1)
{
for (i=3; i<(int)sqrt((double)val)+1; i+=2)
{
if ((val%i) == 0) return 0;
}
return 1; /* prime */
}
else return 0; /* even */
}

66
src/Rhodey.cpp Normal file
View File

@@ -0,0 +1,66 @@
/******************************************/
/* Fender Rhodes Electric Piano Subclass */
/* of Algorithm 5 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/******************************************/
#include "Rhodey.h"
Rhodey :: Rhodey() : FM4Alg5()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file1[128];
char file2[128];
char file3[128];
char file4[128];
strcpy(file1, RAWWAVE_PATH);
strcpy(file2, RAWWAVE_PATH);
strcpy(file3, RAWWAVE_PATH);
strcpy(file4, RAWWAVE_PATH);
this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"),
strcat(file2,"rawwaves/sinewave.raw"),
strcat(file3,"rawwaves/sinewave.raw"),
strcat(file4,"rawwaves/fwavblnk.raw"));
this->setRatio(0,(MY_FLOAT) 1.0);
this->setRatio(1,(MY_FLOAT) 0.5);
this->setRatio(2,(MY_FLOAT) 1.0);
this->setRatio(3,(MY_FLOAT) 15.0);
gains[0] = __FM4Op_gains[99];
gains[1] = __FM4Op_gains[90];
gains[2] = __FM4Op_gains[99];
gains[3] = __FM4Op_gains[67];
adsr[0]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
adsr[1]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
adsr[2]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.00,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
adsr[3]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.25,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
twozero->setGain((MY_FLOAT) 1.0);
}
Rhodey :: ~Rhodey()
{
}
void Rhodey :: setFreq(MY_FLOAT frequency)
{
baseFreq = frequency * (MY_FLOAT) 2.0;
waves[0]->setFreq(baseFreq * ratios[0]);
waves[1]->setFreq(baseFreq * ratios[1]);
waves[2]->setFreq(baseFreq * ratios[2]);
waves[3]->setFreq(baseFreq * ratios[3]);
}
void Rhodey :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
gains[0] = amp * __FM4Op_gains[99];
gains[1] = amp * __FM4Op_gains[90];
gains[2] = amp * __FM4Op_gains[99];
gains[3] = amp * __FM4Op_gains[67];
this->setFreq(freq);
this->keyOn();
#if defined(_debug_)
printf("Rhodey : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}

1810
src/RtAudio.cpp Normal file

File diff suppressed because it is too large Load Diff

102
src/RtDuplex.cpp Normal file
View File

@@ -0,0 +1,102 @@
/*******************************************/
/* Real-Time Duplex Input/Output Class, */
/* by Gary P. Scavone, 1999-2000 */
/* */
/* This object opens the sound i/o */
/* device, reads buffers in from it, and */
/* pokes buffers of samples out to it. */
/* */
/* At the moment, duplex mode is possible */
/* only on Linux (OSS), IRIX, and */
/* Windows95/98 platforms. */
/*******************************************/
#include "RtDuplex.h"
#if (defined(__STK_REALTIME_) )
RtDuplex :: RtDuplex(int chans, MY_FLOAT srate, int device)
{
// We'll let RTSoundIO deal with channel and srate limitations.
channels = chans;
sound_dev = new RtAudio(channels, srate, "duplex", device);
writeCounter = 0;
gain = 0.00003052;
data_length = RT_BUFFER_SIZE*channels;
indata = (INT16 *) new INT16[data_length+10];
outdata = (INT16 *) new INT16[data_length+10];
insamples = new MY_FLOAT[channels];
#if (defined(__STK_REALTIME_) && defined(__OS_IRIX_))
// This is necessary under IRIX because it scales the input by 0.5
// when using single-channel input.
if (channels == 1) gain *= 2;
#endif
// Read half a buffer to get started
readCounter = data_length / 2;
sound_dev->recordBuffer(&indata[readCounter],(data_length/2));
}
RtDuplex :: ~RtDuplex()
{
sound_dev->playBuffer(outdata,writeCounter);
writeCounter = 0;
while (writeCounter<data_length) {
outdata[writeCounter++] = 0;
}
sound_dev->playBuffer(outdata,writeCounter);
sound_dev->playBuffer(outdata,writeCounter); // Are these extra writes necessary?
sound_dev->playBuffer(outdata,writeCounter);
delete [ ] insamples;
delete [ ] indata;
delete [ ] outdata;
delete sound_dev;
}
MY_FLOAT RtDuplex :: tick(MY_FLOAT outsample)
{
// We offset the data read and data write calls by RT_BUFFER_SIZE / 2
if (readCounter >= data_length) {
sound_dev->recordBuffer(indata,data_length);
readCounter = 0;
}
*insamples = (MY_FLOAT) indata[readCounter++];
if (channels > 1) {
int i;
for (i=1;i<channels;i++)
*insamples += (MY_FLOAT) indata[readCounter++];
*insamples /= i;
}
*insamples *= gain;
for (int i=0;i<channels;i++)
outdata[writeCounter++] = (short) (outsample * 32000.0);
if (writeCounter >= data_length) {
sound_dev->playBuffer(outdata,data_length);
writeCounter = 0;
}
return *insamples;
}
MY_MULTI RtDuplex :: mtick(MY_MULTI outsamples)
{
int i;
// We offset the data read and data write calls by RT_BUFFER_SIZE / 2
if (readCounter >= data_length) {
sound_dev->recordBuffer(indata,data_length);
readCounter = 0;
}
for (i=0;i<channels;i++)
insamples[i] = (MY_FLOAT) (indata[readCounter++]*gain);
for (i=0;i<channels;i++)
outdata[writeCounter++] = (short) (*outsamples++ * 32000.0);
if (writeCounter >= data_length) {
sound_dev->playBuffer(outdata,data_length);
writeCounter = 0;
}
return insamples;
}
#endif

1078
src/RtMidi.cpp Normal file

File diff suppressed because it is too large Load Diff

146
src/RtWvIn.cpp Normal file
View File

@@ -0,0 +1,146 @@
/*******************************************/
/* RtWvIn Input Class, */
/* by Gary P. Scavone, 1999-2000 */
/* */
/* This object inherits from WvIn and is */
/* used to read in realtime 16-bit data */
/* from a computer's audio port. */
/* */
/* NOTE: This object is NOT intended for */
/* use in achieving simultaneous realtime */
/* audio input/output (together with */
/* RtWvOut). Under certain circumstances */
/* such a scheme is possible, though you */
/* should definitely know what you are */
/* doing before trying. For safer "full- */
/* duplex" operation, use the RtDuplex */
/* class. */
/*******************************************/
#include "RtWvIn.h"
RtWvIn :: RtWvIn(int chans, MY_FLOAT srate, int device)
{
chunking = 1;
looping = 0;
sound_dev = new RtAudio(chans, srate, "record", device);
channels = chans;
bufferSize = RT_BUFFER_SIZE;
data = 0;
rtdata = (INT16 *) new INT16[(bufferSize+1)*channels];
lastSamples = (INT16 *) new INT16[channels];
for (int i=0;i<channels;i++) {
lastSamples[i] = (INT16) 0.0;
}
this->getData(0);
rate = (MY_FLOAT) srate / SRATE;
if (fmod(rate, 1.0) > 0.0) interpolate = 1;
else interpolate = 0;
phaseOffset = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
this->reset();
gain = 0.00003052;
#if (defined(__STK_REALTIME_) && defined(__OS_IRIX_))
// This is necessary under IRIX because it scales the input by 0.5
// when using single-channel input.
if (channels == 1) gain *= 2;
#endif
}
RtWvIn :: ~RtWvIn()
{
delete sound_dev;
if (rtdata) {
delete [ ] rtdata;
rtdata = 0;
}
if (lastSamples) {
delete [ ] lastSamples;
lastSamples = 0;
}
}
void RtWvIn :: setRate(MY_FLOAT aRate)
{
// Negative rates not allowed for realtime input
rate = fabs(aRate);
if (fmod(rate, 1.0) != 0.0) interpolate = 1;
else interpolate = 0;
}
void RtWvIn :: addTime(MY_FLOAT aTime)
{
// Negative time shift no allowed for realtime input
time += fabs(aTime);
}
void RtWvIn :: setLooping(int aLoopStatus)
{
// No looping for realtime data.
looping = 0;
}
long RtWvIn :: getSize()
{
return bufferSize;
}
void RtWvIn :: getData(long index)
{
static long temp = RT_BUFFER_SIZE*channels;
sound_dev->recordBuffer(&rtdata[channels],temp);
/* Fill in the extra sample frame for interpolation.
* We do this by pre-pending the last sample frame
* from the previous input buffer to the current one.
*/
for (int i=0;i<channels;i++) {
rtdata[i] = lastSamples[i];
lastSamples[i] = rtdata[temp+i];
}
}
int RtWvIn :: informTick()
{
static MY_FLOAT alpha;
static long index;
if (time >= bufferSize) {
this->getData(0);
while (time >= bufferSize)
time -= bufferSize;
}
// integer part of time address
index = (long) time;
if (interpolate) {
// fractional part of time address
alpha = time - (MY_FLOAT) index;
index *= channels;
for (int i=0;i<channels;i++) {
// Do linear interpolation
lastOutput[i] = (MY_FLOAT) rtdata[index];
lastOutput[i] += alpha * ((MY_FLOAT) rtdata[index+channels] - lastOutput[i]);
lastOutput[i] *= gain;
index++;
}
}
else {
index *= channels;
for (int i=0;i<channels;i++) {
lastOutput[i] = rtdata[index++];
lastOutput[i] *= gain;
}
}
// increment time, which can be negative
time += rate;
return finished;
}

81
src/RtWvOut.cpp Normal file
View File

@@ -0,0 +1,81 @@
/*******************************************/
/* Real-Time Audio Output Class, */
/* by Perry R. Cook, 1996 */
/* Revised by Gary P. Scavone, 2000 */
/* */
/* This object opens a realtime soundout */
/* device, and pokes buffers of samples */
/* into it. */
/*******************************************/
#include "RtWvOut.h"
#if defined(__STK_REALTIME_)
RtWvOut :: RtWvOut(int chans, int device)
{
// We'll let RTSoundIO deal with channel and srate limitations.
channels = chans;
sound_dev = new RtAudio(channels, SRATE, "play", device);
data_length = RT_BUFFER_SIZE*channels;
// Add a few extra samples for good measure
data = (INT16 *) new INT16[data_length+10];
}
RtWvOut :: ~RtWvOut()
{
sound_dev->playBuffer(data,counter);
counter = 0;
while (counter<data_length) {
data[counter++] = 0;
}
sound_dev->playBuffer(data,counter);
sound_dev->playBuffer(data,counter); // Are these extra writes necessary?
sound_dev->playBuffer(data,counter);
delete sound_dev;
}
void RtWvOut :: tick(MY_FLOAT sample)
{
for (int i=0;i<channels;i++)
data[counter++] = (INT16) (sample * 32000.0);
if (counter >= data_length) {
sound_dev->playBuffer(data,data_length);
counter = 0;
}
}
void RtWvOut :: mtick(MY_MULTI samples)
{
for (int i=0;i<channels;i++)
data[counter++] = (INT16) (*samples++ * 32000.0);
if (counter >= data_length) {
sound_dev->playBuffer(data,data_length);
counter = 0;
}
}
//windows stop and start methods ... because windoze sucks
#if (defined(__OS_Win_) )
void RtWvOut :: stopPlay() {
sound_dev->stopPlay();
}
void RtWvOut :: startPlay() {
sound_dev->startPlay();
}
void RtWvOut :: stopRecord() {
sound_dev->stopRecord();
}
void RtWvOut :: startRecord() {
sound_dev->startRecord();
}
#endif // extra windoze crap
#endif

344
src/SKINI11.cpp Normal file
View File

@@ -0,0 +1,344 @@
/******************************************/
/* 3nd generation SKINI Text File Reader */
/* Class, by Perry R. Cook, 1999 */
/* This Object can open a SKINI File */
/* and parse it. The file spec is mine */
/* and mine alone, but it's all text so */
/* that should help you figuring it out. */
/* */
/* SKINI (Synthesis toolKit Instrument */
/* Network Interface) is like MIDI, but */
/* allows for floating point control */
/* changes, note numbers, etc. Example: */
/* noteOn 60.01 111.132 plays a sharp */
/* middle C with a velocity of 111.132 */
/* See SKINI11.txt for more information */
/* */
/******************************************/
#include "SKINI11.h"
SKINI11 :: SKINI11(char *fileName) /* Constructor for reading SKINI files */
{ /* Use nextMessage() method */
myFile = fopen(fileName,"r");
if ((int) myFile < 0) printf("SKINI11: Can't open SKINI score file\n");
this->nextMessage();
}
SKINI11 :: SKINI11() /* Constructor to use this object for parsing */
{ /* SKINI Strings (coming over socket for example */
} /* Use parseThis() method with string argument */
SKINI11 :: ~SKINI11()
{
}
/***************** SOME HANDY ROUTINES *******************/
#include "SKINI11.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 SKINI11 :: 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 = -1;
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[0][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 SKINI11 :: nextMessage()
{
int notDone;
char inputString[1024];
notDone = 1;
while (notDone) {
notDone = 0;
if (!fgets(inputString,1024,myFile)) {
printf("//End of Score. Thanks for using SKINI0.9 Bye Bye!!\n");
messageType = -1;
return messageType;
}
else if (parseThis(inputString) == 0) {
notDone = 1;
}
}
return messageType;
}
long SKINI11 :: getType()
{
return messageType;
}
long SKINI11 :: getChannel()
{
return channel;
}
MY_FLOAT SKINI11 :: getDelta()
{
return deltaTime;
}
MY_FLOAT SKINI11 :: getByteTwo()
{
return byteTwo;
}
long SKINI11 :: getByteTwoInt()
{
return byteTwoInt;
}
MY_FLOAT SKINI11 :: getByteThree()
{
return byteThree;
}
long SKINI11 :: getByteThreeInt()
{
return byteThreeInt;
}
char* SKINI11 :: getRemainderString()
{
return remainderString;
}
char* SKINI11 :: getMessageTypeString()
{
return msgTypeString;
}
char sk_tempString[1024];
char* SKINI11 :: whatsThisType(long type)
{
int i = 0;
sk_tempString[0] = 0;
for (i=0;i<__SK_MaxMsgTypes_;i++) {
if (type == skini_msgs[i].type) {
strcat(sk_tempString,skini_msgs[i].messageString);
strcat(sk_tempString,",");
}
}
return sk_tempString;
}
char* SKINI11 :: whatsThisController(long contNum)
{
int i = 0;
sk_tempString[0] = 0;
for (i=0;i<__SK_MaxMsgTypes_;i++) {
if (skini_msgs[i].type == __SK_ControlChange_
&& contNum == skini_msgs[i].data2) {
strcat(sk_tempString,skini_msgs[i].messageString);
strcat(sk_tempString,",");
}
}
return sk_tempString;
}
/************ Test Main Program *****************/
/*
void main(int argc,char *argv[])
{
SKINI11 testFile(argv[1]);
while(testFile.nextMessage() > 0) ;
}
*/

130
src/SKINI11.tbl Normal file
View File

@@ -0,0 +1,130 @@
#include "SKINI11.msg"
#define __SK_MaxMsgTypes_ 128
struct SKINISpec { char messageString[32];
long type;
long data2;
long data3;
};
/* SEE COMMENT BLOCK AT BOTTOM FOR FIELDS AND USES */
/* MessageString ,type, ch?, data2 , data3 */
struct SKINISpec skini_msgs[__SK_MaxMsgTypes_] =
{
{"NoteOff" , __SK_NoteOff_, SK_DBL, SK_DBL},
{"NoteOn" , __SK_NoteOn_, SK_DBL, SK_DBL},
{"PolyPressure" , __SK_PolyPressure_, SK_DBL, SK_DBL},
{"ControlChange" , __SK_ControlChange_, SK_INT, SK_DBL},
{"ProgramChange" , __SK_ProgramChange_, SK_DBL, SK_DBL},
{"AfterTouch" , __SK_AfterTouch_, SK_DBL, NOPE},
{"ChannelPressure" ,__SK_ChannelPressure_, SK_DBL, NOPE},
{"PitchWheel" , __SK_PitchWheel_, SK_DBL, NOPE},
{"PitchBend" , __SK_PitchBend_, SK_DBL, NOPE},
{"Clock" , __SK_Clock_, NOPE, NOPE},
{"Undefined" , 249, NOPE, NOPE},
{"SongStart" , __SK_SongStart_, NOPE, NOPE},
{"Continue" , __SK_Continue_, NOPE, NOPE},
{"SongStop" , __SK_SongStop_, NOPE, NOPE},
{"Undefined" , 253, NOPE, NOPE},
{"ActiveSensing" , __SK_ActiveSensing_, NOPE, NOPE},
{"SystemReset" , __SK_SystemReset_, NOPE, NOPE},
{"Volume" , __SK_ControlChange_, __SK_Volume_ , SK_DBL},
{"ModWheel" , __SK_ControlChange_, __SK_ModWheel_ , SK_DBL},
{"Modulation" , __SK_ControlChange_, __SK_Modulation_ , SK_DBL},
{"Breath" , __SK_ControlChange_, __SK_Breath_ , SK_DBL},
{"FootControl" , __SK_ControlChange_, __SK_FootControl_ , SK_DBL},
{"Portamento" , __SK_ControlChange_, __SK_Portamento_ , SK_DBL},
{"Balance" , __SK_ControlChange_, __SK_Balance_ , SK_DBL},
{"Pan" , __SK_ControlChange_, __SK_Pan_ , SK_DBL},
{"Sustain" , __SK_ControlChange_, __SK_Sustain_ , SK_DBL},
{"Damper" , __SK_ControlChange_, __SK_Damper_ , SK_DBL},
{"Expression" , __SK_ControlChange_, __SK_Expression_ , SK_DBL},
{"NoiseLevel" , __SK_ControlChange_, __SK_NoiseLevel_ , SK_DBL},
{"PickPosition" , __SK_ControlChange_, __SK_PickPosition_ , SK_DBL},
{"StringDamping" , __SK_ControlChange_, __SK_StringDamping_ , SK_DBL},
{"StringDetune" , __SK_ControlChange_, __SK_StringDetune_ , SK_DBL},
{"BodySize" , __SK_ControlChange_, __SK_BodySize_ , SK_DBL},
{"BowPressure" , __SK_ControlChange_, __SK_BowPressure_ , SK_DBL},
{"BowPosition" , __SK_ControlChange_, __SK_BowPosition_ , SK_DBL},
{"BowBeta" , __SK_ControlChange_, __SK_BowBeta_ , SK_DBL},
{"ReedStiffness" , __SK_ControlChange_, __SK_ReedStiffness_ , SK_DBL},
{"ReedRestPos" , __SK_ControlChange_, __SK_ReedRestPos_ , SK_DBL},
{"FluteEmbouchure" , __SK_ControlChange_, __SK_FluteEmbouchure_, SK_DBL},
{"LipTension" , __SK_ControlChange_, __SK_LipTension_ , SK_DBL},
{"StrikePosition" , __SK_ControlChange_, __SK_StrikePosition_, SK_DBL},
{"StickHardness" , __SK_ControlChange_, __SK_StickHardness_ , SK_DBL},
{"TrillDepth" , __SK_ControlChange_, __SK_TrillDepth_ , SK_DBL},
{"TrillSpeed" , __SK_ControlChange_, __SK_TrillSpeed_ , SK_DBL},
{"Strumming" , __SK_ControlChange_, __SK_Strumming_ , 127 },
{"NotStrumming" , __SK_ControlChange_, __SK_Strumming_ , 0 },
{"PlayerSkill" , __SK_ControlChange_, __SK_PlayerSkill_ , SK_DBL},
{"Chord" , __SK_Chord_ , SK_DBL , SK_STR },
{"ChordOff" , __SK_ChordOff_ , SK_DBL , NOPE },
{"ShakerInst" , __SK_ControlChange_, __SK_ShakerInst_ , SK_DBL},
{"Maraca" , __SK_ControlChange_, __SK_ShakerInst_ , 0 },
{"Sekere" , __SK_ControlChange_, __SK_ShakerInst_ , 1 },
{"Cabasa" , __SK_ControlChange_, __SK_ShakerInst_ , 2 },
{"Bamboo" , __SK_ControlChange_, __SK_ShakerInst_ , 3 },
{"Waterdrp" , __SK_ControlChange_, __SK_ShakerInst_ , 4 },
{"Tambourn" , __SK_ControlChange_, __SK_ShakerInst_ , 5 },
{"Sleighbl" , __SK_ControlChange_, __SK_ShakerInst_ , 6 },
{"Guiro" , __SK_ControlChange_, __SK_ShakerInst_ , 7 },
{"OpenFile" , 256, SK_STR , NOPE},
{"SetPath" , 257, SK_STR , NOPE},
{"FilePath" , __SK_SINGER_FilePath_, SK_STR , NOPE},
{"Frequency" , __SK_SINGER_Frequency_, SK_STR , NOPE},
{"NoteName" , __SK_SINGER_NoteName_, SK_STR , NOPE},
{"VocalShape" , __SK_SINGER_Shape_ , SK_STR , NOPE},
{"Glottis" , __SK_SINGER_Glot_ , SK_STR , NOPE},
{"VoicedUnVoiced" , __SK_SINGER_VoicedUnVoiced_, SK_DBL , SK_STR},
{"Synthesize" , __SK_SINGER_Synthesize_, SK_STR , NOPE},
{"Silence" , __SK_SINGER_Silence_, SK_STR , NOPE},
{"VibratoAmt" , __SK_ControlChange_ ,__SK_SINGER_VibratoAmt_,SK_DBL},
{"RndVibAmt" , __SK_SINGER_RndVibAmt_ ,SK_STR, NOPE},
{"VibFreq" , __SK_ControlChange_ ,__SK_SINGER_VibFreq_ ,SK_DBL}
};
/** FORMAT: *************************************************************/
/* */
/* MessageStr$ ,type, data2, data3, */
/* */
/* type is the message type sent back from the SKINI line parser. */
/* data<n> is either */
/* NOPE : field not used, specifically, there aren't going */
/* to be any more fields on this line. So if there */
/* is is NOPE in data2, data3 won't even be checked */
/* SK_INT : byte (actually scanned as 32 bit signed integer) */
/* If it's a MIDI data field which is required to */
/* be an integer, like a controller number, it's */
/* 0-127. Otherwise) get creative with SK_INTs */
/* SK_DBL : double precision floating point. SKINI uses these */
/* in the MIDI context for note numbers with micro */
/* tuning, velocities, controller values, etc. */
/* SK_STR : only valid in final field. This allows (nearly) */
/* arbitrary message types to be supported by simply */
/* scanning the string to EndOfLine and then passing */
/* it to a more intellegent handler. For example, */
/* MIDI SYSEX (system exclusive) messages of up to */
/* 256bytes can be read as space-delimited integers */
/* into the 1K SK_STR buffer. Longer bulk dumps, */
/* soundfiles, etc. should be handled as a new */
/* message type pointing to a FileName stored in the */
/* SK_STR field, or as a new type of multi-line */
/* message. */
/* */
/*************************************************************************/

48
src/SamplFlt.cpp Normal file
View File

@@ -0,0 +1,48 @@
/*******************************************/
/* Swept Filter SubClass of Sampling */
/* Synthesizer, by Perry R. Cook, 1995-96*/
/* This instrument inherits up to 5 */
/* attack waves, 5 looped waves, an ADSR */
/* envelope, and adds a 4 pole swept */
/* filter. */
/*******************************************/
#include "SamplFlt.h"
SamplFlt :: SamplFlt() : Sampler()
{
MY_FLOAT tempCoeffs[2] = {(MY_FLOAT) 0.0,(MY_FLOAT) -1.0};
twozeroes[0] = new TwoZero;
twozeroes[0]->setZeroCoeffs(tempCoeffs);
twozeroes[0]->setGain((MY_FLOAT) 1.0);
twozeroes[1] = new TwoZero;
twozeroes[1]->setZeroCoeffs(tempCoeffs);
twozeroes[1]->setGain((MY_FLOAT) 1.0);
filters[0] = new FormSwep;
filters[0]->setTargets((MY_FLOAT) 0.0,(MY_FLOAT) 0.7,(MY_FLOAT) 0.5);
filters[1] = new FormSwep;
filters[1]->setTargets((MY_FLOAT) 0.0,(MY_FLOAT) 0.7,(MY_FLOAT) 0.5);
}
SamplFlt :: ~SamplFlt()
{
delete filters[0];
delete filters[1];
delete twozeroes[0];
delete twozeroes[1];
}
MY_FLOAT SamplFlt :: tick()
{
MY_FLOAT output;
output = Sampler :: tick();
output = twozeroes[0]->tick(output);
output = filters[0]->tick(output);
output = twozeroes[1]->tick(output);
output = filters[1]->tick(output);
return output;
}
void SamplFlt :: controlChange(int number, MY_FLOAT value)
{
}

63
src/Sampler.cpp Normal file
View File

@@ -0,0 +1,63 @@
/*******************************************/
/* Master Class for Sampling Synthesizer */
/* by Perry R. Cook, 1995-96 */
/* This instrument contains up to 5 */
/* attack waves, 5 looped waves, and */
/* an ADSR envelope. */
/*******************************************/
#include "Sampler.h"
Sampler :: Sampler()
{
adsr = new ADSR;
/* We don't make the waves here yet, because */
/* we don't know what they will be. */
baseFreq = (MY_FLOAT) 440.0;
filter = new OnePole;
attackGain = (MY_FLOAT) 0.25;
loopGain = (MY_FLOAT) 0.25;
whichOne = 0;
}
Sampler :: ~Sampler()
{
delete adsr;
delete filter;
}
void Sampler :: keyOn()
{
adsr->keyOn();
attacks[0]->reset();
}
void Sampler :: keyOff()
{
adsr->keyOff();
}
void Sampler :: noteOff(MY_FLOAT amplitude)
{
this->keyOff();
#if defined(_debug_)
printf("Sampler : NoteOff: Amp=%lf\n",amplitude);
#endif
}
void Sampler :: setFreq(MY_FLOAT frequency)
{
}
MY_FLOAT Sampler :: tick()
{
lastOutput = attackGain * attacks[whichOne]->tick();
lastOutput += loopGain * loops[whichOne]->tick();
lastOutput = filter->tick(lastOutput);
lastOutput *= adsr->tick();
return lastOutput;
}
void Sampler :: controlChange(int number, MY_FLOAT value)
{
}

842
src/Shakers.cpp Normal file
View File

@@ -0,0 +1,842 @@
/**********************************************************/
/* PhISEM (Physically Informed Stochastic Event Modeling */
/* by Perry R. Cook, Princeton, February 1997 */
/* */
/* Meta-model that simulates all of: */
/* Maraca Simulation by Perry R. Cook, Princeton, 1996-7 */
/* Sekere Simulation by Perry R. Cook, Princeton, 1996-7 */
/* Cabasa Simulation by Perry R. Cook, Princeton, 1996-7 */
/* Bamboo Windchime Simulation, by Perry R. Cook, 1996-7 */
/* Water Drops Simulation, by Perry R. Cook, 1996-7 */
/* Tambourine Simulation, by Perry R. Cook, 1996-7 */
/* Sleighbells Simulation, by Perry R. Cook, 1996-7 */
/* Guiro Simulation, by Perry R. Cook, 1996-7 */
/* */
/**********************************************************/
/* PhOLIES (Physically-Oriented Library of */
/* Imitated Environmental Sounds), Perry Cook, 1997-9 */
/* */
/* Stix1 (walking on brittle sticks) */
/* Crunch1 (like new fallen snow, or not) */
/* Wrench (basic socket wrench, friend of guiro) */
/* Sandpapr (sandpaper) */
/**********************************************************/
#include "Object.h"
#ifdef __OS_NeXT_
#include <libc.h>
#endif
int my_random(int max) // Return Random Int Between 0 and max
{
long temp;
#if defined(__OS_Win_) /* For Windoze */
temp = (long) rand();
#else /* This is for unix */
temp = random() >> 16;
#endif
temp = temp * (long) max;
temp = temp >> 15;
return (int) temp;
}
MY_FLOAT float_random(MY_FLOAT max) // Return random float between 0.0 and max
{
MY_FLOAT temp;
temp = (MY_FLOAT) my_random(32767);
temp = temp * 0.0000305185;
temp = temp * max;
return temp;
}
MY_FLOAT noise_tick() // Return random MY_FLOAT float between -1.0 and 1.0
{
MY_FLOAT temp;
temp = my_random(32767) - 16384;
temp *= 0.0000610352;
return temp;
}
/************************* MARACA *****************************/
#define MARA_SOUND_DECAY 0.95
#define MARA_SYSTEM_DECAY 0.999
//#define MARA_GAIN 25.0
#define MARA_GAIN 20.0
#define MARA_NUM_BEANS 25
#define MARA_CENTER_FREQ 3200.0
#define MARA_RESON 0.96
/*********************** SEKERE *****************************/
#define SEKE_SOUND_DECAY 0.96
#define SEKE_SYSTEM_DECAY 0.999
//#define SEKE_GAIN 30.0
#define SEKE_GAIN 20.0
#define SEKE_NUM_BEANS 64
#define SEKE_CENTER_FREQ 5500.0
#define SEKE_RESON 0.6
/*********************** SANDPAPER **************************/
#define SANDPAPR_SOUND_DECAY 0.999
#define SANDPAPR_SYSTEM_DECAY 0.999
//#define SANDPAPR_GAIN 1.0
#define SANDPAPR_GAIN 0.5
#define SANDPAPR_NUM_GRAINS 128
#define SANDPAPR_CENTER_FREQ 4500.0
#define SANDPAPR_RESON 0.6
/************************ CABASA *****************************/
#define CABA_SOUND_DECAY 0.96
#define CABA_SYSTEM_DECAY 0.997
//#define CABA_GAIN 150.0
#define CABA_GAIN 40.0
#define CABA_NUM_BEADS 512
#define CABA_CENTER_FREQ 3000.0
#define CABA_RESON 0.7
/************************ Bamboo Wind Chimes *****************/
#define BAMB_SOUND_DECAY 0.95
//#define BAMB_SYSTEM_DECAY 0.99995
#define BAMB_SYSTEM_DECAY 0.9999
#define BAMB_GAIN 2.0
#define BAMB_NUM_TUBES 1.25
#define BAMB_CENTER_FREQ0 2800.0
#define BAMB_CENTER_FREQ1 0.8 * 2800.0
#define BAMB_CENTER_FREQ2 1.2 * 2800.0
#define BAMB_RESON 0.995
/******************* Water Drops ****************************/
#define WUTR_SOUND_DECAY 0.95
//#define WUTR_SYSTEM_DECAY 0.999
#define WUTR_SYSTEM_DECAY 0.996
#define WUTR_GAIN 1.0
//#define WUTR_NUM_SOURCES 4
#define WUTR_NUM_SOURCES 10
#define WUTR_CENTER_FREQ0 450.0
#define WUTR_CENTER_FREQ1 600.0
#define WUTR_CENTER_FREQ2 750.0
#define WUTR_RESON 0.9985
#define WUTR_FREQ_SWEEP 1.0001
/****************** TAMBOURINE *****************************/
#define TAMB_SOUND_DECAY 0.95
#define TAMB_SYSTEM_DECAY 0.9985
//#define TAMB_GAIN 10.0
#define TAMB_GAIN 5.0
#define TAMB_NUM_TIMBRELS 32
#define TAMB_SHELL_FREQ 2300
#define TAMB_SHELL_GAIN 0.1
#define TAMB_SHELL_RESON 0.96
#define TAMB_CYMB_FREQ1 5600
#define TAMB_CYMB_FREQ2 8100
#define TAMB_CYMB_RESON 0.99
/********************** SLEIGHBELLS *************************/
#define SLEI_SOUND_DECAY 0.97
#define SLEI_SYSTEM_DECAY 0.9994
//#define SLEI_GAIN 2.0
#define SLEI_GAIN 1.0
#define SLEI_NUM_BELLS 32
#define SLEI_CYMB_FREQ0 2500
#define SLEI_CYMB_FREQ1 5300
#define SLEI_CYMB_FREQ2 6500
#define SLEI_CYMB_FREQ3 8300
#define SLEI_CYMB_FREQ4 9800
#define SLEI_CYMB_RESON 0.99
/*************************** GUIRO ***********************/
#define GUIR_SOUND_DECAY 0.95
#define GUIR_GAIN 10.0
#define GUIR_NUM_PARTS 128
#define GUIR_GOURD_FREQ 2500.0
#define GUIR_GOURD_RESON 0.97
#define GUIR_GOURD_FREQ2 4000.0
#define GUIR_GOURD_RESON2 0.97
/************************** WRENCH ***********************/
#define WRENCH_SOUND_DECAY 0.95
#define WRENCH_GAIN 5
#define WRENCH_NUM_PARTS 128
#define WRENCH_FREQ 3200.0
#define WRENCH_RESON 0.99
#define WRENCH_FREQ2 8000.0
#define WRENCH_RESON2 0.992
/************************ COKECAN **************************/
#define COKECAN_SOUND_DECAY 0.97
#define COKECAN_SYSTEM_DECAY 0.999
//#define COKECAN_GAIN 1.0
#define COKECAN_GAIN 0.8
#define COKECAN_NUM_PARTS 48
#define COKECAN_HELMFREQ 370
#define COKECAN_HELM_RES 0.99
#define COKECAN_METLFREQ0 1025
#define COKECAN_METLFREQ1 1424
#define COKECAN_METLFREQ2 2149
#define COKECAN_METLFREQ3 3596
#define COKECAN_METL_RES 0.992
/************************************************************/
/* PhOLIES (Physically-Oriented Library of */
/* Imitated Environmental Sounds), Perry Cook, 1997-8 */
/************************************************************/
/*********************** STIX1 *****************************/
#define STIX1_SOUND_DECAY 0.96
#define STIX1_SYSTEM_DECAY 0.998
#define STIX1_GAIN 30.0
#define STIX1_NUM_BEANS 2
#define STIX1_CENTER_FREQ 5500.0
#define STIX1_RESON 0.6
/************************ Crunch1 ***************************/
#define CRUNCH1_SOUND_DECAY 0.95
#define CRUNCH1_SYSTEM_DECAY 0.99806
//#define CRUNCH1_GAIN 30.0
#define CRUNCH1_GAIN 20.0
#define CRUNCH1_NUM_BEADS 7
#define CRUNCH1_CENTER_FREQ 800.0
#define CRUNCH1_RESON 0.95
/************ THE ACTUAL CLASS ITSELF *********************/
#include "Shakers.h"
#include "SKINI11.msg"
Shakers :: Shakers() : Instrmnt()
{
int i;
instType = 0;
shakeEnergy = 0.0;
num_freqs = 0;
sndLevel = 0.0;
for (i=0;i<MAX_FREQS;i++) {
inputs[i] = 0.0;
outputs[i][0] = 0.0;
outputs[i][1] = 0.0;
coeffs[i][0] = 0.0;
coeffs[i][1] = 0.0;
gains[i] = 0.0;
center_freqs[i] = 0.0;
resons[i] = 0.0;
freq_rand[i] = 0.0;
freqalloc[i] = 0;
}
soundDecay = 0.0;
systemDecay = 0.0;
num_objects = 0.0;
collLikely = 0.0;
totalEnergy = 0.0;
ratchet=0.0;
ratchetDelta=0.0005;
finalZ[0] = 0.0;
finalZ[1] = 0.0;
finalZ[2] = 0.0;
finalZCoeffs[0] = 1.0;
finalZCoeffs[1] = 0.0;
finalZCoeffs[2] = 0.0;
this->setupNum(instType);
}
Shakers :: ~Shakers()
{
}
#define MAX_SHAKE 2000.0
#define NUM_INST 13
char instrs[NUM_INST][16] =
{"Maraca", "Cabasa", "Sekere", "Guiro",
"Waterdrp", "Bamboo", "Tambourn", "Sleighbl",
"Stix1", "Crunch1", "Wrench", "SandPapr", "CokeCan"};
int Shakers :: setupName(char* instr)
{
int i, which = 0;
for (i=0;i<NUM_INST;i++) {
if (!strcmp(instr,instrs[i]))
which = i;
}
#if defined(_debug_)
printf("Shakers: Setting Instrument to %s\n",instrs[which]);
#endif
return this->setupNum(which);
}
void Shakers :: setFinalZs(MY_FLOAT z0, MY_FLOAT z1, MY_FLOAT z2) {
finalZCoeffs[0] = z0;
finalZCoeffs[1] = z1;
finalZCoeffs[2] = z2;
}
void Shakers :: setDecays(MY_FLOAT sndDecay, MY_FLOAT sysDecay) {
soundDecay = sndDecay;
systemDecay = sysDecay;
}
int Shakers :: setFreqAndReson(int which, MY_FLOAT freq, MY_FLOAT reson) {
if (which < MAX_FREQS) {
resons[which] = reson;
center_freqs[which] = freq;
t_center_freqs[which] = freq;
coeffs[which][1] = reson * reson;
coeffs[which][0] = -reson * 2.0 * cos(freq * TWO_PI / SRATE);
return 1;
}
else return 0;
}
int Shakers :: setupNum(int inst)
{
int i, rv = 0;
MY_FLOAT temp;
if (inst==1) { // cabasa_setup();
rv = inst;
num_objects = CABA_NUM_BEADS;
defObjs[inst] = CABA_NUM_BEADS;
setDecays(CABA_SOUND_DECAY, CABA_SYSTEM_DECAY);
defDecays[inst] = CABA_SYSTEM_DECAY;
decayScale[inst] = 0.97;
num_freqs = 1;
baseGain = CABA_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0] = temp;
freqalloc[0] = 0;
setFreqAndReson(0,CABA_CENTER_FREQ,CABA_RESON);
setFinalZs(1.0,-1.0,0.0);
}
else if (inst==2) { // sekere_setup();
rv = inst;
num_objects = SEKE_NUM_BEANS;
defObjs[inst] = SEKE_NUM_BEANS;
this->setDecays(SEKE_SOUND_DECAY,SEKE_SYSTEM_DECAY);
defDecays[inst] = SEKE_SYSTEM_DECAY;
decayScale[inst] = 0.94;
num_freqs = 1;
baseGain = SEKE_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0] = temp;
freqalloc[0] = 0;
this->setFreqAndReson(0,SEKE_CENTER_FREQ,SEKE_RESON);
this->setFinalZs(1.0, 0.0, -1.0);
}
else if (inst==3) { // guiro_setup();
rv = inst;
num_objects = GUIR_NUM_PARTS;
defObjs[inst] = GUIR_NUM_PARTS;
setDecays(GUIR_SOUND_DECAY,1.0);
defDecays[inst] = 0.9999;
decayScale[inst] = 1.0;
num_freqs = 2;
baseGain = GUIR_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0]=temp;
gains[1]=temp;
freqalloc[0] = 0;
freqalloc[1] = 0;
freq_rand[0] = 0.0;
freq_rand[1] = 0.0;
setFreqAndReson(0,GUIR_GOURD_FREQ,GUIR_GOURD_RESON);
setFreqAndReson(1,GUIR_GOURD_FREQ2,GUIR_GOURD_RESON2);
ratchet = 0;
ratchetPos = 10;
}
else if (inst==4) { // wuter_setup();
rv = inst;
num_objects = WUTR_NUM_SOURCES;
defObjs[inst] = WUTR_NUM_SOURCES;
setDecays(WUTR_SOUND_DECAY,WUTR_SYSTEM_DECAY);
defDecays[inst] = WUTR_SYSTEM_DECAY;
decayScale[inst] = 0.8;
num_freqs = 3;
baseGain = WUTR_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0]=temp;
gains[1]=temp;
gains[2]=temp;
freqalloc[0] = 1;
freqalloc[1] = 1;
freqalloc[2] = 1;
freq_rand[0] = 0.2;
freq_rand[1] = 0.2;
freq_rand[2] = 0.2;
setFreqAndReson(0,WUTR_CENTER_FREQ0,WUTR_RESON);
setFreqAndReson(1,WUTR_CENTER_FREQ0,WUTR_RESON);
setFreqAndReson(2,WUTR_CENTER_FREQ0,WUTR_RESON);
setFinalZs(1.0,0.0,0.0);
}
else if (inst==5) { // bamboo_setup();
rv = inst;
num_objects = BAMB_NUM_TUBES;
defObjs[inst] = BAMB_NUM_TUBES;
setDecays(BAMB_SOUND_DECAY, BAMB_SYSTEM_DECAY);
defDecays[inst] = BAMB_SYSTEM_DECAY;
decayScale[inst] = 0.7;
num_freqs = 3;
baseGain = BAMB_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0]=temp;
gains[1]=temp;
gains[2]=temp;
freqalloc[0] = 1;
freqalloc[1] = 1;
freqalloc[2] = 1;
freq_rand[0] = 0.2;
freq_rand[1] = 0.2;
freq_rand[2] = 0.2;
setFreqAndReson(0,BAMB_CENTER_FREQ0,BAMB_RESON);
setFreqAndReson(1,BAMB_CENTER_FREQ1,BAMB_RESON);
setFreqAndReson(2,BAMB_CENTER_FREQ2,BAMB_RESON);
setFinalZs(1.0,0.0,0.0);
}
else if (inst==6) { // tambourine_setup();
rv = inst;
num_objects = TAMB_NUM_TIMBRELS;
defObjs[inst] = TAMB_NUM_TIMBRELS;
setDecays(TAMB_SOUND_DECAY,TAMB_SYSTEM_DECAY);
defDecays[inst] = TAMB_SYSTEM_DECAY;
decayScale[inst] = 0.95;
num_freqs = 3;
baseGain = TAMB_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0]=temp*TAMB_SHELL_GAIN;
gains[1]=temp*0.8;
gains[2]=temp;
freqalloc[0] = 0;
freqalloc[1] = 1;
freqalloc[2] = 1;
freq_rand[0] = 0.0;
freq_rand[1] = 0.05;
freq_rand[2] = 0.05;
setFreqAndReson(0,TAMB_SHELL_FREQ,TAMB_SHELL_RESON);
setFreqAndReson(1,TAMB_CYMB_FREQ1,TAMB_CYMB_RESON);
setFreqAndReson(2,TAMB_CYMB_FREQ2,TAMB_CYMB_RESON);
setFinalZs(1.0,0.0,-1.0);
}
else if (inst==7) { // sleighbell_setup();
rv = inst;
num_objects = SLEI_NUM_BELLS;
defObjs[inst] = SLEI_NUM_BELLS;
setDecays(SLEI_SOUND_DECAY,SLEI_SYSTEM_DECAY);
defDecays[inst] = SLEI_SYSTEM_DECAY;
decayScale[inst] = 0.9;
num_freqs = 5;
baseGain = SLEI_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0]=temp;
gains[1]=temp;
gains[2]=temp;
gains[3]=temp*0.5;
gains[4]=temp*0.3;
for (i=0;i<num_freqs;i++) {
freqalloc[i] = 1;
freq_rand[i] = 0.03;
}
setFreqAndReson(0,SLEI_CYMB_FREQ0,SLEI_CYMB_RESON);
setFreqAndReson(1,SLEI_CYMB_FREQ1,SLEI_CYMB_RESON);
setFreqAndReson(2,SLEI_CYMB_FREQ2,SLEI_CYMB_RESON);
setFreqAndReson(3,SLEI_CYMB_FREQ3,SLEI_CYMB_RESON);
setFreqAndReson(4,SLEI_CYMB_FREQ4,SLEI_CYMB_RESON);
setFinalZs(1.0,0.0,-1.0);
}
else if (inst==8) { // stix1_setup();
rv = inst;
num_objects = STIX1_NUM_BEANS;
defObjs[inst] = STIX1_NUM_BEANS;
setDecays(STIX1_SOUND_DECAY,STIX1_SYSTEM_DECAY);
defDecays[inst] = STIX1_SYSTEM_DECAY;
decayScale[inst] = 0.96;
num_freqs = 1;
baseGain = STIX1_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0]=temp;
freqalloc[0] = 0;
setFreqAndReson(0,STIX1_CENTER_FREQ,STIX1_RESON);
setFinalZs(1.0,0.0,-1.0);
}
else if (inst==9) { // crunch1_setup();
rv = inst;
num_objects = CRUNCH1_NUM_BEADS;
defObjs[inst] = CRUNCH1_NUM_BEADS;
setDecays(CRUNCH1_SOUND_DECAY,CRUNCH1_SYSTEM_DECAY);
defDecays[inst] = CRUNCH1_SYSTEM_DECAY;
decayScale[inst] = 0.96;
num_freqs = 1;
baseGain = CRUNCH1_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0]=temp;
freqalloc[0] = 0;
setFreqAndReson(0,CRUNCH1_CENTER_FREQ,CRUNCH1_RESON);
setFinalZs(1.0,-1.0,0.0);
}
else if (inst==10) { // wrench_setup();
rv = inst;
num_objects = WRENCH_NUM_PARTS;
defObjs[inst] = WRENCH_NUM_PARTS;
setDecays(WRENCH_SOUND_DECAY,1.0);
defDecays[inst] = 0.9999;
decayScale[inst] = 0.98;
num_freqs = 2;
baseGain = WRENCH_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0]=temp;
gains[1]=temp;
freqalloc[0] = 0;
freqalloc[1] = 0;
freq_rand[0] = 0.0;
freq_rand[1] = 0.0;
setFreqAndReson(0,WRENCH_FREQ,WRENCH_RESON);
setFreqAndReson(1,WRENCH_FREQ2,WRENCH_RESON2);
ratchet = 0;
ratchetPos = 10;
}
else if (inst==11) { // sandpapr_setup();
rv = inst;
num_objects = SANDPAPR_NUM_GRAINS;
defObjs[inst] = SANDPAPR_NUM_GRAINS;
this->setDecays(SANDPAPR_SOUND_DECAY,SANDPAPR_SYSTEM_DECAY);
defDecays[inst] = SANDPAPR_SYSTEM_DECAY;
decayScale[inst] = 0.97;
num_freqs = 1;
baseGain = SANDPAPR_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0] = temp;
freqalloc[0] = 0;
this->setFreqAndReson(0,SANDPAPR_CENTER_FREQ,SANDPAPR_RESON);
this->setFinalZs(1.0, 0.0, -1.0);
}
else if (inst==12) { // cokecan_setup();
rv = inst;
num_objects = COKECAN_NUM_PARTS;
defObjs[inst] = COKECAN_NUM_PARTS;
setDecays(COKECAN_SOUND_DECAY,COKECAN_SYSTEM_DECAY);
defDecays[inst] = COKECAN_SYSTEM_DECAY;
decayScale[inst] = 0.95;
num_freqs = 5;
baseGain = COKECAN_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0]=temp;
gains[1]=temp*1.8;
gains[2]=temp*1.8;
gains[3]=temp*1.8;
gains[4]=temp*1.8;
freqalloc[0] = 0;
freqalloc[1] = 0;
freqalloc[2] = 0;
freqalloc[3] = 0;
freqalloc[4] = 0;
setFreqAndReson(0,COKECAN_HELMFREQ,COKECAN_HELM_RES);
setFreqAndReson(1,COKECAN_METLFREQ0,COKECAN_METL_RES);
setFreqAndReson(2,COKECAN_METLFREQ1,COKECAN_METL_RES);
setFreqAndReson(3,COKECAN_METLFREQ2,COKECAN_METL_RES);
setFreqAndReson(4,COKECAN_METLFREQ3,COKECAN_METL_RES);
setFinalZs(1.0,0.0,-1.0);
}
else { // maraca_setup(); inst == 0 or other
num_objects = MARA_NUM_BEANS;
defObjs[0] = MARA_NUM_BEANS;
setDecays(MARA_SOUND_DECAY,MARA_SYSTEM_DECAY);
defDecays[0] = MARA_SYSTEM_DECAY;
decayScale[inst] = 0.9;
num_freqs = 1;
baseGain = MARA_GAIN;
temp = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
gains[0]=temp;
freqalloc[0] = 0;
setFreqAndReson(0,MARA_CENTER_FREQ,MARA_RESON);
setFinalZs(1.0,-1.0,0.0);
}
return rv;
}
void Shakers :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
// Yep ... pretty kludgey, but it works!
int noteNum = (int) ((12*log(freq/220)/log(2.0)) + 57.01) % 32;
if (instType != noteNum) instType = this->setupNum(noteNum);
//shakeEnergy = amp * MAX_SHAKE * 0.1;
shakeEnergy += amp * MAX_SHAKE * 0.1;
if (shakeEnergy > MAX_SHAKE) shakeEnergy = MAX_SHAKE;
if (instType==10 || instType==3) ratchetPos += 1;
#if defined(_debug_)
printf("Shakers : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}
void Shakers :: noteOff(MY_FLOAT amp)
{
shakeEnergy = 0.0;
if (instType==10 || instType==3) ratchetPos = 0;
}
#define MIN_ENERGY 0.3
MY_FLOAT Shakers :: tick()
{
MY_FLOAT data;
MY_FLOAT temp_rand;
int i;
if (instType==4) {
if (shakeEnergy > MIN_ENERGY) {
lastOutput = wuter_tick();
lastOutput *= 0.0001;
}
else {
lastOutput = 0.0;
}
}
else if (instType==10 || instType==3) {
if (ratchetPos > 0) {
ratchet -= (ratchetDelta + (0.002*totalEnergy));
if (ratchet < 0.0) {
ratchet = 1.0;
ratchetPos -= 1;
}
totalEnergy = ratchet;
lastOutput = ratchet_tick();
lastOutput *= 0.0001;
}
else lastOutput = 0.0;
}
else {
// MY_FLOAT generic_tick() {
if (shakeEnergy > MIN_ENERGY) {
shakeEnergy *= systemDecay; // Exponential system decay
if (float_random(1024.0) < num_objects) {
sndLevel += shakeEnergy;
for (i=0;i<num_freqs;i++) {
if (freqalloc[i]) {
temp_rand = t_center_freqs[i] * (1.0 + (freq_rand[i] * noise_tick()));
coeffs[i][0] = -resons[i] * 2.0 * cos(temp_rand * TWO_PI / SRATE);
}
}
}
inputs[0] = sndLevel * noise_tick(); // Actual Sound is Random
for (i=1; i<num_freqs; i++) {
inputs[i] = inputs[0];
}
sndLevel *= soundDecay; // Exponential Sound decay
finalZ[2] = finalZ[1];
finalZ[1] = finalZ[0];
finalZ[0] = 0;
for (i=0;i<num_freqs;i++) {
inputs[i] -= outputs[i][0]*coeffs[i][0]; // Do
inputs[i] -= outputs[i][1]*coeffs[i][1]; // resonant
outputs[i][1] = outputs[i][0]; // filter
outputs[i][0] = inputs[i]; // calculations
finalZ[0] += gains[i] * outputs[i][1];
}
data = finalZCoeffs[0] * finalZ[0]; // Extra zero(s) for shape
data += finalZCoeffs[1] * finalZ[1]; // Extra zero(s) for shape
data += finalZCoeffs[2] * finalZ[2]; // Extra zero(s) for shape
if (data > 10000.0) data = 10000.0;
if (data < -10000.0) data = -10000.0;
lastOutput = data * 0.0001;
}
else lastOutput = 0.0;
}
return lastOutput;
}
void Shakers :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT temp;
int i;
#if defined(_debug_)
printf("Shakers : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_ShakerEnergy_) { // control_change #2
#if defined(_debug_)
printf("shaking \n");
#endif
shakeEnergy += value * NORM_7 * MAX_SHAKE * 0.1;
if (shakeEnergy > MAX_SHAKE) shakeEnergy = MAX_SHAKE;
if (instType==10 || instType==3) {
ratchetPos = (int) fabs(value - lastRatchetPos);
ratchetDelta = 0.0002 * ratchetPos;
lastRatchetPos = (int) value;
}
}
else if (number == __SK_ShakerDamping_) { // control_change #11
#if defined(_debug_)
printf("setting decay\n");
#endif
//systemDecay = 0.998 + (value * NORM_7 * 0.002);
if (instType != 3 && instType != 10) {
systemDecay = defDecays[instType] + ((value - 64.0) * decayScale[instType] * (1.0 - defDecays[instType]) / 64.0 );
gains[0] = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
for (i=1;i<num_freqs;i++) gains[i] = gains[0];
if (instType == 6) { // tambourine
gains[0] *= TAMB_SHELL_GAIN;
gains[1] *= 0.8;
}
else if (instType == 7) { // sleighbell
gains[3] *= 0.5;
gains[4] *= 0.3;
}
else if (instType == 12) { // cokecan
for (i=1;i<num_freqs;i++) gains[i] *= 1.8;
}
for (i=0;i<num_freqs;i++) gains[i] *= ((128-value)/100.0 + 0.36);
}
}
else if (number == __SK_ShakerNumObjects_) { // control_change #4
#if defined(_debug_)
printf("setting number of objects\n");
#endif
if (instType == 5) // bamboo
num_objects = (MY_FLOAT) (value * defObjs[instType] / 64.0) + 0.3;
else
num_objects = (MY_FLOAT) (value * defObjs[instType] / 64.0) + 1.1;
gains[0] = log(num_objects) * baseGain / (MY_FLOAT) num_objects;
for (i=1;i<num_freqs;i++) gains[i] = gains[0];
if (instType == 6) { // tambourine
gains[0] *= TAMB_SHELL_GAIN;
gains[1] *= 0.8;
}
else if (instType == 7) { // sleighbell
gains[3] *= 0.5;
gains[4] *= 0.3;
}
else if (instType == 12) { // cokecan
for (i=1;i<num_freqs;i++) gains[i] *= 1.8;
}
if (instType != 3 && instType != 10) {
// reverse calculate decay setting
MY_FLOAT temp = 64.0 * (systemDecay-defDecays[instType])/(decayScale[instType]*(1-defDecays[instType])) + 64.0;
// scale gains by decay setting
for (i=0;i<num_freqs;i++) gains[i] *= ((128-temp)/100.0 + 0.36);
}
}
else if (number == __SK_ModWheel_) { // control_change #1
#if defined(_debug_)
printf("setting resonance %i freq. to %f\n",i,temp);
#endif
for (i=0;i<num_freqs;i++) {
if (instType == 6 || instType == 2 || instType == 7) // limit range a bit for tambourine
temp = center_freqs[i] * pow (1.008,value-64);
else
temp = center_freqs[i] * pow (1.015,value-64);
t_center_freqs[i] = temp;
coeffs[i][0] = -resons[i] * 2.0 * cos(temp * TWO_PI / SRATE);
coeffs[i][1] = resons[i]*resons[i];
}
}
else if (number == __SK_AfterTouch_Cont_) { // control_change #128
#if defined(_debug_)
printf("shaking \n");
#endif
shakeEnergy += value * NORM_7 * MAX_SHAKE * 0.1;
if (shakeEnergy > MAX_SHAKE) shakeEnergy = MAX_SHAKE;
if (instType==10 || instType==3) {
ratchetPos = (int) fabs(value - lastRatchetPos);
ratchetDelta = 0.0002 * ratchetPos;
lastRatchetPos = (int) value;
}
}
else if (number == __SK_ShakerInst_) { // control_change #1071
instType = (int) (value + 0.5); // Just to be safe
this->setupNum(instType);
}
else {
printf("Shakers : Undefined Control Number!!\n");
}
}
/*********************************************************/
/**************** KLUDGE-O-MATIC-O-RAMA **************/
MY_FLOAT Shakers :: wuter_tick() {
MY_FLOAT data;
int j;
shakeEnergy *= systemDecay; // Exponential system decay
if (my_random(32767) < num_objects) {
sndLevel = shakeEnergy;
j = my_random(3);
if (j == 0) {
center_freqs[0] = WUTR_CENTER_FREQ1 * (0.75 + (0.25 * noise_tick()));
gains[0] = fabs(noise_tick());
}
else if (j == 1) {
center_freqs[1] = WUTR_CENTER_FREQ1 * (1.0 + (0.25 * noise_tick()));
gains[1] = fabs(noise_tick());
}
else {
center_freqs[2] = WUTR_CENTER_FREQ1 * (1.25 + (0.25 * noise_tick()));
gains[2] = fabs(noise_tick());
}
}
gains[0] *= resons[0];
if (gains[0] > 0.001) {
center_freqs[0] *= WUTR_FREQ_SWEEP;
coeffs[0][0] = -resons[0] * 2.0 *
cos(center_freqs[0] * TWO_PI / SRATE);
}
gains[1] *= resons[1];
if (gains[1] > 0.001) {
center_freqs[1] *= WUTR_FREQ_SWEEP;
coeffs[1][0] = -resons[1] * 2.0 *
cos(center_freqs[1] * TWO_PI / SRATE);
}
gains[2] *= resons[2];
if (gains[2] > 0.001) {
center_freqs[2] *= WUTR_FREQ_SWEEP;
coeffs[2][0] = -resons[2] * 2.0 *
cos(center_freqs[2] * TWO_PI / SRATE);
}
sndLevel *= soundDecay; // Each (all) event(s)
// decay(s) exponentially
inputs[0] = sndLevel;
inputs[0] *= noise_tick(); // Actual Sound is Random
inputs[1] = inputs[0] * gains[1];
inputs[2] = inputs[0] * gains[2];
inputs[0] *= gains[0];
inputs[0] -= outputs[0][0]*coeffs[0][0];
inputs[0] -= outputs[0][1]*coeffs[0][1];
outputs[0][1] = outputs[0][0];
outputs[0][0] = inputs[0];
data = gains[0]*outputs[0][0];
inputs[1] -= outputs[1][0]*coeffs[1][0];
inputs[1] -= outputs[1][1]*coeffs[1][1];
outputs[1][1] = outputs[1][0];
outputs[1][0] = inputs[1];
data += gains[1]*outputs[1][0];
inputs[2] -= outputs[2][0]*coeffs[2][0];
inputs[2] -= outputs[2][1]*coeffs[2][1];
outputs[2][1] = outputs[2][0];
outputs[2][0] = inputs[2];
data += gains[2]*outputs[2][0];
finalZ[2] = finalZ[1];
finalZ[1] = finalZ[0];
finalZ[0] = data * 4;
data = finalZ[2] - finalZ[0];
return data;
}
MY_FLOAT Shakers :: ratchet_tick() {
MY_FLOAT data;
if (my_random(1024) < num_objects) {
sndLevel += 512 * ratchet * totalEnergy;
}
inputs[0] = sndLevel;
inputs[0] *= noise_tick() * ratchet;
sndLevel *= soundDecay;
inputs[1] = inputs[0];
inputs[0] -= outputs[0][0]*coeffs[0][0];
inputs[0] -= outputs[0][1]*coeffs[0][1];
outputs[0][1] = outputs[0][0];
outputs[0][0] = inputs[0];
inputs[1] -= outputs[1][0]*coeffs[1][0];
inputs[1] -= outputs[1][1]*coeffs[1][1];
outputs[1][1] = outputs[1][0];
outputs[1][0] = inputs[1];
finalZ[2] = finalZ[1];
finalZ[1] = finalZ[0];
finalZ[0] = gains[0]*outputs[0][1] + gains[1]*outputs[1][1];
data = finalZ[0] - finalZ[2];
return data;
}

114
src/Simple.cpp Normal file
View File

@@ -0,0 +1,114 @@
/*******************************************/
/* Master Class for Simple Instrument */
/* by Perry R. Cook, 1995-96 */
/* This instrument contains 1 looped */
/* wave, 1 noise source, 1 biquad filter */
/* 1 one-pole filter, and 1 ADSR envelope */
/*******************************************/
#include "Simple.h"
Simple :: Simple()
{
MY_FLOAT coeffs[2];
adsr = new ADSR;
baseFreq = (MY_FLOAT) 440.0;
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
loop = new RawWvIn(strcat(file,"rawwaves/impuls10.raw"),"looping");
filter = new OnePole;
noise = new Noise;
bqpoles = new TwoPole;
bqzeroes = new TwoZero;
coeffs[0] = 0;
coeffs[1] = -1;
bqzeroes->setZeroCoeffs(coeffs);
filter->setPole(0.5);
this->setFreq(baseFreq);
loopGain = 0.5;
}
Simple :: ~Simple()
{
delete adsr;
delete loop;
delete filter;
delete bqzeroes;
delete bqpoles;
}
void Simple :: keyOn()
{
adsr->keyOn();
}
void Simple :: keyOff()
{
adsr->keyOff();
}
void Simple :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
this->keyOn();
this->setFreq(freq);
filter->setGain(amp);
#if defined(_debug_)
printf("Simple : NoteOn: Freq= %lf, Amp=%lf\n",freq, amp);
#endif
}
void Simple :: noteOff(MY_FLOAT amplitude)
{
this->keyOff();
#if defined(_debug_)
printf("Simple : NoteOff: Amp=%lf\n",amplitude);
#endif
}
void Simple :: setFreq(MY_FLOAT frequency)
{
#define R 0.98
MY_FLOAT coeffs[2];
coeffs[0] = 2 * R * cos(TWO_PI * ONE_OVER_SRATE * frequency);
coeffs[1] = - R * R;
bqpoles->setPoleCoeffs(coeffs);
bqpoles->setGain(1.0 - R);
loop->setFreq(frequency);
}
MY_FLOAT Simple :: tick()
{
lastOutput = loopGain * loop->tick();
bqzeroes->tick(bqpoles->tick(noise->tick()));
lastOutput += (1.0 - loopGain) * bqzeroes->lastOut();
lastOutput = filter->tick(lastOutput);
lastOutput *= adsr->tick();
return lastOutput;
}
#include "SKINI11.msg"
void Simple :: controlChange(int number, MY_FLOAT value)
{
#if defined(_debug_)
printf("Simple : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_Breath_)
filter->setPole(0.99 * (1.0 - (value * NORM_7 * 2.0)));
else if (number == __SK_NoiseLevel_)
loopGain = 1.0 - (NORM_7 * value);
else if (number == __SK_ModFrequency_) {
adsr->setAttackRate(value * NORM_7);
adsr->setDecayRate(value * NORM_7);
adsr->setReleaseRate(value * NORM_7);
}
else if (number == __SK_ModWheel_)
printf("Mod Wheel Unimplemented\n");
else if (number == __SK_AfterTouch_Cont_)
adsr->setTarget(value * NORM_7);
else {
printf("Simple : Undefined Control Number!!\n");
}
}

260
src/SingWave.cpp Normal file
View File

@@ -0,0 +1,260 @@
/*******************************************/
/* "Singing" Looped Soundfile Class, */
/* by Perry R. Cook, 1995-96 */
/* This Object contains all that's needed */
/* to make a pitched musical sound, like */
/* a simple voice or violin. In general, */
/* it will not be used alone (because of */
/* of munchinification effects from pitch */
/* shifting. It will be used as an */
/* excitation source for other instruments*/
/*******************************************/
#include "SingWave.h"
#include <sys/stat.h>
#include <sys/types.h>
#ifdef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
SingWave :: SingWave(char *fileName)
{
FILE *fd;
char msg[256];
// Use the system call "stat" to determine the file length
struct stat filestat;
if (stat(fileName, &filestat) == -1)
{ /* Opening file failed */
sprintf(msg, "SingWave: Couldn't stat or find file (%s).\n", fileName);
throw StkError(msg, StkError::FILE_NOT_FOUND);
}
length = (long) filestat.st_size / 2; // length in 2-byte samples
// Open the file and read samples into data[]
fd = fopen(fileName,"rb");
if (!fd) {
sprintf(msg, "SingWave: Couldn't open or find file (%s).\n", fileName);
throw StkError(msg, StkError::FILE_NOT_FOUND);
}
fseek(fd,0,0);
data = (MY_FLOAT *) new MY_FLOAT[(length+1)];
// Read samples into data[]. Use MY _FLOAT data structure to store INT16 samples
INT16 *buf = (INT16 *)data;
fseek(fd,0,SEEK_SET); // Only here to bypass bug in Linux glibc 2.1x (RedHat 6.0)
fread(buf,length,2,fd);
// Convert in place (unpack) to MY_FLOAT from the end of the array
for (int i=length-1; i>=0; i--) {
#ifdef __LITTLE_ENDIAN__
swap16((unsigned char *)(buf+i));
#endif
data[i] = buf[i];
}
fclose(fd);
data[length] = data[length-1];
mytime = (MY_FLOAT) 0.0;
rate = (MY_FLOAT) 1.0;
sweepRate = (MY_FLOAT) 0.001;
modulator = new Modulatr;
modulator->setVibFreq((MY_FLOAT) 6.0);
modulator->setVibAmt((MY_FLOAT) 0.04);
modulator->setRndAmt((MY_FLOAT) 0.005);
envelope = new Envelope;
pitchEnvelope = new Envelope;
this->setFreq((MY_FLOAT) 75.0);
pitchEnvelope->setRate((MY_FLOAT) 1.0);
this->tick();
this->tick();
pitchEnvelope->setRate(sweepRate * rate);
}
SingWave :: ~SingWave()
{
delete modulator;
delete envelope;
delete pitchEnvelope;
free(data);
}
void SingWave :: reset()
{
mytime = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0.0;
}
void SingWave :: normalize()
{
long i;
MY_FLOAT max = (MY_FLOAT) 0.0;
for (i=0;i<=length;i++)
if (fabs(data[i]) > max)
max = (MY_FLOAT) fabs((double) data[i]);
if (max > 0.0) {
max = (MY_FLOAT) 1.0 / max;
for (i=0;i<=length;i++)
data[i] *= max;
}
}
void SingWave :: normalize(MY_FLOAT newPeak)
{
long i;
MY_FLOAT max = (MY_FLOAT) 0.0;
for (i=0;i<=length;i++)
if (fabs(data[i]) > max)
max = (MY_FLOAT) fabs((double) data[i]);
if (max > 0.0) {
max = (MY_FLOAT) 1.0 / max;
max *= newPeak;
for (i=0;i<=length;i++)
data[i] *= max;
}
}
void SingWave :: setFreq(MY_FLOAT aFreq)
{
MY_FLOAT temp;
temp = rate;
rate = length * ONE_OVER_SRATE * aFreq;
temp -= rate;
if (temp<0) temp = -temp;
pitchEnvelope->setTarget(rate);
pitchEnvelope->setRate(sweepRate * temp);
}
void SingWave :: setVibFreq(MY_FLOAT vibFreq)
{
modulator->setVibFreq(vibFreq);
}
void SingWave :: setVibAmt(MY_FLOAT vibAmount)
{
modulator->setVibAmt(vibAmount);
}
void SingWave :: setRndAmt(MY_FLOAT rndAmount)
{
modulator->setRndAmt(rndAmount);
}
void SingWave :: setSweepRate(MY_FLOAT swpRate)
{
sweepRate = swpRate;
}
void SingWave :: setGainRate(MY_FLOAT gainRate)
{
envelope->setRate(gainRate);
}
void SingWave :: setGainTarget(MY_FLOAT aTarget)
{
envelope->setTarget(aTarget);
}
void SingWave :: noteOn()
{
envelope->keyOn();
}
void SingWave :: noteOff()
{
envelope->keyOff();
}
MY_FLOAT SingWave :: tick()
{
long temp;
MY_FLOAT alpha, temp_rate;
temp_rate = pitchEnvelope->tick();
mytime += temp_rate; /* Update current time */
mytime += temp_rate * modulator->tick(); /* Add vibratos */
while (mytime >= length) { /* Check for end of sound */
mytime -= length; /* loop back to beginning */
}
while (mytime < 0.0) { /* Check for end of sound */
mytime += length; /* loop back to beginning */
}
temp = (long) mytime; /* Integer part of time address */
alpha = mytime - (MY_FLOAT) temp; /* fractional part of time address */
lastOutput = alpha * data[temp+1]; /* Do linear */
lastOutput += ((MY_FLOAT) 1.0 - alpha) * data[temp]; /* interpolation */
lastOutput *= envelope->tick();
return lastOutput;
}
MY_FLOAT SingWave :: lastOut()
{
return lastOutput;
}
/************ Test Main Program *****************/
/*
void main()
{
SingWave ahhWave("rawwaves/ahh.raw");
SingWave eeeWave("rawwaves/eee.raw");
SingWave oooWave("rawwaves/ooo.raw");
FILE *fd;
short data;
long i,j;
fd = fopen("test.raw","wb");
ahhWave.normalize();
ahhWave.noteOn();
for (j=0;j<6;j++) {
ahhWave.setFreq(100 * pow(2.0,j*0.25));
for (i=0;i<10000;i++) {
data = ahhWave.tick() * 32000.0;
fwrite(&data,2,1,fd);
}
}
ahhWave.noteOff();
for (i=0;i<5000;i++) {
data = ahhWave.tick() * 32000.0;
fwrite(&data,2,1,fd);
}
eeeWave.normalize();
eeeWave.noteOn();
for (j=0;j<6;j++) {
eeeWave.setFreq(100 * pow(2.0,j*0.25));
for (i=0;i<10000;i++) {
data = eeeWave.tick() * 32000.0;
fwrite(&data,2,1,fd);
}
}
eeeWave.noteOff();
for (i=0;i<5000;i++) {
data = eeeWave.tick() * 32000.0;
fwrite(&data,2,1,fd);
}
oooWave.normalize();
oooWave.noteOn();
for (j=0;j<6;j++) {
oooWave.setFreq(100 * pow(2.0,j*0.25));
for (i=0;i<10000;i++) {
data = oooWave.tick() * 32000.0;
fwrite(&data,2,1,fd);
}
}
oooWave.noteOff();
for (i=0;i<5000;i++) {
data = oooWave.tick() * 32000.0;
fwrite(&data,2,1,fd);
}
fclose(fd);
}
*/

172
src/SndWvIn.cpp Normal file
View File

@@ -0,0 +1,172 @@
/*******************************************/
/* SndWvIn Input Class, */
/* by Gary P. Scavone, 2000 */
/* */
/* This object inherits from WvIn and is */
/* used to open NeXT/Sun .snd 16-bit data */
/* (signed integer) files for playback. */
/* */
/* .snd files are always big-endian. */
/*******************************************/
#include "SndWvIn.h"
#ifdef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
SndWvIn :: SndWvIn(char *fileName, const char *mode)
{
char msg[256];
// check mode string
if ( strcmp(mode,"oneshot") && strcmp(mode,"looping") ) {
sprintf(msg, "SndWvIn: constructor parameter 'mode' must be oneshot or looping only.\n");
throw StkError(msg, StkError::FUNCTION_SYNTAX);
}
// Open the file and get header info
fd = fopen(fileName,"rb");
if (!fd) {
sprintf(msg, "SndWvIn: Couldn't open or find file (%s).\n", fileName);
throw StkError(msg, StkError::FILE_NOT_FOUND);
}
// Make sure this is a .snd format file
char magic[4];
fseek(fd,0,SEEK_SET); // Locate magic number in header
fread(magic,4,1,fd);
if (strncmp(magic,".snd",4)) {
fclose(fd);
sprintf(msg, "SndWvIn: %s doesn't appear to be an SND file.\n", fileName);
throw StkError(msg, StkError::FILE_ERROR);
}
// Check that the data is not compressed
INT32 format;
fseek(fd,12,SEEK_SET); // Locate format
fread(&format,4,1,fd);
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&format);
#endif
if (format != 3) { // 16-bit linear PCM = 3
fclose(fd);
sprintf(msg, "SndWvIn: STK does not currently support data formats other than 16 bit signed integer.\n");
throw StkError(msg, StkError::FILE_ERROR);
}
// Get file sample rate from the header and set the default rate
INT32 srate;
fread(&srate,4,1,fd);
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&srate);
#endif
rate = (MY_FLOAT) (srate/SRATE);
// Get number of channels from the header
INT32 chans;
fread(&chans,4,1,fd);
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&chans);
#endif
channels = chans;
fseek(fd,4,SEEK_SET);
fread(&dataOffset,4,1,fd);
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&dataOffset);
#endif
// Get length of data from the header
fread(&fileSize,4,1,fd);
#ifdef __LITTLE_ENDIAN__
swap32((unsigned char *)&fileSize);
#endif
// fileSize is the number of sample frames
fileSize /= 2 * channels;
bufferSize = fileSize;
if ((fileSize*channels) > MAX_FILE_LOAD_SIZE) {
printf("\nSndWvIn: The .SND file (%s) has more than %d samples and\n",
fileName, MAX_FILE_LOAD_SIZE);
printf("will be loaded incrementally from disk. Normalization will be disabled.\n");
chunking = 1;
bufferSize = LOAD_BUFFER_SIZE;
}
// Setup for looping or one-shot playback
if (!strcmp(mode,"looping"))
looping = 1;
else // default = oneshot
looping = 0;
data = (MY_FLOAT *) new MY_FLOAT[(bufferSize+1)*channels];
this->getData(0); // Read samples into data[]
if (fmod(rate, 1.0) != 0.0) interpolate = 1;
else interpolate = 0;
phaseOffset = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
this->reset();
// finally, let's normalize the data by default
this->normalize();
}
SndWvIn :: ~SndWvIn()
{
}
void SndWvIn :: getData(long index)
{
/* Compare index to current readPointer and modify as needed.
* The following while() loops will only execute on calls subsequent
* to class instantiation ... and thus, only when "chunking".
*/
while (index < readPointer) {
readPointer -= LOAD_BUFFER_SIZE;
bufferSize = LOAD_BUFFER_SIZE;
if (readPointer < 0) {
bufferSize += readPointer;
readPointer = 0;
}
}
while (index >= readPointer+bufferSize) {
readPointer += LOAD_BUFFER_SIZE;
bufferSize = LOAD_BUFFER_SIZE;
if (readPointer+LOAD_BUFFER_SIZE >= fileSize) {
bufferSize = fileSize - readPointer;
}
}
fseek(fd, dataOffset+(long)(readPointer*channels*2), SEEK_SET);
long length = bufferSize;
int end_of_file = (readPointer+bufferSize == fileSize);
if (!end_of_file) length += 1;
// Read samples into data[]. Use MY _FLOAT data structure to store INT16 samples
INT16 *buf = (INT16 *)data;
fread(buf, length*channels, 2, fd);
// Convert in place (unpack) to MY_FLOAT from the end of the array
for (int i=length*channels-1; i>=0; i--) {
#ifdef __LITTLE_ENDIAN__
swap16((unsigned char *)(buf+i));
#endif
data[i] = buf[i];
if (chunking) data[i] *= 0.00003051;
}
// fill in the extra sample frame for interpolation
if (end_of_file) {
for (int j=0; j<channels; j++)
if (looping)
data[bufferSize*channels+j] = data[j];
else
data[bufferSize*channels+j] = data[(bufferSize-1)*channels+j];
}
if (!chunking) {
fclose(fd);
fd = 0;
}
}

121
src/SndWvOut.cpp Normal file
View File

@@ -0,0 +1,121 @@
/***********************************************/
/*
NeXT (.snd) and Sun (.au) Soundfile Output Class
by Perry R. Cook, 1996
Revised by Gary P. Scavone, 1999-2000
This one opens a NeXT .snd file, and
even knows how to byte-swap!
*/
/***********************************************/
#include "SndWvOut.h"
#ifdef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
/******** NeXT Soundfile Header Struct *******/
struct headerform {
char pref[4];
INT32 hdr_length;
INT32 file_length;
INT32 mode;
INT32 samp_rate;
INT32 num_channels;
char comment[16];
};
FILE *openNeXTFile(int chans, char *fileName) {
struct headerform hdr = {".sn",40,0,3,(INT32) SRATE,1,"Created by STK"};
char tempName[128];
FILE *fd;
char msg[256];
hdr.pref[3] = 'd';
strcpy(tempName,fileName);
if (strstr(tempName,".snd") == NULL) strcat(tempName,".snd");
hdr.num_channels = chans;
fd = fopen(tempName,"wb");
if (!fd) {
sprintf(msg, "SndWvOut: Could not create soundfile: %s\n", tempName);
throw StkError(msg, StkError::FILE_ERROR);
}
#ifdef __LITTLE_ENDIAN__
swap32 ((unsigned char *)&hdr.hdr_length);
swap32 ((unsigned char *)&hdr.file_length);
swap32 ((unsigned char *)&hdr.mode);
swap32 ((unsigned char *)&hdr.samp_rate);
swap32 ((unsigned char *)&hdr.num_channels);
#endif
printf("\nCreating soundfile: %s\n", tempName);
fwrite(&hdr,4,10,fd);
return fd;
}
SndWvOut :: SndWvOut(char *fileName, int chans)
{
char msg[256];
if (chans < 1) {
sprintf(msg, "SndWvOut: number of channels = %d not supported!\n", chans);
throw StkError(msg, StkError::FUNCTION_SYNTAX);
}
channels = chans;
fd = openNeXTFile(channels,fileName);
data_length = FILE_BUFFER_SIZE*channels;
data = (INT16 *) new INT16[data_length];
}
SndWvOut :: ~SndWvOut()
{
double temp;
fwrite(data,2,counter,fd);
temp = (double) totalCount * ONE_OVER_SRATE;
printf("%f Seconds Computed\n\n", temp);
totalCount *= 2*channels;
#ifdef __LITTLE_ENDIAN__
swap32 ((unsigned char *)&totalCount);
#endif
fseek(fd,8,SEEK_SET); // jump to data size
fwrite(&totalCount,4,1,fd);
fclose(fd);
}
void SndWvOut :: tick(MY_FLOAT sample)
{
INT16 isample;
isample = (INT16) (sample * 32000.0);
#ifdef __LITTLE_ENDIAN__
swap16 ((unsigned char *)&isample);
#endif
for (int i=0;i<channels;i++)
data[counter++] = isample;
totalCount++;
if (counter == data_length) {
fwrite(data,2,data_length,fd);
counter = 0;
}
}
void SndWvOut :: mtick(MY_MULTI samples)
{
for (int i=0;i<channels;i++) {
data[counter] = (INT16) (*samples++ * 32000.0);
#ifdef __LITTLE_ENDIAN__
swap16 ((unsigned char *)&data[counter]);
#endif
counter++;
}
totalCount++;
if (counter == data_length) {
fwrite(data,2,data_length,fd);
counter = 0;
}
}

28
src/StkError.cpp Normal file
View File

@@ -0,0 +1,28 @@
/*************************************************/
/*
STK Error Handling Class
by Gary P. Scavone, 2000.
This is a fairly abstract exception handling
class. There could be sub-classes to take
care of more specific error conditions ... or not.
*/
/*************************************************/
#include "StkError.h"
StkError :: StkError(const char *p, TYPE tipe)
{
type = tipe;
strncpy(errormsg, p, 256);
}
StkError :: ~StkError()
{
}
void StkError :: printMessage()
{
printf("\n%s\n", errormsg);
}

389
src/StrmWvIn.cpp Normal file
View File

@@ -0,0 +1,389 @@
/******************************************/
/*
StrmWvIn Audio Input Class,
by Gary P. Scavone, 2000
This object inherits from WvIn and is used
to accept 16-bit data (signed integer) via
a socket connection (streamed audio).
Streamed data must be in big-endian format,
which conforms to network byte ordering.
This class starts a socket server, which
waits for a remote connection. Actual data
reading and buffering takes place in a thread.
*/
/******************************************/
#include "StrmWvIn.h"
#ifdef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
#define NUM_STREAM_IN_FRAGMENTS 10
#define STK_STREAM_PORT 2005
char *stream_buffer;
int local_socket = 0;
int g_nfrags_filled = 0;
int g_thread_done = 0;
// Do OS dependent declarations and includes
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <unistd.h>
pthread_t strm_thread;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#define MUTEX_LOCK(A) pthread_mutex_lock(A)
#define MUTEX_UNLOCK(A) pthread_mutex_unlock(A)
#elif defined(__OS_Win_)
#include <process.h>
#include <winsock.h>
unsigned long strm_thread;
CRITICAL_SECTION mutex;
#define MUTEX_LOCK(A) EnterCriticalSection(A)
#define MUTEX_UNLOCK(A) LeaveCriticalSection(A)
#endif
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
void *streamInThread(void * chan)
#elif defined(__OS_Win_)
void streamInThread(void * chan)
#endif
{
extern char *stream_buffer;
int remote_socket;
char msg[256];
int fd, i = -1, writefrag = 0;
long fill_size = RT_BUFFER_SIZE * (int)chan * 2; // bytes
fd_set mask, rmask;
struct sockaddr_in mysocket;
static struct timeval timeout = {0, 10000}; // ten milliseconds
#if defined(__OS_Win_) // Windoze-only stuff
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1,1);
WSAStartup(wVersionRequested, &wsaData);
if (wsaData.wVersion != wVersionRequested) {
sprintf(msg, "StrmWvIn: Wrong Windoze socket library version!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
#endif
// Create the server-side socket
local_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (local_socket < 0) {
sprintf(msg, "StrmWvIn: Couldn't create socket!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
mysocket.sin_family=AF_INET;
mysocket.sin_addr.s_addr=INADDR_ANY;
mysocket.sin_port=htons(STK_STREAM_PORT);
/* Bind socket to the appropriate port and interface (INADDR_ANY) */
if (bind(local_socket, (struct sockaddr *)&mysocket, sizeof(mysocket)) < 0) {
sprintf(msg, "StrmWvIn: Couldn't bind socket!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
/* Listen for one incoming connection */
if (listen(local_socket, 1) < 0) {
sprintf(msg, "StrmWvIn: Couldn't set up listen on socket!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
printf("\nStrmWvIn listening for a connection on port %d\n", STK_STREAM_PORT);
remote_socket=accept(local_socket, NULL, NULL);
if (remote_socket < 0) {
sprintf(msg, "StrmWvIn: Couldn't accept connection request!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
printf(" ... connection made!\n");
FD_ZERO(&mask);
fd = remote_socket;
FD_SET(fd, &mask);
for (;;) {
rmask = mask;
// select will block until data is available for reading
select(fd+1, &rmask, (fd_set *)0, (fd_set *)0, NULL);
if (FD_ISSET(fd, &rmask)) { // data is available
MUTEX_LOCK(&mutex);
// don't fill if we're ahead
if (g_nfrags_filled < NUM_STREAM_IN_FRAGMENTS) {
#if defined(__OS_Win_)
// Yet again, we have to make an extra special effort to get things to work
// under windoze ... why do I waste my time supporting this shitty OS?
// The windoze recv() call doesn't allow the use of the MSG_WAITALL flag.
// The following scheme should work but could cause even worse performance if
// we're in an underrun situation.
int j;
i = recv(fd, &stream_buffer[fill_size*writefrag], fill_size, 0);
if (i == SOCKET_ERROR) i = 0;
while (i < fill_size && i > 0) {
j = recv(fd, &stream_buffer[fill_size*writefrag+i], fill_size-i, 0);
if (j == 0 || j == SOCKET_ERROR) {
i = 0;
break;
}
i += j;
}
#else
i = recv(fd, &stream_buffer[fill_size*writefrag], fill_size, MSG_WAITALL);
#endif
if (i==0) {
printf("The remote StrmWvIn socket connection has closed!\n");
#if defined(__OS_Win_)
closesocket(fd);
#else
close(fd);
#endif
g_thread_done = 1;
MUTEX_UNLOCK(&mutex);
break;
}
g_nfrags_filled++;
if (++writefrag == NUM_STREAM_IN_FRAGMENTS)
writefrag = 0;
MUTEX_UNLOCK(&mutex);
}
else { // wait a little
MUTEX_UNLOCK(&mutex);
#if defined(__OS_Win_)
// the windoze select function doesn't work as a timer
Sleep((DWORD) 10);
#else
// reset the timeout values because of linux "select" implementation
timeout.tv_sec = 0;
timeout.tv_usec = 10000; // 0.01 seconds
select(0, NULL, NULL, NULL, &timeout);
#endif
}
}
}
// We can only get here if the remote socket shuts down
#if defined(__OS_Win_)
closesocket(local_socket);
WSACleanup();
_endthread();
#else
close(local_socket);
return NULL;
#endif
}
StrmWvIn :: StrmWvIn(int chans, MY_FLOAT srate)
{
char msg[256];
long buffer_size = RT_BUFFER_SIZE * NUM_STREAM_IN_FRAGMENTS * chans * 2;
chunking = 1;
looping = 0;
stream_buffer = (char *) new char[buffer_size];
memset(stream_buffer, 0, buffer_size);
// start the input stream thread
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
if (pthread_create(&strm_thread, NULL, streamInThread, (void *)chans)) {
sprintf(msg, "StrmWvIn: unable to create input stream thread!\n");
throw StkError(msg, StkError::PROCESS_THREAD);
}
#elif defined(__OS_Win_)
InitializeCriticalSection(&mutex);
strm_thread = _beginthread(streamInThread, 0, (void *)chans);
if (strm_thread == 0) {
sprintf(msg, "StrmWvIn: unable to create input stream thread!\n");
throw StkError(msg, StkError::PROCESS_THREAD);
}
#endif
channels = chans;
bufferSize = RT_BUFFER_SIZE;
data = 0;
strmdata = (INT16 *) new INT16[(bufferSize+1)*channels];
lastSamples = (INT16 *) new INT16[channels];
for (int i=0;i<channels;i++) {
lastSamples[i] = (INT16) 0.0;
}
this->getData(0);
rate = (MY_FLOAT) srate / SRATE;
if (fmod(rate, 1.0) > 0.0) interpolate = 1;
else interpolate = 0;
phaseOffset = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
this->reset();
}
StrmWvIn :: ~StrmWvIn()
{
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
shutdown(local_socket,0);
pthread_cancel(strm_thread);
pthread_join(strm_thread, NULL);
pthread_mutex_destroy(&mutex);
#elif defined(__OS_Win_)
closesocket(local_socket);
WSACleanup();
TerminateThread((HANDLE)strm_thread,0);
DeleteCriticalSection(&mutex);
#endif
if (strmdata) {
delete [ ] strmdata;
strmdata = 0;
}
if (stream_buffer) {
delete [] stream_buffer;
stream_buffer = 0;
}
if (lastSamples) {
delete [ ] lastSamples;
lastSamples = 0;
}
}
void StrmWvIn :: setRate(MY_FLOAT aRate)
{
// Negative rates not allowed for realtime input
rate = fabs(aRate);
if (fmod(rate, 1.0) != 0.0) interpolate = 1;
else interpolate = 0;
}
void StrmWvIn :: addTime(MY_FLOAT aTime)
{
// Negative time shift no allowed for realtime input
time += fabs(aTime);
}
void StrmWvIn :: setLooping(int aLoopStatus)
{
// No looping for realtime data.
looping = 0;
}
long StrmWvIn :: getSize()
{
return bufferSize;
}
void StrmWvIn :: getData(long index)
{
/* We have two potential courses of action should this method
* be called and the stream_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).
*/
static int readfrag = 0, i;
static struct timeval timer = {0, 10000}; // ten milliseconds
long temp = RT_BUFFER_SIZE*channels;
/* wait until data is ready */
while (g_nfrags_filled == 0) {
#if defined(__OS_Win_)
// the windoze select function doesn't work as a timer
Sleep((DWORD) 10);
#else
timer.tv_sec = 0;
timer.tv_usec = 10000; // 0.01 seconds
select(0, NULL, NULL, NULL, &timer);
#endif
if (g_thread_done) {
finished = 1;
return;
}
}
/* copy data from stream_buffer to strmdata */
MUTEX_LOCK(&mutex);
memcpy(&strmdata[channels], &stream_buffer[readfrag*temp*2], temp*2);
#ifdef __LITTLE_ENDIAN__
for (i=0; i<temp+channels; i++)
swap16((unsigned char *) (strmdata+i));
#endif
g_nfrags_filled--;
if (++readfrag == NUM_STREAM_IN_FRAGMENTS)
readfrag = 0;
MUTEX_UNLOCK(&mutex);
/* Fill in the extra sample frame for interpolation.
* We do this by pre-pending the last sample frame
* from the previous input buffer to the current one.
*/
for (i=0;i<channels;i++) {
strmdata[i] = lastSamples[i];
lastSamples[i] = strmdata[temp+i];
}
if (g_thread_done && g_nfrags_filled == 0) finished = 1;
}
int StrmWvIn :: informTick()
{
static MY_FLOAT alpha;
static long index;
if (finished) return finished;
if (time >= bufferSize) {
this->getData(0);
while (time >= bufferSize)
time -= bufferSize;
}
// integer part of time address
index = (long) time;
if (interpolate) {
// fractional part of time address
alpha = time - (MY_FLOAT) index;
index *= channels;
for (int i=0;i<channels;i++) {
// Do linear interpolation
lastOutput[i] = (MY_FLOAT) strmdata[index];
lastOutput[i] += alpha * ((MY_FLOAT) strmdata[index+channels] - lastOutput[i]);
lastOutput[i] *= 0.00003051;
index++;
}
}
else {
index *= channels;
for (int i=0;i<channels;i++) {
lastOutput[i] = strmdata[index++];
lastOutput[i] *= 0.00003051;
}
}
// increment time, which can be negative
time += rate;
return finished;
}

168
src/StrmWvOut.cpp Normal file
View File

@@ -0,0 +1,168 @@
/******************************************/
/*
StrmWvOut Audio Output Class,
by Gary P. Scavone, 2000
This object inherits from WvOut and is used
to send 16-bit data (signed integer) via
a socket connection (streamed audio).
Streamed data must be in big-endian format,
which conforms to network byte ordering.
This class connects to a socket server, the
port and IP address of which must be specified
as constructor arguments. No data buffering
is currently implemented. The class simply
fires off a buffer of data over the socket
connection as soon as it is filled.
*/
/******************************************/
#include "StrmWvOut.h"
#if defined(__STK_REALTIME_)
#ifdef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
#if (defined(__OS_IRIX_) || defined(__OS_Linux_))
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#elif defined(__OS_Win_)
#include <winsock.h>
#endif
StrmWvOut :: StrmWvOut(int port, const char *hostname, int chans)
{
char msg[256];
struct sockaddr_in server_address;
if (chans < 1) {
sprintf(msg, "StrmWvOut: number of channels (%d) must be one or greater.\n", chans);
throw StkError(msg, StkError::FUNCTION_SYNTAX);
}
channels = chans;
#if defined(__OS_Win_) // Windoze-only stuff
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1,1);
WSAStartup(wVersionRequested, &wsaData);
if (wsaData.wVersion != wVersionRequested) {
sprintf(msg, "StrmWvOut: Wrong Windoze socket library version!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
#endif
// Create the client-side socket
local_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (local_socket < 0) {
sprintf(msg, "StrmWvOut: Couldn't create socket!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
struct hostent *hostp;
if ((hostp = gethostbyname(hostname)) == 0) {
sprintf(msg, "StrmWvOut: unknown host (%s)!\n", hostname);
throw StkError(msg, StkError::PROCESS_SOCKET_IPADDR);
}
// Fill in the address structure
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(local_socket, (struct sockaddr *)&server_address,
sizeof(server_address) ) < 0) {
#if defined(__OS_Win_)
closesocket(local_socket);
WSACleanup();
#else
close(local_socket);
#endif
sprintf(msg, "StrmWvOut: Couldn't connect to socket server!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
data_length = RT_BUFFER_SIZE*channels; // samples
// Add a few extra samples for good measure
data = (INT16 *) new INT16[data_length+10];
}
StrmWvOut :: ~StrmWvOut()
{
while (counter<data_length) {
data[counter++] = 0;
}
send(local_socket, (const char *)data, data_length*2, 0);
#if defined(__OS_Win_)
closesocket(local_socket);
WSACleanup();
#else
close(local_socket);
#endif
}
void StrmWvOut :: tick(MY_FLOAT sample)
{
static INT16 isample;
isample = (INT16) (sample * 32000.0);
#ifdef __LITTLE_ENDIAN__
swap16 ((unsigned char *)&isample);
#endif
for (int i=0;i<channels;i++)
data[counter++] = isample;
if (counter >= data_length) {
if (send(local_socket, (const char *)data, data_length*2, 0) < 0) {
char msg[256];
#if defined(__OS_Win_)
closesocket(local_socket);
WSACleanup();
#else
close(local_socket);
#endif
sprintf(msg, "StrmWvOut: connection to socket server failed!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
counter = 0;
}
}
void StrmWvOut :: mtick(MY_MULTI samples)
{
for (int i=0;i<channels;i++) {
data[counter] = (INT16) (*samples++ * 32000.0);
#ifdef __LITTLE_ENDIAN__
swap16((unsigned char *)&data[counter]);
#endif
counter++;
}
if (counter >= data_length) {
if (send(local_socket, (const char *)data, data_length*2, 0) < 0) {
char msg[256];
#if defined(__OS_Win_)
closesocket(local_socket);
WSACleanup();
#else
close(local_socket);
#endif
sprintf(msg, "StrmWvOut: connection to socket server failed!\n");
throw StkError(msg, StkError::PROCESS_SOCKET);
}
counter = 0;
}
}
#endif

52
src/SubNoise.cpp Normal file
View File

@@ -0,0 +1,52 @@
/*******************************************/
/* SubSampled Noise Generator Class, */
/* by Perry R. Cook, 1995-96 */
/* White noise as often as you like. */
/*******************************************/
#include "SubNoise.h"
SubNoise :: SubNoise() : Noise()
{
lastOutput = (MY_FLOAT) 0.0;
howOften = 15;
counter = 15;
}
SubNoise :: ~SubNoise()
{
}
SubNoise :: SubNoise(int subSample) : Noise()
{
lastOutput = (MY_FLOAT) 0.0;
howOften = subSample-1;
counter = subSample-1;
}
MY_FLOAT SubNoise :: tick()
{
if (!counter) {
lastOutput = Noise::tick();
counter = howOften;
}
else counter -= 1;
return lastOutput;
}
void SubNoise :: setHowOften(int howOft)
{
howOften = howOft;
}
/************ Test Main ************************/
/*
void main()
{
long i;
SubNoise test(5);
for (i=0;i<100;i++) printf("%lf\n",test.tick());
}
*/

98
src/TablLook.cpp Normal file
View File

@@ -0,0 +1,98 @@
/********************************************/
/* Table Lookup Class, */
/* by Gary P. Scavone, 1999 */
/* */
/* 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. */
/********************************************/
#include "TablLook.h"
#include <sys/stat.h>
#include <sys/types.h>
#ifdef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
TablLook :: TablLook(char *fileName)
{
char msg[256];
// Use the system call "stat" to determine the file length
struct stat filestat;
if (stat(fileName, &filestat) == -1)
{ /* Opening file failed */
sprintf(msg, "TablLook: Couldn't stat or find file (%s).\n", fileName);
throw StkError(msg, 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(msg, "TablLook: Couldn't open or find file (%s).\n", fileName);
throw StkError(msg, 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;
}
TablLook :: ~TablLook()
{
delete [ ] data;
}
long TablLook :: getLength()
{
return length;
}
MY_FLOAT TablLook :: tick(MY_FLOAT index)
{
static MY_FLOAT alpha;
static long temp;
if (index > length-1) {
fprintf(stderr, "TablLook: Index (%f) exceeds table length ... sticking at end!.",
index);
index = length-1;
}
if (index < 0.0) {
fprintf(stderr,"TablLook: Index (%f) is less than zero ... setting to zero\n",
index);
index = 0.0;
}
// Index OK (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 TablLook :: lastOut()
{
return lastOutput;
}

62
src/TubeBell.cpp Normal file
View File

@@ -0,0 +1,62 @@
/******************************************/
/* Tubular Bell (Orch. Chime) Subclass */
/* of Algorithm 5 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/******************************************/
#include "TubeBell.h"
TubeBell :: TubeBell() : FM4Alg5()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file1[128];
char file2[128];
char file3[128];
char file4[128];
strcpy(file1, RAWWAVE_PATH);
strcpy(file2, RAWWAVE_PATH);
strcpy(file3, RAWWAVE_PATH);
strcpy(file4, RAWWAVE_PATH);
this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"),
strcat(file2,"rawwaves/sinewave.raw"),
strcat(file3,"rawwaves/sinewave.raw"),
strcat(file4,"rawwaves/fwavblnk.raw"));
this->setRatio(0,(MY_FLOAT) (1.0 * 0.995));
this->setRatio(1,(MY_FLOAT) (1.414 * 0.995));
this->setRatio(2,(MY_FLOAT) (1.0 * 1.005));
this->setRatio(3,(MY_FLOAT) (1.414 * 1.000));
gains[0] = __FM4Op_gains[94];
gains[1] = __FM4Op_gains[76];
gains[2] = __FM4Op_gains[99];
gains[3] = __FM4Op_gains[71];
adsr[0]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 4.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
adsr[1]->setAllTimes((MY_FLOAT) 0.005,(MY_FLOAT) 4.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
adsr[2]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 2.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
adsr[3]->setAllTimes((MY_FLOAT) 0.004,(MY_FLOAT) 4.0,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
twozero->setGain((MY_FLOAT) 0.5);
vibWave->setFreq((MY_FLOAT) 2.0);
}
void TubeBell :: setFreq(MY_FLOAT frequency)
{
baseFreq = frequency;
waves[0]->setFreq(baseFreq * ratios[0]);
waves[1]->setFreq(baseFreq * ratios[1]);
waves[2]->setFreq(baseFreq * ratios[2]);
waves[3]->setFreq(baseFreq * ratios[3]);
}
void TubeBell :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
gains[0] = amp * __FM4Op_gains[94];
gains[1] = amp * __FM4Op_gains[76];
gains[2] = amp * __FM4Op_gains[99];
gains[3] = amp * __FM4Op_gains[71];
this->setFreq(freq);
this->keyOn();
#if defined(_debug_)
printf("TubeBell : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}

60
src/TwoPole.cpp Normal file
View File

@@ -0,0 +1,60 @@
/*******************************************/
/* Two Pole Filter Class, */
/* by Perry R. Cook, 1995-96 */
/* See books on filters to understand */
/* more about how this works. Nothing */
/* out of the ordinary in this version. */
/*******************************************/
#include "TwoPole.h"
TwoPole :: TwoPole() : Filter()
{
outputs = (MY_FLOAT *) malloc(2 * sizeof(MY_FLOAT));
poleCoeffs[0] = (MY_FLOAT) 0.0;
poleCoeffs[1] = (MY_FLOAT) 0.0;
gain = (MY_FLOAT) 1.0;
this->clear();
}
TwoPole :: ~TwoPole()
{
free(outputs);
}
void TwoPole :: clear()
{
outputs[0] = (MY_FLOAT) 0.0;
outputs[1] = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0.0;
}
void TwoPole :: setPoleCoeffs(MY_FLOAT *coeffs)
{
poleCoeffs[0] = coeffs[0];
poleCoeffs[1] = coeffs[1];
}
void TwoPole :: setFreqAndReson(MY_FLOAT freq, MY_FLOAT reson)
{
poleCoeffs[1] = - (reson * reson);
poleCoeffs[0] = (MY_FLOAT) 2.0 * reson * cos(TWO_PI * (double) freq / SRATE);
}
void TwoPole :: setGain(MY_FLOAT aValue)
{
gain = aValue;
}
MY_FLOAT TwoPole :: tick(MY_FLOAT sample) // Perform Filter Operation
{ // TwoPole is a two pole filter (duh!)
MY_FLOAT temp; // Look it up in your favorite DSP text
temp = sample * gain;
temp += poleCoeffs[0] * outputs[0];
temp += poleCoeffs[1] * outputs[1];
outputs[1] = outputs[0];
outputs[0] = temp;
lastOutput = outputs[0];
return lastOutput;
}

53
src/TwoZero.cpp Normal file
View File

@@ -0,0 +1,53 @@
/*******************************************/
/* Two Zero Filter Class, */
/* by Perry R. Cook, 1995-96 */
/* See books on filters to understand */
/* more about how this works. Nothing */
/* out of the ordinary in this version. */
/*******************************************/
#include "TwoZero.h"
TwoZero :: TwoZero() : Filter()
{
inputs = (MY_FLOAT *) malloc(2 * sizeof(MY_FLOAT));
zeroCoeffs[0] = (MY_FLOAT) 0.0;
zeroCoeffs[1] = (MY_FLOAT) 0.0;
gain = (MY_FLOAT) 1.0;
this->clear();
}
TwoZero :: ~TwoZero()
{
free(inputs);
}
void TwoZero :: clear()
{
inputs[0] = (MY_FLOAT) 0.0;
inputs[1] = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT) 0.0;
}
void TwoZero :: setZeroCoeffs(MY_FLOAT *coeffs)
{
zeroCoeffs[0] = coeffs[0];
zeroCoeffs[1] = coeffs[1];
}
void TwoZero :: setGain(MY_FLOAT aValue)
{
gain = aValue;
}
MY_FLOAT TwoZero :: tick(MY_FLOAT sample) // Perform Filter Operation
{ // TwoZero is a two zero filter (duh!)
// Look it up in your favorite DSP text
lastOutput = zeroCoeffs[0] * inputs[0];
lastOutput += zeroCoeffs[1] * inputs[1];
inputs[1] = inputs[0];
inputs[0] = gain * sample;
lastOutput += inputs[0];
return lastOutput;
}

232
src/VoicForm.cpp Normal file
View File

@@ -0,0 +1,232 @@
/*******************************************/
/* 4 Formant Synthesis Instrument */
/* by Perry R. Cook, 1995-96 */
/* 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 (from me) 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. */
/* */
/*******************************************/
#include "VoicForm.h"
#include "SKINI11.msg"
VoicForm :: VoicForm() : Instrmnt()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
voiced = new SingWave(strcat(file,"rawwaves/impuls20.raw"));
voiced->normalize();
voiced->setGainRate((MY_FLOAT) 0.001);
voiced->setGainTarget((MY_FLOAT) 0.0);
noise = new Noise;
filters[0] = new FormSwep;
filters[1] = new FormSwep;
filters[2] = new FormSwep;
filters[3] = new FormSwep;
filters[0]->setSweepRate((MY_FLOAT) 0.001);
filters[1]->setSweepRate((MY_FLOAT) 0.001);
filters[2]->setSweepRate((MY_FLOAT) 0.001);
filters[3]->setSweepRate((MY_FLOAT) 0.001);
onezero = new OneZero;
onezero->setCoeff((MY_FLOAT) -0.9);
onepole = new OnePole;
onepole->setPole((MY_FLOAT) 0.9);
noiseEnv = new Envelope;
noiseEnv->setRate((MY_FLOAT) 0.001);
noiseEnv->setTarget((MY_FLOAT) 0.0);
this->setPhoneme("eee");
this->clear();
}
VoicForm :: ~VoicForm()
{
delete voiced;
delete noise;
delete filters[0];
delete filters[1];
delete filters[2];
delete filters[3];
delete onezero;
delete onepole;
delete noiseEnv;
}
void VoicForm :: clear()
{
onezero->clear();
onepole->clear();
filters[0]->clear();
filters[1]->clear();
filters[2]->clear();
filters[3]->clear();
}
void VoicForm :: setFreq(MY_FLOAT frequency)
{
MY_FLOAT temp;
if ((frequency * 22) > SRATE) {
printf("This note is too high!!\n");
frequency = SRATE / 22;
}
lastFreq = frequency;
temp = fabs(1500 - frequency) + 200;
lastGain = 10000.0 / temp / temp;
voiced->setFreq(frequency);
}
void VoicForm :: setFormantAll(int whichOne, MY_FLOAT freq,MY_FLOAT reson,MY_FLOAT gain)
{
filters[whichOne]->setTargets(freq,reson,gain);
}
#include "phontabl.h"
int VoicForm :: setPhoneme(char *phoneme)
{
int i=0,found=0;
while(i<32 && !found) {
if (!strcmp(phonemes[i],phoneme)) {
found = 1;
this->setFormantAll(0,(MY_FLOAT) phonParams[i][0][0],(MY_FLOAT) phonParams[i][0][1],(MY_FLOAT) pow(10.0,phonParams[i][0][2] / 20.0));
this->setFormantAll(1,(MY_FLOAT) phonParams[i][1][0],(MY_FLOAT) phonParams[i][1][1],(MY_FLOAT) 1.0);
this->setFormantAll(2,(MY_FLOAT) phonParams[i][2][0],(MY_FLOAT) phonParams[i][2][1],(MY_FLOAT) 1.0);
this->setFormantAll(3,(MY_FLOAT) phonParams[i][3][0],(MY_FLOAT) phonParams[i][3][1],(MY_FLOAT) 1.0);
this->setVoicedUnVoiced((MY_FLOAT) phonGains[i][0],(MY_FLOAT) phonGains[i][1]);
printf("Found Formant: %s (number %i)\n",phoneme,i);
}
i++;
}
if (!found) printf("Phoneme %s not found\n",phoneme);
return found;
}
void VoicForm :: setVoiced(MY_FLOAT vGain)
{
voiced->setGainTarget(vGain);
}
void VoicForm :: setUnVoiced(MY_FLOAT nGain)
{
noiseEnv->setTarget(nGain);
}
void VoicForm :: setVoicedUnVoiced(MY_FLOAT vGain, MY_FLOAT nGain)
{
this->setVoiced(vGain);
this->setUnVoiced(nGain);
}
void VoicForm :: setFiltSweepRate(int whichOne,MY_FLOAT rate)
{
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((MY_FLOAT) 0.0);
}
void VoicForm :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
this->setFreq(freq);
voiced->setGainTarget(amp);
onepole->setPole((MY_FLOAT) 0.95 - (amp * (MY_FLOAT) NORM_7 * (MY_FLOAT) 0.2));
}
void VoicForm :: noteOff(MY_FLOAT amp)
{
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(lastOutput);
lastOutput = filters[2]->tick(lastOutput);
lastOutput = filters[3]->tick(lastOutput);
lastOutput *= lastGain;
return lastOutput;
}
void VoicForm :: controlChange(int number, MY_FLOAT value)
{
#if defined(_debug_)
printf("VoicForm : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_Breath_) {
this->setVoiced((MY_FLOAT) 1.0 - (value *(MY_FLOAT) NORM_7));
this->setUnVoiced((MY_FLOAT) 0.01 * value * (MY_FLOAT) NORM_7);
}
else if (number == __SK_FootControl_) {
MY_FLOAT temp = 0.0;
int tempi = (int) value;
if (tempi < 32) {
tempi = tempi;
temp = (MY_FLOAT) 0.9;
}
else if (tempi < 64) {
tempi = tempi - 32;
temp = (MY_FLOAT) 1.0;
}
else if (tempi < 96) {
tempi = tempi - 64;
temp = (MY_FLOAT) 1.1;
}
else if (tempi <= 128) {
tempi = tempi - 96;
temp = (MY_FLOAT) 1.2;
}
this->setFormantAll(0,temp*(MY_FLOAT) phonParams[tempi][0][0],
(MY_FLOAT) phonParams[tempi][0][1],
(MY_FLOAT) pow(10.0,phonParams[tempi][0][2] / 20.0));
this->setFormantAll(1,temp*(MY_FLOAT) phonParams[tempi][1][0],
(MY_FLOAT) phonParams[tempi][1][1],(MY_FLOAT) 1.0);
this->setFormantAll(2,temp*(MY_FLOAT) phonParams[tempi][2][0],
(MY_FLOAT) phonParams[tempi][2][1],(MY_FLOAT) 1.0);
this->setFormantAll(3,temp*(MY_FLOAT) phonParams[tempi][3][0],
(MY_FLOAT) phonParams[tempi][3][1],(MY_FLOAT) 1.0);
this->setVoicedUnVoiced(phonGains[tempi][0],phonGains[tempi][1]);
}
else if (number == __SK_ModFrequency_)
voiced->setVibFreq(value * (MY_FLOAT) NORM_7 * (MY_FLOAT) 12.0); /* 0 to 12 Hz */
else if (number == __SK_ModWheel_)
voiced->setVibAmt(value * (MY_FLOAT) NORM_7 * (MY_FLOAT) 0.2);
else if (number == __SK_AfterTouch_Cont_) {
this->setVoiced(value*NORM_7);
onepole->setPole((MY_FLOAT) 0.99 - (value *(MY_FLOAT) NORM_7 * (MY_FLOAT) 0.2));
//onepole->setPole((MY_FLOAT) 0.5 - (value * (MY_FLOAT) NORM_7 * (MY_FLOAT) 0.2));
}
else {
printf("VoicForm : Undefined Control Number!!\n");
}
}

286
src/VoicMang.cpp Normal file
View File

@@ -0,0 +1,286 @@
/******************************************/
/* Simple Voice Manager (Mangler) */
/* for ToolKit96, 1996 Perry R. Cook */
/* Princeton University */
/* */
/* Make one of these by telling it the */
/* maximum number of voices you'll want, */
/* and also what flavor instrument */
/* group it will be mangling. Pipe SKINI*/
/* messages into it and it will return */
/* the mixed channel signal each tick. */
/* For multi-channel (multi-timbral), */
/* make one for each channel and mix */
/* their outputs. */
/* */
/* Each note on returns a unique tag, */
/* (credits to the NeXT MusicKit here), */
/* so you can send control changes to */
/* unique instances of instruments */
/* within an ensemble. */
/* */
/* SKINI (Synthesis toolKit Instrument */
/* Network Interface) is like MIDI, but */
/* allows for floating point control */
/* changes, note numbers, etc. Example: */
/* noteOn 60.01 111.132 plays a sharp */
/* middle C with a velocity of 111.132 */
/* See SKINI09.txt for more information */
/* */
/******************************************/
#include "VoicMang.h"
#include "Mandolin.h"
#include "Plucked.h"
#include "Clarinet.h"
#include "Flute.h"
#include "Brass.h"
#include "Bowed.h"
#include "Rhodey.h"
#include "Wurley.h"
#include "TubeBell.h"
#include "HeavyMtl.h"
#include "PercFlut.h"
#include "BeeThree.h"
#include "Moog1.h"
VoicMang :: VoicMang(int maxVoices, char *instrType)
{
int i;
max_voices = maxVoices;
if (max_voices > __VMang_MAX_) {
printf("You ask for too many voices, setting to %i\n",__VMang_MAX_);
max_voices = __VMang_MAX_;
}
if (!strcmp(instrType,"Mandolin")) {
for (i=0;i<max_voices;i++)
instrument[i] = new Mandolin((MY_FLOAT) 50.0);
mute_time = 1000;
}
else if (!strcmp(instrType,"Plucked")) {
for (i=0;i<max_voices;i++)
instrument[i] = new Plucked(50.0);
mute_time = 1000;
}
else if (!strcmp(instrType,"Clarinet")) {
for (i=0;i<max_voices;i++)
instrument[i] = new Clarinet(50.0);
mute_time = 1000;
}
else if (!strcmp(instrType,"Flute")) {
for (i=0;i<max_voices;i++)
instrument[i] = new Flute(50.0);
mute_time = 1000;
}
else if (!strcmp(instrType,"Brass")) {
for (i=0;i<max_voices;i++)
instrument[i] = new Brass(50.0);
mute_time = 1000;
}
else if (!strcmp(instrType,"Bowed")) {
for (i=0;i<max_voices;i++)
instrument[i] = new Bowed(50.0);
mute_time = 1000;
}
else if (!strcmp(instrType,"Rhodey")) {
for (i=0;i<max_voices;i++)
instrument[i] = new Rhodey;
mute_time = 1000;
}
else if (!strcmp(instrType,"Wurley")) {
for (i=0;i<max_voices;i++)
instrument[i] = new Wurley;
mute_time = 1000;
}
else if (!strcmp(instrType,"TubeBell")) {
for (i=0;i<max_voices;i++)
instrument[i] = new TubeBell;
mute_time = 1000;
}
else if (!strcmp(instrType,"HeavyMtl")) {
for (i=0;i<max_voices;i++)
instrument[i] = new HeavyMtl;
mute_time = 1000;
}
else if (!strcmp(instrType,"PercFlut")) {
for (i=0;i<max_voices;i++)
instrument[i] = new PercFlut;
mute_time = 1000;
}
else if (!strcmp(instrType,"BeeThree")) {
for (i=0;i<max_voices;i++)
instrument[i] = new BeeThree;
mute_time = 1000;
}
else if (!strcmp(instrType,"Moog1")) {
for (i=0;i<max_voices;i++)
instrument[i] = new Moog1;
mute_time = 1000;
}
pitch_bend = (MY_FLOAT) 1.0;
newTag = 1;
for (i=0;i<max_voices;i++) {
notesOn[i] = -1;
voicesOn[i] = 0;
freqBases[i] = (MY_FLOAT) 1.0;
frequencies[i] = (MY_FLOAT) 0.0;
}
}
VoicMang :: ~VoicMang()
{
int i;
for (i=0;i<max_voices;i++)
delete instrument[i];
}
long VoicMang :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
MY_FLOAT temp = 0.0;
temp = freq / (MY_FLOAT) 220.0;
temp = (MY_FLOAT) (log(temp) / log(2.0));
temp = (temp * (MY_FLOAT) 12.0) + 57;
return this->noteOn(temp,amp);
}
#define ONE_OVER_12 0.083333
long VoicMang :: noteOnN(MY_FLOAT note_num, MY_FLOAT amp)
{
int i,gotOne;
MY_FLOAT freq;
freq = (MY_FLOAT) 220.0 * pow(2.0,(note_num - 57.0) * ONE_OVER_12);
gotOne = 0;
for (i=0;i<max_voices;i++) {
if (notesOn[i] < 0) { /* find a dormant instrument */
voicesOn[i] = newTag;
newTag += 1;
notesOn[i] = (int) (note_num + (MY_FLOAT) 0.5); /* Nearest Neighbor MIDI Note# */
instrument[i]->noteOn(freq,amp*(MY_FLOAT) NORM_7);
frequencies[i] = freq;
gotOne = 1;
i = max_voices;
}
}
if (!gotOne) { /* if can't find a free one */
return -1; /* report back */
}
return newTag-1;
}
long VoicMang :: oldestVoice()
{
int i;
long temp2,gotOne;
temp2 = newTag;
gotOne = -1;
for (i=0;i<max_voices;i++) {
temp2 = voicesOn[i];
if (temp2 > 0 && temp2 < newTag) {
gotOne = temp2;
}
}
return gotOne;
}
long VoicMang :: noteOffN(int note_num, MY_FLOAT amp)
{
int i,gotOne;
gotOne = -1;
for (i=0;i<max_voices;i++) {
if (notesOn[i]==note_num) {
instrument[i]->noteOff(amp * (MY_FLOAT) NORM_7);
gotOne = voicesOn[i];
voicesOn[i] = -mute_time - 2;
}
}
return gotOne;
}
void VoicMang :: noteOffT(long tag, MY_FLOAT amp)
{
int i;
for (i=0;i<max_voices;i++) {
if (voicesOn[i]==tag) {
instrument[i]->noteOff(amp * (MY_FLOAT) NORM_7);
voicesOn[i] = -mute_time - 2;
}
}
}
void VoicMang :: kill(long tag)
{
int i;
for (i=0;i<max_voices;i++) {
if (voicesOn[i]==tag) {
instrument[i]->noteOff((MY_FLOAT) 1.0);
voicesOn[i] = 0;
notesOn[i] = -1;
}
}
}
void VoicMang :: pitchBend(MY_FLOAT value)
{
int i;
pitch_bend = value;
for (i=0;i<max_voices;i++) {
freqBases[i] = pitch_bend;
instrument[i]->setFreq(freqBases[i]*frequencies[i]);
}
}
void VoicMang :: pitchBendT(long tag, MY_FLOAT value)
{
int i;
for (i=0;i<max_voices;i++) {
if (voicesOn[i] == tag) {
freqBases[i] = value;
instrument[i]->setFreq(freqBases[i]*frequencies[i]);
i = max_voices;
}
}
}
MY_FLOAT VoicMang :: tick()
{
int i;
MY_FLOAT temp;
temp = (MY_FLOAT) 0.0;
for (i=0;i<max_voices;i++) {
if (voicesOn[i] != 0) {
temp += instrument[i]->tick();
}
if (voicesOn[i] < 0) {
voicesOn[i] += 1;
if (voicesOn[i] == 0) {
notesOn[i] = -1;
frequencies[i] = (MY_FLOAT) 0.0;
freqBases[i] = (MY_FLOAT) 1.0;
}
}
}
return temp;
}
void VoicMang :: controlChange(int number, MY_FLOAT value)
{
int i;
for (i=0;i<max_voices;i++) {
instrument[i]->controlChange(number,value);
}
}
void VoicMang :: controlChangeT(long tag, int number, MY_FLOAT value)
{
int i;
for (i=0;i<max_voices;i++) {
if (voicesOn[i] == tag) {
instrument[i]->controlChange(number,value);
i = max_voices;
}
}
}

204
src/WavWvIn.cpp Normal file
View File

@@ -0,0 +1,204 @@
/*******************************************/
/* WavWvIn Input Class, */
/* by Gary P. Scavone, 2000 */
/* */
/* This object inherits from WvIn and is */
/* used to open DOS/Windows .wav 16-bit */
/* data (signed integer) files for */
/* playback. */
/* */
/* .wav files are always little-endian. */
/*******************************************/
#include "WavWvIn.h"
#ifndef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
WavWvIn :: WavWvIn(char *fileName, const char *mode)
{
char msg[256];
// check mode string
if ( strcmp(mode,"oneshot") && strcmp(mode,"looping") ) {
sprintf(msg, "WavWvIn: constructor parameter 'mode' must be oneshot or looping only.\n");
throw StkError(msg, StkError::FUNCTION_SYNTAX);
}
// Open the file and get header info
fd = fopen(fileName,"rb");
if (!fd) {
sprintf(msg, "WavWvIn: Couldn't open or find file (%s).\n", fileName);
throw StkError(msg, StkError::FILE_NOT_FOUND);
}
// Make sure this is a .wav format file
char id[4];
fseek(fd,8,SEEK_SET); // Locate wave id
fread(&id,4,1,fd);
if (strncmp(id,"WAVE",4)) {
fclose(fd);
sprintf(msg, "WavWvIn: %s doesn't appear to be a WAV file.\n", fileName);
throw StkError(msg, StkError::FILE_ERROR);
}
// Find "format" chunk ... it must come before the "data" chunk
INT32 chunkSize;
fread(&id,4,1,fd);
while (strncmp(id,"fmt ",4)) {
fread(&chunkSize,4,1,fd);
#ifndef __LITTLE_ENDIAN__
swap32((unsigned char *)&chunkSize);
#endif
fseek(fd,chunkSize,SEEK_CUR);
fread(&id,4,1,fd);
}
// Check that the data is not compressed
INT16 format_tag;
fseek(fd,4,SEEK_CUR); // Locate format tag ... jump over chunkSize
fread(&format_tag,2,1,fd);
#ifndef __LITTLE_ENDIAN__
swap16((unsigned char *)&format_tag);
#endif
if (format_tag != 1) { // PCM = 1
fclose(fd);
sprintf(msg, "WavWvIn: %s contains compressed data, which is not supported.\n", fileName);
throw StkError(msg, StkError::FILE_ERROR);
}
// Get number of channels from the header
INT16 temp;
fread(&temp,2,1,fd);
#ifndef __LITTLE_ENDIAN__
swap16((unsigned char *)&temp);
#endif
channels = temp;
// Get file sample rate from the header and set the default rate
INT32 srate;
fread(&srate,4,1,fd);
#ifndef __LITTLE_ENDIAN__
swap32((unsigned char *)&srate);
#endif
rate = (MY_FLOAT) (srate/SRATE); // set default rate based on file sampling rate
// Verify that the data is 16 bits per sample
fseek(fd,6,SEEK_CUR); // Locate bits_per_sample info
fread(&temp,2,1,fd);
#ifndef __LITTLE_ENDIAN__
swap16((unsigned char *)&temp);
#endif
if (temp != 16) {
fclose(fd);
sprintf(msg, "WavWvIn: STK does not currently support data formats other than 16 bit signed integer.\n");
throw StkError(msg, StkError::FILE_ERROR);
}
// Find "data" chunk ... it must come after the "fmt" chunk
fread(&id,4,1,fd);
while (strncmp(id,"data",4)) {
fread(&chunkSize,4,1,fd);
#ifndef __LITTLE_ENDIAN__
swap32((unsigned char *)&chunkSize);
#endif
fseek(fd,chunkSize,SEEK_CUR);
fread(&id,4,1,fd);
}
// Get length of data from the header
INT32 bytes;
fread(&bytes,4,1,fd);
#ifndef __LITTLE_ENDIAN__
swap32((unsigned char *)&bytes);
#endif
// length is the number of sample frames
fileSize = bytes / 2 / channels;
bufferSize = fileSize;
if ((fileSize*channels) > MAX_FILE_LOAD_SIZE) {
printf("\nWavWvIn: The .WAV file (%s) has more than %d samples and\n",
fileName, MAX_FILE_LOAD_SIZE);
printf("will be loaded incrementally from disk. Normalization will be disabled.\n");
chunking = 1;
bufferSize = LOAD_BUFFER_SIZE;
}
// Setup for looping or one-shot playback
if (!strcmp(mode,"looping"))
looping = 1;
else // default = oneshot
looping = 0;
data = (MY_FLOAT *) new MY_FLOAT[(bufferSize+1)*channels];
dataOffset = ftell(fd);
this->getData(0); // Read samples into data[]
if (fmod(rate, 1.0) != 0.0) interpolate = 1;
else interpolate = 0;
phaseOffset = (MY_FLOAT) 0.0;
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
this->reset();
// finally, let's normalize the data by default
this->normalize();
}
WavWvIn :: ~WavWvIn()
{
}
void WavWvIn :: getData(long index)
{
/* Compare index to current readPointer and modify as needed.
* The following while() loops will only execute on calls subsequent
* to class instantiation ... and thus, only when "chunking".
*/
while (index < readPointer) {
readPointer -= LOAD_BUFFER_SIZE;
bufferSize = LOAD_BUFFER_SIZE;
if (readPointer < 0) {
bufferSize += readPointer;
readPointer = 0;
}
}
while (index >= readPointer+bufferSize) {
readPointer += LOAD_BUFFER_SIZE;
bufferSize = LOAD_BUFFER_SIZE;
if (readPointer+LOAD_BUFFER_SIZE >= fileSize) {
bufferSize = fileSize - readPointer;
}
}
fseek(fd, dataOffset+(long)(readPointer*channels*2), SEEK_SET);
long length = bufferSize;
int end_of_file = (readPointer+bufferSize == fileSize);
if (!end_of_file) length += 1;
// Read samples into data[]. Use MY _FLOAT data structure to store INT16 samples
INT16 *buf = (INT16 *)data;
fread(buf, 2, length*channels, fd);
// Convert in place (unpack) to MY_FLOAT from the end of the array
for (int i=length*channels-1; i>=0; i--) {
#ifndef __LITTLE_ENDIAN__
swap16((unsigned char *)(buf+i));
#endif
data[i] = buf[i];
if (chunking) data[i] *= 0.00003051;
}
// fill in the extra sample frame for interpolation
if (end_of_file) {
for (int j=0; j<channels; j++)
if (looping)
data[bufferSize*channels+j] = data[j];
else
data[bufferSize*channels+j] = data[(bufferSize-1)*channels+j];
}
if (!chunking) {
fclose(fd);
fd = 0;
}
}

144
src/WavWvOut.cpp Normal file
View File

@@ -0,0 +1,144 @@
/*******************************************/
/* Wave file Output Class, */
/* by Perry R. Cook, 1995-96 */
/* revised by Gary P. Scavone, 1999 */
/* */
/* This Object opens a DOS/Windows .wav */
/* 16bit data (signed integers) file, and */
/* poke buffers of samples into it. */
/*******************************************/
#include "WavWvOut.h"
#ifndef __LITTLE_ENDIAN__
#include "ByteSwap.h"
#endif
/******** Wav Soundfile Header Struct *******/
struct wavehdr {
char riff[4]; // "RIFF"
INT32 file_size; // in bytes
char wave[4]; // "WAVE"
char fmt[4]; // "fmt "
INT32 block_size; // in bytes (16 for PCM)
INT16 format_tag; // 1=PCM, 257=Mu-Law, 258=A-Law, 259=ADPCM
INT16 num_chans; // 1=mono, 2=stereo
INT32 srate;
INT32 bytes_per_sec;
INT16 bytes_per_samp; // 2=16-bit mono, 4=16-bit stereo
INT16 bits_per_samp;
char data[4]; // "data"
INT32 dlength; // in bytes
};
FILE *openWAVFile(int chans,char *fileName) {
struct wavehdr hdr = {"RIF",44,"WAV","fmt",
16,1,1,(INT32) SRATE,(INT32) SRATE*2,2,16,"dat",0};
char tempName[128];
FILE *fd;
char msg[256];
hdr.riff[3] = 'F';
hdr.wave[3] = 'E';
hdr.fmt[3] = ' ';
hdr.data[3] = 'a';
strcpy(tempName,fileName);
if (strstr(tempName,".wav") == NULL) strcat(tempName,".wav");
hdr.num_chans = chans;
hdr.bytes_per_sec = (long) SRATE*2*chans;
hdr.bytes_per_samp = 2*chans;
hdr.bits_per_samp = 16;
fd = fopen(tempName,"wb");
if (!fd) {
sprintf(msg, "WavWvOut: Could not create soundfile: %s\n", tempName);
throw StkError(msg, StkError::FILE_ERROR);
}
#ifndef __LITTLE_ENDIAN__
swap32((unsigned char *)&hdr.file_size);
swap32((unsigned char *)&hdr.block_size);
swap16((unsigned char *)&hdr.format_tag);
swap16((unsigned char *)&hdr.num_chans);
swap32((unsigned char *)&hdr.srate);
swap32((unsigned char *)&hdr.bytes_per_sec);
swap16((unsigned char *)&hdr.bytes_per_samp);
swap16((unsigned char *)&hdr.bits_per_samp);
#endif
printf("\nCreating soundfile: %s\n", tempName);
fwrite(&hdr,4,11,fd);
return fd;
}
WavWvOut :: WavWvOut(char *fileName, int chans)
{
char msg[256];
if (chans < 1) {
sprintf(msg, "WavWvOut: number of channels = %d not supported!\n", chans);
throw StkError(msg, StkError::FUNCTION_SYNTAX);
}
channels = chans;
fd = openWAVFile(chans,fileName);
data_length = FILE_BUFFER_SIZE*channels;
data = (INT16 *) new INT16[data_length];
}
WavWvOut :: ~WavWvOut()
{
MY_FLOAT time;
INT32 bytes;
fwrite(data,2,counter,fd);
time = (double) totalCount * ONE_OVER_SRATE;
printf("%f Seconds Computed\n\n", time);
bytes = totalCount*2*channels;
#ifndef __LITTLE_ENDIAN__
swap32((unsigned char *)&bytes);
#endif
fseek(fd,40,SEEK_SET); // jump to data length
fwrite(&bytes,4,1,fd);
bytes = totalCount*2*channels + 44;
#ifndef __LITTLE_ENDIAN__
swap32((unsigned char *)&bytes);
#endif
fseek(fd,4,SEEK_SET); // jump to file size
fwrite(&bytes,4,1,fd);
fclose(fd);
}
void WavWvOut :: tick(MY_FLOAT sample)
{
static INT16 isample;
isample = (INT16) (sample * 32000.0);
#ifndef __LITTLE_ENDIAN__
swap16 ((unsigned char *)&isample);
#endif
for (int i=0;i<channels;i++)
data[counter++] = isample;
totalCount++;
if (counter == data_length) {
fwrite(data,2,data_length,fd);
counter = 0;
}
}
void WavWvOut :: mtick(MY_MULTI samples)
{
for (int i=0;i<channels;i++) {
data[counter] = (INT16) (*samples++ * 32000.0);
#ifndef __LITTLE_ENDIAN__
swap16 ((unsigned char *)&data[counter]);
#endif
counter++;
}
totalCount++;
if (counter == data_length) {
fwrite(data,2,data_length,fd);
counter = 0;
}
}

62
src/Wurley.cpp Normal file
View File

@@ -0,0 +1,62 @@
/******************************************/
/* Wurlitzer Electric Piano Subclass */
/* of Algorithm 5 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/******************************************/
#include "Wurley.h"
Wurley :: Wurley() : FM4Alg5()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file1[128];
char file2[128];
char file3[128];
char file4[128];
strcpy(file1, RAWWAVE_PATH);
strcpy(file2, RAWWAVE_PATH);
strcpy(file3, RAWWAVE_PATH);
strcpy(file4, RAWWAVE_PATH);
this->loadWaves(strcat(file1,"rawwaves/sinewave.raw"),
strcat(file2,"rawwaves/sinewave.raw"),
strcat(file3,"rawwaves/sinewave.raw"),
strcat(file4,"rawwaves/fwavblnk.raw"));
this->setRatio(0,(MY_FLOAT) 1.0);
this->setRatio(1,(MY_FLOAT) 4.0);
this->setRatio(2,(MY_FLOAT) -510.0);
this->setRatio(3,(MY_FLOAT) -510.0);
gains[0] = __FM4Op_gains[99];
gains[1] = __FM4Op_gains[82];
gains[2] = __FM4Op_gains[92];
gains[3] = __FM4Op_gains[68]; /* Originally 78, but sounded stinky */
twozero->setGain((MY_FLOAT) 2.0);
adsr[0]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
adsr[1]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 1.50,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
adsr[2]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.25,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
adsr[3]->setAllTimes((MY_FLOAT) 0.001,(MY_FLOAT) 0.15,(MY_FLOAT) 0.0,(MY_FLOAT) 0.04);
vibWave->setFreq((MY_FLOAT) 8.0);
}
void Wurley :: setFreq(MY_FLOAT frequency)
{
baseFreq = frequency;
waves[0]->setFreq(baseFreq * ratios[0]);
waves[1]->setFreq(baseFreq * ratios[1]);
waves[2]->setFreq(ratios[2]); /* Note here a 'fixed resonance' */
waves[3]->setFreq(ratios[3]);
}
void Wurley :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
{
gains[0] = amp * __FM4Op_gains[99];
gains[1] = amp * __FM4Op_gains[82];
gains[2] = amp * __FM4Op_gains[82]; /* Originally 92 */
gains[3] = amp * __FM4Op_gains[68]; /* Originally 78 */
this->setFreq(freq);
this->keyOn();
#if defined(_debug_)
printf("Wurley : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
}

282
src/WvIn.cpp Normal file
View File

@@ -0,0 +1,282 @@
/********************************************/
/*
Audio Data Input Base Class
by Gary P. Scavone, 1999-2000
This class can handle multi-channel
input. Multi-channel input is
interleaved in the vector "data".
Actual data input occurs in the
subclasses of WvIn.
Currently, STK is only supporting a few data
types (16-bit integer .snd, .wav, .raw, and
.aif files and 64-bit double MAT-files). In
order to support more formats AND to make the
writing of subclasses easier, a format ENUM
could be defined and a generalized getData()
function written within this WvIn class. Then,
most subclasses of WvIn would only have to
setup the appropriate parameters and all
other processing would happen here.
*/
/********************************************/
#include "WvIn.h"
#include <stdio.h>
WvIn :: WvIn()
{
fd = 0;
chunking = 0;
readPointer = 0;
fileSize = 0;
}
WvIn :: ~WvIn()
{
if (fd) {
fclose(fd);
fd = 0;
}
if (data) {
delete [ ] data;
data = 0;
}
if (lastOutput) {
delete [ ] lastOutput;
lastOutput = 0;
}
}
void WvIn :: reset()
{
finished = 0;
time = (MY_FLOAT) 0.0;
for (int i=0;i<channels;i++) {
lastOutput[i] = (MY_FLOAT) 0.0;
}
}
void WvIn :: normalize()
{
/* Do nothing for streamed input ... cannot be normalized */
if (chunking) return;
this->normalize((MY_FLOAT) 1.0);
}
// Normalize all channels equally by the greatest magnitude in all of data
void WvIn :: normalize(MY_FLOAT newPeak)
{
/* Do nothing for streamed input ... cannot be normalized */
if (chunking) return;
long i;
MY_FLOAT max = (MY_FLOAT) 0.0;
for (i=0;i<channels*bufferSize;i++) {
if (fabs(data[i]) > max)
max = (MY_FLOAT) fabs((double) data[i]);
}
if (max > 0.0) {
max = (MY_FLOAT) 1.0 / max;
max *= newPeak;
for (i=0;i<=channels*bufferSize;i++)
data[i] *= max;
}
}
void WvIn :: setRate(MY_FLOAT aRate)
{
rate = aRate;
// If negative rate and at beginning of sound, move pointer to end of sound.
if ( (rate < 0) && (time == 0.0) ) time += rate + fileSize;
if (fmod(rate, 1.0) != 0.0) interpolate = 1;
else interpolate = 0;
}
void WvIn :: setFreq(MY_FLOAT aFreq)
{
// This is a looping frequency.
if (looping) {
rate = fileSize * (MY_FLOAT) ONE_OVER_SRATE * aFreq;
if (fmod(rate, 1.0) != 0.0) interpolate = 1;
else interpolate = 0;
}
}
void WvIn :: addTime(MY_FLOAT aTime)
{
// Add an absolute time in samples
time += aTime;
while (time < 0.0)
time += fileSize;
while (time >= fileSize)
time -= fileSize;
}
void WvIn :: addPhase(MY_FLOAT anAngle)
{
// Add a time in cycles (one cycle = fileSize) ... for looping.
if (looping) {
time += fileSize * anAngle;
while (time < 0.0)
time += fileSize;
while (time >= fileSize)
time -= fileSize;
}
}
void WvIn :: addPhaseOffset(MY_FLOAT anAngle)
{
// Add a phase offset in cycles, where 1.0 = fileSize (looping).
if (looping)
phaseOffset = fileSize * anAngle;
}
void WvIn :: setInterpolate(int anInterpStatus)
{
interpolate = anInterpStatus;
}
void WvIn :: setLooping(int aLoopStatus)
{
time = (MY_FLOAT) 0.0;
looping = aLoopStatus;
if (looping && !chunking) {
for (int i=0;i<channels;i++)
data[bufferSize*channels+i] = data[i];
}
else {
for (int i=0;i<channels;i++)
data[bufferSize*channels+i] = data[(bufferSize-1)*channels+i];
}
}
long WvIn :: getSize()
{
return fileSize;
}
void WvIn :: getData(long index)
{
// implemented in subclasses
}
int WvIn :: isFinished()
{
return finished;
}
MY_FLOAT WvIn :: tick()
{
this->informTick();
if (channels > 1) {
MY_FLOAT tempout = 0.0;
for (int i=0;i<channels;i++)
tempout += lastOutput[i];
tempout /= channels;
return tempout;
}
else
return *lastOutput;
}
MY_MULTI WvIn :: mtick()
{
this->informTick();
return lastOutput;
}
int WvIn :: informTick()
{
static MY_FLOAT temp, alpha;
static long index;
if (finished) return finished;
if (looping) {
// Check limits of time address ... if necessary, recalculate modulo fileSize.
while (time < 0.0)
time += fileSize;
while (time >= fileSize)
time -= fileSize;
if (phaseOffset) {
temp = time + phaseOffset;
while (temp < 0.0)
temp += fileSize;
while (temp >= fileSize)
temp -= fileSize;
}
else {
temp = time;
}
}
else {
// oneshot: if out of bounds, we're done
if ( (time < 0.0) || (time >= fileSize) ) {
finished = 1;
return finished;
}
temp = time;
}
if (chunking) {
// check the time address vs. our current buffer limits
if ( (temp < readPointer) || (temp >= readPointer+bufferSize) )
this->getData((long) temp);
// adjust index for current buffer
temp -= readPointer;
}
// integer part of time address
index = (long) temp;
if (interpolate) {
// fractional part of time address
alpha = temp - (MY_FLOAT) index;
index *= channels;
for (int i=0;i<channels;i++) {
// Do linear interpolation
lastOutput[i] = data[index];
lastOutput[i] += (alpha * (data[index+channels] - lastOutput[i]));
index++;
}
}
else {
index *= channels;
for (int i=0;i<channels;i++) {
lastOutput[i] = data[index++];
}
}
// increment time, which can be negative
time += rate;
return finished;
}
MY_FLOAT WvIn :: lastOut()
{
if (channels > 1) {
MY_FLOAT tempout = 0.0;
for (int i=0;i<channels;i++)
tempout += lastOutput[i];
tempout /= channels;
return tempout;
}
else
return *lastOutput;
}
MY_MULTI WvIn :: mlastOut()
{
return lastOutput;
}

51
src/WvOut.cpp Normal file
View File

@@ -0,0 +1,51 @@
/********************************************/
/* WvOut Abstract Class, */
/* by Tim Stilson, 1996 */
/* revised by Gary P. Scavone, 1999-2000 */
/* */
/* This class can handle multi-channel */
/* data via the mtick() method. */
/* */
/* Currently, WvOut and its subclasses are */
/* non-interpolating. Thus, the output */
/* rate is always SRATE (defined in */
/* Object.h). A future upgrade could add */
/* interpolation functionality to allow */
/* output rates different than the STK */
/* internal processing rate (SRATE). */
/********************************************/
#include "WvOut.h"
WvOut :: WvOut()
{
data = 0;
counter = 0;
totalCount = 0;
}
WvOut :: ~WvOut()
{
if (data) {
delete [ ] data;
data = 0;
}
}
long WvOut :: getCounter()
{
return totalCount;
}
MY_FLOAT WvOut :: getTime()
{
return (MY_FLOAT) totalCount * ONE_OVER_SRATE;
}
void WvOut :: tick(MY_FLOAT sample)
{
}
void WvOut :: mtick(MY_MULTI samples)
{
}

229
src/mandplyr.cpp Normal file
View File

@@ -0,0 +1,229 @@
/********************************************/
/* MandPlyr Player Expert Object to control*/
/* commuted dual plucked-string model */
/* by Perry Cook, 1995-96 */
/********************************************/
#include "Instrmnt.h"
#include "Mandolin.h"
#include "mandplyr.h"
#include "VoicMang.h"
#include "Noise.h"
#include "SKINI11.msg"
MandPlyr :: MandPlyr() : Instrmnt()
{
short i;
strings = new VoicMang(NUM_STRINGS,"Mandolin");
noise = new Noise;
strumming = 0;
strumRate = (long) (SRATE * 0.1); // ( 1/(0.1) = 10Hz)
strumCount = 0;
skill = (MY_FLOAT) 0.90;
for (i=0;i<NUM_STRINGS;i++) {
nums[i] = 0;
amps[i] = (MY_FLOAT) 0;
tags[i] = -1;
}
}
MandPlyr :: ~MandPlyr()
{
delete strings;
delete noise;
}
void MandPlyr :: noteOnN(short num, MY_FLOAT amp)
{
long temp;
short i,notDone;
MY_FLOAT isErr,errDir;
short error = 0;
errDir = noise->tick();
if (errDir < 0)
isErr = -errDir;
else
isErr = errDir;
if (isErr < (0.4 * (1.0 - skill))) { /* Should we err here? */
error = 1;
if (strumming < 1) {
strumming = 2;
strumCount = strumRate;
}
if (errDir < 0.0) error = -1; /* random error +/- 1/2 step */
}
i = 0;
notDone = 1;
while (notDone && i<NUM_STRINGS) {
if (tags[i] < 0) {
nums[i] = num;
amps[i] = amp;
tags[i] = strings->noteOnN((MY_FLOAT) num + error,amp);
notDone = 0;
if (tags[i]<0) {
printf("Must Reallocate a note\n");
notDone = 1;
}
}
i++;
}
if (notDone) {
temp = strings->oldestVoice();
i = 0;
while (notDone && i<NUM_STRINGS) {
if (tags[i] < 0) {
strings->kill(temp);
nums[i] = num;
amps[i] = amp;
tags[i] = strings->noteOnN((MY_FLOAT) num + error,amp);
notDone = 0;
}
i++;
}
}
if (notDone) printf("Can't allocate a new note\n");
}
void MandPlyr :: noteOffN(short num,MY_FLOAT amp)
{
long temp;
short i;
temp = strings->noteOffN(num,amp);
if (temp>0) { // Usual Case
for (i=0;i<NUM_STRINGS;i++) {
if (tags[i] == temp) {
tags[i] = -1;
nums[i] = 0;
amps[i] = (MY_FLOAT) 0.0;
}
}
}
else { // In case note already off
for (i=0;i<NUM_STRINGS;i++) { // from strumming
if (nums[i] == num) { // Just clear its entry
strings->noteOffT(num,amp);
tags[i] = -1;
nums[i] = 0;
amps[i] = (MY_FLOAT) 0.0;
}
}
}
if (strumming == 2) strumming = 0;
}
MY_FLOAT MandPlyr :: tick()
{
short i;
MY_FLOAT temp2;
if (strumming > 0) {
strumCount -= 1;
if (strumCount <= 0) {
for (i=0;i<NUM_STRINGS;i++) {
if (tags[i] > 0) {
strings->kill(tags[i]);
temp2 = (MY_FLOAT) fabs(noise->tick());
if (temp2 > 0.2 * (1.0 - skill))
tags[i] = strings->noteOnN(nums[i],amps[i]);
}
}
strumCount = strumRate;
strumCount = strumCount +
(long) (strumCount * (noise->tick() * (1.0 - skill)));
if (strumming == 2) strumming = 0;
}
}
for (i=0;i<NUM_STRINGS;i++) {
if (tags[i] < -1) {
tags[i] += 1;
if (tags[i] == -1) {
tags[i] = strings->noteOnN(nums[i],amps[i]);
}
}
}
lastOutput = strings->tick();
return lastOutput;
}
/* Some very basic Mandolin Chords */
/* Base Strings: G D A E */
/* G Chord: G D B G */
/* C Chord: G E C G */
/* D Chord: A D A F# */
/* E Chord: G# E B G# */
/* A Chord: A E C# A */
/* B Chord: B D# B F# */
#define NUM_CHORDS 6
void MandPlyr :: playChord(MY_FLOAT amp, char *chordString)
{
short i;
char chordStr[NUM_CHORDS+1] = {"GCDEAB"};
short chords[NUM_CHORDS][4] = {
{55,62,71,79},
{55,64,72,79},
{57,62,69,78},
{56,64,71,80},
{57,64,73,81},
{59,63,71,78} };
short whichChord = -1;
for (i=0;i<4;i++)
if (tags[i] >= 0) strings->noteOffT(tags[i],amp);
for (i=0;i<NUM_CHORDS;i++) {
if (chordString[0]==chordStr[i])
whichChord = i;
}
if (chordString[0] == 0) {
}
else {
if (whichChord == -1) {
printf("I don't know this chord: %s\n",chordString);
}
else {
for (i=0;i<4;i++) {
nums[i] = chords[whichChord][i];
tags[i] = -2 - (i * 10 * (200 - (int) amp));
amps[i] = amp;
}
}
}
}
void MandPlyr :: controlChange(int number, MY_FLOAT value)
{
#if defined(_debug_)
printf("MandPlyr : ControlChange: Number=%i Value=%f\n",number,value);
#endif
if (number == __SK_BodySize_)
strings->controlChange(number,value);
else if (number == __SK_PickPosition_)
strings->controlChange(number,value);
else if (number == __SK_StringDamping_)
strings->controlChange(number,value);
else if (number == __SK_StringDetune_)
strings->controlChange(number,value);
else if (number == __SK_Strumming_) {
if (value < 0.5)
strumming = 0;
else
strumming = 1;
}
else if (number == __SK_NotStrumming_)
strumming = 0;
else if (number == __SK_PlayerSkill_)
skill = value * (MY_FLOAT) NORM_7;
else if (number == __SK_StrumSpeed_)
strumRate = (long) (SRATE * value * NORM_7); // (0 - 1Hz.)
else {
printf("MandPlyr : Undefined Control Number!!\n");
}
}