From 1d0ba52445d2b750ac7626c8bd05d6563a576e14 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Thu, 11 Sep 2014 17:15:49 -0700 Subject: [PATCH] implemented new audio panning object which implements constant power sin^2+cos^2=1 law --- interface/src/Audio.h | 1 + libraries/audio/src/AudioPan.cpp | 23 +++++ libraries/audio/src/AudioPan.h | 141 +++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 libraries/audio/src/AudioPan.cpp create mode 100644 libraries/audio/src/AudioPan.h diff --git a/interface/src/Audio.h b/interface/src/Audio.h index f73e0331de..f100c04684 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -26,6 +26,7 @@ #include "AudioSourceTone.h" #include "AudioSourceNoise.h" #include "AudioGain.h" +#include "AudioPan.h" #include "AudioFilter.h" #include "AudioFilterBank.h" diff --git a/libraries/audio/src/AudioPan.cpp b/libraries/audio/src/AudioPan.cpp new file mode 100644 index 0000000000..8f9b568b6a --- /dev/null +++ b/libraries/audio/src/AudioPan.cpp @@ -0,0 +1,23 @@ +// +// AudioSourceTone.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 +#include +#include +#include "AudioRingBuffer.h" +#include "AudioFormat.h" +#include "AudioBuffer.h" +#include "AudioPan.h" + +float32_t AudioPan::ONE_MINUS_EPSILON = 1.0f - EPSILON; +float32_t AudioPan::ZERO_PLUS_EPSILON = 0.0f + EPSILON; +float32_t AudioPan::ONE_HALF_MINUS_EPSILON = 0.5f - EPSILON; +float32_t AudioPan::ONE_HALF_PLUS_EPSILON = 0.5f + EPSILON; diff --git a/libraries/audio/src/AudioPan.h b/libraries/audio/src/AudioPan.h new file mode 100644 index 0000000000..85e739b255 --- /dev/null +++ b/libraries/audio/src/AudioPan.h @@ -0,0 +1,141 @@ +// +// AudioPan.h +// hifi +// +// Created by Craig Hansen-Sturm on 9/1/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_AudioPan_h +#define hifi_AudioPan_h + +class AudioPan +{ + float32_t _pan; + float32_t _gainLeft; + float32_t _gainRight; + + static float32_t ONE_MINUS_EPSILON; + static float32_t ZERO_PLUS_EPSILON; + static float32_t ONE_HALF_MINUS_EPSILON; + static float32_t ONE_HALF_PLUS_EPSILON; + + void updateCoefficients() { + + // implement constant power sin^2 + cos^2 = 1 panning law + + if (_pan >= ONE_MINUS_EPSILON) { // full right + _gainLeft = 0.0f; + _gainRight = 1.0f; + } + else if (_pan <= ZERO_PLUS_EPSILON) { // full left + _gainLeft = 1.0f; + _gainRight = 0.0f; + } + else if ((_pan >= ONE_HALF_MINUS_EPSILON) && (_pan <= ONE_HALF_PLUS_EPSILON)) { // center + _gainLeft = 1.0f / SQUARE_ROOT_OF_2; + _gainRight = 1.0f / SQUARE_ROOT_OF_2; + } + else { // intermediate cases + _gainLeft = cosf( TWO_PI * _pan ); + _gainRight = sinf( TWO_PI * _pan ); + } + } + +public: + AudioPan() { + initialize(); + } + + ~AudioPan() { + finalize(); + } + + void initialize() { + setParameters(0.5f); + } + + void finalize() { + } + + void reset() { + initialize(); + } + + void setParameters(const float32_t pan) { + // pan ranges between 0.0 and 1.0f inclusive. 0.5f is midpoint between full left and full right + _pan = std::min(std::max(pan, 0.0f), 1.0f); + updateCoefficients(); + } + + void getParameters(float32_t& pan) { + pan = _pan; + } + + void render(AudioBufferFloat32& frameBuffer) { + + if (frameBuffer.getChannelCount() != 2) { + return; + } + + float32_t** samples = frameBuffer.getFrameData(); + + bool frameAlignment16 = (frameBuffer.getFrameCount() & 0x0F) == 0; + if (frameAlignment16) { + + if (frameBuffer.getChannelCount() == 2) { + + for (uint16_t i = 0; i < frameBuffer.getFrameCount(); i += 16) { + samples[0][i + 0] *= _gainLeft; + samples[0][i + 1] *= _gainLeft; + samples[0][i + 2] *= _gainLeft; + samples[0][i + 3] *= _gainLeft; + samples[0][i + 4] *= _gainLeft; + samples[0][i + 5] *= _gainLeft; + samples[0][i + 6] *= _gainLeft; + samples[0][i + 7] *= _gainLeft; + samples[0][i + 8] *= _gainLeft; + samples[0][i + 9] *= _gainLeft; + samples[0][i + 10] *= _gainLeft; + samples[0][i + 11] *= _gainLeft; + samples[0][i + 12] *= _gainLeft; + samples[0][i + 13] *= _gainLeft; + samples[0][i + 14] *= _gainLeft; + samples[0][i + 15] *= _gainLeft; + samples[1][i + 0] *= _gainRight; + samples[1][i + 1] *= _gainRight; + samples[1][i + 2] *= _gainRight; + samples[1][i + 3] *= _gainRight; + samples[1][i + 4] *= _gainRight; + samples[1][i + 5] *= _gainRight; + samples[1][i + 6] *= _gainRight; + samples[1][i + 7] *= _gainRight; + samples[1][i + 8] *= _gainRight; + samples[1][i + 9] *= _gainRight; + samples[1][i + 10] *= _gainRight; + samples[1][i + 11] *= _gainRight; + samples[1][i + 12] *= _gainRight; + samples[1][i + 13] *= _gainRight; + samples[1][i + 14] *= _gainRight; + samples[1][i + 15] *= _gainRight; + } + } + else { + assert("unsupported channel format"); + } + } + else { + for (uint16_t i = 0; i < frameBuffer.getFrameCount(); i += 1) { + samples[0][i] *= _gainLeft; + samples[1][i] *= _gainRight; + } + } + } +}; + +#endif // AudioPan_h + +