Browse Source

A MIDI implementation that should work in theory under Linux

LouYihua 7 years ago
parent
commit
bdda630d16
1 changed files with 154 additions and 0 deletions
  1. 154 0
      unix/native_midi.cpp

+ 154 - 0
unix/native_midi.cpp

@@ -0,0 +1,154 @@
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <future>
+#include <thread>
+#include <condition_variable>
+#include <mutex>
+#include <atomic>
+#include <vector>
+
+#include "native_midi/native_midi.h"
+
+// Warning: this is a unverified implementation and this comment should be removed if verified
+
+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* timidity = nullptr;
+
+extern "C" int native_midi_detect()
+{
+    // FIXME!!!
+    if (timidity)
+    {
+        free(timidity);
+        timidity = nullptr;
+    }
+    if (system("timidity -v") == 0)
+    {
+        timidity = strdup("/usr/bin/timidity");
+        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)))
+            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 (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[] = { timidity, song->file, NULL };
+                if (-1 == execv(timidity, 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;
+        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 "";
+}