suyu/src/audio_core/out/audio_out_system.cpp
2024-03-15 21:48:09 +00:00

220 lines
5.6 KiB
C++

// 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();
}
std::string_view System::GetDefaultOutputDeviceName() const {
return "DeviceOut";
}
Result System::IsConfigValid(std::string_view device_name,
const AudioOutParameter& in_params) const {
if ((device_name.size() > 0) && (device_name != GetDefaultOutputDeviceName())) {
return Service::Audio::ResultNotFound;
}
if (in_params.sample_rate != TargetSampleRate && in_params.sample_rate > 0) {
return Service::Audio::ResultInvalidSampleRate;
}
if (in_params.channel_count == 0 || in_params.channel_count == 2 ||
in_params.channel_count == 6) {
return ResultSuccess;
}
return Service::Audio::ResultInvalidChannelCount;
}
Result System::Initialize(std::string device_name, const AudioOutParameter& in_params,
Kernel::KProcess* 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::ResultOperationFailed;
}
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;
boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};
buffers.RegisterBuffers(buffers_to_flush);
session->AppendBuffers(buffers_to_flush);
session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
return ResultSuccess;
}
Result System::Stop() {
if (state == State::Started) {
session->Stop();
session->SetVolume(0.0f);
session->ClearBuffers();
if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) {
buffer_event->Signal();
}
state = State::Stopped;
}
return ResultSuccess;
}
bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) {
if (buffers.GetTotalBufferCount() == BufferCount) {
return false;
}
const auto timestamp{buffers.GetNextTimestamp()};
const AudioBuffer new_buffer{
.start_timestamp = timestamp,
.end_timestamp = timestamp + buffer.size / (channel_count * sizeof(s16)),
.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) {
boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};
buffers.RegisterBuffers(registered_buffers);
session->AppendBuffers(registered_buffers);
}
}
void System::ReleaseBuffers() {
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)};
if (signal) {
// Signal if any buffer was released, or if none are registered, we need more.
buffer_event->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->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) const {
return buffers.ContainsBuffer(tag);
}
u32 System::GetBufferCount() const {
return buffers.GetAppendedRegisteredCount();
}
u64 System::GetPlayedSampleCount() const {
return session->GetPlayedSampleCount();
}
} // namespace AudioCore::AudioOut