SleepIsDeath/gameSource/Timbre.cpp

235 lines
5.8 KiB
C++

#include "Timbre.h"
#include "minorGems/util/stringUtils.h"
#include "minorGems/util/log/AppLog.h"
#include <math.h>
#include <stdio.h>
double twelthRootOfTwo = pow( 2, 1.0/12 );
// #define SCALE_SIZE 7
// for major scale
// W, W, H, W, W, W, H
//int halfstepMap[ SCALE_SIZE ] = { 0, 2, 4, 5, 7, 9, 11 };
// minor scale
// W,H,W,W,H,W,W
//int halfstepMap[ SCALE_SIZE ] = { 0, 2, 3, 5, 7, 8, 10 };
#define MAX_SCALE_SIZE 12
int halfstepMap[ MAX_SCALE_SIZE ] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
int usedScaleNotes = 5;
void setDefaultScale() {
usedScaleNotes = 5;
// major pentatonic
//int halfstepMap[ SCALE_SIZE ] = { 0, 2, 4, 7, 9 };
// minor pentatonic
//int halfstepMap[ SCALE_SIZE ] = { 0, 3, 5, 7, 10 };
halfstepMap[0] = 0;
halfstepMap[1] = 3;
halfstepMap[2] = 5;
halfstepMap[3] = 7;
halfstepMap[4] = 10;
}
void setTimbreScale( char inToneOn[12] ) {
usedScaleNotes = 0;
int nextStep = 0;
for( int i=0; i<12; i++ ) {
if( inToneOn[i] ) {
halfstepMap[nextStep] = i;
usedScaleNotes ++;
nextStep++;
}
}
printf( "halfstep map= " );
for( int i=0; i<usedScaleNotes; i++ ) {
printf( "%d ", halfstepMap[i] );
}
printf( "\n" );
}
// gets frequency of note in our scale
double getFrequency( double inBaseFrequency, int inScaleNoteNumber ) {
if( usedScaleNotes == 0 ) {
return inBaseFrequency;
}
int octavesUp = inScaleNoteNumber / usedScaleNotes;
int numHalfsteps =
halfstepMap[ inScaleNoteNumber % usedScaleNotes ] + octavesUp * 12;
return inBaseFrequency * pow( twelthRootOfTwo, numHalfsteps );
}
/*
Was used during testing
#include "minorGems/sound/formats/aiff.h"
int outputFileNumber = 0;
// outputs a wave table as an AIFF
void outputWaveTable( Sint16 *inTable, int inLength, int inSampleRate ) {
// generate the header
int headerSize;
unsigned char *aiffHeader =
getAIFFHeader( 1,
16,
inSampleRate,
inLength,
&headerSize );
char *fileName = autoSprintf( "waveTable%d.aiff", outputFileNumber );
outputFileNumber++;
FILE *aiffFile = fopen( fileName, "wb" );
delete [] fileName;
//printf( "Header size = %d\n", headerSize );
fwrite( aiffHeader, 1, headerSize, aiffFile );
delete [] aiffHeader;
for( int i=0; i<inLength; i++ ) {
Sint16 val = inTable[i];
unsigned char msb = val >> 8 & 0xFF;
unsigned char lsb = val && 0xFF;
fwrite( &msb, 1, 1, aiffFile );
fwrite( &lsb, 1, 1, aiffFile );
}
fclose( aiffFile );
}
*/
Timbre::Timbre( int inSampleRate,
double inLoudness,
double inBaseFrequency,
int inNumWaveTableEntries,
double( *inWaveFunction )( double ),
int inPeriodsPerTable )
: mNumWaveTableEntries( inNumWaveTableEntries ),
mWaveTable( new Sint16*[ inNumWaveTableEntries ] ),
mWaveTableLengths( new int[ inNumWaveTableEntries ] ),
mActiveNoteCount( 0 ) {
// build wave table for each possible pitch in image
for( int i=0; i<mNumWaveTableEntries; i++ ) {
double freq = getFrequency( inBaseFrequency, i );
if( freq > inSampleRate / 2 ) {
// cap at Nyquist limit
freq = inSampleRate / 2;
}
double period = 1.0 / freq;
int tableLength = (int)(
rint( inPeriodsPerTable * period * inSampleRate ) );
if( tableLength == 0 ) {
AppLog::getLog()->logPrintf( Log::CRITICAL_ERROR_LEVEL,
"table length 0 for freq %f\n",
freq );
}
mWaveTableLengths[i] = tableLength;
mWaveTable[i] = new Sint16[ tableLength ];
// store double samples in temp table so we can compute
// max value for normalization
double *tempTable = new double[ tableLength ];
double maxValue = 0;
int s;
for( s=0; s<tableLength; s++ ) {
//double t = (double)s / (double)inSampleRate;
//double waveValue = inWaveFunction( 2 * M_PI * t * freq );
// base t on table length to ensure a perfect set of periods
// in our table. Otherwise, we hear clicks when table is looped
double t = (double)s / (double)(tableLength);
double waveValue =
inWaveFunction( 2 * M_PI * t * inPeriodsPerTable );
tempTable[s] = waveValue;
// track max value
if( waveValue > maxValue ) {
maxValue = waveValue;
}
else if( -waveValue > maxValue ) {
maxValue = -waveValue;
}
}
//printf( "max value = %f\n", maxValue );
// now normalize and convert to int
for( s=0; s<tableLength; s++ ) {
double waveValue = tempTable[s] * inLoudness / maxValue;
// convert to int
mWaveTable[i][s] = (Sint16)( 32767 * waveValue );
}
delete [] tempTable;
mWaveTableLengths[i] = tableLength;
// to examine waveforms for testing
// outputWaveTable( mWaveTable[i], tableLength, inSampleRate );
}
}
Timbre::~Timbre() {
delete [] mWaveTableLengths;
for( int i=0; i<mNumWaveTableEntries; i++ ) {
delete [] mWaveTable[i];
}
delete [] mWaveTable;
}