From 2c8e5cab3faa6d360e222b7a6c40a80083d021ac Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 9 Jul 2010 12:28:50 -0700 Subject: First submission of audio effect library from NXP software. This CL contains the first open sourceable version of the audio effect library from NXP software. The effects implemented are: - Bass boost - Virtualizer (stereo widening) - Equalizer - Spectrum analyzer Source file for the effect engines are located under libeffects/lvm/lib The wrapper implementing the interface with the audio effect framework in under libeffects/lvm/wrapper The code of other effect libraries has also been reorganized fo clarity: - the effect factory is now under libeffects/factory - the test equalizer and reverb effects are under libeffect/testlibs - the visualizer is under libeffects/virtualizer Change-Id: I8d91e2181f81b89f8fc0c1e1e6bf552c5809b2eb --- media/libeffects/testlibs/AudioBiquadFilter.cpp | 260 ++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 media/libeffects/testlibs/AudioBiquadFilter.cpp (limited to 'media/libeffects/testlibs/AudioBiquadFilter.cpp') diff --git a/media/libeffects/testlibs/AudioBiquadFilter.cpp b/media/libeffects/testlibs/AudioBiquadFilter.cpp new file mode 100644 index 0000000..72917a3 --- /dev/null +++ b/media/libeffects/testlibs/AudioBiquadFilter.cpp @@ -0,0 +1,260 @@ +/* //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 "AudioBiquadFilter.h" + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +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 (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 (UNLIKELY(immediate)) { + memcpy(mCoefs, mTargetCoefs, sizeof(mCoefs)); + setState(STATE_NORMAL); + } else { + setState(STATE_TRANSITION_TO_NORMAL); + } +} + +void AudioBiquadFilter::disable(bool immediate) { + if (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); +} + +} -- cgit v1.1