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.
37 const float SmoothingTimeConstant = 0.050f;
38
39 namespace blink {
40
EqualPowerPanner(float sampleRate)41 EqualPowerPanner::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
pan(double azimuth,double,const AudioBus * inputBus,AudioBus * outputBus,size_t framesToProcess)50 void 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