1408 lines
37 KiB
C
1408 lines
37 KiB
C
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mixer.c
|
|
|
|
Abstract:
|
|
|
|
Mixer code for the Pro Audio Spectrum card.
|
|
|
|
Author:
|
|
|
|
Robin Speed (RobinSp) 10-Oct-1993
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "sound.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
|
|
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
|
|
);
|
|
|
|
BOOLEAN
|
|
SoundMixerSet(
|
|
PGLOBAL_DEVICE_INFO pGDI,
|
|
ULONG ControlId
|
|
);
|
|
|
|
VOID
|
|
SoundSetVolumeControlIds(
|
|
PGLOBAL_DEVICE_INFO pGDI
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,SoundMixerInit)
|
|
#pragma alloc_text(INIT,SoundSetVolumeControlIds)
|
|
|
|
#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,SoundMixerSet)
|
|
|
|
#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;
|
|
|
|
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;
|
|
|
|
LocalMixerData = &((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->LocalMixerData;
|
|
|
|
/*
|
|
** The only VU Meter is on wave in so let them know that's changed.
|
|
*/
|
|
|
|
switch (Code) {
|
|
case SOUND_LINE_NOTIFY_WAVE:
|
|
Line = DestWaveIn;
|
|
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;
|
|
|
|
LocalMixerData = &((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->LocalMixerData;
|
|
|
|
/*
|
|
** Set the volume
|
|
*/
|
|
|
|
MixSetVolume((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo,
|
|
ControlLineoutWaveoutVolume);
|
|
|
|
SoundMixerChangedItem(
|
|
&((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->MixerInfo,
|
|
&LocalMixerData->LineNotification[DestLineoutSourceWaveout]);
|
|
}
|
|
|
|
VOID
|
|
SoundSynthLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
)
|
|
{
|
|
PLOCAL_MIXER_DATA LocalMixerData;
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
|
|
pGDI = CONTAINING_RECORD(pLDI->pGlobalInfo, GLOBAL_DEVICE_INFO, Synth);
|
|
|
|
/*
|
|
** Set the volume. We don't know (although we could find out!) which
|
|
** output midi is switched to so we set both volumes.
|
|
*/
|
|
|
|
MixSetVolume(pGDI, ControlLineoutMidioutVolume);
|
|
|
|
MixSetVolume(pGDI, ControlWaveInMidioutVolume);
|
|
|
|
LocalMixerData = &pGDI->LocalMixerData;
|
|
|
|
SoundMixerChangedItem(
|
|
&pGDI->MixerInfo,
|
|
&LocalMixerData->LineNotification[DestLineoutSourceMidiout]);
|
|
}
|
|
VOID
|
|
SoundSetVolumeControlIds(
|
|
PGLOBAL_DEVICE_INFO pGDI
|
|
)
|
|
{
|
|
SoundSetVolumeControlId(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[WaveOutDevice]->DeviceExtension,
|
|
ControlLineoutWaveoutVolume);
|
|
|
|
SoundSetVolumeControlId(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[LineInDevice]->DeviceExtension,
|
|
ControlLineoutAux1Volume);
|
|
|
|
SoundSetVolumeControlId(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[CDInternal]->DeviceExtension,
|
|
ControlLineoutInternalCDVolume);
|
|
}
|
|
|
|
/*
|
|
** 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;
|
|
|
|
pGDI = pLDI->pGlobalInfo;
|
|
MixerInfo = &pGDI->MixerInfo;
|
|
|
|
/*
|
|
** Init the generic mixer stuff first so we can use it
|
|
*/
|
|
|
|
SoundInitMixerInfo(&pGDI->MixerInfo,
|
|
HwGetLineFlags,
|
|
HwGetControl,
|
|
HwGetCombinedControl,
|
|
HwSetControl);
|
|
|
|
/*
|
|
** Mute it
|
|
*/
|
|
|
|
SetMute(pGDI, TRUE);
|
|
|
|
/*
|
|
** 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;
|
|
PLOCAL_DEVICE_INFO pLDIDev;
|
|
|
|
for (pDO = pGDI->DeviceObject[WaveInDevice]->DriverObject->DeviceObject;
|
|
pDO != NULL;
|
|
pDO = pDO->NextDevice) {
|
|
|
|
|
|
pLDIDev = (PLOCAL_DEVICE_INFO)pDO->DeviceExtension;
|
|
|
|
/*
|
|
** For multiple cards the following test may fail
|
|
*/
|
|
|
|
if (pLDIDev->pGlobalInfo == pGDI) {
|
|
pLDIDev->MixerDevice = pLDI;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fix up the synth device
|
|
//
|
|
|
|
pLDIDev = (PLOCAL_DEVICE_INFO)pGDI->Synth.DeviceObject->DeviceExtension;
|
|
pLDIDev->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_MIXER ||
|
|
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, a Mux or a Mixer.
|
|
**
|
|
** 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 ControlLineoutBass:
|
|
case ControlLineoutTreble:
|
|
#ifdef LOUDNESS
|
|
case ControlLineoutLoudness:
|
|
case ControlLineoutStereoEnhance:
|
|
#endif // LOUDNESS
|
|
|
|
case ControlWaveInVolume:
|
|
|
|
case ControlLineoutAux1Volume:
|
|
|
|
case ControlLineoutAux2Volume:
|
|
|
|
case ControlLineoutMidioutVolume:
|
|
|
|
case ControlLineoutMicVolume:
|
|
|
|
case ControlLineoutInternalCDVolume:
|
|
|
|
case ControlLineoutPCSpeakerVolume:
|
|
|
|
case ControlLineoutWaveoutVolume:
|
|
|
|
case ControlLineoutMixerVolume:
|
|
|
|
case ControlWaveInAux1Volume:
|
|
|
|
case ControlWaveInAux2Volume:
|
|
|
|
case ControlWaveInMidioutVolume:
|
|
|
|
case ControlWaveInMicVolume:
|
|
|
|
case ControlWaveInInternalCDVolume:
|
|
|
|
case ControlWaveInPCSpeakerVolume:
|
|
|
|
case ControlVoiceInAux1Volume:
|
|
|
|
case ControlVoiceInMicVolume:
|
|
|
|
/*
|
|
** Half volume
|
|
*/
|
|
|
|
pGDI->LocalMixerData.ControlInfo[i].Data.v[0].u = 48000;
|
|
pGDI->LocalMixerData.ControlInfo[i].Data.v[1].u = 48000;
|
|
break;
|
|
|
|
case ControlLineoutMute:
|
|
|
|
/*
|
|
** Already 0
|
|
*/
|
|
break;
|
|
|
|
case ControlLineoutMux:
|
|
/*
|
|
** Play everything except the microphone.
|
|
*/
|
|
pGDI->LocalMixerData.ControlInfo[i].Data.MixMask =
|
|
(1 << MixerLineInit[DestLineoutSourceWaveout].Source) +
|
|
(1 << MixerLineInit[DestLineoutSourceMixer].Source) +
|
|
((1 << MixerLineInit[DestWaveIn].cConnections) - 1) -
|
|
(1 << MixerLineInit[DestLineoutSourceMic].Source);
|
|
break;
|
|
|
|
case ControlWaveInMux:
|
|
/*
|
|
** Record microphone only
|
|
*/
|
|
pGDI->LocalMixerData.ControlInfo[i].Data.MixMask =
|
|
1 << MixerLineInit[DestLineoutSourceMic].Source;
|
|
break;
|
|
|
|
case ControlWaveInPeak:
|
|
/*
|
|
** Has no init value
|
|
*/
|
|
ASSERT(FALSE);
|
|
break;
|
|
|
|
case ControlVoiceInMux:
|
|
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);
|
|
|
|
|
|
if (pGDI->Synth.DeviceObject) {
|
|
SoundSetLineNotify(
|
|
(PLOCAL_DEVICE_INFO)pGDI->Synth.DeviceObject->DeviceExtension,
|
|
SoundSynthLineChanged);
|
|
}
|
|
|
|
/*
|
|
** Note - these generic volume controls control both the recording and
|
|
** the playing volumes - whichever they are switched to
|
|
*/
|
|
|
|
SoundSetVolumeControlIds(pGDI);
|
|
|
|
/*
|
|
** Set everything up.
|
|
*/
|
|
|
|
for (i = 0; i < MAXCONTROLS; i++) {
|
|
if (i != ControlLineoutMute) {
|
|
SoundMixerSet(pGDI, i);
|
|
}
|
|
}
|
|
SoundMixerSet(pGDI, ControlLineoutMute);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
SoundMixerDumpConfiguration(
|
|
IN PLOCAL_DEVICE_INFO pLDI,
|
|
IN OUT PIRP pIrp,
|
|
IN PIO_STACK_LOCATION IrpStack
|
|
)
|
|
{
|
|
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;
|
|
|
|
|
|
/*
|
|
** 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));
|
|
|
|
/*
|
|
** Fix up pid for waveout and wavein so that they match
|
|
*/
|
|
OurConfigData->LineData[DestLineoutSourceWaveout].wPid =
|
|
GetWaveoutPid(pGDI);
|
|
|
|
OurConfigData->LineData[DestWaveIn].wPid =
|
|
GetWaveinPid(pGDI);
|
|
|
|
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 = MM_MEDIAVISION;
|
|
OurConfigData->Header.DeviceCaps.wPid = MM_PROAUD_MIXER;
|
|
OurConfigData->Header.DeviceCaps.vDriverVersion = DRIVER_VERSION;
|
|
OurConfigData->Header.DeviceCaps.PnameStringId = IDS_MIXER_PNAME;
|
|
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;
|
|
}
|
|
|
|
/*
|
|
** 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);
|
|
|
|
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;
|
|
|
|
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 DestLineoutSourceMixer:
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
break;
|
|
|
|
case DestLineoutSourceAux2:
|
|
|
|
/*
|
|
** This line valid only for a CDPC_LC
|
|
*/
|
|
|
|
if (!HAS_AUX2(pGDI)) {
|
|
*fdwLine |= MIXERLINE_LINEF_DISCONNECTED;
|
|
break;
|
|
}
|
|
|
|
case DestLineoutSourceMidiout:
|
|
case DestLineoutSourcePCSpeaker:
|
|
case DestLineoutSourceInternal:
|
|
case DestLineoutSourceAux1:
|
|
case DestLineoutSourceMic:
|
|
if (pGDI->LocalMixerData.ControlInfo[ControlLineoutMux].Data.MixMask &
|
|
(1 << MixerLineInit[LineId].Source)) {
|
|
|
|
if (LineId == DestLineoutSourceMidiout) {
|
|
if (pGDI->Synth.Hw.SynthBase == NULL) {
|
|
*fdwLine |= MIXERLINE_LINEF_DISCONNECTED;
|
|
} else {
|
|
if (pGDI->Synth.DeviceInUse != 0xFF) {
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
}
|
|
}
|
|
} else {
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
}
|
|
ASSERT(pGDI->AllowMicOrLineInToLineOut ||
|
|
LineId != DestLineoutSourceAux1 &&
|
|
LineId != DestLineoutSourceMic);
|
|
} else {
|
|
if (!pGDI->AllowMicOrLineInToLineOut &&
|
|
(LineId == DestLineoutSourceAux1 ||
|
|
(LineId == DestLineoutSourceMic))) {
|
|
*fdwLine |= MIXERLINE_LINEF_DISCONNECTED;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case DestWaveInSourceAux2:
|
|
/*
|
|
** This line valid only for some cards
|
|
*/
|
|
|
|
if (!HAS_AUX2(pGDI)) {
|
|
*fdwLine |= MIXERLINE_LINEF_DISCONNECTED;
|
|
break;
|
|
}
|
|
|
|
case DestWaveInSourceMidiout:
|
|
case DestWaveInSourcePCSpeaker:
|
|
case DestWaveInSourceInternal:
|
|
case DestWaveInSourceAux1:
|
|
case DestWaveInSourceMic:
|
|
|
|
if (pGDI->LocalMixerData.ControlInfo[ControlWaveInMux].Data.MixMask &
|
|
(1 << MixerLineInit[LineId].Source)) {
|
|
switch (LineId) {
|
|
case DestWaveInSourceMidiout:
|
|
if (pGDI->Synth.Hw.SynthBase == NULL) {
|
|
*fdwLine |= MIXERLINE_LINEF_DISCONNECTED;
|
|
} else {
|
|
if (pGDI->Synth.DeviceInUse != 0xFF) {
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DestWaveInSourceAux1:
|
|
case DestWaveInSourceMic:
|
|
/*
|
|
** These are active for wave-in provided they're not
|
|
** active for voice-in
|
|
*/
|
|
|
|
if (pGDI->Usage == WaveInDevice &&
|
|
pGDI->WaveInfo.LowPriorityHandle != NULL &&
|
|
!pGDI->WaveInfo.LowPrioritySaved &&
|
|
(LineId == DestWaveInSourceAux1 &&
|
|
pGDI->LocalMixerData.ControlInfo[
|
|
ControlVoiceInMux].Data.v[0].u != MUXINPUT_MIC ||
|
|
LineId == DestWaveInSourceMic &&
|
|
pGDI->LocalMixerData.ControlInfo[
|
|
ControlVoiceInMux].Data.v[0].u == MUXINPUT_MIC)) {
|
|
} else {
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
*fdwLine |= MIXERLINE_LINEF_ACTIVE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
** Wave out deemed 'active' when open
|
|
*/
|
|
|
|
case DestLineoutSourceWaveout:
|
|
if (pGDI->Usage == WaveOutDevice) {
|
|
*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->Usage == 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->Usage == 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;
|
|
|
|
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);
|
|
|
|
/*
|
|
** The hardware controls these levels so always return the same
|
|
** thing.
|
|
*/
|
|
|
|
Vol[0] = 0xFFFF;
|
|
Vol[1] = 0xFFFF;
|
|
|
|
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[MAX_INPUTS - 2];
|
|
|
|
/*
|
|
** Validate control ID
|
|
*/
|
|
|
|
if (ControlId > MAXCONTROLS) {
|
|
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
|
|
*/
|
|
|
|
if (DataLength != sizeof(LONG)) {
|
|
if (!ControlInfo->Mux) {
|
|
if (DataLength != 2 * sizeof(LONG)) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
} else {
|
|
/*
|
|
** Mux
|
|
*/
|
|
|
|
if (DataLength != MixerControlInit[ControlId].cMultipleItems *
|
|
sizeof(LONG)) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
ASSERT(sizeof(Values) >= DataLength);
|
|
}
|
|
}
|
|
/*
|
|
** Pull out the data
|
|
*/
|
|
|
|
if (ControlInfo->SetIndex == MIXER_SET_INDEX_INVALID) {
|
|
/*
|
|
** Must be the VU 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 ControlWaveInPeak:
|
|
if (pGDI->Usage != WaveInDevice) {
|
|
break;
|
|
}
|
|
|
|
if (WaveInfo->LowPriorityHandle != NULL &&
|
|
!WaveInfo->LowPrioritySaved) {
|
|
break;
|
|
}
|
|
ComputePeak = TRUE;
|
|
break;
|
|
|
|
default:
|
|
ASSERTMSG("Invalid control id", FALSE);
|
|
break;
|
|
}
|
|
|
|
if (ComputePeak) {
|
|
HwVUMeter(pGDI, (PULONG)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) {
|
|
|
|
if (MixerControlInit[ControlId].dwControlType ==
|
|
MIXERCONTROL_CONTROLTYPE_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 {
|
|
int i;
|
|
for (i = 0; i < MixerControlInit[ControlId].cMultipleItems; i++) {
|
|
if ((1 << i) & ControlInfo->Data.MixMask) {
|
|
Values[i] = TRUE;
|
|
} else {
|
|
Values[i] = FALSE;
|
|
}
|
|
}
|
|
}
|
|
} 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:
|
|
{
|
|
int i;
|
|
for (i = 1 ; i < MixerControlInit[ControlId].cMultipleItems;
|
|
i++) {
|
|
Values[0] = Values[0] | Values[i];
|
|
}
|
|
}
|
|
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
|
|
*/
|
|
|
|
}
|
|
|
|
RtlCopyMemory((PVOID)ControlData, (PVOID)Values, DataLength);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
SoundMixerChangedMuxItem(
|
|
PGLOBAL_DEVICE_INFO pGDI,
|
|
ULONG ControlId,
|
|
int Subitem
|
|
)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUMBEROFTEXTITEMS; i++) {
|
|
|
|
if (MixerTextInit[i].ControlId == ControlId) {
|
|
|
|
SoundMixerChangedItem(
|
|
&pGDI->MixerInfo,
|
|
&pGDI->LocalMixerData.LineNotification[
|
|
MixerTextInit[i + Subitem].dwParam1]);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
SoundMixerSet(
|
|
PGLOBAL_DEVICE_INFO pGDI,
|
|
ULONG ControlId
|
|
)
|
|
{
|
|
switch (ControlId) {
|
|
case ControlLineoutVolume:
|
|
case ControlLineoutAux1Volume:
|
|
case ControlLineoutAux2Volume:
|
|
case ControlLineoutMidioutVolume:
|
|
case ControlLineoutMicVolume:
|
|
case ControlLineoutInternalCDVolume:
|
|
case ControlLineoutPCSpeakerVolume:
|
|
case ControlLineoutWaveoutVolume:
|
|
case ControlLineoutMixerVolume:
|
|
case ControlWaveInAux1Volume:
|
|
case ControlWaveInAux2Volume:
|
|
case ControlWaveInMidioutVolume:
|
|
case ControlWaveInMicVolume:
|
|
case ControlWaveInInternalCDVolume:
|
|
case ControlWaveInPCSpeakerVolume:
|
|
case ControlVoiceInAux1Volume:
|
|
case ControlVoiceInMicVolume:
|
|
case ControlWaveInVolume:
|
|
return MixSetVolume(pGDI, ControlId);
|
|
|
|
case ControlLineoutMute:
|
|
return MixSetMute(pGDI, ControlId);
|
|
|
|
case ControlLineoutMux:
|
|
case ControlWaveInMux:
|
|
return MixSetMultiMux(pGDI, ControlId);
|
|
|
|
case ControlLineoutBass:
|
|
case ControlLineoutTreble:
|
|
return MixSetTrebleBass(pGDI, ControlId);
|
|
|
|
#ifdef LOUDNESS
|
|
case ControlLineoutLoudness:
|
|
case ControlLineoutStereoEnhance:
|
|
return MixSetLineControl(pGDI, ControlId);
|
|
#endif // LOUDNESS
|
|
|
|
case ControlWaveInPeak:
|
|
return FALSE;
|
|
|
|
case ControlVoiceInMux:
|
|
return MixSetSingleMux(pGDI, ControlId);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
HwSetControl(
|
|
PMIXER_INFO MixerInfo,
|
|
ULONG ControlId,
|
|
ULONG DataLength,
|
|
PVOID ControlData
|
|
)
|
|
{
|
|
PLOCAL_MIXER_CONTROL_INFO ControlInfo;
|
|
int i;
|
|
BOOLEAN Changed;
|
|
LONG Values[MAX_INPUTS - 2];
|
|
BOOLEAN MixerSetResult;
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
int NumberOfValues;
|
|
|
|
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);
|
|
|
|
/*
|
|
** Find out how may values this control has
|
|
*/
|
|
|
|
if (ControlInfo->Mux) {
|
|
NumberOfValues = MixerControlInit[ControlId].cMultipleItems;
|
|
} else {
|
|
NumberOfValues = 2;
|
|
}
|
|
|
|
/*
|
|
** Validate data length and values
|
|
*/
|
|
|
|
if (DataLength != sizeof(LONG)) {
|
|
|
|
if (DataLength != NumberOfValues * sizeof(LONG)) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
ASSERT(sizeof(Values) >= DataLength);
|
|
RtlCopyMemory((PVOID)Values, (PVOID)ControlData, DataLength);
|
|
|
|
} else {
|
|
int i;
|
|
|
|
/*
|
|
** Make them all the same
|
|
*/
|
|
|
|
for (i = 0; i < sizeof(Values) / sizeof(LONG); i++) {
|
|
Values[i] = *(PLONG)ControlData;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Check the item ranges and assign the values. Note that
|
|
** this stuff only works for <= 2 channels/items.
|
|
*/
|
|
|
|
for (i = 0, Changed = FALSE; i < NumberOfValues; i++) {
|
|
|
|
/*
|
|
** Apparently Boolean values can be anything
|
|
*/
|
|
|
|
if (ControlInfo->Boolean) {
|
|
Values[i] = (LONG)!!Values[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 (MixerControlInit[ControlId].dwControlType ==
|
|
MIXERCONTROL_CONTROLTYPE_MUX) {
|
|
if (Values[i]) {
|
|
/*
|
|
** '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 {
|
|
ASSERT(MixerControlInit[ControlId].dwControlType ==
|
|
MIXERCONTROL_CONTROLTYPE_MIXER);
|
|
|
|
/*
|
|
** Store a set of flags for this guy
|
|
** and modify the partner mux
|
|
*/
|
|
|
|
if (((ControlInfo->Data.MixMask &
|
|
(1 << i)) != 0) != Values[i]) {
|
|
|
|
PLOCAL_MIXER_CONTROL_INFO OtherMixer;
|
|
|
|
/*
|
|
** It's changed!
|
|
*/
|
|
|
|
Changed = TRUE;
|
|
|
|
OtherMixer = &pGDI->LocalMixerData.ControlInfo[
|
|
ControlLineoutMux + ControlWaveInMux -
|
|
ControlId];
|
|
|
|
ASSERT((ControlInfo->Data.MixMask ^
|
|
OtherMixer->Data.MixMask) & (1 << i));
|
|
|
|
ControlInfo->Data.MixMask ^=
|
|
1 << i;
|
|
|
|
OtherMixer->Data.MixMask ^=
|
|
1 << i;
|
|
|
|
|
|
/*
|
|
** The code below won't notify about the
|
|
** other control which changed
|
|
*/
|
|
|
|
SoundMixerChangedItem(
|
|
MixerInfo,
|
|
&pGDI->LocalMixerData.ControlNotification[
|
|
OtherMixer->SetIndex]);
|
|
|
|
/*
|
|
** Also mention the line changes !
|
|
** (well, they might have changed).
|
|
*/
|
|
|
|
SoundMixerChangedMuxItem(
|
|
pGDI,
|
|
ControlLineoutMux,
|
|
i);
|
|
|
|
SoundMixerChangedMuxItem(
|
|
pGDI,
|
|
ControlWaveInMux,
|
|
i);
|
|
}
|
|
}
|
|
|
|
} 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;
|
|
}
|
|
|
|
#if 0 // Not required since the volume is controlled in hardware
|
|
/*
|
|
** Notify the Win32 Midi driver of changes
|
|
*/
|
|
|
|
if (ControlId == ControlLineoutMidioutVolume) {
|
|
SoundVolumeNotify((PLOCAL_DEVICE_INFO)
|
|
pGDI->Synth.DeviceObject->DeviceExtension);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
** 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)
|
|
*/
|
|
|
|
MixerSetResult = SoundMixerSet(pGDI, ControlId);
|
|
if (MixerSetResult) {
|
|
|
|
SoundMixerChangedItem(MixerInfo,
|
|
&pGDI->LocalMixerData.ControlNotification[
|
|
ControlInfo->SetIndex]);
|
|
|
|
return STATUS_SUCCESS;
|
|
} else {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|