| /* |
| * 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) { |
| ALOGV("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) { |
| ALOGV("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() { |
| ALOGV("AudioEqualizer::clear()"); |
| mpLowShelf->clear(); |
| for (int i = 0; i < mNumPeaking; ++i) { |
| mpPeakingFilters[i].clear(); |
| } |
| mpHighShelf->clear(); |
| } |
| |
| void AudioEqualizer::free() { |
| ALOGV("AudioEqualizer::free()"); |
| if (mpMem != NULL) { |
| ::free(mpMem); |
| } |
| } |
| |
| void AudioEqualizer::reset() { |
| ALOGV("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) { |
| ALOGV("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) { |
| ALOGV("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) { |
| ALOGV("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) { |
| ALOGV("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) { |
| ALOGV("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) { |
| // ALOGV("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) { |
| ALOGV("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) { |
| ALOGV("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(); |
| } |
| |
| } |