1/*
2 * Copyright 2014 The Chromium Authors. All rights reserved.
3 * Copyright (C) 2020 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "StereoPanner.h"
29
30#if ENABLE(WEB_AUDIO)
31
32#include "AudioBus.h"
33#include "AudioUtilities.h"
34#include <wtf/MathExtras.h>
35
36namespace WebCore {
37
38void StereoPanner::panWithSampleAccurateValues(const AudioBus* inputBus, AudioBus* outputBus, const float* panValues, size_t framesToProcess)
39{
40 bool isInputSafe = inputBus && (inputBus->numberOfChannels() == 1 || inputBus->numberOfChannels() == 2) && framesToProcess <= inputBus->length();
41 ASSERT(isInputSafe);
42 if (!isInputSafe)
43 return;
44
45 unsigned numberOfInputChannels = inputBus->numberOfChannels();
46
47 bool isOutputSafe = outputBus && outputBus->numberOfChannels() == 2 && framesToProcess <= outputBus->length();
48 ASSERT(isOutputSafe);
49 if (!isOutputSafe)
50 return;
51
52 const float* sourceL = inputBus->channel(0)->data();
53 const float* sourceR = numberOfInputChannels > 1 ? inputBus->channel(1)->data() : sourceL;
54 float* destinationL = outputBus->channelByType(AudioBus::ChannelLeft)->mutableData();
55 float* destinationR = outputBus->channelByType(AudioBus::ChannelRight)->mutableData();
56
57 if (!sourceL || !sourceR || !destinationL || !destinationR)
58 return;
59
60 double gainL;
61 double gainR;
62 double panRadian;
63
64 int n = framesToProcess;
65
66 // Handles mono source case first, then stereo source case.
67 if (numberOfInputChannels == 1) {
68 while (n--) {
69 float inputL = *sourceL++;
70 double pan = clampTo(*panValues++, -1.0, 1.0);
71 // Pan from left to right [-1; 1] will be normalized as [0; 1].
72 panRadian = (pan * 0.5 + 0.5) * piOverTwoDouble;
73 gainL = cos(panRadian);
74 gainR = sin(panRadian);
75 *destinationL++ = static_cast<float>(inputL * gainL);
76 *destinationR++ = static_cast<float>(inputL * gainR);
77 }
78 } else {
79 while (n--) {
80 float inputL = *sourceL++;
81 float inputR = *sourceR++;
82 double pan = clampTo(*panValues++, -1.0, 1.0);
83 // Normalize [-1; 0] to [0; 1]. Do nothing when [0; 1].
84 panRadian = (pan <= 0 ? pan + 1 : pan) * piOverTwoDouble;
85 gainL = cos(panRadian);
86 gainR = sin(panRadian);
87 if (pan <= 0) {
88 *destinationL++ = static_cast<float>(inputL + inputR * gainL);
89 *destinationR++ = static_cast<float>(inputR * gainR);
90 } else {
91 *destinationL++ = static_cast<float>(inputL * gainL);
92 *destinationR++ = static_cast<float>(inputR + inputL * gainR);
93 }
94 }
95 }
96}
97
98void StereoPanner::panToTargetValue(const AudioBus* inputBus, AudioBus* outputBus, float panValue, size_t framesToProcess)
99{
100 bool isInputSafe = inputBus && (inputBus->numberOfChannels() == 1 || inputBus->numberOfChannels() == 2) && framesToProcess <= inputBus->length();
101 ASSERT(isInputSafe);
102 if (!isInputSafe)
103 return;
104
105 unsigned numberOfInputChannels = inputBus->numberOfChannels();
106
107 bool isOutputSafe = outputBus && outputBus->numberOfChannels() == 2 && framesToProcess <= outputBus->length();
108 ASSERT(isOutputSafe);
109 if (!isOutputSafe)
110 return;
111
112 const float* sourceL = inputBus->channel(0)->data();
113 const float* sourceR = numberOfInputChannels > 1 ? inputBus->channel(1)->data() : sourceL;
114 float* destinationL = outputBus->channelByType(AudioBus::ChannelLeft)->mutableData();
115 float* destinationR = outputBus->channelByType(AudioBus::ChannelRight)->mutableData();
116
117 if (!sourceL || !sourceR || !destinationL || !destinationR)
118 return;
119
120 float targetPan = clampTo(panValue, -1.0, 1.0);
121
122 int n = framesToProcess;
123
124 if (numberOfInputChannels == 1) {
125 double panRadian = (targetPan * 0.5 + 0.5) * piOverTwoDouble;
126
127 double gainL = cos(panRadian);
128 double gainR = sin(panRadian);
129
130 while (n--) {
131 float inputL = *sourceL++;
132 *destinationL++ = static_cast<float>(inputL * gainL);
133 *destinationR++ = static_cast<float>(inputL * gainR);
134 }
135 } else {
136 double panRadian = (targetPan <= 0 ? targetPan + 1 : targetPan) * piOverTwoDouble;
137
138 double gainL = cos(panRadian);
139 double gainR = sin(panRadian);
140
141 while (n--) {
142 float inputL = *sourceL++;
143 float inputR = *sourceR++;
144 if (targetPan <= 0) {
145 *destinationL++ = static_cast<float>(inputL + inputR * gainL);
146 *destinationR++ = static_cast<float>(inputR * gainR);
147 } else {
148 *destinationL++ = static_cast<float>(inputL * gainL);
149 *destinationR++ = static_cast<float>(inputR + inputL * gainR);
150 }
151 }
152 }
153}
154
155} // namespace WebCore
156
157#endif // ENABLE(WEB_AUDIO)