diff options
Diffstat (limited to 'src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.cc')
-rw-r--r-- | src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.cc | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.cc new file mode 100644 index 000000000..79889fabd --- /dev/null +++ b/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.cc @@ -0,0 +1,495 @@ +/* +Copyright 2018 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 "graph/binaural_surround_renderer_impl.h" + +#include <algorithm> +#include <functional> + +#include "base/misc_math.h" +#include "base/simd_utils.h" +#include "base/spherical_angle.h" +#include "graph/resonance_audio_api_impl.h" +#include "platforms/common/room_effects_utils.h" +#include "platforms/common/room_properties.h" +#include "utils/planar_interleaved_conversion.h" + +namespace vraudio { + +namespace { + +// Maximum number of audio buffers in buffer queue. +const size_t kNumMaxBuffers = 64; + +// Output gain, to avoid clipping of individual virtual speaker channels. +const float kGain = 0.5f; + +} // namespace + +BinauralSurroundRendererImpl::BinauralSurroundRendererImpl( + size_t frames_per_buffer, int sample_rate_hz) + : + resonance_audio_api_(nullptr), + frames_per_buffer_(frames_per_buffer), + sample_rate_hz_(sample_rate_hz), + surround_format_(kInvalid), + num_input_channels_(0), + output_buffer_(kNumStereoChannels, frames_per_buffer), + total_frames_buffered_(0), + num_zero_padded_frames_(0), + output_gain_(1.0f) { +} + +bool BinauralSurroundRendererImpl::Init(SurroundFormat surround_format) { + surround_format_ = surround_format; + num_input_channels_ = + GetExpectedNumChannelsFromSurroundFormat(surround_format); + + temp_planar_buffer_ptrs_.resize(num_input_channels_); + + input_audio_buffer_queue_.reset(new ThreadsafeFifo<AudioBuffer>( + kNumMaxBuffers, AudioBuffer(num_input_channels_, frames_per_buffer_))); + + buffer_partitioner_.reset(new BufferPartitioner( + num_input_channels_, frames_per_buffer_, + std::bind(&BinauralSurroundRendererImpl::BufferPartitionerCallback, this, + std::placeholders::_1))); + + buffer_unpartitioner_.reset(new BufferUnpartitioner( + kNumStereoChannels, frames_per_buffer_, + std::bind(&BinauralSurroundRendererImpl::ProcessBuffer, this))); + + resonance_audio_api_.reset(CreateResonanceAudioApi( + kNumStereoChannels, frames_per_buffer_, sample_rate_hz_)); + + if (surround_format == kSurroundMono || surround_format == kSurroundStereo || + surround_format == kSurroundFiveDotOne || + surround_format == kSurroundSevenDotOne) { + InitializeRoomReverb(); + } + // Initialize rendering mode. + switch (surround_format) { + case kSurroundMono: + InitializeBinauralMono(); + break; + case kSurroundStereo: + InitializeBinauralStereo(); + break; + case kSurroundFiveDotOne: + InitializeBinauralSurround5dot1(); + break; + case kSurroundSevenDotOne: + InitializeBinauralSurround7dot1(); + break; + case kFirstOrderAmbisonics: + case kSecondOrderAmbisonics: + case kThirdOrderAmbisonics: + InitializeAmbisonics(); + break; + case kFirstOrderAmbisonicsWithNonDiegeticStereo: + case kSecondOrderAmbisonicsWithNonDiegeticStereo: + case kThirdOrderAmbisonicsWithNonDiegeticStereo: + InitializeAmbisonicsWithNonDiegeticStereo(); + break; + default: + LOG(FATAL) << "Undefined rendering mode"; + return false; + break; + } + return true; +} + +BinauralSurroundRendererImpl::BinauralSurroundRendererImpl() + : + resonance_audio_api_(nullptr), + frames_per_buffer_(0), + sample_rate_hz_(0), + total_frames_buffered_(0), + num_zero_padded_frames_(0) { +} + +AudioBuffer* BinauralSurroundRendererImpl::BufferPartitionerCallback( + AudioBuffer* processed_buffer) { + if (processed_buffer != nullptr) { + input_audio_buffer_queue_->ReleaseInputObject(processed_buffer); + } + DCHECK(!input_audio_buffer_queue_->Full()); + return input_audio_buffer_queue_->AcquireInputObject(); +} + +void BinauralSurroundRendererImpl::SetStereoSpeakerMode(bool enabled) { + resonance_audio_api_->SetStereoSpeakerMode(enabled); +} + +size_t BinauralSurroundRendererImpl::GetExpectedNumChannelsFromSurroundFormat( + SurroundFormat surround_format) { + switch (surround_format) { + case kSurroundMono: + return kNumMonoChannels; + case kSurroundStereo: + return kNumStereoChannels; + case kSurroundFiveDotOne: + return kNumSurroundFiveDotOneChannels; + case kSurroundSevenDotOne: + return kNumSurroundSevenDotOneChannels; + case kFirstOrderAmbisonics: + return kNumFirstOrderAmbisonicChannels; + case kSecondOrderAmbisonics: + return kNumSecondOrderAmbisonicChannels; + case kThirdOrderAmbisonics: + return kNumThirdOrderAmbisonicChannels; + case kFirstOrderAmbisonicsWithNonDiegeticStereo: + return kNumFirstOrderAmbisonicChannels + kNumStereoChannels; + case kSecondOrderAmbisonicsWithNonDiegeticStereo: + return kNumSecondOrderAmbisonicChannels + kNumStereoChannels; + case kThirdOrderAmbisonicsWithNonDiegeticStereo: + return kNumThirdOrderAmbisonicChannels + kNumStereoChannels; + default: + LOG(FATAL) << "Undefined surround format mode"; + return false; + break; + } + return 0; +} + +void BinauralSurroundRendererImpl::InitializeBinauralMono() { + source_ids_.resize(kNumMonoChannels); + // Front (0 degrees): + source_ids_[0] = CreateSoundObject(0.0f); + output_gain_ = kGain; +} + +void BinauralSurroundRendererImpl::InitializeBinauralStereo() { + + source_ids_.resize(kNumStereoChannels); + // Front left (30 degrees): + source_ids_[0] = CreateSoundObject(30.0f); + // Front right (-30 degrees): + source_ids_[1] = CreateSoundObject(-30.0f); + output_gain_ = kGain; +} + +void BinauralSurroundRendererImpl::InitializeBinauralSurround5dot1() { + source_ids_.resize(kNumSurroundFiveDotOneChannels); + // Left (30 degrees): + source_ids_[0] = CreateSoundObject(30.0f); + // Right (-30 degrees): + source_ids_[1] = CreateSoundObject(-30.0f); + // Center (0 degrees): + source_ids_[2] = CreateSoundObject(0.0f); + // Low frequency effects at front center: + source_ids_[3] = CreateSoundObject(0.0f); + // Left surround (110 degrees): + source_ids_[4] = CreateSoundObject(110.0f); + // Right surround (-110 degrees): + source_ids_[5] = CreateSoundObject(-110.0f); + output_gain_ = kGain; +} + +void BinauralSurroundRendererImpl::InitializeBinauralSurround7dot1() { + source_ids_.resize(kNumSurroundSevenDotOneChannels); + // Left (30 degrees): + source_ids_[0] = CreateSoundObject(30.0f); + // Right (-30 degrees): + source_ids_[1] = CreateSoundObject(-30.0f); + // Center (0 degrees): + source_ids_[2] = CreateSoundObject(0.0f); + // Low frequency effects at front center: + source_ids_[3] = CreateSoundObject(0.0f); + // Left surround 1 (90 degrees): + source_ids_[4] = CreateSoundObject(90.0f); + // Right surround 1 (-90 degrees): + source_ids_[5] = CreateSoundObject(-90.0f); + // Left surround 2 (150 degrees): + source_ids_[6] = CreateSoundObject(150.0f); + // Right surround 2 (-150 degrees): + source_ids_[7] = CreateSoundObject(-150.0f); + output_gain_ = kGain; +} + +void BinauralSurroundRendererImpl::InitializeAmbisonics() { + source_ids_.resize(1); + source_ids_[0] = + resonance_audio_api_->CreateAmbisonicSource(num_input_channels_); +} + +void BinauralSurroundRendererImpl::InitializeAmbisonicsWithNonDiegeticStereo() { + source_ids_.resize(2); + CHECK_GT(num_input_channels_, kNumStereoChannels); + source_ids_[0] = resonance_audio_api_->CreateAmbisonicSource( + num_input_channels_ - kNumStereoChannels); + source_ids_[1] = resonance_audio_api_->CreateStereoSource(kNumStereoChannels); +} + +SourceId BinauralSurroundRendererImpl::CreateSoundObject(float azimuth_deg) { + static const float kZeroElevation = 0.0f; + auto speaker_position = + vraudio::SphericalAngle::FromDegrees(azimuth_deg, kZeroElevation) + .GetWorldPositionOnUnitSphere(); + const SourceId source_id = resonance_audio_api_->CreateSoundObjectSource( + RenderingMode::kBinauralHighQuality); + resonance_audio_api_->SetSourcePosition( + source_id, speaker_position[0], speaker_position[1], speaker_position[2]); + return source_id; +} + +void BinauralSurroundRendererImpl::InitializeRoomReverb() { + // The following settings has been applied based on AESTD1001.1.01-10. + RoomProperties room_properties; + room_properties.dimensions[0] = 9.54f; + room_properties.dimensions[1] = 6.0f; + room_properties.dimensions[2] = 15.12f; + room_properties.reverb_brightness = 0.0f; + room_properties.reflection_scalar = 1.0f; + // Reduce reverb gain to compensate for virtual speakers gain. + room_properties.reverb_gain = output_gain_; + for (size_t i = 0; i < kNumRoomSurfaces; ++i) { + room_properties.material_names[i] = MaterialName::kUniform; + } + resonance_audio_api_->SetReflectionProperties( + ComputeReflectionProperties(room_properties)); + resonance_audio_api_->SetReverbProperties( + ComputeReverbProperties(room_properties)); + resonance_audio_api_->EnableRoomEffects(true); +} + +size_t BinauralSurroundRendererImpl::GetNumAvailableFramesInInputBuffer() + const { + DCHECK_NE(surround_format_, kInvalid); + if (num_zero_padded_frames_ > 0) { + // Zero padded output buffers must be consumed prior to + // |AddInterleavedBuffer| calls; + return 0; + } + if (input_audio_buffer_queue_->Full()) { + return 0; + } + // Subtract two buffers from the available input slots to ensure the buffer + // partitioner can be flushed at any time while keeping an extra buffer + // available in the |buffer_partitioner_| callback for the next incoming data. + const size_t num_frames_available_in_input_slots = + (kNumMaxBuffers - input_audio_buffer_queue_->Size() - 2) * + frames_per_buffer_; + DCHECK_GT(frames_per_buffer_, buffer_partitioner_->GetNumBufferedFrames()); + const size_t num_frames_available_in_buffer_partitioner = + frames_per_buffer_ - buffer_partitioner_->GetNumBufferedFrames(); + return num_frames_available_in_input_slots + + num_frames_available_in_buffer_partitioner; +} + +size_t BinauralSurroundRendererImpl::AddInterleavedInput( + const int16* input_buffer_ptr, size_t num_channels, size_t num_frames) { + return AddInputBufferTemplated<const int16*>(input_buffer_ptr, num_channels, + num_frames); +} + +size_t BinauralSurroundRendererImpl::AddInterleavedInput( + const float* input_buffer_ptr, size_t num_channels, size_t num_frames) { + return AddInputBufferTemplated<const float*>(input_buffer_ptr, num_channels, + num_frames); +} + +size_t BinauralSurroundRendererImpl::AddPlanarInput( + const int16* const* input_buffer_ptrs, size_t num_channels, + size_t num_frames) { + return AddInputBufferTemplated<const int16* const*>(input_buffer_ptrs, + num_channels, num_frames); +} + +size_t BinauralSurroundRendererImpl::AddPlanarInput( + const float* const* input_buffer_ptrs, size_t num_channels, + size_t num_frames) { + return AddInputBufferTemplated<const float* const*>(input_buffer_ptrs, + num_channels, num_frames); +} + +template <typename BufferType> +size_t BinauralSurroundRendererImpl::AddInputBufferTemplated( + const BufferType input_buffer_ptr, size_t num_channels, size_t num_frames) { + DCHECK_NE(surround_format_, kInvalid); + if (num_channels != num_input_channels_) { + LOG(WARNING) << "Invalid number of input channels"; + return 0; + } + + if (num_zero_padded_frames_ > 0) { + LOG(WARNING) << "Zero padded output buffers must be consumed prior to " + "|AddInterleavedBuffer| calls"; + return 0; + } + const size_t num_available_input_frames = + std::min(num_frames, GetNumAvailableFramesInInputBuffer()); + + buffer_partitioner_->AddBuffer(input_buffer_ptr, num_input_channels_, + num_available_input_frames); + total_frames_buffered_ += num_available_input_frames; + return num_available_input_frames; +} + +size_t BinauralSurroundRendererImpl::GetAvailableFramesInStereoOutputBuffer() + const { + const size_t num_available_samples_in_buffers = + (input_audio_buffer_queue_->Size() * frames_per_buffer_) + + buffer_unpartitioner_->GetNumBufferedFrames(); + return std::min(total_frames_buffered_, num_available_samples_in_buffers); +} + +size_t BinauralSurroundRendererImpl::GetInterleavedStereoOutput( + int16* output_buffer_ptr, size_t num_frames) { + return GetStereoOutputBufferTemplated<int16*>(output_buffer_ptr, num_frames); +} + +size_t BinauralSurroundRendererImpl::GetInterleavedStereoOutput( + float* output_buffer_ptr, size_t num_frames) { + return GetStereoOutputBufferTemplated<float*>(output_buffer_ptr, num_frames); +} + +size_t BinauralSurroundRendererImpl::GetPlanarStereoOutput( + int16** output_buffer_ptrs, size_t num_frames) { + return GetStereoOutputBufferTemplated<int16**>(output_buffer_ptrs, + num_frames); +} + +size_t BinauralSurroundRendererImpl::GetPlanarStereoOutput( + float** output_buffer_ptrs, size_t num_frames) { + return GetStereoOutputBufferTemplated<float**>(output_buffer_ptrs, + num_frames); +} + +template <typename BufferType> +size_t BinauralSurroundRendererImpl::GetStereoOutputBufferTemplated( + BufferType output_buffer_ptr, size_t num_frames) { + DCHECK_NE(surround_format_, kInvalid); + const size_t num_frames_available = GetAvailableFramesInStereoOutputBuffer(); + size_t num_frames_to_be_processed = + std::min(num_frames_available, num_frames); + if (num_frames_to_be_processed > total_frames_buffered_) { + // Avoid outputting zero padded input frames from |TriggerProcessing| + // calls. + num_frames_to_be_processed = total_frames_buffered_; + } + + const size_t num_frames_written = buffer_unpartitioner_->GetBuffer( + output_buffer_ptr, kNumStereoChannels, num_frames_to_be_processed); + + DCHECK_GE(total_frames_buffered_, num_frames_written); + total_frames_buffered_ -= num_frames_written; + + if (total_frames_buffered_ == 0) { + // Clear zero padded frames from |TriggerProcessing| calls. + buffer_unpartitioner_->Clear(); + num_zero_padded_frames_ = 0; + } + + return num_frames_written; +} + +void BinauralSurroundRendererImpl::Clear() { + input_audio_buffer_queue_->Clear(); + buffer_partitioner_->Clear(); + buffer_unpartitioner_->Clear(); + total_frames_buffered_ = 0; + num_zero_padded_frames_ = 0; +} + +bool BinauralSurroundRendererImpl::TriggerProcessing() { + if (num_zero_padded_frames_ > 0) { + LOG(WARNING) << "Zero padded output buffers must be consumed prior to " + "|TriggerProcessing| calls"; + return false; + } + num_zero_padded_frames_ = buffer_partitioner_->Flush(); + return num_zero_padded_frames_ > 0; +} + +void BinauralSurroundRendererImpl::SetHeadRotation(float w, float x, float y, + float z) { + resonance_audio_api_->SetHeadRotation(x, y, z, w); +} + +AudioBuffer* BinauralSurroundRendererImpl::ProcessBuffer() { + if (input_audio_buffer_queue_->Size() == 0) { + LOG(WARNING) << "Buffer underflow detected"; + return nullptr; + } + + const AudioBuffer* input = input_audio_buffer_queue_->AcquireOutputObject(); + DCHECK_EQ(input->num_frames(), frames_per_buffer_); + DCHECK_EQ(num_input_channels_, input->num_channels()); + GetRawChannelDataPointersFromAudioBuffer(*input, &temp_planar_buffer_ptrs_); + // Initialize surround rendering. + const float* planar_ptr; + + switch (surround_format_) { + case kSurroundMono: + case kSurroundStereo: + case kSurroundFiveDotOne: + case kSurroundSevenDotOne: + DCHECK_EQ(input->num_channels(), source_ids_.size()); + for (size_t source_itr = 0; source_itr < source_ids_.size(); + ++source_itr) { + planar_ptr = (*input)[source_itr].begin(); + resonance_audio_api_->SetPlanarBuffer(source_ids_[source_itr], + &planar_ptr, kNumMonoChannels, + input->num_frames()); + } + break; + case kFirstOrderAmbisonics: + case kSecondOrderAmbisonics: + case kThirdOrderAmbisonics: + DCHECK_EQ(source_ids_.size(), 1U); + resonance_audio_api_->SetPlanarBuffer( + source_ids_[0], temp_planar_buffer_ptrs_.data(), + input->num_channels(), input->num_frames()); + break; + case kFirstOrderAmbisonicsWithNonDiegeticStereo: + case kSecondOrderAmbisonicsWithNonDiegeticStereo: + case kThirdOrderAmbisonicsWithNonDiegeticStereo: + DCHECK_EQ(source_ids_.size(), 2U); + DCHECK_GT(input->num_channels(), kNumStereoChannels); + static_cast<ResonanceAudioApiImpl*>(resonance_audio_api_.get()) + ->SetPlanarBuffer(source_ids_[0], temp_planar_buffer_ptrs_.data(), + input->num_channels() - kNumStereoChannels, + input->num_frames()); + static_cast<ResonanceAudioApiImpl*>(resonance_audio_api_.get()) + ->SetPlanarBuffer(source_ids_[1], + temp_planar_buffer_ptrs_.data() + + (input->num_channels() - kNumStereoChannels), + kNumStereoChannels, input->num_frames()); + break; + default: + LOG(FATAL) << "Undefined surround format"; + break; + } + + // Create a copy of the processed |AudioBuffer| to pass it to output buffer + // queue. + auto* const vraudio_api_impl = + static_cast<ResonanceAudioApiImpl*>(resonance_audio_api_.get()); + vraudio_api_impl->ProcessNextBuffer(); + output_buffer_ = *vraudio_api_impl->GetStereoOutputBuffer(); + + if (output_gain_ != 1.0f) { + for (AudioBuffer::Channel& channel : output_buffer_) { + ScalarMultiply(output_buffer_.num_frames(), output_gain_, channel.begin(), + channel.begin()); + } + } + input_audio_buffer_queue_->ReleaseOutputObject(input); + return &output_buffer_; +} + +} // namespace vraudio |