?? mixerdrv.cpp
字號:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
// -----------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// -----------------------------------------------------------------------------
//
// @doc WDEV_EXT
//
// @module mixerdrv.cpp | Implements the WODM_XXX and WIDM_XXX messages that are
// passed to the wave audio driver via the <f WAV_IOControl> function.
// This module contains code that is common or very similar between
// input and output functions.
//
// @xref <t Wave Input Driver Messages> (WIDM_XXX) <nl>
// <t Wave Output Driver Messages> (WODM_XXX)
//
// -----------------------------------------------------------------------------
#include <wavemain.h>
#include <mmreg.h>
#define LOGICAL_VOLUME_MAX 0xFFFF
#define LOGICAL_VOLUME_MIN 0
#define LOGICAL_VOLUME_STEPS 16
// only support Mic, LineIn as inputs
#define INPUT_SELECT_COUNT 2
#define DEVICE_NAME L"Audio Mixer"
#define DRIVER_VERSION 0x100
#define NELEMS(x) (sizeof(x)/sizeof((x)[0]))
// mixer handle data types
typedef DWORD (* PFNDRIVERCALL)(DWORD hmx, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
typedef struct _tagMCB MIXER_CALLBACK, * PMIXER_CALLBACK;
struct _tagMCB
{
DWORD hmx;
PFNDRIVERCALL pfnCallback;
PMIXER_CALLBACK pNext;
};
// mixer line ID are 16-bit values formed by concatenating the source and destination line indices
//
#define MXLINEID(dst,src) ((USHORT) ((USHORT)(dst) | (((USHORT) (src)) << 8)))
#define GET_MXLINE_SRC(lineid) ((lineid) >> 8)
#define GET_MXLINE_DST(lineid) ((lineid) & 0xff)
#define GET_MXCONTROL_ID(ctl) ((ctl)-g_controls)
#ifdef DEBUG
// DEBUG-only support for displaying control type codes in readable form
typedef struct
{
DWORD val;
PWSTR str;
} MMSYSCODE;
#define MXCTYPE(typ) {typ, TEXT(#typ)}
MMSYSCODE ctype_table[] =
{
MXCTYPE(MIXERCONTROL_CONTROLTYPE_CUSTOM),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_BASS),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_EQUALIZER),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_FADER),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_TREBLE),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_VOLUME),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_MIXER),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_MUX),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_SINGLESELECT),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_PEAKMETER),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_DECIBELS),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_PERCENT),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_SIGNED),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_UNSIGNED),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_PAN),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_SLIDER),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_BOOLEAN),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_BUTTON),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_LOUDNESS),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_MONO),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_MUTE),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_ONOFF),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_STEREOENH),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_MICROTIME),
MXCTYPE(MIXERCONTROL_CONTROLTYPE_MILLITIME),
};
PWSTR
COMPTYPE(DWORD dwValue)
{ int i;
for (i = 0; i < NELEMS(ctype_table); i++) {
if (ctype_table[i].val == dwValue) {
return ctype_table[i].str;
}
}
return TEXT("<unknown>");
}
#else
#define COMPTYPE(n) TEXT("<>")
#endif
// Destinations:
// LINE_OUT - line out jack
// PCM_IN - input to ADC
// Sources
// MIC - MIC jack
enum {
LINE_OUT = 0x80,
PCM_IN,
MIC,
NOLINE = 0xff // doesn't HAVE to be FF, but makes it easier to see
};
const USHORT
g_dst_lines[] =
{
MXLINEID(LINE_OUT,NOLINE),
MXLINEID(PCM_IN,NOLINE)
};
const USHORT
g_PCM_IN_sources[] =
{
MXLINEID(PCM_IN,MIC)
};
// MXLINEDESC corresponds to MIXERLINE, but is designed to conserve space
typedef struct tagMXLINEDESC const * PMXLINEDESC, MXLINEDESC;
typedef struct tagMXCONTROLDESC const * PMXCONTROLDESC, MXCONTROLDESC;
struct tagMXLINEDESC {
DWORD dwComponentType;
PCWSTR szShortName;
PCWSTR szName;
DWORD ucFlags;
USHORT const * pSources;
USHORT usLineID;
UINT8 ucChannels;
UINT8 ucConnections;
UINT8 ucControls;
DWORD dwTargetType;
UINT8 ucDstIndex;
UINT8 ucSrcIndex;
} ;
// MXCONTROLDESC is driver shorthand for Volume and Mute MIXERCONTROL
struct tagMXCONTROLDESC
{
PWSTR szName;
DWORD dwType;
USHORT usLineID; // line that owns this control
};
// mixerline ID
#define _MXLE(id,dst,src,flags,comptype,nch,ncnx,nctrl,sname,lname,target,sarray)\
{comptype,\
TEXT(sname),TEXT(lname),\
flags, \
sarray,\
id,\
nch,ncnx,nctrl,\
target,\
dst,src}
// declare a destination line
#define MXLED(id,dst,flags,comptype,nch,nctrl,sname,lname,target,srcarray,nsrcs)\
_MXLE(MXLINEID(id,NOLINE),dst,NOLINE,flags,comptype,nch,nsrcs,nctrl,sname,lname,target,srcarray)\
// declare a source line
#define MXSLE(id,dst,src,flags,comptype,nch,nctrl,sname,lname,target)\
_MXLE(id,dst,src,flags,comptype,nch,0,nctrl,sname,lname,target,NULL)\
MXLINEDESC
g_mixerline[] =
{
// dst line 0 - speaker out
MXLED(LINE_OUT, 0,
MIXERLINE_LINEF_ACTIVE, // no flags
MIXERLINE_COMPONENTTYPE_DST_SPEAKERS,
1, // mono
2, // controls: volume + mute
"Volume Control","Volume Control",
MIXERLINE_TARGETTYPE_WAVEOUT,
NULL, 0
),
// dst line 1 - microphone input
MXLED(PCM_IN, 1,
MIXERLINE_LINEF_ACTIVE, // flags
MIXERLINE_COMPONENTTYPE_DST_WAVEIN,
1, // mono
1, // 1 control (MUX) - the record gain is virtualized across all mux inputs
"Recording Contr","Recording Control",
MIXERLINE_TARGETTYPE_WAVEIN,
g_PCM_IN_sources, NELEMS(g_PCM_IN_sources)
),
// ----------------------
// PCM_IN Sources Lines
// ----------------------
// src line - Microphone
MXSLE(MXLINEID(PCM_IN, MIC), 1, 1,
MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE, // flags
MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE,
1, // mono
2, // controls: volume, mute
"Microphone","Microphone",
MIXERLINE_TARGETTYPE_WAVEIN
),
};
const int nlines = NELEMS(g_mixerline);
#undef _MXLE
#undef MXLED
#undef MXSLE
#define MXCE(dst,src,nme,type) {TEXT(nme),type,MXLINEID(dst,src)}
MXCONTROLDESC
g_controls[] =
{
MXCE(LINE_OUT, NOLINE, "Master Volume", MIXERCONTROL_CONTROLTYPE_VOLUME),
MXCE(LINE_OUT, NOLINE, "Master Mute", MIXERCONTROL_CONTROLTYPE_MUTE),
MXCE(PCM_IN, MIC, "Mic Volume", MIXERCONTROL_CONTROLTYPE_VOLUME),
MXCE(PCM_IN, MIC, "Mic Mute", MIXERCONTROL_CONTROLTYPE_MUTE),
};
const UINT ncontrols = NELEMS(g_controls);
#undef MXCE
PMXLINEDESC
LookupMxLine(USHORT usLineID)
{
// scan for mixer line
int i;
for (i = 0; i < nlines; i++) {
if (g_mixerline[i].usLineID == usLineID) {
return &g_mixerline[i];
}
}
return NULL;
}
int
LookupMxControl (USHORT usLineID, DWORD dwControlType)
{
UINT i;
for (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 = LOGICAL_VOLUME_STEPS;
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;
default:
DEBUGMSG(ZONE_ERROR, (TEXT("Unexpected control type %08x\r\n"), pSrc->dwType));
ASSERT(0);
}
}
static PMIXER_CALLBACK g_pHead; // list of open mixer instances
void
PerformMixerCallbacks(DWORD dwMessage, DWORD dwId)
{
PMIXER_CALLBACK pCurr;
for (pCurr = g_pHead; pCurr != NULL; pCurr = pCurr->pNext) {
if (pCurr->pfnCallback != NULL) {
DEBUGMSG(ZONE_VERBOSE, (TEXT("MixerCallback(%d)\r\n"), dwId));
pCurr->pfnCallback(pCurr->hmx, dwMessage, 0, dwId, 0);
}
}
}
DWORD
wdev_MXDM_GETDEVCAPS (PMIXERCAPS pCaps, DWORD dwSize)
{
pCaps->wMid = MM_MICROSOFT;
pCaps->wPid = MM_MSFT_WSS_MIXER;
wcscpy(pCaps->szPname, DEVICE_NAME);
pCaps->vDriverVersion = DRIVER_VERSION;
pCaps->cDestinations = NELEMS(g_dst_lines);
pCaps->fdwSupport = 0;
return MMSYSERR_NOERROR;
}
DWORD
wdev_MXDM_OPEN (PDWORD phMixer, PMIXEROPENDESC pMOD, DWORD dwFlags)
{
PMIXER_CALLBACK pNew;
pNew = (PMIXER_CALLBACK) LocalAlloc(LMEM_FIXED, sizeof(MIXER_CALLBACK));
if (pNew == NULL) {
ERRMSG("wdev_MXDM_OPEN: out of memory");
return MMSYSERR_NOMEM;
}
pNew->hmx = (DWORD) pMOD->hmx;
if (dwFlags & CALLBACK_FUNCTION) {
pNew->pfnCallback = (PFNDRIVERCALL) pMOD->dwCallback;
}
else {
pNew->pfnCallback = NULL;
}
pNew->pNext = g_pHead;
g_pHead = pNew;
*phMixer = (DWORD) pNew;
return MMSYSERR_NOERROR;
}
DWORD
wdev_MXDM_CLOSE (DWORD dwHandle)
{
PMIXER_CALLBACK pCurr, pPrev;
pPrev = NULL;
for (pCurr = g_pHead; pCurr != NULL; pCurr = pCurr->pNext) {
if (pCurr == (PMIXER_CALLBACK) dwHandle) {
if (pPrev == NULL) {
// we're deleting the first item
g_pHead = pCurr->pNext;
}
else {
pPrev->pNext = pCurr->pNext;
}
LocalFree(pCurr);
break;
}
pPrev = pCurr;
}
return MMSYSERR_NOERROR;
}
DWORD
wdev_MXDM_GETLINEINFO(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 = NOLINE;
switch (dwFlags & MIXER_GETLINEINFOF_QUERYMASK) {
case MIXER_GETLINEINFOF_DESTINATION:
DEBUGMSG(ZONE_VERBOSE, (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_VERBOSE, (TEXT("GetMixerLineInfo LINEID %x\r\n"), pQuery->dwLineID));
usLineID = (USHORT) pQuery->dwLineID;
break;
case MIXER_GETLINEINFOF_SOURCE:
DEBUGMSG(ZONE_VERBOSE, (TEXT("GetMixerLineInfo SOURCE %x %x\r\n"), pQuery->dwDestination, pQuery->dwSource));
{
PMXLINEDESC pLine;
// 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;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -