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