summaryrefslogtreecommitdiff
path: root/src/audio_core/stream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core/stream.cpp')
-rw-r--r--src/audio_core/stream.cpp175
1 files changed, 0 insertions, 175 deletions
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
deleted file mode 100644
index cf3d94c53..000000000
--- a/src/audio_core/stream.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <cmath>
6
7#include "audio_core/sink.h"
8#include "audio_core/sink_details.h"
9#include "audio_core/sink_stream.h"
10#include "audio_core/stream.h"
11#include "common/assert.h"
12#include "common/logging/log.h"
13#include "common/settings.h"
14#include "core/core_timing.h"
15
16namespace AudioCore {
17
18constexpr std::size_t MaxAudioBufferCount{32};
19
20u32 Stream::GetNumChannels() const {
21 switch (format) {
22 case Format::Mono16:
23 return 1;
24 case Format::Stereo16:
25 return 2;
26 case Format::Multi51Channel16:
27 return 6;
28 }
29 UNIMPLEMENTED_MSG("Unimplemented format={}", static_cast<u32>(format));
30 return {};
31}
32
33Stream::Stream(Core::Timing::CoreTiming& core_timing_, u32 sample_rate_, Format format_,
34 ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_)
35 : sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)},
36 sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} {
37 release_event = Core::Timing::CreateEvent(
38 name, [this](std::uintptr_t, s64 time, std::chrono::nanoseconds ns_late) {
39 ReleaseActiveBuffer(ns_late);
40 return std::nullopt;
41 });
42}
43
44void Stream::Play() {
45 state = State::Playing;
46 PlayNextBuffer();
47}
48
49void Stream::Stop() {
50 state = State::Stopped;
51 UNIMPLEMENTED();
52}
53
54bool Stream::Flush() {
55 const bool had_buffers = !queued_buffers.empty();
56 while (!queued_buffers.empty()) {
57 queued_buffers.pop();
58 }
59 return had_buffers;
60}
61
62void Stream::SetVolume(float volume) {
63 game_volume = volume;
64}
65
66Stream::State Stream::GetState() const {
67 return state;
68}
69
70std::chrono::nanoseconds Stream::GetBufferReleaseNS(const Buffer& buffer) const {
71 const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
72 return std::chrono::nanoseconds((static_cast<u64>(num_samples) * 1000000000ULL) / sample_rate);
73}
74
75static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
76 const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)};
77
78 if (volume == 1.0f) {
79 return;
80 }
81
82 // Perceived volume is not the same as the volume level
83 const float volume_scale_factor = (0.85f * ((volume * volume) - volume)) + volume;
84 for (auto& sample : samples) {
85 sample = static_cast<s16>(sample * volume_scale_factor);
86 }
87}
88
89void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
90 if (!IsPlaying()) {
91 // Ensure we are in playing state before playing the next buffer
92 sink_stream.Flush();
93 return;
94 }
95
96 if (active_buffer) {
97 // Do not queue a new buffer if we are already playing a buffer
98 return;
99 }
100
101 if (queued_buffers.empty()) {
102 // No queued buffers - we are effectively paused
103 sink_stream.Flush();
104 return;
105 }
106
107 active_buffer = queued_buffers.front();
108 queued_buffers.pop();
109
110 auto& samples = active_buffer->GetSamples();
111
112 VolumeAdjustSamples(samples, game_volume);
113
114 sink_stream.EnqueueSamples(GetNumChannels(), samples);
115 played_samples += samples.size();
116
117 const auto buffer_release_ns = GetBufferReleaseNS(*active_buffer);
118
119 // If ns_late is higher than the update rate ignore the delay
120 if (ns_late > buffer_release_ns) {
121 ns_late = {};
122 }
123
124 core_timing.ScheduleEvent(buffer_release_ns - ns_late, release_event, {});
125}
126
127void Stream::ReleaseActiveBuffer(std::chrono::nanoseconds ns_late) {
128 ASSERT(active_buffer);
129 released_buffers.push(std::move(active_buffer));
130 release_callback();
131 PlayNextBuffer(ns_late);
132}
133
134bool Stream::QueueBuffer(BufferPtr&& buffer) {
135 if (queued_buffers.size() < MaxAudioBufferCount) {
136 queued_buffers.push(std::move(buffer));
137 PlayNextBuffer();
138 return true;
139 }
140 return false;
141}
142
143bool Stream::ContainsBuffer([[maybe_unused]] Buffer::Tag tag) const {
144 UNIMPLEMENTED();
145 return {};
146}
147
148std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
149 std::vector<Buffer::Tag> tags;
150 for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
151 if (released_buffers.front()) {
152 tags.push_back(released_buffers.front()->GetTag());
153 } else {
154 ASSERT_MSG(false, "Invalid tag in released_buffers!");
155 }
156 released_buffers.pop();
157 }
158 return tags;
159}
160
161std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {
162 std::vector<Buffer::Tag> tags;
163 tags.reserve(released_buffers.size());
164 while (!released_buffers.empty()) {
165 if (released_buffers.front()) {
166 tags.push_back(released_buffers.front()->GetTag());
167 } else {
168 ASSERT_MSG(false, "Invalid tag in released_buffers!");
169 }
170 released_buffers.pop();
171 }
172 return tags;
173}
174
175} // namespace AudioCore