|
|
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.