mirror of
https://github.com/thestk/stk
synced 2026-01-20 07:51:53 +00:00
Version 4.0
This commit is contained in:
committed by
Stephen Sinclair
parent
3f126af4e5
commit
81475b04c5
387
src/ADSR.cpp
387
src/ADSR.cpp
@@ -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;
|
||||
}
|
||||
|
||||
209
src/AifWvIn.cpp
209
src/AifWvIn.cpp
@@ -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;
|
||||
}
|
||||
}
|
||||
192
src/AifWvOut.cpp
192
src/AifWvOut.cpp
@@ -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
342
src/BandedWG.cpp
Normal 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
|
||||
}
|
||||
|
||||
|
||||
163
src/BeeThree.cpp
163
src/BeeThree.cpp
@@ -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;
|
||||
}
|
||||
|
||||
232
src/BiQuad.cpp
232
src/BiQuad.cpp
@@ -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
158
src/BlowBotl.cpp
Normal 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
|
||||
}
|
||||
209
src/BlowHole.cpp
209
src/BlowHole.cpp
@@ -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
|
||||
}
|
||||
|
||||
115
src/BowTabl.cpp
115
src/BowTabl.cpp
@@ -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;
|
||||
}
|
||||
|
||||
208
src/Bowed.cpp
208
src/Bowed.cpp
@@ -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
|
||||
}
|
||||
|
||||
314
src/BowedBar.cpp
314
src/BowedBar.cpp
@@ -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");
|
||||
}
|
||||
}
|
||||
182
src/Brass.cpp
182
src/Brass.cpp
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
106
src/Chorus.cpp
Normal 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;
|
||||
}
|
||||
295
src/Clarinet.cpp
295
src/Clarinet.cpp
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
107
src/DLineA.cpp
107
src/DLineA.cpp
@@ -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
|
||||
}
|
||||
172
src/DLineL.cpp
172
src/DLineL.cpp
@@ -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;
|
||||
}
|
||||
158
src/DLineN.cpp
158
src/DLineN.cpp
@@ -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
159
src/Delay.cpp
Normal 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
117
src/DelayA.cpp
Normal 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
107
src/DelayL.cpp
Normal 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];
|
||||
}
|
||||
183
src/DrumSynt.cpp
183
src/DrumSynt.cpp
@@ -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
181
src/Drummer.cpp
Normal 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
79
src/Echo.cpp
Normal 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;
|
||||
}
|
||||
236
src/Envelope.cpp
236
src/Envelope.cpp
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
67
src/FIR.cpp
67
src/FIR.cpp
@@ -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
221
src/FM.cpp
Normal 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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
179
src/FM4Op.cpp
179
src/FM4Op.cpp
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
233
src/FMVoices.cpp
233
src/FMVoices.cpp
@@ -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
|
||||
}
|
||||
|
||||
|
||||
268
src/Filter.cpp
268
src/Filter.cpp
@@ -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;
|
||||
}
|
||||
|
||||
215
src/Flute.cpp
215
src/Flute.cpp
@@ -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
|
||||
}
|
||||
|
||||
257
src/FormSwep.cpp
257
src/FormSwep.cpp
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
113
src/HevyMetl.cpp
Normal 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;
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
211
src/JCRev.cpp
211
src/JCRev.cpp
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
410
src/MD2SKINI.cpp
410
src/MD2SKINI.cpp
@@ -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
|
||||
105
src/Makefile
105
src/Makefile
@@ -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)
|
||||
|
||||
235
src/Mandolin.cpp
235
src/Mandolin.cpp
@@ -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
|
||||
}
|
||||
|
||||
235
src/MatWvIn.cpp
235
src/MatWvIn.cpp
@@ -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;
|
||||
}
|
||||
}
|
||||
196
src/MatWvOut.cpp
196
src/MatWvOut.cpp
@@ -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
388
src/Mesh2D.cpp
Normal 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
385
src/Messager.cpp
Normal 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
225
src/Modal.cpp
Normal 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;
|
||||
}
|
||||
202
src/Modal4.cpp
202
src/Modal4.cpp
@@ -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;
|
||||
}
|
||||
|
||||
185
src/ModalBar.cpp
185
src/ModalBar.cpp
@@ -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
71
src/Modulate.cpp
Normal 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;
|
||||
}
|
||||
@@ -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
155
src/Moog.cpp
Normal 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
|
||||
}
|
||||
|
||||
|
||||
|
||||
111
src/Moog1.cpp
111
src/Moog1.cpp
@@ -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;
|
||||
}
|
||||
|
||||
183
src/NRev.cpp
183
src/NRev.cpp
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
200
src/OnePole.cpp
200
src/OnePole.cpp
@@ -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;
|
||||
}
|
||||
|
||||
163
src/OneZero.cpp
163
src/OneZero.cpp
@@ -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;
|
||||
}
|
||||
|
||||
142
src/PRCRev.cpp
142
src/PRCRev.cpp
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
159
src/PercFlut.cpp
159
src/PercFlut.cpp
@@ -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
90
src/PitShift.cpp
Normal 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
127
src/PluckTwo.cpp
Normal 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
|
||||
}
|
||||
|
||||
121
src/Plucked.cpp
121
src/Plucked.cpp
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
165
src/PoleZero.cpp
165
src/PoleZero.cpp
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
134
src/RawWvIn.cpp
134
src/RawWvIn.cpp
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
122
src/ReedTabl.cpp
122
src/ReedTabl.cpp
@@ -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
150
src/Resonate.cpp
Normal 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
|
||||
}
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
150
src/Rhodey.cpp
150
src/Rhodey.cpp
@@ -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;
|
||||
}
|
||||
|
||||
6811
src/RtAudio.cpp
6811
src/RtAudio.cpp
File diff suppressed because it is too large
Load Diff
255
src/RtDuplex.cpp
255
src/RtDuplex.cpp
@@ -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;
|
||||
}
|
||||
|
||||
2088
src/RtMidi.cpp
2088
src/RtMidi.cpp
File diff suppressed because it is too large
Load Diff
267
src/RtWvIn.cpp
267
src/RtWvIn.cpp
@@ -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 );
|
||||
}
|
||||
|
||||
206
src/RtWvOut.cpp
206
src/RtWvOut.cpp
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
130
src/SKINI11.tbl
130
src/SKINI11.tbl
@@ -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. */
|
||||
/* */
|
||||
/*************************************************************************/
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
@@ -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
203
src/Saxofony.cpp
Normal 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
|
||||
|
||||
}
|
||||
1967
src/Shakers.cpp
1967
src/Shakers.cpp
File diff suppressed because it is too large
Load Diff
137
src/Simple.cpp
137
src/Simple.cpp
@@ -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
|
||||
}
|
||||
|
||||
260
src/SingWave.cpp
260
src/SingWave.cpp
@@ -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
107
src/Sitar.cpp
Normal 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;
|
||||
}
|
||||
172
src/SndWvIn.cpp
172
src/SndWvIn.cpp
@@ -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;
|
||||
}
|
||||
}
|
||||
121
src/SndWvOut.cpp
121
src/SndWvOut.cpp
@@ -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
240
src/Socket.cpp
Normal 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
227
src/StifKarp.cpp
Normal 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
143
src/Stk.cpp
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
389
src/StrmWvIn.cpp
389
src/StrmWvIn.cpp
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user