1/* 2 * Copyright (C) 2010, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25#include "config.h" 26 27#if ENABLE(WEB_AUDIO) 28 29#include "platform/audio/EqualPowerPanner.h" 30 31#include <algorithm> 32#include "platform/audio/AudioBus.h" 33#include "platform/audio/AudioUtilities.h" 34#include "wtf/MathExtras.h" 35 36// Use a 50ms smoothing / de-zippering time-constant. 37const float SmoothingTimeConstant = 0.050f; 38 39namespace blink { 40 41EqualPowerPanner::EqualPowerPanner(float sampleRate) 42 : Panner(PanningModelEqualPower) 43 , m_isFirstRender(true) 44 , m_gainL(0.0) 45 , m_gainR(0.0) 46{ 47 m_smoothingConstant = AudioUtilities::discreteTimeConstantForSampleRate(SmoothingTimeConstant, sampleRate); 48} 49 50void EqualPowerPanner::pan(double azimuth, double /*elevation*/, const AudioBus* inputBus, AudioBus* outputBus, size_t framesToProcess) 51{ 52 bool isInputSafe = inputBus && (inputBus->numberOfChannels() == 1 || inputBus->numberOfChannels() == 2) && framesToProcess <= inputBus->length(); 53 ASSERT(isInputSafe); 54 if (!isInputSafe) 55 return; 56 57 unsigned numberOfInputChannels = inputBus->numberOfChannels(); 58 59 bool isOutputSafe = outputBus && outputBus->numberOfChannels() == 2 && framesToProcess <= outputBus->length(); 60 ASSERT(isOutputSafe); 61 if (!isOutputSafe) 62 return; 63 64 const float* sourceL = inputBus->channel(0)->data(); 65 const float* sourceR = numberOfInputChannels > 1 ? inputBus->channel(1)->data() : sourceL; 66 float* destinationL = outputBus->channelByType(AudioBus::ChannelLeft)->mutableData(); 67 float* destinationR = outputBus->channelByType(AudioBus::ChannelRight)->mutableData(); 68 69 if (!sourceL || !sourceR || !destinationL || !destinationR) 70 return; 71 72 // Clamp azimuth to allowed range of -180 -> +180. 73 azimuth = std::max(-180.0, azimuth); 74 azimuth = std::min(180.0, azimuth); 75 76 // Alias the azimuth ranges behind us to in front of us: 77 // -90 -> -180 to -90 -> 0 and 90 -> 180 to 90 -> 0 78 if (azimuth < -90) 79 azimuth = -180 - azimuth; 80 else if (azimuth > 90) 81 azimuth = 180 - azimuth; 82 83 double desiredPanPosition; 84 double desiredGainL; 85 double desiredGainR; 86 87 if (numberOfInputChannels == 1) { // For mono source case. 88 // Pan smoothly from left to right with azimuth going from -90 -> +90 degrees. 89 desiredPanPosition = (azimuth + 90) / 180; 90 } else { // For stereo source case. 91 if (azimuth <= 0) { // from -90 -> 0 92 // sourceL -> destL and "equal-power pan" sourceR as in mono case 93 // by transforming the "azimuth" value from -90 -> 0 degrees into the range -90 -> +90. 94 desiredPanPosition = (azimuth + 90) / 90; 95 } else { // from 0 -> +90 96 // sourceR -> destR and "equal-power pan" sourceL as in mono case 97 // by transforming the "azimuth" value from 0 -> +90 degrees into the range -90 -> +90. 98 desiredPanPosition = azimuth / 90; 99 } 100 } 101 102 desiredGainL = std::cos(piOverTwoDouble * desiredPanPosition); 103 desiredGainR = std::sin(piOverTwoDouble * desiredPanPosition); 104 105 // Don't de-zipper on first render call. 106 if (m_isFirstRender) { 107 m_isFirstRender = false; 108 m_gainL = desiredGainL; 109 m_gainR = desiredGainR; 110 } 111 112 // Cache in local variables. 113 double gainL = m_gainL; 114 double gainR = m_gainR; 115 116 // Get local copy of smoothing constant. 117 const double SmoothingConstant = m_smoothingConstant; 118 119 int n = framesToProcess; 120 121 if (numberOfInputChannels == 1) { // For mono source case. 122 while (n--) { 123 float inputL = *sourceL++; 124 gainL += (desiredGainL - gainL) * SmoothingConstant; 125 gainR += (desiredGainR - gainR) * SmoothingConstant; 126 *destinationL++ = static_cast<float>(inputL * gainL); 127 *destinationR++ = static_cast<float>(inputL * gainR); 128 } 129 } else { // For stereo source case. 130 if (azimuth <= 0) { // from -90 -> 0 131 while (n--) { 132 float inputL = *sourceL++; 133 float inputR = *sourceR++; 134 gainL += (desiredGainL - gainL) * SmoothingConstant; 135 gainR += (desiredGainR - gainR) * SmoothingConstant; 136 *destinationL++ = static_cast<float>(inputL + inputR * gainL); 137 *destinationR++ = static_cast<float>(inputR * gainR); 138 } 139 } else { // from 0 -> +90 140 while (n--) { 141 float inputL = *sourceL++; 142 float inputR = *sourceR++; 143 gainL += (desiredGainL - gainL) * SmoothingConstant; 144 gainR += (desiredGainR - gainR) * SmoothingConstant; 145 *destinationL++ = static_cast<float>(inputL * gainL); 146 *destinationR++ = static_cast<float>(inputR + inputL * gainR); 147 } 148 } 149 } 150 151 m_gainL = gainL; 152 m_gainR = gainR; 153} 154 155} // namespace blink 156 157#endif // ENABLE(WEB_AUDIO) 158