1 /+
2               Copyright Elias Batek 2017 - 2018.
3      Distributed under the Boost Software License, Version 1.0.
4         (See accompanying file LICENSE_1_0.txt or copy at
5               https://www.boost.org/LICENSE_1_0.txt)
6  +/
7 module midigamepad.lib.midi.input;
8 
9 import core.sys.windows.windef : LOBYTE, LOWORD, HIBYTE, HIWORD;
10 import core.sys.windows.mmsystem;
11 import std.conv : to;
12 
13 import midigamepad.lib.util;
14 
15 public import dplug.client.midi : MidiMessage;
16 public import midigamepad.lib.midi.device : MIDIDeviceInfo, MIDIDeviceType;
17 public import core.sys.windows.mmsystem : MIDIINCAPS, MMRESULT;
18 
19 /++
20     Opens a handle for the specified MIDI input device and starts it
21 
22     Returns:
23         true on success
24  +/
25 bool bootstrapMIDIInputDevice(uint deviceID, MidiInProc callback,
26         out HMIDIIN handle, size_t callbackData = 0) @nogc nothrow
27 {
28     if (openMIDIInputDevice(deviceID, callback, handle, callbackData) != MMSYSERR_NOERROR)
29         return false;
30 
31     return startMIDIInputDevice(handle);
32 }
33 
34 /++ ditto +/
35 bool bootstrapMIDIInputDevice(MIDIDeviceInfo di, MidiInProc callback,
36         out HMIDIIN handle, size_t callbackData = 0) @nogc nothrow
37 {
38     return bootstrapMIDIInputDevice(di.id, callback, handle, callbackData);
39 }
40 
41 /++
42     Disposes the specified MIDI input device
43 
44     Returns:
45         MMSYSERR_NOERROR on success
46  +/
47 MMRESULT closeMIDIInputDevice(HMIDIIN handle)
48 {
49     return midiInClose(handle);
50 }
51 
52 
53 /++
54     Returns: All connected MIDI input devices
55  +/
56 MIDIDeviceInfo[] getAllMIDIInputDevices()
57 {
58     const uint count = getMIDIInputDevicesCount();
59     MIDIDeviceInfo[] output = new MIDIDeviceInfo[count];
60 
61     for (uint i = 0; i < count; i++)
62     {
63         output[i] = getMIDIInputDeviceInfo(i);
64     }
65 
66     return output;
67 }
68 
69 /++
70     Returns: the count of connected MIDI input devices
71  +/
72 uint getMIDIInputDevicesCount() @nogc nothrow
73 {
74     return midiInGetNumDevs();
75 }
76 
77 /++
78     Returns: the name of the given MIDI input device
79  +/
80 string getMIDIInputDeviceName(MIDIINCAPS caps) pure @safe
81 {
82     return caps.szPname.trimEndNull.to!string;
83 }
84 
85 /++
86     Returns: the device info of the specified MIDI input device
87  +/
88 MIDIDeviceInfo getMIDIInputDeviceInfo(uint deviceID)
89 {
90     MIDIINCAPS caps = getWinDeviceInfo(deviceID);
91     auto output = MIDIDeviceInfo(caps.getMIDIInputDeviceName(), deviceID, MIDIDeviceType.input);
92 
93     return output;
94 }
95 
96 /++
97     Returns: the capabilities of the specified MIDI input device
98  +/
99 MIDIINCAPS getWinDeviceInfo(uint deviceID) @nogc nothrow
100 {
101     MIDIINCAPS capabilities = MIDIINCAPS();
102 
103     MMRESULT rslt;
104     if ((rslt = midiInGetDevCaps(deviceID, &capabilities, MIDIINCAPS.sizeof)) == MMSYSERR_NOERROR)
105     {
106         return capabilities;
107     }
108     else
109     {
110         switch (rslt)
111         {
112         case MMSYSERR_BADDEVICEID:
113             assert(0, MMSYSERR_BADDEVICEID.stringof);
114 
115         case MMSYSERR_INVALPARAM:
116             assert(0, MMSYSERR_INVALPARAM.stringof);
117 
118         case MMSYSERR_NODRIVER:
119             assert(0, MMSYSERR_NODRIVER.stringof);
120 
121         case MMSYSERR_NOMEM:
122             assert(0, MMSYSERR_NOMEM.stringof);
123 
124         default:
125             assert(0);
126         }
127     }
128 }
129 
130 extern (Windows) alias MidiInProc = void function(HMIDIIN, uint, size_t, size_t, size_t);
131 
132 /++
133     Opens a handle for the specified MIDI input device
134 
135     Returns:
136         MMSYSERR_NOERROR on success
137  +/
138 MMRESULT openMIDIInputDevice(uint deviceID, MidiInProc callback,
139         out HMIDIIN handle, size_t callbackData = 0) @nogc nothrow
140 {
141     // BUG: THIS RETURNS 1 (MMSYSERR_ERROR) WHEN COMPILED IN x86 MODE
142     return midiInOpen(&handle, deviceID, cast(size_t)(callback), callbackData, CALLBACK_FUNCTION);
143 }
144 
145 /++ ditto ++/
146 MMRESULT openMIDIInputDevice(MIDIDeviceInfo di, MidiInProc callback,
147         out HMIDIIN handle, size_t callbackData = 0) @nogc nothrow
148 {
149     return openMIDIInputDevice(di.id, callback, handle, callbackData);
150 }
151 
152 /++
153     Starts the MIDI input device which is represented by the passed handle
154 
155     Returns:
156         true on success
157         false if the passed handle was invalid
158  +/
159 bool startMIDIInputDevice(HMIDIIN handle) @nogc nothrow
160 {
161     return (midiInStart(handle) == MMSYSERR_NOERROR);
162 }
163 
164 /++
165     Parses param1 of a MIM_DATA message
166 
167     See_Also:
168         https://msdn.microsoft.com/en-us/library/vs/alm/dd757284(v=vs.85).aspx,
169         https://users.cs.cf.ac.uk/Dave.Marshall/Multimedia/node158.html
170  +/
171 MidiMessage parseMIMDATA(size_t param1) @nogc nothrow pure
172 {
173     immutable ubyte status = param1.LOWORD.LOBYTE;
174     immutable ubyte data1 = param1.LOWORD.HIBYTE;
175     immutable ubyte data2 = param1.HIWORD.LOBYTE;
176 
177     return MidiMessage(0, status, data1, data2);
178 }