aboutsummaryrefslogtreecommitdiff
path: root/backends
diff options
context:
space:
mode:
authorJerome Fisher2004-11-28 05:35:07 +0000
committerJerome Fisher2004-11-28 05:35:07 +0000
commit077d19f5008e5cb911b1291f59b63fc46da18cc8 (patch)
treed3361671ac04d9d7f0e5e4354a78f4bd48a847d1 /backends
parent67b8abac08befed3e545dd500df4f28b7524348a (diff)
downloadscummvm-rg350-077d19f5008e5cb911b1291f59b63fc46da18cc8.tar.gz
scummvm-rg350-077d19f5008e5cb911b1291f59b63fc46da18cc8.tar.bz2
scummvm-rg350-077d19f5008e5cb911b1291f59b63fc46da18cc8.zip
- Added graphical representation of initialisation progress. This is quite hacky.
- Initialisation is now interruptible. - All data is now loaded from MT32_CONTROL.ROM. drumpat.rom, Preset1.syx, Preset2.syx and patchlog.cfg are no longer used. - Major cleanup. In particular, separated Rhythm part into a new class, instead of dealing with it as a special case everywhere. - Improved accuracy of pitch key-follow. - Recaching now happens lazily. - Changed some right-shifts to divs, due to the former not being arithmetic on some architectures. - Setting "MT32EMU_ACCURATENOTES" to 1 will generate lookup tables for the exact frequency of every note played. Not recommended. - Several small bugs fixed. svn-id: r15929
Diffstat (limited to 'backends')
-rw-r--r--backends/midi/mt32.cpp232
-rw-r--r--backends/midi/mt32/mt32_file.h2
-rw-r--r--backends/midi/mt32/mt32emu.h4
-rw-r--r--backends/midi/mt32/part.cpp505
-rw-r--r--backends/midi/mt32/part.h66
-rw-r--r--backends/midi/mt32/partial.cpp287
-rw-r--r--backends/midi/mt32/partial.h35
-rw-r--r--backends/midi/mt32/partialManager.cpp49
-rw-r--r--backends/midi/mt32/partialManager.h20
-rw-r--r--backends/midi/mt32/structures.h179
-rw-r--r--backends/midi/mt32/synth.cpp800
-rw-r--r--backends/midi/mt32/synth.h84
-rw-r--r--backends/midi/mt32/tables.cpp126
-rw-r--r--backends/midi/mt32/tables.h28
14 files changed, 1184 insertions, 1233 deletions
diff --git a/backends/midi/mt32.cpp b/backends/midi/mt32.cpp
index 73775639f1..921141d4ea 100644
--- a/backends/midi/mt32.cpp
+++ b/backends/midi/mt32.cpp
@@ -1,10 +1,6 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2001-2004 The ScummVM project
*
- * YM2612 tone generation code written by Tomoaki Hayasaka.
- * Used under the terms of the GNU General Public License.
- * Adpated to ScummVM by Jamieson Christian.
- *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -36,6 +32,9 @@
#include "common/file.h"
#include "common/config-manager.h"
+#include "graphics/font.h"
+#include "graphics/surface.h"
+
class MidiChannel_MT32 : public MidiChannel_MPU401 {
void effectLevel(byte value) { }
void chorusLevel(byte value) { }
@@ -54,6 +53,8 @@ protected:
void generateSamples(int16 *buf, int len);
public:
+ bool _initialising;
+
MidiDriver_MT32(SoundMixer *mixer);
virtual ~MidiDriver_MT32();
@@ -109,6 +110,54 @@ public:
}
};
+static int eatSystemEvents() {
+ OSystem::Event event;
+ while (g_system->pollEvent(event)) {
+ switch (event.event_code) {
+ case OSystem::EVENT_QUIT:
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void drawProgress(float progress) {
+ Graphics::Surface surf;
+ uint32 borderColor = 0x2;
+ uint32 fillColor = 0x4;
+ surf.w = g_system->getWidth() / 7 * 5;
+ surf.h = Graphics::g_sysfont.getFontHeight();
+ int x = g_system->getWidth() / 7;
+ int y = g_system->getHeight() / 2 - surf.h / 2;
+ surf.pitch = surf.w;
+ surf.bytesPerPixel = 1;
+ surf.pixels = calloc(surf.w, surf.h);
+ Common::Rect r(surf.w, surf.h);
+ surf.frameRect(r, borderColor);
+ r.grow(-1);
+ r.right = r.left + (uint16)(r.width() * progress);
+ surf.fillRect(r, fillColor);
+ g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, x, y, surf.w, surf.h);
+ g_system->updateScreen();
+ free(surf.pixels);
+}
+
+static void drawMessage(int offset, const Common::String &text) {
+ const Graphics::Font &font(Graphics::g_sysfont);
+ Graphics::Surface surf;
+ uint32 color = 0x2;
+ surf.w = g_system->getWidth();
+ surf.h = font.getFontHeight();
+ surf.pitch = surf.w;
+ surf.bytesPerPixel = 1;
+ surf.pixels = calloc(surf.w, surf.h);
+ font.drawString(&surf, text, 0, 0, surf.w, color, Graphics::kTextAlignCenter);
+ int y = g_system->getHeight() / 2 - font.getFontHeight() / 2 + offset * (font.getFontHeight() + 1);
+ g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, 0, y, surf.w, surf.h);
+ g_system->updateScreen();
+ free(surf.pixels);
+}
+
static MT32Emu::File *MT32_OpenFile(void *userData, const char *filename, MT32Emu::File::OpenMode mode) {
MT32File *file = new MT32File();
if (!file->open(filename, mode)) {
@@ -119,32 +168,36 @@ static MT32Emu::File *MT32_OpenFile(void *userData, const char *filename, MT32Em
}
static void MT32_PrintDebug(void *userData, const char *fmt, va_list list) {
+ char buf[512];
+ if (((MidiDriver_MT32 *)userData)->_initialising) {
+ vsprintf(buf, fmt, list);
+ buf[70] = 0; // Truncate to a reasonable length
+ drawMessage(1, buf);
+ }
//vdebug(0, fmt, list); // FIXME: Use a higher debug level
}
-static void MT32_Report(void *userData, MT32Emu::ReportType type, const void *reportData) {
+static int MT32_Report(void *userData, MT32Emu::ReportType type, const void *reportData) {
switch(type) {
case MT32Emu::ReportType_lcdMessage:
g_system->displayMessageOnOSD((const char *)reportData);
break;
- case MT32Emu::ReportType_errorPreset1:
- error("Couldn't open Preset1.syx file");
+ case MT32Emu::ReportType_errorControlROM:
+ error("Failed to load MT32_CONTROL.ROM");
break;
- case MT32Emu::ReportType_errorPreset2:
- error("Couldn't open Preset2.syx file");
+ case MT32Emu::ReportType_errorPCMROM:
+ error("Failed to load MT32_PCM.ROM");
break;
- case MT32Emu::ReportType_errorDrumpat:
- error("Couldn't open drumpat.rom file");
- break;
- case MT32Emu::ReportType_errorPatchlog:
- error("Couldn't open patchlog.cfg file");
- break;
- case MT32Emu::ReportType_errorMT32ROM:
- error("Couldn't open MT32_PCM.ROM file");
+ case MT32Emu::ReportType_progressInit:
+ if (((MidiDriver_MT32 *)userData)->_initialising) {
+ drawProgress(*((float *)reportData));
+ return eatSystemEvents();
+ }
break;
default:
break;
}
+ return 0;
}
////////////////////////////////////////
@@ -160,10 +213,15 @@ MidiDriver_MT32::MidiDriver_MT32(SoundMixer *mixer) : MidiDriver_Emulated(mixer)
_midiChannels[i].init(this, i);
}
_synth = NULL;
-
- _baseFreq = 1000;
-
+ // A higher baseFreq reduces the length used in generateSamples(),
+ // and means that the timer callback will be called more often.
+ // That results in more accurate timing.
+ _baseFreq = 10000;
+ // Unfortunately bugs in the emulator cause inaccurate tuning
+ // at rates other than 32KHz, thus we produce data at 32KHz and
+ // rely on SoundMixer to convert.
_outputRate = 32000; //_mixer->getOutputRate();
+ _initialising = false;
}
MidiDriver_MT32::~MidiDriver_MT32() {
@@ -186,15 +244,19 @@ int MidiDriver_MT32::open() {
prop.reverbType = 0;
prop.reverbTime = 5;
prop.reverbLevel = 3;
+ prop.userData = this;
prop.printDebug = MT32_PrintDebug;
prop.report = MT32_Report;
prop.openFile = MT32_OpenFile;
_synth = new MT32Emu::Synth();
+ _initialising = true;
+ drawMessage(-1, "Initialising MT-32 Emulator");
if (!_synth->open(prop))
return MERR_DEVICE_NOT_AVAILABLE;
-
+ _initialising = false;
+ g_system->clearScreen();
+ g_system->updateScreen();
_mixer->playInputStream(&_handle, this, false, 255, 0, -1, false);
-
return 0;
}
@@ -232,7 +294,9 @@ void MidiDriver_MT32::close() {
return;
_isOpen = false;
- // Detach the premix callback handler
+ // Detach the player callback handler
+ setTimerCallback(NULL, NULL);
+ // Detach the mixer callback handler
_mixer->stopHandle(_handle);
_synth->close();
@@ -273,6 +337,128 @@ MidiChannel *MidiDriver_MT32::getPercussionChannel() {
return &_midiChannels[9];
}
+// This code should be used when calling the timer callback from the mixer thread is undesirable.
+// Note that it results in less accurate timing.
+#if 0
+class MidiEvent_MT32 {
+public:
+ MidiEvent_MT32 *_next;
+ uint32 _msg; // 0xFFFFFFFF indicates a sysex message
+ byte *_data;
+ uint32 _len;
+
+ MidiEvent_MT32(uint32 msg, byte *data, uint32 len) {
+ _msg = msg;
+ if (len > 0) {
+ _data = new byte[len];
+ memcpy(_data, data, len);
+ }
+ _len = len;
+ _next = NULL;
+ }
+
+ MidiEvent_MT32() {
+ if (_len > 0)
+ delete _data;
+ }
+};
+
+class MidiDriver_ThreadedMT32 : public MidiDriver_MT32 {
+private:
+ OSystem::MutexRef _eventMutex;
+ MidiEvent_MT32 *_events;
+ Timer::TimerProc _timer_proc;
+
+ void pushMidiEvent(MidiEvent_MT32 *event);
+ MidiEvent_MT32 *popMidiEvent();
+
+protected:
+ void send(uint32 b);
+ void sysEx(byte *msg, uint16 length);
+
+public:
+ MidiDriver_ThreadedMT32(SoundMixer *mixer);
+ virtual ~MidiDriver_ThreadedMT32();
+
+ void onTimer();
+ void close();
+ void setTimerCallback(void *timer_param, Timer::TimerProc timer_proc);
+};
+
+
+MidiDriver_ThreadedMT32::MidiDriver_ThreadedMT32(SoundMixer *mixer) : MidiDriver_MT32(mixer) {
+ _eventMutex = g_system->createMutex();
+ _events = NULL;
+ _timer_proc = NULL;
+}
+
+MidiDriver_ThreadedMT32::~MidiDriver_ThreadedMT32() {
+ g_system->deleteMutex(_eventMutex);
+}
+
+void MidiDriver_ThreadedMT32::close() {
+ MidiDriver_MT32::close();
+ while ((popMidiEvent() != NULL)) {
+ // Just eat any leftover events
+ }
+}
+
+void MidiDriver_ThreadedMT32::setTimerCallback(void *timer_param, Timer::TimerProc timer_proc) {
+ if (!_timer_proc || !timer_proc) {
+ if (_timer_proc)
+ g_timer->removeTimerProc(_timer_proc);
+ _timer_proc = timer_proc;
+ if (timer_proc)
+ g_timer->installTimerProc(timer_proc, getBaseTempo(), timer_param);
+ }
+}
+
+void MidiDriver_ThreadedMT32::pushMidiEvent(MidiEvent_MT32 *event) {
+ g_system->lockMutex(_eventMutex);
+ if (_events == NULL) {
+ _events = event;
+ } else {
+ MidiEvent_MT32 *last = _events;
+ while (last->_next != NULL)
+ last = last->_next;
+ last->_next = event;
+ }
+ g_system->unlockMutex(_eventMutex);
+}
+
+MidiEvent_MT32 *MidiDriver_ThreadedMT32::popMidiEvent() {
+ MidiEvent_MT32 *event;
+ g_system->lockMutex(_eventMutex);
+ event = _events;
+ if (event != NULL)
+ _events = event->_next;
+ g_system->unlockMutex(_eventMutex);
+ return event;
+}
+
+void MidiDriver_ThreadedMT32::send(uint32 b) {
+ MidiEvent_MT32 *event = new MidiEvent_MT32(b, NULL, 0);
+ pushMidiEvent(event);
+}
+
+void MidiDriver_ThreadedMT32::sysEx(byte *msg, uint16 length) {
+ MidiEvent_MT32 *event = new MidiEvent_MT32(0xFFFFFFFF, msg, length);
+ pushMidiEvent(event);
+}
+
+void MidiDriver_ThreadedMT32::onTimer() {
+ MidiEvent_MT32 *event;
+ while ((event = popMidiEvent()) != NULL) {
+ if (event->_msg == 0xFFFFFFFF) {
+ MidiDriver_MT32::sysEx(event->_data, event->_len);
+ } else {
+ MidiDriver_MT32::send(event->_msg);
+ }
+ delete event;
+ }
+}
+#endif
+
////////////////////////////////////////
//
// MidiDriver_MT32 factory
diff --git a/backends/midi/mt32/mt32_file.h b/backends/midi/mt32/mt32_file.h
index f5888db5e3..7ebb6449b6 100644
--- a/backends/midi/mt32/mt32_file.h
+++ b/backends/midi/mt32/mt32_file.h
@@ -41,7 +41,7 @@ public:
virtual bool readBit32u(Bit32u *in);
virtual size_t write(const void *out, size_t size) = 0;
virtual bool writeBit8u(Bit8u out) = 0;
- // Note: May write some a single byte to the file before failing
+ // Note: May write a single byte to the file before failing
virtual bool writeBit16u(Bit16u out);
// Note: May write some (<4) bytes to the file before failing
virtual bool writeBit32u(Bit32u out);
diff --git a/backends/midi/mt32/mt32emu.h b/backends/midi/mt32/mt32emu.h
index 48c3d903fd..9fffa721f5 100644
--- a/backends/midi/mt32/mt32emu.h
+++ b/backends/midi/mt32/mt32emu.h
@@ -27,10 +27,6 @@
#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_DUMP_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
diff --git a/backends/midi/mt32/part.cpp b/backends/midi/mt32/part.cpp
index 6fe783e1b6..c240874143 100644
--- a/backends/midi/mt32/part.cpp
+++ b/backends/midi/mt32/part.cpp
@@ -34,16 +34,19 @@ static const Bit8u PartialMixStruct[13] = {
0, 1, 0, 1, 1, 0,
1, 3, 3, 2, 2, 2, 2 };
-// This caches the timbres/settings in use by the rhythm part
-static PatchCache drumCache[94][4];
-static StereoVolume drumPan[64];
+static const float floatKeyfollow[17] = {
+ -1.0f, -1.0f/2.0f, -1.0f/4.0f, 0.0f,
+ 1.0f/8.0f, 1.0f/4.0f, 3.0f/8.0f, 1.0f/2.0f, 5.0f/8.0f, 3.0f/4.0f, 7.0f/8.0f, 1.0f,
+ 5.0f/4.0f, 3.0f/2.0f, 2.0f,
+ 1.0009765625f, 1.0048828125f
+};
//FIXME:KG: Put this dpoly stuff somewhere better
-bool dpoly::isActive() {
+bool dpoly::isActive() const {
return partials[0] != NULL || partials[1] != NULL || partials[2] != NULL || partials[3] != NULL;
}
-Bit64s dpoly::getAge() {
+Bit64s dpoly::getAge() const {
for (int i = 0; i < 4; i++) {
if (partials[i] != NULL) {
return partials[i]->age;
@@ -52,36 +55,34 @@ Bit64s dpoly::getAge() {
return 0;
}
-Part::Part(Synth *useSynth, int usePartNum) {
+RhythmPart::RhythmPart(Synth *useSynth, unsigned int usePartNum): Part(useSynth, usePartNum) {
+ strcpy(name, "Rhythm");
+ rhythmTemp = &synth->mt32ram.rhythmSettings[0];
+ refresh();
+}
+
+Part::Part(Synth *useSynth, unsigned int usePartNum) {
this->synth = useSynth;
this->partNum = usePartNum;
- isRhythm = (usePartNum == 8);
+ patchCache[0].dirty = true;
holdpedal = false;
- if (isRhythm) {
- strcpy(name, "Rhythm");
+ if (usePartNum == 8) {
+ // Nasty hack for rhythm
patchTemp = NULL;
timbreTemp = NULL;
- rhythmTemp = &synth->mt32ram.params.rhythmSettings[0];
} else {
sprintf(name, "Part %d", partNum + 1);
- patchTemp = &synth->mt32ram.params.patchSettings[partNum];
- timbreTemp = &synth->mt32ram.params.timbreSettings[partNum];
- rhythmTemp = NULL;
+ patchTemp = &synth->mt32ram.patchSettings[partNum];
+ timbreTemp = &synth->mt32ram.timbreSettings[partNum];
}
currentInstr[0] = 0;
currentInstr[10] = 0;
- volume = 102;
+ volume = voltable[102]; //FIXME:KG: Original was just volume=102; I assume this is intended
volumesetting.leftvol = 32767;
volumesetting.rightvol = 32767;
bend = 0.0f;
memset(polyTable,0,sizeof(polyTable));
memset(patchCache, 0, sizeof(patchCache));
-
- if (isRhythm) {
- init = true;
- refreshDrumCache();
- }
- init = false;
}
void Part::setHoldPedal(bool pedalval) {
@@ -90,16 +91,17 @@ void Part::setHoldPedal(bool pedalval) {
holdpedal = pedalval;
}
-void Part::setBend(int midiBend) {
- if (isRhythm) {
- synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend);
- return;
- }
- // FIXME:KG: Slightly uneven increments, but I wanted min -1.0, centre 0.0 and max 1.0
+void RhythmPart::setBend(unsigned int midiBend) {
+ synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend);
+ return;
+}
+
+void Part::setBend(unsigned int midiBend) {
+ // FIXME:KG: Slightly unbalanced increments, but I wanted min -1.0, centre 0.0 and max 1.0
if (midiBend <= 0x2000) {
- bend = (midiBend - 0x2000) / (float)0x2000;
+ bend = ((signed int)midiBend - 0x2000) / (float)0x2000;
} else {
- bend = (midiBend - 0x2000) / (float)0x1FFF;
+ bend = ((signed int)midiBend - 0x2000) / (float)0x1FFF;
}
// Loop through all partials to update their bend
for (int i = 0; i < MT32EMU_MAX_POLY; i++) {
@@ -111,15 +113,15 @@ void Part::setBend(int midiBend) {
}
}
-void Part::setModulation(int vol) {
- if (isRhythm) {
- synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, vol);
- return;
- }
+void RhythmPart::setModulation(unsigned int midiModulation) {
+ synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, midiModulation);
+}
+
+void Part::setModulation(unsigned int midiModulation) {
// Just a bloody guess, as always, before I get things figured out
for (int t = 0; t < 4; t++) {
if (patchCache[t].playPartial) {
- int newrate = (patchCache[t].modsense * vol) >> 7;
+ int newrate = (patchCache[t].modsense * midiModulation) >> 7;
//patchCache[t].lfoperiod = lfotable[newrate];
patchCache[t].lfodepth = newrate;
//FIXME:KG: timbreTemp->partial[t].lfo.depth =
@@ -127,52 +129,78 @@ void Part::setModulation(int vol) {
}
}
-void Part::refreshDrumCache() {
- if (!isRhythm) {
- synth->printDebug("ERROR: RefreshDrumCache() called on non-rhythm part");
- }
- // Cache drum patches
- for (int m = 0; m < 64; m++) {
- int drumTimbre = rhythmTemp[m].timbre;
- if (drumTimbre >= 94)
+void RhythmPart::refresh() {
+ // (Re-)cache all the mapped timbres ahead of time
+ for (unsigned int drumNum = 0; drumNum < 64; drumNum++) {
+ int drumTimbreNum = rhythmTemp[drumNum].timbre;
+ if (drumTimbreNum >= 94)
continue;
- 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...
+ Bit16s pan = rhythmTemp[drumNum].panpot; // They use R-L 0-14...
+ // FIXME:KG: Panning cache should be backed up to partials using it, too
// FIXME:KG: If I don't have left/right mixed up here, it's pure luck
if (pan < 7) {
- drumPan[m].leftvol = 32767;
- drumPan[m].rightvol = pan * 4681;
+ drumPan[drumNum].leftvol = 32767;
+ drumPan[drumNum].rightvol = pan * 4681;
} else {
- drumPan[m].rightvol = 32767;
- drumPan[m].leftvol = (14 - pan) * 4681;
+ drumPan[drumNum].rightvol = 32767;
+ drumPan[drumNum].leftvol = (14 - pan) * 4681;
}
+ PatchCache *cache = drumCache[drumNum];
+ backupCacheToPartials(cache);
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ cache[t].dirty = true;
+ cache[t].pitchShift = 0.0f;
+ cache[t].benderRange = 0.0f;
+ cache[t].pansetptr = &drumPan[drumNum];
+ cache[t].reverb = rhythmTemp[drumNum].reverbSwitch > 0;
+ }
+ }
+}
+
+void Part::refresh() {
+ backupCacheToPartials(patchCache);
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ patchCache[t].dirty = true;
+ patchCache[t].pitchShift = (patchTemp->patch.keyShift - 24) + (patchTemp->patch.fineTune - 50) / 100.0f;
+ patchCache[t].benderRange = patchTemp->patch.benderRange;
+ patchCache[t].pansetptr = &volumesetting;
+ patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0;
+ }
+ memcpy(currentInstr, timbreTemp->common.name, 10);
+}
+
+void RhythmPart::refreshTimbre(unsigned int absTimbreNum) {
+ for (int m = 0; m < 64; m++) {
+ if (rhythmTemp[m].timbre == absTimbreNum - 128)
+ drumCache[m][0].dirty = true;
+ }
+}
+
+void Part::refreshTimbre(unsigned int absTimbreNum) {
+ if (getAbsTimbreNum() == absTimbreNum) {
+ memcpy(currentInstr, timbreTemp->common.name, 10);
+ patchCache[0].dirty = true;
}
}
int Part::fixBiaslevel(int srcpnt, int *dir) {
- int noteat = srcpnt & 63;
+ int noteat = srcpnt & 0x3F;
int outnote;
- *dir = 1;
if (srcpnt < 64)
*dir = 0;
+ else
+ *dir = 1;
outnote = 33 + noteat;
//synth->printDebug("Bias note %d, dir %d", outnote, *dir);
return outnote;
}
-int Part::fixKeyfollow(int srckey, int *dir) {
+int Part::fixKeyfollow(int srckey) {
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};
-
- if (srckey<3)
- *dir = -1;
- else if (srckey==3)
- *dir = 0;
- else
- *dir = 1;
-
+ 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};
return keyfix[srckey];
} else {
//LOG(LOG_ERROR|LOG_MISC,"Missed key: %d", srckey);
@@ -180,10 +208,6 @@ int Part::fixKeyfollow(int srckey, int *dir) {
}
}
-void Part::refreshPatch() {
- setPatch(-1);
-}
-
void Part::abortPoly(dpoly *poly) {
if (!poly->isPlaying) {
return;
@@ -197,7 +221,7 @@ void Part::abortPoly(dpoly *poly) {
poly->isPlaying = false;
}
-void Part::setPatch(PatchParam *patch) {
+void Part::setPatch(const PatchParam *patch) {
patchTemp->patch = *patch;
}
@@ -205,187 +229,165 @@ void Part::setTimbre(TimbreParam *timbre) {
*timbreTemp = *timbre;
}
-unsigned int Part::getAbsTimbreNum() {
- if (isRhythm) {
- synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm");
- return 0;
- }
+unsigned int RhythmPart::getAbsTimbreNum() const {
+ synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm");
+ return 0;
+}
+
+unsigned int Part::getAbsTimbreNum() const {
return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum;
}
-void Part::setPatch(int patchNum) {
- int absTimbreNum = -1; // Initialised to please compiler
- const TimbreParam *timbre;
- if (isRhythm) {
- // "patchNum" is treated as "timbreNum" for rhythm part
- if (patchNum < 128) {
- synth->printDebug("%s: Patch #%d is not valid for rhythm (must be >= 128)", name, patchNum);
- return;
- }
- absTimbreNum = patchNum;
- timbre = &synth->mt32ram.params.timbres[absTimbreNum].timbre;
- } else {
- if (patchNum >= 0) {
- setPatch(&synth->mt32ram.params.patches[patchNum]);
- }
- if (patchNum >= 0) {
- setTimbre(&synth->mt32ram.params.timbres[getAbsTimbreNum()].timbre);
- }
- timbre = timbreTemp;
+void RhythmPart::setProgram(unsigned int patchNum) {
+ synth->printDebug("%s: Attempt to set program (%d) on rhythm is invalid", name, patchNum);
+}
+
+void Part::setProgram(unsigned int patchNum) {
+ setPatch(&synth->mt32ram.patches[patchNum]);
+ setTimbre(&synth->mt32ram.timbres[getAbsTimbreNum()].timbre);
#if 0
- // Immediately stop all partials on this part (this is apparently *not* the correct behaviour)
- for (int m = 0; m < MT32EMU_MAX_POLY; m++) {
- AbortPoly(poly);
- }
-#else
- // check if any partials are still playing with the old patch cache
- // 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.
- // We delay this until now to avoid a copy operation with every note played
- 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 && partial->patchCache == &patchCache[i]) {
- // copy cache data
- partial->cachebackup = patchCache[i];
- // update pointers
- partial->patchCache = &partial->cachebackup;
- }
+ // Immediately stop all partials on this part (this is apparently *not* the correct behaviour)
+ for (int m = 0; m < MT32EMU_MAX_POLY; m++) {
+ AbortPoly(poly);
+ }
+#endif
+
+ refresh();
+
+ allStop(); //FIXME:KG: Is this correct?
+}
+
+void Part::backupCacheToPartials(PatchCache cache[4]) {
+ // check if any partials are still playing with the old patch cache
+ // 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.
+ // We delay this until now to avoid a copy operation with every note played
+ 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 && partial->patchCache == &cache[i]) {
+ partial->cachebackup = cache[i];
+ partial->patchCache = &partial->cachebackup;
}
}
-#endif
}
+}
- memcpy(currentInstr, timbre->common.name, 10);
-
+void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) {
+ backupCacheToPartials(cache);
int partialCount = 0;
for (int t = 0; t < 4; t++) {
if (((timbre->common.pmute >> t) & 0x1) == 1) {
- patchCache[t].playPartial = true;
+ cache[t].playPartial = true;
partialCount++;
} else {
- patchCache[t].playPartial = false;
+ cache[t].playPartial = false;
continue;
}
// Calculate and cache common parameters
- patchCache[t].pcm = timbre->partial[t].wg.pcmwave;
- patchCache[t].useBender = (timbre->partial[t].wg.bender == 1);
+ cache[t].pcm = timbre->partial[t].wg.pcmwave;
+ cache[t].useBender = (timbre->partial[t].wg.bender == 1);
switch (t) {
case 0:
- patchCache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x2) ? true : false;
- patchCache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
- patchCache[t].structurePosition = 0;
- patchCache[t].structurePair = 1;
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x2) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
+ cache[t].structurePosition = 0;
+ cache[t].structurePair = 1;
break;
case 1:
- patchCache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x1) ? true : false;
- patchCache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
- patchCache[t].structurePosition = 1;
- patchCache[t].structurePair = 0;
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x1) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
+ cache[t].structurePosition = 1;
+ cache[t].structurePair = 0;
break;
case 2:
- patchCache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x2) ? true : false;
- patchCache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
- patchCache[t].structurePosition = 0;
- patchCache[t].structurePair = 3;
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x2) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
+ cache[t].structurePosition = 0;
+ cache[t].structurePair = 3;
break;
case 3:
- patchCache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x1) ? true : false;
- patchCache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
- patchCache[t].structurePosition = 1;
- patchCache[t].structurePair = 2;
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x1) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
+ cache[t].structurePosition = 1;
+ cache[t].structurePair = 2;
break;
default:
break;
}
- patchCache[t].waveform = timbre->partial[t].wg.waveform;
- patchCache[t].pulsewidth = timbre->partial[t].wg.pulsewid;
- patchCache[t].pwsens = timbre->partial[t].wg.pwvelo;
- patchCache[t].pitchkeyfollow = fixKeyfollow(timbre->partial[t].wg.keyfollow, &patchCache[t].pitchkeydir);
-
- // Calculate and cache pitch stuff
- patchCache[t].pitchshift = timbre->partial[t].wg.coarse;
- Bit32s pFine, fShift;
- pFine = (Bit32s)timbre->partial[t].wg.fine;
- if (isRhythm) {
- patchCache[t].pitchshift += 24;
- fShift = pFine + 50;
+ cache[t].waveform = timbre->partial[t].wg.waveform;
+ cache[t].pulsewidth = timbre->partial[t].wg.pulsewid;
+ cache[t].pwsens = timbre->partial[t].wg.pwvelo;
+ if (timbre->partial[t].wg.keyfollow > 16) {
+ synth->printDebug("Bad keyfollow value in timbre!");
+ cache[t].pitchKeyfollow = 1.0f;
} else {
- patchCache[t].pitchshift += patchTemp->patch.keyShift;
- fShift = pFine + (Bit32s)patchTemp->patch.fineTune;
+ cache[t].pitchKeyfollow = floatKeyfollow[timbre->partial[t].wg.keyfollow];
}
- patchCache[t].fineshift = finetable[fShift];
- patchCache[t].pitchEnv = timbre->partial[t].env;
- patchCache[t].pitchEnv.sensitivity = (char)((float)patchCache[t].pitchEnv.sensitivity*1.27);
- patchCache[t].pitchsustain = patchCache[t].pitchEnv.level[3];
+ cache[t].pitch = timbre->partial[t].wg.coarse + (timbre->partial[t].wg.fine - 50) / 100.0f + 24.0f;
+ cache[t].pitchEnv = timbre->partial[t].env;
+ cache[t].pitchEnv.sensitivity = (char)((float)cache[t].pitchEnv.sensitivity * 1.27f);
+ cache[t].pitchsustain = cache[t].pitchEnv.level[3];
// Calculate and cache TVA envelope stuff
- patchCache[t].ampEnv = timbre->partial[t].tva;
+ cache[t].ampEnv = timbre->partial[t].tva;
for (int i = 0; i < 4; i++)
- patchCache[t].ampEnv.envlevel[i] = (char)((float)patchCache[t].ampEnv.envlevel[i] * 1.27f);
- patchCache[t].ampEnv.level = (char)((float)patchCache[t].ampEnv.level * 1.27f);
+ cache[t].ampEnv.envlevel[i] = (char)((float)cache[t].ampEnv.envlevel[i] * 1.27f);
+ cache[t].ampEnv.level = (char)((float)cache[t].ampEnv.level * 1.27f);
float tvelo = ((float)timbre->partial[t].tva.velosens / 100.0f);
float velo = fabs(tvelo-0.5f) * 2.0f;
velo *= 63.0f;
- patchCache[t].ampEnv.velosens = (char)velo;
+ cache[t].ampEnv.velosens = (char)velo;
if (tvelo<0.5f)
- patchCache[t].ampenvdir = 1;
+ cache[t].ampenvdir = 1;
else
- patchCache[t].ampenvdir = 0;
+ cache[t].ampenvdir = 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].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];
- patchCache[t].amplevel = patchCache[t].ampEnv.level;
+ cache[t].ampbias[0] = fixBiaslevel(cache[t].ampEnv.biaspoint1, &cache[t].ampdir[0]);
+ cache[t].ampblevel[0] = 12 - cache[t].ampEnv.biaslevel1;
+ cache[t].ampbias[1] = fixBiaslevel(cache[t].ampEnv.biaspoint2, &cache[t].ampdir[1]);
+ cache[t].ampblevel[1] = 12 - cache[t].ampEnv.biaslevel2;
+ cache[t].ampdepth = cache[t].ampEnv.envvkf * cache[t].ampEnv.envvkf;
+ cache[t].ampsustain = cache[t].ampEnv.envlevel[3];
+ cache[t].amplevel = cache[t].ampEnv.level;
// Calculate and cache filter stuff
- patchCache[t].filtEnv = timbre->partial[t].tvf;
- patchCache[t].tvfdepth = patchCache[t].filtEnv.envdkf;
- 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].tvfblevel = patchCache[t].filtEnv.biaslevel;
- patchCache[t].filtsustain = patchCache[t].filtEnv.envlevel[3];
+ cache[t].filtEnv = timbre->partial[t].tvf;
+ cache[t].tvfdepth = cache[t].filtEnv.envdkf;
+ cache[t].filtkeyfollow = fixKeyfollow(cache[t].filtEnv.keyfollow);
+ cache[t].filtEnv.envdepth = (char)((float)cache[t].filtEnv.envdepth * 1.27);
+ cache[t].tvfbias = fixBiaslevel(cache[t].filtEnv.biaspoint, &cache[t].tvfdir);
+ cache[t].tvfblevel = cache[t].filtEnv.biaslevel;
+ cache[t].filtsustain = cache[t].filtEnv.envlevel[3];
// Calculate and cache LFO stuff
- patchCache[t].lfodepth = timbre->partial[t].lfo.depth;
- patchCache[t].lfoperiod = lfotable[(int)timbre->partial[t].lfo.rate];
- patchCache[t].lforate = timbre->partial[t].lfo.rate;
- patchCache[t].modsense = timbre->partial[t].lfo.modsense;
+ cache[t].lfodepth = timbre->partial[t].lfo.depth;
+ cache[t].lfoperiod = lfotable[(int)timbre->partial[t].lfo.rate];
+ cache[t].lforate = timbre->partial[t].lfo.rate;
+ cache[t].modsense = timbre->partial[t].lfo.modsense;
}
for (int t = 0; t < 4; t++) {
// Common parameters, stored redundantly
- patchCache[t].partialCount = partialCount;
- patchCache[t].sustain = (timbre->common.nosustain == 0);
- if (isRhythm) {
- patchCache[t].benderRange = 0;
- } else {
- patchCache[t].benderRange = patchTemp->patch.benderRange;
- }
+ cache[t].dirty = false;
+ cache[t].partialCount = partialCount;
+ cache[t].sustain = (timbre->common.nosustain == 0);
}
- //synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", patchCache[0].waveform, patchCache[1].waveform, patchCache[2].waveform, patchCache[3].waveform);
+ //synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", cache[0].waveform, cache[1].waveform, cache[2].waveform, cache[3].waveform);
- if (isRhythm)
- memcpy(drumCache[absTimbreNum - 128], patchCache, sizeof(patchCache));
- else
- allStop();
#if MT32EMU_MONITOR_INSTRUMENTS == 1
- synth->printDebug("%s: Recache, param %d (timbre: %s)", name, patchNum, currentInstr);
+ synth->printDebug("%s (%s): Recached timbre", name, currentInstr);
for (int i = 0; i < 4; i++) {
- synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, patchCache[i].playPartial ? "YES" : "NO", patchCache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmwave, timbre->partial[i].wg.waveform);
+ synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, cache[i].playPartial ? "YES" : "NO", cache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmwave, timbre->partial[i].wg.waveform);
}
#endif
}
-char *Part::getName() {
+const char *Part::getName() const {
return name;
}
@@ -393,72 +395,88 @@ void Part::setVolume(int vol) {
volume = voltable[vol];
}
-void Part::setPan(int pan) {
+void RhythmPart::setPan(unsigned int midiPan)
+{
// FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct?
+ synth->printDebug("%s: Setting pan (%d) not supported on rhythm", name, midiPan);
+}
+
+void Part::setPan(unsigned int midiPan) {
// FIXME:KG: Tweaked this a bit so that we have a left 100%, centre and right 100%
// (But this makes the range somewhat skewed)
- if (pan < 64) {
+ if (midiPan < 64) {
volumesetting.leftvol = 32767;
- volumesetting.rightvol = (Bit16s)(pan * 512);
- } else if (pan == 64) {
+ volumesetting.rightvol = (Bit16s)(midiPan * 512);
+ } else if (midiPan == 64) {
volumesetting.leftvol = 32767;
volumesetting.rightvol = 32767;
} else {
volumesetting.rightvol = 32767;
- volumesetting.leftvol = (Bit16s)((127 - pan) * 520);
+ volumesetting.leftvol = (Bit16s)((127 - midiPan) * 520);
}
//synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
}
-void Part::playNote(PartialManager *partialManager, unsigned int key, int vel) {
- int drumNum = -1; // Initialised to please compiler
- int drumTimbre = -1; // As above
- int freqNum;
+void RhythmPart::playNote(unsigned int key, int vel) {
+ if (key < 24 || key > 87) {
+ synth->printDebug("%s: Attempted to play invalid key %d", name, key);
+ return;
+ }
+ int drumNum = key - 24;
+ int drumTimbreNum = rhythmTemp[drumNum].timbre;
+ if (drumTimbreNum >= 94) {
+ synth->printDebug("%s: Attempted to play unmapped key %d", name, key);
+ return;
+ }
+ int absTimbreNum = drumTimbreNum + 128;
+ TimbreParam *timbre = &synth->mt32ram.timbres[absTimbreNum].timbre;
+ memcpy(currentInstr, timbre->common.name, 10);
+#if MT32EMU_MONITOR_INSTRUMENTS == 1
+ synth->printDebug("%s (%s): starting poly (drum %d, timbre %d) - Vel %d Key %d Vol %d", name, currentInstr, drumNum, absTimbreNum, vel, key, volume);
+#endif
+ if (drumCache[drumNum][0].dirty) {
+ cacheTimbre(drumCache[drumNum], timbre);
+ }
+ playPoly(drumCache[drumNum], key, MIDDLEC, vel);
+}
- if (isRhythm) {
- if (key < 24 || key > 87) {
- synth->printDebug("%s: Attempted to play invalid key %d", name, key);
- return;
- }
- drumNum = key - 24;
- drumTimbre = rhythmTemp[drumNum].timbre;
- if (drumTimbre >= 94) {
- synth->printDebug("%s: Attempted to play unmapped key %d", name, key);
- return;
+void Part::playNote(unsigned int key, int vel) {
+ int freqNum = key;
+ if (freqNum < 12) {
+ synth->printDebug("%s (%s): Attempted to play invalid key %d < 12; moving up by octave", name, currentInstr, key);
+ freqNum += 12;
+ } else if (freqNum > 108) {
+ synth->printDebug("%s (%s): Attempted to play invalid key %d > 108; moving down by octave", name, currentInstr, key);
+ while (freqNum > 108) {
+ freqNum -= 12;
}
- memcpy(patchCache, drumCache[drumTimbre], sizeof(patchCache));
- memcpy(&currentInstr, synth->mt32ram.params.timbres[128 + drumTimbre].timbre.common.name, 10);
- freqNum = MIDDLEC;
- } else {
- if (key < 12) {
- synth->printDebug("%s (%s): Attempted to play invalid key %d < 12; moving up by octave", name, currentInstr, key);
- key += 12;
- } else if (key > 108) {
- synth->printDebug("%s (%s): Attempted to play invalid key %d > 108; moving down by octave", name, currentInstr, key);
- while (key > 108) {
- key -= 12;
- }
- }
- freqNum = key;
}
// POLY1 mode, Single Assign
// Haven't found any software that uses any of the other poly modes
// FIXME:KG: Should this also apply to rhythm?
- if (!isRhythm) {
- for (unsigned int i = 0; i < MT32EMU_MAX_POLY; i++) {
- if (polyTable[i].isActive() && (polyTable[i].key == key)) {
- //AbortPoly(&polyTable[i]);
- stopNote(key);
- break;
- }
+ for (unsigned int i = 0; i < MT32EMU_MAX_POLY; i++) {
+ if (polyTable[i].isActive() && (polyTable[i].key == key)) {
+ //AbortPoly(&polyTable[i]);
+ stopNote(key);
+ break;
}
}
+#if MT32EMU_MONITOR_INSTRUMENTS == 1
+ synth->printDebug("%s (%s): starting poly - Vel %d Key %d Vol %d", name, currentInstr, vel, key, volume);
+#endif
+ if (patchCache[0].dirty) {
+ cacheTimbre(patchCache, timbreTemp);
+ }
+ playPoly(patchCache, key, freqNum, vel);
+}
- unsigned int needPartials = patchCache[0].partialCount;
+void Part::playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel) {
+ unsigned int needPartials = cache[0].partialCount;
+ unsigned int freePartials = synth->partialManager->getFreePartialCount();
- if (needPartials > partialManager->GetFreePartialCount()) {
- if (!partialManager->FreePartials(needPartials, partNum)) {
- synth->printDebug("%s (%s): Insufficient free partials to play key %d (vel=%d)", name, currentInstr, key, vel);
+ if (freePartials < needPartials) {
+ if (!synth->partialManager->freePartials(needPartials - freePartials, partNum)) {
+ synth->printDebug("%s (%s): Insufficient free partials to play key %d (vel=%d); needed=%d, free=%d", name, currentInstr, key, vel, needPartials, synth->partialManager->getFreePartialCount());
return;
}
}
@@ -485,8 +503,8 @@ void Part::playNote(PartialManager *partialManager, unsigned int key, int vel) {
bool allnull = true;
for (int x = 0; x < 4; x++) {
- if (patchCache[x].playPartial) {
- tpoly->partials[x] = partialManager->AllocPartial(partNum);
+ if (cache[x].playPartial) {
+ tpoly->partials[x] = synth->partialManager->allocPartial(partNum);
allnull = false;
} else {
tpoly->partials[x] = NULL;
@@ -496,27 +514,12 @@ void Part::playNote(PartialManager *partialManager, unsigned int key, int vel) {
if (allnull)
synth->printDebug("%s (%s): No partials to play for this instrument", name, this->currentInstr);
- if (isRhythm) {
- tpoly->pansetptr = &drumPan[drumNum];
- tpoly->reverb = rhythmTemp[drumNum].reverbSwitch > 0;
- } else {
- tpoly->pansetptr = &volumesetting;
- tpoly->reverb = patchTemp->patch.reverbSwitch > 0;
- }
- tpoly->sustain = patchCache[0].sustain;
+ tpoly->sustain = cache[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, key, volume);
- } else {
- synth->printDebug("%s (%s): starting poly %d - Vel %d Key %d Vol %d", name, currentInstr, m, vel, key, 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]->startPartial(tpoly, &cache[x], tpoly->partials[cache[x].structurePair]);
tpoly->partials[x]->setBend(bend);
}
}
diff --git a/backends/midi/mt32/part.h b/backends/midi/mt32/part.h
index e9b8e923e3..1214ec52f9 100644
--- a/backends/midi/mt32/part.h
+++ b/backends/midi/mt32/part.h
@@ -29,20 +29,12 @@ class Synth;
class Part {
private:
- Synth *synth; // Only used for sending debug output
-
// Pointers to the areas of the MT-32's memory dedicated to this part (for parts 1-8)
MemParams::PatchTemp *patchTemp;
TimbreParam *timbreTemp;
- //... and for rhythm
- MemParams::RhythmTemp *rhythmTemp;
-
- bool isRhythm;
- bool init;
- int partNum;
- char name[8]; // "Part 1".."Part 8", "Rhythm"
- char currentInstr[11];
+ // 0=Part 1, .. 7=Part 8, 8=Rhythm
+ unsigned int partNum;
bool holdpedal;
@@ -51,33 +43,61 @@ private:
PatchCache patchCache[4];
float bend; // -1.0 .. +1.0
- Bit32s volume;
dpoly polyTable[MT32EMU_MAX_POLY];
void abortPoly(dpoly *poly);
- static int fixKeyfollow(int srckey, int *dir);
+ static int fixKeyfollow(int srckey);
static int fixBiaslevel(int srcpnt, int *dir);
+ void setPatch(const PatchParam *patch);
+
+protected:
+ Synth *synth;
+ char name[8]; // "Part 1".."Part 8", "Rhythm"
+ char currentInstr[11];
+ Bit32u volume;
+ void backupCacheToPartials(PatchCache cache[4]);
+ void cacheTimbre(PatchCache cache[4], const TimbreParam *timbre);
+ void playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel);
+ const char *getName() const;
+
public:
- Part(Synth *synth, int usePartNum);
- char *getName();
- void playNote(PartialManager *partialManager, unsigned int key, int vel);
+ Part(Synth *synth, unsigned int usePartNum);
+ virtual void playNote(unsigned int key, int vel);
void stopNote(unsigned int key);
void allStop();
void setVolume(int vol);
- void setPan(int vol);
- void setBend(int vol);
- void setModulation(int vol);
- void setPatch(int patchnum);
+ virtual void setPan(unsigned int midiPan);
+ virtual void setBend(unsigned int midiBend);
+ virtual void setModulation(unsigned int midiModulation);
+ virtual void setProgram(unsigned int patchNum);
void setHoldPedal(bool pedalval);
void stopPedalHold();
- void refreshPatch();
- void refreshDrumCache();
- void setPatch(PatchParam *patch);
+ virtual void refresh();
+ virtual void refreshTimbre(unsigned int absTimbreNum);
void setTimbre(TimbreParam *timbre);
- unsigned int getAbsTimbreNum();
+ virtual unsigned int getAbsTimbreNum() const;
+};
+
+class RhythmPart: public Part {
+ // Pointer to the area of the MT-32's memory dedicated to rhythm
+ const MemParams::RhythmTemp *rhythmTemp;
+
+ // This caches the timbres/settings in use by the rhythm part
+ PatchCache drumCache[64][4];
+ StereoVolume drumPan[64];
+public:
+ RhythmPart(Synth *synth, unsigned int usePartNum);
+ void refreshTimbre(unsigned int timbreNum);
+ void refresh();
+ void playNote(unsigned int key, int vel);
+ unsigned int getAbsTimbreNum() const;
+ void setPan(unsigned int midiPan);
+ void setBend(unsigned int midiBend);
+ void setModulation(unsigned int midiModulation);
+ void setProgram(unsigned int patchNum);
};
}
diff --git a/backends/midi/mt32/partial.cpp b/backends/midi/mt32/partial.cpp
index f785f22d67..97158ed396 100644
--- a/backends/midi/mt32/partial.cpp
+++ b/backends/midi/mt32/partial.cpp
@@ -25,6 +25,11 @@
#include "mt32emu.h"
+#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y)))
+#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y)))
+#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point)
+#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point))
+
using namespace MT32Emu;
Partial::Partial(Synth *useSynth) {
@@ -32,9 +37,20 @@ Partial::Partial(Synth *useSynth) {
ownerPart = -1;
poly = NULL;
pair = NULL;
+#if MT32EMU_ACCURATENOTES == 1
+ for (int i = 0; i < 3; i++) {
+ noteLookupStorage.waveforms[i] = new Bit16s[65536];
+ }
+ noteLookup = &noteLookupStorage;
+#endif
}
Partial::~Partial() {
+#if MT32EMU_ACCURATENOTES == 1
+ for (int i = 0; i < 3; i++) {
+ delete[] noteLookupStorage.waveforms[i];
+ }
+#endif
}
int Partial::getOwnerPart() {
@@ -68,55 +84,49 @@ void Partial::deactivate() {
void Partial::initKeyFollow(int key) {
// Setup partial keyfollow
// Note follow relative to middle C
- int keyfollow;
- int realfol = (key * 2 - MIDDLEC * 2) / 2;
- int antirealfol = (MIDDLEC * 2 - key * 2) / 2;
+
// Calculate keyfollow for pitch
- switch(patchCache->pitchkeydir) {
- case -1:
- keyfollow = (antirealfol * patchCache->pitchkeyfollow) >> 12;
- break;
- case 0:
- keyfollow = 0;
- break;
- case 1:
- keyfollow = (realfol * patchCache->pitchkeyfollow) >> 12;
- break;
- default:
- keyfollow = 0; // Please the compiler
- }
- if ((patchCache->pitchkeyfollow>4096) && (patchCache->pitchkeyfollow<4200)) {
- // Be sure to round up on keys below MIDDLEC
- if (realfol < 0)
- keyfollow++;
+#if 1
+ float rel = key == -1 ? 0.0f : (key - MIDDLEC);
+ float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch + patchCache->pitchShift;
+ //FIXME:KG: Does it truncate the keyfollowed pitch to a semitone (towards MIDDLEC)?
+ //int newKey = (int)(rel * patchCache->pitchKeyfollow);
+ //float newPitch = newKey + patchCache->pitch + patchCache->pitchShift;
+#else
+ float rel = key == -1 ? 0.0f : (key + patchCache->pitchShift - MIDDLEC);
+ float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch;
+#endif
+#if MT32EMU_ACCURATENOTES == 1
+ noteVal = newPitch;
+ synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch);
+#else
+ float newPitchInt;
+ float newPitchFract = modff(newPitch, &newPitchInt);
+ synth->printDebug("Really: newPitch=%f, newPitchInt=%f, newPitchFract=%f", newPitch, newPitchInt, newPitchFract);
+ if (newPitchFract > 0.5f) {
+ newPitchInt += 1.0f;
+ newPitchFract -= 1.0f;
}
- noteVal = (keyfollow + patchCache->pitchshift);
- if (noteVal > 108)
- noteVal = 108;
- if (noteVal < 12)
- noteVal = 12;
-
+ noteVal = (int)newPitchInt;
+ fineShift = (int)(powf(2.0f, newPitchFract / 12.0f) * 4096.0f);
+ synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f, noteVal=%d, fineShift=%d", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch, noteVal, fineShift);
+#endif
+ // FIXME:KG: Raise/lower by octaves until in the supported range.
+ while (noteVal > HIGHEST_NOTE) // FIXME:KG: see tables.cpp: >108?
+ noteVal -= 12;
+ while (noteVal < LOWEST_NOTE) // FIXME:KG: see tables.cpp: <12?
+ noteVal += 12;
// Calculate keyfollow for filter
- switch(patchCache->keydir) {
- case -1:
- keyfollow = (antirealfol * patchCache->filtkeyfollow) >> 12;
- break;
- case 0:
- keyfollow = key;
- break;
- case 1:
- keyfollow = (realfol * patchCache->filtkeyfollow) >> 12;
- break;
- }
+ int keyfollow = ((key - MIDDLEC) * patchCache->filtkeyfollow) / 4096;
if (keyfollow > 108)
keyfollow = 108;
if (keyfollow < -108)
keyfollow = -108;
filtVal = keytable[keyfollow + 108];
- realVal = keytable[realfol + 108];
+ realVal = keytable[(key - MIDDLEC) + 108];
}
-void Partial::startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPartial) {
+void Partial::startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial) {
if (usePoly == NULL || useCache == NULL) {
synth->printDebug("*** Error: Starting partial for owner %d, usePoly=%s, useCache=%s", ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", useCache == NULL ? "*** NULL ***" : "OK");
return;
@@ -128,11 +138,11 @@ void Partial::startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPa
play = true;
initKeyFollow(poly->freqnum); // Initialises noteVal, filtVal and realVal
- noteLookup = &noteLookups[noteVal];
-
- // FIXME:KG: I think most places should refer to noteVal/noteLookup instead of these.
- keyVal = poly->freqnum;
- keyLookup = &noteLookups[poly->freqnum];
+#if MT32EMU_ACCURATENOTES == 0
+ noteLookup = &noteLookups[noteVal - LOWEST_NOTE];
+#else
+ TableInitialiser::initNote(synth, &noteLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->PCMList, NULL);
+#endif
lfoPos = 0;
pulsewidth = patchCache->pulsewidth + pwveltable[patchCache->pwsens][poly->vel];
@@ -154,8 +164,8 @@ void Partial::startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPa
envs[e].counter = 0;
envs[e].count = 0;
}
- ampEnvCache = 0;
- pitchEnvCache = 0;
+ ampEnvVal = 0;
+ pitchEnvVal = 0;
pitchSustain = false;
loopPos = 0;
partialOff.pcmoffset = partialOff.pcmplace = 0;
@@ -181,22 +191,16 @@ Bit16s *Partial::generateSamples(long length) {
Bit16s *partialBuf = &myBuffer[0];
while (length--) {
- Bit32s envval, ampval;
- Bit32s ptemp = 0;
- if (envs[EnvelopeType_amp].sustaining)
- ampval = ampEnvCache;
- else {
+ Bit32s envval;
+ Bit32s sample = 0;
+ if (!envs[EnvelopeType_amp].sustaining) {
if (envs[EnvelopeType_amp].count <= 0) {
- ampval = getAmpEnvelope();
+ Bit32u ampval = getAmpEnvelope();
if (!play) {
deactivate();
break;
}
- if (ampval < 0) {
- //TODO: check what is going on here
- synth->printDebug("ampval<0! ampval=%ld, active=%d", ampval, isActive());
- ampval = 0;
- } else if (ampval > 127) {
+ if (ampval > 127) {
ampval = 127;
}
@@ -208,15 +212,12 @@ Bit16s *Partial::generateSamples(long length) {
tmpvel = poly->vel;
ampval = (ampval * ampveltable[tmpvel][(int)patchCache->ampEnv.velosens]) >> 8;
//if (envs[EnvelopeType_amp].sustaining)
- ampEnvCache = ampval;
- } else
- ampval = ampEnvCache;
+ ampEnvVal = ampval;
+ }
--envs[EnvelopeType_amp].count;
}
- // Calculate Pitch envelope
int lfoat = 0x1000;
- int pdep;
if (pitchSustain) {
// Calculate LFO position
// LFO does not kick in completely until pitch envelope sustains
@@ -224,17 +225,15 @@ Bit16s *Partial::generateSamples(long length) {
lfoPos++;
if (lfoPos >= patchCache->lfoperiod)
lfoPos = 0;
- int lfoatm = (lfoPos << 16) / patchCache->lfoperiod;
+ int lfoatm = FIXEDPOINT_UDIV(lfoPos, patchCache->lfoperiod, 16);
int lfoatr = sintable[lfoatm];
lfoat = lfoptable[patchCache->lfodepth][lfoatr];
}
- pdep = pitchEnvCache;
} else {
+ // Calculate Pitch envelope
envval = getPitchEnvelope();
int pd = patchCache->pitchEnv.depth;
- pdep = penvtable[pd][envval];
- if (pitchSustain)
- pitchEnvCache = pdep;
+ pitchEnvVal = penvtable[pd][envval];
}
int delta;
@@ -242,36 +241,23 @@ Bit16s *Partial::generateSamples(long length) {
PCMWaveEntry *pcmWave = NULL; // Initialise to please compiler
int pcmAddr = 0; // Initialise to please compiler
- // Get waveform - either PCM or synthesized sawtooth or square
+ // Wrap positions or end if necessary
if (patchCache->PCMPartial) {
// PCM partial
int len;
pcmWave = &synth->PCMList[patchCache->pcm];
- if (pcmWave->aggSound == -1) {
- delta = noteLookup->wavTable[pcmWave->pcmnum];
- pcmAddr = pcmWave->addr;
- len = pcmWave->len;
- if (partialOff.pcmplace >= len) {
- if (pcmWave->loop) {
- partialOff.pcmplace = partialOff.pcmoffset = 0;
- // FIXME:KG: Use this?: partialOff.pcmplace %= len;
- } else {
- play = false;
- deactivate();
- break;
- }
- }
- } else {
- int tmppcm = LoopPatterns[pcmWave->aggSound][loopPos];
- delta = noteLookup->loopTable[pcmWave->aggSound][loopPos];
- pcmAddr = synth->PCM[tmppcm].addr;
- len = synth->PCM[tmppcm].len;
- if (partialOff.pcmplace >= len) {
- loopPos++;
- if (LoopPatterns[pcmWave->aggSound][loopPos] == -1)
- loopPos = 0;
- partialOff.pcmplace = partialOff.pcmoffset = 0;
+ delta = noteLookup->wavTable[patchCache->pcm];
+ pcmAddr = pcmWave->addr;
+ len = pcmWave->len;
+ if (partialOff.pcmplace >= len) {
+ if (pcmWave->loop) {
+ //partialOff.pcmplace = partialOff.pcmoffset = 0;
+ partialOff.pcmplace %= len;
+ } else {
+ play = false;
+ deactivate();
+ break;
}
}
} else {
@@ -282,14 +268,18 @@ 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;
+ Bit64u tdelta = (Bit64u)delta;
+#if MT32EMU_ACCURATENOTES == 0
+ tdelta = (tdelta * fineShift) >> 12;
+#endif
+ tdelta = (tdelta * pitchEnvVal) >> 12;
tdelta = (tdelta * lfoat) >> 12;
tdelta = (tdelta * bendShift) >> 12;
delta = (int)tdelta;
+ Bit32u volume = *poly->volumeptr;
- if (ampval > 0) {
+ // Get waveform - either PCM or synthesized sawtooth or square
+ if (ampEnvVal > 0) {
if (patchCache->PCMPartial) {
// Render PCM sample
int ra, rb, dist;
@@ -301,7 +291,7 @@ Bit16s *Partial::generateSamples(long length) {
//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));
+ sample = (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
@@ -311,11 +301,11 @@ Bit16s *Partial::generateSamples(long length) {
ra = 0;
for (int ix = 0; ix < idelta; ix++)
ra += synth->romfile[taddr++];
- ptemp = ra / idelta;
+ sample = ra / idelta;
}
} else {
// Render synthesised sample
- int div = noteLookup->div;
+ Bit32u div = noteLookup->div;
int wf = patchCache->waveform;
int toff = partialOff.pcmplace;
int minorplace = partialOff.pcmoffset >> 14;
@@ -328,26 +318,23 @@ 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 divmark = div << 8;
-
if (div == 0) {
synth->printDebug("ERROR: div=0 generating square wave, this should never happen!");
div = 1;
}
- int ofsA = toff % div;
- int ofsB = toff + ((divmark * pulsetable[pulsewidth]) >> 16);
+ Bit32u ofsA = toff % div;
+ Bit32u ofsB = toff + FIXEDPOINT_UMULT(div, pulsetable[pulsewidth], 8);
ofsB = ofsB % div;
- pa = noteLookup->waveforms[0][(ofsA << 2) + minorplace];
- pb = noteLookup->waveforms[0][(ofsB << 2) + minorplace];
- ptemp = (pa - pb) * 4;
+ Bit16s pa = noteLookup->waveforms[0][(ofsA << 2) + minorplace];
+ Bit16s pb = noteLookup->waveforms[0][(ofsB << 2) + minorplace];
+ sample = (pa - pb) * 4;
// Non-bandlimited squarewave
/*
ofs = ((div << 1) * pulsetable[patchCache->pulsewidth]) >> 8;
if (toff < ofs)
- ptemp = 1 * WGAMP;
+ sample = 1 * WGAMP;
else
- ptemp = -1 * WGAMP;
+ sample = -1 * WGAMP;
*/
} else {
// Sawtooth. Made by combining the full cosine and half cosine according
@@ -355,33 +342,32 @@ Bit16s *Partial::generateSamples(long length) {
// square wave and multiplies it by a full cosine
int waveoff = (toff << 2) + minorplace;
if (toff < noteLookup->sawTable[pulsewidth])
- ptemp = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]];
+ sample = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]];
else
- ptemp = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]];
- ptemp = ptemp * 4;
+ sample = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]];
+ sample = sample * 4;
// This is the correct way
// Seems slow to me (though bandlimited) -- doesn't seem to
// sound any better though
/*
- int divmark = noteLookup->div << 8;
//int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8;
- int ofs = toff % div;
+ Bit32u ofs = toff % div;
- int ofs3 = toff + ((divmark * pulsetable[patchCache->pulsewidth]) >> 16);
+ Bit32u ofs3 = toff + FIXEDPOINT_UMULT(div, pulsetable[patchCache->pulsewidth], 8);
ofs3 = ofs3 % div;
pa = noteLookup->waveforms[0][ofs];
pb = noteLookup->waveforms[0][ofs3];
- ptemp = ((pa - pb) * noteLookup->waveforms[2][toff]) / WGAMP;
- ptemp = ptemp *4;
+ sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / WGAMP;
+ sample = sample *4;
*/
}
//Very exact filter
if (filtval > ((FILTERGRAN * 15) / 16))
filtval = ((FILTERGRAN * 15) / 16);
- ptemp = (Bit32s)floor((usefilter)((float)ptemp, &history[0], filtcoeff[filtval][(int)patchCache->filtEnv.resonance], patchCache->filtEnv.resonance));
+ sample = (Bit32s)floor((synth->iirFilter)((float)sample, &history[0], filtcoeff[filtval][(int)patchCache->filtEnv.resonance], patchCache->filtEnv.resonance));
}
}
@@ -392,14 +378,13 @@ Bit16s *Partial::generateSamples(long length) {
partialOff.pcmoffset = (Bit16u)(absOff & 0xFFFF);
// Put volume envelope over generated sample
- ptemp = (ptemp * ampval) >> 9;
- ptemp = (ptemp * *poly->volumeptr) >> 7;
-
+ sample = FIXEDPOINT_SMULT(sample, ampEnvVal, 9);
+ sample = FIXEDPOINT_SMULT(sample, volume, 7);
envs[EnvelopeType_amp].envpos++;
envs[EnvelopeType_pitch].envpos++;
envs[EnvelopeType_filt].envpos++;
- *partialBuf++ = (Bit16s)ptemp;
+ *partialBuf++ = (Bit16s)sample;
}
// We may have deactivated and broken out of the loop before the end of the buffer,
// if so then fill the remainder with 0s.
@@ -414,9 +399,10 @@ void Partial::setBend(float factor) {
return;
}
// NOTE:KG: We can't do this smoothly with lookup tables, unless we use several MB.
+ // FIXME:KG: Bend should be influenced by pitch key-follow too, according to docs.
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);
+ synth->printDebug("setBend(): factor=%f, benderRange=%f, semitones=%f, mult=%f\n", factor, patchCache->benderRange, bendSemitones, mult);
bendShift = (int)(mult * 4096.0f);
}
@@ -601,8 +587,8 @@ bool Partial::produceOutput(Bit16s *partialBuf, long length) {
return false;
Bit16s leftvol, rightvol;
- leftvol = poly->pansetptr->leftvol;
- rightvol = poly->pansetptr->rightvol;
+ leftvol = patchCache->pansetptr->leftvol;
+ rightvol = patchCache->pansetptr->rightvol;
#if MT32EMU_USE_MMX >= 2
// FIXME:KG: This appears to introduce crackle
@@ -624,7 +610,7 @@ Bit32s Partial::getFiltEnvelope() {
int cutoff,depth,keyfollow, realfollow;
- envstatus *tStat = &envs[EnvelopeType_filt];
+ EnvelopeStatus *tStat = &envs[EnvelopeType_filt];
keyfollow = filtVal;
realfollow = realVal;
@@ -651,7 +637,7 @@ Bit32s Partial::getFiltEnvelope() {
if (tStat->envstat == 3) {
tStat->envsize = lasttimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]];
} else {
- tStat->envsize = (envtimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]] * keyLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8;
+ tStat->envsize = (envtimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8;
}
tStat->envsize++;
@@ -678,16 +664,17 @@ Bit32s Partial::getFiltEnvelope() {
int dist;
if (bias!=0) {
+ //FIXME:KG: Is this really based on pitch (as now), or key pressed?
//synth->printDebug("Cutoff before %d", cutoff);
if (patchCache->tvfdir == 0) {
- if (keyVal < bias) {
- dist = bias - keyVal;
+ if (noteVal < bias) {
+ dist = bias - noteVal;
cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;
}
} else {
// > Bias
- if (keyVal > bias) {
- dist = keyVal - bias;
+ if (noteVal > bias) {
+ dist = noteVal - bias;
cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;
}
@@ -695,7 +682,7 @@ Bit32s Partial::getFiltEnvelope() {
//synth->printDebug("Cutoff after %d", cutoff);
}
- depth = (depth * keyLookup->fildepTable[patchCache->tvfdepth]) >> 8;
+ depth = (depth * noteLookup->fildepTable[patchCache->tvfdepth]) >> 8;
reshigh = (reshigh * depth) >> 7;
Bit32s tmp;
@@ -714,7 +701,7 @@ Bit32s Partial::getFiltEnvelope() {
reshigh = 100;
else if (reshigh<0)
reshigh = 0;
- tmp = keyLookup->nfiltTable[cutoff][reshigh];
+ tmp = noteLookup->nfiltTable[cutoff][reshigh];
//tmp *= keyfollow;
//tmp /= realfollow;
@@ -725,13 +712,13 @@ Bit32s Partial::getFiltEnvelope() {
bool Partial::shouldReverb() {
if (!isActive())
return false;
- return poly->reverb;
+ return patchCache->reverb;
}
-Bit32s Partial::getAmpEnvelope() {
- Bit32s tc;
+Bit32u Partial::getAmpEnvelope() {
+ Bit32u tc;
- envstatus *tStat = &envs[EnvelopeType_amp];
+ EnvelopeStatus *tStat = &envs[EnvelopeType_amp];
if (!play)
return 0;
@@ -780,7 +767,7 @@ Bit32s Partial::getAmpEnvelope() {
default:
//Spot for timekey follow
//Only used in subsquent envelope parameters, including the decay
- tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * keyLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8;
+ tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8;
//synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
break;
@@ -809,21 +796,19 @@ PastCalc:
//Bias level crap stuff now
- int dist, bias;
-
for (int i = 0; i < 2; i++) {
if (patchCache->ampblevel[i]!=0) {
- bias = patchCache->ampbias[i];
+ int bias = patchCache->ampbias[i];
if (patchCache->ampdir[i]==0) {
// < Bias
- if (keyVal < bias) {
- dist = bias - keyVal;
+ if (noteVal < bias) {
+ int dist = bias - noteVal;
tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8;
}
} else {
// > Bias
- if (keyVal > bias) {
- dist = keyVal - bias;
+ if (noteVal > bias) {
+ int dist = noteVal - bias;
tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8;
}
}
@@ -833,7 +818,7 @@ PastCalc:
}
Bit32s Partial::getPitchEnvelope() {
- envstatus *tStat = &envs[EnvelopeType_pitch];
+ EnvelopeStatus *tStat = &envs[EnvelopeType_pitch];
Bit32s tc;
pitchSustain = false;
@@ -856,7 +841,7 @@ Bit32s Partial::getPitchEnvelope() {
tStat->envstat++;
tStat->envbase = patchCache->pitchEnv.level[tStat->envstat];
- tStat->envsize = (envtimetable[(int)patchCache->pitchEnv.time[tStat->envstat]] * keyLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8;
+ tStat->envsize = (envtimetable[(int)patchCache->pitchEnv.time[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8;
tStat->envpos = 0;
tStat->envsize++;
@@ -878,7 +863,7 @@ void Partial::startDecayAll() {
}
void Partial::startDecay(EnvelopeType envnum, Bit32s startval) {
- envstatus *tStat = &envs[envnum];
+ EnvelopeStatus *tStat = &envs[envnum];
tStat->sustaining = false;
tStat->decaying = true;
@@ -887,15 +872,15 @@ void Partial::startDecay(EnvelopeType envnum, Bit32s startval) {
switch(envnum) {
case EnvelopeType_amp:
- tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * keyLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8;
+ tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8;
tStat->envdist = -startval;
break;
case EnvelopeType_filt:
- tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * keyLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8;
+ tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8;
tStat->envdist = -startval;
break;
case EnvelopeType_pitch:
- tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * keyLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8 ;
+ tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * noteLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8 ;
tStat->envdist = patchCache->pitchEnv.level[4] - startval;
break;
default:
diff --git a/backends/midi/mt32/partial.h b/backends/midi/mt32/partial.h
index 2760b8fa2a..52b07bfeb1 100644
--- a/backends/midi/mt32/partial.h
+++ b/backends/midi/mt32/partial.h
@@ -33,7 +33,7 @@ enum EnvelopeType {
EnvelopeType_pitch = 2
};
-struct envstatus {
+struct EnvelopeStatus {
Bit32s envpos;
Bit32s envstat;
Bit32s envbase;
@@ -51,7 +51,7 @@ struct envstatus {
// Class definition of MT-32 partials. 32 in all.
class Partial {
private:
- Synth *synth; // Only used for sending debug output
+ Synth *synth;
int ownerPart; // -1 if unassigned
int mixType;
@@ -63,25 +63,28 @@ private:
bool play;
// Keyfollowed note value
+#if MT32EMU_ACCURATENOTES == 1
+ NoteLookup noteLookupStorage;
+ float noteVal;
+#else
int noteVal;
- NoteLookup *noteLookup; // Lookup stuff for this noteVal
-
- int keyVal;
- NoteLookup *keyLookup;
+ int fineShift;
+#endif
+ const NoteLookup *noteLookup; // LUTs for this noteVal
// Keyfollowed filter values
int realVal;
int filtVal;
- envstatus envs[3];
+ EnvelopeStatus envs[3];
int pulsewidth;
Bit32u lfoPos;
soundaddr partialOff;
- Bit32u ampEnvCache;
- Bit32u pitchEnvCache;
+ Bit32u ampEnvVal;
+ Bit32u pitchEnvVal;
float history[32];
@@ -93,19 +96,19 @@ private:
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);
- void mixBuffersStereo(Bit16s * buf1, Bit16s * buf2, Bit16s * outBuf, int len);
+ Bit16s *mixBuffers(Bit16s *buf1, Bit16s *buf2, int len);
+ Bit16s *mixBuffersRingMix(Bit16s *buf1, Bit16s *buf2, int len);
+ Bit16s *mixBuffersRing(Bit16s *buf1, Bit16s *buf2, int len);
+ void mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len);
Bit32s getFiltEnvelope();
- Bit32s getAmpEnvelope();
+ Bit32u getAmpEnvelope();
Bit32s getPitchEnvelope();
void initKeyFollow(int freqNum);
public:
- PatchCache *patchCache;
+ const PatchCache *patchCache;
PatchCache cachebackup;
Partial *pair;
@@ -119,7 +122,7 @@ public:
bool isActive();
void activate(int part);
void deactivate(void);
- void startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPartial);
+ void startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial);
void startDecay(EnvelopeType envnum, Bit32s startval);
void startDecayAll();
void setBend(float factor);
diff --git a/backends/midi/mt32/partialManager.cpp b/backends/midi/mt32/partialManager.cpp
index 4c9d264128..01fb57b026 100644
--- a/backends/midi/mt32/partialManager.cpp
+++ b/backends/midi/mt32/partialManager.cpp
@@ -36,7 +36,7 @@ PartialManager::~PartialManager(void) {
delete partialTable[i];
}
-void PartialManager::GetPerPartPartialUsage(int usage[9]) {
+void PartialManager::getPerPartPartialUsage(int usage[9]) {
memset(usage, 0, 9 * sizeof (int));
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
if (partialTable[i]->isActive())
@@ -44,12 +44,12 @@ void PartialManager::GetPerPartPartialUsage(int usage[9]) {
}
}
-void PartialManager::ClearAlreadyOutputed() {
+void PartialManager::clearAlreadyOutputed() {
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
partialTable[i]->alreadyOutputed = false;
}
-void PartialManager::AgeAll() {
+void PartialManager::ageAll() {
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
partialTable[i]->age++;
}
@@ -58,28 +58,28 @@ bool PartialManager::shouldReverb(int i) {
return partialTable[i]->shouldReverb();
}
-bool PartialManager::ProduceOutput(int i, Bit16s *buffer, Bit32u bufferLength) {
+bool PartialManager::produceOutput(int i, Bit16s *buffer, Bit32u bufferLength) {
return partialTable[i]->produceOutput(buffer, bufferLength);
}
-void PartialManager::DeactivateAll() {
+void PartialManager::deactivateAll() {
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
partialTable[i]->deactivate();
}
}
-unsigned int PartialManager::SetReserve(char *rset) {
+unsigned int PartialManager::setReserve(Bit8u *rset) {
unsigned int pr = 0;
for (int x = 0; x < 9; x++) {
for (int y = 0; y < rset[x]; y++) {
- PartialReserveTable[pr] = x;
+ partialReserveTable[pr] = x;
pr++;
}
}
return pr;
}
-Partial * PartialManager::AllocPartial(int partNum) {
+Partial *PartialManager::allocPartial(int partNum) {
Partial *outPartial = NULL;
// Use the first inactive partial reserved for the specified part (if there are any)
@@ -87,7 +87,7 @@ Partial * PartialManager::AllocPartial(int partNum) {
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
if (!partialTable[i]->isActive()) {
outPartial = partialTable[i];
- if (PartialReserveTable[i] == partNum)
+ if (partialReserveTable[i] == partNum)
break;
}
}
@@ -98,7 +98,7 @@ Partial * PartialManager::AllocPartial(int partNum) {
return outPartial;
}
-unsigned int PartialManager::GetFreePartialCount(void) {
+unsigned int PartialManager::getFreePartialCount(void) {
int count = 0;
memset(partialPart, 0, sizeof(partialPart));
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
@@ -111,9 +111,9 @@ unsigned int PartialManager::GetFreePartialCount(void) {
}
/*
-bool PartialManager::FreePartials(unsigned int needed, int partNum) {
+bool PartialManager::freePartials(unsigned int needed, int partNum) {
int i;
- int myPartPrior = (int)mt32ram.params.system.reserveSettings[partNum];
+ int myPartPrior = (int)mt32ram.system.reserveSettings[partNum];
if (myPartPrior<partialPart[partNum]) {
//This can have more parts, must kill off those with less priority
int most, mostPart;
@@ -125,7 +125,7 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {
int diff;
for (i=0;i<9;i++) {
- diff = partialPart[i] - (int)mt32ram.params.system.reserveSettings[i];
+ diff = partialPart[i] - (int)mt32ram.system.reserveSettings[i];
if (diff>0) {
if (diff>most) {
@@ -143,7 +143,7 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {
bool found;
int oldest;
int oldnum;
- while (partialPart[selectPart] > (int)mt32ram.params.system.reserveSettings[selectPart]) {
+ while (partialPart[selectPart] > (int)mt32ram.system.reserveSettings[selectPart]) {
oldest = -1;
oldnum = -1;
found = false;
@@ -198,7 +198,7 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {
}
*/
-bool PartialManager::FreePartials(unsigned int needed, int partNum) {
+bool PartialManager::freePartials(unsigned int needed, int partNum) {
if (needed == 0) {
return true;
}
@@ -206,7 +206,7 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {
// Kill those that are already decaying first
/*
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (PartialReserveTable[i] == partNum) {
+ if (partialReserveTable[i] == partNum) {
if (partialTable[i]->ownerPart != partNum) {
if (partialTable[i]->partCache->envs[AMPENV].decaying) {
partialTable[i]->isActive = false;
@@ -223,10 +223,10 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {
int priornum = -1;
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (PartialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) {
+ if (partialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) {
/*
- if (mt32ram.params.system.reserveSettings[partialTable[i]->ownerPart] < prior) {
- prior = mt32ram.params.system.reserveSettings[partialTable[i]->ownerPart];
+ if (mt32ram.system.reserveSettings[partialTable[i]->ownerPart] < prior) {
+ prior = mt32ram.system.reserveSettings[partialTable[i]->ownerPart];
priornum = i;
}*/
if (partialTable[i]->age > prior) {
@@ -244,17 +244,14 @@ bool PartialManager::FreePartials(unsigned int needed, int partNum) {
}
// Kill off the oldest partials within this part
-
while (needed > 0) {
Bit64s oldest = -1;
Bit64s oldlist = -1;
for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (partialTable[i]->isActive()) {
- if (partialTable[i]->getOwnerPart() == partNum) {
- if (partialTable[i]->age > oldest) {
- oldest = partialTable[i]->age;
- oldlist = i;
- }
+ if (partialTable[i]->getOwnerPart() == partNum && partialTable[i]->isActive()) {
+ if (partialTable[i]->age > oldest) {
+ oldest = partialTable[i]->age;
+ oldlist = i;
}
}
}
diff --git a/backends/midi/mt32/partialManager.h b/backends/midi/mt32/partialManager.h
index 7902a3cdc5..ee145d4c9f 100644
--- a/backends/midi/mt32/partialManager.h
+++ b/backends/midi/mt32/partialManager.h
@@ -31,22 +31,22 @@ private:
Synth *synth; // Only used for sending debug output
Partial *partialTable[MT32EMU_MAX_PARTIALS];
- Bit32s PartialReserveTable[MT32EMU_MAX_PARTIALS];
+ Bit32s partialReserveTable[MT32EMU_MAX_PARTIALS];
Bit32s partialPart[9]; // The count of partials played per part
public:
PartialManager(Synth *synth);
~PartialManager();
- Partial *AllocPartial(int partNum);
- unsigned int GetFreePartialCount(void);
- bool FreePartials(unsigned int needed, int partNum);
- unsigned int SetReserve(char *rset);
- void DeactivateAll();
- void AgeAll();
- bool ProduceOutput(int i, Bit16s *buffer, Bit32u bufferLength);
+ Partial *allocPartial(int partNum);
+ unsigned int getFreePartialCount(void);
+ bool freePartials(unsigned int needed, int partNum);
+ unsigned int setReserve(Bit8u *rset);
+ void deactivateAll();
+ void ageAll();
+ bool produceOutput(int i, Bit16s *buffer, Bit32u bufferLength);
bool shouldReverb(int i);
- void ClearAlreadyOutputed();
- void GetPerPartPartialUsage(int usage[9]);
+ void clearAlreadyOutputed();
+ void getPerPartPartialUsage(int usage[9]);
};
}
diff --git a/backends/midi/mt32/structures.h b/backends/midi/mt32/structures.h
index d64f0a44f2..047f69f8bd 100644
--- a/backends/midi/mt32/structures.h
+++ b/backends/midi/mt32/structures.h
@@ -56,92 +56,92 @@ typedef signed char Bit8s;
struct TimbreParam {
struct commonParam {
char name[10];
- char pstruct12; // 1&2 0-12 (1-13)
- char pstruct34; // #3&4 0-12 (1-13)
- char pmute; // 0-15 (0000-1111)
- char nosustain; // 0-1(Normal, No sustain)
+ Bit8u pstruct12; // 1&2 0-12 (1-13)
+ Bit8u pstruct34; // #3&4 0-12 (1-13)
+ Bit8u pmute; // 0-15 (0000-1111)
+ Bit8u nosustain; // 0-1(Normal, No sustain)
} MT32EMU_ALIGN_PACKED common;
struct partialParam {
struct wgParam {
- char coarse; // 0-96 (C1,C#1-C9)
- char fine; // 0-100 (-50 to +50 (cents?))
- char keyfollow; // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2)
- char bender; // 0,1 (ON/OFF)
- char waveform; // 0-1 (SQU/SAW)
- char pcmwave; // 0-127 (1-128)
- char pulsewid; // 0-100
- char pwvelo; // 0-14 (-7 - +7)
+ Bit8u coarse; // 0-96 (C1,C#1-C9)
+ Bit8u fine; // 0-100 (-50 to +50 (cents?))
+ Bit8u keyfollow; // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2)
+ Bit8u bender; // 0,1 (ON/OFF)
+ Bit8u waveform; // 0-1 (SQU/SAW)
+ Bit8u pcmwave; // 0-127 (1-128)
+ Bit8u pulsewid; // 0-100
+ Bit8u pwvelo; // 0-14 (-7 - +7)
} MT32EMU_ALIGN_PACKED wg;
struct envParam {
- char depth; // 0-10
- char sensitivity; // 1-100
- char timekeyfollow; // 0-4
- char time[4]; // 1-100
- char level[5]; // 1-100 (-50 - +50)
+ Bit8u depth; // 0-10
+ Bit8u sensitivity; // 1-100
+ Bit8u timekeyfollow; // 0-4
+ Bit8u time[4]; // 1-100
+ Bit8u level[5]; // 1-100 (-50 - +50)
} MT32EMU_ALIGN_PACKED env;
struct lfoParam {
- char rate; // 0-100
- char depth; // 0-100
- char modsense; // 0-100
+ Bit8u rate; // 0-100
+ Bit8u depth; // 0-100
+ Bit8u modsense; // 0-100
} MT32EMU_ALIGN_PACKED lfo;
struct tvfParam {
- char cutoff; // 0-100
- char resonance; // 0-30
- char keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2)
- char biaspoint; // 0-127 (<1A-<7C >1A-7C)
- char biaslevel; // 0-14 (-7 - +7)
- char envdepth; // 0-100
- char envsense; // 0-100
- char envdkf; // DEPTH KEY FOLL0W 0-4
- char envtkf; // TIME KEY FOLLOW 0-4
- char envtime[5]; // 1-100
- char envlevel[4]; // 1-100
+ Bit8u cutoff; // 0-100
+ Bit8u resonance; // 0-30
+ Bit8u keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2)
+ Bit8u biaspoint; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biaslevel; // 0-14 (-7 - +7)
+ Bit8u envdepth; // 0-100
+ Bit8u envsense; // 0-100
+ Bit8u envdkf; // DEPTH KEY FOLL0W 0-4
+ Bit8u envtkf; // TIME KEY FOLLOW 0-4
+ Bit8u envtime[5]; // 1-100
+ Bit8u envlevel[4]; // 1-100
} MT32EMU_ALIGN_PACKED tvf;
struct tvaParam {
- char level; // 0-100
- char velosens; // 0-100
- char biaspoint1; // 0-127 (<1A-<7C >1A-7C)
- char biaslevel1; // 0-12 (-12 - 0)
- char biaspoint2; // 0-127 (<1A-<7C >1A-7C)
- char biaslevel2; // 0-12 (-12 - 0)
- char envtkf; // TIME KEY FOLLOW 0-4
- char envvkf; // VELOS KEY FOLL0W 0-4
- char envtime[5]; // 1-100
- char envlevel[4]; // 1-100
+ Bit8u level; // 0-100
+ Bit8u velosens; // 0-100
+ Bit8u biaspoint1; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biaslevel1; // 0-12 (-12 - 0)
+ Bit8u biaspoint2; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biaslevel2; // 0-12 (-12 - 0)
+ Bit8u envtkf; // TIME KEY FOLLOW 0-4
+ Bit8u envvkf; // VELOS KEY FOLL0W 0-4
+ Bit8u envtime[5]; // 1-100
+ Bit8u envlevel[4]; // 1-100
} MT32EMU_ALIGN_PACKED tva;
} MT32EMU_ALIGN_PACKED partial[4];
} MT32EMU_ALIGN_PACKED;
struct PatchParam {
- char timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
- char timbreNum; // TIMBRE NUMBER 0-63
- char keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones)
- char fineTune; // FINE TUNE 0-100 (-50 - +50 cents)
- char benderRange; // BENDER RANGE 0-24
- char assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
- char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
- char dummy; // (DUMMY)
+ Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
+ Bit8u timbreNum; // TIMBRE NUMBER 0-63
+ Bit8u keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones)
+ Bit8u fineTune; // FINE TUNE 0-100 (-50 - +50 cents)
+ Bit8u benderRange; // BENDER RANGE 0-24
+ Bit8u assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
+ Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
+ Bit8u dummy; // (DUMMY)
} MT32EMU_ALIGN_PACKED;
struct MemParams {
struct PatchTemp {
PatchParam patch;
- char outlevel; // OUTPUT LEVEL 0-100
- char panpot; // PANPOT 0-14 (R-L)
- char dummyv[6];
+ Bit8u outlevel; // OUTPUT LEVEL 0-100
+ Bit8u panpot; // PANPOT 0-14 (R-L)
+ Bit8u dummyv[6];
} MT32EMU_ALIGN_PACKED patchSettings[8];
struct RhythmTemp {
- char timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF)
- char outlevel; // OUTPUT LEVEL 0-100
- char panpot; // PANPOT 0-14 (R-L)
- char reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
- } MT32EMU_ALIGN_PACKED rhythmSettings[64];
+ Bit8u timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF)
+ Bit8u outlevel; // OUTPUT LEVEL 0-100
+ Bit8u panpot; // PANPOT 0-14 (R-L)
+ Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
+ } MT32EMU_ALIGN_PACKED rhythmSettings[86]; // FIXME: Was 64, but let's support the next model...
TimbreParam MT32EMU_ALIGN_PACKED timbreSettings[8];
@@ -149,58 +149,43 @@ struct MemParams {
struct PaddedTimbre {
TimbreParam timbre;
- char padding[10];
+ Bit8u padding[10];
} MT32EMU_ALIGN_PACKED timbres[64 + 64 + 64 + 30]; // Group A, Group B, Memory, Rhythm
struct SystemArea {
- char masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
- char reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay)
- char reverbTime; // REVERB TIME 0-7 (1-8)
- char reverbLevel; // REVERB LEVEL 0-7 (1-8)
- char reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32
- char chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
- char masterVol; // MASTER VOLUME 0-100
+ Bit8u masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
+ Bit8u reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay)
+ Bit8u reverbTime; // REVERB TIME 0-7 (1-8)
+ Bit8u reverbLevel; // REVERB LEVEL 0-7 (1-8)
+ Bit8u reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32
+ Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
+ Bit8u masterVol; // MASTER VOLUME 0-100
} MT32EMU_ALIGN_PACKED system;
};
struct MemBanks {
- char pTemp[8][sizeof(MemParams::PatchTemp)];
- char rTemp[64][sizeof(MemParams::RhythmTemp)];
- char tTemp[8][sizeof(TimbreParam)];
- char patchBank[128][sizeof(PatchParam)];
- char timbreBank[64 + 64 + 64 + 30][sizeof(MemParams::PaddedTimbre)];
- char systemBank[sizeof(MemParams::SystemArea)];
+ Bit8u pTemp[8][sizeof(MemParams::PatchTemp)];
+ Bit8u rTemp[86][sizeof(MemParams::RhythmTemp)];
+ Bit8u tTemp[8][sizeof(TimbreParam)];
+ Bit8u patchBank[128][sizeof(PatchParam)];
+ Bit8u timbreBank[64 + 64 + 64 + 30][sizeof(MemParams::PaddedTimbre)];
+ Bit8u systemBank[sizeof(MemParams::SystemArea)];
// System memory 0x100000
// Display 0x200000
// Reset 0x7F0000
};
-union MT32RAMFormat {
- MemParams params;
- MemBanks banks;
-} MT32EMU_ALIGN_PACKED;
-
#if defined(_MSC_VER) || defined (__MINGW32__)
#pragma pack(pop)
#else
#pragma pack()
#endif
-struct PCMWave {
- char name[16];
- Bit32u addr;
- Bit32u len;
- bool loop;
- float tune;
- Bit32s ampval;
-};
-
struct PCMWaveEntry {
Bit32u addr;
Bit32u len;
- Bit32u pcmnum;
+ double tune;
bool loop;
- Bit32s aggSound; // This variable is for the last 9 PCM samples, which are actually loop combinations
};
struct soundaddr {
@@ -221,17 +206,15 @@ struct PatchCache {
char waveform;
int pulsewidth;
int pwsens;
- int pitchshift;
- int fineshift;
+
+ float pitch;
int lfodepth;
int lforate;
Bit32u lfoperiod;
int modsense;
- int keydir;
- int pitchkeyfollow;
- int pitchkeydir;
+ float pitchKeyfollow;
int filtkeyfollow;
@@ -264,8 +247,12 @@ struct PatchCache {
int structurePair;
// The following fields are actually common to all partials in the timbre
+ bool dirty;
Bit32u partialCount;
bool sustain;
+ float pitchShift;
+ bool reverb;
+ const StereoVolume *pansetptr;
};
class Partial; // Forward reference for class defined in partial.h
@@ -277,19 +264,17 @@ struct dpoly {
int freqnum;
int vel;
- bool reverb;
bool isDecay;
- const Bit32s *volumeptr;
- const StereoVolume *pansetptr;
+ const Bit32u *volumeptr;
Partial *partials[4];
bool pedalhold; // This marks keys that have been released on the keyboard, but are being held by the pedal
bool sustain;
- bool isActive();
- Bit64s getAge();
+ bool isActive() const;
+ Bit64s getAge() const;
};
}
diff --git a/backends/midi/mt32/synth.cpp b/backends/midi/mt32/synth.cpp
index 9361c3bda9..28d09f4d4d 100644
--- a/backends/midi/mt32/synth.cpp
+++ b/backends/midi/mt32/synth.cpp
@@ -26,20 +26,10 @@
#include "mt32emu.h"
-#if MT32EMU_BENCHMARK_FILTERS > 0
-#include <time.h>
-#endif
-
namespace MT32Emu {
const int MAX_SYSEX_SIZE = 512;
-iir_filter_type usefilter;
-
-static const Bit8u InitPatches[8] = {
- 68, 48, 95, 78, 41, 3, 110, 122
-};
-
// Maps MIDI channel numbers to MT-32 parts (not to be confused with "partials")
// This is the default (FIXME: the mapping from 11->9 is undocumented, is this correct?):
static const Bit8s InitChanTable[16] = {
@@ -51,23 +41,6 @@ static const Bit8s InitChanTable[16] = {
// 0, 1, 2, 3, 4, 5, 6, 7, -1, 8, -1, -1, -1, -1, -1, -1
//};
-static int axtoi(char *str) {
- int result = 0;
- while (*str) {
- char ch = *str++;
- if (ch >= '0' && ch <= '9')
- ch -= '0';
- else if (ch >= 'a' && ch <= 'f')
- ch = ch + 10 - 'a';
- else if (ch >= 'A' && ch <= 'F')
- ch = ch + 10 - 'A';
- else
- break;
- result = (result << 4) | ch;
- }
- return result;
-}
-
float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
float *hist2_ptr;
float output,new_hist;
@@ -103,7 +76,7 @@ float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLe
return(output);
}
-Bit8u Synth::calcSysexChecksum(Bit8u *data, Bit32u len, Bit8u checksum) {
+Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {
for (unsigned int i = 0; i < len; i++) {
checksum = checksum + data[i];
}
@@ -125,10 +98,11 @@ Synth::~Synth() {
close(); // Make sure we're closed and everything is freed
}
-void Synth::report(ReportType type, const void *data) {
+int Synth::report(ReportType type, const void *data) {
if (myProp.report != NULL) {
- myProp.report(myProp.userData, type, data);
+ return myProp.report(myProp.userData, type, data);
}
+ return 0;
}
void Synth::printDebug(const char *fmt, ...) {
@@ -143,15 +117,12 @@ void Synth::printDebug(const char *fmt, ...) {
va_end(ap);
}
-void Synth::initReverb(char newRevMode, char newRevTime) {
+void Synth::initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel) {
// FIXME:KG: I don't think it's necessary to recreate the reverbModel... Just set the parameters
if (reverbModel != NULL)
delete reverbModel;
reverbModel = new revmodel();
- curRevTime = newRevTime;
- curRevMode = newRevMode;
-
switch(newRevMode) {
case 0:
reverbModel->setroomsize(.1f);
@@ -175,8 +146,8 @@ void Synth::initReverb(char newRevMode, char newRevTime) {
break;
}
reverbModel->setdry(1);
- reverbModel->setwet((float)mt32ram.params.system.reverbLevel / 8.0f);
- reverbModel->setwidth((float)curRevTime / 8.0f);
+ reverbModel->setwet((float)newRevLevel / 8.0f);
+ reverbModel->setwidth((float)newRevTime / 8.0f);
}
File *Synth::openFile(const char *filename, File::OpenMode mode) {
@@ -228,7 +199,7 @@ bool Synth::loadPreset(const char *filename) {
if (inSys) {
syslen++;
if (c == 0xF7) {
- playSysex(&sysexBuf[0],syslen);
+ playSysex(&sysexBuf[0], syslen);
inSys = false;
syslen = 0;
} else if (syslen == MAX_SYSEX_SIZE) {
@@ -245,231 +216,22 @@ bool Synth::loadPreset(const char *filename) {
return rc;
}
-bool Synth::loadDrums(const char *filename) {
- File *file = openFile(filename, File::OpenMode_read);
- if (file == NULL) {
- return false;
- }
- int drumnum = 0;
- for (;;) {
- //Read common area
- TimbreParam *timbre = &mt32ram.params.timbres[drumnum + 192].timbre;
- if (file->read(&timbre->common, 14) != 14)
- break;
- char drumname[11];
- strncpy(drumname, timbre->common.name, 10);
- drumname[10] = 0;
- bool breakout = false;
- for (int t=0;t<4;t++) {
- if (((timbre->common.pmute >> t) & 0x1) == 0x1) {
- if (file->read(&timbre->partial[t], 58) != 58) {
- breakout = true;
- break;
- }
- //printDebug("Loaded drum #%d (%s) - t %d", drumnum, drumname, t);
- }
- }
- if (breakout) {
- break;
- }
- //printDebug("Loaded drum #%d (%s)", drumnum, drumname);
- drumnum++;
- }
- closeFile(file);
- return true;
-}
-
-void Synth::dumpDrums(const char *filename) {
- File *file = openFile(filename, File::OpenMode_write);
- if (file == NULL)
- return;
- char dumbtext[10];
- memset(dumbtext,0,10);
- for (int drumnum=0;drumnum<30;drumnum++) {
- // Sysex header
- if (!file->writeBit8u(0xf0))
- break;
- if (!file->writeBit8u(0x41))
- break;
- if (!file->writeBit8u(0x10))
- break;
- if (!file->writeBit8u(0x16))
- break;
- if (!file->writeBit8u(0x12))
- break;
-
- int useaddr = drumnum * 256;
- char lsb = (char)(useaddr & 0x7f);
- char isb = (char)((useaddr >> 7) & 0x7f);
- char msb = (char)(((useaddr >> 14) & 0x7f) | 0x08);
- //Address
- if (!file->writeBit8u(msb))
- break;
- if (!file->writeBit8u(isb))
- break;
- if (!file->writeBit8u(lsb))
- break;
-
- TimbreParam *timbre = &mt32ram.params.timbres[192 + drumnum].timbre;
- //Data
- if (file->write(&timbre->common,0xE) != 0xE)
- break;
- if (file->write(&timbre->partial[0],0x3A) != 0x3A)
- break;
- if (file->write(&timbre->partial[1],0x3A) != 0x3A)
- break;
- if (file->write(&timbre->partial[2],0x3A) != 0x3A)
- break;
- if (file->write(&timbre->partial[3],0x3A) != 0x3A)
- break;
- //Checksum
- unsigned char *dat = (unsigned char *)timbre;
- unsigned char checksum = calcSysexChecksum(dat, 246, msb + isb + lsb);
- if (!file->writeBit8u(checksum))
- break;
-
- //End of sysex
- if (!file->writeBit8u(0xf7))
- break;
- }
- closeFile(file);
-}
-
-bool Synth::loadPCMToROMMap(const char *filename) {
- File *file = openFile(filename, File::OpenMode_read); // Original specified text mode
-
+bool Synth::loadControlROM(const char *filename) {
+ File *file = openFile(filename, File::OpenMode_read); // ROM File
if (file == NULL) {
return false;
}
+ bool rc = (file->read(controlROMData, sizeof(controlROMData)) == sizeof(controlROMData));
- Bit32u PCMReassign[54];
- for (int i = 0; i < 54; i++) {
- PCMReassign[i] = i;
- PCM[i].tune = 220.0f;
- PCM[i].ampval = 256;
- }
- //PCM[53].ampval = 128;
-
- char tbuf[512];
- char *cp;
- if (!file->readLine(tbuf,sizeof(tbuf))) {
- return false;
- }
- Bit32u patchstart = 0; //axtoi(tbuf);
- Bit32u patchend = 0;
- Bit32u patchcount = 0;
- bool rc = true;
- for (;;) {
- if (!file->readLine(tbuf,sizeof(tbuf))) {
- if (!file->isEOF()) {
- rc = false;
- }
- break;
- }
- cp = strtok(tbuf," \n\r");
- PCM[patchcount].loop = false;
- if (cp != NULL) {
- patchend = axtoi(cp);
- cp = strtok(NULL," \n\r");
- 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);
- PCMReassign[newpcm] = patchcount;
- cp = strtok(NULL," \n\r");
- if (cp != NULL) {
- if (atoi(cp)==1)
- PCM[patchcount].loop = true;
- cp = strtok(NULL," \n\r");
- if (cp != NULL) {
- PCM[patchcount].tune = (float)atoi(cp) / 100.0f;
- //printDebug("PCM %d tuning at %f", patchcount, PCM[patchcount].tune);
- }
- }
- }
- }
- }
- }
- if (patchend==0)
- break;
-
- PCM[patchcount].addr = patchstart;
- PCM[patchcount].len = patchend - patchstart;
- patchcount++;
- //printf("Patch %d %d %d %d", patchcount, patchstart, patchend, mt32ram.PCM[patchcount].len);
- patchstart = patchend;
- }
closeFile(file);
- if (!rc)
- return rc;
-
- PCM[53].len = 1950;
-
- // Generate official PCM list
-
- // Normal sounds
- int pat = 0;
- for (int p = 0; p < 54; p++) {
- PCMList[pat].addr = PCM[PCMReassign[p]].addr;
- PCMList[pat].len = PCM[PCMReassign[p]].len;
- PCMList[pat].loop = PCM[PCMReassign[p]].loop;
- PCMList[pat].aggSound = -1;
- PCMList[pat].pcmnum = PCMReassign[p];
- pat++;
- }
-
- // Drum specific sounds. Not exactly sure yet how these are different
- for (int p = 0; p < 20; p++) {
- PCMList[pat] = PCMList[p];
- pat++;
- }
-
- // Looped PCM sounds. The last remaining 9 are aggregate sounds;
- // FIXME:KG: I hope this is correct; the original was heavily broken,
- // and it was hard to determine the author's intention.
- for (int p = 0; p < 54; p++) {
- if (p < 45) {
- int pcmNum = p > 7 ? p - 1 : p;
- PCMList[pat].addr = PCM[PCMReassign[pcmNum]].addr;
- PCMList[pat].len = PCM[PCMReassign[pcmNum]].len;
- PCMList[pat].pcmnum = PCMReassign[pcmNum];
- PCMList[pat].loop = true;
- PCMList[pat].aggSound = -1;
- } else {
- //Calculate aggregate length
- int aggsnd = p - 45;
- int tmplen = 0;
- int sndpos = 0;
- while (LoopPatterns[aggsnd][sndpos] != -1) {
- tmplen += PCM[LoopPatterns[aggsnd][sndpos]].len;
- sndpos++;
- }
- PCMList[pat].addr = 0;
- PCMList[pat].len = tmplen;
- PCMList[pat].loop = true;
- PCMList[pat].aggSound = aggsnd;
- }
- pat++;
- }
-
- //for (p=0;p<128;p++)
- // printDebug("PCM #%d addr 0x%x len %d loop %d aggSound %d pcmnum %d", p, PCMList[p].addr, PCMList[p].len, PCMList[p].loop, PCMList[p].aggSound, PCMList[p].pcmnum);
- return true;
+ return rc;
}
-bool Synth::loadROM(const char *filename) {
+bool Synth::loadPCMROM(const char *filename) {
File *file = openFile(filename, File::OpenMode_read); // ROM File
if (file == NULL) {
return false;
}
-#ifdef MT32OUT
- File *outFile = openFile("mt32out.raw", File::OpenMode_write);
- File *outFileB = openFile("mt32out2.raw", File::OpenMode_write);
-#endif
bool rc = true;
for (int i = 0; ; i++) {
Bit8u s;
@@ -495,12 +257,12 @@ bool Synth::loadROM(const char *filename) {
int order[16] = {0, 9, 1 ,2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8};
- e=0;
- for (u=0;u<15;u++) {
- if (order[u]<8)
- bit = (s >> (7-order[u])) & 0x1;
+ e = 0;
+ for (u = 0; u < 15; u++) {
+ if (order[u] < 8)
+ bit = (s >> (7 - order[u])) & 0x1;
else
- bit = (c >> (7-(order[u]-8))) & 0x1;
+ bit = (c >> (7 - (order[u] - 8))) & 0x1;
e = e | (short)(bit << (15 - u));
}
@@ -514,10 +276,6 @@ bool Synth::loadROM(const char *filename) {
e = (int)((float)e * (x/3200));
*/
-#ifdef MT32OUT
- outFile->writeBit8u(e & 0xff);
- outFile->writeBit8u(((e >> 8) & 0x7f));
-#endif
// File is encoded in dB, convert to PCM
// MINDB = -96
// MAXDB = -15
@@ -525,107 +283,169 @@ bool Synth::loadROM(const char *filename) {
testval = (float)((~e) & 0x7fff);
testval = -(testval / 400.00f);
//testval = -(testval / 341.32291666666666666666666666667);
- float vol = powf(8,(testval / 20)) * 32767.0f;
+ float vol = powf(8, testval / 20) * 32767.0f;
- if (e>0)
+ if (e > 0)
vol = -vol;
romfile[i] = (Bit16s)vol;
-#ifdef MT32OUT
- outFileB->writeBit8u(romfile[i] & 0xff);
- outFileB->writeBit8u(romfile[i] >> 8);
-#endif
}
-#ifdef MT32OUT
- closeFile(outFileB);
- closeFile(outFile);
-#endif
closeFile(file);
return rc;
}
+struct TempPCMStruct
+{
+ Bit8u pos;
+ Bit8u len;
+ Bit8u pitchLSB;
+ Bit8u pitchMSB;
+};
+
+void Synth::initPCMList() {
+ TempPCMStruct *tps = (TempPCMStruct *)&controlROMData[0x3000];
+ printDebug("********************************");
+ for (int i = 0; i < 128; i++) {
+ int rAddr = tps[i].pos * 0x800;
+ int rLenExp = (tps[i].len & 0x70) >> 4;
+ int rLen = 0x800 << rLenExp;
+ bool rLoop = (tps[i].len & 0x80) != 0;
+ Bit8u rFlag = tps[i].len & 0x0F;
+ Bit16u rTuneOffset = (tps[i].pitchMSB << 8) | tps[i].pitchLSB;
+ //FIXME:KG: Pick a number, any number. 260.1f sounded best to me in listening tests, but needs to be confirmed.
+ //double STANDARDFREQ = 261.6255653005986346778499935233; // A below Middle C of 440Hz
+ double STANDARDFREQ = 260.1f;
+ float rTune = (float)(STANDARDFREQ * pow(2.0, (0x5000 - rTuneOffset) / 4096.0));
+ //printDebug("%f,%d,%d", pTune, tps[i].pitchCoarse, tps[i].pitchFine);
+ PCMList[i].addr = rAddr;
+ PCMList[i].len = rLen;
+ PCMList[i].loop = rLoop;
+ PCMList[i].tune = rTune;
+ }
+ printDebug("********************************");
+}
+
+void Synth::initRhythmTimbre(int timbreNum, const Bit8u *mem) {
+ TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
+ memcpy(&timbre->common, mem, 14);
+ mem += 14;
+ char drumname[11];
+ strncpy(drumname, timbre->common.name, 10);
+ drumname[10] = 0;
+ bool breakout = false;
+ for (int t = 0; t < 4; t++) {
+ if (((timbre->common.pmute >> t) & 0x1) == 0x1) {
+ memcpy(&timbre->partial[t], mem, 58);
+ mem += 58;
+ }
+ }
+}
+
+void Synth::initRhythmTimbres() {
+ TempPCMStruct *tps = (TempPCMStruct *)&controlROMData[0x3000];
+ const Bit8u *drumMap = &controlROMData[0x3200];
+ int timbreNum = 192;
+ for (Bit16u i = 0x3200; i < 0x323C; i += 2) {
+ Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i];
+ initRhythmTimbre(timbreNum++, &controlROMData[address]);
+ }
+}
+
+void Synth::initTimbres(Bit16u mapAddress, int startTimbre) {
+ for (Bit16u i = mapAddress; i < mapAddress + 0x80; i += 2) {
+ Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i];
+ address = address + (mapAddress - 0x8000);
+ TimbreParam *timbre = &mt32ram.timbres[startTimbre++].timbre;
+ memcpy(timbre, &controlROMData[address], sizeof(TimbreParam));
+ }
+}
+
bool Synth::open(SynthProperties &useProp) {
if (isOpen)
return false;
- // Initalise patch information
-
myProp = useProp;
- usefilter = &iir_filter_normal;
-
- partialManager = new PartialManager(this);
-
- // This is so that names won't be garbage during early setup debug output, but we can detect bugs
+ // This is to help detect bugs
memset(&mt32ram, '?', sizeof(mt32ram));
- printDebug("Initialising patch banks");
- initmode = 0;
- if (!loadPreset("Preset1.syx")) {
- report(ReportType_errorPreset1, &errno);
+ printDebug("Loading Control ROM");
+ if (!loadControlROM("MT32_CONTROL.ROM")) {
+ printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM");
+ report(ReportType_errorControlROM, &errno);
return false;
}
- initmode = 1;
- if (!loadPreset("Preset2.syx")) {
- report(ReportType_errorPreset2, &errno);
- return false;
- }
- initmode = 2;
- printDebug("Initialising Drums");
- if (!loadDrums("drumpat.rom")) {
- report(ReportType_errorDrumpat, &errno);
+ printDebug("Loading PCM ROM");
+ if (!loadPCMROM("MT32_PCM.ROM")) {
+ printDebug("Init Error - Missing MT32_PCM.ROM");
+ report(ReportType_errorPCMROM, &errno);
return false;
}
-#if MT32EMU_DUMP_DRUMS == 1
- strcpy(&pathBuf[0], baseDir);
- dumpDrums(strcat(&pathBuf[0],"drumsys.syx"));
-#endif
+ partialManager = new PartialManager(this);
- printDebug("Initialising PCM-to-ROM map");
- if (!loadPCMToROMMap("patchlog.cfg")) {
- printDebug("Init Error - Missing patchlog.cfg");
- report(ReportType_errorPatchlog, &errno);
- return false;
- }
+ printDebug("Initialising PCM List");
+ initPCMList();
- printDebug("Initialising ROM");
- if (!loadROM("MT32_PCM.ROM")) {
- printDebug("Init Error - Missing MT32_PCM.ROM");
- report(ReportType_errorMT32ROM, &errno);
- return false;
- }
- memcpy(chantable, InitChanTable, sizeof (chantable));
- for (unsigned char i = 0; i < 128; i++) {
- mt32ram.params.patches[i].timbreGroup = i >> 6;
- mt32ram.params.patches[i].timbreNum = i & 63;
+ printDebug("Initialising Timbre Bank A");
+ initTimbres(0x8000, 0);
+
+ printDebug("Initialising Timbre Bank B");
+ initTimbres(0xC000, 64);
+
+ printDebug("Initialising Timbre Bank R");
+ initRhythmTimbres();
+
+ printDebug("Initialising Rhythm Temp");
+ memcpy(mt32ram.rhythmSettings, &controlROMData[0x741C], 344);
+
+ printDebug("Initialising Patches");
+ for (Bit8u i = 0; i < 128; i++) {
+ PatchParam *patch = &mt32ram.patches[i];
+ patch->timbreGroup = i / 64;
+ patch->timbreNum = i % 64;
+ patch->keyShift = 24;
+ patch->fineTune = 50;
+ patch->benderRange = 12;
+ patch->assignMode = 0;
+ patch->reverbSwitch = 1;
+ patch->dummy = 0;
}
- if (!TableInitialiser::initMT32Tables(this, PCM, (float)myProp.sampleRate)) {
- report(ReportType_errorSampleRate, NULL);
- return false;
+ printDebug("Initialising System");
+ //FIXME: Confirm that these are all correct
+ // The MT-32 manual claims that "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.
+ // Regardless, I'm setting the default masterTune to 440Hz
+ mt32ram.system.masterTune = 0x40;
+ mt32ram.system.reverbMode = 0;
+ mt32ram.system.reverbTime = 5;
+ mt32ram.system.reverbTime = 3;
+ memcpy(mt32ram.system.reserveSettings, &controlROMData[0x57E5], 9);
+ for (Bit8u i = 0; i < 9; i++) {
+ mt32ram.system.chanAssign[i] = i + 1;
}
- if (myProp.useDefaultReverb)
- initReverb(0, 5);
- else
- initReverb(myProp.reverbType, myProp.reverbTime);
+ mt32ram.system.masterVol = 100;
+ if (!refreshSystem())
+ return false;
- for (int i = 0; i < 9; i++) {
+ for (int i = 0; i < 8; i++) {
+ mt32ram.patchSettings[i].outlevel = 80;
+ mt32ram.patchSettings[i].panpot = controlROMData[0x5800 + i];
+ memset(mt32ram.patchSettings[i].dummyv, 0, sizeof(mt32ram.patchSettings[i].dummyv));
parts[i] = new Part(this, i);
-
- if (i<8) {
- // 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]->setProgram(controlROMData[0x57EE + i]);
}
+ parts[8] = new RhythmPart(this, 8);
// For resetting mt32 mid-execution
mt32default = mt32ram;
+ iirFilter = &iir_filter_normal;
+
#ifdef MT32EMU_HAVE_X86
bool availableSSE = DetectSIMD();
bool available3DNow = Detect3DNow();
@@ -637,52 +457,19 @@ bool Synth::open(SynthProperties &useProp) {
if (available3DNow) {
printDebug("Detected and using SIMD (AMD 3DNow) extensions");
- usefilter = &iir_filter_3dnow;
+ iirFilter = &iir_filter_3dnow;
report(ReportType_using3DNow, NULL);
} else if (availableSSE) {
printDebug("Detected and using SIMD (Intel SSE) extensions");
- usefilter = &iir_filter_sse;
+ iirFilter = &iir_filter_sse;
report(ReportType_usingSSE, NULL);
}
#endif
-#if MT32EMU_BENCHMARK_FILTERS > 1
- // Benchmark 3DNow, Floating point, and SSE filters
- clock_t start, end;
- float histval[50];
-
- for (int bench = 0; bench < 3; bench++) {
- start = clock();
- for (int benchcnt=0;benchcnt<2000000;benchcnt++) {
- switch (bench) {
- case 0:
- iir_filter_normal(0,&histval[0],filtcoeff[0][0],0);
- break;
- case 1:
- if (!availableSSE) {
- printDebug("Skipping SSE benchmark, SSE not available");
- continue;
- }
- iir_filter_sse(0,&histval[0],filtcoeff[0][0],0);
- break;
- case 2:
- if (!available3DNow) {
- printDebug("Skipping 3DNow benchmark, 3DNow not available");
- continue;
- }
- iir_filter_3dnow(0,&histval[0],filtcoeff[0][0],0);
- break;
- }
- }
- end = clock();
- printDebug("Bench %ld completed in %ld milliseconds", bench, (end - start) * 1000 / CLOCKS_PER_SEC);
- }
-#endif
-
- isOpen=true;
- isEnabled=false;
+ isOpen = true;
+ isEnabled = false;
- printDebug("**************** Initialisation complete ****************");
+ printDebug("*** Initialisation complete ***");
return true;
}
@@ -690,15 +477,7 @@ void Synth::close(void) {
if (!isOpen)
return;
- for (int t = 0; t < 3; t++) {
- for (int m = 0; m < NUM_NOTES; m++) {
- if (noteLookups[m].waveforms[t] != NULL) {
- delete[] noteLookups[m].waveforms[t];
- noteLookups[m].waveforms[t] = NULL;
- noteLookups[m].waveformSize[t] = 0;
- }
- }
- }
+ TableInitialiser::freeNotes();
if (partialManager != NULL) {
delete partialManager;
partialManager = NULL;
@@ -752,7 +531,7 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char
// MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40
parts[part]->stopNote(note);
} else {
- parts[part]->playNote(partialManager, note, velocity);
+ parts[part]->playNote(note, velocity);
}
break;
case 0xB: // Control change
@@ -796,11 +575,7 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char
break;
case 0xC: // Program change
//printDebug("Program change %01x", note);
- if (part < 8) {
- parts[part]->setPatch(note);
- } else {
- printDebug("Program change attempted on rhythm part");
- }
+ parts[part]->setProgram(note);
break;
case 0xE: // Pitch bender
bend = (velocity << 7) | (note);
@@ -815,7 +590,7 @@ void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char
//midiOutShortMsg(m_out, msg);
}
-void Synth::playSysex(Bit8u * sysex,Bit32u len) {
+void Synth::playSysex(const Bit8u * sysex,Bit32u len) {
if (len < 3) {
printDebug("playSysex: Message is too short for sysex (%d bytes)", len);
}
@@ -830,7 +605,7 @@ void Synth::playSysex(Bit8u * sysex,Bit32u len) {
playSysexWithoutFraming(sysex + 1, len - 2);
}
-void Synth::playSysexWithoutFraming(Bit8u * sysex, Bit32u len) {
+void Synth::playSysexWithoutFraming(const Bit8u * sysex, Bit32u len) {
if (len < 4) {
printDebug("playSysexWithoutFraming: Message is too short (%d bytes)!", len);
return;
@@ -861,7 +636,7 @@ void Synth::playSysexWithoutFraming(Bit8u * sysex, Bit32u len) {
#define NUMTOUCHED(x,y) (((x) + sizeof(y) - 1) / sizeof(y))
-void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u len) {
+void Synth::playSysexWithoutHeader(unsigned char device, const Bit8u *sysex, Bit32u len) {
if (device > 0x10) {
// We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels
printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", (int)device);
@@ -920,21 +695,21 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le
}
if (addr >= MEMADDR(0x030000) && addr < MEMADDR(0x030110)) {
int off = addr - MEMADDR(0x030000);
- if (off + len > sizeof(mt32ram.banks.pTemp)) {
+ if (off + len > sizeof(mt32ram.patchSettings)) {
printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
return;
}
int firstPart = off / sizeof(MemParams::PatchTemp);
off %= sizeof(MemParams::PatchTemp);
for (unsigned int m = 0; m < len; m++)
- mt32ram.banks.pTemp[firstPart][off + m] = sysex[m];
+ ((Bit8u *)&mt32ram.patchSettings[firstPart])[off + m] = sysex[m];
//printDebug("Patch temp: Patch %d, offset %x, len %d", off/16, off % 16, len);
int lastPart = firstPart + NUMTOUCHED(off + len, MemParams::PatchTemp) - 1;
for (int i = firstPart; i <= lastPart; i++) {
- int absTimbreNum = mt32ram.params.patchSettings[i].patch.timbreGroup * 64 + mt32ram.params.patchSettings[i].patch.timbreNum;
+ int absTimbreNum = mt32ram.patchSettings[i].patch.timbreGroup * 64 + mt32ram.patchSettings[i].patch.timbreNum;
char timbreName[11];
- memcpy(timbreName, mt32ram.params.timbres[absTimbreNum].timbre.common.name, 10);
+ memcpy(timbreName, mt32ram.timbres[absTimbreNum].timbre.common.name, 10);
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) {
@@ -942,83 +717,84 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le
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]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre);
}
- parts[i]->refreshPatch();
+ parts[i]->refresh();
}
}
} else if (addr >= MEMADDR(0x030110) && addr < MEMADDR(0x040000)) {
int off = addr - MEMADDR(0x030110);
- if (off + len > sizeof(mt32ram.banks.rTemp)) {
+ if (off + len > sizeof(mt32ram.rhythmSettings)) {
printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
return;
}
int firstDrum = off / sizeof(MemParams::RhythmTemp);
off %= sizeof(MemParams::RhythmTemp);
for (unsigned int m = 0; m < len; m++)
- mt32ram.banks.rTemp[firstDrum][off + m] = sysex[m];
+ ((Bit8u *)&mt32ram.rhythmSettings[firstDrum])[off + m] = sysex[m];
int lastDrum = firstDrum + NUMTOUCHED(off + len, MemParams::RhythmTemp) - 1;
for (int i = firstDrum; i <= lastDrum; i++) {
- int timbreNum = mt32ram.params.rhythmSettings[i].timbre;
+ int timbreNum = mt32ram.rhythmSettings[i].timbre;
char timbreName[11];
if (timbreNum < 94) {
- memcpy(timbreName, mt32ram.params.timbres[128 + timbreNum].timbre.common.name, 10);
+ memcpy(timbreName, mt32ram.timbres[128 + timbreNum].timbre.common.name, 10);
timbreName[10] = 0;
} else {
strcpy(timbreName, "[None]");
}
- 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);
+ printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", firstDrum, lastDrum, off, off + len, i, mt32ram.rhythmSettings[i].outlevel, mt32ram.rhythmSettings[i].panpot, mt32ram.rhythmSettings[i].reverbSwitch, mt32ram.rhythmSettings[i].timbre, timbreName);
}
if (parts[8] != NULL) {
- parts[8]->refreshDrumCache();
+ parts[8]->refresh();
}
} else if (addr >= MEMADDR(0x040000) && addr < MEMADDR(0x050000)) {
int off = addr - MEMADDR(0x040000);
- if (off + len > sizeof(mt32ram.banks.tTemp)) {
+ if (off + len > sizeof(mt32ram.timbreSettings)) {
printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
return;
}
int firstPart = off / sizeof(TimbreParam);
off %= sizeof(TimbreParam);
for (unsigned int m = 0; m < len; m++)
- mt32ram.banks.tTemp[firstPart][off + m] = sysex[m];
+ ((Bit8u *)&mt32ram.timbreSettings[firstPart])[off + m] = sysex[m];
int lastPart = firstPart + NUMTOUCHED(off + len, TimbreParam) - 1;
for (int i = firstPart; i <= lastPart; i++) {
char instrumentName[11];
- memcpy(instrumentName, mt32ram.params.timbreSettings[i].common.name, 10);
+ memcpy(instrumentName, mt32ram.timbreSettings[i].common.name, 10);
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]->refresh();
}
}
}
else if (addr >= MEMADDR(0x050000) && addr < MEMADDR(0x060000)) {
int off = addr - MEMADDR(0x050000);
- if (off + len > sizeof(mt32ram.banks.patchBank)) {
+ if (off + len > sizeof(mt32ram.patches)) {
printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
return;
}
int firstPatch = off / sizeof(PatchParam);
off %= sizeof(PatchParam);
for (unsigned int m = 0; m < len; m++)
- mt32ram.banks.patchBank[firstPatch][off + m] = sysex[m];
+ ((Bit8u *)&mt32ram.patches[firstPatch])[off + m] = sysex[m];
int lastPatch = firstPatch + NUMTOUCHED(off + len, PatchParam) - 1;
for (int i = firstPatch; i <= lastPatch; i++) {
- PatchParam *patch = &mt32ram.params.patches[i];
+ PatchParam *patch = &mt32ram.patches[i];
int patchAbsTimbreNum = patch->timbreGroup * 64 + patch->timbreNum;
char instrumentName[11];
- memcpy(instrumentName, mt32ram.params.timbres[patchAbsTimbreNum].timbre.common.name, 10);
+ memcpy(instrumentName, mt32ram.timbres[patchAbsTimbreNum].timbre.common.name, 10);
instrumentName[10] = 0;
- printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s)", firstPatch, lastPatch, off, off + len, i, patchAbsTimbreNum, instrumentName);
+ Bit8u *n = (Bit8u *)patch;
+ printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s) %02X%02X%02X%02X%02X%02X%02X%02X", firstPatch, lastPatch, off, off + len, i, patchAbsTimbreNum, instrumentName, n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]);
// FIXME:KG: The below is definitely dodgy. We just guess that this is the patch that the part was using
// based on a timbre match (but many patches could have the same timbre!)
// If this refresh is really correct, we should store the patch number in use by each part.
/*
for (int part = 0; part < 8; part++) {
if (parts[part] != NULL) {
- int partPatchAbsTimbreNum = mt32ram.params.patchSettings[part].patch.timbreGroup * 64 + mt32ram.params.patchSettings[part].patch.timbreNum;
- if (partPatchAbsTimbreNum == patchAbsTimbreNum) {
+ int partPatchAbsTimbreNum = mt32ram.patchSettings[part].patch.timbreGroup * 64 + mt32ram.patchSettings[part].patch.timbreNum;
+ if (parts[part]->getAbsTimbreNum() == patchAbsTimbreNum) {
parts[part]->setPatch(patch);
parts[part]->RefreshPatch();
}
@@ -1027,7 +803,7 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le
*/
}
} else if (addr >= MEMADDR(0x080000) && addr < MEMADDR(0x090000)) {
- // Timbre patches
+ // Timbres
int off = addr - MEMADDR(0x080000);
if (off + len > sizeof(MemParams::PaddedTimbre) * 64) {
// You can only write to one group at a time
@@ -1049,75 +825,34 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le
// Write into user timbre group
}
for (unsigned int m = 0; m < len; m++)
- mt32ram.banks.timbreBank[firstTimbre][off + m] = sysex[m];
+ ((Bit8u *)&mt32ram.timbres[firstTimbre])[off + m] = sysex[m];
unsigned int lastTimbre = firstTimbre + NUMTOUCHED(len + off, MemParams::PaddedTimbre) - 1;
for (unsigned int i = firstTimbre; i <= lastTimbre; i++) {
char instrumentName[11];
- memcpy(instrumentName, mt32ram.params.timbres[i].timbre.common.name, 10);
+ memcpy(instrumentName, mt32ram.timbres[i].timbre.common.name, 10);
instrumentName[10] = 0;
printDebug("WRITE-TIMBRE (%d-%d@%d..%d): %d; name=\"%s\"", firstTimbre, lastTimbre, off, off + len, i, instrumentName);
// FIXME:KG: Not sure if the stuff below should be done (for rhythm and/or parts)...
// 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
- }
- for (unsigned int part = 0; part < 8; part++) {
+ for (unsigned int part = 0; part < 9; part++) {
if (parts[part] != NULL) {
- if (parts[part]->getAbsTimbreNum() == i) {
- parts[part]->refreshPatch();
- }
+ parts[part]->refreshTimbre(i);
}
}
}
} else if (addr >= MEMADDR(0x100000) && addr < MEMADDR(0x200000)) {
int off = addr - MEMADDR(0x100000);
- if (off + len > sizeof(mt32ram.banks.systemBank)) {
+ if (off + len > sizeof(mt32ram.system)) {
printDebug("playSysexWithoutHeader: Message goes beyond bounds of memory region (addr=0x%06x, len=%d)!", SYSEXMEMADDR(addr), len);
return;
}
for (unsigned int m = 0; m < len; m++)
- mt32ram.banks.systemBank[m + off] = sysex[m];
+ ((Bit8u *)&mt32ram.system)[m + off] = sysex[m];
report(ReportType_devReconfig, NULL);
- printDebug("System Reconfiguration:");
- memset(chantable,-1,sizeof(chantable));
-
- 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();
- } else {
- chantable[(int)mt32ram.params.system.chanAssign[i]] = (char)i;
- }
- }
-
- printDebug(" Master Tune: %f", ((float)mt32ram.params.system.masterTune)*0.2+432.1);
- printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.params.system.reverbMode, mt32ram.params.system.reverbTime, mt32ram.params.system.reverbLevel);
- report(ReportType_newReverbMode, &mt32ram.params.system.reverbMode);
- report(ReportType_newReverbTime, &mt32ram.params.system.reverbTime);
- report(ReportType_newReverbLevel, &mt32ram.params.system.reverbLevel);
-
- if ((mt32ram.params.system.reverbMode != curRevMode) || (mt32ram.params.system.reverbTime != curRevTime)) {
- if (myProp.useDefaultReverb) {
- initReverb(mt32ram.params.system.reverbMode, mt32ram.params.system.reverbTime);
- curRevLevel = mt32ram.params.system.reverbLevel;
- } else {
- initReverb(myProp.reverbType, myProp.reverbTime);
- curRevLevel = myProp.reverbLevel;
- }
- }
-
- char *rset = mt32ram.params.system.reserveSettings;
- printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
- int pr = partialManager->SetReserve(rset);
- if (pr != 32)
- printDebug(" (Partial Reserve Table with less than 32 partials reserved!)");
- rset = mt32ram.params.system.chanAssign;
- printDebug(" Part assign: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
- printDebug(" Master volume: %d", mt32ram.params.system.masterVol);
- mastervolume = (Bit16s)((float)mt32ram.params.system.masterVol * 327.0);
+ printDebug("WRITE-SYSTEM:");
+ refreshSystem();
} else if (addr == MEMADDR(0x200000)) {
char buf[MAX_SYSEX_SIZE];
if (len > MAX_SYSEX_SIZE - 1) {
@@ -1131,73 +866,112 @@ void Synth::playSysexWithoutHeader(unsigned char device, Bit8u *sysex, Bit32u le
} else if (addr >= MEMADDR(0x7f0000)) {
printDebug("Reset");
report(ReportType_devReset, NULL);
- partialManager->DeactivateAll();
+ partialManager->deactivateAll();
mt32ram = mt32default;
- for (int i = 0; i < 8; i++) {
- parts[i]->refreshPatch();
+ for (int i = 0; i < 9; i++) {
+ parts[i]->refresh();
}
- parts[8]->refreshDrumCache();
isEnabled = false;
} else {
printDebug("Sysex write to unrecognised address %06x", SYSEXMEMADDR(addr));
}
}
-int Synth::dumpSysex(char *filename) {
- File *file = openFile(filename, File::OpenMode_write);
- if (file == NULL)
- return -1;
+bool Synth::refreshSystem() {
+ memset(chantable,-1,sizeof(chantable));
- int patchnum;
- for (patchnum=0;patchnum<64;patchnum++) {
- // Sysex header
- if (!file->writeBit8u(0xF0))
- break;
- if (!file->writeBit8u(0x41))
- break;
- if (!file->writeBit8u(0x10))
- break;
- if (!file->writeBit8u(0x16))
- break;
- if (!file->writeBit8u(0x12))
- break;
+ for (unsigned int i = 0; i < 9; i++) {
+ //LOG(LOG_MISC|LOG_ERROR,"Part %d set to MIDI channel %d",i,mt32ram.system.chanAssign[i]);
+ if (mt32ram.system.chanAssign[i] == 16 && parts[i] != NULL) {
+ parts[i]->allStop();
+ } else {
+ chantable[(int)mt32ram.system.chanAssign[i]] = (char)i;
+ }
+ }
+ //FIXME:KG: This is just an educated guess.
+ // The LAPC-I documentation claims a range of 427.5Hz-452.6Hz (similar to what we have here)
+ // The MT-32 documentation claims a range of 432.1Hz-457.6Hz
+ masterTune = 440.0f * powf(2.0f, (mt32ram.system.masterTune - 64.0f) / (128.0f * 12.0f));
+ printDebug(" Master Tune: %f", masterTune);
+ printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
+ report(ReportType_newReverbMode, &mt32ram.system.reverbMode);
+ report(ReportType_newReverbTime, &mt32ram.system.reverbTime);
+ report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel);
+
+ if (myProp.useDefaultReverb) {
+ initReverb(mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
+ } else {
+ initReverb(myProp.reverbType, myProp.reverbTime, mt32ram.system.reverbLevel);
+ }
- int useaddr = patchnum * 256;
- char lsb = (char)(useaddr & 0x7f);
- char isb = (char)((useaddr >> 7) & 0x7f);
- char msb = (char)(((useaddr >> 14) & 0x7f) | 0x08);
- //Address
- if (!file->writeBit8u(msb))
- break;
- if (!file->writeBit8u(isb))
- break;
- if (!file->writeBit8u(lsb))
- break;
+ Bit8u *rset = mt32ram.system.reserveSettings;
+ printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
+ int pr = partialManager->setReserve(rset);
+ if (pr != 32)
+ printDebug(" (Partial Reserve Table with less than 32 partials reserved!)");
+ rset = mt32ram.system.chanAssign;
+ printDebug(" Part assign: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
+ printDebug(" Master volume: %d", mt32ram.system.masterVol);
+ masterVolume = (Bit16u)(mt32ram.system.masterVol * 327);
+ if (!TableInitialiser::initMT32Tables(this, PCMList, (float)myProp.sampleRate, masterTune)) {
+ report(ReportType_errorSampleRate, NULL);
+ return false;
+ }
+ return true;
+}
- //Data
- if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.common,0xE) != 0xE)
- break;
- if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[0],0x3A) != 0x3A)
- break;
- if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[1],0x3A) != 0x3A)
- break;
- if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[2],0x3A) != 0x3A)
- break;
- if (file->write(&mt32ram.params.timbres[patchnum + 128].timbre.partial[3],0x3A) != 0x3A)
- break;
- //Checksum
- unsigned char *dat = (unsigned char *)&mt32ram.params.timbres[patchnum + 128].timbre;
- unsigned char checksum = calcSysexChecksum(dat, 246, msb + isb + lsb);
+bool Synth::dumpTimbre(File *file, const TimbreParam *timbre, Bit32u address) {
+ // Sysex header
+ if (!file->writeBit8u(0xF0))
+ return false;
+ if (!file->writeBit8u(0x41))
+ return false;
+ if (!file->writeBit8u(0x10))
+ return false;
+ if (!file->writeBit8u(0x16))
+ return false;
+ if (!file->writeBit8u(0x12))
+ return false;
- if (!file->writeBit8u(checksum))
- break;
+ char lsb = (char)(address & 0x7f);
+ char isb = (char)((address >> 7) & 0x7f);
+ char msb = (char)(((address >> 14) & 0x7f) | 0x08);
+
+ //Address
+ if (!file->writeBit8u(msb))
+ return false;
+ if (!file->writeBit8u(isb))
+ return false;
+ if (!file->writeBit8u(lsb))
+ return false;
+
+ //Data
+ if (file->write(timbre, 246) != 246)
+ return false;
+
+ //Checksum
+ unsigned char checksum = calcSysexChecksum((unsigned char *)timbre, 246, msb + isb + lsb);
+ if (!file->writeBit8u(checksum))
+ return false;
+
+ //End of sysex
+ if (!file->writeBit8u(0xF7))
+ return false;
+ return true;
+}
+
+int Synth::dumpTimbres(const char *filename, int start, int len) {
+ File *file = openFile(filename, File::OpenMode_write);
+ if (file == NULL)
+ return -1;
- //End of sysex
- if (!file->writeBit8u(0xF7))
+ for (int timbreNum = start; timbreNum < start + len; timbreNum++) {
+ int useaddr = (timbreNum - start) * 256;
+ TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
+ if (!dumpTimbre(file, timbre, useaddr))
break;
}
closeFile(file);
- printDebug("Wrote temp patches to %s", filename);
return 0;
}
@@ -1231,14 +1005,14 @@ void Synth::render(Bit16s *stream, Bit32u len) {
void Synth::doRender(Bit16s * stream,Bit32u len) {
Bit32u m;
- partialManager->AgeAll();
+ partialManager->ageAll();
if (myProp.useReverb) {
bool hasOutput = false;
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);
+ if (partialManager->produceOutput(i, &tmpBuffer[0], len)) {
+ ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
hasOutput = true;
}
}
@@ -1263,19 +1037,19 @@ void Synth::doRender(Bit16s * stream,Bit32u len) {
}
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);
+ if (partialManager->produceOutput(i, &tmpBuffer[0], len)) {
+ ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
}
}
}
} else {
for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
- if (partialManager->ProduceOutput(i, &tmpBuffer[0], len))
- ProduceOutput1(&tmpBuffer[0], stream, len, mastervolume);
+ if (partialManager->produceOutput(i, &tmpBuffer[0], len))
+ ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
}
}
- partialManager->ClearAlreadyOutputed();
+ partialManager->clearAlreadyOutputed();
#if MT32EMU_MONITOR_PARTIALS == 1
samplepos += len;
diff --git a/backends/midi/mt32/synth.h b/backends/midi/mt32/synth.h
index dbd77c1dfd..c93ff01803 100644
--- a/backends/midi/mt32/synth.h
+++ b/backends/midi/mt32/synth.h
@@ -39,27 +39,27 @@ class PartialManager;
class Part;
enum ReportType {
- // Files missing
- ReportType_errorPreset1 = 1,
- ReportType_errorPreset2 = 2,
- ReportType_errorDrumpat = 3,
- ReportType_errorPatchlog = 4,
- ReportType_errorMT32ROM = 5,
- ReportType_errorSampleRate = 6,
+ // Errors
+ ReportType_errorControlROM = 1,
+ ReportType_errorPCMROM,
+ ReportType_errorSampleRate,
+
+ // Progress
+ ReportType_progressInit,
// HW spec
- ReportType_availableSSE = 7,
- ReportType_available3DNow = 8,
- ReportType_usingSSE = 9,
- ReportType_using3DNow = 10,
+ ReportType_availableSSE,
+ ReportType_available3DNow,
+ ReportType_usingSSE,
+ ReportType_using3DNow,
// General info
- ReportType_lcdMessage = 11,
- ReportType_devReset = 12,
- ReportType_devReconfig = 13,
- ReportType_newReverbMode = 14,
- ReportType_newReverbTime = 15,
- ReportType_newReverbLevel = 16
+ ReportType_lcdMessage,
+ ReportType_devReset,
+ ReportType_devReconfig,
+ ReportType_newReverbMode,
+ ReportType_newReverbTime,
+ ReportType_newReverbLevel
};
struct SynthProperties {
@@ -84,7 +84,7 @@ struct SynthProperties {
// This is used as the first argument to all callbacks
void *userData;
// Callback for reporting various errors and information. May be NULL
- void (*report)(void *userData, ReportType type, const void *reportData);
+ int (*report)(void *userData, ReportType type, const void *reportData);
// Callback for debug messages, in vprintf() format
void (*printDebug)(void *userData, const char *fmt, va_list list);
// Callback for providing an implementation of File, opened and ready for use
@@ -105,19 +105,20 @@ typedef void (*recalcStatusCallback)(int percDone);
bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);
typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
-extern iir_filter_type usefilter;
class Synth {
friend class Part;
+friend class RhythmPart;
friend class Partial;
friend class TableInitialiser;
private:
bool isEnabled;
- PCMWave PCM[54];
+ iir_filter_type iirFilter;
+
PCMWaveEntry PCMList[128];
- Bit32s PCMLoopTable[54];
+ Bit8u controlROMData[64 * 1024];
Bit16s romfile[PCMSIZE + GRAN];
Bit8s chantable[32];
@@ -125,15 +126,12 @@ private:
static Bit32s samplepos = 0;
#endif
- MT32RAMFormat mt32ram, mt32default;
+ MemParams mt32ram, mt32default;
revmodel *reverbModel;
- Bit16s mastervolume;
-
- char curRevMode;
- char curRevTime;
- Bit32u curRevLevel;
+ float masterTune;
+ Bit16u masterVolume;
unsigned char initmode;
bool isOpen;
@@ -150,25 +148,30 @@ private:
SynthProperties myProp;
bool loadPreset(const char *filename);
- void initReverb(char newRevMode, char newRevTime);
+ void initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel);
void doRender(Bit16s * stream, Bit32u len);
void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
- void playSysexWithoutHeader(unsigned char channel, Bit8u *sysex, Bit32u len);
-
- bool loadDrums(const char *filename);
- bool loadPCMToROMMap(const char *filename);
- bool loadROM(const char *filename);
- void dumpDrums(const char *filename);
- // Save the system state to a sysex file specified by filename
- int dumpSysex(char *filename);
-
+ void playSysexWithoutHeader(unsigned char channel, const Bit8u *sysex, Bit32u len);
+
+ bool loadControlROM(const char *filename);
+ bool loadPCMROM(const char *filename);
+ bool dumpTimbre(File *file, const TimbreParam *timbre, Bit32u addr);
+ int dumpTimbres(const char *filename, int start, int len);
+
+ void initPCMList();
+ void initRhythmTimbres();
+ void initTimbres(Bit16u mapAddress, int startTimbre);
+ void initRhythmTimbre(int drumNum, const Bit8u *mem);
+ bool refreshSystem();
protected:
- void report(ReportType type, const void *reportData);
+ int report(ReportType type, const void *reportData);
File *openFile(const char *filename, File::OpenMode mode);
void closeFile(File *file);
void printDebug(const char *fmt, ...);
public:
+ static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);
+
Synth();
~Synth();
@@ -182,11 +185,10 @@ public:
// Sends a 4-byte MIDI message to the MT-32 for immediate playback
void playMsg(Bit32u msg);
- static Bit8u calcSysexChecksum(Bit8u *data, Bit32u len, Bit8u checksum);
// Sends a string of Sysex commands to the MT-32 for immediate interpretation
// The length is in bytes
- void playSysex(Bit8u *sysex, Bit32u len);
- void playSysexWithoutFraming(Bit8u *sysex, Bit32u len);
+ void playSysex(const Bit8u *sysex, Bit32u len);
+ void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len);
// This callback routine is used to have the MT-32 generate samples to the specified
// output stream. The length is in whole samples, not bytes. (I.E. in 16-bit stereo,
diff --git a/backends/midi/mt32/tables.cpp b/backends/midi/mt32/tables.cpp
index 2f710e8e0e..3d0ab5ea45 100644
--- a/backends/midi/mt32/tables.cpp
+++ b/backends/midi/mt32/tables.cpp
@@ -25,36 +25,14 @@
#include "mt32emu.h"
+#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x))
+
namespace MT32Emu {
//Amplitude time velocity follow exponential coefficients
const double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637};
const double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539};
-const Bit8s LoopPatterns[9][10] = {
- { 2, 3, 4, 5, 6, 7, -1, -1, -1, -1},
- { 8, 9, 10, 11, 12, 13, 14, 15, 16, -1},
- {17, 18, 19, 20, 21, -1, -1, -1, -1, -1},
- {22, 23, 24, 25, 26, 27, 28, 29, -1, -1},
- {30, 31, 32, 33, 34, 35, 36, 37, -1, -1},
- {45, 46, 47, 48, 49, 50, 51, 52, 53, -1},
- {15, 11, 12, 13, 14, 15, 16, -1, -1, -1},
- {30, 35, 32, 33, 34, -1, -1, -1, -1, -1},
- { 2, 3, -1, -1, -1, -1, -1, -1, -1, -1},
-};
-
-static const Bit32s LoopPatternTuning[9][10] = {
- {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1, -1, -1},
- {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1},
- {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1, -1, -1, -1},
- {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1},
- {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1},
- {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1},
- {0x2590B, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1, -1},
- {0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, -1, -1, -1, -1, -1},
- {0x1294A, 0x1294A, -1, -1, -1, -1, -1, -1, -1, -1},
-};
-
// These are division constants for the TVF depth key follow
static const Bit32u depexp[5] = {3000, 950, 485, 255, 138};
@@ -63,6 +41,7 @@ static const double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143,
static const double tkcatmult[5] = {1.0, 1.058245688, 1.048488989, 1.016049301, 1.097538067};
static float initialisedSampleRate = 0.0f;
+static float initialisedMasterTune = 0.0f;
Bit16s smallnoise[MAX_SAMPLE_OUTPUT];
@@ -215,7 +194,7 @@ static void initEnvelopes(float samplerate) {
//lasttimetable[lf] = (int)((exp(logtime)/(312.12*6)) * (float)samplerate);
float mv = (float)lf / 100.0f;
- float pt = mv-0.5f;
+ float pt = mv - 0.5f;
if (pt < 0)
pt = 0;
@@ -319,7 +298,7 @@ void TableInitialiser::initMT32ConstantTables(Synth *synth) {
for (lf = 0; lf < 128; lf++) {
// Converts MIDI velocity to volume.
- voltable[lf] = (int)(127.0f * powf((float)lf / 127.0f, FLOAT_LN));
+ voltable[lf] = FIXEDPOINT_MAKE(powf((float)lf / 127.0f, FLOAT_LN), 7);
}
for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) {
int myRand;
@@ -523,7 +502,7 @@ File *TableInitialiser::initWave(Synth *synth, NoteLookup *noteLookup, float amp
#if 0
//FIXME:KG: Credit Timo Strunk (bastardo on #scummvm) for help with this!
- float saw = 0.5f * FLOAT_PI - sa / 2;
+ double saw = 0.5 * DOUBLE_PI - sa / 2;
#else
double saw = 0.0;
for (int sinus = 1; sinus < div; sinus++) {
@@ -581,12 +560,15 @@ static void initNFiltTable(NoteLookup *noteLookup, float freq, float rate) {
}
}
-File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWave pcmWaves[54], File *file) {
+File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry pcmWaves[128], File *file) {
float ampsize = WGAMP;
- float freq = (float)(tuning * pow(2.0, ((double)note - MIDDLEA) / 12.0));
+ float freq = (float)(masterTune * pow(2.0, ((double)note - MIDDLEA) / 12.0));
float div = rate / freq;
noteLookup->div = (int)div;
+ if (noteLookup->div == 0)
+ noteLookup->div = 1;
+
initSaw(noteLookup, noteLookup->div);
initDep(noteLookup, note);
@@ -595,18 +577,10 @@ File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float not
// Create the pitch tables
- float rateMult = 32000.0f / rate;
- float tuner = rateMult * freq * 65536.0f;
- for (int pc = 0; pc < 54; pc++) {
- noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune);
- }
- for (int lp = 0; lp < 9; lp++) {
- for (int ln = 0; ln < 10; ln++) {
- // FIXME:KG: Surely this needs to be adjusted for the rate?
- // If not, remove rateMult * from below
- // (Note: I'm assuming the LoopPatternTuning constants were intended for 32k rate)
- noteLookup->loopTable[lp][ln] = (int)(rateMult * (float)LoopPatternTuning[lp][ln] * (freq / 220.0f));
- }
+ double rateMult = 32000.0 / rate;
+ double tuner = freq * 65536.0f;
+ for (int pc = 0; pc < 128; pc++) {
+ noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune * rateMult);
}
initFiltTable(noteLookup, freq, rate);
@@ -614,11 +588,14 @@ File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float not
return file;
}
-void TableInitialiser::initNotes(Synth *synth, PCMWave pcmWaves[54], float rate, float tuning) {
+bool TableInitialiser::initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], float rate, float masterTune) {
+ const char *NoteNames[12] = {
+ "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "
+ };
char filename[64];
int intRate = (int)rate;
- char version[4] = {0, 0, 0, 1};
- sprintf(filename, "waveformcache-%d-%.1f.raw", intRate, tuning);
+ char version[4] = {0, 0, 0, 2};
+ sprintf(filename, "waveformcache-%d-%.2f.raw", intRate, masterTune);
File *file = NULL;
char header[20];
@@ -631,11 +608,11 @@ void TableInitialiser::initNotes(Synth *synth, PCMWave pcmWaves[54], float rate,
header[pos++] = (char)((intRate >> 16) & 0xFF);
header[pos++] = (char)((intRate >> 8) & 0xFF);
header[pos++] = (char)(intRate & 0xFF);
- int intTuning = (int)tuning;
+ int intTuning = (int)masterTune;
header[pos++] = (char)((intTuning >> 8) & 0xFF);
header[pos++] = (char)(intTuning & 0xFF);
header[pos++] = 0;
- header[pos] = (char)((tuning - intTuning) * 10);
+ header[pos] = (char)((masterTune - intTuning) * 10);
#if MT32EMU_WAVECACHEMODE < 2
bool reading = false;
file = synth->openFile(filename, File::OpenMode_read);
@@ -648,33 +625,37 @@ void TableInitialiser::initNotes(Synth *synth, PCMWave pcmWaves[54], float rate,
if (endianCheck == 1) {
reading = true;
} else {
- synth->printDebug("Endian check in %s does not match expected - will generate", filename);
+ synth->printDebug("Endian check in %s does not match expected", filename);
}
} else {
- synth->printDebug("Unable to read endian check in %s - will generate", filename);
+ synth->printDebug("Unable to read endian check in %s", filename);
}
} else {
- synth->printDebug("Header of %s does not match expected - will generate", filename);
+ synth->printDebug("Header of %s does not match expected", filename);
}
} else {
- synth->printDebug("Error reading 16 bytes of %s - will generate", filename);
+ synth->printDebug("Error reading 16 bytes of %s", filename);
}
if (!reading) {
file->close();
file = NULL;
}
} else {
- synth->printDebug("Unable to open %s for reading - will generate", filename);
+ synth->printDebug("Unable to open %s for reading", filename);
}
#endif
- //FIXME:KG: may only need to do 12 to 108
- //12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow
- //and adjustment for timbre pitch, so the results can be outside that range. Do move it (by octave) into
- // the 12..108 range, or keep it in 0..127 range, or something else altogether?
- for (int f = 12; f < 109; f++) {
- NoteLookup *noteLookup = &noteLookups[f];
- file = initNote(synth, noteLookup, (float)f, rate, tuning, pcmWaves, file);
+ float progress = 0.0f;
+ bool abort = false;
+ synth->report(ReportType_progressInit, &progress);
+ for (int f = LOWEST_NOTE; f <= HIGHEST_NOTE; f++) {
+ synth->printDebug("Initialising note %s%d", NoteNames[f % 12], (f / 12) - 1);
+ NoteLookup *noteLookup = &noteLookups[f - LOWEST_NOTE];
+ file = initNote(synth, noteLookup, (float)f, rate, masterTune, pcmWaves, file);
+ progress = (f - LOWEST_NOTE + 1) / (float)NUM_NOTES;
+ abort = synth->report(ReportType_progressInit, &progress) != 0;
+ if (abort)
+ break;
}
#if MT32EMU_WAVECACHEMODE == 0 || MT32EMU_WAVECACHEMODE == 2
@@ -682,7 +663,7 @@ void TableInitialiser::initNotes(Synth *synth, PCMWave pcmWaves[54], float rate,
file = synth->openFile(filename, File::OpenMode_write);
if (file != NULL) {
if (file->write(header, 16) == 16 && file->writeBit16u(1)) {
- for (int f = 12; f < 109 && file != NULL; f++) {
+ for (int f = 0; f < NUM_NOTES; f++) {
for (int i = 0; i < 3 && file != NULL; i++) {
int len = noteLookups[f].waveformSize[i];
for (int j = 0; j < len; j++) {
@@ -706,21 +687,42 @@ void TableInitialiser::initNotes(Synth *synth, PCMWave pcmWaves[54], float rate,
if (file != NULL)
synth->closeFile(file);
+ return !abort;
+}
+
+void TableInitialiser::freeNotes() {
+ for (int t = 0; t < 3; t++) {
+ for (int m = 0; m < NUM_NOTES; m++) {
+ if (noteLookups[m].waveforms[t] != NULL) {
+ delete[] noteLookups[m].waveforms[t];
+ noteLookups[m].waveforms[t] = NULL;
+ noteLookups[m].waveformSize[t] = 0;
+ }
+ }
+ }
+ initialisedMasterTune = 0.0f;
}
-bool TableInitialiser::initMT32Tables(Synth *synth, PCMWave pcms[54], float sampleRate) {
+bool TableInitialiser::initMT32Tables(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune) {
if (sampleRate <= 0.0f) {
synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate);
return false;
}
if (initialisedSampleRate == 0.0f) {
initMT32ConstantTables(synth);
+ }
+ if (initialisedSampleRate != sampleRate) {
initFiltCoeff(sampleRate);
initEnvelopes(sampleRate);
+ }
+ if (initialisedSampleRate != sampleRate || initialisedMasterTune != masterTune) {
+ freeNotes();
+ if (!initNotes(synth, pcmWaves, sampleRate, masterTune)) {
+ return false;
+ }
initialisedSampleRate = sampleRate;
+ initialisedMasterTune = masterTune;
}
- // This always needs to be done, to allocate the waveforms
- initNotes(synth, pcms, sampleRate, TUNING);
return true;
}
diff --git a/backends/midi/mt32/tables.h b/backends/midi/mt32/tables.h
index 46be3f1548..0c85796f4f 100644
--- a/backends/midi/mt32/tables.h
+++ b/backends/midi/mt32/tables.h
@@ -36,21 +36,19 @@ const int FILTERGRAN = 512;
const int MIDDLEC = 60;
const int MIDDLEA = 69; // By this I mean "A above middle C"
-// 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;
-
-const int NUM_NOTES = 128; // Number of slots for note LUT (we actually only use 12..108)
+//FIXME:KG: may only need to do 12 to 108
+//12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow
+//and adjustment for timbre pitch, so the results can be outside that range. Do move it (by octave) into
+// the 12..108 range, or keep it in 0..127 range, or something else altogether?
+const int LOWEST_NOTE = 12;
+const int HIGHEST_NOTE = 127;
+const int NUM_NOTES = HIGHEST_NOTE - LOWEST_NOTE + 1; // Number of slots for note LUT
// 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
@@ -75,9 +73,8 @@ extern Bit32s voltable[128];
extern float ResonInv[31];
struct NoteLookup {
- Bit32s div;
- Bit32u wavTable[54];
- Bit32u loopTable[9][10];
+ Bit32u div;
+ Bit32u wavTable[128];
Bit32s sawTable[101];
Bit32s fildepTable[5];
Bit32s timekeyTable[5];
@@ -92,10 +89,11 @@ extern NoteLookup noteLookups[NUM_NOTES];
class TableInitialiser {
static void initMT32ConstantTables(Synth *synth);
static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file);
- static void initNotes(Synth *synth, PCMWave pcms[54], float rate, float tuning);
+ static bool initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], float rate, float tuning);
public:
- static bool initMT32Tables(Synth *synth, PCMWave pcms[54], float sampleRate);
- static File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWave pcmWaves[54], File *file);
+ static bool initMT32Tables(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune);
+ static File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWaveEntry pcmWaves[128], File *file);
+ static void freeNotes();
};
}