From 1aeca6838b1ba5405674d0f5e38174fe6dedd138 Mon Sep 17 00:00:00 2001 From: Jerome Fisher Date: Sun, 14 Nov 2004 04:13:15 +0000 Subject: MT32 MidiDriver: - Cleanup MT32Emu: - Lots more cleanup. - Properly implemented pitch bending (not fast, but theoretically perfect). - Full position delta is now calculated before PCM interpolation/decimation is performed, so that pitch modifiers will be considered. - Now reports when using SSE or 3DNow, and when the samplerate is invalid. svn-id: r15801 --- backends/midi/mt32.cpp | 12 +-- backends/midi/mt32/i386.cpp | 4 +- backends/midi/mt32/i386.h | 4 +- backends/midi/mt32/mt32emu.h | 36 ++++++++- backends/midi/mt32/part.cpp | 159 ++++++++++++++++++------------------ backends/midi/mt32/part.h | 44 +++++----- backends/midi/mt32/partial.cpp | 175 +++++++++++++++++++++++----------------- backends/midi/mt32/partial.h | 16 +++- backends/midi/mt32/structures.h | 28 +++---- backends/midi/mt32/synth.cpp | 118 ++++++++++++++------------- backends/midi/mt32/synth.h | 53 ++++++------ backends/midi/mt32/tables.cpp | 70 +++++++--------- backends/midi/mt32/tables.h | 64 ++++++++------- 13 files changed, 419 insertions(+), 364 deletions(-) (limited to 'backends/midi') diff --git a/backends/midi/mt32.cpp b/backends/midi/mt32.cpp index 28f331e694..d9dbf3c9ee 100644 --- a/backends/midi/mt32.cpp +++ b/backends/midi/mt32.cpp @@ -179,12 +179,12 @@ int MidiDriver_MT32::open() { MidiDriver_Emulated::open(); memset(&prop, 0, sizeof(prop)); - prop.SampleRate = getRate(); - prop.UseReverb = true; - prop.UseDefault = false; - prop.RevType = 0; - prop.RevTime = 5; - prop.RevLevel = 3; + prop.sampleRate = getRate(); + prop.useReverb = true; + prop.useDefaultReverb = false; + prop.reverbType = 0; + prop.reverbTime = 5; + prop.reverbLevel = 3; prop.printDebug = MT32_PrintDebug; prop.report = MT32_Report; prop.openFile = MT32_OpenFile; diff --git a/backends/midi/mt32/i386.cpp b/backends/midi/mt32/i386.cpp index 9bc5bcb4c5..e2e4b0f790 100644 --- a/backends/midi/mt32/i386.cpp +++ b/backends/midi/mt32/i386.cpp @@ -21,7 +21,7 @@ #include "mt32emu.h" -#ifdef HAVE_X86 +#ifdef MT32EMU_HAVE_X86 namespace MT32Emu { @@ -628,7 +628,7 @@ float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLev return output; } -#if USE_MMX > 0 +#if MT32EMU_USE_MMX > 0 int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf) { int tmplen = len >> 1; diff --git a/backends/midi/mt32/i386.h b/backends/midi/mt32/i386.h index 544a7bf6da..c26fcb4110 100644 --- a/backends/midi/mt32/i386.h +++ b/backends/midi/mt32/i386.h @@ -23,7 +23,7 @@ #define MT32EMU_I386_H namespace MT32Emu { -#ifdef HAVE_X86 +#ifdef MT32EMU_HAVE_X86 // Function that detects the availablity of SSE SIMD instructions bool DetectSIMD(); @@ -34,7 +34,7 @@ float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr, int revLevel float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel); float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel); -#if USE_MMX > 0 +#if MT32EMU_USE_MMX > 0 int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf); int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len); int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len); diff --git a/backends/midi/mt32/mt32emu.h b/backends/midi/mt32/mt32emu.h index c31afee389..bf4648c905 100644 --- a/backends/midi/mt32/mt32emu.h +++ b/backends/midi/mt32/mt32emu.h @@ -22,15 +22,49 @@ #ifndef MT32EMU_MT32EMU_H #define MT32EMU_MT32EMU_H +// Debugging +// Show the instruments played +#define MT32EMU_MONITOR_INSTRUMENTS 1 +// Shows number of partials MT-32 is playing, and on which parts +#define MT32EMU_MONITOR_PARTIALS 0 +// Dump drum patches to syx file for viewing +#define MT32EMU_DRUMP_DRUMS 0 +// Output benchmarks for the filter implementations +#define MT32EMU_BENCHMARK_FILTERS 0 +// Determines how the waveform cache file is handled (must be regenerated after sampling rate change) +#define MT32EMU_WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache +//#define MT32EMU_WAVECACHEMODE 1 // Load existing cache if possible, otherwise generage but don't save cache +//#define MT32EMU_WAVECACHEMODE 2 // Ignore existing cache, generate and save cache +//#define MT32EMU_WAVECACHEMODE 3 // Ignore existing cache, generate but don't save cache + +// Configuration +// The maximum number of partials playing simultaneously +#define MT32EMU_MAX_PARTIALS 32 +// The maximum number of notes playing simultaneously per part. +// No point making it more than MT32EMU_MAX_PARTIALS, since each note needs at least one partial. +#define MT32EMU_MAX_POLY 32 +// This calculates the exact frequencies of notes as they are played, instead of offsetting from pre-cached semitones. Potentially very slow. +#define MT32EMU_ACCURATENOTES 0 + +#if (defined (_MSC_VER) && defined(_M_IX86)) || (defined(__GNUC__) && defined(__i386__)) +#define MT32EMU_HAVE_X86 +#endif + +#ifdef MT32EMU_HAVE_X86 +#define MT32EMU_USE_MMX 1 +#else +#define MT32EMU_USE_MMX 0 +#endif + #include "freeverb.h" #include "structures.h" #include "i386.h" #include "mt32_file.h" +#include "tables.h" #include "partial.h" #include "partialManager.h" #include "part.h" -#include "tables.h" #include "synth.h" #endif diff --git a/backends/midi/mt32/part.cpp b/backends/midi/mt32/part.cpp index cfb40f3d50..bc5f7d8939 100644 --- a/backends/midi/mt32/part.cpp +++ b/backends/midi/mt32/part.cpp @@ -24,10 +24,6 @@ #include "mt32emu.h" -// Debugging stuff -// Shows the instruments played -#define DISPLAYINSTR 1 - namespace MT32Emu { static const Bit8u PartialStruct[13] = { @@ -38,12 +34,9 @@ static const Bit8u PartialMixStruct[13] = { 0, 1, 0, 1, 1, 0, 1, 3, 3, 2, 2, 2, 2 }; -static const Bit32u drumBend = 0x1000; - // This caches the timbres/settings in use by the rhythm part static PatchCache drumCache[94][4]; - -static volset drumPan[64]; +static StereoVolume drumPan[64]; //FIXME:KG: Put this dpoly stuff somewhere better bool dpoly::isActive() { @@ -76,39 +69,49 @@ Part::Part(Synth *useSynth, int usePartNum) { rhythmTemp = NULL; } currentInstr[0] = 0; + currentInstr[10] = 0; volume = 102; volumesetting.leftvol = 32767; volumesetting.rightvol = 32767; - bend = 0x1000; + bend = 0.0f; memset(polyTable,0,sizeof(polyTable)); memset(patchCache, 0, sizeof(patchCache)); if (isRhythm) { init = true; - RefreshDrumCache(); + refreshDrumCache(); } init = false; } -void Part::SetHoldPedal(bool pedalval) { +void Part::setHoldPedal(bool pedalval) { if (holdpedal && !pedalval) - StopPedalHold(); + stopPedalHold(); holdpedal = pedalval; } -void Part::SetBend(int vol) { +void Part::setBend(int midiBend) { if (isRhythm) { - synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, vol); + synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend); return; } - //int tmpbend = ((vol - 0x2000) * (int)patchTemp->patch.benderRange) >> 13; - //bend = bendtable[tmpbend+24]; - - float bend_range = (float)patchTemp->patch.benderRange / 24; - bend = (Bit32u)(4096 + (vol - 8192) * bend_range); + // FIXME:KG: Slightly uneven increments, but I wanted min -1.0, centre 0.0 and max 1.0 + if (midiBend <= 0x2000) { + bend = (midiBend - 0x2000) / (float)0x2000; + } else { + bend = (midiBend - 0x2000) / (float)0x1FFF; + } + // Loop through all partials to update their bend + for (int i = 0; i < MT32EMU_MAX_POLY; i++) { + for (int j = 0; j < 4; j++) { + if (polyTable[i].partials[j] != NULL) { + polyTable[i].partials[j]->setBend(bend); + } + } + } } -void Part::SetModulation(int vol) { +void Part::setModulation(int vol) { if (isRhythm) { synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, vol); return; @@ -124,7 +127,7 @@ void Part::SetModulation(int vol) { } } -void Part::RefreshDrumCache() { +void Part::refreshDrumCache() { if (!isRhythm) { synth->printDebug("ERROR: RefreshDrumCache() called on non-rhythm part"); } @@ -133,7 +136,7 @@ void Part::RefreshDrumCache() { int drumTimbre = rhythmTemp[m].timbre; if (drumTimbre >= 94) continue; - SetPatch(drumTimbre + 128); // This is to cache all the mapped drum timbres ahead of time + setPatch(drumTimbre + 128); // This is to cache all the mapped drum timbres ahead of time Bit16s pan = rhythmTemp[m].panpot; // They use R-L 0-14... // FIXME:KG: If I don't have left/right mixed up here, it's pure luck if (pan < 7) { @@ -146,7 +149,7 @@ void Part::RefreshDrumCache() { } } -int Part::FixBiaslevel(int srcpnt, int *dir) { +int Part::fixBiaslevel(int srcpnt, int *dir) { int noteat = srcpnt & 63; int outnote; *dir = 1; @@ -158,7 +161,7 @@ int Part::FixBiaslevel(int srcpnt, int *dir) { return outnote; } -int Part::FixKeyfollow(int srckey, int *dir) { +int Part::fixKeyfollow(int srckey, int *dir) { if (srckey>=0 && srckey<=16) { //int keyfix[17] = { 256, 128, 64, 0, 32, 64, 96, 128, 128+32, 192, 192+32, 256, 256+64, 256+128, 512, 259, 269 }; int keyfix[17] = { 256*16, 128*16, 64*16, 0, 32*16, 64*16, 96*16, 128*16, (128+32)*16, 192*16, (192+32)*16, 256*16, (256+64)*16, (256+128)*16, (512)*16, 4100, 4116}; @@ -177,11 +180,11 @@ int Part::FixKeyfollow(int srckey, int *dir) { } } -void Part::RefreshPatch() { - SetPatch(-1); +void Part::refreshPatch() { + setPatch(-1); } -void Part::AbortPoly(dpoly *poly) { +void Part::abortPoly(dpoly *poly) { if (!poly->isPlaying) { return; } @@ -210,8 +213,7 @@ unsigned int Part::getAbsTimbreNum() { return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum; } -void Part::SetPatch(int patchNum) { - int pcm; +void Part::setPatch(int patchNum) { int absTimbreNum = -1; // Initialised to please compiler TimbreParam timSrc; if (isRhythm) { @@ -232,7 +234,7 @@ void Part::SetPatch(int patchNum) { timSrc = *timbreTemp; #if 0 // Immediately stop all partials on this part (this is apparently *not* the correct behaviour) - for (int m = 0; m < MAXPOLY; m++) { + for (int m = 0; m < MT32EMU_MAX_POLY; m++) { AbortPoly(poly); } #else @@ -240,7 +242,7 @@ void Part::SetPatch(int patchNum) { // if so then duplicate the cached data from the part to the partial so that // we can change the part's cache without affecting the partial. // Hopefully this is fairly rare. - for (int m = 0; m < MAXPOLY; m++) { + for (int m = 0; m < MT32EMU_MAX_POLY; m++) { for (int i = 0; i < 4; i++) { Partial *partial = polyTable[m].partials[i]; if (partial != NULL) { @@ -255,7 +257,6 @@ void Part::SetPatch(int patchNum) { } memcpy(currentInstr, timSrc.common.name, 10); - currentInstr[10] = 0; int partialCount = 0; for (int t=0;t<4;t++) { @@ -269,33 +270,31 @@ void Part::SetPatch(int patchNum) { // Calculate and cache common parameters - pcm = timSrc.partial[t].wg.pcmwave; - patchCache[t].pcm = timSrc.partial[t].wg.pcmwave; patchCache[t].useBender = (timSrc.partial[t].wg.bender == 1); switch (t) { case 0: patchCache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct12] & 0x2) ? true : false; - patchCache[t].mix = PartialMixStruct[(int)timSrc.common.pstruct12]; + patchCache[t].structureMix = PartialMixStruct[(int)timSrc.common.pstruct12]; patchCache[t].structurePosition = 0; patchCache[t].structurePair = 1; break; case 1: patchCache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct12] & 0x1) ? true : false; - patchCache[t].mix = PartialMixStruct[(int)timSrc.common.pstruct12]; + patchCache[t].structureMix = PartialMixStruct[(int)timSrc.common.pstruct12]; patchCache[t].structurePosition = 1; patchCache[t].structurePair = 0; break; case 2: patchCache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct34] & 0x2) ? true : false; - patchCache[t].mix = PartialMixStruct[(int)timSrc.common.pstruct34]; + patchCache[t].structureMix = PartialMixStruct[(int)timSrc.common.pstruct34]; patchCache[t].structurePosition = 0; patchCache[t].structurePair = 3; break; case 3: patchCache[t].PCMPartial = (PartialStruct[(int)timSrc.common.pstruct34] & 0x1) ? true : false; - patchCache[t].mix = PartialMixStruct[(int)timSrc.common.pstruct34]; + patchCache[t].structureMix = PartialMixStruct[(int)timSrc.common.pstruct34]; patchCache[t].structurePosition = 1; patchCache[t].structurePair = 2; break; @@ -306,7 +305,7 @@ void Part::SetPatch(int patchNum) { patchCache[t].waveform = timSrc.partial[t].wg.waveform; patchCache[t].pulsewidth = timSrc.partial[t].wg.pulsewid; patchCache[t].pwsens = timSrc.partial[t].wg.pwvelo; - patchCache[t].pitchkeyfollow = FixKeyfollow(timSrc.partial[t].wg.keyfollow, &patchCache[t].pitchkeydir); + patchCache[t].pitchkeyfollow = fixKeyfollow(timSrc.partial[t].wg.keyfollow, &patchCache[t].pitchkeydir); // Calculate and cache pitch stuff patchCache[t].pitchshift = timSrc.partial[t].wg.coarse; @@ -339,9 +338,9 @@ void Part::SetPatch(int patchNum) { else patchCache[t].ampenvdir = 0; - patchCache[t].ampbias[0] = FixBiaslevel(patchCache[t].ampEnv.biaspoint1, &patchCache[t].ampdir[0]); + patchCache[t].ampbias[0] = fixBiaslevel(patchCache[t].ampEnv.biaspoint1, &patchCache[t].ampdir[0]); patchCache[t].ampblevel[0] = 12 - patchCache[t].ampEnv.biaslevel1; - patchCache[t].ampbias[1] = FixBiaslevel(patchCache[t].ampEnv.biaspoint2, &patchCache[t].ampdir[1]); + patchCache[t].ampbias[1] = fixBiaslevel(patchCache[t].ampEnv.biaspoint2, &patchCache[t].ampdir[1]); patchCache[t].ampblevel[1] = 12 - patchCache[t].ampEnv.biaslevel2; patchCache[t].ampdepth = patchCache[t].ampEnv.envvkf * patchCache[t].ampEnv.envvkf; patchCache[t].ampsustain = patchCache[t].ampEnv.envlevel[3]; @@ -350,9 +349,9 @@ void Part::SetPatch(int patchNum) { // Calculate and cache filter stuff patchCache[t].filtEnv = timSrc.partial[t].tvf; patchCache[t].tvfdepth = patchCache[t].filtEnv.envdkf; - patchCache[t].filtkeyfollow = FixKeyfollow(patchCache[t].filtEnv.keyfollow, &patchCache[t].keydir); + patchCache[t].filtkeyfollow = fixKeyfollow(patchCache[t].filtEnv.keyfollow, &patchCache[t].keydir); patchCache[t].filtEnv.envdepth = (char)((float)patchCache[t].filtEnv.envdepth * 1.27); - patchCache[t].tvfbias = FixBiaslevel(patchCache[t].filtEnv.biaspoint, &patchCache[t].tvfdir); + patchCache[t].tvfbias = fixBiaslevel(patchCache[t].filtEnv.biaspoint, &patchCache[t].tvfdir); patchCache[t].tvfblevel = patchCache[t].filtEnv.biaslevel; patchCache[t].filtsustain = patchCache[t].filtEnv.envlevel[3]; @@ -366,17 +365,22 @@ void Part::SetPatch(int patchNum) { // Common parameters, stored redundantly patchCache[t].partialCount = partialCount; patchCache[t].sustain = (timSrc.common.nosustain == 0); + if (isRhythm) { + patchCache[t].benderRange = 0; + } else { + patchCache[t].benderRange = patchTemp->patch.benderRange; + } } //synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", patchCache[0].waveform, patchCache[1].waveform, patchCache[2].waveform, patchCache[3].waveform); if (isRhythm) memcpy(drumCache[absTimbreNum - 128], patchCache, sizeof(patchCache)); else - AllStop(); -#if DISPLAYINSTR == 1 - synth->printDebug("%s: Recache, param %d (timbre: %s), %d partials", name, patchNum, currentInstr, partialCount); + allStop(); +#if MT32EMU_MONITOR_INSTRUMENTS == 1 + synth->printDebug("%s: Recache, param %d (timbre: %s)", name, patchNum, currentInstr); for (int i = 0; i < 4; i++) { - synth->printDebug(" %d: play=%s, pcm=%d, wave=%d", i, patchCache[i].playPartial ? "YES" : "NO", timSrc.partial[i].wg.pcmwave, timSrc.partial[i].wg.waveform); + synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, patchCache[i].playPartial ? "YES" : "NO", patchCache[i].PCMPartial ? "YES" : "NO", timSrc.partial[i].wg.pcmwave, timSrc.partial[i].wg.waveform); } #endif } @@ -385,11 +389,11 @@ char *Part::getName() { return name; } -void Part::SetVolume(int vol) { +void Part::setVolume(int vol) { volume = voltable[vol]; } -void Part::SetPan(int pan) { +void Part::setPan(int pan) { // FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct? // FIXME:KG: There is no way to get a centred balance here... And the middle two // pan settings have a distance of 1024, double the usual. @@ -405,7 +409,7 @@ void Part::SetPan(int pan) { //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot); } -void Part::PlayNote(PartialManager *partialManager, int f, int vel) { +void Part::playNote(PartialManager *partialManager, int f, int vel) { int drumNum = -1; // Initialised to please compiler int drumTimbre = -1; // As above int freqNum; @@ -436,10 +440,10 @@ void Part::PlayNote(PartialManager *partialManager, int f, int vel) { // Haven't found any software that uses any of the other poly modes // FIXME:KG: Should this also apply to rhythm? if (!isRhythm) { - for (int i = 0; i < MAXPOLY; i++) { + for (int i = 0; i < MT32EMU_MAX_POLY; i++) { if (polyTable[i].isActive() && (polyTable[i].note == f)) { //AbortPoly(&polyTable[i]); - StopNote(f); + stopNote(f); break; } } @@ -455,12 +459,12 @@ void Part::PlayNote(PartialManager *partialManager, int f, int vel) { } // Find free note int m; - for (m = 0; m < MAXPOLY; m++) { + for (m = 0; m < MT32EMU_MAX_POLY; m++) { if (!polyTable[m].isActive()) { break; } } - if (m == MAXPOLY) { + if (m == MT32EMU_MAX_POLY) { synth->printDebug("%s (%s): No free poly to play note %d (vel %d)", name, currentInstr, f, vel); return; } @@ -490,33 +494,32 @@ void Part::PlayNote(PartialManager *partialManager, int f, int vel) { synth->printDebug("%s (%s): No partials to play for this instrument", name, this->currentInstr); if (isRhythm) { - tpoly->bendptr = &drumBend; tpoly->pansetptr = &drumPan[drumNum]; tpoly->reverb = rhythmTemp[drumNum].reverbSwitch > 0; } else { - tpoly->bendptr = &bend; tpoly->pansetptr = &volumesetting; tpoly->reverb = patchTemp->patch.reverbSwitch > 0; } tpoly->sustain = patchCache[0].sustain; tpoly->volumeptr = &volume; +#if MT32EMU_MONITOR_INSTRUMENTS == 1 + if (isRhythm) { + synth->printDebug("%s (%s): starting poly %d (drum %d, timbre %d) - Vel %d Key %d Vol %d", name, currentInstr, m, drumNum, drumTimbre, vel, f, volume); + } else { + synth->printDebug("%s (%s): starting poly %d - Vel %d Key %d Vol %d", name, currentInstr, m, vel, f, volume); + } +#endif + for (int x = 0; x < 4; x++) { if (tpoly->partials[x] != NULL) { tpoly->partials[x]->startPartial(tpoly, &patchCache[x], tpoly->partials[patchCache[x].structurePair]); + tpoly->partials[x]->setBend(bend); } } - -#if DISPLAYINSTR == 1 - if (isRhythm) { - synth->printDebug("%s (%s): starting note poly %d (drum %d, timbre %d) - Vel %d Vol %d", name, currentInstr, m, drumNum, drumTimbre, vel, volume); - } else { - synth->printDebug("%s (%s): starting note poly %d - Vel %d Freq %d Vol %d", name, currentInstr, m, vel, f, volume); - } -#endif } -static void StartDecayPoly(dpoly *tpoly) { +static void startDecayPoly(dpoly *tpoly) { if (tpoly->isDecay) { return; } @@ -531,41 +534,41 @@ static void StartDecayPoly(dpoly *tpoly) { tpoly->isPlaying = false; } -void Part::AllStop() { - for (int q = 0; q < MAXPOLY; q++) { +void Part::allStop() { + for (int q = 0; q < MT32EMU_MAX_POLY; q++) { dpoly *tpoly = &polyTable[q]; if (tpoly->isPlaying) { - StartDecayPoly(tpoly); + startDecayPoly(tpoly); } } } -void Part::StopPedalHold() { - for (int q = 0; q < MAXPOLY; q++) { +void Part::stopPedalHold() { + for (int q = 0; q < MT32EMU_MAX_POLY; q++) { dpoly *tpoly; tpoly = &polyTable[q]; if (tpoly->isActive() && tpoly->pedalhold) - StopNote(tpoly->note); + stopNote(tpoly->note); } } -void Part::StopNote(int f) { +void Part::stopNote(int f) { // Non-sustaining instruments ignore stop note commands. // They die away eventually anyway //if (!tpoly->sustain) return; -#if DISPLAYINSTR == 1 - synth->printDebug("%s (%s): stopping note %d", name, currentInstr, f); +#if MT32EMU_MONITOR_INSTRUMENTS == 1 + synth->printDebug("%s (%s): stopping key %d", name, currentInstr, f); #endif if (f != -1) { - for (int q = 0; q < MAXPOLY; q++) { + for (int q = 0; q < MT32EMU_MAX_POLY; q++) { dpoly *tpoly = &polyTable[q]; if (tpoly->isPlaying && tpoly->note == f) { if (holdpedal) tpoly->pedalhold = true; else if (tpoly->sustain) - StartDecayPoly(tpoly); + startDecayPoly(tpoly); } } return; @@ -576,7 +579,7 @@ void Part::StopNote(int f) { int oldest = -1; Bit64s oldage = -1; - for (int q = 0; q < MAXPOLY; q++) { + for (int q = 0; q < MT32EMU_MAX_POLY; q++) { dpoly *tpoly = &polyTable[q]; if (tpoly->isPlaying && !tpoly->isDecay) { @@ -588,7 +591,7 @@ void Part::StopNote(int f) { } if (oldest!=-1) { - StartDecayPoly(&polyTable[oldest]); + startDecayPoly(&polyTable[oldest]); } } diff --git a/backends/midi/mt32/part.h b/backends/midi/mt32/part.h index 302ca4d2fb..da73334af1 100644 --- a/backends/midi/mt32/part.h +++ b/backends/midi/mt32/part.h @@ -22,12 +22,6 @@ #ifndef MT32EMU_PART_H #define MT32EMU_PART_H -#define AMPENV 0 -#define FILTENV 1 -#define PITCHENV 2 - -#define MAXPOLY 64 - namespace MT32Emu { class PartialManager; @@ -52,38 +46,38 @@ private: bool holdpedal; - volset volumesetting; + StereoVolume volumesetting; PatchCache patchCache[4]; - Bit32u bend; + float bend; // -1.0 .. +1.0 Bit32s volume; - dpoly polyTable[MAXPOLY]; + dpoly polyTable[MT32EMU_MAX_POLY]; - void AbortPoly(dpoly *poly); + void abortPoly(dpoly *poly); + + static int fixKeyfollow(int srckey, int *dir); + static int fixBiaslevel(int srcpnt, int *dir); public: Part(Synth *synth, int usePartNum); char *getName(); - void PlayNote(PartialManager *partialManager, int f, int vel); - void StopNote(int f); - void AllStop(); - void SetVolume(int vol); - void SetPan(int vol); - void SetBend(int vol); - void SetModulation(int vol); - void SetPatch(int patchnum); - void SetHoldPedal(bool pedalval); - void StopPedalHold(); - void RefreshPatch(); - void RefreshDrumCache(); + void playNote(PartialManager *partialManager, int f, int vel); + void stopNote(int f); + void allStop(); + void setVolume(int vol); + void setPan(int vol); + void setBend(int vol); + void setModulation(int vol); + void setPatch(int patchnum); + void setHoldPedal(bool pedalval); + void stopPedalHold(); + void refreshPatch(); + void refreshDrumCache(); void setPatch(PatchParam *patch); void setTimbre(TimbreParam *timbre); unsigned int getAbsTimbreNum(); - - int FixKeyfollow(int srckey, int *dir); - int FixBiaslevel(int srcpnt, int *dir); }; } diff --git a/backends/midi/mt32/partial.cpp b/backends/midi/mt32/partial.cpp index bc5151e565..b3e2f661ba 100644 --- a/backends/midi/mt32/partial.cpp +++ b/backends/midi/mt32/partial.cpp @@ -34,6 +34,9 @@ Partial::Partial(Synth *useSynth) { pair = NULL; } +Partial::~Partial() { +} + int Partial::getOwnerPart() { return ownerPart; } @@ -62,12 +65,12 @@ void Partial::deactivate() { } } -void Partial::initKeyFollow(int freqNum) { +void Partial::initKeyFollow(int key) { // Setup partial keyfollow // Note follow relative to middle C int keyfollow; - int realfol = (freqNum * 2 - MIDDLEC * 2) / 2; - int antirealfol = (MIDDLEC * 2 - freqNum * 2) / 2; + int realfol = (key * 2 - MIDDLEC * 2) / 2; + int antirealfol = (MIDDLEC * 2 - key * 2) / 2; // Calculate keyfollow for pitch switch(patchCache->pitchkeydir) { case -1: @@ -99,7 +102,7 @@ void Partial::initKeyFollow(int freqNum) { keyfollow = (antirealfol * patchCache->filtkeyfollow) >> 12; break; case 0: - keyfollow = freqNum; + keyfollow = key; break; case 1: keyfollow = (realfol * patchCache->filtkeyfollow) >> 12; @@ -120,11 +123,11 @@ void Partial::startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPa } patchCache = useCache; poly = usePoly; - mixType = patchCache->mix; + mixType = patchCache->structureMix; structurePosition = patchCache->structurePosition; play = true; - initKeyFollow(poly->freqnum); + initKeyFollow(poly->freqnum); // Initialises noteVal, filtVal and realVal lfoPos = 0; pulsewidth = patchCache->pulsewidth + pwveltable[patchCache->pwsens][poly->vel]; if (pulsewidth > 100) { @@ -174,10 +177,10 @@ Bit16s *Partial::generateSamples(long length) { while (length--) { Bit32s envval, ampval; Bit32s ptemp = 0; - if (envs[AMPENV].sustaining) + if (envs[EnvelopeType_amp].sustaining) ampval = ampEnvCache; else { - if (envs[AMPENV].count<=0) { + if (envs[EnvelopeType_amp].count <= 0) { ampval = getAmpEnvelope(); if (!play) { deactivate(); @@ -192,26 +195,26 @@ Bit16s *Partial::generateSamples(long length) { } ampval = voltable[ampval]; - int tmpvel = poly->vel; + int tmpvel; if (patchCache->ampenvdir == 1) - tmpvel = 127 - tmpvel; + tmpvel = 127 - poly->vel; + else + tmpvel = poly->vel; ampval = (ampval * ampveltable[tmpvel][(int)patchCache->ampEnv.velosens]) >> 8; - //if (envs[AMPENV].sustaining) + //if (envs[EnvelopeType_amp].sustaining) ampEnvCache = ampval; } else ampval = ampEnvCache; - --envs[AMPENV].count; + --envs[EnvelopeType_amp].count; } - int delta = 0x10707; - // Calculate Pitch envelope int lfoat = 0x1000; int pdep; if (pitchSustain) { // Calculate LFO position // LFO does not kick in completely until pitch envelope sustains - if (patchCache->lfodepth>0) { + if (patchCache->lfodepth > 0) { lfoPos++; if (lfoPos >= patchCache->lfoperiod) lfoPos = 0; @@ -228,18 +231,23 @@ Bit16s *Partial::generateSamples(long length) { pitchEnvCache = pdep; } + int delta; + // These two are only for PCM partials, obviously + PCMWaveEntry *pcmWave = NULL; // Initialise to please compiler + int pcmAddr = 0; // Initialise to please compiler + // Get waveform - either PCM or synthesized sawtooth or square if (patchCache->PCMPartial) { // PCM partial - int addr,len; - sampleTable *tPCM = &synth->PCMList[patchCache->pcm]; + int len; + pcmWave = &synth->PCMList[patchCache->pcm]; - if (tPCM->aggSound == -1) { - delta = wavtabler[tPCM->pcmnum][noteVal]; - addr = tPCM->addr; - len = tPCM->len; + if (pcmWave->aggSound == -1) { + delta = wavtabler[pcmWave->pcmnum][noteVal]; + pcmAddr = pcmWave->addr; + len = pcmWave->len; if (partialOff.pcmplace >= len) { - if (tPCM->loop) { + if (pcmWave->loop) { partialOff.pcmplace = partialOff.pcmoffset = 0; // FIXME:KG: Use this?: partialOff.pcmplace %= len; } else { @@ -249,57 +257,67 @@ Bit16s *Partial::generateSamples(long length) { } } } else { - int tmppcm = LoopPatterns[tPCM->aggSound][loopPos]; - delta = looptabler[tPCM->aggSound][loopPos][noteVal]; - addr = synth->PCM[tmppcm].addr; + int tmppcm = LoopPatterns[pcmWave->aggSound][loopPos]; + delta = looptabler[pcmWave->aggSound][loopPos][noteVal]; + pcmAddr = synth->PCM[tmppcm].addr; len = synth->PCM[tmppcm].len; if (partialOff.pcmplace >= len) { loopPos++; - if (LoopPatterns[tPCM->aggSound][loopPos]==-1) - loopPos=0; + if (LoopPatterns[pcmWave->aggSound][loopPos] == -1) + loopPos = 0; partialOff.pcmplace = partialOff.pcmoffset = 0; } } + } else { + // Synthesis partial + delta = 0x10707; + partialOff.pcmplace %= (Bit16u)(divtable[noteVal] >> 15); + } - if (ampval>0) { - int ra,rb,dist; + // Build delta for position of next sample + // Fix delta code + Bit64s tdelta = (Bit64s)delta; + tdelta = (tdelta * patchCache->fineshift) >> 12; + tdelta = (tdelta * pdep) >> 12; + tdelta = (tdelta * lfoat) >> 12; + tdelta = (tdelta * bendShift) >> 12; + delta = (int)tdelta; + + if (ampval > 0) { + if (patchCache->PCMPartial) { + // Render PCM sample + int ra, rb, dist; int taddr; - if (delta<0x10000) { + if (delta < 0x10000) { // Linear sound interpolation - taddr = addr + partialOff.pcmplace; + taddr = pcmAddr + partialOff.pcmplace; if (taddr >= ROMSIZE) { synth->printDebug("Overflow ROMSIZE!"); taddr = ROMSIZE - 1; } ra = synth->romfile[taddr]; - rb = synth->romfile[taddr+1]; - dist = rb-ra; + //FIXME:KG: Deal with condition that taddr + 1 is past PCM length + rb = synth->romfile[taddr + 1]; + dist = rb - ra; ptemp = (ra + ((dist * (Bit32s)(partialOff.pcmoffset >> 8)) >> 8)); } else { // Sound decimation // The right way to do it is to use a lowpass filter on the waveform before selecting // a point. This is too slow. The following approximates this as fast as possible int idelta = delta >> 16; - taddr = addr + partialOff.pcmplace; + taddr = pcmAddr + partialOff.pcmplace; ra = 0; for (int ix = 0; ix < idelta; ix++) ra += synth->romfile[taddr++]; ptemp = ra / idelta; } - } - } else { - // Synthesis partial - int divis = divtable[noteVal] >> 15; - - partialOff.pcmplace %= (Bit16u)divis; - - if (ampval > 0) { - int wf = patchCache->waveform ; + } else { + // Render synthesised sample + int divis = divtable[noteVal] >> 15; + int wf = patchCache->waveform; int toff = partialOff.pcmplace; int minorplace = partialOff.pcmoffset >> 14; - int pa, pb; - Bit32s filtval = getFiltEnvelope(); //synth->printDebug("Filtval: %d", filtval); @@ -308,7 +326,7 @@ Bit16s *Partial::generateSamples(long length) { // Square waveform. Made by combining two pregenerated bandlimited // sawtooth waveforms // Pulse width is not yet correct - + int pa, pb; int hdivis = divis >> 1; int divmark = smalldivtable[noteVal]; @@ -371,18 +389,9 @@ Bit16s *Partial::generateSamples(long length) { } } - // Build delta for position of next sample - // Fix delta code - Bit64s tdelta = (Bit64s)delta; - tdelta = (tdelta * patchCache->fineshift)>>12; - tdelta = (tdelta * pdep)>>12; - tdelta = (tdelta * lfoat)>>12; - if (patchCache->useBender) - tdelta = (tdelta * *poly->bendptr)>>12; - // Add calculated delta to our waveform offset Bit32u absOff = ((partialOff.pcmplace << 16) | partialOff.pcmoffset); - absOff += (int)tdelta; + absOff += delta; partialOff.pcmplace = (Bit16u)((absOff & 0xFFFF0000) >> 16); partialOff.pcmoffset = (Bit16u)(absOff & 0xFFFF); @@ -390,17 +399,31 @@ Bit16s *Partial::generateSamples(long length) { ptemp = (ptemp * ampval) >> 9; ptemp = (ptemp * *poly->volumeptr) >> 7; - envs[AMPENV].envpos++; - envs[PITCHENV].envpos++; - envs[FILTENV].envpos++; + envs[EnvelopeType_amp].envpos++; + envs[EnvelopeType_pitch].envpos++; + envs[EnvelopeType_filt].envpos++; *partialBuf++ = (Bit16s)ptemp; } + // We may have deactivated and broken out of the loop before the end of the buffer, + // if so then fill the remainder with 0s. if (++length > 0) memset(partialBuf, 0, length * 2); return &myBuffer[0]; } +void Partial::setBend(float factor) { + if (!patchCache->useBender || factor == 0.0f) { + bendShift = 4096; + return; + } + // NOTE:KG: We can't do this smoothly with lookup tables, unless we use several MB. + float bendSemitones = factor * patchCache->benderRange; // -24 .. 24 + float mult = powf(2.0f, bendSemitones / 12.0f); + synth->printDebug("setBend(): semitones=%f, mult=%f, factor=%f, benderRange=%d\n", bendSemitones, mult, factor, patchCache->benderRange); + bendShift = (int)(mult * 4096.0f); +} + Bit16s *Partial::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) { if (buf1 == NULL) return buf2; @@ -408,7 +431,7 @@ Bit16s *Partial::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) { return buf1; Bit16s *outBuf = buf1; -#if USE_MMX >= 1 +#if MT32EMU_USE_MMX >= 1 // KG: This seems to be fine int donelen = i386_mixBuffers(buf1, buf2, len); len -= donelen; @@ -438,7 +461,7 @@ Bit16s *Partial::mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) { } Bit16s *outBuf = buf1; -#if USE_MMX >= 1 +#if MT32EMU_USE_MMX >= 1 // KG: This seems to be fine int donelen = i386_mixBuffersRingMix(buf1, buf2, len); len -= donelen; @@ -471,7 +494,7 @@ Bit16s *Partial::mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) { } Bit16s *outBuf = buf1; -#if USE_MMX >= 1 +#if MT32EMU_USE_MMX >= 1 // FIXME:KG: Not really checked as working int donelen = i386_mixBuffersRing(buf1, buf2, len); len -= donelen; @@ -585,7 +608,7 @@ bool Partial::produceOutput(Bit16s *partialBuf, long length) { leftvol = poly->pansetptr->leftvol; rightvol = poly->pansetptr->rightvol; -#if USE_MMX >= 2 +#if MT32EMU_USE_MMX >= 2 // FIXME:KG: This appears to introduce crackle int donelen = i386_partialProductOutput(length, leftvol, rightvol, partialBuf, mixedBuf); length -= donelen; @@ -605,7 +628,7 @@ Bit32s Partial::getFiltEnvelope() { int cutoff,depth,keyfollow, realfollow; - envstatus *tStat = &envs[FILTENV]; + envstatus *tStat = &envs[EnvelopeType_filt]; keyfollow = filtVal; realfollow = realVal; @@ -621,7 +644,7 @@ Bit32s Partial::getFiltEnvelope() { if (tStat->envstat==4) { reshigh = patchCache->filtsustain; if (!poly->sustain) { - startDecay(FILTENV, reshigh); + startDecay(EnvelopeType_filt, reshigh); } } else { if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) { @@ -713,7 +736,7 @@ bool Partial::shouldReverb() { Bit32s Partial::getAmpEnvelope() { Bit32s tc; - envstatus *tStat = &envs[AMPENV]; + envstatus *tStat = &envs[EnvelopeType_amp]; if (!play) return 0; @@ -754,7 +777,7 @@ Bit32s Partial::getAmpEnvelope() { //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize); tc = patchCache->ampsustain; if (!poly->sustain) - startDecay(AMPENV, tc); + startDecay(EnvelopeType_amp, tc); else tStat->sustaining = true; @@ -815,7 +838,7 @@ PastCalc: } Bit32s Partial::getPitchEnvelope() { - envstatus *tStat = &envs[PITCHENV]; + envstatus *tStat = &envs[EnvelopeType_pitch]; Bit32s tc; pitchSustain = false; @@ -832,7 +855,7 @@ Bit32s Partial::getPitchEnvelope() { if (poly->sustain) pitchSustain = true; else - startDecay(PITCHENV, tc); + startDecay(EnvelopeType_pitch, tc); } else { if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) { tStat->envstat++; @@ -853,13 +876,13 @@ Bit32s Partial::getPitchEnvelope() { } void Partial::startDecayAll() { - startDecay(AMPENV, envs[AMPENV].prevlevel); - startDecay(FILTENV, envs[FILTENV].prevlevel); - startDecay(PITCHENV, envs[PITCHENV].prevlevel); + startDecay(EnvelopeType_amp, envs[EnvelopeType_amp].prevlevel); + startDecay(EnvelopeType_filt, envs[EnvelopeType_filt].prevlevel); + startDecay(EnvelopeType_pitch, envs[EnvelopeType_pitch].prevlevel); pitchSustain = false; } -void Partial::startDecay(int envnum, Bit32s startval) { +void Partial::startDecay(EnvelopeType envnum, Bit32s startval) { envstatus *tStat = &envs[envnum]; tStat->sustaining = false; @@ -868,15 +891,15 @@ void Partial::startDecay(int envnum, Bit32s startval) { tStat->envbase = startval; switch(envnum) { - case AMPENV: + case EnvelopeType_amp: tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * timekeytable[(int)patchCache->ampEnv.envtkf][poly->freqnum]) >> 8; tStat->envdist = -startval; break; - case FILTENV: + case EnvelopeType_filt: tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * timekeytable[(int)patchCache->filtEnv.envtkf][poly->freqnum]) >> 8; tStat->envdist = -startval; break; - case PITCHENV: + case EnvelopeType_pitch: tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * timekeytable[(int)patchCache->pitchEnv.timekeyfollow][poly->freqnum]) >> 8 ; tStat->envdist = patchCache->pitchEnv.level[4] - startval; break; diff --git a/backends/midi/mt32/partial.h b/backends/midi/mt32/partial.h index 20160dca5f..ad7cd65b0b 100644 --- a/backends/midi/mt32/partial.h +++ b/backends/midi/mt32/partial.h @@ -24,6 +24,14 @@ namespace MT32Emu { +class Synth; + +enum EnvelopeType { + EnvelopeType_amp = 0, + EnvelopeType_filt = 1, + EnvelopeType_pitch = 2 +}; + struct envstatus { Bit32s envpos; Bit32s envstat; @@ -39,8 +47,6 @@ struct envstatus { Bit32s count; }; -class Synth; - // Class definition of MT-32 partials. 32 in all. class Partial { private: @@ -80,6 +86,8 @@ private: dpoly *poly; + int bendShift; + Bit16s *mixBuffers(Bit16s * buf1, Bit16s * buf2, int len); Bit16s *mixBuffersRingMix(Bit16s * buf1, Bit16s * buf2, int len); Bit16s *mixBuffersRing(Bit16s * buf1, Bit16s * buf2, int len); @@ -100,14 +108,16 @@ public: Bit64s age; Partial(Synth *synth); + ~Partial(); int getOwnerPart(); bool isActive(); void activate(int part); void deactivate(void); void startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPartial); - void startDecay(int envnum, Bit32s startval); + void startDecay(EnvelopeType envnum, Bit32s startval); void startDecayAll(); + void setBend(float factor); bool shouldReverb(); // Returns true only if data written to buffer diff --git a/backends/midi/mt32/structures.h b/backends/midi/mt32/structures.h index 5b1db5921b..4c1e237f3e 100644 --- a/backends/midi/mt32/structures.h +++ b/backends/midi/mt32/structures.h @@ -22,19 +22,10 @@ #ifndef MT32EMU_STRUCTURES_H #define MT32EMU_STRUCTURES_H -#if (defined (_MSC_VER) && defined(_M_IX86)) || (defined(__GNUC__) && defined(__i386__)) -#define HAVE_X86 -#endif - -#define MAX_SAMPLE_OUTPUT 4096 -#ifdef HAVE_X86 -#define USE_MMX 1 -#else -#define USE_MMX 0 -#endif - namespace MT32Emu { +const unsigned int MAX_SAMPLE_OUTPUT = 4096; + #ifdef _MSC_VER #define MT32EMU_ALIGN_PACKED __declspec(align(1)) typedef unsigned __int64 Bit64u; @@ -195,7 +186,8 @@ union MT32RAMFormat { #pragma pack() #endif -struct sampleFormat { +struct PCMWave { + char name[16]; Bit32u addr; Bit32u len; bool loop; @@ -203,7 +195,7 @@ struct sampleFormat { Bit32s ampval; }; -struct sampleTable { +struct PCMWaveEntry { Bit32u addr; Bit32u len; Bit32u pcmnum; @@ -216,7 +208,7 @@ struct soundaddr { Bit16u pcmoffset; }; -struct volset { +struct StereoVolume { Bit16s leftvol; Bit16s rightvol; }; @@ -257,6 +249,7 @@ struct PatchCache { int tvfdepth; bool useBender; + float benderRange; // 0.0, 1.0, .., 24.0 (semitones) TimbreParam::partialParam::envParam pitchEnv; TimbreParam::partialParam::tvaParam ampEnv; @@ -266,7 +259,7 @@ struct PatchCache { Bit32s pitchsustain; Bit32s filtsustain; - Bit32u mix; + Bit32u structureMix; int structurePosition; int structurePair; @@ -288,9 +281,8 @@ struct dpoly { bool reverb; bool isDecay; - const Bit32u *bendptr; - Bit32s *volumeptr; - volset *pansetptr; + const Bit32s *volumeptr; + const StereoVolume *pansetptr; Partial *partials[4]; diff --git a/backends/midi/mt32/synth.cpp b/backends/midi/mt32/synth.cpp index 2647fdae85..4c697b893f 100644 --- a/backends/midi/mt32/synth.cpp +++ b/backends/midi/mt32/synth.cpp @@ -19,21 +19,16 @@ * IN THE SOFTWARE. */ -#define BENCHMARK 0 - #include #include #include #include -#if BENCHMARK > 0 -#include -#endif #include "mt32emu.h" -// Debugging stuff -// Used to dump drum patches to syx file for viewing -#define DUMPDRUMS 0 +#if MT32EMU_BENCHMARK_FILTERS > 0 +#include +#endif namespace MT32Emu { @@ -347,7 +342,8 @@ bool Synth::loadPCMToROMMap(const char *filename) { return false; } - for (int i=0;i<54;i++) { + Bit32u PCMReassign[54]; + for (int i = 0; i < 54; i++) { PCMReassign[i] = i; PCM[i].tune = 220.0f; PCM[i].ampval = 256; @@ -378,6 +374,8 @@ bool Synth::loadPCMToROMMap(const char *filename) { if (cp != NULL) { cp = strtok(NULL," \n\r"); if (cp != NULL) { + strncpy(PCM[patchcount].name, cp, 15); + PCM[patchcount].name[15] = 0; cp = strtok(NULL," \n\r"); if (cp !=NULL) { int newpcm = atoi(cp); @@ -581,7 +579,7 @@ bool Synth::open(SynthProperties &useProp) { return false; } -#if DUMPDRUMS == 1 +#if MT32EMU_DUMP_DRUMS == 1 strcpy(&pathBuf[0], baseDir); dumpDrums(strcat(&pathBuf[0],"drumsys.syx")); #endif @@ -605,12 +603,14 @@ bool Synth::open(SynthProperties &useProp) { mt32ram.params.patches[i].timbreNum = i & 63; } - TableInitialiser tableInitialiser; - tableInitialiser.initMT32Tables(this, PCM, (float)myProp.SampleRate); - if (myProp.UseDefault) - initReverb(0,5); + if (!TableInitialiser::initMT32Tables(this, PCM, (float)myProp.sampleRate)) { + report(ReportType_errorSampleRate, NULL); + return false; + } + if (myProp.useDefaultReverb) + initReverb(0, 5); else - initReverb(myProp.RevType, myProp.RevTime); + initReverb(myProp.reverbType, myProp.reverbTime); for (int i = 0; i < 9; i++) { parts[i] = new Part(this, i); @@ -619,14 +619,14 @@ bool Synth::open(SynthProperties &useProp) { // The patch is already set by the presets, now set the timbre it wants parts[i]->setTimbre(&mt32ram.params.timbres[parts[i]->getAbsTimbreNum()].timbre); // And refresh the part's cache - parts[i]->RefreshPatch(); + parts[i]->refreshPatch(); } } // For resetting mt32 mid-execution mt32default = mt32ram; -#ifdef HAVE_X86 +#ifdef MT32EMU_HAVE_X86 bool availableSSE = DetectSIMD(); bool available3DNow = Detect3DNow(); @@ -646,7 +646,7 @@ bool Synth::open(SynthProperties &useProp) { } #endif -#if BENCHMARK > 1 +#if MT32EMU_BENCHMARK_FILTERS > 1 // Benchmark 3DNow, Floating point, and SSE filters clock_t start, end; float histval[50]; @@ -691,7 +691,7 @@ void Synth::close(void) { return; for (int t = 0; t < 4; t++) { - for (int m = 0; m < 128; m++) { + for (int m = 0; m < NUM_NOTES; m++) { if (waveforms[t][m]!=NULL) { delete[] waveforms[t][m]; waveforms[t][m] = NULL; @@ -743,51 +743,52 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char case 0x8: //printDebug("Note OFF - Part %d", part); // The MT-32 ignores velocity for note off - parts[part]->StopNote(note); + parts[part]->stopNote(note); break; case 0x9: //printDebug("Note ON - Part %d, Note %d Vel %d", part, note, velocity); if (velocity == 0) { // MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40 - parts[part]->StopNote(note); + parts[part]->stopNote(note); } else { - parts[part]->PlayNote(partialManager, note, velocity); + parts[part]->playNote(partialManager, note, velocity); } break; case 0xB: // Control change switch (note) { case 0x01: // Modulation //printDebug("Modulation: %d", velocity); - parts[part]->SetModulation(velocity); - break; - case 0x0B: - //printDebug("Expression set: %d", velocity); - parts[part]->SetVolume(velocity); + parts[part]->setModulation(velocity); break; case 0x07: // Set volume //if (part!=3) return; //printDebug("Volume set: %d", velocity); - parts[part]->SetVolume(velocity); + parts[part]->setVolume(velocity); break; case 0x0A: // Pan //printDebug("Pan set: %d", velocity); - parts[part]->SetPan(velocity); + parts[part]->setPan(velocity); + break; + case 0x0B: + //printDebug("Expression set: %d", velocity); + parts[part]->setVolume(velocity); break; case 0x40: // Hold pedal //printDebug("Hold pedal set: %d", velocity); - parts[part]->SetHoldPedal(velocity>=64); + parts[part]->setHoldPedal(velocity>=64); break; - case 0x7B: // All notes off - //printDebug("All notes off"); - parts[part]->AllStop(); - break; case 0x79: // Reset all controllers printDebug("Reset all controllers (NYI)"); break; + case 0x7B: // All notes off + //printDebug("All notes off"); + parts[part]->allStop(); + break; + default: - printDebug("Unknown MIDI Control code: 0x%02x - vel %02x",note, velocity); + printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity); break; } @@ -795,7 +796,7 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char case 0xC: // Program change //printDebug("Program change %01x", note); if (part < 8) { - parts[part]->SetPatch(note); + parts[part]->setPatch(note); } else { printDebug("Program change attempted on rhythm part"); } @@ -803,7 +804,7 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char case 0xE: // Pitch bender bend = (velocity << 7) | (note); //printDebug("Pitch bender %02x", bend); - parts[part]->SetBend(bend); + parts[part]->setBend(bend); break; default: printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity); @@ -936,8 +937,13 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le timbreName[10] = 0; printDebug("WRITE-PARTPATCH (%d-%d@%d..%d): %d; timbre=%d (%s)", firstPart, lastPart, off, off + len, i, absTimbreNum, timbreName); if (parts[i] != NULL) { - parts[i]->setTimbre(&mt32ram.params.timbres[parts[i]->getAbsTimbreNum()].timbre); - parts[i]->RefreshPatch(); + if (i == firstPart && off > 2) { + printDebug(" (Not updating timbre, since those values weren't touched)"); + } else { + // Not sure whether we should do this at all, really. + parts[i]->setTimbre(&mt32ram.params.timbres[parts[i]->getAbsTimbreNum()].timbre); + } + parts[i]->refreshPatch(); } } } else if (addr >= MEMADDR(0x030110) && addr < MEMADDR(0x040000)) { @@ -963,7 +969,7 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", firstDrum, lastDrum, off, off + len, i, mt32ram.params.rhythmSettings[i].outlevel, mt32ram.params.rhythmSettings[i].panpot, mt32ram.params.rhythmSettings[i].reverbSwitch, mt32ram.params.rhythmSettings[i].timbre, timbreName); } if (parts[8] != NULL) { - parts[8]->RefreshDrumCache(); + parts[8]->refreshDrumCache(); } } else if (addr >= MEMADDR(0x040000) && addr < MEMADDR(0x050000)) { int off = addr - MEMADDR(0x040000); @@ -982,7 +988,7 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le instrumentName[10] = 0; printDebug("WRITE-PARTTIMBRE (%d-%d@%d..%d): timbre=%d (%s)", firstPart, lastPart, off, off + len, i, instrumentName); if (parts[i] != NULL) { - parts[i]->RefreshPatch(); + parts[i]->refreshPatch(); } } } @@ -1053,12 +1059,12 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le // Does the real MT-32 automatically do this? if (i >= 128 && parts[8] != NULL) { // FIXME:KG: Only bother to re-cache when this timbre's actually in the rhythm map - parts[8]->SetPatch(i); // Re-cache this timbre + parts[8]->setPatch(i); // Re-cache this timbre } for (unsigned int part = 0; part < 8; part++) { if (parts[part] != NULL) { if (parts[part]->getAbsTimbreNum() == i) { - parts[part]->RefreshPatch(); + parts[part]->refreshPatch(); } } } @@ -1080,7 +1086,7 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le for (unsigned int i = 0; i < 9; i++) { //LOG(LOG_MISC|LOG_ERROR,"Part %d set to MIDI channel %d",i,mt32ram.params.system.chanAssign[i]); if (mt32ram.params.system.chanAssign[i] == 16) { - parts[i]->AllStop(); + parts[i]->allStop(); } else { chantable[(int)mt32ram.params.system.chanAssign[i]] = (char)i; } @@ -1093,12 +1099,12 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le report(ReportType_newReverbLevel, &mt32ram.params.system.reverbLevel); if ((mt32ram.params.system.reverbMode != curRevMode) || (mt32ram.params.system.reverbTime != curRevTime)) { - if (myProp.UseDefault) { + if (myProp.useDefaultReverb) { initReverb(mt32ram.params.system.reverbMode, mt32ram.params.system.reverbTime); curRevLevel = mt32ram.params.system.reverbLevel; } else { - initReverb(myProp.RevType, myProp.RevTime); - curRevLevel = myProp.RevLevel; + initReverb(myProp.reverbType, myProp.reverbTime); + curRevLevel = myProp.reverbLevel; } } @@ -1127,9 +1133,9 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le partialManager->DeactivateAll(); mt32ram = mt32default; for (int i = 0; i < 8; i++) { - parts[i]->RefreshPatch(); + parts[i]->refreshPatch(); } - parts[8]->RefreshDrumCache(); + parts[8]->refreshDrumCache(); isEnabled = false; } else { printDebug("Sysex write to unrecognised address %06x", SYSEXMEMADDR(addr)); @@ -1195,7 +1201,7 @@ int Synth::dumpSysex(char *filename) { } void ProduceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) { -#if USE_MMX > 2 +#if MT32EMU_USE_MMX > 2 //FIXME:KG: This appears to introduce crackle int donelen = i386_produceOutput1(useBuf, stream, len, volume); len -= donelen; @@ -1226,9 +1232,9 @@ void Synth::doRender(Bit16s * stream,Bit32u len) { partialManager->AgeAll(); - if (myProp.UseReverb) { + if (myProp.useReverb) { bool hasOutput = false; - for (unsigned int i = 0; i < MAXPARTIALS; i++) { + for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { if (partialManager->shouldReverb(i)) { if (partialManager->ProduceOutput(i, &tmpBuffer[0], len)) { ProduceOutput1(&tmpBuffer[0], stream, len, mastervolume); @@ -1254,7 +1260,7 @@ void Synth::doRender(Bit16s * stream,Bit32u len) { m++; } } - for (unsigned int i = 0; i < MAXPARTIALS; i++) { + for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { if (!partialManager->shouldReverb(i)) { if (partialManager->ProduceOutput(i, &tmpBuffer[0], len)) { ProduceOutput1(&tmpBuffer[0], stream, len, mastervolume); @@ -1262,7 +1268,7 @@ void Synth::doRender(Bit16s * stream,Bit32u len) { } } } else { - for (unsigned int i = 0; i < MAXPARTIALS; i++) { + for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { if (partialManager->ProduceOutput(i, &tmpBuffer[0], len)) ProduceOutput1(&tmpBuffer[0], stream, len, mastervolume); } @@ -1270,14 +1276,14 @@ void Synth::doRender(Bit16s * stream,Bit32u len) { partialManager->ClearAlreadyOutputed(); -#if MONITORPARTIALS == 1 +#if MT32EMU_MONITOR_PARTIALS == 1 samplepos += len; if (samplepos > myProp.SampleRate * 5) { samplepos = 0; int partialUsage[9]; partialManager->GetPerPartPartialUsage(partialUsage); printDebug("1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7]); - printDebug("Rhythm: %02d TOTAL: %02d", partialUsage[8], MAXPARTIALS - partialManager->GetFreePartialCount()); + printDebug("Rhythm: %02d TOTAL: %02d", partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->GetFreePartialCount()); } #endif } diff --git a/backends/midi/mt32/synth.h b/backends/midi/mt32/synth.h index f2fc3ffae6..3609592a2f 100644 --- a/backends/midi/mt32/synth.h +++ b/backends/midi/mt32/synth.h @@ -24,17 +24,14 @@ #include -// Shows number of partials MT-32 is playing -#define MONITORPARTIALS 0 - -#define ROMSIZE (512 * 1024) -#define PCMSIZE (ROMSIZE / 2) -#define GRAN 512 - class revmodel; namespace MT32Emu { +const int ROMSIZE = 512 * 1024; +const int PCMSIZE = ROMSIZE / 2; +const int GRAN = 512; + class File; class TableInitialiser; class Partial; @@ -48,38 +45,39 @@ enum ReportType { ReportType_errorDrumpat = 3, ReportType_errorPatchlog = 4, ReportType_errorMT32ROM = 5, + ReportType_errorSampleRate = 6, // HW spec - ReportType_availableSSE = 6, - ReportType_available3DNow = 7, - ReportType_usingSSE = 8, - ReportType_using3DNow = 9, + ReportType_availableSSE = 7, + ReportType_available3DNow = 8, + ReportType_usingSSE = 9, + ReportType_using3DNow = 10, // General info - ReportType_lcdMessage = 10, - ReportType_devReset = 11, - ReportType_devReconfig = 12, - ReportType_newReverbMode = 13, - ReportType_newReverbTime = 14, - ReportType_newReverbLevel = 15 + ReportType_lcdMessage = 11, + ReportType_devReset = 12, + ReportType_devReconfig = 13, + ReportType_newReverbMode = 14, + ReportType_newReverbTime = 15, + ReportType_newReverbLevel = 16 }; struct SynthProperties { // Sample rate to use in mixing - int SampleRate; + int sampleRate; // Flag to activate reverb. True = use reverb, False = no reverb - bool UseReverb; - // Flag True to use software set reverb settings, Flag False to set reverb settings in + bool useReverb; + // True to use software set reverb settings, False to set reverb settings in // following parameters - bool UseDefault; + bool useDefaultReverb; // When not using the default settings, this specifies one of the 4 reverb types // 1 = Room 2 = Hall 3 = Plate 4 = Tap - unsigned char RevType; + unsigned char reverbType; // This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement) - unsigned char RevTime; + unsigned char reverbTime; // This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement) - unsigned char RevLevel; + unsigned char reverbLevel; // The name of the directory in which the ROM and data files are stored (with trailing slash/backslash) // Not used if "openFile" is set. May be NULL in any case. char *baseDir; @@ -116,15 +114,14 @@ friend class TableInitialiser; private: bool isEnabled; - sampleFormat PCM[54]; - sampleTable PCMList[128]; - Bit32u PCMReassign[54]; + PCMWave PCM[54]; + PCMWaveEntry PCMList[128]; Bit32s PCMLoopTable[54]; Bit16s romfile[PCMSIZE + GRAN]; Bit8s chantable[32]; - #if MONITORPARTIALS == 1 + #if MT32EMU_MONITOR_PARTIALS == 1 static Bit32s samplepos = 0; #endif diff --git a/backends/midi/mt32/tables.cpp b/backends/midi/mt32/tables.cpp index b09b3c58bc..3afd03e226 100644 --- a/backends/midi/mt32/tables.cpp +++ b/backends/midi/mt32/tables.cpp @@ -25,15 +25,6 @@ #include "mt32emu.h" -// Determines how the waveform cache file is handled (must be regenerated after sampling rate change) -#define WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache -//#define WAVECACHEMODE 1 // Load existing cache if possible, otherwise generage but don't save cache -//#define WAVECACHEMODE 2 // Ignore existing cache, generate and save cache -//#define WAVECACHEMODE 3 // Ignore existing cache, generate but don't save cache - -// Constant tuning for now -#define TUNING 440.0f - namespace MT32Emu { //Amplitude time velocity follow exponential coefficients @@ -95,26 +86,25 @@ Bit32s envtimetable[101]; Bit32s decaytimetable[101]; Bit32s lasttimetable[101]; Bit32s voltable[128]; -Bit32s bendtable[49]; float ResonFactor[31]; float ResonInv[31]; // Per-note initialisation tables follow -Bit16s freqtable[NUMNOTES]; -Bit32s fildeptable[5][NUMNOTES]; -Bit32s timekeytable[5][NUMNOTES]; -int filttable[2][NUMNOTES][201]; -int nfilttable[NUMNOTES][101][101]; +Bit16s freqtable[NUM_NOTES]; +Bit32s fildeptable[5][NUM_NOTES]; +Bit32s timekeytable[5][NUM_NOTES]; +int filttable[2][NUM_NOTES][201]; +int nfilttable[NUM_NOTES][101][101]; -Bit32s divtable[NUMNOTES]; -Bit32s smalldivtable[NUMNOTES]; -Bit32u wavtabler[54][NUMNOTES]; -Bit32u looptabler[9][10][NUMNOTES]; -Bit32s sawtable[NUMNOTES][101]; +Bit32s divtable[NUM_NOTES]; +Bit32s smalldivtable[NUM_NOTES]; +Bit32u wavtabler[54][NUM_NOTES]; +Bit32u looptabler[9][10][NUM_NOTES]; +Bit32s sawtable[NUM_NOTES][101]; -Bit16s *waveforms[4][NUMNOTES]; -Bit32u waveformsize[4][NUMNOTES]; +Bit16s *waveforms[4][NUM_NOTES]; +Bit32u waveformsize[4][NUM_NOTES]; static Bit16s tmpforms[4][65536]; @@ -126,7 +116,7 @@ static Bit16s tmpforms[4][65536]; static void prewarp(double *a1, double *a2, double fc, double fs) { double wp; - wp = 2.0 * fs * tan(PI * fc / fs); + wp = 2.0 * fs * tan(DOUBLE_PI * fc / fs); *a2 = *a2 / (wp * wp); *a1 = *a1 / wp; @@ -331,8 +321,6 @@ void TableInitialiser::initMT32ConstantTables(Synth *synth) { //FIXME:KG: I'm fairly sure this is wrong... lf=100 should yield no fine-tuning (4096)? finetable[lf] = (int)((powf(2.0f, (((float)lf / 200.0f) - 1.0f) / 12.0f)) * 4096.0f); } - for (lf = 0; lf <= 48; lf++) - bendtable[lf] = (int)((powf(2.0f, (((float)lf / 12.0f) - 2.0f))) * 4096.0f); float lff; for (lf = 0; lf < 128; lf++) { @@ -345,7 +333,7 @@ void TableInitialiser::initMT32ConstantTables(Synth *synth) { for (lf = 0; lf < 128; lf++) { // Converts MIDI velocity to volume. - voltable[lf] = (int)(127.0 * pow((float)lf / 127.0, LN)); + voltable[lf] = (int)(127.0 * pow((float)lf / 127.0, DOUBLE_LN)); } for (lf = 0; lf < MAX_SAMPLE_OUTPUT; lf++) { int myRand; @@ -440,7 +428,7 @@ void TableInitialiser::initMT32ConstantTables(Synth *synth) { dval = 1; ampbiastable[lf][distval] = 256; } else { - amplog = powf(1.431817011f, (float)lf) / (float)PI; + amplog = powf(1.431817011f, (float)lf) / FLOAT_PI; dval = ((128.0f - (float)distval) / 128.0f); amplog = expf(amplog); dval = powf(amplog, dval) / amplog; @@ -459,8 +447,8 @@ void TableInitialiser::initMT32ConstantTables(Synth *synth) { dval = 1; fbiastable[lf][distval] = 256; } else { - //amplog = pow(1.431817011, filval) / (float)PI; - amplog = powf(1.531817011f, filval) / (float)PI; + //amplog = pow(1.431817011, filval) / FLOAT_PI; + amplog = powf(1.531817011f, filval) / FLOAT_PI; dval = (128.0f - (float)distval) / 128.0f; amplog = expf(amplog); dval = powf(amplog,dval)/amplog; @@ -519,7 +507,7 @@ static void initDep(int f) { } void TableInitialiser::initWave(Synth *synth, File *file, bool reading, bool writing, int f, float freq, float rate, double ampsize, Bit32s div) { - double sd = (2.0*PI)/((((float)div/65536.0)) * 4.0); + double sd = (2.0 * DOUBLE_PI) / ((((float)div / 65536.0)) * 4.0); waveformsize[0][f] = div >> 14; waveformsize[1][f] = div >> 14; waveformsize[2][f] = div >> 14; @@ -541,15 +529,15 @@ void TableInitialiser::initWave(Synth *synth, File *file, bool reading, bool wri int fa = 0; memset(tmpforms, 0,sizeof(tmpforms)); - while (sa <= (2.0 * PI)) { + while (sa <= (2.0 * DOUBLE_PI)) { float sqp; - if (sa < PI) { + if (sa < DOUBLE_PI) { sqp = -1; - sqp = (float)(sqp + (0.25 * (sa/PI))); + sqp = (float)(sqp + (0.25 * (sa/DOUBLE_PI))); } else { sqp=1; - sqp = (float)(sqp - (0.25 * ((sa-PI)/PI))); + sqp = (float)(sqp - (0.25 * ((sa - DOUBLE_PI)/DOUBLE_PI))); } saw = 0; @@ -565,8 +553,8 @@ void TableInitialiser::initWave(Synth *synth, File *file, bool reading, bool wri tmpforms[1][fa] = (Bit16s)(saw * ampsize / 2); tmpforms[2][fa] = (Bit16s)(cos(dumbfire) * -ampsize); - tmpforms[3][fa * 2] = (Bit16s)(cos(sa - PI) * -ampsize); - tmpforms[3][fa * 2 + 1] = (Bit16s)(cos((sa + (sd / 2)) - PI) * -ampsize); + tmpforms[3][fa * 2] = (Bit16s)(cos(sa - DOUBLE_PI) * -ampsize); + tmpforms[3][fa * 2 + 1] = (Bit16s)(cos((sa + (sd / 2)) - DOUBLE_PI) * -ampsize); fa++; sa += sd; @@ -628,7 +616,7 @@ static void initNFiltTable(int f, float freq, float rate) { } } -void TableInitialiser::initNotes(Synth *synth, sampleFormat pcms[54], float rate) { +void TableInitialiser::initNotes(Synth *synth, PCMWave pcms[54], float rate) { char filename[32]; int intRate = (int)rate; sprintf(filename, "waveformcache-%d.raw", intRate); @@ -645,7 +633,7 @@ void TableInitialiser::initNotes(Synth *synth, sampleFormat pcms[54], float rate header[pos++] = (char)((intRate >> 8) & 0xFF); header[pos++] = (char)((intRate >> 16) & 0xFF); header[pos] = (char)((intRate >> 24) & 0xFF); -#if WAVECACHEMODE < 2 +#if MT32EMU_WAVECACHEMODE < 2 file = synth->openFile(filename, File::OpenMode_read); if (file != NULL) { char fileHeader[16]; @@ -662,7 +650,7 @@ void TableInitialiser::initNotes(Synth *synth, sampleFormat pcms[54], float rate synth->printDebug("Unable to open %s for reading - will generate", filename); } #endif -#if WAVECACHEMODE == 0 || WAVECACHEMODE == 2 +#if MT32EMU_WAVECACHEMODE == 0 || MT32EMU_WAVECACHEMODE == 2 if (!reading) { file = synth->openFile(filename, File::OpenMode_write); if (file != NULL) { @@ -710,9 +698,10 @@ void TableInitialiser::initNotes(Synth *synth, sampleFormat pcms[54], float rate synth->closeFile(file); } -void TableInitialiser::initMT32Tables(Synth *synth, sampleFormat pcms[54], float sampleRate) { +bool TableInitialiser::initMT32Tables(Synth *synth, PCMWave pcms[54], float sampleRate) { if (sampleRate <= 0.0f) { synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate); + return false; } if (initialisedSampleRate == 0.0f) { initMT32ConstantTables(synth); @@ -722,6 +711,7 @@ void TableInitialiser::initMT32Tables(Synth *synth, sampleFormat pcms[54], float } // This always needs to be done, to allocate the waveforms initNotes(synth, pcms, sampleRate); + return true; } } diff --git a/backends/midi/mt32/tables.h b/backends/midi/mt32/tables.h index 1f4fdab8b2..3e73400edf 100644 --- a/backends/midi/mt32/tables.h +++ b/backends/midi/mt32/tables.h @@ -22,44 +22,50 @@ #ifndef MT32EMU_TABLES_H #define MT32EMU_TABLES_H +namespace MT32Emu { + // Mathematical constants -#ifndef PI -#define PI 3.1415926535897932384626433832795 -#endif -#ifndef LN -#define LN 2.30258509 -#endif +const double DOUBLE_PI = 3.1415926535897932384626433832795; +const double DOUBLE_LN = 2.3025850929940456840179914546844; +const float FLOAT_PI = 3.1415926535897932384626433832795f; +const float FLOAT_LN = 2.3025850929940456840179914546844f; // Filter settings -#define FILTERGRAN 512 +const int FILTERGRAN = 512; -#define MIDDLEC 60 +const int MIDDLEC = 60; +const int MIDDLEA = 69; // By this I mean "A above middle C" -#define NUMNOTES 128 // MIDI supports 128 notes/keys +// Constant tuning for now. The manual claims "Standard pitch" is 442.0. +// I assume they mean this is the MT-32 default pitch, and not concert pitch, +// since the latter has been internationally defined as 440Hz for decades. +// FIXME:KG: Keeping it at 440.0f for now, as in original. Check with CC +const float TUNING = 440.0f; -// Amplitude of waveform generator -#define WGAMP (7168) -//#define WGAMP (8192) +const int NUM_NOTES = 128; // MIDI supports 128 notes/keys -namespace MT32Emu { +// Amplitude of waveform generator +const int WGAMP = 7168; // 8192? class Synth; +extern const Bit8s LoopPatterns[9][10]; + extern Bit16s smallnoise[MAX_SAMPLE_OUTPUT]; // Some optimization stuff extern Bit32s keytable[217]; -extern Bit32s divtable[NUMNOTES]; -extern Bit32s smalldivtable[NUMNOTES]; -extern Bit32u wavtabler[54][NUMNOTES]; -extern Bit32u looptabler[9][10][NUMNOTES]; +extern Bit32s divtable[NUM_NOTES]; +extern Bit32s smalldivtable[NUM_NOTES]; +extern Bit32u wavtabler[54][NUM_NOTES]; +extern Bit32u looptabler[9][10][NUM_NOTES]; extern Bit16s sintable[65536]; extern Bit32u lfotable[101]; extern Bit32s penvtable[16][101]; extern Bit32s filveltable[128][101]; extern Bit32s veltkeytable[5][128]; extern Bit32s pulsetable[101]; -extern Bit32s sawtable[NUMNOTES][101]; +extern Bit32s sawtable[NUM_NOTES][101]; extern Bit32s ampbiastable[13][128]; extern Bit32s fbiastable[15][128]; extern float filtcoeff[FILTERGRAN][31][8]; @@ -73,23 +79,23 @@ extern Bit32s lasttimetable[101]; extern Bit32s voltable[128]; extern float ResonInv[31]; -extern Bit16s freqtable[NUMNOTES]; -extern Bit32s fildeptable[5][NUMNOTES]; -extern Bit32s timekeytable[5][NUMNOTES]; -extern int filttable[2][NUMNOTES][201]; -extern int nfilttable[NUMNOTES][101][101]; +extern Bit16s freqtable[NUM_NOTES]; +extern Bit32s fildeptable[5][NUM_NOTES]; +extern Bit32s timekeytable[5][NUM_NOTES]; +extern int filttable[2][NUM_NOTES][201]; +extern int nfilttable[NUM_NOTES][101][101]; extern const Bit8s LoopPatterns[9][10]; -extern Bit16s *waveforms[4][NUMNOTES]; -extern Bit32u waveformsize[4][NUMNOTES]; +extern Bit16s *waveforms[4][NUM_NOTES]; +extern Bit32u waveformsize[4][NUM_NOTES]; class TableInitialiser { - void initMT32ConstantTables(Synth *synth); - void initWave(Synth *synth, File *file, bool reading, bool writing, int f, float freq, float rate, double ampsize, Bit32s div); - void initNotes(Synth *synth, sampleFormat pcms[54], float rate); + static void initMT32ConstantTables(Synth *synth); + static void initWave(Synth *synth, File *file, bool reading, bool writing, int f, float freq, float rate, double ampsize, Bit32s div); + static void initNotes(Synth *synth, PCMWave pcms[54], float rate); public: - void initMT32Tables(Synth *synth, sampleFormat pcms[54], float sampleRate); + static bool initMT32Tables(Synth *synth, PCMWave pcms[54], float sampleRate); }; } -- cgit v1.2.3