1 module iota.audio.midiwin;
2 
3 version (Windows):
4 
5 import iota.audio.midiin;
6 import iota.audio.midiout;
7 import core.sys.windows.windows;
8 import core.sys.windows.wtypes;
9 package import iota.audio.backend.windows;
10 public import iota.audio.types;
11 
12 public class WindowsMIDIInput : MIDIInput {
13 	/** 
14 	 * Function for handling callbacks from OS side.
15 	 */
16 	package static extern(Windows) void midiHandler(HMIDIIN midi, UINT msg, DWORD_PTR instance, DWORD_PTR param1, 
17 			DWORD_PTR param2) @nogc nothrow {
18 		if (msg != MIM_OPEN && msg != MIM_CLOSE)
19 			(cast(WindowsMIDIInput)(cast(void*)instance)).handleEvent(msg, param1, param2);
20 	}
21 	///Contains all currently received MIDI messages.
22 	///If full, further MIDI messages will be dropped.
23 	protected ubyte[] buffer;
24 	protected size_t bufferpos;
25 	protected HMIDIIN handle;
26 	///The last error code produced by the OS
27 	public MMRESULT lastErrorCode;
28 	package this(UINT deviceID, size_t bufferSize) nothrow {
29 		lastErrorCode = midiInOpen(&handle, deviceID, cast(size_t)(cast(void*)&midiHandler), cast(size_t)(cast(void*)this), 
30 				CALLBACK_FUNCTION);
31 		buffer.length = bufferSize;
32 	}
33 	~this() {
34 		if (handle)
35 			midiInClose(handle);
36 	}
37 	package void handleEvent(UINT msg, DWORD_PTR param1, DWORD_PTR param2) @nogc nothrow {
38 		if (midiInCallback is null) {
39 			switch (msg) {
40 				case MIM_ERROR:
41 					size_t data = param1;
42 					int i = 4;
43 					while ((i) && bufferpos < buffer.length) {
44 						buffer[bufferpos] = cast(ubyte)data;
45 						data>>=8;
46 						bufferpos++;
47 						i--;
48 					}
49 					break;
50 				case MIM_DATA:
51 					size_t data = param1;
52 					int i = 3;
53 					if ((data & 0xf0) == 0xc0 || (data & 0xf0) == 0xD0) i = 2;
54 					if ((data & 0xf0) == 0xf0 && (data & 0x0f) > 0x05) i = 1;
55 					while ((i) && bufferpos < buffer.length) {
56 						buffer[bufferpos] = cast(ubyte)data;
57 						data>>=8;
58 						bufferpos++;
59 						i--;
60 					}
61 					break;
62 				case MIM_LONGDATA, MIM_LONGERROR:
63 					LPMIDIHDR data = cast(LPMIDIHDR)(cast(void*)param1);
64 					const int t = data.dwBytesRecorded;
65 					int i;
66 					while ((i < t) && bufferpos < buffer.length) {
67 						buffer[bufferpos] = cast(ubyte)(data.lpData[i]);
68 						bufferpos++;
69 						i++;
70 					}
71 					break;
72 				default:
73 					break;
74 			}
75 		} else {
76 			switch (msg) {
77 				case MIM_LONGDATA, MIM_LONGERROR:
78 					LPMIDIHDR data = cast(LPMIDIHDR)(cast(void*)param1);
79 					midiInCallback((cast(ubyte*)data.lpData)[0..data.dwBytesRecorded], param2);
80 					break;
81 				case MIM_DATA:
82 					size_t data = param1;
83 					int i = 3;
84 					if ((data & 0xf0) == 0xc0 || (data & 0xf0) == 0xD0) i = 2;
85 					if ((data & 0xf0) == 0xf0 && (data & 0x0f) > 0x05) i = 1;
86 					ubyte* dp = cast(ubyte*)&data;
87 					midiInCallback(dp[0..i], param2);
88 					break;
89 				case MIM_ERROR:
90 					size_t data = param1;
91 					ubyte* dp = cast(ubyte*)&data;
92 					midiInCallback(dp[0..size_t.sizeof], param2);
93 					break;
94 				default:
95 					break;
96 			}
97 		}
98 		
99 	}
100 	override public ubyte[] read() pure nothrow {
101 		const size_t pos = bufferpos;
102 		bufferpos = 0;
103 		return buffer[0..pos];
104 	}
105 	/** 
106      * Starts the MIDI input stream.
107      * Returns: Zero on success, or a specific error code.
108      */
109     override public int start() nothrow {
110 		lastErrorCode = midiInStart(handle);
111 		if (lastErrorCode == MMSYSERR_INVALHANDLE)	return lastStatusCode = MIDIDeviceStatus.DeviceDisconnected;
112 		else return lastStatusCode = MIDIDeviceStatus.AllOk;
113 	}
114     /** 
115      * Stops the MIDI input stream.
116      * Returns: Zero on success, or a specific error code.
117      */
118     override public int stop() nothrow {
119 		lastErrorCode = midiInStop(handle);
120 		if (lastErrorCode == MMSYSERR_INVALHANDLE)	return lastStatusCode = MIDIDeviceStatus.DeviceDisconnected;
121 		else return lastStatusCode = MIDIDeviceStatus.AllOk;
122 	}
123 }
124 public class WindowsMIDIOutput : MIDIOutput {
125 	protected HMIDIOUT handle;
126 	///The last error code produced by the OS
127 	public MMRESULT lastErrorCode;
128 	package this(UINT deviceID) @nogc nothrow {
129 		lastErrorCode = midiOutOpen(&handle, deviceID, 0, 0, CALLBACK_NULL);
130 	}
131 	~this() {
132 		midiOutClose(handle);
133 	}
134 	override public void write(ubyte[] buffer) {
135 		if (buffer.length <= 3) {
136 			DWORD msg;
137 			for (int i ; i < buffer.length ; i++) {
138 				msg |= buffer[i]<<(i*8);
139 			}
140 			lastErrorCode = midiOutShortMsg(handle, msg);
141 		} else {
142 			MIDIHDR msg;
143 			msg.lpData = cast(char*)buffer.ptr;
144 			msg.dwBufferLength = cast(DWORD)buffer.length;
145 			msg.dwBytesRecorded = cast(DWORD)buffer.length;
146 			msg.dwFlags = MHDR_DONE;
147 			lastErrorCode = midiOutPrepareHeader(handle, &msg, cast(UINT)MIDIHDR.sizeof);
148 			lastErrorCode = midiOutLongMsg(handle, &msg, cast(UINT)MIDIHDR.sizeof);
149 			lastErrorCode = midiOutUnprepareHeader(handle, &msg, cast(UINT)MIDIHDR.sizeof);
150 		}
151 	}
152 	/** 
153      * Starts the MIDI output stream.
154      * Returns: Zero on success, or a specific error code.
155      */
156     override public int start() nothrow {
157 		return lastStatusCode = MIDIDeviceStatus.AllOk;
158 	}
159     /** 
160      * Stops the MIDI output stream.
161      * Returns: Zero on success, or a specific error code.
162      */
163     override public int stop() nothrow {
164 		return lastStatusCode = MIDIDeviceStatus.AllOk;
165 	}
166 }