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 }