diff --git a/DOSBox-mt32-patch/README b/DOSBox-mt32-patch/README index 18e66a8..d04365a 100644 --- a/DOSBox-mt32-patch/README +++ b/DOSBox-mt32-patch/README @@ -2,5 +2,5 @@ Patches for DOSBox to add mt32emu MIDI device dosbox-0.74-mt32-patch.diff - diff file to be applied to official DOSBox release v.0.74 source distribution. -dosbox-SVN-r3858-mt32-patch.diff - diff file to be applied to official DOSBox sources SVN r3858 (and up, hopefully). +dosbox-SVN-r3873-mt32-patch.diff - diff file to be applied to official DOSBox sources SVN r3873 (and up, hopefully). It uses a bit different and clear approach introduced since SVN r3836. diff --git a/DOSBox-mt32-patch/dosbox-0.74-mt32-patch.diff b/DOSBox-mt32-patch/dosbox-0.74-mt32-patch.diff index 631d3e2..5f7bf30 100644 --- a/DOSBox-mt32-patch/dosbox-0.74-mt32-patch.diff +++ b/DOSBox-mt32-patch/dosbox-0.74-mt32-patch.diff @@ -1,6 +1,6 @@ -commit ffb81f8bad5b520712ff0a7d2a9cd1ae00e07c50 +commit 2f29fdee73d7171cbd075b5a8b281f0b58c26b0c Author: sergm -Date: Mon Dec 1 17:49:42 2014 +0200 +Date: Mon Dec 1 21:20:05 2014 +0200 mt32emu patch @@ -128,234 +128,198 @@ index 3fed5e6..d4aeabc 100644 + midi_coremidi.h midi_mt32.h sdl_gui.cpp dosbox_splash.h diff --git a/src/gui/midi.cpp b/src/gui/midi.cpp -index 61d15e5..ed03ea4 100644 +index 61d15e5..0ec8ebc 100644 --- a/src/gui/midi.cpp +++ b/src/gui/midi.cpp -@@ -98,6 +98,8 @@ MidiHandler Midi_none; +@@ -98,6 +98,11 @@ MidiHandler Midi_none; #endif -+#include "midi_mt32.h" ++#define DOSBOX_MIDI_H ++#include "midi_mt32.cpp" ++static MidiHandler_mt32 &Midi_mt32 = MidiHandler_mt32::GetInstance(); ++#undef DOSBOX_MIDI_H + static struct { Bitu status; Bitu cmd_len; -diff --git a/src/gui/midi_mt32.h b/src/gui/midi_mt32.h +diff --git a/src/gui/midi_mt32.cpp b/src/gui/midi_mt32.cpp new file mode 100644 -index 0000000..af96b3c +index 0000000..ce71157 --- /dev/null -+++ b/src/gui/midi_mt32.h -@@ -0,0 +1,264 @@ -+#include ++++ b/src/gui/midi_mt32.cpp +@@ -0,0 +1,248 @@ +#include +#include +#include -+#include "mixer.h" +#include "control.h" + -+static class MidiHandler_mt32 : public MidiHandler { -+private: -+ static const Bitu MILLIS_PER_SECOND = 1000; -+ static const Bitu MINIMUM_RENDER_MILLIS = 16; -+ static const Bitu AUDIO_LATENCY_MILLIS = 2 * MINIMUM_RENDER_MILLIS; ++#ifndef DOSBOX_MIDI_H ++#include "midi.h" ++#endif + -+ MixerChannel *chan; -+ MT32Emu::Synth *synth; -+ SDL_Thread *thread; -+ Bit16s *audioBuffer; -+ Bitu audioBufferSize; -+ Bitu framesPerAudioBuffer; -+ Bitu minimumRenderFrames; -+ double sampleRateRatio; -+ volatile Bitu renderPos, playPos, playedBuffers; -+ volatile bool stopProcessing; -+ bool open, noise, renderInThread; ++#include "midi_mt32.h" + -+ class MT32ReportHandler : public MT32Emu::ReportHandler { -+ protected: -+ virtual void onErrorControlROM() { -+ LOG_MSG("MT32: Couldn't open Control ROM file"); -+ } ++static const Bitu MILLIS_PER_SECOND = 1000; ++static const Bitu MINIMUM_RENDER_MILLIS = 16; ++static const Bitu AUDIO_LATENCY_MILLIS = 2 * MINIMUM_RENDER_MILLIS; + -+ virtual void onErrorPCMROM() { -+ LOG_MSG("MT32: Couldn't open PCM ROM file"); -+ } ++MidiHandler_mt32 &MidiHandler_mt32::GetInstance() { ++ static MidiHandler_mt32 midiHandler_mt32; ++ return midiHandler_mt32; ++} + -+ virtual void showLCDMessage(const char *message) { -+ LOG_MSG("MT32: LCD-Message: %s", message); -+ } ++MidiHandler_mt32::MidiHandler_mt32() : open(false), chan(NULL), synth(NULL), thread(NULL) { ++} + -+ virtual void printDebug(const char *fmt, va_list list); -+ } reportHandler; ++MidiHandler_mt32::~MidiHandler_mt32() { ++ Close(); ++} + -+ static void mixerCallBack(Bitu len); -+ static int processingThread(void *); ++const char *MidiHandler_mt32::GetName(void) { ++ return "mt32"; ++} + -+ static void makeROMPathName(char pathName[], const char romDir[], const char fileName[], bool addPathSeparator) { -+ strcpy(pathName, romDir); -+ if (addPathSeparator) { -+ strcat(pathName, "/"); -+ } -+ strcat(pathName, fileName); ++void MidiHandler_mt32::makeROMPathName(char pathName[], const char romDir[], const char fileName[], bool addPathSeparator) { ++ strcpy(pathName, romDir); ++ if (addPathSeparator) { ++ strcat(pathName, "/"); ++ } ++ strcat(pathName, fileName); ++} ++ ++bool MidiHandler_mt32::Open(const char *conf) { ++ Section_prop *section = static_cast(control->GetSection("midi")); ++ const char *romDir = section->Get_string("mt32.romdir"); ++ if (romDir == NULL) romDir = "./"; // Paranoid NULL-check, should never happen ++ size_t romDirLen = strlen(romDir); ++ bool addPathSeparator = false; ++ if (romDirLen < 1) { ++ romDir = "./"; ++ } else if (4080 < romDirLen) { ++ LOG_MSG("MT32: mt32.romdir is too long, using the current dir."); ++ romDir = "./"; ++ } else { ++ char lastChar = romDir[strlen(romDir) - 1]; ++ addPathSeparator = lastChar != '/' && lastChar != '\\'; + } + -+public: -+ MidiHandler_mt32() : open(false), chan(NULL), synth(NULL), thread(NULL), audioBuffer(NULL) {} ++ char pathName[4096]; ++ MT32Emu::FileStream controlROMFile; ++ MT32Emu::FileStream pcmROMFile; + -+ ~MidiHandler_mt32() { -+ Close(); -+ } -+ -+ const char *GetName(void) { -+ return "mt32"; -+ } -+ -+ bool Open(const char *conf) { -+ Section_prop *section = static_cast(control->GetSection("midi")); -+ const char *romDir = section->Get_string("mt32.romdir"); -+ if (romDir == NULL) romDir = "./"; // Paranoid NULL-check, should never happen -+ size_t romDirLen = strlen(romDir); -+ bool addPathSeparator = false; -+ if (romDirLen < 1) { -+ romDir = "./"; -+ } else if (4080 < romDirLen) { -+ LOG_MSG("MT32: mt32.romdir is too long, using the current dir."); -+ romDir = "./"; -+ } else { -+ char lastChar = romDir[strlen(romDir) - 1]; -+ addPathSeparator = lastChar != '/' && lastChar != '\\'; -+ } -+ -+ char pathName[4096]; -+ MT32Emu::FileStream controlROMFile; -+ MT32Emu::FileStream pcmROMFile; -+ -+ makeROMPathName(pathName, romDir, "CM32L_CONTROL.ROM", addPathSeparator); ++ makeROMPathName(pathName, romDir, "CM32L_CONTROL.ROM", addPathSeparator); ++ if (!controlROMFile.open(pathName)) { ++ makeROMPathName(pathName, romDir, "MT32_CONTROL.ROM", addPathSeparator); + if (!controlROMFile.open(pathName)) { -+ makeROMPathName(pathName, romDir, "MT32_CONTROL.ROM", addPathSeparator); -+ if (!controlROMFile.open(pathName)) { -+ LOG_MSG("MT32: Control ROM file not found"); -+ return false; -+ } -+ } -+ makeROMPathName(pathName, romDir, "CM32L_PCM.ROM", addPathSeparator); -+ if (!pcmROMFile.open(pathName)) { -+ makeROMPathName(pathName, romDir, "MT32_PCM.ROM", addPathSeparator); -+ if (!pcmROMFile.open(pathName)) { -+ LOG_MSG("MT32: PCM ROM file not found"); -+ return false; -+ } -+ } -+ const MT32Emu::ROMImage *controlROMImage = MT32Emu::ROMImage::makeROMImage(&controlROMFile); -+ const MT32Emu::ROMImage *pcmROMImage = MT32Emu::ROMImage::makeROMImage(&pcmROMFile); -+ -+ MT32Emu::AnalogOutputMode analogOutputMode = MT32Emu::AnalogOutputMode_ACCURATE; -+ if (strcmp(section->Get_string("mt32.analog"), "auto") != 0) { -+ analogOutputMode = (MT32Emu::AnalogOutputMode)atoi(section->Get_string("mt32.analog")); -+ } -+ -+ synth = new MT32Emu::Synth(&reportHandler); -+ if (!synth->open(*controlROMImage, *pcmROMImage, analogOutputMode)) { -+ LOG_MSG("MT32: Error initialising emulation"); ++ LOG_MSG("MT32: Control ROM file not found"); + return false; + } -+ MT32Emu::ROMImage::freeROMImage(controlROMImage); -+ MT32Emu::ROMImage::freeROMImage(pcmROMImage); -+ -+ if (strcmp(section->Get_string("mt32.reverb.mode"), "auto") != 0) { -+ Bit8u reverbsysex[] = {0x10, 0x00, 0x01, 0x00, 0x05, 0x03}; -+ reverbsysex[3] = (Bit8u)atoi(section->Get_string("mt32.reverb.mode")); -+ reverbsysex[4] = (Bit8u)section->Get_int("mt32.reverb.time"); -+ reverbsysex[5] = (Bit8u)section->Get_int("mt32.reverb.level"); -+ synth->writeSysex(16, reverbsysex, 6); -+ synth->setReverbOverridden(true); -+ } -+ -+ if (strcmp(section->Get_string("mt32.dac"), "auto") != 0) { -+ synth->setDACInputMode((MT32Emu::DACInputMode)atoi(section->Get_string("mt32.dac"))); -+ } -+ -+ synth->setReversedStereoEnabled(strcmp(section->Get_string("mt32.reverse.stereo"), "on") == 0); -+ noise = strcmp(section->Get_string("mt32.verbose"), "on") == 0; -+ renderInThread = strcmp(section->Get_string("mt32.thread"), "on") == 0; -+ -+ if (noise) LOG_MSG("MT32: Adding mixer channel at sample rate %d", synth->getStereoOutputSampleRate()); -+ chan = MIXER_AddChannel(mixerCallBack, synth->getStereoOutputSampleRate(), "MT32"); -+ if (renderInThread) { -+ stopProcessing = false; -+ renderPos = 0; -+ playPos = 0; -+ sampleRateRatio = MT32Emu::SAMPLE_RATE / (double)synth->getStereoOutputSampleRate(); -+ minimumRenderFrames = (MINIMUM_RENDER_MILLIS * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND; -+ framesPerAudioBuffer = (AUDIO_LATENCY_MILLIS * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND; -+ audioBufferSize = framesPerAudioBuffer << 1; -+ audioBuffer = new Bit16s[audioBufferSize]; -+ render(framesPerAudioBuffer - 1, audioBuffer); -+ playedBuffers = 1; -+ thread = SDL_CreateThread(processingThread, NULL); -+ } -+ chan->Enable(true); -+ -+ open = true; -+ return true; + } -+ -+ void Close(void) { -+ if (!open) return; -+ chan->Enable(false); -+ if (renderInThread) { -+ stopProcessing = true; -+ SDL_WaitThread(thread, NULL); -+ thread = NULL; -+ delete[] audioBuffer; -+ audioBuffer = NULL; -+ } -+ MIXER_DelChannel(chan); -+ chan = NULL; -+ synth->close(); -+ delete synth; -+ synth = NULL; -+ open = false; -+ } -+ -+ void PlayMsg(Bit8u *msg) { -+ if (renderInThread) { -+ synth->playMsg(SDL_SwapLE32(*(Bit32u *)msg), getMidiEventTimestamp()); -+ } else { -+ synth->playMsg(SDL_SwapLE32(*(Bit32u *)msg)); ++ makeROMPathName(pathName, romDir, "CM32L_PCM.ROM", addPathSeparator); ++ if (!pcmROMFile.open(pathName)) { ++ makeROMPathName(pathName, romDir, "MT32_PCM.ROM", addPathSeparator); ++ if (!pcmROMFile.open(pathName)) { ++ LOG_MSG("MT32: PCM ROM file not found"); ++ return false; + } + } ++ const MT32Emu::ROMImage *controlROMImage = MT32Emu::ROMImage::makeROMImage(&controlROMFile); ++ const MT32Emu::ROMImage *pcmROMImage = MT32Emu::ROMImage::makeROMImage(&pcmROMFile); + -+ void PlaySysex(Bit8u *sysex, Bitu len) { -+ if (renderInThread) { -+ synth->playSysex(sysex, len, getMidiEventTimestamp()); -+ } else { -+ synth->playSysex(sysex, len); -+ } ++ MT32Emu::AnalogOutputMode analogOutputMode = MT32Emu::AnalogOutputMode_ACCURATE; ++ if (strcmp(section->Get_string("mt32.analog"), "auto") != 0) { ++ analogOutputMode = (MT32Emu::AnalogOutputMode)atoi(section->Get_string("mt32.analog")); + } + -+private: -+ Bit32u inline getMidiEventTimestamp() { -+ return Bit32u((playedBuffers * framesPerAudioBuffer + (playPos >> 1)) * sampleRateRatio); ++ synth = new MT32Emu::Synth(&reportHandler); ++ if (!synth->open(*controlROMImage, *pcmROMImage, analogOutputMode)) { ++ LOG_MSG("MT32: Error initialising emulation"); ++ return false; ++ } ++ MT32Emu::ROMImage::freeROMImage(controlROMImage); ++ MT32Emu::ROMImage::freeROMImage(pcmROMImage); ++ ++ if (strcmp(section->Get_string("mt32.reverb.mode"), "auto") != 0) { ++ Bit8u reverbsysex[] = {0x10, 0x00, 0x01, 0x00, 0x05, 0x03}; ++ reverbsysex[3] = (Bit8u)atoi(section->Get_string("mt32.reverb.mode")); ++ reverbsysex[4] = (Bit8u)section->Get_int("mt32.reverb.time"); ++ reverbsysex[5] = (Bit8u)section->Get_int("mt32.reverb.level"); ++ synth->writeSysex(16, reverbsysex, 6); ++ synth->setReverbOverridden(true); + } + -+ void render(const Bitu len, Bit16s *buf) { -+ Bit16s *revBuf = &buf[renderPos]; -+ synth->render(revBuf, len); -+ renderPos = (renderPos + (len << 1)) % audioBufferSize; ++ if (strcmp(section->Get_string("mt32.dac"), "auto") != 0) { ++ synth->setDACInputMode((MT32Emu::DACInputMode)atoi(section->Get_string("mt32.dac"))); + } -+} midiHandler_mt32; + -+void MidiHandler_mt32::MT32ReportHandler::printDebug(const char *fmt, va_list list) { -+ if (midiHandler_mt32.noise) { -+ char s[1024]; -+ strcpy(s, "MT32: "); -+ vsnprintf(s + 6, 1017, fmt, list); -+ LOG_MSG(s); ++ synth->setReversedStereoEnabled(strcmp(section->Get_string("mt32.reverse.stereo"), "on") == 0); ++ noise = strcmp(section->Get_string("mt32.verbose"), "on") == 0; ++ renderInThread = strcmp(section->Get_string("mt32.thread"), "on") == 0; ++ ++ if (noise) LOG_MSG("MT32: Adding mixer channel at sample rate %d", synth->getStereoOutputSampleRate()); ++ chan = MIXER_AddChannel(mixerCallBack, synth->getStereoOutputSampleRate(), "MT32"); ++ if (renderInThread) { ++ stopProcessing = false; ++ renderPos = 0; ++ playPos = 0; ++ sampleRateRatio = MT32Emu::SAMPLE_RATE / (double)synth->getStereoOutputSampleRate(); ++ minimumRenderFrames = (MINIMUM_RENDER_MILLIS * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND; ++ framesPerAudioBuffer = (AUDIO_LATENCY_MILLIS * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND; ++ audioBufferSize = framesPerAudioBuffer << 1; ++ audioBuffer = new Bit16s[audioBufferSize]; ++ render(framesPerAudioBuffer - 1, audioBuffer); ++ playedBuffers = 1; ++ thread = SDL_CreateThread(processingThread, NULL); ++ } ++ chan->Enable(true); ++ ++ open = true; ++ return true; ++} ++ ++void MidiHandler_mt32::Close(void) { ++ if (!open) return; ++ chan->Enable(false); ++ if (renderInThread) { ++ stopProcessing = true; ++ SDL_WaitThread(thread, NULL); ++ thread = NULL; ++ delete[] audioBuffer; ++ audioBuffer = NULL; ++ } ++ MIXER_DelChannel(chan); ++ chan = NULL; ++ synth->close(); ++ delete synth; ++ synth = NULL; ++ open = false; ++} ++ ++void MidiHandler_mt32::PlayMsg(Bit8u *msg) { ++ if (renderInThread) { ++ synth->playMsg(SDL_SwapLE32(*(Bit32u *)msg), getMidiEventTimestamp()); ++ } else { ++ synth->playMsg(SDL_SwapLE32(*(Bit32u *)msg)); + } +} + ++void MidiHandler_mt32::PlaySysex(Bit8u *sysex, Bitu len) { ++ if (renderInThread) { ++ synth->playSysex(sysex, len, getMidiEventTimestamp()); ++ } else { ++ synth->playSysex(sysex, len); ++ } ++} ++ ++void MidiHandler_mt32::render(const Bitu len, Bit16s *buf) { ++ Bit16s *revBuf = &buf[renderPos]; ++ synth->render(revBuf, len); ++ renderPos = (renderPos + (len << 1)) % audioBufferSize; ++} ++ +void MidiHandler_mt32::mixerCallBack(Bitu len) { ++ MidiHandler_mt32 &midiHandler_mt32 = MidiHandler_mt32::GetInstance(); + if (midiHandler_mt32.renderInThread) { + while (midiHandler_mt32.renderPos == midiHandler_mt32.playPos) { + SDL_Delay(1); @@ -386,6 +350,7 @@ index 0000000..af96b3c +} + +int MidiHandler_mt32::processingThread(void *) { ++ MidiHandler_mt32 &midiHandler_mt32 = MidiHandler_mt32::GetInstance(); + while (!midiHandler_mt32.stopProcessing) { + Bitu renderPos = midiHandler_mt32.renderPos; + Bitu playPos = midiHandler_mt32.playPos; @@ -410,3 +375,85 @@ index 0000000..af96b3c + } + return 0; +} ++ ++void MidiHandler_mt32::MT32ReportHandler::onErrorControlROM() { ++ LOG_MSG("MT32: Couldn't open Control ROM file"); ++} ++ ++void MidiHandler_mt32::MT32ReportHandler::onErrorPCMROM() { ++ LOG_MSG("MT32: Couldn't open PCM ROM file"); ++} ++ ++void MidiHandler_mt32::MT32ReportHandler::showLCDMessage(const char *message) { ++ LOG_MSG("MT32: LCD-Message: %s", message); ++} ++ ++void MidiHandler_mt32::MT32ReportHandler::printDebug(const char *fmt, va_list list) { ++ MidiHandler_mt32 &midiHandler_mt32 = MidiHandler_mt32::GetInstance(); ++ if (midiHandler_mt32.noise) { ++ char s[1024]; ++ strcpy(s, "MT32: "); ++ vsnprintf(s + 6, 1017, fmt, list); ++ LOG_MSG(s); ++ } ++} +diff --git a/src/gui/midi_mt32.h b/src/gui/midi_mt32.h +new file mode 100644 +index 0000000..45b20cd +--- /dev/null ++++ b/src/gui/midi_mt32.h +@@ -0,0 +1,54 @@ ++#ifndef DOSBOX_MIDI_MT32_H ++#define DOSBOX_MIDI_MT32_H ++ ++#include "mixer.h" ++#include ++ ++struct SDL_Thread; ++ ++class MidiHandler_mt32 : public MidiHandler { ++public: ++ static MidiHandler_mt32 &GetInstance(void); ++ ++ const char *GetName(void); ++ bool Open(const char *conf); ++ void Close(void); ++ void PlayMsg(Bit8u *msg); ++ void PlaySysex(Bit8u *sysex, Bitu len); ++ ++private: ++ MixerChannel *chan; ++ MT32Emu::Synth *synth; ++ SDL_Thread *thread; ++ Bit16s *audioBuffer; ++ Bitu audioBufferSize; ++ Bitu framesPerAudioBuffer; ++ Bitu minimumRenderFrames; ++ double sampleRateRatio; ++ volatile Bitu renderPos, playPos, playedBuffers; ++ volatile bool stopProcessing; ++ bool open, noise, renderInThread; ++ ++ class MT32ReportHandler : public MT32Emu::ReportHandler { ++ protected: ++ virtual void onErrorControlROM(); ++ virtual void onErrorPCMROM(); ++ virtual void showLCDMessage(const char *message); ++ virtual void printDebug(const char *fmt, va_list list); ++ } reportHandler; ++ ++ static void mixerCallBack(Bitu len); ++ static int processingThread(void *); ++ static void makeROMPathName(char pathName[], const char romDir[], const char fileName[], bool addPathSeparator); ++ ++ MidiHandler_mt32(); ++ ~MidiHandler_mt32(); ++ ++ void render(const Bitu len, Bit16s *buf); ++ ++ Bit32u inline getMidiEventTimestamp() { ++ return Bit32u((playedBuffers * framesPerAudioBuffer + (playPos >> 1)) * sampleRateRatio); ++ } ++}; ++ ++#endif /* DOSBOX_MIDI_MT32_H */ diff --git a/DOSBox-mt32-patch/dosbox-SVN-r3858-mt32-patch.diff b/DOSBox-mt32-patch/dosbox-SVN-r3873-mt32-patch.diff similarity index 76% rename from DOSBox-mt32-patch/dosbox-SVN-r3858-mt32-patch.diff rename to DOSBox-mt32-patch/dosbox-SVN-r3873-mt32-patch.diff index 33bd20b..fba1956 100644 --- a/DOSBox-mt32-patch/dosbox-SVN-r3858-mt32-patch.diff +++ b/DOSBox-mt32-patch/dosbox-SVN-r3873-mt32-patch.diff @@ -1,3 +1,9 @@ +commit 32425e02444c9339ee52a1d5d083e84834c4da47 +Author: sergm +Date: Mon Dec 1 21:22:55 2014 +0200 + + mt32emu patch + diff --git a/src/Makefile.am b/src/Makefile.am index 184469e..286638d 100644 --- a/src/Makefile.am @@ -9,7 +15,7 @@ index 184469e..286638d 100644 - +LIBS += -lmt32emu diff --git a/src/dosbox.cpp b/src/dosbox.cpp -index 6aad419..0ab8cba 100644 +index 6aad419..9a3f96c 100644 --- a/src/dosbox.cpp +++ b/src/dosbox.cpp @@ -483,7 +483,7 @@ void DOSBOX_Init(void) { @@ -21,7 +27,7 @@ index 6aad419..0ab8cba 100644 Pstring = secprop->Add_string("mpu401",Property::Changeable::WhenIdle,"intelligent"); Pstring->Set_values(mputypes); Pstring->Set_help("Type of MPU-401 to emulate."); -@@ -499,6 +499,68 @@ void DOSBOX_Init(void) { +@@ -499,6 +499,88 @@ void DOSBOX_Init(void) { " In that case, add 'delaysysex', for example: midiconfig=2 delaysysex\n" " See the README/Manual for more details."); @@ -58,8 +64,7 @@ index 6aad419..0ab8cba 100644 + "Produces samples that exactly match the bits output from the emulated LA32.\n" + "Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)\n" + "Much less likely to overdrive than any other mode.\n" -+ "Half the volume of any of the other modes, meaning its volume relative to the reverb\n" -+ "output when mixed together directly will sound wrong. So, reverb level must be lowered.\n" ++ "Half the volume of any of the other modes.\n" + "Perfect for developers while debugging :)\n\n" + + "GENERATION1 = 2\n" @@ -68,10 +73,31 @@ index 6aad419..0ab8cba 100644 + "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX\n\n" + + "GENERATION2 = 3\n" -+ "Re-orders the LA32 output bits as in later geneerations (personally confirmed on my CM-32L - KG).\n" ++ "Re-orders the LA32 output bits as in later generations (personally confirmed on my CM-32L - KG).\n" + "Bit order at DAC (where each number represents the original LA32 output bit number):\n" + "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14\n"); + ++ const char *mt32analogModes[] = {"0", "1", "2", "3", "auto",0}; ++ Pstring = secprop->Add_string("mt32.analog",Property::Changeable::WhenIdle,"auto"); ++ Pstring->Set_values(mt32analogModes); ++ Pstring->Set_help("MT-32 analogue output emulation mode\n" ++ "Digital = 0\n" ++ "Only digital path is emulated. The output samples correspond to the digital output signal appeared at the DAC entrance.\n" ++ "Fastest mode.\n\n" ++ ++ "Coarse = 1\n" ++ "Coarse emulation of LPF circuit. High frequencies are boosted, sample rate remains unchanged.\n" ++ "A bit better sounding but also a bit slower.\n\n" ++ ++ "Accurate = 2 - default\n" ++ "Finer emulation of LPF circuit. Output signal is upsampled to 48 kHz to allow emulation of audible mirror spectra above 16 kHz,\n" ++ "which is passed through the LPF circuit without significant attenuation.\n" ++ "Sounding is closer to the analog output from real hardware but also slower than the modes 0 and 1.\n\n" ++ ++ "Oversampled = 3\n" ++ "Same as the default mode 2 but the output signal is 2x oversampled, i.e. the output sample rate is 96 kHz.\n" ++ "Even slower than all the other modes but better retains highest frequencies while further resampled in DOSBox mixer.\n"); ++ + const char *mt32reverbModes[] = {"0", "1", "2", "3", "auto",0}; + Pstring = secprop->Add_string("mt32.reverb.mode",Property::Changeable::WhenIdle,"auto"); + Pstring->Set_values(mt32reverbModes); @@ -102,7 +128,7 @@ index 3fed5e6..ca36a07 100644 + midi_coremidi.h midi_mt32.h midi_mt32.cpp sdl_gui.cpp dosbox_splash.h diff --git a/src/gui/midi.cpp b/src/gui/midi.cpp -index ce4647f..dae3b7d 100644 +index ce4647f..3d289b8 100644 --- a/src/gui/midi.cpp +++ b/src/gui/midi.cpp @@ -91,6 +91,9 @@ MidiHandler Midi_none; @@ -110,24 +136,32 @@ index ce4647f..dae3b7d 100644 #endif +#include "midi_mt32.h" -+static MidiHandler_mt32 &midiHandler_mt32 = MidiHandler_mt32::GetInstance(); ++static MidiHandler_mt32 &Midi_mt32 = MidiHandler_mt32::GetInstance(); + DB_Midi midi; void MIDI_RawOutByte(Bit8u data) { diff --git a/src/gui/midi_mt32.cpp b/src/gui/midi_mt32.cpp new file mode 100644 -index 0000000..2f5d77b +index 0000000..ce71157 --- /dev/null +++ b/src/gui/midi_mt32.cpp -@@ -0,0 +1,228 @@ -+#include "midi_mt32.h" -+ +@@ -0,0 +1,248 @@ +#include +#include +#include +#include "control.h" + ++#ifndef DOSBOX_MIDI_H ++#include "midi.h" ++#endif ++ ++#include "midi_mt32.h" ++ ++static const Bitu MILLIS_PER_SECOND = 1000; ++static const Bitu MINIMUM_RENDER_MILLIS = 16; ++static const Bitu AUDIO_LATENCY_MILLIS = 2 * MINIMUM_RENDER_MILLIS; ++ +MidiHandler_mt32 &MidiHandler_mt32::GetInstance() { + static MidiHandler_mt32 midiHandler_mt32; + return midiHandler_mt32; @@ -190,8 +224,14 @@ index 0000000..2f5d77b + } + const MT32Emu::ROMImage *controlROMImage = MT32Emu::ROMImage::makeROMImage(&controlROMFile); + const MT32Emu::ROMImage *pcmROMImage = MT32Emu::ROMImage::makeROMImage(&pcmROMFile); ++ ++ MT32Emu::AnalogOutputMode analogOutputMode = MT32Emu::AnalogOutputMode_ACCURATE; ++ if (strcmp(section->Get_string("mt32.analog"), "auto") != 0) { ++ analogOutputMode = (MT32Emu::AnalogOutputMode)atoi(section->Get_string("mt32.analog")); ++ } ++ + synth = new MT32Emu::Synth(&reportHandler); -+ if (!synth->open(*controlROMImage, *pcmROMImage)) { ++ if (!synth->open(*controlROMImage, *pcmROMImage, analogOutputMode)) { + LOG_MSG("MT32: Error initialising emulation"); + return false; + } @@ -205,8 +245,6 @@ index 0000000..2f5d77b + reverbsysex[5] = (Bit8u)section->Get_int("mt32.reverb.level"); + synth->writeSysex(16, reverbsysex, 6); + synth->setReverbOverridden(true); -+ } else { -+ LOG_MSG("MT32: Using default reverb"); + } + + if (strcmp(section->Get_string("mt32.dac"), "auto") != 0) { @@ -217,12 +255,18 @@ index 0000000..2f5d77b + noise = strcmp(section->Get_string("mt32.verbose"), "on") == 0; + renderInThread = strcmp(section->Get_string("mt32.thread"), "on") == 0; + -+ playPos = 0; -+ chan = MIXER_AddChannel(mixerCallBack, MT32Emu::SAMPLE_RATE, "MT32"); ++ if (noise) LOG_MSG("MT32: Adding mixer channel at sample rate %d", synth->getStereoOutputSampleRate()); ++ chan = MIXER_AddChannel(mixerCallBack, synth->getStereoOutputSampleRate(), "MT32"); + if (renderInThread) { + stopProcessing = false; + renderPos = 0; -+ render(FRAMES_PER_AUDIO_BUFFER - 1, audioBuffer); ++ playPos = 0; ++ sampleRateRatio = MT32Emu::SAMPLE_RATE / (double)synth->getStereoOutputSampleRate(); ++ minimumRenderFrames = (MINIMUM_RENDER_MILLIS * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND; ++ framesPerAudioBuffer = (AUDIO_LATENCY_MILLIS * synth->getStereoOutputSampleRate()) / MILLIS_PER_SECOND; ++ audioBufferSize = framesPerAudioBuffer << 1; ++ audioBuffer = new Bit16s[audioBufferSize]; ++ render(framesPerAudioBuffer - 1, audioBuffer); + playedBuffers = 1; + thread = SDL_CreateThread(processingThread, NULL); + } @@ -239,6 +283,8 @@ index 0000000..2f5d77b + stopProcessing = true; + SDL_WaitThread(thread, NULL); + thread = NULL; ++ delete[] audioBuffer; ++ audioBuffer = NULL; + } + MIXER_DelChannel(chan); + chan = NULL; @@ -267,7 +313,7 @@ index 0000000..2f5d77b +void MidiHandler_mt32::render(const Bitu len, Bit16s *buf) { + Bit16s *revBuf = &buf[renderPos]; + synth->render(revBuf, len); -+ renderPos = (renderPos + (len << 1)) % AUDIO_BUFFER_SIZE; ++ renderPos = (renderPos + (len << 1)) % audioBufferSize; +} + +void MidiHandler_mt32::mixerCallBack(Bitu len) { @@ -280,7 +326,7 @@ index 0000000..2f5d77b + Bitu playPos = midiHandler_mt32.playPos; + Bitu samplesReady; + if (renderPos < playPos) { -+ samplesReady = AUDIO_BUFFER_SIZE - playPos; ++ samplesReady = midiHandler_mt32.audioBufferSize - playPos; + } else { + samplesReady = renderPos - playPos; + } @@ -289,8 +335,8 @@ index 0000000..2f5d77b + } + midiHandler_mt32.chan->AddSamples_s16(len, &midiHandler_mt32.audioBuffer[playPos]); + playPos += (len << 1); -+ while (AUDIO_BUFFER_SIZE <= playPos) { -+ playPos -= AUDIO_BUFFER_SIZE; ++ while (midiHandler_mt32.audioBufferSize <= playPos) { ++ playPos -= midiHandler_mt32.audioBufferSize; + midiHandler_mt32.playedBuffers++; + } + midiHandler_mt32.playPos = playPos; @@ -309,16 +355,16 @@ index 0000000..2f5d77b + Bitu framesToRender; + if (renderPos < playPos) { + framesToRender = (playPos - renderPos - 2) >> 1; -+ if (framesToRender < MINIMUM_RENDER_FRAMES) { -+ SDL_Delay(1 + (MILLIS_PER_SECOND * (MINIMUM_RENDER_FRAMES - framesToRender)) / SAMPLE_RATE); ++ if (framesToRender < midiHandler_mt32.minimumRenderFrames) { ++ SDL_Delay(1 + (MILLIS_PER_SECOND * (midiHandler_mt32.minimumRenderFrames - framesToRender)) / midiHandler_mt32.synth->getStereoOutputSampleRate()); + continue; + } + } else { -+ framesToRender = (AUDIO_BUFFER_SIZE - renderPos) >> 1; ++ framesToRender = (midiHandler_mt32.audioBufferSize - renderPos) >> 1; + if (playPos == 0) { + framesToRender--; + if (framesToRender == 0) { -+ SDL_Delay(1 + (MILLIS_PER_SECOND * MINIMUM_RENDER_FRAMES) / SAMPLE_RATE); ++ SDL_Delay(1 + (MILLIS_PER_SECOND * midiHandler_mt32.minimumRenderFrames) / midiHandler_mt32.synth->getStereoOutputSampleRate()); + continue; + } + } @@ -351,16 +397,15 @@ index 0000000..2f5d77b +} diff --git a/src/gui/midi_mt32.h b/src/gui/midi_mt32.h new file mode 100644 -index 0000000..e401666 +index 0000000..45b20cd --- /dev/null +++ b/src/gui/midi_mt32.h -@@ -0,0 +1,57 @@ +@@ -0,0 +1,54 @@ +#ifndef DOSBOX_MIDI_MT32_H +#define DOSBOX_MIDI_MT32_H + -+#include +#include "mixer.h" -+#include "midi.h" ++#include + +struct SDL_Thread; + @@ -375,16 +420,14 @@ index 0000000..e401666 + void PlaySysex(Bit8u *sysex, Bitu len); + +private: -+ static const Bitu SAMPLE_RATE = MT32Emu::SAMPLE_RATE; -+ static const Bitu MILLIS_PER_SECOND = 1000; -+ static const Bitu MINIMUM_RENDER_FRAMES = (16 * SAMPLE_RATE) / MILLIS_PER_SECOND; -+ static const Bitu AUDIO_BUFFER_SIZE = MIXER_BUFSIZE >> 1; -+ static const Bitu FRAMES_PER_AUDIO_BUFFER = AUDIO_BUFFER_SIZE >> 1; -+ + MixerChannel *chan; + MT32Emu::Synth *synth; + SDL_Thread *thread; -+ Bit16s audioBuffer[AUDIO_BUFFER_SIZE]; ++ Bit16s *audioBuffer; ++ Bitu audioBufferSize; ++ Bitu framesPerAudioBuffer; ++ Bitu minimumRenderFrames; ++ double sampleRateRatio; + volatile Bitu renderPos, playPos, playedBuffers; + volatile bool stopProcessing; + bool open, noise, renderInThread; @@ -407,7 +450,7 @@ index 0000000..e401666 + void render(const Bitu len, Bit16s *buf); + + Bit32u inline getMidiEventTimestamp() { -+ return playedBuffers * FRAMES_PER_AUDIO_BUFFER + (playPos >> 1); ++ return Bit32u((playedBuffers * framesPerAudioBuffer + (playPos >> 1)) * sampleRateRatio); + } +}; +