// // 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