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