mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-03 08:29:03 -06:00 
			
		
		
		
	audio_core: Implement audren_u audio playback.
This commit is contained in:
		@@ -15,13 +15,10 @@
 | 
			
		||||
 | 
			
		||||
namespace Service::Audio {
 | 
			
		||||
 | 
			
		||||
/// TODO(bunnei): Find a proper value for the audio_ticks
 | 
			
		||||
constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
 | 
			
		||||
 | 
			
		||||
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
 | 
			
		||||
public:
 | 
			
		||||
    explicit IAudioRenderer(AudioRendererParameter audren_params)
 | 
			
		||||
        : ServiceFramework("IAudioRenderer"), worker_params(audren_params) {
 | 
			
		||||
    explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
 | 
			
		||||
        : ServiceFramework("IAudioRenderer") {
 | 
			
		||||
        static const FunctionInfo functions[] = {
 | 
			
		||||
            {0, nullptr, "GetAudioRendererSampleRate"},
 | 
			
		||||
            {1, nullptr, "GetAudioRendererSampleCount"},
 | 
			
		||||
@@ -39,21 +36,8 @@ public:
 | 
			
		||||
        RegisterHandlers(functions);
 | 
			
		||||
 | 
			
		||||
        system_event =
 | 
			
		||||
            Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent");
 | 
			
		||||
 | 
			
		||||
        // Register event callback to update the Audio Buffer
 | 
			
		||||
        audio_event = CoreTiming::RegisterEvent(
 | 
			
		||||
            "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) {
 | 
			
		||||
                UpdateAudioCallback();
 | 
			
		||||
                CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // Start the audio event
 | 
			
		||||
        CoreTiming::ScheduleEvent(audio_ticks, audio_event);
 | 
			
		||||
        voice_status_list.resize(worker_params.voice_count);
 | 
			
		||||
    }
 | 
			
		||||
    ~IAudioRenderer() {
 | 
			
		||||
        CoreTiming::UnscheduleEvent(audio_event, 0);
 | 
			
		||||
            Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
 | 
			
		||||
        renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
@@ -62,60 +46,9 @@ private:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        UpdateDataHeader config{};
 | 
			
		||||
        auto buf = ctx.ReadBuffer();
 | 
			
		||||
        std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader));
 | 
			
		||||
        u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
 | 
			
		||||
 | 
			
		||||
        std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
 | 
			
		||||
        std::memcpy(mem_pool_info.data(),
 | 
			
		||||
                    buf.data() + sizeof(UpdateDataHeader) + config.behavior_size,
 | 
			
		||||
                    memory_pool_count * sizeof(MemoryPoolInfo));
 | 
			
		||||
 | 
			
		||||
        std::vector<VoiceInfo> voice_info(worker_params.voice_count);
 | 
			
		||||
        std::memcpy(voice_info.data(),
 | 
			
		||||
                    buf.data() + sizeof(UpdateDataHeader) + config.behavior_size +
 | 
			
		||||
                        config.memory_pools_size + config.voice_resource_size,
 | 
			
		||||
                    worker_params.voice_count * sizeof(VoiceInfo));
 | 
			
		||||
 | 
			
		||||
        UpdateDataHeader response_data{worker_params};
 | 
			
		||||
 | 
			
		||||
        ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
 | 
			
		||||
 | 
			
		||||
        std::vector<u8> output(response_data.total_size);
 | 
			
		||||
        std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader));
 | 
			
		||||
        std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
 | 
			
		||||
        for (unsigned i = 0; i < memory_pool.size(); i++) {
 | 
			
		||||
            if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach)
 | 
			
		||||
                memory_pool[i].state = MemoryPoolStates::Attached;
 | 
			
		||||
            else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
 | 
			
		||||
                memory_pool[i].state = MemoryPoolStates::Detached;
 | 
			
		||||
        }
 | 
			
		||||
        std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
 | 
			
		||||
                    response_data.memory_pools_size);
 | 
			
		||||
 | 
			
		||||
        for (unsigned i = 0; i < voice_info.size(); i++) {
 | 
			
		||||
            if (voice_info[i].is_new) {
 | 
			
		||||
                voice_status_list[i].played_sample_count = 0;
 | 
			
		||||
                voice_status_list[i].wave_buffer_consumed = 0;
 | 
			
		||||
            } else if (voice_info[i].play_state == (u8)PlayStates::Started) {
 | 
			
		||||
                for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) {
 | 
			
		||||
                    voice_status_list[i].played_sample_count +=
 | 
			
		||||
                        (voice_info[i].wave_buffer[buff_idx].end_sample_offset -
 | 
			
		||||
                         voice_info[i].wave_buffer[buff_idx].start_sample_offset) /
 | 
			
		||||
                        2;
 | 
			
		||||
                    voice_status_list[i].wave_buffer_consumed++;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size,
 | 
			
		||||
                    voice_status_list.data(), response_data.voices_size);
 | 
			
		||||
 | 
			
		||||
        ctx.WriteBuffer(output);
 | 
			
		||||
 | 
			
		||||
        ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
 | 
			
		||||
        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -136,8 +69,6 @@ private:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        // system_event->Signal();
 | 
			
		||||
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2, 1};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
        rb.PushCopyObjects(system_event);
 | 
			
		||||
@@ -145,131 +76,8 @@ private:
 | 
			
		||||
        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enum class MemoryPoolStates : u32 { // Should be LE
 | 
			
		||||
        Invalid = 0x0,
 | 
			
		||||
        Unknown = 0x1,
 | 
			
		||||
        RequestDetach = 0x2,
 | 
			
		||||
        Detached = 0x3,
 | 
			
		||||
        RequestAttach = 0x4,
 | 
			
		||||
        Attached = 0x5,
 | 
			
		||||
        Released = 0x6,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    enum class PlayStates : u8 {
 | 
			
		||||
        Started = 0,
 | 
			
		||||
        Stopped = 1,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct MemoryPoolEntry {
 | 
			
		||||
        MemoryPoolStates state;
 | 
			
		||||
        u32_le unknown_4;
 | 
			
		||||
        u32_le unknown_8;
 | 
			
		||||
        u32_le unknown_c;
 | 
			
		||||
    };
 | 
			
		||||
    static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
 | 
			
		||||
 | 
			
		||||
    struct MemoryPoolInfo {
 | 
			
		||||
        u64_le pool_address;
 | 
			
		||||
        u64_le pool_size;
 | 
			
		||||
        MemoryPoolStates pool_state;
 | 
			
		||||
        INSERT_PADDING_WORDS(3); // Unknown
 | 
			
		||||
    };
 | 
			
		||||
    static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
 | 
			
		||||
 | 
			
		||||
    struct UpdateDataHeader {
 | 
			
		||||
        UpdateDataHeader() {}
 | 
			
		||||
 | 
			
		||||
        explicit UpdateDataHeader(const AudioRendererParameter& config) {
 | 
			
		||||
            revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
 | 
			
		||||
            behavior_size = 0xb0;
 | 
			
		||||
            memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
 | 
			
		||||
            voices_size = config.voice_count * 0x10;
 | 
			
		||||
            voice_resource_size = 0x0;
 | 
			
		||||
            effects_size = config.effect_count * 0x10;
 | 
			
		||||
            mixes_size = 0x0;
 | 
			
		||||
            sinks_size = config.sink_count * 0x20;
 | 
			
		||||
            performance_manager_size = 0x10;
 | 
			
		||||
            total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +
 | 
			
		||||
                         voices_size + effects_size + sinks_size + performance_manager_size;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        u32_le revision;
 | 
			
		||||
        u32_le behavior_size;
 | 
			
		||||
        u32_le memory_pools_size;
 | 
			
		||||
        u32_le voices_size;
 | 
			
		||||
        u32_le voice_resource_size;
 | 
			
		||||
        u32_le effects_size;
 | 
			
		||||
        u32_le mixes_size;
 | 
			
		||||
        u32_le sinks_size;
 | 
			
		||||
        u32_le performance_manager_size;
 | 
			
		||||
        INSERT_PADDING_WORDS(6);
 | 
			
		||||
        u32_le total_size;
 | 
			
		||||
    };
 | 
			
		||||
    static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
 | 
			
		||||
 | 
			
		||||
    struct BiquadFilter {
 | 
			
		||||
        u8 enable;
 | 
			
		||||
        INSERT_PADDING_BYTES(1);
 | 
			
		||||
        s16_le numerator[3];
 | 
			
		||||
        s16_le denominator[2];
 | 
			
		||||
    };
 | 
			
		||||
    static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
 | 
			
		||||
 | 
			
		||||
    struct WaveBuffer {
 | 
			
		||||
        u64_le buffer_addr;
 | 
			
		||||
        u64_le buffer_sz;
 | 
			
		||||
        s32_le start_sample_offset;
 | 
			
		||||
        s32_le end_sample_offset;
 | 
			
		||||
        u8 loop;
 | 
			
		||||
        u8 end_of_stream;
 | 
			
		||||
        u8 sent_to_server;
 | 
			
		||||
        INSERT_PADDING_BYTES(5);
 | 
			
		||||
        u64 context_addr;
 | 
			
		||||
        u64 context_sz;
 | 
			
		||||
        INSERT_PADDING_BYTES(8);
 | 
			
		||||
    };
 | 
			
		||||
    static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
 | 
			
		||||
 | 
			
		||||
    struct VoiceInfo {
 | 
			
		||||
        u32_le id;
 | 
			
		||||
        u32_le node_id;
 | 
			
		||||
        u8 is_new;
 | 
			
		||||
        u8 is_in_use;
 | 
			
		||||
        u8 play_state;
 | 
			
		||||
        u8 sample_format;
 | 
			
		||||
        u32_le sample_rate;
 | 
			
		||||
        u32_le priority;
 | 
			
		||||
        u32_le sorting_order;
 | 
			
		||||
        u32_le channel_count;
 | 
			
		||||
        float_le pitch;
 | 
			
		||||
        float_le volume;
 | 
			
		||||
        BiquadFilter biquad_filter[2];
 | 
			
		||||
        u32_le wave_buffer_count;
 | 
			
		||||
        u16_le wave_buffer_head;
 | 
			
		||||
        INSERT_PADDING_BYTES(6);
 | 
			
		||||
        u64_le additional_params_addr;
 | 
			
		||||
        u64_le additional_params_sz;
 | 
			
		||||
        u32_le mix_id;
 | 
			
		||||
        u32_le splitter_info_id;
 | 
			
		||||
        WaveBuffer wave_buffer[4];
 | 
			
		||||
        u32_le voice_channel_resource_ids[6];
 | 
			
		||||
        INSERT_PADDING_BYTES(24);
 | 
			
		||||
    };
 | 
			
		||||
    static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
 | 
			
		||||
 | 
			
		||||
    struct VoiceOutStatus {
 | 
			
		||||
        u64_le played_sample_count;
 | 
			
		||||
        u32_le wave_buffer_consumed;
 | 
			
		||||
        INSERT_PADDING_WORDS(1);
 | 
			
		||||
    };
 | 
			
		||||
    static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
 | 
			
		||||
 | 
			
		||||
    /// This is used to trigger the audio event callback.
 | 
			
		||||
    CoreTiming::EventType* audio_event;
 | 
			
		||||
 | 
			
		||||
    Kernel::SharedPtr<Kernel::Event> system_event;
 | 
			
		||||
    AudioRendererParameter worker_params;
 | 
			
		||||
    std::vector<VoiceOutStatus> voice_status_list;
 | 
			
		||||
    std::unique_ptr<AudioCore::AudioRenderer> renderer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
 | 
			
		||||
@@ -368,7 +176,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
 | 
			
		||||
 | 
			
		||||
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    IPC::RequestParser rp{ctx};
 | 
			
		||||
    auto params = rp.PopRaw<AudioRendererParameter>();
 | 
			
		||||
    auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
 | 
			
		||||
    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
 | 
			
		||||
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
@@ -379,7 +187,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
 | 
			
		||||
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    IPC::RequestParser rp{ctx};
 | 
			
		||||
    auto params = rp.PopRaw<AudioRendererParameter>();
 | 
			
		||||
    auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
 | 
			
		||||
 | 
			
		||||
    u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40);
 | 
			
		||||
    buffer_sz += params.unknown_c * 1024;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "audio_core/audio_renderer.h"
 | 
			
		||||
#include "core/hle/service/service.h"
 | 
			
		||||
 | 
			
		||||
namespace Kernel {
 | 
			
		||||
@@ -12,24 +13,6 @@ class HLERequestContext;
 | 
			
		||||
 | 
			
		||||
namespace Service::Audio {
 | 
			
		||||
 | 
			
		||||
struct AudioRendererParameter {
 | 
			
		||||
    u32_le sample_rate;
 | 
			
		||||
    u32_le sample_count;
 | 
			
		||||
    u32_le unknown_8;
 | 
			
		||||
    u32_le unknown_c;
 | 
			
		||||
    u32_le voice_count;
 | 
			
		||||
    u32_le sink_count;
 | 
			
		||||
    u32_le effect_count;
 | 
			
		||||
    u32_le unknown_1c;
 | 
			
		||||
    u8 unknown_20;
 | 
			
		||||
    INSERT_PADDING_BYTES(3);
 | 
			
		||||
    u32_le splitter_count;
 | 
			
		||||
    u32_le unknown_2c;
 | 
			
		||||
    INSERT_PADDING_WORDS(1);
 | 
			
		||||
    u32_le revision;
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
 | 
			
		||||
 | 
			
		||||
class AudRenU final : public ServiceFramework<AudRenU> {
 | 
			
		||||
public:
 | 
			
		||||
    explicit AudRenU();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user