3
0
Fork 0
forked from suyu/suyu

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:
st4rk 2018-01-24 19:17:54 -08:00 committed by bunnei
parent b35cf672c0
commit 44eb840232
4 changed files with 168 additions and 14 deletions

View file

@ -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) \

View file

@ -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

View file

@ -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") {

View file

@ -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