//--------------------------------------------------------------------------- // // CONTROLS.C // // Copyright (c) 1993 Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // // Module: controls.c // // Purpose: Mixer control interface for SNDSYS.DRV // //@@BEGIN_MSINTERNAL // Development Team: // Bernie McIlroy (BernieM) // Bryan A. Woodruff (BryanW) // // History: Date Author Comment // 4/??/93 BernieM Wrote it. // 5/ 4/93 BryanW Added master volume support. // 5/ 8/93 BryanW Added this comment block. // 5/21/93 BryanW Fixed levels/muting when < min. // 9/24/93 RobinSp Adapted for Windows NT //@@END_MSINTERNAL // //--------------------------------------------------------------------------- // // 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. // //@@BEGIN_MSINTERNAL // History: Date Author Comment // 5/ 5/93 BryanW Added this comment block. //@@END_MSINTERNAL // //------------------------------------------------------------------------ BOOLEAN MixSetVolume ( PGLOBAL_DEVICE_INFO pGDI, ULONG ControlId ) { BYTE bNumStepsL, bNumStepsR ; ULONG MuteControlId; dprintf2(( "MixSetVolume" )) ; 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; break; case ControlLineoutAux1Volume: MuteControlId = 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: // AUX1 added gain in the K grade parts... if (pGDI->Hw.CODECClass != CODEC_J_CLASS) { // Mute when out of range... if (bNumStepsL > 0x1F) bNumStepsL = 0x80 ; if (bNumStepsR > 0x1F) bNumStepsR = 0x80 ; } else { // 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: // Mute when out of range... if (pGDI->Hw.CODECClass != CODEC_J_CLASS) { if (bNumStepsL > 0x3F) bNumStepsL = 0x80 ; if (bNumStepsR > 0x3F) bNumStepsR = 0x80 ; } else { // 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) { CODECRegisterWrite( &pGDI->Hw, REGISTER_LEFTOUTPUT, bNumStepsL ) ; CODECRegisterWrite( &pGDI->Hw, REGISTER_RIGHTOUTPUT, bNumStepsR ) ; } break ; default: ASSERT(FALSE); break; } HwLeave(&pGDI->Hw); 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 // //@@BEGIN_MSINTERNAL // History: Date Author Comment // 8/18/93 BryanW //@@END_MSINTERNAL // //-------------------------------------------------------------------------- BOOLEAN MixSetADCHardware ( PGLOBAL_DEVICE_INFO pGDI, ULONG ControlId ) { BYTE bSource; BYTE bMicGainL = 0, bMicGainR = 0 ; 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 // //@@BEGIN_MSINTERNAL // History: Date Author Comment // 5/20/93 BryanW Added this comment block //@@END_MSINTERNAL // //-------------------------------------------------------------------------- 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 // //@@BEGIN_MSINTERNAL // History: Date Author Comment // 5/20/93 BryanW Added this comment block //@@END_MSINTERNAL // //-------------------------------------------------------------------------- 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 // //@@BEGIN_MSINTERNAL // History: Date Author Comment // 5/20/93 BryanW Added this comment block. //@@END_MSINTERNAL // //-------------------------------------------------------------------------- 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 //---------------------------------------------------------------------------