| 1 | /* | 
| 2 | Copyright 2018 Google Inc. All Rights Reserved. | 
| 3 |  | 
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); | 
| 5 | you may not use this file except in compliance with the License. | 
| 6 | You may obtain a copy of the License at | 
| 7 |  | 
| 8 |     http://www.apache.org/licenses/LICENSE-2.0 | 
| 9 |  | 
| 10 | Unless required by applicable law or agreed to in writing, software | 
| 11 | distributed under the License is distributed on an "AS-IS" BASIS, | 
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
| 13 | See the License for the specific language governing permissions and | 
| 14 | limitations under the License. | 
| 15 | */ | 
| 16 |  | 
| 17 | #include "graph/binaural_surround_renderer_impl.h" | 
| 18 |  | 
| 19 | #include <algorithm> | 
| 20 | #include <functional> | 
| 21 |  | 
| 22 | #include "base/misc_math.h" | 
| 23 | #include "base/simd_utils.h" | 
| 24 | #include "base/spherical_angle.h" | 
| 25 | #include "graph/resonance_audio_api_impl.h" | 
| 26 | #include "platforms/common/room_effects_utils.h" | 
| 27 | #include "platforms/common/room_properties.h" | 
| 28 | #include "utils/planar_interleaved_conversion.h" | 
| 29 |  | 
| 30 | namespace vraudio { | 
| 31 |  | 
| 32 | namespace { | 
| 33 |  | 
| 34 | // Maximum number of audio buffers in buffer queue. | 
| 35 | const size_t kNumMaxBuffers = 64; | 
| 36 |  | 
| 37 | // Output gain, to avoid clipping of individual virtual speaker channels. | 
| 38 | const float kGain = 0.5f; | 
| 39 |  | 
| 40 | }  // namespace | 
| 41 |  | 
| 42 | BinauralSurroundRendererImpl::BinauralSurroundRendererImpl( | 
| 43 |     size_t frames_per_buffer, int sample_rate_hz) | 
| 44 |     : | 
| 45 |       resonance_audio_api_(nullptr), | 
| 46 |       frames_per_buffer_(frames_per_buffer), | 
| 47 |       sample_rate_hz_(sample_rate_hz), | 
| 48 |       surround_format_(kInvalid), | 
| 49 |       num_input_channels_(0), | 
| 50 |       output_buffer_(kNumStereoChannels, frames_per_buffer), | 
| 51 |       total_frames_buffered_(0), | 
| 52 |       num_zero_padded_frames_(0), | 
| 53 |       output_gain_(1.0f) { | 
| 54 | } | 
| 55 |  | 
| 56 | bool BinauralSurroundRendererImpl::Init(SurroundFormat surround_format) { | 
| 57 |   surround_format_ = surround_format; | 
| 58 |   num_input_channels_ = | 
| 59 |       GetExpectedNumChannelsFromSurroundFormat(surround_format); | 
| 60 |  | 
| 61 |   temp_planar_buffer_ptrs_.resize(new_size: num_input_channels_); | 
| 62 |  | 
| 63 |   input_audio_buffer_queue_.reset(p: new ThreadsafeFifo<AudioBuffer>( | 
| 64 |       kNumMaxBuffers, AudioBuffer(num_input_channels_, frames_per_buffer_))); | 
| 65 |  | 
| 66 |   buffer_partitioner_.reset(p: new BufferPartitioner( | 
| 67 |       num_input_channels_, frames_per_buffer_, | 
| 68 |       std::bind(f: &BinauralSurroundRendererImpl::BufferPartitionerCallback, args: this, | 
| 69 |                 args: std::placeholders::_1))); | 
| 70 |  | 
| 71 |   buffer_unpartitioner_.reset(p: new BufferUnpartitioner( | 
| 72 |       kNumStereoChannels, frames_per_buffer_, | 
| 73 |       std::bind(f: &BinauralSurroundRendererImpl::ProcessBuffer, args: this))); | 
| 74 |  | 
| 75 |   resonance_audio_api_.reset(p: CreateResonanceAudioApi( | 
| 76 |       num_channels: kNumStereoChannels, frames_per_buffer: frames_per_buffer_, sample_rate_hz: sample_rate_hz_)); | 
| 77 |  | 
| 78 |   if (surround_format == kSurroundMono || surround_format == kSurroundStereo || | 
| 79 |       surround_format == kSurroundFiveDotOne || | 
| 80 |       surround_format == kSurroundSevenDotOne) { | 
| 81 |     InitializeRoomReverb(); | 
| 82 |   } | 
| 83 |   // Initialize rendering mode. | 
| 84 |   switch (surround_format) { | 
| 85 |     case kSurroundMono: | 
| 86 |       InitializeBinauralMono(); | 
| 87 |       break; | 
| 88 |     case kSurroundStereo: | 
| 89 |       InitializeBinauralStereo(); | 
| 90 |       break; | 
| 91 |     case kSurroundFiveDotOne: | 
| 92 |       InitializeBinauralSurround5dot1(); | 
| 93 |       break; | 
| 94 |     case kSurroundSevenDotOne: | 
| 95 |       InitializeBinauralSurround7dot1(); | 
| 96 |       break; | 
| 97 |     case kFirstOrderAmbisonics: | 
| 98 |     case kSecondOrderAmbisonics: | 
| 99 |     case kThirdOrderAmbisonics: | 
| 100 |       InitializeAmbisonics(); | 
| 101 |       break; | 
| 102 |     case kFirstOrderAmbisonicsWithNonDiegeticStereo: | 
| 103 |     case kSecondOrderAmbisonicsWithNonDiegeticStereo: | 
| 104 |     case kThirdOrderAmbisonicsWithNonDiegeticStereo: | 
| 105 |       InitializeAmbisonicsWithNonDiegeticStereo(); | 
| 106 |       break; | 
| 107 |     default: | 
| 108 |       LOG(FATAL) << "Undefined rendering mode" ; | 
| 109 |       return false; | 
| 110 |       break; | 
| 111 |   } | 
| 112 |   return true; | 
| 113 | } | 
| 114 |  | 
| 115 | BinauralSurroundRendererImpl::BinauralSurroundRendererImpl() | 
| 116 |     : | 
| 117 |       resonance_audio_api_(nullptr), | 
| 118 |       frames_per_buffer_(0), | 
| 119 |       sample_rate_hz_(0), | 
| 120 |       total_frames_buffered_(0), | 
| 121 |       num_zero_padded_frames_(0) { | 
| 122 | } | 
| 123 |  | 
| 124 | AudioBuffer* BinauralSurroundRendererImpl::BufferPartitionerCallback( | 
| 125 |     AudioBuffer* processed_buffer) { | 
| 126 |   if (processed_buffer != nullptr) { | 
| 127 |     input_audio_buffer_queue_->ReleaseInputObject(object: processed_buffer); | 
| 128 |   } | 
| 129 |   DCHECK(!input_audio_buffer_queue_->Full()); | 
| 130 |   return input_audio_buffer_queue_->AcquireInputObject(); | 
| 131 | } | 
| 132 |  | 
| 133 | void BinauralSurroundRendererImpl::SetStereoSpeakerMode(bool enabled) { | 
| 134 |   resonance_audio_api_->SetStereoSpeakerMode(enabled); | 
| 135 | } | 
| 136 |  | 
| 137 | size_t BinauralSurroundRendererImpl::GetExpectedNumChannelsFromSurroundFormat( | 
| 138 |     SurroundFormat surround_format) { | 
| 139 |   switch (surround_format) { | 
| 140 |     case kSurroundMono: | 
| 141 |       return kNumMonoChannels; | 
| 142 |     case kSurroundStereo: | 
| 143 |       return kNumStereoChannels; | 
| 144 |     case kSurroundFiveDotOne: | 
| 145 |       return kNumSurroundFiveDotOneChannels; | 
| 146 |     case kSurroundSevenDotOne: | 
| 147 |       return kNumSurroundSevenDotOneChannels; | 
| 148 |     case kFirstOrderAmbisonics: | 
| 149 |       return kNumFirstOrderAmbisonicChannels; | 
| 150 |     case kSecondOrderAmbisonics: | 
| 151 |       return kNumSecondOrderAmbisonicChannels; | 
| 152 |     case kThirdOrderAmbisonics: | 
| 153 |       return kNumThirdOrderAmbisonicChannels; | 
| 154 |     case kFirstOrderAmbisonicsWithNonDiegeticStereo: | 
| 155 |       return kNumFirstOrderAmbisonicChannels + kNumStereoChannels; | 
| 156 |     case kSecondOrderAmbisonicsWithNonDiegeticStereo: | 
| 157 |       return kNumSecondOrderAmbisonicChannels + kNumStereoChannels; | 
| 158 |     case kThirdOrderAmbisonicsWithNonDiegeticStereo: | 
| 159 |       return kNumThirdOrderAmbisonicChannels + kNumStereoChannels; | 
| 160 |     default: | 
| 161 |       LOG(FATAL) << "Undefined surround format mode" ; | 
| 162 |       return false; | 
| 163 |       break; | 
| 164 |   } | 
| 165 |   return 0; | 
| 166 | } | 
| 167 |  | 
| 168 | void BinauralSurroundRendererImpl::InitializeBinauralMono() { | 
| 169 |   source_ids_.resize(new_size: kNumMonoChannels); | 
| 170 |   // Front (0 degrees): | 
| 171 |   source_ids_[0] = CreateSoundObject(azimuth_deg: 0.0f); | 
| 172 |   output_gain_ = kGain; | 
| 173 | } | 
| 174 |  | 
| 175 | void BinauralSurroundRendererImpl::InitializeBinauralStereo() { | 
| 176 |  | 
| 177 |   source_ids_.resize(new_size: kNumStereoChannels); | 
| 178 |   // Front left (30 degrees): | 
| 179 |   source_ids_[0] = CreateSoundObject(azimuth_deg: 30.0f); | 
| 180 |   // Front right (-30 degrees): | 
| 181 |   source_ids_[1] = CreateSoundObject(azimuth_deg: -30.0f); | 
| 182 |   output_gain_ = kGain; | 
| 183 | } | 
| 184 |  | 
| 185 | void BinauralSurroundRendererImpl::InitializeBinauralSurround5dot1() { | 
| 186 |   source_ids_.resize(new_size: kNumSurroundFiveDotOneChannels); | 
| 187 |   // Left (30 degrees): | 
| 188 |   source_ids_[0] = CreateSoundObject(azimuth_deg: 30.0f); | 
| 189 |   // Right (-30 degrees): | 
| 190 |   source_ids_[1] = CreateSoundObject(azimuth_deg: -30.0f); | 
| 191 |   // Center (0 degrees): | 
| 192 |   source_ids_[2] = CreateSoundObject(azimuth_deg: 0.0f); | 
| 193 |   // Low frequency effects at front center: | 
| 194 |   source_ids_[3] = CreateSoundObject(azimuth_deg: 0.0f); | 
| 195 |   // Left surround (110 degrees): | 
| 196 |   source_ids_[4] = CreateSoundObject(azimuth_deg: 110.0f); | 
| 197 |   // Right surround (-110 degrees): | 
| 198 |   source_ids_[5] = CreateSoundObject(azimuth_deg: -110.0f); | 
| 199 |   output_gain_ = kGain; | 
| 200 | } | 
| 201 |  | 
| 202 | void BinauralSurroundRendererImpl::InitializeBinauralSurround7dot1() { | 
| 203 |   source_ids_.resize(new_size: kNumSurroundSevenDotOneChannels); | 
| 204 |   // Left (30 degrees): | 
| 205 |   source_ids_[0] = CreateSoundObject(azimuth_deg: 30.0f); | 
| 206 |   // Right (-30 degrees): | 
| 207 |   source_ids_[1] = CreateSoundObject(azimuth_deg: -30.0f); | 
| 208 |   // Center (0 degrees): | 
| 209 |   source_ids_[2] = CreateSoundObject(azimuth_deg: 0.0f); | 
| 210 |   // Low frequency effects at front center: | 
| 211 |   source_ids_[3] = CreateSoundObject(azimuth_deg: 0.0f); | 
| 212 |   // Left surround 1 (90 degrees): | 
| 213 |   source_ids_[4] = CreateSoundObject(azimuth_deg: 90.0f); | 
| 214 |   // Right surround 1 (-90 degrees): | 
| 215 |   source_ids_[5] = CreateSoundObject(azimuth_deg: -90.0f); | 
| 216 |   // Left surround 2 (150 degrees): | 
| 217 |   source_ids_[6] = CreateSoundObject(azimuth_deg: 150.0f); | 
| 218 |   // Right surround 2 (-150 degrees): | 
| 219 |   source_ids_[7] = CreateSoundObject(azimuth_deg: -150.0f); | 
| 220 |   output_gain_ = kGain; | 
| 221 | } | 
| 222 |  | 
| 223 | void BinauralSurroundRendererImpl::InitializeAmbisonics() { | 
| 224 |   source_ids_.resize(new_size: 1); | 
| 225 |   source_ids_[0] = | 
| 226 |       resonance_audio_api_->CreateAmbisonicSource(num_channels: num_input_channels_); | 
| 227 | } | 
| 228 |  | 
| 229 | void BinauralSurroundRendererImpl::InitializeAmbisonicsWithNonDiegeticStereo() { | 
| 230 |   source_ids_.resize(new_size: 2); | 
| 231 |   CHECK_GT(num_input_channels_, kNumStereoChannels); | 
| 232 |   source_ids_[0] = resonance_audio_api_->CreateAmbisonicSource( | 
| 233 |       num_channels: num_input_channels_ - kNumStereoChannels); | 
| 234 |   source_ids_[1] = resonance_audio_api_->CreateStereoSource(num_channels: kNumStereoChannels); | 
| 235 | } | 
| 236 |  | 
| 237 | SourceId BinauralSurroundRendererImpl::CreateSoundObject(float azimuth_deg) { | 
| 238 |   static const float kZeroElevation = 0.0f; | 
| 239 |   auto speaker_position = | 
| 240 |       vraudio::SphericalAngle::FromDegrees(azimuth_degrees: azimuth_deg, elevation_degrees: kZeroElevation) | 
| 241 |           .GetWorldPositionOnUnitSphere(); | 
| 242 |   const SourceId source_id = resonance_audio_api_->CreateSoundObjectSource( | 
| 243 |       rendering_mode: RenderingMode::kBinauralHighQuality); | 
| 244 |   resonance_audio_api_->SetSourcePosition( | 
| 245 |       source_id, x: speaker_position[0], y: speaker_position[1], z: speaker_position[2]); | 
| 246 |   return source_id; | 
| 247 | } | 
| 248 |  | 
| 249 | void BinauralSurroundRendererImpl::InitializeRoomReverb() { | 
| 250 |   // The following settings has been applied based on AESTD1001.1.01-10. | 
| 251 |   RoomProperties room_properties; | 
| 252 |   room_properties.dimensions[0] = 9.54f; | 
| 253 |   room_properties.dimensions[1] = 6.0f; | 
| 254 |   room_properties.dimensions[2] = 15.12f; | 
| 255 |   room_properties.reverb_brightness = 0.0f; | 
| 256 |   room_properties.reflection_scalar = 1.0f; | 
| 257 |   // Reduce reverb gain to compensate for virtual speakers gain. | 
| 258 |   room_properties.reverb_gain = output_gain_; | 
| 259 |   for (size_t i = 0; i < kNumRoomSurfaces; ++i) { | 
| 260 |     room_properties.material_names[i] = MaterialName::kUniform; | 
| 261 |   } | 
| 262 |   resonance_audio_api_->SetReflectionProperties( | 
| 263 |       ComputeReflectionProperties(room_properties)); | 
| 264 |   resonance_audio_api_->SetReverbProperties( | 
| 265 |       ComputeReverbProperties(room_properties)); | 
| 266 |   resonance_audio_api_->EnableRoomEffects(enable: true); | 
| 267 | } | 
| 268 |  | 
| 269 | size_t BinauralSurroundRendererImpl::GetNumAvailableFramesInInputBuffer() | 
| 270 |     const { | 
| 271 |   DCHECK_NE(surround_format_, kInvalid); | 
| 272 |   if (num_zero_padded_frames_ > 0) { | 
| 273 |     // Zero padded output buffers must be consumed prior to | 
| 274 |     // |AddInterleavedBuffer| calls; | 
| 275 |     return 0; | 
| 276 |   } | 
| 277 |   if (input_audio_buffer_queue_->Full()) { | 
| 278 |     return 0; | 
| 279 |   } | 
| 280 |   // Subtract two buffers from the available input slots to ensure the buffer | 
| 281 |   // partitioner can be flushed at any time while keeping an extra buffer | 
| 282 |   // available in the |buffer_partitioner_| callback for the next incoming data. | 
| 283 |   const size_t num_frames_available_in_input_slots = | 
| 284 |       (kNumMaxBuffers - input_audio_buffer_queue_->Size() - 2) * | 
| 285 |       frames_per_buffer_; | 
| 286 |   DCHECK_GT(frames_per_buffer_, buffer_partitioner_->GetNumBufferedFrames()); | 
| 287 |   const size_t num_frames_available_in_buffer_partitioner = | 
| 288 |       frames_per_buffer_ - buffer_partitioner_->GetNumBufferedFrames(); | 
| 289 |   return num_frames_available_in_input_slots + | 
| 290 |          num_frames_available_in_buffer_partitioner; | 
| 291 | } | 
| 292 |  | 
| 293 | size_t BinauralSurroundRendererImpl::AddInterleavedInput( | 
| 294 |     const int16* input_buffer_ptr, size_t num_channels, size_t num_frames) { | 
| 295 |   return AddInputBufferTemplated<const int16*>(input_buffer_ptr, num_channels, | 
| 296 |                                                num_frames); | 
| 297 | } | 
| 298 |  | 
| 299 | size_t BinauralSurroundRendererImpl::AddInterleavedInput( | 
| 300 |     const float* input_buffer_ptr, size_t num_channels, size_t num_frames) { | 
| 301 |   return AddInputBufferTemplated<const float*>(input_buffer_ptr, num_channels, | 
| 302 |                                                num_frames); | 
| 303 | } | 
| 304 |  | 
| 305 | size_t BinauralSurroundRendererImpl::AddPlanarInput( | 
| 306 |     const int16* const* input_buffer_ptrs, size_t num_channels, | 
| 307 |     size_t num_frames) { | 
| 308 |   return AddInputBufferTemplated<const int16* const*>(input_buffer_ptr: input_buffer_ptrs, | 
| 309 |                                                       num_channels, num_frames); | 
| 310 | } | 
| 311 |  | 
| 312 | size_t BinauralSurroundRendererImpl::AddPlanarInput( | 
| 313 |     const float* const* input_buffer_ptrs, size_t num_channels, | 
| 314 |     size_t num_frames) { | 
| 315 |   return AddInputBufferTemplated<const float* const*>(input_buffer_ptr: input_buffer_ptrs, | 
| 316 |                                                       num_channels, num_frames); | 
| 317 | } | 
| 318 |  | 
| 319 | template <typename BufferType> | 
| 320 | size_t BinauralSurroundRendererImpl::AddInputBufferTemplated( | 
| 321 |     const BufferType input_buffer_ptr, size_t num_channels, size_t num_frames) { | 
| 322 |   DCHECK_NE(surround_format_, kInvalid); | 
| 323 |   if (num_channels != num_input_channels_) { | 
| 324 |     LOG(WARNING) << "Invalid number of input channels" ; | 
| 325 |     return 0; | 
| 326 |   } | 
| 327 |  | 
| 328 |   if (num_zero_padded_frames_ > 0) { | 
| 329 |     LOG(WARNING) << "Zero padded output buffers must be consumed prior to "  | 
| 330 |                     "|AddInterleavedBuffer| calls" ; | 
| 331 |     return 0; | 
| 332 |   } | 
| 333 |   const size_t num_available_input_frames = | 
| 334 |       std::min(a: num_frames, b: GetNumAvailableFramesInInputBuffer()); | 
| 335 |  | 
| 336 |   buffer_partitioner_->AddBuffer(input_buffer_ptr, num_input_channels_, | 
| 337 |                                  num_available_input_frames); | 
| 338 |   total_frames_buffered_ += num_available_input_frames; | 
| 339 |   return num_available_input_frames; | 
| 340 | } | 
| 341 |  | 
| 342 | size_t BinauralSurroundRendererImpl::GetAvailableFramesInStereoOutputBuffer() | 
| 343 |     const { | 
| 344 |   const size_t num_available_samples_in_buffers = | 
| 345 |       (input_audio_buffer_queue_->Size() * frames_per_buffer_) + | 
| 346 |       buffer_unpartitioner_->GetNumBufferedFrames(); | 
| 347 |   return std::min(a: total_frames_buffered_, b: num_available_samples_in_buffers); | 
| 348 | } | 
| 349 |  | 
| 350 | size_t BinauralSurroundRendererImpl::GetInterleavedStereoOutput( | 
| 351 |     int16* output_buffer_ptr, size_t num_frames) { | 
| 352 |   return GetStereoOutputBufferTemplated<int16*>(output_buffer_ptr, num_frames); | 
| 353 | } | 
| 354 |  | 
| 355 | size_t BinauralSurroundRendererImpl::GetInterleavedStereoOutput( | 
| 356 |     float* output_buffer_ptr, size_t num_frames) { | 
| 357 |   return GetStereoOutputBufferTemplated<float*>(output_buffer_ptr, num_frames); | 
| 358 | } | 
| 359 |  | 
| 360 | size_t BinauralSurroundRendererImpl::GetPlanarStereoOutput( | 
| 361 |     int16** output_buffer_ptrs, size_t num_frames) { | 
| 362 |   return GetStereoOutputBufferTemplated<int16**>(output_buffer_ptr: output_buffer_ptrs, | 
| 363 |                                                  num_frames); | 
| 364 | } | 
| 365 |  | 
| 366 | size_t BinauralSurroundRendererImpl::GetPlanarStereoOutput( | 
| 367 |     float** output_buffer_ptrs, size_t num_frames) { | 
| 368 |   return GetStereoOutputBufferTemplated<float**>(output_buffer_ptr: output_buffer_ptrs, | 
| 369 |                                                  num_frames); | 
| 370 | } | 
| 371 |  | 
| 372 | template <typename BufferType> | 
| 373 | size_t BinauralSurroundRendererImpl::GetStereoOutputBufferTemplated( | 
| 374 |     BufferType output_buffer_ptr, size_t num_frames) { | 
| 375 |   DCHECK_NE(surround_format_, kInvalid); | 
| 376 |   const size_t num_frames_available = GetAvailableFramesInStereoOutputBuffer(); | 
| 377 |   size_t num_frames_to_be_processed = | 
| 378 |       std::min(a: num_frames_available, b: num_frames); | 
| 379 |   if (num_frames_to_be_processed > total_frames_buffered_) { | 
| 380 |     // Avoid outputting zero padded input frames from |TriggerProcessing| | 
| 381 |     // calls. | 
| 382 |     num_frames_to_be_processed = total_frames_buffered_; | 
| 383 |   } | 
| 384 |  | 
| 385 |   const size_t num_frames_written = buffer_unpartitioner_->GetBuffer( | 
| 386 |       output_buffer_ptr, kNumStereoChannels, num_frames_to_be_processed); | 
| 387 |  | 
| 388 |   DCHECK_GE(total_frames_buffered_, num_frames_written); | 
| 389 |   total_frames_buffered_ -= num_frames_written; | 
| 390 |  | 
| 391 |   if (total_frames_buffered_ == 0) { | 
| 392 |     // Clear zero padded frames from |TriggerProcessing| calls. | 
| 393 |     buffer_unpartitioner_->Clear(); | 
| 394 |     num_zero_padded_frames_ = 0; | 
| 395 |   } | 
| 396 |  | 
| 397 |   return num_frames_written; | 
| 398 | } | 
| 399 |  | 
| 400 | void BinauralSurroundRendererImpl::Clear() { | 
| 401 |   input_audio_buffer_queue_->Clear(); | 
| 402 |   buffer_partitioner_->Clear(); | 
| 403 |   buffer_unpartitioner_->Clear(); | 
| 404 |   total_frames_buffered_ = 0; | 
| 405 |   num_zero_padded_frames_ = 0; | 
| 406 | } | 
| 407 |  | 
| 408 | bool BinauralSurroundRendererImpl::TriggerProcessing() { | 
| 409 |   if (num_zero_padded_frames_ > 0) { | 
| 410 |     LOG(WARNING) << "Zero padded output buffers must be consumed prior to "  | 
| 411 |                     "|TriggerProcessing| calls" ; | 
| 412 |     return false; | 
| 413 |   } | 
| 414 |   num_zero_padded_frames_ = buffer_partitioner_->Flush(); | 
| 415 |   return num_zero_padded_frames_ > 0; | 
| 416 | } | 
| 417 |  | 
| 418 | void BinauralSurroundRendererImpl::SetHeadRotation(float w, float x, float y, | 
| 419 |                                                    float z) { | 
| 420 |   resonance_audio_api_->SetHeadRotation(x, y, z, w); | 
| 421 | } | 
| 422 |  | 
| 423 | AudioBuffer* BinauralSurroundRendererImpl::ProcessBuffer() { | 
| 424 |   if (input_audio_buffer_queue_->Size() == 0) { | 
| 425 |     LOG(WARNING) << "Buffer underflow detected" ; | 
| 426 |     return nullptr; | 
| 427 |   } | 
| 428 |  | 
| 429 |   const AudioBuffer* input = input_audio_buffer_queue_->AcquireOutputObject(); | 
| 430 |   DCHECK_EQ(input->num_frames(), frames_per_buffer_); | 
| 431 |   DCHECK_EQ(num_input_channels_, input->num_channels()); | 
| 432 |   GetRawChannelDataPointersFromAudioBuffer(audio_buffer: *input, channel_ptr_vector: &temp_planar_buffer_ptrs_); | 
| 433 |   // Initialize surround rendering. | 
| 434 |   const float* planar_ptr; | 
| 435 |  | 
| 436 |   switch (surround_format_) { | 
| 437 |     case kSurroundMono: | 
| 438 |     case kSurroundStereo: | 
| 439 |     case kSurroundFiveDotOne: | 
| 440 |     case kSurroundSevenDotOne: | 
| 441 |       DCHECK_EQ(input->num_channels(), source_ids_.size()); | 
| 442 |       for (size_t source_itr = 0; source_itr < source_ids_.size(); | 
| 443 |            ++source_itr) { | 
| 444 |         planar_ptr = (*input)[source_itr].begin(); | 
| 445 |         resonance_audio_api_->SetPlanarBuffer(source_id: source_ids_[source_itr], | 
| 446 |                                               audio_buffer_ptr: &planar_ptr, num_channels: kNumMonoChannels, | 
| 447 |                                               num_frames: input->num_frames()); | 
| 448 |       } | 
| 449 |       break; | 
| 450 |     case kFirstOrderAmbisonics: | 
| 451 |     case kSecondOrderAmbisonics: | 
| 452 |     case kThirdOrderAmbisonics: | 
| 453 |       DCHECK_EQ(source_ids_.size(), 1U); | 
| 454 |       resonance_audio_api_->SetPlanarBuffer( | 
| 455 |           source_id: source_ids_[0], audio_buffer_ptr: temp_planar_buffer_ptrs_.data(), | 
| 456 |           num_channels: input->num_channels(), num_frames: input->num_frames()); | 
| 457 |       break; | 
| 458 |     case kFirstOrderAmbisonicsWithNonDiegeticStereo: | 
| 459 |     case kSecondOrderAmbisonicsWithNonDiegeticStereo: | 
| 460 |     case kThirdOrderAmbisonicsWithNonDiegeticStereo: | 
| 461 |       DCHECK_EQ(source_ids_.size(), 2U); | 
| 462 |       DCHECK_GT(input->num_channels(), kNumStereoChannels); | 
| 463 |       static_cast<ResonanceAudioApiImpl*>(resonance_audio_api_.get()) | 
| 464 |           ->SetPlanarBuffer(source_id: source_ids_[0], audio_buffer_ptr: temp_planar_buffer_ptrs_.data(), | 
| 465 |                             num_channels: input->num_channels() - kNumStereoChannels, | 
| 466 |                             num_frames: input->num_frames()); | 
| 467 |       static_cast<ResonanceAudioApiImpl*>(resonance_audio_api_.get()) | 
| 468 |           ->SetPlanarBuffer(source_id: source_ids_[1], | 
| 469 |                             audio_buffer_ptr: temp_planar_buffer_ptrs_.data() + | 
| 470 |                                 (input->num_channels() - kNumStereoChannels), | 
| 471 |                             num_channels: kNumStereoChannels, num_frames: input->num_frames()); | 
| 472 |       break; | 
| 473 |     default: | 
| 474 |       LOG(FATAL) << "Undefined surround format" ; | 
| 475 |       break; | 
| 476 |   } | 
| 477 |  | 
| 478 |   // Create a copy of the processed |AudioBuffer| to pass it to output buffer | 
| 479 |   // queue. | 
| 480 |   auto* const vraudio_api_impl = | 
| 481 |       static_cast<ResonanceAudioApiImpl*>(resonance_audio_api_.get()); | 
| 482 |   vraudio_api_impl->ProcessNextBuffer(); | 
| 483 |   output_buffer_ = *vraudio_api_impl->GetStereoOutputBuffer(); | 
| 484 |  | 
| 485 |   if (output_gain_ != 1.0f) { | 
| 486 |     for (AudioBuffer::Channel& channel : output_buffer_) { | 
| 487 |       ScalarMultiply(length: output_buffer_.num_frames(), gain: output_gain_, input: channel.begin(), | 
| 488 |                      output: channel.begin()); | 
| 489 |     } | 
| 490 |   } | 
| 491 |   input_audio_buffer_queue_->ReleaseOutputObject(object: input); | 
| 492 |   return &output_buffer_; | 
| 493 | } | 
| 494 |  | 
| 495 | }  // namespace vraudio | 
| 496 |  |