Source code of Windows XP (NT5)
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.

393 lines
9.2 KiB

  1. /******************************************************************
  2. midint.c - midi routines for NT
  3. Copyright (c) 1991-1994 Microsoft Corporation
  4. *******************************************************************/
  5. #include <windows.h>
  6. #include <mmsystem.h>
  7. #include <mmddk.h>
  8. #include <devioctl.h>
  9. #include <ntddwave.h>
  10. #include <ntddmidi.h>
  11. #include <ntddaux.h>
  12. #include "driver.h"
  13. //
  14. // global variable saying whether the kernel driver thinks
  15. // we have an opl3-type or an adlib-type device
  16. //
  17. UINT gMidiType;
  18. //
  19. // For NT we pipe the port writes to the kernel driver in batches.
  20. // Each batch is a pair of port,data values in DeviceData.
  21. //
  22. // MidiPosition contains the next position to use in the array.
  23. SYNTH_DATA DeviceData[SYNTH_DATA_SIZE];
  24. int MidiPosition;
  25. HANDLE MidiDeviceHandle;
  26. static MIDI_DD_VOLUME MidiVolume;
  27. static MIDI_DD_VOLUME CurrentVolume;
  28. static OVERLAPPED WriteOverlapped; // We need to use this, otherwise
  29. // write file complains.
  30. static OVERLAPPED VolumeOverlapped;// For asynch IO for volume notify
  31. /*
  32. * Translate a win error code (ERROR_...) to a multi-media error code
  33. * (MMSYSERR_...).
  34. *
  35. */
  36. /*************************************************************************
  37. VolLinearToLog - converts a linear scale to logarithm
  38. 0xffff -> 0
  39. 0x0001 -> 191
  40. inputs
  41. WORD volume - 0 to 0xffff
  42. returns
  43. BYTE - value in decibels attenuation, each unit is 1.5 dB
  44. */
  45. BYTE VolLinearToLog (WORD volume)
  46. {
  47. WORD gain, shift;
  48. WORD temp;
  49. WORD lut[16] = {0,0,0,1,1,1,2,2,2,2,3,3,3,3,3,3};
  50. BYTE out;
  51. /* get an estimate to within 6 dB of gain */
  52. for (temp = volume, gain = 0, shift = 0;
  53. temp != 0;
  54. gain += 4, temp >>= 1, shift++);
  55. /* look at highest 3 bits in number into look-up-table to
  56. find how many more dB */
  57. if (shift > 5)
  58. temp = volume >> (shift - 5);
  59. else if (shift < 5)
  60. temp = volume << (5 - shift);
  61. else
  62. temp = volume;
  63. temp &= 0x000f;
  64. gain += lut[temp];
  65. out = (BYTE) ((16 * 4) + 3 - gain);
  66. return (out < 128) ? out : (BYTE)127;
  67. }
  68. /*
  69. * Set the MIDI device volume
  70. */
  71. VOID MidiSetTheVolume(DWORD Left, DWORD Right)
  72. {
  73. CurrentVolume.Left = Left;
  74. CurrentVolume.Right = Right;
  75. //
  76. // Call the routine to store and set the settings
  77. //
  78. MidiNewVolume(VolLinearToLog(HIWORD(Left)), VolLinearToLog(HIWORD(Right)));
  79. }
  80. /*
  81. * See if the device volume has changed, if it has then copy it
  82. * to our local variables.
  83. *
  84. * This is achieved by passing an IOCTL_SOUND_GET_CHANGED volume
  85. * packet to the kernel driver then testing if it's completed.
  86. */
  87. VOID MidiCheckVolume(VOID)
  88. {
  89. DWORD BytesReturned;
  90. if (WaitForSingleObject(VolumeOverlapped.hEvent, 0) == 0) {
  91. //
  92. // We got a volume change - Set the volume we've now got
  93. //
  94. MidiSetTheVolume(MidiVolume.Left, MidiVolume.Right);
  95. //
  96. // Wait until the volume does not change (so the IO does
  97. // not complete
  98. //
  99. while (DeviceIoControl(MidiDeviceHandle,
  100. IOCTL_SOUND_GET_CHANGED_VOLUME,
  101. &MidiVolume,
  102. sizeof(MidiVolume),
  103. &MidiVolume,
  104. sizeof(MidiVolume),
  105. &BytesReturned,
  106. &VolumeOverlapped)) {
  107. MidiSetTheVolume(MidiVolume.Left, MidiVolume.Right);
  108. }
  109. if (GetLastError() == ERROR_IO_PENDING) {
  110. //
  111. // This is what we want
  112. //
  113. return;
  114. } else {
  115. //
  116. // We failed so make sure the next caller doesn't hang!
  117. //
  118. SetEvent(VolumeOverlapped.hEvent);
  119. }
  120. }
  121. }
  122. /*
  123. * Send any data in our output strem to the kernel driver
  124. */
  125. VOID MidiFlush(VOID)
  126. {
  127. DWORD BytesWritten;
  128. if (MidiPosition != 0) {
  129. WriteFile(MidiDeviceHandle,
  130. DeviceData,
  131. MidiPosition * sizeof(SYNTH_DATA),
  132. &BytesWritten,
  133. &WriteOverlapped);
  134. }
  135. //
  136. // We know our kernel driver doesn't operate asynchronously so
  137. // we don't need to wait for the write to complete.
  138. //
  139. MidiPosition = 0;
  140. }
  141. /*
  142. * Close the kernel device (if write type)
  143. */
  144. VOID MidiCloseDevice(HANDLE DeviceHandle)
  145. {
  146. /*
  147. * Close the device first to stop any more events
  148. */
  149. CloseHandle(DeviceHandle);
  150. CloseHandle(VolumeOverlapped.hEvent);
  151. CloseHandle(WriteOverlapped.hEvent);
  152. DeviceHandle = NULL;
  153. VolumeOverlapped.hEvent = NULL;
  154. WriteOverlapped.hEvent = NULL;
  155. }
  156. /*
  157. * Open the kernel device corresponding to our midi device
  158. */
  159. MMRESULT MidiOpenDevice(LPHANDLE lpHandle, BOOL Write)
  160. {
  161. MMRESULT mmr;
  162. HANDLE DeviceHandle;
  163. mmr = sndOpenDev(SYNTH_DEVICE, 0, &DeviceHandle,
  164. Write ? GENERIC_READ | GENERIC_WRITE :
  165. GENERIC_READ);
  166. if (mmr != MMSYSERR_NOERROR) {
  167. return mmr;
  168. }
  169. //
  170. // Load patches etc if we're actually going to write to the device
  171. //
  172. if (Write) {
  173. DWORD dwBytesReturned;
  174. /*
  175. * Try to set it into OPL3 mode
  176. */
  177. gMidiType = DeviceIoControl(DeviceHandle,
  178. IOCTL_MIDI_SET_OPL3_MODE,
  179. NULL,
  180. 0,
  181. NULL,
  182. 0,
  183. &dwBytesReturned,
  184. NULL) ?
  185. TYPE_OPL3 : TYPE_ADLIB;
  186. /*
  187. * always call MidiInit, in case we have not loaded the patches
  188. * for this device type. MidiInit can have a static bInit if needed
  189. */
  190. MidiInit();
  191. //
  192. // Create an event for waiting for volume changes and an
  193. // event for writes.
  194. //
  195. VolumeOverlapped.hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
  196. if (VolumeOverlapped.hEvent == NULL) {
  197. CloseHandle(DeviceHandle);
  198. return sndTranslateStatus();
  199. }
  200. WriteOverlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  201. if (WriteOverlapped.hEvent == NULL) {
  202. CloseHandle(VolumeOverlapped.hEvent);
  203. CloseHandle(DeviceHandle);
  204. return sndTranslateStatus();
  205. }
  206. }
  207. //
  208. // Return our handle to the caller
  209. //
  210. *lpHandle = DeviceHandle;
  211. //
  212. // Set ourselves up to find out about volume changes
  213. //
  214. if (Write) {
  215. MidiCheckVolume();
  216. }
  217. return MMSYSERR_NOERROR;
  218. }
  219. /*
  220. * Read the current volume setting direct from the kernel driver
  221. */
  222. MMRESULT MidiGetVolume(LPDWORD lpVolume)
  223. {
  224. HANDLE hDevice;
  225. MIDI_DD_VOLUME Vol;
  226. MMRESULT mRc;
  227. DWORD BytesReturned;
  228. //
  229. // Open a new device and get the volume
  230. //
  231. mRc = MidiOpenDevice(&hDevice, FALSE); // Open for read only
  232. if (mRc == MMSYSERR_NOERROR) {
  233. if (!DeviceIoControl(hDevice,
  234. IOCTL_MIDI_GET_VOLUME,
  235. NULL,
  236. 0,
  237. &Vol,
  238. sizeof(MIDI_DD_VOLUME),
  239. &BytesReturned,
  240. NULL)) {
  241. mRc = sndTranslateStatus();
  242. } else {
  243. *lpVolume = (DWORD)MAKELONG(HIWORD(Vol.Left), HIWORD(Vol.Right));
  244. }
  245. CloseHandle(hDevice);
  246. }
  247. return mRc;
  248. }
  249. /*
  250. * Set the volume by calling the kernel driver - this will cause our
  251. * IOCTL_SOUND_GET_CHANGED_VOLUME packet to complete
  252. */
  253. MMRESULT MidiSetVolume(DWORD Left, DWORD Right)
  254. {
  255. HANDLE hDevice;
  256. MIDI_DD_VOLUME Vol;
  257. MMRESULT mRc;
  258. DWORD BytesReturned;
  259. //
  260. // Open a new device and set the volume
  261. //
  262. Vol.Left = Left;
  263. Vol.Right = Right;
  264. mRc = MidiOpenDevice(&hDevice, FALSE); // Open for read only
  265. if (mRc == MMSYSERR_NOERROR) {
  266. if (!DeviceIoControl(hDevice,
  267. IOCTL_MIDI_SET_VOLUME,
  268. &Vol,
  269. sizeof(MIDI_DD_VOLUME),
  270. NULL,
  271. 0,
  272. &BytesReturned,
  273. NULL)) {
  274. mRc = sndTranslateStatus();
  275. }
  276. CloseHandle(hDevice);
  277. }
  278. return mRc;
  279. }
  280. /**************************************************************
  281. * MidiSendFM - Sends a byte to the FM chip.
  282. *
  283. * inputs
  284. * WORD wAddress - 0x00 to 0x1ff
  285. * BYTE bValue - value wirtten
  286. * returns
  287. * none
  288. */
  289. VOID FAR PASCAL MidiSendFM (DWORD wAddress, BYTE bValue)
  290. {
  291. // NT :
  292. //
  293. // Pipe our port writes to the kernel driver
  294. // Note that MidiFlush is called again after each midi message
  295. // is processed by modMessage.
  296. //
  297. if (MidiPosition == SYNTH_DATA_SIZE) {
  298. MidiFlush();
  299. }
  300. DeviceData[MidiPosition].IoPort = wAddress < 0x100 ? 0x388 : 0x38a;
  301. DeviceData[MidiPosition].PortData = (WORD)(BYTE)wAddress;
  302. DeviceData[MidiPosition + 1].IoPort = wAddress < 0x100 ? 0x389 : 0x38b;
  303. DeviceData[MidiPosition + 1].PortData = (WORD)bValue;
  304. MidiPosition += 2;
  305. }