summaryrefslogtreecommitdiff
path: root/src/audio_core/out
diff options
context:
space:
mode:
authorGravatar Kelebek12022-07-16 23:48:45 +0100
committerGravatar Kelebek12022-07-22 01:11:32 +0100
commit458da8a94877677f086f06cdeecf959ec4283a33 (patch)
tree583166d77602ad90a0d552f37de8729ad80fd6c1 /src/audio_core/out
parentMerge pull request #8598 from Link4565/recv-dontwait (diff)
downloadyuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.gz
yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.xz
yuzu-458da8a94877677f086f06cdeecf959ec4283a33.zip
Project Andio
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 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/audio_out_manager.h"
5#include "audio_core/out/audio_out.h"
6#include "core/hle/kernel/k_event.h"
7
8namespace AudioCore::AudioOut {
9
10Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
11 : manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
12 session_id_} {}
13
14void Out::Free() {
15 std::scoped_lock l{parent_mutex};
16 manager.ReleaseSessionId(system.GetSessionId());
17}
18
19System& Out::GetSystem() {
20 return system;
21}
22
23AudioOut::State Out::GetState() {
24 std::scoped_lock l{parent_mutex};
25 return system.GetState();
26}
27
28Result Out::StartSystem() {
29 std::scoped_lock l{parent_mutex};
30 return system.Start();
31}
32
33void Out::StartSession() {
34 std::scoped_lock l{parent_mutex};
35 system.StartSession();
36}
37
38Result Out::StopSystem() {
39 std::scoped_lock l{parent_mutex};
40 return system.Stop();
41}
42
43Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
44 std::scoped_lock l{parent_mutex};
45
46 if (system.AppendBuffer(buffer, tag)) {
47 return ResultSuccess;
48 }
49 return Service::Audio::ERR_BUFFER_COUNT_EXCEEDED;
50}
51
52void Out::ReleaseAndRegisterBuffers() {
53 std::scoped_lock l{parent_mutex};
54 if (system.GetState() == State::Started) {
55 system.ReleaseBuffers();
56 system.RegisterBuffers();
57 }
58}
59
60bool Out::FlushAudioOutBuffers() {
61 std::scoped_lock l{parent_mutex};
62 return system.FlushAudioOutBuffers();
63}
64
65u32 Out::GetReleasedBuffers(std::span<u64> tags) {
66 std::scoped_lock l{parent_mutex};
67 return system.GetReleasedBuffers(tags);
68}
69
70Kernel::KReadableEvent& Out::GetBufferEvent() {
71 std::scoped_lock l{parent_mutex};
72 return event->GetReadableEvent();
73}
74
75f32 Out::GetVolume() {
76 std::scoped_lock l{parent_mutex};
77 return system.GetVolume();
78}
79
80void Out::SetVolume(const f32 volume) {
81 std::scoped_lock l{parent_mutex};
82 system.SetVolume(volume);
83}
84
85bool Out::ContainsAudioBuffer(const u64 tag) {
86 std::scoped_lock l{parent_mutex};
87 return system.ContainsAudioBuffer(tag);
88}
89
90u32 Out::GetBufferCount() {
91 std::scoped_lock l{parent_mutex};
92 return system.GetBufferCount();
93}
94
95u64 Out::GetPlayedSampleCount() {
96 std::scoped_lock l{parent_mutex};
97 return system.GetPlayedSampleCount();
98}
99
100} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "audio_core/out/audio_out_system.h"
9
10namespace Core {
11class System;
12}
13
14namespace Kernel {
15class KEvent;
16class KReadableEvent;
17} // namespace Kernel
18
19namespace AudioCore::AudioOut {
20class Manager;
21
22/**
23 * Interface between the service and audio out system. Mainly responsible for forwarding service
24 * calls to the system.
25 */
26class Out {
27public:
28 explicit Out(Core::System& system, Manager& manager, Kernel::KEvent* event, size_t session_id);
29
30 /**
31 * Free this audio out from the audio out manager.
32 */
33 void Free();
34
35 /**
36 * Get this audio out's system.
37 */
38 System& GetSystem();
39
40 /**
41 * Get the current state.
42 *
43 * @return Started or Stopped.
44 */
45 AudioOut::State GetState();
46
47 /**
48 * Start the system
49 *
50 * @return Result code
51 */
52 Result StartSystem();
53
54 /**
55 * Start the system's device session.
56 */
57 void StartSession();
58
59 /**
60 * Stop the system.
61 *
62 * @return Result code
63 */
64 Result StopSystem();
65
66 /**
67 * Append a new buffer to the system, the buffer event will be signalled when it is filled.
68 *
69 * @param buffer - The new buffer to append.
70 * @param tag - Unique tag for this buffer.
71 * @return Result code.
72 */
73 Result AppendBuffer(const AudioOutBuffer& buffer, u64 tag);
74
75 /**
76 * Release all completed buffers, and register any appended.
77 */
78 void ReleaseAndRegisterBuffers();
79
80 /**
81 * Flush all buffers.
82 */
83 bool FlushAudioOutBuffers();
84
85 /**
86 * Get all of the currently released buffers.
87 *
88 * @param tags - Output container for the buffer tags which were released.
89 * @return The number of buffers released.
90 */
91 u32 GetReleasedBuffers(std::span<u64> tags);
92
93 /**
94 * Get the buffer event for this audio out, this event will be signalled when a buffer is
95 * filled.
96 * @return The buffer event.
97 */
98 Kernel::KReadableEvent& GetBufferEvent();
99
100 /**
101 * Get the current system volume.
102 *
103 * @return The current volume.
104 */
105 f32 GetVolume();
106
107 /**
108 * Set the system volume.
109 *
110 * @param volume - The volume to set.
111 */
112 void SetVolume(f32 volume);
113
114 /**
115 * Check if a buffer is in the system.
116 *
117 * @param tag - The tag to search for.
118 * @return True if the buffer is in the system, otherwise false.
119 */
120 bool ContainsAudioBuffer(u64 tag);
121
122 /**
123 * Get the maximum number of buffers.
124 *
125 * @return The maximum number of buffers.
126 */
127 u32 GetBufferCount();
128
129 /**
130 * Get the total played sample count for this audio out.
131 *
132 * @return The played sample count.
133 */
134 u64 GetPlayedSampleCount();
135
136private:
137 /// The AudioOut::Manager this audio out is registered with
138 Manager& manager;
139 /// Manager's mutex
140 std::recursive_mutex& parent_mutex;
141 /// Buffer event, signalled when buffers are ready to be released
142 Kernel::KEvent* event;
143 /// Main audio out system
144 System system;
145};
146
147} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <mutex>
5
6#include "audio_core/audio_event.h"
7#include "audio_core/audio_manager.h"
8#include "audio_core/out/audio_out_system.h"
9#include "common/logging/log.h"
10#include "core/core.h"
11#include "core/core_timing.h"
12#include "core/hle/kernel/k_event.h"
13
14namespace AudioCore::AudioOut {
15
16System::System(Core::System& system_, Kernel::KEvent* event_, size_t session_id_)
17 : system{system_}, buffer_event{event_},
18 session_id{session_id_}, session{std::make_unique<DeviceSession>(system_)} {}
19
20System::~System() {
21 Finalize();
22}
23
24void System::Finalize() {
25 Stop();
26 session->Finalize();
27 buffer_event->GetWritableEvent().Signal();
28}
29
30std::string_view System::GetDefaultOutputDeviceName() {
31 return "DeviceOut";
32}
33
34Result System::IsConfigValid(std::string_view device_name, const AudioOutParameter& in_params) {
35 if ((device_name.size() > 0) && (device_name != GetDefaultOutputDeviceName())) {
36 return Service::Audio::ERR_INVALID_DEVICE_NAME;
37 }
38
39 if (in_params.sample_rate != TargetSampleRate && in_params.sample_rate > 0) {
40 return Service::Audio::ERR_INVALID_SAMPLE_RATE;
41 }
42
43 if (in_params.channel_count == 0 || in_params.channel_count == 2 ||
44 in_params.channel_count == 6) {
45 return ResultSuccess;
46 }
47
48 return Service::Audio::ERR_INVALID_CHANNEL_COUNT;
49}
50
51Result System::Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle_,
52 u64& applet_resource_user_id_) {
53 auto result = IsConfigValid(device_name, in_params);
54 if (result.IsError()) {
55 return result;
56 }
57
58 handle = handle_;
59 applet_resource_user_id = applet_resource_user_id_;
60 if (device_name.empty() || device_name[0] == '\0') {
61 name = std::string(GetDefaultOutputDeviceName());
62 } else {
63 name = std::move(device_name);
64 }
65
66 sample_rate = TargetSampleRate;
67 sample_format = SampleFormat::PcmInt16;
68 channel_count = in_params.channel_count <= 2 ? 2 : 6;
69 volume = 1.0f;
70 return ResultSuccess;
71}
72
73void System::StartSession() {
74 session->Start();
75}
76
77size_t System::GetSessionId() const {
78 return session_id;
79}
80
81Result System::Start() {
82 if (state != State::Stopped) {
83 return Service::Audio::ERR_OPERATION_FAILED;
84 }
85
86 session->Initialize(name, sample_format, channel_count, session_id, handle,
87 applet_resource_user_id, Sink::StreamType::Out);
88 session->SetVolume(volume);
89 session->Start();
90 state = State::Started;
91
92 std::vector<AudioBuffer> buffers_to_flush{};
93 buffers.RegisterBuffers(buffers_to_flush);
94 session->AppendBuffers(buffers_to_flush);
95
96 return ResultSuccess;
97}
98
99Result System::Stop() {
100 if (state == State::Started) {
101 session->Stop();
102 session->SetVolume(0.0f);
103 state = State::Stopped;
104 }
105
106 return ResultSuccess;
107}
108
109bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) {
110 if (buffers.GetTotalBufferCount() == BufferCount) {
111 return false;
112 }
113
114 AudioBuffer new_buffer{
115 .played_timestamp = 0, .samples = buffer.samples, .tag = tag, .size = buffer.size};
116
117 buffers.AppendBuffer(new_buffer);
118 RegisterBuffers();
119
120 return true;
121}
122
123void System::RegisterBuffers() {
124 if (state == State::Started) {
125 std::vector<AudioBuffer> registered_buffers{};
126 buffers.RegisterBuffers(registered_buffers);
127 session->AppendBuffers(registered_buffers);
128 }
129}
130
131void System::ReleaseBuffers() {
132 bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)};
133 if (signal) {
134 // Signal if any buffer was released, or if none are registered, we need more.
135 buffer_event->GetWritableEvent().Signal();
136 }
137}
138
139u32 System::GetReleasedBuffers(std::span<u64> tags) {
140 return buffers.GetReleasedBuffers(tags);
141}
142
143bool System::FlushAudioOutBuffers() {
144 if (state != State::Started) {
145 return false;
146 }
147
148 u32 buffers_released{};
149 buffers.FlushBuffers(buffers_released);
150
151 if (buffers_released > 0) {
152 buffer_event->GetWritableEvent().Signal();
153 }
154 return true;
155}
156
157u16 System::GetChannelCount() const {
158 return channel_count;
159}
160
161u32 System::GetSampleRate() const {
162 return sample_rate;
163}
164
165SampleFormat System::GetSampleFormat() const {
166 return sample_format;
167}
168
169State System::GetState() {
170 switch (state) {
171 case State::Started:
172 case State::Stopped:
173 return state;
174 default:
175 LOG_ERROR(Service_Audio, "AudioOut invalid state!");
176 state = State::Stopped;
177 break;
178 }
179 return state;
180}
181
182std::string System::GetName() const {
183 return name;
184}
185
186f32 System::GetVolume() const {
187 return volume;
188}
189
190void System::SetVolume(const f32 volume_) {
191 volume = volume_;
192 session->SetVolume(volume_);
193}
194
195bool System::ContainsAudioBuffer(const u64 tag) {
196 return buffers.ContainsBuffer(tag);
197}
198
199u32 System::GetBufferCount() {
200 return buffers.GetAppendedRegisteredCount();
201}
202
203u64 System::GetPlayedSampleCount() const {
204 return session->GetPlayedSampleCount();
205}
206
207} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <atomic>
7#include <memory>
8#include <span>
9#include <string>
10
11#include "audio_core/common/common.h"
12#include "audio_core/device/audio_buffers.h"
13#include "audio_core/device/device_session.h"
14#include "core/hle/service/audio/errors.h"
15
16namespace Core {
17class System;
18}
19
20namespace Kernel {
21class KEvent;
22}
23
24namespace AudioCore::AudioOut {
25
26constexpr SessionTypes SessionType = SessionTypes::AudioOut;
27
28struct AudioOutParameter {
29 /* 0x0 */ s32_le sample_rate;
30 /* 0x4 */ u16_le channel_count;
31 /* 0x6 */ u16_le reserved;
32};
33static_assert(sizeof(AudioOutParameter) == 0x8, "AudioOutParameter is an invalid size");
34
35struct AudioOutParameterInternal {
36 /* 0x0 */ u32_le sample_rate;
37 /* 0x4 */ u32_le channel_count;
38 /* 0x8 */ u32_le sample_format;
39 /* 0xC */ u32_le state;
40};
41static_assert(sizeof(AudioOutParameterInternal) == 0x10,
42 "AudioOutParameterInternal is an invalid size");
43
44struct AudioOutBuffer {
45 /* 0x00 */ AudioOutBuffer* next;
46 /* 0x08 */ VAddr samples;
47 /* 0x10 */ u64 capacity;
48 /* 0x18 */ u64 size;
49 /* 0x20 */ u64 offset;
50};
51static_assert(sizeof(AudioOutBuffer) == 0x28, "AudioOutBuffer is an invalid size");
52
53enum class State {
54 Started,
55 Stopped,
56};
57
58/**
59 * Controls and drives audio output.
60 */
61class System {
62public:
63 explicit System(Core::System& system, Kernel::KEvent* event, size_t session_id);
64 ~System();
65
66 /**
67 * Get the default audio output device name.
68 *
69 * @return The default audio output device name.
70 */
71 std::string_view GetDefaultOutputDeviceName();
72
73 /**
74 * Is the given initialize config valid?
75 *
76 * @param device_name - The name of the requested output device.
77 * @param in_params - Input parameters, see AudioOutParameter.
78 * @return Result code.
79 */
80 Result IsConfigValid(std::string_view device_name, const AudioOutParameter& in_params);
81
82 /**
83 * Initialize this system.
84 *
85 * @param device_name - The name of the requested output device.
86 * @param in_params - Input parameters, see AudioOutParameter.
87 * @param handle - Unused.
88 * @param applet_resource_user_id - Unused.
89 * @return Result code.
90 */
91 Result Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle,
92 u64& applet_resource_user_id);
93
94 /**
95 * Start this system.
96 *
97 * @return Result code.
98 */
99 Result Start();
100
101 /**
102 * Stop this system.
103 *
104 * @return Result code.
105 */
106 Result Stop();
107
108 /**
109 * Finalize this system.
110 */
111 void Finalize();
112
113 /**
114 * Start this system's device session.
115 */
116 void StartSession();
117
118 /**
119 * Get this system's id.
120 */
121 size_t GetSessionId() const;
122
123 /**
124 * Append a new buffer to the device.
125 *
126 * @param buffer - New buffer to append.
127 * @param tag - Unique tag of the buffer.
128 * @return True if the buffer was appended, otherwise false.
129 */
130 bool AppendBuffer(const AudioOutBuffer& buffer, u64 tag);
131
132 /**
133 * Register all appended buffers.
134 */
135 void RegisterBuffers();
136
137 /**
138 * Release all registered buffers.
139 */
140 void ReleaseBuffers();
141
142 /**
143 * Get all released buffers.
144 *
145 * @param tags - Container to be filled with the released buffers' tags.
146 * @return The number of buffers released.
147 */
148 u32 GetReleasedBuffers(std::span<u64> tags);
149
150 /**
151 * Flush all appended and registered buffers.
152 *
153 * @return True if buffers were successfully flushed, otherwise false.
154 */
155 bool FlushAudioOutBuffers();
156
157 /**
158 * Get this system's current channel count.
159 *
160 * @return The channel count.
161 */
162 u16 GetChannelCount() const;
163
164 /**
165 * Get this system's current sample rate.
166 *
167 * @return The sample rate.
168 */
169 u32 GetSampleRate() const;
170
171 /**
172 * Get this system's current sample format.
173 *
174 * @return The sample format.
175 */
176 SampleFormat GetSampleFormat() const;
177
178 /**
179 * Get this system's current state.
180 *
181 * @return The current state.
182 */
183 State GetState();
184
185 /**
186 * Get this system's name.
187 *
188 * @return The system's name.
189 */
190 std::string GetName() const;
191
192 /**
193 * Get this system's current volume.
194 *
195 * @return The system's current volume.
196 */
197 f32 GetVolume() const;
198
199 /**
200 * Set this system's current volume.
201 *
202 * @param The new volume.
203 */
204 void SetVolume(f32 volume);
205
206 /**
207 * Does the system contain this buffer?
208 *
209 * @param tag - Unique tag to search for.
210 * @return True if the buffer is in the system, otherwise false.
211 */
212 bool ContainsAudioBuffer(u64 tag);
213
214 /**
215 * Get the maximum number of usable buffers (default 32).
216 *
217 * @return The number of buffers.
218 */
219 u32 GetBufferCount();
220
221 /**
222 * Get the total number of samples played by this system.
223 *
224 * @return The number of samples.
225 */
226 u64 GetPlayedSampleCount() const;
227
228private:
229 /// Core system
230 Core::System& system;
231 /// (Unused)
232 u32 handle{};
233 /// (Unused)
234 u64 applet_resource_user_id{};
235 /// Buffer event, signalled when a buffer is ready
236 Kernel::KEvent* buffer_event;
237 /// Session id of this system
238 size_t session_id{};
239 /// Device session for this system
240 std::unique_ptr<DeviceSession> session;
241 /// Audio buffers in use by this system
242 AudioBuffers<BufferCount> buffers{BufferCount};
243 /// Sample rate of this system
244 u32 sample_rate{};
245 /// Sample format of this system
246 SampleFormat sample_format{SampleFormat::PcmInt16};
247 /// Channel count of this system
248 u16 channel_count{};
249 /// State of this system
250 std::atomic<State> state{State::Stopped};
251 /// Name of this system
252 std::string name{};
253 /// Volume of this system
254 f32 volume{1.0f};
255};
256
257} // namespace AudioCore::AudioOut