Browse Source

native midi support for uwp

LouYihua 7 years ago
parent
commit
a35adab1f5

+ 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;

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

@@ -0,0 +1,66 @@
+#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)
 	{

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

@@ -1,7 +1,6 @@
 #include <wrl.h>
 #include <string>
 #include <DXGI.h>
-#include <ppltasks.h>
 #include <shcore.h>
 #include <unordered_set>
 #include "AsyncHelper.h"
@@ -66,73 +65,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"\\");
+		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;
 	}
-	else
+
+	if (directory->Length() == 0)
 	{
-		path.assign(Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data());
+		directory = Windows::Storage::ApplicationData::Current->LocalFolder->Path;
 	}
 
-	Windows::Storage::StorageFolder^ folder = nullptr;
-	Event eventHandle;
-	try
+	bool r, w, b = false;
+	switch (*_Mode)
 	{
-		folder = AWait(Windows::Storage::StorageFolder::GetFolderFromPathAsync(ref new Platform::String(path.c_str())), eventHandle);
+	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::AccessDeniedException^)
+	for (size_t i = 1; i < strlen(_Mode); i++)
 	{
-		return EACCES;
-	}
-	catch (Platform::Exception^)
-	{
-		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">

+ 6 - 4
winrt/SDLPal.UWP/SDLPal.UWP.vcxproj

@@ -140,7 +140,7 @@
     </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>
@@ -153,7 +153,7 @@
     </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>
@@ -192,7 +192,7 @@
     </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>
@@ -205,7 +205,7 @@
     </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>
@@ -235,6 +235,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">
@@ -308,6 +309,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"

+ 356 - 0
winrt/native_midi.cpp

@@ -0,0 +1,356 @@
+/* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
+//
+// 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;
+}
+//
+//void CALLBACK MidiProc(HMIDIIN hMidi, UINT uMsg, unsigned long dwInstance,
+//	unsigned long dwParam1, unsigned long dwParam2)
+//{
+//	switch (uMsg)
+//	{
+//	case MOM_DONE:
+//		if ((currentsong->MusicLoaded) && (dwParam1 == (unsigned long)&currentsong->MidiStreamHdr))
+//			BlockOut(currentsong);
+//		break;
+//	case MOM_POSITIONCB:
+//		if ((currentsong->MusicLoaded) && (dwParam1 == (unsigned long)&currentsong->MidiStreamHdr))
+//			currentsong->MusicPlaying = 0;
+//		break;
+//	default:
+//		break;
+//	}
+//}
+
+int native_midi_detect()
+{
+	auto synthesizer = AWait(Windows::Devices::Midi::MidiSynthesizer::CreateAsync());
+	if (synthesizer)
+	{
+		delete synthesizer;
+		return 0;
+	}
+	return 1;
+
+	//auto devs = AWait(Windows::Devices::Enumeration::DeviceInformation::FindAllAsync(Windows::Devices::Enumeration::DeviceClass::AudioRender));
+	//for each(auto dev in devs)
+	//{
+	//	if (wcsstr(dev->Name->Data(), L"ASUS"))
+	//	{
+	//		Synthesizer = AWait(Windows::Devices::Midi::MidiSynthesizer::CreateAsync(dev));
+	//		std::unique_ptr<SDL_RWops> rw(SDL_RWFromConstMem(data, sizeof(data)));
+	//		native_midi_loadsong_RW(rw.get());
+	//	}
+	//}
+	//return Synthesizer ? 0 : 1;
+}
+
+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