Version 4.0

This commit is contained in:
Gary Scavone
2013-09-25 14:50:19 +02:00
committed by Stephen Sinclair
parent 3f126af4e5
commit 81475b04c5
473 changed files with 36355 additions and 28396 deletions

View File

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

View File

@@ -1,209 +0,0 @@
/*******************************************/
/*
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;
}
}

View File

@@ -1,192 +0,0 @@
/*******************************************/
/*
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;
}
}

342
src/BandedWG.cpp Normal file
View File

@@ -0,0 +1,342 @@
/***************************************************/
/*! \class BandedWG
\brief Banded waveguide modeling class.
This class uses banded waveguide techniques to
model a variety of sounds, including bowed
bars, glasses, and bowls. For more
information, see Essl, G. and Cook, P. "Banded
Waveguides: Towards Physical Modelling of Bar
Percussion Instruments", Proceedings of the
1999 International Computer Music Conference.
Control Change Numbers:
- Bow Pressure = 2
- Bow Motion = 4
- Strike Position = 8 (not implemented)
- Vibrato Frequency = 11
- Gain = 1
- Bow Velocity = 128
- Set Striking = 64
- Instrument Presets = 16
- Uniform Bar = 0
- Tuned Bar = 1
- Glass Harmonica = 2
- Tibetan Bowl = 3
by Georg Essl, 1999 - 2002.
Modified for Stk 4.0 by Gary Scavone.
*/
/***************************************************/
#include "BandedWG.h"
#include "SKINI.msg"
#include "Noise.h"
#include <math.h>
BandedWG :: BandedWG()
{
doPluck = true;
delay = new Delay[MAX_BANDED_MODES];
bandpass = new BiQuad[MAX_BANDED_MODES];
bowTabl = new BowTabl;
bowTabl->setSlope( 3.0 );
adsr = new ADSR;
adsr->setAllTimes( 0.02, 0.005, 0.9, 0.01);
freakency = 220.0;
setPreset(0);
bowPosition = 0;
baseGain = (MY_FLOAT) 0.999;
integrationConstant = 0.0;
trackVelocity = false;
bowVelocity = 0.0;
bowTarget = 0.0;
}
BandedWG :: ~BandedWG()
{
delete bowTabl;
delete adsr;
delete [] bandpass;
delete [] delay;
}
void BandedWG :: clear()
{
for (int i=0; i<nModes; i++) {
delay[i].clear();
bandpass[i].clear();
}
}
void BandedWG :: setPreset(int preset)
{
int i;
switch (preset){
case 1: // Tuned Bar
presetModes = 4;
modes[0] = (MY_FLOAT) 1.0;
modes[1] = (MY_FLOAT) 4.0198391420;
modes[2] = (MY_FLOAT) 10.7184986595;
modes[3] = (MY_FLOAT) 18.0697050938;
for (i=0; i<presetModes; i++)
gains[i] = (MY_FLOAT) pow(0.999,(double) i);
break;
case 2: // Glass Harmonica
presetModes = 5;
modes[0] = (MY_FLOAT) 1.0;
modes[1] = (MY_FLOAT) 2.32;
modes[2] = (MY_FLOAT) 4.25;
modes[3] = (MY_FLOAT) 6.63;
modes[4] = (MY_FLOAT) 9.38;
// modes[5] = (MY_FLOAT) 12.22;
for (i=0; i<presetModes; i++)
gains[i] = (MY_FLOAT) pow(0.999,(double) i);
/*
baseGain = (MY_FLOAT) 0.99999;
for (i=0; i<presetModes; i++)
gains[i]= (MY_FLOAT) pow(baseGain, delay[i].getDelay()+i);
*/
break;
case 3: // Tibetan Prayer Bowl
presetModes = 17;
modes[0] = (MY_FLOAT) 1.0;
modes[1] = (MY_FLOAT) 1.135;
modes[2] = (MY_FLOAT) 2.329;
modes[3] = (MY_FLOAT) 3.210;
modes[4] = (MY_FLOAT) 6.046;
modes[5] = (MY_FLOAT) 6.106;
modes[6] = (MY_FLOAT) 6.419;
modes[7] = (MY_FLOAT) 9.689;
modes[8] = (MY_FLOAT) 12.212;
modes[9] = (MY_FLOAT) 13.869;
modes[10] = (MY_FLOAT) 15.735;
modes[11] = (MY_FLOAT) 15.795;
modes[12] = (MY_FLOAT) 18.601;
modes[13] = (MY_FLOAT) 18.661;
modes[14] = (MY_FLOAT) 19.363;
modes[15] = (MY_FLOAT) 23.901;
modes[16] = (MY_FLOAT) 32.470;
for (i=0; i<presetModes; i++)
gains[i]=0.9995;
break;
default: // Uniform Bar
presetModes = 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<presetModes; i++)
gains[i] = (MY_FLOAT) pow(0.9,(double) i);
break;
}
nModes = presetModes;
setFrequency( freakency );
}
void BandedWG :: setFrequency(MY_FLOAT frequency)
{
freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "BandedWG: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
if (freakency > 1568.0) freakency = 1568.0;
MY_FLOAT radius;
MY_FLOAT base = Stk::sampleRate() / freakency;
int length;
for (int i=0; i<presetModes; i++) {
// Calculate the delay line lengths for each mode.
length = (int) (base / modes[i]);
if ( length > 2)
delay[i].setDelay( length );
else {
nModes = i;
break;
}
// Set the bandpass filter resonances
radius = 1.0 - PI * freakency * modes[i] / Stk::sampleRate();
if ( radius < 0.0 ) radius = 0.0;
bandpass[i].setResonance(freakency * modes[i], radius, true);
delay[i].clear();
bandpass[i].clear();
}
//int olen = (int)(delay[0].getDelay());
//strikePosition = (int)(strikePosition*(length/modes[0])/olen);
}
void BandedWG :: setStrikePosition(MY_FLOAT position)
{
strikePosition = (int)(delay[0].getDelay() * position / 2);
}
void BandedWG :: startBowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
adsr->setRate(rate);
adsr->keyOn();
maxVelocity = 0.03 + (0.1 * amplitude);
}
void BandedWG :: stopBowing(MY_FLOAT rate)
{
adsr->setRate(rate);
adsr->keyOff();
}
void BandedWG :: pluck(MY_FLOAT amplitude)
{
for (int i=0; i<nModes; i++)
delay[i].tick( amplitude / nModes );
}
void BandedWG :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
this->setFrequency(frequency);
if ( doPluck )
this->pluck(amplitude);
else
this->startBowing(amplitude, amplitude * 0.001);
#if defined(_STK_DEBUG_)
cerr << "BandedWG: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void BandedWG :: noteOff(MY_FLOAT amplitude)
{
if ( !doPluck )
this->stopBowing((1.0 - amplitude) * 0.005);
#if defined(_STK_DEBUG_)
cerr << "BandedWG: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT BandedWG :: tick()
{
int k;
MY_FLOAT velocityInput = 0.0;
MY_FLOAT input = 0.0;
if ( doPluck )
input = 0.0;
else {
if (integrationConstant == 0.0)
velocityInput = 0.0;
else
velocityInput = integrationConstant * velocityInput;
for (k=0; k<nModes; k++)
velocityInput += baseGain * delay[k].lastOut();
if ( trackVelocity ) {
bowVelocity *= 0.9995;
bowVelocity += bowTarget;
bowTarget *= 0.995;
}
else
bowVelocity = adsr->tick() * maxVelocity;
input = bowVelocity - velocityInput;
input = input * bowTabl->tick(input);
input = input/(MY_FLOAT)nModes;
}
MY_FLOAT data = 0.0;
for (k=0; k<nModes; k++) {
bandpass[k].tick(input + gains[k] * delay[k].lastOut());
delay[k].tick(bandpass[k].lastOut());
data += bandpass[k].lastOut();
}
//lastOutput = data * nModes;
lastOutput = data * 4;
return lastOutput;
}
void BandedWG :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "BandedWG: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "BandedWG: Control value greater than 128.0!" << endl;
}
if (number == __SK_BowPressure_) { // 2
if ( norm == 0.0 )
doPluck = true;
else {
doPluck = false;
bowTabl->setSlope( 10.0 - (9.0 * norm));
}
}
else if (number == 4) { // 4
if ( !trackVelocity ) trackVelocity = true;
bowTarget += 0.005 * (norm - bowPosition);
bowPosition = norm;
//adsr->setTarget(bowPosition);
}
else if (number == 8) // 8
this->setStrikePosition( norm );
else if (number == __SK_AfterTouch_Cont_) { // 128
//bowTarget += 0.02 * (norm - bowPosition);
//bowPosition = norm;
if ( trackVelocity ) trackVelocity = false;
maxVelocity = 0.13 * norm;
adsr->setTarget(norm);
}
else if (number == __SK_ModWheel_) { // 1
baseGain = 0.9989999999 + (0.001 * norm );
for (int i=0; i<nModes; i++)
gains[i]=(MY_FLOAT) pow(baseGain, delay[i].getDelay()+i);
}
else if (number == __SK_ModFrequency_) // 11
integrationConstant = norm;
else if (number == __SK_Sustain_) { // 64
if (value < 65) doPluck = true;
else doPluck = false;
}
else if (number == __SK_Portamento_) { // 65
if (value < 65) trackVelocity = false;
else trackVelocity = true;
}
else if (number == __SK_ProphesyRibbon_) // 16
this->setPreset((int) value);
else
cerr << "BandedWG: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "BandedWG: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

@@ -1,81 +1,114 @@
/******************************************/
/* Hammond(OID) Organ Subclass */
/* of Algorithm 8 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/******************************************/
/***************************************************/
/*! \class BeeThree
\brief STK Hammond-oid organ FM synthesis instrument.
This class implements a simple 4 operator
topology, also referred to as algorithm 8 of
the TX81Z.
\code
Algorithm 8 is :
1 --.
2 -\|
+-> Out
3 -/|
4 --
\endcode
Control Change Numbers:
- Operator 4 (feedback) Gain = 2
- Operator 3 Gain = 4
- LFO Speed = 11
- LFO Depth = 1
- ADSR 2 & 4 Target = 128
The basic Chowning/Stanford FM patent expired
in 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.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "BeeThree.h"
#include <string.h>
BeeThree :: BeeThree() : FM4Alg8()
BeeThree :: BeeThree()
: FM()
{
// 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"));
int i;
char files[4][128];
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);
}
// Concatenate the STK RAWWAVE_PATH to the rawwave file
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );
this->setRatio(0, 0.999);
this->setRatio(1, 1.997);
this->setRatio(2, 3.006);
this->setRatio(3, 6.009);
gains[0] = __FM_gains[95];
gains[1] = __FM_gains[95];
gains[2] = __FM_gains[99];
gains[3] = __FM_gains[95];
adsr[0]->setAllTimes( 0.005, 0.003, 1.0, 0.01);
adsr[1]->setAllTimes( 0.005, 0.003, 1.0, 0.01);
adsr[2]->setAllTimes( 0.005, 0.003, 1.0, 0.01);
adsr[3]->setAllTimes( 0.005, 0.001, 0.4, 0.03);
twozero->setGain( 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]);
void BeeThree :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
gains[0] = amplitude * __FM_gains[95];
gains[1] = amplitude * __FM_gains[95];
gains[2] = amplitude * __FM_gains[99];
gains[3] = amplitude * __FM_gains[95];
this->setFrequency(frequency);
this->keyOn();
#if defined(_STK_DEBUG_)
cerr << "BeeThree: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
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;
}
register MY_FLOAT temp;
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
}
if (modDepth > 0.0) {
temp = 1.0 + (modDepth * vibrato->tick() * 0.1);
waves[0]->setFrequency(baseFrequency * temp * ratios[0]);
waves[1]->setFrequency(baseFrequency * temp * ratios[1]);
waves[2]->setFrequency(baseFrequency * temp * ratios[2]);
waves[3]->setFrequency(baseFrequency * temp * ratios[3]);
}
waves[3]->addPhaseOffset(twozero->lastOut());
temp = control1 * 2.0 * gains[3] * adsr[3]->tick() * waves[3]->tick();
twozero->tick(temp);
temp += control2 * 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 * 0.125;
return lastOutput;
}

View File

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

158
src/BlowBotl.cpp Normal file
View File

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

View File

@@ -1,26 +1,53 @@
/***********************************************/
/*
Waveguide "reed" instrument model with a
register hole and one tonehole
/***************************************************/
/*! \class BlowHole
\brief STK clarinet physical model with one
register hole and one tonehole.
by Gary P. Scavone, 2000.
This class is based on the clarinet model,
with the addition of a two-port register hole
and a three-port dynamic tonehole
implementation, as discussed by Scavone and
Cook (1998).
In this implementation, the distances between
the reed/register hole and tonehole/bell are
fixed. As a result, both the tonehole and
register hole will have variable influence on
the playing frequency, which is dependent on
the length of the air column. In addition,
the highest playing freqeuency is limited by
these fixed lengths.
This is a digital waveguide model, making its
use possibly subject to patents held by Stanford
University, Yamaha, and others.
Control Change Numbers:
- Reed Stiffness = 2
- Noise Gain = 4
- Tonehole State = 11
- Register State = 1
- Breath Pressure = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***********************************************/
/***************************************************/
#include "BlowHole.h"
#include "SKINI11.msg"
#include "SKINI.msg"
#include <math.h>
#include <string.h>
BlowHole :: BlowHole(MY_FLOAT lowestFreq)
BlowHole :: BlowHole(MY_FLOAT lowestFrequency)
{
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;
length = (long) (Stk::sampleRate() / lowestFrequency + 1);
// delays[0] is the delay line between the reed and the register vent.
delays[0] = (DelayL *) new DelayL( 5.0 * Stk::sampleRate() / 22050.0, 100 );
// delays[1] is the delay line between the register vent and the tonehole.
delays[1] = (DelayL *) new DelayL( length >> 1, length );
// delays[2] is the delay line between the tonehole and the end of the bore.
delays[2] = (DelayL *) new DelayL( 4.0 * Stk::sampleRate() / 22050.0, 100 );
reedTable = new ReedTabl();
reedTable->setOffset((MY_FLOAT) 0.7);
reedTable->setSlope((MY_FLOAT) -0.3);
filter = new OneZero;
@@ -28,13 +55,13 @@ BlowHole :: BlowHole(MY_FLOAT lowestFreq)
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 */
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);
MY_FLOAT te = 1.4 * r_th; // effective length of the open hole
th_coeff = (te*2*Stk::sampleRate() - 347.23) / (te*2*Stk::sampleRate() + 347.23);
tonehole = new PoleZero;
// Start with tonehole open
tonehole->setA1(-th_coeff);
@@ -42,13 +69,13 @@ BlowHole :: BlowHole(MY_FLOAT lowestFreq)
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 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);
rh_coeff = (zeta - 2 * Stk::sampleRate() * psi) / (zeta + 2 * Stk::sampleRate() * psi);
rh_gain = -347.23 / (zeta + 2 * Stk::sampleRate() * psi);
vent = new PoleZero;
vent->setA1(rh_coeff);
vent->setB0(1.0);
@@ -59,51 +86,59 @@ BlowHole :: BlowHole(MY_FLOAT lowestFreq)
// 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);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato->setFrequency((MY_FLOAT) 5.735);
outputGain = (MY_FLOAT) 1.0;
noiseGain = (MY_FLOAT) 0.2;
vibrGain = (MY_FLOAT) 0.01;
vibratoGain = (MY_FLOAT) 0.01;
}
BlowHole :: ~BlowHole()
{
delete [] delays;
delete delays[0];
delete delays[1];
delete delays[2];
delete reedTable;
delete filter;
delete tonehole;
delete vent;
delete envelope;
delete noise;
delete vibr;
delete vibrato;
}
void BlowHole :: clear()
{
delays[0].clear();
delays[1].clear();
delays[0]->clear();
delays[1]->clear();
delays[2]->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)
void BlowHole :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT new_length = (SRATE / frequency) * (MY_FLOAT) 0.5 - (MY_FLOAT) 1.5;
new_length -= 9;
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "BlowHole: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
if (new_length <= 1.0) new_length = 1.0;
else if (new_length >= length) new_length = length;
delays[1].setDelay(new_length);
// Delay = length - approximate filter delay.
MY_FLOAT delay = (Stk::sampleRate() / freakency) * 0.5 - 3.5;
delay -= delays[0]->getDelay() + delays[2]->getDelay();
if (delay <= 0.0) delay = 0.3;
else if (delay > length) delay = length;
delays[1]->setDelay(delay);
}
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).
*/
// This method allows setting of the register vent "open-ness" at
// any point between "Open" (newValue = 1) and "Closed"
// (newValue = 0).
MY_FLOAT gain;
@@ -115,11 +150,9 @@ void BlowHole :: setVent(MY_FLOAT newValue)
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).
*/
// 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;
@@ -129,7 +162,7 @@ void BlowHole :: setTonehole(MY_FLOAT newValue)
tonehole->setB0(new_coeff);
}
void BlowHole :: startBlowing(MY_FLOAT amplitude,MY_FLOAT rate)
void BlowHole :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
envelope->setRate(rate);
envelope->setTarget(amplitude);
@@ -141,16 +174,24 @@ void BlowHole :: stopBlowing(MY_FLOAT rate)
envelope->setTarget((MY_FLOAT) 0.0);
}
void BlowHole :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
void BlowHole :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
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;
setFrequency(frequency);
startBlowing((MY_FLOAT) 0.55 + (amplitude * 0.30), amplitude * 0.005);
outputGain = amplitude + 0.001;
#if defined(_STK_DEBUG_)
cerr << "BlowHole: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void BlowHole :: noteOff(MY_FLOAT amp)
void BlowHole :: noteOff(MY_FLOAT amplitude)
{
this->stopBlowing(amp * (MY_FLOAT) 0.01);
this->stopBlowing(amplitude * 0.01);
#if defined(_STK_DEBUG_)
cerr << "BlowHole: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT BlowHole :: tick()
@@ -159,30 +200,30 @@ MY_FLOAT BlowHole :: tick()
MY_FLOAT breathPressure;
MY_FLOAT temp;
// Calculate the breath pressure (envelope + noise + vibrator)
// Calculate the breath pressure (envelope + noise + vibrato)
breathPressure = envelope->tick();
breathPressure += breathPressure * noiseGain * noise->tick();
breathPressure += breathPressure * vibrGain * vibr->tick();
breathPressure += breathPressure * vibratoGain * vibrato->tick();
// Calculate the differential pressure = reflected - mouthpiece pressures
pressureDiff = delays[0].lastOut() - breathPressure;
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();
MY_FLOAT pa = breathPressure + pressureDiff * reedTable->tick(pressureDiff);
MY_FLOAT pb = delays[1]->lastOut();
vent->tick(pa+pb);
lastOutput = delays[0].tick(vent->lastOut()+pb);
lastOutput = delays[0]->tick(vent->lastOut()+pb);
lastOutput *= outputGain;
// Do three-port junction scattering (under tonehole)
pa += vent->lastOut();
pb = delays[2].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);
delays[2]->tick(filter->tick(pa + temp) * -0.95);
delays[1]->tick(pb + temp);
tonehole->tick(pa + pb - pth + temp);
return lastOutput;
@@ -190,20 +231,30 @@ MY_FLOAT BlowHole :: tick()
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);
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Clarinet: Control value less than zero!" << endl;
}
else {
printf("BlowHole : Undefined Control Number!!\n");
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Clarinet: Control value greater than 128.0!" << endl;
}
if (number == __SK_ReedStiffness_) // 2
reedTable->setSlope( -0.44 + (0.26 * norm) );
else if (number == __SK_NoiseLevel_) // 4
noiseGain = ( norm * 0.4);
else if (number == __SK_ModFrequency_) // 11
this->setTonehole( norm );
else if (number == __SK_ModWheel_) // 1
this->setVent( norm );
else if (number == __SK_AfterTouch_Cont_) // 128
envelope->setValue( norm );
else
cerr << "BlowHole: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "BlowHole: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

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

View File

@@ -1,55 +1,57 @@
/******************************************/
/* 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 */
/* */
/******************************************/
/***************************************************/
/*! \class Bowed
\brief STK bowed string instrument class.
This class implements a bowed string model, a
la Smith (1986), after McIntyre, Schumacher,
Woodhouse (1983).
This is a digital waveguide model, making its
use possibly subject to patents held by
Stanford University, Yamaha, and others.
Control Change Numbers:
- Bow Pressure = 2
- Bow Position = 4
- Vibrato Frequency = 11
- Vibrato Gain = 1
- Volume = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Bowed.h"
#include "SKINI11.msg"
#include "SKINI.msg"
#include <string.h>
Bowed :: Bowed(MY_FLOAT lowestFreq)
Bowed :: Bowed(MY_FLOAT lowestFrequency)
{
long length;
length = (long) (SRATE / lowestFreq + 1);
neckDelay = new DLineL(length);
length = (long) (Stk::sampleRate() / lowestFrequency + 1);
neckDelay = new DelayL(100.0, length);
length >>= 1;
bridgeDelay = new DLineL(length);
bowTabl = new BowTabl;
reflFilt = new OnePole;
bodyFilt = new BiQuad;
bridgeDelay = new DelayL(29.0, length);
bowTable = new BowTabl;
bowTable->setSlope((MY_FLOAT) 3.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");
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato->setFrequency((MY_FLOAT) 6.12723);
vibratoGain = (MY_FLOAT) 0.0;
stringFilter = new OnePole;
stringFilter->setPole((MY_FLOAT) (0.6 - (0.1 * 22050.0 / Stk::sampleRate() ) ) );
stringFilter->setGain((MY_FLOAT) 0.95);
bodyFilter = new BiQuad;
bodyFilter->setResonance( 500.0, 0.85, TRUE );
bodyFilter->setGain((MY_FLOAT) 0.2);
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;
@@ -59,10 +61,10 @@ Bowed :: ~Bowed()
{
delete neckDelay;
delete bridgeDelay;
delete bowTabl;
delete reflFilt;
delete bodyFilt;
delete vibr;
delete bowTable;
delete stringFilter;
delete bodyFilter;
delete vibrato;
delete adsr;
}
@@ -72,11 +74,19 @@ void Bowed :: clear()
bridgeDelay->clear();
}
void Bowed :: setFreq(MY_FLOAT frequency)
void Bowed :: setFrequency(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 */
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "Bowed: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
// Delay = length - approximate filter delay.
baseDelay = Stk::sampleRate() / freakency - (MY_FLOAT) 4.0;
if ( baseDelay <= 0.0 ) baseDelay = 0.3;
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)
@@ -92,74 +102,88 @@ void Bowed :: stopBowing(MY_FLOAT rate)
adsr->keyOff();
}
void Bowed :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
void Bowed :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
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
this->startBowing(amplitude, amplitude * 0.001);
this->setFrequency(frequency);
#if defined(_STK_DEBUG_)
cerr << "Bowed: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Bowed :: noteOff(MY_FLOAT amp)
void Bowed :: noteOff(MY_FLOAT amplitude)
{
this->stopBowing(((MY_FLOAT) 1.0 - amp) * (MY_FLOAT) 0.005);
#if defined(_debug_)
printf("Bowed : NoteOff: Amp=%lf\n",amp);
#endif
this->stopBowing(((MY_FLOAT) 1.0 - amplitude) * (MY_FLOAT) 0.005);
#if defined(_STK_DEBUG_)
cerr << "Bowed: NoteOff amplitude = " << amplitude << endl;
#endif
}
void Bowed :: setVibrato(MY_FLOAT amount)
void Bowed :: setVibrato(MY_FLOAT gain)
{
vibrGain = amount;
vibratoGain = gain;
}
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;
MY_FLOAT bridgeRefl;
MY_FLOAT nutRefl;
MY_FLOAT newVel;
MY_FLOAT velDiff;
MY_FLOAT stringVel;
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 */
bridgeRefl = -stringFilter->tick( bridgeDelay->lastOut() );
nutRefl = -neckDelay->lastOut();
stringVel = bridgeRefl + nutRefl; // Sum is String Velocity
velDiff = bowVelocity - stringVel; // Differential Velocity
newVel = velDiff * bowTable->tick( velDiff ); // Non-Linear Bow Function
neckDelay->tick(bridgeRefl + newVel); // Do string propagations
bridgeDelay->tick(nutRefl + newVel);
if (vibrGain > 0.0) {
if (vibratoGain > 0.0) {
neckDelay->setDelay((baseDelay * ((MY_FLOAT) 1.0 - betaRatio)) +
(baseDelay * vibrGain*vibr->tick()));
(baseDelay * vibratoGain * vibrato->tick()));
}
lastOutput = bodyFilt->tick(bridgeDelay->lastOut());
lastOutput = bodyFilter->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 */
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Bowed: Control value less than zero!" << endl;
}
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");
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Bowed: Control value greater than 128.0!" << endl;
}
if (number == __SK_BowPressure_) // 2
bowTable->setSlope( 5.0 - (4.0 * norm) );
else if (number == __SK_BowPosition_) { // 4
betaRatio = 0.027236 + (0.2 * norm);
bridgeDelay->setDelay(baseDelay * betaRatio);
neckDelay->setDelay(baseDelay * ((MY_FLOAT) 1.0 - betaRatio));
}
else if (number == __SK_ModFrequency_) // 11
vibrato->setFrequency( norm * 12.0 );
else if (number == __SK_ModWheel_) // 1
vibratoGain = ( norm * 0.4 );
else if (number == __SK_AfterTouch_Cont_) // 128
adsr->setTarget(norm);
else
cerr << "Bowed: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Bowed: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

@@ -1,314 +0,0 @@
/*********************************************/
/* 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");
}
}

View File

@@ -1,40 +1,53 @@
/******************************************/
/* 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 */
/******************************************/
/***************************************************/
/*! \class Brass
\brief STK simple brass instrument class.
This class implements a simple brass instrument
waveguide model, a la Cook (TBone, HosePlayer).
This is a digital waveguide model, making its
use possibly subject to patents held by
Stanford University, Yamaha, and others.
Control Change Numbers:
- Lip Tension = 2
- Slide Length = 4
- Vibrato Frequency = 11
- Vibrato Gain = 1
- Volume = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Brass.h"
#include "SKINI11.msg"
#include "SKINI.msg"
#include <string.h>
#include <math.h>
Brass :: Brass(MY_FLOAT lowestFreq)
Brass :: Brass(MY_FLOAT lowestFrequency)
{
length = (long) (SRATE / lowestFreq + 1);
delayLine = new DLineA(length);
lipFilter = new LipFilt;
dcBlock = new DCBlock;
length = (long) (Stk::sampleRate() / lowestFrequency + 1);
delayLine = new DelayA( 0.5 * length, length );
lipFilter = new BiQuad();
lipFilter->setGain( 0.03 );
dcBlock = new PoleZero();
dcBlock->setBlockZero();
adsr = new ADSR;
adsr->setAllTimes((MY_FLOAT) 0.005, (MY_FLOAT) 0.001, (MY_FLOAT) 1.0, (MY_FLOAT) 0.010);
adsr->setAllTimes( 0.005, 0.001, 1.0, 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");
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato->setFrequency( 6.137 );
vibratoGain = 0.0;
this->clear();
vibr->setFreq((MY_FLOAT) 6.137);
vibrGain = (MY_FLOAT) 0.05; /* breath periodic vibrato component */
maxPressure = (MY_FLOAT) 0.0;
lipTarget = 0.0;
}
Brass :: ~Brass()
@@ -43,7 +56,7 @@ Brass :: ~Brass()
delete lipFilter;
delete dcBlock;
delete adsr;
delete vibr;
delete vibrato;
}
void Brass :: clear()
@@ -53,21 +66,34 @@ void Brass :: clear()
dcBlock->clear();
}
void Brass :: setFreq(MY_FLOAT frequency)
void Brass :: setFrequency(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);
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "Brass: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
// Fudge correction for filter delays.
slideTarget = (Stk::sampleRate() / freakency * 2.0) + 3.0;
delayLine->setDelay(slideTarget); // play a harmonic
lipTarget = freakency;
lipFilter->setResonance( freakency, 0.997 );
}
void Brass :: setLip(MY_FLOAT frequency)
{
lipFilter->setFreq(frequency);
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "Brass: setLip parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
lipFilter->setResonance( freakency, 0.997 );
}
void Brass :: startBlowing(MY_FLOAT amplitude,MY_FLOAT rate)
void Brass :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
adsr->setAttackRate(rate);
maxPressure = amplitude;
@@ -80,55 +106,71 @@ void Brass :: stopBlowing(MY_FLOAT rate)
adsr->keyOff();
}
void Brass :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
void Brass :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
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
setFrequency(frequency);
this->startBlowing(amplitude, amplitude * 0.001);
#if defined(_STK_DEBUG_)
cerr << "Brass: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Brass :: noteOff(MY_FLOAT amp)
void Brass :: noteOff(MY_FLOAT amplitude)
{
this->stopBlowing(amp * (MY_FLOAT) 0.005);
#if defined(_debug_)
printf("Brass : NoteOff: Amp=%lf\n",amp);
#endif
this->stopBlowing(amplitude * 0.005);
#if defined(_STK_DEBUG_)
cerr << "Brass: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT Brass :: tick()
{
MY_FLOAT breathPressure;
MY_FLOAT breathPressure = maxPressure * adsr->tick();
breathPressure += vibratoGain * vibrato->tick();
MY_FLOAT mouthPressure = 0.3 * breathPressure;
MY_FLOAT borePressure = 0.85 * delayLine->lastOut();
MY_FLOAT deltaPressure = mouthPressure - borePressure; // Differential pressure.
deltaPressure = lipFilter->tick( deltaPressure ); // Force - > position.
deltaPressure *= deltaPressure; // Basic position to area mapping.
if ( deltaPressure > 1.0 ) deltaPressure = 1.0; // Non-linear saturation.
// The following input scattering assumes the mouthPressure = area.
lastOutput = deltaPressure * mouthPressure + ( 1.0 - deltaPressure) * borePressure;
lastOutput = delayLine->tick( dcBlock->tick( lastOutput ) );
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);
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Brass: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Brass: Control value greater than 128.0!" << endl;
}
if (number == __SK_LipTension_) { // 2
MY_FLOAT temp = lipTarget * pow( 4.0, (2.0 * norm) - 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");
}
else if (number == __SK_SlideLength_) // 4
delayLine->setDelay( slideTarget * (0.5 + norm) );
else if (number == __SK_ModFrequency_) // 11
vibrato->setFrequency( norm * 12.0 );
else if (number == __SK_ModWheel_ ) // 1
vibratoGain = norm * 0.4;
else if (number == __SK_AfterTouch_Cont_) // 128
adsr->setTarget( norm );
else
cerr << "Brass: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Brass: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

@@ -1,55 +0,0 @@
#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;
}

106
src/Chorus.cpp Normal file
View File

@@ -0,0 +1,106 @@
/***************************************************/
/*! \class Chorus
\brief STK chorus effect class.
This class implements a chorus effect.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Chorus.h"
#include <string.h>
#include <iostream.h>
Chorus :: Chorus(MY_FLOAT baseDelay)
{
delayLine[0] = new DelayL((long) baseDelay, (long) (baseDelay * 1.414) + 2);
delayLine[1] = new DelayL((long) (baseDelay), (long) baseDelay + 2);
baseLength = baseDelay;
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char path[128];
strcpy(path, RAWWAVE_PATH);
mods[0] = new WaveLoop( strcat(path,"rawwaves/sinewave.raw"), TRUE );
strcpy(path, RAWWAVE_PATH);
mods[1] = new WaveLoop( strcat(path,"rawwaves/sinewave.raw"), TRUE );
mods[0]->setFrequency(0.2);
mods[1]->setFrequency(0.222222);
modDepth = 0.05;
effectMix = 0.5;
this->clear();
}
Chorus :: ~Chorus()
{
delete delayLine[0];
delete delayLine[1];
delete mods[0];
delete mods[1];
}
void Chorus :: clear()
{
delayLine[0]->clear();
delayLine[1]->clear();
lastOutput[0] = 0.0;
lastOutput[1] = 0.0;
}
void Chorus :: setEffectMix(MY_FLOAT mix)
{
effectMix = mix;
if ( mix < 0.0 ) {
cerr << "Chorus: setEffectMix parameter is less than zero!" << endl;
effectMix = 0.0;
}
else if ( mix > 1.0 ) {
cerr << "Chorus: setEffectMix parameter is greater than 1.0!" << endl;
effectMix = 1.0;
}
}
void Chorus :: setModDepth(MY_FLOAT depth)
{
modDepth = depth;
}
void Chorus :: setModFrequency(MY_FLOAT frequency)
{
mods[0]->setFrequency(frequency);
mods[1]->setFrequency(frequency * 1.1111);
}
MY_FLOAT Chorus :: lastOut() const
{
return (lastOutput[0] + lastOutput[1]) * (MY_FLOAT) 0.5;
}
MY_FLOAT Chorus :: lastOutLeft() const
{
return lastOutput[0];
}
MY_FLOAT Chorus :: lastOutRight() const
{
return lastOutput[1];
}
MY_FLOAT Chorus :: tick(MY_FLOAT input)
{
delayLine[0]->setDelay(baseLength * 0.707 * (1.0 + mods[0]->tick()));
delayLine[1]->setDelay(baseLength * 0.5 * (1.0 - mods[1]->tick()));
lastOutput[0] = input * (1.0 - effectMix);
lastOutput[0] += effectMix * delayLine[0]->tick(input);
lastOutput[1] = input * (1.0 - effectMix);
lastOutput[1] += effectMix * delayLine[1]->tick(input);
return (lastOutput[0] + lastOutput[1]) * (MY_FLOAT) 0.5;
}
MY_FLOAT *Chorus :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

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

View File

@@ -1,490 +0,0 @@
/******************************************/
/*
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

View File

@@ -1,40 +0,0 @@
/*******************************************/
/* 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;
}

View File

@@ -1,107 +0,0 @@
/*******************************************/
/* 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
}

View File

@@ -1,172 +0,0 @@
/*******************************************/
/*
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;
}

View File

@@ -1,158 +0,0 @@
/*******************************************/
/*
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;
}

159
src/Delay.cpp Normal file
View File

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

117
src/DelayA.cpp Normal file
View File

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

107
src/DelayL.cpp Normal file
View File

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

View File

@@ -1,183 +0,0 @@
/*******************************************/
/* 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;
}

181
src/Drummer.cpp Normal file
View File

@@ -0,0 +1,181 @@
/***************************************************/
/*! \class Drummer
\brief STK drum sample player class.
This class implements a drum sampling
synthesizer using WvIn objects and one-pole
filters. The drum rawwave files are sampled
at 22050 Hz, but will be appropriately
interpolated for other sample rates. You can
specify the maximum polyphony (maximum number
of simultaneous voices) via a #define in the
Drummer.h.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Drummer.h"
#include <string.h>
#include <math.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"
};
Drummer :: Drummer() : Instrmnt()
{
for (int i=0; i<DRUM_POLYPHONY; i++) {
filters[i] = new OnePole;
sounding[i] = -1;
}
// This counts the number of sounding voices.
nSounding = 0;
}
Drummer :: ~Drummer()
{
int i;
for ( i=0; i<nSounding-1; i++ ) delete waves[i];
for ( i=0; i<DRUM_POLYPHONY; i++ ) delete filters[i];
}
void Drummer :: noteOn(MY_FLOAT instrument, MY_FLOAT amplitude)
{
#if defined(_STK_DEBUG_)
cerr << "Drummer: NoteOn instrument = " << instrument << ", amplitude = " << amplitude << endl;
#endif
MY_FLOAT gain = amplitude;
if ( amplitude > 1.0 ) {
cerr << "Drummer: noteOn amplitude parameter is greater than 1.0!" << endl;
gain = 1.0;
}
else if ( amplitude < 0.0 ) {
cerr << "Drummer: noteOn amplitude parameter is less than 0.0!" << endl;
return;
}
// Yes, this is tres kludgey.
int noteNum = (int) ((12*log(instrument/220.0)/log(2.0)) + 57.01);
// Check first to see if there's already one like this sounding.
int i, waveIndex = -1;
for (i=0; i<DRUM_POLYPHONY; i++) {
if (sounding[i] == noteNum) waveIndex = i;
}
if ( waveIndex >= 0 ) {
// Reset this sound.
waves[waveIndex]->reset();
filters[waveIndex]->setPole((MY_FLOAT) 0.999 - (gain * 0.6));
filters[waveIndex]->setGain(gain);
}
else {
if (nSounding == DRUM_POLYPHONY) {
// If we're already at maximum polyphony, then preempt the oldest voice.
delete waves[0];
filters[0]->clear();
WvIn *tempWv = waves[0];
OnePole *tempFilt = filters[0];
// Re-order the list.
for (i=0; i<DRUM_POLYPHONY-1; i++) {
waves[i] = waves[i+1];
filters[i] = filters[i+1];
}
waves[DRUM_POLYPHONY-1] = tempWv;
filters[DRUM_POLYPHONY-1] = tempFilt;
}
else
nSounding += 1;
sounding[nSounding-1] = noteNum;
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char path[128];
strcpy(path, RAWWAVE_PATH);
strcat(path, "rawwaves/");
strcat(path, waveNames[genMIDIMap[noteNum]]);
waves[nSounding-1] = new WvIn(path, TRUE);
if (Stk::sampleRate() != 22050.0)
waves[nSounding-1]->setRate( 22050.0 / Stk::sampleRate() );
filters[nSounding-1]->setPole((MY_FLOAT) 0.999 - (gain * 0.6) );
filters[nSounding-1]->setGain( gain );
}
#if defined(_STK_DEBUG_)
cerr << "Number Sounding = " << nSounding << endl;
for (i=0; i<nSounding; i++) cerr << sounding[i] << " ";
cerr << "\n";
#endif
}
void Drummer :: noteOff(MY_FLOAT amplitude)
{
// Set all sounding wave filter gains low.
int i = 0;
while(i < nSounding) {
filters[i++]->setGain( amplitude * 0.01 );
}
}
MY_FLOAT Drummer :: tick()
{
MY_FLOAT output = 0.0;
OnePole *tempFilt;
int j, i = 0;
while (i < nSounding) {
if ( waves[i]->isFinished() ) {
delete waves[i];
tempFilt = filters[i];
// Re-order the list.
for (j=i; j<nSounding-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;
nSounding -= 1;
i -= 1;
}
else
output += filters[i]->tick( waves[i]->tick() );
i++;
}
return output;
}

79
src/Echo.cpp Normal file
View File

@@ -0,0 +1,79 @@
/***************************************************/
/*! \class Echo
\brief STK echo effect class.
This class implements a echo effect.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Echo.h"
#include <iostream.h>
Echo :: Echo(MY_FLOAT longestDelay)
{
length = (long) longestDelay + 2;
delayLine = new Delay(length>>1, length);
effectMix = 0.5;
this->clear();
}
Echo :: ~Echo()
{
delete delayLine;
}
void Echo :: clear()
{
delayLine->clear();
lastOutput = 0.0;
}
void Echo :: setDelay(MY_FLOAT delay)
{
MY_FLOAT size = delay;
if ( delay < 0.0 ) {
cerr << "Echo: setDelay parameter is less than zero!" << endl;
size = 0.0;
}
else if ( delay > length ) {
cerr << "Echo: setDelay parameter is greater than delay length!" << endl;
size = length;
}
delayLine->setDelay((long)size);
}
void Echo :: setEffectMix(MY_FLOAT mix)
{
effectMix = mix;
if ( mix < 0.0 ) {
cerr << "Echo: setEffectMix parameter is less than zero!" << endl;
effectMix = 0.0;
}
else if ( mix > 1.0 ) {
cerr << "Echo: setEffectMix parameter is greater than 1.0!" << endl;
effectMix = 1.0;
}
}
MY_FLOAT Echo :: lastOut() const
{
return lastOutput;
}
MY_FLOAT Echo :: tick(MY_FLOAT input)
{
lastOutput = effectMix * delayLine->tick(input);
lastOutput += input * (1.0 - effectMix);
return lastOutput;
}
MY_FLOAT *Echo :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

View File

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

View File

@@ -1,67 +0,0 @@
/********************************************/
/*
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;
}

221
src/FM.cpp Normal file
View File

@@ -0,0 +1,221 @@
/***************************************************/
/*! \class FM
\brief STK abstract FM synthesis base class.
This class controls an arbitrary number of
waves and envelopes, determined via a
constructor argument.
Control Change Numbers:
- Control One = 2
- Control Two = 4
- LFO Speed = 11
- LFO Depth = 1
- ADSR 2 & 4 Target = 128
The basic Chowning/Stanford FM patent expired
in 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.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "FM.h"
#include "SKINI.msg"
#include <string.h>
#include <stdlib.h>
FM :: FM(int operators)
: nOperators(operators)
{
if ( nOperators <= 0 ) {
char msg[256];
sprintf(msg, "FM: Invalid number of operators (%d) argument to constructor!", operators);
handleError(msg, StkError::FUNCTION_ARGUMENT);
}
twozero = new TwoZero();
twozero->setB2( -1.0 );
twozero->setGain( 0.0 );
// Concatenate the STK RAWWAVE_PATH to the rawwave file.
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato->setFrequency(6.0);
int i;
ratios = (MY_FLOAT *) new MY_FLOAT[nOperators];
gains = (MY_FLOAT *) new MY_FLOAT[nOperators];
adsr = (ADSR **) calloc( nOperators, sizeof(ADSR *) );
waves = (WaveLoop **) calloc( nOperators, sizeof(WaveLoop *) );
for (i=0; i<nOperators; i++ ) {
ratios[i] = 1.0;
gains[i] = 1.0;
adsr[i] = new ADSR();
}
modDepth = (MY_FLOAT) 0.0;
control1 = (MY_FLOAT) 1.0;
control2 = (MY_FLOAT) 1.0;
baseFrequency = (MY_FLOAT) 440.0;
MY_FLOAT temp = 1.0;
for (i=99; i>=0; i--) {
__FM_gains[i] = temp;
temp *= 0.933033;
}
temp = 1.0;
for (i=15; i>=0; i--) {
__FM_susLevels[i] = temp;
temp *= 0.707101;
}
temp = 8.498186;
for (i=0; i<32; i++) {
__FM_attTimes[i] = temp;
temp *= 0.707101;
}
}
FM :: ~FM()
{
delete vibrato;
delete twozero;
delete [] ratios;
delete [] gains;
for (int i=0; i<nOperators; i++ ) {
delete adsr[i];
delete waves[i];
}
free(adsr);
free(waves);
}
void FM :: loadWaves(const char **filenames )
{
for (int i=0; i<nOperators; i++ )
waves[i] = new WaveLoop( filenames[i], TRUE );
}
void FM :: setFrequency(MY_FLOAT frequency)
{
baseFrequency = frequency;
for (int i=0; i<nOperators; i++ )
waves[i]->setFrequency( baseFrequency * ratios[i] );
}
void FM :: setRatio(int waveIndex, MY_FLOAT ratio)
{
if ( waveIndex < 0 ) {
cerr << "FM: setRatio waveIndex parameter is less than zero!" << endl;
return;
}
else if ( waveIndex >= nOperators ) {
cerr << "FM: setRatio waveIndex parameter is greater than the number of operators!" << endl;
return;
}
ratios[waveIndex] = ratio;
if (ratio > 0.0)
waves[waveIndex]->setFrequency(baseFrequency * ratio);
else
waves[waveIndex]->setFrequency(ratio);
}
void FM :: setGain(int waveIndex, MY_FLOAT gain)
{
if ( waveIndex < 0 ) {
cerr << "FM: setGain waveIndex parameter is less than zero!" << endl;
return;
}
else if ( waveIndex >= nOperators ) {
cerr << "FM: setGain waveIndex parameter is greater than the number of operators!" << endl;
return;
}
gains[waveIndex] = gain;
}
void FM :: setModulationSpeed(MY_FLOAT mSpeed)
{
vibrato->setFrequency(mSpeed);
}
void FM :: setModulationDepth(MY_FLOAT mDepth)
{
modDepth = mDepth;
}
void FM :: setControl1(MY_FLOAT cVal)
{
control1 = cVal * (MY_FLOAT) 2.0;
}
void FM :: setControl2(MY_FLOAT cVal)
{
control2 = cVal * (MY_FLOAT) 2.0;
}
void FM :: keyOn()
{
for (int i=0; i<nOperators; i++ )
adsr[i]->keyOn();
}
void FM :: keyOff()
{
for (int i=0; i<nOperators; i++ )
adsr[i]->keyOff();
}
void FM :: noteOff(MY_FLOAT amplitude)
{
keyOff();
#if defined(_STK_DEBUG_)
cerr << "FM: NoteOff amplitude = " << amplitude << endl;
#endif
}
void FM :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "FM: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "FM: Control value greater than 128.0!" << endl;
}
if (number == __SK_Breath_) // 2
setControl1( norm );
else if (number == __SK_FootControl_) // 4
setControl2( norm );
else if (number == __SK_ModFrequency_) // 11
setModulationSpeed( norm * 12.0);
else if (number == __SK_ModWheel_) // 1
setModulationDepth( norm );
else if (number == __SK_AfterTouch_Cont_) { // 128
//adsr[0]->setTarget( norm );
adsr[1]->setTarget( norm );
//adsr[2]->setTarget( norm );
adsr[3]->setTarget( norm );
}
else
cerr << "FM: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "FM: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

@@ -1,56 +0,0 @@
/******************************************/
/* 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;
}

View File

@@ -1,53 +0,0 @@
/******************************************/
/* 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;
}

View File

@@ -1,53 +0,0 @@
/******************************************/
/* 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;
}

View File

@@ -1,58 +0,0 @@
/******************************************/
/* 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)
{
}

View File

@@ -1,47 +0,0 @@
/******************************************/
/* 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;
}

View File

@@ -1,179 +0,0 @@
/*******************************************/
/* 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");
}
}

View File

@@ -1,63 +1,93 @@
/******************************************/
/* Singing Voice Synthesis Subclass */
/* of Algorithm 6 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1996 */
/******************************************/
/***************************************************/
/*! \class FMVoices
\brief STK singing FM synthesis instrument.
This class implements 3 carriers and a common
modulator, also referred to as algorithm 6 of
the TX81Z.
\code
Algorithm 6 is :
/->1 -\
4-|-->2 - +-> Out
\->3 -/
\endcode
Control Change Numbers:
- Vowel = 2
- Spectral Tilt = 4
- LFO Speed = 11
- LFO Depth = 1
- ADSR 2 & 4 Target = 128
The basic Chowning/Stanford FM patent expired
in 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.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "FMVoices.h"
#include "SKINI11.msg"
#include "SKINI.msg"
#include "phontabl.tbl"
#include <string.h>
FMVoices :: FMVoices() : FM4Alg6()
FMVoices :: FMVoices()
: FM()
{
int i;
char files[4][128];
// 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);
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );
this->setRatio(0, 2.00);
this->setRatio(1, 4.00);
this->setRatio(2, 12.0);
this->setRatio(3, 1.00);
gains[3] = __FM_gains[80];
adsr[0]->setAllTimes( 0.05, 0.05, __FM_susLevels[15], 0.05);
adsr[1]->setAllTimes( 0.05, 0.05, __FM_susLevels[15], 0.05);
adsr[2]->setAllTimes( 0.05, 0.05, __FM_susLevels[15], 0.05);
adsr[3]->setAllTimes( 0.01, 0.01, __FM_susLevels[15], 0.5);
twozero->setGain( 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);
tilt[0] = 1.0;
tilt[1] = 0.5;
tilt[2] = 0.2;
mods[0] = 1.0;
mods[1] = 1.1;
mods[2] = 1.1;
baseFrequency = 110.0;
setFrequency( 110.0 );
}
/* #include "phonTabl.h" */
FMVoices :: ~FMVoices()
{
}
extern double phonGains[32][2];
extern double phonParams[32][4][3];
extern char phonemes[32][4];
void FMVoices :: setFreq(MY_FLOAT frequency)
{
void FMVoices :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT temp, temp2 = 0.0;
int tempi, tempi2 = 0;
@@ -77,60 +107,91 @@ void FMVoices :: setFreq(MY_FLOAT frequency)
tempi2 = currentVowel - 96;
temp2 = (MY_FLOAT) 1.2;
}
baseFreq = frequency;
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][0][0] / baseFreq) + (MY_FLOAT) 0.5;
baseFrequency = frequency;
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][0][0] / baseFrequency) + 0.5;
tempi = (int) temp;
this->setRatio(0,(MY_FLOAT) tempi);
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][1][0] / baseFreq) + (MY_FLOAT) 0.5;
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][1][0] / baseFrequency) + 0.5;
tempi = (int) temp;
this->setRatio(1,(MY_FLOAT) tempi);
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][2][0] / baseFreq) + (MY_FLOAT) 0.5;
temp = (temp2 * (MY_FLOAT) phonParams[tempi2][2][0] / baseFrequency) + 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);
this->setRatio(2, (MY_FLOAT) tempi);
gains[0] = 1.0;
gains[1] = 1.0;
gains[2] = 1.0;
}
void FMVoices :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
void FMVoices :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
this->setFreq(freq);
tilt[0] = amp;
tilt[1] = amp * amp;
tilt[2] = amp * amp * amp;
this->setFrequency(frequency);
tilt[0] = amplitude;
tilt[1] = amplitude * amplitude;
tilt[2] = tilt[1] * amplitude;
this->keyOn();
#if defined(_debug_)
printf("FMVoices : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
#if defined(_STK_DEBUG_)
cerr << "FMVoices: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT FMVoices :: tick()
{
register MY_FLOAT temp, temp2;
temp = gains[3] * adsr[3]->tick() * waves[3]->tick();
temp2 = vibrato->tick() * modDepth * (MY_FLOAT) 0.1;
waves[0]->setFrequency(baseFrequency * (1.0 + temp2) * ratios[0]);
waves[1]->setFrequency(baseFrequency * (1.0 + temp2) * ratios[1]);
waves[2]->setFrequency(baseFrequency * (1.0 + temp2) * ratios[2]);
waves[3]->setFrequency(baseFrequency * (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 * 0.33;
}
void FMVoices :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT temp;
int tempi;
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "FMVoices: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "FMVoices: Control value greater than 128.0!" << endl;
}
#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);
if (number == __SK_Breath_) // 2
gains[3] = __FM_gains[(int) ( norm * 100.0 )];
else if (number == __SK_FootControl_) { // 4
currentVowel = (int) (norm * 128.0);
this->setFrequency(baseFrequency);
}
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 if (number == __SK_ModFrequency_) // 11
this->setModulationSpeed( norm * 12.0);
else if (number == __SK_ModWheel_) // 1
this->setModulationDepth( norm );
else if (number == __SK_AfterTouch_Cont_) { // 128
tilt[0] = norm;
tilt[1] = norm * norm;
tilt[2] = tilt[1] * norm;
}
else {
printf("FM4Op : Undefined Control Number!!\n");
}
else
cerr << "FMVoices: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "FMVoices: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

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

View File

@@ -1,52 +1,59 @@
/******************************************/
/* 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 */
/******************************************/
/***************************************************/
/*! \class Fluate
\brief STK flute physical model class.
This class implements a simple flute
physical model, as discussed by Karjalainen,
Smith, Waryznyk, etc. The jet model uses
a polynomial, a la Cook.
This is a digital waveguide model, making its
use possibly subject to patents held by Stanford
University, Yamaha, and others.
Control Change Numbers:
- Jet Delay = 2
- Noise Gain = 4
- Vibrato Frequency = 11
- Vibrato Gain = 1
- Breath Pressure = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Flute.h"
#include "SKINI11.msg"
#include "SKINI.msg"
#include <string.h>
Flute :: Flute(MY_FLOAT lowestFreq)
Flute :: Flute(MY_FLOAT lowestFrequency)
{
long length;
length = (long) (SRATE / lowestFreq + 1);
boreDelay = new DLineL(length);
length = (long) (Stk::sampleRate() / lowestFrequency + 1);
boreDelay = new DelayL( 100.0, length );
length >>= 1;
jetDelay = new DLineL(length);
jetTable = new JetTabl;
filter = new OnePole;
dcBlock = new DCBlock;
noise = new Noise;
adsr = new ADSR;
jetDelay = new DelayL( 49.0, length );
jetTable = new JetTabl();
filter = new OnePole();
dcBlock = new PoleZero();
dcBlock->setBlockZero();
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");
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato->setFrequency( 5.925 );
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 */
filter->setPole( 0.7 - ((MY_FLOAT) 0.1 * 22050.0 / Stk::sampleRate() ) );
filter->setGain( -1.0 );
adsr->setAllTimes( 0.005, 0.01, 0.8, 0.010);
endReflection = (MY_FLOAT) 0.5;
jetReflection = (MY_FLOAT) 0.5;
noiseGain = 0.15; // Breath pressure random component.
vibratoGain = (MY_FLOAT) 0.05; // Breath periodic vibrato component.
jetRatio = (MY_FLOAT) 0.32;
maxPressure = (MY_FLOAT) 0.0;
@@ -61,7 +68,7 @@ Flute :: ~Flute()
delete dcBlock;
delete noise;
delete adsr;
delete vibr;
delete vibrato;
}
void Flute :: clear()
@@ -72,13 +79,23 @@ void Flute :: clear()
dcBlock->clear();
}
void Flute :: setFreq(MY_FLOAT frequency)
void Flute :: setFrequency(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 */
lastFrequency = frequency;
if ( frequency <= 0.0 ) {
cerr << "Flute: setFrequency parameter is less than or equal to zero!" << endl;
lastFrequency = 220.0;
}
// We're overblowing here.
lastFrequency *= 0.66666;
// Delay = length - approximate filter delay.
MY_FLOAT delay = Stk::sampleRate() / lastFrequency - (MY_FLOAT) 2.0;
if (delay <= 0.0) delay = 0.3;
else if (delay > length) delay = length;
boreDelay->setDelay(delay);
jetDelay->setDelay(delay * jetRatio);
}
void Flute :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate)
@@ -94,84 +111,92 @@ void Flute :: stopBlowing(MY_FLOAT rate)
adsr->keyOff();
}
void Flute :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
void Flute :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
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
setFrequency(frequency);
startBlowing( 1.1 + (amplitude * 0.20), amplitude * 0.02);
outputGain = amplitude + 0.001;
#if defined(_STK_DEBUG_)
cerr << "Flute: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Flute :: noteOff(MY_FLOAT amp)
void Flute :: noteOff(MY_FLOAT amplitude)
{
this->stopBlowing(amp * (MY_FLOAT) 0.02);
#if defined(_debug_)
printf("Flute : NoteOff: Amp=%lf\n",amp);
#endif
this->stopBlowing(amplitude * 0.02);
#if defined(_STK_DEBUG_)
cerr << "Flute: NoteOff amplitude = " << amplitude << endl;
#endif
}
void Flute :: setJetRefl(MY_FLOAT refl)
void Flute :: setJetReflection(MY_FLOAT coefficient)
{
jetRefl = refl;
jetReflection = coefficient;
}
void Flute :: setEndRefl(MY_FLOAT refl)
void Flute :: setEndReflection(MY_FLOAT coefficient)
{
endRefl = refl;
endReflection = coefficient;
}
void Flute :: setJetDelay(MY_FLOAT aRatio)
{
MY_FLOAT temp;
temp = SRATE / lastFreq - (MY_FLOAT) 2.0; /* Length - approx. filter delay */
// Delay = length - approximate filter delay.
MY_FLOAT temp = Stk::sampleRate() / lastFrequency - (MY_FLOAT) 2.0;
jetRatio = aRatio;
jetDelay->setDelay(temp * aRatio); /* Scaled by ratio */
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 */
// Calculate the breath pressure (envelope + noise + vibrato)
breathPressure = maxPressure * adsr->tick();
breathPressure += breathPressure * noiseGain * noise->tick();
breathPressure += breathPressure * vibratoGain * vibrato->tick();
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 */
MY_FLOAT temp = filter->tick( boreDelay->lastOut() );
temp = dcBlock->tick(temp); // Block DC on reflection.
pressureDiff = breathPressure - (jetReflection * temp);
pressureDiff = jetDelay->tick( pressureDiff );
pressureDiff = jetTable->tick( pressureDiff ) + (endReflection * temp);
lastOutput = (MY_FLOAT) 0.3 * boreDelay->tick( pressureDiff );
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");
}
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Flute: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Flute: Control value greater than 128.0!" << endl;
}
if (number == __SK_JetDelay_) // 2
this->setJetDelay( 0.08 + (0.48 * norm) );
else if (number == __SK_NoiseLevel_) // 4
noiseGain = ( norm * 0.4);
else if (number == __SK_ModFrequency_) // 11
vibrato->setFrequency( norm * 12.0);
else if (number == __SK_ModWheel_) // 1
vibratoGain = ( norm * 0.4 );
else if (number == __SK_AfterTouch_Cont_) // 128
adsr->setTarget( norm );
else
cerr << "Flute: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Flute: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

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

View File

@@ -1,68 +0,0 @@
/******************************************/
/* 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
}

113
src/HevyMetl.cpp Normal file
View File

@@ -0,0 +1,113 @@
/***************************************************/
/*! \class HevyMetl
\brief STK heavy metal FM synthesis instrument.
This class implements 3 cascade operators with
feedback modulation, also referred to as
algorithm 3 of the TX81Z.
Algorithm 3 is : 4--\
3-->2-- + -->1-->Out
Control Change Numbers:
- Total Modulator Index = 2
- Modulator Crossfade = 4
- LFO Speed = 11
- LFO Depth = 1
- ADSR 2 & 4 Target = 128
The basic Chowning/Stanford FM patent expired
in 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.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "HevyMetl.h"
#include <string.h>
HevyMetl :: HevyMetl()
: FM()
{
int i;
char files[4][128];
// Concatenate the STK RAWWAVE_PATH to the rawwave file
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );
this->setRatio(0, 1.0 * 1.000);
this->setRatio(1, 4.0 * 0.999);
this->setRatio(2, 3.0 * 1.001);
this->setRatio(3, 0.5 * 1.002);
gains[0] = __FM_gains[92];
gains[1] = __FM_gains[76];
gains[2] = __FM_gains[91];
gains[3] = __FM_gains[68];
adsr[0]->setAllTimes( 0.001, 0.001, 1.0, 0.01);
adsr[1]->setAllTimes( 0.001, 0.010, 1.0, 0.50);
adsr[2]->setAllTimes( 0.010, 0.005, 1.0, 0.20);
adsr[3]->setAllTimes( 0.030, 0.010, 0.2, 0.20);
twozero->setGain( 2.0 );
vibrato->setFrequency( 5.5 );
modDepth = 0.0;
}
HevyMetl :: ~HevyMetl()
{
}
void HevyMetl :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
gains[0] = amplitude * __FM_gains[92];
gains[1] = amplitude * __FM_gains[76];
gains[2] = amplitude * __FM_gains[91];
gains[3] = amplitude * __FM_gains[68];
this->setFrequency(frequency);
this->keyOn();
#if defined(_STK_DEBUG_)
cerr << "HevyMetl: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT HevyMetl :: tick()
{
register MY_FLOAT temp;
temp = vibrato->tick() * modDepth * 0.2;
waves[0]->setFrequency(baseFrequency * (1.0 + temp) * ratios[0]);
waves[1]->setFrequency(baseFrequency * (1.0 + temp) * ratios[1]);
waves[2]->setFrequency(baseFrequency * (1.0 + temp) * ratios[2]);
waves[3]->setFrequency(baseFrequency * (1.0 + temp) * ratios[3]);
temp = gains[2] * adsr[2]->tick() * waves[2]->tick();
waves[1]->addPhaseOffset(temp);
waves[3]->addPhaseOffset(twozero->lastOut());
temp = (1.0 - (control2 * 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 * 0.5;
return lastOutput;
}

View File

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

View File

@@ -1,166 +1,121 @@
/*******************************************/
/* 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. */
/*******************************************/
/***************************************************/
/*! \class JCRev
\brief John Chowning's reverberator class.
This class is derived from the CLM JCRev
function, which is based on the use of
networks of simple allpass and comb delay
filters. This class implements three series
allpass units, followed by four parallel comb
filters, and two decorrelation delay lines in
parallel at the output.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "JCRev.h"
//#define LOWPASS
#include <math.h>
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;
// Delay lengths for 44100 Hz sample rate.
int lengths[9] = {1777, 1847, 1993, 2137, 389, 127, 43, 211, 179};
double scaler = Stk::sampleRate() / 44100.0;
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;
int delay, i;
if ( scaler != 1.0 ) {
for (i=0; i<9; i++) {
delay = (int) floor(scaler * lengths[i]);
if ( (delay & 1) == 0) delay++;
while ( !this->isPrime(delay) ) delay += 2;
lengths[i] = delay;
}
}
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;
allpassDelays[i] = new Delay(lengths[i+4], lengths[i+4]);
for (i=0; i<4; i++) {
combDelays[i] = new Delay(lengths[i], lengths[i]);
combCoefficient[i] = pow(10,(-3 * lengths[i] / (T60 * Stk::sampleRate())));
}
outLeftDelay = new Delay(lengths[7], lengths[7]);
outRightDelay = new Delay(lengths[8], lengths[8]);
allpassCoefficient = 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;
delete allpassDelays[0];
delete allpassDelays[1];
delete allpassDelays[2];
delete combDelays[0];
delete combDelays[1];
delete combDelays[2];
delete combDelays[3];
delete outLeftDelay;
delete outRightDelay;
}
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;
allpassDelays[0]->clear();
allpassDelays[1]->clear();
allpassDelays[2]->clear();
combDelays[0]->clear();
combDelays[1]->clear();
combDelays[2]->clear();
combDelays[3]->clear();
outRightDelay->clear();
outLeftDelay->clear();
lastOutput[0] = 0.0;
lastOutput[1] = 0.0;
}
MY_FLOAT JCRev :: tick(MY_FLOAT input)
{
MY_FLOAT temp,temp0,temp1,temp2,temp3,temp4,temp5,temp6;
MY_FLOAT temp, temp0, temp1, temp2, temp3, temp4, temp5, temp6;
MY_FLOAT filtout;
temp = APdelayLine[0]->lastOut();
temp0 = allPassCoeff * temp;
temp = allpassDelays[0]->lastOut();
temp0 = allpassCoefficient * temp;
temp0 += input;
APdelayLine[0]->tick(temp0);
temp0 = -(allPassCoeff * temp0) + temp;
allpassDelays[0]->tick(temp0);
temp0 = -(allpassCoefficient * temp0) + temp;
temp = APdelayLine[1]->lastOut();
temp1 = allPassCoeff * temp;
temp = allpassDelays[1]->lastOut();
temp1 = allpassCoefficient * temp;
temp1 += temp0;
APdelayLine[1]->tick(temp1);
temp1 = -(allPassCoeff * temp1) + temp;
allpassDelays[1]->tick(temp1);
temp1 = -(allpassCoefficient * temp1) + temp;
temp = APdelayLine[2]->lastOut();
temp2 = allPassCoeff * temp;
temp = allpassDelays[2]->lastOut();
temp2 = allpassCoefficient * temp;
temp2 += temp1;
APdelayLine[2]->tick(temp2);
temp2 = -(allPassCoeff * temp2) + temp;
allpassDelays[2]->tick(temp2);
temp2 = -(allpassCoefficient * 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());
temp3 = temp2 + (combCoefficient[0] * combDelays[0]->lastOut());
temp4 = temp2 + (combCoefficient[1] * combDelays[1]->lastOut());
temp5 = temp2 + (combCoefficient[2] * combDelays[2]->lastOut());
temp6 = temp2 + (combCoefficient[3] * combDelays[3]->lastOut());
CdelayLine[0]->tick(temp3);
CdelayLine[1]->tick(temp4);
CdelayLine[2]->tick(temp5);
CdelayLine[3]->tick(temp6);
combDelays[0]->tick(temp3);
combDelays[1]->tick(temp4);
combDelays[2]->tick(temp5);
combDelays[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
filtout = temp3 + temp4 + temp5 + temp6;
lastOutL = effectMix * (outLdelayLine->tick(filtout));
lastOutR = effectMix * (outRdelayLine->tick(filtout));
lastOutput[0] = effectMix * (outLeftDelay->tick(filtout));
lastOutput[1] = effectMix * (outRightDelay->tick(filtout));
temp = (1.0 - effectMix) * input;
lastOutL += temp;
lastOutR += temp;
lastOutput[0] += temp;
lastOutput[1] += temp;
return (lastOutL + lastOutR) * 0.5;
return (lastOutput[0] + lastOutput[1]) * 0.5;
}

View File

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

View File

@@ -1,65 +0,0 @@
/************************************************/
/* 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;
}

View File

@@ -1,410 +0,0 @@
/*******************************************/
/* 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

View File

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

View File

@@ -1,152 +1,191 @@
/********************************************/
/* 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. */
/********************************************/
/***************************************************/
/*! \class Mandolin
\brief STK mandolin instrument model class.
This class inherits from PluckTwo and uses
"commuted synthesis" techniques to model a
mandolin instrument.
This is a digital waveguide model, making its
use possibly subject to patents held by
Stanford University, Yamaha, and others.
Commuted Synthesis, in particular, is covered
by patents, granted, pending, and/or
applied-for. All are assigned to the Board of
Trustees, Stanford University. For
information, contact the Office of Technology
Licensing, Stanford University.
Control Change Numbers:
- Body Size = 2
- Pluck Position = 4
- String Sustain = 11
- String Detuning = 1
- Microphone Position = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Mandolin.h"
#include "SKINI11.msg"
#include "SKINI.msg"
Mandolin :: Mandolin(MY_FLOAT lowestFreq) : Plucked2(lowestFreq)
#include <string.h>
Mandolin :: Mandolin(MY_FLOAT lowestFrequency)
: PluckTwo(lowestFrequency)
{
// 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");
soundfile[0] = new WvIn( strcat(temp,"rawwaves/mand1.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[1] = new RawWvIn(strcat(temp,"rawwaves/mand2.raw"),"oneshot");
soundfile[1] = new WvIn( strcat(temp,"rawwaves/mand2.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[2] = new RawWvIn(strcat(temp,"rawwaves/mand3.raw"),"oneshot");
soundfile[2] = new WvIn( strcat(temp,"rawwaves/mand3.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[3] = new RawWvIn(strcat(temp,"rawwaves/mand4.raw"),"oneshot");
soundfile[3] = new WvIn( strcat(temp,"rawwaves/mand4.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[4] = new RawWvIn(strcat(temp,"rawwaves/mand5.raw"),"oneshot");
soundfile[4] = new WvIn( strcat(temp,"rawwaves/mand5.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[5] = new RawWvIn(strcat(temp,"rawwaves/mand6.raw"),"oneshot");
soundfile[5] = new WvIn( strcat(temp,"rawwaves/mand6.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[6] = new RawWvIn(strcat(temp,"rawwaves/mand7.raw"),"oneshot");
soundfile[6] = new WvIn( strcat(temp,"rawwaves/mand7.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[7] = new RawWvIn(strcat(temp,"rawwaves/mand8.raw"),"oneshot");
soundfile[7] = new WvIn( strcat(temp,"rawwaves/mand8.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[8] = new RawWvIn(strcat(temp,"rawwaves/mand9.raw"),"oneshot");
soundfile[8] = new WvIn( strcat(temp,"rawwaves/mand9.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[9] = new RawWvIn(strcat(temp,"rawwaves/mand10.raw"),"oneshot");
soundfile[9] = new WvIn( strcat(temp,"rawwaves/mand10.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[10] = new RawWvIn(strcat(temp,"rawwaves/mand11.raw"),"oneshot");
soundfile[10] = new WvIn( strcat(temp,"rawwaves/mand11.raw"), TRUE );
strcpy(temp, RAWWAVE_PATH);
soundfile[11] = new RawWvIn(strcat(temp,"rawwaves/mand12.raw"),"oneshot");
soundfile[11] = new WvIn( strcat(temp,"rawwaves/mand12.raw"), TRUE );
directBody = 1.0;
mic = 0;
dampTime = 0;
waveDone = 1;
waveDone = soundfile[mic]->isFinished();
}
Mandolin :: ~Mandolin()
{
for ( int i=0; i<12; i++ )
delete soundfile[i];
}
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. */
{
// This function gets interesting, 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;
waveDone = false;
pluckAmplitude = amplitude;
if ( amplitude < 0.0 ) {
cerr << "Mandolin: pluck amplitude parameter less than zero!" << endl;
pluckAmplitude = 0.0;
}
else if ( amplitude > 1.0 ) {
cerr << "Mandolin: pluck amplitude parameter greater than 1.0!" << endl;
pluckAmplitude = 1.0;
}
// Set the pick position, which puts zeroes at position * length.
combDelay->setDelay((MY_FLOAT) 0.5 * pluckPosition * lastLength);
dampTime = (long) lastLength; // See tick method below.
}
void Mandolin :: pluck(MY_FLOAT amplitude, MY_FLOAT position)
{
pluckPos = position; /* pluck position is zeroes at pos*length */
{
// Pluck position puts zeroes at position * length.
pluckPosition = position;
if ( position < 0.0 ) {
cerr << "Mandolin: pluck position parameter less than zero!" << endl;
pluckPosition = 0.0;
}
else if ( position > 1.0 ) {
cerr << "Mandolin: pluck position parameter greater than 1.0!" << endl;
pluckPosition = 1.0;
}
this->pluck(amplitude);
}
void Mandolin :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
void Mandolin :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
this->setFreq(freq);
this->pluck(amp);
#if defined(_debug_)
printf("Mandolin : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
this->setFrequency(frequency);
this->pluck(amplitude);
#if defined(_STK_DEBUG_)
cerr << "Mandolin: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Mandolin :: setBodySize(MY_FLOAT size)
{
int i;
for (i=0;i<12;i++) {
soundfile[i]->setRate(size);
}
// Scale the commuted body response by its sample rate (22050).
MY_FLOAT rate = size * 22050.0 / Stk::sampleRate();
for ( int i=0; i<12; i++ )
soundfile[i]->setRate(rate);
}
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)));
MY_FLOAT temp = 0.0;
if ( !waveDone ) {
// Scale the pluck excitation with comb
// filtering for the duration of the file.
temp = soundfile[mic]->tick() * pluckAmplitude;
temp = temp - combDelay->tick(temp);
waveDone = soundfile[mic]->isFinished();
}
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)));
// Damping hack to help avoid overflow on re-plucking.
if ( dampTime >=0 ) {
dampTime -= 1;
// Calculate 1st delay filtered reflection plus pluck excitation.
lastOutput = delayLine->tick( filter->tick( temp + (delayLine->lastOut() * (MY_FLOAT) 0.7) ) );
// Calculate 2nd delay just like the 1st.
lastOutput += delayLine2->tick( filter2->tick( temp + (delayLine2->lastOut() * (MY_FLOAT) 0.7) ) );
}
else { // No damping hack after 1 period.
// Calculate 1st delay filtered reflection plus pluck excitation.
lastOutput = delayLine->tick( filter->tick( temp + (delayLine->lastOut() * loopGain) ) );
// Calculate 2nd delay just like the 1st.
lastOutput += delayLine2->tick( filter2->tick( 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;
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Mandolin: Control value less than zero!" << endl;
}
else {
printf("Mandolin : Undefined Control Number!! %i\n",number);
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Mandolin: Control value greater than 128.0!" << endl;
}
if (number == __SK_BodySize_) // 2
this->setBodySize( norm * 2.0 );
else if (number == __SK_PickPosition_) // 4
this->setPluckPosition( norm );
else if (number == __SK_StringDamping_) // 11
this->setBaseLoopGain((MY_FLOAT) 0.97 + (norm * (MY_FLOAT) 0.03));
else if (number == __SK_StringDetune_) // 1
this->setDetune((MY_FLOAT) 1.0 - (norm * (MY_FLOAT) 0.1));
else if (number == __SK_AfterTouch_Cont_) // 128
mic = (int) (norm * 11.0);
else
cerr << "Mandolin: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Mandolin: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

@@ -1,235 +0,0 @@
/*******************************************/
/* 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;
}
}

View File

@@ -1,196 +0,0 @@
/*******************************************/
/* 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;
}
}

388
src/Mesh2D.cpp Normal file
View File

@@ -0,0 +1,388 @@
/***************************************************/
/*! \class Mesh2D
\brief Two-dimensional rectilinear waveguide mesh class.
This class implements a rectilinear,
two-dimensional digital waveguide mesh
structure. For details, see Van Duyne and
Smith, "Physical Modeling with the 2-D Digital
Waveguide Mesh", Proceedings of the 1993
International Computer Music Conference.
This is a digital waveguide model, making its
use possibly subject to patents held by Stanford
University, Yamaha, and others.
Control Change Numbers:
- X Dimension = 2
- Y Dimension = 4
- Mesh Decay = 11
- X-Y Input Position = 1
by Julius Smith, 2000 - 2002.
Revised by Gary Scavone for STK, 2002.
*/
/***************************************************/
#include "Mesh2D.h"
#include "SKINI.msg"
#include <stdlib.h>
Mesh2D :: Mesh2D(short nX, short nY)
{
this->setNX(nX);
this->setNY(nY);
MY_FLOAT pole = 0.05;
short i;
for (i=0; i<NYMAX; i++) {
filterY[i] = new OnePole(pole);
filterY[i]->setGain(0.99);
}
for (i=0; i<NXMAX; i++) {
filterX[i] = new OnePole(pole);
filterX[i]->setGain(0.99);
}
this->clearMesh();
counter=0;
xInput = 0;
yInput = 0;
}
Mesh2D :: ~Mesh2D()
{
short i;
for (i=0; i<NYMAX; i++)
delete filterY[i];
for (i=0; i<NXMAX; i++)
delete filterX[i];
}
void Mesh2D :: clear()
{
this->clearMesh();
short i;
for (i=0; i<NY; i++)
filterY[i]->clear();
for (i=0; i<NX; i++)
filterX[i]->clear();
counter=0;
}
void Mesh2D :: clearMesh()
{
int x, y;
for (x=0; x<NXMAX-1; x++) {
for (y=0; y<NYMAX-1; y++) {
v[x][y] = 0;
}
}
for (x=0; x<NXMAX; x++) {
for (y=0; y<NYMAX; y++) {
vxp[x][y] = 0;
vxm[x][y] = 0;
vyp[x][y] = 0;
vym[x][y] = 0;
vxp1[x][y] = 0;
vxm1[x][y] = 0;
vyp1[x][y] = 0;
vym1[x][y] = 0;
}
}
}
MY_FLOAT Mesh2D :: energy()
{
// Return total energy contained in wave variables Note that some
// energy is also contained in any filter delay elements.
int x, y;
MY_FLOAT t;
MY_FLOAT e = 0;
if ( counter & 1 ) { // Ready for Mesh2D::tick1() to be called.
for (x=0; x<NX; x++) {
for (y=0; y<NY; y++) {
t = vxp1[x][y];
e += t*t;
t = vxm1[x][y];
e += t*t;
t = vyp1[x][y];
e += t*t;
t = vym1[x][y];
e += t*t;
}
}
}
else { // Ready for Mesh2D::tick0() to be called.
for (x=0; x<NX; x++) {
for (y=0; y<NY; y++) {
t = vxp[x][y];
e += t*t;
t = vxm[x][y];
e += t*t;
t = vyp[x][y];
e += t*t;
t = vym[x][y];
e += t*t;
}
}
}
return(e);
}
void Mesh2D :: setNX(short lenX)
{
NX = lenX;
if ( lenX < 2 ) {
cerr << "Mesh2D::setNX(" << lenX << "): Minimum length is 2!" << endl;
NX = 2;
}
else if ( lenX > NXMAX ) {
cerr << "Mesh2D::setNX(" << lenX << "): Maximum length is " << NXMAX << "!" << endl;
NX = NXMAX;
}
}
void Mesh2D :: setNY(short lenY)
{
NY = lenY;
if ( lenY < 2 ) {
cerr << "Mesh2D::setNY(" << lenY << "): Minimum length is 2!" << endl;
NY = 2;
}
else if ( lenY > NYMAX ) {
cerr << "Mesh2D::setNY(" << lenY << "): Maximum length is " << NYMAX << "!" << endl;
NY = NYMAX;
}
}
void Mesh2D :: setDecay(MY_FLOAT decayFactor)
{
MY_FLOAT gain = decayFactor;
if ( decayFactor < 0.0 ) {
cerr << "Mesh2D::setDecay decayFactor value is less than 0.0!" << endl;
gain = 0.0;
}
else if ( decayFactor > 1.0 ) {
cerr << "Mesh2D::setDecay decayFactor value is greater than 1.0!" << endl;
gain = 1.0;
}
int i;
for (i=0; i<NYMAX; i++)
filterY[i]->setGain(gain);
for (i=0; i<NXMAX; i++)
filterX[i]->setGain(gain);
}
void Mesh2D :: setInputPosition(MY_FLOAT xFactor, MY_FLOAT yFactor)
{
if ( xFactor < 0.0 ) {
cerr << "Mesh2D::setInputPosition xFactor value is less than 0.0!" << endl;
xInput = 0;
}
else if ( xFactor > 1.0 ) {
cerr << "Mesh2D::setInputPosition xFactor value is greater than 1.0!" << endl;
xInput = NX - 1;
}
else
xInput = (short) (xFactor * (NX - 1));
if ( yFactor < 0.0 ) {
cerr << "Mesh2D::setInputPosition yFactor value is less than 0.0!" << endl;
yInput = 0;
}
else if ( yFactor > 1.0 ) {
cerr << "Mesh2D::setInputPosition yFactor value is greater than 1.0!" << endl;
yInput = NY - 1;
}
else
yInput = (short) (yFactor * (NY - 1));
}
void Mesh2D :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
// Input at corner.
if ( counter & 1 ) {
vxp1[xInput][yInput] += amplitude;
vyp1[xInput][yInput] += amplitude;
}
else {
vxp[xInput][yInput] += amplitude;
vyp[xInput][yInput] += amplitude;
}
#if defined(_STK_DEBUG_)
cerr << "Mesh2D: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Mesh2D :: noteOff(MY_FLOAT amplitude)
{
#if defined(_STK_DEBUG_)
cerr << "Mesh2D: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT Mesh2D :: tick(MY_FLOAT input)
{
if ( counter & 1 ) {
vxp1[xInput][yInput] += input;
vyp1[xInput][yInput] += input;
lastOutput = tick1();
}
else {
vxp[xInput][yInput] += input;
vyp[xInput][yInput] += input;
lastOutput = tick0();
}
counter++;
return lastOutput;
}
MY_FLOAT Mesh2D :: tick()
{
lastOutput = ((counter & 1) ? this->tick1() : this->tick0());
counter++;
return lastOutput;
}
#define VSCALE ((MY_FLOAT) (0.5))
MY_FLOAT Mesh2D :: tick0()
{
int x, y;
MY_FLOAT outsamp = 0;
// Update junction velocities.
for (x=0; x<NX-1; x++) {
for (y=0; y<NY-1; y++) {
v[x][y] = ( vxp[x][y] + vxm[x+1][y] +
vyp[x][y] + vym[x][y+1] ) * VSCALE;
}
}
// Update junction outgoing waves, using alternate wave-variable buffers.
for (x=0; x<NX-1; x++) {
for (y=0; y<NY-1; y++) {
MY_FLOAT vxy = v[x][y];
// Update positive-going waves.
vxp1[x+1][y] = vxy - vxm[x+1][y];
vyp1[x][y+1] = vxy - vym[x][y+1];
// Update minus-going waves.
vxm1[x][y] = vxy - vxp[x][y];
vym1[x][y] = vxy - vyp[x][y];
}
}
// Loop over velocity-junction boundary faces, update edge
// reflections, with filtering. We're only filtering on one x and y
// edge here and even this could be made much sparser.
for (y=0; y<NY-1; y++) {
vxp1[0][y] = filterY[y]->tick(vxm[0][y]);
vxm1[NX-1][y] = vxp[NX-1][y];
}
for (x=0; x<NX-1; x++) {
vyp1[x][0] = filterX[x]->tick(vym[x][0]);
vym1[x][NY-1] = vyp[x][NY-1];
}
// Output = sum of outgoing waves at far corner. Note that the last
// index in each coordinate direction is used only with the other
// coordinate indices at their next-to-last values. This is because
// the "unit strings" attached to each velocity node to terminate
// the mesh are not themselves connected together.
outsamp = vxp[NX-1][NY-2] + vyp[NX-2][NY-1];
return outsamp;
}
MY_FLOAT Mesh2D :: tick1()
{
int x, y;
MY_FLOAT outsamp = 0;
// Update junction velocities.
for (x=0; x<NX-1; x++) {
for (y=0; y<NY-1; y++) {
v[x][y] = ( vxp1[x][y] + vxm1[x+1][y] +
vyp1[x][y] + vym1[x][y+1] ) * VSCALE;
}
}
// Update junction outgoing waves,
// using alternate wave-variable buffers.
for (x=0; x<NX-1; x++) {
for (y=0; y<NY-1; y++) {
MY_FLOAT vxy = v[x][y];
// Update positive-going waves.
vxp[x+1][y] = vxy - vxm1[x+1][y];
vyp[x][y+1] = vxy - vym1[x][y+1];
// Update minus-going waves.
vxm[x][y] = vxy - vxp1[x][y];
vym[x][y] = vxy - vyp1[x][y];
}
}
// Loop over velocity-junction boundary faces, update edge
// reflections, with filtering. We're only filtering on one x and y
// edge here and even this could be made much sparser.
for (y=0; y<NY-1; y++) {
vxp[0][y] = filterY[y]->tick(vxm1[0][y]);
vxm[NX-1][y] = vxp1[NX-1][y];
}
for (x=0; x<NX-1; x++) {
vyp[x][0] = filterX[x]->tick(vym1[x][0]);
vym[x][NY-1] = vyp1[x][NY-1];
}
// Output = sum of outgoing waves at far corner.
outsamp = vxp1[NX-1][NY-2] + vyp1[NX-2][NY-1];
return outsamp;
}
void Mesh2D :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Mesh2D: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Mesh2D: Control value greater than 128.0!" << endl;
}
if (number == 2) // 2
setNX( (short) (norm * (NXMAX-2) + 2) );
else if (number == 4) // 4
setNY( (short) (norm * (NYMAX-2) + 2) );
else if (number == 11) // 11
setDecay( 0.9 + (norm * 0.1) );
else if (number == __SK_ModWheel_) // 1
setInputPosition(norm, norm);
else if (number == __SK_AfterTouch_Cont_) // 128
;
else
cerr << "Mesh2D: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Mesh2D: controlChange number = " << number << ", value = " << value << endl;
#endif
}

385
src/Messager.cpp Normal file
View File

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

225
src/Modal.cpp Normal file
View File

@@ -0,0 +1,225 @@
/***************************************************/
/*! \class Modal
\brief STK resonance model instrument.
This class contains an excitation wavetable,
an envelope, an oscillator, and N resonances
(non-sweeping BiQuad filters), where N is set
during instantiation.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Modal.h"
#include <string.h>
#include <stdlib.h>
Modal :: Modal(int modes)
: nModes(modes)
{
if ( nModes <= 0 ) {
char msg[256];
sprintf(msg, "Modal: Invalid number of modes (%d) argument to constructor!", modes);
handleError(msg, StkError::FUNCTION_ARGUMENT);
}
// We don't make the excitation wave here yet, because we don't know
// what it's going to be.
ratios = (MY_FLOAT *) new MY_FLOAT[nModes];
radii = (MY_FLOAT *) new MY_FLOAT[nModes];
filters = (BiQuad **) calloc( nModes, sizeof(BiQuad *) );
for (int i=0; i<nModes; i++ ) {
filters[i] = new BiQuad;
filters[i]->setEqualGainZeroes();
}
envelope = new Envelope;
onepole = new OnePole;
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE);
// Set some default values.
vibrato->setFrequency( 6.0 );
vibratoGain = 0.0;
directGain = 0.0;
masterGain = 1.0;
baseFrequency = 440.0;
this->clear();
stickHardness = 0.5;
strikePosition = 0.561;
}
Modal :: ~Modal()
{
delete envelope;
delete onepole;
delete vibrato;
delete [] ratios;
delete [] radii;
for (int i=0; i<nModes; i++ ) {
delete filters[i];
}
free(filters);
}
void Modal :: clear()
{
onepole->clear();
for (int i=0; i<nModes; i++ )
filters[i]->clear();
}
void Modal :: setFrequency(MY_FLOAT frequency)
{
baseFrequency = frequency;
for (int i=0; i<nModes; i++ )
this->setRatioAndRadius(i, ratios[i], radii[i]);
}
void Modal :: setRatioAndRadius(int modeIndex, MY_FLOAT ratio, MY_FLOAT radius)
{
if ( modeIndex < 0 ) {
cerr << "Modal: setRatioAndRadius modeIndex parameter is less than zero!" << endl;
return;
}
else if ( modeIndex >= nModes ) {
cerr << "Modal: setRatioAndRadius modeIndex parameter is greater than the number of operators!" << endl;
return;
}
MY_FLOAT nyquist = Stk::sampleRate() / 2.0;
MY_FLOAT temp;
if (ratio * baseFrequency < nyquist) {
ratios[modeIndex] = ratio;
}
else {
temp = ratio;
while (temp * baseFrequency > nyquist) temp *= (MY_FLOAT) 0.5;
ratios[modeIndex] = temp;
#if defined(_STK_DEBUG_)
cerr << "Modal : Aliasing would occur here ... correcting." << endl;
#endif
}
radii[modeIndex] = radius;
if (ratio < 0)
temp = -ratio;
else
temp = ratio*baseFrequency;
filters[modeIndex]->setResonance(temp, radius);
}
void Modal :: setMasterGain(MY_FLOAT aGain)
{
masterGain = aGain;
}
void Modal :: setDirectGain(MY_FLOAT aGain)
{
directGain = aGain;
}
void Modal :: setModeGain(int modeIndex, MY_FLOAT gain)
{
if ( modeIndex < 0 ) {
cerr << "Modal: setModeGain modeIndex parameter is less than zero!" << endl;
return;
}
else if ( modeIndex >= nModes ) {
cerr << "Modal: setModeGain modeIndex parameter is greater than the number of operators!" << endl;
return;
}
filters[modeIndex]->setGain(gain);
}
void Modal :: strike(MY_FLOAT amplitude)
{
MY_FLOAT gain = amplitude;
if ( amplitude < 0.0 ) {
cerr << "Modal: strike amplitude is less than zero!" << endl;
gain = 0.0;
}
else if ( amplitude > 1.0 ) {
cerr << "Modal: strike amplitude is greater than 1.0!" << endl;
gain = 1.0;
}
envelope->setRate(1.0);
envelope->setTarget(gain);
onepole->setPole(1.0 - gain);
envelope->tick();
wave->reset();
MY_FLOAT temp;
for (int i=0; i<nModes; i++) {
if (ratios[i] < 0)
temp = -ratios[i];
else
temp = ratios[i] * baseFrequency;
filters[i]->setResonance(temp, radii[i]);
}
}
void Modal :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
this->strike(amplitude);
this->setFrequency(frequency);
#if defined(_STK_DEBUG_)
cerr << "Modal: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Modal :: noteOff(MY_FLOAT amplitude)
{
// This calls damp, but inverts the meaning of amplitude (high
// amplitude means fast damping).
this->damp(1.0 - (amplitude * 0.03));
#if defined(_STK_DEBUG_)
cerr << "Modal: NoteOff amplitude = " << amplitude << endl;
#endif
}
void Modal :: damp(MY_FLOAT amplitude)
{
MY_FLOAT temp;
for (int i=0; i<nModes; i++) {
if (ratios[i] < 0)
temp = -ratios[i];
else
temp = ratios[i] * baseFrequency;
filters[i]->setResonance(temp, radii[i]*amplitude);
}
}
MY_FLOAT Modal :: tick()
{
MY_FLOAT temp = masterGain * onepole->tick(wave->tick() * envelope->tick());
MY_FLOAT temp2 = 0.0;
for (int i=0; i<nModes; i++)
temp2 += filters[i]->tick(temp);
temp2 -= temp2 * directGain;
temp2 += directGain * temp;
if (vibratoGain != 0.0) {
// Calculate AM and apply to master out
temp = 1.0 + (vibrato->tick() * vibratoGain);
temp2 = temp * temp2;
}
lastOutput = temp2;
return lastOutput;
}

View File

@@ -1,202 +0,0 @@
/*******************************************/
/*
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;
}

View File

@@ -1,73 +1,106 @@
/*******************************************/
/*
ModalBar SubClass of Modal4 Instrument
by Perry R. Cook, 1999-2000
/***************************************************/
/*! \class ModalBar
\brief STK resonant bar instrument class.
Controls: CONTROL1 = stickHardness
CONTROL2 = strikePosition
CONTROL3 = Mode Presets
This class implements a number of different
struck bar instruments. It inherits from the
Modal class.
Control Change Numbers:
- Stick Hardness = 2
- Stick Position = 4
- Vibrato Gain = 11
- Vibrato Frequency = 7
- Direct Stick Mix = 1
- Volume = 128
- Modal Presets = 16
- Marimba = 0
- Vibraphone = 1
- Agogo = 2
- Wood1 = 3
- Reso = 4
- Wood2 = 5
- Beats = 6
- Two Fixed = 7
- Clump = 8
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/*******************************************/
/***************************************************/
#include "ModalBar.h"
#include "SKINI11.msg"
#include "SKINI.msg"
#include <string.h>
#include <math.h>
ModalBar :: ModalBar() : Modal4()
ModalBar :: ModalBar()
: Modal()
{
// 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 */
wave = new WvIn( strcat(file,"rawwaves/marmstk1.raw"), TRUE );
wave->setRate((MY_FLOAT) 0.5 * 22050.0 / Stk::sampleRate() );
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;
}
// Set the resonances for preset 0 (marimba).
setPreset( 0 );
}
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);
if ( hardness < 0.0 ) {
cerr << "ModalBar: setStickHardness parameter is less than zero!" << endl;
stickHardness = 0.0;
}
else if ( hardness > 1.0 ) {
cerr << "ModalBar: setStickHarness parameter is greater than 1.0!" << endl;
stickHardness = 1.0;
}
wave->setRate( (0.25 * (MY_FLOAT) pow(4.0, stickHardness)) );
masterGain = 0.1 + (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. */
strikePosition = position;
if ( position < 0.0 ) {
cerr << "ModalBar: setStrikePositions parameter is less than zero!" << endl;
strikePosition = 0.0;
}
else if ( position > 1.0 ) {
cerr << "ModalBar: setStrikePosition parameter is greater than 1.0!" << endl;
strikePosition = 1.0;
}
// Hack only first three modes.
MY_FLOAT temp2 = position * PI;
MY_FLOAT temp = sin(temp2);
this->setModeGain(0, 0.12 * temp);
temp = sin(0.05 + (3.9 * temp2));
this->setModeGain(1,(MY_FLOAT) -0.03 * temp);
temp = (MY_FLOAT) sin(-0.05 + (11 * temp2));
this->setFiltGain(2,(MY_FLOAT) 0.11 * temp); /* 3rd mode function of pos. */
this->setModeGain(2,(MY_FLOAT) 0.11 * temp);
}
void ModalBar :: setModalPreset(int which)
void ModalBar :: setPreset(int preset)
{
/* 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] = {
// 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
static 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},
@@ -106,44 +139,52 @@ void ModalBar :: setModalPreset(int which)
{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]);
int temp = (preset % 9);
for (int i=0; i<nModes; i++) {
this->setRatioAndRadius(i, presets[temp][0][i], presets[temp][1][i]);
this->setModeGain(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;
vibratoGain = 0.2;
else
vibrGain = 0.0;
vibratoGain = 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);
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "ModalBar: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "ModalBar: Control value greater than 128.0!" << endl;
}
else {
printf("ModalBar : Undefined Control Number!!\n");
}
}
if (number == __SK_StickHardness_) // 2
this->setStickHardness( norm );
else if (number == __SK_StrikePosition_) // 4
this->setStrikePosition( norm );
else if (number == __SK_ProphesyRibbon_) // 16
this->setPreset((int) value);
else if (number == __SK_ModWheel_) // 1
directGain = norm;
else if (number == 11) // 11
vibratoGain = norm * 0.3;
else if (number == __SK_ModFrequency_) // 7
vibrato->setFrequency( norm * 12.0 );
else if (number == __SK_AfterTouch_Cont_) // 128
envelope->setTarget( norm );
else
cerr << "ModalBar: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "ModalBar: controlChange number = " << number << ", value = " << value << endl;
#endif
}

71
src/Modulate.cpp Normal file
View File

@@ -0,0 +1,71 @@
/***************************************************/
/*! \class Modulate
\brief STK periodic/random modulator.
This class combines random and periodic
modulations to give a nice, natural human
modulation function.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Modulate.h"
#include <string.h>
Modulate :: Modulate()
{
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char file[128];
strcpy(file, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE );
vibrato->setFrequency( 6.0 );
vibratoGain = 0.04;
noise = new SubNoise(330);
randomGain = 0.05;
filter = new OnePole( 0.999 );
filter->setGain( randomGain );
}
Modulate :: ~Modulate()
{
delete vibrato;
delete noise;
delete filter;
}
void Modulate :: reset()
{
lastOutput = (MY_FLOAT) 0.0;
}
void Modulate :: setVibratoRate(MY_FLOAT aRate)
{
vibrato->setFrequency( aRate );
}
void Modulate :: setVibratoGain(MY_FLOAT aGain)
{
vibratoGain = aGain;
}
void Modulate :: setRandomGain(MY_FLOAT aGain)
{
randomGain = aGain;
filter->setGain( randomGain );
}
MY_FLOAT Modulate :: tick()
{
// Compute periodic and random modulations.
lastOutput = vibratoGain * vibrato->tick();
lastOutput += filter->tick( noise->tick() );
return lastOutput;
}
MY_FLOAT Modulate :: lastOut() const
{
return lastOutput;
}

View File

@@ -1,85 +0,0 @@
/*******************************************/
/* 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);
}
*/

155
src/Moog.cpp Normal file
View File

@@ -0,0 +1,155 @@
/***************************************************/
/*! \class Moog
\brief STK moog-like swept filter sampling synthesis class.
This instrument uses one attack wave, one
looped wave, and an ADSR envelope (inherited
from the Sampler class) and adds two sweepable
formant (FormSwep) filters.
Control Change Numbers:
- Filter Q = 2
- Filter Sweep Rate = 4
- Vibrato Frequency = 11
- Vibrato Gain = 1
- Gain = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Moog.h"
#include "SKINI.msg"
#include <string.h>
Moog :: Moog()
{
// 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 WvIn( strcat(file,"rawwaves/mandpluk.raw"), TRUE );
strcpy(file,temp);
loops[0] = new WaveLoop( strcat(file,"rawwaves/impuls20.raw"), TRUE );
strcpy(file,temp);
loops[1] = new WaveLoop( strcat(file,"rawwaves/sinewave.raw"), TRUE ); // vibrato
loops[1]->setFrequency((MY_FLOAT) 6.122);
filters[0] = new FormSwep();
filters[0]->setTargets( 0.0, 0.7 );
filters[1] = new FormSwep();
filters[1]->setTargets( 0.0, 0.7 );
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;
}
Moog :: ~Moog()
{
delete attacks[0];
delete loops[0];
delete loops[1];
delete filters[0];
delete filters[1];
}
void Moog :: setFrequency(MY_FLOAT frequency)
{
baseFrequency = frequency;
if ( frequency <= 0.0 ) {
cerr << "Moog: setFrequency parameter is less than or equal to zero!" << endl;
baseFrequency = 220.0;
}
MY_FLOAT rate = attacks[0]->getSize() * 0.01 * baseFrequency / sampleRate();
attacks[0]->setRate( rate );
loops[0]->setFrequency(baseFrequency);
}
void Moog :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
MY_FLOAT temp;
this->setFrequency( frequency );
this->keyOn();
attackGain = amplitude * (MY_FLOAT) 0.5;
loopGain = amplitude;
temp = filterQ + (MY_FLOAT) 0.05;
filters[0]->setStates( 2000.0, temp );
filters[1]->setStates( 2000.0, temp );
temp = filterQ + (MY_FLOAT) 0.099;
filters[0]->setTargets( frequency, temp );
filters[1]->setTargets( frequency, temp );
filters[0]->setSweepRate( filterRate * 22050.0 / Stk::sampleRate() );
filters[1]->setSweepRate( filterRate * 22050.0 / Stk::sampleRate() );
#if defined(_STK_DEBUG_)
cerr << "Moog: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Moog :: setModulationSpeed(MY_FLOAT mSpeed)
{
loops[1]->setFrequency(mSpeed);
}
void Moog :: setModulationDepth(MY_FLOAT mDepth)
{
modDepth = mDepth * (MY_FLOAT) 0.5;
}
MY_FLOAT Moog :: tick()
{
MY_FLOAT temp;
if ( modDepth != 0.0 ) {
temp = loops[1]->tick() * modDepth;
loops[0]->setFrequency( baseFrequency * (1.0 + temp) );
}
temp = Sampler::tick();
temp = filters[0]->tick( temp );
lastOutput = filters[1]->tick( temp );
return lastOutput * 3.0;
}
void Moog :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Moog: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Moog: Control value greater than 128.0!" << endl;
}
if (number == __SK_FilterQ_) // 2
filterQ = 0.80 + ( 0.1 * norm );
else if (number == __SK_FilterSweepRate_) // 4
filterRate = norm * 0.0002;
else if (number == __SK_ModFrequency_) // 11
this->setModulationSpeed( norm * 12.0 );
else if (number == __SK_ModWheel_) // 1
this->setModulationDepth( norm );
else if (number == __SK_AfterTouch_Cont_) // 128
adsr->setTarget( norm );
else
cerr << "Moog: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Moog: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

@@ -1,111 +0,0 @@
/******************************************/
/* 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;
}

View File

@@ -1,140 +1,109 @@
/******************************************/
/* 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. */
/******************************************/
/***************************************************/
/*! \class NRev
\brief CCRMA's NRev reverberator class.
This class is derived from the CLM NRev
function, which is based on the use of
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.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "NRev.h"
#include <math.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;
int lengths[15] = {1433, 1601, 1867, 2053, 2251, 2399, 347, 113, 37, 59, 53, 43, 37, 29, 19};
double scaler = Stk::sampleRate() / 25641.0;
int delay, i;
for (i=0; i<15; i++) {
delay = (int) floor(scaler * lengths[i]);
if ( (delay & 1) == 0) delay++;
while ( !this->isPrime(delay) ) delay += 2;
lengths[i] = delay;
}
for (i=0; i<6; i++) {
combDelays[i] = new Delay( lengths[i], lengths[i]);
combCoefficient[i] = pow(10, (-3 * lengths[i] / (T60 * Stk::sampleRate())));
}
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;
allpassDelays[i] = new Delay(lengths[i+6], lengths[i+6]);
allpassCoefficient = 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];
int i;
for (i=0; i<6; i++) delete combDelays[i];
for (i=0; i<8; i++) delete allpassDelays[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;
int i;
for (i=0; i<6; i++) combDelays[i]->clear();
for (i=0; i<8; i++) allpassDelays[i]->clear();
lastOutput[0] = 0.0;
lastOutput[1] = 0.0;
lowpassState = 0.0;
}
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;
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;
for (i=0; i<6; i++) {
temp = input + (combCoefficient[i] * combDelays[i]->lastOut());
temp0 += combDelays[i]->tick(temp);
}
for (i=0; i<3; i++) {
temp = allpassDelays[i]->lastOut();
temp1 = allpassCoefficient * temp;
temp1 += temp0;
allpassDelays[i]->tick(temp1);
temp0 = -(allpassCoefficient * temp1) + temp;
}
// One-pole lowpass filter.
lowpassState = 0.7*lowpassState + 0.3*temp0;
temp = allpassDelays[3]->lastOut();
temp1 = allpassCoefficient * temp;
temp1 += lowpassState;
allpassDelays[3]->tick(temp1);
temp1 = -(allpassCoefficient * temp1) + temp;
temp = APdelayLine[4]->lastOut();
temp2 = allPassCoeff * temp;
temp = allpassDelays[4]->lastOut();
temp2 = allpassCoefficient * temp;
temp2 += temp1;
APdelayLine[4]->tick(temp2);
lastOutL = effectMix*(-(allPassCoeff * temp2) + temp);
allpassDelays[4]->tick(temp2);
lastOutput[0] = effectMix*(-(allpassCoefficient * temp2) + temp);
temp = APdelayLine[5]->lastOut();
temp3 = allPassCoeff * temp;
temp = allpassDelays[5]->lastOut();
temp3 = allpassCoefficient * temp;
temp3 += temp1;
APdelayLine[5]->tick(temp3);
lastOutR = effectMix*(-(allPassCoeff * temp3) + temp);
allpassDelays[5]->tick(temp3);
lastOutput[1] = effectMix*(-(allpassCoefficient * temp3) + temp);
temp = (1.0 - effectMix) * input;
lastOutL += temp;
lastOutR += temp;
lastOutput[0] += temp;
lastOutput[1] += temp;
return (lastOutL + lastOutR) * 0.5;
return (lastOutput[0] + lastOutput[1]) * 0.5;
}

View File

@@ -1,37 +1,44 @@
/*******************************************/
/* 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;
}
/***************************************************/
/*! \class Noise
\brief STK noise generator.
Generic random number generation using the
C rand() function. The quality of the rand()
function varies from one OS to another.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Noise.h"
#include <stdlib.h>
Noise :: Noise() : Stk()
{
lastOutput = (MY_FLOAT) 0.0;
}
Noise :: ~Noise()
{
}
MY_FLOAT Noise :: tick()
{
lastOutput = (MY_FLOAT) (2.0 * rand() / (RAND_MAX + 1.0) );
lastOutput -= 1.0;
return lastOutput;
}
MY_FLOAT *Noise :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick();
return vector;
}
MY_FLOAT Noise :: lastOut() const
{
return lastOutput;
}

View File

@@ -1,24 +0,0 @@
/*********************************************/
/* 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()
{
}

View File

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

View File

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

View File

@@ -1,113 +1,93 @@
/*******************************************/
/* 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. */
/*******************************************/
/***************************************************/
/*! \class PRCRev
\brief Perry's simple reverberator class.
This class is based on some of the famous
Stanford/CCRMA reverbs (NRev, KipRev), which
were based on the Chowning/Moorer/Schroeder
reverberators using networks of simple allpass
and comb delay filters. This class implements
two series allpass units and two parallel comb
filters.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "PRCRev.h"
#include <math.h>
PRCRev :: PRCRev(MY_FLOAT T60)
{
int lens[4]={353,1097,1777,2137};
double srscale = SRATE / 44100.0;
int val, i;
// Delay lengths for 44100 Hz sample rate.
int lengths[4]= {353, 1097, 1777, 2137};
double scaler = Stk::sampleRate() / 44100.0;
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;
}
// Scale the delay lengths if necessary.
int delay, i;
if ( scaler != 1.0 ) {
for (i=0; i<4; i++) {
delay = (int) floor(scaler * lengths[i]);
if ( (delay & 1) == 0) delay++;
while ( !this->isPrime(delay) ) delay += 2;
lengths[i] = delay;
}
}
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)));
for (i=0; i<2; i++) {
allpassDelays[i] = new Delay( lengths[i], lengths[i] );
combDelays[i] = new Delay( lengths[i+2], lengths[i+2] );
combCoefficient[i] = pow(10,(-3 * lengths[i+2] / (T60 * Stk::sampleRate())));
}
allPassCoeff = (MY_FLOAT) 0.7;
effectMix = (MY_FLOAT) 0.5;
allpassCoefficient = 0.7;
effectMix = 0.5;
this->clear();
}
PRCRev :: ~PRCRev()
{
delete APdelayLine[0];
delete APdelayLine[1];
delete CdelayLine[0];
delete CdelayLine[1];
delete allpassDelays[0];
delete allpassDelays[1];
delete combDelays[0];
delete combDelays[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;
allpassDelays[0]->clear();
allpassDelays[1]->clear();
combDelays[0]->clear();
combDelays[1]->clear();
lastOutput[0] = 0.0;
lastOutput[1] = 0.0;
}
MY_FLOAT PRCRev :: tick(MY_FLOAT input)
{
MY_FLOAT temp,temp0,temp1,temp2,temp3;
MY_FLOAT temp, temp0, temp1, temp2, temp3;
temp = APdelayLine[0]->lastOut();
temp0 = allPassCoeff * temp;
temp = allpassDelays[0]->lastOut();
temp0 = allpassCoefficient * temp;
temp0 += input;
APdelayLine[0]->tick(temp0);
temp0 = -(allPassCoeff * temp0) + temp;
allpassDelays[0]->tick(temp0);
temp0 = -(allpassCoefficient * temp0) + temp;
temp = APdelayLine[1]->lastOut();
temp1 = allPassCoeff * temp;
temp = allpassDelays[1]->lastOut();
temp1 = allpassCoefficient * temp;
temp1 += temp0;
APdelayLine[1]->tick(temp1);
temp1 = -(allPassCoeff * temp1) + temp;
allpassDelays[1]->tick(temp1);
temp1 = -(allpassCoefficient * temp1) + temp;
temp2 = temp1 + (combCoeff[0] * CdelayLine[0]->lastOut());
temp3 = temp1 + (combCoeff[1] * CdelayLine[1]->lastOut());
temp2 = temp1 + (combCoefficient[0] * combDelays[0]->lastOut());
temp3 = temp1 + (combCoefficient[1] * combDelays[1]->lastOut());
lastOutL = effectMix * (CdelayLine[0]->tick(temp2));
lastOutR = effectMix * (CdelayLine[1]->tick(temp3));
lastOutput[0] = effectMix * (combDelays[0]->tick(temp2));
lastOutput[1] = effectMix * (combDelays[1]->tick(temp3));
temp = (MY_FLOAT) (1.0 - effectMix) * input;
lastOutL += temp;
lastOutR += temp;
lastOutput[0] += temp;
lastOutput[1] += temp;
return (lastOutL + lastOutR) * (MY_FLOAT) 0.5;
return (lastOutput[0] + lastOutput[1]) * (MY_FLOAT) 0.5;
}

View File

@@ -1,63 +1,116 @@
/******************************************/
/* Percussive Flute Subclass */
/* of Algorithm 4 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/******************************************/
/***************************************************/
/*! \class PercFlut
\brief STK percussive flute FM synthesis instrument.
This class implements algorithm 4 of the TX81Z.
\code
Algorithm 4 is : 4->3--\
2-- + -->1-->Out
\endcode
Control Change Numbers:
- Total Modulator Index = 2
- Modulator Crossfade = 4
- LFO Speed = 11
- LFO Depth = 1
- ADSR 2 & 4 Target = 128
The basic Chowning/Stanford FM patent expired
in 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.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "PercFlut.h"
#include <string.h>
PercFlut :: PercFlut() : FM4Alg4()
PercFlut :: PercFlut()
: FM()
{
int i;
char files[4][128];
// 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;
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );
this->setRatio(0, 1.50 * 1.000);
this->setRatio(1, 3.00 * 0.995);
this->setRatio(2, 2.99 * 1.005);
this->setRatio(3, 6.00 * 0.997);
gains[0] = __FM_gains[99];
gains[1] = __FM_gains[71];
gains[2] = __FM_gains[93];
gains[3] = __FM_gains[85];
adsr[0]->setAllTimes( 0.05, 0.05, __FM_susLevels[14], 0.05);
adsr[1]->setAllTimes( 0.02, 0.50, __FM_susLevels[13], 0.5);
adsr[2]->setAllTimes( 0.02, 0.30, __FM_susLevels[11], 0.05);
adsr[3]->setAllTimes( 0.02, 0.05, __FM_susLevels[13], 0.01);
twozero->setGain( 0.0 );
modDepth = 0.005;
}
void PercFlut :: setFreq(MY_FLOAT frequency)
{
baseFreq = frequency;
}
void PercFlut :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
PercFlut :: ~PercFlut()
{
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
}
void PercFlut :: setFrequency(MY_FLOAT frequency)
{
baseFrequency = frequency;
}
void PercFlut :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
gains[0] = amplitude * __FM_gains[99] * 0.5;
gains[1] = amplitude * __FM_gains[71] * 0.5;
gains[2] = amplitude * __FM_gains[93] * 0.5;
gains[3] = amplitude * __FM_gains[85] * 0.5;
this->setFrequency(frequency);
this->keyOn();
#if defined(_STK_DEBUG_)
cerr << "PercFlut: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT PercFlut :: tick()
{
register MY_FLOAT temp;
temp = vibrato->tick() * modDepth * (MY_FLOAT) 0.2;
waves[0]->setFrequency(baseFrequency * ((MY_FLOAT) 1.0 + temp) * ratios[0]);
waves[1]->setFrequency(baseFrequency * ((MY_FLOAT) 1.0 + temp) * ratios[1]);
waves[2]->setFrequency(baseFrequency * ((MY_FLOAT) 1.0 + temp) * ratios[2]);
waves[3]->setFrequency(baseFrequency * ((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 = (1.0 - (control2 * 0.5)) * gains[2] * adsr[2]->tick() * waves[2]->tick();
temp += control2 * 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;
}

90
src/PitShift.cpp Normal file
View File

@@ -0,0 +1,90 @@
/***************************************************/
/*! \class PitShift
\brief STK simple pitch shifter effect class.
This class implements a simple pitch shifter
using delay lines.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "PitShift.h"
#include <iostream.h>
#include <math.h>
PitShift :: PitShift()
{
delay[0] = 12;
delay[1] = 512;
delayLine[0] = new DelayL(delay[0], (long) 1024);
delayLine[1] = new DelayL(delay[1], (long) 1024);
effectMix = (MY_FLOAT) 0.5;
rate = 1.0;
}
PitShift :: ~PitShift()
{
delete delayLine[0];
delete delayLine[1];
}
void PitShift :: setEffectMix(MY_FLOAT mix)
{
effectMix = mix;
if ( mix < 0.0 ) {
cerr << "PitShift: setEffectMix parameter is less than zero!" << endl;
effectMix = 0.0;
}
else if ( mix > 1.0 ) {
cerr << "PitShift: setEffectMix parameter is greater than 1.0!" << endl;
effectMix = 1.0;
}
}
void PitShift :: setShift(MY_FLOAT shift)
{
if (shift < 1.0) {
rate = 1.0 - shift;
}
else if (shift > 1.0) {
rate = 1.0 - shift;
}
else {
rate = 0.0;
delay[0] = 512;
}
}
MY_FLOAT PitShift :: lastOut() const
{
return lastOutput;
}
MY_FLOAT PitShift :: tick(MY_FLOAT input)
{
delay[0] = delay[0] + rate;
while (delay[0] > 1012) delay[0] -= 1000;
while (delay[0] < 12) delay[0] += 1000;
delay[1] = delay[0] + 500;
while (delay[1] > 1012) delay[1] -= 1000;
while (delay[1] < 12) delay[1] += 1000;
delayLine[0]->setDelay((long)delay[0]);
delayLine[1]->setDelay((long)delay[1]);
env[1] = fabs(delay[0] - 512) * 0.002;
env[0] = 1.0 - env[1];
lastOutput = env[0] * delayLine[0]->tick(input);
lastOutput += env[1] * delayLine[1]->tick(input);
lastOutput *= effectMix;
lastOutput += (1.0 - effectMix) * input;
return lastOutput;
}
MY_FLOAT *PitShift :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}

127
src/PluckTwo.cpp Normal file
View File

@@ -0,0 +1,127 @@
/***************************************************/
/*! \class PluckTwo
\brief STK enhanced plucked string model class.
This class implements an enhanced two-string,
plucked physical model, a la Jaffe-Smith,
Smith, and others.
PluckTwo is an abstract class, with no excitation
specified. Therefore, it can't be directly
instantiated.
This is a digital waveguide model, making its
use possibly subject to patents held by
Stanford University, Yamaha, and others.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "PluckTwo.h"
PluckTwo :: PluckTwo(MY_FLOAT lowestFrequency)
{
length = (long) (Stk::sampleRate() / lowestFrequency + 1);
baseLoopGain = (MY_FLOAT) 0.995;
loopGain = (MY_FLOAT) 0.999;
delayLine = new DelayA((MY_FLOAT)(length / 2.0), length);
delayLine2 = new DelayA((MY_FLOAT)(length / 2.0), length);
combDelay = new DelayL((MY_FLOAT)(length / 2.0), length);
filter = new OneZero;
filter2 = new OneZero;
pluckAmplitude = (MY_FLOAT) 0.3;
pluckPosition = (MY_FLOAT) 0.4;
detuning = (MY_FLOAT) 0.995;
lastFrequency = lowestFrequency * (MY_FLOAT) 2.0;
lastLength = length * (MY_FLOAT) 0.5;
}
PluckTwo :: ~PluckTwo()
{
delete delayLine;
delete delayLine2;
delete combDelay;
delete filter;
delete filter2;
}
void PluckTwo :: clear()
{
delayLine->clear();
delayLine2->clear();
combDelay->clear();
filter->clear();
filter2->clear();
}
void PluckTwo :: setFrequency(MY_FLOAT frequency)
{
lastFrequency = frequency;
if ( lastFrequency <= 0.0 ) {
cerr << "PluckTwo: setFrequency parameter less than or equal to zero!" << endl;
lastFrequency = 220.0;
}
// Delay = length - approximate filter delay.
lastLength = ( Stk::sampleRate() / lastFrequency);
MY_FLOAT delay = (lastLength / detuning) - (MY_FLOAT) 0.5;
if ( delay <= 0.0 ) delay = 0.3;
else if ( delay > length ) delay = length;
delayLine->setDelay( delay );
delay = (lastLength * detuning) - (MY_FLOAT) 0.5;
if ( delay <= 0.0 ) delay = 0.3;
else if ( delay > length ) delay = length;
delayLine2->setDelay( delay );
loopGain = baseLoopGain + (frequency * (MY_FLOAT) 0.000005);
if ( loopGain > 1.0 ) loopGain = (MY_FLOAT) 0.99999;
}
void PluckTwo :: setDetune(MY_FLOAT detune)
{
detuning = detune;
if ( detuning <= 0.0 ) {
cerr << "PluckTwo: setDetune parameter less than or equal to zero!" << endl;
detuning = 0.1;
}
delayLine->setDelay(( lastLength / detuning) - (MY_FLOAT) 0.5);
delayLine2->setDelay( (lastLength * detuning) - (MY_FLOAT) 0.5);
}
void PluckTwo :: setFreqAndDetune(MY_FLOAT frequency, MY_FLOAT detune)
{
detuning = detune;
this->setFrequency(frequency);
}
void PluckTwo :: setPluckPosition(MY_FLOAT position)
{
pluckPosition = position;
if ( position < 0.0 ) {
cerr << "PluckTwo: setPluckPosition parameter is less than zero!" << endl;
pluckPosition = 0.0;
}
else if ( position > 1.0 ) {
cerr << "PluckTwo: setPluckPosition parameter is greater than 1.0!" << endl;
pluckPosition = 1.0;
}
}
void PluckTwo :: setBaseLoopGain(MY_FLOAT aGain)
{
baseLoopGain = aGain;
loopGain = baseLoopGain + (lastFrequency * (MY_FLOAT) 0.000005);
if ( loopGain > 0.99999 ) loopGain = (MY_FLOAT) 0.99999;
}
void PluckTwo :: noteOff(MY_FLOAT amplitude)
{
loopGain = ((MY_FLOAT) 1.0 - amplitude) * (MY_FLOAT) 0.5;
#if defined(_STK_DEBUG_)
cerr << "PluckTwo: NoteOff amplitude = " << amplitude << endl;
#endif
}

View File

@@ -1,21 +1,31 @@
/******************************************/
/* 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. */
/******************************************/
/***************************************************/
/*! \class Plucked
\brief STK plucked string model class.
This class implements a simple plucked string
physical model based on the Karplus-Strong
algorithm.
This is a digital waveguide model, making its
use possibly subject to patents held by
Stanford University, Yamaha, and others.
There exist at least two patents, assigned to
Stanford, bearing the names of Karplus and/or
Strong.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Plucked.h"
Plucked :: Plucked(MY_FLOAT lowestFreq)
Plucked :: Plucked(MY_FLOAT lowestFrequency)
{
length = (long) (SRATE / lowestFreq + 1);
length = (long) (Stk::sampleRate() / lowestFrequency + 1);
loopGain = (MY_FLOAT) 0.999;
delayLine = new DLineA(length);
loopFilt = new OneZero;
pickFilt = new OnePole;
delayLine = new DelayA( (MY_FLOAT)(length / 2.0), length );
loopFilter = new OneZero;
pickFilter = new OnePole;
noise = new Noise;
this->clear();
}
@@ -23,62 +33,85 @@ Plucked :: Plucked(MY_FLOAT lowestFreq)
Plucked :: ~Plucked()
{
delete delayLine;
delete loopFilt;
delete pickFilt;
delete loopFilter;
delete pickFilter;
delete noise;
}
void Plucked :: clear()
{
delayLine->clear();
loopFilt->clear();
pickFilt->clear();
loopFilter->clear();
pickFilter->clear();
}
void Plucked :: setFreq(MY_FLOAT frequency)
void Plucked :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT delay;
delay = (SRATE / frequency) - (MY_FLOAT) 0.5; /* length - delays */
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "Plucked: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
// Delay = length - approximate filter delay.
MY_FLOAT delay = (Stk::sampleRate() / freakency) - (MY_FLOAT) 0.5;
if (delay <= 0.0) delay = 0.3;
else if (delay > length) delay = length;
delayLine->setDelay(delay);
loopGain = (MY_FLOAT) 0.995 + (frequency * (MY_FLOAT) 0.000005);
if (loopGain>1.0) loopGain = (MY_FLOAT) 0.99999;
loopGain = 0.995 + (freakency * 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()));
MY_FLOAT gain = amplitude;
if ( gain > 1.0 ) {
cerr << "Plucked: pluck amplitude greater than 1.0!" << endl;
gain = 1.0;
}
else if ( gain < 0.0 ) {
cerr << "Plucked: pluck amplitude less than zero!" << endl;
gain = 0.0;
}
pickFilter->setPole((MY_FLOAT) 0.999 - (gain * (MY_FLOAT) 0.15));
pickFilter->setGain(gain * (MY_FLOAT) 0.5);
for (long i=0; i<length; i++)
// Fill delay with noise additively with current contents.
delayLine->tick( 0.6 * delayLine->lastOut() + pickFilter->tick( noise->tick() ) );
}
void Plucked :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
void Plucked :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
this->setFreq(freq);
this->pluck(amp);
#if defined(_debug_)
printf("Plucked : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
this->setFrequency(frequency);
this->pluck(amplitude);
#if defined(_STK_DEBUG_)
cerr << "Plucked: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Plucked :: noteOff(MY_FLOAT amp)
void Plucked :: noteOff(MY_FLOAT amplitude)
{
loopGain = (MY_FLOAT) 1.0 - amp;
#if defined(_debug_)
printf("Plucked : NoteOff: Amp=%lf\n",amp);
#endif
loopGain = (MY_FLOAT) 1.0 - amplitude;
if ( loopGain < 0.0 ) {
cerr << "Plucked: noteOff amplitude greater than 1.0!" << endl;
loopGain = 0.0;
}
else if ( loopGain > 1.0 ) {
cerr << "Plucked: noteOff amplitude less than or zero!" << endl;
loopGain = (MY_FLOAT) 0.99999;
}
#if defined(_STK_DEBUG_)
cerr << "Plucked: NoteOff amplitude = " << amplitude << endl;
#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));
// Here's the whole inner loop of the instrument!!
lastOutput = delayLine->tick( loopFilter->tick( delayLine->lastOut() * loopGain ) );
lastOutput *= (MY_FLOAT) 3.0;
return lastOutput;
}

View File

@@ -1,90 +0,0 @@
/******************************************/
/* 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
}

View File

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

View File

@@ -1,134 +0,0 @@
/*******************************************/
/* 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;
}
}

View File

@@ -1,77 +0,0 @@
/*******************************************/
/* 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;
}
}

View File

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

150
src/Resonate.cpp Normal file
View File

@@ -0,0 +1,150 @@
/***************************************************/
/*! \class Resonate
\brief STK noise driven formant filter.
This instrument contains a noise source, which
excites a biquad resonance filter, with volume
controlled by an ADSR.
Control Change Numbers:
- Resonance Frequency (0-Nyquist) = 2
- Pole Radii = 4
- Notch Frequency (0-Nyquist) = 11
- Zero Radii = 1
- Envelope Gain = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Resonate.h"
#include "SKINI.msg"
Resonate :: Resonate()
{
adsr = new ADSR;
noise = new Noise;
filter = new BiQuad;
poleFrequency = 4000.0;
poleRadius = 0.95;
// Set the filter parameters.
filter->setResonance( poleFrequency, poleRadius, TRUE );
zeroFrequency = 0.0;
zeroRadius = 0.0;
}
Resonate :: ~Resonate()
{
delete adsr;
delete filter;
delete noise;
}
void Resonate :: keyOn()
{
adsr->keyOn();
}
void Resonate :: keyOff()
{
adsr->keyOff();
}
void Resonate :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
adsr->setTarget( amplitude );
this->keyOn();
this->setResonance(frequency, poleRadius);
#if defined(_STK_DEBUG_)
cerr << "Resonate: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Resonate :: noteOff(MY_FLOAT amplitude)
{
this->keyOff();
#if defined(_STK_DEBUG_)
cerr << "Resonate: NoteOff amplitude = " << amplitude << endl;
#endif
}
void Resonate :: setResonance(MY_FLOAT frequency, MY_FLOAT radius)
{
poleFrequency = frequency;
if ( frequency < 0.0 ) {
cerr << "Resonate: setResonance frequency parameter is less than zero!" << endl;
poleFrequency = 0.0;
}
poleRadius = radius;
if ( radius < 0.0 ) {
cerr << "Resonate: setResonance radius parameter is less than 0.0!" << endl;
poleRadius = 0.0;
}
else if ( radius >= 1.0 ) {
cerr << "Resonate: setResonance radius parameter is greater than or equal to 1.0, which is unstable!" << endl;
poleRadius = 0.9999;
}
filter->setResonance( poleFrequency, poleRadius, TRUE );
}
void Resonate :: setNotch(MY_FLOAT frequency, MY_FLOAT radius)
{
zeroFrequency = frequency;
if ( frequency < 0.0 ) {
cerr << "Resonate: setNotch frequency parameter is less than zero!" << endl;
zeroFrequency = 0.0;
}
zeroRadius = radius;
if ( radius < 0.0 ) {
cerr << "Resonate: setNotch radius parameter is less than 0.0!" << endl;
zeroRadius = 0.0;
}
filter->setNotch( zeroFrequency, zeroRadius );
}
void Resonate :: setEqualGainZeroes()
{
filter->setEqualGainZeroes();
}
MY_FLOAT Resonate :: tick()
{
lastOutput = filter->tick(noise->tick());
lastOutput *= adsr->tick();
return lastOutput;
}
void Resonate :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Resonate: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Resonate: Control value greater than 128.0!" << endl;
}
if (number == 2) // 2
setResonance( norm * Stk::sampleRate() * 0.5, poleRadius );
else if (number == 4) // 4
setResonance( poleFrequency, norm*0.9999 );
else if (number == 11) // 11
this->setNotch( norm * Stk::sampleRate() * 0.5, zeroRadius );
else if (number == 1)
this->setNotch( zeroFrequency, norm );
else if (number == __SK_AfterTouch_Cont_) // 128
adsr->setTarget( norm );
else
cerr << "Resonate: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Resonate: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

@@ -1,12 +1,16 @@
/********************************************/
/* Reverb Abstract Class, */
/* by Tim Stilson, 1998 */
/* */
/* Integrated into STK by Gary Scavone */
/* with T60 argument. */
/********************************************/
/***************************************************/
/*! \class Reverb
\brief STK abstract reverberator parent class.
This class provides common functionality for
STK reverberator subclasses.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Reverb.h"
#include <math.h>
Reverb :: Reverb()
{
@@ -16,28 +20,41 @@ 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)
{
effectMix = mix;
}
int Reverb :: isprime(int val)
MY_FLOAT Reverb :: lastOut() const
{
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 */
return (lastOutput[0] + lastOutput[1]) * 0.5;
}
MY_FLOAT Reverb :: lastOutLeft() const
{
return lastOutput[0];
}
MY_FLOAT Reverb :: lastOutRight() const
{
return lastOutput[1];
}
MY_FLOAT *Reverb :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for (unsigned int i=0; i<vectorSize; i++)
vector[i] = tick(vector[i]);
return vector;
}
bool Reverb :: isPrime(int number)
{
if (number == 2) return true;
if (number & 1) {
for (int i=3; i<(int)sqrt((double)number)+1; i+=2)
if ( (number % i) == 0) return false;
return true; /* prime */
}
else return false; /* even */
}

View File

@@ -1,66 +1,120 @@
/******************************************/
/* Fender Rhodes Electric Piano Subclass */
/* of Algorithm 5 (TX81Z) Subclass of */
/* 4 Operator FM Synth */
/* by Perry R. Cook, 1995-96 */
/******************************************/
/***************************************************/
/*! \class Rhodey
\brief STK Fender Rhodes-like electric piano FM
synthesis instrument.
This class implements two simple FM Pairs
summed together, also referred to as algorithm
5 of the TX81Z.
\code
Algorithm 5 is : 4->3--\
+ --> Out
2->1--/
\endcode
Control Change Numbers:
- Modulator Index One = 2
- Crossfade of Outputs = 4
- LFO Speed = 11
- LFO Depth = 1
- ADSR 2 & 4 Target = 128
The basic Chowning/Stanford FM patent expired
in 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.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Rhodey.h"
#include <string.h>
Rhodey :: Rhodey() : FM4Alg5()
Rhodey :: Rhodey()
: FM()
{
// 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);
int i;
char files[4][128];
// Concatenate the STK RAWWAVE_PATH to the rawwave file.
for ( i=0; i<4; i++ )
strcpy( files[i], RAWWAVE_PATH);
strcat(files[0], "rawwaves/sinewave.raw");
strcat(files[1], "rawwaves/sinewave.raw");
strcat(files[2], "rawwaves/sinewave.raw");
strcat(files[3], "rawwaves/fwavblnk.raw");
for ( i=0; i<4; i++ )
waves[i] = new WaveLoop( files[i], TRUE );
this->setRatio(0, 1.0);
this->setRatio(1, 0.5);
this->setRatio(2, 1.0);
this->setRatio(3, 15.0);
gains[0] = __FM_gains[99];
gains[1] = __FM_gains[90];
gains[2] = __FM_gains[99];
gains[3] = __FM_gains[67];
adsr[0]->setAllTimes( 0.001, 1.50, 0.0, 0.04);
adsr[1]->setAllTimes( 0.001, 1.50, 0.0, 0.04);
adsr[2]->setAllTimes( 0.001, 1.00, 0.0, 0.04);
adsr[3]->setAllTimes( 0.001, 0.25, 0.0, 0.04);
twozero->setGain((MY_FLOAT) 1.0);
}
Rhodey :: ~Rhodey()
{
}
void Rhodey :: setFreq(MY_FLOAT frequency)
void Rhodey :: setFrequency(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]);
baseFrequency = frequency * (MY_FLOAT) 2.0;
for (int i=0; i<nOperators; i++ )
waves[i]->setFrequency( baseFrequency * ratios[i] );
}
void Rhodey :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
void Rhodey :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
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);
gains[0] = amplitude * __FM_gains[99];
gains[1] = amplitude * __FM_gains[90];
gains[2] = amplitude * __FM_gains[99];
gains[3] = amplitude * __FM_gains[67];
this->setFrequency(frequency);
this->keyOn();
#if defined(_debug_)
printf("Rhodey : NoteOn: Freq=%lf Amp=%lf\n",freq,amp);
#endif
#if defined(_STK_DEBUG_)
cerr << "Rhodey: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT Rhodey :: 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 = ( 1.0 - (control2 * 0.5)) * gains[0] * adsr[0]->tick() * waves[0]->tick();
temp += control2 * 0.5 * gains[2] * adsr[2]->tick() * waves[2]->tick();
// Calculate amplitude modulation and apply it to output.
temp2 = vibrato->tick() * modDepth;
temp = temp * (1.0 + temp2);
lastOutput = temp * 0.5;
return lastOutput;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,102 +1,153 @@
/*******************************************/
/* 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
/***************************************************/
/*! \class RtDuplex
\brief STK realtime audio input/output class.
This class provides a simplified interface to
RtAudio for realtime audio input/output. It
is also possible to achieve duplex operation
using separate RtWvIn and RtWvOut classes, but
this class ensures better input/output
syncronization.
RtDuplex supports multi-channel data in
interleaved format. It is important to
distinguish the tick() methods, which output
single samples to all channels in a sample frame
and return samples produced by averaging across
sample frames, from the tickFrame() methods, which
take/return pointers to multi-channel sample frames.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "RtDuplex.h"
RtDuplex :: RtDuplex(int nChannels, MY_FLOAT sampleRate, int device, int bufferFrames, int nBuffers )
{
channels = nChannels;
bufferSize = bufferFrames;
RtAudio::RTAUDIO_FORMAT format = ( sizeof(MY_FLOAT) == 8 ) ? RtAudio::RTAUDIO_FLOAT64 : RtAudio::RTAUDIO_FLOAT32;
try {
audio = new RtAudio(&stream, device, channels, device, channels, format,
(int)sampleRate, &bufferSize, nBuffers);
data = (MY_FLOAT *) audio->getStreamBuffer(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
for (unsigned int i=0; i<channels; i++) lastOutput[i] = 0.0;
counter = 0;
stopped = true;
}
RtDuplex :: ~RtDuplex()
{
if ( !stopped )
audio->stopStream(stream);
delete audio;
delete [] lastOutput;
data = 0; // RtAudio deletes the buffer itself.
}
void RtDuplex :: start()
{
if ( stopped ) {
audio->startStream(stream);
stopped = false;
}
}
void RtDuplex :: stop()
{
if ( !stopped ) {
audio->stopStream(stream);
stopped = true;
}
}
MY_FLOAT RtDuplex :: lastOut(void) const
{
if ( channels == 1 )
return *lastOutput;
MY_FLOAT output = 0.0;
for (unsigned int i=0; i<channels; i++ ) {
output += lastOutput[i];
}
return output / channels;
}
MY_FLOAT RtDuplex :: tick(const MY_FLOAT sample)
{
if ( stopped )
start();
if (counter == 0) {
try {
audio->tickStream(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
}
unsigned long temp = counter * channels;
for (unsigned int i=0; i<channels; i++) {
lastOutput[i] = data[temp];
data[temp++] = sample;
}
counter++;
if (counter >= (long) bufferSize)
counter = 0;
return lastOut();
}
MY_FLOAT *RtDuplex :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
for ( unsigned int i=0; i<vectorSize; i++ )
vector[i] = tick(vector[i]);
return vector;
}
const MY_FLOAT *RtDuplex :: lastFrame() const
{
return lastOutput;
}
MY_FLOAT *RtDuplex :: tickFrame(MY_FLOAT *frameVector, unsigned int frames)
{
if ( stopped )
start();
unsigned int i;
unsigned long temp;
for (unsigned int j=0; j<frames; j++ ) {
if (counter == 0) {
try {
audio->tickStream(stream);
}
catch (RtError &error) {
handleError( error.getMessage(), StkError::AUDIO_SYSTEM );
}
}
temp = counter * channels;
for (i=0; i<channels; i++) {
lastOutput[i] = data[temp];
data[temp++] = frameVector[j*channels+i];
frameVector[j*channels+i] = lastOutput[i];
}
counter++;
if (counter >= (long) bufferSize)
counter = 0;
}
return frameVector;
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@@ -1,130 +0,0 @@
#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. */
/* */
/*************************************************************************/

View File

@@ -1,48 +0,0 @@
/*******************************************/
/* 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)
{
}

View File

@@ -1,22 +1,25 @@
/*******************************************/
/* 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. */
/*******************************************/
/***************************************************/
/*! \class Sampler
\brief STK sampling synthesis abstract base class.
This instrument contains up to 5 attack waves,
5 looped waves, and an ADSR envelope.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Sampler.h"
Sampler :: Sampler()
{
// We don't make the waves here yet, because
// we don't know what they will be.
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;
baseFrequency = 440.0;
filter = new OnePole;
attackGain = (MY_FLOAT) 0.25;
loopGain = (MY_FLOAT) 0.25;
attackGain = 0.25;
loopGain = 0.25;
whichOne = 0;
}
@@ -40,13 +43,10 @@ void Sampler :: 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)
{
#if defined(_STK_DEBUG_)
cerr << "Sampler: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT Sampler :: tick()
@@ -57,7 +57,3 @@ MY_FLOAT Sampler :: tick()
lastOutput *= adsr->tick();
return lastOutput;
}
void Sampler :: controlChange(int number, MY_FLOAT value)
{
}

203
src/Saxofony.cpp Normal file
View File

@@ -0,0 +1,203 @@
/***************************************************/
/*! \class Saxofony
\brief STK faux conical bore reed instrument class.
This class implements a "hybrid" digital
waveguide instrument that can generate a
variety of wind-like sounds. It has also been
referred to as the "blowed string" model. The
waveguide section is essentially that of a
string, with one rigid and one lossy
termination. The non-linear function is a
reed table. The string can be "blown" at any
point between the terminations, though just as
with strings, it is impossible to excite the
system at either end. If the excitation is
placed at the string mid-point, the sound is
that of a clarinet. At points closer to the
"bridge", the sound is closer to that of a
saxophone. See Scavone (2002) for more details.
This is a digital waveguide model, making its
use possibly subject to patents held by Stanford
University, Yamaha, and others.
Control Change Numbers:
- Reed Stiffness = 2
- Reed Aperture = 26
- Noise Gain = 4
- Blow Position = 11
- Vibrato Frequency = 29
- Vibrato Gain = 1
- Breath Pressure = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Saxofony.h"
#include <string.h>
#include "SKINI.msg"
Saxofony :: Saxofony(MY_FLOAT lowestFrequency)
{
length = (long) (Stk::sampleRate() / lowestFrequency + 1);
// Initialize blowing position to 0.2 of length / 2.
position = 0.2;
delays[0] = (DelayL *) new DelayL( (1.0-position) * (length >> 1), length );
delays[1] = (DelayL *) new DelayL( position * (length >> 1), length );
reedTable = new ReedTabl;
reedTable->setOffset((MY_FLOAT) 0.7);
reedTable->setSlope((MY_FLOAT) 0.3);
filter = new OneZero;
envelope = new Envelope;
noise = new Noise;
// Concatenate the STK RAWWAVE_PATH to the rawwave file
char path[128];
strcpy(path, RAWWAVE_PATH);
vibrato = new WaveLoop( strcat(path,"rawwaves/sinewave.raw"), TRUE );
vibrato->setFrequency((MY_FLOAT) 5.735);
outputGain = (MY_FLOAT) 0.3;
noiseGain = (MY_FLOAT) 0.2;
vibratoGain = (MY_FLOAT) 0.1;
}
Saxofony :: ~Saxofony()
{
delete delays[0];
delete delays[1];
delete reedTable;
delete filter;
delete envelope;
delete noise;
delete vibrato;
}
void Saxofony :: clear()
{
delays[0]->clear();
delays[1]->clear();
filter->tick((MY_FLOAT) 0.0);
}
void Saxofony :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "Saxofony: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
MY_FLOAT delay = (Stk::sampleRate() / freakency) - (MY_FLOAT) 3.0;
if (delay <= 0.0) delay = 0.3;
else if (delay > length) delay = length;
delays[0]->setDelay((1.0-position) * delay);
delays[1]->setDelay(position * delay);
}
void Saxofony :: setBlowPosition(MY_FLOAT aPosition)
{
if (position == aPosition) return;
if (aPosition < 0.0) position = 0.0;
else if (aPosition > 1.0) position = 1.0;
else position = aPosition;
MY_FLOAT total_delay = delays[0]->getDelay();
total_delay += delays[1]->getDelay();
delays[0]->setDelay((1.0-position) * total_delay);
delays[1]->setDelay(position * total_delay);
}
void Saxofony :: startBlowing(MY_FLOAT amplitude, MY_FLOAT rate)
{
envelope->setRate(rate);
envelope->setTarget(amplitude);
}
void Saxofony :: stopBlowing(MY_FLOAT rate)
{
envelope->setRate(rate);
envelope->setTarget((MY_FLOAT) 0.0);
}
void Saxofony :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
setFrequency(frequency);
startBlowing((MY_FLOAT) 0.55 + (amplitude * 0.30), amplitude * 0.005);
outputGain = amplitude + 0.001;
#if defined(_STK_DEBUG_)
cerr << "Saxofony: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Saxofony :: noteOff(MY_FLOAT amplitude)
{
this->stopBlowing(amplitude * 0.01);
#if defined(_STK_DEBUG_)
cerr << "Saxofony: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT Saxofony :: tick()
{
MY_FLOAT pressureDiff;
MY_FLOAT breathPressure;
MY_FLOAT temp;
// Calculate the breath pressure (envelope + noise + vibrato)
breathPressure = envelope->tick();
breathPressure += breathPressure * noiseGain * noise->tick();
breathPressure += breathPressure * vibratoGain * vibrato->tick();
temp = -0.95 * filter->tick( delays[0]->lastOut() );
lastOutput = temp - delays[1]->lastOut();
pressureDiff = breathPressure - lastOutput;
delays[1]->tick(temp);
delays[0]->tick(breathPressure - (pressureDiff * reedTable->tick(pressureDiff)) - temp);
lastOutput *= outputGain;
return lastOutput;
}
void Saxofony :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Saxofony: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Saxofony: Control value greater than 128.0!" << endl;
}
if (number == __SK_ReedStiffness_) // 2
reedTable->setSlope( 0.1 + (0.4 * norm) );
else if (number == __SK_NoiseLevel_) // 4
noiseGain = ( norm * 0.4 );
else if (number == 29) // 29
vibrato->setFrequency( norm * 12.0 );
else if (number == __SK_ModWheel_) // 1
vibratoGain = ( norm * 0.5 );
else if (number == __SK_AfterTouch_Cont_) // 128
envelope->setValue( norm );
else if (number == 11) // 11
this->setBlowPosition( norm );
else if (number == 26) // reed table offset
reedTable->setOffset(0.4 + ( norm * 0.6));
else
cerr << "Saxofony: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Saxofony: controlChange number = " << number << ", value = " << value << endl;
#endif
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,41 @@
/*******************************************/
/* 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 */
/*******************************************/
/***************************************************/
/*! \class Simple
\brief STK wavetable/noise instrument.
This class combines a looped wave, a
noise source, a biquad resonance filter,
a one-pole filter, and an ADSR envelope
to create some interesting sounds.
Control Change Numbers:
- Filter Pole Position = 2
- Noise/Pitched Cross-Fade = 4
- Envelope Rate = 11
- Gain = 128
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "Simple.h"
#include "SKINI.msg"
#include <string.h>
Simple :: Simple()
{
MY_FLOAT coeffs[2];
adsr = new ADSR;
baseFreq = (MY_FLOAT) 440.0;
baseFrequency = (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;
loop = new WaveLoop( strcat(file,"rawwaves/impuls10.raw"), TRUE );
filter = new OnePole(0.5);
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);
biquad = new BiQuad();
setFrequency(baseFrequency);
loopGain = 0.5;
}
@@ -35,8 +44,7 @@ Simple :: ~Simple()
delete adsr;
delete loop;
delete filter;
delete bqzeroes;
delete bqpoles;
delete biquad;
}
void Simple :: keyOn()
@@ -49,66 +57,69 @@ void Simple :: keyOff()
adsr->keyOff();
}
void Simple :: noteOn(MY_FLOAT freq, MY_FLOAT amp)
void Simple :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
this->keyOn();
this->setFreq(freq);
filter->setGain(amp);
#if defined(_debug_)
printf("Simple : NoteOn: Freq= %lf, Amp=%lf\n",freq, amp);
#endif
keyOn();
setFrequency(frequency);
filter->setGain(amplitude);
#if defined(_STK_DEBUG_)
cerr << "Simple: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Simple :: noteOff(MY_FLOAT amplitude)
{
this->keyOff();
#if defined(_debug_)
printf("Simple : NoteOff: Amp=%lf\n",amplitude);
#endif
keyOff();
#if defined(_STK_DEBUG_)
cerr << "Simple: NoteOff amplitude = " << amplitude << endl;
#endif
}
void Simple :: setFreq(MY_FLOAT frequency)
void Simple :: setFrequency(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);
biquad->setResonance( frequency, 0.98, true );
loop->setFrequency(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);
biquad->tick( noise->tick() );
lastOutput += (1.0 - loopGain) * biquad->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);
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "Clarinet: Control value less than zero!" << endl;
}
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");
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "Clarinet: Control value greater than 128.0!" << endl;
}
if (number == __SK_Breath_) // 2
filter->setPole( 0.99 * (1.0 - (norm * 2.0)) );
else if (number == __SK_NoiseLevel_) // 4
loopGain = norm;
else if (number == __SK_ModFrequency_) { // 11
norm /= 0.2 * Stk::sampleRate();
adsr->setAttackRate( norm );
adsr->setDecayRate( norm );
adsr->setReleaseRate( norm );
}
else if (number == __SK_AfterTouch_Cont_) // 128
adsr->setTarget( norm );
else
cerr << "Simple: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "Simple: controlChange number = " << number << ", value = " << value << endl;
#endif
}

View File

@@ -1,260 +0,0 @@
/*******************************************/
/* "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);
}
*/

107
src/Sitar.cpp Normal file
View File

@@ -0,0 +1,107 @@
/******************************************/
/* Karplus-Strong Sitar1 string model */
/* by Perry Cook, 1995-96 */
/* */
/* There exist at least two patents, */
/* assigned to Stanford, bearing the */
/* names of Karplus and/or Strong. */
/******************************************/
#include "Sitar.h"
#include <math.h>
Sitar :: Sitar(MY_FLOAT lowestFrequency)
{
length = (long) (Stk::sampleRate() / lowestFrequency + 1);
loopGain = (MY_FLOAT) 0.999;
delayLine = new DelayA( (MY_FLOAT)(length / 2.0), length );
delay = length / 2.0;
targetDelay = delay;
loopFilter = new OneZero;
loopFilter->setZero(0.01);
envelope = new ADSR();
envelope->setAllTimes(0.001, 0.04, 0.0, 0.5);
noise = new Noise;
this->clear();
}
Sitar :: ~Sitar()
{
delete delayLine;
delete loopFilter;
delete noise;
delete envelope;
}
void Sitar :: clear()
{
delayLine->clear();
loopFilter->clear();
}
void Sitar :: setFrequency(MY_FLOAT frequency)
{
MY_FLOAT freakency = frequency;
if ( frequency <= 0.0 ) {
cerr << "Sitar: setFrequency parameter is less than or equal to zero!" << endl;
freakency = 220.0;
}
targetDelay = (Stk::sampleRate() / freakency);
delay = targetDelay * (1.0 + (0.05 * noise->tick()));
delayLine->setDelay(delay);
loopGain = 0.995 + (freakency * 0.0000005);
if (loopGain > 0.9995) loopGain = 0.9995;
}
void Sitar :: pluck(MY_FLOAT amplitude)
{
envelope->keyOn();
}
void Sitar :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
setFrequency(frequency);
pluck(amplitude);
amGain = 0.1 * amplitude;
#if defined(_STK_DEBUG_)
cerr << "Sitar: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void Sitar :: noteOff(MY_FLOAT amplitude)
{
loopGain = (MY_FLOAT) 1.0 - amplitude;
if ( loopGain < 0.0 ) {
cerr << "Plucked: noteOff amplitude greater than 1.0!" << endl;
loopGain = 0.0;
}
else if ( loopGain > 1.0 ) {
cerr << "Plucked: noteOff amplitude less than or zero!" << endl;
loopGain = (MY_FLOAT) 0.99999;
}
#if defined(_STK_DEBUG_)
cerr << "Plucked: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT Sitar :: tick()
{
if ( fabs(targetDelay - delay) > 0.001 ) {
if (targetDelay < delay)
delay *= 0.99999;
else
delay *= 1.00001;
delayLine->setDelay(delay);
}
lastOutput = delayLine->tick( loopFilter->tick( delayLine->lastOut() * loopGain ) +
(amGain * envelope->tick() * noise->tick()));
return lastOutput;
}

View File

@@ -1,172 +0,0 @@
/*******************************************/
/* 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;
}
}

View File

@@ -1,121 +0,0 @@
/***********************************************/
/*
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;
}
}

240
src/Socket.cpp Normal file
View File

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

227
src/StifKarp.cpp Normal file
View File

@@ -0,0 +1,227 @@
/***************************************************/
/*! \class StifKarp
\brief STK plucked stiff string instrument.
This class implements a simple plucked string
algorithm (Karplus Strong) with enhancements
(Jaffe-Smith, Smith, and others), including
string stiffness and pluck position controls.
The stiffness is modeled with allpass filters.
This is a digital waveguide model, making its
use possibly subject to patents held by
Stanford University, Yamaha, and others.
Control Change Numbers:
- Pickup Position = 4
- String Sustain = 11
- String Stretch = 1
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "StifKarp.h"
#include "SKINI.msg"
#include <string.h>
#include <math.h>
StifKarp :: StifKarp(MY_FLOAT lowestFrequency)
{
length = (long) (Stk::sampleRate() / lowestFrequency + 1);
delayLine = new DelayA(0.5 * length, length);
combDelay = new DelayL( 0.2 * length, length);
filter = new OneZero();
noise = new Noise();
biQuad[0] = new BiQuad();
biQuad[1] = new BiQuad();
biQuad[2] = new BiQuad();
biQuad[3] = new BiQuad();
pluckAmplitude = 0.3;
pickupPosition = (MY_FLOAT) 0.4;
lastFrequency = lowestFrequency * 2.0;
lastLength = length * 0.5;
stretching = 0.9999;
baseLoopGain = 0.995;
loopGain = 0.999;
clear();
}
StifKarp :: ~StifKarp()
{
delete delayLine;
delete combDelay;
delete filter;
delete noise;
delete biQuad[0];
delete biQuad[1];
delete biQuad[2];
delete biQuad[3];
}
void StifKarp :: clear()
{
delayLine->clear();
combDelay->clear();
filter->clear();
}
void StifKarp :: setFrequency(MY_FLOAT frequency)
{
lastFrequency = frequency;
if ( frequency <= 0.0 ) {
cerr << "StifKarp: setFrequency parameter is less than or equal to zero!" << endl;
lastFrequency = 220.0;
}
lastLength = Stk::sampleRate() / lastFrequency;
MY_FLOAT delay = lastLength - 0.5;
if (delay <= 0.0) delay = 0.3;
else if (delay > length) delay = length;
delayLine->setDelay( delay );
loopGain = baseLoopGain + (frequency * (MY_FLOAT) 0.000005);
if (loopGain >= 1.0) loopGain = (MY_FLOAT) 0.99999;
setStretch(stretching);
combDelay->setDelay((MY_FLOAT) 0.5 * pickupPosition * lastLength);
}
void StifKarp :: setStretch(MY_FLOAT stretch)
{
stretching = stretch;
MY_FLOAT coefficient;
MY_FLOAT freq = lastFrequency * 2.0;
MY_FLOAT dFreq = ( (0.5 * Stk::sampleRate()) - freq ) * 0.25;
MY_FLOAT temp = 0.5 + (stretch * 0.5);
if (temp > 0.9999) temp = 0.9999;
for (int i=0; i<4; i++) {
coefficient = temp * temp;
biQuad[i]->setA2( coefficient );
biQuad[i]->setB0( coefficient );
biQuad[i]->setB2( 1.0 );
coefficient = -2.0 * temp * cos(TWO_PI * freq / Stk::sampleRate());
biQuad[i]->setA1( coefficient );
biQuad[i]->setB1( coefficient );
freq += dFreq;
}
}
void StifKarp :: setPickupPosition(MY_FLOAT position) {
pickupPosition = position;
if ( position < 0.0 ) {
cerr << "StifKarp: setPickupPosition parameter is less than zero!" << endl;
pickupPosition = 0.0;
}
else if ( position > 1.0 ) {
cerr << "StifKarp: setPickupPosition parameter is greater than 1.0!" << endl;
pickupPosition = 1.0;
}
// Set the pick position, which puts zeroes at position * length.
combDelay->setDelay(0.5 * pickupPosition * lastLength);
}
void StifKarp :: setBaseLoopGain(MY_FLOAT aGain)
{
baseLoopGain = aGain;
loopGain = baseLoopGain + (lastFrequency * 0.000005);
if ( loopGain > 0.99999 ) loopGain = (MY_FLOAT) 0.99999;
}
void StifKarp :: pluck(MY_FLOAT amplitude)
{
MY_FLOAT gain = amplitude;
if ( gain > 1.0 ) {
cerr << "StifKarp: pluck amplitude greater than 1.0!" << endl;
gain = 1.0;
}
else if ( gain < 0.0 ) {
cerr << "StifKarp: pluck amplitude less than zero!" << endl;
gain = 0.0;
}
pluckAmplitude = amplitude;
for (long i=0; i<length; i++) {
// Fill delay with noise additively with current contents.
delayLine->tick((delayLine->lastOut() * 0.6) + 0.4 * noise->tick() * pluckAmplitude);
//delayLine->tick( combDelay->tick((delayLine->lastOut() * 0.6) + 0.4 * noise->tick() * pluckAmplitude));
}
}
void StifKarp :: noteOn(MY_FLOAT frequency, MY_FLOAT amplitude)
{
this->setFrequency(frequency);
this->pluck(amplitude);
#if defined(_STK_DEBUG_)
cerr << "StifKarp: NoteOn frequency = " << frequency << ", amplitude = " << amplitude << endl;
#endif
}
void StifKarp :: noteOff(MY_FLOAT amplitude)
{
MY_FLOAT gain = amplitude;
if ( gain > 1.0 ) {
cerr << "StifKarp: noteOff amplitude greater than 1.0!" << endl;
gain = 1.0;
}
else if ( gain < 0.0 ) {
cerr << "StifKarp: noteOff amplitude less than zero!" << endl;
gain = 0.0;
}
loopGain = (1.0 - gain) * 0.5;
#if defined(_STK_DEBUG_)
cerr << "StifPluck: NoteOff amplitude = " << amplitude << endl;
#endif
}
MY_FLOAT StifKarp :: tick()
{
MY_FLOAT temp = delayLine->lastOut() * loopGain;
// Calculate allpass stretching.
for (int i=0; i<4; i++)
temp = biQuad[i]->tick(temp);
// Moving average filter.
temp = filter->tick(temp);
lastOutput = delayLine->tick(temp);
lastOutput = lastOutput - combDelay->tick(lastOutput);
return lastOutput;
}
void StifKarp :: controlChange(int number, MY_FLOAT value)
{
MY_FLOAT norm = value * ONE_OVER_128;
if ( norm < 0 ) {
norm = 0.0;
cerr << "StifKarp: Control value less than zero!" << endl;
}
else if ( norm > 1.0 ) {
norm = 1.0;
cerr << "StifKarp: Control value greater than 128.0!" << endl;
}
if (number == __SK_PickPosition_) // 4
setPickupPosition( norm );
else if (number == __SK_StringDamping_) // 11
setBaseLoopGain( 0.97 + (norm * 0.03) );
else if (number == __SK_StringDetune_) // 1
setStretch( 0.9 + (0.1 * (1.0 - norm)) );
else
cerr << "StifKarp: Undefined Control Number (" << number << ")!!" << endl;
#if defined(_STK_DEBUG_)
cerr << "StifKarp: controlChange number = " << number << ", value = " << value << endl;
#endif
}

143
src/Stk.cpp Normal file
View File

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

View File

@@ -1,28 +0,0 @@
/*************************************************/
/*
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);
}

View File

@@ -1,389 +0,0 @@
/******************************************/
/*
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;
}

View File

@@ -1,168 +0,0 @@
/******************************************/
/*
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

View File

@@ -1,52 +1,43 @@
/*******************************************/
/* 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());
}
*/
/***************************************************/
/*! \class SubNoise
\brief STK sub-sampled noise generator.
Generates a new random number every "rate" ticks
using the C rand() function. The quality of the
rand() function varies from one OS to another.
by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/
#include "SubNoise.h"
SubNoise :: SubNoise(int subRate) : Noise()
{
rate = subRate;
counter = rate;
}
SubNoise :: ~SubNoise()
{
}
int SubNoise :: subRate(void) const
{
return rate;
}
void SubNoise :: setRate(int subRate)
{
if (subRate > 0)
rate = subRate;
}
MY_FLOAT SubNoise :: tick()
{
if ( ++counter > rate ) {
Noise::tick();
counter = 1;
}
return lastOutput;
}

Some files were not shown because too many files have changed in this diff Show More