|
|
1.1 ! root 1: /****************************************************************** ! 2: ! 3: midint.c - midi routines for NT ! 4: ! 5: ! 6: Copyright (c) 1991 Microsoft Corporation. All Rights Reserved. ! 7: ! 8: *******************************************************************/ ! 9: ! 10: #include <windows.h> ! 11: #include <mmsystem.h> ! 12: #include <mmddk.h> ! 13: #include <devioctl.h> ! 14: #include <ntddwave.h> ! 15: #include <ntddmidi.h> ! 16: #include <ntddaux.h> ! 17: #include "driver.h" ! 18: ! 19: // ! 20: // global variable saying whether the kernel driver thinks ! 21: // we have an opl3-type or an adlib-type device ! 22: // ! 23: UINT gMidiType; ! 24: ! 25: // ! 26: // For NT we pipe the port writes to the kernel driver in batches. ! 27: // Each batch is a pair of port,data values in DeviceData. ! 28: // ! 29: // MidiPosition contains the next position to use in the array. ! 30: ! 31: SYNTH_DATA DeviceData[SYNTH_DATA_SIZE]; ! 32: int MidiPosition; ! 33: HANDLE MidiDeviceHandle; ! 34: static MIDI_DD_VOLUME MidiVolume; ! 35: static MIDI_DD_VOLUME CurrentVolume; ! 36: ! 37: static OVERLAPPED WriteOverlapped; // We need to use this, otherwise ! 38: // write file complains. ! 39: ! 40: static OVERLAPPED VolumeOverlapped;// For asynch IO for volume notify ! 41: ! 42: /* ! 43: * Translate a win error code (ERROR_...) to a multi-media error code ! 44: * (MMSYSERR_...). ! 45: * ! 46: */ ! 47: ! 48: MMRESULT MidiTranslateStatus(VOID) ! 49: { ! 50: // ! 51: // NOTE code copied from mmdrv!drvutil.c!sndTranslateStatus ! 52: // ! 53: ! 54: switch (GetLastError()) { ! 55: case NO_ERROR: ! 56: break; ! 57: ! 58: case ERROR_BUSY: ! 59: return MMSYSERR_ALLOCATED; ! 60: ! 61: case ERROR_NOT_SUPPORTED: ! 62: case ERROR_INVALID_FUNCTION: ! 63: return MMSYSERR_NOTSUPPORTED; ! 64: ! 65: case ERROR_NOT_ENOUGH_MEMORY: ! 66: return MMSYSERR_NOMEM; ! 67: ! 68: case ERROR_ACCESS_DENIED: ! 69: return MMSYSERR_BADDEVICEID; ! 70: ! 71: case ERROR_INSUFFICIENT_BUFFER: ! 72: return MMSYSERR_INVALPARAM; ! 73: ! 74: default: ! 75: return MMSYSERR_ERROR; ! 76: } ! 77: ! 78: } ! 79: ! 80: ! 81: /************************************************************************* ! 82: VolLinearToLog - converts a linear scale to logarithm ! 83: 0xffff -> 0 ! 84: 0x0001 -> 191 ! 85: ! 86: inputs ! 87: WORD volume - 0 to 0xffff ! 88: returns ! 89: BYTE - value in decibels attenuation, each unit is 1.5 dB ! 90: */ ! 91: BYTE VolLinearToLog (WORD volume) ! 92: { ! 93: WORD gain, shift; ! 94: WORD temp; ! 95: WORD lut[16] = {0,0,0,1,1,1,2,2,2,2,3,3,3,3,3,3}; ! 96: BYTE out; ! 97: ! 98: /* get an estimate to within 6 dB of gain */ ! 99: for (temp = volume, gain = 0, shift = 0; ! 100: temp != 0; ! 101: gain += 4, temp >>= 1, shift++); ! 102: ! 103: /* look at highest 3 bits in number into look-up-table to ! 104: find how many more dB */ ! 105: if (shift > 5) ! 106: temp = volume >> (shift - 5); ! 107: else if (shift < 5) ! 108: temp = volume << (5 - shift); ! 109: else ! 110: temp = volume; ! 111: temp &= 0x000f; ! 112: ! 113: gain += lut[temp]; ! 114: ! 115: out = (BYTE) ((16 * 4) + 3 - gain); ! 116: return (out < 128) ? out : (BYTE)127; ! 117: } ! 118: ! 119: /* ! 120: * Set the MIDI device volume ! 121: */ ! 122: ! 123: VOID MidiSetTheVolume(DWORD Left, DWORD Right) ! 124: { ! 125: CurrentVolume.Left = Left; ! 126: CurrentVolume.Right = Right; ! 127: ! 128: ! 129: // ! 130: // Call the routine to store and set the settings ! 131: // ! 132: ! 133: MidiNewVolume(VolLinearToLog(HIWORD(Left)), VolLinearToLog(HIWORD(Right))); ! 134: } ! 135: ! 136: /* ! 137: * See if the device volume has changed, if it has then copy it ! 138: * to our local variables. ! 139: * ! 140: * This is achieved by passing an IOCTL_SOUND_GET_CHANGED volume ! 141: * packet to the kernel driver then testing if it's completed. ! 142: */ ! 143: ! 144: VOID MidiCheckVolume(VOID) ! 145: { ! 146: DWORD BytesReturned; ! 147: ! 148: if (WaitForSingleObject(VolumeOverlapped.hEvent, 0) == 0) { ! 149: // ! 150: // We got a volume change - Set the volume we've now got ! 151: // ! 152: ! 153: MidiSetTheVolume(MidiVolume.Left, MidiVolume.Right); ! 154: ! 155: // ! 156: // Wait until the volume does not change (so the IO does ! 157: // not complete ! 158: // ! 159: ! 160: while (DeviceIoControl(MidiDeviceHandle, ! 161: IOCTL_SOUND_GET_CHANGED_VOLUME, ! 162: &MidiVolume, ! 163: sizeof(MidiVolume), ! 164: &MidiVolume, ! 165: sizeof(MidiVolume), ! 166: &BytesReturned, ! 167: &VolumeOverlapped)) { ! 168: MidiSetTheVolume(MidiVolume.Left, MidiVolume.Right); ! 169: } ! 170: if (GetLastError() == ERROR_IO_PENDING) { ! 171: // ! 172: // This is what we want ! 173: // ! 174: return; ! 175: } else { ! 176: // ! 177: // We failed so make sure the next caller doesn't hang! ! 178: // ! 179: SetEvent(VolumeOverlapped.hEvent); ! 180: } ! 181: } ! 182: } ! 183: ! 184: /* ! 185: * Send any data in our output strem to the kernel driver ! 186: */ ! 187: ! 188: VOID MidiFlush(VOID) ! 189: { ! 190: ! 191: DWORD BytesWritten; ! 192: ! 193: if (MidiPosition != 0) { ! 194: WriteFile(MidiDeviceHandle, ! 195: DeviceData, ! 196: MidiPosition * sizeof(SYNTH_DATA), ! 197: &BytesWritten, ! 198: &WriteOverlapped); ! 199: } ! 200: ! 201: // ! 202: // We know our kernel driver doesn't operate asynchronously so ! 203: // we don't need to wait for the write to complete. ! 204: // ! 205: ! 206: MidiPosition = 0; ! 207: } ! 208: ! 209: /* ! 210: * Close the kernel device (if write type) ! 211: */ ! 212: ! 213: VOID MidiCloseDevice(HANDLE DeviceHandle) ! 214: { ! 215: /* ! 216: * Close the device first to stop any more events ! 217: */ ! 218: ! 219: CloseHandle(DeviceHandle); ! 220: CloseHandle(VolumeOverlapped.hEvent); ! 221: CloseHandle(WriteOverlapped.hEvent); ! 222: DeviceHandle = NULL; ! 223: VolumeOverlapped.hEvent = NULL; ! 224: WriteOverlapped.hEvent = NULL; ! 225: ! 226: } ! 227: ! 228: /* ! 229: * Open the kernel device corresponding to our midi device ! 230: */ ! 231: ! 232: MMRESULT MidiOpenDevice(LPHANDLE lpHandle, BOOL Write) ! 233: { ! 234: HANDLE DeviceHandle; ! 235: ! 236: /* attempt to open the OPL3 device first. if this fails, ! 237: * try the adlib. ! 238: */ ! 239: DeviceHandle = CreateFile(L"\\\\.\\opl3.mid", ! 240: Write ? GENERIC_READ | GENERIC_WRITE : ! 241: GENERIC_READ, ! 242: FILE_SHARE_WRITE, ! 243: NULL, ! 244: OPEN_EXISTING, ! 245: Write ? FILE_FLAG_OVERLAPPED : 0, ! 246: NULL); ! 247: ! 248: if (DeviceHandle != INVALID_HANDLE_VALUE) { ! 249: gMidiType = TYPE_OPL3; ! 250: } else { ! 251: /* try for an adlib card instead */ ! 252: DeviceHandle = CreateFile(L"\\\\.\\adlib.mid", ! 253: Write ? GENERIC_READ | GENERIC_WRITE : ! 254: GENERIC_READ, ! 255: FILE_SHARE_WRITE, ! 256: NULL, ! 257: OPEN_EXISTING, ! 258: Write ? FILE_FLAG_OVERLAPPED : 0, ! 259: NULL); ! 260: ! 261: if (DeviceHandle == INVALID_HANDLE_VALUE) { ! 262: return MidiTranslateStatus(); ! 263: } else { ! 264: gMidiType = TYPE_ADLIB; ! 265: } ! 266: } ! 267: ! 268: // ! 269: // Load patches etc if we're actually going to write to the device ! 270: // ! 271: ! 272: if (Write) { ! 273: /* ! 274: * always call MidiInit, in case we have not loaded the patches ! 275: * for this device type. MidiInit can have a static bInit if needed ! 276: */ ! 277: MidiInit(); ! 278: ! 279: // ! 280: // Create an event for waiting for volume changes and an ! 281: // event for writes. ! 282: // ! 283: ! 284: VolumeOverlapped.hEvent = CreateEvent(NULL, FALSE, TRUE, NULL); ! 285: ! 286: if (VolumeOverlapped.hEvent == NULL) { ! 287: CloseHandle(DeviceHandle); ! 288: ! 289: return MidiTranslateStatus(); ! 290: } ! 291: ! 292: WriteOverlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); ! 293: ! 294: if (WriteOverlapped.hEvent == NULL) { ! 295: CloseHandle(VolumeOverlapped.hEvent); ! 296: CloseHandle(DeviceHandle); ! 297: ! 298: return MidiTranslateStatus(); ! 299: } ! 300: } ! 301: ! 302: // ! 303: // Return our handle to the caller ! 304: // ! 305: ! 306: *lpHandle = DeviceHandle; ! 307: ! 308: // ! 309: // Set ourselves up to find out about volume changes ! 310: // ! 311: ! 312: if (Write) { ! 313: MidiCheckVolume(); ! 314: } ! 315: ! 316: ! 317: return MMSYSERR_NOERROR; ! 318: ! 319: } ! 320: ! 321: /* ! 322: * Read the current volume setting direct from the kernel driver ! 323: */ ! 324: ! 325: MMRESULT MidiGetVolume(LPDWORD lpVolume) ! 326: { ! 327: HANDLE hDevice; ! 328: MIDI_DD_VOLUME Vol; ! 329: MMRESULT mRc; ! 330: DWORD BytesReturned; ! 331: ! 332: // ! 333: // Open a new device and get the volume ! 334: // ! 335: ! 336: mRc = MidiOpenDevice(&hDevice, FALSE); // Open for read only ! 337: ! 338: if (mRc == MMSYSERR_NOERROR) { ! 339: ! 340: if (!DeviceIoControl(hDevice, ! 341: IOCTL_MIDI_GET_VOLUME, ! 342: NULL, ! 343: 0, ! 344: &Vol, ! 345: sizeof(MIDI_DD_VOLUME), ! 346: &BytesReturned, ! 347: NULL)) { ! 348: mRc = MidiTranslateStatus(); ! 349: } else { ! 350: *lpVolume = (DWORD)MAKELONG(HIWORD(Vol.Left), HIWORD(Vol.Right)); ! 351: } ! 352: CloseHandle(hDevice); ! 353: } ! 354: ! 355: return mRc; ! 356: } ! 357: ! 358: /* ! 359: * Set the volume by calling the kernel driver - this will cause our ! 360: * IOCTL_SOUND_GET_CHANGED_VOLUME packet to complete ! 361: */ ! 362: ! 363: MMRESULT MidiSetVolume(DWORD Left, DWORD Right) ! 364: { ! 365: HANDLE hDevice; ! 366: MIDI_DD_VOLUME Vol; ! 367: MMRESULT mRc; ! 368: DWORD BytesReturned; ! 369: ! 370: // ! 371: // Open a new device and set the volume ! 372: // ! 373: ! 374: Vol.Left = Left; ! 375: Vol.Right = Right; ! 376: ! 377: mRc = MidiOpenDevice(&hDevice, FALSE); // Open for read only ! 378: ! 379: if (mRc == MMSYSERR_NOERROR) { ! 380: ! 381: if (!DeviceIoControl(hDevice, ! 382: IOCTL_MIDI_SET_VOLUME, ! 383: &Vol, ! 384: sizeof(MIDI_DD_VOLUME), ! 385: NULL, ! 386: 0, ! 387: &BytesReturned, ! 388: NULL)) { ! 389: mRc = MidiTranslateStatus(); ! 390: } ! 391: CloseHandle(hDevice); ! 392: } ! 393: ! 394: return mRc; ! 395: } ! 396: ! 397: ! 398: /************************************************************** ! 399: * MidiSendFM - Sends a byte to the FM chip. ! 400: * ! 401: * inputs ! 402: * WORD wAddress - 0x00 to 0x1ff ! 403: * BYTE bValue - value wirtten ! 404: * returns ! 405: * none ! 406: */ ! 407: VOID FAR PASCAL MidiSendFM (DWORD wAddress, BYTE bValue) ! 408: { ! 409: ! 410: ! 411: // NT : ! 412: // ! 413: // Pipe our port writes to the kernel driver ! 414: // Note that MidiFlush is called again after each midi message ! 415: // is processed by modMessage. ! 416: // ! 417: ! 418: if (MidiPosition == SYNTH_DATA_SIZE) { ! 419: MidiFlush(); ! 420: } ! 421: ! 422: DeviceData[MidiPosition].IoPort = wAddress < 0x100 ? 0x388 : 0x38a; ! 423: DeviceData[MidiPosition].PortData = (WORD)(BYTE)wAddress; ! 424: DeviceData[MidiPosition + 1].IoPort = wAddress < 0x100 ? 0x389 : 0x38b; ! 425: DeviceData[MidiPosition + 1].PortData = (WORD)bValue; ! 426: ! 427: MidiPosition += 2; ! 428: ! 429: } ! 430:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.