#include #include #include #include #include #include #include #include #include #include #include #include #include "native_midi/native_midi.h" #include "util.h" #include "palcfg.h" #define CLIPLAYER_EXECUTABLE (which("timidity")) struct _NativeMidiSong { std::thread Thread; std::mutex Mutex; char *file; int pid; volatile bool playing; bool looping; }; const char* midi_file = "/tmp/sdlpal.temp.mid"; char* cliplayer = nullptr; static char *which(const char *cmd) { static char path[PATH_MAX] = { '\0' }; FILE *fp = popen(PAL_va(0, "which %s", cmd), "r"); if (fp == NULL) { return NULL; }else{ if (fgets(path, sizeof(path)-1, fp) != NULL) { if (path[strlen(path) - 1] == '\n') path[strlen(path) - 1] = '\0'; if (path[strlen(path) - 1] == '\r') path[strlen(path) - 1] = '\0'; } pclose(fp); return path; } } extern "C" int native_midi_detect() { if (cliplayer) { free(cliplayer); cliplayer = nullptr; } char *path = (gConfig.pszMIDIClient ? gConfig.pszMIDIClient : CLIPLAYER_EXECUTABLE); if (path && access(path,F_OK) == 0) { cliplayer = strdup(path); return 1; } else { return 0; } } extern "C" NativeMidiSong *native_midi_loadsong(const char *midifile) { struct stat st; if (0 != stat(midifile, &st)) return NULL; auto song = new NativeMidiSong; if (NULL == song) return NULL; if (NULL == (song->file = new char[strlen(midifile) + 1])) return NULL; song->pid = -1; song->playing = false; song->looping = false; strcpy(song->file, midifile); return song; } extern "C" NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw) { FILE *fp = fopen(midi_file, "wb+"); if (fp) { char buf[4096]; size_t bytes; while((bytes = SDL_RWread(rw, buf, sizeof(char), sizeof(buf))) > 0) fwrite(buf, sizeof(char), bytes, fp); fclose(fp); return native_midi_loadsong(midi_file); } return NULL; } extern "C" void native_midi_freesong(NativeMidiSong *song) { if (song) { if (native_midi_active(song)) native_midi_stop(song); if (song->file) delete []song->file; delete song; } } extern "C" void native_midi_start(NativeMidiSong *song, int looping) { if (!song) return; native_midi_stop(song); song->playing = true; song->looping = looping ? true : false; song->Thread = std::move(std::thread([](NativeMidiSong *song)->void { while(song->looping) { auto pid = fork(); int status; if (0 == pid) { char* args[] = { cliplayer, song->file, NULL }; if (-1 == execv(cliplayer, args)) exit(-1); } else if (-1 == pid) { return; } song->Mutex.lock(); song->pid = pid; song->Mutex.unlock(); waitpid(pid, &status, 0); } song->playing = false; }, song)); } extern "C" void native_midi_stop(NativeMidiSong *song) { if (song) { song->looping = false; song->playing = false; if (-1 != song->pid) { song->Mutex.lock(); kill(song->pid, SIGTERM); song->Mutex.unlock(); } if (song->Thread.joinable()) song->Thread.join(); song->Thread = std::move(std::thread()); } } extern "C" int native_midi_active(NativeMidiSong *song) { return (song && song->playing) ? 1 : 0; } extern "C" void native_midi_setvolume(NativeMidiSong *song, int volume) { // TODO } extern "C" const char *native_midi_error(NativeMidiSong *song) { return ""; }