| /* |
| * Copyright 2015 Google Inc. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "media/base/shell_audio_bus.h" |
| |
| #include <algorithm> |
| #include <limits> |
| |
| namespace media { |
| |
| namespace { |
| |
| const float kFloat32ToInt16Factor = 32768.f; |
| |
| inline void ConvertSample(ShellAudioBus::SampleType src_type, |
| const uint8* src_ptr, |
| ShellAudioBus::SampleType dest_type, |
| uint8* dest_ptr) { |
| if (src_type == dest_type) { |
| memcpy(dest_ptr, src_ptr, |
| src_type == ShellAudioBus::kInt16 ? sizeof(int16) : sizeof(float)); |
| } else if (src_type == ShellAudioBus::kFloat32) { |
| float sample_in_float = *reinterpret_cast<const float*>(src_ptr); |
| int32 sample_in_int32 = |
| static_cast<int32>(sample_in_float * kFloat32ToInt16Factor); |
| sample_in_int32 = |
| std::max<int32>(sample_in_int32, std::numeric_limits<int16>::min()); |
| sample_in_int32 = |
| std::min<int32>(sample_in_int32, std::numeric_limits<int16>::max()); |
| *reinterpret_cast<int16*>(dest_ptr) = static_cast<int16>(sample_in_int32); |
| } else { |
| int16 sample = *reinterpret_cast<const int16*>(src_ptr); |
| *reinterpret_cast<float*>(dest_ptr) = |
| static_cast<float>(sample) / kFloat32ToInt16Factor; |
| } |
| } |
| |
| } // namespace |
| |
| ShellAudioBus::ShellAudioBus(size_t channels, size_t frames, |
| SampleType sample_type, StorageType storage_type) |
| : channels_(channels), |
| frames_(frames), |
| sample_type_(sample_type), |
| storage_type_(storage_type) { |
| DCHECK_GT(channels_, 0); |
| |
| if (storage_type_ == kInterleaved) { |
| data_.reset(static_cast<uint8*>(base::AlignedAlloc( |
| GetSampleSizeInBytes() * frames * channels, kChannelAlignmentInBytes))); |
| channel_data_.push_back(data_.get()); |
| } else { |
| DCHECK_EQ(storage_type_, kPlanar); |
| size_t aligned_per_channel_size_in_bytes = |
| (GetSampleSizeInBytes() * frames + kChannelAlignmentInBytes - 1) / |
| kChannelAlignmentInBytes * kChannelAlignmentInBytes; |
| data_.reset(static_cast<uint8*>( |
| base::AlignedAlloc(aligned_per_channel_size_in_bytes * channels, |
| kChannelAlignmentInBytes))); |
| channel_data_.reserve(channels); |
| for (size_t i = 0; i < channels_; ++i) { |
| channel_data_.push_back(data_.get() + |
| aligned_per_channel_size_in_bytes * i); |
| } |
| } |
| } |
| |
| ShellAudioBus::ShellAudioBus(size_t frames, const std::vector<float*>& samples) |
| : channels_(samples.size()), |
| frames_(frames), |
| sample_type_(kFloat32), |
| storage_type_(kPlanar) { |
| DCHECK_GT(channels_, 0); |
| |
| channel_data_.reserve(samples.size()); |
| for (size_t i = 0; i < samples.size(); ++i) { |
| channel_data_.push_back(reinterpret_cast<uint8*>(samples[i])); |
| } |
| } |
| |
| ShellAudioBus::ShellAudioBus(size_t channels, size_t frames, float* samples) |
| : channels_(channels), |
| frames_(frames), |
| sample_type_(kFloat32), |
| storage_type_(kInterleaved) { |
| DCHECK_GT(channels_, 0); |
| |
| channel_data_.push_back(reinterpret_cast<uint8*>(samples)); |
| } |
| |
| ShellAudioBus::ShellAudioBus(size_t frames, const std::vector<int16*>& samples) |
| : channels_(samples.size()), |
| frames_(frames), |
| sample_type_(kInt16), |
| storage_type_(kPlanar) { |
| DCHECK_GT(channels_, 0); |
| |
| channel_data_.reserve(samples.size()); |
| for (size_t i = 0; i < samples.size(); ++i) { |
| channel_data_.push_back(reinterpret_cast<uint8*>(samples[i])); |
| } |
| } |
| |
| ShellAudioBus::ShellAudioBus(size_t channels, size_t frames, int16* samples) |
| : channels_(channels), |
| frames_(frames), |
| sample_type_(kInt16), |
| storage_type_(kInterleaved) { |
| DCHECK_GT(channels_, 0); |
| |
| channel_data_.push_back(reinterpret_cast<uint8*>(samples)); |
| } |
| |
| size_t ShellAudioBus::GetSampleSizeInBytes() const { |
| if (sample_type_ == kInt16) { |
| return sizeof(int16); |
| } |
| DCHECK_EQ(sample_type_, kFloat32); |
| return sizeof(float); |
| } |
| |
| const uint8* ShellAudioBus::interleaved_data() const { |
| DCHECK_EQ(storage_type_, kInterleaved); |
| return channel_data_[0]; |
| } |
| |
| const uint8* ShellAudioBus::planar_data(size_t channel) const { |
| DCHECK_LT(channel, channels_); |
| DCHECK_EQ(storage_type_, kPlanar); |
| return channel_data_[channel]; |
| } |
| |
| void ShellAudioBus::ZeroFrames(size_t start_frame, size_t end_frame) { |
| DCHECK_LE(start_frame, end_frame); |
| DCHECK_LE(end_frame, frames_); |
| end_frame = std::min(end_frame, frames_); |
| start_frame = std::min(start_frame, end_frame); |
| if (start_frame >= end_frame) { |
| return; |
| } |
| if (storage_type_ == kInterleaved) { |
| memset(GetSamplePtr(0, start_frame), 0, |
| GetSampleSizeInBytes() * (end_frame - start_frame) * channels_); |
| } else { |
| for (size_t channel = 0; channel < channels_; ++channel) { |
| memset(GetSamplePtr(channel, start_frame), 0, |
| GetSampleSizeInBytes() * (end_frame - start_frame)); |
| } |
| } |
| } |
| |
| void ShellAudioBus::Assign(const ShellAudioBus& source) { |
| DCHECK_EQ(channels_, source.channels_); |
| if (channels_ != source.channels_) { |
| ZeroAllFrames(); |
| return; |
| } |
| |
| if (sample_type_ == source.sample_type_ && |
| storage_type_ == source.storage_type_) { |
| size_t frames = std::min(frames_, source.frames_); |
| if (storage_type_ == kInterleaved) { |
| memcpy(GetSamplePtr(0, 0), source.GetSamplePtr(0, 0), |
| GetSampleSizeInBytes() * frames * channels_); |
| } else { |
| for (size_t channel = 0; channel < channels_; ++channel) { |
| memcpy(GetSamplePtr(channel, 0), source.GetSamplePtr(channel, 0), |
| GetSampleSizeInBytes() * frames); |
| } |
| } |
| return; |
| } |
| |
| size_t frames = std::min(frames_, source.frames_); |
| for (size_t channel = 0; channel < channels_; ++channel) { |
| for (size_t frame = 0; frame < frames; ++frame) { |
| ConvertSample(source.sample_type_, source.GetSamplePtr(channel, frame), |
| sample_type_, GetSamplePtr(channel, frame)); |
| } |
| } |
| } |
| |
| void ShellAudioBus::Assign(const ShellAudioBus& source, |
| const std::vector<float>& matrix) { |
| DCHECK_EQ(channels() * source.channels(), matrix.size()); |
| DCHECK_EQ(sample_type_, kFloat32); |
| DCHECK_EQ(source.sample_type_, kFloat32); |
| if (channels() * source.channels() != matrix.size() || |
| sample_type_ != kFloat32 || source.sample_type_ != kFloat32) { |
| ZeroAllFrames(); |
| return; |
| } |
| |
| size_t frames = std::min(frames_, source.frames_); |
| for (size_t dest_channel = 0; dest_channel < channels_; ++dest_channel) { |
| for (size_t frame = 0; frame < frames; ++frame) { |
| float mixed_sample = 0.f; |
| for (size_t src_channel = 0; src_channel < source.channels_; |
| ++src_channel) { |
| mixed_sample += source.GetFloat32Sample(src_channel, frame) * |
| matrix[dest_channel * source.channels_ + src_channel]; |
| } |
| SetFloat32Sample(dest_channel, frame, mixed_sample); |
| } |
| } |
| } |
| |
| template <ShellAudioBus::StorageType T> |
| inline uint8* ShellAudioBus::GetSamplePtrForType(size_t channel, |
| size_t frame) const { |
| DCHECK_LT(channel, channels_); |
| DCHECK_LT(frame, frames_); |
| |
| if (T == kInterleaved) { |
| return channel_data_[0] + sizeof(float) * (channels_ * frame + channel); |
| } else if (T == kPlanar) { |
| return channel_data_[channel] + sizeof(float) * frame; |
| } else { |
| NOTREACHED(); |
| } |
| |
| return NULL; |
| } |
| |
| template <ShellAudioBus::StorageType T> |
| inline float ShellAudioBus::GetFloat32SampleForType(size_t channel, |
| size_t frame) const { |
| return *reinterpret_cast<const float*>( |
| GetSamplePtrForType<T>(channel, frame)); |
| } |
| |
| template <ShellAudioBus::StorageType SourceStorageType, |
| ShellAudioBus::StorageType DestStorageType> |
| void ShellAudioBus::MixForType(const ShellAudioBus& source) { |
| const size_t frames = std::min(frames_, source.frames_); |
| |
| for (size_t channel = 0; channel < channels_; ++channel) { |
| for (size_t frame = 0; frame < frames; ++frame) { |
| *reinterpret_cast<float*>( |
| GetSamplePtrForType<DestStorageType>(channel, frame)) += |
| source.GetFloat32SampleForType<SourceStorageType>(channel, frame); |
| } |
| } |
| } |
| |
| void ShellAudioBus::Mix(const ShellAudioBus& source) { |
| DCHECK_EQ(channels_, source.channels_); |
| DCHECK_EQ(sample_type_, kFloat32); |
| DCHECK_EQ(source.sample_type_, kFloat32); |
| if (channels_ != source.channels_ || sample_type_ != kFloat32 || |
| source.sample_type_ != kFloat32) { |
| ZeroAllFrames(); |
| return; |
| } |
| |
| // Profiling has identified this area of code as hot, so instead of calling |
| // GetSamplePtr, which branches on storage_type_ each time it is called, we |
| // branch once before we loop and inline the branch of the function we want. |
| DCHECK_EQ(GetSampleSizeInBytes(), sizeof(float)); |
| if (source.storage_type_ == kInterleaved && storage_type_ == kInterleaved) { |
| MixForType<kInterleaved, kInterleaved>(source); |
| } else if (source.storage_type_ == kInterleaved && storage_type_ == kPlanar) { |
| MixForType<kInterleaved, kPlanar>(source); |
| } else if (source.storage_type_ == kPlanar && storage_type_ == kInterleaved) { |
| MixForType<kPlanar, kInterleaved>(source); |
| } else if (source.storage_type_ == kPlanar && storage_type_ == kPlanar) { |
| MixForType<kPlanar, kPlanar>(source); |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| void ShellAudioBus::Mix(const ShellAudioBus& source, |
| const std::vector<float>& matrix) { |
| DCHECK_EQ(channels() * source.channels(), matrix.size()); |
| DCHECK_EQ(sample_type_, kFloat32); |
| DCHECK_EQ(source.sample_type_, kFloat32); |
| if (channels() * source.channels() != matrix.size() || |
| sample_type_ != kFloat32 || source.sample_type_ != kFloat32) { |
| ZeroAllFrames(); |
| return; |
| } |
| |
| size_t frames = std::min(frames_, source.frames_); |
| for (size_t dest_channel = 0; dest_channel < channels_; ++dest_channel) { |
| for (size_t frame = 0; frame < frames; ++frame) { |
| float mixed_sample = 0.f; |
| for (size_t src_channel = 0; src_channel < source.channels_; |
| ++src_channel) { |
| mixed_sample += source.GetFloat32Sample(src_channel, frame) * |
| matrix[dest_channel * source.channels_ + src_channel]; |
| } |
| mixed_sample += GetFloat32Sample(dest_channel, frame); |
| SetFloat32Sample(dest_channel, frame, mixed_sample); |
| } |
| } |
| } |
| |
| uint8* ShellAudioBus::GetSamplePtr(size_t channel, size_t frame) { |
| DCHECK_LT(channel, channels_); |
| DCHECK_LT(frame, frames_); |
| |
| if (storage_type_ == kInterleaved) { |
| return channel_data_[0] + |
| GetSampleSizeInBytes() * (channels_ * frame + channel); |
| } else { |
| return channel_data_[channel] + GetSampleSizeInBytes() * frame; |
| } |
| } |
| |
| const uint8* ShellAudioBus::GetSamplePtr(size_t channel, size_t frame) const { |
| DCHECK_LT(channel, channels_); |
| DCHECK_LT(frame, frames_); |
| |
| if (storage_type_ == kInterleaved) { |
| return channel_data_[0] + |
| GetSampleSizeInBytes() * (channels_ * frame + channel); |
| } else { |
| return channel_data_[channel] + GetSampleSizeInBytes() * frame; |
| } |
| } |
| |
| } // namespace media |