502 lines
12 KiB
C
502 lines
12 KiB
C
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sbpromix.c
|
|
|
|
Abstract:
|
|
|
|
Mixer code for the Sound Blaster Pro card.
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "sound.h"
|
|
#include "sbpromix.h"
|
|
|
|
/*
|
|
** Local functions and data
|
|
*/
|
|
|
|
VOID
|
|
SBPROWaveinLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
);
|
|
VOID
|
|
SBPROWaveoutLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
);
|
|
VOID
|
|
SBPROSynthLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
);
|
|
|
|
VOID
|
|
SBPROSetVolumeControlIds(
|
|
PGLOBAL_DEVICE_INFO pGDI
|
|
);
|
|
|
|
BOOLEAN
|
|
SBPROMixerSet(
|
|
PGLOBAL_DEVICE_INFO pGDI,
|
|
ULONG ControlId
|
|
);
|
|
|
|
BOOLEAN
|
|
SBPROLineActive(
|
|
IN PGLOBAL_DEVICE_INFO pGDI,
|
|
IN ULONG LinelId
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,SBPROMixerInit)
|
|
#pragma alloc_text(INIT,SBPROSetVolumeControlIds)
|
|
|
|
#pragma alloc_text(PAGE,SBPROWaveinLineChanged)
|
|
#pragma alloc_text(PAGE,SBPROLineActive)
|
|
#pragma alloc_text(PAGE,SBPROMixerOutputFree)
|
|
#pragma alloc_text(PAGE,SBPROWaveoutLineChanged)
|
|
#pragma alloc_text(PAGE,SBPROSynthLineChanged)
|
|
#pragma alloc_text(PAGE,SBPROMixerSet)
|
|
#endif
|
|
|
|
|
|
VOID
|
|
SBPROMixerInit(
|
|
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 = SBPROLineInit;
|
|
LocalMixerData->NumberOfLines = NumberOfLines;
|
|
LocalMixerData->MixerControlInit = SBPROControlInit;
|
|
LocalMixerData->NumberOfControls = NumberOfControls;
|
|
LocalMixerData->MixerTextInit = SBPROTextInit;
|
|
LocalMixerData->MaxSettableItems = NumberOfControls - 6; // 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 = SBPROMixerSet;
|
|
LocalMixerData->MixerLineActive = SBPROLineActive;
|
|
|
|
/*
|
|
** Note - these generic volume controls control both the recording and
|
|
** the playing volumes - whichever they are switched to
|
|
*/
|
|
|
|
SBPROSetVolumeControlIds(pGDI);
|
|
|
|
/*
|
|
** Set up line notifications and volume control ids for non-mixer devices
|
|
*/
|
|
|
|
SoundSetLineNotify(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[WaveInDevice]->DeviceExtension,
|
|
SBPROWaveinLineChanged);
|
|
SoundSetLineNotify(
|
|
(PLOCAL_DEVICE_INFO)pGDI->DeviceObject[WaveOutDevice]->DeviceExtension,
|
|
SBPROWaveoutLineChanged);
|
|
|
|
|
|
if (pGDI->Synth.DeviceObject) {
|
|
SoundSetLineNotify(
|
|
(PLOCAL_DEVICE_INFO)pGDI->Synth.DeviceObject->DeviceExtension,
|
|
SBPROSynthLineChanged);
|
|
}
|
|
|
|
/*
|
|
** 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 ControlLineoutAuxVolume:
|
|
case ControlLineoutMidioutVolume:
|
|
case ControlLineoutWaveoutVolume:
|
|
case ControlLineoutInternalCDVolume:
|
|
case ControlWaveInAuxVolume:
|
|
case ControlWaveInInternalCDVolume:
|
|
case ControlVoiceInAuxVolume:
|
|
|
|
/*
|
|
** Half volume
|
|
*/
|
|
|
|
LocalMixerData->ControlInfo[i].Data.v[0].u = 32768;
|
|
LocalMixerData->ControlInfo[i].Data.v[1].u = 32768;
|
|
break;
|
|
|
|
case ControlWaveInMux:
|
|
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:
|
|
/*
|
|
** Already 0
|
|
*/
|
|
break;
|
|
|
|
case ControlLineoutWaveoutPeak:
|
|
case ControlWaveInAuxPeak:
|
|
case ControlWaveInMicPeak:
|
|
case ControlWaveInInternalCDPeak:
|
|
case ControlVoiceInAuxPeak:
|
|
case ControlVoiceInMicPeak:
|
|
/*
|
|
** Not settable
|
|
*/
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE); // Added control but forgot to set it!
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SBPROWaveinLineChanged(
|
|
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)) {
|
|
SBPROSetADCHardware(pGDI);
|
|
} else {
|
|
SBPROResetADCHardware(pGDI);
|
|
}
|
|
|
|
/*
|
|
** The only VU Meter is on currently selected wave input
|
|
*/
|
|
|
|
switch (Code) {
|
|
case SOUND_LINE_NOTIFY_WAVE:
|
|
Line = DestWaveInSourceAux +
|
|
LocalMixerData->ControlInfo[ControlWaveInMux].Data.v[0].u;
|
|
break;
|
|
|
|
case SOUND_LINE_NOTIFY_VOICE:
|
|
Line = DestVoiceInSourceAux +
|
|
LocalMixerData->ControlInfo[ControlVoiceInMux].Data.v[0].u;
|
|
break;
|
|
}
|
|
|
|
SoundMixerChangedItem(
|
|
&((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->MixerInfo,
|
|
&LocalMixerData->LineNotification[Line]);
|
|
}
|
|
VOID
|
|
SBPROWaveoutLineChanged(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
UCHAR Code
|
|
)
|
|
{
|
|
PLOCAL_MIXER_DATA LocalMixerData;
|
|
|
|
LocalMixerData = &((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->LocalMixerData;
|
|
|
|
/*
|
|
** Set the volume
|
|
*/
|
|
|
|
SBPROSetVolume((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo,
|
|
ControlLineoutWaveoutVolume);
|
|
|
|
SoundMixerChangedItem(
|
|
&((PGLOBAL_DEVICE_INFO)pLDI->pGlobalInfo)->MixerInfo,
|
|
&LocalMixerData->LineNotification[DestLineoutSourceWaveout]);
|
|
}
|
|
|
|
VOID
|
|
SBPROSynthLineChanged(
|
|
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.
|
|
*/
|
|
|
|
SBPROSetVolume(pGDI, ControlLineoutMidioutVolume);
|
|
|
|
|
|
LocalMixerData = &pGDI->LocalMixerData;
|
|
|
|
SoundMixerChangedItem(
|
|
&pGDI->MixerInfo,
|
|
&LocalMixerData->LineNotification[DestLineoutSourceMidiout]);
|
|
}
|
|
VOID
|
|
SBPROSetVolumeControlIds(
|
|
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
|
|
SBPROMixerSet(
|
|
PGLOBAL_DEVICE_INFO pGDI,
|
|
ULONG ControlId
|
|
)
|
|
{
|
|
switch (ControlId) {
|
|
|
|
case ControlLineoutVolume: // 0
|
|
case ControlLineoutAuxVolume: // 4
|
|
case ControlLineoutMidioutVolume: // 6
|
|
case ControlLineoutInternalCDVolume: // 8
|
|
case ControlLineoutWaveoutVolume: // 10
|
|
case ControlWaveInAuxVolume: // 13
|
|
case ControlWaveInInternalCDVolume: // 16
|
|
case ControlVoiceInAuxVolume: // 18
|
|
return SBPROSetVolume(pGDI, ControlId);
|
|
|
|
case ControlLineoutMidioutMute: // 7
|
|
case ControlLineoutAuxMute: // 5
|
|
case ControlLineoutMute: // 1
|
|
case ControlLineoutInternalCDMute: // 9
|
|
case ControlLineoutWaveoutMute: // 11
|
|
return SBPROSetMute(pGDI, ControlId);
|
|
|
|
case ControlWaveInMux: // 2
|
|
case ControlVoiceInMux: // 3
|
|
return SBPROSetSources(pGDI, ControlId);
|
|
|
|
case ControlLineoutWaveoutPeak: // 12
|
|
case ControlWaveInAuxPeak: // 14
|
|
case ControlWaveInMicPeak: // 15
|
|
case ControlWaveInInternalCDPeak: // 17
|
|
case ControlVoiceInAuxPeak: // 19
|
|
case ControlVoiceInMicPeak: // 20
|
|
/*
|
|
** Not settable
|
|
*/
|
|
return FALSE;
|
|
default:
|
|
dprintf1(("Invalid ControlId %u", ControlId));
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
SBPROLineActive(
|
|
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:
|
|
return TRUE;
|
|
|
|
case DestLineoutSourceInternal:
|
|
case DestLineoutSourceAux:
|
|
return SBPROMixerOutputFree(pGDI, LineId);
|
|
|
|
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
|
|
SBPROMixerOutputFree(
|
|
IN PGLOBAL_DEVICE_INFO pGDI,
|
|
IN ULONG LineId
|
|
)
|
|
{
|
|
if (WAVE_IN_ACTIVE(&pGDI->WaveInfo)) {
|
|
ULONG CounterpartId;
|
|
switch(LineId) {
|
|
case DestLineoutSourceInternal:
|
|
CounterpartId = DestWaveInSourceInternal;
|
|
break;
|
|
case DestLineoutSourceAux:
|
|
CounterpartId = DestWaveInSourceAux;
|
|
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 {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|