DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/gui/midi_mt32.h
00001 #include "mt32emu.h"
00002 #include <SDL_thread.h>
00003 #include <SDL_timer.h>
00004 #include "mixer.h"
00005 #include "control.h"
00006 
00007 class RingBuffer {
00008 private:
00009         static const unsigned int bufferSize = 1024;
00010         volatile unsigned int startpos;
00011         volatile unsigned int endpos;
00012     Bit64u ringBuffer[bufferSize] = {};
00013 
00014 public:
00015         RingBuffer() {
00016                 startpos = 0;
00017                 endpos = 0;
00018         }
00019 
00020         bool put(Bit32u data) {
00021                 unsigned int newEndpos = endpos;
00022                 newEndpos++;
00023                 if (newEndpos == bufferSize) newEndpos = 0;
00024                 if (startpos == newEndpos) return false;
00025                 ringBuffer[endpos] = data;
00026                 endpos = newEndpos;
00027                 return true;
00028         }
00029 
00030         Bit32u get() {
00031                 if (startpos == endpos) return 0;
00032                 Bit32u data = (Bit32u)ringBuffer[startpos]; /* <- FIXME: Um.... really? */
00033                 startpos++;
00034                 if (startpos == bufferSize) startpos = 0;
00035                 return data;
00036         }
00037         void reset() {
00038                 startpos = 0;
00039                 endpos = 0;
00040         }
00041 };
00042 
00043 static class MidiHandler_mt32 : public MidiHandler {
00044 private:
00045         static const Bitu MIXER_BUFFER_SIZE = MIXER_BUFSIZE >> 2;
00046         MixerChannel *chan;
00047         MT32Emu::Synth *synth;
00048         RingBuffer midiBuffer;
00049         SDL_Thread *thread;
00050         SDL_mutex *synthMutex;
00051         SDL_semaphore *procIdleSem, *mixerReqSem;
00052     Bit16s mixerBuffer[2 * MIXER_BUFFER_SIZE] = {};
00053         volatile Bitu mixerBufferSize = 0;
00054         volatile bool stopProcessing = false;
00055         bool open, noise = false, reverseStereo = false, renderInThread = false;
00056         Bit16s numPartials = 0;
00057 
00058         class MT32ReportHandler : public MT32Emu::ReportHandler {
00059         protected:
00060                 virtual void onErrorControlROM() {
00061                         LOG(LOG_MISC,LOG_WARN)("MT32: Couldn't open Control ROM file");
00062                 }
00063 
00064                 virtual void onErrorPCMROM() {
00065                         LOG(LOG_MISC,LOG_WARN)("MT32: Couldn't open PCM ROM file");
00066                 }
00067 
00068                 virtual void showLCDMessage(const char *message) {
00069                         LOG(LOG_MISC,LOG_DEBUG)("MT32: LCD-Message: %s", message);
00070                 }
00071 
00072                 virtual void printDebug(const char *fmt, va_list list);
00073         } reportHandler;
00074 
00075         static void mixerCallBack(Bitu len);
00076         static int processingThread(void *);
00077 
00078 public:
00079         MidiHandler_mt32() : chan(NULL), synth(NULL), thread(NULL), synthMutex(NULL), procIdleSem(NULL), mixerReqSem(NULL), open(false) {}
00080 
00081         ~MidiHandler_mt32() {
00082                 MidiHandler_mt32::Close();
00083         }
00084 
00085         const char *GetName(void) {
00086                 return "mt32";
00087         }
00088 
00089     void user_romhelp(void) {
00090         /* don't just grunt and say "ROM file not found", help the user get the ROMs in place so we can work! */
00091         /* we're now 25 years past the era of limited RAM and "error code -25" we have more than enough disk and RAM
00092          * to contain explanatory text where the user went wrong! */
00093         LOG_MSG("MT32 emulation cannot work without the PCM and CONTROL ROM files.");
00094         LOG_MSG("To eliminate this error message, either change mididevice= to something else, or");
00095         LOG_MSG("place the ROM files in what will be the \"current working directory\" for DOSBox-X");
00096         LOG_MSG("when it starts up and initializes MIDI emulation.");
00097         LOG_MSG("The ROM files are: CM32L_CONTROL.ROM, MT32_CONTROL.ROM, CM32L_PCM.ROM, MT32_PCM.ROM");
00098     }
00099 
00100         bool Open(const char *conf) {
00101         (void)conf;//UNUSED
00102 
00103                 MT32Emu::FileStream controlROMFile;
00104                 MT32Emu::FileStream pcmROMFile;
00105 
00106         /* mt32.romdir added from DOSBox ECE by request. This romdir code taken from DOSBox ECE */
00107         Section_prop *section = static_cast<Section_prop *>(control->GetSection("midi"));
00108         const char *romDir = section->Get_string("mt32.romdir");
00109         if (romDir == NULL) romDir = "./"; // Paranoid NULL-check, should never happen
00110         size_t romDirLen = strlen(romDir);
00111         bool addPathSeparator = false;
00112         if (romDirLen < 1) {
00113             romDir = "./";
00114         } else if (4080 < romDirLen) {
00115             LOG_MSG("MT32: mt32.romdir is too long, using the current dir.");
00116             romDir = "./";
00117         } else {
00118             char lastChar = romDir[strlen(romDir) - 1];
00119             addPathSeparator = lastChar != '/' && lastChar != '\\';
00120         }
00121 
00122         char pathName[4096];
00123 
00124         makeROMPathName(pathName, romDir, "CM32L_CONTROL.ROM", addPathSeparator);
00125         if (!controlROMFile.open(pathName)) {
00126             makeROMPathName(pathName, romDir, "MT32_CONTROL.ROM", addPathSeparator);
00127             if (!controlROMFile.open(pathName)) {
00128                 LOG(LOG_MISC,LOG_WARN)("MT32: Control ROM file not found");
00129                 user_romhelp();
00130                 return false;
00131             }
00132         }
00133         makeROMPathName(pathName, romDir, "CM32L_PCM.ROM", addPathSeparator);
00134         if (!pcmROMFile.open(pathName)) {
00135             makeROMPathName(pathName, romDir, "MT32_PCM.ROM", addPathSeparator);
00136             if (!pcmROMFile.open(pathName)) {
00137                 LOG(LOG_MISC,LOG_WARN)("MT32: PCM ROM file not found");
00138                 user_romhelp();
00139                 return false;
00140             }
00141         }
00142 
00143                 const MT32Emu::ROMImage *controlROMImage = MT32Emu::ROMImage::makeROMImage(&controlROMFile);
00144                 const MT32Emu::ROMImage *pcmROMImage = MT32Emu::ROMImage::makeROMImage(&pcmROMFile);
00145 
00146                 synth = new MT32Emu::Synth(&reportHandler);
00147 
00148                 /* NTS: Read and apply mt32.partials BEFORE opening the synth, because the
00149                         synth assumes an initial number of partials, and allocates PartialManager()
00150                         instances which in turn initialize THAT many partials. If we later
00151                         call setPartialLimit() with the (often lower) number we wanted, then
00152                         a memory leak will occur because the PartialManager() will only free
00153                         the new lower limit and leave the rest in memory. */
00154                 numPartials = section->Get_int("mt32.partials");
00155                 if(numPartials>MT32EMU_MAX_PARTIALS) numPartials=MT32EMU_MAX_PARTIALS;
00156                 synth->setPartialLimit((unsigned int)numPartials);
00157 
00158                 if (!synth->open(*controlROMImage, *pcmROMImage)) {
00159                         LOG(LOG_MISC,LOG_WARN)("MT32: Error initialising emulation");
00160                         return false;
00161                 }
00162 
00163                 if (strcmp(section->Get_string("mt32.reverb.mode"), "auto") != 0) {
00164                         Bit8u reverbsysex[] = {0x10, 0x00, 0x01, 0x00, 0x05, 0x03};
00165                         reverbsysex[3] = (Bit8u)atoi(section->Get_string("mt32.reverb.mode"));
00166                         reverbsysex[4] = (Bit8u)section->Get_int("mt32.reverb.time");
00167                         reverbsysex[5] = (Bit8u)section->Get_int("mt32.reverb.level");
00168                         synth->writeSysex(16, reverbsysex, 6);
00169                         synth->setReverbOverridden(true);
00170                 } else {
00171                         LOG(LOG_MISC,LOG_DEBUG)("MT32: Using default reverb");
00172                 }
00173 
00174                 if (strcmp(section->Get_string("mt32.dac"), "auto") != 0) {
00175                         synth->setDACInputMode((MT32Emu::DACInputMode)atoi(section->Get_string("mt32.dac")));
00176                         // PURE mode = 1/2 reverb output
00177                         if( atoi(section->Get_string("mt32.dac")) == 1 ) {
00178                                 synth->setReverbOutputGain( 0.68f / 2 );
00179                         }
00180                 }
00181 
00182                 reverseStereo = strcmp(section->Get_string("mt32.reverse.stereo"), "on") == 0;
00183                 noise = strcmp(section->Get_string("mt32.verbose"), "on") == 0;
00184                 renderInThread = strcmp(section->Get_string("mt32.thread"), "on") == 0;
00185 
00186                 chan = MIXER_AddChannel(mixerCallBack, MT32Emu::SAMPLE_RATE, "MT32");
00187                 if (renderInThread) {
00188                         mixerBufferSize = 0;
00189                         stopProcessing = false;
00190                         synthMutex = SDL_CreateMutex();
00191                         procIdleSem = SDL_CreateSemaphore(0);
00192                         mixerReqSem = SDL_CreateSemaphore(0);
00193 #if defined(C_SDL2)
00194                         thread = SDL_CreateThread(processingThread, "MT32", NULL);
00195 #else
00196                         thread = SDL_CreateThread(processingThread, NULL);
00197 #endif
00198                         //if (thread == NULL || synthMutex == NULL || sleepMutex == NULL) renderInThread = false;
00199                 }
00200                 chan->Enable(true);
00201 
00202                 open = true;
00203                 return true;
00204         }
00205 
00206         void Close(void) {
00207                 if (!open) return;
00208                 chan->Enable(false);
00209                 if (renderInThread) {
00210                         stopProcessing = true;
00211                         SDL_SemPost(mixerReqSem);
00212                         SDL_WaitThread(thread, NULL);
00213                         thread = NULL;
00214                         SDL_DestroyMutex(synthMutex);
00215                         synthMutex = NULL;
00216                         SDL_DestroySemaphore(procIdleSem);
00217                         procIdleSem = NULL;
00218                         SDL_DestroySemaphore(mixerReqSem);
00219                         mixerReqSem = NULL;
00220                 }
00221                 MIXER_DelChannel(chan);
00222                 chan = NULL;
00223                 synth->close();
00224                 delete synth;
00225                 synth = NULL;
00226                 open = false;
00227         }
00228 
00229         void PlayMsg(Bit8u *msg) {
00230                 //if (!midiBuffer.put(*(Bit32u *)msg | (Bit64u(playPos + AUDIO_BUFFER_SIZE) << 32))) LOG_MSG("MT32: Playback buffer full!");
00231                 if (!midiBuffer.put(*(Bit32u *)msg)) { } //LOG_MSG("MT32: Playback buffer full!");
00232         }
00233 
00234         void PlaySysex(Bit8u *sysex, Bitu len) {
00235                 if (renderInThread) SDL_LockMutex(synthMutex);
00236                 synth->playSysex(sysex, (Bit32u)len);
00237                 if (renderInThread) SDL_UnlockMutex(synthMutex);
00238         }
00239 
00240         MT32Emu::Synth* GetSynth() { return synth; }
00241 
00242         void Reset() {
00243                 midiBuffer.reset();
00244 
00245                 if (renderInThread) SDL_LockMutex(synthMutex);
00246                 mixerBufferSize = 0;
00247                 if (renderInThread) SDL_UnlockMutex(synthMutex);
00248         }
00249 
00250         static void makeROMPathName(char pathName[], const char romDir[], const char fileName[], bool addPathSeparator);
00251 
00252 private:
00253         void render(Bitu len, Bit16s *buf) {
00254                 Bit32u msg = midiBuffer.get();
00255                 if (msg != 0) synth->playMsg(msg);
00256                 synth->render(buf, (Bit32u)len);
00257                 if (reverseStereo) {
00258                         Bit16s *revBuf = buf;
00259                         for(Bitu i = 0; i < len; i++) {
00260                                 Bit16s left = revBuf[0];
00261                                 Bit16s right = revBuf[1];
00262                                 *revBuf++ = right;
00263                                 *revBuf++ = left;
00264                         }
00265                 }
00266                 chan->AddSamples_s16(len, buf);
00267         }
00268 } midiHandler_mt32;
00269 
00270 void MidiHandler_mt32::makeROMPathName(char pathName[], const char romDir[], const char fileName[], bool addPathSeparator) {
00271         strcpy(pathName, romDir);
00272         if (addPathSeparator) {
00273                 strcat(pathName, "/");
00274         }
00275         strcat(pathName, fileName);
00276 }
00277 
00278 void MidiHandler_mt32::MT32ReportHandler::printDebug(const char *fmt, va_list list) {
00279         if (midiHandler_mt32.noise) {
00280                 char s[1024];
00281                 strcpy(s, "MT32: ");
00282                 vsnprintf(s + 6, 1017, fmt, list);
00283                 LOG_MSG("%s", s);
00284         }
00285 }
00286 
00287 void MidiHandler_mt32::mixerCallBack(Bitu len) {
00288         if (midiHandler_mt32.renderInThread) {
00289                 SDL_SemWait(midiHandler_mt32.procIdleSem);
00290                 midiHandler_mt32.mixerBufferSize += len;
00291                 SDL_SemPost(midiHandler_mt32.mixerReqSem);
00292         } else {
00293                 midiHandler_mt32.render(len, (Bit16s *)MixTemp);
00294         }
00295 }
00296 
00297 int MidiHandler_mt32::processingThread(void *) {
00298         while (!midiHandler_mt32.stopProcessing) {
00299                 SDL_SemPost(midiHandler_mt32.procIdleSem);
00300                 SDL_SemWait(midiHandler_mt32.mixerReqSem);
00301                 for (;;) {
00302                         Bitu samplesToRender = midiHandler_mt32.mixerBufferSize;
00303                         if (samplesToRender == 0) break;
00304                         SDL_LockMutex(midiHandler_mt32.synthMutex);
00305                         midiHandler_mt32.render(samplesToRender, midiHandler_mt32.mixerBuffer);
00306                         SDL_UnlockMutex(midiHandler_mt32.synthMutex);
00307                         midiHandler_mt32.mixerBufferSize -= samplesToRender;
00308                 }
00309         }
00310         return 0;
00311 }