📄 miniport.cpp
字号:
if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_VOLUMELEVEL)
{
ntStatus = BasicSupportHandler(PropertyRequest);
}
} // (Verb & KSPROPERTY_TYPE_BASICSUPPORT)
} // (Node == eFMVolumeNode)
return ntStatus;
}
#pragma code_seg()
// convert from 16.16 dB to [0,63], set m_wSynthAttenR
void
CMiniportMidiStreamFM::
SetFMAtten
(
IN LONG channel,
IN LONG level
)
{
KIRQL oldIrql;
if ((channel == CHAN_LEFT) || (channel == CHAN_MASTER))
{
m_SavedVolValue[CHAN_LEFT] = level;
if (level > m_MaxVolValue)
m_wSynthAttenL = 0;
else if (level < m_MinVolValue)
m_wSynthAttenL = 63;
else
m_wSynthAttenL = WORD(-level / (LONG)m_VolStepDelta);
}
if ((channel == CHAN_RIGHT) || (channel == CHAN_MASTER))
{
m_SavedVolValue[CHAN_RIGHT] = level;
if (level > m_MaxVolValue)
m_wSynthAttenR = 0;
else if (level < m_MinVolValue)
m_wSynthAttenR = 63;
else
m_wSynthAttenR = WORD(-level / (LONG)m_VolStepDelta);
}
#ifdef USE_KDPRINT
KdPrint(("'StreamFM::SetFMAtten: channel: 0x%X, level: 0x%X, m_wSynthAttenL: 0x%X, m_wSynthAttenR: 0x%X \n",
channel, level, m_wSynthAttenL, m_wSynthAttenR));
#else // USE_KDPRINT
_DbgPrintF(DEBUGLVL_VERBOSE,("StreamFM::SetFMAtten: channel: 0x%X, level: 0x%X, m_wSynthAttenL: 0x%X, m_wSynthAttenR: 0x%X \n",
channel, level, m_wSynthAttenL, m_wSynthAttenR));
#endif // USE_KDPRINT
KeAcquireSpinLock(&m_Miniport->m_SpinLock,&oldIrql);
Opl3_SetVolume(0xFF); // 0xFF means all channels
KeReleaseSpinLock(&m_Miniport->m_SpinLock,oldIrql);
}
#pragma code_seg("PAGE")
/*****************************************************************************
* PropertyHandler_CpuResources()
*****************************************************************************
* Processes a KSPROPERTY_AUDIO_CPU_RESOURCES request
*/
static
NTSTATUS PropertyHandler_CpuResources
(
IN PPCPROPERTY_REQUEST PropertyRequest
)
{
PAGED_CODE();
ASSERT(PropertyRequest);
_DbgPrintF(DEBUGLVL_VERBOSE,("PropertyHandler_CpuResources"));
NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
// validate node
if(PropertyRequest->Node == eFMVolumeNode)
{
if(PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
{
if(PropertyRequest->ValueSize >= sizeof(LONG))
{
*(PLONG(PropertyRequest->Value)) = KSAUDIO_CPU_RESOURCES_NOT_HOST_CPU;
PropertyRequest->ValueSize = sizeof(LONG);
ntStatus = STATUS_SUCCESS;
}
else
{
_DbgPrintF(DEBUGLVL_VERBOSE,("PropertyHandler_CpuResources failed, buffer too small"));
ntStatus = STATUS_BUFFER_TOO_SMALL;
}
}
}
return ntStatus;
}
#pragma code_seg()
// ==============================================================================
// SoundMidiSendFM
// Writes out to the device.
// Called from DPC code (Write->WriteMidiData->Opl3_NoteOn->Opl3_FMNote->here)
// ==============================================================================
void
CMiniportMidiFM::
SoundMidiSendFM
(
IN PUCHAR PortBase,
IN ULONG Address,
IN UCHAR Data
)
{
ASSERT(Address < 0x200);
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
// these delays need to be 23us at least for old opl2 chips, even
// though new chips can handle 1 us delays.
#ifdef USE_KDPRINT
KdPrint(("'SoundMidiSendFM(%02x %02x) \n",Address,Data));
#else // USE_KDPRINT
_DbgPrintF(DEBUGLVL_VERBOSE, ("%X\t%X", Address,Data));
#endif // USE_KDPRINT
WRITE_PORT_UCHAR(PortBase + (Address < 0x100 ? 0 : 2), (UCHAR)Address);
KeStallExecutionProcessor(23);
WRITE_PORT_UCHAR(PortBase + (Address < 0x100 ? 1 : 3), Data);
KeStallExecutionProcessor(23);
m_SavedRegValues[Address] = Data;
}
#pragma code_seg()
// ==============================================================================
// Service()
// DPC-mode service call from the port.
// ==============================================================================
STDMETHODIMP_(void)
CMiniportMidiFM::
Service
( void
)
{
}
#pragma code_seg()
// ==============================================================================
// CMiniportMidiStreamFM::Read()
// Reads incoming MIDI data.
// ==============================================================================
STDMETHODIMP_(NTSTATUS)
CMiniportMidiStreamFM::
Read
(
IN PVOID BufferAddress,
IN ULONG Length,
OUT PULONG BytesRead
)
{
return STATUS_NOT_IMPLEMENTED;
}
#pragma code_seg()
// ==============================================================================
// CMiniportMidiStreamFM::Write()
// Writes outgoing MIDI data.
//
// N.B.!!!
// THIS DATA SINK ASSUMES THAT DATA COMES IN ONE MESSAGE AT A TIME!!!
// IF LENGTH IS MORE THAN THREE BYTES, SUCH AS SYSEX OR MULTIPLE MIDI
// MESSAGES, ALL THE DATA IS DROPPED UNCEREMONIOUSLY ON THE FLOOR!!!
// ALSO DOES NOT PLAY WELL WITH RUNNING STATUS!!!
//
// CLEARLY, THIS MINIPORT HAS SOME "ISSUES".
//
// ==============================================================================
STDMETHODIMP_(NTSTATUS)
CMiniportMidiStreamFM::
Write
(
IN PVOID BufferAddress, // pointer to Midi Data.
IN ULONG Length,
OUT PULONG BytesWritten
)
{
ASSERT(BufferAddress);
ASSERT(BytesWritten);
_DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportMidiStreamFM::Write"));
BYTE statusByte = *(PBYTE)BufferAddress & 0xF0;
*BytesWritten = Length;
if (statusByte < 0x80)
{
_DbgPrintF(DEBUGLVL_TERSE, ("CMiniportMidiStreamFM::Write requires first byte to be status -- ignored"));
}
else if (statusByte == 0xF0)
{
_DbgPrintF(DEBUGLVL_VERBOSE, ("StreamFM::Write doesn't handle System messages -- ignored"));
}
else if (statusByte == 0xA0)
{
_DbgPrintF(DEBUGLVL_VERBOSE, ("StreamFM::Write doesn't handle Polyphonic key pressure/Aftertouch messages -- ignored"));
}
else if (statusByte == 0xD0)
{
_DbgPrintF(DEBUGLVL_VERBOSE, ("StreamFM::Write doesn't handle Channel pressure/Aftertouch messages -- ignored"));
}
else if (Length < 4)
{
WriteMidiData(*(DWORD *)BufferAddress);
}
else
{
_DbgPrintF(DEBUGLVL_TERSE, ("StreamFM::Write doesn't handle Length > 3."));
}
return STATUS_SUCCESS;
}
// ==============================================================================
// ==============================================================================
// Private Methods of CMiniportMidiFM
// ==============================================================================
// ==============================================================================
#pragma code_seg()
// =================================================================
// SoundMidiIsOpl3
// Checks if the midi synthesizer is Opl3 compatible or just adlib-compatible.
// returns: TRUE if OPL3-compatible chip. FALSE otherwise.
//
// NOTE: This has been taken as is from the nt driver code.
// =================================================================
BOOL CMiniportMidiFM::
SoundMidiIsOpl3(void)
{
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
BOOL bIsOpl3 = FALSE;
/*
* theory: an opl3-compatible synthesizer chip looks
* exactly like two separate 3812 synthesizers (for left and right
* channels) until switched into opl3 mode. Then, the timer-control
* register for the right half is replaced by a channel connection register
* (among other changes).
*
* We can detect 3812 synthesizers by starting a timer and looking for
* timer overflow. So if we find 3812s at both left and right addresses,
* we then switch to opl3 mode and look again for the right-half. If we
* still find it, then the switch failed and we have an old synthesizer
* if the right half disappeared, we have a new opl3 synthesizer.
*
* NB we use either monaural base-level synthesis, or stereo opl3
* synthesis. If we discover two 3812s (as on early SB Pro and
* PAS), we ignore one of them.
*/
/*
* nice theory - but wrong. The timer on the right half of the
* opl3 chip reports its status in the left-half status register.
* There is no right-half status register on the opl3 chip.
*/
/* ensure base mode */
SoundMidiSendFM(m_PortBase, AD_NEW, 0x00);
KeStallExecutionProcessor(20);
/* look for right half of chip */
if (SoundSynthPresent(m_PortBase + 2, m_PortBase))
{
/* yes - is this two separate chips or a new opl3 chip ? */
/* switch to opl3 mode */
SoundMidiSendFM(m_PortBase, AD_NEW, 0x01);
KeStallExecutionProcessor(20);
if (!SoundSynthPresent(m_PortBase + 2, m_PortBase))
{
_DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportMidiFM: In SoundMidiIsOpl3 right half disappeared"));
/* right-half disappeared - so opl3 */
bIsOpl3 = TRUE;
}
}
if (!bIsOpl3)
{
/* reset to 3812 mode */
SoundMidiSendFM(m_PortBase, AD_NEW, 0x00);
KeStallExecutionProcessor(20);
}
_DbgPrintF(DEBUGLVL_VERBOSE, ("CMiniportMidiFM: In SoundMidiIsOpl3 returning bIsOpl3 = 0x%X", bIsOpl3));
return(bIsOpl3);
}
#pragma code_seg()
// ==============================================================================
// SoundSynthPresent
//
// Detect the presence or absence of a 3812 (opl2/adlib-compatible) synthesizer
// at the given i/o address by starting the timer and looking for an
// overflow. Can be used to detect left and right synthesizers separately.
//
// Returns: True if a synthesiser is present at that address and false if not.
//
// NOTE: This and has been taken as is from the nt driver code.
// ==============================================================================
BOOL
CMiniportMidiFM::
SoundSynthPresent
(
IN PUCHAR base,
IN PUCHAR inbase
)
{
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
UCHAR t1, t2;
// check if the chip is present
SoundMidiSendFM(base, 4, 0x60); // mask T1 & T2
SoundMidiSendFM(base, 4, 0x80); // reset IRQ
t1 = READ_PORT_UCHAR((PUCHAR)inbase); // read status register
SoundMidiSendFM(base, 2, 0xff); // set timer - 1 latch
SoundMidiSendFM(base, 4, 0x21); // unmask & start T1
// this timer should go off in 80 us. It sometimes
// takes more than 100us, but will always have expired within
// 200 us if it is ever going to.
KeStallExecutionProcessor(200);
t2 = READ_PORT_UCHAR((PUCHAR)inbase); // read status register
SoundMidiSendFM(base, 4, 0x60);
SoundMidiSendFM(base, 4, 0x80);
if (!((t1 & 0xE0) == 0) || !((t2 & 0xE0) == 0xC0))
{
_DbgPrintF(DEBUGLVL_VERBOSE, ("SoundSynthPresent: returning false"));
return(FALSE);
}
_DbgPrintF(DEBUGLVL_VERBOSE, ("SoundSynthPresent: returning true"));
return TRUE;
}
// ==============================================================================
// this array gives the offsets of the slots within an opl2
// chip. This is needed to set the attenuation for all slots to max,
// to ensure that the chip is silenced completely - switching off the
// voices alone will not do this.
// ==============================================================================
BYTE offsetSlot[] =
{
0, 1, 2, 3, 4, 5,
8, 9, 10, 11, 12, 13,
16, 17, 18, 19, 20, 21
};
#pragma code_seg()
// =========================================================================
// WriteMidiData
// Converts a MIDI atom into the corresponding FM transaction.
// =========================================================================
void
CMiniportMidiStreamFM::
WriteMidiData(DWORD dwData)
{
BYTE bMsgType,bChannel, bVelocity, bNote;
WORD wTemp;
KIRQL oldIrql;
bMsgType = (BYTE) dwData & (BYTE)0xf0;
bChannel = (BYTE) dwData & (BYTE)0x0f;
bNote = (BYTE) ((WORD) dwData >> 8) & (BYTE)0x7f;
bVelocity = (BYTE) (dwData >> 16) & (BYTE)0x7f;
#ifdef USE_KDPRINT
KdPrint(("'StreamFM::WriteMidiData: (%x %x %x) \n",bMsgType+bChannel,bNote,bVelocity));
#else // USE_KDPRINT
_DbgPrintF(DEBUGLVL_VERBOSE,("StreamFM::WriteMidiData: (%x %x %x) \n",bMsgType+bChannel,bNote,bVelocity));
#endif // USE_KDPRINT
KeAcquireSpinLock(&m_Miniport->m_SpinLock,&oldIrql);
switch (bMsgType)
{
case 0x90: /* turn key on, or key off if volume == 0 */
if (bVelocity)
{
if (bChannel == DRUMCHANNEL)
{
Opl3_NoteOn((BYTE)(bNote + 128),bNote,bChannel,bVelocity,(short)m_iBend[bChannel]);
}
else
{
Opl3_NoteOn((BYTE)m_bPatch[bChannel],bNote,bChannel,bVelocity,(short) m_iBend[bChannel]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -