• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *
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  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "config.h"
30 
31 #if ENABLE(WEB_AUDIO)
32 
33 #include "AudioBus.h"
34 
35 #if !PLATFORM(MAC)
36 #include "SincResampler.h"
37 #endif
38 #include "VectorMath.h"
39 #include <algorithm>
40 #include <assert.h>
41 #include <math.h>
42 #include <wtf/OwnPtr.h>
43 #include <wtf/PassOwnPtr.h>
44 
45 namespace WebCore {
46 
47 using namespace VectorMath;
48 
AudioBus(unsigned numberOfChannels,size_t length,bool allocate)49 AudioBus::AudioBus(unsigned numberOfChannels, size_t length, bool allocate)
50     : m_length(length)
51     , m_busGain(1.0)
52     , m_isFirstTime(true)
53     , m_sampleRate(0.0)
54 {
55     m_channels.reserveInitialCapacity(numberOfChannels);
56 
57     for (unsigned i = 0; i < numberOfChannels; ++i) {
58         PassOwnPtr<AudioChannel> channel = allocate ? adoptPtr(new AudioChannel(length)) : adoptPtr(new AudioChannel(0, length));
59         m_channels.append(channel);
60     }
61 
62     m_layout = LayoutCanonical; // for now this is the only layout we define
63 }
64 
setChannelMemory(unsigned channelIndex,float * storage,size_t length)65 void AudioBus::setChannelMemory(unsigned channelIndex, float* storage, size_t length)
66 {
67     if (channelIndex < m_channels.size()) {
68         channel(channelIndex)->set(storage, length);
69         m_length = length; // FIXME: verify that this length matches all the other channel lengths
70     }
71 }
72 
zero()73 void AudioBus::zero()
74 {
75     for (unsigned i = 0; i < m_channels.size(); ++i)
76         m_channels[i]->zero();
77 }
78 
channelByType(unsigned channelType)79 AudioChannel* AudioBus::channelByType(unsigned channelType)
80 {
81     // For now we only support canonical channel layouts...
82     if (m_layout != LayoutCanonical)
83         return 0;
84 
85     switch (numberOfChannels()) {
86     case 1: // mono
87         if (channelType == ChannelMono || channelType == ChannelLeft)
88             return channel(0);
89         return 0;
90 
91     case 2: // stereo
92         switch (channelType) {
93         case ChannelLeft: return channel(0);
94         case ChannelRight: return channel(1);
95         default: return 0;
96         }
97 
98     case 4: // quad
99         switch (channelType) {
100         case ChannelLeft: return channel(0);
101         case ChannelRight: return channel(1);
102         case ChannelSurroundLeft: return channel(2);
103         case ChannelSurroundRight: return channel(3);
104         default: return 0;
105         }
106 
107     case 5: // 5.0
108         switch (channelType) {
109         case ChannelLeft: return channel(0);
110         case ChannelRight: return channel(1);
111         case ChannelCenter: return channel(2);
112         case ChannelSurroundLeft: return channel(3);
113         case ChannelSurroundRight: return channel(4);
114         default: return 0;
115         }
116 
117     case 6: // 5.1
118         switch (channelType) {
119         case ChannelLeft: return channel(0);
120         case ChannelRight: return channel(1);
121         case ChannelCenter: return channel(2);
122         case ChannelLFE: return channel(3);
123         case ChannelSurroundLeft: return channel(4);
124         case ChannelSurroundRight: return channel(5);
125         default: return 0;
126         }
127     }
128 
129     ASSERT_NOT_REACHED();
130     return 0;
131 }
132 
133 // Returns true if the channel count and frame-size match.
topologyMatches(const AudioBus & bus) const134 bool AudioBus::topologyMatches(const AudioBus& bus) const
135 {
136     if (numberOfChannels() != bus.numberOfChannels())
137         return false; // channel mismatch
138 
139     // Make sure source bus has enough frames.
140     if (length() > bus.length())
141         return false; // frame-size mismatch
142 
143     return true;
144 }
145 
createBufferFromRange(AudioBus * sourceBuffer,unsigned startFrame,unsigned endFrame)146 PassOwnPtr<AudioBus> AudioBus::createBufferFromRange(AudioBus* sourceBuffer, unsigned startFrame, unsigned endFrame)
147 {
148     size_t numberOfSourceFrames = sourceBuffer->length();
149     unsigned numberOfChannels = sourceBuffer->numberOfChannels();
150 
151     // Sanity checking
152     bool isRangeSafe = startFrame < endFrame && endFrame <= numberOfSourceFrames;
153     ASSERT(isRangeSafe);
154     if (!isRangeSafe)
155         return 0;
156 
157     size_t rangeLength = endFrame - startFrame;
158 
159     OwnPtr<AudioBus> audioBus = adoptPtr(new AudioBus(numberOfChannels, rangeLength));
160     audioBus->setSampleRate(sourceBuffer->sampleRate());
161 
162     for (unsigned i = 0; i < numberOfChannels; ++i)
163         audioBus->channel(i)->copyFromRange(sourceBuffer->channel(i), startFrame, endFrame);
164 
165     return audioBus.release();
166 }
167 
maxAbsValue() const168 float AudioBus::maxAbsValue() const
169 {
170     float max = 0.0f;
171     for (unsigned i = 0; i < numberOfChannels(); ++i) {
172         const AudioChannel* channel = this->channel(i);
173         max = std::max(max, channel->maxAbsValue());
174     }
175 
176     return max;
177 }
178 
normalize()179 void AudioBus::normalize()
180 {
181     float max = maxAbsValue();
182     if (max)
183         scale(1.0f / max);
184 }
185 
scale(double scale)186 void AudioBus::scale(double scale)
187 {
188     for (unsigned i = 0; i < numberOfChannels(); ++i)
189         channel(i)->scale(scale);
190 }
191 
192 // Just copies the samples from the source bus to this one.
193 // This is just a simple copy if the number of channels match, otherwise a mixup or mixdown is done.
194 // For now, we just support a mixup from mono -> stereo.
copyFrom(const AudioBus & sourceBus)195 void AudioBus::copyFrom(const AudioBus& sourceBus)
196 {
197     if (&sourceBus == this)
198         return;
199 
200     if (numberOfChannels() == sourceBus.numberOfChannels()) {
201         for (unsigned i = 0; i < numberOfChannels(); ++i)
202             channel(i)->copyFrom(sourceBus.channel(i));
203     } else if (numberOfChannels() == 2 && sourceBus.numberOfChannels() == 1) {
204         // Handle mono -> stereo case (for now simply copy mono channel into both left and right)
205         // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center...
206         const AudioChannel* sourceChannel = sourceBus.channel(0);
207         channel(0)->copyFrom(sourceChannel);
208         channel(1)->copyFrom(sourceChannel);
209     } else {
210         // Case not handled
211         ASSERT_NOT_REACHED();
212     }
213 }
214 
sumFrom(const AudioBus & sourceBus)215 void AudioBus::sumFrom(const AudioBus &sourceBus)
216 {
217     if (numberOfChannels() == sourceBus.numberOfChannels()) {
218         for (unsigned i = 0; i < numberOfChannels(); ++i)
219             channel(i)->sumFrom(sourceBus.channel(i));
220     } else if (numberOfChannels() == 2 && sourceBus.numberOfChannels() == 1) {
221         // Handle mono -> stereo case (for now simply sum mono channel into both left and right)
222         // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center...
223         const AudioChannel* sourceChannel = sourceBus.channel(0);
224         channel(0)->sumFrom(sourceChannel);
225         channel(1)->sumFrom(sourceChannel);
226     } else {
227         // Case not handled
228         ASSERT_NOT_REACHED();
229     }
230 }
231 
processWithGainFromMonoStereo(const AudioBus & sourceBus,double * lastMixGain,double targetGain,bool sumToBus)232 void AudioBus::processWithGainFromMonoStereo(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus)
233 {
234     // We don't want to suddenly change the gain from mixing one time slice to the next,
235     // so we "de-zipper" by slowly changing the gain each sample-frame until we've achieved the target gain.
236 
237     // FIXME: optimize this method (SSE, etc.)
238     // FIXME: Need fast path here when gain has converged on targetGain. In this case, de-zippering is no longer needed.
239     // FIXME: Need fast path when this==sourceBus && lastMixGain==targetGain==1.0 && sumToBus==false (this is a NOP)
240 
241     // Take master bus gain into account as well as the targetGain.
242     double totalDesiredGain = m_busGain * targetGain;
243 
244     // First time, snap directly to totalDesiredGain.
245     double gain = m_isFirstTime ? totalDesiredGain : *lastMixGain;
246     m_isFirstTime = false;
247 
248     int numberOfSourceChannels = sourceBus.numberOfChannels();
249     int numberOfDestinationChannels = numberOfChannels();
250 
251     AudioBus& sourceBusSafe = const_cast<AudioBus&>(sourceBus);
252     const float* sourceL = sourceBusSafe.channelByType(ChannelLeft)->data();
253     const float* sourceR = numberOfSourceChannels > 1 ? sourceBusSafe.channelByType(ChannelRight)->data() : 0;
254 
255     float* destinationL = channelByType(ChannelLeft)->data();
256     float* destinationR = numberOfDestinationChannels > 1 ? channelByType(ChannelRight)->data() : 0;
257 
258     const double DezipperRate = 0.005;
259     int framesToProcess = length();
260 
261     if (sumToBus) {
262         // Sum to our bus
263         if (sourceR && destinationR) {
264             // Stereo
265             while (framesToProcess--) {
266                 float sampleL = *sourceL++;
267                 float sampleR = *sourceR++;
268                 *destinationL++ += static_cast<float>(gain * sampleL);
269                 *destinationR++ += static_cast<float>(gain * sampleR);
270 
271                 // Slowly change gain to desired gain.
272                 gain += (totalDesiredGain - gain) * DezipperRate;
273             }
274         } else if (destinationR) {
275             // Mono -> stereo (mix equally into L and R)
276             // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center...
277             while (framesToProcess--) {
278                 float sample = *sourceL++;
279                 *destinationL++ += static_cast<float>(gain * sample);
280                 *destinationR++ += static_cast<float>(gain * sample);
281 
282                 // Slowly change gain to desired gain.
283                 gain += (totalDesiredGain - gain) * DezipperRate;
284             }
285         } else {
286             // Mono
287             while (framesToProcess--) {
288                 float sampleL = *sourceL++;
289                 *destinationL++ += static_cast<float>(gain * sampleL);
290 
291                 // Slowly change gain to desired gain.
292                 gain += (totalDesiredGain - gain) * DezipperRate;
293             }
294         }
295     } else {
296         // Process directly (without summing) to our bus
297         if (sourceR && destinationR) {
298             // Stereo
299             while (framesToProcess--) {
300                 float sampleL = *sourceL++;
301                 float sampleR = *sourceR++;
302                 *destinationL++ = static_cast<float>(gain * sampleL);
303                 *destinationR++ = static_cast<float>(gain * sampleR);
304 
305                 // Slowly change gain to desired gain.
306                 gain += (totalDesiredGain - gain) * DezipperRate;
307             }
308         } else if (destinationR) {
309             // Mono -> stereo (mix equally into L and R)
310             // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center...
311             while (framesToProcess--) {
312                 float sample = *sourceL++;
313                 *destinationL++ = static_cast<float>(gain * sample);
314                 *destinationR++ = static_cast<float>(gain * sample);
315 
316                 // Slowly change gain to desired gain.
317                 gain += (totalDesiredGain - gain) * DezipperRate;
318             }
319         } else {
320             // Mono
321             while (framesToProcess--) {
322                 float sampleL = *sourceL++;
323                 *destinationL++ = static_cast<float>(gain * sampleL);
324 
325                 // Slowly change gain to desired gain.
326                 gain += (totalDesiredGain - gain) * DezipperRate;
327             }
328         }
329     }
330 
331     // Save the target gain as the starting point for next time around.
332     *lastMixGain = gain;
333 }
334 
processWithGainFrom(const AudioBus & sourceBus,double * lastMixGain,double targetGain,bool sumToBus)335 void AudioBus::processWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain, bool sumToBus)
336 {
337     // Make sure we're summing from same type of bus.
338     // We *are* able to sum from mono -> stereo
339     if (sourceBus.numberOfChannels() != 1 && !topologyMatches(sourceBus))
340         return;
341 
342     // Dispatch for different channel layouts
343     switch (numberOfChannels()) {
344     case 1: // mono
345     case 2: // stereo
346         processWithGainFromMonoStereo(sourceBus, lastMixGain, targetGain, sumToBus);
347         break;
348     case 4: // FIXME: implement quad
349     case 5: // FIXME: implement 5.0
350     default:
351         ASSERT_NOT_REACHED();
352         break;
353     }
354 }
355 
copyWithGainFrom(const AudioBus & sourceBus,double * lastMixGain,double targetGain)356 void AudioBus::copyWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain)
357 {
358     processWithGainFrom(sourceBus, lastMixGain, targetGain, false);
359 }
360 
sumWithGainFrom(const AudioBus & sourceBus,double * lastMixGain,double targetGain)361 void AudioBus::sumWithGainFrom(const AudioBus &sourceBus, double* lastMixGain, double targetGain)
362 {
363     processWithGainFrom(sourceBus, lastMixGain, targetGain, true);
364 }
365 
366 #if !PLATFORM(MAC)
createBySampleRateConverting(AudioBus * sourceBus,bool mixToMono,double newSampleRate)367 PassOwnPtr<AudioBus> AudioBus::createBySampleRateConverting(AudioBus* sourceBus, bool mixToMono, double newSampleRate)
368 {
369     // sourceBus's sample-rate must be known.
370     ASSERT(sourceBus && sourceBus->sampleRate());
371     if (!sourceBus || !sourceBus->sampleRate())
372         return 0;
373 
374     double sourceSampleRate = sourceBus->sampleRate();
375     double destinationSampleRate = newSampleRate;
376     unsigned numberOfSourceChannels = sourceBus->numberOfChannels();
377 
378     if (numberOfSourceChannels == 1)
379         mixToMono = false; // already mono
380 
381     if (sourceSampleRate == destinationSampleRate) {
382         // No sample-rate conversion is necessary.
383         if (mixToMono)
384             return AudioBus::createByMixingToMono(sourceBus);
385 
386         // Return exact copy.
387         return AudioBus::createBufferFromRange(sourceBus, 0, sourceBus->length());
388     }
389 
390     // First, mix to mono (if necessary) then sample-rate convert.
391     AudioBus* resamplerSourceBus;
392     OwnPtr<AudioBus> mixedMonoBus;
393     if (mixToMono) {
394         mixedMonoBus = AudioBus::createByMixingToMono(sourceBus);
395         resamplerSourceBus = mixedMonoBus.get();
396     } else {
397         // Directly resample without down-mixing.
398         resamplerSourceBus = sourceBus;
399     }
400 
401     // Calculate destination length based on the sample-rates.
402     double sampleRateRatio = sourceSampleRate / destinationSampleRate;
403     int sourceLength = resamplerSourceBus->length();
404     int destinationLength = sourceLength / sampleRateRatio;
405 
406     // Create destination bus with same number of channels.
407     unsigned numberOfDestinationChannels = resamplerSourceBus->numberOfChannels();
408     OwnPtr<AudioBus> destinationBus(adoptPtr(new AudioBus(numberOfDestinationChannels, destinationLength)));
409 
410     // Sample-rate convert each channel.
411     for (unsigned i = 0; i < numberOfDestinationChannels; ++i) {
412         float* source = resamplerSourceBus->channel(i)->data();
413         float* destination = destinationBus->channel(i)->data();
414 
415         SincResampler resampler(sampleRateRatio);
416         resampler.process(source, destination, sourceLength);
417     }
418 
419     return destinationBus.release();
420 }
421 #endif // !PLATFORM(MAC)
422 
createByMixingToMono(AudioBus * sourceBus)423 PassOwnPtr<AudioBus> AudioBus::createByMixingToMono(AudioBus* sourceBus)
424 {
425     switch (sourceBus->numberOfChannels()) {
426     case 1:
427         // Simply create an exact copy.
428         return AudioBus::createBufferFromRange(sourceBus, 0, sourceBus->length());
429     case 2:
430         {
431             unsigned n = sourceBus->length();
432             OwnPtr<AudioBus> destinationBus(adoptPtr(new AudioBus(1, n)));
433 
434             float* sourceL = sourceBus->channel(0)->data();
435             float* sourceR = sourceBus->channel(1)->data();
436             float* destination = destinationBus->channel(0)->data();
437 
438             // Do the mono mixdown.
439             for (unsigned i = 0; i < n; ++i)
440                 destination[i] = 0.5 * (sourceL[i] + sourceR[i]);
441 
442             return destinationBus.release();
443         }
444     }
445 
446     ASSERT_NOT_REACHED();
447     return 0;
448 }
449 
450 } // WebCore
451 
452 #endif // ENABLE(WEB_AUDIO)
453