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