summaryrefslogtreecommitdiffstats
path: root/src/audio_core/out
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core/out')
-rw-r--r--src/audio_core/out/audio_out.cpp100
-rw-r--r--src/audio_core/out/audio_out.h147
-rw-r--r--src/audio_core/out/audio_out_system.cpp207
-rw-r--r--src/audio_core/out/audio_out_system.h257
4 files changed, 711 insertions, 0 deletions
diff --git a/src/audio_core/out/audio_out.cpp b/src/audio_core/out/audio_out.cpp
new file mode 100644
index 000000000..9a8d8a742
--- /dev/null
+++ b/src/audio_core/out/audio_out.cpp
@@ -0,0 +1,100 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "audio_core/audio_out_manager.h"
+#include "audio_core/out/audio_out.h"
+#include "core/hle/kernel/k_event.h"
+
+namespace AudioCore::AudioOut {
+
+Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
+ : manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
+ session_id_} {}
+
+void Out::Free() {
+ std::scoped_lock l{parent_mutex};
+ manager.ReleaseSessionId(system.GetSessionId());
+}
+
+System& Out::GetSystem() {
+ return system;
+}
+
+AudioOut::State Out::GetState() {
+ std::scoped_lock l{parent_mutex};
+ return system.GetState();
+}
+
+Result Out::StartSystem() {
+ std::scoped_lock l{parent_mutex};
+ return system.Start();
+}
+
+void Out::StartSession() {
+ std::scoped_lock l{parent_mutex};
+ system.StartSession();
+}
+
+Result Out::StopSystem() {
+ std::scoped_lock l{parent_mutex};
+ return system.Stop();
+}
+
+Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
+ std::scoped_lock l{parent_mutex};
+
+ if (system.AppendBuffer(buffer, tag)) {
+ return ResultSuccess;
+ }
+ return Service::Audio::ERR_BUFFER_COUNT_EXCEEDED;
+}
+
+void Out::ReleaseAndRegisterBuffers() {
+ std::scoped_lock l{parent_mutex};
+ if (system.GetState() == State::Started) {
+ system.ReleaseBuffers();
+ system.RegisterBuffers();
+ }
+}
+
+bool Out::FlushAudioOutBuffers() {
+ std::scoped_lock l{parent_mutex};
+ return system.FlushAudioOutBuffers();
+}
+
+u32 Out::GetReleasedBuffers(std::span<u64> tags) {
+ std::scoped_lock l{parent_mutex};
+ return system.GetReleasedBuffers(tags);
+}
+
+Kernel::KReadableEvent& Out::GetBufferEvent() {
+ std::scoped_lock l{parent_mutex};
+ return event->GetReadableEvent();
+}
+
+f32 Out::GetVolume() {
+ std::scoped_lock l{parent_mutex};
+ return system.GetVolume();
+}
+
+void Out::SetVolume(const f32 volume) {
+ std::scoped_lock l{parent_mutex};
+ system.SetVolume(volume);
+}
+
+bool Out::ContainsAudioBuffer(const u64 tag) {
+ std::scoped_lock l{parent_mutex};
+ return system.ContainsAudioBuffer(tag);
+}
+
+u32 Out::GetBufferCount() {
+ std::scoped_lock l{parent_mutex};
+ return system.GetBufferCount();
+}
+
+u64 Out::GetPlayedSampleCount() {
+ std::scoped_lock l{parent_mutex};
+ return system.GetPlayedSampleCount();
+}
+
+} // namespace AudioCore::AudioOut
diff --git a/src/audio_core/out/audio_out.h b/src/audio_core/out/audio_out.h
new file mode 100644
index 000000000..f6b921645
--- /dev/null
+++ b/src/audio_core/out/audio_out.h
@@ -0,0 +1,147 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <mutex>
+
+#include "audio_core/out/audio_out_system.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace AudioCore::AudioOut {
+class Manager;
+
+/**
+ * Interface between the service and audio out system. Mainly responsible for forwarding service
+ * calls to the system.
+ */
+class Out {
+public:
+ explicit Out(Core::System& system, Manager& manager, Kernel::KEvent* event, size_t session_id);
+
+ /**
+ * Free this audio out from the audio out manager.
+ */
+ void Free();
+
+ /**
+ * Get this audio out's system.
+ */
+ System& GetSystem();
+
+ /**
+ * Get the current state.
+ *
+ * @return Started or Stopped.
+ */
+ AudioOut::State GetState();
+
+ /**
+ * Start the system
+ *
+ * @return Result code
+ */
+ Result StartSystem();
+
+ /**
+ * Start the system's device session.
+ */
+ void StartSession();
+
+ /**
+ * Stop the system.
+ *
+ * @return Result code
+ */
+ Result StopSystem();
+
+ /**
+ * Append a new buffer to the system, the buffer event will be signalled when it is filled.
+ *
+ * @param buffer - The new buffer to append.
+ * @param tag - Unique tag for this buffer.
+ * @return Result code.
+ */
+ Result AppendBuffer(const AudioOutBuffer& buffer, u64 tag);
+
+ /**
+ * Release all completed buffers, and register any appended.
+ */
+ void ReleaseAndRegisterBuffers();
+
+ /**
+ * Flush all buffers.
+ */
+ bool FlushAudioOutBuffers();
+
+ /**
+ * Get all of the currently released buffers.
+ *
+ * @param tags - Output container for the buffer tags which were released.
+ * @return The number of buffers released.
+ */
+ u32 GetReleasedBuffers(std::span<u64> tags);
+
+ /**
+ * Get the buffer event for this audio out, this event will be signalled when a buffer is
+ * filled.
+ * @return The buffer event.
+ */
+ Kernel::KReadableEvent& GetBufferEvent();
+
+ /**
+ * Get the current system volume.
+ *
+ * @return The current volume.
+ */
+ f32 GetVolume();
+
+ /**
+ * Set the system volume.
+ *
+ * @param volume - The volume to set.
+ */
+ void SetVolume(f32 volume);
+
+ /**
+ * Check if a buffer is in the system.
+ *
+ * @param tag - The tag to search for.
+ * @return True if the buffer is in the system, otherwise false.
+ */
+ bool ContainsAudioBuffer(u64 tag);
+
+ /**
+ * Get the maximum number of buffers.
+ *
+ * @return The maximum number of buffers.
+ */
+ u32 GetBufferCount();
+
+ /**
+ * Get the total played sample count for this audio out.
+ *
+ * @return The played sample count.
+ */
+ u64 GetPlayedSampleCount();
+
+private:
+ /// The AudioOut::Manager this audio out is registered with
+ Manager& manager;
+ /// Manager's mutex
+ std::recursive_mutex& parent_mutex;
+ /// Buffer event, signalled when buffers are ready to be released
+ Kernel::KEvent* event;
+ /// Main audio out system
+ System system;
+};
+
+} // namespace AudioCore::AudioOut
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp
new file mode 100644
index 000000000..35afddf06
--- /dev/null
+++ b/src/audio_core/out/audio_out_system.cpp
@@ -0,0 +1,207 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <mutex>
+
+#include "audio_core/audio_event.h"
+#include "audio_core/audio_manager.h"
+#include "audio_core/out/audio_out_system.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/kernel/k_event.h"
+
+namespace AudioCore::AudioOut {
+
+System::System(Core::System& system_, Kernel::KEvent* event_, size_t session_id_)
+ : system{system_}, buffer_event{event_},
+ session_id{session_id_}, session{std::make_unique<DeviceSession>(system_)} {}
+
+System::~System() {
+ Finalize();
+}
+
+void System::Finalize() {
+ Stop();
+ session->Finalize();
+ buffer_event->GetWritableEvent().Signal();
+}
+
+std::string_view System::GetDefaultOutputDeviceName() {
+ return "DeviceOut";
+}
+
+Result System::IsConfigValid(std::string_view device_name, const AudioOutParameter& in_params) {
+ if ((device_name.size() > 0) && (device_name != GetDefaultOutputDeviceName())) {
+ return Service::Audio::ERR_INVALID_DEVICE_NAME;
+ }
+
+ if (in_params.sample_rate != TargetSampleRate && in_params.sample_rate > 0) {
+ return Service::Audio::ERR_INVALID_SAMPLE_RATE;
+ }
+
+ if (in_params.channel_count == 0 || in_params.channel_count == 2 ||
+ in_params.channel_count == 6) {
+ return ResultSuccess;
+ }
+
+ return Service::Audio::ERR_INVALID_CHANNEL_COUNT;
+}
+
+Result System::Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle_,
+ u64& applet_resource_user_id_) {
+ auto result = IsConfigValid(device_name, in_params);
+ if (result.IsError()) {
+ return result;
+ }
+
+ handle = handle_;
+ applet_resource_user_id = applet_resource_user_id_;
+ if (device_name.empty() || device_name[0] == '\0') {
+ name = std::string(GetDefaultOutputDeviceName());
+ } else {
+ name = std::move(device_name);
+ }
+
+ sample_rate = TargetSampleRate;
+ sample_format = SampleFormat::PcmInt16;
+ channel_count = in_params.channel_count <= 2 ? 2 : 6;
+ volume = 1.0f;
+ return ResultSuccess;
+}
+
+void System::StartSession() {
+ session->Start();
+}
+
+size_t System::GetSessionId() const {
+ return session_id;
+}
+
+Result System::Start() {
+ if (state != State::Stopped) {
+ return Service::Audio::ERR_OPERATION_FAILED;
+ }
+
+ session->Initialize(name, sample_format, channel_count, session_id, handle,
+ applet_resource_user_id, Sink::StreamType::Out);
+ session->SetVolume(volume);
+ session->Start();
+ state = State::Started;
+
+ std::vector<AudioBuffer> buffers_to_flush{};
+ buffers.RegisterBuffers(buffers_to_flush);
+ session->AppendBuffers(buffers_to_flush);
+
+ return ResultSuccess;
+}
+
+Result System::Stop() {
+ if (state == State::Started) {
+ session->Stop();
+ session->SetVolume(0.0f);
+ state = State::Stopped;
+ }
+
+ return ResultSuccess;
+}
+
+bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) {
+ if (buffers.GetTotalBufferCount() == BufferCount) {
+ return false;
+ }
+
+ AudioBuffer new_buffer{
+ .played_timestamp = 0, .samples = buffer.samples, .tag = tag, .size = buffer.size};
+
+ buffers.AppendBuffer(new_buffer);
+ RegisterBuffers();
+
+ return true;
+}
+
+void System::RegisterBuffers() {
+ if (state == State::Started) {
+ std::vector<AudioBuffer> registered_buffers{};
+ buffers.RegisterBuffers(registered_buffers);
+ session->AppendBuffers(registered_buffers);
+ }
+}
+
+void System::ReleaseBuffers() {
+ bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)};
+ if (signal) {
+ // Signal if any buffer was released, or if none are registered, we need more.
+ buffer_event->GetWritableEvent().Signal();
+ }
+}
+
+u32 System::GetReleasedBuffers(std::span<u64> tags) {
+ return buffers.GetReleasedBuffers(tags);
+}
+
+bool System::FlushAudioOutBuffers() {
+ if (state != State::Started) {
+ return false;
+ }
+
+ u32 buffers_released{};
+ buffers.FlushBuffers(buffers_released);
+
+ if (buffers_released > 0) {
+ buffer_event->GetWritableEvent().Signal();
+ }
+ return true;
+}
+
+u16 System::GetChannelCount() const {
+ return channel_count;
+}
+
+u32 System::GetSampleRate() const {
+ return sample_rate;
+}
+
+SampleFormat System::GetSampleFormat() const {
+ return sample_format;
+}
+
+State System::GetState() {
+ switch (state) {
+ case State::Started:
+ case State::Stopped:
+ return state;
+ default:
+ LOG_ERROR(Service_Audio, "AudioOut invalid state!");
+ state = State::Stopped;
+ break;
+ }
+ return state;
+}
+
+std::string System::GetName() const {
+ return name;
+}
+
+f32 System::GetVolume() const {
+ return volume;
+}
+
+void System::SetVolume(const f32 volume_) {
+ volume = volume_;
+ session->SetVolume(volume_);
+}
+
+bool System::ContainsAudioBuffer(const u64 tag) {
+ return buffers.ContainsBuffer(tag);
+}
+
+u32 System::GetBufferCount() {
+ return buffers.GetAppendedRegisteredCount();
+}
+
+u64 System::GetPlayedSampleCount() const {
+ return session->GetPlayedSampleCount();
+}
+
+} // namespace AudioCore::AudioOut
diff --git a/src/audio_core/out/audio_out_system.h b/src/audio_core/out/audio_out_system.h
new file mode 100644
index 000000000..4ca2f3417
--- /dev/null
+++ b/src/audio_core/out/audio_out_system.h
@@ -0,0 +1,257 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <atomic>
+#include <memory>
+#include <span>
+#include <string>
+
+#include "audio_core/common/common.h"
+#include "audio_core/device/audio_buffers.h"
+#include "audio_core/device/device_session.h"
+#include "core/hle/service/audio/errors.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+class KEvent;
+}
+
+namespace AudioCore::AudioOut {
+
+constexpr SessionTypes SessionType = SessionTypes::AudioOut;
+
+struct AudioOutParameter {
+ /* 0x0 */ s32_le sample_rate;
+ /* 0x4 */ u16_le channel_count;
+ /* 0x6 */ u16_le reserved;
+};
+static_assert(sizeof(AudioOutParameter) == 0x8, "AudioOutParameter is an invalid size");
+
+struct AudioOutParameterInternal {
+ /* 0x0 */ u32_le sample_rate;
+ /* 0x4 */ u32_le channel_count;
+ /* 0x8 */ u32_le sample_format;
+ /* 0xC */ u32_le state;
+};
+static_assert(sizeof(AudioOutParameterInternal) == 0x10,
+ "AudioOutParameterInternal is an invalid size");
+
+struct AudioOutBuffer {
+ /* 0x00 */ AudioOutBuffer* next;
+ /* 0x08 */ VAddr samples;
+ /* 0x10 */ u64 capacity;
+ /* 0x18 */ u64 size;
+ /* 0x20 */ u64 offset;
+};
+static_assert(sizeof(AudioOutBuffer) == 0x28, "AudioOutBuffer is an invalid size");
+
+enum class State {
+ Started,
+ Stopped,
+};
+
+/**
+ * Controls and drives audio output.
+ */
+class System {
+public:
+ explicit System(Core::System& system, Kernel::KEvent* event, size_t session_id);
+ ~System();
+
+ /**
+ * Get the default audio output device name.
+ *
+ * @return The default audio output device name.
+ */
+ std::string_view GetDefaultOutputDeviceName();
+
+ /**
+ * Is the given initialize config valid?
+ *
+ * @param device_name - The name of the requested output device.
+ * @param in_params - Input parameters, see AudioOutParameter.
+ * @return Result code.
+ */
+ Result IsConfigValid(std::string_view device_name, const AudioOutParameter& in_params);
+
+ /**
+ * Initialize this system.
+ *
+ * @param device_name - The name of the requested output device.
+ * @param in_params - Input parameters, see AudioOutParameter.
+ * @param handle - Unused.
+ * @param applet_resource_user_id - Unused.
+ * @return Result code.
+ */
+ Result Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle,
+ u64& applet_resource_user_id);
+
+ /**
+ * Start this system.
+ *
+ * @return Result code.
+ */
+ Result Start();
+
+ /**
+ * Stop this system.
+ *
+ * @return Result code.
+ */
+ Result Stop();
+
+ /**
+ * Finalize this system.
+ */
+ void Finalize();
+
+ /**
+ * Start this system's device session.
+ */
+ void StartSession();
+
+ /**
+ * Get this system's id.
+ */
+ size_t GetSessionId() const;
+
+ /**
+ * Append a new buffer to the device.
+ *
+ * @param buffer - New buffer to append.
+ * @param tag - Unique tag of the buffer.
+ * @return True if the buffer was appended, otherwise false.
+ */
+ bool AppendBuffer(const AudioOutBuffer& buffer, u64 tag);
+
+ /**
+ * Register all appended buffers.
+ */
+ void RegisterBuffers();
+
+ /**
+ * Release all registered buffers.
+ */
+ void ReleaseBuffers();
+
+ /**
+ * Get all released buffers.
+ *
+ * @param tags - Container to be filled with the released buffers' tags.
+ * @return The number of buffers released.
+ */
+ u32 GetReleasedBuffers(std::span<u64> tags);
+
+ /**
+ * Flush all appended and registered buffers.
+ *
+ * @return True if buffers were successfully flushed, otherwise false.
+ */
+ bool FlushAudioOutBuffers();
+
+ /**
+ * Get this system's current channel count.
+ *
+ * @return The channel count.
+ */
+ u16 GetChannelCount() const;
+
+ /**
+ * Get this system's current sample rate.
+ *
+ * @return The sample rate.
+ */
+ u32 GetSampleRate() const;
+
+ /**
+ * Get this system's current sample format.
+ *
+ * @return The sample format.
+ */
+ SampleFormat GetSampleFormat() const;
+
+ /**
+ * Get this system's current state.
+ *
+ * @return The current state.
+ */
+ State GetState();
+
+ /**
+ * Get this system's name.
+ *
+ * @return The system's name.
+ */
+ std::string GetName() const;
+
+ /**
+ * Get this system's current volume.
+ *
+ * @return The system's current volume.
+ */
+ f32 GetVolume() const;
+
+ /**
+ * Set this system's current volume.
+ *
+ * @param The new volume.
+ */
+ void SetVolume(f32 volume);
+
+ /**
+ * Does the system contain this buffer?
+ *
+ * @param tag - Unique tag to search for.
+ * @return True if the buffer is in the system, otherwise false.
+ */
+ bool ContainsAudioBuffer(u64 tag);
+
+ /**
+ * Get the maximum number of usable buffers (default 32).
+ *
+ * @return The number of buffers.
+ */
+ u32 GetBufferCount();
+
+ /**
+ * Get the total number of samples played by this system.
+ *
+ * @return The number of samples.
+ */
+ u64 GetPlayedSampleCount() const;
+
+private:
+ /// Core system
+ Core::System& system;
+ /// (Unused)
+ u32 handle{};
+ /// (Unused)
+ u64 applet_resource_user_id{};
+ /// Buffer event, signalled when a buffer is ready
+ Kernel::KEvent* buffer_event;
+ /// Session id of this system
+ size_t session_id{};
+ /// Device session for this system
+ std::unique_ptr<DeviceSession> session;
+ /// Audio buffers in use by this system
+ AudioBuffers<BufferCount> buffers{BufferCount};
+ /// Sample rate of this system
+ u32 sample_rate{};
+ /// Sample format of this system
+ SampleFormat sample_format{SampleFormat::PcmInt16};
+ /// Channel count of this system
+ u16 channel_count{};
+ /// State of this system
+ std::atomic<State> state{State::Stopped};
+ /// Name of this system
+ std::string name{};
+ /// Volume of this system
+ f32 volume{1.0f};
+};
+
+} // namespace AudioCore::AudioOut