PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/DOSBox/src/gui/midi_mt32.h

https://bitbucket.org/alunbestor/boxer/
C Header | 314 lines | 259 code | 37 blank | 18 comment | 36 complexity | b131ec91124aa4baee24c4f0c34b7e79 MD5 | raw file
Possible License(s): GPL-3.0
  1. #include "MT32Emu/mt32emu.h"
  2. #include "mixer.h"
  3. #include "control.h"
  4. #include "SDL_thread.h"
  5. //--Added 2011-09-22 by Alun Bestor to allow Boxer to hook into MT-32 emulation.
  6. #import "BXCoalfaceAudio.h"
  7. //--End of modifications
  8. #include <iostream>
  9. #include <string>
  10. using namespace std;
  11. //#define MT32MULTICORE 1
  12. #ifdef MT32MULTICORE
  13. static inline void cpuID(unsigned i, unsigned regs[4]) {
  14. __asm__ __volatile__
  15. ("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]): "a" (i), "c" (0));
  16. // ECX is set to zero for CPUID function 4
  17. }
  18. static inline int cpu_check(void) {
  19. unsigned regs[4];
  20. char vendor[12];
  21. cpuID(0, regs);
  22. ((unsigned *)vendor)[0] = regs[1]; // EBX
  23. ((unsigned *)vendor)[1] = regs[3]; // EDX
  24. ((unsigned *)vendor)[2] = regs[2]; // ECX
  25. string cpuVendor = string(vendor, 12);
  26. // Get CPU features
  27. cpuID(1, regs);
  28. unsigned cpuFeatures = regs[3]; // EDX
  29. if (cpuVendor == "GenuineIntel" && cpuFeatures & (1 << 28)) { // HTT bit
  30. // Logical core count per CPU
  31. cpuID(1, regs); unsigned logical = (regs[1] >> 16) & 0xff; // EBX[23:16]
  32. unsigned cores = logical;
  33. if (cpuVendor == "GenuineIntel") {
  34. // Get DCP cache info
  35. cpuID(4, regs);
  36. cores = ((regs[0] >> 26) & 0x3f) + 1; // EAX[31:26] + 1
  37. } else if (cpuVendor == "AuthenticAMD") {
  38. // Get NC: Number of CPU cores - 1
  39. cpuID(0x80000008, regs);
  40. cores = ((unsigned)(regs[2] & 0xff)) + 1; // ECX[7:0] + 1
  41. } else return 1;
  42. return cores;
  43. }
  44. return 0;
  45. }
  46. #endif
  47. MT32Emu::Synth *_usesynth;
  48. MixerChannel *mt32chan = NULL;
  49. bool mt32ReverseStereo = false;
  50. struct mt32 {
  51. #ifdef MT32MULTICORE
  52. SDL_mutex * mutex;
  53. SDL_semaphore * sem;
  54. SDL_Thread * thread;
  55. bool multicore;
  56. #endif
  57. volatile bool running, busy;
  58. Bit8u len, play;
  59. Bit8u Temp[MIXER_BUFSIZE], msg[SYSEX_SIZE];
  60. };
  61. void ReverseStereo(Bitu len, Bit16s *buf) {
  62. for(Bitu i = 0; i < len; i++) {
  63. Bit16s left = *buf;
  64. Bit16s right = buf[1];
  65. *buf++ = right;
  66. *buf++ = left;
  67. }
  68. }
  69. #ifdef MT32MULTICORE
  70. static int MT32_Thread(void*) {
  71. SDL_LockMutex(mt32.mutex);
  72. while(mt32.running) {
  73. Bitu len;
  74. if(!(mt32.play) && !(mt32.len)) {
  75. SDL_UnlockMutex(mt32.mutex);
  76. SDL_SemWait(mt32.sem);
  77. SDL_LockMutex(mt32.mutex);
  78. }
  79. mt32.busy = true;
  80. while(mt32.play) {
  81. len = mt32.play;
  82. Bit32u *tmp = ((Bit32u*)mt32.msg);
  83. SDL_UnlockMutex(mt32.mutex);
  84. while(len--) {
  85. _usesynth->playMsg(*tmp++);
  86. }
  87. SDL_LockMutex(mt32.mutex);
  88. len = tmp-(Bit32u*)mt32.msg;
  89. mt32.play -= len;
  90. if(mt32.play) SDL_memmove(mt32.msg, tmp, mt32.play<<2);
  91. }
  92. while(mt32.len) {
  93. len = (mt32.len>>2) < MIXER_BUFSIZE ? mt32.len : MIXER_BUFSIZE<<2;
  94. mt32.len -= len;
  95. #ifdef MT32DEBUG
  96. if(mt32.len) LOG_MSG("MT32:WARNING: len left (%d)", mt32.len);
  97. #endif
  98. SDL_UnlockMutex(mt32.mutex);
  99. _usesynth->render((Bit16s *)mt32.Temp, len);
  100. if (mt32ReverseStereo) {
  101. ReverseStereo(len, (Bit16s *)MixTemp);
  102. }
  103. mt32chan->AddSamples_s16(len,(Bit16s *)mt32.Temp);
  104. SDL_LockMutex(mt32.mutex);
  105. break;
  106. }
  107. mt32.busy = false;
  108. }
  109. SDL_UnlockMutex(mt32.mutex);
  110. return 0;
  111. }
  112. #endif
  113. static void MT32_CallBack(Bitu len) {
  114. #ifdef MT32MULTICORE
  115. if(mt32.multicore) {
  116. SDL_LockMutex(mt32.mutex);
  117. mt32.len += len;
  118. SDL_UnlockMutex(mt32.mutex);
  119. SDL_SemPost(mt32.sem);
  120. } else {
  121. #endif
  122. _usesynth->render((Bit16s *)MixTemp, len);
  123. if (mt32ReverseStereo) {
  124. ReverseStereo(len, (Bit16s *)MixTemp);
  125. }
  126. mt32chan->AddSamples_s16(len,(Bit16s *)MixTemp);
  127. #ifdef MT32MULTICORE
  128. }
  129. #endif
  130. }
  131. static int report(void *userData, MT32Emu::ReportType type, const void *reportData) {
  132. switch(type) {
  133. case MT32Emu::ReportType_errorControlROM:
  134. LOG_MSG("MT32:Couldn't find control files");
  135. break;
  136. case MT32Emu::ReportType_errorPCMROM:
  137. LOG_MSG("MT32:Couldn't open MT32_PCM.ROM file");
  138. break;
  139. default:
  140. //LOG(LOG_ALL,LOG_NORMAL)("MT32: Report %d",type);
  141. break;
  142. }
  143. return 0;
  144. }
  145. class MidiHandler_mt32: public MidiHandler {
  146. private:
  147. MT32Emu::Synth *_synth;
  148. int _outputRate;
  149. bool isOpen;
  150. public:
  151. MidiHandler_mt32() : isOpen(false),MidiHandler() {};
  152. const char * GetName(void) { return "mt32";};
  153. bool Open(const char * conf) {
  154. MT32Emu::SynthProperties tmpProp;
  155. memset(&tmpProp, 0, sizeof(tmpProp));
  156. tmpProp.sampleRate = 32000;
  157. tmpProp.useDefaultReverb = false;
  158. tmpProp.useReverb = true;
  159. tmpProp.reverbType = 0;
  160. tmpProp.reverbTime = 5;
  161. tmpProp.reverbLevel = 3;
  162. tmpProp.userData = this;
  163. //tmpProp.printDebug = &vdebug;
  164. //--Modified 2011-09-22 by Alun Bestor to use Boxer's own callbacks instead.
  165. //tmpProp.report = &report;
  166. tmpProp.report = &boxer_reportMT32Message;
  167. tmpProp.openFile = &boxer_openMT32ROM;
  168. tmpProp.closeFile = &boxer_closeMT32ROM;
  169. tmpProp.printDebug = &boxer_logMT32DebugMessage;
  170. //--End of modifications
  171. _synth = new MT32Emu::Synth();
  172. if (_synth->open(tmpProp)==0) {
  173. LOG(LOG_ALL,LOG_ERROR)("MT32:Error initialising emulation");
  174. return false;
  175. }
  176. _usesynth=_synth;
  177. Section_prop* section=static_cast<Section_prop *>(control->GetSection("midi"));
  178. if(strcmp(section->Get_string("mt32reverb.mode"),"auto")) {
  179. Bit8u reverbsysex[] = {0x10, 0x00, 0x01, 0x00, 0x05, 0x03};
  180. reverbsysex[3] = (Bit8u)atoi(section->Get_string("mt32reverb.mode"));
  181. reverbsysex[4] = (Bit8u)section->Get_int("mt32reverb.time");
  182. reverbsysex[5] = (Bit8u)section->Get_int("mt32reverb.level");
  183. _synth->writeSysex(16, reverbsysex, 6);
  184. _synth->setReverbOverridden(true);
  185. } else {
  186. LOG_MSG("MT32:Using default reverb");
  187. }
  188. if(strcmp(section->Get_string("mt32DAC"),"auto")) {
  189. _synth->setDACInputMode((MT32Emu::DACInputMode)atoi(section->Get_string("mt32DAC")));
  190. }
  191. if(!strcmp(section->Get_string("mt32ReverseStereo"),"on")) {
  192. mt32ReverseStereo = true;
  193. } else {
  194. mt32ReverseStereo = false;
  195. }
  196. if (mt32chan == NULL)
  197. mt32chan=MIXER_AddChannel(MT32_CallBack,tmpProp.sampleRate,"MT32");
  198. mt32chan->Enable(true);
  199. /* Create MT32 thread */
  200. #ifdef MT32MULTICORE
  201. mt32.multicore=(cpu_check()>1?true:false);
  202. if(mt32.multicore) {
  203. mt32.mutex = SDL_CreateMutex();
  204. mt32.sem = SDL_CreateSemaphore(0);
  205. mt32.running = true;
  206. mt32.busy = false;
  207. mt32.play = 0;
  208. mt32.thread = SDL_CreateThread(MT32_Thread, NULL);
  209. }
  210. #endif
  211. return true;
  212. };
  213. void Close(void) {
  214. if (!isOpen) return;
  215. mt32chan->Enable(false);
  216. #ifdef MT32MULTICORE
  217. if(mt32.multicore) {
  218. mt32.running = false;
  219. SDL_SemPost(mt32.sem);
  220. SDL_WaitThread(mt32.thread, NULL);
  221. SDL_DestroyMutex(mt32.mutex);
  222. }
  223. #endif
  224. _synth->close();
  225. delete _synth;
  226. _synth = NULL;
  227. _usesynth=_synth;
  228. isOpen=false;
  229. };
  230. void PlayMsg(Bit8u * msg) {
  231. #ifdef MT32MULTICORE
  232. if(mt32.multicore) {
  233. // Try to queue play commands
  234. SDL_LockMutex(mt32.mutex);
  235. // Playcommand buffer full?
  236. while(!(mt32.play < SYSEX_SIZE>>2)) {
  237. SDL_UnlockMutex(mt32.mutex);
  238. LOG_MSG("MT32:Playback buffer full...");
  239. SDL_LockMutex(mt32.mutex);
  240. }
  241. SDL_memcpy(mt32.msg+(mt32.play<<2), msg, sizeof(Bit32u));
  242. mt32.play ++;
  243. SDL_UnlockMutex(mt32.mutex);
  244. SDL_SemPost(mt32.sem);
  245. } else {
  246. #endif
  247. //--Modified 2011-09-22 by Alun Bestor to fix endianness bug from byte-array casting.
  248. _synth->playMsg(boxer_MIDIMessageToLong(msg));
  249. //--End of modifications
  250. #ifdef MT32MULTICORE
  251. }
  252. #endif
  253. };
  254. void PlaySysex(Bit8u * sysex,Bitu len) {
  255. #ifdef MT32MULTICORE
  256. if(mt32.multicore) {
  257. SDL_LockMutex(mt32.mutex);
  258. while(mt32.busy) {
  259. SDL_UnlockMutex(mt32.mutex);
  260. #ifdef MT32DEBUG
  261. LOG_MSG("MT32:Waiting to deliver sysex");
  262. #endif
  263. SDL_LockMutex(mt32.mutex);
  264. }
  265. }
  266. #endif
  267. if(sysex[0] == 0xf0) {
  268. _synth->playSysex(sysex, len);
  269. } else {
  270. _synth->playSysexWithoutFraming(sysex, len);
  271. }
  272. #ifdef MT32MULTICORE
  273. if(mt32.multicore) SDL_UnlockMutex(mt32.mutex);
  274. #endif
  275. };
  276. };
  277. MidiHandler_mt32 Midi_mt32;