2020-09-30 17:12:29 +02:00

574 lines
13 KiB
C

//---------------------------------------------------------------------------
//
// CONTROLS.C
//
// Copyright (c) 1993 Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//
// Module: controls.c
//
// Purpose: Mixer control interface for MVAUDIO.SYS
//
#include "sound.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, MixSetMute)
#pragma alloc_text(PAGE, MixSetTrebleBass)
#pragma alloc_text(PAGE, MixSetSingleMux)
#pragma alloc_text(PAGE, MixSetMultiMux)
#pragma alloc_text(PAGE, MixSetVolume)
#endif
//
// Utility routines
//
/*
** Translate our control ids into real hardware ids
*/
VOID
GetInputAndOutputId(
PGLOBAL_DEVICE_INFO pGDI,
ULONG ControlId,
PUCHAR Input,
PUCHAR Output,
PBOOLEAN IsInput
)
{
switch (ControlId) {
case ControlLineoutMute:
case ControlLineoutMux:
case ControlLineoutBass:
case ControlLineoutTreble:
#ifdef LOUDNESS
case ControlLineoutLoudness:
case ControlLineoutStereoEnhance:
#endif // LOUDNESS
case ControlWaveInMux:
case ControlWaveInPeak:
case ControlVoiceInMux:
ASSERTMSG("Mixer invalid control id for volume setting", FALSE);
break;
case ControlLineoutVolume:
*Input = 0;
*Output = OUT_AMPLIFIER;
*IsInput = FALSE;
break;
case ControlWaveInVolume:
*Input = 0;
*Output = OUT_PCM;
*IsInput = FALSE;
break;
case ControlLineoutAux1Volume:
*Input = IN_EXTERNAL;
*Output = OUT_AMPLIFIER;
*IsInput = TRUE;
break;
case ControlLineoutMidioutVolume:
*Input = IN_SYNTHESIZER;
*Output = OUT_AMPLIFIER;
*IsInput = TRUE;
break;
case ControlLineoutMicVolume:
*Input = IN_MICROPHONE;
*Output = OUT_AMPLIFIER;
*IsInput = TRUE;
break;
case ControlLineoutInternalCDVolume:
*Input = IN_INTERNAL;
*Output = OUT_AMPLIFIER;
*IsInput = TRUE;
break;
case ControlLineoutPCSpeakerVolume:
if (IS_MIXER_508B(pGDI)) {
*Input = IN_SNDBLASTER;
} else {
*Input = IN_PC_SPEAKER;
}
*Output = OUT_AMPLIFIER;
*IsInput = TRUE;
break;
case ControlLineoutAux2Volume:
ASSERT(IS_MIXER_508B(pGDI));
*Input = IN_PC_SPEAKER;
*Output = OUT_AMPLIFIER;
*IsInput = TRUE;
break;
case ControlLineoutWaveoutVolume:
*Input = IN_PCM;
*Output = OUT_AMPLIFIER;
*IsInput = TRUE;
break;
case ControlLineoutMixerVolume:
*Input = IN_MIXER;
*Output = OUT_AMPLIFIER;
*IsInput = TRUE;
break;
case ControlWaveInAux1Volume:
*Input = IN_EXTERNAL;
*Output = OUT_PCM;
*IsInput = TRUE;
break;
case ControlWaveInMidioutVolume:
*Input = IN_SYNTHESIZER;
*Output = OUT_PCM;
*IsInput = TRUE;
break;
case ControlWaveInMicVolume:
*Input = IN_MICROPHONE;
*Output = OUT_PCM;
*IsInput = TRUE;
break;
case ControlWaveInInternalCDVolume:
*Input = IN_INTERNAL;
*Output = OUT_PCM;
*IsInput = TRUE;
break;
case ControlWaveInAux2Volume:
ASSERT(IS_MIXER_508B(pGDI));
*Input = IN_PC_SPEAKER;
*Output = OUT_PCM;
*IsInput = TRUE;
break;
case ControlWaveInPCSpeakerVolume:
if (IS_MIXER_508B(pGDI)) {
*Input = IN_SNDBLASTER;
} else {
*Input = IN_PC_SPEAKER;
}
*Output = OUT_PCM;
*IsInput = TRUE;
break;
case ControlVoiceInAux1Volume:
*Input = IN_EXTERNAL;
*Output = OUT_PCM;
*IsInput = TRUE;
break;
case ControlVoiceInMicVolume:
*Input = IN_MICROPHONE;
*Output = OUT_PCM;
*IsInput = TRUE;
break;
}
}
/*
** Set mute
*/
VOID
SetMute(
PGLOBAL_DEVICE_INFO pGDI,
BOOLEAN Mute
)
{
/*
** Update the mute status and if it's changed set the master volume
** directly (so the change doesn't NOOP
*/
if (pGDI->MixerState.Mute != Mute) {
pGDI->MixerState.Mute = Mute;
SetOutput(pGDI,
OUT_AMPLIFIER,
pGDI->MixerState.OutputSettings[OUT_AMPLIFIER].Left,
_LEFT);
SetOutput(pGDI,
OUT_AMPLIFIER,
pGDI->MixerState.OutputSettings[OUT_AMPLIFIER].Right,
_RIGHT);
}
}
VOID
ChangeInput(
PGLOBAL_DEVICE_INFO pGDI,
UCHAR Input,
UCHAR Output,
USHORT Channel,
USHORT Old,
USHORT New
)
/*++
Routine Description
Change an input gradually from its current value to its target value
NOTE - this routine ASSUMES that each input has 32 steps
--*/
{
USHORT Current, Final;
Current = Old >> 11;
Final = New >> 11;
/*
** At least once to get the output amp right
*/
while (TRUE) {
SetInput(pGDI,
Input,
(USHORT)(Current << 11),
Channel,
MIXCROSSCAPS_NORMAL_STEREO,
Output);
if (Current == Final) {
break;
}
Current = Current > Final ? Current - 1 : Current + 1;
}
}
VOID
ChangeOutput(
PGLOBAL_DEVICE_INFO pGDI,
UCHAR Output,
USHORT Channel,
USHORT Old,
USHORT New
)
/*++
Routine Description
Change an output gradually from its current value to its target value
NOTE - this routine ASSUMES that each output has 64 steps
--*/
{
USHORT Current, Final;
Current = Old >> 10;
Final = New >> 10;
/*
** At least once to get the output amp right
*/
while (TRUE) {
SetOutput(pGDI,
Output,
(USHORT)(Current << 10),
Channel);
if (Current == Final) {
break;
}
Current = Current > Final ? Current - 1 : Current + 1;
}
}
VOID
UpdateInput(
PGLOBAL_DEVICE_INFO pGDI,
UCHAR Input,
UCHAR Output,
USHORT Left,
USHORT Right
)
{
ASSERT(Input < NUM_INPUTS && Output < NUM_OUTPUTS);
/*
** Check to see if any change is required
*/
if (pGDI->MixerState.InputSettings[Input].Output != Output ||
pGDI->MixerState.InputSettings[Input].Left != Left ||
pGDI->MixerState.InputSettings[Input].Right != Right) {
ChangeInput(pGDI,
Input,
Output,
_LEFT,
pGDI->MixerState.InputSettings[Input].Left,
Left);
ChangeInput(pGDI,
Input,
Output,
_RIGHT,
pGDI->MixerState.InputSettings[Input].Right,
Right);
pGDI->MixerState.InputSettings[Input].Output = Output;
pGDI->MixerState.InputSettings[Input].Left = Left;
pGDI->MixerState.InputSettings[Input].Right = Right;
}
}
VOID
UpdateOutputLevel(
PGLOBAL_DEVICE_INFO pGDI,
UCHAR Output,
USHORT Left,
USHORT Right
)
{
ASSERT(Output < NUM_OUTPUTS);
/*
** Check to see if any change is required
*/
if (pGDI->MixerState.OutputSettings[Output].Left != Left ||
pGDI->MixerState.OutputSettings[Output].Right != Right) {
ChangeOutput(pGDI,
Output,
_LEFT,
pGDI->MixerState.OutputSettings[Output].Left,
Left);
ChangeOutput(pGDI,
Output,
_RIGHT,
pGDI->MixerState.OutputSettings[Output].Right,
Right);
pGDI->MixerState.OutputSettings[Output].Left = Left;
pGDI->MixerState.OutputSettings[Output].Right = Right;
}
}
BOOLEAN
MixSetVolume(
PGLOBAL_DEVICE_INFO pGDI,
ULONG ControlId
)
{
PLOCAL_MIXER_CONTROL_INFO ControlInfo;
PLOCAL_MIXER_DATA LocalMixerData;
ULONG LineFlags;
NTSTATUS Status;
UCHAR Input;
UCHAR Output;
BOOLEAN IsInput;
LocalMixerData = &pGDI->LocalMixerData;
ControlInfo = &LocalMixerData->ControlInfo[ControlId];
/*
** We need to work out :
**
** 1. Is the line active - if not do nothing
**
** NOTE: To not set it if it isn't active means we must
** make sure we set the volume if the line becomes active.
** This applies specifically to wave out and midi out which
** are only active when the respective devices are playing.
** Set mixer.c!SoundSynthLineChanged and
** mixer.c!SoundWaveoutLineChanged.
**
** 2. Otherwise, deduce the hardware line id and change the
** 3. Setting
*/
Status = HwGetLineFlags(&pGDI->MixerInfo,
MixerControlInit[ControlId].LineID,
sizeof(ULONG),
&LineFlags);
ASSERTMSG("HwGetLineFlags returned bad for internal call!",
NT_SUCCESS(Status));
if (!(LineFlags & MIXERLINE_LINEF_ACTIVE)) {
return TRUE;
}
/*
** Deduce the hardware info
*/
GetInputAndOutputId(pGDI, ControlId, &Input, &Output, &IsInput);
/*
** Set the relevant control
*/
if (IsInput) {
UpdateInput(pGDI,
Input,
Output,
ControlInfo->Data.v[0].u,
ControlInfo->Data.v[1].u);
} else {
/*
** We're updating an 'output' line - ie OUT_PCM or OUT_AMPLIFIER
*/
UpdateOutputLevel(pGDI,
Output,
ControlInfo->Data.v[0].u,
ControlInfo->Data.v[1].u);
}
return TRUE;
}
BOOLEAN
MixSetSingleMux(
PGLOBAL_DEVICE_INFO pGDI,
ULONG ControlId
)
{
/*
** We don't need to do anything if the stuff isn't 'active'
** If it is 'active' we just need to pull the lines to their
** correct destinations.
*/
ASSERT(ControlId == ControlVoiceInMux);
MixSetVolume(pGDI, ControlVoiceInAux1Volume);
MixSetVolume(pGDI, ControlVoiceInMicVolume);
return TRUE;
}
BOOLEAN
MixSetMultiMux(
PGLOBAL_DEVICE_INFO pGDI,
ULONG ControlId
)
{
int i;
ASSERT(ControlId == ControlWaveInMux ||
ControlId == ControlLineoutMux);
/*
** Set everything that can be set
*/
for (i = 0;
i < MAX_INPUTS - 2;
i++) {
MixSetVolume(pGDI, ControlLineoutAux1Volume + i);
MixSetVolume(pGDI, ControlWaveInAux1Volume + i);
}
return TRUE;
}
VOID
UpdateEQ(
PGLOBAL_DEVICE_INFO pGDI,
USHORT Type,
USHORT Level
)
/*++
Routine Description
Set the Treble and Bass levels - these are assumed to always be switched
to the OUT_AMPLIFIER
--*/
{
PUSHORT DestLevel;
USHORT Current, Target;
DestLevel = Type == _TREBLE ? &pGDI->MixerState.Treble :
&pGDI->MixerState.Bass;
for (Current = *DestLevel >> 11,
Target = Level >> 11;
Current != Target;
Current = Current > Target ? Current - 1 : Current + 1
) {
SetEQ(pGDI,
OUT_AMPLIFIER,
Type,
Current);
}
*DestLevel = Level;
}
BOOLEAN
MixSetTrebleBass(
PGLOBAL_DEVICE_INFO pGDI,
ULONG ControlId
)
{
UpdateEQ(pGDI,
(USHORT)(ControlId == ControlLineoutTreble ?
_TREBLE : _BASS),
pGDI->LocalMixerData.ControlInfo[ControlId].Data.v[0].u);
return TRUE;
}
#ifdef LOUDNESS
BOOLEAN
MixSetLineControl(
PGLOBAL_DEVICE_INFO pGDI,
ULONG ControlId
)
{
ASSERT(ControlId == ControlLineoutLoudness ||
ControlId == ControlLineoutStereoEnhance);
SetEqMode(
pGDI,
pGDI->LocalMixerData.ControlInfo[ControlLineoutLoudness].Data.v[0].u,
pGDI->LocalMixerData.ControlInfo[ControlLineoutStereoEnhance].Data.v[0].u);
return TRUE;
}
#endif
BOOLEAN
MixSetMute(
PGLOBAL_DEVICE_INFO pGDI,
ULONG ControlId
)
{
ASSERT(ControlId == ControlLineoutMute);
SetMute(pGDI,
(BOOLEAN)
(pGDI->LocalMixerData.ControlInfo[ControlLineoutMute].Data.v[0].u != 0));
return TRUE;
}
//---------------------------------------------------------------------------
// End of File: controls.c
//---------------------------------------------------------------------------