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
This commit is contained in:
Filippos Karapetis
2012-12-26 21:28:34 +02:00
parent 0b72bd2dea
commit 5711d23231
20 changed files with 464 additions and 365 deletions
+74 -127
View File
@@ -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) {
+8 -8
View File
@@ -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 {
+1 -1
View File
@@ -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);
+1 -3
View File
@@ -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++) {
+1 -1
View File
@@ -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);
+15 -19
View File
@@ -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++;
}
+1 -4
View File
@@ -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);
+1 -3
View File
@@ -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);
}
+1 -1
View File
@@ -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);
+7
View File
@@ -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);
}
}
+5 -4
View File
@@ -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;
+112
View File
@@ -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;
}
}
+77
View File
@@ -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
+104 -109
View File
@@ -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];
}
}
int Synth::report(ReportType type, const void *data) {
if (myProp.report != NULL) {
return myProp.report(myProp.userData, type, data);
if (isDefaultReportHandler) {
delete reportHandler;
}
return 0;
}
unsigned int Synth::getSampleRate() const {
return myProp.sampleRate;
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;
}
}
}
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 (fileSize != (2 * pcmROMSize)) {
#if MT32EMU_MONITOR_INIT
printDebug("PCM ROM file has wrong size (expected %d, got %d)", 2 * pcmROMSize, fileSize);
#endif
return false;
}
if (file->err()) {
closeFile(file);
return LoadResult_Unreadable;
}
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++) {
+43 -79
View File
@@ -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);
+6
View File
@@ -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
+2 -3
View File
@@ -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) {
+3 -1
View File
@@ -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;
+1
View File
@@ -10,6 +10,7 @@ MODULE_OBJS := \
Partial.o \
PartialManager.o \
Poly.o \
ROMInfo.o \
Synth.o \
TVA.o \
TVF.o \
+1 -2
View File
@@ -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