summaryrefslogtreecommitdiffstats
path: root/src/audio_core/renderer/mix/mix_info.cpp
blob: cc18e57eef3fde0573217278e1715fa744730bf5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "audio_core/renderer/behavior/behavior_info.h"
#include "audio_core/renderer/effect/effect_context.h"
#include "audio_core/renderer/mix/mix_info.h"
#include "audio_core/renderer/nodes/edge_matrix.h"
#include "audio_core/renderer/splitter/splitter_context.h"

namespace AudioCore::AudioRenderer {

MixInfo::MixInfo(std::span<s32> effect_order_buffer_, s32 effect_count_, BehaviorInfo& behavior)
    : effect_order_buffer{effect_order_buffer_}, effect_count{effect_count_},
      long_size_pre_delay_supported{behavior.IsLongSizePreDelaySupported()} {
    ClearEffectProcessingOrder();
}

void MixInfo::Cleanup() {
    mix_id = UnusedMixId;
    dst_mix_id = UnusedMixId;
    dst_splitter_id = UnusedSplitterId;
}

void MixInfo::ClearEffectProcessingOrder() {
    for (s32 i = 0; i < effect_count; i++) {
        effect_order_buffer[i] = -1;
    }
}

bool MixInfo::Update(EdgeMatrix& edge_matrix, const InParameter& in_params,
                     EffectContext& effect_context, SplitterContext& splitter_context,
                     const BehaviorInfo& behavior) {
    volume = in_params.volume;
    sample_rate = in_params.sample_rate;
    buffer_count = static_cast<s16>(in_params.buffer_count);
    in_use = in_params.in_use;
    mix_id = in_params.mix_id;
    node_id = in_params.node_id;
    mix_volumes = in_params.mix_volumes;

    bool sort_required{false};
    if (behavior.IsSplitterSupported()) {
        sort_required = UpdateConnection(edge_matrix, in_params, splitter_context);
    } else {
        if (dst_mix_id != in_params.dest_mix_id) {
            dst_mix_id = in_params.dest_mix_id;
            sort_required = true;
        }
        dst_splitter_id = UnusedSplitterId;
    }

    ClearEffectProcessingOrder();

    // Check all effects, and set their order if they belong to this mix.
    const auto count{effect_context.GetCount()};
    for (u32 i = 0; i < count; i++) {
        const auto& info{effect_context.GetInfo(i)};
        if (mix_id == info.GetMixId()) {
            const auto processing_order{info.GetProcessingOrder()};
            if (processing_order > effect_count) {
                break;
            }
            effect_order_buffer[processing_order] = i;
        }
    }

    return sort_required;
}

bool MixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const InParameter& in_params,
                               SplitterContext& splitter_context) {
    auto has_new_connection{false};
    if (dst_splitter_id != UnusedSplitterId) {
        auto& splitter_info{splitter_context.GetInfo(dst_splitter_id)};
        has_new_connection = splitter_info.HasNewConnection();
    }

    // Check if this mix matches the input parameters.
    // If everything is the same, don't bother updating.
    if (dst_mix_id == in_params.dest_mix_id && dst_splitter_id == in_params.dest_splitter_id &&
        !has_new_connection) {
        return false;
    }

    // Reset the mix in the graph, as we're about to update it.
    edge_matrix.RemoveEdges(mix_id);

    if (in_params.dest_mix_id == UnusedMixId) {
        if (in_params.dest_splitter_id != UnusedSplitterId) {
            // If the splitter is used, connect this mix to each active destination.
            auto& splitter_info{splitter_context.GetInfo(in_params.dest_splitter_id)};
            auto const destination_count{splitter_info.GetDestinationCount()};

            for (u32 i = 0; i < destination_count; i++) {
                auto destination{
                    splitter_context.GetDesintationData(in_params.dest_splitter_id, i)};

                if (destination) {
                    const auto destination_id{destination->GetMixId()};
                    if (destination_id != UnusedMixId) {
                        edge_matrix.Connect(mix_id, destination_id);
                    }
                }
            }
        }
    } else {
        // If the splitter is not used, only connect this mix to its destination.
        edge_matrix.Connect(mix_id, in_params.dest_mix_id);
    }

    dst_mix_id = in_params.dest_mix_id;
    dst_splitter_id = in_params.dest_splitter_id;
    return true;
}

bool MixInfo::HasAnyConnection() const {
    return dst_mix_id != UnusedMixId || dst_splitter_id != UnusedSplitterId;
}

} // namespace AudioCore::AudioRenderer