aboutsummaryrefslogtreecommitdiff
path: root/src/audio_core/renderer/splitter/splitter_destinations_data.h
blob: 6bfe5e0e3462a4b0866853581195abc54493937d (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <span>

#include "audio_core/common/common.h"
#include "common/common_types.h"

namespace AudioCore::Renderer {
/**
 * Represents a mixing node, can be connected to a previous and next destination forming a chain
 * that a certain mix buffer will pass through to output.
 */
class SplitterDestinationData {
public:
    /**
     * Biquad filter parameter with float coefficients (SDK REV15+).
     * Defined here to avoid circular dependency with VoiceInfo.
     */
    struct BiquadFilterParameter2 {
        /* 0x00 */ bool enabled;
        /* 0x01 */ u8 reserved1;
        /* 0x02 */ u8 reserved2;
        /* 0x03 */ u8 reserved3;
        /* 0x04 */ std::array<f32, 3> numerator;   // b0, b1, b2
        /* 0x10 */ std::array<f32, 2> denominator; // a1, a2 (a0 = 1)
    };
    static_assert(sizeof(BiquadFilterParameter2) == 0x18,
                  "BiquadFilterParameter2 has the wrong size!");

    /**
     * Legacy biquad filter parameter with fixed-point coefficients (SDK REV12+ for splitters).
     * Matches the old voice biquad format.
     */
    struct BiquadFilterParameterLegacy {
        /* 0x00 */ bool enabled;
        /* 0x02 */ std::array<s16, 3> b; // numerator
        /* 0x08 */ std::array<s16, 2> a; // denominator (a0 = 1)
    };
    static_assert(sizeof(BiquadFilterParameterLegacy) == 0xC,
                  "BiquadFilterParameterLegacy has the wrong size!");

    struct InParameter {
        /* 0x00 */ u32 magic; // 'SNDD'
        /* 0x04 */ s32 id;
        /* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes;
        /* 0x68 */ u32 mix_id;
        /* 0x6C */ bool in_use;
        /* 0x6D */ bool reset_prev_volume;
    };
    static_assert(sizeof(InParameter) == 0x70,
                  "SplitterDestinationData::InParameter has the wrong size!");

    struct InParameterVersion2a {
        /* 0x00 */ u32 magic; // 'SNDD'
        /* 0x04 */ s32 id;
        /* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes;
        /* 0x68 */ u32 mix_id;
        /* 0x6C */ std::array<SplitterDestinationData::BiquadFilterParameterLegacy, MaxBiquadFilters>
            biquad_filters;
        /* 0x84 */ bool in_use;
        /* 0x85 */ bool reset_prev_volume; // only effective if supported
        /* 0x86 */ u8 reserved[10];
    };
    static_assert(sizeof(InParameterVersion2a) == 0x90,
                  "SplitterDestinationData::InParameterVersion2a has the wrong size!");

    struct InParameterVersion2b {
        /* 0x00 */ u32 magic; // 'SNDD'
        /* 0x04 */ s32 id;
        /* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes;
        /* 0x68 */ u32 mix_id;
        /* 0x6C */ std::array<SplitterDestinationData::BiquadFilterParameter2, MaxBiquadFilters>
            biquad_filters;
        /* 0x9C */ bool in_use;
        /* 0x9D */ bool reset_prev_volume;
        /* 0x9E */ u8 reserved[10];
    };
    static_assert(sizeof(InParameterVersion2b) == 0xA8,
                  "SplitterDestinationData::InParameterVersion2b has the wrong size!");

    SplitterDestinationData(s32 id);

    /**
     * Reset the mix volumes for this destination.
     */
    void ClearMixVolume();

    /**
     * Get the id of this destination.
     *
     * @return Id for this destination.
     */
    s32 GetId() const;

    /**
     * Check if this destination is correctly configured.
     *
     * @return True if configured, otherwise false.
     */
    bool IsConfigured() const;

    /**
     * Get the mix id for this destination.
     *
     * @return Mix id for this destination.
     */
    s32 GetMixId() const;

    /**
     * Get the current mix volume of a given index in this destination.
     *
     * @param index - Mix buffer index to get the volume for.
     * @return Current volume of the specified mix.
     */
    f32 GetMixVolume(u32 index) const;

    /**
     * Get the current mix volumes for all mix buffers in this destination.
     *
     * @return Span of current mix buffer volumes.
     */
    std::span<f32> GetMixVolume();

    /**
     * Get the previous mix volume of a given index in this destination.
     *
     * @param index - Mix buffer index to get the volume for.
     * @return Previous volume of the specified mix.
     */
    f32 GetMixVolumePrev(u32 index) const;

    /**
     * Get the previous mix volumes for all mix buffers.
     *
     * @return Span of previous mix buffer volumes.
     */
    std::span<f32> GetMixVolumePrev();

    /**
     * Update this destination.
     *
     * @param params - Input parameters to update the destination.
     */
    void Update(const InParameter& params);

    /**
     * Mark this destination as needing its volumes updated.
     */
    void MarkAsNeedToUpdateInternalState();

    /**
     * Copy current volumes to previous if an update is required.
     */
    void UpdateInternalState();

    /**
     * Get the next destination in the mix chain.
     *
     * @return The next splitter destination, may be nullptr if this is the last in the chain.
     */
    SplitterDestinationData* GetNext() const;

    /**
     * Set the next destination in the mix chain.
     *
     * @param next - Destination this one is to be connected to.
     */
    void SetNext(SplitterDestinationData* next);

    /**
     * Get biquad filter parameters for this destination (REV15+ or mapped from REV12).
     *
     * @return Span of biquad filter parameters.
     */
    std::span<BiquadFilterParameter2> GetBiquadFilters();

    /**
     * Get const biquad filter parameters for this destination (REV15+ or mapped from REV12).
     *
     * @return Const span of biquad filter parameters.
     */
    std::span<const BiquadFilterParameter2> GetBiquadFilters() const;

private:
    /// Id of this destination
    const s32 id;
    /// Mix id this destination represents
    s32 destination_id{UnusedMixId};
    /// Current mix volumes
    std::array<f32, MaxMixBuffers> mix_volumes{0.0f};
    /// Previous mix volumes
    std::array<f32, MaxMixBuffers> prev_mix_volumes{0.0f};
    /// Biquad filter parameters (REV15+ or mapped from REV12)
    std::array<BiquadFilterParameter2, MaxBiquadFilters> biquad_filters{};
    /// Next destination in the mix chain
    SplitterDestinationData* next{};
    /// Is this destination in use?
    bool in_use{};
    /// Does this destination need its volumes updated?
    bool need_update{};
};

} // namespace AudioCore::Renderer