diff options
-rw-r--r-- | audio/softsynth/mt32.cpp | 201 | ||||
-rw-r--r-- | audio/softsynth/mt32/AReverbModel.cpp | 16 | ||||
-rw-r--r-- | audio/softsynth/mt32/AReverbModel.h | 2 | ||||
-rw-r--r-- | audio/softsynth/mt32/BReverbModel.cpp | 4 | ||||
-rw-r--r-- | audio/softsynth/mt32/BReverbModel.h | 2 | ||||
-rw-r--r-- | audio/softsynth/mt32/DelayReverb.cpp | 34 | ||||
-rw-r--r-- | audio/softsynth/mt32/DelayReverb.h | 5 | ||||
-rw-r--r-- | audio/softsynth/mt32/FreeverbModel.cpp | 4 | ||||
-rw-r--r-- | audio/softsynth/mt32/FreeverbModel.h | 2 | ||||
-rw-r--r-- | audio/softsynth/mt32/Part.cpp | 7 | ||||
-rw-r--r-- | audio/softsynth/mt32/Partial.cpp | 9 | ||||
-rw-r--r-- | audio/softsynth/mt32/ROMInfo.cpp | 112 | ||||
-rw-r--r-- | audio/softsynth/mt32/ROMInfo.h | 77 | ||||
-rw-r--r-- | audio/softsynth/mt32/Synth.cpp | 209 | ||||
-rw-r--r-- | audio/softsynth/mt32/Synth.h | 122 | ||||
-rw-r--r-- | audio/softsynth/mt32/TVA.cpp | 6 | ||||
-rw-r--r-- | audio/softsynth/mt32/TVP.cpp | 5 | ||||
-rw-r--r-- | audio/softsynth/mt32/Tables.h | 4 | ||||
-rw-r--r-- | audio/softsynth/mt32/module.mk | 1 | ||||
-rw-r--r-- | audio/softsynth/mt32/mt32emu.h | 3 |
20 files changed, 462 insertions, 363 deletions
diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp index 186118262f..3e87963113 100644 --- a/audio/softsynth/mt32.cpp +++ b/audio/softsynth/mt32.cpp @@ -25,6 +25,7 @@ #ifdef USE_MT32EMU #include "audio/softsynth/mt32/mt32emu.h" +#include "audio/softsynth/mt32/ROMInfo.h" #include "audio/softsynth/emumidi.h" #include "audio/musicplugin.h" @@ -47,6 +48,57 @@ #include "graphics/palette.h" #include "graphics/font.h" +#include "gui/message.h" + +static void drawMessage(int offset, const Common::String &text); + +namespace MT32Emu { + +class ReportHandlerScummVM : public ReportHandler { +friend class Synth; + +public: + virtual ~ReportHandlerScummVM() {} + +protected: + + // Callback for debug messages, in vprintf() format + void printDebug(const char *fmt, va_list list) { + char buf[512]; + + vsnprintf(buf, 512, fmt, list); + buf[70] = 0; // Truncate to a reasonable length + + drawMessage(1, buf); + } + + // Callbacks for reporting various errors and information + void onErrorControlROM() { + GUI::MessageDialog dialog("MT32emu: Init Error - Missing or invalid Control ROM image", "OK"); + dialog.runModal(); + error("MT32emu: Init Error - Missing or invalid Control ROM image"); + } + void onErrorPCMROM() { + GUI::MessageDialog dialog("MT32emu: Init Error - Missing PCM ROM image", "OK"); + dialog.runModal(); + error("MT32emu: Init Error - Missing PCM ROM image"); + } + void showLCDMessage(const char *message) { + g_system->displayMessageOnOSD(message); + } + void onDeviceReset() {} + void onDeviceReconfig() {} + void onNewReverbMode(Bit8u /* mode */) {} + void onNewReverbTime(Bit8u /* time */) {} + void onNewReverbLevel(Bit8u /* level */) {} + void onPartStateChanged(int /* partNum */, bool /* isActive */) {} + void onPolyStateChanged(int /* partNum */) {} + void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {} + void onProgramChanged(int /* partNum */, char * /* patchName */) {} +}; + +} // end of namespace MT32Emu + class MidiChannel_MT32 : public MidiChannel_MPU401 { void effectLevel(byte value) { } void chorusLevel(byte value) { } @@ -57,6 +109,9 @@ private: MidiChannel_MT32 _midiChannels[16]; uint16 _channelMask; MT32Emu::Synth *_synth; + MT32Emu::ReportHandlerScummVM *_reportHandler; + const MT32Emu::ROMImage *_controlROM; + const MT32Emu::ROMImage *_pcmROM; int _outputRate; @@ -84,59 +139,6 @@ public: int getRate() const { return _outputRate; } }; -static int eatSystemEvents() { - Common::Event event; - Common::EventManager *eventMan = g_system->getEventManager(); - while (eventMan->pollEvent(event)) { - switch (event.type) { - case Common::EVENT_QUIT: - return 1; - default: - break; - } - } - return 0; -} - -static void drawProgress(float progress) { - const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kGUIFont)); - Graphics::Surface *screen = g_system->lockScreen(); - - assert(screen); - assert(screen->pixels); - - Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); - - int16 w = g_system->getWidth() / 7 * 5; - int16 h = font.getFontHeight(); - int16 x = g_system->getWidth() / 7; - int16 y = g_system->getHeight() / 2 - h / 2; - - Common::Rect r(x, y, x + w, y + h); - - uint32 col; - - if (screenFormat.bytesPerPixel > 1) - col = screenFormat.RGBToColor(0, 171, 0); - else - col = 1; - - screen->frameRect(r, col); - - r.grow(-1); - r.setWidth(uint16(progress * w)); - - if (screenFormat.bytesPerPixel > 1) - col = screenFormat.RGBToColor(171, 0, 0); - else - col = 2; - - screen->fillRect(r, col); - - g_system->unlockScreen(); - g_system->updateScreen(); -} - static void drawMessage(int offset, const Common::String &text) { const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kGUIFont)); Graphics::Surface *screen = g_system->lockScreen(); @@ -170,63 +172,6 @@ static void drawMessage(int offset, const Common::String &text) { g_system->updateScreen(); } -static Common::File *MT32_OpenFile(void *userData, const char *filename) { - Common::File *file = new Common::File(); - if (!file->open(filename)) { - delete file; - return NULL; - } - return file; -} - -static void MT32_PrintDebug(void *userData, const char *fmt, va_list list) { - if (((MidiDriver_MT32 *)userData)->_initializing) { - char buf[512]; - - vsnprintf(buf, 512, fmt, list); - buf[70] = 0; // Truncate to a reasonable length - - drawMessage(1, buf); - } - - //vdebug(0, fmt, list); // FIXME: Use a higher debug level -} - -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_errorControlROM: - error("Failed to load MT32_CONTROL.ROM"); - break; - case MT32Emu::ReportType_errorPCMROM: - error("Failed to load MT32_PCM.ROM"); - break; - case MT32Emu::ReportType_progressInit: - if (((MidiDriver_MT32 *)userData)->_initializing) { - drawProgress(*((const float *)reportData)); - return eatSystemEvents(); - } - break; - case MT32Emu::ReportType_availableSSE: - debug(1, "MT32emu: SSE is available"); - break; - case MT32Emu::ReportType_usingSSE: - debug(1, "MT32emu: using SSE"); - break; - case MT32Emu::ReportType_available3DNow: - debug(1, "MT32emu: 3DNow! is available"); - break; - case MT32Emu::ReportType_using3DNow: - debug(1, "MT32emu: using 3DNow!"); - break; - default: - break; - } - return 0; -} - //////////////////////////////////////// // // MidiDriver_MT32 @@ -239,6 +184,7 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) { _midiChannels[i].init(this, i); } + _reportHandler = NULL; _synth = NULL; // A higher baseFreq reduces the length used in generateSamples(), // and means that the timer callback will be called more often. @@ -253,29 +199,18 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe MidiDriver_MT32::~MidiDriver_MT32() { delete _synth; + delete _reportHandler; + MT32Emu::ROMImage::freeROMImage(_controlROM); + MT32Emu::ROMImage::freeROMImage(_pcmROM); } int MidiDriver_MT32::open() { - MT32Emu::SynthProperties prop; - if (_isOpen) return MERR_ALREADY_OPEN; MidiDriver_Emulated::open(); - - memset(&prop, 0, sizeof(prop)); - prop.sampleRate = getRate(); - prop.useReverb = true; - prop.useDefaultReverb = false; - 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(); + _reportHandler = new MT32Emu::ReportHandlerScummVM(); + _synth = new MT32Emu::Synth(_reportHandler); Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); @@ -291,7 +226,15 @@ int MidiDriver_MT32::open() { _initializing = true; drawMessage(-1, _s("Initializing MT-32 Emulator")); - if (!_synth->open(prop)) + Common::File *controlFile = new Common::File(); + if (!controlFile->open("MT32_CONTROL.ROM")) + error("Error opening MT32_CONTROL.ROM"); + Common::File *pcmFile = new Common::File(); + if (!pcmFile->open("MT32_PCM.ROM")) + error("Error opening MT32_PCM.ROM"); + _controlROM = MT32Emu::ROMImage::makeROMImage(controlFile); + _pcmROM = MT32Emu::ROMImage::makeROMImage(pcmFile); + if (!_synth->open(*_controlROM, *_pcmROM)) return MERR_DEVICE_NOT_AVAILABLE; double gain = (double)ConfMan.getInt("midi_gain") / 100.0; @@ -354,6 +297,10 @@ void MidiDriver_MT32::close() { _synth->close(); delete _synth; _synth = NULL; + delete _reportHandler; + _reportHandler = NULL; + MT32Emu::ROMImage::freeROMImage(_controlROM); + MT32Emu::ROMImage::freeROMImage(_pcmROM); } void MidiDriver_MT32::generateSamples(int16 *data, int len) { diff --git a/audio/softsynth/mt32/AReverbModel.cpp b/audio/softsynth/mt32/AReverbModel.cpp index ec24394e71..595b286a31 100644 --- a/audio/softsynth/mt32/AReverbModel.cpp +++ b/audio/softsynth/mt32/AReverbModel.cpp @@ -48,7 +48,7 @@ static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; -static const Bit32u MODE_0_LEVELS[] = {0, 10*3, 10*5, 10*7, 11*9, 11*12, 11*15, 13*15}; +static const Bit32u MODE_0_LEVELS[] = {10*1, 10*3, 10*5, 10*7, 11*9, 11*12, 11*15, 13*15}; static const Bit32u MODE_0_LPF_AMP = 6; static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; @@ -60,7 +60,7 @@ static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; -static const Bit32u MODE_1_LEVELS[] = {0, 10*3, 11*5, 11*7, 11*9, 11*12, 11*15, 14*15}; +static const Bit32u MODE_1_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 11*15, 14*15}; static const Bit32u MODE_1_LPF_AMP = 6; static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; @@ -72,7 +72,7 @@ static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; -static const Bit32u MODE_2_LEVELS[] = {0, 10*3, 11*5, 11*7, 11*9, 11*12, 12*15, 14*15}; +static const Bit32u MODE_2_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 12*15, 14*15}; static const Bit32u MODE_2_LPF_AMP = 8; static const AReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_LEVELS, MODE_0_LPF_AMP}; @@ -165,9 +165,7 @@ AReverbModel::~AReverbModel() { close(); } -void AReverbModel::open(unsigned int /*sampleRate*/) { - // FIXME: filter sizes must be multiplied by sample rate to 32000Hz ratio - // IIR filter values depend on sample rate as well +void AReverbModel::open() { allpasses = new AllpassFilter*[NUM_ALLPASSES]; for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]); @@ -218,10 +216,12 @@ void AReverbModel::setParameters(Bit8u time, Bit8u level) { // FIXME: wetLevel definitely needs ramping when changed // Although, most games don't set reverb level during MIDI playback if (combs == NULL) return; + level &= 7; + time &= 7; for (Bit32u i = 0; i < NUM_COMBS; i++) { - combs[i]->setFeedbackFactor(currentSettings.decayTimes[(i << 3) + (time & 7)] / 256.0f); + combs[i]->setFeedbackFactor(currentSettings.decayTimes[(i << 3) + time] / 256.0f); } - wetLevel = 0.5f * lpfAmp * currentSettings.wetLevels[(level & 7)] / 256.0f; + wetLevel = (level == 0 && time == 0) ? 0.0f : 0.5f * lpfAmp * currentSettings.wetLevels[level] / 256.0f; } bool AReverbModel::isActive() const { diff --git a/audio/softsynth/mt32/AReverbModel.h b/audio/softsynth/mt32/AReverbModel.h index d70e9ee14f..be1ca4916b 100644 --- a/audio/softsynth/mt32/AReverbModel.h +++ b/audio/softsynth/mt32/AReverbModel.h @@ -75,7 +75,7 @@ class AReverbModel : public ReverbModel { public: AReverbModel(const ReverbMode mode); ~AReverbModel(); - void open(unsigned int sampleRate); + void open(); void close(); void setParameters(Bit8u time, Bit8u level); void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp index b5f4467c20..2570424187 100644 --- a/audio/softsynth/mt32/BReverbModel.cpp +++ b/audio/softsynth/mt32/BReverbModel.cpp @@ -241,9 +241,7 @@ BReverbModel::~BReverbModel() { close(); } -void BReverbModel::open(unsigned int /*sampleRate*/) { - // FIXME: filter sizes must be multiplied by sample rate to 32000Hz ratio - // IIR filter values depend on sample rate as well +void BReverbModel::open() { if (currentSettings.numberOfAllpasses > 0) { allpasses = new AllpassFilter*[currentSettings.numberOfAllpasses]; for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h index 7096ae04ae..f728d1a99c 100644 --- a/audio/softsynth/mt32/BReverbModel.h +++ b/audio/softsynth/mt32/BReverbModel.h @@ -100,7 +100,7 @@ class BReverbModel : public ReverbModel { public: BReverbModel(const ReverbMode mode); ~BReverbModel(); - void open(unsigned int sampleRate); + void open(); void close(); void setParameters(Bit8u time, Bit8u level); void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); diff --git a/audio/softsynth/mt32/DelayReverb.cpp b/audio/softsynth/mt32/DelayReverb.cpp index 77521b4a01..bf925f8419 100644 --- a/audio/softsynth/mt32/DelayReverb.cpp +++ b/audio/softsynth/mt32/DelayReverb.cpp @@ -45,9 +45,10 @@ static const Bit32u REVERB_FEEDBACK67 = 0x60; static const Bit32u REVERB_FEEDBACK = 0x68; static const float LPF_VALUE = 0x68 / 256.0f; +static const Bit32u BUFFER_SIZE = 16384; + DelayReverb::DelayReverb() { buf = NULL; - sampleRate = 0; setParameters(0, 0); } @@ -55,27 +56,22 @@ DelayReverb::~DelayReverb() { delete[] buf; } -void DelayReverb::open(unsigned int newSampleRate) { - if (newSampleRate != sampleRate || buf == NULL) { - sampleRate = newSampleRate; - +void DelayReverb::open() { + if (buf == NULL) { delete[] buf; - // If we ever need a speedup, set bufSize to EXP2F(ceil(log2(bufSize))) and use & instead of % to find buf indexes - bufSize = 16384 * sampleRate / 32000; - buf = new float[bufSize]; + buf = new float[BUFFER_SIZE]; recalcParameters(); // mute buffer bufIx = 0; if (buf != NULL) { - for (unsigned int i = 0; i < bufSize; i++) { + for (unsigned int i = 0; i < BUFFER_SIZE; i++) { buf[i] = 0.0f; } } } - // FIXME: IIR filter value depends on sample rate as well } void DelayReverb::close() { @@ -92,11 +88,11 @@ void DelayReverb::setParameters(Bit8u newTime, Bit8u newLevel) { void DelayReverb::recalcParameters() { // Number of samples between impulse and eventual appearance on the left channel - delayLeft = REVERB_TIMINGS[time][0] * sampleRate / 32000; + delayLeft = REVERB_TIMINGS[time][0]; // Number of samples between impulse and eventual appearance on the right channel - delayRight = REVERB_TIMINGS[time][1] * sampleRate / 32000; + delayRight = REVERB_TIMINGS[time][1]; // Number of samples between a response and that response feeding back/echoing - delayFeedback = REVERB_TIMINGS[time][2] * sampleRate / 32000; + delayFeedback = REVERB_TIMINGS[time][2]; if (level < 3 || time < 6) { feedback = REVERB_FEEDBACK / 256.0f; @@ -113,10 +109,10 @@ void DelayReverb::process(const float *inLeft, const float *inRight, float *outL for (unsigned int sampleIx = 0; sampleIx < numSamples; sampleIx++) { // The ring buffer write index moves backwards; reads are all done with positive offsets. - Bit32u bufIxPrev = (bufIx + 1) % bufSize; - Bit32u bufIxLeft = (bufIx + delayLeft) % bufSize; - Bit32u bufIxRight = (bufIx + delayRight) % bufSize; - Bit32u bufIxFeedback = (bufIx + delayFeedback) % bufSize; + Bit32u bufIxPrev = (bufIx + 1) % BUFFER_SIZE; + Bit32u bufIxLeft = (bufIx + delayLeft) % BUFFER_SIZE; + Bit32u bufIxRight = (bufIx + delayRight) % BUFFER_SIZE; + Bit32u bufIxFeedback = (bufIx + delayFeedback) % BUFFER_SIZE; // Attenuated input samples and feedback response are directly added to the current ring buffer location float lpfIn = amp * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback]; @@ -127,7 +123,7 @@ void DelayReverb::process(const float *inLeft, const float *inRight, float *outL outLeft[sampleIx] = buf[bufIxLeft]; outRight[sampleIx] = buf[bufIxRight]; - bufIx = (bufSize + bufIx - 1) % bufSize; + bufIx = (BUFFER_SIZE + bufIx - 1) % BUFFER_SIZE; } } @@ -136,7 +132,7 @@ bool DelayReverb::isActive() const { float *b = buf; float max = 0.001f; - for (Bit32u i = 0; i < bufSize; i++) { + for (Bit32u i = 0; i < BUFFER_SIZE; i++) { if ((*b < -max) || (*b > max)) return true; b++; } diff --git a/audio/softsynth/mt32/DelayReverb.h b/audio/softsynth/mt32/DelayReverb.h index e967ddc49c..1abb49f128 100644 --- a/audio/softsynth/mt32/DelayReverb.h +++ b/audio/softsynth/mt32/DelayReverb.h @@ -25,10 +25,7 @@ private: Bit8u time; Bit8u level; - unsigned int sampleRate; - Bit32u bufSize; Bit32u bufIx; - float *buf; Bit32u delayLeft; @@ -43,7 +40,7 @@ private: public: DelayReverb(); ~DelayReverb(); - void open(unsigned int sampleRate); + void open(); void close(); void setParameters(Bit8u time, Bit8u level); void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); diff --git a/audio/softsynth/mt32/FreeverbModel.cpp b/audio/softsynth/mt32/FreeverbModel.cpp index d9bd17e62e..2ed302fddc 100644 --- a/audio/softsynth/mt32/FreeverbModel.cpp +++ b/audio/softsynth/mt32/FreeverbModel.cpp @@ -35,9 +35,7 @@ FreeverbModel::~FreeverbModel() { delete freeverb; } -void FreeverbModel::open(unsigned int /*sampleRate*/) { - // FIXME: scaleTuning must be multiplied by sample rate to 32000Hz ratio - // IIR filter values depend on sample rate as well +void FreeverbModel::open() { if (freeverb == NULL) { freeverb = new revmodel(scaleTuning); } diff --git a/audio/softsynth/mt32/FreeverbModel.h b/audio/softsynth/mt32/FreeverbModel.h index 925b2dbf96..bf1102b14a 100644 --- a/audio/softsynth/mt32/FreeverbModel.h +++ b/audio/softsynth/mt32/FreeverbModel.h @@ -32,7 +32,7 @@ class FreeverbModel : public ReverbModel { public: FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp); ~FreeverbModel(); - void open(unsigned int sampleRate); + void open(); void close(); void setParameters(Bit8u time, Bit8u level); void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp index 88d42dbbd4..852319be70 100644 --- a/audio/softsynth/mt32/Part.cpp +++ b/audio/softsynth/mt32/Part.cpp @@ -209,6 +209,7 @@ void RhythmPart::setTimbre(TimbreParam * /*timbre*/) { void Part::setTimbre(TimbreParam *timbre) { *timbreTemp = *timbre; + synth->newTimbreSet(partNum, timbre->common.name); } unsigned int RhythmPart::getAbsTimbreNum() const { @@ -537,6 +538,8 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt #if MT32EMU_MONITOR_PARTIALS > 1 synth->printPartialUsage(); #endif + synth->partStateChanged(partNum, true); + synth->polyStateChanged(partNum); } void Part::allNotesOff() { @@ -619,6 +622,10 @@ void Part::partialDeactivated(Poly *poly) { if (!poly->isActive()) { activePolys.remove(poly); freePolys.push_front(poly); + synth->polyStateChanged(partNum); + } + if (activePartialCount == 0) { + synth->partStateChanged(partNum, false); } } diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp index a6d164f218..58878a3513 100644 --- a/audio/softsynth/mt32/Partial.cpp +++ b/audio/softsynth/mt32/Partial.cpp @@ -85,6 +85,7 @@ void Partial::deactivate() { pair->pair = NULL; } } + synth->partialStateChanged(this, tva->getPhase(), TVA_PHASE_DEAD); #if MT32EMU_MONITOR_PARTIALS > 2 synth->printDebug("[+%lu] [Partial %d] Deactivated", sampleNum, debugPartialNum); synth->printPartialUsage(sampleNum); @@ -232,12 +233,12 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) #if MT32EMU_ACCURATE_WG == 1 float amp = EXP2F((32772 - ampRampVal / 2048) / -2048.0f); - float freq = EXP2F(pitch / 4096.0f - 16.0f) * 32000.0f; + float freq = EXP2F(pitch / 4096.0f - 16.0f) * SAMPLE_RATE; #else static const float ampFactor = EXP2F(32772 / -2048.0f); float amp = EXP2I(ampRampVal >> 10) * ampFactor; - static const float freqFactor = EXP2F(-16.0f) * 32000.0f; + static const float freqFactor = EXP2F(-16.0f) * SAMPLE_RATE; float freq = EXP2I(pitch) * freqFactor; #endif @@ -251,7 +252,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) break; } Bit32u pcmAddr = pcmWave->addr; - float positionDelta = freq * 2048.0f / synth->myProp.sampleRate; + float positionDelta = freq * 2048.0f / SAMPLE_RATE; // Linear interpolation float firstSample = synth->pcmROMData[pcmAddr + intPCMPosition]; @@ -296,7 +297,7 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) } // Wave length in samples - float waveLen = synth->myProp.sampleRate / freq; + float waveLen = SAMPLE_RATE / freq; // Init cosineLen float cosineLen = 0.5f * waveLen; diff --git a/audio/softsynth/mt32/ROMInfo.cpp b/audio/softsynth/mt32/ROMInfo.cpp new file mode 100644 index 0000000000..5735409655 --- /dev/null +++ b/audio/softsynth/mt32/ROMInfo.cpp @@ -0,0 +1,112 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +//#include <cstring> +#include "ROMInfo.h" + +namespace MT32Emu { + +// Known ROMs +static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL, NULL}; +static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo * const ROM_INFOS[] = { + &CTRL_MT32_V1_04, + &CTRL_MT32_V1_05, + &CTRL_MT32_V1_06, + &CTRL_MT32_V1_07, + &CTRL_MT32_BLUER, + &CTRL_CM32L_V1_00, + &CTRL_CM32L_V1_02, + &PCM_MT32, + &PCM_CM32L, + NULL}; + +const ROMInfo* ROMInfo::getROMInfo(Common::File *file) { + size_t fileSize = file->size(); + // We haven't added the SHA1 checksum code in ScummVM, as the file size + // suffices for our needs for now. + //const char *fileDigest = file->getSHA1(); + for (int i = 0; ROM_INFOS[i] != NULL; i++) { + const ROMInfo *romInfo = ROM_INFOS[i]; + if (fileSize == romInfo->fileSize /*&& !strcmp(fileDigest, romInfo->sha1Digest)*/) { + return romInfo; + } + } + return NULL; +} + +void ROMInfo::freeROMInfo(const ROMInfo *romInfo) { + (void) romInfo; +} + +static int getROMCount() { + int count; + for(count = 0; ROM_INFOS[count] != NULL; count++) { + } + return count; +} + +const ROMInfo** ROMInfo::getROMInfoList(unsigned int types, unsigned int pairTypes) { + const ROMInfo **romInfoList = new const ROMInfo*[getROMCount() + 1]; + const ROMInfo **currentROMInList = romInfoList; + for(int i = 0; ROM_INFOS[i] != NULL; i++) { + const ROMInfo *romInfo = ROM_INFOS[i]; + if ((types & (1 << romInfo->type)) && (pairTypes & (1 << romInfo->pairType))) { + *currentROMInList++ = romInfo; + } + } + *currentROMInList = NULL; + return romInfoList; +} + +void ROMInfo::freeROMInfoList(const ROMInfo **romInfoList) { + delete[] romInfoList; +} + +const ROMImage* ROMImage::makeROMImage(Common::File *file) { + ROMImage *romImage = new ROMImage; + romImage->file = file; + romImage->romInfo = ROMInfo::getROMInfo(romImage->file); + return romImage; +} + +void ROMImage::freeROMImage(const ROMImage *romImage) { + ROMInfo::freeROMInfo(romImage->romInfo); + delete (romImage->file); + delete romImage; +} + + +Common::File* ROMImage::getFile() const { + return file; +} + +const ROMInfo* ROMImage::getROMInfo() const { + return romInfo; +} + +} diff --git a/audio/softsynth/mt32/ROMInfo.h b/audio/softsynth/mt32/ROMInfo.h new file mode 100644 index 0000000000..2ffd2b72c6 --- /dev/null +++ b/audio/softsynth/mt32/ROMInfo.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MT32EMU_ROMINFO_H +#define MT32EMU_ROMINFO_H + +//#include <cstddef> +#include "common/file.h" + +namespace MT32Emu { + +// Defines vital info about ROM file to be used by synth and applications + +struct ROMInfo { +public: + size_t fileSize; + const char *sha1Digest; + enum Type {PCM, Control, Reverb} type; + const char *shortName; + const char *description; + enum PairType {Full, FirstHalf, SecondHalf, Mux0, Mux1} pairType; + ROMInfo *pairROMInfo; + void *controlROMInfo; + + // Returns a ROMInfo struct by inspecting the size and the SHA1 hash + static const ROMInfo* getROMInfo(Common::File *file); + + // Currently no-op + static void freeROMInfo(const ROMInfo *romInfo); + + // Allows retrieving a NULL-terminated list of ROMInfos for a range of types and pairTypes + // (specified by bitmasks) + // Useful for GUI/console app to output information on what ROMs it supports + static const ROMInfo** getROMInfoList(unsigned int types, unsigned int pairTypes); + + // Frees the list of ROMInfos given + static void freeROMInfoList(const ROMInfo **romInfos); +}; + +// Synth::open() is to require a full control ROMImage and a full PCM ROMImage to work + +class ROMImage { +private: + Common::File *file; + const ROMInfo *romInfo; + +public: + + // Creates a ROMImage object given a ROMInfo and a File. Keeps a reference + // to the File and ROMInfo given, which must be freed separately by the user + // after the ROMImage is freed + static const ROMImage* makeROMImage(Common::File *file); + + // Must only be done after all Synths using the ROMImage are deleted + static void freeROMImage(const ROMImage *romImage); + + Common::File *getFile() const; + const ROMInfo *getROMInfo() const; +}; + +} + +#endif diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp index 9b5518b207..7f4ba44999 100644 --- a/audio/softsynth/mt32/Synth.cpp +++ b/audio/softsynth/mt32/Synth.cpp @@ -142,11 +142,19 @@ Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) { return checksum; } -Synth::Synth() { +Synth::Synth(ReportHandler *useReportHandler) { isOpen = false; reverbEnabled = true; reverbOverridden = false; + if (useReportHandler == NULL) { + reportHandler = new ReportHandler; + isDefaultReportHandler = true; + } else { + reportHandler = useReportHandler; + isDefaultReportHandler = false; + } + #if MT32EMU_USE_REVERBMODEL == 1 reverbModels[REVERB_MODE_ROOM] = new AReverbModel(REVERB_MODE_ROOM); reverbModels[REVERB_MODE_HALL] = new AReverbModel(REVERB_MODE_HALL); @@ -178,31 +186,49 @@ Synth::~Synth() { for (int i = 0; i < 4; i++) { delete reverbModels[i]; } + if (isDefaultReportHandler) { + delete reportHandler; + } } -int Synth::report(ReportType type, const void *data) { - if (myProp.report != NULL) { - return myProp.report(myProp.userData, type, data); +void ReportHandler::showLCDMessage(const char *data) { + printf("WRITE-LCD: %s", data); + printf("\n"); +} + +void ReportHandler::printDebug(const char *fmt, va_list list) { + vprintf(fmt, list); + printf("\n"); +} + +void Synth::partStateChanged(int partNum, bool isPartActive) { + reportHandler->onPartStateChanged(partNum, isPartActive); +} + +void Synth::polyStateChanged(int partNum) { + reportHandler->onPolyStateChanged(partNum); +} + +void Synth::partialStateChanged(const Partial * const partial, int oldPartialPhase, int newPartialPhase) { + for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) { + if (getPartial(i) == partial) { + reportHandler->onPartialStateChanged(i, oldPartialPhase, newPartialPhase); + break; + } } - return 0; } -unsigned int Synth::getSampleRate() const { - return myProp.sampleRate; +void Synth::newTimbreSet(int partNum, char patchName[]) { + reportHandler->onProgramChanged(partNum, patchName); } void Synth::printDebug(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - if (myProp.printDebug != NULL) { - myProp.printDebug(myProp.userData, fmt, ap); - } else { #if MT32EMU_DEBUG_SAMPLESTAMPS > 0 - printf("[%u] ", renderedSampleCount); + reportHandler->printDebug("[%u] ", renderedSampleCount); #endif - vprintf(fmt, ap); - printf("\n"); - } + reportHandler->printDebug(fmt, ap); va_end(ap); } @@ -252,80 +278,60 @@ void Synth::setReverbOutputGain(float newReverbOutputGain) { reverbOutputGain = newReverbOutputGain; } -Common::File *Synth::openFile(const char *filename) { - if (myProp.openFile != NULL) { - return myProp.openFile(myProp.userData, filename); - } - char pathBuf[2048]; - if (myProp.baseDir != NULL) { - strcpy(&pathBuf[0], myProp.baseDir); - strcat(&pathBuf[0], filename); - filename = pathBuf; - } - Common::File *file = new Common::File(); - if (!file->open(filename)) { - delete file; - return NULL; - } - return file; -} - -void Synth::closeFile(Common::File *file) { - if (myProp.closeFile != NULL) { - myProp.closeFile(myProp.userData, file); - } else { - file->close(); - delete file; - } -} - -LoadResult Synth::loadControlROM(const char *filename) { - Common::File *file = openFile(filename); // ROM File - if (file == NULL) { - return LoadResult_NotFound; - } - size_t fileSize = file->size(); - if (fileSize != CONTROL_ROM_SIZE) { - printDebug("Control ROM file %s size mismatch: %i", filename, fileSize); +bool Synth::loadControlROM(const ROMImage &controlROMImage) { + if (&controlROMImage == NULL) return false; + Common::File *file = controlROMImage.getFile(); + const ROMInfo *controlROMInfo = controlROMImage.getROMInfo(); + if ((controlROMInfo == NULL) + || (controlROMInfo->type != ROMInfo::Control) + || (controlROMInfo->pairType != ROMInfo::Full)) { + return false; } +#if MT32EMU_MONITOR_INIT + printDebug("Found Control ROM: %s, %s", controlROMInfo->shortName, controlROMInfo->description); +#endif file->read(controlROMData, CONTROL_ROM_SIZE); - if (file->err()) { - closeFile(file); - return LoadResult_Unreadable; - } - closeFile(file); // Control ROM successfully loaded, now check whether it's a known type controlROMMap = NULL; for (unsigned int i = 0; i < sizeof(ControlROMMaps) / sizeof(ControlROMMaps[0]); i++) { if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) { controlROMMap = &ControlROMMaps[i]; - return LoadResult_OK; + return true; } } - printDebug("%s does not match a known control ROM type", filename); - return LoadResult_Invalid; +#if MT32EMU_MONITOR_INIT + printDebug("Control ROM failed to load"); +#endif + return false; } -LoadResult Synth::loadPCMROM(const char *filename) { - Common::File *file = openFile(filename); // ROM File - if (file == NULL) { - return LoadResult_NotFound; +bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { + if (&pcmROMImage == NULL) return false; + Common::File *file = pcmROMImage.getFile(); + const ROMInfo *pcmROMInfo = pcmROMImage.getROMInfo(); + if ((pcmROMInfo == NULL) + || (pcmROMInfo->type != ROMInfo::PCM) + || (pcmROMInfo->pairType != ROMInfo::Full)) { + return false; } +#if MT32EMU_MONITOR_INIT + printDebug("Found PCM ROM: %s, %s", pcmROMInfo->shortName, pcmROMInfo->description); +#endif size_t fileSize = file->size(); - if (fileSize < (size_t)(2 * pcmROMSize)) { - printDebug("PCM ROM file is too short (expected %d, got %d)", 2 * pcmROMSize, fileSize); - closeFile(file); - return LoadResult_Invalid; - } - if (file->err()) { - closeFile(file); - return LoadResult_Unreadable; + if (fileSize != (2 * pcmROMSize)) { +#if MT32EMU_MONITOR_INIT + printDebug("PCM ROM file has wrong size (expected %d, got %d)", 2 * pcmROMSize, fileSize); +#endif + return false; } - LoadResult rc = LoadResult_OK; - for (unsigned int i = 0; i < pcmROMSize; i++) { - Bit8u s = file->readByte(); - Bit8u c = file->readByte(); + + byte *buffer = new byte[file->size()]; + file->read(buffer, file->size()); + const byte *fileData = buffer; + for (size_t i = 0; i < pcmROMSize; i++) { + Bit8u s = *(fileData++); + Bit8u c = *(fileData++); int order[16] = {0, 9, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8}; @@ -351,16 +357,18 @@ LoadResult Synth::loadPCMROM(const char *filename) { pcmROMData[i] = lin; } - closeFile(file); - return rc; + + delete[] buffer; + + return true; } bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) { ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress]; for (int i = 0; i < count; i++) { - unsigned int rAddr = tps[i].pos * 0x800; - int rLenExp = (tps[i].len & 0x70) >> 4; - int rLen = 0x800 << rLenExp; + size_t rAddr = tps[i].pos * 0x800; + size_t rLenExp = (tps[i].len & 0x70) >> 4; + size_t rLen = 0x800 << rLenExp; if (rAddr + rLen > pcmROMSize) { printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen); return false; @@ -422,12 +430,11 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi return true; } -bool Synth::open(SynthProperties &useProp) { +bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage) { if (isOpen) { return false; } prerenderReadIx = prerenderWriteIx = 0; - myProp = useProp; #if MT32EMU_MONITOR_INIT printDebug("Initialising Constant Tables"); #endif @@ -436,11 +443,6 @@ bool Synth::open(SynthProperties &useProp) { reverbModels[i]->open(useProp.sampleRate); } #endif - if (useProp.baseDir != NULL) { - char *baseDirCopy = new char[strlen(useProp.baseDir) + 1]; - strcpy(baseDirCopy, useProp.baseDir); - myProp.baseDir = baseDirCopy; - } // This is to help detect bugs memset(&mt32ram, '?', sizeof(mt32ram)); @@ -448,12 +450,10 @@ bool Synth::open(SynthProperties &useProp) { #if MT32EMU_MONITOR_INIT printDebug("Loading Control ROM"); #endif - if (loadControlROM("CM32L_CONTROL.ROM") != LoadResult_OK) { - if (loadControlROM("MT32_CONTROL.ROM") != LoadResult_OK) { - printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM"); - //report(ReportType_errorControlROM, &errno); - return false; - } + if (!loadControlROM(controlROMImage)) { + printDebug("Init Error - Missing or invalid Control ROM image"); + reportHandler->onErrorControlROM(); + return false; } initMemoryRegions(); @@ -467,12 +467,10 @@ bool Synth::open(SynthProperties &useProp) { #if MT32EMU_MONITOR_INIT printDebug("Loading PCM ROM"); #endif - if (loadPCMROM("CM32L_PCM.ROM") != LoadResult_OK) { - if (loadPCMROM("MT32_PCM.ROM") != LoadResult_OK) { - printDebug("Init Error - Missing MT32_PCM.ROM"); - //report(ReportType_errorPCMROM, &errno); - return false; - } + if (!loadPCMROM(pcmROMImage)) { + printDebug("Init Error - Missing PCM ROM image"); + reportHandler->onErrorPCMROM(); + return false; } #if MT32EMU_MONITOR_INIT @@ -601,9 +599,6 @@ void Synth::close() { parts[i] = NULL; } - delete[] myProp.baseDir; - myProp.baseDir = NULL; - delete[] pcmWaves; delete[] pcmROMData; @@ -1190,7 +1185,7 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le case MR_System: region->write(0, off, data, len); - report(ReportType_devReconfig, NULL); + reportHandler->onDeviceReconfig(); // FIXME: We haven't properly confirmed any of this behaviour // In particular, we tend to reset things such as reverb even if the write contained // the same parameters as were already set, which may be wrong. @@ -1228,7 +1223,7 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le #if MT32EMU_MONITOR_SYSEX > 0 printDebug("WRITE-LCD: %s", buf); #endif - report(ReportType_lcdMessage, buf); + reportHandler->showLCDMessage(buf); break; case MR_Reset: reset(); @@ -1256,9 +1251,9 @@ void Synth::refreshSystemReverbParameters() { #endif return; } - report(ReportType_newReverbMode, &mt32ram.system.reverbMode); - report(ReportType_newReverbTime, &mt32ram.system.reverbTime); - report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel); + reportHandler->onNewReverbMode(mt32ram.system.reverbMode); + reportHandler->onNewReverbTime(mt32ram.system.reverbTime); + reportHandler->onNewReverbLevel(mt32ram.system.reverbLevel); ReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode]; #if MT32EMU_REDUCE_REVERB_MEMORY @@ -1266,7 +1261,7 @@ void Synth::refreshSystemReverbParameters() { if (reverbModel != NULL) { reverbModel->close(); } - newReverbModel->open(myProp.sampleRate); + newReverbModel->open(); } #endif reverbModel = newReverbModel; @@ -1321,7 +1316,7 @@ void Synth::reset() { #if MT32EMU_MONITOR_SYSEX > 0 printDebug("RESET"); #endif - report(ReportType_devReset, NULL); + reportHandler->onDeviceReset(); partialManager->deactivateAll(); mt32ram = mt32default; for (int i = 0; i < 9; i++) { diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h index 2534b7af35..91375c0fc0 100644 --- a/audio/softsynth/mt32/Synth.h +++ b/audio/softsynth/mt32/Synth.h @@ -26,6 +26,7 @@ class TableInitialiser; class Partial; class PartialManager; class Part; +class ROMImage; /** * Methods for emulating the connection between the LA32 and the DAC, which involves @@ -57,71 +58,6 @@ enum DACInputMode { DACInputMode_GENERATION2 }; -enum ReportType { - // Errors - ReportType_errorControlROM = 1, - ReportType_errorPCMROM, - ReportType_errorSampleRate, - - // Progress - ReportType_progressInit, - - // HW spec - ReportType_availableSSE, - ReportType_available3DNow, - ReportType_usingSSE, - ReportType_using3DNow, - - // General info - ReportType_lcdMessage, - ReportType_devReset, - ReportType_devReconfig, - ReportType_newReverbMode, - ReportType_newReverbTime, - ReportType_newReverbLevel -}; - -enum LoadResult { - LoadResult_OK, - LoadResult_NotFound, - LoadResult_Unreadable, - LoadResult_Invalid -}; - -struct SynthProperties { - // Sample rate to use in mixing - unsigned int sampleRate; - - // Deprecated - ignored. Use Synth::setReverbEnabled() instead. - bool useReverb; - // Deprecated - ignored. Use Synth::setReverbOverridden() instead. - bool useDefaultReverb; - // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. - unsigned char reverbType; - // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. - unsigned char reverbTime; - // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. - unsigned char reverbLevel; - // The name of the directory in which the ROM and data files are stored (with trailing slash/backslash) - // Not used if "openFile" is set. May be NULL in any case. - const char *baseDir; - // This is used as the first argument to all callbacks - void *userData; - // Callback for reporting various errors and information. May be NULL - 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 - // May be NULL, in which case a default implementation will be used. - Common::File *(*openFile)(void *userData, const char *filename); - // Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted. - void (*closeFile)(void *userData, Common::File *file); -}; - -// This is the specification of the Callback routine used when calling the RecalcWaveforms -// function -typedef void (*recalcStatusCallback)(int percDone); - typedef void (*FloatToBit16sFunc)(Bit16s *target, const float *source, Bit32u len, float outputGain); const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41; @@ -285,7 +221,7 @@ class ReverbModel { public: virtual ~ReverbModel() {} // After construction or a close(), open() will be called at least once before any other call (with the exception of close()). - virtual void open(unsigned int sampleRate) = 0; + virtual void open() = 0; // May be called multiple times without an open() in between. virtual void close() = 0; virtual void setParameters(Bit8u time, Bit8u level) = 0; @@ -293,6 +229,32 @@ public: virtual bool isActive() const = 0; }; +class ReportHandler { +friend class Synth; + +public: + virtual ~ReportHandler() {} + +protected: + + // Callback for debug messages, in vprintf() format + virtual void printDebug(const char *fmt, va_list list); + + // Callbacks for reporting various errors and information + virtual void onErrorControlROM() {} + virtual void onErrorPCMROM() {} + virtual void showLCDMessage(const char *message); + virtual void onDeviceReset() {} + virtual void onDeviceReconfig() {} + virtual void onNewReverbMode(Bit8u /* mode */) {} + virtual void onNewReverbTime(Bit8u /* time */) {} + virtual void onNewReverbLevel(Bit8u /* level */) {} + virtual void onPartStateChanged(int /* partNum */, bool /* isActive */) {} + virtual void onPolyStateChanged(int /* partNum */) {} + virtual void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {} + virtual void onProgramChanged(int /* partNum */, char * /* patchName */) {} +}; + class Synth { friend class Part; friend class RhythmPart; @@ -322,7 +284,7 @@ private: const ControlROMMap *controlROMMap; Bit8u controlROMData[CONTROL_ROM_SIZE]; float *pcmROMData; - unsigned int pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM + size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM Bit8s chantable[32]; @@ -343,6 +305,9 @@ private: bool isOpen; + bool isDefaultReportHandler; + ReportHandler *reportHandler; + PartialManager *partialManager; Part *parts[9]; @@ -375,8 +340,6 @@ private: int prerenderReadIx; int prerenderWriteIx; - SynthProperties myProp; - bool prerender(); void copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len); void checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len); @@ -390,8 +353,8 @@ private: void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data); void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data); - LoadResult loadControlROM(const char *filename); - LoadResult loadPCMROM(const char *filename); + bool loadControlROM(const ROMImage &controlROMImage); + bool loadPCMROM(const ROMImage &pcmROMImage); bool initPCMList(Bit16u mapAddress, Bit16u count); bool initTimbres(Bit16u mapAddress, Bit16u offset, int timbreCount, int startTimbre, bool compressed); @@ -405,24 +368,25 @@ private: void refreshSystem(); void reset(); - unsigned int getSampleRate() const; - void printPartialUsage(unsigned long sampleOffset = 0); -protected: - int report(ReportType type, const void *reportData); - Common::File *openFile(const char *filename); - void closeFile(Common::File *file); + + void partStateChanged(int partNum, bool isPartActive); + void polyStateChanged(int partNum); + void partialStateChanged(const Partial * const partial, int oldPartialPhase, int newPartialPhase); + void newTimbreSet(int partNum, char patchName[]); void printDebug(const char *fmt, ...); public: static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum); - Synth(); + // Optionally sets callbacks for reporting various errors, information and debug messages + Synth(ReportHandler *useReportHandler = NULL); ~Synth(); // Used to initialise the MT-32. Must be called before any other function. // Returns true if initialization was sucessful, otherwise returns false. - bool open(SynthProperties &useProp); + // controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth. + bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage); // Closes the MT-32 and deallocates any memory used by the synthesizer void close(void); diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp index c581259a38..fd442da905 100644 --- a/audio/softsynth/mt32/TVA.cpp +++ b/audio/softsynth/mt32/TVA.cpp @@ -34,6 +34,9 @@ TVA::TVA(const Partial *usePartial, LA32Ramp *useAmpRamp) : } void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) { + if (newPhase != phase) { + partial->getSynth()->partialStateChanged(partial, phase, newPhase); + } target = newTarget; phase = newPhase; ampRamp->startRamp(newTarget, newIncrement); @@ -43,6 +46,9 @@ void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) { } void TVA::end(int newPhase) { + if (newPhase != phase) { + partial->getSynth()->partialStateChanged(partial, phase, newPhase); + } phase = newPhase; playing = false; #if MT32EMU_MONITOR_TVA >= 1 diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp index 32a6067841..5dc4ca6b66 100644 --- a/audio/softsynth/mt32/TVP.cpp +++ b/audio/softsynth/mt32/TVP.cpp @@ -47,12 +47,11 @@ static Bit16u keyToPitchTable[] = { TVP::TVP(const Partial *usePartial) : partial(usePartial), system_(&usePartial->getSynth()->mt32ram.system) { - unsigned int sampleRate = usePartial->getSynth()->myProp.sampleRate; // We want to do processing 4000 times per second. FIXME: This is pretty arbitrary. - maxCounter = sampleRate / 4000; + maxCounter = SAMPLE_RATE / 4000; // The timer runs at 500kHz. We only need to bother updating it every maxCounter samples, before we do processing. // This is how much to increment it by every maxCounter samples. - processTimerIncrement = 500000 * maxCounter / sampleRate; + processTimerIncrement = 500000 * maxCounter / SAMPLE_RATE; } static Bit16s keyToPitch(unsigned int key) { diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h index a63eaf6d26..b353cf4c51 100644 --- a/audio/softsynth/mt32/Tables.h +++ b/audio/softsynth/mt32/Tables.h @@ -20,7 +20,9 @@ namespace MT32Emu { -// Sample rate to use in mixing +// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent. +// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator. +// The output from the synth is supposed to be resampled to convert the sample rate. const unsigned int SAMPLE_RATE = 32000; const int MIDDLEC = 60; diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk index c0ee363c04..ed6b0d33ed 100644 --- a/audio/softsynth/mt32/module.mk +++ b/audio/softsynth/mt32/module.mk @@ -10,6 +10,7 @@ MODULE_OBJS := \ Partial.o \ PartialManager.o \ Poly.o \ + ROMInfo.o \ Synth.o \ TVA.o \ TVF.o \ diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h index a5a72b01ca..ae5f4955b1 100644 --- a/audio/softsynth/mt32/mt32emu.h +++ b/audio/softsynth/mt32/mt32emu.h @@ -71,8 +71,6 @@ // 1: Use precise float math. Use this setting to achieve more accurate wave generator. If your system performs better with this setting, it is really notable. :) #define MT32EMU_ACCURATE_WG 0 -#define MT32EMU_USE_EXTINT 0 - // Configuration // The maximum number of partials playing simultaneously #define MT32EMU_MAX_PARTIALS 32 @@ -117,6 +115,7 @@ const unsigned int MAX_PRERENDER_SAMPLES = 1024; #include "TVF.h" #include "Partial.h" #include "Part.h" +#include "ROMInfo.h" #include "Synth.h" #endif |