native_midi.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. #include <stdlib.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <sys/wait.h>
  5. #include <signal.h>
  6. #include <unistd.h>
  7. #include <future>
  8. #include <thread>
  9. #include <condition_variable>
  10. #include <mutex>
  11. #include <atomic>
  12. #include <vector>
  13. #include "native_midi/native_midi.h"
  14. // Warning: this is a unverified implementation and this comment should be removed if verified
  15. struct _NativeMidiSong
  16. {
  17. std::thread Thread;
  18. std::mutex Mutex;
  19. char *file;
  20. int pid;
  21. volatile bool playing;
  22. bool looping;
  23. };
  24. const char* midi_file = "/tmp/sdlpal.temp.mid";
  25. char* timidity = nullptr;
  26. extern "C" int native_midi_detect()
  27. {
  28. // FIXME!!!
  29. if (timidity)
  30. {
  31. free(timidity);
  32. timidity = nullptr;
  33. }
  34. //system `timidity -v` will cause CLI blocked by the version info...
  35. if (access("/usr/bin/timidity",F_OK) == 0)
  36. {
  37. timidity = strdup("/usr/bin/timidity");
  38. return 1;
  39. }
  40. else
  41. {
  42. return 0;
  43. }
  44. }
  45. extern "C" NativeMidiSong *native_midi_loadsong(const char *midifile)
  46. {
  47. struct stat st;
  48. if (0 != stat(midifile, &st)) return NULL;
  49. auto song = new NativeMidiSong;
  50. if (NULL == song) return NULL;
  51. if (NULL == (song->file = new char[strlen(midifile) + 1])) return NULL;
  52. song->pid = -1;
  53. song->playing = false;
  54. song->looping = false;
  55. strcpy(song->file, midifile);
  56. return song;
  57. }
  58. extern "C" NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
  59. {
  60. FILE *fp = fopen(midi_file, "wb+");
  61. if (fp)
  62. {
  63. char buf[4096];
  64. size_t bytes;
  65. while(bytes = SDL_RWread(rw, buf, sizeof(char), sizeof(buf)))
  66. fwrite(buf, sizeof(char), bytes, fp);
  67. fclose(fp);
  68. return native_midi_loadsong(midi_file);
  69. }
  70. return NULL;
  71. }
  72. extern "C" void native_midi_freesong(NativeMidiSong *song)
  73. {
  74. if (song)
  75. {
  76. //stop it first to prevent app terminated by destructing joinable thread destruction
  77. if (native_midi_active(song))
  78. native_midi_stop(song);
  79. if (song->file) delete []song->file;
  80. delete song;
  81. }
  82. }
  83. extern "C" void native_midi_start(NativeMidiSong *song, int looping)
  84. {
  85. if (!song) return;
  86. native_midi_stop(song);
  87. song->playing = true;
  88. song->looping = looping ? true : false;
  89. song->Thread = std::move(std::thread([](NativeMidiSong *song)->void {
  90. while(song->looping)
  91. {
  92. auto pid = fork();
  93. int status;
  94. if (0 == pid)
  95. {
  96. char* args[] = { timidity, song->file, NULL };
  97. if (-1 == execv(timidity, args)) exit(-1);
  98. }
  99. else if (-1 == pid)
  100. {
  101. return;
  102. }
  103. song->Mutex.lock();
  104. song->pid = pid;
  105. song->Mutex.unlock();
  106. waitpid(pid, &status, 0);
  107. }
  108. song->playing = false;
  109. }, song));
  110. }
  111. extern "C" void native_midi_stop(NativeMidiSong *song)
  112. {
  113. if (song)
  114. {
  115. song->looping = false;
  116. song->playing = false;
  117. if (-1 != song->pid)
  118. {
  119. song->Mutex.lock();
  120. kill(song->pid, SIGTERM);
  121. song->Mutex.unlock();
  122. }
  123. if (song->Thread.joinable())
  124. song->Thread.join();
  125. song->Thread = std::move(std::thread());
  126. }
  127. }
  128. extern "C" int native_midi_active(NativeMidiSong *song)
  129. {
  130. return (song && song->playing) ? 1 : 0;
  131. }
  132. extern "C" void native_midi_setvolume(NativeMidiSong *song, int volume)
  133. {
  134. // TODO
  135. }
  136. extern "C" const char *native_midi_error(NativeMidiSong *song)
  137. {
  138. return "";
  139. }