316 lines
10 KiB
C++
316 lines
10 KiB
C++
/*
|
|
* 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 <assert.h>
|
|
#include <stdlib.h>
|
|
#include <new>
|
|
#include <utils/Log.h>
|
|
|
|
#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<AudioPeakingFilter *>(pMem);
|
|
for (int i = 0; i < mNumPeaking; ++i) {
|
|
new (&mpPeakingFilters[i]) AudioPeakingFilter(nChannels,
|
|
sampleRate);
|
|
}
|
|
}
|
|
reset();
|
|
}
|
|
|
|
}
|