/* //device/servers/AudioFlinger/AudioBiquadFilter.cpp ** ** Copyright 2009, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #include #include #include #include "AudioBiquadFilter.h" namespace android { const audio_coef_t AudioBiquadFilter::IDENTITY_COEFS[AudioBiquadFilter::NUM_COEFS] = { AUDIO_COEF_ONE, 0, 0, 0, 0 }; AudioBiquadFilter::AudioBiquadFilter(int nChannels, int sampleRate) { configure(nChannels, sampleRate); reset(); } void AudioBiquadFilter::configure(int nChannels, int sampleRate) { assert(nChannels > 0 && nChannels <= MAX_CHANNELS); assert(sampleRate > 0); mNumChannels = nChannels; mMaxDelta = static_cast(MAX_DELTA_PER_SEC) * AUDIO_COEF_ONE / sampleRate; clear(); } void AudioBiquadFilter::reset() { memcpy(mCoefs, IDENTITY_COEFS, sizeof(mCoefs)); mCoefDirtyBits = 0; setState(STATE_BYPASS); } void AudioBiquadFilter::clear() { memset(mDelays, 0, sizeof(mDelays)); } void AudioBiquadFilter::setCoefs(const audio_coef_t coefs[NUM_COEFS], bool immediate) { memcpy(mTargetCoefs, coefs, sizeof(mTargetCoefs)); if (mState & STATE_ENABLED_MASK) { if (CC_UNLIKELY(immediate)) { memcpy(mCoefs, coefs, sizeof(mCoefs)); setState(STATE_NORMAL); } else { setState(STATE_TRANSITION_TO_NORMAL); } } } void AudioBiquadFilter::process(const audio_sample_t in[], audio_sample_t out[], int frameCount) { (this->*mCurProcessFunc)(in, out, frameCount); } void AudioBiquadFilter::enable(bool immediate) { if (CC_UNLIKELY(immediate)) { memcpy(mCoefs, mTargetCoefs, sizeof(mCoefs)); setState(STATE_NORMAL); } else { setState(STATE_TRANSITION_TO_NORMAL); } } void AudioBiquadFilter::disable(bool immediate) { if (CC_UNLIKELY(immediate)) { memcpy(mCoefs, IDENTITY_COEFS, sizeof(mCoefs)); setState(STATE_BYPASS); } else { setState(STATE_TRANSITION_TO_BYPASS); } } void AudioBiquadFilter::setState(state_t state) { switch (state) { case STATE_BYPASS: mCurProcessFunc = &AudioBiquadFilter::process_bypass; break; case STATE_TRANSITION_TO_BYPASS: if (mNumChannels == 1) { mCurProcessFunc = &AudioBiquadFilter::process_transition_bypass_mono; } else { mCurProcessFunc = &AudioBiquadFilter::process_transition_bypass_multi; } mCoefDirtyBits = (1 << NUM_COEFS) - 1; break; case STATE_TRANSITION_TO_NORMAL: if (mNumChannels == 1) { mCurProcessFunc = &AudioBiquadFilter::process_transition_normal_mono; } else { mCurProcessFunc = &AudioBiquadFilter::process_transition_normal_multi; } mCoefDirtyBits = (1 << NUM_COEFS) - 1; break; case STATE_NORMAL: if (mNumChannels == 1) { mCurProcessFunc = &AudioBiquadFilter::process_normal_mono; } else { mCurProcessFunc = &AudioBiquadFilter::process_normal_multi; } break; } mState = state; } bool AudioBiquadFilter::updateCoefs(const audio_coef_t coefs[NUM_COEFS], int frameCount) { int64_t maxDelta = mMaxDelta * frameCount; for (int i = 0; i < NUM_COEFS; ++i) { if (mCoefDirtyBits & (1< maxDelta) { mCoefs[i] += maxDelta; } else if (diff < -maxDelta) { mCoefs[i] -= maxDelta; } else { mCoefs[i] = coefs[i]; mCoefDirtyBits ^= (1< 0) { audio_sample_t x0 = *(in++); audio_coef_sample_acc_t acc; acc = mul_coef_sample(b0, x0); acc = mac_coef_sample(b1, x1, acc); acc = mac_coef_sample(b2, x2, acc); acc = mac_coef_sample(a1, y1, acc); acc = mac_coef_sample(a2, y2, acc); audio_sample_t y0 = coef_sample_acc_to_sample(acc); y2 = y1; y1 = y0; x2 = x1; x1 = x0; (*out++) = y0; } mDelays[0][0] = x1; mDelays[0][1] = x2; mDelays[0][2] = y1; mDelays[0][3] = y2; } void AudioBiquadFilter::process_transition_normal_mono(const audio_sample_t * in, audio_sample_t * out, int frameCount) { if (updateCoefs(mTargetCoefs, frameCount)) { setState(STATE_NORMAL); } process_normal_mono(in, out, frameCount); } void AudioBiquadFilter::process_transition_bypass_mono(const audio_sample_t * in, audio_sample_t * out, int frameCount) { if (updateCoefs(IDENTITY_COEFS, frameCount)) { setState(STATE_NORMAL); } process_normal_mono(in, out, frameCount); } void AudioBiquadFilter::process_normal_multi(const audio_sample_t * in, audio_sample_t * out, int frameCount) { const audio_coef_t b0 = mCoefs[0]; const audio_coef_t b1 = mCoefs[1]; const audio_coef_t b2 = mCoefs[2]; const audio_coef_t a1 = mCoefs[3]; const audio_coef_t a2 = mCoefs[4]; for (int ch = 0; ch < mNumChannels; ++ch) { size_t nFrames = frameCount; audio_sample_t x1 = mDelays[ch][0]; audio_sample_t x2 = mDelays[ch][1]; audio_sample_t y1 = mDelays[ch][2]; audio_sample_t y2 = mDelays[ch][3]; while (nFrames-- > 0) { audio_sample_t x0 = *in; audio_coef_sample_acc_t acc; acc = mul_coef_sample(b0, x0); acc = mac_coef_sample(b1, x1, acc); acc = mac_coef_sample(b2, x2, acc); acc = mac_coef_sample(a1, y1, acc); acc = mac_coef_sample(a2, y2, acc); audio_sample_t y0 = coef_sample_acc_to_sample(acc); y2 = y1; y1 = y0; x2 = x1; x1 = x0; *out = y0; in += mNumChannels; out += mNumChannels; } mDelays[ch][0] = x1; mDelays[ch][1] = x2; mDelays[ch][2] = y1; mDelays[ch][3] = y2; in -= frameCount * mNumChannels - 1; out -= frameCount * mNumChannels - 1; } } void AudioBiquadFilter::process_transition_normal_multi(const audio_sample_t * in, audio_sample_t * out, int frameCount) { if (updateCoefs(mTargetCoefs, frameCount)) { setState(STATE_NORMAL); } process_normal_multi(in, out, frameCount); } void AudioBiquadFilter::process_transition_bypass_multi(const audio_sample_t * in, audio_sample_t * out, int frameCount) { if (updateCoefs(IDENTITY_COEFS, frameCount)) { setState(STATE_NORMAL); } process_normal_multi(in, out, frameCount); } }