xref: /reactos/dll/win32/mmdrv/midi.c (revision c2c66aff)
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 synchronization
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 }
359