diff options
| author | 2022-07-16 23:48:45 +0100 | |
|---|---|---|
| committer | 2022-07-22 01:11:32 +0100 | |
| commit | 458da8a94877677f086f06cdeecf959ec4283a33 (patch) | |
| tree | 583166d77602ad90a0d552f37de8729ad80fd6c1 /src/audio_core/device | |
| parent | Merge pull request #8598 from Link4565/recv-dontwait (diff) | |
| download | yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.gz yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.xz yuzu-458da8a94877677f086f06cdeecf959ec4283a33.zip | |
Project Andio
Diffstat (limited to 'src/audio_core/device')
| -rw-r--r-- | src/audio_core/device/audio_buffer.h | 21 | ||||
| -rw-r--r-- | src/audio_core/device/audio_buffers.h | 304 | ||||
| -rw-r--r-- | src/audio_core/device/device_session.cpp | 114 | ||||
| -rw-r--r-- | src/audio_core/device/device_session.h | 126 |
4 files changed, 565 insertions, 0 deletions
diff --git a/src/audio_core/device/audio_buffer.h b/src/audio_core/device/audio_buffer.h new file mode 100644 index 000000000..cae7fa970 --- /dev/null +++ b/src/audio_core/device/audio_buffer.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | |||
| 8 | namespace AudioCore { | ||
| 9 | |||
| 10 | struct AudioBuffer { | ||
| 11 | /// Timestamp this buffer completed playing. | ||
| 12 | s64 played_timestamp; | ||
| 13 | /// Game memory address for these samples. | ||
| 14 | VAddr samples; | ||
| 15 | /// Unqiue identifier for this buffer. | ||
| 16 | u64 tag; | ||
| 17 | /// Size of the samples buffer. | ||
| 18 | u64 size; | ||
| 19 | }; | ||
| 20 | |||
| 21 | } // namespace AudioCore | ||
diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h new file mode 100644 index 000000000..5d1979ea0 --- /dev/null +++ b/src/audio_core/device/audio_buffers.h | |||
| @@ -0,0 +1,304 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <mutex> | ||
| 8 | #include <span> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "audio_buffer.h" | ||
| 12 | #include "audio_core/device/device_session.h" | ||
| 13 | #include "core/core_timing.h" | ||
| 14 | |||
| 15 | namespace AudioCore { | ||
| 16 | |||
| 17 | constexpr s32 BufferAppendLimit = 4; | ||
| 18 | |||
| 19 | /** | ||
| 20 | * A ringbuffer of N audio buffers. | ||
| 21 | * The buffer contains 3 sections: | ||
| 22 | * Appended - Buffers added to the ring, but have yet to be sent to the audio backend. | ||
| 23 | * Registered - Buffers sent to the backend and queued for playback. | ||
| 24 | * Released - Buffers which have been played, and can now be recycled. | ||
| 25 | * Any others are free/untracked. | ||
| 26 | * | ||
| 27 | * @tparam N - Maximum number of buffers in the ring. | ||
| 28 | */ | ||
| 29 | template <size_t N> | ||
| 30 | class AudioBuffers { | ||
| 31 | public: | ||
| 32 | explicit AudioBuffers(size_t limit) : append_limit{static_cast<u32>(limit)} {} | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Append a new audio buffer to the ring. | ||
| 36 | * | ||
| 37 | * @param buffer - The new buffer. | ||
| 38 | */ | ||
| 39 | void AppendBuffer(AudioBuffer& buffer) { | ||
| 40 | std::scoped_lock l{lock}; | ||
| 41 | buffers[appended_index] = buffer; | ||
| 42 | appended_count++; | ||
| 43 | appended_index = (appended_index + 1) % append_limit; | ||
| 44 | } | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Register waiting buffers, up to a maximum of BufferAppendLimit. | ||
| 48 | * | ||
| 49 | * @param out_buffers - The buffers which were registered. | ||
| 50 | */ | ||
| 51 | void RegisterBuffers(std::vector<AudioBuffer>& out_buffers) { | ||
| 52 | std::scoped_lock l{lock}; | ||
| 53 | const s32 to_register{std::min(std::min(appended_count, BufferAppendLimit), | ||
| 54 | BufferAppendLimit - registered_count)}; | ||
| 55 | |||
| 56 | for (s32 i = 0; i < to_register; i++) { | ||
| 57 | s32 index{appended_index - appended_count}; | ||
| 58 | if (index < 0) { | ||
| 59 | index += N; | ||
| 60 | } | ||
| 61 | out_buffers.push_back(buffers[index]); | ||
| 62 | registered_count++; | ||
| 63 | registered_index = (registered_index + 1) % append_limit; | ||
| 64 | |||
| 65 | appended_count--; | ||
| 66 | if (appended_count == 0) { | ||
| 67 | break; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Release a single buffer. Must be already registered. | ||
| 74 | * | ||
| 75 | * @param index - The buffer index to release. | ||
| 76 | * @param timestamp - The released timestamp for this buffer. | ||
| 77 | */ | ||
| 78 | void ReleaseBuffer(s32 index, s64 timestamp) { | ||
| 79 | std::scoped_lock l{lock}; | ||
| 80 | buffers[index].played_timestamp = timestamp; | ||
| 81 | |||
| 82 | registered_count--; | ||
| 83 | released_count++; | ||
| 84 | released_index = (released_index + 1) % append_limit; | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Release all registered buffers. | ||
| 89 | * | ||
| 90 | * @param timestamp - The released timestamp for this buffer. | ||
| 91 | * @return Is the buffer was released. | ||
| 92 | */ | ||
| 93 | bool ReleaseBuffers(Core::Timing::CoreTiming& core_timing, DeviceSession& session) { | ||
| 94 | std::scoped_lock l{lock}; | ||
| 95 | bool buffer_released{false}; | ||
| 96 | while (registered_count > 0) { | ||
| 97 | auto index{registered_index - registered_count}; | ||
| 98 | if (index < 0) { | ||
| 99 | index += N; | ||
| 100 | } | ||
| 101 | |||
| 102 | // Check with the backend if this buffer can be released yet. | ||
| 103 | if (!session.IsBufferConsumed(buffers[index].tag)) { | ||
| 104 | break; | ||
| 105 | } | ||
| 106 | |||
| 107 | ReleaseBuffer(index, core_timing.GetGlobalTimeNs().count()); | ||
| 108 | buffer_released = true; | ||
| 109 | } | ||
| 110 | |||
| 111 | return buffer_released || registered_count == 0; | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Get all released buffers. | ||
| 116 | * | ||
| 117 | * @param tags - Container to be filled with the released buffers' tags. | ||
| 118 | * @return The number of buffers released. | ||
| 119 | */ | ||
| 120 | u32 GetReleasedBuffers(std::span<u64> tags) { | ||
| 121 | std::scoped_lock l{lock}; | ||
| 122 | u32 released{0}; | ||
| 123 | |||
| 124 | while (released_count > 0) { | ||
| 125 | auto index{released_index - released_count}; | ||
| 126 | if (index < 0) { | ||
| 127 | index += N; | ||
| 128 | } | ||
| 129 | |||
| 130 | auto& buffer{buffers[index]}; | ||
| 131 | released_count--; | ||
| 132 | |||
| 133 | auto tag{buffer.tag}; | ||
| 134 | buffer.played_timestamp = 0; | ||
| 135 | buffer.samples = 0; | ||
| 136 | buffer.tag = 0; | ||
| 137 | buffer.size = 0; | ||
| 138 | |||
| 139 | if (tag == 0) { | ||
| 140 | break; | ||
| 141 | } | ||
| 142 | |||
| 143 | tags[released++] = tag; | ||
| 144 | |||
| 145 | if (released >= tags.size()) { | ||
| 146 | break; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | return released; | ||
| 151 | } | ||
| 152 | |||
| 153 | /** | ||
| 154 | * Get all appended and registered buffers. | ||
| 155 | * | ||
| 156 | * @param buffers_flushed - Output vector for the buffers which are released. | ||
| 157 | * @param max_buffers - Maximum number of buffers to released. | ||
| 158 | * @return The number of buffers released. | ||
| 159 | */ | ||
| 160 | u32 GetRegisteredAppendedBuffers(std::vector<AudioBuffer>& buffers_flushed, u32 max_buffers) { | ||
| 161 | std::scoped_lock l{lock}; | ||
| 162 | if (registered_count + appended_count == 0) { | ||
| 163 | return 0; | ||
| 164 | } | ||
| 165 | |||
| 166 | size_t buffers_to_flush{ | ||
| 167 | std::min(static_cast<u32>(registered_count + appended_count), max_buffers)}; | ||
| 168 | if (buffers_to_flush == 0) { | ||
| 169 | return 0; | ||
| 170 | } | ||
| 171 | |||
| 172 | while (registered_count > 0) { | ||
| 173 | auto index{registered_index - registered_count}; | ||
| 174 | if (index < 0) { | ||
| 175 | index += N; | ||
| 176 | } | ||
| 177 | |||
| 178 | buffers_flushed.push_back(buffers[index]); | ||
| 179 | |||
| 180 | registered_count--; | ||
| 181 | released_count++; | ||
| 182 | released_index = (released_index + 1) % append_limit; | ||
| 183 | |||
| 184 | if (buffers_flushed.size() >= buffers_to_flush) { | ||
| 185 | break; | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | while (appended_count > 0) { | ||
| 190 | auto index{appended_index - appended_count}; | ||
| 191 | if (index < 0) { | ||
| 192 | index += N; | ||
| 193 | } | ||
| 194 | |||
| 195 | buffers_flushed.push_back(buffers[index]); | ||
| 196 | |||
| 197 | appended_count--; | ||
| 198 | released_count++; | ||
| 199 | released_index = (released_index + 1) % append_limit; | ||
| 200 | |||
| 201 | if (buffers_flushed.size() >= buffers_to_flush) { | ||
| 202 | break; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | return static_cast<u32>(buffers_flushed.size()); | ||
| 207 | } | ||
| 208 | |||
| 209 | /** | ||
| 210 | * Check if the given tag is in the buffers. | ||
| 211 | * | ||
| 212 | * @param tag - Unique tag of the buffer to search for. | ||
| 213 | * @return True if the buffer is still in the ring, otherwise false. | ||
| 214 | */ | ||
| 215 | bool ContainsBuffer(const u64 tag) const { | ||
| 216 | std::scoped_lock l{lock}; | ||
| 217 | const auto registered_buffers{appended_count + registered_count + released_count}; | ||
| 218 | |||
| 219 | if (registered_buffers == 0) { | ||
| 220 | return false; | ||
| 221 | } | ||
| 222 | |||
| 223 | auto index{released_index - released_count}; | ||
| 224 | if (index < 0) { | ||
| 225 | index += append_limit; | ||
| 226 | } | ||
| 227 | |||
| 228 | for (s32 i = 0; i < registered_buffers; i++) { | ||
| 229 | if (buffers[index].tag == tag) { | ||
| 230 | return true; | ||
| 231 | } | ||
| 232 | index = (index + 1) % append_limit; | ||
| 233 | } | ||
| 234 | |||
| 235 | return false; | ||
| 236 | } | ||
| 237 | |||
| 238 | /** | ||
| 239 | * Get the number of active buffers in the ring. | ||
| 240 | * That is, appended, registered and released buffers. | ||
| 241 | * | ||
| 242 | * @return Number of active buffers. | ||
| 243 | */ | ||
| 244 | u32 GetAppendedRegisteredCount() const { | ||
| 245 | std::scoped_lock l{lock}; | ||
| 246 | return appended_count + registered_count; | ||
| 247 | } | ||
| 248 | |||
| 249 | /** | ||
| 250 | * Get the total number of active buffers in the ring. | ||
| 251 | * That is, appended, registered and released buffers. | ||
| 252 | * | ||
| 253 | * @return Number of active buffers. | ||
| 254 | */ | ||
| 255 | u32 GetTotalBufferCount() const { | ||
| 256 | std::scoped_lock l{lock}; | ||
| 257 | return static_cast<u32>(appended_count + registered_count + released_count); | ||
| 258 | } | ||
| 259 | |||
| 260 | /** | ||
| 261 | * Flush all of the currently appended and registered buffers | ||
| 262 | * | ||
| 263 | * @param buffers_released - Output count for the number of buffers released. | ||
| 264 | * @return True if buffers were successfully flushed, otherwise false. | ||
| 265 | */ | ||
| 266 | bool FlushBuffers(u32& buffers_released) { | ||
| 267 | std::scoped_lock l{lock}; | ||
| 268 | std::vector<AudioBuffer> buffers_flushed{}; | ||
| 269 | |||
| 270 | buffers_released = GetRegisteredAppendedBuffers(buffers_flushed, append_limit); | ||
| 271 | |||
| 272 | if (registered_count > 0) { | ||
| 273 | return false; | ||
| 274 | } | ||
| 275 | |||
| 276 | if (static_cast<u32>(released_count + appended_count) > append_limit) { | ||
| 277 | return false; | ||
| 278 | } | ||
| 279 | |||
| 280 | return true; | ||
| 281 | } | ||
| 282 | |||
| 283 | private: | ||
| 284 | /// Buffer lock | ||
| 285 | mutable std::recursive_mutex lock{}; | ||
| 286 | /// The audio buffers | ||
| 287 | std::array<AudioBuffer, N> buffers{}; | ||
| 288 | /// Current released index | ||
| 289 | s32 released_index{}; | ||
| 290 | /// Number of released buffers | ||
| 291 | s32 released_count{}; | ||
| 292 | /// Current registered index | ||
| 293 | s32 registered_index{}; | ||
| 294 | /// Number of registered buffers | ||
| 295 | s32 registered_count{}; | ||
| 296 | /// Current appended index | ||
| 297 | s32 appended_index{}; | ||
| 298 | /// Number of appended buffers | ||
| 299 | s32 appended_count{}; | ||
| 300 | /// Maximum number of buffers (default 32) | ||
| 301 | u32 append_limit{}; | ||
| 302 | }; | ||
| 303 | |||
| 304 | } // namespace AudioCore | ||
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp new file mode 100644 index 000000000..095fc96ce --- /dev/null +++ b/src/audio_core/device/device_session.cpp | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/audio_core.h" | ||
| 5 | #include "audio_core/audio_manager.h" | ||
| 6 | #include "audio_core/device/audio_buffer.h" | ||
| 7 | #include "audio_core/device/device_session.h" | ||
| 8 | #include "audio_core/sink/sink_stream.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "core/memory.h" | ||
| 11 | |||
| 12 | namespace AudioCore { | ||
| 13 | |||
| 14 | DeviceSession::DeviceSession(Core::System& system_) : system{system_} {} | ||
| 15 | |||
| 16 | DeviceSession::~DeviceSession() { | ||
| 17 | Finalize(); | ||
| 18 | } | ||
| 19 | |||
| 20 | Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_format_, | ||
| 21 | u16 channel_count_, size_t session_id_, u32 handle_, | ||
| 22 | u64 applet_resource_user_id_, Sink::StreamType type_) { | ||
| 23 | if (stream) { | ||
| 24 | Finalize(); | ||
| 25 | } | ||
| 26 | name = fmt::format("{}-{}", name_, session_id_); | ||
| 27 | type = type_; | ||
| 28 | sample_format = sample_format_; | ||
| 29 | channel_count = channel_count_; | ||
| 30 | session_id = session_id_; | ||
| 31 | handle = handle_; | ||
| 32 | applet_resource_user_id = applet_resource_user_id_; | ||
| 33 | |||
| 34 | if (type == Sink::StreamType::In) { | ||
| 35 | sink = &system.AudioCore().GetInputSink(); | ||
| 36 | } else { | ||
| 37 | sink = &system.AudioCore().GetOutputSink(); | ||
| 38 | } | ||
| 39 | stream = sink->AcquireSinkStream(system, channel_count, name, type); | ||
| 40 | initialized = true; | ||
| 41 | return ResultSuccess; | ||
| 42 | } | ||
| 43 | |||
| 44 | void DeviceSession::Finalize() { | ||
| 45 | if (initialized) { | ||
| 46 | Stop(); | ||
| 47 | sink->CloseStream(stream); | ||
| 48 | stream = nullptr; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | void DeviceSession::Start() { | ||
| 53 | stream->SetPlayedSampleCount(played_sample_count); | ||
| 54 | stream->Start(); | ||
| 55 | } | ||
| 56 | |||
| 57 | void DeviceSession::Stop() { | ||
| 58 | if (stream) { | ||
| 59 | played_sample_count = stream->GetPlayedSampleCount(); | ||
| 60 | stream->Stop(); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const { | ||
| 65 | auto& memory{system.Memory()}; | ||
| 66 | |||
| 67 | for (size_t i = 0; i < buffers.size(); i++) { | ||
| 68 | Sink::SinkBuffer new_buffer{ | ||
| 69 | .frames = buffers[i].size / (channel_count * sizeof(s16)), | ||
| 70 | .frames_played = 0, | ||
| 71 | .tag = buffers[i].tag, | ||
| 72 | .consumed = false, | ||
| 73 | }; | ||
| 74 | |||
| 75 | if (type == Sink::StreamType::In) { | ||
| 76 | std::vector<s16> samples{}; | ||
| 77 | stream->AppendBuffer(new_buffer, samples); | ||
| 78 | } else { | ||
| 79 | std::vector<s16> samples(buffers[i].size / sizeof(s16)); | ||
| 80 | memory.ReadBlockUnsafe(buffers[i].samples, samples.data(), buffers[i].size); | ||
| 81 | stream->AppendBuffer(new_buffer, samples); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | void DeviceSession::ReleaseBuffer(AudioBuffer& buffer) const { | ||
| 87 | if (type == Sink::StreamType::In) { | ||
| 88 | auto& memory{system.Memory()}; | ||
| 89 | auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))}; | ||
| 90 | memory.WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | bool DeviceSession::IsBufferConsumed(u64 tag) const { | ||
| 95 | if (stream) { | ||
| 96 | return stream->IsBufferConsumed(tag); | ||
| 97 | } | ||
| 98 | return true; | ||
| 99 | } | ||
| 100 | |||
| 101 | void DeviceSession::SetVolume(f32 volume) const { | ||
| 102 | if (stream) { | ||
| 103 | stream->SetSystemVolume(volume); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | u64 DeviceSession::GetPlayedSampleCount() const { | ||
| 108 | if (stream) { | ||
| 109 | return stream->GetPlayedSampleCount(); | ||
| 110 | } | ||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | |||
| 114 | } // namespace AudioCore | ||
diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h new file mode 100644 index 000000000..4a031b765 --- /dev/null +++ b/src/audio_core/device/device_session.h | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <span> | ||
| 7 | |||
| 8 | #include "audio_core/common/common.h" | ||
| 9 | #include "audio_core/sink/sink.h" | ||
| 10 | #include "core/hle/service/audio/errors.h" | ||
| 11 | |||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace AudioCore { | ||
| 17 | namespace Sink { | ||
| 18 | class SinkStream; | ||
| 19 | struct SinkBuffer; | ||
| 20 | } // namespace Sink | ||
| 21 | |||
| 22 | struct AudioBuffer; | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Represents an input or output device stream for audio in and audio out (not used for render). | ||
| 26 | **/ | ||
| 27 | class DeviceSession { | ||
| 28 | public: | ||
| 29 | explicit DeviceSession(Core::System& system); | ||
| 30 | ~DeviceSession(); | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Initialize this device session. | ||
| 34 | * | ||
| 35 | * @param name - Name of this device. | ||
| 36 | * @param sample_format - Sample format for this device's output. | ||
| 37 | * @param channel_count - Number of channels for this device (2 or 6). | ||
| 38 | * @param session_id - This session's id. | ||
| 39 | * @param handle - Handle for this device session (unused). | ||
| 40 | * @param applet_resource_user_id - Applet resource user id for this device session (unused). | ||
| 41 | * @param type - Type of this stream (Render, In, Out). | ||
| 42 | * @return Result code for this call. | ||
| 43 | */ | ||
| 44 | Result Initialize(std::string_view name, SampleFormat sample_format, u16 channel_count, | ||
| 45 | size_t session_id, u32 handle, u64 applet_resource_user_id, | ||
| 46 | Sink::StreamType type); | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Finalize this device session. | ||
| 50 | */ | ||
| 51 | void Finalize(); | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Append audio buffers to this device session to be played back. | ||
| 55 | * | ||
| 56 | * @param buffers - The buffers to play. | ||
| 57 | */ | ||
| 58 | void AppendBuffers(std::span<AudioBuffer> buffers) const; | ||
| 59 | |||
| 60 | /** | ||
| 61 | * (Audio In only) Pop samples from the backend, and write them back to this buffer's address. | ||
| 62 | * | ||
| 63 | * @param buffer - The buffer to write to. | ||
| 64 | */ | ||
| 65 | void ReleaseBuffer(AudioBuffer& buffer) const; | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Check if the buffer for the given tag has been consumed by the backend. | ||
| 69 | * | ||
| 70 | * @param tag - Unqiue tag of the buffer to check. | ||
| 71 | * @return true if the buffer has been consumed, otherwise false. | ||
| 72 | */ | ||
| 73 | bool IsBufferConsumed(u64 tag) const; | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Start this device session, starting the backend stream. | ||
| 77 | */ | ||
| 78 | void Start(); | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Stop this device session, stopping the backend stream. | ||
| 82 | */ | ||
| 83 | void Stop(); | ||
| 84 | |||
| 85 | /** | ||
| 86 | * Set this device session's volume. | ||
| 87 | * | ||
| 88 | * @param volume - New volume for this session. | ||
| 89 | */ | ||
| 90 | void SetVolume(f32 volume) const; | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Get this device session's total played sample count. | ||
| 94 | * | ||
| 95 | * @return Samples played by this session. | ||
| 96 | */ | ||
| 97 | u64 GetPlayedSampleCount() const; | ||
| 98 | |||
| 99 | private: | ||
| 100 | /// System | ||
| 101 | Core::System& system; | ||
| 102 | /// Output sink this device will use | ||
| 103 | Sink::Sink* sink{}; | ||
| 104 | /// The backend stream for this device session to send samples to | ||
| 105 | Sink::SinkStream* stream{}; | ||
| 106 | /// Name of this device session | ||
| 107 | std::string name{}; | ||
| 108 | /// Type of this device session (render/in/out) | ||
| 109 | Sink::StreamType type{}; | ||
| 110 | /// Sample format for this device. | ||
| 111 | SampleFormat sample_format{SampleFormat::PcmInt16}; | ||
| 112 | /// Channel count for this device session | ||
| 113 | u16 channel_count{}; | ||
| 114 | /// Session id of this device session | ||
| 115 | size_t session_id{}; | ||
| 116 | /// Handle of this device session | ||
| 117 | u32 handle{}; | ||
| 118 | /// Applet resource user id of this device session | ||
| 119 | u64 applet_resource_user_id{}; | ||
| 120 | /// Total number of samples played by this device session | ||
| 121 | u64 played_sample_count{}; | ||
| 122 | /// Is this session initialised? | ||
| 123 | bool initialized{}; | ||
| 124 | }; | ||
| 125 | |||
| 126 | } // namespace AudioCore | ||