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/Android.mk_ | 66 + media/libeffects/testlibs/AudioBiquadFilter.cpp | 260 ++ media/libeffects/testlibs/AudioBiquadFilter.h | 180 ++ .../libeffects/testlibs/AudioCoefInterpolator.cpp | 84 + media/libeffects/testlibs/AudioCoefInterpolator.h | 98 + media/libeffects/testlibs/AudioCommon.h | 92 + media/libeffects/testlibs/AudioEqualizer.cpp | 315 +++ media/libeffects/testlibs/AudioEqualizer.h | 246 ++ media/libeffects/testlibs/AudioFormatAdapter.h | 184 ++ .../testlibs/AudioHighShelfFilterCoef.inl | 225 ++ .../testlibs/AudioLowShelfFilterCoef.inl | 375 +++ media/libeffects/testlibs/AudioPeakingFilter.cpp | 121 + media/libeffects/testlibs/AudioPeakingFilter.h | 151 ++ .../libeffects/testlibs/AudioPeakingFilterCoef.inl | 2700 ++++++++++++++++++++ media/libeffects/testlibs/AudioShelvingFilter.cpp | 109 + media/libeffects/testlibs/AudioShelvingFilter.h | 146 ++ media/libeffects/testlibs/EffectEqualizer.cpp | 654 +++++ media/libeffects/testlibs/EffectReverb.c | 2135 ++++++++++++++++ media/libeffects/testlibs/EffectReverb.h | 437 ++++ media/libeffects/testlibs/EffectsMath.c | 143 ++ media/libeffects/testlibs/EffectsMath.h | 424 +++ 21 files changed, 9145 insertions(+) create mode 100644 media/libeffects/testlibs/Android.mk_ create mode 100644 media/libeffects/testlibs/AudioBiquadFilter.cpp create mode 100644 media/libeffects/testlibs/AudioBiquadFilter.h create mode 100644 media/libeffects/testlibs/AudioCoefInterpolator.cpp create mode 100644 media/libeffects/testlibs/AudioCoefInterpolator.h create mode 100644 media/libeffects/testlibs/AudioCommon.h create mode 100644 media/libeffects/testlibs/AudioEqualizer.cpp create mode 100644 media/libeffects/testlibs/AudioEqualizer.h create mode 100644 media/libeffects/testlibs/AudioFormatAdapter.h create mode 100644 media/libeffects/testlibs/AudioHighShelfFilterCoef.inl create mode 100644 media/libeffects/testlibs/AudioLowShelfFilterCoef.inl create mode 100644 media/libeffects/testlibs/AudioPeakingFilter.cpp create mode 100644 media/libeffects/testlibs/AudioPeakingFilter.h create mode 100644 media/libeffects/testlibs/AudioPeakingFilterCoef.inl create mode 100644 media/libeffects/testlibs/AudioShelvingFilter.cpp create mode 100644 media/libeffects/testlibs/AudioShelvingFilter.h create mode 100644 media/libeffects/testlibs/EffectEqualizer.cpp create mode 100644 media/libeffects/testlibs/EffectReverb.c create mode 100644 media/libeffects/testlibs/EffectReverb.h create mode 100644 media/libeffects/testlibs/EffectsMath.c create mode 100644 media/libeffects/testlibs/EffectsMath.h (limited to 'media/libeffects/testlibs') diff --git a/media/libeffects/testlibs/Android.mk_ b/media/libeffects/testlibs/Android.mk_ new file mode 100644 index 0000000..9ba71ed --- /dev/null +++ b/media/libeffects/testlibs/Android.mk_ @@ -0,0 +1,66 @@ +LOCAL_PATH:= $(call my-dir) + +# Test Reverb library +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + EffectReverb.c.arm \ + EffectsMath.c.arm +LOCAL_CFLAGS+= -O2 + +LOCAL_SHARED_LIBRARIES := \ + libcutils + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE:= libreverbtest + +ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) +LOCAL_LDLIBS += -ldl +endif + +ifneq ($(TARGET_SIMULATOR),true) +LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_C_INCLUDES := \ + $(call include-path-for, graphics corecg) + +LOCAL_PRELINK_MODULE := false + +include $(BUILD_SHARED_LIBRARY) + +# Test Equalizer library +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + EffectsMath.c.arm \ + EffectEqualizer.cpp \ + AudioBiquadFilter.cpp.arm \ + AudioCoefInterpolator.cpp.arm \ + AudioPeakingFilter.cpp.arm \ + AudioShelvingFilter.cpp.arm \ + AudioEqualizer.cpp.arm + +LOCAL_CFLAGS+= -O2 + +LOCAL_SHARED_LIBRARIES := \ + libcutils + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx +LOCAL_MODULE:= libequalizertest + +ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true) +LOCAL_LDLIBS += -ldl +endif + +ifneq ($(TARGET_SIMULATOR),true) +LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_C_INCLUDES := \ + $(call include-path-for, graphics corecg) + +LOCAL_PRELINK_MODULE := false + +include $(BUILD_SHARED_LIBRARY) + 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); +} + +} diff --git a/media/libeffects/testlibs/AudioBiquadFilter.h b/media/libeffects/testlibs/AudioBiquadFilter.h new file mode 100644 index 0000000..2b0e2d6 --- /dev/null +++ b/media/libeffects/testlibs/AudioBiquadFilter.h @@ -0,0 +1,180 @@ +/* //device/include/server/AudioFlinger/AudioBiquadFilter.h +** +** Copyright 2007, 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. +*/ + +#ifndef ANDROID_AUDIO_BIQUAD_FILTER_H +#define ANDROID_AUDIO_BIQUAD_FILTER_H + +#include "AudioCommon.h" + +namespace android { +// A biquad filter. +// Implements the recursion y[n]=a0*y[n-1]+a1*y[n-2]+b0*x[n]+b1*x[n-1]+b2*x[n-2] +// (the a0 and a1 coefficients have an opposite sign to the common convention) +// The filter works on fixed sized blocks of data (frameCount multi-channel +// samples, as defined during construction). An arbitrary number of interlaced +// channels is supported. +// Filter can operate in an enabled (active) or disabled (bypassed) states. +// A mechanism for suppression of artifacts caused by abrupt coefficient changes +// is implemented: normally, when the enable(), disable() and setCoefs() methods +// are called without the immediate flag set, the filter smoothly transitions +// from its current state to the desired state. +class AudioBiquadFilter { +public: + // Max number of channels (can be changed here, and everything should work). + static const int MAX_CHANNELS = 2; + // Number of coefficients. + static const int NUM_COEFS = 5; + + // Constructor. + // nChannels Number of input/output channels. + // sampleRate Sample rate, in Hz. + AudioBiquadFilter(int nChannels, int sampleRate); + + // Reconfiguration of the filter. Implies clear(). + // nChannels Number of input/output channels. + // sampleRate Sample rate, in Hz. + void configure(int nChannels, int sampleRate); + + // Resets the internal state of the filter. + // Coefficients are reset to identity, state becomes disabled. This change + // happens immediately and might cause discontinuities in the output. + // Delay lines are not cleared. + void reset(); + + // Clears the delay lines. + // This change happens immediately and might cause discontinuities in the + // output. + void clear(); + + // Sets the coefficients. + // If called when filter is disabled, will have no immediate effect, but the + // new coefficients will be set and used next time the filter is enabled. + // coefs The new coefficients. + // immediate If true, transitions to new coefficients smoothly, without + // introducing discontinuities in the output. Otherwise, + // transitions immediately. + void setCoefs(const audio_coef_t coefs[NUM_COEFS], bool immediate = false); + + // Process a buffer of data. Always processes frameCount multi-channel + // samples. Processing can be done in-place, by passing the same buffer as + // both arguments. + // in The input buffer. Should be of size frameCount * nChannels. + // out The output buffer. Should be of size frameCount * nChannels. + // frameCount Number of multi-channel samples to process. + void process(const audio_sample_t in[], audio_sample_t out[], + int frameCount); + + // Enables (activates) the filter. + // immediate If true, transitions to new state smoothly, without + // introducing discontinuities in the output. Otherwise, + // transitions immediately. + void enable(bool immediate = false); + + // Disables (bypasses) the filter. + // immediate If true, transitions to new state smoothly, without + // introducing discontinuities in the output. Otherwise, + // transitions immediately. + void disable(bool immediate = false); + +private: + // A prototype of the actual processing function. Has the same semantics as + // the process() method. + typedef void (AudioBiquadFilter::*process_func)(const audio_sample_t[], + audio_sample_t[], + int frameCount); + + // The maximum rate of coefficient change, measured in coefficient units per + // second. + static const audio_coef_t MAX_DELTA_PER_SEC = 2000; + + // Coefficients of identity transformation. + static const audio_coef_t IDENTITY_COEFS[NUM_COEFS]; + + // Filter state. + enum state_t { + // Bypass. + STATE_BYPASS = 0x01, + // In the process of smooth transition to bypass state. + STATE_TRANSITION_TO_BYPASS = 0x02, + // In the process of smooth transition to normal (enabled) state. + STATE_TRANSITION_TO_NORMAL = 0x04, + // In normal (enabled) state. + STATE_NORMAL = 0x05, + // A bit-mask for determining whether the filter is enabled or disabled + // in the eyes of the client. + STATE_ENABLED_MASK = 0x04 + }; + + // Number of channels. + int mNumChannels; + // Current state. + state_t mState; + // Maximum coefficient delta per sample. + audio_coef_t mMaxDelta; + + // A bit-mask designating for which coefficients the current value is not + // necessarily identical to the target value (since we're in transition + // state). + uint32_t mCoefDirtyBits; + // The current coefficients. + audio_coef_t mCoefs[NUM_COEFS]; + // The target coefficients. Will not be identical to mCoefs if we are in a + // transition state. + audio_coef_t mTargetCoefs[NUM_COEFS]; + + // The delay lines. + audio_sample_t mDelays[MAX_CHANNELS][4]; + + // Current processing function (determines according to current state and + // number of channels). + process_func mCurProcessFunc; + + // Sets a new state. Updates the processing function accordingly, and sets + // the dirty bits if changing to a transition state. + void setState(state_t state); + + // In a transition state, modifies the current coefs towards the passed + // coefs, while keeping a smooth change rate. Whenever a coef reaches its + // target value, the dirty bit is cleared. If all are clear, the function + // returns true, and we can then change to our target state. + bool updateCoefs(const audio_coef_t coefs[NUM_COEFS], int frameCount); + + // Processing function when in disabled state. + void process_bypass(const audio_sample_t * in, audio_sample_t * out, + int frameCount); + // Processing function when in normal state, mono. + void process_normal_mono(const audio_sample_t * in, audio_sample_t * out, + int frameCount); + // Processing function when transitioning to normal state, mono. + void process_transition_normal_mono(const audio_sample_t * in, + audio_sample_t * out, int frameCount); + // Processing function when transitioning to bypass state, mono. + void process_transition_bypass_mono(const audio_sample_t * in, + audio_sample_t * out, int frameCount); + // Processing function when in normal state, multi-channel. + void process_normal_multi(const audio_sample_t * in, audio_sample_t * out, + int frameCount); + // Processing function when transitioning to normal state, multi-channel. + void process_transition_normal_multi(const audio_sample_t * in, + audio_sample_t * out, int frameCount); + // Processing function when transitioning to bypass state, multi-channel. + void process_transition_bypass_multi(const audio_sample_t * in, + audio_sample_t * out, int frameCount); +}; +} + +#endif // ANDROID_AUDIO_BIQUAD_FILTER_H diff --git a/media/libeffects/testlibs/AudioCoefInterpolator.cpp b/media/libeffects/testlibs/AudioCoefInterpolator.cpp new file mode 100644 index 0000000..039ab9f --- /dev/null +++ b/media/libeffects/testlibs/AudioCoefInterpolator.cpp @@ -0,0 +1,84 @@ +/* //device/servers/AudioFlinger/AudioCoefInterpolator.cpp + ** + ** Copyright 2008, 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 "AudioCoefInterpolator.h" + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { + +AudioCoefInterpolator::AudioCoefInterpolator(size_t nInDims, + const size_t inDims[], + size_t nOutDims, + const audio_coef_t * table) { + mNumInDims = nInDims; + memcpy(mInDims, inDims, nInDims * sizeof(size_t)); + mNumOutDims = nOutDims; + mTable = table; + // Initialize offsets array + size_t dim = nInDims - 1; + mInDimOffsets[nInDims - 1] = nOutDims; + while (dim-- > 0) { + mInDimOffsets[dim] = mInDimOffsets[dim + 1] * mInDims[dim + 1]; + } +} + +void AudioCoefInterpolator::getCoef(const int intCoord[], uint32_t fracCoord[], + audio_coef_t out[]) { + size_t index = 0; + size_t dim = mNumInDims; + while (dim-- > 0) { + if (UNLIKELY(intCoord[dim] < 0)) { + fracCoord[dim] = 0; + } else if (UNLIKELY(intCoord[dim] >= (int)mInDims[dim] - 1)) { + fracCoord[dim] = 0; + index += mInDimOffsets[dim] * (mInDims[dim] - 1); + } else { + index += mInDimOffsets[dim] * intCoord[dim]; + } + } + getCoefRecurse(index, fracCoord, out, 0); +} + +void AudioCoefInterpolator::getCoefRecurse(size_t index, + const uint32_t fracCoord[], + audio_coef_t out[], size_t dim) { + if (dim == mNumInDims) { + memcpy(out, mTable + index, mNumOutDims * sizeof(audio_coef_t)); + } else { + getCoefRecurse(index, fracCoord, out, dim + 1); + if (LIKELY(fracCoord != 0)) { + audio_coef_t tempCoef[MAX_OUT_DIMS]; + getCoefRecurse(index + mInDimOffsets[dim], fracCoord, tempCoef, + dim + 1); + size_t d = mNumOutDims; + while (d-- > 0) { + out[d] = interp(out[d], tempCoef[d], fracCoord[dim]); + } + } + } +} + +audio_coef_t AudioCoefInterpolator::interp(audio_coef_t lo, audio_coef_t hi, + uint32_t frac) { + int64_t delta = static_cast(hi-lo) * frac; + return lo + static_cast (delta >> 32); +} + +} diff --git a/media/libeffects/testlibs/AudioCoefInterpolator.h b/media/libeffects/testlibs/AudioCoefInterpolator.h new file mode 100644 index 0000000..13e5697 --- /dev/null +++ b/media/libeffects/testlibs/AudioCoefInterpolator.h @@ -0,0 +1,98 @@ +/* //device/include/server/AudioFlinger/AudioCoefInterpolator.h + ** + ** Copyright 2007, 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. + */ + +#ifndef ANDROID_AUDIO_COEF_INTERPOLATOR_H +#define ANDROID_AUDIO_COEF_INTERPOLATOR_H + +#include "AudioCommon.h" + +namespace android { + +// A helper class for linear interpolation of N-D -> M-D coefficient tables. +// This class provides support for out-of-range indexes. +// Details: +// The purpose is efficient approximation of a N-dimensional vector to +// M-dimensional function. The approximation is based on a table of output +// values on a uniform grid of the input values. Values not on the grid are +// linearly interpolated. +// Access to values are done by specifying input values in table index units, +// having an integer and a fractional part, e.g. retrieving a value from index +// 1.4 will result in linear interpolation between index 1 and index 2. +class AudioCoefInterpolator { +public: + // Constructor. + // nInDims Number of input dimensions (limited to MAX_IN_DIMS). + // inDims An array of size nInDims with the size of the table on each + // respective dimension. + // nOutDims Number of output dimensions (limited to MAX_OUT_DIMS). + // table The coefficient table. Should be of size: + // inDims[0]*inDims[1]*...*inDims[nInDims-1]*nOutDims, where + // func([i,j,k]) = table(i,j,k,:) + AudioCoefInterpolator(size_t nInDims, const size_t inDims[], + size_t nOutDims, const audio_coef_t * table); + + // Get the value of the approximated function at a given point. + // intCoord The integer part of the input value. Should be an array of + // size nInDims. + // fracCoord The fractional part of the input value. Should be an array + // of size nInDims. This value is in 32-bit precision. + // out An array for the output value. Should be of size nOutDims. + void getCoef(const int intCoord[], uint32_t fracCoord[], audio_coef_t out[]); + +private: + // Maximum allowed number of input dimensions. + static const size_t MAX_IN_DIMS = 8; + // Maximum allowed number of output dimensions. + static const size_t MAX_OUT_DIMS = 8; + + // Number of input dimensions. + size_t mNumInDims; + // Number of input dimensions. + size_t mInDims[MAX_IN_DIMS]; + // The offset between two consecutive indexes of each dimension. This is in + // fact a cumulative product of mInDims (done in reverse). + size_t mInDimOffsets[MAX_IN_DIMS]; + // Number of output dimensions. + size_t mNumOutDims; + // The coefficient table. + const audio_coef_t * mTable; + + // A recursive function for getting an interpolated coefficient value. + // The recursion depth is the number of input dimensions. + // At each step, we fetch two interpolated values of the current dimension, + // by two recursive calls to this method for the next dimensions. We then + // linearly interpolate these values over the current dimension. + // index The linear integer index of the value we need to interpolate. + // fracCoord A vector of fractional coordinates for each of the input + // dimensions. + // out Where the output should be written. Needs to be of size + // mNumOutDims. + // dim The input dimensions we are currently interpolating. This + // value will be increased on recursive calls. + void getCoefRecurse(size_t index, const uint32_t fracCoord[], + audio_coef_t out[], size_t dim); + + // Scalar interpolation of two data points. + // lo The first data point. + // hi The second data point. + // frac A 32-bit fraction designating the weight of the second point. + static audio_coef_t interp(audio_coef_t lo, audio_coef_t hi, uint32_t frac); +}; + +} + +#endif // ANDROID_AUDIO_COEF_INTERPOLATOR_H diff --git a/media/libeffects/testlibs/AudioCommon.h b/media/libeffects/testlibs/AudioCommon.h new file mode 100644 index 0000000..444f93a --- /dev/null +++ b/media/libeffects/testlibs/AudioCommon.h @@ -0,0 +1,92 @@ +/* +** +** 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. +*/ + +#ifndef ANDROID_AUDIO_COMMON_H +#define ANDROID_AUDIO_COMMON_H + +#include +#include + +namespace android { + +// Audio coefficient type. +typedef int32_t audio_coef_t; +// Audio sample type. +typedef int32_t audio_sample_t; +// Accumulator type for coef x sample. +typedef int64_t audio_coef_sample_acc_t; + +// Number of fraction bits for audio coefficient. +static const int AUDIO_COEF_PRECISION = 24; +// Audio coefficient with the value of 1.0 +static const audio_coef_t AUDIO_COEF_ONE = 1 << AUDIO_COEF_PRECISION; +// Audio coefficient with the value of 0.5 +static const audio_coef_t AUDIO_COEF_HALF = 1 << (AUDIO_COEF_PRECISION - 1); +// Number of fraction bits for audio sample. +static const int AUDIO_SAMPLE_PRECISION = 24; +// Audio sample with the value of 1.0 +static const audio_sample_t AUDIO_SAMPLE_ONE = 1 << AUDIO_SAMPLE_PRECISION; + +// TODO: These are just temporary naive implementations of the necessary +// arithmetic operations needed for the filter. They should be moved to a more +// generic location and implemented more efficiently. + +// Multiply a sample by a coefficient to return an accumulator. +inline audio_coef_sample_acc_t mul_coef_sample(audio_coef_t x, audio_sample_t y) { + return ((audio_coef_sample_acc_t) (x)) * y; +} + +// Multiply and accumulate sample by a coefficient to return an accumulator. +inline audio_coef_sample_acc_t mac_coef_sample(audio_coef_t x, audio_sample_t y, audio_coef_sample_acc_t acc) { + return acc + ((audio_coef_sample_acc_t) (x)) * y; +} + +// Convert a sample-coefficient accumulator to a sample. +inline audio_sample_t coef_sample_acc_to_sample(audio_coef_sample_acc_t acc) { + if (acc < 0) { + acc += AUDIO_COEF_ONE - 1; + } + return (audio_sample_t) (acc >> AUDIO_COEF_PRECISION); +} + +// Convert a S15 sample to audio_sample_t +inline audio_sample_t s15_to_audio_sample_t(int16_t s15) { + return audio_sample_t(s15) << 9; +} + +// Convert a audio_sample_t sample to S15 (no clipping) +inline int16_t audio_sample_t_to_s15(audio_sample_t sample) { + return int16_t((sample + (1 << 8)) >> 9); +} + +// Convert a audio_sample_t sample to S15 (with clipping) +inline int16_t audio_sample_t_to_s15_clip(audio_sample_t sample) { + // TODO: optimize for targets supporting this as an atomic operation. + if (__builtin_expect(sample >= (0x7FFF << 9), 0)) { + return 0x7FFF; + } else if (__builtin_expect(sample <= -(0x8000 << 9), 0)) { + return 0x8000; + } else { + return audio_sample_t_to_s15(sample); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +} + +#endif // ANDROID_AUDIO_COMMON_H diff --git a/media/libeffects/testlibs/AudioEqualizer.cpp b/media/libeffects/testlibs/AudioEqualizer.cpp new file mode 100644 index 0000000..44c9476 --- /dev/null +++ b/media/libeffects/testlibs/AudioEqualizer.cpp @@ -0,0 +1,315 @@ +/* + * 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. + */ + +#define LOG_TAG "AudioEqualizer" + +#include +#include +#include +#include + +#include "AudioEqualizer.h" +#include "AudioPeakingFilter.h" +#include "AudioShelvingFilter.h" +#include "EffectsMath.h" + +namespace android { + +size_t AudioEqualizer::GetInstanceSize(int nBands) { + assert(nBands >= 2); + return sizeof(AudioEqualizer) + + sizeof(AudioShelvingFilter) * 2 + + sizeof(AudioPeakingFilter) * (nBands - 2); +} + +AudioEqualizer * AudioEqualizer::CreateInstance(void * pMem, int nBands, + int nChannels, int sampleRate, + const PresetConfig * presets, + int nPresets) { + LOGV("AudioEqualizer::CreateInstance(pMem=%p, nBands=%d, nChannels=%d, " + "sampleRate=%d, nPresets=%d)", + pMem, nBands, nChannels, sampleRate, nPresets); + assert(nBands >= 2); + bool ownMem = false; + if (pMem == NULL) { + pMem = malloc(GetInstanceSize(nBands)); + if (pMem == NULL) { + return NULL; + } + ownMem = true; + } + return new (pMem) AudioEqualizer(pMem, nBands, nChannels, sampleRate, + ownMem, presets, nPresets); +} + +void AudioEqualizer::configure(int nChannels, int sampleRate) { + LOGV("AudioEqualizer::configure(nChannels=%d, sampleRate=%d)", nChannels, + sampleRate); + mpLowShelf->configure(nChannels, sampleRate); + for (int i = 0; i < mNumPeaking; ++i) { + mpPeakingFilters[i].configure(nChannels, sampleRate); + } + mpHighShelf->configure(nChannels, sampleRate); +} + +void AudioEqualizer::clear() { + LOGV("AudioEqualizer::clear()"); + mpLowShelf->clear(); + for (int i = 0; i < mNumPeaking; ++i) { + mpPeakingFilters[i].clear(); + } + mpHighShelf->clear(); +} + +void AudioEqualizer::free() { + LOGV("AudioEqualizer::free()"); + if (mpMem != NULL) { + ::free(mpMem); + } +} + +void AudioEqualizer::reset() { + LOGV("AudioEqualizer::reset()"); + const int32_t bottom = Effects_log2(kMinFreq); + const int32_t top = Effects_log2(mSampleRate * 500); + const int32_t jump = (top - bottom) / (mNumPeaking + 2); + int32_t centerFreq = bottom + jump/2; + + mpLowShelf->reset(); + mpLowShelf->setFrequency(Effects_exp2(centerFreq)); + centerFreq += jump; + for (int i = 0; i < mNumPeaking; ++i) { + mpPeakingFilters[i].reset(); + mpPeakingFilters[i].setFrequency(Effects_exp2(centerFreq)); + centerFreq += jump; + } + mpHighShelf->reset(); + mpHighShelf->setFrequency(Effects_exp2(centerFreq)); + commit(true); + mCurPreset = PRESET_CUSTOM; +} + +void AudioEqualizer::setGain(int band, int32_t millibel) { + LOGV("AudioEqualizer::setGain(band=%d, millibel=%d)", band, millibel); + assert(band >= 0 && band < mNumPeaking + 2); + if (band == 0) { + mpLowShelf->setGain(millibel); + } else if (band == mNumPeaking + 1) { + mpHighShelf->setGain(millibel); + } else { + mpPeakingFilters[band - 1].setGain(millibel); + } + mCurPreset = PRESET_CUSTOM; +} + +void AudioEqualizer::setFrequency(int band, uint32_t millihertz) { + LOGV("AudioEqualizer::setFrequency(band=%d, millihertz=%d)", band, + millihertz); + assert(band >= 0 && band < mNumPeaking + 2); + if (band == 0) { + mpLowShelf->setFrequency(millihertz); + } else if (band == mNumPeaking + 1) { + mpHighShelf->setFrequency(millihertz); + } else { + mpPeakingFilters[band - 1].setFrequency(millihertz); + } + mCurPreset = PRESET_CUSTOM; +} + +void AudioEqualizer::setBandwidth(int band, uint32_t cents) { + LOGV("AudioEqualizer::setBandwidth(band=%d, cents=%d)", band, cents); + assert(band >= 0 && band < mNumPeaking + 2); + if (band > 0 && band < mNumPeaking + 1) { + mpPeakingFilters[band - 1].setBandwidth(cents); + mCurPreset = PRESET_CUSTOM; + } +} + +int32_t AudioEqualizer::getGain(int band) const { + assert(band >= 0 && band < mNumPeaking + 2); + if (band == 0) { + return mpLowShelf->getGain(); + } else if (band == mNumPeaking + 1) { + return mpHighShelf->getGain(); + } else { + return mpPeakingFilters[band - 1].getGain(); + } +} + +uint32_t AudioEqualizer::getFrequency(int band) const { + assert(band >= 0 && band < mNumPeaking + 2); + if (band == 0) { + return mpLowShelf->getFrequency(); + } else if (band == mNumPeaking + 1) { + return mpHighShelf->getFrequency(); + } else { + return mpPeakingFilters[band - 1].getFrequency(); + } +} + +uint32_t AudioEqualizer::getBandwidth(int band) const { + assert(band >= 0 && band < mNumPeaking + 2); + if (band == 0 || band == mNumPeaking + 1) { + return 0; + } else { + return mpPeakingFilters[band - 1].getBandwidth(); + } +} + +void AudioEqualizer::getBandRange(int band, uint32_t & low, + uint32_t & high) const { + assert(band >= 0 && band < mNumPeaking + 2); + if (band == 0) { + low = 0; + high = mpLowShelf->getFrequency(); + } else if (band == mNumPeaking + 1) { + low = mpHighShelf->getFrequency(); + high = mSampleRate * 500; + } else { + mpPeakingFilters[band - 1].getBandRange(low, high); + } +} + +const char * AudioEqualizer::getPresetName(int preset) const { + assert(preset < mNumPresets && preset >= PRESET_CUSTOM); + if (preset == PRESET_CUSTOM) { + return "Custom"; + } else { + return mpPresets[preset].name; + } +} + +int AudioEqualizer::getNumPresets() const { + return mNumPresets; +} + +int AudioEqualizer::getPreset() const { + return mCurPreset; +} + +void AudioEqualizer::setPreset(int preset) { + LOGV("AudioEqualizer::setPreset(preset=%d)", preset); + assert(preset < mNumPresets && preset >= 0); + const PresetConfig &presetCfg = mpPresets[preset]; + for (int band = 0; band < (mNumPeaking + 2); ++band) { + const BandConfig & bandCfg = presetCfg.bandConfigs[band]; + setGain(band, bandCfg.gain); + setFrequency(band, bandCfg.freq); + setBandwidth(band, bandCfg.bandwidth); + } + mCurPreset = preset; +} + +void AudioEqualizer::commit(bool immediate) { + LOGV("AudioEqualizer::commit(immediate=%d)", immediate); + mpLowShelf->commit(immediate); + for (int i = 0; i < mNumPeaking; ++i) { + mpPeakingFilters[i].commit(immediate); + } + mpHighShelf->commit(immediate); +} + +void AudioEqualizer::process(const audio_sample_t * pIn, + audio_sample_t * pOut, + int frameCount) { +// LOGV("AudioEqualizer::process(frameCount=%d)", frameCount); + mpLowShelf->process(pIn, pOut, frameCount); + for (int i = 0; i < mNumPeaking; ++i) { + mpPeakingFilters[i].process(pIn, pOut, frameCount); + } + mpHighShelf->process(pIn, pOut, frameCount); +} + +void AudioEqualizer::enable(bool immediate) { + LOGV("AudioEqualizer::enable(immediate=%d)", immediate); + mpLowShelf->enable(immediate); + for (int i = 0; i < mNumPeaking; ++i) { + mpPeakingFilters[i].enable(immediate); + } + mpHighShelf->enable(immediate); +} + +void AudioEqualizer::disable(bool immediate) { + LOGV("AudioEqualizer::disable(immediate=%d)", immediate); + mpLowShelf->disable(immediate); + for (int i = 0; i < mNumPeaking; ++i) { + mpPeakingFilters[i].disable(immediate); + } + mpHighShelf->disable(immediate); +} + +int AudioEqualizer::getMostRelevantBand(uint32_t targetFreq) const { + // First, find the two bands that the target frequency is between. + uint32_t low = mpLowShelf->getFrequency(); + if (targetFreq <= low) { + return 0; + } + uint32_t high = mpHighShelf->getFrequency(); + if (targetFreq >= high) { + return mNumPeaking + 1; + } + int band = mNumPeaking; + for (int i = 0; i < mNumPeaking; ++i) { + uint32_t freq = mpPeakingFilters[i].getFrequency(); + if (freq >= targetFreq) { + high = freq; + band = i; + break; + } + low = freq; + } + // Now, low is right below the target and high is right above. See which one + // is closer on a log scale. + low = Effects_log2(low); + high = Effects_log2(high); + targetFreq = Effects_log2(targetFreq); + if (high - targetFreq < targetFreq - low) { + return band + 1; + } else { + return band; + } +} + + +AudioEqualizer::AudioEqualizer(void * pMem, int nBands, int nChannels, + int sampleRate, bool ownMem, + const PresetConfig * presets, int nPresets) + : mSampleRate(sampleRate) + , mpPresets(presets) + , mNumPresets(nPresets) { + assert(pMem != NULL); + assert(nPresets == 0 || nPresets > 0 && presets != NULL); + mpMem = ownMem ? pMem : NULL; + + pMem = (char *) pMem + sizeof(AudioEqualizer); + mpLowShelf = new (pMem) AudioShelvingFilter(AudioShelvingFilter::kLowShelf, + nChannels, sampleRate); + pMem = (char *) pMem + sizeof(AudioShelvingFilter); + mpHighShelf = new (pMem) AudioShelvingFilter(AudioShelvingFilter::kHighShelf, + nChannels, sampleRate); + pMem = (char *) pMem + sizeof(AudioShelvingFilter); + mNumPeaking = nBands - 2; + if (mNumPeaking > 0) { + mpPeakingFilters = reinterpret_cast(pMem); + for (int i = 0; i < mNumPeaking; ++i) { + new (&mpPeakingFilters[i]) AudioPeakingFilter(nChannels, + sampleRate); + } + } + reset(); +} + +} diff --git a/media/libeffects/testlibs/AudioEqualizer.h b/media/libeffects/testlibs/AudioEqualizer.h new file mode 100644 index 0000000..4028462 --- /dev/null +++ b/media/libeffects/testlibs/AudioEqualizer.h @@ -0,0 +1,246 @@ +/* + * 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. + */ + +#ifndef AUDIOEQUALIZER_H_ +#define AUDIOEQUALIZER_H_ + +#include "AudioCommon.h" + +namespace android { + +class AudioShelvingFilter; +class AudioPeakingFilter; + +// A parametric audio equalizer. Supports an arbitrary number of bands and +// presets. +// The EQ is composed of a low-shelf, zero or more peaking filters and a high +// shelf, where each band has frequency and gain controls, and the peaking +// filters have an additional bandwidth control. +class AudioEqualizer { +public: + // Configuration of a single band. + struct BandConfig { + // Gain in millibel. + int32_t gain; + // Frequency in millihertz. + uint32_t freq; + // Bandwidth in cents (ignored on shelving filters). + uint32_t bandwidth; + }; + + // Preset configuration. + struct PresetConfig { + // Human-readable name. + const char * name; + // An array of size nBands where each element is a configuration for the + // corresponding band. + const BandConfig * bandConfigs; + }; + + // This value is used when requesting current preset, and EQ is not using a + // preset. + static const int PRESET_CUSTOM = -1; + + // Get the required memory size for an instance of this class. + // nBands Number of bands required in the instance. + static size_t GetInstanceSize(int nBands); + + // Create an instance of this class. + // If succeeds, a respective call is expected to freeInstance(), regardless + // of who owns the context memory. + // pMem A memory buffer of at least the size returned by + // GetInstanceSize(), where the instance context is to be + // stored. If NULL, it will be automatically allocated (using + // malloc). + // nBands Number of bands. Must be >= 2. + // nChannels Number of input/output channels (interlaced). + // sampleRate The input/output sample rate, in Hz. + // presets The presets configuration. May be NULL, but in that case the + // client is required not to call preset-related functions. + // This array is owned by the client and is not copied. It + // must be kept valid by the client as long as the instance is + // alive. + // nPresets Number of elements in the presets array. + // returns The instance if success. NULL if pMem is NULL and allocation + // failed. + static AudioEqualizer * CreateInstance(void * pMem, int nBands, + int nChannels, + int sampleRate, + const PresetConfig * presets, + int nPresets); + + // Reconfiguration of the filter. Changes input/output format, but does not + // alter current parameter values. Causes reset of the delay lines. + // nChannels Number of input/output channels (interlaced). + // sampleRate The input/output sample rate, in Hz. + void configure(int nChannels, int sampleRate); + + // Resets the filter parameters to the following values: + // frequency: 0 + // gain: 0 + // bandwidth: 1200 cents. + // It also disables the filter. Does not clear the delay lines. + void reset(); + + // Clears delay lines. Does not alter parameter values. + void clear(); + + // Frees the object. Will free the memory if the object owned it, i.e. if + // a NULL pointer was passed to CreateInstance as pMem. + void free(); + + // Sets gain value. Actual change will only take place upon commit(). + // This value will be remembered even if the filter is in disabled() state. + // band The band to set the gain for. + // millibel Gain value in millibel (1/100 of decibel). + void setGain(int band, int32_t millibel); + + // Gets gain of a certain band. This is always the last value set (or + // default value after reset). + // band The band to get the gain for. + // returns Gain value in millibel (1/100 of decibel). + int32_t getGain(int band) const; + + // Sets cutoff frequency value. Actual change will only take place upon + // commit(). + // This value will be remembered even if the filter is in disabled() state. + // band The band to set the frequency for. + // millihertz Frequency value in mHz. + void setFrequency(int band, uint32_t millihertz); + + // Gets frequency of a certain band. This is always the last value set (or + // default value after reset). + // band The band to get the frequency for. + // returns Frequency value in mHz. + uint32_t getFrequency(int band) const; + + // Sets bandwidth value. Actual change will only take place upon commit(). + // This value will be remembered even if the filter is in disabled() state. + // If called on the first or last band, this call is ignored. + // band The band to set the frequency for. + // cents Bandwidth value in cents (1/1200 octave). + void setBandwidth(int band, uint32_t cents); + + // Gets bandwidth of a certain band. This is always the last value set (or + // default value after reset). For the first and last bands, 0 is always + // returned. + // band The band to get the bandwidth for. + // returns Bandwidth value in cents (1/1200 octave). + uint32_t getBandwidth(int band) const; + + // Gets lower and upper boundaries of a band. + // For the low shelf, the low bound is 0 and the high bound is the band + // frequency. + // For the high shelf, the low bound is the band frequency and the high + // bound is Nyquist. + // For the peaking filters, they are the gain[dB]/2 points. + void getBandRange(int band, uint32_t & low, uint32_t & high) const; + + // Gets a human-readable name for a preset ID. Will return "Custom" if + // PRESET_CUSTOM is passed. + // preset The preset ID. Must be less than number of presets. + const char * getPresetName(int preset) const; + + // Gets the number of presets. + int getNumPresets() const; + + // Gets the currently set preset ID. + // Will return PRESET_CUSTOM in case the EQ parameters have been modified + // manually since a preset was set. + int getPreset() const; + + // Sets the current preset by ID. + // All the band parameters will be overridden. + // Change will not be applied until commit() is called. + // preset The preset ID. Must be less than number of presets. + // PRESET_CUSTOM is NOT a valid value here. + void setPreset(int preset); + + // Applies all parameter changes done to this point in time. + // If the filter is disabled, the new parameters will take place when it is + // enabled again. Does not introduce artifacts, unless immediate is set. + // immediate Whether to apply change abruptly (ignored if filter is + // disabled). + void commit(bool immediate = false); + + // Process a buffer of input data. The input and output should contain + // frameCount * nChannels interlaced samples. Processing can be done + // in-place, by passing the same buffer as both arguments. + // pIn Input buffer. + // pOut Output buffer. + // frameCount Number of frames to produce on each call to process(). + void process(const audio_sample_t * pIn, audio_sample_t * pOut, + int frameCount); + + // Enables the filter, so it would start processing input. Does not + // introduce artifacts, unless immediate is set. + // immediate Whether to apply change abruptly. + void enable(bool immediate = false); + + // Disabled (bypasses) the filter. Does not introduce artifacts, unless + // immediate is set. + // immediate Whether to apply change abruptly. + void disable(bool immediate = false); + + // Returns the band with the maximum influence on a given frequency. + // Result is unaffected by whether EQ is enabled or not, or by whether + // changes have been committed or not. + // targetFreq The target frequency, in millihertz. + int getMostRelevantBand(uint32_t targetFreq) const; + +private: + // Bottom frequency, in mHz. + static const int kMinFreq = 20000; + // Sample rate, in Hz. + int mSampleRate; + // Number of peaking filters. Total number of bands is +2. + int mNumPeaking; + // Preset configurations. + const PresetConfig * mpPresets; + // Number of elements in mpPresets; + int mNumPresets; + // Current preset. + int mCurPreset; + + // Memory space to free when instance is deleted, or NULL if no memory is + // owned. + void * mpMem; + // The low-shelving filter. + AudioShelvingFilter * mpLowShelf; + // The high-shelving filter. + AudioShelvingFilter * mpHighShelf; + // An array of size mNumPeaking of peaking filters. + AudioPeakingFilter * mpPeakingFilters; + + // Constructor. Resets the filter (see reset()). Must call init() doing + // anything else. + // pMem Memory buffer for bands. + // nChannels Number of input/output channels (interlaced). + // sampleRate The input/output sample rate, in Hz. + // ownMem Whether pMem is owned by me. + // presets The presets configuration. May be NULL, but in that case the + // client is required not to call preset-related functions. + // This array is owned by the client and is not copied. It + // must be kept valid by the client as long as the instance is + // alive. + // nPresets Number of elements in the presets array. + AudioEqualizer(void * pMem, int nBands, int nChannels, int sampleRate, + bool ownMem, const PresetConfig * presets, int nPresets); +}; + +} + +#endif // AUDIOEQUALIZER_H_ diff --git a/media/libeffects/testlibs/AudioFormatAdapter.h b/media/libeffects/testlibs/AudioFormatAdapter.h new file mode 100644 index 0000000..d93ebe9 --- /dev/null +++ b/media/libeffects/testlibs/AudioFormatAdapter.h @@ -0,0 +1,184 @@ +/* /android/src/frameworks/base/media/libeffects/AudioFormatAdapter.h +** +** 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. +*/ + +#ifndef AUDIOFORMATADAPTER_H_ +#define AUDIOFORMATADAPTER_H_ + +#include + + +#define min(x,y) (((x) < (y)) ? (x) : (y)) + +namespace android { + +// An adapter for an audio processor working on audio_sample_t samples with a +// buffer override behavior to arbitrary sample formats and buffer behaviors. +// The adapter may work on any processing class which has a processing function +// with the following signature: +// void process(const audio_sample_t * pIn, +// audio_sample_t * pOut, +// int frameCount); +// It is assumed that the underlying processor works in S7.24 format and an +// overwrite behavior. +// +// Usage is simple: just work with the processor normally, but instead of +// calling its process() function directly, work with the process() function of +// the adapter. +// The adapter supports re-configuration to a different format on the fly. +// +// T The processor class. +// bufSize The maximum number of samples (single channel) to process on a +// single call to the underlying processor. Setting this to a small +// number will save a little memory, but will cost function call +// overhead, resulting from multiple calls to the underlying process() +// per a single call to this class's process(). +template +class AudioFormatAdapter { +public: + // Configure the adapter. + // processor The underlying audio processor. + // nChannels Number of input and output channels. The adapter does not do + // channel conversion - this parameter must be in sync with the + // actual processor. + // pcmFormat The desired input/output sample format. + // behavior The desired behavior (overwrite or accumulate). + void configure(T & processor, int nChannels, uint8_t pcmFormat, + uint32_t behavior) { + mpProcessor = &processor; + mNumChannels = nChannels; + mPcmFormat = pcmFormat; + mBehavior = behavior; + mMaxSamplesPerCall = bufSize / nChannels; + } + + // Process a block of samples. + // pIn A buffer of samples with the format specified on + // configure(). + // pOut A buffer of samples with the format specified on + // configure(). May be the same as pIn. + // numSamples The number of multi-channel samples to process. + void process(const void * pIn, void * pOut, uint32_t numSamples) { + while (numSamples > 0) { + uint32_t numSamplesIter = min(numSamples, mMaxSamplesPerCall); + uint32_t nSamplesChannels = numSamplesIter * mNumChannels; + if (mPcmFormat == SAMPLE_FORMAT_PCM_S7_24) { + if (mBehavior == EFFECT_BUFFER_ACCESS_WRITE) { + mpProcessor->process( + reinterpret_cast (pIn), + reinterpret_cast (pOut), + numSamplesIter); + } else if (mBehavior == EFFECT_BUFFER_ACCESS_ACCUMULATE) { + mpProcessor->process( + reinterpret_cast (pIn), + mBuffer, numSamplesIter); + MixOutput(pOut, numSamplesIter); + } else { + assert(false); + } + pIn = reinterpret_cast (pIn) + + nSamplesChannels; + pOut = reinterpret_cast (pOut) + + nSamplesChannels; + } else { + ConvertInput(pIn, nSamplesChannels); + mpProcessor->process(mBuffer, mBuffer, numSamplesIter); + ConvertOutput(pOut, nSamplesChannels); + } + numSamples -= numSamplesIter; + } + } + +private: + // The underlying processor. + T * mpProcessor; + // The number of input/output channels. + int mNumChannels; + // The desired PCM format. + uint8_t mPcmFormat; + // The desired buffer behavior. + uint32_t mBehavior; + // An intermediate buffer for processing. + audio_sample_t mBuffer[bufSize]; + // The buffer size, divided by the number of channels - represents the + // maximum number of multi-channel samples that can be stored in the + // intermediate buffer. + size_t mMaxSamplesPerCall; + + // Converts a buffer of input samples to audio_sample_t format. + // Output is written to the intermediate buffer. + // pIn The input buffer with the format designated in configure(). + // When function exist will point to the next unread input + // sample. + // numSamples The number of single-channel samples to process. + void ConvertInput(const void *& pIn, uint32_t numSamples) { + if (mPcmFormat == SAMPLE_FORMAT_PCM_S15) { + const int16_t * pIn16 = reinterpret_cast(pIn); + audio_sample_t * pOut = mBuffer; + while (numSamples-- > 0) { + *(pOut++) = s15_to_audio_sample_t(*(pIn16++)); + } + pIn = pIn16; + } else { + assert(false); + } + } + + // Converts audio_sample_t samples from the intermediate buffer to the + // output buffer, converting to the desired format and buffer behavior. + // pOut The buffer to write the output to. + // When function exist will point to the next output sample. + // numSamples The number of single-channel samples to process. + void ConvertOutput(void *& pOut, uint32_t numSamples) { + if (mPcmFormat == SAMPLE_FORMAT_PCM_S15) { + const audio_sample_t * pIn = mBuffer; + int16_t * pOut16 = reinterpret_cast(pOut); + if (mBehavior == EFFECT_BUFFER_ACCESS_WRITE) { + while (numSamples-- > 0) { + *(pOut16++) = audio_sample_t_to_s15_clip(*(pIn++)); + } + } else if (mBehavior == EFFECT_BUFFER_ACCESS_ACCUMULATE) { + while (numSamples-- > 0) { + *(pOut16++) += audio_sample_t_to_s15_clip(*(pIn++)); + } + } else { + assert(false); + } + pOut = pOut16; + } else { + assert(false); + } + } + + // Accumulate data from the intermediate buffer to the output. Output is + // assumed to be of audio_sample_t type. + // pOut The buffer to mix the output to. + // When function exist will point to the next output sample. + // numSamples The number of single-channel samples to process. + void MixOutput(void *& pOut, uint32_t numSamples) { + const audio_sample_t * pIn = mBuffer; + audio_sample_t * pOut24 = reinterpret_cast(pOut); + numSamples *= mNumChannels; + while (numSamples-- > 0) { + *(pOut24++) += *(pIn++); + } + pOut = pOut24; + } +}; + +} + +#endif // AUDIOFORMATADAPTER_H_ diff --git a/media/libeffects/testlibs/AudioHighShelfFilterCoef.inl b/media/libeffects/testlibs/AudioHighShelfFilterCoef.inl new file mode 100644 index 0000000..ebba139 --- /dev/null +++ b/media/libeffects/testlibs/AudioHighShelfFilterCoef.inl @@ -0,0 +1,225 @@ +13679, +21575, +8921, +32315056, +-15582015, +26172, +37678, +14797, +31891096, +-15192527, +51020, +64449, +23945, +31322988, +-14685186, +101817, +106144, +37744, +30562882, +-14031372, +208996, +162163, +58536, +29548538, +-13201017, +442996, +207459, +93300, +28200792, +-12167331, +971423, +128786, +169690, +26422744, +-10915428, +2200461, +-465686, +394986, +24103317, +-9455862, +5119991, +-2777199, +1147245, +21129473, +-7842294, +12120379, +-10198160, +3631544, +17411837, +-6188384, +28834234, +-31647135, +11337795, +12924960, +-4672638, +68037766, +-88974388, +33477255, +7752680, +-3516098, +157369944, +-232063160, +92282129, +2113926, +-2925624, +353720112, +-567427144, +237164112, +-3659993, +-3019871, +769091151, +-1309871949, +570539430, +-9202114, +-3779302, +66791, +121706, +55888, +30571245, +-14038415, +120426, +212178, +94820, +29559679, +-13209886, +217130, +365165, +157610, +28215554, +-12178243, +391489, +617016, +255010, +26442131, +-10928431, +705862, +1015147, +398457, +24128430, +-9470680, +1272682, +1605251, +596102, +21161334, +-7858153, +2294668, +2386833, +848523, +17451072, +-6203880, +4137327, +3198789, +1155536, +12971362, +-4685798, +7459675, +3470266, +1567219, +7804818, +-3524761, +13449926, +1738911, +2347406, +2169089, +-2928116, +24250455, +-5211241, +4358971, +-3605298, +-3015671, +43724001, +-23849570, +9823315, +-9151253, +-3769277, +78835150, +-66542375, +23686373, +-14161143, +-5040790, +142141173, +-156324261, +56024234, +-18451275, +-6612656, +256283057, +-335606326, +126341244, +-21970004, +-8270755, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, +16777216, +33554432, +16777216, +-33554432, +-16777216, diff --git a/media/libeffects/testlibs/AudioLowShelfFilterCoef.inl b/media/libeffects/testlibs/AudioLowShelfFilterCoef.inl new file mode 100644 index 0000000..b57deb4 --- /dev/null +++ b/media/libeffects/testlibs/AudioLowShelfFilterCoef.inl @@ -0,0 +1,375 @@ +16212506, +-32420574, +16208068, +32401411, +-15662521, +16356129, +-32706246, +16350118, +32695525, +-15939752, +16464399, +-32920670, +16456274, +32914686, +-16149441, +16546128, +-33081289, +16535165, +33077955, +-16307411, +16608101, +-33201422, +16593328, +33199569, +-16426067, +16655539, +-33291174, +16635647, +33290147, +-16514997, +16692478, +-33358171, +16665715, +33357608, +-16581540, +16722089, +-33408149, +16686099, +33407850, +-16631271, +16746930, +-33445409, +16698549, +33445267, +-16668405, +16769156, +-33473168, +16704138, +33473133, +-16696114, +16790706, +-33493827, +16703348, +33493886, +-16716779, +16813466, +-33509166, +16696111, +33509342, +-16732186, +16839437, +-33520498, +16681802, +33520852, +-16743669, +16870911, +-33528765, +16659191, +33529424, +-16752226, +16910681, +-33534607, +16626337, +33535807, +-16758602, +15667401, +-31326224, +15658825, +31252161, +-14623074, +15945865, +-31880007, +15934146, +31838205, +-15144597, +16157521, +-32299091, +16141578, +32275604, +-15545369, +16318267, +-32614903, +16296651, +32601755, +-15850850, +16440710, +-32852157, +16411473, +32844820, +-16082303, +16534751, +-33029985, +16495281, +33025910, +-16256891, +16608171, +-33163043, +16554957, +33160803, +-16388152, +16667145, +-33262468, +16595477, +33261275, +-16486599, +16716699, +-33336671, +16620252, +33336105, +-16560301, +16761100, +-33391976, +16631379, +33391836, +-16615404, +16804207, +-33433103, +16629806, +33433341, +-16656560, +16849794, +-33463551, +16615399, +33464251, +-16687277, +16901887, +-33485857, +16586933, +33487271, +-16710189, +16965125, +-33501781, +16542000, +33504415, +-16727274, +17045198, +-33512384, +16476824, +33517183, +-16740008, +14635201, +-29254376, +14619184, +28977711, +-12753834, +15157556, +-30292825, +15135285, +30133938, +-13674513, +15561511, +-31092298, +15530817, +31001860, +-14405551, +15872211, +-31702342, +15830187, +31651218, +-14976306, +16111094, +-32164834, +16053843, +32136101, +-15416453, +16295848, +-32513802, +16218140, +32497757, +-15752817, +16440852, +-32776179, +16335665, +32767326, +-16008155, +16557804, +-32972907, +16415715, +32968179, +-16201031, +16656403, +-33120052, +16464758, +33117802, +-16346195, +16745001, +-33229805, +16486810, +33229247, +-16455153, +16831240, +-33311306, +16483692, +33312252, +-16536771, +16922682, +-33371278, +16455145, +33374070, +-16597819, +17027472, +-33414465, +16398818, +33420110, +-16643430, +17155108, +-33443875, +16310110, +33454398, +-16677479, +17317384, +-33460760, +16181887, +33479933, +-16702882, +12792703, +-25557388, +12764716, +24590507, +-9747085, +13706465, +-27372621, +13666215, +26798296, +-11169790, +14439425, +-28821830, +14382518, +28486469, +-12380088, +15018453, +-29957273, +14939030, +29764018, +-13373522, +15472460, +-30834757, +15362688, +30724568, +-14168120, +15828725, +-31506123, +15678118, +31443928, +-14791822, +16111406, +-32015988, +15905901, +31981391, +-15274688, +16341329, +-32400984, +16062061, +32382398, +-15644761, +16536484, +-32690238, +16158133, +32681354, +-15926286, +16712853, +-32906337, +16201432, +32904128, +-16139278, +16885430, +-33066336, +16195306, +33070090, +-16299767, +17069377, +-33182599, +16139282, +33193711, +-16420332, +17281371, +-33263294, +16029039, +33285785, +-16510702, +17541271, +-33312390, +15856243, +33354359, +-16578329, +17874330, +-33328902, +15608287, +33405430, +-16628873, +9881279, +-19719268, +9838084, +16734303, +-5927111, +11264857, +-22463447, +11198784, +20577584, +-7572288, +12460736, +-24823046, +12362697, +23667359, +-9201903, +13459805, +-26776851, +13317799, +26085683, +-10691555, +14276287, +-28349256, +14074406, +27943899, +-11978834, +14936943, +-29588248, +14654011, +29354533, +-13047453, +15473300, +-30549299, +15081036, +30417203, +-13909216, +15917271, +-31285788, +15377817, +31213972, +-14589687, +16299384, +-31844320, +15562006, +31809683, +-15118811, +16648774, +-32262999, +15645414, +32254329, +-15525641, +16994277, +-32571090, +15633607, +32585895, +-15835862, +17366374, +-32789019, +15525801, +32833003, +-16070975, +17799955, +-32927834, +15314895, +33017107, +-16248361, +18338206, +-32987318, +14987686, +33154246, +-16381747, +19038270, +-32951545, +14525592, +33256392, +-16481800, diff --git a/media/libeffects/testlibs/AudioPeakingFilter.cpp b/media/libeffects/testlibs/AudioPeakingFilter.cpp new file mode 100644 index 0000000..60fefe6 --- /dev/null +++ b/media/libeffects/testlibs/AudioPeakingFilter.cpp @@ -0,0 +1,121 @@ +/* //device/include/server/AudioFlinger/AudioPeakingFilter.cpp + ** + ** Copyright 2007, 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 "AudioPeakingFilter.h" +#include "AudioCommon.h" +#include "EffectsMath.h" + +#include +#include + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { +// Format of the coefficient table: +// kCoefTable[freq][gain][bw][coef] +// freq - peak frequency, in octaves below Nyquist,from -9 to -1. +// gain - gain, in millibel, starting at -9600, jumps of 1024, to 4736 millibel. +// bw - bandwidth, starting at 1 cent, jumps of 1024, to 3073 cents. +// coef - 0: b0 +// 1: b1 +// 2: b2 +// 3: -a1 +// 4: -a2 +static const size_t kInDims[3] = {9, 15, 4}; +static const audio_coef_t kCoefTable[9*15*4*5] = { +#include "AudioPeakingFilterCoef.inl" +}; + +AudioCoefInterpolator AudioPeakingFilter::mCoefInterp(3, kInDims, 5, (const audio_coef_t*) kCoefTable); + +AudioPeakingFilter::AudioPeakingFilter(int nChannels, int sampleRate) + : mBiquad(nChannels, sampleRate) { + configure(nChannels, sampleRate); + reset(); +} + +void AudioPeakingFilter::configure(int nChannels, int sampleRate) { + mNiquistFreq = sampleRate * 500; + mFrequencyFactor = ((1ull) << 42) / mNiquistFreq; + mBiquad.configure(nChannels, sampleRate); + setFrequency(mNominalFrequency); + commit(true); +} + +void AudioPeakingFilter::reset() { + setGain(0); + setFrequency(0); + setBandwidth(2400); + commit(true); +} + +void AudioPeakingFilter::setFrequency(uint32_t millihertz) { + mNominalFrequency = millihertz; + if (UNLIKELY(millihertz > mNiquistFreq / 2)) { + millihertz = mNiquistFreq / 2; + } + uint32_t normFreq = static_cast( + (static_cast(millihertz) * mFrequencyFactor) >> 10); + if (LIKELY(normFreq > (1 << 23))) { + mFrequency = (Effects_log2(normFreq) - ((32-9) << 15)) << (FREQ_PRECISION_BITS - 15); + } else { + mFrequency = 0; + } +} + +void AudioPeakingFilter::setGain(int32_t millibel) { + mGain = millibel + 9600; +} + +void AudioPeakingFilter::setBandwidth(uint32_t cents) { + mBandwidth = cents - 1; +} + +void AudioPeakingFilter::commit(bool immediate) { + audio_coef_t coefs[5]; + int intCoord[3] = { + mFrequency >> FREQ_PRECISION_BITS, + mGain >> GAIN_PRECISION_BITS, + mBandwidth >> BANDWIDTH_PRECISION_BITS + }; + uint32_t fracCoord[3] = { + mFrequency << (32 - FREQ_PRECISION_BITS), + static_cast(mGain) << (32 - GAIN_PRECISION_BITS), + mBandwidth << (32 - BANDWIDTH_PRECISION_BITS) + }; + mCoefInterp.getCoef(intCoord, fracCoord, coefs); + mBiquad.setCoefs(coefs, immediate); +} + +void AudioPeakingFilter::getBandRange(uint32_t & low, uint32_t & high) const { + // Half bandwidth, in octaves, 15-bit precision + int32_t halfBW = (((mBandwidth + 1) / 2) << 15) / 1200; + + low = static_cast((static_cast(mNominalFrequency) * Effects_exp2(-halfBW + (16 << 15))) >> 16); + if (UNLIKELY(halfBW >= (16 << 15))) { + high = mNiquistFreq; + } else { + high = static_cast((static_cast(mNominalFrequency) * Effects_exp2(halfBW + (16 << 15))) >> 16); + if (UNLIKELY(high > mNiquistFreq)) { + high = mNiquistFreq; + } + } +} + +} + diff --git a/media/libeffects/testlibs/AudioPeakingFilter.h b/media/libeffects/testlibs/AudioPeakingFilter.h new file mode 100644 index 0000000..d0f49c9 --- /dev/null +++ b/media/libeffects/testlibs/AudioPeakingFilter.h @@ -0,0 +1,151 @@ +/* //device/include/server/AudioFlinger/AudioPeakingFilter.h +** +** 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. +*/ + +#ifndef ANDROID_AUDIO_PEAKING_FILTER_H +#define ANDROID_AUDIO_PEAKING_FILTER_H + +#include "AudioBiquadFilter.h" +#include "AudioCoefInterpolator.h" + +namespace android { + +// A peaking audio filter, with unity skirt gain, and controllable peak +// frequency, gain and bandwidth. +// This filter is able to suppress introduce discontinuities and other artifacts +// in the output, even when changing parameters abruptly. +// Parameters can be set to any value - this class will make sure to clip them +// when they are out of supported range. +// +// Implementation notes: +// This class uses an underlying biquad filter whose parameters are determined +// using a linear interpolation from a coefficient table, using a +// AudioCoefInterpolator. +// All is left for this class to do is mapping between high-level parameters to +// fractional indices into the coefficient table. +class AudioPeakingFilter { +public: + // Constructor. Resets the filter (see reset()). + // nChannels Number of input/output channels (interlaced). + // sampleRate The input/output sample rate, in Hz. + AudioPeakingFilter(int nChannels, int sampleRate); + + // Reconfiguration of the filter. Changes input/output format, but does not + // alter current parameter values. Clears delay lines. + // nChannels Number of input/output channels (interlaced). + // sampleRate The input/output sample rate, in Hz. + void configure(int nChannels, int sampleRate); + + // Resets the filter parameters to the following values: + // frequency: 0 + // gain: 0 + // bandwidth: 1200 cents. + // It also disables the filter. Does not clear the delay lines. + void reset(); + + // Clears delay lines. Does not alter parameter values. + void clear() { mBiquad.clear(); } + + // Sets gain value. Actual change will only take place upon commit(). + // This value will be remembered even if the filter is in disabled() state. + // millibel Gain value in millibel (1/100 of decibel). + void setGain(int32_t millibel); + + // Gets the gain, in millibel, as set. + int32_t getGain() const { return mGain - 9600; } + + // Sets bandwidth value. Actual change will only take place upon commit(). + // This value will be remembered even if the filter is in disabled() state. + // cents Bandwidth value in cents (1/1200 octave). + void setBandwidth(uint32_t cents); + + // Gets the gain, in cents, as set. + uint32_t getBandwidth() const { return mBandwidth + 1; } + + // Sets frequency value. Actual change will only take place upon commit(). + // This value will be remembered even if the filter is in disabled() state. + // millihertz Frequency value in mHz. + void setFrequency(uint32_t millihertz); + + // Gets the frequency, in mHz, as set. + uint32_t getFrequency() const { return mNominalFrequency; } + + // Gets gain[dB]/2 points. + // Results in mHz, and are computed based on the nominal values set, not on + // possibly rounded or truncated actual values. + void getBandRange(uint32_t & low, uint32_t & high) const; + + // Applies all parameter changes done to this point in time. + // If the filter is disabled, the new parameters will take place when it is + // enabled again. Does not introduce artifacts, unless immediate is set. + // immediate Whether to apply change abruptly (ignored if filter is + // disabled). + void commit(bool immediate = false); + + // Process a buffer of input data. The input and output should contain + // frameCount * nChannels interlaced samples. Processing can be done + // in-place, by passing the same buffer as both arguments. + // in Input buffer. + // out Output buffer. + // frameCount Number of frames to produce. + void process(const audio_sample_t in[], audio_sample_t out[], + int frameCount) { mBiquad.process(in, out, frameCount); } + + // Enables the filter, so it would start processing input. Does not + // introduce artifacts, unless immediate is set. + // immediate Whether to apply change abruptly. + void enable(bool immediate = false) { mBiquad.enable(immediate); } + + // Disabled (bypasses) the filter. Does not introduce artifacts, unless + // immediate is set. + // immediate Whether to apply change abruptly. + void disable(bool immediate = false) { mBiquad.disable(immediate); } + +private: + // Precision for the mFrequency member. + static const int FREQ_PRECISION_BITS = 26; + // Precision for the mGain member. + static const int GAIN_PRECISION_BITS = 10; + // Precision for the mBandwidth member. + static const int BANDWIDTH_PRECISION_BITS = 10; + + // Nyquist, in mHz. + uint32_t mNiquistFreq; + // Fractional index into the gain dimension of the coef table in + // GAIN_PRECISION_BITS precision. + int32_t mGain; + // Fractional index into the bandwidth dimension of the coef table in + // BANDWIDTH_PRECISION_BITS precision. + uint32_t mBandwidth; + // Fractional index into the frequency dimension of the coef table in + // FREQ_PRECISION_BITS precision. + uint32_t mFrequency; + // Nominal value of frequency, as set. + uint32_t mNominalFrequency; + // 1/Nyquist[mHz], in 42-bit precision (very small). + // Used for scaling the frequency. + uint32_t mFrequencyFactor; + + // A biquad filter, used for the actual processing. + AudioBiquadFilter mBiquad; + // A coefficient interpolator, used for mapping the high level parameters to + // the low-level biquad coefficients. + static AudioCoefInterpolator mCoefInterp; +}; + +} + +#endif // ANDROID_AUDIO_PEAKING_FILTER_H diff --git a/media/libeffects/testlibs/AudioPeakingFilterCoef.inl b/media/libeffects/testlibs/AudioPeakingFilterCoef.inl new file mode 100644 index 0000000..374c6e1 --- /dev/null +++ b/media/libeffects/testlibs/AudioPeakingFilterCoef.inl @@ -0,0 +1,2700 @@ +16769751, +-33538871, +16769751, +33538871, +-16762286, +11468083, +-22935566, +11467915, +22935566, +-6158781, +8532673, +-17064763, +8532412, +17064763, +-287869, +6567366, +-13134160, +6567042, +13134160, +3642808, +16773075, +-33545518, +16773075, +33545518, +-16768934, +13349658, +-26698459, +13349304, +26698459, +-9921746, +10923581, +-21846147, +10922977, +21846147, +-5069342, +9009390, +-18017641, +9008590, +18017641, +-1240764, +16774919, +-33549207, +16774919, +33549207, +-16772622, +14686150, +-29371047, +14685450, +29371047, +-12594384, +12933736, +-25865697, +12932448, +25865697, +-9088968, +11350364, +-22698482, +11348545, +22698482, +-5921693, +16775943, +-33551252, +16775941, +33551252, +-16774668, +15549746, +-31097569, +15548408, +31097569, +-14320938, +14404163, +-28805197, +14401577, +28805197, +-12028524, +13261978, +-26519626, +13258147, +26519626, +-9742909, +16776511, +-33552387, +16776508, +33552387, +-16775803, +16074349, +-32145600, +16071856, +32145600, +-15368989, +15374244, +-30742933, +15369268, +30742933, +-13966296, +14629431, +-29250695, +14621814, +29250695, +-12474030, +16776826, +-33553017, +16776822, +33553017, +-16776432, +16381548, +-32757900, +16376968, +32757900, +-15981300, +15972161, +-31934402, +15962842, +31934402, +-15157787, +15518882, +-31022614, +15504316, +31022614, +-14245982, +16777003, +-33553366, +16776995, +33553366, +-16776781, +16558315, +-33107660, +16549969, +33107660, +-16331067, +16326848, +-32635910, +16309677, +32635910, +-15859308, +16064456, +-32101133, +16037281, +32101133, +-15324521, +16777103, +-33553559, +16777088, +33553559, +-16776975, +16660325, +-33304885, +16645187, +33304885, +-16528296, +16535200, +-33038436, +16503858, +33038436, +-16261842, +16391425, +-32732273, +16341464, +32732273, +-15955673, +16777162, +-33553667, +16777136, +33553667, +-16777082, +16721651, +-33415288, +16694267, +33415288, +-16638701, +16661761, +-33265997, +16604862, +33265997, +-16489407, +16592417, +-33093137, +16501343, +33093137, +-16316544, +16777203, +-33553726, +16777155, +33553726, +-16777142, +16763466, +-33476836, +16714001, +33476836, +-16700251, +16748588, +-33393565, +16645605, +33393565, +-16616978, +16731288, +-33296733, +16566071, +33296733, +-16520144, +16777238, +-33553759, +16777152, +33553759, +-16777175, +16800489, +-33511071, +16711212, +33511071, +-16734485, +16825723, +-33464740, +16639647, +33464740, +-16688154, +16855137, +-33410736, +16556228, +33410736, +-16634149, +16777282, +-33553778, +16777127, +33553778, +-16777193, +16845889, +-33530088, +16684830, +33530088, +-16753503, +16920437, +-33504347, +16584541, +33504347, +-16727762, +17007449, +-33474302, +16467484, +33474302, +-16697716, +16777349, +-33553788, +16777070, +33553788, +-16777203, +16915880, +-33540645, +16625396, +33540645, +-16764060, +17066504, +-33526354, +16460481, +33526354, +-16749769, +17242444, +-33509662, +16267849, +33509662, +-16733077, +16777464, +-33553793, +16776961, +33553793, +-16777209, +17035487, +-33546503, +16511647, +33546503, +-16769918, +17316137, +-33538572, +16223067, +33538572, +-16761988, +17644089, +-33529306, +15885848, +33529306, +-16752721, +16777668, +-33553796, +16776760, +33553796, +-16777212, +17247484, +-33549752, +16302900, +33549752, +-16773168, +17758603, +-33545353, +15787381, +33545353, +-16768768, +18356007, +-33540211, +15184835, +33540211, +-16763626, +16762293, +-33522062, +16762293, +33522062, +-16747370, +8711405, +-17421242, +8711149, +17421242, +-645338, +5721250, +-11441288, +5720900, +11441288, +5335066, +4082836, +-8164655, +4082434, +8164655, +8611946, +16768936, +-33535347, +16768936, +33535347, +-16760656, +11085056, +-22167856, +11084469, +22167856, +-5392309, +8098223, +-16194333, +8097329, +16194333, +581664, +6158323, +-12314623, +6157228, +12314623, +4461665, +16772623, +-33542719, +16772622, +33542719, +-16768029, +13058634, +-26114055, +13057388, +26114055, +-9338806, +10523211, +-21042742, +10521116, +21042742, +-4267110, +8576560, +-17149081, +8573812, +17149081, +-373156, +16774670, +-33546810, +16774667, +33546810, +-16772120, +14489725, +-28974776, +14487233, +28974776, +-12199742, +12619509, +-25232587, +12614979, +25232587, +-8457271, +10965151, +-21922319, +10958818, +21922319, +-5146753, +16775805, +-33549079, +16775800, +33549079, +-16774390, +15428102, +-30849095, +15423317, +30849095, +-14074202, +14188157, +-28364996, +14178975, +28364996, +-11589916, +12969900, +-25924346, +12956398, +25924346, +-9149082, +16776437, +-33550338, +16776428, +33550338, +-16775648, +16004216, +-31997074, +15995268, +31997074, +-15222267, +15241233, +-30462393, +15223454, +30462393, +-13687471, +14437070, +-28844880, +14409982, +28844880, +-12069836, +16776789, +-33551036, +16776773, +33551036, +-16776347, +16345158, +-32671383, +16328685, +32671383, +-15896627, +15900462, +-31765104, +15867034, +31765104, +-14990280, +15410846, +-30767279, +15358750, +30767279, +-13992380, +16776990, +-33551423, +16776960, +33551423, +-16776734, +16545156, +-33057769, +16515103, +33057769, +-16283043, +16300504, +-32536823, +16238769, +32536823, +-15762058, +16024071, +-31948201, +15926537, +31948201, +-15173391, +16777109, +-33551638, +16777056, +33551638, +-16776949, +16666542, +-33276035, +16611999, +33276035, +-16501325, +16548270, +-32981225, +16435439, +32981225, +-16206493, +16412621, +-32643103, +16232940, +32643103, +-15868346, +16777189, +-33551757, +16777094, +33551757, +-16777068, +16749778, +-33398338, +16651075, +33398338, +-16623637, +16720233, +-33232976, +16515245, +33232976, +-16458262, +16686059, +-33041706, +16358135, +33041706, +-16266978, +16777261, +-33551823, +16777089, +33551823, +-16777134, +16823703, +-33466559, +16645376, +33466559, +-16691863, +16873974, +-33374267, +16502806, +33374267, +-16599564, +16932398, +-33267006, +16337114, +33267006, +-16492295, +16777348, +-33551860, +16777038, +33551860, +-16777170, +16914465, +-33504517, +16592574, +33504517, +-16729824, +17063236, +-33453150, +16392432, +33453150, +-16678453, +17236595, +-33393293, +16159213, +33393293, +-16618592, +16777483, +-33551880, +16776924, +33551880, +-16777191, +17054436, +-33525606, +16473695, +33525606, +-16750914, +17355321, +-33497061, +16144262, +33497061, +-16722367, +17706453, +-33463749, +15759816, +33463749, +-16689053, +16777713, +-33551891, +16776705, +33551891, +-16777202, +17293646, +-33537314, +16246194, +33537314, +-16762623, +17854571, +-33521465, +15669419, +33521465, +-16746773, +18509706, +-33502954, +14995772, +33502954, +-16728261, +16778120, +-33551898, +16776304, +33551898, +-16777208, +17717639, +-33543811, +15828698, +33543811, +-16769121, +18739501, +-33535016, +14798040, +33535016, +-16760325, +19933534, +-33524738, +13593729, +33524738, +-16750047, +16747397, +-33484705, +16747396, +33484705, +-16717577, +5883112, +-11762335, +5882767, +11762335, +5011337, +3448701, +-6894903, +3448279, +6894903, +9880236, +2324286, +-4646714, +2323828, +4646714, +12129102, +16760665, +-33511232, +16760663, +33511232, +-16744112, +8276984, +-16548106, +8276108, +16548106, +224124, +5337400, +-10670406, +5336221, +10670406, +6103596, +3771525, +-7539439, +3770185, +7539439, +9235506, +16768033, +-33525963, +16768030, +33525963, +-16758848, +10689564, +-21370649, +10687524, +21370649, +-4599872, +7666068, +-15324466, +7663015, +15324466, +1448133, +5761266, +-11515371, +5757575, +11515371, +5258376, +16772124, +-33534139, +16772118, +33534139, +-16767026, +12751423, +-25490780, +12747036, +25490780, +-8721243, +10113942, +-20214533, +10106682, +20214533, +-3443407, +8145025, +-16275741, +8135620, +16275741, +496571, +16774395, +-33538675, +16774385, +33538675, +-16771564, +14280137, +-28542819, +14271281, +28542819, +-11774202, +12292391, +-24561476, +12276485, +24561476, +-7791660, +10572949, +-21117532, +10550945, +21117532, +-4346678, +16775657, +-33541192, +16775639, +33541192, +-16774081, +15299683, +-30573051, +15282579, +30573051, +-13805046, +13964251, +-27887539, +13931690, +27887539, +-11118724, +12672686, +-25290241, +12625174, +25290241, +-8520644, +16776363, +-33542588, +16776330, +33542588, +-16775477, +15935196, +-31828699, +15903092, +31828699, +-15061072, +15112356, +-30152153, +15048881, +30152153, +-13384021, +14253857, +-28402950, +14157649, +28402950, +-11634291, +16776763, +-33543362, +16776704, +33543362, +-16776252, +16319830, +-32570615, +16260598, +32570615, +-15803212, +15851783, +-31574208, +15731938, +31574208, +-14806505, +15339614, +-30483871, +15153441, +30483871, +-13715838, +16777001, +-33543791, +16776896, +33543791, +-16776681, +16557673, +-32997208, +16449476, +32997208, +-16229933, +16326977, +-32422299, +16105089, +32422299, +-15654851, +16067244, +-31775024, +15717353, +31775024, +-15007381, +16777163, +-33544029, +16776972, +33544029, +-16776919, +16722590, +-33238660, +16526083, +33238660, +-16471458, +16664322, +-32912610, +16258203, +32912610, +-16145310, +16597629, +-32539419, +15951593, +32539419, +-15772007, +16777306, +-33544162, +16776962, +33544162, +-16777051, +16869955, +-33374105, +16514205, +33374105, +-16606944, +16969714, +-33190996, +16231282, +33190996, +-16423780, +17084972, +-32979442, +15904406, +32979442, +-16212162, +16777480, +-33544235, +16776860, +33544235, +-16777125, +17051328, +-33449703, +16408452, +33449703, +-16682565, +17347583, +-33347437, +16009901, +33347437, +-16580267, +17691668, +-33228659, +15547002, +33228659, +-16461454, +16777750, +-33544275, +16776632, +33544275, +-16777165, +17331222, +-33491780, +16170648, +33491780, +-16724654, +17931550, +-33434840, +15513363, +33434840, +-16667697, +18630855, +-33368512, +14747710, +33368512, +-16601349, +16778210, +-33544298, +16776194, +33544298, +-16777188, +17809628, +-33515162, +15715631, +33515162, +-16748043, +18929991, +-33483514, +14563611, +33483514, +-16716385, +20237212, +-33446587, +13219451, +33446587, +-16679448, +16779025, +-33544310, +16775392, +33544310, +-16777200, +18657612, +-33528145, +14880634, +33528145, +-16761030, +20699844, +-33510571, +12820822, +33510571, +-16743451, +23084863, +-33490047, +10415274, +33490047, +-16722921, +16717684, +-33395091, +16717682, +33395091, +-16658149, +3567009, +-7125006, +3566590, +7125006, +9643617, +1921926, +-3838751, +1921455, +3838751, +12933835, +1248662, +-2493824, +1248170, +2493824, +14280384, +16744147, +-33447952, +16744143, +33447952, +-16711074, +5493785, +-10973174, +5492622, +10973174, +5790808, +3173694, +-6338342, +3172292, +6338342, +10431230, +2124754, +-4242881, +2123244, +4242881, +12529218, +16758861, +-33477342, +16758855, +33477342, +-16740500, +7843885, +-15665884, +7840892, +15665884, +1092439, +4968649, +-9921376, +4964692, +9921376, +6843875, +3478437, +-6944043, +3473981, +6944043, +9824798, +16767035, +-33493665, +16767024, +33493665, +-16756842, +10284421, +-20536999, +10277346, +20536999, +-3784550, +7240254, +-14452686, +7229862, +14452686, +2307100, +5379798, +-10734231, +5367379, +10734231, +6030039, +16771575, +-33502725, +16771555, +33502725, +-16765913, +12431046, +-24816749, +12415632, +24816749, +-8069462, +9702049, +-19355664, +9676957, +19355664, +-2601790, +7722611, +-15394544, +7690499, +15394544, +1364106, +16774099, +-33507752, +16774063, +33507752, +-16770946, +14062583, +-28059903, +14031160, +28059903, +-11316527, +11962999, +-23841519, +11907273, +23841519, +-7093056, +10188929, +-20277142, +10112667, +20277142, +-3524380, +16775510, +-33510541, +16775445, +33510541, +-16773738, +15175102, +-30252637, +15114019, +30252637, +-13511905, +13753207, +-27358123, +13637911, +27358123, +-10613901, +12400971, +-24605416, +12234119, +24605416, +-7857874, +16776310, +-33512088, +16776193, +33512088, +-16775287, +15888242, +-31623221, +15773117, +31623221, +-14884143, +15028972, +-29795608, +14802570, +29795608, +-13054325, +14142237, +-27909578, +13801000, +27909578, +-11166021, +16776787, +-33512946, +16776575, +33512946, +-16776146, +16345172, +-32438302, +16132251, +32438302, +-15700207, +15905856, +-31344484, +15476429, +31344484, +-14605069, +15428324, +-30155514, +14763558, +30155514, +-13414665, +16777110, +-33513422, +16776729, +33513422, +-16776623, +16668950, +-32908744, +16279482, +32908744, +-16171216, +16555594, +-32275015, +15758344, +32275015, +-15536722, +16428465, +-31564284, +15173886, +31564284, +-14825134, +16777395, +-33513686, +16776708, +33513686, +-16776887, +16961758, +-33175594, +16253845, +33175594, +-16438387, +17158212, +-32815329, +15696692, +32815329, +-16077688, +17382578, +-32403880, +15060381, +32403880, +-15665743, +16777745, +-33513832, +16776505, +33513832, +-16777033, +17323903, +-33325470, +16041757, +33325470, +-16588444, +17911332, +-33122874, +15251488, +33122874, +-16385604, +18589203, +-32889086, +14339546, +32889086, +-16151534, +16778283, +-33513913, +16776048, +33513913, +-16777115, +17883505, +-33409180, +15565966, +33409180, +-16672255, +19078451, +-33295944, +14257648, +33295944, +-16558883, +20465440, +-33164510, +12739067, +33164510, +-16427290, +16779203, +-33513958, +16775173, +33513958, +-16777160, +18840265, +-33455789, +14655871, +33455789, +-16718921, +21075121, +-33392715, +12357866, +33392715, +-16655771, +23677621, +-33319266, +9681828, +33319266, +-16582233, +16780834, +-33513983, +16773567, +33513983, +-16777185, +20536227, +-33481696, +12985848, +33481696, +-16744859, +24614855, +-33446630, +8872112, +33446630, +-16709751, +29373075, +-33405721, +4072934, +33405721, +-16668793, +16658572, +-33156710, +16658568, +33156710, +-16539925, +1995694, +-3971702, +1995225, +3971702, +12786296, +1019334, +-2028355, +1018835, +2028355, +14739047, +648397, +-1290041, +647886, +1290041, +15480933, +16711208, +-33261470, +16711201, +33261470, +-16645192, +3284873, +-6536727, +3283483, +6536727, +10208860, +1752746, +-3487071, +1751198, +3487071, +13273272, +1134254, +-2255981, +1132642, +2255981, +14510319, +16740546, +-33319859, +16740534, +33319859, +-16703864, +5119019, +-10184853, +5115113, +10184853, +6543083, +2916766, +-5800820, +2912122, +5800820, +10948329, +1940950, +-3858260, +1935979, +3858260, +12900288, +16756866, +-33352332, +16756844, +33352332, +-16736493, +7416141, +-14750709, +7405941, +14750709, +1955134, +4618132, +-9178604, +4604884, +9178604, +7554200, +3205731, +-6365873, +3190944, +6365873, +10380541, +16765937, +-33370369, +16765897, +33370369, +-16754618, +9875714, +-19631961, +9851238, +19631961, +-2949736, +6828874, +-13556870, +6793592, +13556870, +3154751, +5021920, +-9953987, +4980230, +9953987, +6775066, +16770983, +-33380381, +16770911, +33380381, +-16764679, +12107731, +-24045066, +12053679, +24045066, +-7384194, +9304697, +-18433704, +9218200, +18433704, +-1745681, +7330173, +-14480925, +7220819, +14480925, +2226224, +16773804, +-33385937, +16773674, +33385937, +-16770261, +13857067, +-27469883, +13745731, +27469883, +-10825583, +11667230, +-23028209, +11472403, +23028209, +-6362417, +9861509, +-19365642, +9597835, +19365642, +-2682128, +16775405, +-33389019, +16775170, +33389019, +-16773358, +15094166, +-29826057, +14876207, +29826057, +-13193157, +13629575, +-26722225, +13221948, +26722225, +-10074307, +12261304, +-23822520, +11676482, +23822520, +-7160571, +16776358, +-33390729, +16775935, +33390729, +-16775076, +15939973, +-31315809, +15527360, +31315809, +-14690117, +15140254, +-29331850, +14333520, +29331850, +-12696558, +14324552, +-27308240, +13115821, +27308240, +-10663157, +16777004, +-33391677, +16776241, +33391677, +-16776029, +16564519, +-32208051, +15799374, +32208051, +-15586676, +16349720, +-31011538, +14811870, +31011538, +-14384374, +16117884, +-29720119, +13746038, +29720119, +-13086705, +16777574, +-33392203, +16776199, +33392203, +-16776558, +17142623, +-32725183, +15740903, +32725183, +-16106310, +17523746, +-32028793, +14660021, +32028793, +-15406551, +17949452, +-31250941, +13452699, +31250941, +-14624935, +16778273, +-33392495, +16775794, +33392495, +-16776851, +17864511, +-33019221, +15314476, +33019221, +-16401771, +19019582, +-32622294, +13760557, +32622294, +-16002923, +20335886, +-32169961, +11989731, +32169961, +-15548401, +16779350, +-33392657, +16774879, +33392657, +-16777014, +18982972, +-33184592, +14362186, +33184592, +-16567942, +21350554, +-32961046, +11769976, +32961046, +-16343314, +24079688, +-32703362, +8781912, +32703362, +-16084383, +16781190, +-33392746, +16773130, +33392746, +-16777104, +20896303, +-33277026, +12541737, +33277026, +-16660824, +25343199, +-33151977, +7969185, +33151977, +-16535169, +30502384, +-33006896, +2664219, +33006896, +-16389387, +16784451, +-33392796, +16769918, +33392796, +-16777154, +24288258, +-33328516, +9201520, +33328516, +-16712562, +32423187, +-33258828, +996567, +33258828, +-16642538, +41895320, +-33177686, +-8557101, +33177686, +-16561003, +16541595, +-32447498, +16541587, +32447498, +-16305966, +1060925, +-2080590, +1060426, +2080590, +14655865, +525478, +-1030256, +524962, +1030256, +15726776, +330321, +-647437, +329800, +647437, +16117095, +16645717, +-32651734, +16645703, +32651734, +-16514203, +1820789, +-3570095, +1819248, +3570095, +13137179, +924575, +-1812018, +922942, +1812018, +14929699, +586684, +-1149186, +585016, +1149186, +15605516, +16704036, +-32766121, +16704011, +32766121, +-16630831, +3020875, +-5921138, +3016266, +5921138, +10740076, +1597573, +-3128764, +1592487, +3128764, +13587156, +1030124, +-2015487, +1024848, +2015487, +14722244, +16736565, +-32829909, +16736521, +32829909, +-16695869, +4762013, +-9328185, +4748922, +9328185, +7266281, +2679566, +-5241091, +2664205, +5241091, +11433446, +1773912, +-3463620, +1757564, +3463620, +13245740, +16754673, +-32865395, +16754593, +32865395, +-16732050, +7001772, +-13700468, +6967104, +13700468, +2808340, +4293363, +-8378312, +4249089, +8378312, +8234764, +2960061, +-5758307, +2911058, +5758307, +10906097, +16764756, +-32885110, +16764611, +32885110, +-16752151, +9480188, +-18513215, +9395722, +18513215, +-2098694, +6453693, +-12542171, +6334194, +12542171, +3989330, +4710641, +-9103264, +4570966, +9103264, +7495609, +16770393, +-32896054, +16770133, +32896054, +-16763310, +11816276, +-22992949, +11627131, +22992949, +-6666191, +8975726, +-17314789, +8678280, +17314789, +-876790, +7032408, +-13430164, +6660869, +13430164, +3083940, +16773593, +-32902127, +16773124, +32902127, +-16769502, +13735615, +-26557052, +13341720, +26557052, +-10300120, +11528165, +-21946606, +10848400, +21946606, +-5599349, +9752563, +-18238110, +8842853, +18238110, +-1818200, +16775499, +-32905496, +16774654, +32905496, +-16772937, +15200588, +-29054957, +14423589, +29054957, +-12846961, +13857004, +-25769995, +12417855, +25769995, +-9497643, +12624453, +-22756500, +10577873, +22756500, +-6425111, +16776792, +-32907365, +16775267, +32907365, +-16774843, +16366344, +-30654089, +14888294, +30654089, +-14477422, +15978753, +-28526295, +13106406, +28526295, +-12307942, +15588057, +-26381456, +11310242, +26381456, +-10121083, +16777933, +-32908402, +16775183, +32908402, +-16775900, +17493802, +-31619287, +14744943, +31619287, +-15461529, +18212414, +-30325234, +12706927, +30325234, +-14142124, +18982725, +-28938081, +10522286, +28938081, +-12727795, +16779330, +-32908977, +16774372, +32908977, +-16776486, +18928041, +-32181280, +13883707, +32181280, +-16034532, +21163118, +-31424334, +10876855, +31424334, +-15262757, +23650871, +-30581814, +7530076, +30581814, +-14403731, +16781484, +-32909296, +16772543, +32909296, +-16776811, +21161996, +-32501674, +11976424, +32501674, +-16361204, +25812009, +-32068975, +6885234, +32068975, +-15920027, +31103385, +-31576594, +1091831, +31576594, +-15418000, +16785164, +-32909473, +16769043, +32909473, +-16776991, +24988064, +-32682139, +8334356, +32682139, +-16545204, +33795794, +-32438043, +-722252, +32438043, +-16296325, +43946128, +-32156738, +-11159403, +32156738, +-16009510, +16791687, +-32909571, +16762620, +32909571, +-16777091, +31772521, +-32783096, +1652834, +32783096, +-16648139, +47961229, +-32646423, +-14675225, +32646423, +-16508788, +66751682, +-32487785, +-33627423, +32487785, +-16347043, +16312500, +-30141556, +16312485, +30141556, +-15847769, +547598, +-1011354, +547084, +1011354, +15682534, +266448, +-491849, +265925, +491849, +16244843, +166111, +-306446, +165584, +306446, +16445521, +16516262, +-30518049, +16516236, +30518049, +-16255282, +962487, +-1776938, +960857, +1776938, +14853872, +474742, +-875656, +473062, +875656, +15829413, +297520, +-548176, +295821, +548176, +16183875, +16631492, +-30730944, +16631443, +30730944, +-16485719, +1660146, +-3062870, +1655081, +3062870, +13461989, +838172, +-1543806, +832831, +1543806, +15106213, +530135, +-974532, +524691, +974532, +15722390, +16696110, +-30850307, +16696022, +30850307, +-16614916, +2776642, +-5116472, +2761387, +5116472, +11239187, +1456845, +-2676477, +1440152, +2676477, +13880219, +936151, +-1713835, +918891, +1713835, +14922174, +16732191, +-30916910, +16732031, +30916910, +-16687006, +4430219, +-8145523, +4386431, +8145523, +7960566, +2467715, +-4512857, +2416966, +4512857, +11892535, +1628113, +-2958725, +1574387, +2958725, +13574716, +16752314, +-30953973, +16752025, +30953973, +-16727123, +6622193, +-12127616, +6504644, +12127616, +3650379, +4016541, +-7285135, +3868831, +7285135, +8891843, +2762675, +-4954883, +2600451, +4954883, +11414089, +16763575, +-30974568, +16763055, +30974568, +-16749415, +9150431, +-16639142, +8859646, +16639142, +-1232861, +6182181, +-11049974, +5778225, +11049974, +4816810, +4518629, +-7917532, +4051247, +7917532, +8207341, +16769973, +-30986002, +16769034, +30986002, +-16761791, +11676269, +-20964630, +11015683, +20964630, +-5914736, +8893418, +-15489638, +7872446, +15489638, +11352, +7041383, +-11845939, +5780568, +11845939, +3955265, +16773783, +-30992347, +16772092, +30992347, +-16768659, +13953280, +-24496534, +12561577, +24496534, +-9737641, +11970921, +-19931025, +9602265, +19931025, +-4795970, +10413172, +-16343421, +7276820, +16343421, +-912775, +16776368, +-30995868, +16773318, +30995868, +-16772470, +16007790, +-27021343, +13239901, +27021343, +-12470475, +15365527, +-23700024, +10287194, +23700024, +-8875505, +14785947, +-20702857, +7622664, +20702857, +-5631395, +16778650, +-30997821, +16773150, +30997821, +-16774583, +18157056, +-28659649, +12863925, +28659649, +-14243764, +19443806, +-26476950, +9214638, +26476950, +-11881228, +20728681, +-24297433, +5570671, +24297433, +-9522136, +16781445, +-30998904, +16771527, +30998904, +-16775756, +20988060, +-29656921, +11112361, +29656921, +-15323204, +25187743, +-28317150, +5462520, +28317150, +-13873047, +29672124, +-26886556, +-570326, +26886556, +-12324582, +16785752, +-30999505, +16767870, +30999505, +-16776406, +25444300, +-30240545, +7287830, +30240545, +-15954914, +34434980, +-29452472, +-2555854, +29452472, +-15101910, +44441242, +-28575379, +-13511474, +28575379, +-14152552, +16793112, +-30999838, +16760871, +30999838, +-16776767, +33095506, +-30574250, +-2177, +30574250, +-16316113, +50408034, +-30122291, +-17803902, +30122291, +-15826917, +70153765, +-29606811, +-38107583, +29606811, +-15268966, +16806158, +-31000023, +16748025, +31000023, +-16776967, +46670155, +-30762526, +-13373037, +30762526, +-16519902, +78789544, +-30507092, +-45768905, +30507092, +-16243423, +115942747, +-30211627, +-83241918, +30211627, +-15923613, +15872835, +-22447559, +15872807, +22447559, +-14968426, +277754, +-392434, +277231, +392434, +16222231, +133061, +-187804, +132533, +187804, +16511622, +81763, +-115256, +81233, +115256, +16614220, +16263303, +-22999746, +16263250, +22999746, +-15749337, +494605, +-698290, +492927, +698290, +15789685, +238776, +-336475, +237071, +336475, +16301369, +147228, +-207000, +145514, +207000, +16484473, +16488278, +-23317877, +16488181, +23317877, +-16199242, +872378, +-1229961, +867049, +1229961, +15037788, +426889, +-599839, +421411, +599839, +15928916, +264840, +-370629, +259308, +370629, +16253068, +16615785, +-23498145, +16615609, +23498145, +-16454179, +1513757, +-2129016, +1497126, +2129016, +13766333, +758261, +-1060001, +740806, +1060001, +15278149, +475551, +-659971, +457789, +659971, +15843875, +16687407, +-23599332, +16687089, +23599332, +-16597280, +2557116, +-3580647, +2506684, +3580647, +11713416, +1332007, +-1845009, +1277230, +1845009, +14167979, +851272, +-1163942, +794791, +1163942, +15131154, +16727486, +-23655830, +16726910, +23655830, +-16677180, +4144749, +-5758163, +3998523, +5758163, +8633944, +2297803, +-3131069, +2130198, +3131069, +12349216, +1516025, +-2019070, +1339371, +2019070, +13921820, +16749957, +-23687282, +16748918, +23687282, +-16721660, +6342807, +-8688774, +5944975, +8688774, +4489434, +3855722, +-5104454, +3363066, +5104454, +9558428, +2678286, +-3407564, +2140738, +3407564, +11958192, +16762736, +-23704762, +16760861, +23704762, +-16746380, +9059948, +-12106014, +8060542, +12106014, +-343274, +6231959, +-7847669, +4866322, +7847669, +5678936, +4678573, +-5508603, +3111768, +5508603, +8986875, +16770353, +-23714468, +16766970, +23714468, +-16760107, +12100768, +-15483427, +9796104, +15483427, +-5119656, +9659401, +-11180047, +6151573, +11180047, +966242, +8066003, +-8371376, +3772911, +8371376, +4938302, +16775520, +-23719855, +16769420, +23719855, +-16767725, +15410647, +-18317794, +10494626, +18317794, +-9128056, +14477439, +-14624233, +6204350, +14624233, +-3904573, +13754127, +-11761419, +2879032, +11761419, +144058, +16780083, +-23722844, +16769084, +23722844, +-16771951, +19348947, +-20387735, +9483664, +20387735, +-12055395, +21467038, +-17637855, +3476656, +17637855, +-8166478, +23369301, +-15168181, +-1918253, +15168181, +-4673832, +16785673, +-23724501, +16765839, +23724501, +-16774296, +24868547, +-21750945, +5891934, +21750945, +-13983265, +32392543, +-19913847, +-4230110, +19913847, +-11385217, +39938641, +-18071353, +-14381888, +18071353, +-8779537, +16794288, +-23725421, +16758525, +23725421, +-16775596, +33739025, +-22588636, +-1793870, +22588636, +-15167940, +50727371, +-21448926, +-20394010, +21448926, +-13556146, +69082218, +-20217540, +-40490298, +20217540, +-11814703, +16809008, +-23725931, +16744526, +23725931, +-16776318, +49053594, +-23081666, +-16411189, +23081666, +-15865189, +82824296, +-22406908, +-51136143, +22406908, +-14910937, +121049915, +-21643138, +-90441895, +21643138, +-13830803, +16835099, +-23726214, +16718835, +23726214, +-16776718, +76261468, +-23364505, +-43219068, +23364505, +-16265184, +140080603, +-22976059, +-107587549, +22976059, +-15715838, +214348557, +-22524014, +-182494791, +22524014, +-15076550, +15060971, +0, +15060916, +0, +-13344671, +137715, +0, +137188, +0, +16502313, +62494, +0, +61964, +0, +16652757, +35499, +0, +34969, +0, +16706748, +15779939, +0, +15779837, +0, +-14782560, +247059, +0, +245356, +0, +16284801, +112726, +0, +111009, +0, +16553481, +64282, +0, +62560, +0, +16650375, +16209124, +0, +16208934, +0, +-15640842, +441483, +0, +436010, +0, +15899723, +203401, +0, +197848, +0, +16375967, +116794, +0, +111212, +0, +16549211, +16457433, +0, +16457085, +0, +-16137302, +783510, +0, +766083, +0, +15227623, +367229, +0, +349349, +0, +16060638, +213457, +0, +195409, +0, +16368349, +16598556, +0, +16597923, +0, +-16419263, +1374332, +0, +1319705, +0, +14083179, +663683, +0, +606536, +0, +15506996, +394077, +0, +335974, +0, +16047166, +16678051, +0, +16676903, +0, +-16577738, +2365010, +0, +2198183, +0, +12214023, +1201479, +0, +1021183, +0, +14554554, +739666, +0, +554025, +0, +15483525, +16722789, +0, +16720714, +0, +-16666287, +3953214, +0, +3464275, +0, +9359728, +2180807, +0, +1624291, +0, +12972118, +1423936, +0, +838564, +0, +14514716, +16748282, +0, +16744535, +0, +-16715601, +6353786, +0, +5003926, +0, +5419504, +3973108, +0, +2314944, +0, +10489165, +2837511, +0, +1032284, +0, +12907421, +16763496, +0, +16756735, +0, +-16743015, +9775978, +0, +6325602, +0, +675636, +7269959, +0, +2584558, +0, +6922698, +5878520, +0, +507384, +0, +10391313, +16773826, +0, +16761629, +0, +-16758239, +14526462, +0, +6429723, +0, +-4178968, +13351086, +0, +1026117, +0, +2400012, +12566846, +0, +-2579306, +0, +6789676, +16782950, +0, +16760954, +0, +-16766688, +21346996, +0, +3817102, +0, +-8386881, +24548257, +0, +-5261828, +0, +-2509212, +27136746, +0, +-12602908, +0, +2243378, +16794129, +0, +16754463, +0, +-16771376, +31942756, +0, +-3625011, +0, +-11540529, +45048673, +0, +-21256423, +0, +-7015034, +57646140, +0, +-38203817, +0, +-2665107, +16811358, +0, +16739835, +0, +-16773977, +49676415, +0, +-19243342, +0, +-13655857, +82339984, +0, +-55005915, +0, +-10556853, +118287496, +0, +-94363999, +0, +-7146281, +16840799, +0, +16711837, +0, +-16775419, +80691383, +0, +-48942961, +0, +-14971206, +149858505, +0, +-120064524, +0, +-13016765, +233520652, +0, +-206090695, +0, +-10652741, +16892981, +0, +16660455, +0, +-16776220, +136000868, +0, +-103472696, +0, +-15750956, +271801469, +0, +-240442250, +0, +-14582003, +446309601, +0, +-416452524, +0, +-13079862, diff --git a/media/libeffects/testlibs/AudioShelvingFilter.cpp b/media/libeffects/testlibs/AudioShelvingFilter.cpp new file mode 100644 index 0000000..b8650ba --- /dev/null +++ b/media/libeffects/testlibs/AudioShelvingFilter.cpp @@ -0,0 +1,109 @@ +/* /android/src/frameworks/base/libs/audioflinger/AudioShelvingFilter.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 "AudioShelvingFilter.h" +#include "AudioCommon.h" +#include "EffectsMath.h" + +#include +#include + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { +// Format of the coefficient tables: +// kCoefTable[freq][gain][coef] +// freq - cutoff frequency, in octaves below Nyquist,from -10 to -6 in low +// shelf, -2 to 0 in high shelf. +// gain - gain, in millibel, starting at -9600, jumps of 1024, to 4736 millibel. +// coef - 0: b0 +// 1: b1 +// 2: b2 +// 3: -a1 +// 4: -a2 +static const size_t kHiInDims[2] = {3, 15}; +static const audio_coef_t kHiCoefTable[3*15*5] = { +#include "AudioHighShelfFilterCoef.inl" +}; +static const size_t kLoInDims[2] = {5, 15}; +static const audio_coef_t kLoCoefTable[5*15*5] = { +#include "AudioLowShelfFilterCoef.inl" +}; + +AudioCoefInterpolator AudioShelvingFilter::mHiCoefInterp(2, kHiInDims, 5, (const audio_coef_t*) kHiCoefTable); +AudioCoefInterpolator AudioShelvingFilter::mLoCoefInterp(2, kLoInDims, 5, (const audio_coef_t*) kLoCoefTable); + +AudioShelvingFilter::AudioShelvingFilter(ShelfType type, int nChannels, + int sampleRate) + : mType(type), + mBiquad(nChannels, sampleRate) { + configure(nChannels, sampleRate); +} + +void AudioShelvingFilter::configure(int nChannels, int sampleRate) { + mNiquistFreq = sampleRate * 500; + mFrequencyFactor = ((1ull) << 42) / mNiquistFreq; + mBiquad.configure(nChannels, sampleRate); + setFrequency(mNominalFrequency); + commit(true); +} + +void AudioShelvingFilter::reset() { + setGain(0); + setFrequency(mType == kLowShelf ? 0 : mNiquistFreq); + commit(true); +} + +void AudioShelvingFilter::setFrequency(uint32_t millihertz) { + mNominalFrequency = millihertz; + if (UNLIKELY(millihertz > mNiquistFreq / 2)) { + millihertz = mNiquistFreq / 2; + } + uint32_t normFreq = static_cast( + (static_cast(millihertz) * mFrequencyFactor) >> 10); + uint32_t log2minFreq = (mType == kLowShelf ? (32-10) : (32-2)); + if (LIKELY(normFreq > (1U << log2minFreq))) { + mFrequency = (Effects_log2(normFreq) - (log2minFreq << 15)) << (FREQ_PRECISION_BITS - 15); + } else { + mFrequency = 0; + } +} + +void AudioShelvingFilter::setGain(int32_t millibel) { + mGain = millibel + 9600; +} + +void AudioShelvingFilter::commit(bool immediate) { + audio_coef_t coefs[5]; + int intCoord[2] = { + mFrequency >> FREQ_PRECISION_BITS, + mGain >> GAIN_PRECISION_BITS + }; + uint32_t fracCoord[2] = { + mFrequency << (32 - FREQ_PRECISION_BITS), + static_cast(mGain) << (32 - GAIN_PRECISION_BITS) + }; + if (mType == kHighShelf) { + mHiCoefInterp.getCoef(intCoord, fracCoord, coefs); + } else { + mLoCoefInterp.getCoef(intCoord, fracCoord, coefs); + } + mBiquad.setCoefs(coefs, immediate); +} + +} diff --git a/media/libeffects/testlibs/AudioShelvingFilter.h b/media/libeffects/testlibs/AudioShelvingFilter.h new file mode 100644 index 0000000..f72d793 --- /dev/null +++ b/media/libeffects/testlibs/AudioShelvingFilter.h @@ -0,0 +1,146 @@ +/* /android/src/frameworks/base/libs/audioflinger/AudioShelvingFilter.h +** +** 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. +*/ + +#ifndef AUDIO_SHELVING_FILTER_H +#define AUDIO_SHELVING_FILTER_H + +#include "AudioBiquadFilter.h" +#include "AudioCoefInterpolator.h" + +namespace android { + +// A shelving audio filter, with unity skirt gain, and controllable cutoff +// frequency and gain. +// This filter is able to suppress introduce discontinuities and other artifacts +// in the output, even when changing parameters abruptly. +// Parameters can be set to any value - this class will make sure to clip them +// when they are out of supported range. +// +// Implementation notes: +// This class uses an underlying biquad filter whose parameters are determined +// using a linear interpolation from a coefficient table, using a +// AudioCoefInterpolator. +// All is left for this class to do is mapping between high-level parameters to +// fractional indices into the coefficient table. +class AudioShelvingFilter { +public: + // Shelf type + enum ShelfType { + kLowShelf, + kHighShelf + }; + + // Constructor. Resets the filter (see reset()). + // type Type of the filter (high shelf or low shelf). + // nChannels Number of input/output channels (interlaced). + // sampleRate The input/output sample rate, in Hz. + AudioShelvingFilter(ShelfType type, int nChannels, int sampleRate); + + // Reconfiguration of the filter. Changes input/output format, but does not + // alter current parameter values. Clears delay lines. + // nChannels Number of input/output channels (interlaced). + // sampleRate The input/output sample rate, in Hz. + void configure(int nChannels, int sampleRate); + + // Resets the filter parameters to the following values: + // frequency: 0 + // gain: 0 + // It also disables the filter. Does not clear the delay lines. + void reset(); + + // Clears delay lines. Does not alter parameter values. + void clear() { mBiquad.clear(); } + + // Sets gain value. Actual change will only take place upon commit(). + // This value will be remembered even if the filter is in disabled() state. + // millibel Gain value in millibel (1/100 of decibel). + void setGain(int32_t millibel); + + // Gets the gain, in millibel, as set. + int32_t getGain() const { return mGain - 9600; } + + // Sets cutoff frequency value. Actual change will only take place upon + // commit(). + // This value will be remembered even if the filter is in disabled() state. + // millihertz Frequency value in mHz. + void setFrequency(uint32_t millihertz); + + // Gets the frequency, in mHz, as set. + uint32_t getFrequency() const { return mNominalFrequency; } + + // Applies all parameter changes done to this point in time. + // If the filter is disabled, the new parameters will take place when it is + // enabled again. Does not introduce artifacts, unless immediate is set. + // immediate Whether to apply change abruptly (ignored if filter is + // disabled). + void commit(bool immediate = false); + + // Process a buffer of input data. The input and output should contain + // frameCount * nChannels interlaced samples. Processing can be done + // in-place, by passing the same buffer as both arguments. + // in Input buffer. + // out Output buffer. + // frameCount Number of frames to produce. + void process(const audio_sample_t in[], audio_sample_t out[], + int frameCount) { mBiquad.process(in, out, frameCount); } + + // Enables the filter, so it would start processing input. Does not + // introduce artifacts, unless immediate is set. + // immediate Whether to apply change abruptly. + void enable(bool immediate = false) { mBiquad.enable(immediate); } + + // Disabled (bypasses) the filter. Does not introduce artifacts, unless + // immediate is set. + // immediate Whether to apply change abruptly. + void disable(bool immediate = false) { mBiquad.disable(immediate); } + +private: + // Precision for the mFrequency member. + static const int FREQ_PRECISION_BITS = 26; + // Precision for the mGain member. + static const int GAIN_PRECISION_BITS = 10; + + // Shelf type. + ShelfType mType; + // Nyquist, in mHz. + uint32_t mNiquistFreq; + // Fractional index into the gain dimension of the coef table in + // GAIN_PRECISION_BITS precision. + int32_t mGain; + // Fractional index into the frequency dimension of the coef table in + // FREQ_PRECISION_BITS precision. + uint32_t mFrequency; + // Nominal value of frequency, as set. + uint32_t mNominalFrequency; + // 1/Nyquist[mHz], in 42-bit precision (very small). + // Used for scaling the frequency. + uint32_t mFrequencyFactor; + + // A biquad filter, used for the actual processing. + AudioBiquadFilter mBiquad; + // A coefficient interpolator, used for mapping the high level parameters to + // the low-level biquad coefficients. This one is used for the high shelf. + static AudioCoefInterpolator mHiCoefInterp; + // A coefficient interpolator, used for mapping the high level parameters to + // the low-level biquad coefficients. This one is used for the low shelf. + static AudioCoefInterpolator mLoCoefInterp; +}; + +} + + +#endif // AUDIO_SHELVING_FILTER_H diff --git a/media/libeffects/testlibs/EffectEqualizer.cpp b/media/libeffects/testlibs/EffectEqualizer.cpp new file mode 100644 index 0000000..0eb2bdf --- /dev/null +++ b/media/libeffects/testlibs/EffectEqualizer.cpp @@ -0,0 +1,654 @@ +/* + * Copyright (C) 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. + */ + +#define LOG_TAG "Equalizer" +#define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) +// +#define LOG_NDEBUG 0 +#include +#include +#include +#include +#include +#include "AudioEqualizer.h" +#include "AudioBiquadFilter.h" +#include "AudioFormatAdapter.h" +#include + +// effect_interface_t interface implementation for equalizer effect +extern "C" const struct effect_interface_s gEqualizerInterface; + +enum equalizer_state_e { + EQUALIZER_STATE_UNINITIALIZED, + EQUALIZER_STATE_INITIALIZED, + EQUALIZER_STATE_ACTIVE, +}; + +namespace android { +namespace { + +// Google Graphic Equalizer UUID: e25aa840-543b-11df-98a5-0002a5d5c51b +const effect_descriptor_t gEqualizerDescriptor = { + {0x0bed4300, 0xddd6, 0x11db, 0x8f34, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type + {0xe25aa840, 0x543b, 0x11df, 0x98a5, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid + EFFECT_API_VERSION, + (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST), + 0, // TODO + 1, + "Graphic Equalizer", + "Google Inc.", +}; + +/////////////////// BEGIN EQ PRESETS /////////////////////////////////////////// +const int kNumBands = 5; +const uint32_t gFreqs[kNumBands] = { 50000, 125000, 900000, 3200000, 6300000 }; +const uint32_t gBandwidths[kNumBands] = { 0, 3600, 3600, 2400, 0 }; + +const AudioEqualizer::BandConfig gBandsClassic[kNumBands] = { + { 300, gFreqs[0], gBandwidths[0] }, + { 400, gFreqs[1], gBandwidths[1] }, + { 0, gFreqs[2], gBandwidths[2] }, + { 200, gFreqs[3], gBandwidths[3] }, + { -300, gFreqs[4], gBandwidths[4] } +}; + +const AudioEqualizer::BandConfig gBandsJazz[kNumBands] = { + { -600, gFreqs[0], gBandwidths[0] }, + { 200, gFreqs[1], gBandwidths[1] }, + { 400, gFreqs[2], gBandwidths[2] }, + { -400, gFreqs[3], gBandwidths[3] }, + { -600, gFreqs[4], gBandwidths[4] } +}; + +const AudioEqualizer::BandConfig gBandsPop[kNumBands] = { + { 400, gFreqs[0], gBandwidths[0] }, + { -400, gFreqs[1], gBandwidths[1] }, + { 300, gFreqs[2], gBandwidths[2] }, + { -400, gFreqs[3], gBandwidths[3] }, + { 600, gFreqs[4], gBandwidths[4] } +}; + +const AudioEqualizer::BandConfig gBandsRock[kNumBands] = { + { 700, gFreqs[0], gBandwidths[0] }, + { 400, gFreqs[1], gBandwidths[1] }, + { -400, gFreqs[2], gBandwidths[2] }, + { 400, gFreqs[3], gBandwidths[3] }, + { 200, gFreqs[4], gBandwidths[4] } +}; + +const AudioEqualizer::PresetConfig gEqualizerPresets[] = { + { "Classic", gBandsClassic }, + { "Jazz", gBandsJazz }, + { "Pop", gBandsPop }, + { "Rock", gBandsRock } +}; + +/////////////////// END EQ PRESETS ///////////////////////////////////////////// + +static const size_t kBufferSize = 32; + +typedef AudioFormatAdapter FormatAdapter; + +struct EqualizerContext { + const struct effect_interface_s *itfe; + effect_config_t config; + FormatAdapter adapter; + AudioEqualizer * pEqualizer; + uint32_t state; +}; + +//--- local function prototypes + +int Equalizer_init(EqualizerContext *pContext); +int Equalizer_configure(EqualizerContext *pContext, effect_config_t *pConfig); +int Equalizer_getParameter(AudioEqualizer * pEqualizer, int32_t *pParam, size_t *pValueSize, void *pValue); +int Equalizer_setParameter(AudioEqualizer * pEqualizer, int32_t *pParam, void *pValue); + + +// +//--- Effect Library Interface Implementation +// + +extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) { + *pNumEffects = 1; + return 0; +} /* end EffectQueryNumberEffects */ + +extern "C" int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) { + if (pDescriptor == NULL) { + return -EINVAL; + } + if (index > 0) { + return -EINVAL; + } + memcpy(pDescriptor, &gEqualizerDescriptor, sizeof(effect_descriptor_t)); + return 0; +} /* end EffectQueryNext */ + +extern "C" int EffectCreate(effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_interface_t *pInterface) { + int ret; + int i; + + LOGV("EffectLibCreateEffect start"); + + if (pInterface == NULL || uuid == NULL) { + return -EINVAL; + } + + if (memcmp(uuid, &gEqualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) { + return -EINVAL; + } + + EqualizerContext *pContext = new EqualizerContext; + + pContext->itfe = &gEqualizerInterface; + pContext->pEqualizer = NULL; + pContext->state = EQUALIZER_STATE_UNINITIALIZED; + + ret = Equalizer_init(pContext); + if (ret < 0) { + LOGW("EffectLibCreateEffect() init failed"); + delete pContext; + return ret; + } + + *pInterface = (effect_interface_t)pContext; + pContext->state = EQUALIZER_STATE_INITIALIZED; + + LOGV("EffectLibCreateEffect %p, size %d", pContext, AudioEqualizer::GetInstanceSize(kNumBands)+sizeof(EqualizerContext)); + + return 0; + +} /* end EffectCreate */ + +extern "C" int EffectRelease(effect_interface_t interface) { + EqualizerContext * pContext = (EqualizerContext *)interface; + + LOGV("EffectLibReleaseEffect %p", interface); + if (pContext == NULL) { + return -EINVAL; + } + + pContext->state = EQUALIZER_STATE_UNINITIALIZED; + pContext->pEqualizer->free(); + delete pContext; + + return 0; +} /* end EffectRelease */ + + +// +//--- local functions +// + +#define CHECK_ARG(cond) { \ + if (!(cond)) { \ + LOGV("Invalid argument: "#cond); \ + return -EINVAL; \ + } \ +} + +//---------------------------------------------------------------------------- +// Equalizer_configure() +//---------------------------------------------------------------------------- +// Purpose: Set input and output audio configuration. +// +// Inputs: +// pContext: effect engine context +// pConfig: pointer to effect_config_t structure holding input and output +// configuration parameters +// +// Outputs: +// +//---------------------------------------------------------------------------- + +int Equalizer_configure(EqualizerContext *pContext, effect_config_t *pConfig) +{ + LOGV("Equalizer_configure start"); + + CHECK_ARG(pContext != NULL); + CHECK_ARG(pConfig != NULL); + + CHECK_ARG(pConfig->inputCfg.samplingRate == pConfig->outputCfg.samplingRate); + CHECK_ARG(pConfig->inputCfg.channels == pConfig->outputCfg.channels); + CHECK_ARG(pConfig->inputCfg.format == pConfig->outputCfg.format); + CHECK_ARG((pConfig->inputCfg.channels == CHANNEL_MONO) || (pConfig->inputCfg.channels == CHANNEL_STEREO)); + CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE + || pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE); + CHECK_ARG(pConfig->inputCfg.format == SAMPLE_FORMAT_PCM_S7_24 + || pConfig->inputCfg.format == SAMPLE_FORMAT_PCM_S15); + + int channelCount; + if (pConfig->inputCfg.channels == CHANNEL_MONO) { + channelCount = 1; + } else { + channelCount = 2; + } + CHECK_ARG(channelCount <= AudioBiquadFilter::MAX_CHANNELS); + + memcpy(&pContext->config, pConfig, sizeof(effect_config_t)); + + pContext->pEqualizer->configure(channelCount, + pConfig->inputCfg.samplingRate); + + pContext->adapter.configure(*pContext->pEqualizer, channelCount, + pConfig->inputCfg.format, + pConfig->outputCfg.accessMode); + + return 0; +} // end Equalizer_configure + + +//---------------------------------------------------------------------------- +// Equalizer_init() +//---------------------------------------------------------------------------- +// Purpose: Initialize engine with default configuration and creates +// AudioEqualizer instance. +// +// Inputs: +// pContext: effect engine context +// +// Outputs: +// +//---------------------------------------------------------------------------- + +int Equalizer_init(EqualizerContext *pContext) +{ + int status; + + LOGV("Equalizer_init start"); + + CHECK_ARG(pContext != NULL); + + if (pContext->pEqualizer != NULL) { + pContext->pEqualizer->free(); + } + + pContext->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + pContext->config.inputCfg.channels = CHANNEL_STEREO; + pContext->config.inputCfg.format = SAMPLE_FORMAT_PCM_S15; + pContext->config.inputCfg.samplingRate = 44100; + pContext->config.inputCfg.bufferProvider.getBuffer = NULL; + pContext->config.inputCfg.bufferProvider.releaseBuffer = NULL; + pContext->config.inputCfg.bufferProvider.cookie = NULL; + pContext->config.inputCfg.mask = EFFECT_CONFIG_ALL; + pContext->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; + pContext->config.outputCfg.channels = CHANNEL_STEREO; + pContext->config.outputCfg.format = SAMPLE_FORMAT_PCM_S15; + pContext->config.outputCfg.samplingRate = 44100; + pContext->config.outputCfg.bufferProvider.getBuffer = NULL; + pContext->config.outputCfg.bufferProvider.releaseBuffer = NULL; + pContext->config.outputCfg.bufferProvider.cookie = NULL; + pContext->config.outputCfg.mask = EFFECT_CONFIG_ALL; + + pContext->pEqualizer = AudioEqualizer::CreateInstance( + NULL, + kNumBands, + AudioBiquadFilter::MAX_CHANNELS, + 44100, + gEqualizerPresets, + ARRAY_SIZE(gEqualizerPresets)); + + for (int i = 0; i < kNumBands; ++i) { + pContext->pEqualizer->setFrequency(i, gFreqs[i]); + pContext->pEqualizer->setBandwidth(i, gBandwidths[i]); + } + + pContext->pEqualizer->enable(true); + + Equalizer_configure(pContext, &pContext->config); + + return 0; +} // end Equalizer_init + + +//---------------------------------------------------------------------------- +// Equalizer_getParameter() +//---------------------------------------------------------------------------- +// Purpose: +// Get a Equalizer parameter +// +// Inputs: +// pEqualizer - handle to instance data +// pParam - pointer to parameter +// pValue - pointer to variable to hold retrieved value +// pValueSize - pointer to value size: maximum size as input +// +// Outputs: +// *pValue updated with parameter value +// *pValueSize updated with actual value size +// +// +// Side Effects: +// +//---------------------------------------------------------------------------- + +int Equalizer_getParameter(AudioEqualizer * pEqualizer, int32_t *pParam, size_t *pValueSize, void *pValue) +{ + int status = 0; + int32_t param = *pParam++; + int32_t param2; + char *name; + + switch (param) { + case EQ_PARAM_NUM_BANDS: + case EQ_PARAM_CUR_PRESET: + case EQ_PARAM_GET_NUM_OF_PRESETS: + if (*pValueSize < sizeof(int16_t)) { + return -EINVAL; + } + *pValueSize = sizeof(int16_t); + break; + + case EQ_PARAM_LEVEL_RANGE: + case EQ_PARAM_BAND_FREQ_RANGE: + if (*pValueSize < 2 * sizeof(int32_t)) { + return -EINVAL; + } + *pValueSize = 2 * sizeof(int32_t); + break; + case EQ_PARAM_BAND_LEVEL: + case EQ_PARAM_GET_BAND: + case EQ_PARAM_CENTER_FREQ: + if (*pValueSize < sizeof(int32_t)) { + return -EINVAL; + } + *pValueSize = sizeof(int32_t); + break; + + case EQ_PARAM_GET_PRESET_NAME: + break; + + default: + return -EINVAL; + } + + switch (param) { + case EQ_PARAM_NUM_BANDS: + *(int16_t *)pValue = kNumBands; + LOGV("Equalizer_getParameter() EQ_PARAM_NUM_BANDS %d", *(int16_t *)pValue); + break; + + case EQ_PARAM_LEVEL_RANGE: + *(int32_t *)pValue = -9600; + *((int32_t *)pValue + 1) = 4800; + LOGV("Equalizer_getParameter() EQ_PARAM_LEVEL_RANGE min %d, max %d", *(int32_t *)pValue, *((int32_t *)pValue + 1)); + break; + + case EQ_PARAM_BAND_LEVEL: + param2 = *pParam; + if (param2 >= kNumBands) { + status = -EINVAL; + break; + } + *(int32_t *)pValue = pEqualizer->getGain(param2); + LOGV("Equalizer_getParameter() EQ_PARAM_BAND_LEVEL band %d, level %d", param2, *(int32_t *)pValue); + break; + + case EQ_PARAM_CENTER_FREQ: + param2 = *pParam; + if (param2 >= kNumBands) { + status = -EINVAL; + break; + } + *(int32_t *)pValue = pEqualizer->getFrequency(param2); + LOGV("Equalizer_getParameter() EQ_PARAM_CENTER_FREQ band %d, frequency %d", param2, *(int32_t *)pValue); + break; + + case EQ_PARAM_BAND_FREQ_RANGE: + param2 = *pParam; + if (param2 >= kNumBands) { + status = -EINVAL; + break; + } + pEqualizer->getBandRange(param2, *(uint32_t *)pValue, *((uint32_t *)pValue + 1)); + LOGV("Equalizer_getParameter() EQ_PARAM_BAND_FREQ_RANGE band %d, min %d, max %d", param2, *(int32_t *)pValue, *((int32_t *)pValue + 1)); + break; + + case EQ_PARAM_GET_BAND: + param2 = *pParam; + *(int32_t *)pValue = pEqualizer->getMostRelevantBand(param2); + LOGV("Equalizer_getParameter() EQ_PARAM_GET_BAND frequency %d, band %d", param2, *(int32_t *)pValue); + break; + + case EQ_PARAM_CUR_PRESET: + *(int16_t *)pValue = pEqualizer->getPreset(); + LOGV("Equalizer_getParameter() EQ_PARAM_CUR_PRESET %d", *(int32_t *)pValue); + break; + + case EQ_PARAM_GET_NUM_OF_PRESETS: + *(int16_t *)pValue = pEqualizer->getNumPresets(); + LOGV("Equalizer_getParameter() EQ_PARAM_GET_NUM_OF_PRESETS %d", *(int16_t *)pValue); + break; + + case EQ_PARAM_GET_PRESET_NAME: + param2 = *pParam; + if (param2 >= pEqualizer->getNumPresets()) { + status = -EINVAL; + break; + } + name = (char *)pValue; + strncpy(name, pEqualizer->getPresetName(param2), *pValueSize - 1); + name[*pValueSize - 1] = 0; + *pValueSize = strlen(name) + 1; + LOGV("Equalizer_getParameter() EQ_PARAM_GET_PRESET_NAME preset %d, name %s len %d", param2, gEqualizerPresets[param2].name, *pValueSize); + break; + + default: + LOGV("Equalizer_getParameter() invalid param %d", param); + status = -EINVAL; + break; + } + + return status; +} // end Equalizer_getParameter + + +//---------------------------------------------------------------------------- +// Equalizer_setParameter() +//---------------------------------------------------------------------------- +// Purpose: +// Set a Equalizer parameter +// +// Inputs: +// pEqualizer - handle to instance data +// pParam - pointer to parameter +// pValue - pointer to value +// +// Outputs: +// +// +// Side Effects: +// +//---------------------------------------------------------------------------- + +int Equalizer_setParameter (AudioEqualizer * pEqualizer, int32_t *pParam, void *pValue) +{ + int status = 0; + int32_t preset; + int32_t band; + int32_t level; + int32_t param = *pParam++; + + + switch (param) { + case EQ_PARAM_CUR_PRESET: + preset = *(int16_t *)pValue; + + LOGV("setParameter() EQ_PARAM_CUR_PRESET %d", preset); + if (preset >= pEqualizer->getNumPresets()) { + status = -EINVAL; + break; + } + pEqualizer->setPreset(preset); + pEqualizer->commit(true); + break; + case EQ_PARAM_BAND_LEVEL: + band = *pParam; + level = *(int32_t *)pValue; + LOGV("setParameter() EQ_PARAM_BAND_LEVEL band %d, level %d", band, level); + if (band >= kNumBands) { + status = -EINVAL; + break; + } + pEqualizer->setGain(band, level); + pEqualizer->commit(true); + break; + default: + LOGV("setParameter() invalid param %d", param); + break; + } + + return status; +} // end Equalizer_setParameter + +} // namespace +} // namespace + + +// +//--- Effect Control Interface Implementation +// + +extern "C" int Equalizer_process(effect_interface_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) +{ + android::EqualizerContext * pContext = (android::EqualizerContext *) self; + + if (pContext == NULL) { + return -EINVAL; + } + if (inBuffer == NULL || inBuffer->raw == NULL || + outBuffer == NULL || outBuffer->raw == NULL || + inBuffer->frameCount != outBuffer->frameCount) { + return -EINVAL; + } + + if (pContext->state == EQUALIZER_STATE_UNINITIALIZED) { + return -EINVAL; + } + if (pContext->state == EQUALIZER_STATE_INITIALIZED) { + return -ENODATA; + } + + pContext->adapter.process(inBuffer->raw, outBuffer->raw, outBuffer->frameCount); + + return 0; +} // end Equalizer_process + +extern "C" int Equalizer_command(effect_interface_t self, int cmdCode, int cmdSize, + void *pCmdData, int *replySize, void *pReplyData) { + + android::EqualizerContext * pContext = (android::EqualizerContext *) self; + int retsize; + + if (pContext == NULL || pContext->state == EQUALIZER_STATE_UNINITIALIZED) { + return -EINVAL; + } + + android::AudioEqualizer * pEqualizer = pContext->pEqualizer; + + LOGV("Equalizer_command command %d cmdSize %d",cmdCode, cmdSize); + + switch (cmdCode) { + case EFFECT_CMD_INIT: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + *(int *) pReplyData = Equalizer_init(pContext); + break; + case EFFECT_CMD_CONFIGURE: + if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) + || pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + *(int *) pReplyData = Equalizer_configure(pContext, + (effect_config_t *) pCmdData); + break; + case EFFECT_CMD_RESET: + Equalizer_configure(pContext, &pContext->config); + break; + case EFFECT_CMD_GET_PARAM: { + if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) || + pReplyData == NULL || *replySize < (int) (sizeof(effect_param_t) + sizeof(int32_t))) { + return -EINVAL; + } + effect_param_t *p = (effect_param_t *)pCmdData; + memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + p->psize); + p = (effect_param_t *)pReplyData; + int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); + p->status = android::Equalizer_getParameter(pEqualizer, (int32_t *)p->data, &p->vsize, + p->data + voffset); + *replySize = sizeof(effect_param_t) + voffset + p->vsize; + LOGV("Equalizer_command EFFECT_CMD_GET_PARAM *pCmdData %d, *replySize %d, *pReplyData %08x %08x", + *(int32_t *)((char *)pCmdData + sizeof(effect_param_t)), *replySize, + *(int32_t *)((char *)pReplyData + sizeof(effect_param_t) + voffset), + *(int32_t *)((char *)pReplyData + sizeof(effect_param_t) + voffset + sizeof(int32_t))); + + } break; + case EFFECT_CMD_SET_PARAM: { + LOGV("Equalizer_command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %d, pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData); + if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) || + pReplyData == NULL || *replySize != sizeof(int32_t)) { + return -EINVAL; + } + effect_param_t *p = (effect_param_t *) pCmdData; + *(int *)pReplyData = android::Equalizer_setParameter(pEqualizer, (int32_t *)p->data, + p->data + p->psize); + } break; + case EFFECT_CMD_ENABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + if (pContext->state != EQUALIZER_STATE_INITIALIZED) { + return -ENOSYS; + } + pContext->state = EQUALIZER_STATE_ACTIVE; + LOGV("EFFECT_CMD_ENABLE() OK"); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_DISABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + if (pContext->state != EQUALIZER_STATE_ACTIVE) { + return -ENOSYS; + } + pContext->state = EQUALIZER_STATE_INITIALIZED; + LOGV("EFFECT_CMD_DISABLE() OK"); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_SET_DEVICE: + case EFFECT_CMD_SET_VOLUME: + case EFFECT_CMD_SET_AUDIO_MODE: + break; + default: + LOGW("Equalizer_command invalid command %d",cmdCode); + return -EINVAL; + } + + return 0; +} + +// effect_interface_t interface implementation for equalizer effect +const struct effect_interface_s gEqualizerInterface = { + Equalizer_process, + Equalizer_command +}; + + diff --git a/media/libeffects/testlibs/EffectReverb.c b/media/libeffects/testlibs/EffectReverb.c new file mode 100644 index 0000000..2ce7558 --- /dev/null +++ b/media/libeffects/testlibs/EffectReverb.c @@ -0,0 +1,2135 @@ +/* + * Copyright (C) 2008 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. + */ + +#define LOG_TAG "EffectReverb" +//#define LOG_NDEBUG 0 +#include +#include +#include +#include +#include "EffectReverb.h" +#include "EffectsMath.h" + +// effect_interface_t interface implementation for reverb effect +const struct effect_interface_s gReverbInterface = { + Reverb_Process, + Reverb_Command +}; + +// Google auxiliary environmental reverb UUID: 1f0ae2e0-4ef7-11df-bc09-0002a5d5c51b +static const effect_descriptor_t gAuxEnvReverbDescriptor = { + {0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}}, + {0x1f0ae2e0, 0x4ef7, 0x11df, 0xbc09, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + EFFECT_API_VERSION, + // flags other than EFFECT_FLAG_TYPE_AUXILIARY set for test purpose + EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_AUDIO_MODE_IND, + 0, // TODO + 33, + "Aux Environmental Reverb", + "Google Inc." +}; + +// Google insert environmental reverb UUID: aa476040-6342-11df-91a4-0002a5d5c51b +static const effect_descriptor_t gInsertEnvReverbDescriptor = { + {0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}}, + {0xaa476040, 0x6342, 0x11df, 0x91a4, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + EFFECT_API_VERSION, + EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST, + 0, // TODO + 33, + "Insert Environmental reverb", + "Google Inc." +}; + +// Google auxiliary preset reverb UUID: 63909320-53a6-11df-bdbd-0002a5d5c51b +static const effect_descriptor_t gAuxPresetReverbDescriptor = { + {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0x63909320, 0x53a6, 0x11df, 0xbdbd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + EFFECT_API_VERSION, + EFFECT_FLAG_TYPE_AUXILIARY, + 0, // TODO + 33, + "Aux Preset Reverb", + "Google Inc." +}; + +// Google insert preset reverb UUID: d93dc6a0-6342-11df-b128-0002a5d5c51b +static const effect_descriptor_t gInsertPresetReverbDescriptor = { + {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0xd93dc6a0, 0x6342, 0x11df, 0xb128, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + EFFECT_API_VERSION, + EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST, + 0, // TODO + 33, + "Insert Preset Reverb", + "Google Inc." +}; + +// gDescriptors contains pointers to all defined effect descriptor in this library +static const effect_descriptor_t * const gDescriptors[] = { + &gAuxEnvReverbDescriptor, + &gInsertEnvReverbDescriptor, + &gAuxPresetReverbDescriptor, + &gInsertPresetReverbDescriptor +}; + +/*---------------------------------------------------------------------------- + * Effect API implementation + *--------------------------------------------------------------------------*/ + +/*--- Effect Library Interface Implementation ---*/ + +int EffectQueryNumberEffects(uint32_t *pNumEffects) { + *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *); + return 0; +} + +int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) { + if (pDescriptor == NULL) { + return -EINVAL; + } + if (index >= sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)) { + return -EINVAL; + } + memcpy(pDescriptor, gDescriptors[index], + sizeof(effect_descriptor_t)); + return 0; +} + +int EffectCreate(effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_interface_t *pInterface) { + int ret; + int i; + reverb_module_t *module; + const effect_descriptor_t *desc; + int aux = 0; + int preset = 0; + + LOGV("EffectLibCreateEffect start"); + + if (pInterface == NULL || uuid == NULL) { + return -EINVAL; + } + + for (i = 0; gDescriptors[i] != NULL; i++) { + desc = gDescriptors[i]; + if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) + == 0) { + break; + } + } + + if (gDescriptors[i] == NULL) { + return -ENOENT; + } + + module = malloc(sizeof(reverb_module_t)); + + module->itfe = &gReverbInterface; + + module->context.mState = REVERB_STATE_UNINITIALIZED; + + if (memcmp(&desc->type, SL_IID_PRESETREVERB, sizeof(effect_uuid_t)) == 0) { + preset = 1; + } + if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { + aux = 1; + } + ret = Reverb_Init(module, aux, preset); + if (ret < 0) { + LOGW("EffectLibCreateEffect() init failed"); + free(module); + return ret; + } + + *pInterface = (effect_interface_t) module; + + module->context.mState = REVERB_STATE_INITIALIZED; + + LOGV("EffectLibCreateEffect %p ,size %d", module, sizeof(reverb_module_t)); + + return 0; +} + +int EffectRelease(effect_interface_t interface) { + reverb_module_t *pRvbModule = (reverb_module_t *)interface; + + LOGV("EffectLibReleaseEffect %p", interface); + if (interface == NULL) { + return -EINVAL; + } + + pRvbModule->context.mState = REVERB_STATE_UNINITIALIZED; + + free(pRvbModule); + return 0; +} + + +/*--- Effect Control Interface Implementation ---*/ + +static int Reverb_Process(effect_interface_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) { + reverb_object_t *pReverb; + int16_t *pSrc, *pDst; + reverb_module_t *pRvbModule = (reverb_module_t *)self; + + if (pRvbModule == NULL) { + return -EINVAL; + } + + if (inBuffer == NULL || inBuffer->raw == NULL || + outBuffer == NULL || outBuffer->raw == NULL || + inBuffer->frameCount != outBuffer->frameCount) { + return -EINVAL; + } + + pReverb = (reverb_object_t*) &pRvbModule->context; + + if (pReverb->mState == REVERB_STATE_UNINITIALIZED) { + return -EINVAL; + } + if (pReverb->mState == REVERB_STATE_INITIALIZED) { + return -ENODATA; + } + + //if bypassed or the preset forces the signal to be completely dry + if (pReverb->m_bBypass != 0) { + if (inBuffer->raw != outBuffer->raw) { + int16_t smp; + pSrc = inBuffer->s16; + pDst = outBuffer->s16; + size_t count = inBuffer->frameCount; + if (pRvbModule->config.inputCfg.channels == pRvbModule->config.outputCfg.channels) { + count *= 2; + while (count--) { + *pDst++ = *pSrc++; + } + } else { + while (count--) { + smp = *pSrc++; + *pDst++ = smp; + *pDst++ = smp; + } + } + } + return 0; + } + + if (pReverb->m_nNextRoom != pReverb->m_nCurrentRoom) { + ReverbUpdateRoom(pReverb, true); + } + + pSrc = inBuffer->s16; + pDst = outBuffer->s16; + size_t numSamples = outBuffer->frameCount; + while (numSamples) { + uint32_t processedSamples; + if (numSamples > (uint32_t) pReverb->m_nUpdatePeriodInSamples) { + processedSamples = (uint32_t) pReverb->m_nUpdatePeriodInSamples; + } else { + processedSamples = numSamples; + } + + /* increment update counter */ + pReverb->m_nUpdateCounter += (int16_t) processedSamples; + /* check if update counter needs to be reset */ + if (pReverb->m_nUpdateCounter >= pReverb->m_nUpdatePeriodInSamples) { + /* update interval has elapsed, so reset counter */ + pReverb->m_nUpdateCounter -= pReverb->m_nUpdatePeriodInSamples; + ReverbUpdateXfade(pReverb, pReverb->m_nUpdatePeriodInSamples); + + } /* end if m_nUpdateCounter >= update interval */ + + Reverb(pReverb, processedSamples, pDst, pSrc); + + numSamples -= processedSamples; + if (pReverb->m_Aux) { + pSrc += processedSamples; + } else { + pSrc += processedSamples * NUM_OUTPUT_CHANNELS; + } + pDst += processedSamples * NUM_OUTPUT_CHANNELS; + } + + return 0; +} + + +static int Reverb_Command(effect_interface_t self, int cmdCode, int cmdSize, + void *pCmdData, int *replySize, void *pReplyData) { + reverb_module_t *pRvbModule = (reverb_module_t *) self; + reverb_object_t *pReverb; + int retsize; + + if (pRvbModule == NULL || + pRvbModule->context.mState == REVERB_STATE_UNINITIALIZED) { + return -EINVAL; + } + + pReverb = (reverb_object_t*) &pRvbModule->context; + + LOGV("Reverb_Command command %d cmdSize %d",cmdCode, cmdSize); + + switch (cmdCode) { + case EFFECT_CMD_INIT: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + *(int *) pReplyData = Reverb_Init(pRvbModule, pReverb->m_Aux, pReverb->m_Preset); + if (*(int *) pReplyData == 0) { + pRvbModule->context.mState = REVERB_STATE_INITIALIZED; + } + break; + case EFFECT_CMD_CONFIGURE: + if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) + || pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + *(int *) pReplyData = Reverb_Configure(pRvbModule, + (effect_config_t *)pCmdData, false); + break; + case EFFECT_CMD_RESET: + Reverb_Reset(pReverb, false); + break; + case EFFECT_CMD_GET_PARAM: + LOGV("Reverb_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %d, pReplyData: %p",pCmdData, *replySize, pReplyData); + + if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) || + pReplyData == NULL || *replySize < (int) sizeof(effect_param_t)) { + return -EINVAL; + } + effect_param_t *rep = (effect_param_t *) pReplyData; + memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t)); + LOGV("Reverb_Command EFFECT_CMD_GET_PARAM param %d, replySize %d",*(int32_t *)rep->data, rep->vsize); + rep->status = Reverb_getParameter(pReverb, *(int32_t *)rep->data, &rep->vsize, + rep->data + sizeof(int32_t)); + *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize; + break; + case EFFECT_CMD_SET_PARAM: + LOGV("Reverb_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %d, pReplyData %p", + cmdSize, pCmdData, *replySize, pReplyData); + if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t))) + || pReplyData == NULL || *replySize != (int)sizeof(int32_t)) { + return -EINVAL; + } + effect_param_t *cmd = (effect_param_t *) pCmdData; + *(int *)pReplyData = Reverb_setParameter(pReverb, *(int32_t *)cmd->data, + cmd->vsize, cmd->data + sizeof(int32_t)); + break; + case EFFECT_CMD_ENABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + if (pReverb->mState != REVERB_STATE_INITIALIZED) { + return -ENOSYS; + } + pReverb->mState = REVERB_STATE_ACTIVE; + LOGV("EFFECT_CMD_ENABLE() OK"); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_DISABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + return -EINVAL; + } + if (pReverb->mState != REVERB_STATE_ACTIVE) { + return -ENOSYS; + } + pReverb->mState = REVERB_STATE_INITIALIZED; + LOGV("EFFECT_CMD_DISABLE() OK"); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_SET_DEVICE: + if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) { + return -EINVAL; + } + LOGV("Reverb_Command EFFECT_CMD_SET_DEVICE: 0x%08x", *(uint32_t *)pCmdData); + break; + case EFFECT_CMD_SET_VOLUME: { + // audio output is always stereo => 2 channel volumes + if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) { + return -EINVAL; + } + float left = (float)(*(uint32_t *)pCmdData) / (1 << 24); + float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24); + LOGV("Reverb_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right); + break; + } + case EFFECT_CMD_SET_AUDIO_MODE: + if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) { + return -EINVAL; + } + LOGV("Reverb_Command EFFECT_CMD_SET_AUDIO_MODE: %d", *(uint32_t *)pCmdData); + break; + default: + LOGW("Reverb_Command invalid command %d",cmdCode); + return -EINVAL; + } + + return 0; +} + + +/*---------------------------------------------------------------------------- + * Reverb internal functions + *--------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Reverb_Init() + *---------------------------------------------------------------------------- + * Purpose: + * Initialize reverb context and apply default parameters + * + * Inputs: + * pRvbModule - pointer to reverb effect module + * aux - indicates if the reverb is used as auxiliary (1) or insert (0) + * preset - indicates if the reverb is used in preset (1) or environmental (0) mode + * + * Outputs: + * + * Side Effects: + * + *---------------------------------------------------------------------------- + */ + +int Reverb_Init(reverb_module_t *pRvbModule, int aux, int preset) { + int ret; + + LOGV("Reverb_Init module %p, aux: %d, preset: %d", pRvbModule,aux, preset); + + memset(&pRvbModule->context, 0, sizeof(reverb_object_t)); + + pRvbModule->context.m_Aux = (uint16_t)aux; + pRvbModule->context.m_Preset = (uint16_t)preset; + + pRvbModule->config.inputCfg.samplingRate = 44100; + if (aux) { + pRvbModule->config.inputCfg.channels = CHANNEL_MONO; + } else { + pRvbModule->config.inputCfg.channels = CHANNEL_STEREO; + } + pRvbModule->config.inputCfg.format = SAMPLE_FORMAT_PCM_S15; + pRvbModule->config.inputCfg.bufferProvider.getBuffer = NULL; + pRvbModule->config.inputCfg.bufferProvider.releaseBuffer = NULL; + pRvbModule->config.inputCfg.bufferProvider.cookie = NULL; + pRvbModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + pRvbModule->config.inputCfg.mask = EFFECT_CONFIG_ALL; + pRvbModule->config.outputCfg.samplingRate = 44100; + pRvbModule->config.outputCfg.channels = CHANNEL_STEREO; + pRvbModule->config.outputCfg.format = SAMPLE_FORMAT_PCM_S15; + pRvbModule->config.outputCfg.bufferProvider.getBuffer = NULL; + pRvbModule->config.outputCfg.bufferProvider.releaseBuffer = NULL; + pRvbModule->config.outputCfg.bufferProvider.cookie = NULL; + pRvbModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; + pRvbModule->config.outputCfg.mask = EFFECT_CONFIG_ALL; + + ret = Reverb_Configure(pRvbModule, &pRvbModule->config, true); + if (ret < 0) { + LOGV("Reverb_Init error %d on module %p", ret, pRvbModule); + } + + return ret; +} + +/*---------------------------------------------------------------------------- + * Reverb_Init() + *---------------------------------------------------------------------------- + * Purpose: + * Set input and output audio configuration. + * + * Inputs: + * pRvbModule - pointer to reverb effect module + * pConfig - pointer to effect_config_t structure containing input + * and output audio parameters configuration + * init - true if called from init function + * Outputs: + * + * Side Effects: + * + *---------------------------------------------------------------------------- + */ + +int Reverb_Configure(reverb_module_t *pRvbModule, effect_config_t *pConfig, + bool init) { + reverb_object_t *pReverb = &pRvbModule->context; + int bufferSizeInSamples; + int updatePeriodInSamples; + int xfadePeriodInSamples; + + // Check configuration compatibility with build options + if (pConfig->inputCfg.samplingRate + != pConfig->outputCfg.samplingRate + || pConfig->outputCfg.channels != OUTPUT_CHANNELS + || pConfig->inputCfg.format != SAMPLE_FORMAT_PCM_S15 + || pConfig->outputCfg.format != SAMPLE_FORMAT_PCM_S15) { + LOGV("Reverb_Configure invalid config"); + return -EINVAL; + } + if ((pReverb->m_Aux && (pConfig->inputCfg.channels != CHANNEL_MONO)) || + (!pReverb->m_Aux && (pConfig->inputCfg.channels != CHANNEL_STEREO))) { + LOGV("Reverb_Configure invalid config"); + return -EINVAL; + } + + memcpy(&pRvbModule->config, pConfig, sizeof(effect_config_t)); + + pReverb->m_nSamplingRate = pRvbModule->config.outputCfg.samplingRate; + + switch (pReverb->m_nSamplingRate) { + case 8000: + pReverb->m_nUpdatePeriodInBits = 5; + bufferSizeInSamples = 4096; + pReverb->m_nCosWT_5KHz = -23170; + break; + case 16000: + pReverb->m_nUpdatePeriodInBits = 6; + bufferSizeInSamples = 8192; + pReverb->m_nCosWT_5KHz = -12540; + break; + case 22050: + pReverb->m_nUpdatePeriodInBits = 7; + bufferSizeInSamples = 8192; + pReverb->m_nCosWT_5KHz = 4768; + break; + case 32000: + pReverb->m_nUpdatePeriodInBits = 7; + bufferSizeInSamples = 16384; + pReverb->m_nCosWT_5KHz = 18205; + break; + case 44100: + pReverb->m_nUpdatePeriodInBits = 8; + bufferSizeInSamples = 16384; + pReverb->m_nCosWT_5KHz = 24799; + break; + case 48000: + pReverb->m_nUpdatePeriodInBits = 8; + bufferSizeInSamples = 16384; + pReverb->m_nCosWT_5KHz = 25997; + break; + default: + LOGV("Reverb_Configure invalid sampling rate %d", pReverb->m_nSamplingRate); + return -EINVAL; + } + + // Define a mask for circular addressing, so that array index + // can wraparound and stay in array boundary of 0, 1, ..., (buffer size -1) + // The buffer size MUST be a power of two + pReverb->m_nBufferMask = (int32_t) (bufferSizeInSamples - 1); + /* reverb parameters are updated every 2^(pReverb->m_nUpdatePeriodInBits) samples */ + updatePeriodInSamples = (int32_t) (0x1L << pReverb->m_nUpdatePeriodInBits); + /* + calculate the update counter by bitwise ANDING with this value to + generate a 2^n modulo value + */ + pReverb->m_nUpdatePeriodInSamples = (int32_t) updatePeriodInSamples; + + xfadePeriodInSamples = (int32_t) (REVERB_XFADE_PERIOD_IN_SECONDS + * (double) pReverb->m_nSamplingRate); + + // set xfade parameters + pReverb->m_nPhaseIncrement + = (int16_t) (65536 / ((int16_t) xfadePeriodInSamples + / (int16_t) updatePeriodInSamples)); + + if (init) { + ReverbReadInPresets(pReverb); + + // for debugging purposes, allow noise generator + pReverb->m_bUseNoise = true; + + // for debugging purposes, allow bypass + pReverb->m_bBypass = 0; + + pReverb->m_nNextRoom = 1; + + pReverb->m_nNoise = (int16_t) 0xABCD; + } + + Reverb_Reset(pReverb, init); + + return 0; +} + +/*---------------------------------------------------------------------------- + * Reverb_Reset() + *---------------------------------------------------------------------------- + * Purpose: + * Reset internal states and clear delay lines. + * + * Inputs: + * pReverb - pointer to reverb context + * init - true if called from init function + * + * Outputs: + * + * Side Effects: + * + *---------------------------------------------------------------------------- + */ + +void Reverb_Reset(reverb_object_t *pReverb, bool init) { + int bufferSizeInSamples = (int32_t) (pReverb->m_nBufferMask + 1); + int maxApSamples; + int maxDelaySamples; + int maxEarlySamples; + int ap1In; + int delay0In; + int delay1In; + int32_t i; + uint16_t nOffset; + + maxApSamples = ((int32_t) (MAX_AP_TIME * pReverb->m_nSamplingRate) >> 16); + maxDelaySamples = ((int32_t) (MAX_DELAY_TIME * pReverb->m_nSamplingRate) + >> 16); + maxEarlySamples = ((int32_t) (MAX_EARLY_TIME * pReverb->m_nSamplingRate) + >> 16); + + ap1In = (AP0_IN + maxApSamples + GUARD); + delay0In = (ap1In + maxApSamples + GUARD); + delay1In = (delay0In + maxDelaySamples + GUARD); + // Define the max offsets for the end points of each section + // i.e., we don't expect a given section's taps to go beyond + // the following limits + + pReverb->m_nEarly0in = (delay1In + maxDelaySamples + GUARD); + pReverb->m_nEarly1in = (pReverb->m_nEarly0in + maxEarlySamples + GUARD); + + pReverb->m_sAp0.m_zApIn = AP0_IN; + + pReverb->m_zD0In = delay0In; + + pReverb->m_sAp1.m_zApIn = ap1In; + + pReverb->m_zD1In = delay1In; + + pReverb->m_zOutLpfL = 0; + pReverb->m_zOutLpfR = 0; + + pReverb->m_nRevFbkR = 0; + pReverb->m_nRevFbkL = 0; + + // set base index into circular buffer + pReverb->m_nBaseIndex = 0; + + // clear the reverb delay line + for (i = 0; i < bufferSizeInSamples; i++) { + pReverb->m_nDelayLine[i] = 0; + } + + ReverbUpdateRoom(pReverb, init); + + pReverb->m_nUpdateCounter = 0; + + pReverb->m_nPhase = -32768; + + pReverb->m_nSin = 0; + pReverb->m_nCos = 0; + pReverb->m_nSinIncrement = 0; + pReverb->m_nCosIncrement = 0; + + // set delay tap lengths + nOffset = ReverbCalculateNoise(pReverb); + + pReverb->m_zD1Cross = pReverb->m_nDelay1Out - pReverb->m_nMaxExcursion + + nOffset; + + nOffset = ReverbCalculateNoise(pReverb); + + pReverb->m_zD0Cross = pReverb->m_nDelay0Out - pReverb->m_nMaxExcursion + - nOffset; + + nOffset = ReverbCalculateNoise(pReverb); + + pReverb->m_zD0Self = pReverb->m_nDelay0Out - pReverb->m_nMaxExcursion + - nOffset; + + nOffset = ReverbCalculateNoise(pReverb); + + pReverb->m_zD1Self = pReverb->m_nDelay1Out - pReverb->m_nMaxExcursion + + nOffset; +} + +/*---------------------------------------------------------------------------- + * Reverb_getParameter() + *---------------------------------------------------------------------------- + * Purpose: + * Get a Reverb parameter + * + * Inputs: + * pReverb - handle to instance data + * param - parameter + * pValue - pointer to variable to hold retrieved value + * pSize - pointer to value size: maximum size as input + * + * Outputs: + * *pValue updated with parameter value + * *pSize updated with actual value size + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- + */ +int Reverb_getParameter(reverb_object_t *pReverb, int32_t param, size_t *pSize, + void *pValue) { + int32_t *pValue32; + int16_t *pValue16; + t_reverb_properties *pProperties; + int32_t i; + int32_t temp; + int32_t temp2; + size_t size; + + if (pReverb->m_Preset) { + if (param != REVERB_PARAM_PRESET || *pSize < sizeof(int16_t)) { + return -EINVAL; + } + size = sizeof(int16_t); + pValue16 = (int16_t *)pValue; + // REVERB_PRESET_NONE is mapped to bypass + if (pReverb->m_bBypass != 0) { + *pValue16 = (int16_t)REVERB_PRESET_NONE; + } else { + *pValue16 = (int16_t)(pReverb->m_nNextRoom + 1); + } + LOGV("get REVERB_PARAM_PRESET, preset %d", *pValue16); + } else { + switch (param) { + case REVERB_PARAM_ROOM_LEVEL: + case REVERB_PARAM_ROOM_HF_LEVEL: + case REVERB_PARAM_DECAY_HF_RATIO: + case REVERB_PARAM_REFLECTIONS_LEVEL: + case REVERB_PARAM_REVERB_LEVEL: + case REVERB_PARAM_DIFFUSION: + case REVERB_PARAM_DENSITY: + size = sizeof(int16_t); + break; + + case REVERB_PARAM_BYPASS: + case REVERB_PARAM_DECAY_TIME: + case REVERB_PARAM_REFLECTIONS_DELAY: + case REVERB_PARAM_REVERB_DELAY: + size = sizeof(int32_t); + break; + + case REVERB_PARAM_PROPERTIES: + size = sizeof(t_reverb_properties); + break; + + default: + return -EINVAL; + } + + if (*pSize < size) { + return -EINVAL; + } + + pValue32 = (int32_t *) pValue; + pValue16 = (int16_t *) pValue; + pProperties = (t_reverb_properties *) pValue; + + switch (param) { + case REVERB_PARAM_BYPASS: + *pValue32 = (int32_t) pReverb->m_bBypass; + break; + + case REVERB_PARAM_PROPERTIES: + pValue16 = &pProperties->roomLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_ROOM_LEVEL: + // Convert m_nRoomLpfFwd to millibels + temp = (pReverb->m_nRoomLpfFwd << 15) + / (32767 - pReverb->m_nRoomLpfFbk); + *pValue16 = Effects_Linear16ToMillibels(temp); + + LOGV("get REVERB_PARAM_ROOM_LEVEL %d, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", *pValue16, temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); + + if (param == REVERB_PARAM_ROOM_LEVEL) { + break; + } + pValue16 = &pProperties->roomHFLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_ROOM_HF_LEVEL: + // The ratio between linear gain at 0Hz and at 5000Hz for the room low pass is: + // (1 + a1) / sqrt(a1^2 + 2*C*a1 + 1) where: + // - a1 is minus the LP feedback gain: -pReverb->m_nRoomLpfFbk + // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz + + temp = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFbk); + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 %d", temp); + temp2 = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nCosWT_5KHz) + << 1; + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, 2 Cos a1 %d", temp2); + temp = 32767 + temp - temp2; + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 + 2 Cos a1 + 1 %d", temp); + temp = Effects_Sqrt(temp) * 181; + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, SQRT(a1^2 + 2 Cos a1 + 1) %d", temp); + temp = ((32767 - pReverb->m_nRoomLpfFbk) << 15) / temp; + + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); + + *pValue16 = Effects_Linear16ToMillibels(temp); + + if (param == REVERB_PARAM_ROOM_HF_LEVEL) { + break; + } + pValue32 = &pProperties->decayTime; + /* FALL THROUGH */ + + case REVERB_PARAM_DECAY_TIME: + // Calculate reverb feedback path gain + temp = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk); + temp = Effects_Linear16ToMillibels(temp); + + // Calculate decay time: g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time + temp = (-6000 * pReverb->m_nLateDelay) / temp; + + // Convert samples to ms + *pValue32 = (temp * 1000) / pReverb->m_nSamplingRate; + + LOGV("get REVERB_PARAM_DECAY_TIME, samples %d, ms %d", temp, *pValue32); + + if (param == REVERB_PARAM_DECAY_TIME) { + break; + } + pValue16 = &pProperties->decayHFRatio; + /* FALL THROUGH */ + + case REVERB_PARAM_DECAY_HF_RATIO: + // If r is the decay HF ratio (r = REVERB_PARAM_DECAY_HF_RATIO/1000) we have: + // DT_5000Hz = DT_0Hz * r + // and G_5000Hz = -6000 * d / DT_5000Hz and G_0Hz = -6000 * d / DT_0Hz in millibels so : + // r = G_0Hz/G_5000Hz in millibels + // The linear gain at 5000Hz is b0 / sqrt(a1^2 + 2*C*a1 + 1) where: + // - a1 is minus the LP feedback gain: -pReverb->m_nRvbLpfFbk + // - b0 is the LP forward gain: pReverb->m_nRvbLpfFwd + // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz + if (pReverb->m_nRvbLpfFbk == 0) { + *pValue16 = 1000; + LOGV("get REVERB_PARAM_DECAY_HF_RATIO, pReverb->m_nRvbLpfFbk == 0, ratio %d", *pValue16); + } else { + temp = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFbk); + temp2 = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nCosWT_5KHz) + << 1; + temp = 32767 + temp - temp2; + temp = Effects_Sqrt(temp) * 181; + temp = (pReverb->m_nRvbLpfFwd << 15) / temp; + // The linear gain at 0Hz is b0 / (a1 + 1) + temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 + - pReverb->m_nRvbLpfFbk); + + temp = Effects_Linear16ToMillibels(temp); + temp2 = Effects_Linear16ToMillibels(temp2); + LOGV("get REVERB_PARAM_DECAY_HF_RATIO, gain 5KHz %d mB, gain DC %d mB", temp, temp2); + + if (temp == 0) + temp = 1; + temp = (int16_t) ((1000 * temp2) / temp); + if (temp > 1000) + temp = 1000; + + *pValue16 = temp; + LOGV("get REVERB_PARAM_DECAY_HF_RATIO, ratio %d", *pValue16); + } + + if (param == REVERB_PARAM_DECAY_HF_RATIO) { + break; + } + pValue16 = &pProperties->reflectionsLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_REFLECTIONS_LEVEL: + *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nEarlyGain); + + LOGV("get REVERB_PARAM_REFLECTIONS_LEVEL, %d", *pValue16); + if (param == REVERB_PARAM_REFLECTIONS_LEVEL) { + break; + } + pValue32 = &pProperties->reflectionsDelay; + /* FALL THROUGH */ + + case REVERB_PARAM_REFLECTIONS_DELAY: + // convert samples to ms + *pValue32 = (pReverb->m_nEarlyDelay * 1000) / pReverb->m_nSamplingRate; + + LOGV("get REVERB_PARAM_REFLECTIONS_DELAY, samples %d, ms %d", pReverb->m_nEarlyDelay, *pValue32); + + if (param == REVERB_PARAM_REFLECTIONS_DELAY) { + break; + } + pValue16 = &pProperties->reverbLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_REVERB_LEVEL: + // Convert linear gain to millibels + *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nLateGain << 2); + + LOGV("get REVERB_PARAM_REVERB_LEVEL %d", *pValue16); + + if (param == REVERB_PARAM_REVERB_LEVEL) { + break; + } + pValue32 = &pProperties->reverbDelay; + /* FALL THROUGH */ + + case REVERB_PARAM_REVERB_DELAY: + // convert samples to ms + *pValue32 = (pReverb->m_nLateDelay * 1000) / pReverb->m_nSamplingRate; + + LOGV("get REVERB_PARAM_REVERB_DELAY, samples %d, ms %d", pReverb->m_nLateDelay, *pValue32); + + if (param == REVERB_PARAM_REVERB_DELAY) { + break; + } + pValue16 = &pProperties->diffusion; + /* FALL THROUGH */ + + case REVERB_PARAM_DIFFUSION: + temp = (int16_t) ((1000 * (pReverb->m_sAp0.m_nApGain - AP0_GAIN_BASE)) + / AP0_GAIN_RANGE); + + if (temp < 0) + temp = 0; + if (temp > 1000) + temp = 1000; + + *pValue16 = temp; + LOGV("get REVERB_PARAM_DIFFUSION, %d, AP0 gain %d", *pValue16, pReverb->m_sAp0.m_nApGain); + + if (param == REVERB_PARAM_DIFFUSION) { + break; + } + pValue16 = &pProperties->density; + /* FALL THROUGH */ + + case REVERB_PARAM_DENSITY: + // Calculate AP delay in time units + temp = ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) << 16) + / pReverb->m_nSamplingRate; + + temp = (int16_t) ((1000 * (temp - AP0_TIME_BASE)) / AP0_TIME_RANGE); + + if (temp < 0) + temp = 0; + if (temp > 1000) + temp = 1000; + + *pValue16 = temp; + + LOGV("get REVERB_PARAM_DENSITY, %d, AP0 delay smps %d", *pValue16, pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn); + break; + + default: + break; + } + } + + *pSize = size; + + LOGV("Reverb_getParameter, context %p, param %d, value %d", + pReverb, param, *(int *)pValue); + + return 0; +} /* end Reverb_getParameter */ + +/*---------------------------------------------------------------------------- + * Reverb_setParameter() + *---------------------------------------------------------------------------- + * Purpose: + * Set a Reverb parameter + * + * Inputs: + * pReverb - handle to instance data + * param - parameter + * pValue - pointer to parameter value + * size - value size + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- + */ +int Reverb_setParameter(reverb_object_t *pReverb, int32_t param, size_t size, + void *pValue) { + int32_t value32; + int16_t value16; + t_reverb_properties *pProperties; + int32_t i; + int32_t temp; + int32_t temp2; + reverb_preset_t *pPreset; + int maxSamples; + int32_t averageDelay; + size_t paramSize; + + LOGV("Reverb_setParameter, context %p, param %d, value16 %d, value32 %d", + pReverb, param, *(int16_t *)pValue, *(int32_t *)pValue); + + if (pReverb->m_Preset) { + if (param != REVERB_PARAM_PRESET || size != sizeof(int16_t)) { + return -EINVAL; + } + value16 = *(int16_t *)pValue; + LOGV("set REVERB_PARAM_PRESET, preset %d", value16); + if (value16 < REVERB_PRESET_NONE || value16 > REVERB_PRESET_PLATE) { + return -EINVAL; + } + // REVERB_PRESET_NONE is mapped to bypass + if (value16 == REVERB_PRESET_NONE) { + pReverb->m_bBypass = 1; + } else { + pReverb->m_bBypass = 0; + pReverb->m_nNextRoom = value16 - 1; + } + } else { + switch (param) { + case REVERB_PARAM_ROOM_LEVEL: + case REVERB_PARAM_ROOM_HF_LEVEL: + case REVERB_PARAM_DECAY_HF_RATIO: + case REVERB_PARAM_REFLECTIONS_LEVEL: + case REVERB_PARAM_REVERB_LEVEL: + case REVERB_PARAM_DIFFUSION: + case REVERB_PARAM_DENSITY: + paramSize = sizeof(int16_t); + break; + + case REVERB_PARAM_BYPASS: + case REVERB_PARAM_DECAY_TIME: + case REVERB_PARAM_REFLECTIONS_DELAY: + case REVERB_PARAM_REVERB_DELAY: + paramSize = sizeof(int32_t); + break; + + case REVERB_PARAM_PROPERTIES: + paramSize = sizeof(t_reverb_properties); + break; + + default: + return -EINVAL; + } + + if (size != paramSize) { + return -EINVAL; + } + + if (paramSize == sizeof(int16_t)) { + value16 = *(int16_t *) pValue; + } else if (paramSize == sizeof(int32_t)) { + value32 = *(int32_t *) pValue; + } else { + pProperties = (t_reverb_properties *) pValue; + } + + pPreset = &pReverb->m_sPreset.m_sPreset[pReverb->m_nNextRoom]; + + switch (param) { + case REVERB_PARAM_BYPASS: + pReverb->m_bBypass = (uint16_t)value32; + break; + + case REVERB_PARAM_PROPERTIES: + value16 = pProperties->roomLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_ROOM_LEVEL: + // Convert millibels to linear 16 bit signed => m_nRoomLpfFwd + if (value16 > 0) + return -EINVAL; + + temp = Effects_MillibelsToLinear16(value16); + + pReverb->m_nRoomLpfFwd + = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRoomLpfFbk)); + + LOGV("REVERB_PARAM_ROOM_LEVEL, gain %d, new m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); + if (param == REVERB_PARAM_ROOM_LEVEL) + break; + value16 = pProperties->roomHFLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_ROOM_HF_LEVEL: + + // Limit to 0 , -40dB range because of low pass implementation + if (value16 > 0 || value16 < -4000) + return -EINVAL; + // Convert attenuation @ 5000H expressed in millibels to => m_nRoomLpfFbk + // m_nRoomLpfFbk is -a1 where a1 is the solution of: + // a1^2 + 2*(C-dG^2)/(1-dG^2)*a1 + 1 = 0 where: + // - C is cos(2*pi*5000/Fs) (pReverb->m_nCosWT_5KHz) + // - dG is G0/Gf (G0 is the linear gain at DC and Gf is the wanted gain at 5000Hz) + + // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged + // while changing HF level + temp2 = (pReverb->m_nRoomLpfFwd << 15) / (32767 + - pReverb->m_nRoomLpfFbk); + if (value16 == 0) { + pReverb->m_nRoomLpfFbk = 0; + } else { + int32_t dG2, b, delta; + + // dG^2 + temp = Effects_MillibelsToLinear16(value16); + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, HF gain %d", temp); + temp = (1 << 30) / temp; + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain %d", temp); + dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15); + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain ^ 2 %d", dG2); + // b = 2*(C-dG^2)/(1-dG^2) + b = (int32_t) ((((int64_t) 1 << (15 + 1)) + * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2)) + / ((int64_t) 32767 - (int64_t) dG2)); + + // delta = b^2 - 4 + delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15 + + 2))); + + LOGV_IF(delta > (1<<30), " delta overflow %d", delta); + + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, dG2 %d, b %d, delta %d, m_nCosWT_5KHz %d", dG2, b, delta, pReverb->m_nCosWT_5KHz); + // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2 + pReverb->m_nRoomLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1; + } + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, olg DC gain %d new m_nRoomLpfFbk %d, old m_nRoomLpfFwd %d", + temp2, pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFwd); + + pReverb->m_nRoomLpfFwd + = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRoomLpfFbk)); + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, new m_nRoomLpfFwd %d", pReverb->m_nRoomLpfFwd); + + if (param == REVERB_PARAM_ROOM_HF_LEVEL) + break; + value32 = pProperties->decayTime; + /* FALL THROUGH */ + + case REVERB_PARAM_DECAY_TIME: + + // Convert milliseconds to => m_nRvbLpfFwd (function of m_nRvbLpfFbk) + // convert ms to samples + value32 = (value32 * pReverb->m_nSamplingRate) / 1000; + + // calculate valid decay time range as a function of current reverb delay and + // max feed back gain. Min value <=> -40dB in one pass, Max value <=> feedback gain = -1 dB + // Calculate attenuation for each round in late reverb given a total attenuation of -6000 millibels. + // g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time + averageDelay = pReverb->m_nLateDelay - pReverb->m_nMaxExcursion; + averageDelay += ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) + + (pReverb->m_sAp1.m_zApOut - pReverb->m_sAp1.m_zApIn)) >> 1; + + temp = (-6000 * averageDelay) / value32; + LOGV("REVERB_PARAM_DECAY_TIME, delay smps %d, DT smps %d, gain mB %d",averageDelay, value32, temp); + if (temp < -4000 || temp > -100) + return -EINVAL; + + // calculate low pass gain by adding reverb input attenuation (pReverb->m_nLateGain) and substrating output + // xfade and sum gain (max +9dB) + temp -= Effects_Linear16ToMillibels(pReverb->m_nLateGain) + 900; + temp = Effects_MillibelsToLinear16(temp); + + // DC gain (temp) = b0 / (1 + a1) = pReverb->m_nRvbLpfFwd / (32767 - pReverb->m_nRvbLpfFbk) + pReverb->m_nRvbLpfFwd + = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRvbLpfFbk)); + + LOGV("REVERB_PARAM_DECAY_TIME, gain %d, new m_nRvbLpfFwd %d, old m_nRvbLpfFbk %d, reverb gain %d", temp, pReverb->m_nRvbLpfFwd, pReverb->m_nRvbLpfFbk, Effects_Linear16ToMillibels(pReverb->m_nLateGain)); + + if (param == REVERB_PARAM_DECAY_TIME) + break; + value16 = pProperties->decayHFRatio; + /* FALL THROUGH */ + + case REVERB_PARAM_DECAY_HF_RATIO: + + // We limit max value to 1000 because reverb filter is lowpass only + if (value16 < 100 || value16 > 1000) + return -EINVAL; + // Convert per mille to => m_nLpfFwd, m_nLpfFbk + + // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged + // while changing HF level + temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk); + + if (value16 == 1000) { + pReverb->m_nRvbLpfFbk = 0; + } else { + int32_t dG2, b, delta; + + temp = Effects_Linear16ToMillibels(temp2); + // G_5000Hz = G_DC * (1000/REVERB_PARAM_DECAY_HF_RATIO) in millibels + + value32 = ((int32_t) 1000 << 15) / (int32_t) value16; + LOGV("REVERB_PARAM_DECAY_HF_RATIO, DC gain %d, DC gain mB %d, 1000/R %d", temp2, temp, value32); + + temp = (int32_t) (((int64_t) temp * (int64_t) value32) >> 15); + + if (temp < -4000) { + LOGV("REVERB_PARAM_DECAY_HF_RATIO HF gain overflow %d mB", temp); + temp = -4000; + } + + temp = Effects_MillibelsToLinear16(temp); + LOGV("REVERB_PARAM_DECAY_HF_RATIO, HF gain %d", temp); + // dG^2 + temp = (temp2 << 15) / temp; + dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15); + + // b = 2*(C-dG^2)/(1-dG^2) + b = (int32_t) ((((int64_t) 1 << (15 + 1)) + * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2)) + / ((int64_t) 32767 - (int64_t) dG2)); + + // delta = b^2 - 4 + delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15 + + 2))); + + // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2 + pReverb->m_nRvbLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1; + + LOGV("REVERB_PARAM_DECAY_HF_RATIO, dG2 %d, b %d, delta %d", dG2, b, delta); + + } + + LOGV("REVERB_PARAM_DECAY_HF_RATIO, gain %d, m_nRvbLpfFbk %d, m_nRvbLpfFwd %d", temp2, pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFwd); + + pReverb->m_nRvbLpfFwd + = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRvbLpfFbk)); + + if (param == REVERB_PARAM_DECAY_HF_RATIO) + break; + value16 = pProperties->reflectionsLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_REFLECTIONS_LEVEL: + // We limit max value to 0 because gain is limited to 0dB + if (value16 > 0 || value16 < -6000) + return -EINVAL; + + // Convert millibels to linear 16 bit signed and recompute m_sEarlyL.m_nGain[i] and m_sEarlyR.m_nGain[i]. + value16 = Effects_MillibelsToLinear16(value16); + for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { + pReverb->m_sEarlyL.m_nGain[i] + = MULT_EG1_EG1(pPreset->m_sEarlyL.m_nGain[i],value16); + pReverb->m_sEarlyR.m_nGain[i] + = MULT_EG1_EG1(pPreset->m_sEarlyR.m_nGain[i],value16); + } + pReverb->m_nEarlyGain = value16; + LOGV("REVERB_PARAM_REFLECTIONS_LEVEL, m_nEarlyGain %d", pReverb->m_nEarlyGain); + + if (param == REVERB_PARAM_REFLECTIONS_LEVEL) + break; + value32 = pProperties->reflectionsDelay; + /* FALL THROUGH */ + + case REVERB_PARAM_REFLECTIONS_DELAY: + // We limit max value MAX_EARLY_TIME + // convert ms to time units + temp = (value32 * 65536) / 1000; + if (temp < 0 || temp > MAX_EARLY_TIME) + return -EINVAL; + + maxSamples = (int32_t) (MAX_EARLY_TIME * pReverb->m_nSamplingRate) + >> 16; + temp = (temp * pReverb->m_nSamplingRate) >> 16; + for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { + temp2 = temp + (((int32_t) pPreset->m_sEarlyL.m_zDelay[i] + * pReverb->m_nSamplingRate) >> 16); + if (temp2 > maxSamples) + temp2 = maxSamples; + pReverb->m_sEarlyL.m_zDelay[i] = pReverb->m_nEarly0in + temp2; + temp2 = temp + (((int32_t) pPreset->m_sEarlyR.m_zDelay[i] + * pReverb->m_nSamplingRate) >> 16); + if (temp2 > maxSamples) + temp2 = maxSamples; + pReverb->m_sEarlyR.m_zDelay[i] = pReverb->m_nEarly1in + temp2; + } + pReverb->m_nEarlyDelay = temp; + + LOGV("REVERB_PARAM_REFLECTIONS_DELAY, m_nEarlyDelay smps %d max smp delay %d", pReverb->m_nEarlyDelay, maxSamples); + + // Convert milliseconds to sample count => m_nEarlyDelay + if (param == REVERB_PARAM_REFLECTIONS_DELAY) + break; + value16 = pProperties->reverbLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_REVERB_LEVEL: + // We limit max value to 0 because gain is limited to 0dB + if (value16 > 0 || value16 < -6000) + return -EINVAL; + // Convert millibels to linear 16 bits (gange 0 - 8191) => m_nLateGain. + pReverb->m_nLateGain = Effects_MillibelsToLinear16(value16) >> 2; + + LOGV("REVERB_PARAM_REVERB_LEVEL, m_nLateGain %d", pReverb->m_nLateGain); + + if (param == REVERB_PARAM_REVERB_LEVEL) + break; + value32 = pProperties->reverbDelay; + /* FALL THROUGH */ + + case REVERB_PARAM_REVERB_DELAY: + // We limit max value to MAX_DELAY_TIME + // convert ms to time units + temp = (value32 * 65536) / 1000; + if (temp < 0 || temp > MAX_DELAY_TIME) + return -EINVAL; + + maxSamples = (int32_t) (MAX_DELAY_TIME * pReverb->m_nSamplingRate) + >> 16; + temp = (temp * pReverb->m_nSamplingRate) >> 16; + if ((temp + pReverb->m_nMaxExcursion) > maxSamples) { + temp = maxSamples - pReverb->m_nMaxExcursion; + } + if (temp < pReverb->m_nMaxExcursion) { + temp = pReverb->m_nMaxExcursion; + } + + temp -= pReverb->m_nLateDelay; + pReverb->m_nDelay0Out += temp; + pReverb->m_nDelay1Out += temp; + pReverb->m_nLateDelay += temp; + + LOGV("REVERB_PARAM_REVERB_DELAY, m_nLateDelay smps %d max smp delay %d", pReverb->m_nLateDelay, maxSamples); + + // Convert milliseconds to sample count => m_nDelay1Out + m_nMaxExcursion + if (param == REVERB_PARAM_REVERB_DELAY) + break; + + value16 = pProperties->diffusion; + /* FALL THROUGH */ + + case REVERB_PARAM_DIFFUSION: + if (value16 < 0 || value16 > 1000) + return -EINVAL; + + // Convert per mille to m_sAp0.m_nApGain, m_sAp1.m_nApGain + pReverb->m_sAp0.m_nApGain = AP0_GAIN_BASE + ((int32_t) value16 + * AP0_GAIN_RANGE) / 1000; + pReverb->m_sAp1.m_nApGain = AP1_GAIN_BASE + ((int32_t) value16 + * AP1_GAIN_RANGE) / 1000; + + LOGV("REVERB_PARAM_DIFFUSION, m_sAp0.m_nApGain %d m_sAp1.m_nApGain %d", pReverb->m_sAp0.m_nApGain, pReverb->m_sAp1.m_nApGain); + + if (param == REVERB_PARAM_DIFFUSION) + break; + + value16 = pProperties->density; + /* FALL THROUGH */ + + case REVERB_PARAM_DENSITY: + if (value16 < 0 || value16 > 1000) + return -EINVAL; + + // Convert per mille to m_sAp0.m_zApOut, m_sAp1.m_zApOut + maxSamples = (int32_t) (MAX_AP_TIME * pReverb->m_nSamplingRate) >> 16; + + temp = AP0_TIME_BASE + ((int32_t) value16 * AP0_TIME_RANGE) / 1000; + /*lint -e{702} shift for performance */ + temp = (temp * pReverb->m_nSamplingRate) >> 16; + if (temp > maxSamples) + temp = maxSamples; + pReverb->m_sAp0.m_zApOut = (uint16_t) (pReverb->m_sAp0.m_zApIn + temp); + + LOGV("REVERB_PARAM_DENSITY, Ap0 delay smps %d", temp); + + temp = AP1_TIME_BASE + ((int32_t) value16 * AP1_TIME_RANGE) / 1000; + /*lint -e{702} shift for performance */ + temp = (temp * pReverb->m_nSamplingRate) >> 16; + if (temp > maxSamples) + temp = maxSamples; + pReverb->m_sAp1.m_zApOut = (uint16_t) (pReverb->m_sAp1.m_zApIn + temp); + + LOGV("Ap1 delay smps %d", temp); + + break; + + default: + break; + } + } + + return 0; +} /* end Reverb_setParameter */ + +/*---------------------------------------------------------------------------- + * ReverbUpdateXfade + *---------------------------------------------------------------------------- + * Purpose: + * Update the xfade parameters as required + * + * Inputs: + * nNumSamplesToAdd - number of samples to write to buffer + * + * Outputs: + * + * + * Side Effects: + * - xfade parameters will be changed + * + *---------------------------------------------------------------------------- + */ +static int ReverbUpdateXfade(reverb_object_t *pReverb, int nNumSamplesToAdd) { + uint16_t nOffset; + int16_t tempCos; + int16_t tempSin; + + if (pReverb->m_nXfadeCounter >= pReverb->m_nXfadeInterval) { + /* update interval has elapsed, so reset counter */ + pReverb->m_nXfadeCounter = 0; + + // Pin the sin,cos values to min / max values to ensure that the + // modulated taps' coefs are zero (thus no clicks) + if (pReverb->m_nPhaseIncrement > 0) { + // if phase increment > 0, then sin -> 1, cos -> 0 + pReverb->m_nSin = 32767; + pReverb->m_nCos = 0; + + // reset the phase to match the sin, cos values + pReverb->m_nPhase = 32767; + + // modulate the cross taps because their tap coefs are zero + nOffset = ReverbCalculateNoise(pReverb); + + pReverb->m_zD1Cross = pReverb->m_nDelay1Out + - pReverb->m_nMaxExcursion + nOffset; + + nOffset = ReverbCalculateNoise(pReverb); + + pReverb->m_zD0Cross = pReverb->m_nDelay0Out + - pReverb->m_nMaxExcursion - nOffset; + } else { + // if phase increment < 0, then sin -> 0, cos -> 1 + pReverb->m_nSin = 0; + pReverb->m_nCos = 32767; + + // reset the phase to match the sin, cos values + pReverb->m_nPhase = -32768; + + // modulate the self taps because their tap coefs are zero + nOffset = ReverbCalculateNoise(pReverb); + + pReverb->m_zD0Self = pReverb->m_nDelay0Out + - pReverb->m_nMaxExcursion - nOffset; + + nOffset = ReverbCalculateNoise(pReverb); + + pReverb->m_zD1Self = pReverb->m_nDelay1Out + - pReverb->m_nMaxExcursion + nOffset; + + } // end if-else (pReverb->m_nPhaseIncrement > 0) + + // Reverse the direction of the sin,cos so that the + // tap whose coef was previously increasing now decreases + // and vice versa + pReverb->m_nPhaseIncrement = -pReverb->m_nPhaseIncrement; + + } // end if counter >= update interval + + //compute what phase will be next time + pReverb->m_nPhase += pReverb->m_nPhaseIncrement; + + //calculate what the new sin and cos need to reach by the next update + ReverbCalculateSinCos(pReverb->m_nPhase, &tempSin, &tempCos); + + //calculate the per-sample increment required to get there by the next update + /*lint -e{702} shift for performance */ + pReverb->m_nSinIncrement = (tempSin - pReverb->m_nSin) + >> pReverb->m_nUpdatePeriodInBits; + + /*lint -e{702} shift for performance */ + pReverb->m_nCosIncrement = (tempCos - pReverb->m_nCos) + >> pReverb->m_nUpdatePeriodInBits; + + /* increment update counter */ + pReverb->m_nXfadeCounter += (uint16_t) nNumSamplesToAdd; + + return 0; + +} /* end ReverbUpdateXfade */ + +/*---------------------------------------------------------------------------- + * ReverbCalculateNoise + *---------------------------------------------------------------------------- + * Purpose: + * Calculate a noise sample and limit its value + * + * Inputs: + * nMaxExcursion - noise value is limited to this value + * pnNoise - return new noise sample in this (not limited) + * + * Outputs: + * new limited noise value + * + * Side Effects: + * - *pnNoise noise value is updated + * + *---------------------------------------------------------------------------- + */ +static uint16_t ReverbCalculateNoise(reverb_object_t *pReverb) { + int16_t nNoise = pReverb->m_nNoise; + + // calculate new noise value + if (pReverb->m_bUseNoise) { + nNoise = (int16_t) (nNoise * 5 + 1); + } else { + nNoise = 0; + } + + pReverb->m_nNoise = nNoise; + // return the limited noise value + return (pReverb->m_nMaxExcursion & nNoise); + +} /* end ReverbCalculateNoise */ + +/*---------------------------------------------------------------------------- + * ReverbCalculateSinCos + *---------------------------------------------------------------------------- + * Purpose: + * Calculate a new sin and cosine value based on the given phase + * + * Inputs: + * nPhase - phase angle + * pnSin - input old value, output new value + * pnCos - input old value, output new value + * + * Outputs: + * + * Side Effects: + * - *pnSin, *pnCos are updated + * + *---------------------------------------------------------------------------- + */ +static int ReverbCalculateSinCos(int16_t nPhase, int16_t *pnSin, int16_t *pnCos) { + int32_t nTemp; + int32_t nNetAngle; + + // -1 <= nPhase < 1 + // However, for the calculation, we need a value + // that ranges from -1/2 to +1/2, so divide the phase by 2 + /*lint -e{702} shift for performance */ + nNetAngle = nPhase >> 1; + + /* + Implement the following + sin(x) = (2-4*c)*x^2 + c + x + cos(x) = (2-4*c)*x^2 + c - x + + where c = 1/sqrt(2) + using the a0 + x*(a1 + x*a2) approach + */ + + /* limit the input "angle" to be between -0.5 and +0.5 */ + if (nNetAngle > EG1_HALF) { + nNetAngle = EG1_HALF; + } else if (nNetAngle < EG1_MINUS_HALF) { + nNetAngle = EG1_MINUS_HALF; + } + + /* calculate sin */ + nTemp = EG1_ONE + MULT_EG1_EG1(REVERB_PAN_G2, nNetAngle); + nTemp = REVERB_PAN_G0 + MULT_EG1_EG1(nTemp, nNetAngle); + *pnSin = (int16_t) SATURATE_EG1(nTemp); + + /* calculate cos */ + nTemp = -EG1_ONE + MULT_EG1_EG1(REVERB_PAN_G2, nNetAngle); + nTemp = REVERB_PAN_G0 + MULT_EG1_EG1(nTemp, nNetAngle); + *pnCos = (int16_t) SATURATE_EG1(nTemp); + + return 0; +} /* end ReverbCalculateSinCos */ + +/*---------------------------------------------------------------------------- + * Reverb + *---------------------------------------------------------------------------- + * Purpose: + * apply reverb to the given signal + * + * Inputs: + * nNu + * pnSin - input old value, output new value + * pnCos - input old value, output new value + * + * Outputs: + * number of samples actually reverberated + * + * Side Effects: + * + *---------------------------------------------------------------------------- + */ +static int Reverb(reverb_object_t *pReverb, int nNumSamplesToAdd, + short *pOutputBuffer, short *pInputBuffer) { + int32_t i; + int32_t nDelayOut0; + int32_t nDelayOut1; + uint16_t nBase; + + uint32_t nAddr; + int32_t nTemp1; + int32_t nTemp2; + int32_t nApIn; + int32_t nApOut; + + int32_t j; + int32_t nEarlyOut; + + int32_t tempValue; + + // get the base address + nBase = pReverb->m_nBaseIndex; + + for (i = 0; i < nNumSamplesToAdd; i++) { + // ********** Left Allpass - start + nApIn = *pInputBuffer; + if (!pReverb->m_Aux) { + pInputBuffer++; + } + // store to early delay line + nAddr = CIRCULAR(nBase, pReverb->m_nEarly0in, pReverb->m_nBufferMask); + pReverb->m_nDelayLine[nAddr] = (short) nApIn; + + // left input = (left dry * m_nLateGain) + right feedback from previous period + + nApIn = SATURATE(nApIn + pReverb->m_nRevFbkR); + nApIn = MULT_EG1_EG1(nApIn, pReverb->m_nLateGain); + + // fetch allpass delay line out + //nAddr = CIRCULAR(nBase, psAp0->m_zApOut, pReverb->m_nBufferMask); + nAddr + = CIRCULAR(nBase, pReverb->m_sAp0.m_zApOut, pReverb->m_nBufferMask); + nDelayOut0 = pReverb->m_nDelayLine[nAddr]; + + // calculate allpass feedforward; subtract the feedforward result + nTemp1 = MULT_EG1_EG1(nApIn, pReverb->m_sAp0.m_nApGain); + nApOut = SATURATE(nDelayOut0 - nTemp1); // allpass output + + // calculate allpass feedback; add the feedback result + nTemp1 = MULT_EG1_EG1(nApOut, pReverb->m_sAp0.m_nApGain); + nTemp1 = SATURATE(nApIn + nTemp1); + + // inject into allpass delay + nAddr + = CIRCULAR(nBase, pReverb->m_sAp0.m_zApIn, pReverb->m_nBufferMask); + pReverb->m_nDelayLine[nAddr] = (short) nTemp1; + + // inject allpass output into delay line + nAddr = CIRCULAR(nBase, pReverb->m_zD0In, pReverb->m_nBufferMask); + pReverb->m_nDelayLine[nAddr] = (short) nApOut; + + // ********** Left Allpass - end + + // ********** Right Allpass - start + nApIn = (*pInputBuffer++); + // store to early delay line + nAddr = CIRCULAR(nBase, pReverb->m_nEarly1in, pReverb->m_nBufferMask); + pReverb->m_nDelayLine[nAddr] = (short) nApIn; + + // right input = (right dry * m_nLateGain) + left feedback from previous period + /*lint -e{702} use shift for performance */ + nApIn = SATURATE(nApIn + pReverb->m_nRevFbkL); + nApIn = MULT_EG1_EG1(nApIn, pReverb->m_nLateGain); + + // fetch allpass delay line out + nAddr + = CIRCULAR(nBase, pReverb->m_sAp1.m_zApOut, pReverb->m_nBufferMask); + nDelayOut1 = pReverb->m_nDelayLine[nAddr]; + + // calculate allpass feedforward; subtract the feedforward result + nTemp1 = MULT_EG1_EG1(nApIn, pReverb->m_sAp1.m_nApGain); + nApOut = SATURATE(nDelayOut1 - nTemp1); // allpass output + + // calculate allpass feedback; add the feedback result + nTemp1 = MULT_EG1_EG1(nApOut, pReverb->m_sAp1.m_nApGain); + nTemp1 = SATURATE(nApIn + nTemp1); + + // inject into allpass delay + nAddr + = CIRCULAR(nBase, pReverb->m_sAp1.m_zApIn, pReverb->m_nBufferMask); + pReverb->m_nDelayLine[nAddr] = (short) nTemp1; + + // inject allpass output into delay line + nAddr = CIRCULAR(nBase, pReverb->m_zD1In, pReverb->m_nBufferMask); + pReverb->m_nDelayLine[nAddr] = (short) nApOut; + + // ********** Right Allpass - end + + // ********** D0 output - start + // fetch delay line self out + nAddr = CIRCULAR(nBase, pReverb->m_zD0Self, pReverb->m_nBufferMask); + nDelayOut0 = pReverb->m_nDelayLine[nAddr]; + + // calculate delay line self out + nTemp1 = MULT_EG1_EG1(nDelayOut0, pReverb->m_nSin); + + // fetch delay line cross out + nAddr = CIRCULAR(nBase, pReverb->m_zD1Cross, pReverb->m_nBufferMask); + nDelayOut0 = pReverb->m_nDelayLine[nAddr]; + + // calculate delay line self out + nTemp2 = MULT_EG1_EG1(nDelayOut0, pReverb->m_nCos); + + // calculate unfiltered delay out + nDelayOut0 = SATURATE(nTemp1 + nTemp2); + + // ********** D0 output - end + + // ********** D1 output - start + // fetch delay line self out + nAddr = CIRCULAR(nBase, pReverb->m_zD1Self, pReverb->m_nBufferMask); + nDelayOut1 = pReverb->m_nDelayLine[nAddr]; + + // calculate delay line self out + nTemp1 = MULT_EG1_EG1(nDelayOut1, pReverb->m_nSin); + + // fetch delay line cross out + nAddr = CIRCULAR(nBase, pReverb->m_zD0Cross, pReverb->m_nBufferMask); + nDelayOut1 = pReverb->m_nDelayLine[nAddr]; + + // calculate delay line self out + nTemp2 = MULT_EG1_EG1(nDelayOut1, pReverb->m_nCos); + + // calculate unfiltered delay out + nDelayOut1 = SATURATE(nTemp1 + nTemp2); + + // ********** D1 output - end + + // ********** mixer and feedback - start + // sum is fedback to right input (R + L) + nDelayOut0 = (short) SATURATE(nDelayOut0 + nDelayOut1); + + // difference is feedback to left input (R - L) + /*lint -e{685} lint complains that it can't saturate negative */ + nDelayOut1 = (short) SATURATE(nDelayOut1 - nDelayOut0); + + // ********** mixer and feedback - end + + // calculate lowpass filter (mixer scale factor included in LPF feedforward) + nTemp1 = MULT_EG1_EG1(nDelayOut0, pReverb->m_nRvbLpfFwd); + + nTemp2 = MULT_EG1_EG1(pReverb->m_nRevFbkL, pReverb->m_nRvbLpfFbk); + + // calculate filtered delay out and simultaneously update LPF state variable + // filtered delay output is stored in m_nRevFbkL + pReverb->m_nRevFbkL = (short) SATURATE(nTemp1 + nTemp2); + + // calculate lowpass filter (mixer scale factor included in LPF feedforward) + nTemp1 = MULT_EG1_EG1(nDelayOut1, pReverb->m_nRvbLpfFwd); + + nTemp2 = MULT_EG1_EG1(pReverb->m_nRevFbkR, pReverb->m_nRvbLpfFbk); + + // calculate filtered delay out and simultaneously update LPF state variable + // filtered delay output is stored in m_nRevFbkR + pReverb->m_nRevFbkR = (short) SATURATE(nTemp1 + nTemp2); + + // ********** start early reflection generator, left + //psEarly = &(pReverb->m_sEarlyL); + + + for (j = 0; j < REVERB_MAX_NUM_REFLECTIONS; j++) { + // fetch delay line out + //nAddr = CIRCULAR(nBase, psEarly->m_zDelay[j], pReverb->m_nBufferMask); + nAddr + = CIRCULAR(nBase, pReverb->m_sEarlyL.m_zDelay[j], pReverb->m_nBufferMask); + + nTemp1 = pReverb->m_nDelayLine[nAddr]; + + // calculate reflection + //nTemp1 = MULT_EG1_EG1(nDelayOut0, psEarly->m_nGain[j]); + nTemp1 = MULT_EG1_EG1(nTemp1, pReverb->m_sEarlyL.m_nGain[j]); + + nDelayOut0 = SATURATE(nDelayOut0 + nTemp1); + + } // end for (j=0; j < REVERB_MAX_NUM_REFLECTIONS; j++) + + // apply lowpass to early reflections and reverb output + //nTemp1 = MULT_EG1_EG1(nEarlyOut, psEarly->m_nRvbLpfFwd); + nTemp1 = MULT_EG1_EG1(nDelayOut0, pReverb->m_nRoomLpfFwd); + + //nTemp2 = MULT_EG1_EG1(psEarly->m_zLpf, psEarly->m_nLpfFbk); + nTemp2 = MULT_EG1_EG1(pReverb->m_zOutLpfL, pReverb->m_nRoomLpfFbk); + + // calculate filtered out and simultaneously update LPF state variable + // filtered output is stored in m_zOutLpfL + pReverb->m_zOutLpfL = (short) SATURATE(nTemp1 + nTemp2); + + //sum with output buffer + tempValue = *pOutputBuffer; + *pOutputBuffer++ = (short) SATURATE(tempValue+pReverb->m_zOutLpfL); + + // ********** end early reflection generator, left + + // ********** start early reflection generator, right + //psEarly = &(pReverb->m_sEarlyR); + + for (j = 0; j < REVERB_MAX_NUM_REFLECTIONS; j++) { + // fetch delay line out + nAddr + = CIRCULAR(nBase, pReverb->m_sEarlyR.m_zDelay[j], pReverb->m_nBufferMask); + nTemp1 = pReverb->m_nDelayLine[nAddr]; + + // calculate reflection + nTemp1 = MULT_EG1_EG1(nTemp1, pReverb->m_sEarlyR.m_nGain[j]); + + nDelayOut1 = SATURATE(nDelayOut1 + nTemp1); + + } // end for (j=0; j < REVERB_MAX_NUM_REFLECTIONS; j++) + + // apply lowpass to early reflections + nTemp1 = MULT_EG1_EG1(nDelayOut1, pReverb->m_nRoomLpfFwd); + + nTemp2 = MULT_EG1_EG1(pReverb->m_zOutLpfR, pReverb->m_nRoomLpfFbk); + + // calculate filtered out and simultaneously update LPF state variable + // filtered output is stored in m_zOutLpfR + pReverb->m_zOutLpfR = (short) SATURATE(nTemp1 + nTemp2); + + //sum with output buffer + tempValue = *pOutputBuffer; + *pOutputBuffer++ = (short) SATURATE(tempValue + pReverb->m_zOutLpfR); + + // ********** end early reflection generator, right + + // decrement base addr for next sample period + nBase--; + + pReverb->m_nSin += pReverb->m_nSinIncrement; + pReverb->m_nCos += pReverb->m_nCosIncrement; + + } // end for (i=0; i < nNumSamplesToAdd; i++) + + // store the most up to date version + pReverb->m_nBaseIndex = nBase; + + return 0; +} /* end Reverb */ + +/*---------------------------------------------------------------------------- + * ReverbUpdateRoom + *---------------------------------------------------------------------------- + * Purpose: + * Update the room's preset parameters as required + * + * Inputs: + * + * Outputs: + * + * + * Side Effects: + * - reverb paramters (fbk, fwd, etc) will be changed + * - m_nCurrentRoom := m_nNextRoom + *---------------------------------------------------------------------------- + */ +static int ReverbUpdateRoom(reverb_object_t *pReverb, bool fullUpdate) { + int temp; + int i; + int maxSamples; + int earlyDelay; + int earlyGain; + + reverb_preset_t *pPreset = + &pReverb->m_sPreset.m_sPreset[pReverb->m_nNextRoom]; + + if (fullUpdate) { + pReverb->m_nRvbLpfFwd = pPreset->m_nRvbLpfFwd; + pReverb->m_nRvbLpfFbk = pPreset->m_nRvbLpfFbk; + + pReverb->m_nEarlyGain = pPreset->m_nEarlyGain; + //stored as time based, convert to sample based + pReverb->m_nLateGain = pPreset->m_nLateGain; + pReverb->m_nRoomLpfFbk = pPreset->m_nRoomLpfFbk; + pReverb->m_nRoomLpfFwd = pPreset->m_nRoomLpfFwd; + + // set the early reflections gains + earlyGain = pPreset->m_nEarlyGain; + for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { + pReverb->m_sEarlyL.m_nGain[i] + = MULT_EG1_EG1(pPreset->m_sEarlyL.m_nGain[i],earlyGain); + pReverb->m_sEarlyR.m_nGain[i] + = MULT_EG1_EG1(pPreset->m_sEarlyR.m_nGain[i],earlyGain); + } + + pReverb->m_nMaxExcursion = pPreset->m_nMaxExcursion; + + pReverb->m_sAp0.m_nApGain = pPreset->m_nAp0_ApGain; + pReverb->m_sAp1.m_nApGain = pPreset->m_nAp1_ApGain; + + // set the early reflections delay + earlyDelay = ((int) pPreset->m_nEarlyDelay * pReverb->m_nSamplingRate) + >> 16; + pReverb->m_nEarlyDelay = earlyDelay; + maxSamples = (int32_t) (MAX_EARLY_TIME * pReverb->m_nSamplingRate) + >> 16; + for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { + //stored as time based, convert to sample based + temp = earlyDelay + (((int) pPreset->m_sEarlyL.m_zDelay[i] + * pReverb->m_nSamplingRate) >> 16); + if (temp > maxSamples) + temp = maxSamples; + pReverb->m_sEarlyL.m_zDelay[i] = pReverb->m_nEarly0in + temp; + //stored as time based, convert to sample based + temp = earlyDelay + (((int) pPreset->m_sEarlyR.m_zDelay[i] + * pReverb->m_nSamplingRate) >> 16); + if (temp > maxSamples) + temp = maxSamples; + pReverb->m_sEarlyR.m_zDelay[i] = pReverb->m_nEarly1in + temp; + } + + maxSamples = (int32_t) (MAX_DELAY_TIME * pReverb->m_nSamplingRate) + >> 16; + //stored as time based, convert to sample based + /*lint -e{702} shift for performance */ + temp = (pPreset->m_nLateDelay * pReverb->m_nSamplingRate) >> 16; + if ((temp + pReverb->m_nMaxExcursion) > maxSamples) { + temp = maxSamples - pReverb->m_nMaxExcursion; + } + temp -= pReverb->m_nLateDelay; + pReverb->m_nDelay0Out += temp; + pReverb->m_nDelay1Out += temp; + pReverb->m_nLateDelay += temp; + + maxSamples = (int32_t) (MAX_AP_TIME * pReverb->m_nSamplingRate) >> 16; + //stored as time based, convert to absolute sample value + temp = pPreset->m_nAp0_ApOut; + /*lint -e{702} shift for performance */ + temp = (temp * pReverb->m_nSamplingRate) >> 16; + if (temp > maxSamples) + temp = maxSamples; + pReverb->m_sAp0.m_zApOut = (uint16_t) (pReverb->m_sAp0.m_zApIn + temp); + + //stored as time based, convert to absolute sample value + temp = pPreset->m_nAp1_ApOut; + /*lint -e{702} shift for performance */ + temp = (temp * pReverb->m_nSamplingRate) >> 16; + if (temp > maxSamples) + temp = maxSamples; + pReverb->m_sAp1.m_zApOut = (uint16_t) (pReverb->m_sAp1.m_zApIn + temp); + //gpsReverbObject->m_sAp1.m_zApOut = pPreset->m_nAp1_ApOut; + } + + //stored as time based, convert to sample based + temp = pPreset->m_nXfadeInterval; + /*lint -e{702} shift for performance */ + temp = (temp * pReverb->m_nSamplingRate) >> 16; + pReverb->m_nXfadeInterval = (uint16_t) temp; + //gsReverbObject.m_nXfadeInterval = pPreset->m_nXfadeInterval; + pReverb->m_nXfadeCounter = pReverb->m_nXfadeInterval + 1; // force update on first iteration + + pReverb->m_nCurrentRoom = pReverb->m_nNextRoom; + + return 0; + +} /* end ReverbUpdateRoom */ + +/*---------------------------------------------------------------------------- + * ReverbReadInPresets() + *---------------------------------------------------------------------------- + * Purpose: sets global reverb preset bank to defaults + * + * Inputs: + * + * Outputs: + * + *---------------------------------------------------------------------------- + */ +static int ReverbReadInPresets(reverb_object_t *pReverb) { + + int preset; + + // this is for test only. OpenSL ES presets are mapped to 4 presets. + // REVERB_PRESET_NONE is mapped to bypass + for (preset = 0; preset < REVERB_NUM_PRESETS; preset++) { + reverb_preset_t *pPreset = &pReverb->m_sPreset.m_sPreset[preset]; + switch (preset + 1) { + case REVERB_PRESET_PLATE: + case REVERB_PRESET_SMALLROOM: + pPreset->m_nRvbLpfFbk = 5077; + pPreset->m_nRvbLpfFwd = 11076; + pPreset->m_nEarlyGain = 27690; + pPreset->m_nEarlyDelay = 1311; + pPreset->m_nLateGain = 8191; + pPreset->m_nLateDelay = 3932; + pPreset->m_nRoomLpfFbk = 3692; + pPreset->m_nRoomLpfFwd = 20474; + pPreset->m_sEarlyL.m_zDelay[0] = 1376; + pPreset->m_sEarlyL.m_nGain[0] = 22152; + pPreset->m_sEarlyL.m_zDelay[1] = 1462; + pPreset->m_sEarlyL.m_nGain[1] = 17537; + pPreset->m_sEarlyL.m_zDelay[2] = 0; + pPreset->m_sEarlyL.m_nGain[2] = 14768; + pPreset->m_sEarlyL.m_zDelay[3] = 1835; + pPreset->m_sEarlyL.m_nGain[3] = 14307; + pPreset->m_sEarlyL.m_zDelay[4] = 0; + pPreset->m_sEarlyL.m_nGain[4] = 13384; + pPreset->m_sEarlyR.m_zDelay[0] = 721; + pPreset->m_sEarlyR.m_nGain[0] = 20306; + pPreset->m_sEarlyR.m_zDelay[1] = 2621; + pPreset->m_sEarlyR.m_nGain[1] = 17537; + pPreset->m_sEarlyR.m_zDelay[2] = 0; + pPreset->m_sEarlyR.m_nGain[2] = 14768; + pPreset->m_sEarlyR.m_zDelay[3] = 0; + pPreset->m_sEarlyR.m_nGain[3] = 16153; + pPreset->m_sEarlyR.m_zDelay[4] = 0; + pPreset->m_sEarlyR.m_nGain[4] = 13384; + pPreset->m_nMaxExcursion = 127; + pPreset->m_nXfadeInterval = 6470; //6483; + pPreset->m_nAp0_ApGain = 14768; + pPreset->m_nAp0_ApOut = 792; + pPreset->m_nAp1_ApGain = 14777; + pPreset->m_nAp1_ApOut = 1191; + pPreset->m_rfu4 = 0; + pPreset->m_rfu5 = 0; + pPreset->m_rfu6 = 0; + pPreset->m_rfu7 = 0; + pPreset->m_rfu8 = 0; + pPreset->m_rfu9 = 0; + pPreset->m_rfu10 = 0; + break; + case REVERB_PRESET_MEDIUMROOM: + case REVERB_PRESET_LARGEROOM: + pPreset->m_nRvbLpfFbk = 5077; + pPreset->m_nRvbLpfFwd = 12922; + pPreset->m_nEarlyGain = 27690; + pPreset->m_nEarlyDelay = 1311; + pPreset->m_nLateGain = 8191; + pPreset->m_nLateDelay = 3932; + pPreset->m_nRoomLpfFbk = 3692; + pPreset->m_nRoomLpfFwd = 21703; + pPreset->m_sEarlyL.m_zDelay[0] = 1376; + pPreset->m_sEarlyL.m_nGain[0] = 22152; + pPreset->m_sEarlyL.m_zDelay[1] = 1462; + pPreset->m_sEarlyL.m_nGain[1] = 17537; + pPreset->m_sEarlyL.m_zDelay[2] = 0; + pPreset->m_sEarlyL.m_nGain[2] = 14768; + pPreset->m_sEarlyL.m_zDelay[3] = 1835; + pPreset->m_sEarlyL.m_nGain[3] = 14307; + pPreset->m_sEarlyL.m_zDelay[4] = 0; + pPreset->m_sEarlyL.m_nGain[4] = 13384; + pPreset->m_sEarlyR.m_zDelay[0] = 721; + pPreset->m_sEarlyR.m_nGain[0] = 20306; + pPreset->m_sEarlyR.m_zDelay[1] = 2621; + pPreset->m_sEarlyR.m_nGain[1] = 17537; + pPreset->m_sEarlyR.m_zDelay[2] = 0; + pPreset->m_sEarlyR.m_nGain[2] = 14768; + pPreset->m_sEarlyR.m_zDelay[3] = 0; + pPreset->m_sEarlyR.m_nGain[3] = 16153; + pPreset->m_sEarlyR.m_zDelay[4] = 0; + pPreset->m_sEarlyR.m_nGain[4] = 13384; + pPreset->m_nMaxExcursion = 127; + pPreset->m_nXfadeInterval = 6449; + pPreset->m_nAp0_ApGain = 15691; + pPreset->m_nAp0_ApOut = 774; + pPreset->m_nAp1_ApGain = 16317; + pPreset->m_nAp1_ApOut = 1155; + pPreset->m_rfu4 = 0; + pPreset->m_rfu5 = 0; + pPreset->m_rfu6 = 0; + pPreset->m_rfu7 = 0; + pPreset->m_rfu8 = 0; + pPreset->m_rfu9 = 0; + pPreset->m_rfu10 = 0; + break; + case REVERB_PRESET_MEDIUMHALL: + pPreset->m_nRvbLpfFbk = 6461; + pPreset->m_nRvbLpfFwd = 14307; + pPreset->m_nEarlyGain = 27690; + pPreset->m_nEarlyDelay = 1311; + pPreset->m_nLateGain = 8191; + pPreset->m_nLateDelay = 3932; + pPreset->m_nRoomLpfFbk = 3692; + pPreset->m_nRoomLpfFwd = 24569; + pPreset->m_sEarlyL.m_zDelay[0] = 1376; + pPreset->m_sEarlyL.m_nGain[0] = 22152; + pPreset->m_sEarlyL.m_zDelay[1] = 1462; + pPreset->m_sEarlyL.m_nGain[1] = 17537; + pPreset->m_sEarlyL.m_zDelay[2] = 0; + pPreset->m_sEarlyL.m_nGain[2] = 14768; + pPreset->m_sEarlyL.m_zDelay[3] = 1835; + pPreset->m_sEarlyL.m_nGain[3] = 14307; + pPreset->m_sEarlyL.m_zDelay[4] = 0; + pPreset->m_sEarlyL.m_nGain[4] = 13384; + pPreset->m_sEarlyR.m_zDelay[0] = 721; + pPreset->m_sEarlyR.m_nGain[0] = 20306; + pPreset->m_sEarlyR.m_zDelay[1] = 2621; + pPreset->m_sEarlyR.m_nGain[1] = 17537; + pPreset->m_sEarlyR.m_zDelay[2] = 0; + pPreset->m_sEarlyR.m_nGain[2] = 14768; + pPreset->m_sEarlyR.m_zDelay[3] = 0; + pPreset->m_sEarlyR.m_nGain[3] = 16153; + pPreset->m_sEarlyR.m_zDelay[4] = 0; + pPreset->m_sEarlyR.m_nGain[4] = 13384; + pPreset->m_nMaxExcursion = 127; + pPreset->m_nXfadeInterval = 6391; + pPreset->m_nAp0_ApGain = 15230; + pPreset->m_nAp0_ApOut = 708; + pPreset->m_nAp1_ApGain = 15547; + pPreset->m_nAp1_ApOut = 1023; + pPreset->m_rfu4 = 0; + pPreset->m_rfu5 = 0; + pPreset->m_rfu6 = 0; + pPreset->m_rfu7 = 0; + pPreset->m_rfu8 = 0; + pPreset->m_rfu9 = 0; + pPreset->m_rfu10 = 0; + break; + case REVERB_PRESET_LARGEHALL: + pPreset->m_nRvbLpfFbk = 8307; + pPreset->m_nRvbLpfFwd = 14768; + pPreset->m_nEarlyGain = 27690; + pPreset->m_nEarlyDelay = 1311; + pPreset->m_nLateGain = 8191; + pPreset->m_nLateDelay = 3932; + pPreset->m_nRoomLpfFbk = 3692; + pPreset->m_nRoomLpfFwd = 24569; + pPreset->m_sEarlyL.m_zDelay[0] = 1376; + pPreset->m_sEarlyL.m_nGain[0] = 22152; + pPreset->m_sEarlyL.m_zDelay[1] = 2163; + pPreset->m_sEarlyL.m_nGain[1] = 17537; + pPreset->m_sEarlyL.m_zDelay[2] = 0; + pPreset->m_sEarlyL.m_nGain[2] = 14768; + pPreset->m_sEarlyL.m_zDelay[3] = 1835; + pPreset->m_sEarlyL.m_nGain[3] = 14307; + pPreset->m_sEarlyL.m_zDelay[4] = 0; + pPreset->m_sEarlyL.m_nGain[4] = 13384; + pPreset->m_sEarlyR.m_zDelay[0] = 721; + pPreset->m_sEarlyR.m_nGain[0] = 20306; + pPreset->m_sEarlyR.m_zDelay[1] = 2621; + pPreset->m_sEarlyR.m_nGain[1] = 17537; + pPreset->m_sEarlyR.m_zDelay[2] = 0; + pPreset->m_sEarlyR.m_nGain[2] = 14768; + pPreset->m_sEarlyR.m_zDelay[3] = 0; + pPreset->m_sEarlyR.m_nGain[3] = 16153; + pPreset->m_sEarlyR.m_zDelay[4] = 0; + pPreset->m_sEarlyR.m_nGain[4] = 13384; + pPreset->m_nMaxExcursion = 127; + pPreset->m_nXfadeInterval = 6388; + pPreset->m_nAp0_ApGain = 15691; + pPreset->m_nAp0_ApOut = 711; + pPreset->m_nAp1_ApGain = 16317; + pPreset->m_nAp1_ApOut = 1029; + pPreset->m_rfu4 = 0; + pPreset->m_rfu5 = 0; + pPreset->m_rfu6 = 0; + pPreset->m_rfu7 = 0; + pPreset->m_rfu8 = 0; + pPreset->m_rfu9 = 0; + pPreset->m_rfu10 = 0; + break; + } + } + + return 0; +} diff --git a/media/libeffects/testlibs/EffectReverb.h b/media/libeffects/testlibs/EffectReverb.h new file mode 100644 index 0000000..ee8e390 --- /dev/null +++ b/media/libeffects/testlibs/EffectReverb.h @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_EFFECTREVERB_H_ +#define ANDROID_EFFECTREVERB_H_ + +#include +#include + + +/*------------------------------------ + * defines + *------------------------------------ +*/ + +/* +CIRCULAR() calculates the array index using modulo arithmetic. +The "trick" is that modulo arithmetic is simplified by masking +the effective address where the mask is (2^n)-1. This only works +if the buffer size is a power of two. +*/ +#define CIRCULAR(base,offset,size) (uint32_t)( \ + ( \ + ((int32_t)(base)) + ((int32_t)(offset)) \ + ) \ + & size \ + ) + +#define NUM_OUTPUT_CHANNELS 2 +#define OUTPUT_CHANNELS CHANNEL_STEREO + +#define REVERB_BUFFER_SIZE_IN_SAMPLES_MAX 16384 + +#define REVERB_NUM_PRESETS REVERB_PRESET_PLATE // REVERB_PRESET_NONE is not included +#define REVERB_MAX_NUM_REFLECTIONS 5 // max num reflections per channel + + +// xfade parameters +#define REVERB_XFADE_PERIOD_IN_SECONDS (double) (100.0 / 1000.0) // xfade once every this many seconds + + +/**********/ +/* the entire synth uses various flags in a bit field */ + +/* if flag is set, synth reset has been requested */ +#define REVERB_FLAG_RESET_IS_REQUESTED 0x01 /* bit 0 */ +#define MASK_REVERB_RESET_IS_REQUESTED 0x01 +#define MASK_REVERB_RESET_IS_NOT_REQUESTED (uint32_t)(~MASK_REVERB_RESET_IS_REQUESTED) + +/* +by default, we always want to update ALL channel parameters +when we reset the synth (e.g., during GM ON) +*/ +#define DEFAULT_REVERB_FLAGS 0x0 + +/* coefficients for generating sin, cos */ +#define REVERB_PAN_G2 4294940151 /* -0.82842712474619 = 2 - 4/sqrt(2) */ +/* +int32_t nPanG1 = +1.0 for sin +int32_t nPanG1 = -1.0 for cos +*/ +#define REVERB_PAN_G0 23170 /* 0.707106781186547 = 1/sqrt(2) */ + +/*************************************************************/ +// define the input injection points +#define GUARD 5 // safety guard of this many samples + +#define MAX_AP_TIME (int) ((20*65536)/1000) // delay time in time units (65536th of sec) +#define MAX_DELAY_TIME (int) ((65*65536)/1000) // delay time in time units +#define MAX_EARLY_TIME (int) ((65*65536)/1000) // delay time in time units + +#define AP0_IN 0 + + +#define REVERB_DEFAULT_ROOM_NUMBER 1 // default preset number +#define DEFAULT_AP0_GAIN 19400 +#define DEFAULT_AP1_GAIN -19400 + +#define REVERB_DEFAULT_WET 32767 +#define REVERB_DEFAULT_DRY 0 + +#define REVERB_WET_MAX 32767 +#define REVERB_WET_MIN 0 +#define REVERB_DRY_MAX 32767 +#define REVERB_DRY_MIN 0 + +// constants for reverb density +// The density expressed in permilles changes the Allpass delay in a linear manner in the range defined by +// AP0_TIME_BASE to AP0_TIME_BASE + AP0_TIME_RANGE +#define AP0_TIME_BASE (int)((9*65536)/1000) +#define AP0_TIME_RANGE (int)((4*65536)/1000) +#define AP1_TIME_BASE (int)((12*65536)/1000) +#define AP1_TIME_RANGE (int)((8*65536)/1000) + +// constants for reverb diffusion +// The diffusion expressed in permilles changes the Allpass gain in a linear manner in the range defined by +// AP0_GAIN_BASE to AP0_GAIN_BASE + AP0_GAIN_RANGE +#define AP0_GAIN_BASE (int)(9830) +#define AP0_GAIN_RANGE (int)(19660-9830) +#define AP1_GAIN_BASE (int)(6553) +#define AP1_GAIN_RANGE (int)(22936-6553) + + +enum reverb_state_e { + REVERB_STATE_UNINITIALIZED, + REVERB_STATE_INITIALIZED, + REVERB_STATE_ACTIVE, +}; + +/* parameters for each allpass */ +typedef struct +{ + uint16_t m_zApOut; // delay offset for ap out + + int16_t m_nApGain; // gain for ap + + uint16_t m_zApIn; // delay offset for ap in + +} allpass_object_t; + + +/* parameters for early reflections */ +typedef struct +{ + uint16_t m_zDelay[REVERB_MAX_NUM_REFLECTIONS]; // delay offset for ap out + + int16_t m_nGain[REVERB_MAX_NUM_REFLECTIONS]; // gain for ap + +} early_reflection_object_t; + +//demo +typedef struct +{ + int16_t m_nRvbLpfFbk; + int16_t m_nRvbLpfFwd; + int16_t m_nRoomLpfFbk; + int16_t m_nRoomLpfFwd; + + int16_t m_nEarlyGain; + int16_t m_nEarlyDelay; + int16_t m_nLateGain; + int16_t m_nLateDelay; + + early_reflection_object_t m_sEarlyL; + early_reflection_object_t m_sEarlyR; + + uint16_t m_nMaxExcursion; //28 + int16_t m_nXfadeInterval; + + int16_t m_nAp0_ApGain; //30 + int16_t m_nAp0_ApOut; + int16_t m_nAp1_ApGain; + int16_t m_nAp1_ApOut; + int16_t m_nDiffusion; + + int16_t m_rfu4; + int16_t m_rfu5; + int16_t m_rfu6; + int16_t m_rfu7; + int16_t m_rfu8; + int16_t m_rfu9; + int16_t m_rfu10; //43 + +} reverb_preset_t; + +typedef struct +{ + reverb_preset_t m_sPreset[REVERB_NUM_PRESETS]; // array of presets(does not include REVERB_PRESET_NONE) + +} reverb_preset_bank_t; + + +/* parameters for each reverb */ +typedef struct +{ + /* update counter keeps track of when synth params need updating */ + /* only needs to be as large as REVERB_UPDATE_PERIOD_IN_SAMPLES */ + int16_t m_nUpdateCounter; + + uint16_t m_nBaseIndex; // base index for circular buffer + + // reverb delay line offsets, allpass parameters, etc: + + short m_nRevFbkR; // combine feedback reverb right out with dry left in + short m_zOutLpfL; // left reverb output + + allpass_object_t m_sAp0; // allpass 0 (left channel) + + uint16_t m_zD0In; // delay offset for delay line D0 in + + short m_nRevFbkL; // combine feedback reverb left out with dry right in + short m_zOutLpfR; // right reverb output + + allpass_object_t m_sAp1; // allpass 1 (right channel) + + uint16_t m_zD1In; // delay offset for delay line D1 in + + // delay output taps, notice criss cross order + uint16_t m_zD0Self; // self feeds forward d0 --> d0 + + uint16_t m_zD1Cross; // cross feeds across d1 --> d0 + + uint16_t m_zD1Self; // self feeds forward d1 --> d1 + + uint16_t m_zD0Cross; // cross feeds across d0 --> d1 + + int16_t m_nSin; // gain for self taps + + int16_t m_nCos; // gain for cross taps + + int16_t m_nSinIncrement; // increment for gain + + int16_t m_nCosIncrement; // increment for gain + + int16_t m_nRvbLpfFwd; // reverb feedback lpf forward gain (includes scaling for mixer) + + int16_t m_nRvbLpfFbk; // reverb feedback lpf feedback gain + + int16_t m_nRoomLpfFwd; // room lpf forward gain (includes scaling for mixer) + + int16_t m_nRoomLpfFbk; // room lpf feedback gain + + uint16_t m_nXfadeInterval; // update/xfade after this many samples + + uint16_t m_nXfadeCounter; // keep track of when to xfade + + int16_t m_nPhase; // -1 <= m_nPhase < 1 + // but during sin,cos calculations + // use m_nPhase/2 + + int16_t m_nPhaseIncrement; // add this to m_nPhase each frame + + int16_t m_nNoise; // random noise sample + + uint16_t m_nMaxExcursion; // the taps can excurse +/- this amount + + uint16_t m_bUseNoise; // if TRUE, use noise as input signal + + uint16_t m_bBypass; // if TRUE, then bypass reverb and copy input to output + + int16_t m_nCurrentRoom; // preset number for current room + + int16_t m_nNextRoom; // preset number for next room + + int16_t m_nEarlyGain; // gain for early (widen) signal + int16_t m_nEarlyDelay; // initial dealy for early (widen) signal + int16_t m_nEarly0in; + int16_t m_nEarly1in; + int16_t m_nLateGain; // gain for late reverb + int16_t m_nLateDelay; + + int16_t m_nDiffusion; + + early_reflection_object_t m_sEarlyL; // left channel early reflections + early_reflection_object_t m_sEarlyR; // right channel early reflections + + short m_nDelayLine[REVERB_BUFFER_SIZE_IN_SAMPLES_MAX]; // one large delay line for all reverb elements + + reverb_preset_t pPreset; + + reverb_preset_bank_t m_sPreset; + + //int8_t preset; + uint32_t m_nSamplingRate; + int32_t m_nUpdatePeriodInBits; + int32_t m_nBufferMask; + int32_t m_nUpdatePeriodInSamples; + int32_t m_nDelay0Out; + int32_t m_nDelay1Out; + int16_t m_nCosWT_5KHz; + + uint16_t m_Aux; // if TRUE, is connected as auxiliary effect + uint16_t m_Preset; // if TRUE, expose preset revert interface + + uint32_t mState; +} reverb_object_t; + + + +typedef struct reverb_module_s { + const struct effect_interface_s *itfe; + effect_config_t config; + reverb_object_t context; +} reverb_module_t; + +/*------------------------------------ + * Effect API + *------------------------------------ +*/ +int EffectQueryNumberEffects(uint32_t *pNumEffects); +int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor); +int EffectCreate(effect_uuid_t *effectUID, int32_t sessionId, int32_t ioId, effect_interface_t *pInterface); +int EffectRelease(effect_interface_t interface); + +static int Reverb_Process(effect_interface_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer); +static int Reverb_Command(effect_interface_t self, int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData); + + +/*------------------------------------ + * internal functions + *------------------------------------ +*/ + +int Reverb_Init(reverb_module_t *pRvbModule, int aux, int preset); +int Reverb_Configure(reverb_module_t *pRvbModule, effect_config_t *pConfig, bool init); +void Reverb_Reset(reverb_object_t *pReverb, bool init); + +int Reverb_setParameter (reverb_object_t *pReverb, int32_t param, size_t size, void *pValue); +int Reverb_getParameter(reverb_object_t *pReverb, int32_t param, size_t *pSize, void *pValue); + +/*---------------------------------------------------------------------------- + * ReverbUpdateXfade + *---------------------------------------------------------------------------- + * Purpose: + * Update the xfade parameters as required + * + * Inputs: + * nNumSamplesToAdd - number of samples to write to buffer + * + * Outputs: + * + * + * Side Effects: + * - xfade parameters will be changed + * + *---------------------------------------------------------------------------- +*/ +static int ReverbUpdateXfade(reverb_object_t* pReverbData, int nNumSamplesToAdd); + +/*---------------------------------------------------------------------------- + * ReverbCalculateNoise + *---------------------------------------------------------------------------- + * Purpose: + * Calculate a noise sample and limit its value + * + * Inputs: + * Pointer to reverb context + * + * Outputs: + * new limited noise value + * + * Side Effects: + * - pReverbData->m_nNoise value is updated + * + *---------------------------------------------------------------------------- +*/ +static uint16_t ReverbCalculateNoise(reverb_object_t *pReverbData); + +/*---------------------------------------------------------------------------- + * ReverbCalculateSinCos + *---------------------------------------------------------------------------- + * Purpose: + * Calculate a new sin and cosine value based on the given phase + * + * Inputs: + * nPhase - phase angle + * pnSin - input old value, output new value + * pnCos - input old value, output new value + * + * Outputs: + * + * Side Effects: + * - *pnSin, *pnCos are updated + * + *---------------------------------------------------------------------------- +*/ +static int ReverbCalculateSinCos(int16_t nPhase, int16_t *pnSin, int16_t *pnCos); + +/*---------------------------------------------------------------------------- + * Reverb + *---------------------------------------------------------------------------- + * Purpose: + * apply reverb to the given signal + * + * Inputs: + * nNu + * pnSin - input old value, output new value + * pnCos - input old value, output new value + * + * Outputs: + * number of samples actually reverberated + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +static int Reverb(reverb_object_t* pReverbData, int nNumSamplesToAdd, short *pOutputBuffer, short *pInputBuffer); + +/*---------------------------------------------------------------------------- + * ReverbReadInPresets() + *---------------------------------------------------------------------------- + * Purpose: sets global reverb preset bank to defaults + * + * Inputs: + * + * Outputs: + * + *---------------------------------------------------------------------------- +*/ +static int ReverbReadInPresets(reverb_object_t* pReverbData); + + +/*---------------------------------------------------------------------------- + * ReverbUpdateRoom + *---------------------------------------------------------------------------- + * Purpose: + * Update the room's preset parameters as required + * + * Inputs: + * + * Outputs: + * + * + * Side Effects: + * - reverb paramters (fbk, fwd, etc) will be changed + * - m_nCurrentRoom := m_nNextRoom + *---------------------------------------------------------------------------- +*/ +static int ReverbUpdateRoom(reverb_object_t* pReverbData, bool fullUpdate); + + +static int ReverbComputeConstants(reverb_object_t *pReverbData, uint32_t samplingRate); + +#endif /*ANDROID_EFFECTREVERB_H_*/ diff --git a/media/libeffects/testlibs/EffectsMath.c b/media/libeffects/testlibs/EffectsMath.c new file mode 100644 index 0000000..41ec662 --- /dev/null +++ b/media/libeffects/testlibs/EffectsMath.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2008 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. + */ +#define LOG_TAG "EFFECTSMATH" +//#define LOG_NDEBUG 0 +#include +#include + +#include "EffectsMath.h" + +// gLogTab contains pre-calculated values of log2(1 + ai5*2^-1 + ai4*2^-2 + ai3*2^-3 + ai2*2^-4 + ai1*2^-5 + ai0*2^-6) +// for integers in the range 0 to 63 (i = ai5*2^5 + ai4*2^4 + ai3*2^3 + ai2*2^2 + ai1*2^1 + ai0*2^0) +// It is used for a better than piece wise approximation of lin to log2 conversion + +static const uint16_t gLogTab[] = +{ + 0, 733, 1455, 2166, + 2866, 3556, 4236, 4907, + 5568, 6220, 6863, 7498, + 8124, 8742, 9352, 9954, + 10549, 11136, 11716, 12289, + 12855, 13415, 13968, 14514, + 15055, 15589, 16117, 16639, + 17156, 17667, 18173, 18673, + 19168, 19658, 20143, 20623, + 21098, 21568, 22034, 22495, + 22952, 23404, 23852, 24296, + 24736, 25172, 25604, 26031, + 26455, 26876, 27292, 27705, + 28114, 28520, 28922, 29321, + 29717, 30109, 30498, 30884, + 31267, 31647, 32024, 32397, + 32768 +}; + +int32_t Effects_log2(uint32_t x) { + int32_t exp = 31 - __builtin_clz(x); + uint32_t segStart = x >> (exp - 6); + uint32_t i = segStart & 0x3F; + int32_t log = (int32_t)gLogTab[i]; + int32_t logEnd = (int32_t)gLogTab[i+1]; + segStart <<= exp - 6; + + return (exp << 15) + log + (((x - segStart) * (logEnd - log)) >> (exp - 6)); +} + +// gExpTab[i] = (2^(i>>6)) << 22 +static const uint32_t gExpTab[] = { + 4194304, 4239977, 4286147, 4332820, + 4380002, 4427697, 4475911, 4524651, + 4573921, 4623728, 4674077, 4724974, + 4776426, 4828438, 4881016, 4934167, + 4987896, 5042211, 5097117, 5152621, + 5208729, 5265449, 5322786, 5380747, + 5439339, 5498570, 5558445, 5618973, + 5680159, 5742012, 5804539, 5867746, + 5931642, 5996233, 6061528, 6127533, + 6194258, 6261709, 6329894, 6398822, + 6468501, 6538938, 6610143, 6682122, + 6754886, 6828442, 6902799, 6977965, + 7053950, 7130763, 7208412, 7286906, + 7366255, 7446469, 7527555, 7609525, + 7692387, 7776152, 7860829, 7946428, + 8032959, 8120432, 8208857, 8298246, + 8388608 +}; + + +uint32_t Effects_exp2(int32_t x) { + int32_t i = x >> 15; + assert(i < 32); + x &= (1 << 15) - 1; + int32_t j = x >> 9; + x &= (1 << 9) - 1; + uint32_t exp = gExpTab[j]; + uint32_t expEnd = gExpTab[j+1]; + + return ((exp << 9) + (expEnd - exp) * x) >> (31 - i); +} + + +int16_t Effects_MillibelsToLinear16 (int32_t nGain) +{ + nGain = ((nGain + MB_TO_LIN_K1) << 15 ) / MB_TO_LIN_K2; + uint32_t exp2 = Effects_exp2(nGain); + + if (exp2 > 32767) exp2 = 32767; + + return (int16_t)exp2; +} + + +int16_t Effects_Linear16ToMillibels (int32_t nGain) +{ + return (int16_t)(((MB_TO_LIN_K2*Effects_log2(nGain))>>15)-MB_TO_LIN_K1); +} + + +int32_t Effects_Sqrt(int32_t in) +{ + int32_t tmp; + int32_t out = 0; + int32_t i; + int32_t j; + + + if (in == 0) return 0; + + if (in >= 0x10000000) + { + out = 0x4000; + in -= 0x10000000; + } + + j = 32 - __builtin_clz(in); + + if (j & 1) j++; + j >>= 1; + + for (i = j; i > 0; i--) { + tmp = (out << i) + (1 << ((i - 1)*2)); + if (in >= tmp) + { + out += 1 << (i-1); + in -= tmp; + } + } + + return out; +} + diff --git a/media/libeffects/testlibs/EffectsMath.h b/media/libeffects/testlibs/EffectsMath.h new file mode 100644 index 0000000..2a44399 --- /dev/null +++ b/media/libeffects/testlibs/EffectsMath.h @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_EFFECTSMATH_H_ +#define ANDROID_EFFECTSMATH_H_ + +#include + +#if __cplusplus +extern "C" { +#endif + +/** coefs for pan, generates sin, cos */ +#define COEFF_PAN_G2 -27146 /* -0.82842712474619 = 2 - 4/sqrt(2) */ +#define COEFF_PAN_G0 23170 /* 0.707106781186547 = 1/sqrt(2) */ + +/* +coefficients for approximating +2^x = gn2toX0 + gn2toX1*x + gn2toX2*x^2 + gn2toX3*x^3 +where x is a int.frac number representing number of octaves. +Actually, we approximate only the 2^(frac) using the power series +and implement the 2^(int) as a shift, so that +2^x == 2^(int.frac) == 2^(int) * 2^(fract) + == (gn2toX0 + gn2toX1*x + gn2toX2*x^2 + gn2toX3*x^3) << (int) + +The gn2toX.. were generated using a best fit for a 3rd +order polynomial, instead of taking the coefficients from +a truncated Taylor (or Maclaurin?) series. +*/ + +#define GN2_TO_X0 32768 /* 1 */ +#define GN2_TO_X1 22833 /* 0.696807861328125 */ +#define GN2_TO_X2 7344 /* 0.22412109375 */ +#define GN2_TO_X3 2588 /* 0.0789794921875 */ + +/*---------------------------------------------------------------------------- + * Fixed Point Math + *---------------------------------------------------------------------------- + * These macros are used for fixed point multiplies. If the processor + * supports fixed point multiplies, replace these macros with inline + * assembly code to improve performance. + *---------------------------------------------------------------------------- +*/ + +/* Fixed point multiply 0.15 x 0.15 = 0.15 returned as 32-bits */ +#define FMUL_15x15(a,b) \ + /*lint -e(704) */ \ + (((int32_t)(a) * (int32_t)(b)) >> 15) + +/* Fixed point multiply 0.7 x 0.7 = 0.15 returned as 32-bits */ +#define FMUL_7x7(a,b) \ + /*lint -e(704) */ \ + (((int32_t)(a) * (int32_t)(b) ) << 1) + +/* Fixed point multiply 0.8 x 0.8 = 0.15 returned as 32-bits */ +#define FMUL_8x8(a,b) \ + /*lint -e(704) */ \ + (((int32_t)(a) * (int32_t)(b) ) >> 1) + +/* Fixed point multiply 0.8 x 1.15 = 0.15 returned as 32-bits */ +#define FMUL_8x15(a,b) \ + /*lint -e(704) */ \ + (((int32_t)((a) << 7) * (int32_t)(b)) >> 15) + +/* macros for fractional phase accumulator */ +/* +Note: changed the _U32 to _I32 on 03/14/02. This should not +affect the phase calculations, and should allow us to reuse these +macros for other audio sample related math. +*/ +#define HARDWARE_BIT_WIDTH 32 + +#define NUM_PHASE_INT_BITS 1 +#define NUM_PHASE_FRAC_BITS 15 + +#define PHASE_FRAC_MASK (uint32_t) ((0x1L << NUM_PHASE_FRAC_BITS) -1) + +#define GET_PHASE_INT_PART(x) (uint32_t)((uint32_t)(x) >> NUM_PHASE_FRAC_BITS) +#define GET_PHASE_FRAC_PART(x) (uint32_t)((uint32_t)(x) & PHASE_FRAC_MASK) + +#define DEFAULT_PHASE_FRAC 0 +#define DEFAULT_PHASE_INT 0 + +/* +Linear interpolation calculates: +output = (1-frac) * sample[n] + (frac) * sample[n+1] + +where conceptually 0 <= frac < 1 + +For a fixed point implementation, frac is actually an integer value +with an implied binary point one position to the left. The value of +one (unity) is given by PHASE_ONE +one half and one quarter are useful for 4-point linear interp. +*/ +#define PHASE_ONE (int32_t) (0x1L << NUM_PHASE_FRAC_BITS) + +/* + Multiply the signed audio sample by the unsigned fraction. +- a is the signed audio sample +- b is the unsigned fraction (cast to signed int as long as coef + uses (n-1) or less bits, where n == hardware bit width) +*/ +#define MULT_AUDIO_COEF(audio,coef) /*lint -e704 */ \ + (int32_t)( \ + ( \ + ((int32_t)(audio)) * ((int32_t)(coef)) \ + ) \ + >> NUM_PHASE_FRAC_BITS \ + ) \ + /* lint +704 */ + +/* wet / dry calculation macros */ +#define NUM_WET_DRY_FRAC_BITS 7 // 15 +#define NUM_WET_DRY_INT_BITS 9 // 1 + +/* define a 1.0 */ +#define WET_DRY_ONE (int32_t) ((0x1L << NUM_WET_DRY_FRAC_BITS)) +#define WET_DRY_MINUS_ONE (int32_t) (~WET_DRY_ONE) +#define WET_DRY_FULL_SCALE (int32_t) (WET_DRY_ONE - 1) + +#define MULT_AUDIO_WET_DRY_COEF(audio,coef) /*lint -e(702) */ \ + (int32_t)( \ + ( \ + ((int32_t)(audio)) * ((int32_t)(coef)) \ + ) \ + >> NUM_WET_DRY_FRAC_BITS \ + ) + +/* Envelope 1 (EG1) calculation macros */ +#define NUM_EG1_INT_BITS 1 +#define NUM_EG1_FRAC_BITS 15 + +/* the max positive gain used in the synth for EG1 */ +/* SYNTH_FULL_SCALE_EG1_GAIN must match the value in the dls2eas +converter, otherwise, the values we read from the .eas file are bogus. */ +#define SYNTH_FULL_SCALE_EG1_GAIN (int32_t) ((0x1L << NUM_EG1_FRAC_BITS) -1) + +/* define a 1.0 */ +#define EG1_ONE (int32_t) ((0x1L << NUM_EG1_FRAC_BITS)) +#define EG1_MINUS_ONE (int32_t) (~SYNTH_FULL_SCALE_EG1_GAIN) + +#define EG1_HALF (int32_t) (EG1_ONE/2) +#define EG1_MINUS_HALF (int32_t) (EG1_MINUS_ONE/2) + +/* +We implement the EG1 using a linear gain value, which means that the +attack segment is handled by incrementing (adding) the linear gain. +However, EG1 treats the Decay, Sustain, and Release differently than +the Attack portion. For Decay, Sustain, and Release, the gain is +linear on dB scale, which is equivalent to exponential damping on +a linear scale. Because we use a linear gain for EG1, we implement +the Decay and Release as multiplication (instead of incrementing +as we did for the attack segment). +Therefore, we need the following macro to implement the multiplication +(i.e., exponential damping) during the Decay and Release segments of +the EG1 +*/ +#define MULT_EG1_EG1(gain,damping) /*lint -e(704) */ \ + (int32_t)( \ + ( \ + ((int32_t)(gain)) * ((int32_t)(damping)) \ + ) \ + >> NUM_EG1_FRAC_BITS \ + ) + +// Use the following macro specifically for the filter, when multiplying +// the b1 coefficient. The 0 <= |b1| < 2, which therefore might overflow +// in certain conditions because we store b1 as a 1.15 value. +// Instead, we could store b1 as b1p (b1' == b1 "prime") where +// b1p == b1/2, thus ensuring no potential overflow for b1p because +// 0 <= |b1p| < 1 +// However, during the filter calculation, we must account for the fact +// that we are using b1p instead of b1, and thereby multiply by +// an extra factor of 2. Rather than multiply by an extra factor of 2, +// we can instead shift the result right by one less, hence the +// modified shift right value of (NUM_EG1_FRAC_BITS -1) +#define MULT_EG1_EG1_X2(gain,damping) /*lint -e(702) */ \ + (int32_t)( \ + ( \ + ((int32_t)(gain)) * ((int32_t)(damping)) \ + ) \ + >> (NUM_EG1_FRAC_BITS -1) \ + ) + +#define SATURATE_EG1(x) /*lint -e{734} saturation operation */ \ + ((int32_t)(x) > SYNTH_FULL_SCALE_EG1_GAIN) ? (SYNTH_FULL_SCALE_EG1_GAIN) : \ + ((int32_t)(x) < EG1_MINUS_ONE) ? (EG1_MINUS_ONE) : (x); + + +/* use "digital cents" == "dents" instead of cents */ +/* we coudl re-use the phase frac macros, but if we do, +we must change the phase macros to cast to _I32 instead of _U32, +because using a _U32 cast causes problems when shifting the exponent +for the 2^x calculation, because right shift a negative values MUST +be sign extended, or else the 2^x calculation is wrong */ + +/* use "digital cents" == "dents" instead of cents */ +#define NUM_DENTS_FRAC_BITS 12 +#define NUM_DENTS_INT_BITS (HARDWARE_BIT_WIDTH - NUM_DENTS_FRAC_BITS) + +#define DENTS_FRAC_MASK (int32_t) ((0x1L << NUM_DENTS_FRAC_BITS) -1) + +#define GET_DENTS_INT_PART(x) /*lint -e(704) */ \ + (int32_t)((int32_t)(x) >> NUM_DENTS_FRAC_BITS) + +#define GET_DENTS_FRAC_PART(x) (int32_t)((int32_t)(x) & DENTS_FRAC_MASK) + +#define DENTS_ONE (int32_t) (0x1L << NUM_DENTS_FRAC_BITS) + +/* use CENTS_TO_DENTS to convert a value in cents to dents */ +#define CENTS_TO_DENTS (int32_t) (DENTS_ONE * (0x1L << NUM_EG1_FRAC_BITS) / 1200L) \ + + +/* +For gain, the LFO generates a value that modulates in terms +of dB. However, we use a linear gain value, so we must convert +the LFO value in dB to a linear gain. Normally, we would use +linear gain = 10^x, where x = LFO value in dB / 20. +Instead, we implement 10^x using our 2^x approximation. +because + + 10^x = 2^(log2(10^x)) = 2^(x * log2(10)) + +so we need to multiply by log2(10) which is just a constant. +Ah, but just wait -- our 2^x actually doesn't exactly implement +2^x, but it actually assumes that the input is in cents, and within +the 2^x approximation converts its input from cents to octaves +by dividing its input by 1200. + +So, in order to convert the LFO gain value in dB to something +that our existing 2^x approximation can use, multiply the LFO gain +by log2(10) * 1200 / 20 + +The divide by 20 helps convert dB to linear gain, and we might +as well incorporate that operation into this conversion. +Of course, we need to keep some fractional bits, so multiply +the constant by NUM_EG1_FRAC_BITS +*/ + +/* use LFO_GAIN_TO_CENTS to convert the LFO gain value to cents */ +#if 0 +#define DOUBLE_LOG2_10 (double) (3.32192809488736) /* log2(10) */ + +#define DOUBLE_LFO_GAIN_TO_CENTS (double) \ + ( \ + (DOUBLE_LOG2_10) * \ + 1200.0 / \ + 20.0 \ + ) + +#define LFO_GAIN_TO_CENTS (int32_t) \ + ( \ + DOUBLE_LFO_GAIN_TO_CENTS * \ + (0x1L << NUM_EG1_FRAC_BITS) \ + ) +#endif + +#define LFO_GAIN_TO_CENTS (int32_t) (1671981156L >> (23 - NUM_EG1_FRAC_BITS)) + + +#define MULT_DENTS_COEF(dents,coef) /*lint -e704 */ \ + (int32_t)( \ + ( \ + ((int32_t)(dents)) * ((int32_t)(coef)) \ + ) \ + >> NUM_DENTS_FRAC_BITS \ + ) \ + /* lint +e704 */ + + +/* we use 16-bits in the PC per audio sample */ +#define BITS_PER_AUDIO_SAMPLE 16 + +/* we define 1 as 1.0 - 1 LSbit */ +#define DISTORTION_ONE (int32_t)((0x1L << (BITS_PER_AUDIO_SAMPLE-1)) -1) +#define DISTORTION_MINUS_ONE (int32_t)(~DISTORTION_ONE) + +/* drive coef is given as int.frac */ +#define NUM_DRIVE_COEF_INT_BITS 1 +#define NUM_DRIVE_COEF_FRAC_BITS 4 + +#define MULT_AUDIO_DRIVE(audio,drive) /*lint -e(702) */ \ + (int32_t) ( \ + ( \ + ((int32_t)(audio)) * ((int32_t)(drive)) \ + ) \ + >> NUM_DRIVE_COEF_FRAC_BITS \ + ) + +#define MULT_AUDIO_AUDIO(audio1,audio2) /*lint -e(702) */ \ + (int32_t) ( \ + ( \ + ((int32_t)(audio1)) * ((int32_t)(audio2)) \ + ) \ + >> (BITS_PER_AUDIO_SAMPLE-1) \ + ) + +#define SATURATE(x) \ + ((((int32_t)(x)) > DISTORTION_ONE) ? (DISTORTION_ONE) : \ + (((int32_t)(x)) < DISTORTION_MINUS_ONE) ? (DISTORTION_MINUS_ONE) : ((int32_t)(x))); + + +/*---------------------------------------------------------------------------- + * Effects_log2() + *---------------------------------------------------------------------------- + * Purpose: + * Fixed-point log2 function. + * + * Inputs: + * Input is interpreted as an integer (should not be 0). + * + * Outputs: + * Output is in 15-bit precision. + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +int32_t Effects_log2(uint32_t x); + +/*---------------------------------------------------------------------------- + * Effects_exp2() + *---------------------------------------------------------------------------- + * Purpose: + * Fixed-point radix-2 exponent. + * + * Inputs: + * Input is in 15-bit precision. Must be non-negative and less than 32. + * + * Outputs: + * Output is an integer. + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +uint32_t Effects_exp2(int32_t x); + +/*---------------------------------------------------------------------------- + * Effects_MillibelsToLinear16() + *---------------------------------------------------------------------------- + * Purpose: + * Transform gain in millibels to linear gain multiplier: + * + * mB = 2000*log(lin/32767) + * => lin = 2^((mB+2000*log(32767))/2000*log(2)) + * => lin = Effects_exp2(((mB + K1) << 15) / K2) + * with: + * K1 = 2000*log(32767) and K2 = 2000*log(2) + * + * Inputs: + * nGain - log scale value in millibels. + * + * Outputs: + * Returns a 16-bit linear value approximately equal to 2^(nGain/1024) + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +#define MB_TO_LIN_K1 9031 +#define MB_TO_LIN_K2 602 +int16_t Effects_MillibelsToLinear16 (int32_t nGain); + +/*---------------------------------------------------------------------------- + * Effects_Linear16ToMillibels() + *---------------------------------------------------------------------------- + * Purpose: + * Transform linear gain multiplier to millibels + * mB = 2000*log(lin/32767) + * = 2000*log(2)*log2(lin)-2000*log(32767) + * => mB = K1*Effects_log2(lin) + K2 + * with: + * K1 = 2000*log(2) and K2 = -2000*log(32767) + * + * Inputs: + * nGain - linear multiplier ranging form 0 to 32767 (corresponding to [0 1] gain range). + * + * Outputs: + * Returns a 16-bit log value expressed in milllibels. + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +int16_t Effects_Linear16ToMillibels (int32_t nGain); + +/*---------------------------------------------------------------------------- + * Effects_Sqrt() + *---------------------------------------------------------------------------- + * Purpose: + * Returns the square root of the argument given. + * + * Inputs: + * in - positive number in the range 0 - 2^28 + * + * Outputs: + * Returned value: square root of in. + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +int32_t Effects_Sqrt(int32_t in); + +#if __cplusplus +} // extern "C" +#endif + +#endif /*ANDROID_EFFECTSMATH_H_*/ + -- cgit v1.1