Source code
Revision control
Copy as Markdown
/*
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
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/graph_manager.h"
#include <functional>
#include "ambisonics/utils.h"
#include "base/constants_and_types.h"
#include "base/logging.h"
#include "graph/foa_rotator_node.h"
#include "graph/gain_node.h"
#include "graph/hoa_rotator_node.h"
#include "graph/mono_from_soundfield_node.h"
#include "graph/near_field_effect_node.h"
#include "graph/occlusion_node.h"
namespace vraudio {
GraphManager::GraphManager(const SystemSettings& system_settings)
:
room_effects_enabled_(true),
config_(GlobalConfig()),
system_settings_(system_settings),
fft_manager_(system_settings.GetFramesPerBuffer()),
output_node_(std::make_shared<SinkNode>()) {
CHECK_LE(system_settings.GetFramesPerBuffer(), kMaxSupportedNumFrames);
stereo_mixer_node_ =
std::make_shared<MixerNode>(system_settings_, kNumStereoChannels);
output_node_->Connect(stereo_mixer_node_);
/// Initialize the Ambisonic Lookup Table.
lookup_table_.reset(new AmbisonicLookupTable(config_.max_ambisonic_order));
// Initialize the Ambisonic Renderer subgraphs.
for (const auto& sh_hrir_filename_itr : config_.sh_hrir_filenames) {
const int ambisonic_order = sh_hrir_filename_itr.first;
const auto& sh_hrir_filename = sh_hrir_filename_itr.second;
InitializeAmbisonicRendererGraph(ambisonic_order, sh_hrir_filename);
// Initialize the Ambisonic Mixing Encoders for HRTF sound object rendering.
ambisonic_mixing_encoder_nodes_[ambisonic_order] =
std::make_shared<AmbisonicMixingEncoderNode>(
system_settings_, *lookup_table_, ambisonic_order);
ambisonic_mixer_nodes_[ambisonic_order]->Connect(
ambisonic_mixing_encoder_nodes_[ambisonic_order]);
}
// Stereo mixing panner node used in non-HRTF sound object rendering.
stereo_mixing_panner_node_ =
std::make_shared<StereoMixingPannerNode>(system_settings_);
stereo_mixer_node_->Connect(stereo_mixing_panner_node_);
// Initialize room effects graphs.
InitializeReflectionsGraph();
InitializeReverbGraph();
// Initialize ambisonic output mixer.
ambisonic_output_mixer_.reset(
new Mixer(GetNumPeriphonicComponents(config_.max_ambisonic_order),
system_settings.GetFramesPerBuffer()));
}
void GraphManager::CreateAmbisonicSource(SourceId ambisonic_source_id,
size_t num_channels) {
DCHECK(source_nodes_.find(ambisonic_source_id) == source_nodes_.end());
// Create a new |ambisonic_source_node| and register to |source_nodes_|.
auto ambisonic_source_node = std::make_shared<BufferedSourceNode>(
ambisonic_source_id, num_channels, system_settings_.GetFramesPerBuffer());
source_nodes_[ambisonic_source_id] = ambisonic_source_node;
// Connect |ambisonic_source_node| to the ambisonic decoding pipeline.
const int ambisonic_order = GetPeriphonicAmbisonicOrder(num_channels);
auto direct_attenuation_node =
std::make_shared<GainNode>(ambisonic_source_id, num_channels,
AttenuationType::kDirect, system_settings_);
direct_attenuation_node->Connect(ambisonic_source_node);
if (ambisonic_order == 1) {
// First order case.
auto foa_rotator_node =
std::make_shared<FoaRotatorNode>(ambisonic_source_id, system_settings_);
foa_rotator_node->Connect(direct_attenuation_node);
ambisonic_mixer_nodes_[ambisonic_order]->Connect(foa_rotator_node);
} else {
// Higher orders case.
auto hoa_rotator_node = std::make_shared<HoaRotatorNode>(
ambisonic_source_id, system_settings_, ambisonic_order);
hoa_rotator_node->Connect(direct_attenuation_node);
ambisonic_mixer_nodes_[ambisonic_order]->Connect(hoa_rotator_node);
}
// Connect to room effects rendering pipeline.
auto mono_from_soundfield_node = std::make_shared<MonoFromSoundfieldNode>(
ambisonic_source_id, system_settings_);
mono_from_soundfield_node->Connect(ambisonic_source_node);
reflections_gain_mixer_node_->Connect(mono_from_soundfield_node);
reverb_gain_mixer_node_->Connect(mono_from_soundfield_node);
}
void GraphManager::CreateSoundObjectSource(SourceId sound_object_source_id,
int ambisonic_order,
bool enable_hrtf,
bool enable_direct_rendering) {
DCHECK(source_nodes_.find(sound_object_source_id) == source_nodes_.end());
// Create a new |sound_object_source_node| and register to |source_nodes_|.
auto sound_object_source_node = std::make_shared<BufferedSourceNode>(
sound_object_source_id, kNumMonoChannels,
system_settings_.GetFramesPerBuffer());
source_nodes_[sound_object_source_id] = sound_object_source_node;
// Create direct rendering pipeline.
if (enable_direct_rendering) {
auto direct_attenuation_node =
std::make_shared<GainNode>(sound_object_source_id, kNumMonoChannels,
AttenuationType::kDirect, system_settings_);
direct_attenuation_node->Connect(sound_object_source_node);
auto occlusion_node = std::make_shared<OcclusionNode>(
sound_object_source_id, system_settings_);
occlusion_node->Connect(direct_attenuation_node);
auto near_field_effect_node = std::make_shared<NearFieldEffectNode>(
sound_object_source_id, system_settings_);
if (enable_hrtf) {
ambisonic_mixing_encoder_nodes_[ambisonic_order]->Connect(occlusion_node);
} else {
stereo_mixing_panner_node_->Connect(occlusion_node);
}
near_field_effect_node->Connect(occlusion_node);
stereo_mixer_node_->Connect(near_field_effect_node);
}
// Connect to room effects rendering pipeline.
reflections_gain_mixer_node_->Connect(sound_object_source_node);
reverb_gain_mixer_node_->Connect(sound_object_source_node);
}
void GraphManager::EnableRoomEffects(bool enable) {
room_effects_enabled_ = enable;
reflections_gain_mixer_node_->SetMute(!room_effects_enabled_);
reverb_gain_mixer_node_->SetMute(!room_effects_enabled_);
}
const AudioBuffer* GraphManager::GetAmbisonicBuffer() const {
ambisonic_output_mixer_->Reset();
for (const auto& ambisonic_mixer_node_itr : ambisonic_mixer_nodes_) {
const auto* ambisonic_buffer =
ambisonic_mixer_node_itr.second->GetOutputBuffer();
if (ambisonic_buffer != nullptr) {
ambisonic_output_mixer_->AddInput(*ambisonic_buffer);
}
}
return ambisonic_output_mixer_->GetOutput();
}
const AudioBuffer* GraphManager::GetStereoBuffer() const {
return stereo_mixer_node_->GetOutputBuffer();
}
const AudioBuffer *GraphManager::GetReverbBuffer() const
{
return reverb_node_->GetOutputBuffer();
}
size_t GraphManager::GetNumMaxAmbisonicChannels() const {
return GetNumPeriphonicComponents(config_.max_ambisonic_order);
}
bool GraphManager::GetRoomEffectsEnabled() const {
return room_effects_enabled_;
}
void GraphManager::UpdateRoomReflections() { reflections_node_->Update(); }
void GraphManager::UpdateRoomReverb() { reverb_node_->Update(); }
void GraphManager::InitializeReverbGraph() {
reverb_gain_mixer_node_ = std::make_shared<GainMixerNode>(
AttenuationType::kReverb, system_settings_, kNumMonoChannels);
reverb_node_ = std::make_shared<ReverbNode>(system_settings_, &fft_manager_);
reverb_node_->Connect(reverb_gain_mixer_node_);
stereo_mixer_node_->Connect(reverb_node_);
}
void GraphManager::InitializeReflectionsGraph() {
reflections_gain_mixer_node_ = std::make_shared<GainMixerNode>(
AttenuationType::kReflections, system_settings_, kNumMonoChannels);
reflections_node_ = std::make_shared<ReflectionsNode>(system_settings_);
reflections_node_->Connect(reflections_gain_mixer_node_);
// Reflections are limited to First Order Ambisonics to reduce complexity.
const int kAmbisonicOrder1 = 1;
ambisonic_mixer_nodes_[kAmbisonicOrder1]->Connect(reflections_node_);
}
void GraphManager::CreateAmbisonicPannerSource(SourceId sound_object_source_id,
bool enable_hrtf) {
DCHECK(source_nodes_.find(sound_object_source_id) == source_nodes_.end());
// Create a new |sound_object_source_node| and register to |source_nodes_|.
auto sound_object_source_node = std::make_shared<BufferedSourceNode>(
sound_object_source_id, kNumMonoChannels,
system_settings_.GetFramesPerBuffer());
source_nodes_[sound_object_source_id] = sound_object_source_node;
if (enable_hrtf) {
ambisonic_mixing_encoder_nodes_[config_.max_ambisonic_order]->Connect(
sound_object_source_node);
} else {
stereo_mixing_panner_node_->Connect(sound_object_source_node);
}
}
void GraphManager::CreateStereoSource(SourceId stereo_source_id) {
DCHECK(source_nodes_.find(stereo_source_id) == source_nodes_.end());
// Create a new |stereo_source_node| and register to |source_nodes_|.
auto stereo_source_node = std::make_shared<BufferedSourceNode>(
stereo_source_id, kNumStereoChannels,
system_settings_.GetFramesPerBuffer());
source_nodes_[stereo_source_id] = stereo_source_node;
// Connect |stereo_source_node| to the stereo rendering pipeline.
auto gain_node =
std::make_shared<GainNode>(stereo_source_id, kNumStereoChannels,
AttenuationType::kInput, system_settings_);
gain_node->Connect(stereo_source_node);
stereo_mixer_node_->Connect(gain_node);
}
void GraphManager::DestroySource(SourceId source_id) {
auto source_node = LookupSourceNode(source_id);
if (source_node != nullptr) {
// Disconnect the source from the graph.
source_node->MarkEndOfStream();
output_node_->CleanUp();
// Unregister the source from |source_nodes_|.
source_nodes_.erase(source_id);
}
}
std::shared_ptr<SinkNode> GraphManager::GetSinkNode() { return output_node_; }
void GraphManager::Process() {
output_node_->ReadInputs();
}
AudioBuffer* GraphManager::GetMutableAudioBuffer(SourceId source_id) {
auto source_node = LookupSourceNode(source_id);
if (source_node == nullptr) {
return nullptr;
}
return source_node->GetMutableAudioBufferAndSetNewBufferFlag();
}
void GraphManager::InitializeAmbisonicRendererGraph(
int ambisonic_order, const std::string& sh_hrir_filename) {
CHECK_LE(ambisonic_order, config_.max_ambisonic_order);
const size_t num_channels = GetNumPeriphonicComponents(ambisonic_order);
// Create binaural decoder pipeline.
ambisonic_mixer_nodes_[ambisonic_order] =
std::make_shared<MixerNode>(system_settings_, num_channels);
auto ambisonic_binaural_decoder_node =
std::make_shared<AmbisonicBinauralDecoderNode>(
system_settings_, ambisonic_order, sh_hrir_filename, &fft_manager_,
&resampler_);
ambisonic_binaural_decoder_node->Connect(
ambisonic_mixer_nodes_[ambisonic_order]);
stereo_mixer_node_->Connect(ambisonic_binaural_decoder_node);
}
std::shared_ptr<BufferedSourceNode> GraphManager::LookupSourceNode(
SourceId source_id) {
auto source_node_itr = source_nodes_.find(source_id);
if (source_node_itr == source_nodes_.end()) {
LOG(WARNING) << "Source node " << source_id << " not found";
return nullptr;
}
return source_node_itr->second;
}
} // namespace vraudio