| 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/graph_manager.h" | 
| 18 |  | 
| 19 | #include <functional> | 
| 20 |  | 
| 21 | #include "ambisonics/utils.h" | 
| 22 | #include "base/constants_and_types.h" | 
| 23 | #include "base/logging.h" | 
| 24 | #include "graph/foa_rotator_node.h" | 
| 25 | #include "graph/gain_node.h" | 
| 26 | #include "graph/hoa_rotator_node.h" | 
| 27 | #include "graph/mono_from_soundfield_node.h" | 
| 28 | #include "graph/near_field_effect_node.h" | 
| 29 | #include "graph/occlusion_node.h" | 
| 30 |  | 
| 31 | namespace vraudio { | 
| 32 |  | 
| 33 | GraphManager::GraphManager(const SystemSettings& system_settings) | 
| 34 |     : | 
| 35 |       room_effects_enabled_(true), | 
| 36 |       config_(GlobalConfig()), | 
| 37 |       system_settings_(system_settings), | 
| 38 |       fft_manager_(system_settings.GetFramesPerBuffer()), | 
| 39 |       output_node_(std::make_shared<SinkNode>()) { | 
| 40 |   CHECK_LE(system_settings.GetFramesPerBuffer(), kMaxSupportedNumFrames); | 
| 41 |  | 
| 42 |   stereo_mixer_node_ = | 
| 43 |       std::make_shared<MixerNode>(args: system_settings_, args: kNumStereoChannels); | 
| 44 |   output_node_->Connect(publisher_node: stereo_mixer_node_); | 
| 45 |  | 
| 46 |   /// Initialize the Ambisonic Lookup Table. | 
| 47 |   lookup_table_.reset(p: new AmbisonicLookupTable(config_.max_ambisonic_order)); | 
| 48 |   // Initialize the Ambisonic Renderer subgraphs. | 
| 49 |   for (const auto& sh_hrir_filename_itr : config_.sh_hrir_filenames) { | 
| 50 |     const int ambisonic_order = sh_hrir_filename_itr.first; | 
| 51 |     const auto& sh_hrir_filename = sh_hrir_filename_itr.second; | 
| 52 |     InitializeAmbisonicRendererGraph(ambisonic_order, sh_hrir_filename); | 
| 53 |     // Initialize the Ambisonic Mixing Encoders for HRTF sound object rendering. | 
| 54 |     ambisonic_mixing_encoder_nodes_[ambisonic_order] = | 
| 55 |         std::make_shared<AmbisonicMixingEncoderNode>( | 
| 56 |             args: system_settings_, args&: *lookup_table_, args: ambisonic_order); | 
| 57 |     ambisonic_mixer_nodes_[ambisonic_order]->Connect( | 
| 58 |         publisher_node: ambisonic_mixing_encoder_nodes_[ambisonic_order]); | 
| 59 |   } | 
| 60 |  | 
| 61 |   // Stereo mixing panner node used in non-HRTF sound object rendering. | 
| 62 |   stereo_mixing_panner_node_ = | 
| 63 |       std::make_shared<StereoMixingPannerNode>(args: system_settings_); | 
| 64 |   stereo_mixer_node_->Connect(publisher_node: stereo_mixing_panner_node_); | 
| 65 |  | 
| 66 |   // Initialize room effects graphs. | 
| 67 |   InitializeReflectionsGraph(); | 
| 68 |   InitializeReverbGraph(); | 
| 69 |   // Initialize ambisonic output mixer. | 
| 70 |   ambisonic_output_mixer_.reset( | 
| 71 |       p: new Mixer(GetNumPeriphonicComponents(ambisonic_order: config_.max_ambisonic_order), | 
| 72 |                 system_settings.GetFramesPerBuffer())); | 
| 73 | } | 
| 74 |  | 
| 75 | void GraphManager::CreateAmbisonicSource(SourceId ambisonic_source_id, | 
| 76 |                                          size_t num_channels) { | 
| 77 |   DCHECK(source_nodes_.find(ambisonic_source_id) == source_nodes_.end()); | 
| 78 |   // Create a new |ambisonic_source_node| and register to |source_nodes_|. | 
| 79 |   auto ambisonic_source_node = std::make_shared<BufferedSourceNode>( | 
| 80 |       args&: ambisonic_source_id, args&: num_channels, args: system_settings_.GetFramesPerBuffer()); | 
| 81 |   source_nodes_[ambisonic_source_id] = ambisonic_source_node; | 
| 82 |  | 
| 83 |   // Connect |ambisonic_source_node| to the ambisonic decoding pipeline. | 
| 84 |   const int ambisonic_order = GetPeriphonicAmbisonicOrder(num_components: num_channels); | 
| 85 |   auto direct_attenuation_node = | 
| 86 |       std::make_shared<GainNode>(args&: ambisonic_source_id, args&: num_channels, | 
| 87 |                                  args: AttenuationType::kDirect, args: system_settings_); | 
| 88 |   direct_attenuation_node->Connect(publisher_node: ambisonic_source_node); | 
| 89 |   if (ambisonic_order == 1) { | 
| 90 |     // First order case. | 
| 91 |     auto foa_rotator_node = | 
| 92 |         std::make_shared<FoaRotatorNode>(args&: ambisonic_source_id, args: system_settings_); | 
| 93 |     foa_rotator_node->Connect(publisher_node: direct_attenuation_node); | 
| 94 |     ambisonic_mixer_nodes_[ambisonic_order]->Connect(publisher_node: foa_rotator_node); | 
| 95 |   } else { | 
| 96 |     // Higher orders case. | 
| 97 |     auto hoa_rotator_node = std::make_shared<HoaRotatorNode>( | 
| 98 |         args&: ambisonic_source_id, args: system_settings_, args: ambisonic_order); | 
| 99 |     hoa_rotator_node->Connect(publisher_node: direct_attenuation_node); | 
| 100 |     ambisonic_mixer_nodes_[ambisonic_order]->Connect(publisher_node: hoa_rotator_node); | 
| 101 |   } | 
| 102 |   // Connect to room effects rendering pipeline. | 
| 103 |   auto mono_from_soundfield_node = std::make_shared<MonoFromSoundfieldNode>( | 
| 104 |       args&: ambisonic_source_id, args: system_settings_); | 
| 105 |   mono_from_soundfield_node->Connect(publisher_node: ambisonic_source_node); | 
| 106 |   reflections_gain_mixer_node_->Connect(publisher_node: mono_from_soundfield_node); | 
| 107 |   reverb_gain_mixer_node_->Connect(publisher_node: mono_from_soundfield_node); | 
| 108 | } | 
| 109 |  | 
| 110 | void GraphManager::CreateSoundObjectSource(SourceId sound_object_source_id, | 
| 111 |                                            int ambisonic_order, | 
| 112 |                                            bool enable_hrtf, | 
| 113 |                                            bool enable_direct_rendering) { | 
| 114 |   DCHECK(source_nodes_.find(sound_object_source_id) == source_nodes_.end()); | 
| 115 |   // Create a new |sound_object_source_node| and register to |source_nodes_|. | 
| 116 |   auto sound_object_source_node = std::make_shared<BufferedSourceNode>( | 
| 117 |       args&: sound_object_source_id, args: kNumMonoChannels, | 
| 118 |       args: system_settings_.GetFramesPerBuffer()); | 
| 119 |   source_nodes_[sound_object_source_id] = sound_object_source_node; | 
| 120 |  | 
| 121 |   // Create direct rendering pipeline. | 
| 122 |   if (enable_direct_rendering) { | 
| 123 |     auto direct_attenuation_node = | 
| 124 |         std::make_shared<GainNode>(args&: sound_object_source_id, args: kNumMonoChannels, | 
| 125 |                                    args: AttenuationType::kDirect, args: system_settings_); | 
| 126 |     direct_attenuation_node->Connect(publisher_node: sound_object_source_node); | 
| 127 |     auto occlusion_node = std::make_shared<OcclusionNode>( | 
| 128 |         args&: sound_object_source_id, args: system_settings_); | 
| 129 |     occlusion_node->Connect(publisher_node: direct_attenuation_node); | 
| 130 |     auto near_field_effect_node = std::make_shared<NearFieldEffectNode>( | 
| 131 |         args&: sound_object_source_id, args: system_settings_); | 
| 132 |  | 
| 133 |     if (enable_hrtf) { | 
| 134 |       ambisonic_mixing_encoder_nodes_[ambisonic_order]->Connect(publisher_node: occlusion_node); | 
| 135 |     } else { | 
| 136 |       stereo_mixing_panner_node_->Connect(publisher_node: occlusion_node); | 
| 137 |     } | 
| 138 |  | 
| 139 |     near_field_effect_node->Connect(publisher_node: occlusion_node); | 
| 140 |     stereo_mixer_node_->Connect(publisher_node: near_field_effect_node); | 
| 141 |   } | 
| 142 |  | 
| 143 |   // Connect to room effects rendering pipeline. | 
| 144 |   reflections_gain_mixer_node_->Connect(publisher_node: sound_object_source_node); | 
| 145 |   reverb_gain_mixer_node_->Connect(publisher_node: sound_object_source_node); | 
| 146 | } | 
| 147 |  | 
| 148 | void GraphManager::EnableRoomEffects(bool enable) { | 
| 149 |   room_effects_enabled_ = enable; | 
| 150 |   reflections_gain_mixer_node_->SetMute(!room_effects_enabled_); | 
| 151 |   reverb_gain_mixer_node_->SetMute(!room_effects_enabled_); | 
| 152 | } | 
| 153 |  | 
| 154 | const AudioBuffer* GraphManager::GetAmbisonicBuffer() const { | 
| 155 |   ambisonic_output_mixer_->Reset(); | 
| 156 |   for (const auto& ambisonic_mixer_node_itr : ambisonic_mixer_nodes_) { | 
| 157 |     const auto* ambisonic_buffer = | 
| 158 |         ambisonic_mixer_node_itr.second->GetOutputBuffer(); | 
| 159 |     if (ambisonic_buffer != nullptr) { | 
| 160 |       ambisonic_output_mixer_->AddInput(input: *ambisonic_buffer); | 
| 161 |     } | 
| 162 |   } | 
| 163 |   return ambisonic_output_mixer_->GetOutput(); | 
| 164 | } | 
| 165 |  | 
| 166 | const AudioBuffer* GraphManager::GetStereoBuffer() const { | 
| 167 |     return stereo_mixer_node_->GetOutputBuffer(); | 
| 168 | } | 
| 169 |  | 
| 170 | const AudioBuffer *GraphManager::GetReverbBuffer() const | 
| 171 | { | 
| 172 |     return reverb_node_->GetOutputBuffer(); | 
| 173 | } | 
| 174 |  | 
| 175 | size_t GraphManager::GetNumMaxAmbisonicChannels() const { | 
| 176 |   return GetNumPeriphonicComponents(ambisonic_order: config_.max_ambisonic_order); | 
| 177 | } | 
| 178 |  | 
| 179 | bool GraphManager::GetRoomEffectsEnabled() const { | 
| 180 |   return room_effects_enabled_; | 
| 181 | } | 
| 182 |  | 
| 183 | void GraphManager::UpdateRoomReflections() { reflections_node_->Update(); } | 
| 184 |  | 
| 185 | void GraphManager::UpdateRoomReverb() { reverb_node_->Update(); } | 
| 186 |  | 
| 187 | void GraphManager::InitializeReverbGraph() { | 
| 188 |   reverb_gain_mixer_node_ = std::make_shared<GainMixerNode>( | 
| 189 |       args: AttenuationType::kReverb, args: system_settings_, args: kNumMonoChannels); | 
| 190 |   reverb_node_ = std::make_shared<ReverbNode>(args: system_settings_, args: &fft_manager_); | 
| 191 |   reverb_node_->Connect(publisher_node: reverb_gain_mixer_node_); | 
| 192 |   stereo_mixer_node_->Connect(publisher_node: reverb_node_); | 
| 193 | } | 
| 194 |  | 
| 195 | void GraphManager::InitializeReflectionsGraph() { | 
| 196 |   reflections_gain_mixer_node_ = std::make_shared<GainMixerNode>( | 
| 197 |       args: AttenuationType::kReflections, args: system_settings_, args: kNumMonoChannels); | 
| 198 |   reflections_node_ = std::make_shared<ReflectionsNode>(args: system_settings_); | 
| 199 |   reflections_node_->Connect(publisher_node: reflections_gain_mixer_node_); | 
| 200 |   // Reflections are limited to First Order Ambisonics to reduce complexity. | 
| 201 |   const int kAmbisonicOrder1 = 1; | 
| 202 |   ambisonic_mixer_nodes_[kAmbisonicOrder1]->Connect(publisher_node: reflections_node_); | 
| 203 | } | 
| 204 |  | 
| 205 | void GraphManager::CreateAmbisonicPannerSource(SourceId sound_object_source_id, | 
| 206 |                                                bool enable_hrtf) { | 
| 207 |   DCHECK(source_nodes_.find(sound_object_source_id) == source_nodes_.end()); | 
| 208 |   // Create a new |sound_object_source_node| and register to |source_nodes_|. | 
| 209 |   auto sound_object_source_node = std::make_shared<BufferedSourceNode>( | 
| 210 |       args&: sound_object_source_id, args: kNumMonoChannels, | 
| 211 |       args: system_settings_.GetFramesPerBuffer()); | 
| 212 |   source_nodes_[sound_object_source_id] = sound_object_source_node; | 
| 213 |  | 
| 214 |   if (enable_hrtf) { | 
| 215 |     ambisonic_mixing_encoder_nodes_[config_.max_ambisonic_order]->Connect( | 
| 216 |         publisher_node: sound_object_source_node); | 
| 217 |   } else { | 
| 218 |     stereo_mixing_panner_node_->Connect(publisher_node: sound_object_source_node); | 
| 219 |   } | 
| 220 | } | 
| 221 |  | 
| 222 | void GraphManager::CreateStereoSource(SourceId stereo_source_id) { | 
| 223 |   DCHECK(source_nodes_.find(stereo_source_id) == source_nodes_.end()); | 
| 224 |   // Create a new |stereo_source_node| and register to |source_nodes_|. | 
| 225 |   auto stereo_source_node = std::make_shared<BufferedSourceNode>( | 
| 226 |       args&: stereo_source_id, args: kNumStereoChannels, | 
| 227 |       args: system_settings_.GetFramesPerBuffer()); | 
| 228 |   source_nodes_[stereo_source_id] = stereo_source_node; | 
| 229 |  | 
| 230 |   // Connect |stereo_source_node| to the stereo rendering pipeline. | 
| 231 |   auto gain_node = | 
| 232 |       std::make_shared<GainNode>(args&: stereo_source_id, args: kNumStereoChannels, | 
| 233 |                                  args: AttenuationType::kInput, args: system_settings_); | 
| 234 |   gain_node->Connect(publisher_node: stereo_source_node); | 
| 235 |   stereo_mixer_node_->Connect(publisher_node: gain_node); | 
| 236 | } | 
| 237 |  | 
| 238 | void GraphManager::DestroySource(SourceId source_id) { | 
| 239 |   auto source_node = LookupSourceNode(source_id); | 
| 240 |   if (source_node != nullptr) { | 
| 241 |     // Disconnect the source from the graph. | 
| 242 |     source_node->MarkEndOfStream(); | 
| 243 |     output_node_->CleanUp(); | 
| 244 |     // Unregister the source from |source_nodes_|. | 
| 245 |     source_nodes_.erase(x: source_id); | 
| 246 |   } | 
| 247 | } | 
| 248 |  | 
| 249 | std::shared_ptr<SinkNode> GraphManager::GetSinkNode() { return output_node_; } | 
| 250 |  | 
| 251 | void GraphManager::Process() { | 
| 252 |  | 
| 253 |   output_node_->ReadInputs(); | 
| 254 | } | 
| 255 |  | 
| 256 | AudioBuffer* GraphManager::GetMutableAudioBuffer(SourceId source_id) { | 
| 257 |   auto source_node = LookupSourceNode(source_id); | 
| 258 |   if (source_node == nullptr) { | 
| 259 |     return nullptr; | 
| 260 |   } | 
| 261 |   return source_node->GetMutableAudioBufferAndSetNewBufferFlag(); | 
| 262 | } | 
| 263 |  | 
| 264 | void GraphManager::InitializeAmbisonicRendererGraph( | 
| 265 |     int ambisonic_order, const std::string& sh_hrir_filename) { | 
| 266 |   CHECK_LE(ambisonic_order, config_.max_ambisonic_order); | 
| 267 |   const size_t num_channels = GetNumPeriphonicComponents(ambisonic_order); | 
| 268 |   // Create binaural decoder pipeline. | 
| 269 |   ambisonic_mixer_nodes_[ambisonic_order] = | 
| 270 |       std::make_shared<MixerNode>(args: system_settings_, args: num_channels); | 
| 271 |   auto ambisonic_binaural_decoder_node = | 
| 272 |       std::make_shared<AmbisonicBinauralDecoderNode>( | 
| 273 |           args: system_settings_, args&: ambisonic_order, args: sh_hrir_filename, args: &fft_manager_, | 
| 274 |           args: &resampler_); | 
| 275 |   ambisonic_binaural_decoder_node->Connect( | 
| 276 |       publisher_node: ambisonic_mixer_nodes_[ambisonic_order]); | 
| 277 |   stereo_mixer_node_->Connect(publisher_node: ambisonic_binaural_decoder_node); | 
| 278 | } | 
| 279 |  | 
| 280 | std::shared_ptr<BufferedSourceNode> GraphManager::LookupSourceNode( | 
| 281 |     SourceId source_id) { | 
| 282 |   auto source_node_itr = source_nodes_.find(x: source_id); | 
| 283 |   if (source_node_itr == source_nodes_.end()) { | 
| 284 |     LOG(WARNING) << "Source node "  << source_id << " not found" ; | 
| 285 |     return nullptr; | 
| 286 |   } | 
| 287 |   return source_node_itr->second; | 
| 288 | } | 
| 289 |  | 
| 290 | }  // namespace vraudio | 
| 291 |  |