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