summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt12
-rw-r--r--src/audio_core/audio_out.cpp17
-rw-r--r--src/audio_core/audio_out.h8
-rw-r--r--src/audio_core/buffer.h3
-rw-r--r--src/audio_core/cubeb_sink.cpp190
-rw-r--r--src/audio_core/cubeb_sink.h31
-rw-r--r--src/audio_core/null_sink.h27
-rw-r--r--src/audio_core/sink.h29
-rw-r--r--src/audio_core/sink_details.cpp44
-rw-r--r--src/audio_core/sink_details.h32
-rw-r--r--src/audio_core/sink_stream.h32
-rw-r--r--src/audio_core/stream.cpp35
-rw-r--r--src/audio_core/stream.h22
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/core.h7
-rw-r--r--src/core/hle/service/audio/audout_u.cpp8
-rw-r--r--src/core/hle/service/audio/audout_u.h2
17 files changed, 462 insertions, 39 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index f00a55994..81121167d 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -2,10 +2,22 @@ add_library(audio_core STATIC
2 audio_out.cpp 2 audio_out.cpp
3 audio_out.h 3 audio_out.h
4 buffer.h 4 buffer.h
5 cubeb_sink.cpp
6 cubeb_sink.h
7 null_sink.h
5 stream.cpp 8 stream.cpp
6 stream.h 9 stream.h
10 sink.h
11 sink_details.cpp
12 sink_details.h
13 sink_stream.h
7) 14)
8 15
9create_target_directory_groups(audio_core) 16create_target_directory_groups(audio_core)
10 17
11target_link_libraries(audio_core PUBLIC common core) 18target_link_libraries(audio_core PUBLIC common core)
19
20if(ENABLE_CUBEB)
21 target_link_libraries(audio_core PRIVATE cubeb)
22 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
23endif()
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index 6d418a05b..77cedb6ba 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -3,13 +3,15 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "audio_core/audio_out.h" 5#include "audio_core/audio_out.h"
6#include "audio_core/sink.h"
7#include "audio_core/sink_details.h"
6#include "common/assert.h" 8#include "common/assert.h"
7#include "common/logging/log.h" 9#include "common/logging/log.h"
8 10
9namespace AudioCore { 11namespace AudioCore {
10 12
11/// Returns the stream format from the specified number of channels 13/// Returns the stream format from the specified number of channels
12static Stream::Format ChannelsToStreamFormat(int num_channels) { 14static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
13 switch (num_channels) { 15 switch (num_channels) {
14 case 1: 16 case 1:
15 return Stream::Format::Mono16; 17 return Stream::Format::Mono16;
@@ -24,11 +26,16 @@ static Stream::Format ChannelsToStreamFormat(int num_channels) {
24 return {}; 26 return {};
25} 27}
26 28
27StreamPtr AudioOut::OpenStream(int sample_rate, int num_channels, 29StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels,
28 Stream::ReleaseCallback&& release_callback) { 30 Stream::ReleaseCallback&& release_callback) {
29 streams.push_back(std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels), 31 if (!sink) {
30 std::move(release_callback))); 32 const SinkDetails& sink_details = GetSinkDetails("auto");
31 return streams.back(); 33 sink = sink_details.factory("");
34 }
35
36 return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
37 std::move(release_callback),
38 sink->AcquireSinkStream(sample_rate, num_channels));
32} 39}
33 40
34std::vector<u64> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) { 41std::vector<u64> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
index a86499d10..8d9b695d4 100644
--- a/src/audio_core/audio_out.h
+++ b/src/audio_core/audio_out.h
@@ -8,20 +8,19 @@
8#include <vector> 8#include <vector>
9 9
10#include "audio_core/buffer.h" 10#include "audio_core/buffer.h"
11#include "audio_core/sink.h"
11#include "audio_core/stream.h" 12#include "audio_core/stream.h"
12#include "common/common_types.h" 13#include "common/common_types.h"
13 14
14namespace AudioCore { 15namespace AudioCore {
15 16
16using StreamPtr = std::shared_ptr<Stream>;
17
18/** 17/**
19 * Represents an audio playback interface, used to open and play audio streams 18 * Represents an audio playback interface, used to open and play audio streams
20 */ 19 */
21class AudioOut { 20class AudioOut {
22public: 21public:
23 /// Opens a new audio stream 22 /// Opens a new audio stream
24 StreamPtr OpenStream(int sample_rate, int num_channels, 23 StreamPtr OpenStream(u32 sample_rate, u32 num_channels,
25 Stream::ReleaseCallback&& release_callback); 24 Stream::ReleaseCallback&& release_callback);
26 25
27 /// Returns a vector of recently released buffers specified by tag for the specified stream 26 /// Returns a vector of recently released buffers specified by tag for the specified stream
@@ -37,8 +36,7 @@ public:
37 bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data); 36 bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
38 37
39private: 38private:
40 /// Active audio streams on the interface 39 SinkPtr sink;
41 std::vector<StreamPtr> streams;
42}; 40};
43 41
44} // namespace AudioCore 42} // namespace AudioCore
diff --git a/src/audio_core/buffer.h b/src/audio_core/buffer.h
index 874ec787e..4bf5fd58a 100644
--- a/src/audio_core/buffer.h
+++ b/src/audio_core/buffer.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
7#include <vector> 8#include <vector>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -34,4 +35,6 @@ private:
34 std::vector<u8> data; 35 std::vector<u8> data;
35}; 36};
36 37
38using BufferPtr = std::shared_ptr<Buffer>;
39
37} // namespace AudioCore 40} // namespace AudioCore
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
new file mode 100644
index 000000000..34ae5b062
--- /dev/null
+++ b/src/audio_core/cubeb_sink.cpp
@@ -0,0 +1,190 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstring>
7
8#include "audio_core/cubeb_sink.h"
9#include "audio_core/stream.h"
10#include "common/logging/log.h"
11
12namespace AudioCore {
13
14class SinkStreamImpl final : public SinkStream {
15public:
16 SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} {
17 cubeb_stream_params params;
18 params.rate = 48000;
19 params.channels = GetNumChannels();
20 params.format = CUBEB_SAMPLE_S16NE;
21 params.layout = CUBEB_LAYOUT_STEREO;
22
23 u32 minimum_latency = 0;
24 if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
25 LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
26 }
27
28 if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr,
29 output_device, &params, std::max(512u, minimum_latency),
30 &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
31 this) != CUBEB_OK) {
32 LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
33 return;
34 }
35
36 if (cubeb_stream_start(stream_backend) != CUBEB_OK) {
37 LOG_CRITICAL(Audio_Sink, "Error starting cubeb stream");
38 return;
39 }
40 }
41
42 ~SinkStreamImpl() {
43 if (!ctx) {
44 return;
45 }
46
47 if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
48 LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
49 }
50
51 cubeb_stream_destroy(stream_backend);
52 }
53
54 void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override {
55 if (!ctx) {
56 return;
57 }
58
59 queue.reserve(queue.size() + sample_count * GetNumChannels());
60
61 if (num_channels == 2) {
62 // Copy as-is
63 std::copy(samples, samples + sample_count * GetNumChannels(),
64 std::back_inserter(queue));
65 } else if (num_channels == 6) {
66 // Downsample 6 channels to 2
67 const size_t sample_count_copy_size = sample_count * num_channels * 2;
68 queue.reserve(sample_count_copy_size);
69 for (size_t i = 0; i < sample_count * num_channels; i += num_channels) {
70 queue.push_back(samples[i]);
71 queue.push_back(samples[i + 1]);
72 }
73 } else {
74 ASSERT_MSG(false, "Unimplemented");
75 }
76 }
77
78 u32 GetNumChannels() const {
79 // Only support 2-channel stereo output for now
80 return 2;
81 }
82
83private:
84 std::vector<std::string> device_list;
85
86 cubeb* ctx{};
87 cubeb_stream* stream_backend{};
88
89 std::vector<s16> queue;
90
91 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
92 void* output_buffer, long num_frames);
93 static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state);
94};
95
96CubebSink::CubebSink(std::string target_device_name) {
97 if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) {
98 LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
99 return;
100 }
101
102 if (target_device_name != auto_device_name && !target_device_name.empty()) {
103 cubeb_device_collection collection;
104 if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
105 LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
106 } else {
107 const auto collection_end{collection.device + collection.count};
108 const auto device{std::find_if(collection.device, collection_end,
109 [&](const cubeb_device_info& device) {
110 return target_device_name == device.friendly_name;
111 })};
112 if (device != collection_end) {
113 output_device = device->devid;
114 }
115 cubeb_device_collection_destroy(ctx, &collection);
116 }
117 }
118}
119
120CubebSink::~CubebSink() {
121 if (!ctx) {
122 return;
123 }
124
125 for (auto& sink_stream : sink_streams) {
126 sink_stream.reset();
127 }
128
129 cubeb_destroy(ctx);
130}
131
132SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) {
133 sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device));
134 return *sink_streams.back();
135}
136
137long SinkStreamImpl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
138 void* output_buffer, long num_frames) {
139 SinkStreamImpl* impl = static_cast<SinkStreamImpl*>(user_data);
140 u8* buffer = reinterpret_cast<u8*>(output_buffer);
141
142 if (!impl) {
143 return {};
144 }
145
146 const size_t frames_to_write{
147 std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))};
148
149 memcpy(buffer, impl->queue.data(), frames_to_write * sizeof(s16) * impl->GetNumChannels());
150 impl->queue.erase(impl->queue.begin(),
151 impl->queue.begin() + frames_to_write * impl->GetNumChannels());
152
153 if (frames_to_write < num_frames) {
154 // Fill the rest of the frames with silence
155 memset(buffer + frames_to_write * sizeof(s16) * impl->GetNumChannels(), 0,
156 (num_frames - frames_to_write) * sizeof(s16) * impl->GetNumChannels());
157 }
158
159 return num_frames;
160}
161
162void SinkStreamImpl::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
163
164std::vector<std::string> ListCubebSinkDevices() {
165 std::vector<std::string> device_list;
166 cubeb* ctx;
167
168 if (cubeb_init(&ctx, "Citra Device Enumerator", nullptr) != CUBEB_OK) {
169 LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
170 return {};
171 }
172
173 cubeb_device_collection collection;
174 if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
175 LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
176 } else {
177 for (size_t i = 0; i < collection.count; i++) {
178 const cubeb_device_info& device = collection.device[i];
179 if (device.friendly_name) {
180 device_list.emplace_back(device.friendly_name);
181 }
182 }
183 cubeb_device_collection_destroy(ctx, &collection);
184 }
185
186 cubeb_destroy(ctx);
187 return device_list;
188}
189
190} // namespace AudioCore
diff --git a/src/audio_core/cubeb_sink.h b/src/audio_core/cubeb_sink.h
new file mode 100644
index 000000000..d07113f1f
--- /dev/null
+++ b/src/audio_core/cubeb_sink.h
@@ -0,0 +1,31 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <vector>
9
10#include <cubeb/cubeb.h>
11
12#include "audio_core/sink.h"
13
14namespace AudioCore {
15
16class CubebSink final : public Sink {
17public:
18 explicit CubebSink(std::string device_id);
19 ~CubebSink() override;
20
21 SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override;
22
23private:
24 cubeb* ctx{};
25 cubeb_devid output_device{};
26 std::vector<SinkStreamPtr> sink_streams;
27};
28
29std::vector<std::string> ListCubebSinkDevices();
30
31} // namespace AudioCore
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h
new file mode 100644
index 000000000..2e04438f7
--- /dev/null
+++ b/src/audio_core/null_sink.h
@@ -0,0 +1,27 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "audio_core/sink.h"
8
9namespace AudioCore {
10
11class NullSink final : public Sink {
12public:
13 explicit NullSink(std::string){};
14 ~NullSink() override = default;
15
16 SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override {
17 return null_sink_stream;
18 }
19
20private:
21 struct NullSinkStreamImpl final : SinkStream {
22 void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/,
23 size_t /*sample_count*/) override {}
24 } null_sink_stream;
25};
26
27} // namespace AudioCore
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
new file mode 100644
index 000000000..d1bb98c3d
--- /dev/null
+++ b/src/audio_core/sink.h
@@ -0,0 +1,29 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include "audio_core/sink_stream.h"
10#include "common/common_types.h"
11
12namespace AudioCore {
13
14constexpr char auto_device_name[] = "auto";
15
16/**
17 * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed
18 * PCM16 format to be output. Sinks *do not* handle resampling and expect the correct sample rate.
19 * They are dumb outputs.
20 */
21class Sink {
22public:
23 virtual ~Sink() = default;
24 virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0;
25};
26
27using SinkPtr = std::unique_ptr<Sink>;
28
29} // namespace AudioCore
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp
new file mode 100644
index 000000000..955ba20fb
--- /dev/null
+++ b/src/audio_core/sink_details.cpp
@@ -0,0 +1,44 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7#include <string>
8#include <vector>
9#include "audio_core/null_sink.h"
10#include "audio_core/sink_details.h"
11#ifdef HAVE_CUBEB
12#include "audio_core/cubeb_sink.h"
13#endif
14#include "common/logging/log.h"
15
16namespace AudioCore {
17
18// g_sink_details is ordered in terms of desirability, with the best choice at the top.
19const std::vector<SinkDetails> g_sink_details = {
20#ifdef HAVE_CUBEB
21 SinkDetails{"cubeb", &std::make_unique<CubebSink, std::string>, &ListCubebSinkDevices},
22#endif
23 SinkDetails{"null", &std::make_unique<NullSink, std::string>,
24 [] { return std::vector<std::string>{"null"}; }},
25};
26
27const SinkDetails& GetSinkDetails(std::string sink_id) {
28 auto iter =
29 std::find_if(g_sink_details.begin(), g_sink_details.end(),
30 [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
31
32 if (sink_id == "auto" || iter == g_sink_details.end()) {
33 if (sink_id != "auto") {
34 LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id {}", sink_id);
35 }
36 // Auto-select.
37 // g_sink_details is ordered in terms of desirability, with the best choice at the front.
38 iter = g_sink_details.begin();
39 }
40
41 return *iter;
42}
43
44} // namespace AudioCore
diff --git a/src/audio_core/sink_details.h b/src/audio_core/sink_details.h
new file mode 100644
index 000000000..aa8aae1a9
--- /dev/null
+++ b/src/audio_core/sink_details.h
@@ -0,0 +1,32 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9#include <vector>
10
11namespace AudioCore {
12
13class Sink;
14
15struct SinkDetails {
16 SinkDetails(const char* id_, std::function<std::unique_ptr<Sink>(std::string)> factory_,
17 std::function<std::vector<std::string>()> list_devices_)
18 : id(id_), factory(factory_), list_devices(list_devices_) {}
19
20 /// Name for this sink.
21 const char* id;
22 /// A method to call to construct an instance of this type of sink.
23 std::function<std::unique_ptr<Sink>(std::string device_id)> factory;
24 /// A method to call to list available devices.
25 std::function<std::vector<std::string>()> list_devices;
26};
27
28extern const std::vector<SinkDetails> g_sink_details;
29
30const SinkDetails& GetSinkDetails(std::string sink_id);
31
32} // namespace AudioCore
diff --git a/src/audio_core/sink_stream.h b/src/audio_core/sink_stream.h
new file mode 100644
index 000000000..e7a3f01b0
--- /dev/null
+++ b/src/audio_core/sink_stream.h
@@ -0,0 +1,32 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include "common/common_types.h"
10
11namespace AudioCore {
12
13/**
14 * Accepts samples in stereo signed PCM16 format to be output. Sinks *do not* handle resampling and
15 * expect the correct sample rate. They are dumb outputs.
16 */
17class SinkStream {
18public:
19 virtual ~SinkStream() = default;
20
21 /**
22 * Feed stereo samples to sink.
23 * @param num_channels Number of channels used.
24 * @param samples Samples in interleaved stereo PCM16 format.
25 * @param sample_count Number of samples.
26 */
27 virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0;
28};
29
30using SinkStreamPtr = std::unique_ptr<SinkStream>;
31
32} // namespace AudioCore
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 82bff4b9e..689f51a1d 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -7,30 +7,37 @@
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/core_timing_util.h" 8#include "core/core_timing_util.h"
9 9
10#include "audio_core/sink.h"
11#include "audio_core/sink_details.h"
10#include "audio_core/stream.h" 12#include "audio_core/stream.h"
11 13
12namespace AudioCore { 14namespace AudioCore {
13 15
14constexpr size_t MaxAudioBufferCount{32}; 16constexpr size_t MaxAudioBufferCount{32};
15 17
16/// Returns the sample size for the specified audio stream format 18u32 Stream::GetNumChannels() const {
17static size_t SampleSizeFromFormat(Stream::Format format) {
18 switch (format) { 19 switch (format) {
19 case Stream::Format::Mono16: 20 case Format::Mono16:
21 return 1;
22 case Format::Stereo16:
20 return 2; 23 return 2;
21 case Stream::Format::Stereo16: 24 case Format::Multi51Channel16:
22 return 4; 25 return 6;
23 case Stream::Format::Multi51Channel16: 26 }
24 return 12;
25 };
26
27 LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format)); 27 LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
28 UNREACHABLE(); 28 UNREACHABLE();
29 return {}; 29 return {};
30} 30}
31 31
32Stream::Stream(int sample_rate, Format format, ReleaseCallback&& release_callback) 32u32 Stream::GetSampleSize() const {
33 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)} { 33 return GetNumChannels() * 2;
34}
35
36Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
37 SinkStream& sink_stream)
38 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
39 sink_stream{sink_stream} {
40
34 release_event = CoreTiming::RegisterEvent( 41 release_event = CoreTiming::RegisterEvent(
35 "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); }); 42 "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
36} 43}
@@ -45,7 +52,7 @@ void Stream::Stop() {
45} 52}
46 53
47s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { 54s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
48 const size_t num_samples{buffer.GetData().size() / SampleSizeFromFormat(format)}; 55 const size_t num_samples{buffer.GetData().size() / GetSampleSize()};
49 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); 56 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
50} 57}
51 58
@@ -68,6 +75,10 @@ void Stream::PlayNextBuffer() {
68 active_buffer = queued_buffers.front(); 75 active_buffer = queued_buffers.front();
69 queued_buffers.pop(); 76 queued_buffers.pop();
70 77
78 sink_stream.EnqueueSamples(GetNumChannels(),
79 reinterpret_cast<const s16*>(active_buffer->GetData().data()),
80 active_buffer->GetData().size() / GetSampleSize());
81
71 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); 82 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
72} 83}
73 84
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 5f43b0798..35253920e 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -10,14 +10,13 @@
10#include <queue> 10#include <queue>
11 11
12#include "audio_core/buffer.h" 12#include "audio_core/buffer.h"
13#include "audio_core/sink_stream.h"
13#include "common/assert.h" 14#include "common/assert.h"
14#include "common/common_types.h" 15#include "common/common_types.h"
15#include "core/core_timing.h" 16#include "core/core_timing.h"
16 17
17namespace AudioCore { 18namespace AudioCore {
18 19
19using BufferPtr = std::shared_ptr<Buffer>;
20
21/** 20/**
22 * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut 21 * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
23 */ 22 */
@@ -33,7 +32,8 @@ public:
33 /// Callback function type, used to change guest state on a buffer being released 32 /// Callback function type, used to change guest state on a buffer being released
34 using ReleaseCallback = std::function<void()>; 33 using ReleaseCallback = std::function<void()>;
35 34
36 Stream(int sample_rate, Format format, ReleaseCallback&& release_callback); 35 Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
36 SinkStream& sink_stream);
37 37
38 /// Plays the audio stream 38 /// Plays the audio stream
39 void Play(); 39 void Play();
@@ -60,6 +60,17 @@ public:
60 return queued_buffers.size(); 60 return queued_buffers.size();
61 } 61 }
62 62
63 /// Gets the sample rate
64 u32 GetSampleRate() const {
65 return sample_rate;
66 }
67
68 /// Gets the number of channels
69 u32 GetNumChannels() const;
70
71 /// Gets the sample size in bytes
72 u32 GetSampleSize() const;
73
63private: 74private:
64 /// Current state of the stream 75 /// Current state of the stream
65 enum class State { 76 enum class State {
@@ -76,7 +87,7 @@ private:
76 /// Gets the number of core cycles when the specified buffer will be released 87 /// Gets the number of core cycles when the specified buffer will be released
77 s64 GetBufferReleaseCycles(const Buffer& buffer) const; 88 s64 GetBufferReleaseCycles(const Buffer& buffer) const;
78 89
79 int sample_rate; ///< Sample rate of the stream 90 u32 sample_rate; ///< Sample rate of the stream
80 Format format; ///< Format of the stream 91 Format format; ///< Format of the stream
81 ReleaseCallback release_callback; ///< Buffer release callback for the stream 92 ReleaseCallback release_callback; ///< Buffer release callback for the stream
82 State state{State::Stopped}; ///< Playback state of the stream 93 State state{State::Stopped}; ///< Playback state of the stream
@@ -84,6 +95,9 @@ private:
84 BufferPtr active_buffer; ///< Actively playing buffer in the stream 95 BufferPtr active_buffer; ///< Actively playing buffer in the stream
85 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream 96 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
86 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream 97 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
98 SinkStream& sink_stream; ///< Output sink for the stream
87}; 99};
88 100
101using StreamPtr = std::shared_ptr<Stream>;
102
89} // namespace AudioCore 103} // namespace AudioCore
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 186fa46df..b7f4b4532 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -177,7 +177,6 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
177 } 177 }
178 178
179 gpu_core = std::make_unique<Tegra::GPU>(); 179 gpu_core = std::make_unique<Tegra::GPU>();
180 audio_core = std::make_unique<AudioCore::AudioOut>();
181 telemetry_session = std::make_unique<Core::TelemetrySession>(); 180 telemetry_session = std::make_unique<Core::TelemetrySession>();
182 service_manager = std::make_shared<Service::SM::ServiceManager>(); 181 service_manager = std::make_shared<Service::SM::ServiceManager>();
183 182
@@ -229,7 +228,6 @@ void System::Shutdown() {
229 service_manager.reset(); 228 service_manager.reset();
230 telemetry_session.reset(); 229 telemetry_session.reset();
231 gpu_core.reset(); 230 gpu_core.reset();
232 audio_core.reset();
233 231
234 // Close all CPU/threading state 232 // Close all CPU/threading state
235 cpu_barrier->NotifyEnd(); 233 cpu_barrier->NotifyEnd();
diff --git a/src/core/core.h b/src/core/core.h
index 6f4df775f..c123fe401 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -8,7 +8,6 @@
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include <thread> 10#include <thread>
11#include "audio_core/audio_out.h"
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "core/arm/exclusive_monitor.h" 12#include "core/arm/exclusive_monitor.h"
14#include "core/core_cpu.h" 13#include "core/core_cpu.h"
@@ -132,11 +131,6 @@ public:
132 return *gpu_core; 131 return *gpu_core;
133 } 132 }
134 133
135 /// Gets the AudioCore interface
136 AudioCore::AudioOut& AudioCore() {
137 return *audio_core;
138 }
139
140 /// Gets the scheduler for the CPU core that is currently running 134 /// Gets the scheduler for the CPU core that is currently running
141 Kernel::Scheduler& CurrentScheduler() { 135 Kernel::Scheduler& CurrentScheduler() {
142 return *CurrentCpuCore().Scheduler(); 136 return *CurrentCpuCore().Scheduler();
@@ -201,7 +195,6 @@ private:
201 /// AppLoader used to load the current executing application 195 /// AppLoader used to load the current executing application
202 std::unique_ptr<Loader::AppLoader> app_loader; 196 std::unique_ptr<Loader::AppLoader> app_loader;
203 std::unique_ptr<Tegra::GPU> gpu_core; 197 std::unique_ptr<Tegra::GPU> gpu_core;
204 std::unique_ptr<AudioCore::AudioOut> audio_core;
205 std::shared_ptr<Tegra::DebugContext> debug_context; 198 std::shared_ptr<Tegra::DebugContext> debug_context;
206 Kernel::SharedPtr<Kernel::Process> current_process; 199 Kernel::SharedPtr<Kernel::Process> current_process;
207 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 200 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index a15d53ff8..ab37c2a69 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -25,9 +25,8 @@ constexpr int DefaultSampleRate{48000};
25 25
26class IAudioOut final : public ServiceFramework<IAudioOut> { 26class IAudioOut final : public ServiceFramework<IAudioOut> {
27public: 27public:
28 IAudioOut(AudoutParams audio_params) 28 IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core)
29 : ServiceFramework("IAudioOut"), audio_params(audio_params), 29 : ServiceFramework("IAudioOut"), audio_params(audio_params), audio_core(audio_core) {
30 audio_core(Core::System::GetInstance().AudioCore()) {
31 30
32 static const FunctionInfo functions[] = { 31 static const FunctionInfo functions[] = {
33 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 32 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
@@ -195,7 +194,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
195 // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl 194 // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl
196 // will likely need to be updated as well. 195 // will likely need to be updated as well.
197 ASSERT_MSG(!audio_out_interface, "Unimplemented"); 196 ASSERT_MSG(!audio_out_interface, "Unimplemented");
198 audio_out_interface = std::make_shared<IAudioOut>(std::move(params)); 197 audio_out_interface = std::make_shared<IAudioOut>(std::move(params), *audio_core);
199 198
200 IPC::ResponseBuilder rb{ctx, 6, 0, 1}; 199 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
201 rb.Push(RESULT_SUCCESS); 200 rb.Push(RESULT_SUCCESS);
@@ -212,6 +211,7 @@ AudOutU::AudOutU() : ServiceFramework("audout:u") {
212 {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, 211 {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
213 {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}}; 212 {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}};
214 RegisterHandlers(functions); 213 RegisterHandlers(functions);
214 audio_core = std::make_unique<AudioCore::AudioOut>();
215} 215}
216 216
217} // namespace Service::Audio 217} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index bc43f1f44..e5c2184d5 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "audio_core/audio_out.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Kernel { 10namespace Kernel {
@@ -33,6 +34,7 @@ public:
33 34
34private: 35private:
35 std::shared_ptr<IAudioOut> audio_out_interface; 36 std::shared_ptr<IAudioOut> audio_out_interface;
37 std::unique_ptr<AudioCore::AudioOut> audio_core;
36 38
37 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); 39 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
38 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); 40 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);