565 lines
14 KiB
C
565 lines
14 KiB
C
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sb16mix.c
|
|
|
|
Abstract:
|
|
|
|
Mixer code for the Sound Blaster 16 card.
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "sound.h"
|
|
#include "sb16mix.h"
|
|
|
|
/*
|
|
** Local functions and data
|
|
*/
|
|
|
|
VOID
|
|
SB16WaveinLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
);
|
|
VOID
|
|
SB16WaveoutLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
);
|
|
VOID
|
|
SB16SynthLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
);
|
|
|
|
VOID
|
|
SB16SetVolumeControlIds(
|
|
PGLOBAL_DEVICE_INFO pGDI
|
|
);
|
|
|
|
BOOLEAN
|
|
SB16MixerSet(
|
|
PGLOBAL_DEVICE_INFO pGDI,
|
|
ULONG ControlId
|
|
);
|
|
|
|
BOOLEAN
|
|
SB16LineActive(
|
|
IN PGLOBAL_DEVICE_INFO pGDI,
|
|
IN ULONG LinelId
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,SB16MixerInit)
|
|
#pragma alloc_text(INIT,SB16SetVolumeControlIds)
|
|
|
|
#pragma alloc_text(PAGE,SB16WaveinLineChanged)
|
|
#pragma alloc_text(PAGE,SB16LineActive)
|
|
#pragma alloc_text(PAGE,SB16MixerOutputFree)
|
|
#pragma alloc_text(PAGE,SB16WaveoutLineChanged)
|
|
#pragma alloc_text(PAGE,SB16SynthLineChanged)
|
|
#pragma alloc_text(PAGE,SB16MixerSet)
|
|
#endif
|
|
|
|
|
|
VOID
|
|
SB16MixerInit(
|
|
PGLOBAL_DEVICE_INFO pGDI
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize our local data for the sound blaster 16
|
|
|
|
Arguments:
|
|
|
|
pGDI - Global card device info
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PLOCAL_MIXER_DATA LocalMixerData;
|
|
ULONG i;
|
|
|
|
LocalMixerData = &pGDI->LocalMixerData;
|
|
|
|
LocalMixerData->MixerLineInit = SB16LineInit;
|
|
LocalMixerData->NumberOfLines = NumberOfLines;
|
|
LocalMixerData->MixerControlInit = SB16ControlInit;
|
|
LocalMixerData->NumberOfControls = NumberOfControls;
|
|
LocalMixerData->MixerTextInit = SB16TextInit;
|
|
LocalMixerData->MaxSettableItems = NumberOfControls - 3; // Can't set meters
|
|
|
|
/*
|
|
** NOTE - generic code works out NumberOfTextItems so we don't need to
|
|
** set it here (and we shouldn't!).
|
|
*/
|
|
|
|
/*
|
|
** Set up our callbacks
|
|
*/
|
|
|
|
LocalMixerData->MixerSet = SB16MixerSet;
|
|
LocalMixerData->MixerLineActive = SB16LineActive;
|
|
|
|
/*
|
|
** Note - these generic volume controls control both the recording and
|
|
** the playing volumes - whichever they are switched to
|
|
*/
|
|
|
|
SB16SetVolumeControlIds(pGDI);
|
|
|
|
/*
|
|
** Set up line notifications and volume control ids for non-mixer devices
|
|
*/
|
|
|
|
SoundSetLineNotify(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[WaveInDevice]->DeviceExtension,
|
|
SB16WaveinLineChanged);
|
|
SoundSetLineNotify(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[WaveOutDevice]->DeviceExtension,
|
|
SB16WaveoutLineChanged);
|
|
|
|
|
|
if (pGDI->Synth.DeviceObject) {
|
|
SoundSetLineNotify(
|
|
(PLOCAL_DEVICE_INFO)pGDI->Synth.DeviceObject->DeviceExtension,
|
|
SB16SynthLineChanged);
|
|
}
|
|
|
|
/*
|
|
** Set up defaults
|
|
*/
|
|
|
|
for (i = 0; i < NumberOfControls; i++) {
|
|
switch (i) {
|
|
|
|
/*
|
|
** For us it's either Mute, a volume, a Mux or a Mixer.
|
|
**
|
|
** Volumes are set to a fixed value
|
|
**
|
|
** Nothing is muted
|
|
*/
|
|
|
|
case ControlLineoutVolume:
|
|
case ControlWaveInVolume:
|
|
case ControlVoiceInVolume:
|
|
case ControlLineoutAuxVolume:
|
|
case ControlLineoutMidioutVolume:
|
|
case ControlLineoutMicVolume:
|
|
case ControlLineoutInternalCDVolume:
|
|
case ControlLineoutWaveoutVolume:
|
|
case ControlWaveInAuxVolume:
|
|
case ControlWaveInMidioutVolume:
|
|
case ControlWaveInMicVolume:
|
|
case ControlWaveInInternalCDVolume:
|
|
case ControlVoiceInAuxVolume:
|
|
case ControlVoiceInMicVolume:
|
|
|
|
case ControlLineoutBass:
|
|
case ControlLineoutTreble:
|
|
/*
|
|
** Half volume
|
|
*/
|
|
|
|
LocalMixerData->ControlInfo[i].Data.v[0].u = 32768;
|
|
LocalMixerData->ControlInfo[i].Data.v[1].u = 32768;
|
|
break;
|
|
|
|
case ControlLineoutMux:
|
|
case ControlWaveInMux:
|
|
/*
|
|
** Everything is selected initially.
|
|
** NOTE - we supply a bogus flag for midi out on line out
|
|
** so that SoundMixerLineActive stuff works out correctly.
|
|
** for that line.
|
|
*/
|
|
LocalMixerData->ControlInfo[i].Data.MixMask =
|
|
(1 << LocalMixerData->MixerLineInit[DestWaveIn].cConnections) - 1;
|
|
break;
|
|
case ControlVoiceInMux:
|
|
/*
|
|
** Only record voice from microphone by default
|
|
*/
|
|
LocalMixerData->ControlInfo[i].Data.v[0].u = MUXINPUT_MIC;
|
|
break;
|
|
|
|
case ControlLineoutMute:
|
|
case ControlLineoutAuxMute:
|
|
case ControlLineoutMidioutMute:
|
|
case ControlLineoutInternalCDMute:
|
|
case ControlLineoutWaveoutMute:
|
|
case ControlLineoutGain:
|
|
/*
|
|
** Already 0
|
|
*/
|
|
break;
|
|
|
|
case ControlLineoutMicMute:
|
|
case ControlLineoutMicAGC:
|
|
case ControlWaveInMicAGC:
|
|
case ControlVoiceInMicAGC:
|
|
/* AGC on by default and mute the mic so as not to confuse
|
|
people */
|
|
LocalMixerData->ControlInfo[i].Data.v[0].u = 1;
|
|
break;
|
|
|
|
|
|
case ControlWaveInPeak:
|
|
case ControlVoiceInPeak:
|
|
case ControlLineoutWaveoutPeak:
|
|
/*
|
|
** Not settable
|
|
*/
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE); // Added control but forgot to set it!
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SB16WaveinLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
)
|
|
{
|
|
UCHAR Line;
|
|
PLOCAL_MIXER_DATA LocalMixerData;
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
|
|
pGDI = pLDI->pGlobalInfo;
|
|
|
|
LocalMixerData = &((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->LocalMixerData;
|
|
|
|
/*
|
|
** Wave Input is starting or stopping - set the hardware accordingly
|
|
*/
|
|
|
|
if (WAVE_IN_ACTIVE(&pGDI->WaveInfo)) {
|
|
SB16SetADCHardware(pGDI);
|
|
} else {
|
|
SB16ResetADCHardware(pGDI);
|
|
}
|
|
|
|
/*
|
|
** 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;
|
|
|
|
default: // Get rid of warning
|
|
case SOUND_LINE_NOTIFY_VOICE:
|
|
Line = DestVoiceIn;
|
|
break;
|
|
}
|
|
|
|
SoundMixerChangedItem(
|
|
&((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->MixerInfo,
|
|
&LocalMixerData->LineNotification[Line]);
|
|
}
|
|
VOID
|
|
SB16WaveoutLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
)
|
|
{
|
|
PLOCAL_MIXER_DATA LocalMixerData;
|
|
|
|
LocalMixerData = &((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->LocalMixerData;
|
|
|
|
/*
|
|
** Set the volume
|
|
*/
|
|
|
|
SB16SetVolume((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo,
|
|
ControlLineoutWaveoutVolume);
|
|
|
|
SoundMixerChangedItem(
|
|
&((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->MixerInfo,
|
|
&LocalMixerData->LineNotification[DestLineoutSourceWaveout]);
|
|
}
|
|
|
|
VOID
|
|
SB16SynthLineChanged(
|
|
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.
|
|
*/
|
|
|
|
SB16SetVolume(pGDI, ControlLineoutMidioutVolume);
|
|
|
|
SB16SetVolume(pGDI, ControlWaveInMidioutVolume);
|
|
|
|
|
|
LocalMixerData = &pGDI->LocalMixerData;
|
|
|
|
SoundMixerChangedItem(
|
|
&pGDI->MixerInfo,
|
|
&LocalMixerData->LineNotification[DestLineoutSourceMidiout]);
|
|
}
|
|
VOID
|
|
SB16SetVolumeControlIds(
|
|
PGLOBAL_DEVICE_INFO pGDI
|
|
)
|
|
{
|
|
SoundSetVolumeControlId(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[WaveOutDevice]->DeviceExtension,
|
|
ControlLineoutWaveoutVolume);
|
|
|
|
SoundSetVolumeControlId(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[LineInDevice]->DeviceExtension,
|
|
ControlLineoutAuxVolume);
|
|
|
|
SoundSetVolumeControlId(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[CDInternal]->DeviceExtension,
|
|
ControlLineoutInternalCDVolume);
|
|
|
|
if (pGDI->Synth.DeviceObject) {
|
|
SoundSetVolumeControlId(
|
|
(PLOCAL_DEVICE_INFO)pGDI->Synth.DeviceObject->DeviceExtension,
|
|
ControlLineoutMidioutVolume);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
SB16MixerSet(
|
|
PGLOBAL_DEVICE_INFO pGDI,
|
|
ULONG ControlId
|
|
)
|
|
{
|
|
switch (ControlId) {
|
|
|
|
case ControlLineoutVolume:
|
|
case ControlWaveInVolume:
|
|
case ControlVoiceInVolume:
|
|
case ControlLineoutAuxVolume:
|
|
case ControlLineoutMidioutVolume:
|
|
case ControlLineoutMicVolume:
|
|
case ControlLineoutInternalCDVolume:
|
|
case ControlLineoutWaveoutVolume:
|
|
case ControlWaveInAuxVolume:
|
|
case ControlWaveInMidioutVolume:
|
|
case ControlWaveInMicVolume:
|
|
case ControlWaveInInternalCDVolume:
|
|
case ControlVoiceInAuxVolume:
|
|
case ControlVoiceInMicVolume:
|
|
return SB16SetVolume(pGDI, ControlId);
|
|
|
|
case ControlLineoutBass:
|
|
case ControlLineoutTreble:
|
|
return SB16SetTone(pGDI, ControlId);
|
|
|
|
case ControlLineoutMux:
|
|
case ControlWaveInMux:
|
|
case ControlVoiceInMux:
|
|
return SB16SetSources(pGDI, ControlId);
|
|
|
|
case ControlLineoutMute:
|
|
case ControlLineoutAuxMute:
|
|
case ControlLineoutMidioutMute:
|
|
case ControlLineoutMicMute:
|
|
case ControlLineoutInternalCDMute:
|
|
case ControlLineoutWaveoutMute:
|
|
return SB16SetMute(pGDI, ControlId);
|
|
|
|
case ControlLineoutMicAGC:
|
|
case ControlWaveInMicAGC:
|
|
case ControlVoiceInMicAGC:
|
|
return SB16SetAGC(pGDI, ControlId);
|
|
|
|
case ControlLineoutGain:
|
|
return SB16SetGain(pGDI, ControlId);
|
|
|
|
case ControlWaveInPeak:
|
|
case ControlVoiceInPeak:
|
|
case ControlLineoutWaveoutPeak:
|
|
/*
|
|
** Not settable
|
|
*/
|
|
return FALSE;
|
|
default:
|
|
dprintf1(("Invalid ControlId %u", ControlId));
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
SB16LineActive(
|
|
IN PGLOBAL_DEVICE_INFO pGDI,
|
|
IN ULONG LineId
|
|
)
|
|
{
|
|
/*
|
|
** The factors that affect this are
|
|
** - the mux settings
|
|
** - whether there is an output device playing
|
|
** - whether we are recording :
|
|
**
|
|
** If a device is deselected via a mux it's inactive
|
|
** A wave output device is only active when it's playing
|
|
** (we can't apply this to the whole of wave input because that
|
|
** dest contains a mux which is always valid.
|
|
**
|
|
** If an output source's counterpart is selected for record then
|
|
** it is inactive.
|
|
*/
|
|
|
|
switch (LineId) {
|
|
|
|
/*
|
|
** Muxes, line out line out are always available
|
|
*/
|
|
|
|
case DestWaveIn:
|
|
/*
|
|
** Well, even though the mux is operational we'd better not return
|
|
** active if we're not recording or all sorts of peak meters will
|
|
** pop up
|
|
*/
|
|
return WAVE_IN_ACTIVE(&pGDI->WaveInfo);
|
|
|
|
case DestVoiceIn:
|
|
return VOICE_IN_ACTIVE(&pGDI->WaveInfo);
|
|
|
|
case DestLineout:
|
|
return TRUE;
|
|
|
|
case DestLineoutSourceMidiout:
|
|
case DestLineoutSourceInternal:
|
|
case DestLineoutSourceAux:
|
|
case DestLineoutSourceMic:
|
|
return SB16MixerOutputFree(pGDI, LineId);
|
|
break;
|
|
|
|
case DestWaveInSourceMidiout:
|
|
case DestWaveInSourceInternal:
|
|
case DestWaveInSourceAux:
|
|
case DestWaveInSourceMic:
|
|
|
|
/*
|
|
** These guys are only active if wave input is active
|
|
*/
|
|
|
|
if (WAVE_IN_ACTIVE(&pGDI->WaveInfo)) {
|
|
if (MixerLineSelected(&pGDI->LocalMixerData, ControlWaveInMux, LineId)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
** Wave out deemed 'active' when open
|
|
*/
|
|
|
|
case DestLineoutSourceWaveout:
|
|
if (WAVE_OUT_ACTIVE(&pGDI->WaveInfo)) {
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
** voice in from aux active when selected and low priority active
|
|
*/
|
|
|
|
case DestVoiceInSourceAux:
|
|
case DestVoiceInSourceMic:
|
|
if (MixerLineSelected(&pGDI->LocalMixerData, ControlVoiceInMux, LineId) &&
|
|
VOICE_IN_ACTIVE(&pGDI->WaveInfo)) {
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE); // Invalid
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
SB16MixerOutputFree(
|
|
IN PGLOBAL_DEVICE_INFO pGDI,
|
|
IN ULONG LineId
|
|
)
|
|
{
|
|
/*
|
|
** Midi out isn't in the input mixer
|
|
*/
|
|
|
|
if (MixerLineSelected(&pGDI->LocalMixerData, ControlLineoutMux, LineId)) {
|
|
if (WAVE_IN_ACTIVE(&pGDI->WaveInfo)) {
|
|
ULONG CounterpartId;
|
|
|
|
switch(LineId) {
|
|
case DestLineoutSourceMidiout:
|
|
CounterpartId = DestWaveInSourceMidiout;
|
|
break;
|
|
case DestLineoutSourceInternal:
|
|
CounterpartId = DestWaveInSourceInternal;
|
|
break;
|
|
case DestLineoutSourceAux:
|
|
CounterpartId = DestWaveInSourceAux;
|
|
break;
|
|
case DestLineoutSourceMic:
|
|
CounterpartId = DestWaveInSourceMic;
|
|
break;
|
|
default:
|
|
return TRUE;
|
|
}
|
|
|
|
if (!MixerLineSelected(&pGDI->LocalMixerData, ControlWaveInMux, CounterpartId)) {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
if (VOICE_IN_ACTIVE(&pGDI->WaveInfo)) {
|
|
if (pGDI->LocalMixerData.
|
|
ControlInfo[ControlVoiceInMux].Data.v[0].u ==
|
|
MUXINPUT_AUX1) {
|
|
if (LineId != DestLineoutSourceAux) {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
if (LineId != DestLineoutSourceMic) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|