DOSBox-X
|
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 }