mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1171 lines
30 KiB
1171 lines
30 KiB
/*++
|
|
"@(#) NEC mixer.c 1.1 95/03/22 21:23:31"
|
|
|
|
Copyright (c) 1995 NEC Corporation
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mixer.c
|
|
|
|
Abstract:
|
|
|
|
Mixer code for the Microsoft sound system card.
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "sound.h"
|
|
#include <soundcfg.h>
|
|
|
|
|
|
#define absval(x) ((x) > 0 ? (x) : -(x))
|
|
|
|
/*
|
|
** Local functions and data
|
|
*/
|
|
|
|
VOID
|
|
SoundWaveinLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
);
|
|
VOID
|
|
SoundWaveoutLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
);
|
|
VOID
|
|
SoundSynthLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
);
|
|
NTSTATUS
|
|
HwGetLineFlags(
|
|
PMIXER_INFO MixerInfo,
|
|
ULONG LineId,
|
|
ULONG Length,
|
|
PVOID pData
|
|
);
|
|
|
|
NTSTATUS
|
|
HwGetControl(
|
|
PMIXER_INFO MixerInfo,
|
|
ULONG ControlId,
|
|
ULONG DataLength,
|
|
PVOID ControlData
|
|
);
|
|
|
|
NTSTATUS
|
|
HwSetControl(
|
|
PMIXER_INFO MixerInfo,
|
|
ULONG ControlId,
|
|
ULONG DataLength,
|
|
PVOID ControlData
|
|
);
|
|
|
|
NTSTATUS
|
|
HwGetCombinedControl(
|
|
PMIXER_INFO MixerInfo,
|
|
ULONG ControlId,
|
|
ULONG DataLength,
|
|
PVOID ControlData
|
|
);
|
|
|
|
VOID
|
|
SoundNotifySynth(
|
|
PGLOBAL_DEVICE_INFO pGDI
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,SoundMixerInit)
|
|
|
|
#pragma alloc_text(PAGE,SoundSaveMixerSettings)
|
|
#pragma alloc_text(PAGE,SoundWaveinLineChanged)
|
|
#pragma alloc_text(PAGE,SoundWaveoutLineChanged)
|
|
#pragma alloc_text(PAGE,SoundSynthLineChanged)
|
|
#pragma alloc_text(PAGE,SoundMixerDumpConfiguration)
|
|
#pragma alloc_text(PAGE,SoundNotifySynth)
|
|
|
|
#pragma alloc_text(PAGE,HwGetLineFlags)
|
|
#pragma alloc_text(PAGE,HwGetControl)
|
|
#pragma alloc_text(PAGE,HwGetCombinedControl)
|
|
#pragma alloc_text(PAGE,HwSetControl)
|
|
#endif
|
|
|
|
|
|
VOID
|
|
SoundSaveMixerSettings(
|
|
PGLOBAL_DEVICE_INFO pGDI
|
|
)
|
|
{
|
|
PLOCAL_MIXER_DATA LocalMixerData;
|
|
MIXER_CONTROL_DATA_ITEM SavedControlData[MAXSETTABLECONTROLS];
|
|
int i;
|
|
int SetIndex;
|
|
|
|
dprintf4(("SoundSaveMixerSettings()"));
|
|
|
|
LocalMixerData = &pGDI->LocalMixerData;
|
|
|
|
/*
|
|
** Condense the data for storing in the registry
|
|
*/
|
|
|
|
for (i = 0, SetIndex = 0; i < MAXCONTROLS; i++) {
|
|
if (pGDI->LocalMixerData.ControlInfo[i].SetIndex != MIXER_SET_INDEX_INVALID) {
|
|
|
|
ASSERT(SetIndex == pGDI->LocalMixerData.ControlInfo[i].SetIndex);
|
|
|
|
SavedControlData[SetIndex] = pGDI->LocalMixerData.ControlInfo[i].Data;
|
|
SetIndex++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Write the saved data
|
|
*/
|
|
|
|
RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
|
|
pGDI->RegistryPathName,
|
|
SOUND_MIXER_SETTINGS_NAME,
|
|
REG_BINARY,
|
|
(PVOID)SavedControlData,
|
|
sizeof(SavedControlData));
|
|
}
|
|
|
|
|
|
VOID SoundWaveinLineChanged( PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code )
|
|
{
|
|
UCHAR Line;
|
|
PLOCAL_MIXER_DATA LocalMixerData;
|
|
|
|
dprintf4(("SoundWaveinLineChanged()"));
|
|
|
|
|
|
LocalMixerData = &((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->LocalMixerData;
|
|
|
|
// Find which device is in use and inspect the relevant MUX
|
|
switch (Code)
|
|
{
|
|
case SOUND_LINE_NOTIFY_WAVE:
|
|
Line = LocalMixerData->ControlInfo[ControlWaveInMux].Data.v[0].u ==
|
|
MUXINPUT_MIC ? DestWaveInSourceMic :
|
|
DestWaveInSourceAux1;
|
|
break;
|
|
|
|
case SOUND_LINE_NOTIFY_VOICE:
|
|
Line = LocalMixerData->ControlInfo[ControlVoiceInMux].Data.v[0].u ==
|
|
MUXINPUT_MIC ? DestVoiceInSourceMic :
|
|
DestVoiceInSourceAux1;
|
|
break;
|
|
}
|
|
|
|
SoundMixerChangedItem(
|
|
&((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->MixerInfo,
|
|
&LocalMixerData->LineNotification[Line]);
|
|
}
|
|
|
|
|
|
|
|
VOID SoundWaveoutLineChanged( PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code )
|
|
{
|
|
PLOCAL_MIXER_DATA LocalMixerData;
|
|
|
|
dprintf4(("SoundWaveoutLineChanged()"));
|
|
|
|
|
|
LocalMixerData = &((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->LocalMixerData;
|
|
|
|
SoundMixerChangedItem(
|
|
&((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->MixerInfo,
|
|
&LocalMixerData->LineNotification[DestLineoutSourceWaveout]);
|
|
}
|
|
|
|
|
|
/*
|
|
** NOTE - the initializations etc here depend on the restricted types
|
|
** of control supported by this device - if other types are used it must
|
|
** be changed
|
|
*/
|
|
|
|
NTSTATUS
|
|
SoundMixerInit(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
PMIXER_CONTROL_DATA_ITEM SavedControlData,
|
|
BOOLEAN MixerSettingsFound
|
|
)
|
|
{
|
|
int i, SetIndex;
|
|
PLOCAL_MIXER_DATA LocalMixerData;
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
PMIXER_INFO MixerInfo;
|
|
|
|
dprintf4(("SoundMixerInit()"));
|
|
|
|
pGDI = pLDI->pGlobalInfo;
|
|
MixerInfo = &pGDI->MixerInfo;
|
|
|
|
/*
|
|
** Init the generic mixer stuff first so we can use it
|
|
*/
|
|
|
|
SoundInitMixerInfo(&pGDI->MixerInfo,
|
|
HwGetLineFlags,
|
|
HwGetControl,
|
|
HwGetCombinedControl,
|
|
HwSetControl);
|
|
|
|
/*
|
|
** Set this device up with its mixer data
|
|
*/
|
|
pLDI->DeviceType = MIXER_DEVICE;
|
|
pLDI->DeviceSpecificData = (PVOID)MixerInfo;
|
|
|
|
/*
|
|
** Make sure everyone can find the mixer device
|
|
*/
|
|
|
|
{
|
|
PDEVICE_OBJECT pDO;
|
|
|
|
for (pDO = pGDI->DeviceObject[WaveInDevice]->DriverObject->DeviceObject;
|
|
pDO != NULL;
|
|
pDO = pDO->NextDevice) {
|
|
|
|
((PLOCAL_DEVICE_INFO)pDO->DeviceExtension)->MixerDevice = pLDI;
|
|
}
|
|
}
|
|
|
|
LocalMixerData = &pGDI->LocalMixerData;
|
|
|
|
/*
|
|
** Create control info
|
|
*/
|
|
|
|
for (i = 0, SetIndex = 0; i < MAXCONTROLS ; i++) {
|
|
|
|
/*
|
|
** Read limits
|
|
*/
|
|
|
|
if ((MixerControlInit[i].dwControlType & MIXERCONTROL_CT_UNITS_MASK) ==
|
|
MIXERCONTROL_CT_UNITS_SIGNED) {
|
|
|
|
pGDI->LocalMixerData.ControlInfo[i].Signed = TRUE;
|
|
pGDI->LocalMixerData.ControlInfo[i].Range.Min.s =
|
|
(SHORT)MixerControlInit[i].Bounds.lMinimum;
|
|
pGDI->LocalMixerData.ControlInfo[i].Range.Max.s =
|
|
(SHORT)MixerControlInit[i].Bounds.lMaximum;
|
|
} else {
|
|
|
|
if ((MixerControlInit[i].dwControlType & MIXERCONTROL_CT_UNITS_MASK) ==
|
|
MIXERCONTROL_CT_UNITS_BOOLEAN) {
|
|
pGDI->LocalMixerData.ControlInfo[i].Boolean = TRUE;
|
|
}
|
|
pGDI->LocalMixerData.ControlInfo[i].Range.Min.u =
|
|
(USHORT)MixerControlInit[i].Bounds.dwMinimum;
|
|
pGDI->LocalMixerData.ControlInfo[i].Range.Max.u =
|
|
(USHORT)MixerControlInit[i].Bounds.dwMaximum;
|
|
}
|
|
|
|
/*
|
|
** Remember if it's a mux
|
|
*/
|
|
|
|
if (MixerControlInit[i].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX) {
|
|
pGDI->LocalMixerData.ControlInfo[i].Mux = TRUE;
|
|
}
|
|
|
|
/*
|
|
** Only meters are not settable here
|
|
*/
|
|
|
|
if ((MixerControlInit[i].dwControlType & MIXERCONTROL_CT_CLASS_MASK) !=
|
|
MIXERCONTROL_CT_CLASS_METER)
|
|
{
|
|
pGDI->LocalMixerData.ControlInfo[i].SetIndex = SetIndex;
|
|
SoundInitDataItem(MixerInfo,
|
|
&pGDI->LocalMixerData.ControlNotification[SetIndex],
|
|
(USHORT)MM_MIXM_CONTROL_CHANGE,
|
|
(USHORT)i);
|
|
if (MixerSettingsFound) {
|
|
|
|
/*
|
|
** What if it's invalid?
|
|
*/
|
|
|
|
pGDI->LocalMixerData.ControlInfo[i].Data =
|
|
SavedControlData[SetIndex];
|
|
} else {
|
|
/*
|
|
** For us it's either Mute, a volume or a Mux.
|
|
**
|
|
** Muxes are assumed to be set to MUXINPUT_MIC as default
|
|
** Only one mute is set
|
|
**
|
|
** Volumes are set to a fixed value
|
|
**
|
|
** Nothing is muted
|
|
*/
|
|
|
|
switch (i) {
|
|
case ControlLineoutVolume:
|
|
case ControlLineoutAux1Volume:
|
|
case ControlLineoutWaveoutVolume:
|
|
case ControlLineoutMidioutVolume:
|
|
case ControlWaveInAux1Volume:
|
|
case ControlWaveInMicVolume:
|
|
case ControlVoiceInAux1Volume:
|
|
case ControlVoiceInMicVolume:
|
|
|
|
/*
|
|
** Half volume
|
|
*/
|
|
|
|
pGDI->LocalMixerData.ControlInfo[i].Data.v[0].u = 32767;
|
|
pGDI->LocalMixerData.ControlInfo[i].Data.v[1].u = 32767;
|
|
break;
|
|
|
|
case ControlWaveInMux:
|
|
case ControlVoiceInMux:
|
|
|
|
/*
|
|
** Microphone input
|
|
*/
|
|
|
|
pGDI->LocalMixerData.ControlInfo[i].Data.v[0].u = MUXINPUT_MIC;
|
|
break;
|
|
}
|
|
}
|
|
SetIndex++;
|
|
} else {
|
|
pGDI->LocalMixerData.ControlInfo[i].SetIndex = MIXER_SET_INDEX_INVALID;
|
|
}
|
|
}
|
|
|
|
ASSERTMSG("MAXSETTABLECONTROLS wrong!", SetIndex == MAXSETTABLECONTROLS);
|
|
|
|
/*
|
|
** Create line info
|
|
*/
|
|
|
|
for (i = 0; i < MAXLINES; i++) {
|
|
SoundInitDataItem(MixerInfo,
|
|
&pGDI->LocalMixerData.LineNotification[i],
|
|
(USHORT)MM_MIXM_LINE_CHANGE,
|
|
(USHORT)i);
|
|
}
|
|
|
|
/*
|
|
** Set up line notifications and volume control ids for non-mixer devices
|
|
*/
|
|
|
|
SoundSetLineNotify(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[WaveInDevice]->DeviceExtension,
|
|
SoundWaveinLineChanged);
|
|
SoundSetLineNotify(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[WaveOutDevice]->DeviceExtension,
|
|
SoundWaveoutLineChanged);
|
|
|
|
SoundSetVolumeControlId(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[WaveOutDevice]->DeviceExtension,
|
|
ControlLineoutWaveoutVolume);
|
|
|
|
SoundSetVolumeControlId(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[LineInDevice]->DeviceExtension,
|
|
ControlLineoutAux1Volume);
|
|
|
|
/*
|
|
** Set the volume levels - this only affects the output for this card's
|
|
** initial settings (ie wave in not active).
|
|
*/
|
|
|
|
MixSetMasterVolume(pGDI, 0);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
SoundMixerDumpConfiguration(
|
|
IN PLOCAL_DEVICE_INFO pLDI,
|
|
IN OUT PIRP pIrp,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PMIXER_INFO MixerInfo;
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
ULONG Length;
|
|
|
|
struct {
|
|
MIXER_DD_CONFIGURATION_DATA Header;
|
|
MIXER_DD_LINE_CONFIGURATION_DATA LineData[MAXLINES];
|
|
MIXER_DD_CONTROL_CONFIGURATION_DATA ControlData[MAXCONTROLS];
|
|
MIXER_DD_CONTROL_LISTTEXT TextData[NUMBEROFTEXTITEMS];
|
|
} *OurConfigData;
|
|
|
|
pGDI = pLDI->pGlobalInfo;
|
|
MixerInfo = &pGDI->MixerInfo;
|
|
|
|
dprintf4(("SoundMixerDumpConfiguration()"));
|
|
|
|
|
|
/*
|
|
** Load and adapt the mixer configuration info
|
|
**
|
|
** Play safe and allocate the space since the kernel stacks are a limited
|
|
** size
|
|
*/
|
|
|
|
OurConfigData = ExAllocatePool(PagedPool, sizeof(*OurConfigData));
|
|
|
|
if (OurConfigData == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/*
|
|
** Copy the data and initialize the rest
|
|
*/
|
|
|
|
OurConfigData->Header.cbSize = sizeof(*OurConfigData);
|
|
ASSERT(sizeof(OurConfigData->LineData) == sizeof(MixerLineInit));
|
|
ASSERT(sizeof(OurConfigData->ControlData) == sizeof(MixerControlInit));
|
|
ASSERT(sizeof(OurConfigData->TextData) == sizeof(MixerTextInit));
|
|
RtlCopyMemory(OurConfigData->LineData, MixerLineInit, sizeof(MixerLineInit));
|
|
RtlCopyMemory(OurConfigData->ControlData, MixerControlInit,
|
|
sizeof(MixerControlInit));
|
|
RtlCopyMemory(OurConfigData->TextData, MixerTextInit, sizeof(MixerTextInit));
|
|
|
|
OurConfigData->Header.NumberOfLines = MAXLINES;
|
|
OurConfigData->Header.NumberOfControls = MAXCONTROLS;
|
|
|
|
/*
|
|
** Create the Device caps
|
|
*/
|
|
|
|
OurConfigData->Header.DeviceCaps.wMid = MID_NEC;
|
|
OurConfigData->Header.DeviceCaps.wPid = PID_MIXER;
|
|
OurConfigData->Header.DeviceCaps.vDriverVersion = DRIVER_VERSION;
|
|
OurConfigData->Header.DeviceCaps.PnameStringId = SR_STR_DRIVER_MIXER;
|
|
OurConfigData->Header.DeviceCaps.fdwSupport = 0;
|
|
|
|
/*
|
|
** Compute the number of destinations
|
|
*/
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAXLINES; i++) {
|
|
if (MixerLineInit[i].cConnections == 0) {
|
|
break;
|
|
}
|
|
}
|
|
OurConfigData->Header.DeviceCaps.cDestinations = i;
|
|
}
|
|
|
|
/*
|
|
** If this card is something other than a AD1848J, set new levels
|
|
*/
|
|
|
|
if (pGDI->Hw.CODECClass != CODEC_J_CLASS) {
|
|
OurConfigData->ControlData[ControlLineoutAux1Volume].Metrics.cSteps = 32;
|
|
OurConfigData->ControlData[ControlWaveInAux1Volume].Metrics.cSteps = 32;
|
|
OurConfigData->ControlData[ControlVoiceInAux1Volume].Metrics.cSteps = 32;
|
|
}
|
|
|
|
/*
|
|
** Set the text data offsets up
|
|
*/
|
|
|
|
{
|
|
PMIXER_DD_CONTROL_LISTTEXT pListText;
|
|
PMIXER_DD_CONTROL_CONFIGURATION_DATA pControlData;
|
|
int i;
|
|
|
|
for (pListText = &OurConfigData->TextData[NUMBEROFTEXTITEMS - 1],
|
|
pControlData = OurConfigData->ControlData,
|
|
i = 0;
|
|
i < NUMBEROFTEXTITEMS;
|
|
pListText--, i++)
|
|
{
|
|
pControlData[pListText->ControlId].TextDataOffset =
|
|
(PBYTE)pListText - (PBYTE)OurConfigData;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Note that having no synth means that we just set the synth line to
|
|
** disconnected when asked for the line information
|
|
*/
|
|
|
|
/*
|
|
** Copy data back to the application - don't copy anything if they
|
|
** ask for less than the basic information.
|
|
*/
|
|
|
|
Length =
|
|
min(sizeof(*OurConfigData),
|
|
IrpStack->Parameters.DeviceIoControl.OutputBufferLength);
|
|
|
|
if (Length < sizeof(*OurConfigData)) {
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
pIrp->IoStatus.Information = Length;
|
|
|
|
RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer,
|
|
OurConfigData,
|
|
pIrp->IoStatus.Information);
|
|
|
|
ExFreePool(OurConfigData);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
HwGetLineFlags(
|
|
PMIXER_INFO MixerInfo,
|
|
ULONG LineId,
|
|
ULONG Length,
|
|
PVOID pData
|
|
)
|
|
{
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
PULONG fdwLine;
|
|
|
|
dprintf4(("HwGetLineFlags()")) ;
|
|
|
|
fdwLine = pData;
|
|
|
|
if (Length != sizeof(ULONG)) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
pGDI = CONTAINING_RECORD(MixerInfo, GLOBAL_DEVICE_INFO, MixerInfo);
|
|
|
|
/*
|
|
** Get default
|
|
*/
|
|
|
|
*fdwLine = MixerLineInit[LineId].cConnections == 0 ?
|
|
MIXERLINE_LINEF_SOURCE : 0;
|
|
|
|
/*
|
|
** The factors that affect this are
|
|
** - the mux settings
|
|
** - whether we have a synth
|
|
** - whether there is an output device playing
|
|
*/
|
|
|
|
switch (LineId) {
|
|
|
|
/*
|
|
** Muxes, line out and aux in -> line out are always available
|
|
*/
|
|
|
|
case DestWaveIn:
|
|
case DestVoiceIn:
|
|
case DestLineout:
|
|
case DestLineoutSourceAux1:
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
break;
|
|
|
|
/*
|
|
** Wave out deemed 'active' when open
|
|
*/
|
|
|
|
case DestLineoutSourceWaveout:
|
|
if (pGDI->DeviceInUse == WaveOutDevice) {
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
}
|
|
break;
|
|
|
|
|
|
/*
|
|
** Midi out deemed 'active' when open
|
|
*/
|
|
|
|
case DestLineoutSourceMidiout:
|
|
|
|
*fdwLine |= MIXERLINE_LINEF_DISCONNECTED;
|
|
|
|
break;
|
|
|
|
/*
|
|
** wave in from aux1 active when selected and not low priority
|
|
*/
|
|
|
|
case DestWaveInSourceAux1:
|
|
if (pGDI->LocalMixerData.ControlInfo[ControlWaveInMux].Data.v[0].u !=
|
|
MUXINPUT_MIC &&
|
|
pGDI->DeviceInUse == WaveInDevice &&
|
|
(pGDI->WaveInfo.LowPriorityHandle == NULL ||
|
|
pGDI->WaveInfo.LowPrioritySaved)) {
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
** wave in from mic active when selected and not low priority
|
|
*/
|
|
|
|
case DestWaveInSourceMic:
|
|
if (pGDI->LocalMixerData.ControlInfo[ControlWaveInMux].Data.v[0].u ==
|
|
MUXINPUT_MIC &&
|
|
pGDI->DeviceInUse == WaveInDevice &&
|
|
(pGDI->WaveInfo.LowPriorityHandle == NULL ||
|
|
pGDI->WaveInfo.LowPrioritySaved)) {
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
** voice in from aux active when selected and low priority active
|
|
*/
|
|
|
|
case DestVoiceInSourceAux1:
|
|
if (pGDI->LocalMixerData.ControlInfo[ControlVoiceInMux].Data.v[0].u !=
|
|
MUXINPUT_MIC &&
|
|
pGDI->DeviceInUse == WaveInDevice &&
|
|
(pGDI->WaveInfo.LowPriorityHandle != NULL &&
|
|
!pGDI->WaveInfo.LowPrioritySaved)) {
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
** voice in from mic active when selected and low priority active
|
|
*/
|
|
|
|
case DestVoiceInSourceMic:
|
|
if (pGDI->LocalMixerData.ControlInfo[ControlVoiceInMux].Data.v[0].u ==
|
|
MUXINPUT_MIC &&
|
|
pGDI->DeviceInUse == WaveInDevice &&
|
|
(pGDI->WaveInfo.LowPriorityHandle != NULL &&
|
|
!pGDI->WaveInfo.LowPrioritySaved)) {
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return STATUS_INVALID_PARAMETER; // Invalid
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
HwGetCombinedControl(
|
|
PMIXER_INFO MixerInfo,
|
|
ULONG ControlId,
|
|
ULONG DataLength,
|
|
PVOID ControlData
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This is an INTERNAL ONLY routine so no validation is required.
|
|
|
|
|
|
--*/
|
|
{
|
|
PULONG Vol;
|
|
PLOCAL_MIXER_CONTROL_INFO ControlInfo;
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
|
|
dprintf4(("HwGetCombinedControl()"));
|
|
|
|
pGDI = CONTAINING_RECORD(MixerInfo, GLOBAL_DEVICE_INFO, MixerInfo);
|
|
|
|
ControlInfo = pGDI->LocalMixerData.ControlInfo;
|
|
|
|
Vol = ControlData;
|
|
|
|
/* This is ONLY allowed for midi output */
|
|
|
|
ASSERTMSG("Invalid control for HwGetCombinedControl",
|
|
ControlId == ControlLineoutMidioutVolume &&
|
|
DataLength == sizeof(ULONG) * 2);
|
|
|
|
/* Combine the volume for both left and right */
|
|
|
|
if (ControlInfo[ControlLineoutMute].Data.v[0].u != 0 ||
|
|
ControlInfo[ControlLineoutMidioutMute].Data.v[0].u != 0) {
|
|
Vol[0] = 0;
|
|
Vol[1] = 0;
|
|
} else {
|
|
Vol[0] = ((ULONG)ControlInfo[ControlLineoutVolume].Data.v[0].u *
|
|
(ULONG)ControlInfo[ControlLineoutMidioutVolume].Data.v[0].u)
|
|
>> 16;
|
|
Vol[1] = ((ULONG)ControlInfo[ControlLineoutVolume].Data.v[1].u *
|
|
(ULONG)ControlInfo[ControlLineoutMidioutVolume].Data.v[1].u)
|
|
>> 16;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
HwGetControl(
|
|
PMIXER_INFO MixerInfo,
|
|
ULONG ControlId,
|
|
ULONG DataLength,
|
|
PVOID ControlData
|
|
)
|
|
{
|
|
PLOCAL_MIXER_CONTROL_INFO ControlInfo;
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
LONG Values[2];
|
|
|
|
dprintf4(("HwGetControl()"));
|
|
|
|
/*
|
|
** Validate control ID
|
|
*/
|
|
|
|
if (ControlId > MAXCONTROLS) {
|
|
dprintf4(("return -1"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/*
|
|
** Establish pointers to our structures
|
|
*/
|
|
|
|
pGDI = CONTAINING_RECORD(MixerInfo, GLOBAL_DEVICE_INFO, MixerInfo);
|
|
ControlInfo = &pGDI->LocalMixerData.ControlInfo[ControlId];
|
|
|
|
/*
|
|
** Validate data length and values
|
|
*/
|
|
|
|
switch (DataLength) {
|
|
case sizeof(LONG):
|
|
break;
|
|
|
|
case sizeof(LONG) * 2:
|
|
break;
|
|
|
|
default:
|
|
dprintf4(("retunr 2"));
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
/*
|
|
** Pull out the data
|
|
*/
|
|
|
|
if (ControlInfo->SetIndex == MIXER_SET_INDEX_INVALID) {
|
|
/*
|
|
** Must be a peak meter - see if it's valid to query it
|
|
*/
|
|
|
|
PWAVE_INFO WaveInfo;
|
|
BOOLEAN ComputePeak;
|
|
|
|
ComputePeak = FALSE;
|
|
|
|
/*
|
|
** Set defaults
|
|
*/
|
|
|
|
Values[0] = 0;
|
|
Values[1] = 0;
|
|
|
|
WaveInfo = &pGDI->WaveInfo;
|
|
|
|
switch (ControlId) {
|
|
case ControlLineoutWaveoutPeak:
|
|
if (pGDI->DeviceInUse == WaveOutDevice) {
|
|
ComputePeak = TRUE;
|
|
}
|
|
break;
|
|
|
|
case ControlWaveInAux1Peak:
|
|
case ControlWaveInMicPeak:
|
|
if (pGDI->DeviceInUse != WaveInDevice) {
|
|
break;
|
|
}
|
|
|
|
if (WaveInfo->LowPriorityHandle != NULL &&
|
|
!WaveInfo->LowPrioritySaved) {
|
|
break;
|
|
}
|
|
|
|
if (pGDI->LocalMixerData.ControlInfo[ControlWaveInMux].Data.v[0].u ==
|
|
MUXINPUT_MIC) {
|
|
|
|
if (ControlId == ControlWaveInAux1Peak) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (ControlId == ControlWaveInMicPeak) {
|
|
break;
|
|
}
|
|
}
|
|
ComputePeak = TRUE;
|
|
break;
|
|
|
|
case ControlVoiceInAux1Peak:
|
|
case ControlVoiceInMicPeak:
|
|
|
|
if (pGDI->DeviceInUse != WaveInDevice) {
|
|
break;
|
|
}
|
|
if (WaveInfo->LowPriorityHandle == NULL ||
|
|
WaveInfo->LowPrioritySaved) {
|
|
break;
|
|
}
|
|
|
|
if (pGDI->LocalMixerData.ControlInfo[ControlVoiceInMux].Data.v[0].u ==
|
|
MUXINPUT_MIC) {
|
|
|
|
if (ControlId == ControlVoiceInAux1Peak) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (ControlId == ControlVoiceInMicPeak) {
|
|
break;
|
|
}
|
|
}
|
|
ComputePeak = TRUE;
|
|
break;
|
|
|
|
default:
|
|
ASSERTMSG("Invalid control id", FALSE);
|
|
break;
|
|
}
|
|
|
|
if (ComputePeak) {
|
|
SoundPeakMeter(WaveInfo, Values);
|
|
}
|
|
|
|
/*
|
|
** Note that we should round these values to the min/max
|
|
** expected in the control but in this case these values
|
|
** are always within range
|
|
*/
|
|
|
|
} else {
|
|
ASSERTMSG("Set index out of range",
|
|
ControlInfo->SetIndex < MAXSETTABLECONTROLS);
|
|
|
|
if (ControlInfo->Mux) {
|
|
Values[ControlInfo->Data.v[0].s] = (LONG)ControlInfo->Range.Max.s;
|
|
Values[1 - ControlInfo->Data.v[0].s] = (LONG)ControlInfo->Range.Min.s;
|
|
} else {
|
|
if (ControlInfo->Signed) {
|
|
Values[0] = (LONG)ControlInfo->Data.v[0].s;
|
|
Values[1] = (LONG)ControlInfo->Data.v[1].s;
|
|
} else {
|
|
Values[0] = (LONG)(ULONG)ControlInfo->Data.v[0].u;
|
|
Values[1] = (LONG)(ULONG)ControlInfo->Data.v[1].u;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** If only 1 channel was asked for then munge the data accordingly
|
|
*/
|
|
|
|
if (DataLength == sizeof(LONG)) {
|
|
switch (MixerControlInit[ControlId].dwControlType &
|
|
MIXERCONTROL_CT_UNITS_MASK) {
|
|
|
|
case MIXERCONTROL_CT_UNITS_BOOLEAN:
|
|
Values[0] = Values[0] != 0 || Values[1] != 0;
|
|
break ;
|
|
|
|
case MIXERCONTROL_CT_UNITS_SIGNED:
|
|
|
|
/*
|
|
** Assumes signed values...
|
|
*/
|
|
|
|
if (absval(Values[1]) > absval(Values[0])) {
|
|
Values[0] = Values[1];
|
|
}
|
|
|
|
break ;
|
|
|
|
case MIXERCONTROL_CT_UNITS_UNSIGNED:
|
|
case MIXERCONTROL_CT_UNITS_DECIBELS:
|
|
case MIXERCONTROL_CT_UNITS_PERCENT:
|
|
|
|
/*
|
|
** Assumes unsigned values...
|
|
*/
|
|
|
|
if ((ULONG)Values[0] < (ULONG)Values[1]) {
|
|
Values[0] = Values[1];
|
|
}
|
|
break ;
|
|
}
|
|
|
|
/*
|
|
** Copy the single value back
|
|
*/
|
|
|
|
} else {
|
|
((PLONG)ControlData)[1] = Values[1];
|
|
}
|
|
|
|
*(PLONG)ControlData = Values[0];
|
|
|
|
dprintf4(("retunr 3"));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
SoundMixerChangedMuxItem(
|
|
PGLOBAL_DEVICE_INFO pGDI,
|
|
ULONG ControlId,
|
|
int Subitem
|
|
)
|
|
{
|
|
|
|
int i;
|
|
|
|
dprintf4(("SoundMixerChangeMuxItem()"));
|
|
|
|
for (i = 0; i < NUMBEROFTEXTITEMS; i++) {
|
|
|
|
if (MixerTextInit[i].ControlId == ControlId) {
|
|
|
|
SoundMixerChangedItem(
|
|
&pGDI->MixerInfo,
|
|
&pGDI->LocalMixerData.LineNotification[
|
|
MixerTextInit[i + Subitem].dwParam1]);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
HwSetControl(
|
|
PMIXER_INFO MixerInfo,
|
|
ULONG ControlId,
|
|
ULONG DataLength,
|
|
PVOID ControlData
|
|
)
|
|
{
|
|
PLOCAL_MIXER_CONTROL_INFO ControlInfo;
|
|
int i;
|
|
BOOLEAN Changed;
|
|
LONG Values[2];
|
|
BOOLEAN MixerSetResult;
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
|
|
dprintf4(("HwSetControl() Start"));
|
|
|
|
pGDI = CONTAINING_RECORD(MixerInfo, GLOBAL_DEVICE_INFO, MixerInfo);
|
|
|
|
/*
|
|
** Validate control ID
|
|
*/
|
|
|
|
if (ControlId > MAXCONTROLS) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/*
|
|
** Establish pointers to our structures
|
|
*/
|
|
|
|
ControlInfo = &pGDI->LocalMixerData.ControlInfo[ControlId];
|
|
|
|
ASSERTMSG("Set index out of range",
|
|
ControlInfo->SetIndex < MAXSETTABLECONTROLS ||
|
|
ControlInfo->SetIndex == MIXER_SET_INDEX_INVALID);
|
|
|
|
/*
|
|
** Validate data length and values
|
|
*/
|
|
|
|
switch (DataLength) {
|
|
case sizeof(LONG):
|
|
Values[0] = *(PLONG)ControlData;
|
|
Values[1] = Values[0];
|
|
break;
|
|
|
|
case sizeof(LONG) * 2:
|
|
Values[0] = *(PLONG)ControlData;
|
|
Values[1] = ((PLONG)ControlData)[1];
|
|
break;
|
|
|
|
default:
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
/*
|
|
** Apparently Boolean values can be anything
|
|
*/
|
|
|
|
if (ControlInfo->Boolean) {
|
|
Values[0] = (ULONG)!!Values[0];
|
|
Values[1] = (ULONG)!!Values[1];
|
|
}
|
|
|
|
/*
|
|
** Check the item ranges and assign the values. Note that
|
|
** this stuff only works for <= 2 channels/items.
|
|
*/
|
|
|
|
for (i = 0, Changed = FALSE; i < 2; i++) {
|
|
if (ControlInfo->Signed) {
|
|
if (Values[i] < (LONG)ControlInfo->Range.Min.s ||
|
|
Values[i] > (LONG)ControlInfo->Range.Max.s) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
if ((SHORT)((PLONG)Values)[i] != ControlInfo->Data.v[i].s) {
|
|
Changed = TRUE;
|
|
ControlInfo->Data.v[i].s = (SHORT)((PLONG)Values)[i];
|
|
}
|
|
}
|
|
} else {
|
|
|
|
if ((((PULONG)Values)[i] < (ULONG)ControlInfo->Range.Min.u ||
|
|
((PULONG)Values)[i] > (ULONG)ControlInfo->Range.Max.u)) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
|
|
/*
|
|
** Do muxes slightly differently so we don't store a big
|
|
** array of n - 1 zeros and 1 one
|
|
*/
|
|
|
|
if (ControlInfo->Mux) {
|
|
if (Values[i] != (LONG)ControlInfo->Range.Min.s) {
|
|
/*
|
|
** 'On' - only turn ONE on
|
|
*/
|
|
|
|
if ((USHORT)i != ControlInfo->Data.v[0].u) {
|
|
Changed = TRUE;
|
|
|
|
/*
|
|
** Notify the one turned off and the
|
|
** one turned on
|
|
*/
|
|
|
|
SoundMixerChangedMuxItem(
|
|
pGDI,
|
|
ControlId,
|
|
ControlInfo->Data.v[0].u);
|
|
|
|
ControlInfo->Data.v[0].u = (USHORT)i;
|
|
|
|
SoundMixerChangedMuxItem(
|
|
pGDI,
|
|
ControlId,
|
|
ControlInfo->Data.v[0].u);
|
|
}
|
|
/*
|
|
** Mux ONLY changes ONE thing
|
|
*/
|
|
|
|
break;
|
|
}
|
|
} else {
|
|
if ((USHORT)((PULONG)Values)[i] != ControlInfo->Data.v[i].u) {
|
|
Changed = TRUE;
|
|
ControlInfo->Data.v[i].u = (USHORT)((PULONG)Values)[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Changed) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
** Notify the Win32 Midi driver of changes
|
|
*/
|
|
|
|
//if (ControlId == ControlLineoutMidioutVolume) {
|
|
//
|
|
// SoundNotifySynth(pGDI);
|
|
//}
|
|
|
|
/*
|
|
** Now pass on to the relevant handler which must :
|
|
** Set the hardware
|
|
** Determine if there is a real change so it can generate notifications
|
|
** Generate related changes (eg mux handling)
|
|
*/
|
|
|
|
switch (ControlId) {
|
|
case ControlWaveInMux:
|
|
case ControlVoiceInMux:
|
|
case ControlWaveInAux1Volume:
|
|
case ControlWaveInMicVolume:
|
|
case ControlVoiceInAux1Volume:
|
|
case ControlVoiceInMicVolume:
|
|
MixerSetResult = MixSetADCHardware(pGDI, ControlId);
|
|
dprintf4(("Call MixSetADCHardware()"));
|
|
break;
|
|
|
|
case ControlLineoutAux1Volume:
|
|
case ControlLineoutWaveoutVolume:
|
|
MixerSetResult = MixSetVolume(pGDI, ControlId);
|
|
dprintf4(("Call MixSetVolume()"));
|
|
break;
|
|
|
|
case ControlLineoutVolume:
|
|
MixerSetResult = MixSetMasterVolume(pGDI, ControlId);
|
|
dprintf4(("Call MixSetMasterVolume()"));
|
|
//SoundNotifySynth(pGDI);
|
|
break;
|
|
|
|
case ControlLineoutMute:
|
|
MixerSetResult = MixSetMute(pGDI, ControlId);
|
|
dprintf4(("Call MixSetMute()"));
|
|
//SoundNotifySynth(pGDI);
|
|
break;
|
|
|
|
case ControlLineoutAux1Mute:
|
|
case ControlLineoutWaveoutMute:
|
|
MixerSetResult = MixSetMute(pGDI, ControlId);
|
|
dprintf4(("Call MixSetMute()"));
|
|
break;
|
|
|
|
case ControlLineoutMidioutVolume:
|
|
case ControlLineoutMidioutMute:
|
|
//SoundNotifySynth(pGDI);
|
|
break;
|
|
|
|
case ControlLineoutWaveoutPeak:
|
|
case ControlWaveInAux1Peak:
|
|
case ControlWaveInMicPeak:
|
|
case ControlVoiceInAux1Peak:
|
|
case ControlVoiceInMicPeak:
|
|
MixerSetResult = FALSE;
|
|
dprintf4(("MixerSetResult = FALSE"));
|
|
break;
|
|
}
|
|
|
|
if (MixerSetResult) {
|
|
|
|
SoundMixerChangedItem(MixerInfo,
|
|
&pGDI->LocalMixerData.ControlNotification[
|
|
ControlInfo->SetIndex]);
|
|
return STATUS_SUCCESS;
|
|
} else {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|