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