/* * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "AVFAudioEqualizer.h" #include #define kAVFMinimumQFactor 1e-9 #define IND_A0 0 #define IND_A1 1 #define IND_A2 2 #define IND_B1 3 #define IND_B2 4 class AVFEqualizerKernel : public AUKernelBase { public: AVFEqualizerKernel(AVFAudioEqualizer *eq, AUEffectBase *inAudioUnit) : AUKernelBase(dynamic_cast(inAudioUnit)), mEQ(eq) {} virtual ~AVFEqualizerKernel() {} virtual void Process(const Float32 *inSourceP, Float32 *inDestP, UInt32 inFramesToProcess, UInt32 inNumChannels, bool& ioSilence) { if (ioSilence) { return; } mEQ->RunFilter(inSourceP, inDestP, inFramesToProcess, mChannelNum); } private: AVFAudioEqualizer *mEQ; }; #pragma mark - AVFEqualizerBand::AVFEqualizerBand(AVFAudioEqualizer *eq, double frequency, double bandwidth, double gain) : CEqualizerBand(bandwidth, gain), mEQ(eq), mBypass(true), mChannels(0), mHistory(NULL), mFrequency(frequency), mFilterType(Peak) // set later by the EQ, can be changed if band moves { // we may not have an audio unit yet int channels = mEQ->GetChannelCount(); if (channels > 0) { SetChannelCount(channels); } RecalculateParams(); } AVFEqualizerBand::~AVFEqualizerBand() { if (mHistory) { delete[] mHistory; mHistory = NULL; } } void AVFEqualizerBand::SetFilterType(AVFEqualizerBand::AVFEqualizerFilterType type) { mFilterType = type; RecalculateParams(); } void AVFEqualizerBand::SetCenterFrequency(double centerFrequency) { mEQ->MoveBand(mFrequency, centerFrequency); mFrequency = centerFrequency; RecalculateParams(); } void AVFEqualizerBand::SetChannelCount(int newCount) { if (newCount == mChannels) { return; } if (mHistory) { delete[] mHistory; mHistory = NULL; } mChannels = newCount; if (mChannels > 0) { mHistory = new AVFEQBandHistory[mChannels]; } } // These calculations are based on the GStreamer equalizer, so we can produce // basically the same results static inline double calculate_omega(double centerFreq, double sampleRate) { if (centerFreq / sampleRate >= 0.5) { return M_PI; } if (centerFreq < 0.0) { return 0.0; } return 2.0 * M_PI * (centerFreq / sampleRate); } static inline double calculate_bandwidth(double bw, double rate) { if (bw / rate >= 0.5) { // tan(M_PI/2) fails, so set to slightly less than pi return M_PI - 0.00000001; } if (bw <= 0.0) { // this effectively disables the filter return 0.0; } return 2.0 * M_PI * (bw / rate); } /* * vDSP_deq22: * https://developer.apple.com/library/mac/documentation/Accelerate/Reference/vDSPRef/Reference/reference.html#//apple_ref/c/func/vDSP_deq22 * * The biquadratic filter equation for the nth sample is: * D[n] = S[n] * a0 + S[n-1] * a1 + S[n-2] * a2 - D[n-1] * b1 - D[n-2] * b2 * * vDSP_deq22 stuffs all coefficients in vector B and uses: * for p in [0,2]: * A(n-p)i * B(p) -> A(n)*B[0] + A(n-1)*B[1] + A(n-2)*B[2] * * for p in [3,4]: * C(n-p+2)k * B(p) -> C(n-1)*B[3] + C(n-2)*B[4] * * where A and C are vectors of at least size N+2 * so B[0..2] is a0 to a2 respectively and B[3..4] is b1 and b2 respectively * * The formulae used to calculate the coefficients are taken from GStreamer so * we can match the behavior of the GStreamer pipeline (and they work well enough) * though modified for SIMD operations using vDSP_deq22. * * Note that the GStreamer coefficient names (a0-a2,b0-b2) are swapped from other * examples, but the use is the same. */ void AVFEqualizerBand::SetupPeakFilter(double omega, double bw, double gain) { double cosF = cos(omega); double alpha = tan(bw / 2.0); double alpha1 = alpha * gain; double alpha2 = alpha / gain; // set up peak filter coefficients mCoefficients[IND_A0] = 1.0 + alpha1; mCoefficients[IND_A1] = -2.0 * cosF; mCoefficients[IND_A2] = 1.0 - alpha1; double b0 = 1.0 + alpha2; mCoefficients[IND_B1] = -2.0 * cosF; mCoefficients[IND_B2] = 1.0 - alpha2; // pre-scale coefficients vDSP_vsdivD(mCoefficients, 1, &b0, mCoefficients, 1, 5); } void AVFEqualizerBand::SetupLowShelfFilter(double omega, double bw, double gain) { double egm = gain - 1.0; double egp = gain + 1.0; double alpha = tan(bw / 2.0); double delta = 2.0 * sqrt(gain) * alpha; double cosF = cos(omega); mCoefficients[IND_A0] = (egp - egm * cosF + delta) * gain; mCoefficients[IND_A1] = (egm - egp * cosF) * 2.0 * gain; mCoefficients[IND_A2] = (egp - egm * cosF - delta) * gain; double b0 = egp + egm * cosF + delta; mCoefficients[IND_B1] = (egm + egp * cosF) * -2.0; mCoefficients[IND_B2] = egp + egm * cosF - delta; // pre-scale coefficients vDSP_vsdivD(mCoefficients, 1, &b0, mCoefficients, 1, 5); } void AVFEqualizerBand::SetupHighShelfFilter(double omega, double bw, double gain) { double egm = gain - 1.0; double egp = gain + 1.0; double alpha = tan(bw / 2.0); double delta = 2.0 * sqrt(gain) * alpha; double cosF = cos(omega); mCoefficients[IND_A0] = (egp + egm * cosF + delta) * gain; mCoefficients[IND_A1] = (egm + egp * cosF) * -2.0 * gain; mCoefficients[IND_A2] = (egp + egm * cosF - delta) * gain; double b0 = egp - egm * cosF + delta; mCoefficients[IND_B1] = (egm - egp * cosF) * 2.0; mCoefficients[IND_B2] = egp - egm * cosF - delta; // pre-scale coefficients vDSP_vsdivD(mCoefficients, 1, &b0, mCoefficients, 1, 5); } void AVFEqualizerBand::RecalculateParams() { double rate = mEQ->GetSampleRate(); mBypass = (rate == 0.0); if (mBypass) { // can't calculate until we have a sample rate return; } // recalculate coefficients based on new parameters double bw = calculate_bandwidth(m_Bandwidth, rate); if (bw <= 0.0) { // no bandwidth, no filter... mBypass = true; return; } double absGain = pow(10, m_Gain / 40); // convert dB to scale double omega = calculate_omega(mFrequency, rate); switch (mFilterType) { case Peak: SetupPeakFilter(omega, bw, absGain); break; case LowShelf: SetupLowShelfFilter(omega, bw, absGain); break; case HighShelf: SetupHighShelfFilter(omega, bw, absGain); break; } } void AVFEqualizerBand::ApplyFilter(double *inSource, double *inDest, int frameCount, int channel) { if (mBypass && mEQ->GetSampleRate() > 0.0) { // Have a sample rate now, can recalculate RecalculateParams(); } if (mBypass || channel < 0) { return; } // We may have more channels now than when we were initialized if (channel > mChannels) { mChannels = mEQ->GetChannelCount(); SetChannelCount(mChannels); } if (mChannels > 0 && mHistory != NULL) { // copy source and dest history inSource[1] = mHistory[channel].x1; inSource[0] = mHistory[channel].x2; inDest[1] = mHistory[channel].y1; inDest[0] = mHistory[channel].y2; vDSP_deq22D(inSource, 1, mCoefficients, inDest, 1, frameCount); // update history mHistory[channel].x1 = inSource[frameCount+1]; mHistory[channel].x2 = inSource[frameCount]; mHistory[channel].y1 = inDest[frameCount+1]; mHistory[channel].y2 = inDest[frameCount]; } } #pragma mark - AVFAudioEqualizer::~AVFAudioEqualizer() { mEQBufferA.free(); mEQBufferB.free(); // Free the EQ bands, otherwise they'll leak for (AVFEQBandIterator iter = mEQBands.begin(); iter != mEQBands.end(); iter++) { if (iter->second) { delete iter->second; } } mEQBands.clear(); } AUKernelBase *AVFAudioEqualizer::NewKernel() { return new AVFEqualizerKernel(this, mAudioUnit); } bool AVFAudioEqualizer::IsEnabled() { return mEnabled; } void AVFAudioEqualizer::SetEnabled(bool isEnabled) { mEnabled = isEnabled; } int AVFAudioEqualizer::GetNumBands() { return (int)mEQBands.size(); } CEqualizerBand *AVFAudioEqualizer::AddBand(double frequency, double bandwidth, double gain) { if (!mEQBands[frequency]) { mEQBands[frequency] = new AVFEqualizerBand(this, frequency, bandwidth, gain); } else { mEQBands[frequency]->SetBandwidth(bandwidth); mEQBands[frequency]->SetGain(gain); } ResetBandParameters(); return mEQBands[frequency]; } bool AVFAudioEqualizer::RemoveBand(double frequency) { AVFEqualizerBand *band = mEQBands[frequency]; if (band) { mEQBands.erase(frequency); delete band; ResetBandParameters(); return true; } return false; } void AVFAudioEqualizer::MoveBand(double oldFrequency, double newFrequency) { // only if freq actually changes if (oldFrequency != newFrequency) { AVFEqualizerBand *band = mEQBands[oldFrequency]; if (band) { RemoveBand(newFrequency); mEQBands[newFrequency] = band; mEQBands.erase(oldFrequency); } ResetBandParameters(); } } void AVFAudioEqualizer::ResetBandParameters() { // Update channel counts, recalculate params if necessary // bands are automatically sorted by the map from low to high for (AVFEQBandIterator iter = mEQBands.begin(); iter != mEQBands.end();) { if (!iter->second) { // NULL pointer protection, just remove the offending band mEQBands.erase(iter++); if (!mEQBands.empty() && (iter == mEQBands.end())) { // re-process the last valid band, otherwise it won't be set to // HighShelf filter type --iter; } else { continue; } } AVFEqualizerBand *band = iter->second; // middle bands are peak/notch filters AVFEqualizerBand::AVFEqualizerFilterType type = AVFEqualizerBand::Peak; if (iter == mEQBands.begin()) { type = AVFEqualizerBand::LowShelf; } else if (iter == --(mEQBands.end())) { type = AVFEqualizerBand::HighShelf; } band->SetFilterType(type); band->SetChannelCount(GetChannelCount()); band->RecalculateParams(); iter++; // here due to NULL ptr protection, otherwise we double increment } } void AVFAudioEqualizer::SetAudioUnit(AUEffectBase *unit) { this->AVFKernelProcessor::SetAudioUnit(unit); ResetBandParameters(); } void AVFAudioEqualizer::RunFilter(const Float32 *inSourceP, Float32 *inDestP, UInt32 inFramesToProcess, UInt32 channel) { if (mEnabled && !mEQBands.empty()) { if (inFramesToProcess + 2 > mEQBufferSize) { mEQBufferSize = inFramesToProcess + 2; mEQBufferA.free(); mEQBufferA.alloc(mEQBufferSize); mEQBufferB.free(); mEQBufferB.alloc(mEQBufferSize); } // start processing with A buffer first bool srcA = true; // The first two elements are copied each time we call a band to process // float* cast is needed for Xcode 4.5 vDSP_vspdp((float*)inSourceP, 1, mEQBufferA.get() + 2, 1, inFramesToProcess); // Run each band in sequence for (AVFEQBandIterator iter = mEQBands.begin(); iter != mEQBands.end(); iter++) { if (iter->second) { if (srcA) { iter->second->ApplyFilter(mEQBufferA, mEQBufferB, inFramesToProcess, channel); } else { iter->second->ApplyFilter(mEQBufferB, mEQBufferA, inFramesToProcess, channel); } srcA = !srcA; } } // Copy back to dest stream vDSP_vdpsp((srcA ? mEQBufferA : mEQBufferB)+2, 1, inDestP, 1, inFramesToProcess); } }