1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/platform/audio/stereo_panner.h"
6
7 #include <algorithm>
8 #include <memory>
9
10 #include "base/memory/ptr_util.h"
11 #include "third_party/blink/renderer/platform/audio/audio_bus.h"
12 #include "third_party/blink/renderer/platform/audio/audio_utilities.h"
13 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
14
15 namespace blink {
16
17 // Implement equal-power panning algorithm for mono or stereo input.
18 // See: http://webaudio.github.io/web-audio-api/#panning-algorithm
19
StereoPanner(float sample_rate)20 StereoPanner::StereoPanner(float sample_rate) {}
21
PanWithSampleAccurateValues(const AudioBus * input_bus,AudioBus * output_bus,const float * pan_values,uint32_t frames_to_process)22 void StereoPanner::PanWithSampleAccurateValues(const AudioBus* input_bus,
23 AudioBus* output_bus,
24 const float* pan_values,
25 uint32_t frames_to_process) {
26 DCHECK(input_bus);
27 DCHECK_LE(frames_to_process, input_bus->length());
28 DCHECK_GE(input_bus->NumberOfChannels(), 1u);
29 DCHECK_LE(input_bus->NumberOfChannels(), 2u);
30
31 unsigned number_of_input_channels = input_bus->NumberOfChannels();
32
33 DCHECK(output_bus);
34 DCHECK_EQ(output_bus->NumberOfChannels(), 2u);
35 DCHECK_LE(frames_to_process, output_bus->length());
36
37 const float* source_l = input_bus->Channel(0)->Data();
38 const float* source_r =
39 number_of_input_channels > 1 ? input_bus->Channel(1)->Data() : source_l;
40 float* destination_l =
41 output_bus->ChannelByType(AudioBus::kChannelLeft)->MutableData();
42 float* destination_r =
43 output_bus->ChannelByType(AudioBus::kChannelRight)->MutableData();
44
45 if (!source_l || !source_r || !destination_l || !destination_r)
46 return;
47
48 double gain_l, gain_r, pan_radian;
49
50 int n = frames_to_process;
51
52 if (number_of_input_channels == 1) { // For mono source case.
53 while (n--) {
54 float input_l = *source_l++;
55 double pan = clampTo(*pan_values++, -1.0, 1.0);
56 // Pan from left to right [-1; 1] will be normalized as [0; 1].
57 pan_radian = (pan * 0.5 + 0.5) * kPiOverTwoDouble;
58 gain_l = std::cos(pan_radian);
59 gain_r = std::sin(pan_radian);
60 *destination_l++ = static_cast<float>(input_l * gain_l);
61 *destination_r++ = static_cast<float>(input_l * gain_r);
62 }
63 } else { // For stereo source case.
64 while (n--) {
65 float input_l = *source_l++;
66 float input_r = *source_r++;
67 double pan = clampTo(*pan_values++, -1.0, 1.0);
68 // Normalize [-1; 0] to [0; 1]. Do nothing when [0; 1].
69 pan_radian = (pan <= 0 ? pan + 1 : pan) * kPiOverTwoDouble;
70 gain_l = std::cos(pan_radian);
71 gain_r = std::sin(pan_radian);
72 if (pan <= 0) {
73 *destination_l++ = static_cast<float>(input_l + input_r * gain_l);
74 *destination_r++ = static_cast<float>(input_r * gain_r);
75 } else {
76 *destination_l++ = static_cast<float>(input_l * gain_l);
77 *destination_r++ = static_cast<float>(input_r + input_l * gain_r);
78 }
79 }
80 }
81 }
82
PanToTargetValue(const AudioBus * input_bus,AudioBus * output_bus,float pan_value,uint32_t frames_to_process)83 void StereoPanner::PanToTargetValue(const AudioBus* input_bus,
84 AudioBus* output_bus,
85 float pan_value,
86 uint32_t frames_to_process) {
87 DCHECK(input_bus);
88 DCHECK_LE(frames_to_process, input_bus->length());
89 DCHECK_GE(input_bus->NumberOfChannels(), 1u);
90 DCHECK_LE(input_bus->NumberOfChannels(), 2u);
91
92 unsigned number_of_input_channels = input_bus->NumberOfChannels();
93
94 DCHECK(output_bus);
95 DCHECK_EQ(output_bus->NumberOfChannels(), 2u);
96 DCHECK_LE(frames_to_process, output_bus->length());
97
98 const float* source_l = input_bus->Channel(0)->Data();
99 const float* source_r =
100 number_of_input_channels > 1 ? input_bus->Channel(1)->Data() : source_l;
101 float* destination_l =
102 output_bus->ChannelByType(AudioBus::kChannelLeft)->MutableData();
103 float* destination_r =
104 output_bus->ChannelByType(AudioBus::kChannelRight)->MutableData();
105
106 if (!source_l || !source_r || !destination_l || !destination_r)
107 return;
108
109 float target_pan = clampTo(pan_value, -1.0, 1.0);
110
111 int n = frames_to_process;
112
113 if (number_of_input_channels == 1) { // For mono source case.
114 // Pan from left to right [-1; 1] will be normalized as [0; 1].
115 double pan_radian = (target_pan * 0.5 + 0.5) * kPiOverTwoDouble;
116
117 double gain_l = std::cos(pan_radian);
118 double gain_r = std::sin(pan_radian);
119
120 // TODO(rtoy): This can be vectorized using vector_math::Vsmul
121 while (n--) {
122 float input_l = *source_l++;
123 *destination_l++ = static_cast<float>(input_l * gain_l);
124 *destination_r++ = static_cast<float>(input_l * gain_r);
125 }
126 } else { // For stereo source case.
127 // Normalize [-1; 0] to [0; 1] for the left pan position (<= 0), and
128 // do nothing when [0; 1].
129 double pan_radian =
130 (target_pan <= 0 ? target_pan + 1 : target_pan) * kPiOverTwoDouble;
131
132 double gain_l = std::cos(pan_radian);
133 double gain_r = std::sin(pan_radian);
134
135 // TODO(rtoy): Consider moving the if statement outside the loop
136 // since |target_pan| is constant inside the loop.
137 while (n--) {
138 float input_l = *source_l++;
139 float input_r = *source_r++;
140 if (target_pan <= 0) {
141 // When [-1; 0], keep left channel intact and equal-power pan the
142 // right channel only.
143 *destination_l++ = static_cast<float>(input_l + input_r * gain_l);
144 *destination_r++ = static_cast<float>(input_r * gain_r);
145 } else {
146 // When [0; 1], keep right channel intact and equal-power pan the
147 // left channel only.
148 *destination_l++ = static_cast<float>(input_l * gain_l);
149 *destination_r++ = static_cast<float>(input_r + input_l * gain_r);
150 }
151 }
152 }
153 }
154
155 } // namespace blink
156