blob: 40a355dec4aad8d39567c404619959c0347b2ad6 [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 "third_party/blink/renderer/modules/webaudio/audio_buffer.h"
#include <memory>
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_buffer_options.h"
#include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
#include "third_party/blink/renderer/platform/audio/audio_bus.h"
#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/wtf/text/strcat.h"
namespace blink {
AudioBuffer* AudioBuffer::Create(unsigned number_of_channels,
uint32_t number_of_frames,
float sample_rate) {
if (!audio_utilities::IsValidAudioBufferSampleRate(sample_rate) ||
number_of_channels > BaseAudioContext::MaxNumberOfChannels() ||
!number_of_channels || !number_of_frames) {
return nullptr;
}
AudioBuffer* buffer = MakeGarbageCollected<AudioBuffer>(
number_of_channels, number_of_frames, sample_rate);
if (!buffer->CreatedSuccessfully(number_of_channels)) {
return nullptr;
}
return buffer;
}
AudioBuffer* AudioBuffer::Create(unsigned number_of_channels,
uint32_t number_of_frames,
float sample_rate,
ExceptionState& exception_state) {
if (!number_of_channels ||
number_of_channels > BaseAudioContext::MaxNumberOfChannels()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
ExceptionMessages::IndexOutsideRange(
"number of channels", number_of_channels, 1u,
ExceptionMessages::kInclusiveBound,
BaseAudioContext::MaxNumberOfChannels(),
ExceptionMessages::kInclusiveBound));
return nullptr;
}
if (!audio_utilities::IsValidAudioBufferSampleRate(sample_rate)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
ExceptionMessages::IndexOutsideRange(
"sample rate", sample_rate,
audio_utilities::MinAudioBufferSampleRate(),
ExceptionMessages::kInclusiveBound,
audio_utilities::MaxAudioBufferSampleRate(),
ExceptionMessages::kInclusiveBound));
return nullptr;
}
if (!number_of_frames) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
ExceptionMessages::IndexExceedsMinimumBound("number of frames",
number_of_frames, 0u));
return nullptr;
}
AudioBuffer* audio_buffer =
Create(number_of_channels, number_of_frames, sample_rate);
if (!audio_buffer) {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
StrCat({"createBuffer(", String::Number(number_of_channels), ", ",
String::Number(number_of_frames), ", ",
String::Number(sample_rate), ") failed."}));
}
return audio_buffer;
}
AudioBuffer* AudioBuffer::Create(const AudioBufferOptions* options,
ExceptionState& exception_state) {
return Create(options->numberOfChannels(), options->length(),
options->sampleRate(), exception_state);
}
AudioBuffer* AudioBuffer::CreateUninitialized(unsigned number_of_channels,
uint32_t number_of_frames,
float sample_rate) {
if (!audio_utilities::IsValidAudioBufferSampleRate(sample_rate) ||
number_of_channels > BaseAudioContext::MaxNumberOfChannels() ||
!number_of_channels || !number_of_frames) {
return nullptr;
}
AudioBuffer* buffer = MakeGarbageCollected<AudioBuffer>(
number_of_channels, number_of_frames, sample_rate,
InitializationPolicy::kDontInitialize);
if (!buffer->CreatedSuccessfully(number_of_channels)) {
return nullptr;
}
return buffer;
}
AudioBuffer* AudioBuffer::CreateFromAudioBus(AudioBus* bus) {
if (!bus) {
return nullptr;
}
AudioBuffer* buffer = MakeGarbageCollected<AudioBuffer>(bus);
if (buffer->CreatedSuccessfully(bus->NumberOfChannels())) {
return buffer;
}
return nullptr;
}
bool AudioBuffer::CreatedSuccessfully(
unsigned desired_number_of_channels) const {
return numberOfChannels() == desired_number_of_channels;
}
DOMFloat32Array* AudioBuffer::CreateFloat32ArrayOrNull(
uint32_t length,
InitializationPolicy policy) {
return policy == InitializationPolicy::kZeroInitialize
? DOMFloat32Array::CreateOrNull(length)
: DOMFloat32Array::CreateUninitializedOrNull(length);
}
AudioBuffer::AudioBuffer(unsigned number_of_channels,
uint32_t number_of_frames,
float sample_rate,
InitializationPolicy policy)
: sample_rate_(sample_rate), length_(number_of_frames) {
channels_.reserve(number_of_channels);
for (unsigned i = 0; i < number_of_channels; ++i) {
DOMFloat32Array* channel_data_array =
CreateFloat32ArrayOrNull(length_, policy);
// 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 (!channel_data_array) {
return;
}
channels_.push_back(channel_data_array);
}
}
AudioBuffer::AudioBuffer(AudioBus* bus)
: sample_rate_(bus->SampleRate()), length_(bus->length()) {
// Copy audio data from the bus to the Float32Arrays we manage.
unsigned number_of_channels = bus->NumberOfChannels();
channels_.reserve(number_of_channels);
for (unsigned i = 0; i < number_of_channels; ++i) {
DOMFloat32Array* channel_data_array = CreateFloat32ArrayOrNull(
length_, InitializationPolicy::kDontInitialize);
// 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 (!channel_data_array) {
return;
}
const float* src = bus->Channel(i)->Data();
float* dst = channel_data_array->Data();
UNSAFE_TODO(memmove(dst, src, length_ * sizeof(*dst)));
channels_.push_back(channel_data_array);
}
}
NotShared<DOMFloat32Array> AudioBuffer::getChannelData(
unsigned channel_index,
ExceptionState& exception_state) {
if (channel_index >= channels_.size()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kIndexSizeError,
StrCat({"channel index (", String::Number(channel_index),
") exceeds number of channels (",
String::Number(channels_.size()), ")"}));
return NotShared<DOMFloat32Array>(nullptr);
}
return getChannelData(channel_index);
}
NotShared<DOMFloat32Array> AudioBuffer::getChannelData(unsigned channel_index) {
if (channel_index >= channels_.size()) {
return NotShared<DOMFloat32Array>(nullptr);
}
return NotShared<DOMFloat32Array>(channels_[channel_index].Get());
}
void AudioBuffer::copyFromChannel(NotShared<DOMFloat32Array> destination,
int32_t channel_number,
ExceptionState& exception_state) {
return copyFromChannel(destination, channel_number, 0, exception_state);
}
void AudioBuffer::copyFromChannel(NotShared<DOMFloat32Array> destination,
int32_t channel_number,
size_t buffer_offset,
ExceptionState& exception_state) {
if (!destination->length()) {
return;
}
if (channel_number < 0 ||
static_cast<uint32_t>(channel_number) >= channels_.size()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kIndexSizeError,
ExceptionMessages::IndexOutsideRange(
"channelNumber", channel_number, 0,
ExceptionMessages::kInclusiveBound,
static_cast<int32_t>(channels_.size() - 1),
ExceptionMessages::kInclusiveBound));
return;
}
base::span<const float> src = channels_[channel_number].Get()->AsSpan();
base::span<float> dst = destination->AsSpan();
// We don't need to copy anything if a) the buffer offset is past the end of
// the AudioBuffer or b) the internal `Data()` of is a zero-length
// `Float32Array`, which can result a nullptr.
if (buffer_offset >= src.size() || dst.size() <= 0) {
return;
}
size_t count = std::min(dst.size(), src.size() - buffer_offset);
DCHECK(src.data());
DCHECK(dst.data());
dst.first(count).copy_from(src.subspan(buffer_offset, count));
}
void AudioBuffer::copyToChannel(NotShared<DOMFloat32Array> source,
int32_t channel_number,
ExceptionState& exception_state) {
return copyToChannel(source, channel_number, 0, exception_state);
}
void AudioBuffer::copyToChannel(NotShared<DOMFloat32Array> source,
int32_t channel_number,
size_t buffer_offset,
ExceptionState& exception_state) {
if (!source->length()) {
return;
}
if (channel_number < 0 ||
static_cast<uint32_t>(channel_number) >= channels_.size()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kIndexSizeError,
ExceptionMessages::IndexOutsideRange(
"channelNumber", channel_number, 0,
ExceptionMessages::kInclusiveBound,
static_cast<int32_t>(channels_.size() - 1),
ExceptionMessages::kInclusiveBound));
return;
}
base::span<float> dst = channels_[channel_number].Get()->AsSpan();
if (buffer_offset >= dst.size()) {
// Nothing to copy if the buffer offset is past the end of the AudioBuffer.
return;
}
size_t count = dst.size() - buffer_offset;
base::span<const float> src = source->AsSpan();
count = std::min(src.size(), count);
DCHECK(src.data());
DCHECK(dst.data());
dst.subspan(buffer_offset, count).copy_from(src.first(count));
}
void AudioBuffer::Zero() {
for (unsigned i = 0; i < channels_.size(); ++i) {
if (NotShared<DOMFloat32Array> array = getChannelData(i)) {
float* data = array->Data();
UNSAFE_TODO(memset(data, 0, length() * sizeof(*data)));
}
}
}
std::unique_ptr<SharedAudioBuffer> AudioBuffer::CreateSharedAudioBuffer() {
return std::make_unique<SharedAudioBuffer>(this);
}
SharedAudioBuffer::SharedAudioBuffer(AudioBuffer* buffer)
: sample_rate_(buffer->sampleRate()), length_(buffer->length()) {
channels_.resize(buffer->numberOfChannels());
for (unsigned int i = 0; i < buffer->numberOfChannels(); ++i) {
buffer->getChannelData(i)->buffer()->ShareNonSharedForInternalUse(
channels_[i]);
}
}
void SharedAudioBuffer::Zero() {
for (auto& channel : channels_) {
float* data = static_cast<float*>(channel.Data());
UNSAFE_TODO(memset(data, 0, length() * sizeof(*data)));
}
}
} // namespace blink