summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/core/hle/service/audio/audout_u.cpp166
-rw-r--r--src/core/hle/service/audio/audout_u.h14
4 files changed, 168 insertions, 14 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 2bd0a6be9..be53be407 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -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) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 3144a0349..09ea7a2c7 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -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
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 8a436bf97..df04d636e 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -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");
+
+ 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<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") {
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index 69626cc58..7fbce2225 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -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