mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-23 14:14:28 +02:00
Merge pull request #3270 from chansensturm/audio-filter
Added support for biquad, parametric, and multi-band filter eq
This commit is contained in:
commit
0650169c25
6 changed files with 427 additions and 5 deletions
|
@ -56,7 +56,6 @@ static const int MUTE_ICON_SIZE = 24;
|
||||||
|
|
||||||
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 100;
|
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 100;
|
||||||
|
|
||||||
|
|
||||||
Audio::Audio(QObject* parent) :
|
Audio::Audio(QObject* parent) :
|
||||||
AbstractAudioInterface(parent),
|
AbstractAudioInterface(parent),
|
||||||
_audioInput(NULL),
|
_audioInput(NULL),
|
||||||
|
@ -83,6 +82,7 @@ Audio::Audio(QObject* parent) :
|
||||||
_noiseGateSampleCounter(0),
|
_noiseGateSampleCounter(0),
|
||||||
_noiseGateOpen(false),
|
_noiseGateOpen(false),
|
||||||
_noiseGateEnabled(true),
|
_noiseGateEnabled(true),
|
||||||
|
_peqEnabled(false),
|
||||||
_toneInjectionEnabled(false),
|
_toneInjectionEnabled(false),
|
||||||
_noiseGateFramesToClose(0),
|
_noiseGateFramesToClose(0),
|
||||||
_totalInputAudioSamples(0),
|
_totalInputAudioSamples(0),
|
||||||
|
@ -132,6 +132,7 @@ void Audio::init(QGLWidget *parent) {
|
||||||
void Audio::reset() {
|
void Audio::reset() {
|
||||||
_receivedAudioStream.reset();
|
_receivedAudioStream.reset();
|
||||||
resetStats();
|
resetStats();
|
||||||
|
_peq.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::resetStats() {
|
void Audio::resetStats() {
|
||||||
|
@ -418,9 +419,15 @@ void Audio::start() {
|
||||||
if (!outputFormatSupported) {
|
if (!outputFormatSupported) {
|
||||||
qDebug() << "Unable to set up audio output because of a problem with output format.";
|
qDebug() << "Unable to set up audio output because of a problem with output format.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_peq.initialize( _inputFormat.sampleRate(), _audioInput->bufferSize() );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::stop() {
|
void Audio::stop() {
|
||||||
|
|
||||||
|
_peq.finalize();
|
||||||
|
|
||||||
// "switch" to invalid devices in order to shut down the state
|
// "switch" to invalid devices in order to shut down the state
|
||||||
switchInputToAudioDevice(QAudioDeviceInfo());
|
switchInputToAudioDevice(QAudioDeviceInfo());
|
||||||
switchOutputToAudioDevice(QAudioDeviceInfo());
|
switchOutputToAudioDevice(QAudioDeviceInfo());
|
||||||
|
@ -463,6 +470,14 @@ void Audio::handleAudioInput() {
|
||||||
|
|
||||||
QByteArray inputByteArray = _inputDevice->readAll();
|
QByteArray inputByteArray = _inputDevice->readAll();
|
||||||
|
|
||||||
|
if (_peqEnabled && !_muted) {
|
||||||
|
// we wish to pre-filter our captured input, prior to loopback
|
||||||
|
|
||||||
|
int16_t* ioBuffer = (int16_t*)inputByteArray.data();
|
||||||
|
|
||||||
|
_peq.render( ioBuffer, ioBuffer, inputByteArray.size() / sizeof(int16_t) );
|
||||||
|
}
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) {
|
||||||
// if this person wants local loopback add that to the locally injected audio
|
// if this person wants local loopback add that to the locally injected audio
|
||||||
|
|
||||||
|
@ -1172,6 +1187,31 @@ void Audio::renderToolBox(int x, int y, bool boxed) {
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Audio::toggleAudioFilter() {
|
||||||
|
_peqEnabled = !_peqEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::selectAudioFilterFlat() {
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterFlat)) {
|
||||||
|
_peq.loadProfile(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Audio::selectAudioFilterTrebleCut() {
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterTrebleCut)) {
|
||||||
|
_peq.loadProfile(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Audio::selectAudioFilterBassCut() {
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterBassCut)) {
|
||||||
|
_peq.loadProfile(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Audio::selectAudioFilterSmiley() {
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::AudioFilterSmiley)) {
|
||||||
|
_peq.loadProfile(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Audio::toggleScope() {
|
void Audio::toggleScope() {
|
||||||
_scopeEnabled = !_scopeEnabled;
|
_scopeEnabled = !_scopeEnabled;
|
||||||
if (_scopeEnabled) {
|
if (_scopeEnabled) {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "AudioStreamStats.h"
|
#include "AudioStreamStats.h"
|
||||||
#include "RingBufferHistory.h"
|
#include "RingBufferHistory.h"
|
||||||
#include "MovingMinMaxAvg.h"
|
#include "MovingMinMaxAvg.h"
|
||||||
|
#include "AudioFilter.h"
|
||||||
|
|
||||||
#include <QAudio>
|
#include <QAudio>
|
||||||
#include <QAudioInput>
|
#include <QAudioInput>
|
||||||
|
@ -125,6 +126,11 @@ public slots:
|
||||||
void selectAudioScopeFiveFrames();
|
void selectAudioScopeFiveFrames();
|
||||||
void selectAudioScopeTwentyFrames();
|
void selectAudioScopeTwentyFrames();
|
||||||
void selectAudioScopeFiftyFrames();
|
void selectAudioScopeFiftyFrames();
|
||||||
|
void toggleAudioFilter();
|
||||||
|
void selectAudioFilterFlat();
|
||||||
|
void selectAudioFilterTrebleCut();
|
||||||
|
void selectAudioFilterBassCut();
|
||||||
|
void selectAudioFilterSmiley();
|
||||||
|
|
||||||
virtual void handleAudioByteArray(const QByteArray& audioByteArray);
|
virtual void handleAudioByteArray(const QByteArray& audioByteArray);
|
||||||
|
|
||||||
|
@ -270,6 +276,11 @@ private:
|
||||||
int _scopeOutputOffset;
|
int _scopeOutputOffset;
|
||||||
int _framesPerScope;
|
int _framesPerScope;
|
||||||
int _samplesPerScope;
|
int _samplesPerScope;
|
||||||
|
|
||||||
|
// Multi-band parametric EQ
|
||||||
|
bool _peqEnabled;
|
||||||
|
AudioFilterPEQ3 _peq;
|
||||||
|
|
||||||
QMutex _guard;
|
QMutex _guard;
|
||||||
QByteArray* _scopeInput;
|
QByteArray* _scopeInput;
|
||||||
QByteArray* _scopeOutputLeft;
|
QByteArray* _scopeOutputLeft;
|
||||||
|
|
|
@ -486,6 +486,48 @@ Menu::Menu() :
|
||||||
true,
|
true,
|
||||||
appInstance->getAudio(),
|
appInstance->getAudio(),
|
||||||
SLOT(toggleAudioNoiseReduction()));
|
SLOT(toggleAudioNoiseReduction()));
|
||||||
|
|
||||||
|
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioFilter,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
appInstance->getAudio(),
|
||||||
|
SLOT(toggleAudioFilter()));
|
||||||
|
|
||||||
|
QMenu* audioFilterMenu = audioDebugMenu->addMenu("Audio Filter Options");
|
||||||
|
addDisabledActionAndSeparator(audioFilterMenu, "Filter Response");
|
||||||
|
{
|
||||||
|
QAction *flat = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterFlat,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
appInstance->getAudio(),
|
||||||
|
SLOT(selectAudioFilterFlat()));
|
||||||
|
|
||||||
|
QAction *trebleCut = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterTrebleCut,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
appInstance->getAudio(),
|
||||||
|
SLOT(selectAudioFilterTrebleCut()));
|
||||||
|
|
||||||
|
QAction *bassCut = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterBassCut,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
appInstance->getAudio(),
|
||||||
|
SLOT(selectAudioFilterBassCut()));
|
||||||
|
|
||||||
|
QAction *smiley = addCheckableActionToQMenuAndActionHash(audioFilterMenu, MenuOption::AudioFilterSmiley,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
appInstance->getAudio(),
|
||||||
|
SLOT(selectAudioFilterSmiley()));
|
||||||
|
|
||||||
|
|
||||||
|
QActionGroup* audioFilterGroup = new QActionGroup(audioFilterMenu);
|
||||||
|
audioFilterGroup->addAction(flat);
|
||||||
|
audioFilterGroup->addAction(trebleCut);
|
||||||
|
audioFilterGroup->addAction(bassCut);
|
||||||
|
audioFilterGroup->addAction(smiley);
|
||||||
|
}
|
||||||
|
|
||||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio);
|
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio);
|
||||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio);
|
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio);
|
||||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::StereoAudio, 0, false,
|
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::StereoAudio, 0, false,
|
||||||
|
|
|
@ -311,6 +311,11 @@ namespace MenuOption {
|
||||||
const QString Animations = "Animations...";
|
const QString Animations = "Animations...";
|
||||||
const QString Atmosphere = "Atmosphere";
|
const QString Atmosphere = "Atmosphere";
|
||||||
const QString Attachments = "Attachments...";
|
const QString Attachments = "Attachments...";
|
||||||
|
const QString AudioFilter = "Audio Filter Bank";
|
||||||
|
const QString AudioFilterFlat = "Flat Response";
|
||||||
|
const QString AudioFilterTrebleCut= "Treble Cut";
|
||||||
|
const QString AudioFilterBassCut = "Bass Cut";
|
||||||
|
const QString AudioFilterSmiley = "Smiley Curve";
|
||||||
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
||||||
const QString AudioScope = "Audio Scope";
|
const QString AudioScope = "Audio Scope";
|
||||||
const QString AudioScopeFiftyFrames = "Fifty";
|
const QString AudioScopeFiftyFrames = "Fifty";
|
||||||
|
|
26
libraries/audio/src/AudioFilter.cpp
Normal file
26
libraries/audio/src/AudioFilter.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// AudioFilter.cpp
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Craig Hansen-Sturm on 8/10/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <SharedUtil.h>
|
||||||
|
#include "AudioRingBuffer.h"
|
||||||
|
#include "AudioFilter.h"
|
||||||
|
|
||||||
|
template<>
|
||||||
|
AudioFilterPEQ3::FilterParameter AudioFilterPEQ3::_profiles[ AudioFilterPEQ3::_profileCount ][ AudioFilterPEQ3::_filterCount ] = {
|
||||||
|
|
||||||
|
// Freq Gain Q Freq Gain Q Freq Gain Q
|
||||||
|
{ { 300.0f, 1.0f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 1.0f, 1.0f } }, // flat response (default)
|
||||||
|
{ { 300.0f, 1.0f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 0.1f, 1.0f } }, // treble cut
|
||||||
|
{ { 300.0f, 0.1f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 1.0f, 1.0f } }, // bass cut
|
||||||
|
{ { 300.0f, 1.5f, 0.71f }, { 1000.0f, 0.5f, 1.0f }, { 4000.0f, 1.50f, 0.71f } } // smiley curve
|
||||||
|
};
|
298
libraries/audio/src/AudioFilter.h
Normal file
298
libraries/audio/src/AudioFilter.h
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
//
|
||||||
|
// AudioFilter.h
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Craig Hansen-Sturm on 8/9/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AudioFilter_h
|
||||||
|
#define hifi_AudioFilter_h
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Implements a standard biquad filter in "Direct Form 1"
|
||||||
|
// Reference http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||||
|
//
|
||||||
|
class AudioBiquad {
|
||||||
|
|
||||||
|
//
|
||||||
|
// private data
|
||||||
|
//
|
||||||
|
float _a0; // gain
|
||||||
|
float _a1; // feedforward 1
|
||||||
|
float _a2; // feedforward 2
|
||||||
|
float _b1; // feedback 1
|
||||||
|
float _b2; // feedback 2
|
||||||
|
|
||||||
|
float _xm1;
|
||||||
|
float _xm2;
|
||||||
|
float _ym1;
|
||||||
|
float _ym2;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//
|
||||||
|
// ctor/dtor
|
||||||
|
//
|
||||||
|
AudioBiquad()
|
||||||
|
: _xm1(0.)
|
||||||
|
, _xm2(0.)
|
||||||
|
, _ym1(0.)
|
||||||
|
, _ym2(0.) {
|
||||||
|
setParameters(0.,0.,0.,0.,0.);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AudioBiquad() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// public interface
|
||||||
|
//
|
||||||
|
void setParameters( const float a0, const float a1, const float a2, const float b1, const float b2 ) {
|
||||||
|
_a0 = a0; _a1 = a1; _a2 = a2; _b1 = b1; _b2 = b2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getParameters( float& a0, float& a1, float& a2, float& b1, float& b2 ) {
|
||||||
|
a0 = _a0; a1 = _a1; a2 = _a2; b1 = _b1; b2 = _b2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void render( const float* in, float* out, const int frames) {
|
||||||
|
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
|
||||||
|
for (int i = 0; i < frames; ++i) {
|
||||||
|
|
||||||
|
x = *in++;
|
||||||
|
|
||||||
|
// biquad
|
||||||
|
y = (_a0 * x)
|
||||||
|
+ (_a1 * _xm1)
|
||||||
|
+ (_a2 * _xm2)
|
||||||
|
- (_b1 * _ym1)
|
||||||
|
- (_b2 * _ym2);
|
||||||
|
|
||||||
|
// update delay line
|
||||||
|
_xm2 = _xm1;
|
||||||
|
_xm1 = x;
|
||||||
|
_ym2 = _ym1;
|
||||||
|
_ym1 = y;
|
||||||
|
|
||||||
|
*out++ = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
_xm1 = _xm2 = _ym1 = _ym2 = 0.;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Implements a single-band parametric EQ using a biquad "peaking EQ" configuration
|
||||||
|
//
|
||||||
|
// gain > 1.0 boosts the center frequency
|
||||||
|
// gain < 1.0 cuts the center frequency
|
||||||
|
//
|
||||||
|
class AudioParametricEQ {
|
||||||
|
|
||||||
|
//
|
||||||
|
// private data
|
||||||
|
//
|
||||||
|
AudioBiquad _kernel;
|
||||||
|
float _sampleRate;
|
||||||
|
float _frequency;
|
||||||
|
float _gain;
|
||||||
|
float _slope;
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
void updateKernel() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
a0 = 1 + alpha*A
|
||||||
|
a1 = -2*cos(w0)
|
||||||
|
a2 = 1 - alpha*A
|
||||||
|
b1 = -2*cos(w0)
|
||||||
|
b2 = 1 - alpha/A
|
||||||
|
*/
|
||||||
|
|
||||||
|
const float a = _gain;
|
||||||
|
const float omega = TWO_PI * _frequency / _sampleRate;
|
||||||
|
const float alpha = 0.5f * sinf(omega) / _slope;
|
||||||
|
const float gamma = 1.0f / ( 1.0f + (alpha/a) );
|
||||||
|
|
||||||
|
const float a0 = 1.0f + (alpha*a);
|
||||||
|
const float a1 = -2.0f * cosf(omega);
|
||||||
|
const float a2 = 1.0f - (alpha*a);
|
||||||
|
const float b1 = a1;
|
||||||
|
const float b2 = 1.0f - (alpha/a);
|
||||||
|
|
||||||
|
_kernel.setParameters( a0*gamma,a1*gamma,a2*gamma,b1*gamma,b2*gamma );
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
//
|
||||||
|
// ctor/dtor
|
||||||
|
//
|
||||||
|
AudioParametricEQ() {
|
||||||
|
|
||||||
|
setParameters(0.,0.,0.,0.);
|
||||||
|
updateKernel();
|
||||||
|
}
|
||||||
|
|
||||||
|
~AudioParametricEQ() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// public interface
|
||||||
|
//
|
||||||
|
void setParameters( const float sampleRate, const float frequency, const float gain, const float slope ) {
|
||||||
|
|
||||||
|
_sampleRate = std::max(sampleRate,1.0f);
|
||||||
|
_frequency = std::max(frequency,2.0f);
|
||||||
|
_gain = std::max(gain,0.0f);
|
||||||
|
_slope = std::max(slope,0.00001f);
|
||||||
|
|
||||||
|
updateKernel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getParameters( float& sampleRate, float& frequency, float& gain, float& slope ) {
|
||||||
|
sampleRate = _sampleRate; frequency = _frequency; gain = _gain; slope = _slope;
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(const float* in, float* out, const int frames ) {
|
||||||
|
_kernel.render(in,out,frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
_kernel.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Helper/convenience class that implements a bank of EQ objects
|
||||||
|
//
|
||||||
|
template< typename T, const int N>
|
||||||
|
class AudioFilterBank {
|
||||||
|
|
||||||
|
//
|
||||||
|
// types
|
||||||
|
//
|
||||||
|
struct FilterParameter {
|
||||||
|
float _p1;
|
||||||
|
float _p2;
|
||||||
|
float _p3;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// private static data
|
||||||
|
//
|
||||||
|
static const int _filterCount = N;
|
||||||
|
static const int _profileCount = 4;
|
||||||
|
|
||||||
|
static FilterParameter _profiles[_profileCount][_filterCount];
|
||||||
|
|
||||||
|
//
|
||||||
|
// private data
|
||||||
|
//
|
||||||
|
T _filters[ _filterCount ];
|
||||||
|
float* _buffer;
|
||||||
|
float _sampleRate;
|
||||||
|
uint16_t _frameCount;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//
|
||||||
|
// ctor/dtor
|
||||||
|
//
|
||||||
|
AudioFilterBank()
|
||||||
|
: _buffer(NULL)
|
||||||
|
, _sampleRate(0.)
|
||||||
|
, _frameCount(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~AudioFilterBank() {
|
||||||
|
finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// public interface
|
||||||
|
//
|
||||||
|
void initialize( const float sampleRate, const int frameCount ) {
|
||||||
|
finalize();
|
||||||
|
|
||||||
|
_buffer = (float*)malloc( frameCount * sizeof(float) );
|
||||||
|
if(!_buffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
_frameCount = frameCount;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
loadProfile(0); // load default profile "flat response" into the bank (see AudioFilter.cpp)
|
||||||
|
}
|
||||||
|
|
||||||
|
void finalize() {
|
||||||
|
if (_buffer ) {
|
||||||
|
free (_buffer);
|
||||||
|
_buffer = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadProfile( int profileIndex ) {
|
||||||
|
if (profileIndex >= 0 && profileIndex < _profileCount) {
|
||||||
|
|
||||||
|
for (int i = 0; i < _filterCount; ++i) {
|
||||||
|
FilterParameter p = _profiles[profileIndex][i];
|
||||||
|
|
||||||
|
_filters[i].setParameters(_sampleRate,p._p1,p._p2,p._p3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void render( const float* in, float* out, const int frameCount ) {
|
||||||
|
for (int i = 0; i < _filterCount; ++i) {
|
||||||
|
_filters[i].render( in, out, frameCount );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void render( const int16_t* in, int16_t* out, const int frameCount ) {
|
||||||
|
if (!_buffer || ( frameCount > _frameCount ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int scale = (2 << ((8*sizeof(int16_t))-1));
|
||||||
|
|
||||||
|
// convert int16_t to float32 (normalized to -1. ... 1.)
|
||||||
|
for (int i = 0; i < frameCount; ++i) {
|
||||||
|
_buffer[i] = ((float)(*in++)) / scale;
|
||||||
|
}
|
||||||
|
// for this filter, we share input/output buffers at each stage, but our design does not mandate this
|
||||||
|
render( _buffer, _buffer, frameCount );
|
||||||
|
|
||||||
|
// convert float32 to int16_t
|
||||||
|
for (int i = 0; i < frameCount; ++i) {
|
||||||
|
*out++ = (int16_t)(_buffer[i] * scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
for (int i = 0; i < _filterCount; ++i ) {
|
||||||
|
_filters[i].reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Specializations of AudioFilterBank
|
||||||
|
//
|
||||||
|
typedef AudioFilterBank< AudioParametricEQ, 1> AudioFilterPEQ1; // bank with one band of PEQ
|
||||||
|
typedef AudioFilterBank< AudioParametricEQ, 2> AudioFilterPEQ2; // bank with two bands of PEQ
|
||||||
|
typedef AudioFilterBank< AudioParametricEQ, 3> AudioFilterPEQ3; // bank with three bands of PEQ
|
||||||
|
// etc....
|
||||||
|
|
||||||
|
|
||||||
|
#endif // hifi_AudioFilter_h
|
Loading…
Reference in a new issue