📄 ac97mixer.cpp
字号:
"Aux Volume","Aux Volume",
MIXERLINE_TARGETTYPE_UNDEFINED
),
// src line 4 - Stereo Mix
MXSLE(MXLINEID(PCM_IN, STEREO_MIX), 1, 4,
MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE, // flags
MIXERLINE_COMPONENTTYPE_SRC_ANALOG,
2,1,
"Stereo Mix", "Stereo Mix",
MIXERLINE_TARGETTYPE_UNDEFINED
),
};
const int nlines = NELEMS(g_mixerline);
DWORD
RegToVol(DWORD dwSetting, DWORD dwMax)
{
return (dwMax - (dwSetting & dwMax)) * (LOGICAL_VOLUME_MAX-1) / dwMax;
}
USHORT
VolToReg(DWORD dwVolume, DWORD dwMax)
{
return (USHORT) (dwMax - dwVolume * dwMax / (LOGICAL_VOLUME_MAX-1));
}
PMXLINEDESC
LookupMxLine(USHORT usLineID)
{
// scan for mixer line
for (int i = 0; i < nlines; i++) {
if (g_mixerline[i].usLineID == usLineID) {
return &g_mixerline[i];
}
}
return NULL;
}
int
LookupMxControl (USHORT usLineID, DWORD dwControlType)
{
for (int i = 0; i < ncontrols; i++) {
PMXCONTROLDESC pSrcControl = &g_controls[i];
if ( pSrcControl->usLineID == usLineID
&& pSrcControl->dwType == dwControlType) {
break;
}
}
return i;
}
void
CopyMixerControl(PMIXERCONTROL pDst, PMXCONTROLDESC pSrc, DWORD dwIndex)
{
// all of our lines have a volume and a mute.
// in addition, the PCM_IN has a MUX control
// fill in the volume:
pDst->cbStruct = sizeof(MIXERCONTROL);
pDst->dwControlID = dwIndex;
wcscpy(pDst->szName, pSrc->szName);
wcscpy(pDst->szShortName, pSrc->szName);
pDst->dwControlType = pSrc->dwType;
pDst->cMultipleItems = 0;
switch(pSrc->dwType) {
case MIXERCONTROL_CONTROLTYPE_VOLUME:
pDst->fdwControl = 0;
pDst->Metrics.cSteps = pSrc->usMask;
pDst->Bounds.lMaximum = LOGICAL_VOLUME_MAX;
pDst->Bounds.lMinimum = LOGICAL_VOLUME_MIN;
break;
case MIXERCONTROL_CONTROLTYPE_ONOFF:
case MIXERCONTROL_CONTROLTYPE_MUTE:
pDst->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
pDst->Metrics.cSteps = 0;
pDst->Bounds.lMaximum = 1;
pDst->Bounds.lMinimum = 0;
break;
case MIXERCONTROL_CONTROLTYPE_MUX:
// this better be Record Select...
pDst->cMultipleItems = 5;
pDst->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM | MIXERCONTROL_CONTROLF_MULTIPLE;
pDst->Metrics.cSteps = 5;
pDst->Bounds.lMaximum = 5;
pDst->Bounds.lMinimum = 0;
break;
default:
DEBUGMSG(ZONE_ERROR, (TEXT("Unexpected control type %08x\r\n"), pSrc->dwType));
ASSERT(0);
}
}
//#########################################################################
// Mixer Driver Entries
//#########################################################################
MMRESULT AC97Mixer::Initialize (AC97Codec *pAC97Codec)
{
int i;
USHORT mux_setting;
m_pAC97Codec = pAC97Codec;
// The mixer lines and controls are represented with two separate structures:
// a 'description' structure, which is constant and global,
// and a 'state' structure, which is modifiable and device-specific
// At initialization time, we allocate the line and control state objects
// and associate them with the corresponding description objects.
// There is a one-to-one correspondence between entries in the global
// line and control description tables (g_mixerline and g_controls) and
// the instance-specific state arrays.
m_pMxLineState = new MXLINESTATE [nlines];
if (m_pMxLineState == NULL)
{
return MMSYSERR_ERROR;
}
m_pMxControlState = new MXCONTROLSTATE [ncontrols];
if (m_pMxControlState == NULL)
{
return MMSYSERR_ERROR;
}
// this function assumes that the audio device has been initialized and that it's
// safe to go read the startup settings from the device
for (i = 0; i < nlines; i++) {
m_pMxLineState[i].dwState = 0;
}
for (i = 0; i < ncontrols; i++) {
// figure out which kind of control
ULONG regval;
m_pAC97Codec->PeekAC97(g_controls[i].ucCodecRegister, ®val);
if (g_controls[i].ucCodecRegister == AC97_RECORD_GAIN) {
m_pMxControlState[i].fVirtualized = TRUE;
}
else {
m_pMxControlState[i].fVirtualized = FALSE;
}
switch (g_controls[i].dwType) {
case MIXERCONTROL_CONTROLTYPE_VOLUME:
{
DWORD l = RegToVol(regval & g_controls[i].usMask, g_controls[i].usMask);
DWORD r = RegToVol((regval >> 8) & g_controls[i].usMask, g_controls[i].usMask);
m_pMxControlState[i].uSetting.dw = l | (r << 16);
}
break;
case MIXERCONTROL_CONTROLTYPE_ONOFF:
case MIXERCONTROL_CONTROLTYPE_MUTE:
m_pMxControlState[i].uSetting.b = !!(regval & g_controls[i].usMask);
break;
case MIXERCONTROL_CONTROLTYPE_MUX:
mux_setting = (USHORT)(regval & 0xff);
m_pMxControlState[i].uSetting.us = mux_setting;
break;
default:
// should never get here!
DEBUGCHK(0);
break;
}
}
// special case handling for the record-select muxed inputs
// figure out which line is selected and un-virtualize it's control
int index = LookupMxControl(MuxIndexToLine[mux_setting], MIXERCONTROL_CONTROLTYPE_VOLUME);
ASSERT(index < ncontrols);
m_pMxControlState[index].fVirtualized = FALSE;
return MMSYSERR_NOERROR;
}
typedef DWORD (* PFNDRIVERCALL)
(
DWORD hmx,
UINT uMsg,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2
);
struct _tagMCB
{
DWORD hmx;
PFNDRIVERCALL pfnCallback;
PMIXER_CALLBACK pNext;
};
void
AC97Mixer::PerformMixerCallbacks(DWORD dwMessage, DWORD dwId)
{
PMIXER_CALLBACK pCurr;
for (pCurr = m_pHead; pCurr != NULL; pCurr = pCurr->pNext) {
if (pCurr->pfnCallback != NULL) {
DEBUGMSG(ZONE_IOCTL, (TEXT("MixerCallback(%d)\r\n"), dwId));
pCurr->pfnCallback(pCurr->hmx, dwMessage, 0, dwId, 0);
}
}
}
MMRESULT
AC97Mixer::OpenMixerHandle(PDWORD phMixer, PMIXEROPENDESC pMOD, DWORD dwFlags)
{
PMIXER_CALLBACK pNew = new MIXER_CALLBACK;
if (pNew == NULL) {
DEBUGMSG(ZONE_ERROR, (TEXT("OpenMixerHandle: out of memory\r\n")));
return MMSYSERR_NOMEM;
}
pNew->hmx = (DWORD) pMOD->hmx;
if (dwFlags & CALLBACK_FUNCTION) {
pNew->pfnCallback = (PFNDRIVERCALL) pMOD->dwCallback;
}
else {
pNew->pfnCallback = NULL;
}
pNew->pNext = m_pHead;
m_pHead = pNew;
*phMixer = (DWORD) pNew;
return MMSYSERR_NOERROR;
}
MMRESULT
AC97Mixer::CloseMixerHandle( MIXHANDLE mxh)
{
PMIXER_CALLBACK pCurr, pPrev;
pPrev = NULL;
for (pCurr = m_pHead; pCurr != NULL; pCurr = pCurr->pNext) {
if (pCurr == (PMIXER_CALLBACK) mxh) {
if (pPrev == NULL) {
// we're deleting the first item
m_pHead = pCurr->pNext;
}
else {
pPrev->pNext = pCurr->pNext;
}
delete pCurr;
break;
}
pPrev = pCurr;
}
return MMSYSERR_NOERROR;
}
/*
- mixerGetLineInfo
MIXER_GETLINEINFOF_COMPONENTTYPE - first line whose type matches dwComponentType member of the MIXERLINE structure.
MIXER_GETLINEINFOF_DESTINATION - line specified by dwDestination
MIXER_GETLINEINFOF_LINEID - line specified by the dwLineID member
MIXER_GETLINEINFOF_SOURCE - line specified by the dwDestination and dwSource
MIXER_GETLINEINFOF_TARGETTYPE - line that is for the dwType member of the Target structure
*/
MMRESULT
AC97Mixer::GetMixerLineInfo( PMIXERLINE pQuery, DWORD dwFlags)
{ int i;
// pQuery is validated by API - points to accessible, properly sized MIXERLINE structure
// result - assume failure
PMXLINEDESC pFound = NULL;
MMRESULT mmRet = MIXERR_INVALLINE;
USHORT usLineID;
switch (dwFlags & MIXER_GETLINEINFOF_QUERYMASK) {
case MIXER_GETLINEINFOF_DESTINATION:
DEBUGMSG(ZONE_MXDM, (TEXT("GetMixerLineInfo DESTINATION %x\r\n"), pQuery->dwDestination));
{
if (pQuery->dwDestination >= NELEMS(g_dst_lines)) {
DEBUGMSG(ZONE_ERROR, (TEXT("GetMixerLineInfo: invalid destination line %d\r\n"), pQuery->dwDestination));
return MIXERR_INVALLINE;
}
usLineID = g_dst_lines[pQuery->dwDestination];
}
break;
case MIXER_GETLINEINFOF_LINEID:
DEBUGMSG(ZONE_MXDM, (TEXT("GetMixerLineInfo LINEID %x\r\n"), pQuery->dwLineID));
usLineID = (USHORT) pQuery->dwLineID;
break;
case MIXER_GETLINEINFOF_SOURCE:
DEBUGMSG(ZONE_MXDM, (TEXT("GetMixerLineInfo SOURCE %x %x\r\n"), pQuery->dwDestination, pQuery->dwSource));
{
// look up the destination line, then index into it's source table
// to find the indexed source.
if (pQuery->dwDestination >= NELEMS(g_dst_lines)) {
DEBUGMSG(ZONE_ERROR, (TEXT("GetMixerLineInfo: invalid destination line %d\r\n"), pQuery->dwDestination));
return MIXERR_INVALLINE;
}
PMXLINEDESC pLine = LookupMxLine(g_dst_lines[pQuery->dwDestination]);
if (pLine == NULL) {
DEBUGMSG(ZONE_ERROR, (TEXT("GetMixerLineInfo: inconsistent internal mixer line table\r\n")));
return MMSYSERR_ERROR;
}
if (pQuery->dwSource >= pLine->ucConnections) {
DEBUGMSG(ZONE_ERROR, (TEXT("GetMixerLineInfo: invalid source line %d\r\n"), pQuery->dwSource));
return MIXERR_INVALLINE;
}
usLineID = pLine->pSources[pQuery->dwSource];
}
break;
case MIXER_GETLINEINFOF_COMPONENTTYPE:
DEBUGMSG(ZONE_MXDM, (TEXT("GetMixerLineInfo COMPONENT\r\n")));
break;
case MIXER_GETLINEINFOF_TARGETTYPE:
DEBUGMSG(ZONE_MXDM, (TEXT("GetMixerLineInfo TARGET\r\n")));
// valid query, but we're not going to form usLineID
break;
default:
DEBUGMSG(ZONE_ERROR, (TEXT("GetMixerLineInfo: invalid query %08x\r\n"), dwFlags & MIXER_GETLINEINFOF_QUERYMASK));
return MMSYSERR_INVALPARAM;
}
switch (dwFlags & MIXER_GETLINEINFOF_QUERYMASK) {
case MIXER_GETLINEINFOF_COMPONENTTYPE:
// scan for line of proper type
for (i = 0; i < nlines; i++) {
if (g_mixerline[i].dwComponentType == pQuery->dwComponentType) {
pFound = &g_mixerline[i];
break;
}
}
#ifdef DEBUG
if (pFound == NULL) {
DEBUGMSG(ZONE_ERROR, (TEXT("GetMixerLineInfo: no line of component type %08x\r\n"), pQuery->dwComponentType));
}
#endif
break;
case MIXER_GETLINEINFOF_TARGETTYPE:
// scan for target type
for (i = 0; i < nlines; i++) {
if (g_mixerline[i].dwTargetType == pQuery->Target.dwType) {
pFound = &g_mixerline[i];
break;
}
}
#ifdef DEBUG
if (pFound == NULL) {
DEBUGMSG(ZONE_ERROR, (TEXT("GetMixerLineInfo: no line of target type %08x\r\n"), pQuery->Target.dwType));
}
#endif
break;
case MIXER_GETLINEINFOF_DESTINATION:
case MIXER_GETLINEINFOF_LINEID:
case MIXER_GETLINEINFOF_SOURCE:
pFound = LookupMxLine(usLineID);
if (pFound == NULL) {
DEBUGMSG(ZONE_ERROR, (TEXT("GetMixerLineInfo: invalid line ID %08x\r\n"), usLineID));
return MMSYSERR_ERROR;
}
break;
default:
// should never happen - we filter for this in the first switch()
break;
}
if (pFound != NULL) {
pQuery->cChannels = pFound->ucChannels;
pQuery->cConnections = pFound->ucConnections;
pQuery->cControls = pFound->ucControls;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -