| 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/occlusion_node.h" | 
| 18 |  | 
| 19 | #include <cmath> | 
| 20 |  | 
| 21 | #include "base/logging.h" | 
| 22 | #include "base/spherical_angle.h" | 
| 23 |  | 
| 24 | #include "dsp/occlusion_calculator.h" | 
| 25 |  | 
| 26 | namespace vraudio { | 
| 27 |  | 
| 28 | namespace { | 
| 29 |  | 
| 30 | // Low pass filter coefficient for smoothing the applied occlusion. This avoids | 
| 31 | // sudden unrealistic changes in the volume of a sound object. Range [0, 1]. | 
| 32 | // The value below has been calculated empirically. | 
| 33 | const float kOcclusionSmoothingCoefficient = 0.75f; | 
| 34 |  | 
| 35 | // This function provides first order low-pass filtering. It is used to smooth | 
| 36 | // the occlusion parameter. | 
| 37 | float Interpolate(float coefficient, float previous_value, float target_value) { | 
| 38 |   return target_value + coefficient * (previous_value - target_value); | 
| 39 | } | 
| 40 |  | 
| 41 | }  // namespace | 
| 42 |  | 
| 43 | OcclusionNode::OcclusionNode(SourceId source_id, | 
| 44 |                              const SystemSettings& system_settings) | 
| 45 |     : system_settings_(system_settings), | 
| 46 |       low_pass_filter_(0.0f), | 
| 47 |       current_occlusion_(0.0f), | 
| 48 |       output_buffer_(kNumMonoChannels, system_settings.GetFramesPerBuffer()) { | 
| 49 |   output_buffer_.Clear(); | 
| 50 |   output_buffer_.set_source_id(source_id); | 
| 51 | } | 
| 52 |  | 
| 53 | const AudioBuffer* OcclusionNode::AudioProcess(const NodeInput& input) { | 
| 54 |  | 
| 55 |   const AudioBuffer* input_buffer = input.GetSingleInput(); | 
| 56 |   DCHECK(input_buffer); | 
| 57 |   DCHECK_EQ(input_buffer->source_id(), output_buffer_.source_id()); | 
| 58 |  | 
| 59 |   const auto source_parameters = | 
| 60 |       system_settings_.GetSourceParameters(source_id: input_buffer->source_id()); | 
| 61 |   if (source_parameters == nullptr) { | 
| 62 |     LOG(WARNING) << "Could not find source parameters" ; | 
| 63 |     return nullptr; | 
| 64 |   } | 
| 65 |  | 
| 66 |   const WorldPosition& listener_position = system_settings_.GetHeadPosition(); | 
| 67 |   const WorldRotation& listener_rotation = system_settings_.GetHeadRotation(); | 
| 68 |   const ObjectTransform& source_transform = source_parameters->object_transform; | 
| 69 |   // Compute the relative listener/source direction in spherical angles. | 
| 70 |   WorldPosition relative_direction; | 
| 71 |   GetRelativeDirection(from_position: listener_position, from_rotation: listener_rotation, | 
| 72 |                        to_position: source_transform.position, relative_direction: &relative_direction); | 
| 73 |   const SphericalAngle listener_direction = | 
| 74 |       SphericalAngle::FromWorldPosition(world_position: relative_direction); | 
| 75 |  | 
| 76 |   GetRelativeDirection(from_position: source_transform.position, from_rotation: source_transform.rotation, | 
| 77 |                        to_position: listener_position, relative_direction: &relative_direction); | 
| 78 |   const SphericalAngle source_direction = | 
| 79 |       SphericalAngle::FromWorldPosition(world_position: relative_direction); | 
| 80 |   // Calculate low-pass filter coefficient based on listener/source directivity | 
| 81 |   // and occlusion values. | 
| 82 |   const float listener_directivity = CalculateDirectivity( | 
| 83 |       alpha: source_parameters->listener_directivity_alpha, | 
| 84 |       order: source_parameters->listener_directivity_order, spherical_angle: listener_direction); | 
| 85 |   const float source_directivity = CalculateDirectivity( | 
| 86 |       alpha: source_parameters->directivity_alpha, | 
| 87 |       order: source_parameters->directivity_order, spherical_angle: source_direction); | 
| 88 |   current_occlusion_ = | 
| 89 |       Interpolate(coefficient: kOcclusionSmoothingCoefficient, previous_value: current_occlusion_, | 
| 90 |                   target_value: source_parameters->occlusion_intensity); | 
| 91 |   const float filter_coefficient = CalculateOcclusionFilterCoefficient( | 
| 92 |       directivity: listener_directivity * source_directivity, occlusion_intensity: current_occlusion_); | 
| 93 |   low_pass_filter_.SetCoefficient(filter_coefficient); | 
| 94 |   if (!low_pass_filter_.Filter(input: (*input_buffer)[0], output: &output_buffer_[0])) { | 
| 95 |     return input_buffer; | 
| 96 |   } | 
| 97 |   // Copy buffer parameters. | 
| 98 |   return &output_buffer_; | 
| 99 | } | 
| 100 |  | 
| 101 | }  // namespace vraudio | 
| 102 |  |