native_midi_alsa.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. #include "native_midi/native_midi.h"
  2. #include "native_midi/native_midi_common.h"
  3. #include "palcfg.h"
  4. #include <SDL_thread.h>
  5. #include <alsa/asoundlib.h>
  6. typedef struct {
  7. uint32_t tick;
  8. uint8_t type, channel, data1, data2;
  9. union {
  10. int32_t pitch;
  11. int32_t tempo;
  12. uint32_t len;
  13. };
  14. uint8_t *sysex;
  15. uint64_t time;
  16. } midi_event_info;
  17. struct _NativeMidiSong {
  18. snd_seq_t *sequencer;
  19. int src_client_id;
  20. int src_port_id;
  21. int dst_client_id;
  22. int dst_port_id;
  23. int queue;
  24. midi_event_info *events;
  25. unsigned int timediv;
  26. volatile int playing;
  27. int current_volume;
  28. volatile int new_volume;
  29. int looping;
  30. SDL_Thread *thread;
  31. };
  32. static const uint8_t alsa_event_types[8] = {
  33. SND_SEQ_EVENT_NOTEOFF,
  34. SND_SEQ_EVENT_NOTEON,
  35. SND_SEQ_EVENT_KEYPRESS,
  36. SND_SEQ_EVENT_CONTROLLER,
  37. SND_SEQ_EVENT_PGMCHANGE,
  38. SND_SEQ_EVENT_CHANPRESS,
  39. SND_SEQ_EVENT_PITCHBEND,
  40. SND_SEQ_EVENT_NONE
  41. };
  42. static int native_midi_available = -1;
  43. static int dst_client_id = 0;
  44. static int dst_port_id = 0;
  45. static void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
  46. {
  47. // do nothing
  48. }
  49. static int create_src_port(NativeMidiSong *song)
  50. {
  51. snd_seq_port_info_t *pinfo;
  52. snd_seq_port_info_alloca(&pinfo);
  53. snd_seq_port_info_set_name(pinfo, "PAL-midi");
  54. snd_seq_port_info_set_capability(pinfo, 0);
  55. snd_seq_port_info_set_type(pinfo, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION);
  56. if (snd_seq_create_port(song->sequencer, pinfo) < 0) return -1;
  57. song->src_port_id = snd_seq_port_info_get_port(pinfo);
  58. return 0;
  59. }
  60. static int find_dst_port(NativeMidiSong *song, const char *midi_client)
  61. {
  62. snd_seq_client_info_t *cinfo;
  63. snd_seq_port_info_t *pinfo;
  64. int client_id, port_id;
  65. if ((midi_client != NULL) && (*midi_client != 0))
  66. {
  67. snd_seq_addr_t addr;
  68. if (snd_seq_parse_address(song->sequencer, &addr, midi_client) < 0) return -1;
  69. song->dst_client_id = addr.client;
  70. song->dst_port_id = addr.port;
  71. snd_seq_port_info_alloca(&pinfo);
  72. if (snd_seq_get_any_port_info(song->sequencer, song->dst_client_id, song->dst_port_id, pinfo) < 0) return -2;
  73. if ((snd_seq_port_info_get_capability(pinfo) & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT)) != (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE))
  74. {
  75. return -3;
  76. }
  77. else
  78. {
  79. return 0;
  80. }
  81. }
  82. else
  83. {
  84. client_id = -1;
  85. snd_seq_client_info_alloca(&cinfo);
  86. snd_seq_port_info_alloca(&pinfo);
  87. snd_seq_client_info_set_client(cinfo, -1);
  88. while (snd_seq_query_next_client(song->sequencer, cinfo) >= 0)
  89. {
  90. snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
  91. snd_seq_port_info_set_port(pinfo, -1);
  92. while (snd_seq_query_next_port(song->sequencer, pinfo) >= 0)
  93. {
  94. if ( ((snd_seq_port_info_get_capability(pinfo) & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT)) == (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) &&
  95. (snd_seq_port_info_get_type(pinfo) & (SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_MIDI_GM))
  96. )
  97. {
  98. if (snd_seq_port_info_get_midi_channels(pinfo))
  99. {
  100. song->dst_client_id = snd_seq_client_info_get_client(cinfo);
  101. song->dst_port_id = snd_seq_port_info_get_port(pinfo);
  102. return 0;
  103. }
  104. else if (client_id == -1)
  105. {
  106. client_id = snd_seq_client_info_get_client(cinfo);
  107. port_id = snd_seq_port_info_get_port(pinfo);
  108. }
  109. }
  110. }
  111. }
  112. if (client_id != -1)
  113. {
  114. song->dst_client_id = client_id;
  115. song->dst_port_id = port_id;
  116. return 0;
  117. }
  118. else
  119. {
  120. return -4;
  121. }
  122. }
  123. }
  124. static int open_alsa_port(NativeMidiSong *song)
  125. {
  126. if (song == NULL) return -1;
  127. if (song->sequencer != NULL) return -2;
  128. if (snd_seq_open(&(song->sequencer), "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
  129. {
  130. song->sequencer = NULL;
  131. return -3;
  132. }
  133. if (snd_seq_set_client_name(song->sequencer, "PAL-midi") < 0)
  134. {
  135. snd_seq_close(song->sequencer);
  136. song->sequencer = NULL;
  137. return -4;
  138. }
  139. song->src_client_id = snd_seq_client_id(song->sequencer);
  140. if (song->src_client_id < 0)
  141. {
  142. snd_seq_close(song->sequencer);
  143. song->sequencer = NULL;
  144. return -5;
  145. }
  146. if (dst_client_id == 0)
  147. {
  148. if (find_dst_port(song, gConfig.pszMIDIClient) < 0)
  149. {
  150. snd_seq_close(song->sequencer);
  151. song->sequencer = NULL;
  152. return -6;
  153. }
  154. }
  155. else
  156. {
  157. song->dst_client_id = dst_client_id;
  158. song->dst_port_id = dst_port_id;
  159. }
  160. if (create_src_port(song) < 0)
  161. {
  162. snd_seq_close(song->sequencer);
  163. song->sequencer = NULL;
  164. return -7;
  165. }
  166. song->queue = snd_seq_alloc_named_queue(song->sequencer, "PAL-midi");
  167. if (song->queue < 0)
  168. {
  169. snd_seq_delete_port(song->sequencer, song->src_port_id);
  170. snd_seq_close(song->sequencer);
  171. song->sequencer = NULL;
  172. return -8;
  173. }
  174. if (snd_seq_connect_to(song->sequencer, song->src_port_id, song->dst_client_id, song->dst_port_id) < 0)
  175. {
  176. snd_seq_free_queue(song->sequencer, song->queue);
  177. snd_seq_delete_port(song->sequencer, song->src_port_id);
  178. snd_seq_close(song->sequencer);
  179. song->sequencer = NULL;
  180. return -9;
  181. }
  182. return 0;
  183. }
  184. static void close_alsa_port(NativeMidiSong *song)
  185. {
  186. if (song->sequencer != NULL)
  187. {
  188. snd_seq_disconnect_to(song->sequencer, song->src_port_id, song->dst_client_id, song->dst_port_id);
  189. snd_seq_free_queue(song->sequencer, song->queue);
  190. snd_seq_delete_port(song->sequencer, song->src_port_id);
  191. snd_seq_close(song->sequencer);
  192. song->sequencer = NULL;
  193. }
  194. }
  195. static int convert_event_list(NativeMidiSong *song, MIDIEvent *eventlist)
  196. {
  197. int eventcount = 0;
  198. MIDIEvent* eventlist1;
  199. for (eventlist1 = eventlist; eventlist1 != NULL; eventlist1 = eventlist1->next)
  200. {
  201. eventcount++;
  202. }
  203. midi_event_info *events = calloc(eventcount + 1, sizeof(midi_event_info));
  204. if (events == NULL) return -1;
  205. events[0].tick = 0;
  206. events[0].type = SND_SEQ_EVENT_NONE;
  207. events[0].len = 0;
  208. events[0].sysex = NULL;
  209. events[0].time = 0;
  210. unsigned int tempo, tempo_tick;
  211. uint64_t tempo_time;
  212. tempo = 500000; // 500000 MPQN = 120 BPM
  213. tempo_tick = 0;
  214. tempo_time = 0;
  215. unsigned int time_division = song->timediv;
  216. eventcount = 0;
  217. for (; eventlist != NULL; eventlist = eventlist->next)
  218. {
  219. midi_event_info event;
  220. event.tick = eventlist->time;
  221. event.sysex = NULL;
  222. event.type = SND_SEQ_EVENT_NONE;
  223. // calculate event time in nanoseconds
  224. {
  225. div_t divres;
  226. divres = div(event.tick - tempo_tick, time_division);
  227. event.time = ( ((1000 * divres.rem) * (uint64_t)tempo) / time_division )
  228. + ( (divres.quot * (uint64_t)tempo) * 1000 )
  229. + tempo_time
  230. ;
  231. //event.time = ( (((event.tick - tempo_tick) * (uint64_t) 1000) * tempo) / time_division ) + tempo_time;
  232. }
  233. int status = (eventlist->status & 0xf0) >> 4;
  234. switch (status)
  235. {
  236. case MIDI_STATUS_NOTE_OFF:
  237. case MIDI_STATUS_NOTE_ON:
  238. case MIDI_STATUS_AFTERTOUCH:
  239. case MIDI_STATUS_CONTROLLER:
  240. case MIDI_STATUS_PROG_CHANGE:
  241. case MIDI_STATUS_PRESSURE:
  242. case MIDI_STATUS_PITCH_WHEEL:
  243. event.type = alsa_event_types[status - 8];
  244. event.channel = eventlist->status & 0x0f;
  245. event.data1 = eventlist->data[0];
  246. event.data2 = eventlist->data[1];
  247. if (status == MIDI_STATUS_PITCH_WHEEL)
  248. {
  249. event.pitch = ( ((int32_t)event.data1) | (((int32_t)event.data2) << 7) ) - 0x2000;
  250. }
  251. break;
  252. case MIDI_STATUS_SYSEX:
  253. if (eventlist->status == 0xff) // meta events
  254. {
  255. if (eventlist->data[0] == 0x51) // set tempo
  256. {
  257. event.type = SND_SEQ_EVENT_TEMPO;
  258. event.channel = eventlist->extraData[0];
  259. event.data1 = eventlist->extraData[1];
  260. event.data2 = eventlist->extraData[2];
  261. event.tempo = (((uint32_t)event.channel) << 16) | (((uint32_t)event.data1) << 8) | ((uint32_t)event.data2);
  262. tempo = event.tempo;
  263. tempo_tick = event.tick;
  264. tempo_time = event.time;
  265. }
  266. }
  267. else if ((eventlist->status == 0xf0) || (eventlist->status == 0xf7)) // sysex
  268. {
  269. event.type = SND_SEQ_EVENT_SYSEX;
  270. event.len = eventlist->extraLen + (eventlist->status == 0xf0)?1:0;
  271. if (event.len)
  272. {
  273. event.sysex = (uint8_t *) malloc(event.len);
  274. if (event.sysex != NULL)
  275. {
  276. if (eventlist->status == 0xf0)
  277. {
  278. event.sysex[0] = 0xf0;
  279. memcpy(event.sysex + 1, eventlist->extraData, eventlist->extraLen);
  280. }
  281. else
  282. {
  283. memcpy(event.sysex, eventlist->extraData, eventlist->extraLen);
  284. }
  285. }
  286. }
  287. }
  288. break;
  289. }
  290. if (event.type != SND_SEQ_EVENT_NONE)
  291. {
  292. eventcount++;
  293. events[eventcount] = event;
  294. }
  295. }
  296. events[0].len = eventcount;
  297. song->events = events;
  298. return 0;
  299. }
  300. static void free_midi_events(NativeMidiSong *song)
  301. {
  302. unsigned int index;
  303. if (song->events != NULL)
  304. {
  305. for (index = song->events[0].len; index != 0; index--)
  306. {
  307. if (song->events[index].sysex != NULL)
  308. {
  309. free(song->events[index].sysex);
  310. song->events[index].sysex = NULL;
  311. }
  312. }
  313. free(song->events);
  314. song->events = NULL;
  315. }
  316. }
  317. static int play_midi(void *_song)
  318. {
  319. NativeMidiSong *song = (NativeMidiSong *)_song;
  320. midi_event_info *events;
  321. unsigned int current_event, base_tick, last_tick, num_events;
  322. uint64_t base_time;
  323. // set queue tempo
  324. {
  325. snd_seq_queue_tempo_t *queue_tempo;
  326. snd_seq_queue_tempo_alloca(&queue_tempo);
  327. snd_seq_queue_tempo_set_tempo(queue_tempo, 500000); // 120 BPM
  328. snd_seq_queue_tempo_set_ppq(queue_tempo, song->timediv);
  329. if (0 > snd_seq_set_queue_tempo(song->sequencer, song->queue, queue_tempo))
  330. {
  331. return 2;
  332. }
  333. }
  334. // start play
  335. if (0 > snd_seq_start_queue(song->sequencer, song->queue, NULL))
  336. {
  337. return 3;
  338. }
  339. if (0 > snd_seq_drain_output(song->sequencer))
  340. {
  341. return 4;
  342. }
  343. events = song->events;
  344. num_events = events[0].len;
  345. current_event = 1;
  346. base_tick = 0;
  347. base_time = 0;
  348. last_tick = 0;
  349. snd_seq_sync_output_queue(song->sequencer);
  350. int do_sleep;
  351. snd_seq_queue_status_t *queue_status;
  352. const snd_seq_real_time_t *real_time;
  353. int64_t time_diff, base_diff;
  354. snd_seq_event_t event;
  355. snd_seq_queue_status_alloca(&queue_status);
  356. snd_seq_ev_clear(&event);
  357. event.queue = song->queue;
  358. event.source.port = song->src_port_id;
  359. event.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_ABS;
  360. do_sleep = 0;
  361. while (1)
  362. {
  363. if (do_sleep)
  364. {
  365. do_sleep = 0;
  366. SDL_Delay(10);
  367. }
  368. if (!song->playing) break;
  369. if (current_event > num_events)
  370. {
  371. if (!song->looping)
  372. {
  373. song->playing = 0;
  374. break;
  375. }
  376. // looping
  377. base_tick += events[num_events].tick;
  378. base_time += events[num_events].time;
  379. current_event = 1;
  380. }
  381. if ((song->new_volume != song->current_volume) && (events[current_event].tick != 0))
  382. {
  383. int chan;
  384. song->current_volume = song->new_volume;
  385. snd_seq_ev_set_fixed(&event);
  386. event.type = SND_SEQ_EVENT_CONTROLLER;
  387. event.time.tick = base_tick + events[current_event - 1].tick;
  388. event.dest.client = song->dst_client_id;
  389. event.dest.port = song->dst_port_id;
  390. event.data.control.param = MIDI_CTL_MSB_MAIN_VOLUME;
  391. event.data.control.value = song->current_volume;
  392. for (chan = 0; chan < 16; chan++)
  393. {
  394. event.data.control.channel = chan;
  395. snd_seq_event_output(song->sequencer, &event);
  396. }
  397. snd_seq_drain_output(song->sequencer);
  398. }
  399. if (0 > snd_seq_get_queue_status(song->sequencer, song->queue, queue_status))
  400. {
  401. do_sleep = 1;
  402. continue;
  403. }
  404. real_time = snd_seq_queue_status_get_real_time(queue_status);
  405. base_diff = ((real_time->tv_sec * (uint64_t)1000000000) + real_time->tv_nsec) - base_time;
  406. time_diff = events[current_event].time - base_diff;
  407. if (time_diff >= 100000000) // 100ms
  408. {
  409. do_sleep = 1;
  410. continue;
  411. }
  412. do
  413. {
  414. // add events to queue
  415. event.type = events[current_event].type;
  416. event.time.tick = base_tick + events[current_event].tick;
  417. event.dest.client = song->dst_client_id;
  418. event.dest.port = song->dst_port_id;
  419. switch (event.type)
  420. {
  421. case SND_SEQ_EVENT_NOTEON:
  422. case SND_SEQ_EVENT_NOTEOFF:
  423. case SND_SEQ_EVENT_KEYPRESS:
  424. snd_seq_ev_set_fixed(&event);
  425. event.data.note.channel = events[current_event].channel;
  426. event.data.note.note = events[current_event].data1;
  427. event.data.note.velocity = events[current_event].data2;
  428. break;
  429. case SND_SEQ_EVENT_CONTROLLER:
  430. snd_seq_ev_set_fixed(&event);
  431. event.data.control.channel = events[current_event].channel;
  432. event.data.control.param = events[current_event].data1;
  433. event.data.control.value = events[current_event].data2;
  434. break;
  435. case SND_SEQ_EVENT_PGMCHANGE:
  436. case SND_SEQ_EVENT_CHANPRESS:
  437. snd_seq_ev_set_fixed(&event);
  438. event.data.control.channel = events[current_event].channel;
  439. event.data.control.value = events[current_event].data1;
  440. break;
  441. case SND_SEQ_EVENT_PITCHBEND:
  442. snd_seq_ev_set_fixed(&event);
  443. event.data.control.channel = events[current_event].channel;
  444. event.data.control.value = events[current_event].pitch;
  445. break;
  446. case SND_SEQ_EVENT_SYSEX:
  447. snd_seq_ev_set_variable(&event, events[current_event].len, events[current_event].sysex);
  448. break;
  449. case SND_SEQ_EVENT_TEMPO:
  450. snd_seq_ev_set_fixed(&event);
  451. event.dest.client = SND_SEQ_CLIENT_SYSTEM;
  452. event.dest.port = SND_SEQ_PORT_SYSTEM_TIMER;
  453. event.data.queue.queue = song->queue;
  454. event.data.queue.param.value = events[current_event].tempo;
  455. break;
  456. }
  457. snd_seq_event_output(song->sequencer, &event);
  458. current_event++;
  459. if (current_event > num_events) break;
  460. time_diff = events[current_event].time - base_diff;
  461. } while (time_diff < 100000000); // 100ms
  462. snd_seq_drain_output(song->sequencer);
  463. last_tick = event.time.tick;
  464. }
  465. // stop playing
  466. event.time.tick = last_tick;
  467. snd_seq_sync_output_queue(song->sequencer);
  468. event.type = SND_SEQ_EVENT_CONTROLLER;
  469. event.dest.client = song->dst_client_id;
  470. event.dest.port = song->dst_port_id;
  471. int chan;
  472. for (chan = 0; chan < 16; chan++)
  473. {
  474. snd_seq_ev_set_fixed(&event);
  475. event.data.control.channel = chan;
  476. event.data.control.param = MIDI_CTL_ALL_NOTES_OFF; // All notes off (this message stops all the notes that are currently playing)
  477. event.data.control.value = 0;
  478. snd_seq_event_output(song->sequencer, &event);
  479. snd_seq_ev_set_fixed(&event);
  480. event.data.control.channel = chan;
  481. event.data.control.param = MIDI_CTL_RESET_CONTROLLERS; // All controllers off (this message clears all the controller values for this channel, back to their default values)
  482. event.data.control.value = 0;
  483. snd_seq_event_output(song->sequencer, &event);
  484. }
  485. snd_seq_ev_set_fixed(&event);
  486. event.type = SND_SEQ_EVENT_STOP;
  487. event.dest.client = SND_SEQ_CLIENT_SYSTEM;
  488. event.dest.port = SND_SEQ_PORT_SYSTEM_TIMER;
  489. event.data.queue.queue = song->queue;
  490. snd_seq_event_output(song->sequencer, &event);
  491. snd_seq_drain_output(song->sequencer);
  492. snd_seq_sync_output_queue(song->sequencer);
  493. return 0;
  494. }
  495. #ifdef __cplusplus
  496. extern "C"
  497. {
  498. #endif
  499. int native_midi_detect()
  500. {
  501. if (native_midi_available != -1) return native_midi_available;
  502. NativeMidiSong *song = calloc(1, sizeof(NativeMidiSong));
  503. if (song == NULL) return 0;
  504. snd_lib_error_set_handler(error_handler);
  505. if (open_alsa_port(song) < 0)
  506. {
  507. free(song);
  508. native_midi_available = 0;
  509. return 0;
  510. }
  511. native_midi_available = 1;
  512. dst_client_id = song->dst_client_id;
  513. dst_port_id = song->dst_port_id;
  514. close_alsa_port(song);
  515. free(song);
  516. return 1;
  517. }
  518. NativeMidiSong *native_midi_loadsong(const char *midifile)
  519. {
  520. // Attempt to load the midi file
  521. SDL_RWops *rw = SDL_RWFromFile(midifile, "rb");
  522. if (rw == NULL) return NULL;
  523. NativeMidiSong *song = native_midi_loadsong_RW(rw);
  524. SDL_RWclose(rw);
  525. return song;
  526. }
  527. NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
  528. {
  529. NativeMidiSong *song = calloc(1, sizeof(NativeMidiSong));
  530. if (song == NULL) return NULL;
  531. Uint16 division;
  532. MIDIEvent *eventlist = CreateMIDIEventList(rw, &division);
  533. if (eventlist == NULL)
  534. {
  535. free(song);
  536. return NULL;
  537. }
  538. song->current_volume = 128;
  539. song->new_volume = 128;
  540. song->timediv = division;
  541. if (convert_event_list(song, eventlist) < 0)
  542. {
  543. FreeMIDIEventList(eventlist);
  544. free(song);
  545. return NULL;
  546. }
  547. FreeMIDIEventList(eventlist);
  548. if (open_alsa_port(song) < 0)
  549. {
  550. free_midi_events(song);
  551. free(song);
  552. return NULL;
  553. }
  554. return song;
  555. }
  556. void native_midi_freesong(NativeMidiSong *song)
  557. {
  558. if (song != NULL)
  559. {
  560. native_midi_stop(song);
  561. close_alsa_port(song);
  562. free_midi_events(song);
  563. free(song);
  564. }
  565. }
  566. void native_midi_start(NativeMidiSong *song, int looping)
  567. {
  568. if (song != NULL)
  569. {
  570. native_midi_stop(song);
  571. song->playing = 1;
  572. song->looping = looping;
  573. #if SDL_VERSION_ATLEAST(2,0,0)
  574. song->thread = SDL_CreateThread(&play_midi, "PAL-midi", (void *)song);
  575. #else
  576. song->thread = SDL_CreateThread(&play_midi, (void *)song);
  577. #endif
  578. }
  579. }
  580. void native_midi_stop(NativeMidiSong *song)
  581. {
  582. if (song != NULL)
  583. {
  584. song->playing = 0;
  585. if (song->thread != NULL)
  586. {
  587. SDL_WaitThread(song->thread, NULL);
  588. song->thread = NULL;
  589. }
  590. }
  591. }
  592. int native_midi_active(NativeMidiSong *song)
  593. {
  594. return (song && song->playing) ? 1 : 0;
  595. }
  596. void native_midi_setvolume(NativeMidiSong *song, int volume)
  597. {
  598. if (song != NULL)
  599. {
  600. if (volume > 127) volume = 127;
  601. if (volume < 0) volume = 0;
  602. song->new_volume = volume;
  603. }
  604. }
  605. const char *native_midi_error(NativeMidiSong *song)
  606. {
  607. return "";
  608. }
  609. #ifdef __cplusplus
  610. }
  611. #endif