Browse Source

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
st4rk 8 years ago
parent
commit
44eb840232

+ 1 - 0
src/common/logging/backend.cpp

@@ -39,6 +39,7 @@ namespace Log {
     SUB(Service, DSP)                                                                              \
     SUB(Service, DSP)                                                                              \
     SUB(Service, HID)                                                                              \
     SUB(Service, HID)                                                                              \
     SUB(Service, NVDRV)                                                                            \
     SUB(Service, NVDRV)                                                                            \
+    SUB(Service, Audio)                                                                            \
     CLS(HW)                                                                                        \
     CLS(HW)                                                                                        \
     SUB(HW, Memory)                                                                                \
     SUB(HW, Memory)                                                                                \
     SUB(HW, LCD)                                                                                   \
     SUB(HW, LCD)                                                                                   \

+ 1 - 0
src/common/logging/log.h

@@ -56,6 +56,7 @@ enum class Class : ClassType {
     Service_DSP,       ///< The DSP (DSP control) service
     Service_DSP,       ///< The DSP (DSP control) service
     Service_HID,       ///< The HID (Human interface device) service
     Service_HID,       ///< The HID (Human interface device) service
     Service_NVDRV,     ///< The NVDRV (Nvidia driver) service
     Service_NVDRV,     ///< The NVDRV (Nvidia driver) service
+    Service_Audio,     ///< The Audio (Audio control) service
     HW,                ///< Low-level hardware emulation
     HW,                ///< Low-level hardware emulation
     HW_Memory,         ///< Memory-map and address translation
     HW_Memory,         ///< Memory-map and address translation
     HW_LCD,            ///< LCD register emulation
     HW_LCD,            ///< LCD register emulation

+ 152 - 14
src/core/hle/service/audio/audout_u.cpp

@@ -2,35 +2,163 @@
 // Licensed under GPLv2 or any later version
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 // Refer to the license.txt file included.
 
 
+#include <vector>
 #include "common/logging/log.h"
 #include "common/logging/log.h"
+#include "core/core_timing.h"
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/event.h"
 #include "core/hle/kernel/hle_ipc.h"
 #include "core/hle/kernel/hle_ipc.h"
 #include "core/hle/service/audio/audout_u.h"
 #include "core/hle/service/audio/audout_u.h"
 
 
 namespace Service {
 namespace Service {
 namespace Audio {
 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> {
 class IAudioOut final : public ServiceFramework<IAudioOut> {
 public:
 public:
-    IAudioOut() : ServiceFramework("IAudioOut") {
+    IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(Stopped) {
         static const FunctionInfo functions[] = {
         static const FunctionInfo functions[] = {
             {0x0, nullptr, "GetAudioOutState"},
             {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"},
             {0x6, nullptr, "ContainsAudioOutBuffer"},
             {0x7, nullptr, "AppendAudioOutBuffer_2"},
             {0x7, nullptr, "AppendAudioOutBuffer_2"},
             {0x8, nullptr, "GetReleasedAudioOutBuffer_2"},
             {0x8, nullptr, "GetReleasedAudioOutBuffer_2"},
         };
         };
         RegisterHandlers(functions);
         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;
     ~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) {
 void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
-    LOG_WARNING(Service, "(STUBBED) called");
+    LOG_WARNING(Service_Audio, "(STUBBED) called");
     IPC::RequestParser rp{ctx};
     IPC::RequestParser rp{ctx};
 
 
     auto& buffer = ctx.BufferDescriptorB()[0];
     auto& buffer = ctx.BufferDescriptorB()[0];
@@ -50,16 +178,26 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
 }
 }
 
 
 void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
 void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
-    LOG_WARNING(Service, "(STUBBED) called");
+    LOG_WARNING(Service_Audio, "(STUBBED) called");
+
+    if (!audio_out_interface) {
+        audio_out_interface = std::make_shared<IAudioOut>();
+    }
 
 
-    IPC::RequestBuilder rb{ctx, 6};
+    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(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") {
 AudOutU::AudOutU() : ServiceFramework("audout:u") {

+ 14 - 0
src/core/hle/service/audio/audout_u.h

@@ -13,14 +13,28 @@ class HLERequestContext;
 namespace Service {
 namespace Service {
 namespace Audio {
 namespace Audio {
 
 
+class IAudioOut;
+
 class AudOutU final : public ServiceFramework<AudOutU> {
 class AudOutU final : public ServiceFramework<AudOutU> {
 public:
 public:
     AudOutU();
     AudOutU();
     ~AudOutU() = default;
     ~AudOutU() = default;
 
 
 private:
 private:
+    std::shared_ptr<IAudioOut> audio_out_interface;
+
     void ListAudioOuts(Kernel::HLERequestContext& ctx);
     void ListAudioOuts(Kernel::HLERequestContext& ctx);
     void OpenAudioOut(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
 } // namespace Audio