565 lines
14 KiB
C
565 lines
14 KiB
C
//---------------------------------------------------------------------------
|
|
// "@(#) NEC controls.c 1.2 95/05/08 18:33:42"
|
|
//
|
|
// CONTROLS.C
|
|
//
|
|
// Copyright (c) 1995 NEC Corporation All right reserved.
|
|
// Copyright (c) 1993 Microsoft Corporation. All rights reserved.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Module: controls.c
|
|
//
|
|
// Purpose: Mixer control interface for SNDSYS.DRV
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) 1993 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "sound.h"
|
|
|
|
#define SILENCE (192)
|
|
|
|
BOOLEAN MixUpdateGlobalLevels
|
|
(
|
|
PGLOBAL_DEVICE_INFO pGDI
|
|
);
|
|
|
|
|
|
UCHAR
|
|
VolLinearToLog(
|
|
IN USHORT volume
|
|
)
|
|
/*++
|
|
|
|
Routine Description :
|
|
|
|
Converts a linear scale to a logarithm :
|
|
|
|
0xffffffff -> 0
|
|
0x00010000 -> 191
|
|
|
|
Arguments :
|
|
|
|
volume - 0 to 0xffff
|
|
|
|
Return Value :
|
|
|
|
value in decibels attenuation, each unit is 1.5 dB
|
|
|
|
--*/
|
|
{
|
|
ULONG gain, shift;
|
|
ULONG temp;
|
|
static CONST UCHAR lut[16] = {0,0,0,1,1,1,2,2,2,2,3,3,3,3,3,3};
|
|
UCHAR out;
|
|
|
|
if (volume == 0) {
|
|
return SILENCE;
|
|
}
|
|
|
|
if (volume == 0xFFFF) {
|
|
return 0;
|
|
}
|
|
|
|
/* get an estimate to within 6 dB of gain */
|
|
for (temp = volume, gain = 0, shift = 0;
|
|
temp != 0;
|
|
gain += 4, temp >>= 1, shift++);
|
|
|
|
/* look at highest 3 bits in number into look-up-table to
|
|
find how many more dB */
|
|
if (shift > 5)
|
|
temp = volume >> (shift - 5);
|
|
else if (shift < 5)
|
|
temp = volume << (5 - shift);
|
|
else
|
|
temp = volume;
|
|
temp &= 0x000f;
|
|
|
|
gain += lut[temp];
|
|
|
|
out = (UCHAR) ((16 * 4) + 3 - gain);
|
|
return (out < 128) ? out : (UCHAR)127;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
// BOOLEAN MixSetVolume
|
|
//
|
|
// Description:
|
|
// Sets the volume for a control.
|
|
//
|
|
// Parameters:
|
|
// LPMIXERCONTROLDETAILS pmcd
|
|
// pointer to mixer control detailss structure
|
|
//
|
|
// UINT uCardNum
|
|
// card number
|
|
//
|
|
//
|
|
// Return Value:
|
|
// Nothing.
|
|
//
|
|
BOOLEAN MixSetVolume( PGLOBAL_DEVICE_INFO pGDI, ULONG ControlId )
|
|
{
|
|
BYTE bNumStepsL;
|
|
BYTE bNumStepsR ;
|
|
ULONG MuteControlId;
|
|
|
|
#if DBG
|
|
//DbgBreakPoint();
|
|
#endif
|
|
|
|
dprintf2(( "MixSetVolume() Start" )) ;
|
|
|
|
dprintf2(( "MixSetVolume() ControlId = %x", ControlId));
|
|
|
|
dprintf2(( "CODEC_ADDRESS Reg = %x", INPORT(&pGDI->Hw, CODEC_ADDRESS)));
|
|
|
|
ASSERTMSG("Volume control has signed data!",
|
|
!pGDI->LocalMixerData.ControlInfo[ControlId].Signed);
|
|
|
|
// Convert to steps of 1.5dB attenuation for the CODEC
|
|
bNumStepsL = VolLinearToLog( pGDI->LocalMixerData.ControlInfo[ControlId].Data.v[0].u ) ;
|
|
bNumStepsR = VolLinearToLog( pGDI->LocalMixerData.ControlInfo[ControlId].Data.v[1].u ) ;
|
|
|
|
// Add master attenuation
|
|
bNumStepsL +=
|
|
VolLinearToLog(pGDI->LocalMixerData.ControlInfo[
|
|
ControlLineoutVolume].Data.v[0].u);
|
|
bNumStepsR +=
|
|
VolLinearToLog(pGDI->LocalMixerData.ControlInfo[
|
|
ControlLineoutVolume].Data.v[1].u);
|
|
|
|
// If either this line's mute or the lineout (master)
|
|
// mute is on, mute it.
|
|
switch (ControlId)
|
|
{
|
|
case ControlLineoutWaveoutVolume:
|
|
MuteControlId = ControlLineoutWaveoutMute;
|
|
dprintf2(("MuteId = ControlLineoutWaveoutMute"));
|
|
break;
|
|
|
|
case ControlLineoutAux1Volume:
|
|
MuteControlId = ControlLineoutAux1Mute;
|
|
dprintf2(("MuteId = ControlLineoutAux1Mute"));
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
if(pGDI->LocalMixerData.ControlInfo[
|
|
ControlLineoutMute].Data.v[0].u != 0 ||
|
|
pGDI->LocalMixerData.ControlInfo[
|
|
MuteControlId].Data.v[0].u != 0)
|
|
{
|
|
bNumStepsL = 0x80 ;
|
|
bNumStepsR = 0x80 ;
|
|
}
|
|
|
|
dprintf2(("bNumStepsL = %d, bNumStepsR = %d",
|
|
(int)bNumStepsL, (int)bNumStepsR )) ;
|
|
|
|
// Note that we perform this compare after setting the mute
|
|
// bit and will reset the mute - the number of cycles wasted
|
|
// vs. the compare of special casing the 0x80 is a wash.
|
|
HwEnter(&pGDI->Hw);
|
|
|
|
switch (ControlId)
|
|
{
|
|
case ControlLineoutAux1Volume:
|
|
|
|
dprintf2(("ControlId = ControlLineoutAuxVolume"));
|
|
|
|
// AUX1 added gain in the K grade parts...
|
|
if (pGDI->Hw.CODECClass != CODEC_J_CLASS)
|
|
{
|
|
dprintf2(("LineoutAux1 K-Grade"));
|
|
|
|
// Mute when out of range...
|
|
if (bNumStepsL > 0x1F)
|
|
bNumStepsL = 0x80 ;
|
|
if (bNumStepsR > 0x1F)
|
|
bNumStepsR = 0x80 ;
|
|
}
|
|
else
|
|
{
|
|
dprintf2(("LineoutAux1 J-Grade"));
|
|
|
|
// Mute when out of range...
|
|
if (bNumStepsL > 0x0F)
|
|
bNumStepsL = 0x80 ;
|
|
if (bNumStepsR > 0x0F)
|
|
bNumStepsR = 0x80 ;
|
|
}
|
|
CODECRegisterWrite( &pGDI->Hw, REGISTER_LEFTAUX1, bNumStepsL ) ;
|
|
CODECRegisterWrite( &pGDI->Hw, REGISTER_RIGHTAUX1, bNumStepsR ) ;
|
|
break ;
|
|
|
|
case ControlLineoutWaveoutVolume:
|
|
dprintf2(("ControlId = ControlLineoutWaveoutVolume"));
|
|
|
|
// Mute when out of range...
|
|
if (pGDI->Hw.CODECClass != CODEC_J_CLASS)
|
|
{
|
|
dprintf2(("LineoutWaveout K-Grade"));
|
|
|
|
if (bNumStepsL > 0x3F)
|
|
bNumStepsL = 0x80 ;
|
|
if (bNumStepsR > 0x3F)
|
|
bNumStepsR = 0x80 ;
|
|
}
|
|
else
|
|
{
|
|
dprintf4(("LineoutWaveout J-Grade"));
|
|
dprintf4(("Don't mute DAC"));
|
|
|
|
// Don't mute DAC, just fully attenuate on 'J' to avoid
|
|
// garbage with muting other sources (bug in chip).
|
|
if (bNumStepsL > 0x3F)
|
|
bNumStepsL = 0x3F ;
|
|
if (bNumStepsR > 0x3F)
|
|
bNumStepsR = 0x3F ;
|
|
}
|
|
|
|
// Only write to hardware when active
|
|
if (pGDI->DeviceInUse == WaveOutDevice)
|
|
{
|
|
dprintf4(("Device is Active, Write a Register Value"));
|
|
|
|
CODECRegisterWrite( &pGDI->Hw, REGISTER_LEFTOUTPUT, bNumStepsL ) ;
|
|
CODECRegisterWrite( &pGDI->Hw, REGISTER_RIGHTOUTPUT, bNumStepsR ) ;
|
|
}
|
|
break ;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
HwLeave(&pGDI->Hw);
|
|
|
|
dprintf2(( "pGDI->Hw.bPower(2) = %x", pGDI->Hw.bPower));
|
|
dprintf2(("MixSetVolume() End"));
|
|
#if DBG
|
|
//DbgBreakPoint();
|
|
#endif
|
|
|
|
return TRUE;
|
|
} // end of MixSetVolume()
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// BOOLEAN MixSetADCHardware
|
|
//
|
|
// Description:
|
|
// Sets the ADC hardware...
|
|
//
|
|
// Parameters:
|
|
// LPMIXERCONTROLDETAILS pmcd
|
|
// pointer to control details
|
|
//
|
|
// UINT uCardNum
|
|
// card number
|
|
//
|
|
// Return (BOOLEAN):
|
|
// TRUE if no problem
|
|
//
|
|
BOOLEAN MixSetADCHardware
|
|
(
|
|
PGLOBAL_DEVICE_INFO pGDI,
|
|
ULONG ControlId
|
|
)
|
|
{
|
|
BYTE bSource;
|
|
BYTE bMicGainL = 1, bMicGainR = 1;
|
|
BYTE bNumStepsL, bNumStepsR;
|
|
BYTE bADCToCODECLeft, bADCToCODECRight;
|
|
ULONG MuxControlId;
|
|
ULONG ADCControlId;
|
|
PWAVE_INFO WaveInfo;
|
|
|
|
WaveInfo = &pGDI->WaveInfo;
|
|
|
|
dprintf2(( "MixSetADCHardware" )) ;
|
|
|
|
/*
|
|
** Determine if we should be changing the hardware
|
|
*/
|
|
|
|
if (pGDI->DeviceInUse != WaveInDevice) {
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
** Is this a control of the currently selected source?
|
|
** If so, just return...
|
|
*/
|
|
|
|
if (WaveInfo->LowPriorityHandle != NULL &&
|
|
!WaveInfo->LowPrioritySaved) {
|
|
MuxControlId = ControlVoiceInMux;
|
|
|
|
ADCControlId =
|
|
pGDI->LocalMixerData.ControlInfo[ControlVoiceInMux].Data.v[0].u ==
|
|
MUXINPUT_AUX1 ?
|
|
ControlVoiceInAux1Volume :
|
|
ControlVoiceInMicVolume;
|
|
} else {
|
|
MuxControlId = ControlWaveInMux;
|
|
|
|
ADCControlId =
|
|
pGDI->LocalMixerData.ControlInfo[ControlWaveInMux].Data.v[0].u ==
|
|
MUXINPUT_AUX1 ?
|
|
ControlWaveInAux1Volume :
|
|
ControlWaveInMicVolume;
|
|
}
|
|
|
|
/*
|
|
** Don't set it if nothing is changing. If ADC is starting we're lazy
|
|
** and just pass in -1.
|
|
*/
|
|
|
|
if (ControlId != (ULONG)-1L &&
|
|
ControlId != MuxControlId &&
|
|
ControlId != ADCControlId)
|
|
{
|
|
dprintf2(( "MixSetADCVolume: User is adjusting an inactive input level..." )) ;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
** If we got here, we know that the user is altering a control that
|
|
** is for the currently active device
|
|
*/
|
|
|
|
bNumStepsL =
|
|
VolLinearToLog(pGDI->LocalMixerData.ControlInfo[ADCControlId].Data.v[0].u);
|
|
bNumStepsR =
|
|
VolLinearToLog(pGDI->LocalMixerData.ControlInfo[ADCControlId].Data.v[1].u);
|
|
|
|
if (pGDI->LocalMixerData.ControlInfo[MuxControlId].Data.v[0].u == MUXINPUT_MIC)
|
|
{
|
|
#ifndef STEREOMIC
|
|
/*
|
|
** Mic is mono, so use Left setting for Right also...
|
|
*/
|
|
|
|
bNumStepsR = bNumStepsL ;
|
|
#endif
|
|
|
|
// Don't use gain on Compaq BA hardware...
|
|
// doesn't seem to like it and peaks quite easily.
|
|
|
|
// if (pGDI->Hw.CompaqBA)
|
|
/*{
|
|
// Bounding...
|
|
|
|
if (bNumStepsL > 0x0F)
|
|
bNumStepsL = 0x0F ;
|
|
|
|
if (bNumStepsR > 0x0F)
|
|
bNumStepsR = 0x0F ;
|
|
|
|
// 16 steps for 22.5 dB of gain...
|
|
|
|
bNumStepsL = 0x0F - bNumStepsL ;
|
|
bNumStepsR = 0x0F - bNumStepsR ;
|
|
}
|
|
|
|
|
|
else
|
|
*/
|
|
{
|
|
// Bounding...
|
|
|
|
if (bNumStepsL > 28)
|
|
bNumStepsL = 28 ;
|
|
|
|
if (bNumStepsR > 28)
|
|
bNumStepsR = 28 ;
|
|
|
|
// 28 steps for 42.5 dB of gain...
|
|
|
|
bNumStepsL = 28 - bNumStepsL ;
|
|
bNumStepsR = 28 - bNumStepsR ;
|
|
|
|
// set 20 dB gain accordingly
|
|
|
|
if (bNumStepsL > 13)
|
|
{
|
|
bMicGainL = 0x20 ;
|
|
bNumStepsL -= 13 ;
|
|
}
|
|
|
|
if (bNumStepsR > 13)
|
|
{
|
|
bMicGainR = 0x20 ;
|
|
bNumStepsR -= 13 ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now set bSource to Mic for hardware
|
|
//
|
|
bSource = 0x80;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
** Bounding...
|
|
*/
|
|
|
|
if (bNumStepsL > 0x0F)
|
|
bNumStepsL = 0x0F ;
|
|
|
|
if (bNumStepsR > 0x0F)
|
|
bNumStepsR = 0x0F ;
|
|
|
|
bNumStepsL = 0x0F - bNumStepsL ;
|
|
bNumStepsR = 0x0F - bNumStepsR ;
|
|
|
|
//
|
|
// Now set bSource to Line-In (for hardware)
|
|
//
|
|
|
|
bSource = 0x40;
|
|
|
|
}
|
|
|
|
bADCToCODECLeft = bSource | bMicGainL | bNumStepsL;
|
|
bADCToCODECRight = bSource | bMicGainR | bNumStepsR;
|
|
|
|
ASSERT( (bADCToCODECLeft & 0xC0) != 0xC0 );
|
|
ASSERT( (bADCToCODECRight & 0xC0) != 0xC0 );
|
|
|
|
// #pragma message( "NEED: Auto-calibrate when changing input levels on 'J'" )
|
|
|
|
HwEnter(&pGDI->Hw);
|
|
CODECRegisterWrite( &pGDI->Hw, REGISTER_LEFTINPUT, bADCToCODECLeft );
|
|
CODECRegisterWrite( &pGDI->Hw, REGISTER_RIGHTINPUT, bADCToCODECRight );
|
|
HwLeave(&pGDI->Hw);
|
|
|
|
return TRUE;
|
|
|
|
} // MixSetADCHardware()
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// BOOLEAN MixSetMasterVolume
|
|
//
|
|
// Description:
|
|
// Sets the master volume
|
|
//
|
|
// Parameters:
|
|
// LPMIXERCONTROLDETAILS pmcd
|
|
// pointer to control details
|
|
//
|
|
// UINT uCardNum
|
|
// card number
|
|
//
|
|
// Return (BOOLEAN):
|
|
// TRUE if no problem
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOLEAN FAR PASCAL MixSetMasterVolume
|
|
(
|
|
PGLOBAL_DEVICE_INFO pGDI,
|
|
ULONG ControlId
|
|
)
|
|
{
|
|
dprintf2(("MixSetMasterVolume")) ;
|
|
|
|
return MixUpdateGlobalLevels(pGDI);
|
|
|
|
} // MixSetMasterVolume()
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// BOOLEAN MixSetMute
|
|
//
|
|
// Description:
|
|
// Turns the mute on and off
|
|
//
|
|
// Parameters:
|
|
// LPMIXERCONTROLDETAILS pmcd
|
|
// pointer to control details
|
|
//
|
|
// UINT uCardNum
|
|
// card number
|
|
//
|
|
// Return (BOOLEAN):
|
|
// TRUE if no problem
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOLEAN FAR PASCAL MixSetMute
|
|
(
|
|
PGLOBAL_DEVICE_INFO pGDI,
|
|
ULONG ControlId
|
|
)
|
|
{
|
|
|
|
//
|
|
// The value in the dwValue field has already been updated.
|
|
// Nothing to do now, but update all the actual levels.
|
|
//
|
|
|
|
MixUpdateGlobalLevels( pGDI ) ;
|
|
|
|
return TRUE ;
|
|
|
|
} // MixSetMute()
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// BOOLEAN MixUpdateGlobalLevels
|
|
//
|
|
// Description:
|
|
// Updates all controls. This is called when some of the modelling
|
|
// causes the hardware to be updated (eg we changed the mux or the
|
|
// master)
|
|
//
|
|
// Parameters:
|
|
// UINT uCardNum
|
|
//
|
|
// Return (BOOLEAN):
|
|
// TRUE if no problem
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOLEAN MixUpdateGlobalLevels
|
|
(
|
|
PGLOBAL_DEVICE_INFO pGDI
|
|
)
|
|
{
|
|
/*
|
|
** Adjust AUX1 volume.
|
|
*/
|
|
|
|
MixSetVolume( pGDI, (ULONG)ControlLineoutAux1Volume ) ;
|
|
|
|
/*
|
|
** Adjust DAC volume.
|
|
*/
|
|
|
|
MixSetVolume( pGDI, (ULONG)ControlLineoutWaveoutVolume ) ;
|
|
|
|
|
|
return TRUE;
|
|
|
|
} // MixUpdateGlobalLevels()
|
|
|
|
//---------------------------------------------------------------------------
|
|
// End of File: controls.c
|
|
//---------------------------------------------------------------------------
|