mirror of
https://github.com/AppleWin/AppleWin.git
synced 2024-12-23 00:30:17 +00:00
PR #275: Attenuate speaker (and 8-bit DAC) output sample after 0.25s of inactivity.
. Cherry-pick from 'master' of https://github.com/rmacri/AppleWin into master:
This commit is contained in:
parent
355f5d0dd7
commit
713efcdcb1
@ -109,6 +109,64 @@ static void DisplayBenchmarkResults ()
|
||||
MB_ICONINFORMATION | MB_SETFOREGROUND);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// DC filtering V2 (Riccardo Macri May 2015) (GH#275)
|
||||
//
|
||||
// To prevent loud clicks on Window's sound buffer underruns and constant DC
|
||||
// being sent out to amplifiers (some soundcards are DC coupled) which is
|
||||
// not good for them, an attenuator slowly drops the speaker output
|
||||
// to 0 after the speaker (or 8 bit DAC) has been idle for a couple hundred
|
||||
// milliseconds.
|
||||
//
|
||||
// The approach works as follows:
|
||||
// - SpkrToggle() is called when the speaker state is flipped by accessing $C030
|
||||
// - This brings audio up to date then calls ResetDCFilter()
|
||||
// - ResetDCFilter() sets a counter to a high value
|
||||
// - every audio sample is processed by DCFilter() as follows:
|
||||
// - if the counter is >= 32768, the speaker has been recently toggled
|
||||
// and the samples are unaffected
|
||||
// - if the counter is < 32768 but > 0, it is used to scale the
|
||||
// sample to reduce +ve or -ve speaker states towards zero
|
||||
// - In the two cases above, the counter is decremented
|
||||
// - if the counter is zero, the speaker has been silent for a while
|
||||
// and the output is 0 regardless of the speaker state.
|
||||
//
|
||||
// - the initial "high value" is chosen so 10000/44100 = about a
|
||||
// quarter of a second of speaker inactivity is needed before attenuation
|
||||
// begins.
|
||||
//
|
||||
// NOTE: The attenuation is not ever reducing the level of audio, just
|
||||
// the DC offset at which the speaker has been left.
|
||||
//
|
||||
// This approach has zero impact on any speaker tones including PWM
|
||||
// due to the samples being unchanged for at least 0.25 seconds after
|
||||
// any speaker activity.
|
||||
//
|
||||
|
||||
static UINT g_uDCFilterState = 0;
|
||||
|
||||
inline void ResetDCFilter(void)
|
||||
{
|
||||
// reset the attenuator with an additional 250ms of full gain
|
||||
// (10000 samples) before it starts attenuating
|
||||
g_uDCFilterState = 32768 + 10000;
|
||||
}
|
||||
|
||||
inline short DCFilter(short sample_in)
|
||||
{
|
||||
if (g_uDCFilterState == 0) // no sound for a while, stay 0
|
||||
return 0;
|
||||
|
||||
if (g_uDCFilterState >= 32768) // full gain after recent sound
|
||||
{
|
||||
g_uDCFilterState--;
|
||||
return sample_in;
|
||||
}
|
||||
|
||||
return (((int)sample_in) * (g_uDCFilterState--)) / 32768; // scale & divide by 32768 (NB. Don't ">>15" as undefined behaviour)
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
static void SetClksPerSpkrSample()
|
||||
@ -283,7 +341,7 @@ static void UpdateRemainderBuffer(ULONG* pnCycleDiff)
|
||||
nSampleMean /= (signed long) g_nRemainderBufferSize;
|
||||
|
||||
if(g_nBufferIdx < SPKR_SAMPLE_RATE-1)
|
||||
g_pSpeakerBuffer[g_nBufferIdx++] = (short) nSampleMean;
|
||||
g_pSpeakerBuffer[g_nBufferIdx++] = DCFilter( (short)nSampleMean );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -301,7 +359,7 @@ static void UpdateSpkr()
|
||||
ULONG nCyclesRemaining = (ULONG) ((double)nCycleDiff - (double)nNumSamples * g_fClksPerSpkrSample);
|
||||
|
||||
while((nNumSamples--) && (g_nBufferIdx < SPKR_SAMPLE_RATE-1))
|
||||
g_pSpeakerBuffer[g_nBufferIdx++] = g_nSpeakerData;
|
||||
g_pSpeakerBuffer[g_nBufferIdx++] = DCFilter(g_nSpeakerData);
|
||||
|
||||
ReinitRemainderBuffer(nCyclesRemaining); // Partially fill 1Mhz sample buffer
|
||||
}
|
||||
@ -336,21 +394,16 @@ BYTE __stdcall SpkrToggle (WORD, WORD, BYTE, BYTE, ULONG nCyclesLeft)
|
||||
|
||||
UpdateSpkr();
|
||||
|
||||
if (g_bQuieterSpeaker)
|
||||
{
|
||||
// quieten the speaker if 8 bit DAC in use
|
||||
if (g_nSpeakerData == (SPKR_DATA_INIT/4)) // NB. Don't shift -ve number right: undefined behaviour (MSDN says: implementation-dependent)
|
||||
g_nSpeakerData = ~g_nSpeakerData;
|
||||
else
|
||||
g_nSpeakerData = SPKR_DATA_INIT/4; // NB. Don't shift -ve number right: undefined behaviour (MSDN says: implementation-dependent)
|
||||
}
|
||||
short speakerDriveLevel = SPKR_DATA_INIT;
|
||||
if (g_bQuieterSpeaker) // quieten the speaker if 8 bit DAC in use
|
||||
speakerDriveLevel /= 4; // NB. Don't shift -ve number right: undefined behaviour (MSDN says: implementation-dependent)
|
||||
|
||||
ResetDCFilter();
|
||||
|
||||
if (g_nSpeakerData == speakerDriveLevel)
|
||||
g_nSpeakerData = ~speakerDriveLevel;
|
||||
else
|
||||
{
|
||||
if (g_nSpeakerData == SPKR_DATA_INIT)
|
||||
g_nSpeakerData = ~g_nSpeakerData;
|
||||
else
|
||||
g_nSpeakerData = SPKR_DATA_INIT;
|
||||
}
|
||||
g_nSpeakerData = speakerDriveLevel;
|
||||
}
|
||||
|
||||
return MemReadFloatingBus(nCyclesLeft);
|
||||
@ -575,7 +628,7 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
|
||||
|
||||
if(dwBufferSize0)
|
||||
{
|
||||
wmemset((wchar_t*)pDSLockedBuffer0, (wchar_t)g_nSpeakerData, dwBufferSize0/sizeof(wchar_t));
|
||||
wmemset((wchar_t*)pDSLockedBuffer0, (wchar_t)DCFilter(g_nSpeakerData), dwBufferSize0/sizeof(wchar_t));
|
||||
#ifdef RIFF_SPKR
|
||||
RiffPutSamples(pDSLockedBuffer0, dwBufferSize0/sizeof(short));
|
||||
#endif
|
||||
@ -583,7 +636,7 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
|
||||
|
||||
if(pDSLockedBuffer1)
|
||||
{
|
||||
wmemset((wchar_t*)pDSLockedBuffer1, (wchar_t)g_nSpeakerData, dwBufferSize1/sizeof(wchar_t));
|
||||
wmemset((wchar_t*)pDSLockedBuffer1, (wchar_t)DCFilter(g_nSpeakerData), dwBufferSize1/sizeof(wchar_t));
|
||||
#ifdef RIFF_SPKR
|
||||
RiffPutSamples(pDSLockedBuffer1, dwBufferSize1/sizeof(short));
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user