blob: fe8639de114c368b583aa512b1ef4d1a93c2bb5e [file] [log] [blame]
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "modules/webaudio/AudioBuffer.h"
#include "bindings/core/v8/ExceptionMessages.h"
#include "bindings/core/v8/ExceptionState.h"
#include "core/dom/ExceptionCode.h"
#include "modules/webaudio/AudioBufferOptions.h"
#include "modules/webaudio/BaseAudioContext.h"
#include "platform/audio/AudioBus.h"
#include "platform/audio/AudioFileReader.h"
#include "platform/audio/AudioUtilities.h"
#include "wtf/typed_arrays/Float32Array.h"
namespace blink {
AudioBuffer* AudioBuffer::create(unsigned numberOfChannels,
size_t numberOfFrames,
float sampleRate) {
if (!AudioUtilities::isValidAudioBufferSampleRate(sampleRate) ||
numberOfChannels > BaseAudioContext::maxNumberOfChannels() ||
!numberOfChannels || !numberOfFrames)
return nullptr;
AudioBuffer* buffer =
new AudioBuffer(numberOfChannels, numberOfFrames, sampleRate);
if (!buffer->createdSuccessfully(numberOfChannels))
return nullptr;
return buffer;
}
AudioBuffer* AudioBuffer::create(unsigned numberOfChannels,
size_t numberOfFrames,
float sampleRate,
ExceptionState& exceptionState) {
if (!numberOfChannels ||
numberOfChannels > BaseAudioContext::maxNumberOfChannels()) {
exceptionState.throwDOMException(
NotSupportedError, ExceptionMessages::indexOutsideRange(
"number of channels", numberOfChannels, 1u,
ExceptionMessages::InclusiveBound,
BaseAudioContext::maxNumberOfChannels(),
ExceptionMessages::InclusiveBound));
return nullptr;
}
if (!AudioUtilities::isValidAudioBufferSampleRate(sampleRate)) {
exceptionState.throwDOMException(
NotSupportedError, ExceptionMessages::indexOutsideRange(
"sample rate", sampleRate,
AudioUtilities::minAudioBufferSampleRate(),
ExceptionMessages::InclusiveBound,
AudioUtilities::maxAudioBufferSampleRate(),
ExceptionMessages::InclusiveBound));
return nullptr;
}
if (!numberOfFrames) {
exceptionState.throwDOMException(
NotSupportedError,
ExceptionMessages::indexExceedsMinimumBound(
"number of frames", numberOfFrames, static_cast<size_t>(0)));
return nullptr;
}
AudioBuffer* audioBuffer =
create(numberOfChannels, numberOfFrames, sampleRate);
if (!audioBuffer) {
exceptionState.throwDOMException(
NotSupportedError, "createBuffer(" + String::number(numberOfChannels) +
", " + String::number(numberOfFrames) + ", " +
String::number(sampleRate) + ") failed.");
}
return audioBuffer;
}
AudioBuffer* AudioBuffer::create(BaseAudioContext* context,
const AudioBufferOptions& options,
ExceptionState& exceptionState) {
unsigned numberOfChannels;
size_t numberOfFrames;
float sampleRate;
if (!options.hasNumberOfChannels()) {
exceptionState.throwDOMException(
NotFoundError, "AudioBufferOptions: numberOfChannels is required.");
return nullptr;
}
numberOfChannels = options.numberOfChannels();
numberOfFrames = options.length();
if (options.hasSampleRate())
sampleRate = options.sampleRate();
else
sampleRate = context->sampleRate();
return create(numberOfChannels, numberOfFrames, sampleRate, exceptionState);
}
AudioBuffer* AudioBuffer::createFromAudioFileData(const void* data,
size_t dataSize,
bool mixToMono,
float sampleRate) {
RefPtr<AudioBus> bus =
createBusFromInMemoryAudioFile(data, dataSize, mixToMono, sampleRate);
if (bus) {
AudioBuffer* buffer = new AudioBuffer(bus.get());
if (buffer->createdSuccessfully(bus->numberOfChannels()))
return buffer;
}
return nullptr;
}
AudioBuffer* AudioBuffer::createFromAudioBus(AudioBus* bus) {
if (!bus)
return nullptr;
AudioBuffer* buffer = new AudioBuffer(bus);
if (buffer->createdSuccessfully(bus->numberOfChannels()))
return buffer;
return nullptr;
}
bool AudioBuffer::createdSuccessfully(unsigned desiredNumberOfChannels) const {
return numberOfChannels() == desiredNumberOfChannels;
}
DOMFloat32Array* AudioBuffer::createFloat32ArrayOrNull(size_t length) {
RefPtr<WTF::Float32Array> bufferView =
WTF::Float32Array::createOrNull(length);
if (!bufferView)
return nullptr;
return DOMFloat32Array::create(bufferView.release());
}
AudioBuffer::AudioBuffer(unsigned numberOfChannels,
size_t numberOfFrames,
float sampleRate)
: m_sampleRate(sampleRate), m_length(numberOfFrames) {
m_channels.reserveCapacity(numberOfChannels);
for (unsigned i = 0; i < numberOfChannels; ++i) {
DOMFloat32Array* channelDataArray = createFloat32ArrayOrNull(m_length);
// If the channel data array could not be created, just return. The caller
// will need to check that the desired number of channels were created.
if (!channelDataArray)
return;
channelDataArray->setNeuterable(false);
m_channels.append(channelDataArray);
}
}
AudioBuffer::AudioBuffer(AudioBus* bus)
: m_sampleRate(bus->sampleRate()), m_length(bus->length()) {
// Copy audio data from the bus to the Float32Arrays we manage.
unsigned numberOfChannels = bus->numberOfChannels();
m_channels.reserveCapacity(numberOfChannels);
for (unsigned i = 0; i < numberOfChannels; ++i) {
DOMFloat32Array* channelDataArray = createFloat32ArrayOrNull(m_length);
// If the channel data array could not be created, just return. The caller
// will need to check that the desired number of channels were created.
if (!channelDataArray)
return;
channelDataArray->setNeuterable(false);
const float* src = bus->channel(i)->data();
float* dst = channelDataArray->data();
memmove(dst, src, m_length * sizeof(*dst));
m_channels.append(channelDataArray);
}
}
DOMFloat32Array* AudioBuffer::getChannelData(unsigned channelIndex,
ExceptionState& exceptionState) {
if (channelIndex >= m_channels.size()) {
exceptionState.throwDOMException(
IndexSizeError, "channel index (" + String::number(channelIndex) +
") exceeds number of channels (" +
String::number(m_channels.size()) + ")");
return nullptr;
}
return getChannelData(channelIndex);
}
DOMFloat32Array* AudioBuffer::getChannelData(unsigned channelIndex) {
if (channelIndex >= m_channels.size())
return nullptr;
return m_channels[channelIndex].get();
}
void AudioBuffer::copyFromChannel(DOMFloat32Array* destination,
long channelNumber,
ExceptionState& exceptionState) {
return copyFromChannel(destination, channelNumber, 0, exceptionState);
}
void AudioBuffer::copyFromChannel(DOMFloat32Array* destination,
long channelNumber,
unsigned long startInChannel,
ExceptionState& exceptionState) {
if (channelNumber < 0 ||
channelNumber >= static_cast<long>(m_channels.size())) {
exceptionState.throwDOMException(
IndexSizeError, ExceptionMessages::indexOutsideRange(
"channelNumber", channelNumber, 0L,
ExceptionMessages::InclusiveBound,
static_cast<long>(m_channels.size() - 1),
ExceptionMessages::InclusiveBound));
return;
}
DOMFloat32Array* channelData = m_channels[channelNumber].get();
if (startInChannel >= channelData->length()) {
exceptionState.throwDOMException(
IndexSizeError, ExceptionMessages::indexOutsideRange(
"startInChannel", startInChannel, 0UL,
ExceptionMessages::InclusiveBound,
static_cast<unsigned long>(channelData->length()),
ExceptionMessages::ExclusiveBound));
return;
}
unsigned count = channelData->length() - startInChannel;
count = std::min(destination->length(), count);
const float* src = channelData->data();
float* dst = destination->data();
DCHECK(src);
DCHECK(dst);
memcpy(dst, src + startInChannel, count * sizeof(*src));
}
void AudioBuffer::copyToChannel(DOMFloat32Array* source,
long channelNumber,
ExceptionState& exceptionState) {
return copyToChannel(source, channelNumber, 0, exceptionState);
}
void AudioBuffer::copyToChannel(DOMFloat32Array* source,
long channelNumber,
unsigned long startInChannel,
ExceptionState& exceptionState) {
if (channelNumber < 0 ||
channelNumber >= static_cast<long>(m_channels.size())) {
exceptionState.throwDOMException(
IndexSizeError, ExceptionMessages::indexOutsideRange(
"channelNumber", channelNumber, 0L,
ExceptionMessages::InclusiveBound,
static_cast<long>(m_channels.size() - 1),
ExceptionMessages::InclusiveBound));
return;
}
DOMFloat32Array* channelData = m_channels[channelNumber].get();
if (startInChannel >= channelData->length()) {
exceptionState.throwDOMException(
IndexSizeError, ExceptionMessages::indexOutsideRange(
"startInChannel", startInChannel, 0UL,
ExceptionMessages::InclusiveBound,
static_cast<unsigned long>(channelData->length()),
ExceptionMessages::ExclusiveBound));
return;
}
unsigned count = channelData->length() - startInChannel;
count = std::min(source->length(), count);
const float* src = source->data();
float* dst = channelData->data();
DCHECK(src);
DCHECK(dst);
memcpy(dst + startInChannel, src, count * sizeof(*dst));
}
void AudioBuffer::zero() {
for (unsigned i = 0; i < m_channels.size(); ++i) {
if (DOMFloat32Array* array = getChannelData(i)) {
float* data = array->data();
memset(data, 0, length() * sizeof(*data));
}
}
}
} // namespace blink