summaryrefslogtreecommitdiffstats
path: root/src/audio_core/renderer/command/effect/capture.cpp
blob: 042fd286e4a9f9e2d820c9d592947619663d2a48 (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
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "audio_core/renderer/adsp/command_list_processor.h"
#include "audio_core/renderer/command/effect/capture.h"
#include "audio_core/renderer/effect/aux_.h"
#include "core/memory.h"

namespace AudioCore::AudioRenderer {
/**
 * Reset an AuxBuffer.
 *
 * @param memory   - Core memory for writing.
 * @param aux_info - Memory address pointing to the AuxInfo to reset.
 */
static void ResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_info) {
    if (aux_info == 0) {
        LOG_ERROR(Service_Audio, "Aux info is 0!");
        return;
    }

    memory.Write32(VAddr(aux_info + offsetof(AuxInfo::AuxInfoDsp, read_offset)), 0);
    memory.Write32(VAddr(aux_info + offsetof(AuxInfo::AuxInfoDsp, write_offset)), 0);
    memory.Write32(VAddr(aux_info + offsetof(AuxInfo::AuxInfoDsp, total_sample_count)), 0);
}

/**
 * Write the given input mix buffer to the memory at send_buffer, and update send_info_ if
 * update_count is set, to notify the game that an update happened.
 *
 * @param memory       - Core memory for writing.
 * @param send_info_   - Header information for where to write the mix buffer.
 * @param send_buffer  - Memory address to write the mix buffer to.
 * @param count_max    - Maximum number of samples in the receiving buffer.
 * @param input        - Input mix buffer to write.
 * @param write_count_ - Number of samples to write.
 * @param write_offset - Current offset to begin writing the receiving buffer at.
 * @param update_count - If non-zero, send_info_ will be updated.
 * @return Number of samples written.
 */
static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr send_info_,
                             const CpuAddr send_buffer, u32 count_max, std::span<const s32> input,
                             const u32 write_count_, const u32 write_offset,
                             const u32 update_count) {
    if (write_count_ > count_max) {
        LOG_ERROR(Service_Audio,
                  "write_count must be smaller than count_max! write_count {}, count_max {}",
                  write_count_, count_max);
        return 0;
    }

    if (send_info_ == 0) {
        LOG_ERROR(Service_Audio, "send_info is 0!");
        return 0;
    }

    if (input.empty()) {
        LOG_ERROR(Service_Audio, "input buffer is empty!");
        return 0;
    }

    if (send_buffer == 0) {
        LOG_ERROR(Service_Audio, "send_buffer is 0!");
        return 0;
    }

    if (count_max == 0) {
        return 0;
    }

    AuxInfo::AuxBufferInfo send_info{};
    memory.ReadBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxBufferInfo));

    u32 target_write_offset{send_info.dsp_info.write_offset + write_offset};
    if (target_write_offset > count_max || write_count_ == 0) {
        return 0;
    }

    u32 write_count{write_count_};
    u32 write_pos{0};
    while (write_count > 0) {
        u32 to_write{std::min(count_max - target_write_offset, write_count)};

        if (to_write > 0) {
            memory.WriteBlockUnsafe(send_buffer + target_write_offset * sizeof(s32),
                                    &input[write_pos], to_write * sizeof(s32));
        }

        target_write_offset = (target_write_offset + to_write) % count_max;
        write_count -= to_write;
        write_pos += to_write;
    }

    if (update_count) {
        const auto count_diff{send_info.dsp_info.total_sample_count -
                              send_info.cpu_info.total_sample_count};
        if (count_diff >= count_max) {
            auto dsp_lost_count{send_info.dsp_info.lost_sample_count + update_count};
            if (dsp_lost_count - send_info.cpu_info.lost_sample_count <
                send_info.dsp_info.lost_sample_count - send_info.cpu_info.lost_sample_count) {
                dsp_lost_count = send_info.cpu_info.lost_sample_count - 1;
            }
            send_info.dsp_info.lost_sample_count = dsp_lost_count;
        }

        send_info.dsp_info.write_offset =
            (send_info.dsp_info.write_offset + update_count + count_max) % count_max;

        auto new_sample_count{send_info.dsp_info.total_sample_count + update_count};
        if (new_sample_count - send_info.cpu_info.total_sample_count < count_diff) {
            new_sample_count = send_info.cpu_info.total_sample_count - 1;
        }
        send_info.dsp_info.total_sample_count = new_sample_count;
    }

    memory.WriteBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxBufferInfo));

    return write_count_;
}

void CaptureCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
                          std::string& string) {
    string += fmt::format("CaptureCommand\n\tenabled {} input {:02X} output {:02X}", effect_enabled,
                          input, output);
}

void CaptureCommand::Process(const ADSP::CommandListProcessor& processor) {
    if (effect_enabled) {
        auto input_buffer{
            processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)};
        WriteAuxBufferDsp(*processor.memory, send_buffer_info, send_buffer, count_max, input_buffer,
                          processor.sample_count, write_offset, update_count);
    } else {
        ResetAuxBufferDsp(*processor.memory, send_buffer_info);
    }
}

bool CaptureCommand::Verify(const ADSP::CommandListProcessor& processor) {
    return true;
}

} // namespace AudioCore::AudioRenderer