[CLT2012]
[reactos.git] / dll / win32 / mmdrv / midi.c
1 /*
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS Multimedia
5 * FILE: lib/mmdrv/midi.c
6 * PURPOSE: Multimedia User Mode Driver
7 * PROGRAMMER: Andrew Greenwood
8 * UPDATE HISTORY:
9 * Jan 30, 2004: Imported into ReactOS tree
10 */
11
12 #include "mmdrv.h"
13 #include "wave.h"
14
15 #define NDEBUG
16 #include <debug.h>
17
18 // MIDI device instance information
19 //
20 #define LOCAL_DATA_SIZE 20
21 typedef struct _LOCALMIDIHDR {
22 OVERLAPPED Ovl;
23 DWORD BytesReturned;
24 struct _LOCALMIDIHDR *lpNext;
25 BOOL Done;
26 PVOID pClient;
27 // MIDI_DD_INPUT_DATA MidiData;
28 BYTE ExtraData[LOCAL_DATA_SIZE - sizeof(ULONG)];
29
30 } LOCALMIDIHDR, *PLOCALMIDIHDR;
31
32 #define LOCAL_MIDI_BUFFERS 8
33
34 typedef struct {
35
36 BOOL fMidiInStarted;
37 DWORD dwMsg;
38 DWORD dwCurData;
39 BYTE status;
40 BOOLEAN fSysex;
41 BOOLEAN Bad;
42 BYTE bBytesLeft;
43 BYTE bBytePos;
44 DWORD dwCurTime;
45 DWORD dwMsgTime;
46
47
48 PLOCALMIDIHDR DeviceQueue;
49
50 LOCALMIDIHDR
51 Bufs[LOCAL_MIDI_BUFFERS];
52
53
54 } LOCALMIDIDATA, *PLOCALMIDIDATA;
55
56
57 typedef struct tag_MIDIALLOC {
58 struct tag_MIDIALLOC *Next; // Chain of devices
59 UINT DeviceNumber; // Number of device
60 UINT DeviceType; // MidiInput or MidiOutput
61 DWORD dwCallback; // client's callback
62 DWORD dwInstance; // client's instance data
63 HMIDI hMidi; // handle for stream
64 HANDLE DeviceHandle; // Midi device handle
65 LPMIDIHDR lpMIQueue; // Buffers sent to device
66 // This is only required so that
67 // CLOSE knows when things have
68 // really finished.
69 // notify. This is only accessed
70 // on the device thread and its
71 // apcs so does not need any
72 // synchronized access.
73 HANDLE Event; // Event for driver syncrhonization
74 // and notification of auxiliary
75 // task operation completion.
76 // MIDITHREADFUNCTION AuxFunction; // Function for thread to perform
77 union {
78 LPMIDIHDR pHdr; // Buffer to pass in aux task
79 ULONG State; // State to set
80 struct {
81 ULONG Function; // IOCTL to use
82 PBYTE pData; // Data to set or get
83 ULONG DataLen; // Length of data
84 } GetSetData;
85
86 } AuxParam;
87 // 0 means terminate task.
88 HANDLE ThreadHandle; // Handle for termination ONLY
89 HANDLE AuxEvent1; // Aux thread waits on this
90 HANDLE AuxEvent2; // Aux thread caller waits on this
91 DWORD AuxReturnCode; // Return code from Aux task
92 DWORD dwFlags; // Open flags
93 PLOCALMIDIDATA Mid; // Extra midi input structures
94 int l; // Helper global for modMidiLength
95
96 } MIDIALLOC, *PMIDIALLOC;
97
98 PMIDIALLOC MidiHandleList; // Our chain of wave handles
99
100
101
102 static DWORD OpenMidiDevice(UINT DeviceType, DWORD ID, DWORD User, DWORD Param1, DWORD Param2)
103 {
104 PMIDIALLOC pClient = NULL;
105 MMRESULT Result = MMSYSERR_NOERROR;
106
107 // Check ID?
108 DPRINT("OpenMidiDevice()\n");
109
110 switch(DeviceType)
111 {
112 case MidiOutDevice :
113 pClient = (PMIDIALLOC) HeapAlloc(Heap, 0, sizeof(MIDIALLOC));
114 if ( pClient ) memset(pClient, 0, sizeof(MIDIALLOC));
115 break;
116
117 case MidiInDevice :
118 pClient = (PMIDIALLOC) HeapAlloc(Heap, 0, sizeof(MIDIALLOC) + sizeof(LOCALMIDIDATA));
119 if ( pClient ) memset(pClient, 0, sizeof(MIDIALLOC) + sizeof(LOCALMIDIDATA));
120 break;
121 };
122
123 if ( !pClient )
124 return MMSYSERR_NOMEM;
125
126 if (DeviceType == MidiInDevice)
127 {
128 int i;
129 pClient->Mid = (PLOCALMIDIDATA)(pClient + 1);
130 for (i = 0 ;i < LOCAL_MIDI_BUFFERS ; i++)
131 {
132 pClient->Mid->Bufs[i].pClient = pClient;
133 }
134 }
135
136 pClient->DeviceType = DeviceType;
137 pClient->dwCallback = ((LPMIDIOPENDESC)Param1)->dwCallback;
138 pClient->dwInstance = ((LPMIDIOPENDESC)Param1)->dwInstance;
139 pClient->hMidi = ((LPMIDIOPENDESC)Param1)->hMidi;
140 pClient->dwFlags = Param2;
141
142 Result = OpenDevice(DeviceType, ID, &pClient->DeviceHandle, (GENERIC_READ | GENERIC_WRITE));
143
144 if ( Result != MMSYSERR_NOERROR )
145 {
146 // cleanup
147 return Result;
148 }
149
150 pClient->Event = CreateEvent(NULL, FALSE, FALSE, NULL);
151
152 if ( !pClient->Event )
153 {
154 // cleanup
155 return MMSYSERR_NOMEM;
156 }
157
158 if (DeviceType == MidiInDevice)
159 {
160
161 pClient->AuxEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);
162 if (pClient->AuxEvent1 == NULL)
163 {
164 // cleanup
165 return MMSYSERR_NOMEM;
166 }
167
168 pClient->AuxEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
169 if (pClient->AuxEvent2 == NULL)
170 {
171 // cleanup
172 return MMSYSERR_NOMEM;
173 }
174
175
176 // TaskCreate
177
178
179 WaitForSingleObject(pClient->AuxEvent2, INFINITE);
180 }
181
182 PMIDIALLOC *pUserHandle;
183 pUserHandle = (PMIDIALLOC*) User;
184 *pUserHandle = pClient;
185
186 // callback
187
188 return MMSYSERR_NOERROR;
189 }
190
191
192
193 static DWORD WriteMidi(PBYTE pData, ULONG Length, PMIDIALLOC pClient)
194 {
195 DWORD BytesReturned;
196
197 DPRINT("IOCTL_MIDI_PLAY == %d [%x]\n", IOCTL_MIDI_PLAY, IOCTL_MIDI_PLAY);
198
199 if ( !DeviceIoControl(pClient->DeviceHandle, IOCTL_MIDI_PLAY, (PVOID)pData,
200 Length, NULL, 0, &BytesReturned, NULL))
201 return TranslateStatus();
202
203 return MMSYSERR_NOERROR;
204 }
205
206
207 static int GetMidiLength(PMIDIALLOC pClient, BYTE b)
208 {
209 if (b >= 0xF8)
210 {
211 // Realtime message - leave running status
212 return 1; // Write one byte
213 }
214
215 switch (b)
216 {
217 case 0xF0: case 0xF4: case 0xF5: case 0xF6: case 0xF7:
218 pClient->l = 1;
219 return pClient->l;
220
221 case 0xF1: case 0xF3:
222 pClient->l = 2;
223 return pClient->l;
224
225 case 0xF2:
226 pClient->l = 3;
227 return pClient->l;
228 }
229
230 switch (b & 0xF0)
231 {
232 case 0x80: case 0x90: case 0xA0: case 0xB0: case 0xE0:
233 pClient->l = 3;
234 return pClient->l;
235
236 case 0xC0: case 0xD0:
237 pClient->l = 2;
238 return pClient->l;
239 }
240
241 return (pClient->l - 1); // uses previous value if data byte (running status)
242 }
243
244
245
246 /* ----------------------------------------------------------------------------
247 Exported functions
248 ----------------------------------------------------------------------------- */
249
250 APIENTRY DWORD midMessage(DWORD dwId, DWORD dwMessage, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
251 {
252 DPRINT("midMessage\n");
253 return MMSYSERR_NOERROR;
254
255 switch (dwMessage) {
256 case MIDM_GETNUMDEVS:
257 DPRINT("MIDM_GETNUMDEVS");
258 return GetDeviceCount(MidiInDevice);
259
260 case MIDM_GETDEVCAPS:
261 DPRINT("MIDM_GETDEVCAPS");
262 return GetDeviceCapabilities(dwId, MidiInDevice, (LPBYTE)dwParam1, (DWORD)dwParam2);
263
264 case MIDM_OPEN:
265 DPRINT("MIDM_OPEN");
266 return MMSYSERR_NOERROR;
267
268 case MIDM_CLOSE:
269 DPRINT("MIDM_CLOSE");
270 return MMSYSERR_NOERROR;
271
272 case MIDM_ADDBUFFER:
273 DPRINT("MIDM_ADDBUFFER");
274 return MMSYSERR_NOERROR;
275
276 case MIDM_STOP:
277 DPRINT("MIDM_PAUSE");
278 return MMSYSERR_NOERROR;
279
280 case MIDM_START:
281 DPRINT("MIDM_RESTART");
282 return MMSYSERR_NOERROR;
283
284 case MIDM_RESET:
285 DPRINT("MIDM_RESET");
286 return MMSYSERR_NOERROR;
287
288 default:
289 return MMSYSERR_NOTSUPPORTED;
290 }
291
292 // the function should never get to this point
293 //FIXME: Would it be wise to assert here?
294 return MMSYSERR_NOTSUPPORTED;
295 }
296
297 APIENTRY DWORD modMessage(DWORD ID, DWORD Message, DWORD User, DWORD Param1, DWORD Param2)
298 {
299 DPRINT("modMessage\n");
300
301 switch(Message)
302 {
303 case MODM_GETNUMDEVS:
304 DPRINT("MODM_GETNUMDEVS == %d\n", (int)GetDeviceCount(MidiOutDevice));
305 return GetDeviceCount(MidiOutDevice);
306
307 case MODM_GETDEVCAPS:
308 DPRINT("MODM_GETDEVCAPS");
309 return GetDeviceCapabilities(ID, MidiOutDevice, (LPBYTE)Param1, (DWORD)Param2);
310
311 case MODM_OPEN :
312 return OpenMidiDevice(MidiOutDevice, ID, User, Param1, Param2);
313
314 case MODM_CLOSE:
315 DPRINT("MODM_CLOSE");
316 return MMSYSERR_NOTSUPPORTED;
317
318 case MODM_DATA:
319 DPRINT("MODM_DATA");
320
321 int i;
322 BYTE b[4];
323 for (i = 0; i < 4; i ++) {
324 b[i] = (BYTE)(Param1 % 256);
325 Param1 /= 256;
326 }
327 return WriteMidi(b, GetMidiLength((PMIDIALLOC)User, b[0]),
328 (PMIDIALLOC)User);
329
330 case MODM_LONGDATA:
331 DPRINT("MODM_LONGDATA");
332 return MMSYSERR_NOTSUPPORTED;
333
334 case MODM_RESET:
335 DPRINT("MODM_RESET");
336 return MMSYSERR_NOTSUPPORTED;
337
338 case MODM_SETVOLUME:
339 DPRINT("MODM_SETVOLUME");
340 return MMSYSERR_NOTSUPPORTED;
341
342 case MODM_GETVOLUME:
343 DPRINT("MODM_GETVOLUME");
344 return MMSYSERR_NOTSUPPORTED;
345
346 case MODM_CACHEPATCHES:
347 DPRINT("MODM_CACHEPATCHES");
348 return MMSYSERR_NOTSUPPORTED;
349
350 case MODM_CACHEDRUMPATCHES:
351 DPRINT("MODM_CACHEDRUMPATCHES");
352 return MMSYSERR_NOTSUPPORTED;
353
354 };
355
356 return MMSYSERR_NOTSUPPORTED;
357 }