aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth
diff options
context:
space:
mode:
authorFilippos Karapetis2012-12-26 21:17:53 +0200
committerFilippos Karapetis2012-12-26 21:28:34 +0200
commit5711d23231d2f7baf965b93e1e771c917bdc246f (patch)
treec8df610d944381d152ff45b61fde59b3f3cb5b74 /audio/softsynth
parent0b72bd2dea0cfea13ab03dc74361aea7ac95c997 (diff)
downloadscummvm-rg350-5711d23231d2f7baf965b93e1e771c917bdc246f.tar.gz
scummvm-rg350-5711d23231d2f7baf965b93e1e771c917bdc246f.tar.bz2
scummvm-rg350-5711d23231d2f7baf965b93e1e771c917bdc246f.zip
MT32: Update the MT32 emulator and adapt to its new API
Previous munt commit was 84b2819 (Dec 22, 2012) Current munt commit is 6afddaf (Dec 23, 2012) This commit also updates the MT32 driver code to the latest munt API
Diffstat (limited to 'audio/softsynth')
-rw-r--r--audio/softsynth/mt32.cpp201
-rw-r--r--audio/softsynth/mt32/AReverbModel.cpp16
-rw-r--r--audio/softsynth/mt32/AReverbModel.h2
-rw-r--r--audio/softsynth/mt32/BReverbModel.cpp4
-rw-r--r--audio/softsynth/mt32/BReverbModel.h2
-rw-r--r--audio/softsynth/mt32/DelayReverb.cpp34
-rw-r--r--audio/softsynth/mt32/DelayReverb.h5
-rw-r--r--audio/softsynth/mt32/FreeverbModel.cpp4
-rw-r--r--audio/softsynth/mt32/FreeverbModel.h2
-rw-r--r--audio/softsynth/mt32/Part.cpp7
-rw-r--r--audio/softsynth/mt32/Partial.cpp9
-rw-r--r--audio/softsynth/mt32/ROMInfo.cpp112
-rw-r--r--audio/softsynth/mt32/ROMInfo.h77
-rw-r--r--audio/softsynth/mt32/Synth.cpp209
-rw-r--r--audio/softsynth/mt32/Synth.h122
-rw-r--r--audio/softsynth/mt32/TVA.cpp6
-rw-r--r--audio/softsynth/mt32/TVP.cpp5
-rw-r--r--audio/softsynth/mt32/Tables.h4
-rw-r--r--audio/softsynth/mt32/module.mk1
-rw-r--r--audio/softsynth/mt32/mt32emu.h3
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