summaryrefslogtreecommitdiffstats
path: root/media/libeffects/testlibs/AudioBiquadFilter.cpp
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2010-07-09 12:28:50 -0700
committerEric Laurent <elaurent@google.com>2010-07-17 06:33:00 -0700
commit2c8e5cab3faa6d360e222b7a6c40a80083d021ac (patch)
treefd19b8baa829edb78116b089d1122ea4ef0921e1 /media/libeffects/testlibs/AudioBiquadFilter.cpp
parentada2ac8e09b6d3f2b3c3155a852ba0fffae1b592 (diff)
downloadframeworks_av-2c8e5cab3faa6d360e222b7a6c40a80083d021ac.zip
frameworks_av-2c8e5cab3faa6d360e222b7a6c40a80083d021ac.tar.gz
frameworks_av-2c8e5cab3faa6d360e222b7a6c40a80083d021ac.tar.bz2
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
Diffstat (limited to 'media/libeffects/testlibs/AudioBiquadFilter.cpp')
-rw-r--r--media/libeffects/testlibs/AudioBiquadFilter.cpp260
1 files changed, 260 insertions, 0 deletions
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 <string.h>
+#include <assert.h>
+
+#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<int64_t>(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<<i)) {
+ audio_coef_t diff = coefs[i] - mCoefs[i];
+ if (diff > maxDelta) {
+ mCoefs[i] += maxDelta;
+ } else if (diff < -maxDelta) {
+ mCoefs[i] -= maxDelta;
+ } else {
+ mCoefs[i] = coefs[i];
+ mCoefDirtyBits ^= (1<<i);
+ }
+ }
+ }
+ return mCoefDirtyBits == 0;
+}
+
+void AudioBiquadFilter::process_bypass(const audio_sample_t * in,
+ audio_sample_t * out,
+ int frameCount) {
+ // The common case is in-place processing, because this is what the EQ does.
+ if (UNLIKELY(in != out)) {
+ memcpy(out, in, frameCount * mNumChannels * sizeof(audio_sample_t));
+ }
+}
+
+void AudioBiquadFilter::process_normal_mono(const audio_sample_t * in,
+ audio_sample_t * out,
+ int frameCount) {
+ size_t nFrames = frameCount;
+ audio_sample_t x1 = mDelays[0][0];
+ audio_sample_t x2 = mDelays[0][1];
+ audio_sample_t y1 = mDelays[0][2];
+ audio_sample_t y2 = mDelays[0][3];
+ 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];
+ 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;
+ }
+ 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);
+}
+
+}