1 #include <SDL.h>
2 
3 #include "Xmi.h"
4 #include "MusicDevice.h"
5 #include "Prefs.h"
6 
7 unsigned int NumTracks;
8 char ChannelThread[16]; //16 device channels
9 int NumUsedChannels; //number of in-use device channels
10 
11 MIDI_EVENT **TrackEvents;
12 short *TrackTiming;
13 unsigned short *TrackUsedChannels;
14 
15 MIDI_EVENT *ThreadEventList[NUM_THREADS];
16 int ThreadTiming[NUM_THREADS];
17 char ThreadChannelRemap[16*NUM_THREADS];
18 SDL_atomic_t DeviceChannelVolume[16]; //only msb: 0-127
19 SDL_atomic_t ThreadPlaying[NUM_THREADS];
20 SDL_atomic_t ThreadCommand[NUM_THREADS];
21 
22 MusicDevice *MusicDev;
23 static SDL_mutex *MyMutex;
24 
MusicCallback(void * userdata,Uint8 * stream,int len)25 void MusicCallback(void *userdata, Uint8 *stream, int len)
26 {
27   MusicDevice *dev;
28 
29   SDL_LockMutex(MyMutex);
30   dev = *(MusicDevice **)userdata;
31   if (!dev || !dev->isOpen)
32   {
33     SDL_UnlockMutex(MyMutex);
34     return;
35   }
36 
37   SDL_memset(stream, 0, (size_t)len); // in case we don't get anything
38   dev->generate(dev, (short*)((void*)stream), len / (int)(2 * sizeof(short)));
39   SDL_UnlockMutex(MyMutex);
40 }
41 
42 
43 
FreeXMI(void)44 void FreeXMI(void)
45 {
46   unsigned int track;
47   MIDI_EVENT *event, *next;
48 
49   for (track=0; track<NumTracks; track++)
50   {
51     event = TrackEvents[track];
52     while (event)
53     {
54       next = event->next;
55       if (event->buffer) free(event->buffer);
56       free(event);
57       event = next;
58     }
59   }
60 
61   if (TrackEvents) {free(TrackEvents); TrackEvents = 0;}
62   if (TrackTiming) {free(TrackTiming); TrackTiming = 0;}
63   if (TrackUsedChannels) {free(TrackUsedChannels); TrackUsedChannels = 0;}
64 
65   NumTracks = 0;
66 }
67 
68 
69 
NewMIDIEvent(MIDI_EVENT ** eventlist,MIDI_EVENT * curevent,int time)70 MIDI_EVENT *NewMIDIEvent(MIDI_EVENT **eventlist, MIDI_EVENT *curevent, int time)
71 {
72   if (*eventlist == 0)
73   {
74     *eventlist = curevent = (MIDI_EVENT *)malloc(sizeof(MIDI_EVENT));
75 
76     curevent->next = 0;
77 
78     if (time < 0) curevent->time = 0; else curevent->time = time;
79     curevent->buffer = 0;
80     curevent->len = 0;
81 
82     return curevent;
83   }
84 
85   if (time < 0)
86   {
87     MIDI_EVENT *event = (MIDI_EVENT *)malloc(sizeof(MIDI_EVENT));
88 
89     event->next = *eventlist;
90     *eventlist = curevent = event;
91 
92     curevent->time = 0;
93     curevent->buffer = 0;
94     curevent->len = 0;
95 
96     return curevent;
97   }
98 
99   if (curevent->time > time) curevent = *eventlist;
100 
101   while (curevent->next)
102   {
103     if (curevent->next->time > time)
104     {
105       MIDI_EVENT *event = (MIDI_EVENT *)malloc(sizeof(MIDI_EVENT));
106 
107       event->next = curevent->next;
108       curevent->next = event;
109       curevent = event;
110 
111       curevent->time = time;
112       curevent->buffer = 0;
113       curevent->len = 0;
114 
115       return curevent;
116     }
117 
118     curevent = curevent->next;
119   }
120 
121   curevent->next = (MIDI_EVENT *)malloc(sizeof(MIDI_EVENT));
122 
123   curevent = curevent->next;
124   curevent->next = 0;
125 
126   curevent->time = time;
127   curevent->buffer = 0;
128   curevent->len = 0;
129 
130   return curevent;
131 }
132 
133 
134 
ReadXMI(const char * filename)135 int ReadXMI(const char *filename)
136 {
137   FILE *f;
138   int size, start, begin, pos, time, end, tempo, tempo_set;
139   unsigned int i, count, len, chunk_len, quant, status, delta, b0, b1, b2, b3;
140   unsigned char *data, *p;
141   short ppqn;
142   unsigned short used_channels;
143   MIDI_EVENT *eventlist, *curevent, *prev;
144   char buf[32];
145   MusicMode mode = Music_GeneralMidi;
146 
147   INFO("Reading XMI %s", filename);
148 
149   extern FILE *fopen_caseless(const char *path, const char *mode); //see caseless.c
150   f = fopen_caseless(filename, "rb");
151   if (f == 0) {
152   	ERROR("Could not read XMI");
153   	return 0;
154   }
155 
156   fseek(f, 0, SEEK_END);
157   size = ftell(f);
158   fseek(f, 0, SEEK_SET);
159   data = (unsigned char *)malloc(size);
160   if (fread(data, size, 1, f) != 1) {free(data); fclose(f); return 0;}
161   fclose(f);
162 
163   p = data;
164 
165   memcpy(buf, p, 4); p += 4;
166   if (memcmp(buf, "FORM", 4)) {free(data); return 0;} //is not an xmi
167 
168   b3 = *p++; b2 = *p++; b1 = *p++; b0 = *p++;
169   len = b0 | (b1<<8) | (b2<<16) | (b3<<24);
170 
171   start = p - data;
172 
173   memcpy(buf, p, 4); p += 4;
174   if (!memcmp(buf, "XMID", 4)) NumTracks = 1; //XMI doesn't have XDIR, so there's only one track
175   else if (memcmp(buf, "XDIR", 4)) {free(data); return 0;} //invalid XMI format
176   else
177   {
178     NumTracks = 0;
179 
180     for (i=4; i<len; i++)
181     {
182       memcpy(buf, p, 4); p += 4;
183 
184       b3 = *p++; b2 = *p++; b1 = *p++; b0 = *p++;
185       chunk_len = b0 | (b1<<8) | (b2<<16) | (b3<<24);
186 
187       i += 8;
188 
189       if (memcmp(buf, "INFO", 4))
190       {
191         p += ((chunk_len+1) & ~1);
192         i += ((chunk_len+1) & ~1);
193         continue;
194       }
195 
196       if (chunk_len < 2) break;
197 
198       b0 = *p++; b1 = *p++;
199       NumTracks = b0 | (b1 << 8);
200 
201       break;
202     }
203 
204     if (NumTracks == 0) {free(data); return 0;} //xmi must have at least one track
205 
206     p = data + start + ((len+1) & ~1);
207 
208     memcpy(buf, p, 4); p += 4;
209     if (memcmp(buf, "CAT ", 4)) {free(data); return 0;} //invalid XMI format
210 
211     b3 = *p++; b2 = *p++; b1 = *p++; b0 = *p++;
212     len = b0 | (b1<<8) | (b2<<16) | (b3<<24);
213 
214     memcpy(buf, p, 4); p += 4;
215     if (memcmp(buf, "XMID", 4)) {free(data); return 0;} //invalid XMI format
216   }
217 
218   INFO("NumTracks: %i", NumTracks);
219 
220   TrackEvents = (MIDI_EVENT **)malloc(NumTracks * sizeof(MIDI_EVENT *));
221   TrackTiming = (short *)malloc(NumTracks * sizeof(short));
222   TrackUsedChannels = (unsigned short *)malloc(NumTracks * sizeof(unsigned short));
223 
224 
225   for (i=0; i<NumTracks; i++) TrackEvents[i] = 0;
226 
227   count = 0;
228 
229   while (p - data < size && count != NumTracks)
230   {
231     memcpy(buf, p, 4); p += 4;
232 
233     b3 = *p++; b2 = *p++; b1 = *p++; b0 = *p++;
234     len = b0 | (b1<<8) | (b2<<16) | (b3<<24);
235 
236     if (!memcmp(buf, "FORM", 4))
237     {
238       p += 4;
239 
240       memcpy(buf, p, 4); p += 4;
241 
242       b3 = *p++; b2 = *p++; b1 = *p++; b0 = *p++;
243       len = b0 | (b1<<8) | (b2<<16) | (b3<<24);
244     }
245 
246     if (memcmp(buf, "EVNT", 4))
247     {
248       p += ((len+1) & ~1);
249       continue;
250     }
251 
252     eventlist = 0;
253     curevent = 0;
254     time = 0;
255     end = 0;
256     tempo = 500000;
257     tempo_set = 0;
258     status = 0;
259     used_channels = 0;
260 
261     begin = p - data;
262 
263     while (!end && p - data < size)
264     {
265       quant = 0; for (i=0; i<4; i++) {b0 = *p++; if (b0 & 0x80) {p--; break;} quant += b0;}
266       time += quant*3;
267 
268       status = *p++;
269       switch (status >> 4)
270       {
271         case 0x9: //note on
272           used_channels |= (1 << (status & 15));
273           b0 = *p++;
274           curevent = NewMIDIEvent(&eventlist, curevent, time);
275           curevent->status = status;
276           curevent->data[0] = b0;
277           curevent->data[1] = *p++;
278           delta = 0; for (i=0; i<4; i++) {b1 = *p++; delta <<= 7; delta |= b1 & 0x7F; if (!(b1 & 0x80)) break;}
279           prev = curevent;
280           curevent = NewMIDIEvent(&eventlist, curevent, time + delta*3);
281           curevent->status = status;
282           curevent->data[0] = b0;
283           curevent->data[1] = 0;
284           curevent = prev;
285         break;
286 
287         case 0x8: case 0xA: case 0xB: case 0xE: //note off, aftertouch, controller, pitch wheel
288           used_channels |= (1 << (status & 15));
289           curevent = NewMIDIEvent(&eventlist, curevent, time);
290           curevent->status = status;
291           curevent->data[0] = *p++;
292           curevent->data[1] = *p++;
293         break;
294 
295         case 0xC: case 0xD: //program change, pressure
296           used_channels |= (1 << (status & 15));
297           curevent = NewMIDIEvent(&eventlist, curevent, time);
298           curevent->status = status;
299           curevent->data[0] = *p++;
300         break;
301 
302         case 0xF: //sysex
303           if (status == 0xFF)
304           {
305             pos = p - data;
306             b0 = *p++;
307             if (b0 == 0x2F) end = 1;
308             else if (b0 == 0x51 && !tempo_set)
309             {
310               p++; b3 = *p++; b2 = *p++; b1 = *p++;
311               tempo = (b1 | (b2 << 8) | (b3 << 16)) * 3;
312               tempo_set = 1;
313             }
314             else if (b0 == 0x51 && tempo_set)
315             {
316               quant = 0; for (i=0; i<4; i++) {b1 = *p++; quant <<= 7; quant |= b1 & 0x7F; if (!(b1 & 0x80)) break;}
317               p += quant;
318               break;
319             }
320             p = data + pos;
321           }
322           curevent = NewMIDIEvent(&eventlist, curevent, time);
323           curevent->status = status;
324           if (status == 0xFF) curevent->data[0] = *p++;
325           quant = 0; for (i=0; i<4; i++) {b0 = *p++; quant <<= 7; quant |= b0 & 0x7F; if (!(b0 & 0x80)) break;}
326           curevent->len = quant;
327           if (curevent->len)
328           {
329             curevent->buffer = (unsigned char *)malloc(curevent->len);
330             memcpy(curevent->buffer, p, curevent->len);
331             p += curevent->len;
332           }
333         break;
334 
335         default: break;
336       }
337     }
338 
339     ppqn = (tempo * 3) / 25000;
340     if (!ppqn) break; //unable to convert data
341 
342     TrackEvents[count] = eventlist;
343     TrackTiming[count] = ppqn;
344     TrackUsedChannels[count] = used_channels;
345     count++;
346 
347     p = data + begin + ((len+1) & ~1);
348   }
349 
350   if (count != NumTracks) //failed to extract all tracks from XMI
351   {
352     free(data);
353     NumTracks = count;
354     FreeXMI();
355     return 0;
356   }
357 
358 
359   free(data);
360 
361 
362   //Setup a sound bank for res/sound/sblaster, or res/sound/genmidi
363   if (strstr(filename, "sblaster") != NULL) mode = Music_SoundBlaster;
364   SDL_LockMutex(MyMutex);
365   if (MusicDev)
366   {
367     MusicDev->setupMode(MusicDev, mode);
368   }
369   SDL_UnlockMutex(MyMutex);
370   switch (mode)
371   {
372     case Music_GeneralMidi:  INFO("Set General MIDI mode");  break;
373     case Music_SoundBlaster: INFO("Set Sound Blaster mode"); break;
374   }
375 
376   return 1; //success
377 }
378 
379 
380 
MyThread(void * arg)381 int MyThread(void *arg)
382 {
383   int i;
384 
385   MIDI_EVENT *event[NUM_THREADS];
386   int ppqn[NUM_THREADS];
387   double Ippqn[NUM_THREADS];
388   int tempo[NUM_THREADS];
389   double tick[NUM_THREADS];
390   double last_tick[NUM_THREADS];
391   double last_time[NUM_THREADS];
392   unsigned int start[NUM_THREADS];
393 
394   for (i=0; i<NUM_THREADS; i++)
395   {
396     event[i] = 0;
397     ppqn[i] = 1;
398     Ippqn[i] = 1;
399     tempo[i] = 0x07A120;
400     tick[i] = 1;
401     last_tick[i] = 0;
402     last_time[i] = 0;
403     start[i] = 0;
404 
405     SDL_AtomicSet(&ThreadPlaying[i], 0);
406     SDL_AtomicSet(&ThreadCommand[i], THREAD_READY);
407   }
408 
409   for (;;)
410   {
411     int delay = 1;
412 
413     for (i=0; i<NUM_THREADS; i++)
414     {
415       if (event[i] && SDL_AtomicGet(&ThreadCommand[i]) != THREAD_STOPTRACK)
416       {
417         double aim = last_time[i] + (event[i]->time - last_tick[i]) * tick[i];
418         double diff = aim - ((SDL_GetTicks() - start[i]) * 1000.0);
419 
420         if (diff > 0)
421         {
422           if (diff < 1200.0) delay = 0;
423           continue;
424         }
425         delay = 0;
426 
427         last_tick[i] = event[i]->time;
428         last_time[i] = aim;
429 
430         if (event[i]->status == 0xFF && event[i]->data[0] == 0x51) //tempo change
431         {
432           tempo[i] = (event[i]->buffer[0] << 16) | (event[i]->buffer[1] << 8) | event[i]->buffer[2];
433           tick[i] = tempo[i] * Ippqn[i];
434         }
435         else if (event[i]->status >= 0x80 && event[i]->status < 0xF0)
436         {
437           int channel = (event[i]->status & 15);
438 
439           if (channel != 9) channel = ThreadChannelRemap[channel+16*i]; //remap channel, except 9 (percussion)
440 
441           uint8_t p1 = event[i]->data[0];
442           uint8_t p2 = event[i]->data[1];
443 
444           if ((event[i]->status & ~15) == 0xB0 && event[i]->data[0] == 0x07)
445           {
446             //set volume msb
447             //store in array in case global volume changes later
448             SDL_AtomicSet(&DeviceChannelVolume[channel], p2); // 0-127
449             //send volume change to device
450             SDL_LockMutex(MyMutex);
451             if (MusicDev && MusicDev->isOpen)
452             {
453                 // scale new volume according to global music volume
454                 extern uchar curr_vol_lev; // 0-100
455                 const int scaledVolume = ((int)p2 * (int)curr_vol_lev) / 100;
456                 MusicDev->sendControllerChange(MusicDev, channel, p1, scaledVolume);
457             }
458             SDL_UnlockMutex(MyMutex);
459           }
460           else
461           {
462             SDL_LockMutex(MyMutex);
463             if (MusicDev && MusicDev->isOpen)
464             {
465                 switch (event[i]->status & ~15)
466                 {
467                   case 0x80: MusicDev->sendNoteOff(MusicDev, channel, p1, p2); break;
468                   case 0x90: MusicDev->sendNoteOn(MusicDev, channel, p1, p2); break;
469                   case 0xA0: MusicDev->sendNoteAfterTouch(MusicDev, channel, p1, p2); break;
470                   case 0xB0: MusicDev->sendControllerChange(MusicDev, channel, p1, p2); break;
471                   case 0xC0: MusicDev->sendProgramChange(MusicDev, channel, p1); break;
472                   case 0xD0: MusicDev->sendChannelAfterTouch(MusicDev, channel, p1); break;
473                   case 0xE0: MusicDev->sendPitchBendML(MusicDev, channel, p2, p1); break;
474                 }
475             }
476             SDL_UnlockMutex(MyMutex);
477           }
478         }
479 
480         event[i] = event[i]->next;
481         if (event[i] == 0) SDL_AtomicSet(&ThreadCommand[i], THREAD_STOPTRACK);
482       }
483 
484       if (SDL_AtomicGet(&ThreadCommand[i]) == THREAD_STOPTRACK)
485       {
486         int channel;
487 
488         delay = 0;
489 
490         event[i] = 0;
491         last_tick[i] = 0;
492         last_time[i] = 0;
493         start[i] = SDL_GetTicks();
494 
495         //here we should turn off all notes for these channels plus 9 (percussion)
496         for (channel=0; channel<16; channel++) if (ChannelThread[channel] == i)
497         {
498           ChannelThread[channel] = -1;
499           NumUsedChannels--;
500           SDL_AtomicSet(&DeviceChannelVolume[channel], 0);
501         }
502 
503         SDL_AtomicSet(&ThreadPlaying[i], 0);
504         SDL_AtomicSet(&ThreadCommand[i], THREAD_READY);
505       }
506 
507 
508       if (SDL_AtomicGet(&ThreadCommand[i]) == THREAD_PLAYTRACK)
509       {
510         delay = 0;
511 
512         event[i] = ThreadEventList[i];
513         ppqn[i] = ThreadTiming[i];
514         Ippqn[i] = 1.0 / ppqn[i];
515         tempo[i] = 0x07A120;
516         tick[i] = tempo[i] * Ippqn[i];
517         last_tick[i] = 0;
518         last_time[i] = 0;
519         start[i] = SDL_GetTicks();
520 
521         SDL_AtomicSet(&ThreadPlaying[i], 1);
522         SDL_AtomicSet(&ThreadCommand[i], THREAD_READY);
523       }
524 
525 
526       if (SDL_AtomicGet(&ThreadCommand[i]) == THREAD_EXIT) return 0;
527     }
528 
529     SDL_Delay(delay);
530   }
531 
532   return 0;
533 }
534 
535 
536 
GetTrackNumChannels(unsigned int track)537 int GetTrackNumChannels(unsigned int track)
538 {
539   int num = 0, channel;
540 
541   //count channels used by track (could be zero if only percussion channel (9) is used)
542   for (channel=0; channel<16; channel++) if (channel != 9 && (TrackUsedChannels[track] & (1 << channel))) num++;
543 
544   return num;
545 }
546 
547 
548 
StartTrack(int thread,unsigned int track)549 void StartTrack(int thread, unsigned int track)
550 {
551   int num, trackChannel, deviceChannel;
552   char channel_remap[16];
553 
554   if (track >= NumTracks) return;
555 
556   num = GetTrackNumChannels(track);
557 
558   while (SDL_AtomicGet(&ThreadCommand[thread]) != THREAD_READY) SDL_Delay(1);
559 
560   //check if enough device channels free; 16 channels available except one (percussion)
561   if (NumUsedChannels + num <= 16-1)
562   {
563     NumUsedChannels += num;
564 
565     memset(channel_remap, 0, 16);
566 
567     //assign channels used by track to device channels that are currently free
568     for (trackChannel=0; trackChannel<16; trackChannel++)
569     {
570       // only map used, non-percussion channels
571       if (trackChannel != 9 && (TrackUsedChannels[track] & (1 << trackChannel)))
572       {
573         // find first unassigned device channel
574         for (deviceChannel=0; deviceChannel<16; deviceChannel++)
575         {
576           if (deviceChannel != 9 && ChannelThread[deviceChannel] == -1) break;
577         }
578         channel_remap[trackChannel] = deviceChannel;
579         ChannelThread[deviceChannel] = thread;
580         // default to full volume
581         SDL_AtomicSet(&DeviceChannelVolume[deviceChannel], 127);
582       }
583     }
584 
585     ThreadEventList[thread] = TrackEvents[track];
586     ThreadTiming[thread] = TrackTiming[track];
587     memcpy(ThreadChannelRemap+16*thread, channel_remap, 16);
588 
589     SDL_AtomicSet(&ThreadCommand[thread], THREAD_PLAYTRACK);
590 
591     while (SDL_AtomicGet(&ThreadCommand[thread]) != THREAD_READY) SDL_Delay(1);
592   }
593 }
594 
595 
596 
StopTrack(int i)597 void StopTrack(int i)
598 {
599   if (!SDL_AtomicGet(&ThreadPlaying[i])) return;
600 
601   while (SDL_AtomicGet(&ThreadCommand[i]) != THREAD_READY) SDL_Delay(1);
602 
603   SDL_AtomicSet(&ThreadCommand[i], THREAD_STOPTRACK);
604 
605   while (SDL_AtomicGet(&ThreadCommand[i]) != THREAD_READY) SDL_Delay(1);
606 }
607 
608 
609 
StopTheMusic(void)610 void StopTheMusic(void)
611 {
612   int i;
613 
614   for (i=0; i<NUM_THREADS; i++) StopTrack(i);
615 
616   SDL_LockMutex(MyMutex);
617   if (MusicDev)
618   {
619     MusicDev->reset(MusicDev);
620   }
621   SDL_UnlockMutex(MyMutex);
622 }
623 
624 
625 
IsPlaying(int i)626 int IsPlaying(int i)
627 {
628   return SDL_AtomicGet(&ThreadPlaying[i]);
629 }
630 
631 
632 
InitReadXMI(void)633 void InitReadXMI(void)
634 {
635   int channel, i;
636   SDL_Thread *thread;
637 
638   InitDecXMI();
639 
640   MyMutex = SDL_CreateMutex();
641 
642   for (channel=0; channel<16; channel++)
643   {
644     ChannelThread[channel] = -1;
645     SDL_AtomicSet(&DeviceChannelVolume[channel], 0);
646   }
647 
648   for (i=0; i<NUM_THREADS; i++)
649   {
650     SDL_AtomicSet(&ThreadPlaying[i], 0);
651     SDL_AtomicSet(&ThreadCommand[i], THREAD_INIT);
652   }
653 
654   thread = SDL_CreateThread(MyThread, "MyThread", NULL);
655   SDL_DetachThread(thread); //thread will go away on its own upon completion
656 
657   i = 0;
658   while (SDL_AtomicGet(&ThreadCommand[i]) == THREAD_INIT) SDL_Delay(1);
659 
660   atexit(ShutdownReadXMI);
661 }
662 
663 
664 
InitDecXMI(void)665 void InitDecXMI(void)
666 {
667   SDL_LockMutex(MyMutex);
668   if (MusicDev)
669   {
670     WARN("InitDecXMI(): *****WARNING***** Creating new music device, but one already exists!");
671   }
672 
673   // Start the Midi device
674   MusicDevice *musicdev = NULL;
675   int musicrate = 48000;
676 
677   switch (gShockPrefs.soMidiBackend)
678   {
679     case OPT_SEQ_ADLMIDI: // adlmidi
680     {
681       INFO("Creating ADLMIDI device");
682       musicdev = CreateMusicDevice(Music_AdlMidi);
683     }
684     break;
685     case OPT_SEQ_NativeMI: // native midi
686     {
687       INFO("Creating native MIDI device");
688       musicdev = CreateMusicDevice(Music_Native);
689     }
690     break;
691 #ifdef USE_FLUIDSYNTH
692     case OPT_SEQ_FluidSyn: // fluidsynth
693     {
694       INFO("Creating FluidSynth MIDI device");
695       musicdev = CreateMusicDevice(Music_FluidSynth);
696     }
697     break;
698 #endif
699   }
700 
701   // init chosen music device
702   INFO("Opening MIDI device using output %d", gShockPrefs.soMidiOutput);
703   if (musicdev && musicdev->init(musicdev, gShockPrefs.soMidiOutput, musicrate) != 0)
704   {
705     musicdev->destroy(musicdev);
706     musicdev = NULL;
707   }
708 
709   // fallback to dummy
710   if (!musicdev)
711   {
712     WARN("Using dummy MIDI driver");
713     musicdev = CreateMusicDevice(Music_None);
714     if (musicdev)
715     {
716       musicdev->init(musicdev, gShockPrefs.soMidiOutput, musicrate);
717     }
718   }
719 
720   // force prefs to align with music device output
721   if (musicdev)
722   {
723     gShockPrefs.soMidiOutput = musicdev->outputIndex;
724   }
725 
726   MusicDev = musicdev;
727   SDL_UnlockMutex(MyMutex);
728 }
729 
730 
731 
ReloadDecXMI(void)732 void ReloadDecXMI(void)
733 {
734   int i;
735 
736   // determine whether a device type change is being requested, by comparing the
737   //  current device type (if any) with current preferences setting
738   short deviceTypeMatch = 0;
739   SDL_LockMutex(MyMutex);
740   if (MusicDev)
741   {
742     switch (MusicDev->deviceType)
743     {
744       case Music_None:       deviceTypeMatch = 0;                                break;
745       case Music_AdlMidi:    deviceTypeMatch = (gShockPrefs.soMidiBackend == 0); break;
746       case Music_Native:     deviceTypeMatch = (gShockPrefs.soMidiBackend == 1); break;
747 #ifdef USE_FLUIDSYNTH
748       case Music_FluidSynth: deviceTypeMatch = (gShockPrefs.soMidiBackend == 2); break;
749 #endif
750     }
751   }
752 
753   // only destroy the device if it exists and any of the following apply:
754   // - it hasn't been opened yet
755   // - device type change requested
756   // - device output change requested
757   // this is needed to protect against reload spam generated by the UI slider
758   if (MusicDev &&
759       (!MusicDev->isOpen ||
760        !deviceTypeMatch ||
761        MusicDev->outputIndex != gShockPrefs.soMidiOutput))
762   {
763     SDL_UnlockMutex(MyMutex);
764     INFO("Closing MIDI driver due to reload");
765 
766     for (i=0; i<NUM_THREADS; i++)
767     {
768       StopTrack(i);
769     }
770 
771     SDL_LockMutex(MyMutex);
772     MusicDev->destroy(MusicDev);
773     MusicDev = NULL;
774   }
775 
776   // only init music device if it doesn't still exist
777   if (!MusicDev)
778   {
779     SDL_UnlockMutex(MyMutex);
780     InitDecXMI();
781   }
782   else
783   {
784     SDL_UnlockMutex(MyMutex);
785   }
786 }
787 
788 
789 
ShutdownReadXMI(void)790 void ShutdownReadXMI(void)
791 {
792   int i;
793 
794   for (i=0; i<NUM_THREADS; i++)
795   {
796     StopTrack(i);
797     // don't set THREAD_EXIT yet, as this seems to cause deadlocks
798   }
799   SDL_Delay(50);
800   for (i=0; i<NUM_THREADS; i++)
801   {
802     SDL_AtomicSet(&ThreadCommand[i], THREAD_EXIT);
803   }
804   SDL_Delay(50); //wait a bit for thread to hopefully exit
805 
806   INFO("Closing MIDI driver due to shutdown");
807   SDL_LockMutex(MyMutex);
808   if (MusicDev)
809   {
810     MusicDev->destroy(MusicDev);
811     MusicDev = NULL;
812   }
813   else
814   {
815     WARN("ShutdownReadXMI(): Shutdown request received, but no music device exists!");
816   }
817   SDL_UnlockMutex(MyMutex);
818 
819   FreeXMI();
820 
821   SDL_DestroyMutex(MyMutex);
822 }
823 
GetOutputCountXMI(void)824 unsigned int GetOutputCountXMI(void)
825 {
826   unsigned int outputCount = 0;
827 
828   SDL_LockMutex(MyMutex);
829   if (MusicDev)
830   {
831     outputCount = MusicDev->getOutputCount(MusicDev);
832   }
833   SDL_UnlockMutex(MyMutex);
834 
835   return outputCount;
836 }
837 
GetOutputNameXMI(const unsigned int outputIndex,char * buffer,const unsigned int bufferSize)838 void GetOutputNameXMI(const unsigned int outputIndex, char *buffer, const unsigned int bufferSize)
839 {
840   SDL_LockMutex(MyMutex);
841   if (MusicDev && buffer && bufferSize >= 1)
842   {
843     MusicDev->getOutputName(MusicDev, outputIndex, buffer, bufferSize);
844   }
845   SDL_UnlockMutex(MyMutex);
846 }
847 
UpdateVolumeXMI(void)848 void UpdateVolumeXMI(void)
849 {
850   // global volume has been changed
851   extern uchar curr_vol_lev; // 0-100
852   INFO("UpdateVolumeXMI(): Global music volume change to %d percent", curr_vol_lev);
853 
854   // tell the music driver
855   SDL_LockMutex(MyMutex);
856   if (MusicDev && MusicDev->isOpen)
857   {
858     // send volume change controller (#7) for all channels
859     for (int i = 0; i <= 15; ++i)
860     {
861       // skip unused device channels
862       if (i != 9 && ChannelThread[i] == -1) continue;
863       // scale new volume according to global music volume
864       const int scaledVolume = ((int)SDL_AtomicGet(&DeviceChannelVolume[i]) * (int)curr_vol_lev) / 100;
865       MusicDev->sendControllerChange(MusicDev, i, 7, scaledVolume);
866     }
867   }
868   SDL_UnlockMutex(MyMutex);
869 }
870