Browse Source

Merge pull request #18 from sdlpal/native_midi

Native midi wrappers completion (with linux support deferred)
Yihua LOU 7 years ago
parent
commit
70bc219d64
38 changed files with 1511 additions and 201 deletions
  1. 2 1
      .gitignore
  2. 1 1
      android/AndroidManifest.xml
  3. 1 1
      android/app/build.gradle
  4. 1 0
      android/jni/src/Android.mk
  5. 195 17
      android/jni/src/android_jni.c
  6. 13 0
      android/jni/src/android_jni.h
  7. 124 0
      android/jni/src/native_midi.c
  8. 6 0
      android/jni/src/pal_config.h
  9. 0 19
      android/src/com/codeplex/sdlpal/PalActivity.java
  10. 57 0
      android/src/io/github/sdlpal/PalActivity.java
  11. 81 37
      ios/SDLPal/SDLPal.xcodeproj/project.pbxproj
  12. 316 0
      ios/SDLPal/SDLPal/native_midi_ios.c
  13. 4 0
      ios/pal_config.h
  14. 86 34
      macos/Pal.xcodeproj/project.pbxproj
  15. 1 2
      native_midi/native_midi_macosx.c
  16. 5 1
      macos/pal_config.h
  17. 1 1
      win32/Makefile
  18. 1 1
      win32/Makefile.mingw
  19. 3 7
      native_midi/native_midi_win32.c
  20. 0 2
      win32/pal_config.h
  21. 1 1
      win32/sdlpal.vcxproj
  22. 3 3
      win32/sdlpal.vcxproj.filters
  23. 29 7
      winrt/SDLPal.Common/AsyncHelper.h
  24. 1 0
      winrt/SDLPal.Common/MainPage.xaml
  25. 3 3
      winrt/SDLPal.Common/MainPage.xaml.cpp
  26. 91 0
      winrt/SDLPal.Common/NativeBuffer.h
  27. 6 3
      winrt/SDLPal.Common/SDLPal.cpp
  28. 25 0
      winrt/SDLPal.Common/StringHelper.h
  29. 81 44
      winrt/SDLPal.Common/WinRTIO.cpp
  30. 1 0
      winrt/SDLPal.UWP/SDLPal.Common.vcxproj
  31. 1 0
      winrt/SDLPal.UWP/SDLPal.Common.vcxproj.filters
  32. 10 6
      winrt/SDLPal.UWP/SDLPal.Core.vcxproj
  33. 15 0
      winrt/SDLPal.UWP/SDLPal.Core.vcxproj.filters
  34. 12 10
      winrt/SDLPal.UWP/SDLPal.UWP.vcxproj
  35. 1 0
      winrt/SDLPal.UWP/SDLPal.UWP.vcxproj.filters
  36. 2 0
      winrt/SDLPal.UWP/pch.h
  37. 329 0
      winrt/native_midi.cpp
  38. 2 0
      winrt/pal_config.h

+ 2 - 1
.gitignore

@@ -241,11 +241,12 @@ android/so
 *.keystore
 resources
 
-sdlpal
+unix/sdlpal
 data
 
 *.xcscmblueprint
 xcuserdata
+*.xcuserstate
 
 # Gradle files
 build/

+ 1 - 1
android/AndroidManifest.xml

@@ -3,7 +3,7 @@
      com.gamemaker.game
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-      package="com.codeplex.sdlpal"
+      package="io.github.sdlpal"
       android:versionCode="2"
       android:versionName="2.0"
       android:installLocation="auto">

+ 1 - 1
android/app/build.gradle

@@ -5,7 +5,7 @@ android {
     buildToolsVersion "25.0.2"
 
     defaultConfig {
-        applicationId "com.codeplex.sdlpal"
+        applicationId "io.github.sdlpal"
         minSdkVersion 10
         targetSdkVersion 12
         externalNativeBuild {

+ 1 - 0
android/jni/src/Android.mk

@@ -16,6 +16,7 @@ LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \
     $(wildcard $(LOCAL_PATH)/../../../adplug/*.c) $(wildcard $(LOCAL_PATH)/../../../adplug/*.cpp) \
     $(wildcard $(LOCAL_PATH)/../../../liboggvorbis/src/*.c) \
     $(wildcard $(LOCAL_PATH)/../../../libmad/*.c) \
+    $(wildcard $(LOCAL_PATH)/../../../native_midi/*.c) \
     $(wildcard $(LOCAL_PATH)/*.c) \
 
 LOCAL_CFLAGS += -std=gnu99 -DPAL_HAS_PLATFORM_SPECIFIC_UTILS

+ 195 - 17
android/jni/src/android_jni.c

@@ -1,30 +1,208 @@
 #include <jni.h>
+#include <android/log.h>
+#define TAG "sdlpal-jni"
+
+#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG,__VA_ARGS__)
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG  , TAG,__VA_ARGS__)
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO   , TAG,__VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN   , TAG,__VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR  , TAG,__VA_ARGS__)
+
 #include "palcommon.h"
 #include "global.h"
 #include "palcfg.h"
 
 char externalStoragePath[255];
+char midiInterFile[255];
+
+static int jstring_to_utf8(JNIEnv *env, jstring j_str, char *buffer, int capacity)
+{
+    jsize pos = 0, length = (*env)->GetStringLength(env, j_str);
+    const jchar * const base = (*env)->GetStringCritical(env, j_str, NULL);
+    if (base == NULL)
+    {
+        return 0;
+    }
+    // Convert at char boundary, no incomplete output can be generated
+    for(const jchar *str = base;pos < length && pos < capacity - 1;str++)
+    {
+        if (*str > 4095 && pos < capacity - 3)
+        {
+            buffer[pos++] = 0xe0 | (*str >> 12);
+            buffer[pos++] = 0x80 | ((*str >> 6) & 0x3f);
+            buffer[pos++] = 0x80 | (*str & 0x3f);
+        }
+        else if (*str > 127 && pos < capacity - 2)
+        {
+            buffer[pos++] = 0xc0 | (*str >> 6);
+            buffer[pos++] = 0x80 | (*str & 0x3f);
+        }
+        else if (*str <= 127)
+        {
+            buffer[pos++] = *str;
+        }
+        else
+        {
+            break;
+        }
+    }
+    (*env)->ReleaseStringCritical(env, j_str, base);
+    buffer[pos] = '\0';
+    return pos;
+}
+
+static jstring jstring_from_utf8(JNIEnv *env, const char *str)
+{
+    jstring retval = NULL;
+    jchar *temp = NULL;
+    int wlen = 0, len = strlen(str);
+    // Count length of the UTF-8 string, stop at any error
+    for(int i = 0, state = 0, count = 0;i < len;i++)
+    {
+        if (state == 0)
+        {
+            if (str[i] < 127)
+            {
+                wlen++;
+            }
+            else if (str[i] >= 0xc0 && str[i] < 0xf0)
+            {
+                state = 1;
+                count = (str[i] >> 5) - 5;
+            }
+            else
+            {
+                break;
+            }
+        }
+        else
+        {
+            if (str[i] >= 0x80 && str[i] < 0xc0)
+            {
+                if (count == 0)
+                {
+                    state = 0;
+                    wlen++;
+                }
+                else
+                {
+                    count--;
+                }
+            }
+            else
+            {
+                break;
+            }
+        }
+    }
+    if (wlen == 0)
+    {
+        return (*env)->NewString(env, L"", 0);
+    }
+
+    temp = (jchar *)malloc(wlen * sizeof(jchar));
+    for(int i = 0, j = 0;j < wlen;j++)
+    {
+        if (str[i] > 127)
+        {
+            // Trick here:
+            // 2-byte form: 110x xxxx -> 0xxx xx000000
+            // 3-byte form: 1110 xxxx -> 10xx xx000000
+            temp[j] = (str[i++] & 0x3f) << 6;
+            temp[j] |= str[i++] & 0x3f;
+            if (temp[j] & 0x800)
+            {
+                // 3-byte form, the top-most bit will be dicarded during shift
+                temp[j] <<= 6;
+                temp[j] |= str[i++] & 0x3f;
+            }
+        }
+        else
+        {
+            temp[j] = str[i++];
+        }
+    }
+    retval = (*env)->NewString(env, temp, wlen);
+    free(temp);
+    return retval;
+}
+
+JavaVM *globalVM;
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_2) != JNI_OK) {
+        return -1;
+    }
+    globalVM = vm;
+    return JNI_VERSION_1_2;
+}
+
+JNIEnv *getJNIEnv()
+{
+    JNIEnv* env;
+    if ((*globalVM)->GetEnv(globalVM, (void**)&env, JNI_VERSION_1_2) != JNI_OK) {
+        return NULL;
+    }
+    return env;
+}
+
 /* 
- * Class:     com_codeplex_sdlpal_PalActivity 
+ * Class:     io_github_sdlpal_PalActivity 
  * Method:    setExternalStorage 
  * Signature: (Ljava/lang/String;)V
  */  
-JNIEXPORT void JNICALL Java_com_codeplex_sdlpal_PalActivity_setExternalStorage(JNIEnv *env, jclass cls, jstring j_str)  
-{  
-    const jchar* c_str= NULL;  
-    char* pBuff = externalStoragePath;  
-    c_str = (*env)->GetStringCritical(env,j_str,NULL);
-    if (c_str == NULL)
-    {  
-        return;  
-    }  
-    while(*c_str)   
-    {  
-        *pBuff++ = *c_str++;  
-    }
-    (*env)->ReleaseStringCritical(env,j_str,c_str);
-    strncat(externalStoragePath,"/sdlpal/",strnlen("/sdlpal/",255));
-    return;  
+JNIEXPORT void JNICALL Java_io_github_sdlpal_PalActivity_setExternalStorage(JNIEnv *env, jclass cls, jstring j_str)  
+{
+    jstring_to_utf8(env, j_str, externalStoragePath, 255 - 8);
+    strncat(externalStoragePath, "/sdlpal/", 8);
+    return;
+}
+
+/* 
+ * Class:     io_github_sdlpal_PalActivity 
+ * Method:    setMIDIInterFile 
+ * Signature: (Ljava/lang/String;)V
+ */  
+JNIEXPORT void JNICALL Java_io_github_sdlpal_PalActivity_setMIDIInterFile(JNIEnv *env, jclass cls, jstring j_str)  
+{
+    jstring_to_utf8(env, j_str, midiInterFile, 255);
+    LOGV("JNI got midi inter filename:%s", midiInterFile);
+    return;
+}
+
+void JNI_mediaplayer_load(const char *filename)
+{
+    JNIEnv *env = getJNIEnv();
+    jclass clazz = (*env)->FindClass(env, "io/github/sdlpal/PalActivity");
+    jmethodID mid = (*env)->GetStaticMethodID(env, clazz, "JNI_mediaplayer_load", "(Ljava/lang/String;)V");
+    (*env)->CallStaticVoidMethod(env, clazz, mid, jstring_from_utf8(env, filename));
+}
+
+void JNI_mediaplayer_play()
+{
+    JNIEnv *env = getJNIEnv();
+    jclass clazz = (*env)->FindClass(env, "io/github/sdlpal/PalActivity");
+    jmethodID mid = (*env)->GetStaticMethodID(env, clazz, "JNI_mediaplayer_play", "()V");
+    (*env)->CallStaticVoidMethod(env, clazz, mid);
+}
+
+void JNI_mediaplayer_stop()
+{
+    JNIEnv *env = getJNIEnv();
+    jclass clazz = (*env)->FindClass(env, "io/github/sdlpal/PalActivity");
+    jmethodID mid = (*env)->GetStaticMethodID(env, clazz, "JNI_mediaplayer_stop", "()V");
+    (*env)->CallStaticVoidMethod(env, clazz, mid);
+}
+
+int JNI_mediaplayer_isplaying()
+{
+    return 0;
+}
+
+void JNI_mediaplayer_setvolume(int volume)
+{
 }
 
 LPCSTR

+ 13 - 0
android/jni/src/android_jni.h

@@ -0,0 +1,13 @@
+#ifndef SDLPAL_JNI_H
+#define SDLPAL_JNI_H
+
+extern char externalStoragePath[255];
+extern char midiInterFile[255];
+
+void JNI_mediaplayer_load();
+void JNI_mediaplayer_play();
+void JNI_mediaplayer_stop();
+int JNI_mediaplayer_isplaying();
+void JNI_mediaplayer_setvolume(int);
+
+#endif // SDLPAL_JNI_H

+ 124 - 0
android/jni/src/native_midi.c

@@ -0,0 +1,124 @@
+/*
+    native_midi_android:  Native Midi support on Android for SDLPal
+    Copyright (C) 2017  Pal Lockheart
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Pal Lockheart
+*/
+
+/* This is Android only, using MediaPlayer ( need java part work together ) */
+
+#include "android_jni.h"
+
+#include "SDL_config.h"
+
+#include "SDL.h"
+#include "SDL_endian.h"
+#include "native_midi/native_midi.h"
+
+/* Native Midi song */
+struct _NativeMidiSong
+{
+    int _placeholder;
+    int playing;
+};
+
+static NativeMidiSong *currentsong = NULL;
+static int latched_volume = 128;
+
+int native_midi_detect()
+{
+    return 1;  /* always available. */
+}
+
+NativeMidiSong *native_midi_loadsong(const char *midifile)
+{
+    NativeMidiSong *retval = (NativeMidiSong *)malloc(sizeof(NativeMidiSong));
+    if (retval)
+    {
+        memset(retval, 0, sizeof(NativeMidiSong));
+        JNI_mediaplayer_load(midifile);
+    }
+    return retval;
+}
+
+NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
+{
+    NativeMidiSong *retval = (NativeMidiSong *)malloc(sizeof(NativeMidiSong));
+    if (retval)
+    {
+        FILE *fp = fopen(midiInterFile, "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);
+
+            memset(retval, 0, sizeof(NativeMidiSong));
+            JNI_mediaplayer_load(midiInterFile);
+        }
+    }
+    return retval;
+}
+
+void native_midi_freesong(NativeMidiSong *song)
+{
+    if (song != NULL)
+    {
+        if (currentsong == song)
+            currentsong = NULL;
+        free(song);
+    }
+}
+
+void native_midi_start(NativeMidiSong *song)
+{
+    native_midi_stop();
+    if (song != NULL)
+    {
+        currentsong = song;
+        currentsong->playing = 1;
+        JNI_mediaplayer_play();
+    }
+}
+
+void native_midi_stop()
+{
+    if (currentsong) {
+        currentsong->playing = 0;
+        JNI_mediaplayer_stop();
+    }
+}
+
+int native_midi_active()
+{
+    return currentsong ? currentsong->playing : 0;
+}
+
+void native_midi_setvolume(int volume)
+{
+    if (latched_volume != volume)
+    {
+        JNI_mediaplayer_setvolume(latched_volume = volume);
+    }
+}
+
+const char *native_midi_error(void)
+{
+    return "";  /* !!! FIXME */
+}

+ 6 - 0
android/jni/src/pal_config.h

@@ -19,6 +19,12 @@
 # define PAL_CREDIT           "Rikku2000"
 # define PAL_PORTYEAR         "2013"
 
+#define PAL_HAS_NATIVEMIDI  1
+
+#define PAL_HAS_CONFIG_PAGE 0
+
+#define PAL_FILESYSTEM_IGNORE_CASE 1
+
 # define PAL_IS_VALID_JOYSTICK(s)  (strcmp((s), "Android Accelerometer") != 0)
 
 LPCSTR

+ 0 - 19
android/src/com/codeplex/sdlpal/PalActivity.java

@@ -1,19 +0,0 @@
-package com.codeplex.sdlpal;
-
-import org.libsdl.app.SDLActivity;
-import android.os.*;
-import android.util.*;
-
-public class PalActivity extends SDLActivity {
-    public static native void setExternalStorage(String str);  
-    @Override  
-    public void onCreate(Bundle savedInstanceState) {  
-        super.onCreate(savedInstanceState);
-//         setContentView(R.layout.main);
-        String externalStorageState = Environment.getExternalStorageState();
-        if(externalStorageState.equals(Environment.MEDIA_MOUNTED)){
-            setExternalStorage(Environment.getExternalStorageDirectory().getPath());
-            Log.v("sdlpal-debug", "sdcard path " + Environment.getExternalStorageDirectory().getPath());
-        }
-    }
-}

+ 57 - 0
android/src/io/github/sdlpal/PalActivity.java

@@ -0,0 +1,57 @@
+package io.github.sdlpal;
+
+import org.libsdl.app.SDLActivity;
+import android.os.*;
+import android.util.*;
+import android.media.*;
+import android.net.Uri;
+import java.io.*;
+
+public class PalActivity extends SDLActivity {
+    private static final String TAG = "sdlpal-debug";
+    private static final MediaPlayer mediaPlayer = new MediaPlayer();
+
+    private static void JNI_mediaplayer_load(String filename){
+        Log.v(TAG, "loading midi:" + filename);
+        mediaPlayer.reset();
+        mediaPlayer.setLooping(true);
+        try {
+            mediaPlayer.setDataSource(mSingleton.getApplicationContext(), Uri.fromFile(new File(filename)));
+            mediaPlayer.prepare();
+        } catch(IOException e) {
+            Log.e(TAG, filename + " not available for playing, check");
+        }
+    }
+
+    private static void JNI_mediaplayer_play() {
+        mediaPlayer.start();
+    }
+
+    private static void JNI_mediaplayer_stop() {
+        mediaPlayer.stop();
+    }
+
+    private static int JNI_mediaplayer_playing() {
+        return mediaPlayer.isPlaying() ? 1 : 0;
+    }
+
+    private static void JNI_mediaplayer_setvolume(int volume) {
+        mediaPlayer.setVolume((float)volume/256, (float)volume/256);
+    }
+
+    public static native void setExternalStorage(String str);
+    public static native void setMIDIInterFile(String str);
+    @Override
+    public void onCreate(Bundle savedInstanceState) {  
+        super.onCreate(savedInstanceState);
+        String appDataPath = mSingleton.getApplicationContext().getFilesDir().getPath();
+        String interFilePath = appDataPath+"/intermediates.midi";
+        Log.v(TAG, "java interfile path " + interFilePath);
+        setMIDIInterFile(interFilePath);
+        String externalStorageState = Environment.getExternalStorageState();
+        if (externalStorageState.equals(Environment.MEDIA_MOUNTED)){
+            setExternalStorage(Environment.getExternalStorageDirectory().getPath());
+            Log.v(TAG, "sdcard path " + Environment.getExternalStorageDirectory().getPath());
+        }
+    }
+}

+ 81 - 37
ios/SDLPal/SDLPal.xcodeproj/project.pbxproj

@@ -102,6 +102,10 @@
 		C622BE741E474CE2000C8970 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C622BE731E474CE2000C8970 /* GameController.framework */; };
 		C622BE761E474CFC000C8970 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C622BE751E474CFC000C8970 /* AVFoundation.framework */; };
 		C622BE771E474DF5000C8970 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = 71655251195BB6DB006E1227 /* util.c */; };
+		C63505731EA6570300186049 /* midi.c in Sources */ = {isa = PBXBuildFile; fileRef = C635056B1EA6570300186049 /* midi.c */; };
+		C63505741EA6570300186049 /* private.c in Sources */ = {isa = PBXBuildFile; fileRef = C635056C1EA6570300186049 /* private.c */; };
+		C635057F1EA6578700186049 /* native_midi_common.c in Sources */ = {isa = PBXBuildFile; fileRef = C635057B1EA6578700186049 /* native_midi_common.c */; };
+		C63505801EA6578700186049 /* native_midi_ios.c in Sources */ = {isa = PBXBuildFile; fileRef = C635057D1EA6578700186049 /* native_midi_ios.c */; };
 		C6E974A21E49C24500F76B17 /* libSDL2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C6E9749D1E49C22D00F76B17 /* libSDL2.a */; };
 /* End PBXBuildFile section */
 
@@ -342,6 +346,13 @@
 		C622BE731E474CE2000C8970 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
 		C622BE751E474CFC000C8970 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
 		C626FFBF1E5BCBE700E39DD9 /* pal_config.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = pal_config.h; path = ../../pal_config.h; sourceTree = "<group>"; };
+		C635056A1EA6570300186049 /* midi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = midi.h; path = ../../../midi.h; sourceTree = "<group>"; };
+		C635056B1EA6570300186049 /* midi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = midi.c; path = ../../../midi.c; sourceTree = "<group>"; };
+		C635056C1EA6570300186049 /* private.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = private.c; path = ../../../private.c; sourceTree = "<group>"; };
+		C635057A1EA6578700186049 /* native_midi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = native_midi.h; sourceTree = "<group>"; };
+		C635057B1EA6578700186049 /* native_midi_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = native_midi_common.c; sourceTree = "<group>"; };
+		C635057C1EA6578700186049 /* native_midi_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = native_midi_common.h; sourceTree = "<group>"; };
+		C635057D1EA6578700186049 /* native_midi_ios.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = native_midi_ios.c; path = ../iOS/SDLPal/SDLPal/native_midi_ios.c; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -523,11 +534,13 @@
 			children = (
 				C622BE4E1E474AAF000C8970 /* SDL.xcodeproj */,
 				716551E4195BB6DA006E1227 /* adplug */,
-				7165505F195BB372006E1227 /* Frameworks */,
 				71655212195BB6DA006E1227 /* libmad */,
 				57FB00F01B7A50B0005FCF4C /* liboggvorbis */,
-				7165505E195BB372006E1227 /* Products */,
+				C63505791EA6578700186049 /* native_midi */,
 				71655066195BB372006E1227 /* SDLPal */,
+				71655067195BB372006E1227 /* Supporting Files */,
+				7165505F195BB372006E1227 /* Frameworks */,
+				7165505E195BB372006E1227 /* Products */,
 			);
 			sourceTree = "<group>";
 		};
@@ -560,14 +573,12 @@
 		71655066195BB372006E1227 /* SDLPal */ = {
 			isa = PBXGroup;
 			children = (
-				C626FFBF1E5BCBE700E39DD9 /* pal_config.h */,
-				57FB015A1B7A50E0005FCF4C /* codepage.h */,
-				57FB015B1B7A50E0005FCF4C /* fontglyph.h */,
+				C63505771EA6575200186049 /* Headers */,
+				C635056B1EA6570300186049 /* midi.c */,
+				C635056C1EA6570300186049 /* private.c */,
 				57FB015C1B7A50E0005FCF4C /* mp3play.c */,
 				57FB015D1B7A50E0005FCF4C /* oggplay.c */,
-				57FB015E1B7A50E0005FCF4C /* players.h */,
 				57FB015F1B7A50E0005FCF4C /* resampler.c */,
-				57FB01601B7A50E0005FCF4C /* resampler.h */,
 				716551FF195BB6DA006E1227 /* battle.c */,
 				71655202195BB6DA006E1227 /* ending.c */,
 				71655204195BB6DA006E1227 /* fight.c */,
@@ -595,38 +606,9 @@
 				71655253195BB6DB006E1227 /* video.c */,
 				71655255195BB6DB006E1227 /* yj1.c */,
 				7165523F195BB6DB006E1227 /* rixplay.cpp */,
-				716551FE195BB6DA006E1227 /* ascii.h */,
-				71655200195BB6DA006E1227 /* battle.h */,
-				71655201195BB6DA006E1227 /* common.h */,
-				71655203195BB6DA006E1227 /* ending.h */,
-				71655205195BB6DA006E1227 /* fight.h */,
-				71655207195BB6DA006E1227 /* font.h */,
-				71655209195BB6DA006E1227 /* game.h */,
-				7165520D195BB6DA006E1227 /* global.h */,
-				7165520F195BB6DA006E1227 /* input.h */,
-				71655211195BB6DA006E1227 /* itemmenu.h */,
-				71655232195BB6DA006E1227 /* magicmenu.h */,
-				71655234195BB6DA006E1227 /* main.h */,
-				71655236195BB6DA006E1227 /* map.h */,
-				71655238195BB6DA006E1227 /* palcommon.h */,
-				7165523A195BB6DB006E1227 /* palette.h */,
-				7165523C195BB6DB006E1227 /* play.h */,
-				7165523E195BB6DB006E1227 /* res.h */,
-				71655242195BB6DB006E1227 /* rngplay.h */,
-				71655244195BB6DB006E1227 /* scene.h */,
-				71655246195BB6DB006E1227 /* script.h */,
 				C622BE641E474B5E000C8970 /* audio.c */,
-				C622BE651E474B5E000C8970 /* audio.h */,
-				7165524A195BB6DB006E1227 /* text.h */,
-				7165524C195BB6DB006E1227 /* ui.h */,
-				7165524E195BB6DB006E1227 /* uibattle.h */,
-				71655250195BB6DB006E1227 /* uigame.h */,
-				71655252195BB6DB006E1227 /* util.h */,
-				71655254195BB6DB006E1227 /* video.h */,
 				C622BE6A1E474C01000C8970 /* palcfg.c */,
-				C622BE6B1E474C01000C8970 /* palcfg.h */,
 				C622BE6F1E474CA9000C8970 /* overlay.c */,
-				71655067195BB372006E1227 /* Supporting Files */,
 			);
 			path = SDLPal;
 			sourceTree = "<group>";
@@ -642,6 +624,7 @@
 				71655076195BB372006E1227 /* Default-568h@2x.png */,
 			);
 			name = "Supporting Files";
+			path = SDLPal;
 			sourceTree = "<group>";
 		};
 		716551E4195BB6DA006E1227 /* adplug */ = {
@@ -715,6 +698,59 @@
 			path = ../../libmad;
 			sourceTree = "<group>";
 		};
+		C63505771EA6575200186049 /* Headers */ = {
+			isa = PBXGroup;
+			children = (
+				C626FFBF1E5BCBE700E39DD9 /* pal_config.h */,
+				C635056A1EA6570300186049 /* midi.h */,
+				57FB015A1B7A50E0005FCF4C /* codepage.h */,
+				57FB015B1B7A50E0005FCF4C /* fontglyph.h */,
+				57FB015E1B7A50E0005FCF4C /* players.h */,
+				57FB01601B7A50E0005FCF4C /* resampler.h */,
+				716551FE195BB6DA006E1227 /* ascii.h */,
+				71655200195BB6DA006E1227 /* battle.h */,
+				71655201195BB6DA006E1227 /* common.h */,
+				71655203195BB6DA006E1227 /* ending.h */,
+				71655205195BB6DA006E1227 /* fight.h */,
+				71655207195BB6DA006E1227 /* font.h */,
+				71655209195BB6DA006E1227 /* game.h */,
+				7165520D195BB6DA006E1227 /* global.h */,
+				7165520F195BB6DA006E1227 /* input.h */,
+				71655211195BB6DA006E1227 /* itemmenu.h */,
+				71655232195BB6DA006E1227 /* magicmenu.h */,
+				71655234195BB6DA006E1227 /* main.h */,
+				71655236195BB6DA006E1227 /* map.h */,
+				71655238195BB6DA006E1227 /* palcommon.h */,
+				7165523A195BB6DB006E1227 /* palette.h */,
+				7165523C195BB6DB006E1227 /* play.h */,
+				7165523E195BB6DB006E1227 /* res.h */,
+				71655242195BB6DB006E1227 /* rngplay.h */,
+				71655244195BB6DB006E1227 /* scene.h */,
+				71655246195BB6DB006E1227 /* script.h */,
+				C622BE651E474B5E000C8970 /* audio.h */,
+				7165524A195BB6DB006E1227 /* text.h */,
+				7165524C195BB6DB006E1227 /* ui.h */,
+				7165524E195BB6DB006E1227 /* uibattle.h */,
+				71655250195BB6DB006E1227 /* uigame.h */,
+				71655252195BB6DB006E1227 /* util.h */,
+				71655254195BB6DB006E1227 /* video.h */,
+				C622BE6B1E474C01000C8970 /* palcfg.h */,
+			);
+			name = Headers;
+			sourceTree = "<group>";
+		};
+		C63505791EA6578700186049 /* native_midi */ = {
+			isa = PBXGroup;
+			children = (
+				C635057A1EA6578700186049 /* native_midi.h */,
+				C635057B1EA6578700186049 /* native_midi_common.c */,
+				C635057C1EA6578700186049 /* native_midi_common.h */,
+				C635057D1EA6578700186049 /* native_midi_ios.c */,
+			);
+			name = native_midi;
+			path = ../../native_midi;
+			sourceTree = "<group>";
+		};
 		C6E974971E49C22D00F76B17 /* Products */ = {
 			isa = PBXGroup;
 			children = (
@@ -856,15 +892,19 @@
 				71655267195BB6DB006E1227 /* game.c in Sources */,
 				C622BE661E474B5E000C8970 /* audio.c in Sources */,
 				71655269195BB6DB006E1227 /* global.c in Sources */,
+				C635057F1EA6578700186049 /* native_midi_common.c in Sources */,
 				7165526A195BB6DB006E1227 /* input.c in Sources */,
 				7165526B195BB6DB006E1227 /* itemmenu.c in Sources */,
 				7165526C195BB6DB006E1227 /* bit.c in Sources */,
 				7165526E195BB6DB006E1227 /* decoder.c in Sources */,
+				C63505731EA6570300186049 /* midi.c in Sources */,
 				57FB01481B7A50B0005FCF4C /* floor0.c in Sources */,
 				7165526F195BB6DB006E1227 /* fixed.c in Sources */,
+				C63505801EA6578700186049 /* native_midi_ios.c in Sources */,
 				71655270195BB6DB006E1227 /* frame.c in Sources */,
 				71655271195BB6DB006E1227 /* huffman.c in Sources */,
 				71655273195BB6DB006E1227 /* layer12.c in Sources */,
+				C63505741EA6570300186049 /* private.c in Sources */,
 				71655274195BB6DB006E1227 /* layer3.c in Sources */,
 				71655275195BB6DB006E1227 /* music_mad.c in Sources */,
 				71655279195BB6DB006E1227 /* stream.c in Sources */,
@@ -978,6 +1018,7 @@
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"__IOS__=1",
 					"DEBUG=1",
+					"MAC_OS_X_VERSION_MIN_REQUIRED=1060",
 				);
 				HEADER_SEARCH_PATHS = (
 					../../SDL2/include,
@@ -999,7 +1040,10 @@
 				DEVELOPMENT_TEAM = "";
 				GCC_PRECOMPILE_PREFIX_HEADER = NO;
 				GCC_PREFIX_HEADER = "";
-				GCC_PREPROCESSOR_DEFINITIONS = "__IOS__=1";
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"__IOS__=1",
+					"MAC_OS_X_VERSION_MIN_REQUIRED=1060",
+				);
 				HEADER_SEARCH_PATHS = (
 					../../SDL2/include,
 					../../,

+ 316 - 0
ios/SDLPal/SDLPal/native_midi_ios.c

@@ -0,0 +1,316 @@
+/*  
+    native_midi_macosx:  Native Midi support on Mac OS X for the SDL_mixer library
+    Copyright (C) 2009  Ryan C. Gordon
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Ryan C. Gordon
+    icculus@icculus.org
+*/
+
+#ifdef __APPLE__
+
+/* This is Mac OS X only, using Core MIDI.
+   Mac OS 9 support via QuickTime is in native_midi_mac.c */
+
+#include "SDL_config.h"
+
+#include <AudioToolbox/AudioToolbox.h>
+#include <AvailabilityMacros.h>
+
+#include "SDL.h"
+#include "SDL_endian.h"
+#include "native_midi.h"
+
+/* Native Midi song */
+struct _NativeMidiSong
+{
+    MusicPlayer player;
+    MusicSequence sequence;
+    MusicTimeStamp endTime;
+    AudioUnit audiounit;
+};
+
+static NativeMidiSong *currentsong = NULL;
+static int latched_volume = 128;
+
+static OSStatus
+GetSequenceLength(MusicSequence sequence, MusicTimeStamp *_sequenceLength)
+{
+    // http://lists.apple.com/archives/Coreaudio-api/2003/Jul/msg00370.html
+    // figure out sequence length
+    UInt32 ntracks, i;
+    MusicTimeStamp sequenceLength = 0;
+    OSStatus err;
+
+    err = MusicSequenceGetTrackCount(sequence, &ntracks);
+    if (err != noErr)
+        return err;
+
+    for (i = 0; i < ntracks; ++i)
+    {
+        MusicTrack track;
+        MusicTimeStamp tracklen = 0;
+        UInt32 tracklenlen = sizeof (tracklen);
+
+        err = MusicSequenceGetIndTrack(sequence, i, &track);
+        if (err != noErr)
+            return err;
+
+        err = MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength,
+                                    &tracklen, &tracklenlen);
+        if (err != noErr)
+            return err;
+
+        if (sequenceLength < tracklen)
+            sequenceLength = tracklen;
+    }
+
+    *_sequenceLength = sequenceLength;
+
+    return noErr;
+}
+
+
+/* we're looking for the sequence output audiounit. */
+static OSStatus
+GetSequenceAudioUnit(MusicSequence sequence, AudioUnit *aunit)
+{
+    AUGraph graph;
+    UInt32 nodecount, i;
+    OSStatus err;
+
+    err = MusicSequenceGetAUGraph(sequence, &graph);
+    if (err != noErr)
+        return err;
+
+    err = AUGraphGetNodeCount(graph, &nodecount);
+    if (err != noErr)
+        return err;
+
+    for (i = 0; i < nodecount; i++) {
+        AUNode node;
+
+        if (AUGraphGetIndNode(graph, i, &node) != noErr)
+            continue;  /* better luck next time. */
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 /* this is deprecated, but works back to 10.0 */
+        {
+            struct ComponentDescription desc;
+            UInt32 classdatasize = 0;
+            void *classdata = NULL;
+            err = AUGraphGetNodeInfo(graph, node, &desc, &classdatasize,
+                                     &classdata, aunit);
+            if (err != noErr)
+                continue;
+            else if (desc.componentType != kAudioUnitType_Output)
+                continue;
+            else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput)
+                continue;
+        }
+        #else  /* not deprecated, but requires 10.5 or later */
+        {
+            AudioComponentDescription desc;
+            if (AUGraphNodeInfo(graph, node, &desc, aunit) != noErr)
+                continue;
+            else if (desc.componentType != kAudioUnitType_Output)
+                continue;
+            else if (desc.componentSubType != kAudioUnitSubType_GenericOutput)
+                continue;
+        }
+        #endif
+
+        return noErr;  /* found it! */
+    }
+
+    return kAUGraphErr_NodeNotFound;
+}
+
+
+int native_midi_detect()
+{
+    return 1;  /* always available. */
+}
+
+NativeMidiSong *native_midi_loadsong(const char *midifile)
+{
+    NativeMidiSong *retval = NULL;
+    SDL_RWops *rw = SDL_RWFromFile(midifile, "rb");
+    if (rw != NULL) {
+        retval = native_midi_loadsong_RW(rw);
+        SDL_RWclose(rw);
+    }
+
+    return retval;
+}
+
+NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
+{
+    NativeMidiSong *retval = NULL;
+    void *buf = NULL;
+    int len = 0;
+    CFDataRef data = NULL;
+
+    if (SDL_RWseek(rw, 0, RW_SEEK_END) < 0)
+        goto fail;
+    len = SDL_RWtell(rw);
+    if (len < 0)
+        goto fail;
+    if (SDL_RWseek(rw, 0, RW_SEEK_SET) < 0)
+        goto fail;
+
+    buf = malloc(len);
+    if (buf == NULL)
+        goto fail;
+
+    if (SDL_RWread(rw, buf, len, 1) != 1)
+        goto fail;
+
+    retval = malloc(sizeof(NativeMidiSong));
+    if (retval == NULL)
+        goto fail;
+
+    memset(retval, '\0', sizeof (*retval));
+
+    if (NewMusicPlayer(&retval->player) != noErr)
+        goto fail;
+    if (NewMusicSequence(&retval->sequence) != noErr)
+        goto fail;
+
+    data = CFDataCreate(NULL, (const UInt8 *) buf, len);
+    if (data == NULL)
+        goto fail;
+
+    free(buf);
+    buf = NULL;
+
+    #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 /* this is deprecated, but works back to 10.3 */
+    if (MusicSequenceLoadSMFDataWithFlags(retval->sequence, data, 0) != noErr)
+        goto fail;
+    #else  /* not deprecated, but requires 10.5 or later */
+    if (MusicSequenceFileLoadData(retval->sequence, data, 0, 0) != noErr)
+        goto fail;
+    #endif
+
+    CFRelease(data);
+    data = NULL;
+
+    if (GetSequenceLength(retval->sequence, &retval->endTime) != noErr)
+        goto fail;
+
+    if (MusicPlayerSetSequence(retval->player, retval->sequence) != noErr)
+        goto fail;
+
+    return retval;
+
+fail:
+    if (retval) {
+        if (retval->sequence)
+            DisposeMusicSequence(retval->sequence);
+        if (retval->player)
+            DisposeMusicPlayer(retval->player);
+        free(retval);
+    }
+
+    if (data)
+        CFRelease(data);
+
+    if (buf)
+        free(buf);
+
+    return NULL;
+}
+
+void native_midi_freesong(NativeMidiSong *song)
+{
+    if (song != NULL) {
+        if (currentsong == song)
+            currentsong = NULL;
+        MusicPlayerStop(song->player);
+        DisposeMusicSequence(song->sequence);
+        DisposeMusicPlayer(song->player);
+        free(song);
+    }
+}
+
+void native_midi_start(NativeMidiSong *song)
+{
+    int vol;
+
+    if (song == NULL)
+        return;
+
+    SDL_PauseAudio(1);
+    SDL_UnlockAudio();
+
+    if (currentsong)
+        MusicPlayerStop(currentsong->player);
+
+    currentsong = song;
+    MusicPlayerStart(song->player);
+
+    GetSequenceAudioUnit(song->sequence, &song->audiounit);
+
+    vol = latched_volume;
+    latched_volume++;  /* just make this not match. */
+    native_midi_setvolume(vol);
+
+    SDL_LockAudio();
+    SDL_PauseAudio(0);
+}
+
+void native_midi_stop()
+{
+    if (currentsong) {
+        SDL_PauseAudio(1);
+        SDL_UnlockAudio();
+        MusicPlayerStop(currentsong->player);
+        currentsong = NULL;
+        SDL_LockAudio();
+        SDL_PauseAudio(0);
+    }
+}
+
+int native_midi_active()
+{
+    MusicTimeStamp currentTime = 0;
+    if (currentsong == NULL)
+        return 0;
+
+    MusicPlayerGetTime(currentsong->player, &currentTime);
+    return ((currentTime < currentsong->endTime) ||
+            (currentTime >= kMusicTimeStamp_EndOfTrack));
+}
+
+void native_midi_setvolume(int volume)
+{
+    if (latched_volume == volume)
+        return;
+
+    latched_volume = volume;
+    if ((currentsong) && (currentsong->audiounit)) {
+        const float floatvol = ((float) volume) / ((float) 128);
+        AudioUnitSetParameter(currentsong->audiounit, kHALOutputParam_Volume,
+                              kAudioUnitScope_Global, 0, floatvol, 0);
+    }
+}
+
+const char *native_midi_error(void)
+{
+    return "";  /* !!! FIXME */
+}
+
+#endif
+

+ 4 - 0
ios/pal_config.h

@@ -21,6 +21,10 @@
 
 # define PAL_IS_VALID_JOYSTICK(s)  (strcmp((s), "applesmc") != 0)
 
+#define PAL_HAS_NATIVEMIDI  1
+
+#define PAL_HAS_CONFIG_PAGE 0
+
 LPCSTR
 UTIL_BasePath(
    VOID

+ 86 - 34
macos/Pal.xcodeproj/project.pbxproj

@@ -94,6 +94,12 @@
 		8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
 		8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
 		C602B0021CEF3E6C003A7B09 /* palcfg.c in Sources */ = {isa = PBXBuildFile; fileRef = C602B0011CEF3E6C003A7B09 /* palcfg.c */; };
+		C61464681EA6425700A76CC9 /* native_midi_common.c in Sources */ = {isa = PBXBuildFile; fileRef = C61464641EA6425700A76CC9 /* native_midi_common.c */; };
+		C61464691EA6425700A76CC9 /* native_midi_macosx.c in Sources */ = {isa = PBXBuildFile; fileRef = C61464661EA6425700A76CC9 /* native_midi_macosx.c */; };
+		C614646C1EA642F200A76CC9 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C614646B1EA642F200A76CC9 /* AudioToolbox.framework */; };
+		C614646E1EA6430100A76CC9 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C614646D1EA6430100A76CC9 /* CoreMIDI.framework */; };
+		C61464741EA643BD00A76CC9 /* midi.c in Sources */ = {isa = PBXBuildFile; fileRef = C61464701EA643BD00A76CC9 /* midi.c */; };
+		C61464751EA643BD00A76CC9 /* private.c in Sources */ = {isa = PBXBuildFile; fileRef = C61464731EA643BD00A76CC9 /* private.c */; };
 		C626003D1E62058F00E39DD9 /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = C626003C1E62058F00E39DD9 /* gtest-all.cc */; };
 		C62600401E620C7300E39DD9 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C626FFE61E6204BE00E39DD9 /* Info.plist */; };
 		C62600431E620CE700E39DD9 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C62600421E620CE700E39DD9 /* Cocoa.framework */; };
@@ -314,6 +320,17 @@
 		8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
 		8D1107320486CEB800E47090 /* Pal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pal.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		C602B0011CEF3E6C003A7B09 /* palcfg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = palcfg.c; sourceTree = "<group>"; };
+		C61464631EA6425700A76CC9 /* native_midi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = native_midi.h; sourceTree = "<group>"; };
+		C61464641EA6425700A76CC9 /* native_midi_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = native_midi_common.c; sourceTree = "<group>"; };
+		C61464651EA6425700A76CC9 /* native_midi_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = native_midi_common.h; sourceTree = "<group>"; };
+		C61464661EA6425700A76CC9 /* native_midi_macosx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = native_midi_macosx.c; path = ../macos/native_midi_macosx.c; sourceTree = "<group>"; };
+		C614646B1EA642F200A76CC9 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
+		C614646D1EA6430100A76CC9 /* CoreMIDI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMIDI.framework; path = System/Library/Frameworks/CoreMIDI.framework; sourceTree = SDKROOT; };
+		C614646F1EA643BD00A76CC9 /* audio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio.h; sourceTree = "<group>"; };
+		C61464701EA643BD00A76CC9 /* midi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = midi.c; sourceTree = "<group>"; };
+		C61464711EA643BD00A76CC9 /* midi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = midi.h; sourceTree = "<group>"; };
+		C61464721EA643BD00A76CC9 /* palcfg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = palcfg.h; sourceTree = "<group>"; };
+		C61464731EA643BD00A76CC9 /* private.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = private.c; sourceTree = "<group>"; };
 		C626003C1E62058F00E39DD9 /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "../../3rd/googletest/googletest/src/gtest-all.cc"; sourceTree = "<group>"; };
 		C626003E1E6206AA00E39DD9 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
 		C62600421E620CE700E39DD9 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
@@ -331,6 +348,8 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				C614646E1EA6430100A76CC9 /* CoreMIDI.framework in Frameworks */,
+				C614646C1EA642F200A76CC9 /* AudioToolbox.framework in Frameworks */,
 				8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
 				717AE630182663E100B10A11 /* SDL2.framework in Frameworks */,
 			);
@@ -381,7 +400,9 @@
 				5757D21E1B7A3D5800464A74 /* liboggvorbis */,
 				71E23E7013F6D188001287B6 /* libmad */,
 				7104FD990D772FAA00A97E53 /* adplug */,
-				29B97315FDCFA39411CA2CEA /* Other Sources */,
+				C61464621EA6425700A76CC9 /* native_midi */,
+				C61464761EA643E600A76CC9 /* Headers */,
+				29B97315FDCFA39411CA2CEA /* Sources */,
 				29B97317FDCFA39411CA2CEA /* Resources */,
 				C626FFE31E6204BE00E39DD9 /* PalTests */,
 				29B97323FDCFA39411CA2CEA /* Frameworks */,
@@ -390,10 +411,9 @@
 			name = Pal;
 			sourceTree = "<group>";
 		};
-		29B97315FDCFA39411CA2CEA /* Other Sources */ = {
+		29B97315FDCFA39411CA2CEA /* Sources */ = {
 			isa = PBXGroup;
 			children = (
-				C626FFC31E5BD49100E39DD9 /* pal_config.h */,
 				7104FD3A0D772F6300A97E53 /* battle.c */,
 				71F0F6D10DAA63B400F88C16 /* ending.c */,
 				71E27E030D8C7E2F0048BA16 /* fight.c */,
@@ -424,41 +444,13 @@
 				7104FD660D772F6300A97E53 /* video.c */,
 				7104FD680D772F6300A97E53 /* yj1.c */,
 				7104FD520D772F6300A97E53 /* rixplay.cpp */,
-				7104FD390D772F6300A97E53 /* ascii.h */,
-				7104FD3B0D772F6300A97E53 /* battle.h */,
-				5757D2981B7A3E0300464A74 /* codepage.h */,
-				7104FD3C0D772F6300A97E53 /* common.h */,
-				71F0F6D20DAA63B400F88C16 /* ending.h */,
-				71E27E040D8C7E2F0048BA16 /* fight.h */,
-				7104FD3E0D772F6300A97E53 /* font.h */,
-				5757D2991B7A3E0300464A74 /* fontglyph.h */,
-				7104FD400D772F6300A97E53 /* game.h */,
-				7104FD440D772F6300A97E53 /* global.h */,
-				7104FD460D772F6300A97E53 /* input.h */,
-				71F0F6D40DAA63B400F88C16 /* itemmenu.h */,
-				71F0F6D60DAA63B400F88C16 /* magicmenu.h */,
-				7104FD470D772F6300A97E53 /* main.h */,
-				7104FD490D772F6300A97E53 /* map.h */,
-				7104FD4B0D772F6300A97E53 /* palcommon.h */,
-				7104FD4D0D772F6300A97E53 /* palette.h */,
-				7104FD4F0D772F6300A97E53 /* play.h */,
-				5757D2921B7A3DCE00464A74 /* players.h */,
-				7104FD510D772F6300A97E53 /* res.h */,
-				5757D2941B7A3DCE00464A74 /* resampler.h */,
-				7104FD550D772F6300A97E53 /* rngplay.h */,
-				7104FD570D772F6300A97E53 /* scene.h */,
-				7104FD590D772F6300A97E53 /* script.h */,
-				7104FD5D0D772F6300A97E53 /* text.h */,
-				7104FD5F0D772F6300A97E53 /* ui.h */,
-				7104FD610D772F6300A97E53 /* uibattle.h */,
-				7104FD630D772F6300A97E53 /* uigame.h */,
-				7104FD650D772F6300A97E53 /* util.h */,
-				7104FD670D772F6300A97E53 /* video.h */,
 				C602B0011CEF3E6C003A7B09 /* palcfg.c */,
 				C65BC11C1CFAF1780037E9A2 /* overlay.c */,
 				C65BC11E1CFAF7790037E9A2 /* audio.c */,
+				C61464701EA643BD00A76CC9 /* midi.c */,
+				C61464731EA643BD00A76CC9 /* private.c */,
 			);
-			name = "Other Sources";
+			name = Sources;
 			path = ..;
 			sourceTree = "<group>";
 		};
@@ -475,6 +467,8 @@
 		29B97323FDCFA39411CA2CEA /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				C614646D1EA6430100A76CC9 /* CoreMIDI.framework */,
+				C614646B1EA642F200A76CC9 /* AudioToolbox.framework */,
 				C62600421E620CE700E39DD9 /* Cocoa.framework */,
 				C626003E1E6206AA00E39DD9 /* XCTest.framework */,
 				1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
@@ -706,6 +700,60 @@
 			path = ..;
 			sourceTree = "<group>";
 		};
+		C61464621EA6425700A76CC9 /* native_midi */ = {
+			isa = PBXGroup;
+			children = (
+				C61464631EA6425700A76CC9 /* native_midi.h */,
+				C61464641EA6425700A76CC9 /* native_midi_common.c */,
+				C61464651EA6425700A76CC9 /* native_midi_common.h */,
+				C61464661EA6425700A76CC9 /* native_midi_macosx.c */,
+			);
+			name = native_midi;
+			path = ../native_midi;
+			sourceTree = "<group>";
+		};
+		C61464761EA643E600A76CC9 /* Headers */ = {
+			isa = PBXGroup;
+			children = (
+				C626FFC31E5BD49100E39DD9 /* pal_config.h */,
+				7104FD390D772F6300A97E53 /* ascii.h */,
+				7104FD3B0D772F6300A97E53 /* battle.h */,
+				5757D2981B7A3E0300464A74 /* codepage.h */,
+				7104FD3C0D772F6300A97E53 /* common.h */,
+				71F0F6D20DAA63B400F88C16 /* ending.h */,
+				71E27E040D8C7E2F0048BA16 /* fight.h */,
+				7104FD3E0D772F6300A97E53 /* font.h */,
+				5757D2991B7A3E0300464A74 /* fontglyph.h */,
+				7104FD400D772F6300A97E53 /* game.h */,
+				7104FD440D772F6300A97E53 /* global.h */,
+				7104FD460D772F6300A97E53 /* input.h */,
+				71F0F6D40DAA63B400F88C16 /* itemmenu.h */,
+				71F0F6D60DAA63B400F88C16 /* magicmenu.h */,
+				7104FD470D772F6300A97E53 /* main.h */,
+				7104FD490D772F6300A97E53 /* map.h */,
+				7104FD4B0D772F6300A97E53 /* palcommon.h */,
+				7104FD4D0D772F6300A97E53 /* palette.h */,
+				7104FD4F0D772F6300A97E53 /* play.h */,
+				5757D2921B7A3DCE00464A74 /* players.h */,
+				7104FD510D772F6300A97E53 /* res.h */,
+				5757D2941B7A3DCE00464A74 /* resampler.h */,
+				7104FD550D772F6300A97E53 /* rngplay.h */,
+				7104FD570D772F6300A97E53 /* scene.h */,
+				7104FD590D772F6300A97E53 /* script.h */,
+				7104FD5D0D772F6300A97E53 /* text.h */,
+				7104FD5F0D772F6300A97E53 /* ui.h */,
+				7104FD610D772F6300A97E53 /* uibattle.h */,
+				7104FD630D772F6300A97E53 /* uigame.h */,
+				7104FD650D772F6300A97E53 /* util.h */,
+				7104FD670D772F6300A97E53 /* video.h */,
+				C614646F1EA643BD00A76CC9 /* audio.h */,
+				C61464711EA643BD00A76CC9 /* midi.h */,
+				C61464721EA643BD00A76CC9 /* palcfg.h */,
+			);
+			name = Headers;
+			path = ..;
+			sourceTree = "<group>";
+		};
 		C626FFE31E6204BE00E39DD9 /* PalTests */ = {
 			isa = PBXGroup;
 			children = (
@@ -842,6 +890,7 @@
 				7104FD840D772F6300A97E53 /* rngplay.c in Sources */,
 				7104FD860D772F6300A97E53 /* scene.c in Sources */,
 				7104FD880D772F6300A97E53 /* script.c in Sources */,
+				C61464691EA6425700A76CC9 /* native_midi_macosx.c in Sources */,
 				7104FD8A0D772F6300A97E53 /* sound.c in Sources */,
 				5757D2801B7A3D5800464A74 /* registry.c in Sources */,
 				7104FD8C0D772F6300A97E53 /* text.c in Sources */,
@@ -854,6 +903,7 @@
 				7104FDA90D772FBC00A97E53 /* binfile.cpp in Sources */,
 				7104FDAB0D772FBC00A97E53 /* binio.cpp in Sources */,
 				5757D27A1B7A3D5800464A74 /* lookup.c in Sources */,
+				C61464681EA6425700A76CC9 /* native_midi_common.c in Sources */,
 				7104FDAD0D772FBC00A97E53 /* emuopl.cpp in Sources */,
 				5757D27D1B7A3D5800464A74 /* mapping0.c in Sources */,
 				5757D2861B7A3D5800464A74 /* vorbisfile.c in Sources */,
@@ -862,12 +912,14 @@
 				7104FDB10D772FBC00A97E53 /* fprovide.cpp in Sources */,
 				5757D28F1B7A3D8100464A74 /* demuopl.cpp in Sources */,
 				7104FDB40D772FBC00A97E53 /* player.cpp in Sources */,
+				C61464741EA643BD00A76CC9 /* midi.c in Sources */,
 				7104FDB60D772FBC00A97E53 /* rix.cpp in Sources */,
 				5757D27B1B7A3D5800464A74 /* lpc.c in Sources */,
 				5757D28D1B7A3D8100464A74 /* dbemuopl.cpp in Sources */,
 				716EB9BC0D77318900D5DE1F /* game.c in Sources */,
 				5757D2711B7A3D5800464A74 /* bitrate.c in Sources */,
 				716EB9CA0D77347B00D5DE1F /* ui.c in Sources */,
+				C61464751EA643BD00A76CC9 /* private.c in Sources */,
 				71E27E050D8C7E2F0048BA16 /* fight.c in Sources */,
 				71F0F6D70DAA63B400F88C16 /* ending.c in Sources */,
 				5757D2751B7A3D5800464A74 /* envelope.c in Sources */,

+ 1 - 2
native_midi/native_midi_macosx.c

@@ -27,7 +27,6 @@
 
 #include "SDL_config.h"
 
-#include <Carbon/Carbon.h>
 #include <AudioToolbox/AudioToolbox.h>
 #include <AvailabilityMacros.h>
 
@@ -128,7 +127,7 @@ GetSequenceAudioUnit(MusicSequence sequence, AudioUnit *aunit)
                 continue;
             else if (desc.componentType != kAudioUnitType_Output)
                 continue;
-            else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput)
+            else if (desc.componentSubType != kAudioUnitSubType_GenericOutput)
                 continue;
         }
         #endif

+ 5 - 1
macos/pal_config.h

@@ -29,6 +29,10 @@
 # define PAL_CREDIT           NULL
 # define PAL_PORTYEAR         NULL
 
-# define PAL_HAS_CONFIG_PAGE  0
+#define PAL_HAS_NATIVEMIDI  1
+
+#define PAL_HAS_CONFIG_PAGE 0
+
+#define PAL_FILESYSTEM_IGNORE_CASE 1
 
 #endif

+ 1 - 1
win32/Makefile

@@ -8,7 +8,7 @@ TEST_CCFLAGS =
 
 GTEST_DIR = ../3rd/googletest/googletest
 
-CFILES = $(wildcard ../adplug/*.c) $(wildcard ../libmad/*.c) $(wildcard ../liboggvorbis/src/*.c) $(wildcard ../*.c) $(wildcard ../native_midi/*.c)
+CFILES = $(wildcard ../adplug/*.c) $(wildcard ../libmad/*.c) $(wildcard ../liboggvorbis/src/*.c) $(wildcard ../*.c) $(wildcard ../native_midi/*.c) $(wildcard ../win32/*.c)
 CPPFILES = $(wildcard ../adplug/*.cpp) $(wildcard ../*.cpp) $(wildcard ./*.cpp)
 RCFILES = $(wildcard ./sdlpal.rc)
 OBJFILES = $(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(RCFILES:.rc=.o)

+ 1 - 1
win32/Makefile.mingw

@@ -8,7 +8,7 @@ TEST_CCFLAGS =
 
 GTEST_DIR = ../3rd/googletest/googletest
 
-CFILES = $(wildcard ../adplug/*.c) $(wildcard ../libmad/*.c) $(wildcard ../liboggvorbis/src/*.c) $(wildcard ../*.c) $(wildcard ../native_midi/*.c)
+CFILES = $(wildcard ../adplug/*.c) $(wildcard ../libmad/*.c) $(wildcard ../liboggvorbis/src/*.c) $(wildcard ../*.c) $(wildcard ../native_midi/*.c) $(wildcard ../win32/*.c)
 CPPFILES = $(wildcard ../adplug/*.cpp) $(wildcard ../*.cpp) $(wildcard ./*.cpp)
 RCFILES = $(wildcard ./sdlpal.rc)
 OBJFILES = $(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(RCFILES:.rc=.o)

+ 3 - 7
native_midi/native_midi_win32.c

@@ -20,8 +20,6 @@
     florian.proff.schulze@gmx.net
 */
 
-#ifdef WIN32
-
 #include "SDL.h"
 
 /* everything below is currently one very big bad hack ;) Proff */
@@ -33,9 +31,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
-#include "native_midi.h"
-#include "native_midi_common.h"
-
+#include "native_midi/native_midi.h"
+#include "native_midi/native_midi_common.h"
+
 struct _NativeMidiSong {
   int MusicLoaded;
   int MusicPlaying;
@@ -318,5 +316,3 @@ const char *native_midi_error(void)
   return "";
 }
 
-#endif
-

+ 0 - 2
win32/pal_config.h

@@ -31,5 +31,3 @@
 #define PAL_HAS_CONFIG_PAGE 1
 
 #define PAL_FILESYSTEM_IGNORE_CASE 1
-
-#define PAL_HAS_NATIVEMIDI  1

+ 1 - 1
win32/sdlpal.vcxproj

@@ -360,7 +360,6 @@
     <ClCompile Include="..\adplug\rix.cpp" />
     <ClCompile Include="..\adplug\surroundopl.cpp" />
     <ClCompile Include="..\native_midi\native_midi_common.c" />
-    <ClCompile Include="..\native_midi\native_midi_win32.c" />
     <ClCompile Include="..\libmad\bit.c" />
     <ClCompile Include="..\libmad\decoder.c" />
     <ClCompile Include="..\libmad\fixed.c" />
@@ -372,6 +371,7 @@
     <ClCompile Include="..\libmad\stream.c" />
     <ClCompile Include="..\libmad\synth.c" />
     <ClCompile Include="..\libmad\timer.c" />
+    <ClCompile Include="native_midi.c" />
     <ClCompile Include="win32.cpp" />
   </ItemGroup>
   <ItemGroup>

+ 3 - 3
win32/sdlpal.vcxproj.filters

@@ -173,9 +173,6 @@
     <ClCompile Include="..\native_midi\native_midi_common.c">
       <Filter>native_midi</Filter>
     </ClCompile>
-    <ClCompile Include="..\native_midi\native_midi_win32.c">
-      <Filter>native_midi</Filter>
-    </ClCompile>
     <ClCompile Include="..\libmad\bit.c">
       <Filter>libmad</Filter>
     </ClCompile>
@@ -326,6 +323,9 @@
     <ClCompile Include="..\tests\test_swprintf.cpp">
       <Filter>Test Files</Filter>
     </ClCompile>
+    <ClCompile Include="native_midi.c">
+      <Filter>native_midi</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\ascii.h">

+ 29 - 7
winrt/SDLPal.Common/AsyncHelper.h

@@ -1,10 +1,32 @@
-/*
-*   AsyncHelper.h
-*
-*   Date: 1st July, 2014   Author: David Huang
-*   (C) 2014 Light Studio. All Rights Reserved.
-*   Modified by louyihua@2016
-*/
+/* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
+//
+// AsyncHelper.h: UWP support library for SDLPal.
+//
+// Original author: David Huang @ 1st July, 2014
+//                  (C) 2014 Light Studio. All Rights Reserved.
+//
+// Modified by: Lou Yihua @ 2016
+//
+// Copyright (c) 2009-2011, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// Copyright (c) 2011-2017 SDLPAL development team.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
 #pragma once
 #include <ppltasks.h>
 #include <Windows.h>

+ 1 - 0
winrt/SDLPal.Common/MainPage.xaml

@@ -44,6 +44,7 @@
                     <ComboBoxItem Content="OGG"/>
                 </ComboBox>
                 <ComboBox x:Name="cbBGM" x:Uid="BGM" HorizontalAlignment="Stretch" Header="背景音乐格式" PlaceholderText="背景音乐格式" SelectionChanged="cbBGM_SelectionChanged">
+                    <ComboBoxItem Content="MIDI"/>
                     <ComboBoxItem Content="RIX"/>
                     <ComboBoxItem Content="MP3"/>
                     <ComboBoxItem Content="OGG"/>

+ 3 - 3
winrt/SDLPal.Common/MainPage.xaml.cpp

@@ -54,7 +54,7 @@ void SDLPal::MainPage::LoadControlContents()
 	slQuality->Value = gConfig.iResampleQuality;
 
 	cbCD->SelectedIndex = (gConfig.eCDType == MUSIC_MP3) ? 0 : 1;
-	cbBGM->SelectedIndex = (gConfig.eMusicType >= MUSIC_RIX && gConfig.eMusicType <= MUSIC_OGG) ? (gConfig.eMusicType - MUSIC_RIX) : 0;
+	cbBGM->SelectedIndex = (gConfig.eMusicType <= MUSIC_OGG) ? gConfig.eMusicType : MUSIC_RIX;
 	cbOPL->SelectedIndex = (int)gConfig.eOPLType;
 
 	if (gConfig.iSampleRate <= 11025)
@@ -104,7 +104,7 @@ void SDLPal::MainPage::SaveControlContents()
 	gConfig.uCodePage = tsLanguage->IsOn ? CP_GBK : CP_BIG5;
 
 	gConfig.eCDType = (MUSICTYPE)(MUSIC_MP3 + cbCD->SelectedIndex);
-	gConfig.eMusicType = (MUSICTYPE)(MUSIC_RIX + cbBGM->SelectedIndex);
+	gConfig.eMusicType = (MUSICTYPE)cbBGM->SelectedIndex;
 	gConfig.eOPLType = (OPLTYPE)cbOPL->SelectedIndex;
 
 	gConfig.iSampleRate = wcstoul(static_cast<Platform::String^>(static_cast<ComboBoxItem^>(cbSampleRate->SelectedItem)->Content)->Data(), nullptr, 10);
@@ -114,7 +114,7 @@ void SDLPal::MainPage::SaveControlContents()
 
 void SDLPal::MainPage::cbBGM_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e)
 {
-	auto visibility = (cbBGM->SelectedIndex == 0) ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed;
+	auto visibility = (cbBGM->SelectedIndex == MUSIC_RIX) ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed;
 	cbOPL->Visibility = visibility;
 	cbOPLSR->Visibility = visibility;
 	tsSurroundOPL->Visibility = visibility;

+ 91 - 0
winrt/SDLPal.Common/NativeBuffer.h

@@ -0,0 +1,91 @@
+/* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
+//
+// NativeBuffer.h: UWP support library for SDLPal.
+// Author: Lou Yihua @ 2017
+//
+// Copyright (c) 2009-2011, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// Copyright (c) 2011-2017 SDLPAL development team.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+#pragma once
+
+#include <wrl.h>
+#include <wrl/implements.h>
+#include <windows.storage.streams.h>
+#include <robuffer.h>
+#include <stdint.h>
+
+class NativeBuffer :
+	public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::WinRtClassicComMix>,
+	ABI::Windows::Storage::Streams::IBuffer,
+	Windows::Storage::Streams::IBufferByteAccess>
+{
+public:
+	virtual ~NativeBuffer()
+	{
+	}
+
+	STDMETHODIMP RuntimeClassInitialize(byte *buffer, UINT totalSize)
+	{
+		m_length = totalSize;
+		m_buffer = buffer;
+
+		return S_OK;
+	}
+
+	STDMETHODIMP Buffer(byte **value)
+	{
+		*value = m_buffer;
+
+		return S_OK;
+	}
+
+	STDMETHODIMP get_Capacity(UINT32 *value)
+	{
+		*value = m_length;
+
+		return S_OK;
+	}
+
+	STDMETHODIMP get_Length(UINT32 *value)
+	{
+		*value = m_length;
+
+		return S_OK;
+	}
+
+	STDMETHODIMP put_Length(UINT32 value)
+	{
+		m_length = value;
+
+		return S_OK;
+	}
+
+	static Windows::Storage::Streams::IBuffer^ GetIBuffer(byte *buffer, uint32_t totalSize)
+	{
+		Microsoft::WRL::ComPtr<NativeBuffer> nativeBuffer;
+		Microsoft::WRL::Details::MakeAndInitialize<NativeBuffer>(&nativeBuffer, buffer, totalSize);
+		auto obj = reinterpret_cast<IInspectable*>(nativeBuffer.Get());
+		return reinterpret_cast<Windows::Storage::Streams::IBuffer^>(obj);
+	}
+
+private:
+	UINT32 m_length;
+	byte *m_buffer;
+};

+ 6 - 3
winrt/SDLPal.Common/SDLPal.cpp

@@ -6,6 +6,9 @@
 #include "../../global.h"
 #include "../../palcfg.h"
 #include "App.xaml.h"
+extern "C" {
+#include "../../native_midi/native_midi.h"
+}
 
 HANDLE g_eventHandle = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
 
@@ -18,7 +21,6 @@ int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
 	PAL_LoadConfig(TRUE);
 
 	bool last_crashed = false;
-
 	try
 	{
 		// Check the 'running' file to determine whether the program is abnormally terminated last time.
@@ -32,8 +34,9 @@ int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
 		}
 		AWait(file->DeleteAsync(), g_eventHandle);
 	}
-	catch(Platform::Exception^)
-	{ }
+	catch (Platform::Exception^)
+	{
+	}
 
 	if (gConfig.fLaunchSetting || last_crashed)
 	{

+ 25 - 0
winrt/SDLPal.Common/StringHelper.h

@@ -1,3 +1,28 @@
+/* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
+//
+// StringHelper.h: UWP support library for SDLPal.
+// Author: Lou Yihua @ 2016
+//
+// Copyright (c) 2009-2011, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// Copyright (c) 2011-2017 SDLPAL development team.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
 #pragma once
 
 #include <wrl.h>

+ 81 - 44
winrt/SDLPal.Common/WinRTIO.cpp

@@ -1,7 +1,31 @@
+/* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
+//
+// WinRTIO.cpp: UWP C-style file I/O wrapper for SDLPal.
+// Author: Lou Yihua @ 2016-2017
+//
+// Copyright (c) 2009-2011, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// Copyright (c) 2011-2017 SDLPAL development team.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
 #include <wrl.h>
 #include <string>
 #include <DXGI.h>
-#include <ppltasks.h>
 #include <shcore.h>
 #include <unordered_set>
 #include "AsyncHelper.h"
@@ -66,73 +90,86 @@ errno_t WRT_fopen_s(WRT_FILE ** pFile, const char * _Filename, const char * _Mod
 {
 	if (nullptr == _Filename || nullptr == _Mode || nullptr == pFile) return EINVAL;
 
-	std::wstring path;
+	auto ptr = _Filename;
+	while (*ptr == '/' || *ptr == '\\') ptr++;
+
+	Platform::String^ directory = ref new Platform::String((ptr != _Filename) ? L"\\" : L"");
 	Platform::String^ filename;
-	ConvertString(_Filename, path);
-	auto ptr = (wchar_t*)path.c_str() + path.length();
-	while (ptr >= path.c_str() && *ptr != L'/' && *ptr != L'\\') ptr--;
-	filename = ref new Platform::String(ptr + 1);
-	path = path.substr(0, ptr - path.c_str());
-	size_t offset = 0;
-	while ((offset = path.find(L'/', offset)) != std::wstring::npos)
-		path[offset++] = L'\\';
-	if (path.size() > 0)
+	while (*ptr)
 	{
-		if (path.back() == L':') path.append(L"\\");
-	}
-	else
-	{
-		path.assign(Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data());
+		std::wstring temp;
+		auto pos = ptr;
+		while (*pos && *pos != '/' && *pos != '\\') pos++;
+		if (*pos)
+		{
+			ConvertString(std::string(ptr, pos - ptr + 1), temp); temp[pos - ptr] = L'\\';
+			directory = Platform::String::Concat(directory, ref new Platform::String(temp.c_str()));
+		}
+		else
+		{
+			ConvertString(std::string(ptr, pos - ptr), temp);
+			filename = ref new Platform::String(temp.c_str());
+		}
+		while (*pos == '/' || *pos == '\\') pos++;
+		ptr = pos;
 	}
 
-	Windows::Storage::StorageFolder^ folder = nullptr;
-	Event eventHandle;
-	try
+	if (directory->Length() == 0)
 	{
-		folder = AWait(Windows::Storage::StorageFolder::GetFolderFromPathAsync(ref new Platform::String(path.c_str())), eventHandle);
+		directory = Windows::Storage::ApplicationData::Current->LocalFolder->Path;
 	}
-	catch (Platform::AccessDeniedException^)
+
+	bool r, w, b = false;
+	switch (*_Mode)
 	{
-		return EACCES;
+	case 'a': w = true; r = false; break;
+	case 'w': w = true; r = false; break;
+	case 'r': w = false; r = true; break;
+	default: delete filename; return EINVAL;
 	}
-	catch (Platform::Exception^)
+	for (size_t i = 1; i < strlen(_Mode); i++)
 	{
-		return EIO;
+		switch (_Mode[i])
+		{
+		case '+': r = w = true; break;
+		case 'b': b = true; break;
+		case 't': b = false; break;
+		default: delete filename; return EINVAL;
+		}
 	}
+	*pFile = nullptr;
 
-	WRT_FILE* ret = nullptr;
 	try
 	{
-		Windows::Storage::StorageFile^ file;
-		bool r, w;
+		Event eventHandle;
+		Windows::Storage::StorageFolder^ folder = AWait(Windows::Storage::StorageFolder::GetFolderFromPathAsync(directory), eventHandle);
+		Windows::Storage::StorageFile^ file = nullptr;
 		switch (*_Mode)
 		{
-		case 'a': file = AWait(folder->CreateFileAsync(filename, Windows::Storage::CreationCollisionOption::OpenIfExists), eventHandle); w = true; r = false; break;
-		case 'w': file = AWait(folder->CreateFileAsync(filename, Windows::Storage::CreationCollisionOption::ReplaceExisting), eventHandle); w = true; r = false; break;
-		case 'r': file = AWait(folder->GetFileAsync(filename), eventHandle); w = false; r = true; break;
-		default: CloseHandle(eventHandle); return EINVAL;
+		case 'a':
+			file = AWait(folder->CreateFileAsync(filename, Windows::Storage::CreationCollisionOption::OpenIfExists), eventHandle);
+			break;
+		case 'w':
+			file = AWait(folder->CreateFileAsync(filename, Windows::Storage::CreationCollisionOption::ReplaceExisting), eventHandle);
+			break;
+		case 'r':
+			file = AWait(folder->GetFileAsync(filename), eventHandle);
+			break;
 		}
 		if (file)
 		{
-			bool b = false;
-			for (size_t i = 1; i < strlen(_Mode); i++)
-			{
-				switch (_Mode[i])
-				{
-				case '+': r = w = true; break;
-				case 'b': b = true; break;
-				case 't': b = false; break;
-				default: return EINVAL;
-				}
-			}
-			ret = new WRT_FILE(AWait(file->OpenAsync(w ? Windows::Storage::FileAccessMode::ReadWrite : Windows::Storage::FileAccessMode::Read), eventHandle), r, w, b);
+			*pFile = new WRT_FILE(AWait(file->OpenAsync(w ? Windows::Storage::FileAccessMode::ReadWrite : Windows::Storage::FileAccessMode::Read), eventHandle), r, w, b);
 		}
 	}
+	catch (Platform::AccessDeniedException^)
+	{
+		return EACCES;
+	}
 	catch (Platform::Exception^)
 	{
 		return EIO;
 	}
-	*pFile = ret;
+
 	return 0;
 }
 

+ 1 - 0
winrt/SDLPal.UWP/SDLPal.Common.vcxproj

@@ -28,6 +28,7 @@
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\SDLPal.Common\AsyncHelper.h" />
+    <ClInclude Include="..\SDLPal.Common\NativeBuffer.h" />
     <ClInclude Include="..\SDLPal.Common\StringHelper.h" />
   </ItemGroup>
   <ItemGroup>

+ 1 - 0
winrt/SDLPal.UWP/SDLPal.Common.vcxproj.filters

@@ -3,6 +3,7 @@
   <ItemGroup>
     <ClInclude Include="..\SDLPal.Common\AsyncHelper.h" />
     <ClInclude Include="..\SDLPal.Common\StringHelper.h" />
+    <ClInclude Include="..\SDLPal.Common\NativeBuffer.h" />
   </ItemGroup>
   <ItemGroup>
     <None Include="..\SDLPal.Common\SDLPal.Common.def" />

+ 10 - 6
winrt/SDLPal.UWP/SDLPal.Core.vcxproj

@@ -132,7 +132,7 @@
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <CompileAsWinRT>false</CompileAsWinRT>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>LONGJMP_EXIT;PAL_HAS_PLATFORM_SPECIFIC_UTILS;_CRT_SECURE_NO_WARNINGS;__WINRT__=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
@@ -146,7 +146,7 @@
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <CompileAsWinRT>false</CompileAsWinRT>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>LONGJMP_EXIT;PAL_HAS_PLATFORM_SPECIFIC_UTILS;_CRT_SECURE_NO_WARNINGS;__WINRT__=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
@@ -160,7 +160,7 @@
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <CompileAsWinRT>false</CompileAsWinRT>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>LONGJMP_EXIT;PAL_HAS_PLATFORM_SPECIFIC_UTILS;_CRT_SECURE_NO_WARNINGS;__WINRT__=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
@@ -174,7 +174,7 @@
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <CompileAsWinRT>false</CompileAsWinRT>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>LONGJMP_EXIT;PAL_HAS_PLATFORM_SPECIFIC_UTILS;_CRT_SECURE_NO_WARNINGS;__WINRT__=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
@@ -188,7 +188,7 @@
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <CompileAsWinRT>false</CompileAsWinRT>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>LONGJMP_EXIT;PAL_HAS_PLATFORM_SPECIFIC_UTILS;_CRT_SECURE_NO_WARNINGS;__WINRT__=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
@@ -202,7 +202,7 @@
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
       <CompileAsWinRT>false</CompileAsWinRT>
       <SDLCheck>true</SDLCheck>
-      <AdditionalIncludeDirectories>..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\..\liboggvorbis\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>LONGJMP_EXIT;PAL_HAS_PLATFORM_SPECIFIC_UTILS;_CRT_SECURE_NO_WARNINGS;__WINRT__=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
@@ -307,6 +307,8 @@
     <ClInclude Include="..\..\main.h" />
     <ClInclude Include="..\..\map.h" />
     <ClInclude Include="..\..\midi.h" />
+    <ClInclude Include="..\..\native_midi\native_midi.h" />
+    <ClInclude Include="..\..\native_midi\native_midi_common.h" />
     <ClInclude Include="..\..\palcfg.h" />
     <ClInclude Include="..\..\palcommon.h" />
     <ClInclude Include="..\..\palette.h" />
@@ -387,7 +389,9 @@
     <ClCompile Include="..\..\magicmenu.c" />
     <ClCompile Include="..\..\main.c" />
     <ClCompile Include="..\..\map.c" />
+    <ClCompile Include="..\..\midi.c" />
     <ClCompile Include="..\..\mp3play.c" />
+    <ClCompile Include="..\..\native_midi\native_midi_common.c" />
     <ClCompile Include="..\..\oggplay.c" />
     <ClCompile Include="..\..\overlay.c" />
     <ClCompile Include="..\..\palcfg.c" />

+ 15 - 0
winrt/SDLPal.UWP/SDLPal.Core.vcxproj.filters

@@ -45,6 +45,9 @@
     <Filter Include="liboggvorbis\src\books\uncoupled">
       <UniqueIdentifier>{7a01992b-546d-4d62-a521-c01bd1500f9c}</UniqueIdentifier>
     </Filter>
+    <Filter Include="native_midi">
+      <UniqueIdentifier>{34823dc2-aeaf-4acc-bdef-5069e35186ed}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\adplug\binfile.h">
@@ -377,6 +380,12 @@
     <ClInclude Include="..\pal_config.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\native_midi\native_midi.h">
+      <Filter>native_midi</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\native_midi\native_midi_common.h">
+      <Filter>native_midi</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\adplug\binfile.cpp">
@@ -622,6 +631,12 @@
     <ClCompile Include="..\..\palcfg.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\native_midi\native_midi_common.c">
+      <Filter>native_midi</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\midi.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\libmad\D.dat">

+ 12 - 10
winrt/SDLPal.UWP/SDLPal.UWP.vcxproj

@@ -138,11 +138,11 @@
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common;$(OutDir)../SDLPal.Core</AdditionalLibraryDirectories>
-      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.core.lib;sdlpal.common.lib;vccorlibd.lib;msvcrtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;sdlpal.core.lib;vccorlibd.lib;msvcrtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>vccorlibd.lib;msvcrtd.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
     </Link>
   </ItemDefinitionGroup>
@@ -151,11 +151,11 @@
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common;$(OutDir)../SDLPal.Core</AdditionalLibraryDirectories>
-      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.core.lib;sdlpal.common.lib;vccorlib.lib;msvcrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;sdlpal.core.lib;vccorlib.lib;msvcrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>vccorlib.lib;msvcrt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
     </Link>
   </ItemDefinitionGroup>
@@ -164,7 +164,7 @@
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common;$(OutDir)../SDLPal.Core</AdditionalLibraryDirectories>
@@ -177,7 +177,7 @@
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common;$(OutDir)../SDLPal.Core</AdditionalLibraryDirectories>
@@ -190,11 +190,11 @@
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common;$(OutDir)../SDLPal.Core</AdditionalLibraryDirectories>
-      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.core.lib;sdlpal.common.lib;vccorlibd.lib;msvcrtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;sdlpal.core.lib;vccorlibd.lib;msvcrtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>vccorlibd.lib;msvcrtd.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
     </Link>
   </ItemDefinitionGroup>
@@ -203,11 +203,11 @@
       <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
       <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
       <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\;..\..\;..\SDLPal.Common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>$(OutDir)../SDLPal.Common;$(OutDir)../SDLPal.Core</AdditionalLibraryDirectories>
-      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.core.lib;sdlpal.common.lib;vccorlib.lib;msvcrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>dxgi.lib;dxguid.lib;sdlpal.common.lib;sdlpal.core.lib;vccorlib.lib;msvcrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>vccorlib.lib;msvcrt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
     </Link>
   </ItemDefinitionGroup>
@@ -237,6 +237,7 @@
     <None Include="SDLPal_TemporaryKey.pfx" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="..\native_midi.cpp" />
     <ClCompile Include="..\SDLPal.Common\SDLPal.cpp" />
     <ClCompile Include="..\SDLPal.Common\WinRTUtil.cpp" />
     <ClCompile Include="App.xaml.cpp">
@@ -310,6 +311,7 @@
     <PRIResource Include="..\SDLPal.Common\Strings\zh-hant\Resources.resw" />
   </ItemGroup>
   <ItemGroup>
+    <SDKReference Include="Microsoft.Midi.GmDls, Version=10.0.10586.0" />
     <SDKReference Include="WindowsMobile, Version=10.0.10586.0" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

+ 1 - 0
winrt/SDLPal.UWP/SDLPal.UWP.vcxproj.filters

@@ -34,6 +34,7 @@
     <ClCompile Include="..\SDLPal.Common\SDLPal.cpp">
       <Filter>Common</Filter>
     </ClCompile>
+    <ClCompile Include="..\native_midi.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="pch.h" />

+ 2 - 0
winrt/SDLPal.UWP/pch.h

@@ -7,5 +7,7 @@
 
 #include <collection.h>
 #include <ppltasks.h>
+#include <experimental\resumable>
+#include <pplawait.h>
 
 #include "App.xaml.h"

+ 329 - 0
winrt/native_midi.cpp

@@ -0,0 +1,329 @@
+/* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
+//
+// native_midi.cpp: Native UWP MIDI player for SDLPal.
+// Author: Lou Yihua @ 2017
+//
+// Copyright (c) 2009-2011, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// Copyright (c) 2011-2017 SDLPAL development team.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "pch.h"
+#include "AsyncHelper.h"
+#include "NativeBuffer.h"
+#include <SDL.h>
+#include <memory>
+#include <future>
+#include <thread>
+#include <condition_variable>
+#include <mutex>
+#include <atomic>
+
+extern "C" {
+#include "native_midi/native_midi.h"
+#include "native_midi/native_midi_common.h"
+}
+
+struct MidiEvent
+{
+	Windows::Devices::Midi::IMidiMessage^ message;
+	uint32_t deltaTime;		// time in ticks
+	uint32_t tempo;			// microseconds per quarter note
+
+	std::chrono::system_clock::duration DeltaTimeAsTick(uint16_t ppq)
+	{
+		return std::chrono::system_clock::duration((int64_t)deltaTime * tempo * 10 / ppq);
+	}
+};
+
+struct _NativeMidiSong {
+	Windows::Devices::Midi::MidiSynthesizer^ Synthesizer;
+	MidiEvent* Events;
+	int        Size;
+	int        Position;
+	Uint16     ppq;		// parts (ticks) per quarter note
+	volatile bool       Playing;
+	bool       Loaded;
+	std::thread Thread;
+	std::mutex  Mutex;
+	std::condition_variable Stop;
+
+	_NativeMidiSong()
+		: Events(nullptr), Size(0), Position(0)
+		, ppq(0), Playing(false), Loaded(false)
+		, Synthesizer(nullptr)
+	{ }
+};
+
+static std::atomic<NativeMidiSong*> CurrentSong = nullptr;
+
+enum class MidiSystemMessage {
+	Exclusive = 0,
+	TimeCode = 1,
+	SongPositionPointer = 2,
+	SongSelect = 3,
+	TuneRequest = 6,
+	TimingClock = 8,
+	Start = 10,
+	Continue = 11,
+	Stop = 12,
+	ActiveSensing = 14,
+	SystemReset = 15
+};
+
+static void MIDItoStream(NativeMidiSong *song, MIDIEvent *eventlist)
+{
+	int eventcount = 0, prevtime = 0, tempo = 500000;
+	MIDIEvent *event = eventlist;
+	while (event)
+	{
+		eventcount++;
+		event = event->next;
+	}
+
+	if (!(song->Events = new MidiEvent[eventcount]))
+		return;
+
+	for (event = eventlist, eventcount = 0; event; event = event->next)
+	{
+		Windows::Devices::Midi::IMidiMessage^ message = nullptr;
+		int status = (event->status & 0xF0) >> 4;
+		switch (status)
+		{
+		case MIDI_STATUS_NOTE_OFF:
+			message = ref new Windows::Devices::Midi::MidiNoteOffMessage(event->status - (status << 4), event->data[0], event->data[1]);
+			break;
+
+		case MIDI_STATUS_NOTE_ON:
+			message = ref new Windows::Devices::Midi::MidiNoteOnMessage(event->status - (status << 4), event->data[0], event->data[1]);
+			break;
+
+		case MIDI_STATUS_AFTERTOUCH:
+			message = ref new Windows::Devices::Midi::MidiPolyphonicKeyPressureMessage(event->status - (status << 4), event->data[0], event->data[1]);
+			break;
+
+		case MIDI_STATUS_CONTROLLER:
+			message = ref new Windows::Devices::Midi::MidiControlChangeMessage(event->status - (status << 4), event->data[0], event->data[1]);
+			break;
+
+		case MIDI_STATUS_PROG_CHANGE:
+			message = ref new Windows::Devices::Midi::MidiProgramChangeMessage(event->status - (status << 4), event->data[0]);
+			break;
+
+		case MIDI_STATUS_PRESSURE:
+			message = ref new Windows::Devices::Midi::MidiChannelPressureMessage(event->status - (status << 4), event->data[0]);
+			break;
+
+		case MIDI_STATUS_PITCH_WHEEL:
+			message = ref new Windows::Devices::Midi::MidiPitchBendChangeMessage(event->status - (status << 4), event->data[0] | (event->data[1] << 7));
+			break;
+
+		case MIDI_STATUS_SYSEX:
+			switch ((MidiSystemMessage)(event->status & 0xF))
+			{
+			case MidiSystemMessage::Exclusive:
+			{
+				auto buffer = NativeBuffer::GetIBuffer(event->extraData, event->extraLen);
+				if (buffer)
+				{
+					message = ref new Windows::Devices::Midi::MidiSystemExclusiveMessage(buffer);
+					delete buffer;
+				}
+			}
+				break;
+
+			case MidiSystemMessage::TimeCode:
+				message = ref new Windows::Devices::Midi::MidiTimeCodeMessage(event->extraData[0] >> 4, event->extraData[0] & 0xF);
+				break;
+
+			case MidiSystemMessage::SongPositionPointer:
+				message = ref new Windows::Devices::Midi::MidiSongPositionPointerMessage(event->extraData[0] | (event->extraData[1] << 7));
+				break;
+
+			case MidiSystemMessage::SongSelect:
+				message = ref new Windows::Devices::Midi::MidiSongSelectMessage(event->extraData[0]);
+				break;
+
+			case MidiSystemMessage::TuneRequest:
+				message = ref new Windows::Devices::Midi::MidiTuneRequestMessage();
+				break;
+
+			case MidiSystemMessage::TimingClock:
+				message = ref new Windows::Devices::Midi::MidiTimingClockMessage();
+				break;
+
+			case MidiSystemMessage::Start:
+				message = ref new Windows::Devices::Midi::MidiStartMessage();
+				break;
+
+			case MidiSystemMessage::Continue:
+				message = ref new Windows::Devices::Midi::MidiContinueMessage();
+				break;
+
+			case MidiSystemMessage::Stop:
+				message = ref new Windows::Devices::Midi::MidiStopMessage();
+				break;
+
+			case MidiSystemMessage::ActiveSensing:
+				message = ref new Windows::Devices::Midi::MidiActiveSensingMessage();
+				break;
+
+			case MidiSystemMessage::SystemReset:
+				message = ref new Windows::Devices::Midi::MidiSystemResetMessage();
+				if (event->data[0] == 0x51)
+					tempo = (event->extraData[0] << 16) | (event->extraData[1] << 8) | event->extraData[2];
+				break;
+			}
+			break;
+		}
+
+		if (message)
+		{
+			song->Events[eventcount].message = message;
+			song->Events[eventcount].deltaTime = event->time - prevtime;
+			song->Events[eventcount].tempo = tempo;
+			prevtime = event->time; eventcount++;
+		}
+	}
+
+	song->Size = eventcount;
+	song->Position = 0;
+	song->Loaded = 1;
+}
+
+int native_midi_detect()
+{
+	auto synthesizer = AWait(Windows::Devices::Midi::MidiSynthesizer::CreateAsync());
+	if (synthesizer)
+	{
+		delete synthesizer;
+		return 1;
+	}
+	return 0;
+}
+
+NativeMidiSong *native_midi_loadsong(const char *midifile)
+{
+	/* Attempt to load the midi file */
+	std::unique_ptr<SDL_RWops> rw(SDL_RWFromFile(midifile, "rb"));
+	return rw ? native_midi_loadsong_RW(rw.get()) : nullptr;
+}
+
+NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw)
+{
+	std::unique_ptr<NativeMidiSong> newsong(new NativeMidiSong);
+
+	if (newsong)
+	{
+		auto evntlist = CreateMIDIEventList(rw, &newsong->ppq);
+
+		if (evntlist)
+		{
+			MIDItoStream(newsong.get(), evntlist);
+			FreeMIDIEventList(evntlist);
+			return newsong.release();
+		}
+	}
+
+	return nullptr;
+}
+
+void native_midi_freesong(NativeMidiSong *song)
+{
+	if (song)
+	{
+		native_midi_stop();
+
+		if (song->Events)
+			delete[] song->Events;
+		delete song;
+	}
+}
+
+void native_midi_start(NativeMidiSong *song)
+{
+	if (!song) return;
+
+	native_midi_stop();
+
+	if (!song->Synthesizer)
+	{
+		song->Synthesizer = AWait(Windows::Devices::Midi::MidiSynthesizer::CreateAsync());
+		if (!song->Synthesizer) return;
+	}
+
+	song->Thread = std::move(std::thread([](NativeMidiSong *song)->void {
+		auto time = std::chrono::system_clock::now();
+		while (song->Position < song->Size)
+		{
+			do
+			{
+				song->Synthesizer->SendMessage(song->Events[song->Position++].message);
+			} while (song->Position < song->Size && song->Events[song->Position].deltaTime == 0);
+			time += std::chrono::system_clock::duration(song->Events[song->Position].DeltaTimeAsTick(song->ppq));
+			if (song->Stop.wait_until(std::unique_lock<std::mutex>(song->Mutex), time) == std::cv_status::no_timeout) break;
+		}
+		song->Playing = false;
+	}, song));
+
+	song->Playing = true;
+
+	CurrentSong.exchange(song);
+}
+
+void native_midi_stop()
+{
+	NativeMidiSong* song;
+	if (song = CurrentSong.exchange(nullptr))
+	{
+		song->Stop.notify_all();
+		if (song->Thread.joinable())
+			song->Thread.join();
+		song->Thread = std::move(std::thread());
+		song->Playing = false;
+		if (song->Synthesizer)
+		{
+			delete song->Synthesizer;
+			song->Synthesizer = nullptr;
+		}
+	}
+}
+
+int native_midi_active()
+{
+	auto song = CurrentSong.load();
+	return (song && song->Playing) ? 1 : 0;
+}
+
+void native_midi_setvolume(int volume)
+{
+	auto song = CurrentSong.load();
+	if (song && song->Synthesizer)
+	{
+		if (volume > 128)
+			volume = 128;
+		else if (volume < 0)
+			volume = 0;
+		song->Synthesizer->Volume = (double)volume / 128.0;
+	}
+}
+
+const char *native_midi_error(void)
+{
+	return "";
+}

+ 2 - 0
winrt/pal_config.h

@@ -23,6 +23,8 @@
 
 #define PAL_HAS_CONFIG_PAGE  1
 
+#define PAL_HAS_NATIVEMIDI  1
+
 #define PAL_FILESYSTEM_IGNORE_CASE 1
 
 LPCSTR