summaryrefslogtreecommitdiffstats
path: root/src/audio_core/in
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core/in')
-rw-r--r--src/audio_core/in/audio_in.cpp100
-rw-r--r--src/audio_core/in/audio_in.h147
-rw-r--r--src/audio_core/in/audio_in_system.cpp213
-rw-r--r--src/audio_core/in/audio_in_system.h275
4 files changed, 735 insertions, 0 deletions
diff --git a/src/audio_core/in/audio_in.cpp b/src/audio_core/in/audio_in.cpp
new file mode 100644
index 000000000..c946895d6
--- /dev/null
+++ b/src/audio_core/in/audio_in.cpp
@@ -0,0 +1,100 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "audio_core/audio_in_manager.h"
+#include "audio_core/in/audio_in.h"
+#include "core/hle/kernel/k_event.h"
+
+namespace AudioCore::AudioIn {
+
+In::In(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 In::Free() {
+ std::scoped_lock l{parent_mutex};
+ manager.ReleaseSessionId(system.GetSessionId());
+}
+
+System& In::GetSystem() {
+ return system;
+}
+
+AudioIn::State In::GetState() {
+ std::scoped_lock l{parent_mutex};
+ return system.GetState();
+}
+
+Result In::StartSystem() {
+ std::scoped_lock l{parent_mutex};
+ return system.Start();
+}
+
+void In::StartSession() {
+ std::scoped_lock l{parent_mutex};
+ system.StartSession();
+}
+
+Result In::StopSystem() {
+ std::scoped_lock l{parent_mutex};
+ return system.Stop();
+}
+
+Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
+ std::scoped_lock l{parent_mutex};
+
+ if (system.AppendBuffer(buffer, tag)) {
+ return ResultSuccess;
+ }
+ return Service::Audio::ERR_BUFFER_COUNT_EXCEEDED;
+}
+
+void In::ReleaseAndRegisterBuffers() {
+ std::scoped_lock l{parent_mutex};
+ if (system.GetState() == State::Started) {
+ system.ReleaseBuffers();
+ system.RegisterBuffers();
+ }
+}
+
+bool In::FlushAudioInBuffers() {
+ std::scoped_lock l{parent_mutex};
+ return system.FlushAudioInBuffers();
+}
+
+u32 In::GetReleasedBuffers(std::span<u64> tags) {
+ std::scoped_lock l{parent_mutex};
+ return system.GetReleasedBuffers(tags);
+}
+
+Kernel::KReadableEvent& In::GetBufferEvent() {
+ std::scoped_lock l{parent_mutex};
+ return event->GetReadableEvent();
+}
+
+f32 In::GetVolume() {
+ std::scoped_lock l{parent_mutex};
+ return system.GetVolume();
+}
+
+void In::SetVolume(f32 volume) {
+ std::scoped_lock l{parent_mutex};
+ system.SetVolume(volume);
+}
+
+bool In::ContainsAudioBuffer(u64 tag) {
+ std::scoped_lock l{parent_mutex};
+ return system.ContainsAudioBuffer(tag);
+}
+
+u32 In::GetBufferCount() {
+ std::scoped_lock l{parent_mutex};
+ return system.GetBufferCount();
+}
+
+u64 In::GetPlayedSampleCount() {
+ std::scoped_lock l{parent_mutex};
+ return system.GetPlayedSampleCount();
+}
+
+} // namespace AudioCore::AudioIn
diff --git a/src/audio_core/in/audio_in.h b/src/audio_core/in/audio_in.h
new file mode 100644
index 000000000..6253891d5
--- /dev/null
+++ b/src/audio_core/in/audio_in.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/in/audio_in_system.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace AudioCore::AudioIn {
+class Manager;
+
+/**
+ * Interface between the service and audio in system. Mainly responsible for forwarding service
+ * calls to the system.
+ */
+class In {
+public:
+ explicit In(Core::System& system, Manager& manager, Kernel::KEvent* event, size_t session_id);
+
+ /**
+ * Free this audio in from the audio in manager.
+ */
+ void Free();
+
+ /**
+ * Get this audio in's system.
+ */
+ System& GetSystem();
+
+ /**
+ * Get the current state.
+ *
+ * @return Started or Stopped.
+ */
+ AudioIn::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 AudioInBuffer& buffer, u64 tag);
+
+ /**
+ * Release all completed buffers, and register any appended.
+ */
+ void ReleaseAndRegisterBuffers();
+
+ /**
+ * Flush all buffers.
+ */
+ bool FlushAudioInBuffers();
+
+ /**
+ * 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 in, 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 in.
+ *
+ * @return The played sample count.
+ */
+ u64 GetPlayedSampleCount();
+
+private:
+ /// The AudioIn::Manager this audio in 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 in system
+ System system;
+};
+
+} // namespace AudioCore::AudioIn
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp
new file mode 100644
index 000000000..ec5d37ed4
--- /dev/null
+++ b/src/audio_core/in/audio_in_system.cpp
@@ -0,0 +1,213 @@
+// 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/in/audio_in_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::AudioIn {
+
+System::System(Core::System& system_, Kernel::KEvent* event_, const 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();
+}
+
+void System::StartSession() {
+ session->Start();
+}
+
+size_t System::GetSessionId() const {
+ return session_id;
+}
+
+std::string_view System::GetDefaultDeviceName() {
+ return "BuiltInHeadset";
+}
+
+std::string_view System::GetDefaultUacDeviceName() {
+ return "Uac";
+}
+
+Result System::IsConfigValid(const std::string_view device_name,
+ const AudioInParameter& in_params) {
+ if ((device_name.size() > 0) &&
+ (device_name != GetDefaultDeviceName() && device_name != GetDefaultUacDeviceName())) {
+ 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;
+ }
+
+ return ResultSuccess;
+}
+
+Result System::Initialize(std::string& device_name, const AudioInParameter& in_params,
+ const u32 handle_, const 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(GetDefaultDeviceName());
+ } 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;
+ is_uac = name == "Uac";
+ return ResultSuccess;
+}
+
+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::In);
+ 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 AudioInBuffer& buffer, const 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::FlushAudioInBuffers() {
+ 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, "AudioIn 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();
+}
+
+bool System::IsUac() const {
+ return is_uac;
+}
+
+} // namespace AudioCore::AudioIn
diff --git a/src/audio_core/in/audio_in_system.h b/src/audio_core/in/audio_in_system.h
new file mode 100644
index 000000000..165e35d83
--- /dev/null
+++ b/src/audio_core/in/audio_in_system.h
@@ -0,0 +1,275 @@
+// 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::AudioIn {
+
+constexpr SessionTypes SessionType = SessionTypes::AudioIn;
+
+struct AudioInParameter {
+ /* 0x0 */ s32_le sample_rate;
+ /* 0x4 */ u16_le channel_count;
+ /* 0x6 */ u16_le reserved;
+};
+static_assert(sizeof(AudioInParameter) == 0x8, "AudioInParameter is an invalid size");
+
+struct AudioInParameterInternal {
+ /* 0x0 */ u32_le sample_rate;
+ /* 0x4 */ u32_le channel_count;
+ /* 0x8 */ u32_le sample_format;
+ /* 0xC */ u32_le state;
+};
+static_assert(sizeof(AudioInParameterInternal) == 0x10,
+ "AudioInParameterInternal is an invalid size");
+
+struct AudioInBuffer {
+ /* 0x00 */ AudioInBuffer* next;
+ /* 0x08 */ VAddr samples;
+ /* 0x10 */ u64 capacity;
+ /* 0x18 */ u64 size;
+ /* 0x20 */ u64 offset;
+};
+static_assert(sizeof(AudioInBuffer) == 0x28, "AudioInBuffer is an invalid size");
+
+enum class State {
+ Started,
+ Stopped,
+};
+
+/**
+ * Controls and drives audio input.
+ */
+class System {
+public:
+ explicit System(Core::System& system, Kernel::KEvent* event, size_t session_id);
+ ~System();
+
+ /**
+ * Get the default audio input device name.
+ *
+ * @return The default audio input device name.
+ */
+ std::string_view GetDefaultDeviceName();
+
+ /**
+ * Get the default USB audio input device name.
+ * This is preferred over non-USB as some games refuse to work with the BuiltInHeadset
+ * (e.g Let's Sing).
+ *
+ * @return The default USB audio input device name.
+ */
+ std::string_view GetDefaultUacDeviceName();
+
+ /**
+ * Is the given initialize config valid?
+ *
+ * @param device_name - The name of the requested input device.
+ * @param in_params - Input parameters, see AudioInParameter.
+ * @return Result code.
+ */
+ Result IsConfigValid(std::string_view device_name, const AudioInParameter& in_params);
+
+ /**
+ * Initialize this system.
+ *
+ * @param device_name - The name of the requested input device.
+ * @param in_params - Input parameters, see AudioInParameter.
+ * @param handle - Unused.
+ * @param applet_resource_user_id - Unused.
+ * @return Result code.
+ */
+ Result Initialize(std::string& device_name, const AudioInParameter& 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 AudioInBuffer& 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 FlushAudioInBuffers();
+
+ /**
+ * 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;
+
+ /**
+ * Is this system using a USB device?
+ *
+ * @return True if using a USB device, otherwise false.
+ */
+ bool IsUac() 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};
+ /// Is this system's device USB?
+ bool is_uac{false};
+};
+
+} // namespace AudioCore::AudioIn