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 "modules/webaudio/AudioBuffer.h"
34
35#include "bindings/core/v8/ExceptionMessages.h"
36#include "bindings/core/v8/ExceptionState.h"
37#include "bindings/core/v8/custom/V8ArrayBufferCustom.h"
38#include "core/dom/ExceptionCode.h"
39#include "modules/webaudio/AudioContext.h"
40#include "platform/audio/AudioBus.h"
41#include "platform/audio/AudioFileReader.h"
42#include "platform/audio/AudioUtilities.h"
43
44namespace blink {
45
46AudioBuffer* AudioBuffer::create(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
47{
48    if (!AudioUtilities::isValidAudioBufferSampleRate(sampleRate) || numberOfChannels > AudioContext::maxNumberOfChannels() || !numberOfChannels || !numberOfFrames)
49        return 0;
50
51    AudioBuffer* buffer = new AudioBuffer(numberOfChannels, numberOfFrames, sampleRate);
52
53    if (!buffer->createdSuccessfully(numberOfChannels))
54        return 0;
55    return buffer;
56}
57
58AudioBuffer* AudioBuffer::create(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionState& exceptionState)
59{
60    if (!numberOfChannels || numberOfChannels > AudioContext::maxNumberOfChannels()) {
61        exceptionState.throwDOMException(
62            NotSupportedError,
63            ExceptionMessages::indexOutsideRange(
64                "number of channels",
65                numberOfChannels,
66                1u,
67                ExceptionMessages::InclusiveBound,
68                AudioContext::maxNumberOfChannels(),
69                ExceptionMessages::InclusiveBound));
70        return 0;
71    }
72
73    if (!AudioUtilities::isValidAudioBufferSampleRate(sampleRate)) {
74        exceptionState.throwDOMException(
75            NotSupportedError,
76            ExceptionMessages::indexOutsideRange(
77                "sample rate",
78                sampleRate,
79                AudioUtilities::minAudioBufferSampleRate(),
80                ExceptionMessages::InclusiveBound,
81                AudioUtilities::maxAudioBufferSampleRate(),
82                ExceptionMessages::InclusiveBound));
83        return 0;
84    }
85
86    if (!numberOfFrames) {
87        exceptionState.throwDOMException(
88            NotSupportedError,
89            ExceptionMessages::indexExceedsMinimumBound(
90                "number of frames",
91                numberOfFrames,
92                static_cast<size_t>(0)));
93        return 0;
94    }
95
96    AudioBuffer* audioBuffer = create(numberOfChannels, numberOfFrames, sampleRate);
97
98    if (!audioBuffer) {
99        exceptionState.throwDOMException(
100            NotSupportedError,
101            "createBuffer("
102            + String::number(numberOfChannels) + ", "
103            + String::number(numberOfFrames) + ", "
104            + String::number(sampleRate)
105            + ") failed.");
106    }
107
108    return audioBuffer;
109}
110
111AudioBuffer* AudioBuffer::createFromAudioFileData(const void* data, size_t dataSize, bool mixToMono, float sampleRate)
112{
113    RefPtr<AudioBus> bus = createBusFromInMemoryAudioFile(data, dataSize, mixToMono, sampleRate);
114    if (bus.get()) {
115        AudioBuffer* buffer = new AudioBuffer(bus.get());
116        if (buffer->createdSuccessfully(bus->numberOfChannels()))
117            return buffer;
118    }
119
120    return 0;
121}
122
123AudioBuffer* AudioBuffer::createFromAudioBus(AudioBus* bus)
124{
125    if (!bus)
126        return 0;
127    AudioBuffer* buffer = new AudioBuffer(bus);
128    if (buffer->createdSuccessfully(bus->numberOfChannels()))
129        return buffer;
130    return 0;
131}
132
133bool AudioBuffer::createdSuccessfully(unsigned desiredNumberOfChannels) const
134{
135    return numberOfChannels() == desiredNumberOfChannels;
136}
137
138AudioBuffer::AudioBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
139    : m_sampleRate(sampleRate)
140    , m_length(numberOfFrames)
141{
142    m_channels.reserveCapacity(numberOfChannels);
143
144    for (unsigned i = 0; i < numberOfChannels; ++i) {
145        RefPtr<Float32Array> channelDataArray = Float32Array::create(m_length);
146        // If the channel data array could not be created, just return. The caller will need to
147        // check that the desired number of channels were created.
148        if (!channelDataArray) {
149            return;
150        }
151
152        channelDataArray->setNeuterable(false);
153        m_channels.append(channelDataArray);
154    }
155}
156
157AudioBuffer::AudioBuffer(AudioBus* bus)
158    : m_sampleRate(bus->sampleRate())
159    , m_length(bus->length())
160{
161    // Copy audio data from the bus to the Float32Arrays we manage.
162    unsigned numberOfChannels = bus->numberOfChannels();
163    m_channels.reserveCapacity(numberOfChannels);
164    for (unsigned i = 0; i < numberOfChannels; ++i) {
165        RefPtr<Float32Array> channelDataArray = Float32Array::create(m_length);
166        // If the channel data array could not be created, just return. The caller will need to
167        // check that the desired number of channels were created.
168        if (!channelDataArray)
169            return;
170
171        channelDataArray->setNeuterable(false);
172        channelDataArray->setRange(bus->channel(i)->data(), m_length, 0);
173        m_channels.append(channelDataArray);
174    }
175}
176
177PassRefPtr<Float32Array> AudioBuffer::getChannelData(unsigned channelIndex, ExceptionState& exceptionState)
178{
179    if (channelIndex >= m_channels.size()) {
180        exceptionState.throwDOMException(IndexSizeError, "channel index (" + String::number(channelIndex) + ") exceeds number of channels (" + String::number(m_channels.size()) + ")");
181        return nullptr;
182    }
183
184    Float32Array* channelData = m_channels[channelIndex].get();
185    return Float32Array::create(channelData->buffer(), channelData->byteOffset(), channelData->length());
186}
187
188Float32Array* AudioBuffer::getChannelData(unsigned channelIndex)
189{
190    if (channelIndex >= m_channels.size())
191        return 0;
192
193    return m_channels[channelIndex].get();
194}
195
196void AudioBuffer::zero()
197{
198    for (unsigned i = 0; i < m_channels.size(); ++i) {
199        if (getChannelData(i))
200            getChannelData(i)->zeroRange(0, length());
201    }
202}
203
204v8::Handle<v8::Object> AudioBuffer::associateWithWrapper(const WrapperTypeInfo* wrapperType, v8::Handle<v8::Object> wrapper, v8::Isolate* isolate)
205{
206    ScriptWrappable::associateWithWrapper(wrapperType, wrapper, isolate);
207
208    if (!wrapper.IsEmpty()) {
209        // We only setDeallocationObservers on array buffers that are held by
210        // some object in the V8 heap, not in the ArrayBuffer constructor
211        // itself. This is because V8 GC only cares about memory it can free on
212        // GC, and until the object is exposed to JavaScript, V8 GC doesn't
213        // affect it.
214        for (unsigned i = 0, n = numberOfChannels(); i < n; ++i) {
215            getChannelData(i)->buffer()->setDeallocationObserver(V8ArrayBufferDeallocationObserver::instanceTemplate());
216        }
217    }
218    return wrapper;
219}
220
221} // namespace blink
222
223#endif // ENABLE(WEB_AUDIO)
224