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, 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) \
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"},
|
{0x1, &IAudioOut::StartAudioOut, "StartAudioOut"},
|
||||||
{0x2, nullptr, "StopAudioOut"},
|
{0x2, &IAudioOut::StopAudioOut, "StopAudioOut"},
|
||||||
{0x3, nullptr, "AppendAudioOutBuffer_1"},
|
{0x3, &IAudioOut::AppendAudioOutBuffer_1, "AppendAudioOutBuffer_1"},
|
||||||
{0x4, nullptr, "RegisterBufferEvent"},
|
{0x4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
|
||||||
{0x5, nullptr, "GetReleasedAudioOutBuffer_1"},
|
{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");
|
||||||
|
|
||||||
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(RESULT_SUCCESS);
|
||||||
rb.Push<u32>(48000); // Sample Rate
|
rb.Push<u32>(sample_rate);
|
||||||
rb.Push<u32>(2); // Channels
|
rb.Push<u32>(audio_channels);
|
||||||
rb.Push<u32>(2); // PCM Format (INT16)
|
rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
|
||||||
rb.Push<u32>(0); // Unknown
|
// this field is unknown
|
||||||
rb.PushIpcInterface<Audio::IAudioOut>();
|
rb.Push<u32>(0);
|
||||||
|
rb.PushMoveObjects(std::move(client));
|
||||||
}
|
}
|
||||||
|
|
||||||
AudOutU::AudOutU() : ServiceFramework("audout:u") {
|
AudOutU::AudOutU() : ServiceFramework("audout:u") {
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue