mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	audout:u OpenAudioOut and IAudioOut (#138)
* Updated the audout:u and IAudioOut, now it might work with RetroArch without trigger an assert, however it's not the ideal implementation * Updated the audout:u and IAudioOut, now it might work with RetroArch without trigger an assert, however it's not the ideal implementation * audout:u OpenAudioOut implementation and IAudioOut cmd 1,2,3,4,5 implementation * using an enum for audio_out_state as well as changing its initialize to member initializer list * Minor fixes, added Service_Audio for LOG_*, changed PcmFormat enum to EnumClass * Minor fixes, added Service_Audio for LOG_*, changed PcmFormat enum to EnumClass * added missing Audio loggin subclass, minor fixes, clang comment breakline * Solving backend logging conflict * minor fix * Fixed duplicated Service NVDRV in backend.cpp, my bad
This commit is contained in:
		
							parent
							
								
									b35cf672c0
								
							
						
					
					
						commit
						44eb840232
					
				@ -39,6 +39,7 @@ namespace Log {
 | 
			
		||||
    SUB(Service, DSP)                                                                              \
 | 
			
		||||
    SUB(Service, HID)                                                                              \
 | 
			
		||||
    SUB(Service, NVDRV)                                                                            \
 | 
			
		||||
    SUB(Service, Audio)                                                                            \
 | 
			
		||||
    CLS(HW)                                                                                        \
 | 
			
		||||
    SUB(HW, Memory)                                                                                \
 | 
			
		||||
    SUB(HW, LCD)                                                                                   \
 | 
			
		||||
 | 
			
		||||
@ -56,6 +56,7 @@ enum class Class : ClassType {
 | 
			
		||||
    Service_DSP,       ///< The DSP (DSP control) service
 | 
			
		||||
    Service_HID,       ///< The HID (Human interface device) service
 | 
			
		||||
    Service_NVDRV,     ///< The NVDRV (Nvidia driver) service
 | 
			
		||||
    Service_Audio,     ///< The Audio (Audio control) service
 | 
			
		||||
    HW,                ///< Low-level hardware emulation
 | 
			
		||||
    HW_Memory,         ///< Memory-map and address translation
 | 
			
		||||
    HW_LCD,            ///< LCD register emulation
 | 
			
		||||
 | 
			
		||||
@ -2,35 +2,163 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "core/core_timing.h"
 | 
			
		||||
#include "core/hle/ipc_helpers.h"
 | 
			
		||||
#include "core/hle/kernel/event.h"
 | 
			
		||||
#include "core/hle/kernel/hle_ipc.h"
 | 
			
		||||
#include "core/hle/service/audio/audout_u.h"
 | 
			
		||||
 | 
			
		||||
namespace Service {
 | 
			
		||||
namespace Audio {
 | 
			
		||||
 | 
			
		||||
/// Switch sample rate frequency
 | 
			
		||||
constexpr u32 sample_rate{48000};
 | 
			
		||||
/// TODO(st4rk): dynamic number of channels, as I think Switch has support
 | 
			
		||||
/// to more audio channels (probably when Docked I guess)
 | 
			
		||||
constexpr u32 audio_channels{2};
 | 
			
		||||
/// TODO(st4rk): find a proper value for the audio_ticks
 | 
			
		||||
constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 500)};
 | 
			
		||||
 | 
			
		||||
class IAudioOut final : public ServiceFramework<IAudioOut> {
 | 
			
		||||
public:
 | 
			
		||||
    IAudioOut() : ServiceFramework("IAudioOut") {
 | 
			
		||||
    IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(Stopped) {
 | 
			
		||||
        static const FunctionInfo functions[] = {
 | 
			
		||||
            {0x0, nullptr, "GetAudioOutState"},
 | 
			
		||||
            {0x1, nullptr, "StartAudioOut"},
 | 
			
		||||
            {0x2, nullptr, "StopAudioOut"},
 | 
			
		||||
            {0x3, nullptr, "AppendAudioOutBuffer_1"},
 | 
			
		||||
            {0x4, nullptr, "RegisterBufferEvent"},
 | 
			
		||||
            {0x5, nullptr, "GetReleasedAudioOutBuffer_1"},
 | 
			
		||||
            {0x1, &IAudioOut::StartAudioOut, "StartAudioOut"},
 | 
			
		||||
            {0x2, &IAudioOut::StopAudioOut, "StopAudioOut"},
 | 
			
		||||
            {0x3, &IAudioOut::AppendAudioOutBuffer_1, "AppendAudioOutBuffer_1"},
 | 
			
		||||
            {0x4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
 | 
			
		||||
            {0x5, &IAudioOut::GetReleasedAudioOutBuffer_1, "GetReleasedAudioOutBuffer_1"},
 | 
			
		||||
            {0x6, nullptr, "ContainsAudioOutBuffer"},
 | 
			
		||||
            {0x7, nullptr, "AppendAudioOutBuffer_2"},
 | 
			
		||||
            {0x8, nullptr, "GetReleasedAudioOutBuffer_2"},
 | 
			
		||||
        };
 | 
			
		||||
        RegisterHandlers(functions);
 | 
			
		||||
 | 
			
		||||
        // This is the event handle used to check if the audio buffer was released
 | 
			
		||||
        buffer_event =
 | 
			
		||||
            Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
 | 
			
		||||
 | 
			
		||||
        // Register event callback to update the Audio Buffer
 | 
			
		||||
        audio_event = CoreTiming::RegisterEvent(
 | 
			
		||||
            "IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) {
 | 
			
		||||
                UpdateAudioBuffersCallback();
 | 
			
		||||
                CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // Start the audio event
 | 
			
		||||
        CoreTiming::ScheduleEvent(audio_ticks, audio_event);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~IAudioOut() = default;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void StartAudioOut(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
 | 
			
		||||
        // start audio
 | 
			
		||||
        audio_out_state = Started;
 | 
			
		||||
 | 
			
		||||
        IPC::RequestBuilder rb{ctx, 2};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void StopAudioOut(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
 | 
			
		||||
        // stop audio
 | 
			
		||||
        audio_out_state = Stopped;
 | 
			
		||||
 | 
			
		||||
        queue_keys.clear();
 | 
			
		||||
 | 
			
		||||
        IPC::RequestBuilder rb{ctx, 2};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
 | 
			
		||||
        IPC::RequestBuilder rb{ctx, 2, 1};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
        rb.PushCopyObjects(buffer_event);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void AppendAudioOutBuffer_1(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
        IPC::RequestParser rp{ctx};
 | 
			
		||||
 | 
			
		||||
        u64 key = rp.Pop<u64>();
 | 
			
		||||
 | 
			
		||||
        queue_keys.insert(queue_keys.begin(), key);
 | 
			
		||||
 | 
			
		||||
        IPC::RequestBuilder rb{ctx, 2};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GetReleasedAudioOutBuffer_1(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
 | 
			
		||||
        const auto& buffer = ctx.BufferDescriptorB()[0];
 | 
			
		||||
 | 
			
		||||
        // TODO(st4rk): this is how libtransistor currently implements the
 | 
			
		||||
        // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the APP and this address
 | 
			
		||||
        // is used to know which buffer should be filled with data and send again to the service
 | 
			
		||||
        // through AppendAudioOutBuffer. Check if this is the proper way to do it.
 | 
			
		||||
 | 
			
		||||
        u64 key{0};
 | 
			
		||||
 | 
			
		||||
        if (queue_keys.size()) {
 | 
			
		||||
            key = queue_keys.back();
 | 
			
		||||
            queue_keys.pop_back();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Memory::WriteBlock(buffer.Address(), &key, sizeof(u64));
 | 
			
		||||
 | 
			
		||||
        IPC::RequestBuilder rb{ctx, 3};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
        // TODO(st4rk): This might be the total of released buffers, needs to be verified on
 | 
			
		||||
        // hardware
 | 
			
		||||
        rb.Push<u32>(static_cast<u32>(queue_keys.size()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void UpdateAudioBuffersCallback() {
 | 
			
		||||
 | 
			
		||||
        if (audio_out_state != Started) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (queue_keys.empty()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        buffer_event->Signal();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enum AudioState : u32 {
 | 
			
		||||
        Started,
 | 
			
		||||
        Stopped,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /// This is used to trigger the audio event callback that is going to read the samples from the
 | 
			
		||||
    /// audio_buffer list and enqueue the samples using the sink (audio_core).
 | 
			
		||||
    CoreTiming::EventType* audio_event;
 | 
			
		||||
 | 
			
		||||
    /// This is the evend handle used to check if the audio buffer was released
 | 
			
		||||
    Kernel::SharedPtr<Kernel::Event> buffer_event;
 | 
			
		||||
 | 
			
		||||
    /// (st4rk): this is just a temporary workaround for the future implementation. Libtransistor
 | 
			
		||||
    /// uses the key as an address in the App, so we need to return when the
 | 
			
		||||
    /// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because
 | 
			
		||||
    /// libtransistor uses the key returned as an pointer;
 | 
			
		||||
    std::vector<u64> queue_keys;
 | 
			
		||||
 | 
			
		||||
    AudioState audio_out_state;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    LOG_WARNING(Service, "(STUBBED) called");
 | 
			
		||||
    LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
    IPC::RequestParser rp{ctx};
 | 
			
		||||
 | 
			
		||||
    auto& buffer = ctx.BufferDescriptorB()[0];
 | 
			
		||||
@ -50,16 +178,26 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    LOG_WARNING(Service, "(STUBBED) called");
 | 
			
		||||
    LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
 | 
			
		||||
    IPC::RequestBuilder rb{ctx, 6};
 | 
			
		||||
    if (!audio_out_interface) {
 | 
			
		||||
        audio_out_interface = std::make_shared<IAudioOut>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto sessions = Kernel::ServerSession::CreateSessionPair(audio_out_interface->GetServiceName());
 | 
			
		||||
    auto server = std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions);
 | 
			
		||||
    auto client = std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions);
 | 
			
		||||
    audio_out_interface->ClientConnected(server);
 | 
			
		||||
    LOG_DEBUG(Service, "called, initialized IAudioOut -> session=%u", client->GetObjectId());
 | 
			
		||||
    IPC::RequestBuilder rb{ctx, 6, 0, 1};
 | 
			
		||||
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    rb.Push<u32>(48000); // Sample Rate
 | 
			
		||||
    rb.Push<u32>(2);     // Channels
 | 
			
		||||
    rb.Push<u32>(2);     // PCM Format (INT16)
 | 
			
		||||
    rb.Push<u32>(0);     // Unknown
 | 
			
		||||
    rb.PushIpcInterface<Audio::IAudioOut>();
 | 
			
		||||
    rb.Push<u32>(sample_rate);
 | 
			
		||||
    rb.Push<u32>(audio_channels);
 | 
			
		||||
    rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
 | 
			
		||||
    // this field is unknown
 | 
			
		||||
    rb.Push<u32>(0);
 | 
			
		||||
    rb.PushMoveObjects(std::move(client));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudOutU::AudOutU() : ServiceFramework("audout:u") {
 | 
			
		||||
 | 
			
		||||
@ -13,14 +13,28 @@ class HLERequestContext;
 | 
			
		||||
namespace Service {
 | 
			
		||||
namespace Audio {
 | 
			
		||||
 | 
			
		||||
class IAudioOut;
 | 
			
		||||
 | 
			
		||||
class AudOutU final : public ServiceFramework<AudOutU> {
 | 
			
		||||
public:
 | 
			
		||||
    AudOutU();
 | 
			
		||||
    ~AudOutU() = default;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::shared_ptr<IAudioOut> audio_out_interface;
 | 
			
		||||
 | 
			
		||||
    void ListAudioOuts(Kernel::HLERequestContext& ctx);
 | 
			
		||||
    void OpenAudioOut(Kernel::HLERequestContext& ctx);
 | 
			
		||||
 | 
			
		||||
    enum class PcmFormat : u32 {
 | 
			
		||||
        Invalid = 0,
 | 
			
		||||
        Int8 = 1,
 | 
			
		||||
        Int16 = 2,
 | 
			
		||||
        Int24 = 3,
 | 
			
		||||
        Int32 = 4,
 | 
			
		||||
        PcmFloat = 5,
 | 
			
		||||
        Adpcm = 6,
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Audio
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user