blob: 1fae6a5b1b6e286d7a8ecc3077354d368bf36de0 [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/platform/audio/audio_bus.h"
#include <assert.h>
#include <math.h>
#include <algorithm>
#include <array>
#include <memory>
#include <utility>
#include "base/compiler_specific.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_audio_bus.h"
#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
#include "third_party/blink/renderer/platform/audio/sinc_resampler.h"
#include "third_party/blink/renderer/platform/audio/vector_math.h"
#include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
namespace blink {
using vector_math::Vadd;
using vector_math::Vsma;
const unsigned kMaxBusChannels = 32;
scoped_refptr<AudioBus> AudioBus::Create(unsigned number_of_channels,
uint32_t length,
bool allocate) {
DCHECK_LE(number_of_channels, kMaxBusChannels);
if (number_of_channels > kMaxBusChannels) {
return nullptr;
}
return base::AdoptRef(new AudioBus(number_of_channels, length, allocate));
}
AudioBus::AudioBus(unsigned number_of_channels, uint32_t length, bool allocate)
: length_(length), sample_rate_(0) {
channels_.ReserveInitialCapacity(number_of_channels);
for (unsigned i = 0; i < number_of_channels; ++i) {
if (allocate) {
channels_.emplace_back(length);
} else {
channels_.emplace_back(nullptr, length);
}
}
layout_ = kLayoutCanonical; // for now this is the only layout we define
}
void AudioBus::SetChannelMemory(unsigned channel_index,
float* storage,
uint32_t length) {
if (channel_index < channels_.size()) {
Channel(channel_index)->Set(storage, length);
// FIXME: verify that this length matches all the other channel lengths
length_ = length;
}
}
void AudioBus::ResizeSmaller(uint32_t new_length) {
DCHECK_LE(new_length, length_);
if (new_length <= length_) {
length_ = new_length;
}
for (AudioChannel& channel : channels_) {
channel.ResizeSmaller(new_length);
}
}
void AudioBus::Zero() {
for (AudioChannel& channel : channels_) {
channel.Zero();
}
}
AudioChannel* AudioBus::ChannelByType(unsigned channel_type) {
// For now we only support canonical channel layouts...
if (layout_ != kLayoutCanonical) {
return nullptr;
}
switch (NumberOfChannels()) {
case 1: // mono
if (channel_type == kChannelMono || channel_type == kChannelLeft) {
return Channel(0);
}
return nullptr;
case 2: // stereo
switch (channel_type) {
case kChannelLeft:
return Channel(0);
case kChannelRight:
return Channel(1);
default:
return nullptr;
}
case 4: // quad
switch (channel_type) {
case kChannelLeft:
return Channel(0);
case kChannelRight:
return Channel(1);
case kChannelSurroundLeft:
return Channel(2);
case kChannelSurroundRight:
return Channel(3);
default:
return nullptr;
}
case 5: // 5.0
switch (channel_type) {
case kChannelLeft:
return Channel(0);
case kChannelRight:
return Channel(1);
case kChannelCenter:
return Channel(2);
case kChannelSurroundLeft:
return Channel(3);
case kChannelSurroundRight:
return Channel(4);
default:
return nullptr;
}
case 6: // 5.1
switch (channel_type) {
case kChannelLeft:
return Channel(0);
case kChannelRight:
return Channel(1);
case kChannelCenter:
return Channel(2);
case kChannelLFE:
return Channel(3);
case kChannelSurroundLeft:
return Channel(4);
case kChannelSurroundRight:
return Channel(5);
default:
return nullptr;
}
}
NOTREACHED();
}
const AudioChannel* AudioBus::ChannelByType(unsigned type) const {
return const_cast<AudioBus*>(this)->ChannelByType(type);
}
// Returns true if the channel count and frame-size match.
bool AudioBus::TopologyMatches(const AudioBus& bus) const {
if (NumberOfChannels() != bus.NumberOfChannels()) {
return false; // channel mismatch
}
// Make sure source bus has enough frames.
if (length() > bus.length()) {
return false; // frame-size mismatch
}
return true;
}
scoped_refptr<AudioBus> AudioBus::CreateBufferFromRange(
const AudioBus* source_buffer,
unsigned start_frame,
unsigned end_frame) {
uint32_t number_of_source_frames = source_buffer->length();
unsigned number_of_channels = source_buffer->NumberOfChannels();
// Sanity checking
bool is_range_safe =
start_frame < end_frame && end_frame <= number_of_source_frames;
DCHECK(is_range_safe);
if (!is_range_safe) {
return nullptr;
}
uint32_t range_length = end_frame - start_frame;
scoped_refptr<AudioBus> audio_bus = Create(number_of_channels, range_length);
audio_bus->SetSampleRate(source_buffer->SampleRate());
for (unsigned i = 0; i < number_of_channels; ++i) {
audio_bus->Channel(i)->CopyFromRange(source_buffer->Channel(i), start_frame,
end_frame);
}
return audio_bus;
}
float AudioBus::MaxAbsValue() const {
float max = 0.0f;
for (unsigned i = 0; i < NumberOfChannels(); ++i) {
const AudioChannel* channel = Channel(i);
max = std::max(max, channel->MaxAbsValue());
}
return max;
}
void AudioBus::Normalize() {
float max = MaxAbsValue();
if (max) {
Scale(1.0f / max);
}
}
void AudioBus::Scale(float scale) {
for (unsigned i = 0; i < NumberOfChannels(); ++i) {
Channel(i)->Scale(scale);
}
}
void AudioBus::CopyFrom(const AudioBus& source_bus,
ChannelInterpretation channel_interpretation) {
if (&source_bus == this) {
return;
}
// Copying bus is equivalent to zeroing and then summing.
Zero();
SumFrom(source_bus, channel_interpretation);
}
void AudioBus::SumFrom(const AudioBus& source_bus,
ChannelInterpretation channel_interpretation) {
if (&source_bus == this) {
return;
}
unsigned number_of_source_channels = source_bus.NumberOfChannels();
unsigned number_of_destination_channels = NumberOfChannels();
// If the channel numbers are equal, perform channels-wise summing.
if (number_of_source_channels == number_of_destination_channels) {
for (unsigned i = 0; i < number_of_source_channels; ++i) {
Channel(i)->SumFrom(source_bus.Channel(i));
}
return;
}
// Otherwise perform up/down-mix or the discrete transfer based on the
// number of channels and the channel interpretation.
switch (channel_interpretation) {
case kSpeakers:
if (number_of_source_channels < number_of_destination_channels) {
SumFromByUpMixing(source_bus);
} else {
SumFromByDownMixing(source_bus);
}
break;
case kDiscrete:
DiscreteSumFrom(source_bus);
break;
}
}
void AudioBus::DiscreteSumFrom(const AudioBus& source_bus) {
unsigned number_of_source_channels = source_bus.NumberOfChannels();
unsigned number_of_destination_channels = NumberOfChannels();
if (number_of_destination_channels < number_of_source_channels) {
// Down-mix by summing channels and dropping the remaining.
for (unsigned i = 0; i < number_of_destination_channels; ++i) {
Channel(i)->SumFrom(source_bus.Channel(i));
}
} else if (number_of_destination_channels > number_of_source_channels) {
// Up-mix by summing as many channels as we have.
for (unsigned i = 0; i < number_of_source_channels; ++i) {
Channel(i)->SumFrom(source_bus.Channel(i));
}
}
}
void AudioBus::SumFromByUpMixing(const AudioBus& source_bus) {
unsigned number_of_source_channels = source_bus.NumberOfChannels();
unsigned number_of_destination_channels = NumberOfChannels();
if ((number_of_source_channels == 1 && number_of_destination_channels == 2) ||
(number_of_source_channels == 1 && number_of_destination_channels == 4)) {
// Up-mixing: 1 -> 2, 1 -> 4
// output.L = input
// output.R = input
// output.SL = 0 (in the case of 1 -> 4)
// output.SR = 0 (in the case of 1 -> 4)
const AudioChannel* source_l = source_bus.ChannelByType(kChannelLeft);
ChannelByType(kChannelLeft)->SumFrom(source_l);
ChannelByType(kChannelRight)->SumFrom(source_l);
} else if (number_of_source_channels == 1 &&
number_of_destination_channels == 6) {
// Up-mixing: 1 -> 5.1
// output.L = 0
// output.R = 0
// output.C = input (put in center channel)
// output.LFE = 0
// output.SL = 0
// output.SR = 0
ChannelByType(kChannelCenter)
->SumFrom(source_bus.ChannelByType(kChannelLeft));
} else if ((number_of_source_channels == 2 &&
number_of_destination_channels == 4) ||
(number_of_source_channels == 2 &&
number_of_destination_channels == 6)) {
// Up-mixing: 2 -> 4, 2 -> 5.1
// output.L = input.L
// output.R = input.R
// output.C = 0 (in the case of 2 -> 5.1)
// output.LFE = 0 (in the case of 2 -> 5.1)
// output.SL = 0
// output.SR = 0
ChannelByType(kChannelLeft)
->SumFrom(source_bus.ChannelByType(kChannelLeft));
ChannelByType(kChannelRight)
->SumFrom(source_bus.ChannelByType(kChannelRight));
} else if (number_of_source_channels == 4 &&
number_of_destination_channels == 6) {
// Up-mixing: 4 -> 5.1
// output.L = input.L
// output.R = input.R
// output.C = 0
// output.LFE = 0
// output.SL = input.SL
// output.SR = input.SR
ChannelByType(kChannelLeft)
->SumFrom(source_bus.ChannelByType(kChannelLeft));
ChannelByType(kChannelRight)
->SumFrom(source_bus.ChannelByType(kChannelRight));
ChannelByType(kChannelSurroundLeft)
->SumFrom(source_bus.ChannelByType(kChannelSurroundLeft));
ChannelByType(kChannelSurroundRight)
->SumFrom(source_bus.ChannelByType(kChannelSurroundRight));
} else {
// All other cases, fall back to the discrete sum. This will silence the
// excessive channels.
DiscreteSumFrom(source_bus);
}
}
void AudioBus::SumFromByDownMixing(const AudioBus& source_bus) {
unsigned number_of_source_channels = source_bus.NumberOfChannels();
unsigned number_of_destination_channels = NumberOfChannels();
if (number_of_source_channels == 2 && number_of_destination_channels == 1) {
// Down-mixing: 2 -> 1
// output = 0.5 * (input.L + input.R)
const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
float* destination = ChannelByType(kChannelLeft)->MutableData();
float scale = 0.5;
Vsma(source_l, 1, &scale, destination, 1, length());
Vsma(source_r, 1, &scale, destination, 1, length());
} else if (number_of_source_channels == 4 &&
number_of_destination_channels == 1) {
// Down-mixing: 4 -> 1
// output = 0.25 * (input.L + input.R + input.SL + input.SR)
const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
const float* source_sl =
source_bus.ChannelByType(kChannelSurroundLeft)->Data();
const float* source_sr =
source_bus.ChannelByType(kChannelSurroundRight)->Data();
float* destination = ChannelByType(kChannelLeft)->MutableData();
float scale = 0.25;
Vsma(source_l, 1, &scale, destination, 1, length());
Vsma(source_r, 1, &scale, destination, 1, length());
Vsma(source_sl, 1, &scale, destination, 1, length());
Vsma(source_sr, 1, &scale, destination, 1, length());
} else if (number_of_source_channels == 6 &&
number_of_destination_channels == 1) {
// Down-mixing: 5.1 -> 1
// output = sqrt(1/2) * (input.L + input.R) + input.C
// + 0.5 * (input.SL + input.SR)
const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
const float* source_c = source_bus.ChannelByType(kChannelCenter)->Data();
const float* source_sl =
source_bus.ChannelByType(kChannelSurroundLeft)->Data();
const float* source_sr =
source_bus.ChannelByType(kChannelSurroundRight)->Data();
float* destination = ChannelByType(kChannelLeft)->MutableData();
float scale_sqrt_half = sqrtf(0.5);
float scale_half = 0.5;
Vsma(source_l, 1, &scale_sqrt_half, destination, 1, length());
Vsma(source_r, 1, &scale_sqrt_half, destination, 1, length());
Vadd(source_c, 1, destination, 1, destination, 1, length());
Vsma(source_sl, 1, &scale_half, destination, 1, length());
Vsma(source_sr, 1, &scale_half, destination, 1, length());
} else if (number_of_source_channels == 4 &&
number_of_destination_channels == 2) {
// Down-mixing: 4 -> 2
// output.L = 0.5 * (input.L + input.SL)
// output.R = 0.5 * (input.R + input.SR)
const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
const float* source_sl =
source_bus.ChannelByType(kChannelSurroundLeft)->Data();
const float* source_sr =
source_bus.ChannelByType(kChannelSurroundRight)->Data();
float* destination_l = ChannelByType(kChannelLeft)->MutableData();
float* destination_r = ChannelByType(kChannelRight)->MutableData();
float scale_half = 0.5;
Vsma(source_l, 1, &scale_half, destination_l, 1, length());
Vsma(source_sl, 1, &scale_half, destination_l, 1, length());
Vsma(source_r, 1, &scale_half, destination_r, 1, length());
Vsma(source_sr, 1, &scale_half, destination_r, 1, length());
} else if (number_of_source_channels == 6 &&
number_of_destination_channels == 2) {
// Down-mixing: 5.1 -> 2
// output.L = input.L + sqrt(1/2) * (input.C + input.SL)
// output.R = input.R + sqrt(1/2) * (input.C + input.SR)
const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
const float* source_c = source_bus.ChannelByType(kChannelCenter)->Data();
const float* source_sl =
source_bus.ChannelByType(kChannelSurroundLeft)->Data();
const float* source_sr =
source_bus.ChannelByType(kChannelSurroundRight)->Data();
float* destination_l = ChannelByType(kChannelLeft)->MutableData();
float* destination_r = ChannelByType(kChannelRight)->MutableData();
float scale_sqrt_half = sqrtf(0.5);
Vadd(source_l, 1, destination_l, 1, destination_l, 1, length());
Vsma(source_c, 1, &scale_sqrt_half, destination_l, 1, length());
Vsma(source_sl, 1, &scale_sqrt_half, destination_l, 1, length());
Vadd(source_r, 1, destination_r, 1, destination_r, 1, length());
Vsma(source_c, 1, &scale_sqrt_half, destination_r, 1, length());
Vsma(source_sr, 1, &scale_sqrt_half, destination_r, 1, length());
} else if (number_of_source_channels == 6 &&
number_of_destination_channels == 4) {
// Down-mixing: 5.1 -> 4
// output.L = input.L + sqrt(1/2) * input.C
// output.R = input.R + sqrt(1/2) * input.C
// output.SL = input.SL
// output.SR = input.SR
const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
const float* source_c = source_bus.ChannelByType(kChannelCenter)->Data();
float* destination_l = ChannelByType(kChannelLeft)->MutableData();
float* destination_r = ChannelByType(kChannelRight)->MutableData();
float scale_sqrt_half = sqrtf(0.5);
Vadd(source_l, 1, destination_l, 1, destination_l, 1, length());
Vsma(source_c, 1, &scale_sqrt_half, destination_l, 1, length());
Vadd(source_r, 1, destination_r, 1, destination_r, 1, length());
Vsma(source_c, 1, &scale_sqrt_half, destination_r, 1, length());
Channel(2)->SumFrom(source_bus.Channel(4));
Channel(3)->SumFrom(source_bus.Channel(5));
} else {
// All other cases, fall back to the discrete sum. This will perform
// channel-wise sum until the destination channels run out.
DiscreteSumFrom(source_bus);
}
}
void AudioBus::CopyWithGainFrom(const AudioBus& source_bus, float gain) {
if (!TopologyMatches(source_bus)) {
NOTREACHED();
}
if (source_bus.IsSilent()) {
Zero();
return;
}
unsigned number_of_channels = NumberOfChannels();
DCHECK_LE(number_of_channels, kMaxBusChannels);
if (number_of_channels > kMaxBusChannels) {
return;
}
// If it is copying from the same bus and no need to change gain, just return.
if (this == &source_bus && gain == 1) {
return;
}
std::array<const float*, kMaxBusChannels> sources;
std::array<float*, kMaxBusChannels> destinations;
for (unsigned i = 0; i < number_of_channels; ++i) {
sources[i] = source_bus.Channel(i)->Data();
destinations[i] = Channel(i)->MutableData();
}
unsigned frames_to_process = length();
// Handle gains of 0 and 1 (exactly) specially.
if (gain == 1) {
for (unsigned channel_index = 0; channel_index < number_of_channels;
++channel_index) {
UNSAFE_TODO(
memcpy(destinations[channel_index], sources[channel_index],
frames_to_process * sizeof(*destinations[channel_index])));
}
} else if (gain == 0) {
for (unsigned channel_index = 0; channel_index < number_of_channels;
++channel_index) {
UNSAFE_TODO(
memset(destinations[channel_index], 0,
frames_to_process * sizeof(*destinations[channel_index])));
}
} else {
for (unsigned channel_index = 0; channel_index < number_of_channels;
++channel_index) {
vector_math::Vsmul(sources[channel_index], 1, &gain,
destinations[channel_index], 1, frames_to_process);
}
}
}
void AudioBus::CopyWithSampleAccurateGainValuesFrom(
const AudioBus& source_bus,
float* gain_values,
unsigned number_of_gain_values) {
// Make sure we're processing from the same type of bus.
// We *are* able to process from mono -> stereo
if (source_bus.NumberOfChannels() != 1 && !TopologyMatches(source_bus)) {
NOTREACHED();
}
if (!gain_values || number_of_gain_values > source_bus.length()) {
NOTREACHED();
}
if (source_bus.length() == number_of_gain_values &&
source_bus.length() == length() && source_bus.IsSilent()) {
Zero();
return;
}
// We handle both the 1 -> N and N -> N case here.
const float* source = source_bus.Channel(0)->Data();
for (unsigned channel_index = 0; channel_index < NumberOfChannels();
++channel_index) {
if (source_bus.NumberOfChannels() == NumberOfChannels()) {
source = source_bus.Channel(channel_index)->Data();
}
float* destination = Channel(channel_index)->MutableData();
vector_math::Vmul(source, 1, gain_values, 1, destination, 1,
number_of_gain_values);
}
}
scoped_refptr<AudioBus> AudioBus::CreateBySampleRateConverting(
const AudioBus* source_bus,
bool mix_to_mono,
double new_sample_rate) {
// sourceBus's sample-rate must be known.
DCHECK(source_bus);
DCHECK(source_bus->SampleRate());
if (!source_bus || !source_bus->SampleRate()) {
return nullptr;
}
double source_sample_rate = source_bus->SampleRate();
double destination_sample_rate = new_sample_rate;
double sample_rate_ratio = source_sample_rate / destination_sample_rate;
unsigned number_of_source_channels = source_bus->NumberOfChannels();
if (number_of_source_channels == 1) {
mix_to_mono = false; // already mono
}
if (source_sample_rate == destination_sample_rate) {
// No sample-rate conversion is necessary.
if (mix_to_mono) {
return AudioBus::CreateByMixingToMono(source_bus);
}
// Return exact copy.
return AudioBus::CreateBufferFromRange(source_bus, 0, source_bus->length());
}
if (source_bus->IsSilent()) {
scoped_refptr<AudioBus> silent_bus = Create(
number_of_source_channels, source_bus->length() / sample_rate_ratio);
silent_bus->SetSampleRate(new_sample_rate);
return silent_bus;
}
// First, mix to mono (if necessary) then sample-rate convert.
const AudioBus* resampler_source_bus;
scoped_refptr<AudioBus> mixed_mono_bus;
if (mix_to_mono) {
mixed_mono_bus = AudioBus::CreateByMixingToMono(source_bus);
resampler_source_bus = mixed_mono_bus.get();
} else {
// Directly resample without down-mixing.
resampler_source_bus = source_bus;
}
// Calculate destination length based on the sample-rates.
int source_length = resampler_source_bus->length();
int destination_length = source_length / sample_rate_ratio;
// Create destination bus with same number of channels.
unsigned number_of_destination_channels =
resampler_source_bus->NumberOfChannels();
scoped_refptr<AudioBus> destination_bus =
Create(number_of_destination_channels, destination_length);
// Sample-rate convert each channel.
for (unsigned i = 0; i < number_of_destination_channels; ++i) {
const float* source = resampler_source_bus->Channel(i)->Data();
float* destination = destination_bus->Channel(i)->MutableData();
SincResampler resampler(sample_rate_ratio);
resampler.Process(source, destination, source_length);
}
destination_bus->ClearSilentFlag();
destination_bus->SetSampleRate(new_sample_rate);
return destination_bus;
}
scoped_refptr<AudioBus> AudioBus::CreateByMixingToMono(
const AudioBus* source_bus) {
if (source_bus->IsSilent()) {
return Create(1, source_bus->length());
}
switch (source_bus->NumberOfChannels()) {
case 1:
// Simply create an exact copy.
return AudioBus::CreateBufferFromRange(source_bus, 0,
source_bus->length());
case 2: {
unsigned n = source_bus->length();
scoped_refptr<AudioBus> destination_bus = Create(1, n);
const float* source_l = source_bus->Channel(0)->Data();
const float* source_r = source_bus->Channel(1)->Data();
float* destination = destination_bus->Channel(0)->MutableData();
// Do the mono mixdown.
for (unsigned i = 0; i < n; ++i) {
UNSAFE_TODO(destination[i]) =
(UNSAFE_TODO(source_l[i]) + UNSAFE_TODO(source_r[i])) / 2;
}
destination_bus->ClearSilentFlag();
destination_bus->SetSampleRate(source_bus->SampleRate());
return destination_bus;
}
}
NOTREACHED();
}
bool AudioBus::IsSilent() const {
return std::ranges::all_of(channels_, &AudioChannel::IsSilent);
}
void AudioBus::ClearSilentFlag() {
for (AudioChannel& channel : channels_) {
channel.ClearSilentFlag();
}
}
scoped_refptr<AudioBus> DecodeAudioFileData(base::span<const char> data) {
WebAudioBus web_audio_bus;
if (Platform::Current()->DecodeAudioFileData(&web_audio_bus, data)) {
return web_audio_bus.Release();
}
return nullptr;
}
scoped_refptr<AudioBus> AudioBus::GetDataResource(int resource_id,
float sample_rate) {
const WebData& resource = Platform::Current()->GetDataResource(resource_id);
if (resource.IsEmpty()) {
return nullptr;
}
// Currently, the only client of this method is caching the result -- so
// it's reasonable to (potentially) pay a one-time flat access cost.
// If this becomes problematic, we'll have the refactor DecodeAudioFileData
// to take WebData and use segmented access.
SegmentedBuffer::DeprecatedFlatData flat_data(
resource.operator scoped_refptr<SharedBuffer>().get());
scoped_refptr<AudioBus> audio_bus = DecodeAudioFileData(flat_data);
if (!audio_bus.get()) {
return nullptr;
}
// If the bus is already at the requested sample-rate then return as is.
if (audio_bus->SampleRate() == sample_rate) {
return audio_bus;
}
return AudioBus::CreateBySampleRateConverting(audio_bus.get(), false,
sample_rate);
}
scoped_refptr<AudioBus> AudioBus::CreateBusFromInMemoryAudioFile(
base::span<const uint8_t> data,
bool mix_to_mono,
float sample_rate) {
scoped_refptr<AudioBus> audio_bus = DecodeAudioFileData(base::as_chars(data));
if (!audio_bus.get()) {
return nullptr;
}
// If the bus needs no conversion then return as is.
if ((!mix_to_mono || audio_bus->NumberOfChannels() == 1) &&
audio_bus->SampleRate() == sample_rate) {
return audio_bus;
}
return AudioBus::CreateBySampleRateConverting(audio_bus.get(), mix_to_mono,
sample_rate);
}
} // namespace blink