mirror of
				https://git.suyu.dev/suyu/suyu.git
				synced 2025-11-04 12:34:39 +08:00 
			
		
		
		
	Merge pull request #6498 from Kelebek1/Audio
[audio_core] Decouple audio update and processing, and process at variable rate
This commit is contained in:
		
						commit
						2fc0a760f0
					
				@ -12,6 +12,7 @@
 | 
			
		||||
#include "audio_core/voice_context.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/settings.h"
 | 
			
		||||
#include "core/core_timing.h"
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
@ -68,7 +69,9 @@ namespace {
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
 | 
			
		||||
constexpr s32 NUM_BUFFERS = 2;
 | 
			
		||||
 | 
			
		||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_,
 | 
			
		||||
                             AudioCommon::AudioRendererParameter params,
 | 
			
		||||
                             Stream::ReleaseCallback&& release_callback,
 | 
			
		||||
                             std::size_t instance_number)
 | 
			
		||||
@ -77,7 +80,8 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
 | 
			
		||||
      sink_context(params.sink_count), splitter_context(),
 | 
			
		||||
      voices(params.voice_count), memory{memory_},
 | 
			
		||||
      command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context,
 | 
			
		||||
                        memory) {
 | 
			
		||||
                        memory),
 | 
			
		||||
      core_timing{core_timing_} {
 | 
			
		||||
    behavior_info.SetUserRevision(params.revision);
 | 
			
		||||
    splitter_context.Initialize(behavior_info, params.splitter_count,
 | 
			
		||||
                                params.num_splitter_send_channels);
 | 
			
		||||
@ -86,16 +90,27 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
 | 
			
		||||
    stream = audio_out->OpenStream(
 | 
			
		||||
        core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
 | 
			
		||||
        fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
 | 
			
		||||
    audio_out->StartStream(stream);
 | 
			
		||||
 | 
			
		||||
    QueueMixedBuffer(0);
 | 
			
		||||
    QueueMixedBuffer(1);
 | 
			
		||||
    QueueMixedBuffer(2);
 | 
			
		||||
    QueueMixedBuffer(3);
 | 
			
		||||
    process_event = Core::Timing::CreateEvent(
 | 
			
		||||
        fmt::format("AudioRenderer-Instance{}-Process", instance_number),
 | 
			
		||||
        [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); });
 | 
			
		||||
    for (s32 i = 0; i < NUM_BUFFERS; ++i) {
 | 
			
		||||
        QueueMixedBuffer(i);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioRenderer::~AudioRenderer() = default;
 | 
			
		||||
 | 
			
		||||
ResultCode AudioRenderer::Start() {
 | 
			
		||||
    audio_out->StartStream(stream);
 | 
			
		||||
    ReleaseAndQueueBuffers();
 | 
			
		||||
    return ResultSuccess;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode AudioRenderer::Stop() {
 | 
			
		||||
    audio_out->StopStream(stream);
 | 
			
		||||
    return ResultSuccess;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u32 AudioRenderer::GetSampleRate() const {
 | 
			
		||||
    return worker_params.sample_rate;
 | 
			
		||||
}
 | 
			
		||||
@ -114,7 +129,7 @@ Stream::State AudioRenderer::GetStreamState() const {
 | 
			
		||||
 | 
			
		||||
ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
 | 
			
		||||
                                              std::vector<u8>& output_params) {
 | 
			
		||||
 | 
			
		||||
    std::scoped_lock lock{mutex};
 | 
			
		||||
    InfoUpdater info_updater{input_params, output_params, behavior_info};
 | 
			
		||||
 | 
			
		||||
    if (!info_updater.UpdateBehaviorInfo(behavior_info)) {
 | 
			
		||||
@ -194,9 +209,6 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
 | 
			
		||||
        LOG_ERROR(Audio, "Audio buffers were not consumed!");
 | 
			
		||||
        return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ReleaseAndQueueBuffers();
 | 
			
		||||
 | 
			
		||||
    return ResultSuccess;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -220,10 +232,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
 | 
			
		||||
    command_generator.PostCommand();
 | 
			
		||||
    // Base sample size
 | 
			
		||||
    std::size_t BUFFER_SIZE{worker_params.sample_count};
 | 
			
		||||
    // Samples
 | 
			
		||||
    std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
 | 
			
		||||
    // Make sure to clear our samples
 | 
			
		||||
    std::memset(buffer.data(), 0, buffer.size() * sizeof(s16));
 | 
			
		||||
    // Samples, making sure to clear
 | 
			
		||||
    std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels(), 0);
 | 
			
		||||
 | 
			
		||||
    if (sink_context.InUse()) {
 | 
			
		||||
        const auto stream_channel_count = stream->GetNumChannels();
 | 
			
		||||
@ -315,10 +325,24 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioRenderer::ReleaseAndQueueBuffers() {
 | 
			
		||||
    const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)};
 | 
			
		||||
    for (const auto& tag : released_buffers) {
 | 
			
		||||
        QueueMixedBuffer(tag);
 | 
			
		||||
    if (!stream->IsPlaying()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        std::scoped_lock lock{mutex};
 | 
			
		||||
        const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)};
 | 
			
		||||
        for (const auto& tag : released_buffers) {
 | 
			
		||||
            QueueMixedBuffer(tag);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const f32 sample_rate = static_cast<f32>(GetSampleRate());
 | 
			
		||||
    const f32 sample_count = static_cast<f32>(GetSampleCount());
 | 
			
		||||
    const f32 consume_rate = sample_rate / (sample_count * (sample_count / 240));
 | 
			
		||||
    const s32 ms = (1000 / static_cast<s32>(consume_rate)) - 1;
 | 
			
		||||
    const std::chrono::milliseconds next_event_time(std::max(ms / NUM_BUFFERS, 1));
 | 
			
		||||
    core_timing.ScheduleEvent(next_event_time, process_event, {});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/behavior_info.h"
 | 
			
		||||
@ -45,6 +46,8 @@ public:
 | 
			
		||||
 | 
			
		||||
    [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
 | 
			
		||||
                                                 std::vector<u8>& output_params);
 | 
			
		||||
    [[nodiscard]] ResultCode Start();
 | 
			
		||||
    [[nodiscard]] ResultCode Stop();
 | 
			
		||||
    void QueueMixedBuffer(Buffer::Tag tag);
 | 
			
		||||
    void ReleaseAndQueueBuffers();
 | 
			
		||||
    [[nodiscard]] u32 GetSampleRate() const;
 | 
			
		||||
@ -68,6 +71,9 @@ private:
 | 
			
		||||
    Core::Memory::Memory& memory;
 | 
			
		||||
    CommandGenerator command_generator;
 | 
			
		||||
    std::size_t elapsed_frame_count{};
 | 
			
		||||
    Core::Timing::CoreTiming& core_timing;
 | 
			
		||||
    std::shared_ptr<Core::Timing::EventType> process_event;
 | 
			
		||||
    std::mutex mutex;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
 | 
			
		||||
@ -795,7 +795,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta
 | 
			
		||||
        state.lowpass_1 = 0.0f;
 | 
			
		||||
    } else {
 | 
			
		||||
        const auto a = 1.0f - hf_gain;
 | 
			
		||||
        const auto b = 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference /
 | 
			
		||||
        const auto b = 2.0f * (2.0f - hf_gain * CosD(256.0f * info.hf_reference /
 | 
			
		||||
                                                     static_cast<f32>(info.sample_rate)));
 | 
			
		||||
        const auto c = std::sqrt(b * b - 4.0f * a * a);
 | 
			
		||||
 | 
			
		||||
@ -843,7 +843,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto max_early_delay = state.early_delay_line.GetMaxDelay();
 | 
			
		||||
    const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f);
 | 
			
		||||
    const auto reflection_time = 1000.0f * (0.9998f * info.reverb_delay + 0.02f);
 | 
			
		||||
    for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) {
 | 
			
		||||
        const auto length = AudioCommon::CalculateDelaySamples(
 | 
			
		||||
            sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]);
 | 
			
		||||
@ -1004,7 +1004,8 @@ void CommandGenerator::GenerateFinalMixCommand() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
 | 
			
		||||
                                  s32 sample_count, s32 channel, std::size_t mix_offset) {
 | 
			
		||||
                                  s32 sample_start_offset, s32 sample_end_offset, s32 sample_count,
 | 
			
		||||
                                  s32 channel, std::size_t mix_offset) {
 | 
			
		||||
    const auto& in_params = voice_info.GetInParams();
 | 
			
		||||
    const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
 | 
			
		||||
    if (wave_buffer.buffer_address == 0) {
 | 
			
		||||
@ -1013,14 +1014,12 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s
 | 
			
		||||
    if (wave_buffer.buffer_size == 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) {
 | 
			
		||||
    if (sample_end_offset < sample_start_offset) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    const auto samples_remaining =
 | 
			
		||||
        (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
 | 
			
		||||
    const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset;
 | 
			
		||||
    const auto start_offset =
 | 
			
		||||
        ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count) *
 | 
			
		||||
        sizeof(s16);
 | 
			
		||||
        ((dsp_state.offset + sample_start_offset) * in_params.channel_count) * sizeof(s16);
 | 
			
		||||
    const auto buffer_pos = wave_buffer.buffer_address + start_offset;
 | 
			
		||||
    const auto samples_processed = std::min(sample_count, samples_remaining);
 | 
			
		||||
 | 
			
		||||
@ -1044,8 +1043,8 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
 | 
			
		||||
                                  s32 sample_count, [[maybe_unused]] s32 channel,
 | 
			
		||||
                                  std::size_t mix_offset) {
 | 
			
		||||
                                  s32 sample_start_offset, s32 sample_end_offset, s32 sample_count,
 | 
			
		||||
                                  [[maybe_unused]] s32 channel, std::size_t mix_offset) {
 | 
			
		||||
    const auto& in_params = voice_info.GetInParams();
 | 
			
		||||
    const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
 | 
			
		||||
    if (wave_buffer.buffer_address == 0) {
 | 
			
		||||
@ -1054,7 +1053,7 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
 | 
			
		||||
    if (wave_buffer.buffer_size == 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) {
 | 
			
		||||
    if (sample_end_offset < sample_start_offset) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1079,10 +1078,9 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
 | 
			
		||||
    s32 coef1 = coeffs[idx * 2];
 | 
			
		||||
    s32 coef2 = coeffs[idx * 2 + 1];
 | 
			
		||||
 | 
			
		||||
    const auto samples_remaining =
 | 
			
		||||
        (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
 | 
			
		||||
    const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset;
 | 
			
		||||
    const auto samples_processed = std::min(sample_count, samples_remaining);
 | 
			
		||||
    const auto sample_pos = wave_buffer.start_sample_offset + dsp_state.offset;
 | 
			
		||||
    const auto sample_pos = dsp_state.offset + sample_start_offset;
 | 
			
		||||
 | 
			
		||||
    const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME;
 | 
			
		||||
    auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) +
 | 
			
		||||
@ -1210,9 +1208,8 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::size_t temp_mix_offset{};
 | 
			
		||||
    bool is_buffer_completed{false};
 | 
			
		||||
    auto samples_remaining = sample_count;
 | 
			
		||||
    while (samples_remaining > 0 && !is_buffer_completed) {
 | 
			
		||||
    while (samples_remaining > 0) {
 | 
			
		||||
        const auto samples_to_output = std::min(samples_remaining, min_required_samples);
 | 
			
		||||
        const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15;
 | 
			
		||||
 | 
			
		||||
@ -1229,24 +1226,38 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
 | 
			
		||||
            const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
 | 
			
		||||
            // No more data can be read
 | 
			
		||||
            if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) {
 | 
			
		||||
                is_buffer_completed = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 &&
 | 
			
		||||
                wave_buffer.context_address != 0 && wave_buffer.context_size != 0) {
 | 
			
		||||
                // TODO(ogniK): ADPCM loop context
 | 
			
		||||
                memory.ReadBlock(wave_buffer.context_address, &dsp_state.context,
 | 
			
		||||
                                 sizeof(ADPCMContext));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            s32 samples_offset_start;
 | 
			
		||||
            s32 samples_offset_end;
 | 
			
		||||
            if (dsp_state.loop_count > 0 && wave_buffer.loop_start_sample != 0 &&
 | 
			
		||||
                wave_buffer.loop_end_sample != 0 &&
 | 
			
		||||
                wave_buffer.loop_start_sample <= wave_buffer.loop_end_sample) {
 | 
			
		||||
                samples_offset_start = wave_buffer.loop_start_sample;
 | 
			
		||||
                samples_offset_end = wave_buffer.loop_end_sample;
 | 
			
		||||
            } else {
 | 
			
		||||
                samples_offset_start = wave_buffer.start_sample_offset;
 | 
			
		||||
                samples_offset_end = wave_buffer.end_sample_offset;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            s32 samples_decoded{0};
 | 
			
		||||
            switch (in_params.sample_format) {
 | 
			
		||||
            case SampleFormat::Pcm16:
 | 
			
		||||
                samples_decoded = DecodePcm16(voice_info, dsp_state, samples_to_read - samples_read,
 | 
			
		||||
                                              channel, temp_mix_offset);
 | 
			
		||||
                samples_decoded =
 | 
			
		||||
                    DecodePcm16(voice_info, dsp_state, samples_offset_start, samples_offset_end,
 | 
			
		||||
                                samples_to_read - samples_read, channel, temp_mix_offset);
 | 
			
		||||
                break;
 | 
			
		||||
            case SampleFormat::Adpcm:
 | 
			
		||||
                samples_decoded = DecodeAdpcm(voice_info, dsp_state, samples_to_read - samples_read,
 | 
			
		||||
                                              channel, temp_mix_offset);
 | 
			
		||||
                samples_decoded =
 | 
			
		||||
                    DecodeAdpcm(voice_info, dsp_state, samples_offset_start, samples_offset_end,
 | 
			
		||||
                                samples_to_read - samples_read, channel, temp_mix_offset);
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
 | 
			
		||||
@ -1257,15 +1268,19 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
 | 
			
		||||
            dsp_state.offset += samples_decoded;
 | 
			
		||||
            dsp_state.played_sample_count += samples_decoded;
 | 
			
		||||
 | 
			
		||||
            if (dsp_state.offset >=
 | 
			
		||||
                    (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) ||
 | 
			
		||||
            if (dsp_state.offset >= (samples_offset_end - samples_offset_start) ||
 | 
			
		||||
                samples_decoded == 0) {
 | 
			
		||||
                // Reset our sample offset
 | 
			
		||||
                dsp_state.offset = 0;
 | 
			
		||||
                if (wave_buffer.is_looping) {
 | 
			
		||||
                    if (samples_decoded == 0) {
 | 
			
		||||
                    dsp_state.loop_count++;
 | 
			
		||||
                    if (wave_buffer.loop_count > 0 &&
 | 
			
		||||
                        (dsp_state.loop_count > wave_buffer.loop_count || samples_decoded == 0)) {
 | 
			
		||||
                        // End of our buffer
 | 
			
		||||
                        is_buffer_completed = true;
 | 
			
		||||
                        voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (samples_decoded == 0) {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@ -1273,15 +1288,8 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
 | 
			
		||||
                        dsp_state.played_sample_count = 0;
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
 | 
			
		||||
                    // Update our wave buffer states
 | 
			
		||||
                    dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false;
 | 
			
		||||
                    dsp_state.wave_buffer_consumed++;
 | 
			
		||||
                    dsp_state.wave_buffer_index =
 | 
			
		||||
                        (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
 | 
			
		||||
                    if (wave_buffer.end_of_stream) {
 | 
			
		||||
                        dsp_state.played_sample_count = 0;
 | 
			
		||||
                    }
 | 
			
		||||
                    voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -86,10 +86,10 @@ private:
 | 
			
		||||
                               std::vector<u8>& work_buffer);
 | 
			
		||||
    void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear);
 | 
			
		||||
    // DSP Code
 | 
			
		||||
    s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
 | 
			
		||||
                    s32 channel, std::size_t mix_offset);
 | 
			
		||||
    s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
 | 
			
		||||
                    s32 channel, std::size_t mix_offset);
 | 
			
		||||
    s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset,
 | 
			
		||||
                    s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset);
 | 
			
		||||
    s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset,
 | 
			
		||||
                    s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset);
 | 
			
		||||
    void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state,
 | 
			
		||||
                               s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -189,9 +189,6 @@ bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
 | 
			
		||||
        if (voice_in_params.is_new) {
 | 
			
		||||
            // Default our values for our voice
 | 
			
		||||
            voice_info.Initialize();
 | 
			
		||||
            if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Zero out our voice states
 | 
			
		||||
            for (std::size_t channel = 0; channel < channel_count; channel++) {
 | 
			
		||||
 | 
			
		||||
@ -66,7 +66,7 @@ void ServerVoiceInfo::Initialize() {
 | 
			
		||||
    in_params.last_volume = 0.0f;
 | 
			
		||||
    in_params.biquad_filter.fill({});
 | 
			
		||||
    in_params.wave_buffer_count = 0;
 | 
			
		||||
    in_params.wave_bufffer_head = 0;
 | 
			
		||||
    in_params.wave_buffer_head = 0;
 | 
			
		||||
    in_params.mix_id = AudioCommon::NO_MIX;
 | 
			
		||||
    in_params.splitter_info_id = AudioCommon::NO_SPLITTER;
 | 
			
		||||
    in_params.additional_params_address = 0;
 | 
			
		||||
@ -75,7 +75,7 @@ void ServerVoiceInfo::Initialize() {
 | 
			
		||||
    out_params.played_sample_count = 0;
 | 
			
		||||
    out_params.wave_buffer_consumed = 0;
 | 
			
		||||
    in_params.voice_drop_flag = false;
 | 
			
		||||
    in_params.buffer_mapped = false;
 | 
			
		||||
    in_params.buffer_mapped = true;
 | 
			
		||||
    in_params.wave_buffer_flush_request_count = 0;
 | 
			
		||||
    in_params.was_biquad_filter_enabled.fill(false);
 | 
			
		||||
 | 
			
		||||
@ -126,7 +126,7 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
 | 
			
		||||
    in_params.volume = voice_in.volume;
 | 
			
		||||
    in_params.biquad_filter = voice_in.biquad_filter;
 | 
			
		||||
    in_params.wave_buffer_count = voice_in.wave_buffer_count;
 | 
			
		||||
    in_params.wave_bufffer_head = voice_in.wave_buffer_head;
 | 
			
		||||
    in_params.wave_buffer_head = voice_in.wave_buffer_head;
 | 
			
		||||
    if (behavior_info.IsFlushVoiceWaveBuffersSupported()) {
 | 
			
		||||
        const auto in_request_count = in_params.wave_buffer_flush_request_count;
 | 
			
		||||
        const auto voice_request_count = voice_in.wave_buffer_flush_request_count;
 | 
			
		||||
@ -185,14 +185,16 @@ void ServerVoiceInfo::UpdateWaveBuffers(
 | 
			
		||||
            wave_buffer.buffer_size = 0;
 | 
			
		||||
            wave_buffer.context_address = 0;
 | 
			
		||||
            wave_buffer.context_size = 0;
 | 
			
		||||
            wave_buffer.loop_start_sample = 0;
 | 
			
		||||
            wave_buffer.loop_end_sample = 0;
 | 
			
		||||
            wave_buffer.sent_to_dsp = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Mark all our wave buffers as invalid
 | 
			
		||||
        for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count);
 | 
			
		||||
             channel++) {
 | 
			
		||||
            for (auto& is_valid : voice_states[channel]->is_wave_buffer_valid) {
 | 
			
		||||
                is_valid = false;
 | 
			
		||||
            for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; ++i) {
 | 
			
		||||
                voice_states[channel]->is_wave_buffer_valid[i] = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -211,7 +213,7 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
 | 
			
		||||
                                       const WaveBuffer& in_wave_buffer, SampleFormat sample_format,
 | 
			
		||||
                                       bool is_buffer_valid,
 | 
			
		||||
                                       [[maybe_unused]] BehaviorInfo& behavior_info) {
 | 
			
		||||
    if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) {
 | 
			
		||||
    if (!is_buffer_valid && out_wavebuffer.sent_to_dsp && out_wavebuffer.buffer_address != 0) {
 | 
			
		||||
        out_wavebuffer.buffer_address = 0;
 | 
			
		||||
        out_wavebuffer.buffer_size = 0;
 | 
			
		||||
    }
 | 
			
		||||
@ -219,11 +221,40 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
 | 
			
		||||
    if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) {
 | 
			
		||||
        // Validate sample offset sizings
 | 
			
		||||
        if (sample_format == SampleFormat::Pcm16) {
 | 
			
		||||
            const auto buffer_size = in_wave_buffer.buffer_size;
 | 
			
		||||
            if (in_wave_buffer.start_sample_offset < 0 || in_wave_buffer.end_sample_offset < 0 ||
 | 
			
		||||
                (buffer_size < (sizeof(s16) * in_wave_buffer.start_sample_offset)) ||
 | 
			
		||||
                (buffer_size < (sizeof(s16) * in_wave_buffer.end_sample_offset))) {
 | 
			
		||||
            const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size);
 | 
			
		||||
            const s64 start = sizeof(s16) * in_wave_buffer.start_sample_offset;
 | 
			
		||||
            const s64 end = sizeof(s16) * in_wave_buffer.end_sample_offset;
 | 
			
		||||
            if (0 > start || start > buffer_size || 0 > end || end > buffer_size) {
 | 
			
		||||
                // TODO(ogniK): Write error info
 | 
			
		||||
                LOG_ERROR(Audio,
 | 
			
		||||
                          "PCM16 wavebuffer has an invalid size. Buffer has size 0x{:08X}, but "
 | 
			
		||||
                          "offsets were "
 | 
			
		||||
                          "{:08X} - 0x{:08X}",
 | 
			
		||||
                          buffer_size, sizeof(s16) * in_wave_buffer.start_sample_offset,
 | 
			
		||||
                          sizeof(s16) * in_wave_buffer.end_sample_offset);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (sample_format == SampleFormat::Adpcm) {
 | 
			
		||||
            const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size);
 | 
			
		||||
            const s64 start_frames = in_wave_buffer.start_sample_offset / 14;
 | 
			
		||||
            const s64 start_extra = in_wave_buffer.start_sample_offset % 14 == 0
 | 
			
		||||
                                        ? 0
 | 
			
		||||
                                        : (in_wave_buffer.start_sample_offset % 14) / 2 + 1 +
 | 
			
		||||
                                              (in_wave_buffer.start_sample_offset % 2);
 | 
			
		||||
            const s64 start = start_frames * 8 + start_extra;
 | 
			
		||||
            const s64 end_frames = in_wave_buffer.end_sample_offset / 14;
 | 
			
		||||
            const s64 end_extra = in_wave_buffer.end_sample_offset % 14 == 0
 | 
			
		||||
                                      ? 0
 | 
			
		||||
                                      : (in_wave_buffer.end_sample_offset % 14) / 2 + 1 +
 | 
			
		||||
                                            (in_wave_buffer.end_sample_offset % 2);
 | 
			
		||||
            const s64 end = end_frames * 8 + end_extra;
 | 
			
		||||
            if (in_wave_buffer.start_sample_offset < 0 || start > buffer_size ||
 | 
			
		||||
                in_wave_buffer.end_sample_offset < 0 || end > buffer_size) {
 | 
			
		||||
                LOG_ERROR(Audio,
 | 
			
		||||
                          "ADPMC wavebuffer has an invalid size. Buffer has size 0x{:08X}, but "
 | 
			
		||||
                          "offsets were "
 | 
			
		||||
                          "{:08X} - 0x{:08X}",
 | 
			
		||||
                          in_wave_buffer.buffer_size, start, end);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -239,29 +270,34 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
 | 
			
		||||
        out_wavebuffer.buffer_size = in_wave_buffer.buffer_size;
 | 
			
		||||
        out_wavebuffer.context_address = in_wave_buffer.context_address;
 | 
			
		||||
        out_wavebuffer.context_size = in_wave_buffer.context_size;
 | 
			
		||||
        out_wavebuffer.loop_start_sample = in_wave_buffer.loop_start_sample;
 | 
			
		||||
        out_wavebuffer.loop_end_sample = in_wave_buffer.loop_end_sample;
 | 
			
		||||
        in_params.buffer_mapped =
 | 
			
		||||
            in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0;
 | 
			
		||||
        // TODO(ogniK): Pool mapper attachment
 | 
			
		||||
        // TODO(ogniK): IsAdpcmLoopContextBugFixed
 | 
			
		||||
        if (sample_format == SampleFormat::Adpcm && in_wave_buffer.context_address != 0 &&
 | 
			
		||||
            in_wave_buffer.context_size != 0 && behavior_info.IsAdpcmLoopContextBugFixed()) {
 | 
			
		||||
        } else {
 | 
			
		||||
            out_wavebuffer.context_address = 0;
 | 
			
		||||
            out_wavebuffer.context_size = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ServerVoiceInfo::WriteOutStatus(
 | 
			
		||||
    VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in,
 | 
			
		||||
    std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) {
 | 
			
		||||
    if (voice_in.is_new) {
 | 
			
		||||
    if (voice_in.is_new || in_params.is_new) {
 | 
			
		||||
        in_params.is_new = true;
 | 
			
		||||
        voice_out.wave_buffer_consumed = 0;
 | 
			
		||||
        voice_out.played_sample_count = 0;
 | 
			
		||||
        voice_out.voice_dropped = false;
 | 
			
		||||
    } else if (!in_params.is_new) {
 | 
			
		||||
        voice_out.wave_buffer_consumed = voice_states[0]->wave_buffer_consumed;
 | 
			
		||||
        voice_out.played_sample_count = voice_states[0]->played_sample_count;
 | 
			
		||||
        voice_out.voice_dropped = in_params.voice_drop_flag;
 | 
			
		||||
    } else {
 | 
			
		||||
        voice_out.wave_buffer_consumed = 0;
 | 
			
		||||
        voice_out.played_sample_count = 0;
 | 
			
		||||
        voice_out.voice_dropped = false;
 | 
			
		||||
        const auto& state = voice_states[0];
 | 
			
		||||
        voice_out.wave_buffer_consumed = state->wave_buffer_consumed;
 | 
			
		||||
        voice_out.played_sample_count = state->played_sample_count;
 | 
			
		||||
        voice_out.voice_dropped = state->voice_dropped;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -283,7 +319,8 @@ ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() {
 | 
			
		||||
 | 
			
		||||
bool ServerVoiceInfo::ShouldSkip() const {
 | 
			
		||||
    // TODO(ogniK): Handle unmapped wave buffers or parameters
 | 
			
		||||
    return !in_params.in_use || (in_params.wave_buffer_count == 0) || in_params.voice_drop_flag;
 | 
			
		||||
    return !in_params.in_use || in_params.wave_buffer_count == 0 || !in_params.buffer_mapped ||
 | 
			
		||||
           in_params.voice_drop_flag;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) {
 | 
			
		||||
@ -381,7 +418,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
 | 
			
		||||
void ServerVoiceInfo::FlushWaveBuffers(
 | 
			
		||||
    u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states,
 | 
			
		||||
    s32 channel_count) {
 | 
			
		||||
    auto wave_head = in_params.wave_bufffer_head;
 | 
			
		||||
    auto wave_head = in_params.wave_buffer_head;
 | 
			
		||||
 | 
			
		||||
    for (u8 i = 0; i < flush_count; i++) {
 | 
			
		||||
        in_params.wave_buffer[wave_head].sent_to_dsp = true;
 | 
			
		||||
@ -401,6 +438,17 @@ bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const {
 | 
			
		||||
    return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ServerVoiceInfo::SetWaveBufferCompleted(VoiceState& dsp_state,
 | 
			
		||||
                                             const ServerWaveBuffer& wave_buffer) {
 | 
			
		||||
    dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false;
 | 
			
		||||
    dsp_state.wave_buffer_consumed++;
 | 
			
		||||
    dsp_state.wave_buffer_index = (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
 | 
			
		||||
    dsp_state.loop_count = 0;
 | 
			
		||||
    if (wave_buffer.end_of_stream) {
 | 
			
		||||
        dsp_state.played_sample_count = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} {
 | 
			
		||||
    for (std::size_t i = 0; i < voice_count; i++) {
 | 
			
		||||
        voice_channel_resources.emplace_back(static_cast<s32>(i));
 | 
			
		||||
 | 
			
		||||
@ -60,10 +60,12 @@ struct WaveBuffer {
 | 
			
		||||
    u8 is_looping{};
 | 
			
		||||
    u8 end_of_stream{};
 | 
			
		||||
    u8 sent_to_server{};
 | 
			
		||||
    INSERT_PADDING_BYTES(5);
 | 
			
		||||
    INSERT_PADDING_BYTES(1);
 | 
			
		||||
    s32 loop_count{};
 | 
			
		||||
    u64 context_address{};
 | 
			
		||||
    u64 context_size{};
 | 
			
		||||
    INSERT_PADDING_BYTES(8);
 | 
			
		||||
    u32 loop_start_sample{};
 | 
			
		||||
    u32 loop_end_sample{};
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size");
 | 
			
		||||
 | 
			
		||||
@ -76,6 +78,9 @@ struct ServerWaveBuffer {
 | 
			
		||||
    bool end_of_stream{};
 | 
			
		||||
    VAddr context_address{};
 | 
			
		||||
    std::size_t context_size{};
 | 
			
		||||
    s32 loop_count{};
 | 
			
		||||
    u32 loop_start_sample{};
 | 
			
		||||
    u32 loop_end_sample{};
 | 
			
		||||
    bool sent_to_dsp{true};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -108,6 +113,7 @@ struct VoiceState {
 | 
			
		||||
    u32 external_context_size;
 | 
			
		||||
    bool is_external_context_used;
 | 
			
		||||
    bool voice_dropped;
 | 
			
		||||
    s32 loop_count;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class VoiceChannelResource {
 | 
			
		||||
@ -206,7 +212,7 @@ public:
 | 
			
		||||
        float last_volume{};
 | 
			
		||||
        std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{};
 | 
			
		||||
        s32 wave_buffer_count{};
 | 
			
		||||
        s16 wave_bufffer_head{};
 | 
			
		||||
        s16 wave_buffer_head{};
 | 
			
		||||
        INSERT_PADDING_BYTES(2);
 | 
			
		||||
        BehaviorFlags behavior_flags{};
 | 
			
		||||
        VAddr additional_params_address{};
 | 
			
		||||
@ -252,6 +258,7 @@ public:
 | 
			
		||||
    void FlushWaveBuffers(u8 flush_count,
 | 
			
		||||
                          std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states,
 | 
			
		||||
                          s32 channel_count);
 | 
			
		||||
    void SetWaveBufferCompleted(VoiceState& dsp_state, const ServerWaveBuffer& wave_buffer);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::vector<s16> stored_samples;
 | 
			
		||||
 | 
			
		||||
@ -96,7 +96,7 @@ private:
 | 
			
		||||
    void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        LOG_DEBUG(Service_Audio, "(STUBBED) called");
 | 
			
		||||
 | 
			
		||||
        std::vector<u8> output_params(ctx.GetWriteBufferSize());
 | 
			
		||||
        std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0);
 | 
			
		||||
        auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
 | 
			
		||||
 | 
			
		||||
        if (result.IsSuccess()) {
 | 
			
		||||
@ -110,17 +110,19 @@ private:
 | 
			
		||||
    void Start(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
        const auto result = renderer->Start();
 | 
			
		||||
 | 
			
		||||
        rb.Push(ResultSuccess);
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
        rb.Push(result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Stop(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
        const auto result = renderer->Stop();
 | 
			
		||||
 | 
			
		||||
        rb.Push(ResultSuccess);
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
        rb.Push(result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user