|
|
/******************************************************************
midint.c - midi routines for NT
Copyright (c) 1991-1994 Microsoft Corporation
*******************************************************************/
#include <windows.h>
#include <mmsystem.h>
#include <mmddk.h>
#include <devioctl.h>
#include <ntddwave.h>
#include <ntddmidi.h>
#include <ntddaux.h>
#include "driver.h"
//
// global variable saying whether the kernel driver thinks
// we have an opl3-type or an adlib-type device
//
UINT gMidiType;
//
// For NT we pipe the port writes to the kernel driver in batches.
// Each batch is a pair of port,data values in DeviceData.
//
// MidiPosition contains the next position to use in the array.
SYNTH_DATA DeviceData[SYNTH_DATA_SIZE]; int MidiPosition; HANDLE MidiDeviceHandle; static MIDI_DD_VOLUME MidiVolume; static MIDI_DD_VOLUME CurrentVolume;
static OVERLAPPED WriteOverlapped; // We need to use this, otherwise
// write file complains.
static OVERLAPPED VolumeOverlapped;// For asynch IO for volume notify
/*
* Translate a win error code (ERROR_...) to a multi-media error code * (MMSYSERR_...). * */
/*************************************************************************
VolLinearToLog - converts a linear scale to logarithm 0xffff -> 0 0x0001 -> 191
inputs WORD volume - 0 to 0xffff returns BYTE - value in decibels attenuation, each unit is 1.5 dB */ BYTE VolLinearToLog (WORD volume) { WORD gain, shift; WORD temp; WORD lut[16] = {0,0,0,1,1,1,2,2,2,2,3,3,3,3,3,3}; BYTE out;
/* get an estimate to within 6 dB of gain */ for (temp = volume, gain = 0, shift = 0; temp != 0; gain += 4, temp >>= 1, shift++);
/* look at highest 3 bits in number into look-up-table to
find how many more dB */ if (shift > 5) temp = volume >> (shift - 5); else if (shift < 5) temp = volume << (5 - shift); else temp = volume; temp &= 0x000f;
gain += lut[temp];
out = (BYTE) ((16 * 4) + 3 - gain); return (out < 128) ? out : (BYTE)127; }
/*
* Set the MIDI device volume */
VOID MidiSetTheVolume(DWORD Left, DWORD Right) { CurrentVolume.Left = Left; CurrentVolume.Right = Right;
//
// Call the routine to store and set the settings
//
MidiNewVolume(VolLinearToLog(HIWORD(Left)), VolLinearToLog(HIWORD(Right))); }
/*
* See if the device volume has changed, if it has then copy it * to our local variables. * * This is achieved by passing an IOCTL_SOUND_GET_CHANGED volume * packet to the kernel driver then testing if it's completed. */
VOID MidiCheckVolume(VOID) { DWORD BytesReturned;
if (WaitForSingleObject(VolumeOverlapped.hEvent, 0) == 0) { //
// We got a volume change - Set the volume we've now got
//
MidiSetTheVolume(MidiVolume.Left, MidiVolume.Right);
//
// Wait until the volume does not change (so the IO does
// not complete
//
while (DeviceIoControl(MidiDeviceHandle, IOCTL_SOUND_GET_CHANGED_VOLUME, &MidiVolume, sizeof(MidiVolume), &MidiVolume, sizeof(MidiVolume), &BytesReturned, &VolumeOverlapped)) { MidiSetTheVolume(MidiVolume.Left, MidiVolume.Right); } if (GetLastError() == ERROR_IO_PENDING) { //
// This is what we want
//
return; } else { //
// We failed so make sure the next caller doesn't hang!
//
SetEvent(VolumeOverlapped.hEvent); } } }
/*
* Send any data in our output strem to the kernel driver */
VOID MidiFlush(VOID) {
DWORD BytesWritten;
if (MidiPosition != 0) { WriteFile(MidiDeviceHandle, DeviceData, MidiPosition * sizeof(SYNTH_DATA), &BytesWritten, &WriteOverlapped); }
//
// We know our kernel driver doesn't operate asynchronously so
// we don't need to wait for the write to complete.
//
MidiPosition = 0; }
/*
* Close the kernel device (if write type) */
VOID MidiCloseDevice(HANDLE DeviceHandle) { /*
* Close the device first to stop any more events */
CloseHandle(DeviceHandle); CloseHandle(VolumeOverlapped.hEvent); CloseHandle(WriteOverlapped.hEvent); DeviceHandle = NULL; VolumeOverlapped.hEvent = NULL; WriteOverlapped.hEvent = NULL;
}
/*
* Open the kernel device corresponding to our midi device */
MMRESULT MidiOpenDevice(LPHANDLE lpHandle, BOOL Write) { MMRESULT mmr; HANDLE DeviceHandle;
mmr = sndOpenDev(SYNTH_DEVICE, 0, &DeviceHandle, Write ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ);
if (mmr != MMSYSERR_NOERROR) { return mmr; }
//
// Load patches etc if we're actually going to write to the device
//
if (Write) {
DWORD dwBytesReturned;
/*
* Try to set it into OPL3 mode */
gMidiType = DeviceIoControl(DeviceHandle, IOCTL_MIDI_SET_OPL3_MODE, NULL, 0, NULL, 0, &dwBytesReturned, NULL) ?
TYPE_OPL3 : TYPE_ADLIB;
/*
* always call MidiInit, in case we have not loaded the patches * for this device type. MidiInit can have a static bInit if needed */ MidiInit();
//
// Create an event for waiting for volume changes and an
// event for writes.
//
VolumeOverlapped.hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
if (VolumeOverlapped.hEvent == NULL) { CloseHandle(DeviceHandle);
return sndTranslateStatus(); }
WriteOverlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (WriteOverlapped.hEvent == NULL) { CloseHandle(VolumeOverlapped.hEvent); CloseHandle(DeviceHandle);
return sndTranslateStatus(); } }
//
// Return our handle to the caller
//
*lpHandle = DeviceHandle;
//
// Set ourselves up to find out about volume changes
//
if (Write) { MidiCheckVolume(); }
return MMSYSERR_NOERROR;
}
/*
* Read the current volume setting direct from the kernel driver */
MMRESULT MidiGetVolume(LPDWORD lpVolume) { HANDLE hDevice; MIDI_DD_VOLUME Vol; MMRESULT mRc; DWORD BytesReturned;
//
// Open a new device and get the volume
//
mRc = MidiOpenDevice(&hDevice, FALSE); // Open for read only
if (mRc == MMSYSERR_NOERROR) {
if (!DeviceIoControl(hDevice, IOCTL_MIDI_GET_VOLUME, NULL, 0, &Vol, sizeof(MIDI_DD_VOLUME), &BytesReturned, NULL)) { mRc = sndTranslateStatus(); } else { *lpVolume = (DWORD)MAKELONG(HIWORD(Vol.Left), HIWORD(Vol.Right)); } CloseHandle(hDevice); }
return mRc; }
/*
* Set the volume by calling the kernel driver - this will cause our * IOCTL_SOUND_GET_CHANGED_VOLUME packet to complete */
MMRESULT MidiSetVolume(DWORD Left, DWORD Right) { HANDLE hDevice; MIDI_DD_VOLUME Vol; MMRESULT mRc; DWORD BytesReturned;
//
// Open a new device and set the volume
//
Vol.Left = Left; Vol.Right = Right;
mRc = MidiOpenDevice(&hDevice, FALSE); // Open for read only
if (mRc == MMSYSERR_NOERROR) {
if (!DeviceIoControl(hDevice, IOCTL_MIDI_SET_VOLUME, &Vol, sizeof(MIDI_DD_VOLUME), NULL, 0, &BytesReturned, NULL)) { mRc = sndTranslateStatus(); } CloseHandle(hDevice); }
return mRc; }
/**************************************************************
* MidiSendFM - Sends a byte to the FM chip. * * inputs * WORD wAddress - 0x00 to 0x1ff * BYTE bValue - value wirtten * returns * none */ VOID FAR PASCAL MidiSendFM (DWORD wAddress, BYTE bValue) {
// NT :
//
// Pipe our port writes to the kernel driver
// Note that MidiFlush is called again after each midi message
// is processed by modMessage.
//
if (MidiPosition == SYNTH_DATA_SIZE) { MidiFlush(); }
DeviceData[MidiPosition].IoPort = wAddress < 0x100 ? 0x388 : 0x38a; DeviceData[MidiPosition].PortData = (WORD)(BYTE)wAddress; DeviceData[MidiPosition + 1].IoPort = wAddress < 0x100 ? 0x389 : 0x38b; DeviceData[MidiPosition + 1].PortData = (WORD)bValue;
MidiPosition += 2;
}
|