Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

476 lines
14 KiB

  1. /****************************************************************************
  2. *
  3. * midiout.c
  4. *
  5. * WDM Audio support for Midi Output devices
  6. *
  7. * Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved.
  8. *
  9. * History
  10. * 5-12-97 - Noel Cross (NoelC)
  11. *
  12. ***************************************************************************/
  13. #include "wdmdrv.h"
  14. #ifndef UNDER_NT
  15. #pragma alloc_text(FIXCODE, modMessage)
  16. #pragma alloc_text(FIXCODE, midiOutWrite)
  17. #endif
  18. /****************************************************************************
  19. This function conforms to the standard Midi output driver message proc
  20. (modMessage), which is documented in mmddk.h
  21. ****************************************************************************/
  22. DWORD FAR PASCAL _loadds modMessage
  23. (
  24. UINT id,
  25. UINT msg,
  26. DWORD_PTR dwUser,
  27. DWORD_PTR dwParam1,
  28. DWORD_PTR dwParam2
  29. )
  30. {
  31. LPDEVICEINFO pOutClient;
  32. LPDWORD pVolume;
  33. LPDEVICEINFO DeviceInfo;
  34. MMRESULT mmr;
  35. switch (msg)
  36. {
  37. case MODM_INIT:
  38. DPF(DL_TRACE|FA_MIDI, ("MODM_INIT") );
  39. return wdmaudAddRemoveDevNode(MidiOutDevice, (LPCWSTR)dwParam2, TRUE);
  40. case DRVM_EXIT:
  41. DPF(DL_TRACE|FA_MIDI, ("DRVM_EXIT: MidiOut") );
  42. return wdmaudAddRemoveDevNode(MidiOutDevice, (LPCWSTR)dwParam2, FALSE);
  43. case MODM_GETNUMDEVS:
  44. DPF(DL_TRACE|FA_MIDI, ("MODM_GETNUMDEVS") );
  45. return wdmaudGetNumDevs(MidiOutDevice, (LPCWSTR)dwParam1);
  46. case MODM_GETDEVCAPS:
  47. DPF(DL_TRACE|FA_MIDI, ("MODM_GETDEVCAPS") );
  48. if (DeviceInfo = GlobalAllocDeviceInfo((LPWSTR)dwParam2))
  49. {
  50. DeviceInfo->DeviceType = MidiOutDevice;
  51. DeviceInfo->DeviceNumber = id;
  52. mmr = wdmaudGetDevCaps(DeviceInfo, (MDEVICECAPSEX FAR*)dwParam1);
  53. GlobalFreeDeviceInfo(DeviceInfo);
  54. return mmr;
  55. } else {
  56. MMRRETURN( MMSYSERR_NOMEM );
  57. }
  58. case MODM_PREFERRED:
  59. DPF(DL_TRACE|FA_MIDI, ("MODM_PREFERRED") );
  60. return wdmaudSetPreferredDevice(
  61. MidiOutDevice,
  62. id,
  63. dwParam1,
  64. dwParam2);
  65. case MODM_OPEN:
  66. {
  67. LPMIDIOPENDESC pmod = (LPMIDIOPENDESC)dwParam1;
  68. DPF(DL_TRACE|FA_MIDI, ("MODM_OPEN") );
  69. if (DeviceInfo = GlobalAllocDeviceInfo((LPWSTR)pmod->dnDevNode))
  70. {
  71. DeviceInfo->DeviceType = MidiOutDevice;
  72. DeviceInfo->DeviceNumber = id;
  73. #ifdef UNDER_NT
  74. DeviceInfo->DeviceHandle = (HANDLE32)pmod->hMidi;
  75. #else
  76. DeviceInfo->DeviceHandle = (HANDLE32)MAKELONG(pmod->hMidi,0);
  77. #endif
  78. mmr = midiOpen(DeviceInfo, dwUser, pmod, (DWORD)dwParam2);
  79. GlobalFreeDeviceInfo(DeviceInfo);
  80. return mmr;
  81. } else {
  82. MMRRETURN( MMSYSERR_NOMEM );
  83. }
  84. }
  85. case MODM_CLOSE:
  86. DPF(DL_TRACE|FA_MIDI, ("MODM_CLOSE") );
  87. pOutClient = (LPDEVICEINFO)dwUser;
  88. if( ( (mmr=IsValidDeviceInfo(pOutClient)) != MMSYSERR_NOERROR) ||
  89. ( (mmr=IsValidDeviceState(pOutClient->DeviceState,FALSE)) != MMSYSERR_NOERROR ) )
  90. {
  91. MMRRETURN( mmr );
  92. }
  93. midiOutAllNotesOff( pOutClient );
  94. mmr = wdmaudCloseDev( pOutClient );
  95. if (MMSYSERR_NOERROR == mmr)
  96. {
  97. //
  98. // Tell the caller we're done
  99. //
  100. midiCallback(pOutClient, MOM_CLOSE, 0L, 0L);
  101. ISVALIDDEVICEINFO(pOutClient);
  102. ISVALIDDEVICESTATE(pOutClient->DeviceState,FALSE);
  103. midiCleanUp(pOutClient);
  104. }
  105. return mmr;
  106. case MODM_DATA:
  107. DPF(DL_TRACE|FA_MIDI, ("MODM_DATA") );
  108. if( ( (mmr=IsValidDeviceInfo((LPDEVICEINFO)dwUser)) != MMSYSERR_NOERROR ) ||
  109. ( (mmr=IsValidDeviceState(((LPDEVICEINFO)dwUser)->DeviceState,FALSE)) != MMSYSERR_NOERROR ) )
  110. {
  111. MMRRETURN( mmr );
  112. }
  113. //
  114. // dwParam1 = MIDI event dword (1, 2 or 3 bytes)
  115. //
  116. return midiOutWrite((LPDEVICEINFO)dwUser, (DWORD)dwParam1);
  117. case MODM_LONGDATA:
  118. DPF(DL_TRACE|FA_MIDI, ("MODM_LONGDATA") );
  119. pOutClient = (LPDEVICEINFO)dwUser;
  120. {
  121. LPMIDIHDR lpHdr;
  122. if( ( (mmr=IsValidDeviceInfo(pOutClient)) != MMSYSERR_NOERROR ) ||
  123. ( (mmr=IsValidDeviceState(pOutClient->DeviceState,FALSE)) != MMSYSERR_NOERROR ) ||
  124. ( (mmr=IsValidMidiHeader((LPMIDIHDR)dwParam1)) != MMSYSERR_NOERROR) )
  125. {
  126. MMRRETURN( mmr );
  127. }
  128. //
  129. // check if it's been prepared
  130. //
  131. lpHdr = (LPMIDIHDR)dwParam1;
  132. if (!(lpHdr->dwFlags & MHDR_PREPARED))
  133. {
  134. MMRRETURN( MIDIERR_UNPREPARED );
  135. }
  136. // Send the data long....
  137. mmr = wdmaudSubmitMidiOutHeader(pOutClient, lpHdr);
  138. //
  139. // The docs say that this call can return an error. Why we didn't
  140. // I don't know. Thus, these lines are getting commented out.
  141. //
  142. // DPFASSERT( mmr == MMSYSERR_NOERROR );
  143. // mmr = MMSYSERR_NOERROR;
  144. // note that clearing the done bit or setting the inqueue bit
  145. // isn't necessary here since this function is synchronous -
  146. // the client will not get control back until it's done.
  147. lpHdr->dwFlags |= MHDR_DONE;
  148. // notify client
  149. //BUGBUG: this is a no-op from the set above?
  150. if (mmr == MMSYSERR_NOERROR)
  151. {
  152. midiCallback(pOutClient, MOM_DONE, (DWORD_PTR)lpHdr, 0L);
  153. }
  154. return mmr;
  155. }
  156. case MODM_RESET:
  157. DPF(DL_TRACE|FA_MIDI, ("MODM_RESET") );
  158. pOutClient = (LPDEVICEINFO)dwUser;
  159. if( ( (mmr=IsValidDeviceInfo(pOutClient)) != MMSYSERR_NOERROR ) ||
  160. ( (mmr=IsValidDeviceState(pOutClient->DeviceState,FALSE)) != MMSYSERR_NOERROR ) )
  161. {
  162. MMRRETURN( mmr );
  163. }
  164. midiOutAllNotesOff(pOutClient);
  165. return MMSYSERR_NOERROR;
  166. case MODM_SETVOLUME:
  167. DPF(DL_TRACE|FA_MIDI, ("MODM_SETVOLUME") );
  168. pOutClient = GlobalAllocDeviceInfo((LPWSTR)dwParam2);
  169. if (NULL == pOutClient)
  170. {
  171. MMRRETURN( MMSYSERR_NOMEM );
  172. }
  173. pOutClient->DeviceType = MidiOutDevice;
  174. pOutClient->DeviceNumber = id;
  175. pOutClient->OpenDone = 0;
  176. PRESETERROR(pOutClient);
  177. mmr = wdmaudIoControl(pOutClient,
  178. sizeof(DWORD),
  179. (LPBYTE)&dwParam1,
  180. IOCTL_WDMAUD_MIDI_OUT_SET_VOLUME);
  181. POSTEXTRACTERROR(mmr,pOutClient);
  182. GlobalFreeDeviceInfo(pOutClient);
  183. return mmr;
  184. case MODM_GETVOLUME:
  185. DPF(DL_TRACE|FA_MIDI, ("MODM_GETVOLUME") );
  186. pOutClient = GlobalAllocDeviceInfo((LPWSTR)dwParam2);
  187. if (pOutClient)
  188. {
  189. pVolume = (LPDWORD) GlobalAllocPtr( GPTR, sizeof(DWORD));
  190. if (pVolume)
  191. {
  192. pOutClient->DeviceType = MidiOutDevice;
  193. pOutClient->DeviceNumber = id;
  194. pOutClient->OpenDone = 0;
  195. PRESETERROR(pOutClient);
  196. mmr = wdmaudIoControl(pOutClient,
  197. sizeof(DWORD),
  198. (LPBYTE)pVolume,
  199. IOCTL_WDMAUD_MIDI_OUT_GET_VOLUME);
  200. POSTEXTRACTERROR(mmr,pOutClient);
  201. //
  202. // Only copy back info on success.
  203. //
  204. if( MMSYSERR_NOERROR == mmr )
  205. *((DWORD FAR *) dwParam1) = *pVolume;
  206. GlobalFreePtr(pVolume);
  207. } else {
  208. mmr = MMSYSERR_NOMEM;
  209. }
  210. GlobalFreeDeviceInfo(pOutClient);
  211. } else {
  212. mmr = MMSYSERR_NOMEM;
  213. }
  214. return mmr;
  215. #ifdef MIDI_STREAM
  216. // TODO: Are we going to support the Midi Streaming
  217. // messages in this rev?
  218. case MODM_PROPERTIES:
  219. return modProperty (&gMidiOutClient, (LPBYTE)dwParam1, dwParam2);
  220. case MODM_STRMDATA:
  221. return modStreamData (&gMidiOutClient, (LPMIDIHDR)dwParam1, (UINT)dwParam2);
  222. case MODM_GETPOS:
  223. return modGetStreamPosition (&gMidiOutClient, (LPMMTIME)dwParam1);
  224. case MODM_STOP:
  225. return modStreamReset (&gMidiOutClient);
  226. case MODM_RESTART:
  227. return modStreamRestart (&gMidiOutClient, dwParam1, dwParam2);
  228. case MODM_PAUSE:
  229. return modStreamPause (&gMidiOutClient);
  230. #endif // MIDI_STREAM support
  231. #ifdef MIDI_THRU
  232. case DRVM_ADD_THRU:
  233. case DRVM_REMOVE_THRU:
  234. // TODO: How do a support thruing in the kernel if I
  235. // only get a device handle from this message.
  236. #endif // MIDI_THRU support
  237. default:
  238. MMRRETURN( MMSYSERR_NOTSUPPORTED );
  239. }
  240. //
  241. // Should not get here
  242. //
  243. DPFASSERT(0);
  244. MMRRETURN( MMSYSERR_NOTSUPPORTED );
  245. }
  246. /****************************************************************************
  247. * @doc INTERNAL
  248. *
  249. * @api DWORD | midiOutWrite | Synchronously process a midi output
  250. * buffer.
  251. *
  252. * @rdesc A MMSYS... type return code for the application.
  253. ***************************************************************************/
  254. MMRESULT FAR midiOutWrite
  255. (
  256. LPDEVICEINFO pClient,
  257. DWORD ulEvent
  258. )
  259. {
  260. MMRESULT mmr = MMSYSERR_ERROR;
  261. BYTE bStatus;
  262. BYTE bNote;
  263. BYTE bVelocity;
  264. UINT uChannel;
  265. DWORD idx;
  266. LPBYTE lpEntry;
  267. bStatus = (BYTE)(ulEvent & 0xFF);
  268. if (!IS_STATUS( bStatus ))
  269. {
  270. bNote = bStatus;
  271. bVelocity = (BYTE)(( ulEvent >> 8 ) & 0x0FF );
  272. bStatus = pClient->DeviceState->bMidiStatus;
  273. }
  274. else
  275. {
  276. bNote = (BYTE)(( ulEvent >> 8 ) & 0xFF );
  277. bVelocity = (BYTE)(( ulEvent >> 16 ) & 0xFF );
  278. pClient->DeviceState->bMidiStatus = bStatus;
  279. }
  280. uChannel = MIDI_CHANNEL( bStatus );
  281. bStatus = MIDI_STATUS( bStatus );
  282. if (MIDI_NOTEON == bStatus ||
  283. MIDI_NOTEOFF == bStatus)
  284. {
  285. idx = ( uChannel << 7 ) | bNote;
  286. lpEntry = &pClient->DeviceState->lpNoteOnMap[idx];
  287. if (( 0 == bVelocity ) ||
  288. ( MIDI_NOTEOFF == bStatus ))
  289. {
  290. if (*lpEntry)
  291. {
  292. --*lpEntry;
  293. }
  294. }
  295. else
  296. {
  297. if (*lpEntry < 255)
  298. {
  299. ++*lpEntry;
  300. }
  301. }
  302. }
  303. //
  304. // Send the MIDI short message
  305. //
  306. mmr = wdmaudIoControl(pClient,
  307. 0, // DataBuffer contains a value and not a pointer
  308. // so we don't need a size.
  309. #ifdef UNDER_NT
  310. UlongToPtr(ulEvent),
  311. #else
  312. &ulEvent,
  313. #endif
  314. IOCTL_WDMAUD_MIDI_OUT_WRITE_DATA);
  315. return mmr;
  316. }
  317. /****************************************************************************
  318. * @doc INTERNAL
  319. *
  320. * @api VOID | midiOutAllNotesOff | Turn off all the notes on this client,
  321. * using the note on map that has been built from outgoing short messages.
  322. *
  323. ***************************************************************************/
  324. VOID FAR midiOutAllNotesOff
  325. (
  326. LPDEVICEINFO pClient
  327. )
  328. {
  329. UINT uNote;
  330. UINT uChannel;
  331. LPBYTE lpNoteOnMap;
  332. LPBYTE lpNoteOnMapEnd;
  333. DWORD dwMessage;
  334. UINT uNoteOffs = 0;
  335. // First turn off the sustain controller on all channels to terminate
  336. // post-note off sound
  337. //
  338. for (uChannel = 0;
  339. uChannel < MIDI_CHANNELS;
  340. uChannel++)
  341. {
  342. dwMessage = MIDI_SUSTAIN( 0, uChannel );
  343. // WorkItem: shouldn't we check the return value here?
  344. wdmaudIoControl(pClient,
  345. 0, // DataBuffer contains a value and not a pointer
  346. // so we don't need a size.
  347. #ifdef UNDER_NT
  348. UlongToPtr(dwMessage),
  349. #else
  350. &dwMessage,
  351. #endif
  352. IOCTL_WDMAUD_MIDI_OUT_WRITE_DATA);
  353. }
  354. // Iterate through the map and track what note and channel each entry corresponds
  355. // to
  356. //
  357. lpNoteOnMap = pClient->DeviceState->lpNoteOnMap;
  358. lpNoteOnMapEnd = lpNoteOnMap + MIDI_NOTE_MAP_SIZE;
  359. uNote = 0;
  360. uChannel = 0;
  361. for ( ;
  362. lpNoteOnMap < lpNoteOnMapEnd;
  363. lpNoteOnMap++ )
  364. {
  365. BYTE bCount = *lpNoteOnMap;
  366. if (bCount)
  367. {
  368. // This note on this channel has some instances playing. Build a note off
  369. // and shut them down
  370. //
  371. *lpNoteOnMap = 0;
  372. dwMessage = MIDI_NOTE_OFF( uNote, uChannel );
  373. while (bCount--)
  374. {
  375. wdmaudIoControl(pClient,
  376. 0, // DataBuffer contains a value and not a pointer
  377. // so we don't need a size.
  378. #ifdef UNDER_NT
  379. UlongToPtr(dwMessage),
  380. #else
  381. &dwMessage,
  382. #endif
  383. IOCTL_WDMAUD_MIDI_OUT_WRITE_DATA);
  384. uNoteOffs++;
  385. }
  386. }
  387. if (++uNote >= MIDI_NOTES)
  388. {
  389. uNote = 0;
  390. ++uChannel;
  391. }
  392. }
  393. }