summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Fernando S2023-12-19 16:15:07 +0100
committerGravatar GitHub2023-12-19 16:15:07 +0100
commitd61df0f4002274b23c0c7139449906ca392a6a3d (patch)
tree18195cd952b7d988570b3586ec1d02feed4a079f /src
parentMerge pull request #12392 from liamwhite/mode (diff)
parentoboe_sink: specify additional required parameters (diff)
downloadyuzu-d61df0f4002274b23c0c7139449906ca392a6a3d.tar.gz
yuzu-d61df0f4002274b23c0c7139449906ca392a6a3d.tar.xz
yuzu-d61df0f4002274b23c0c7139449906ca392a6a3d.zip
Merge pull request #12387 from liamwhite/oboe
android: add oboe audio sink
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/res/values/arrays.xml2
-rw-r--r--src/android/app/src/main/res/values/strings.xml1
-rw-r--r--src/audio_core/CMakeLists.txt11
-rw-r--r--src/audio_core/sink/oboe_sink.cpp223
-rw-r--r--src/audio_core/sink/oboe_sink.h75
-rw-r--r--src/audio_core/sink/sink_details.cpp13
-rw-r--r--src/common/settings_enums.h7
7 files changed, 328 insertions, 4 deletions
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index ab435dce9..e3915ef4f 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -256,11 +256,13 @@
256 256
257 <string-array name="outputEngineEntries"> 257 <string-array name="outputEngineEntries">
258 <item>@string/auto</item> 258 <item>@string/auto</item>
259 <item>@string/oboe</item>
259 <item>@string/cubeb</item> 260 <item>@string/cubeb</item>
260 <item>@string/string_null</item> 261 <item>@string/string_null</item>
261 </string-array> 262 </string-array>
262 <integer-array name="outputEngineValues"> 263 <integer-array name="outputEngineValues">
263 <item>0</item> 264 <item>0</item>
265 <item>4</item>
264 <item>1</item> 266 <item>1</item>
265 <item>3</item> 267 <item>3</item>
266 </integer-array> 268 </integer-array>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index c86c43df2..0b80b04a4 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -503,6 +503,7 @@
503 <string name="theme_mode_dark">Dark</string> 503 <string name="theme_mode_dark">Dark</string>
504 504
505 <!-- Audio output engines --> 505 <!-- Audio output engines -->
506 <string name="oboe">oboe</string>
506 <string name="cubeb">cubeb</string> 507 <string name="cubeb">cubeb</string>
507 508
508 <!-- Black backgrounds theme --> 509 <!-- Black backgrounds theme -->
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 400988c5f..e982d03be 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -253,6 +253,17 @@ if (ENABLE_SDL2)
253 target_compile_definitions(audio_core PRIVATE HAVE_SDL2) 253 target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
254endif() 254endif()
255 255
256if (ANDROID)
257 target_sources(audio_core PRIVATE
258 sink/oboe_sink.cpp
259 sink/oboe_sink.h
260 )
261
262 # FIXME: this port seems broken, it cannot be imported with find_package(oboe REQUIRED)
263 target_link_libraries(audio_core PRIVATE "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/liboboe.a")
264 target_compile_definitions(audio_core PRIVATE HAVE_OBOE)
265endif()
266
256if (YUZU_USE_PRECOMPILED_HEADERS) 267if (YUZU_USE_PRECOMPILED_HEADERS)
257 target_precompile_headers(audio_core PRIVATE precompiled_headers.h) 268 target_precompile_headers(audio_core PRIVATE precompiled_headers.h)
258endif() 269endif()
diff --git a/src/audio_core/sink/oboe_sink.cpp b/src/audio_core/sink/oboe_sink.cpp
new file mode 100644
index 000000000..e61841172
--- /dev/null
+++ b/src/audio_core/sink/oboe_sink.cpp
@@ -0,0 +1,223 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <span>
5#include <vector>
6
7#include <oboe/Oboe.h>
8
9#include "audio_core/common/common.h"
10#include "audio_core/sink/oboe_sink.h"
11#include "audio_core/sink/sink_stream.h"
12#include "common/logging/log.h"
13#include "common/scope_exit.h"
14#include "core/core.h"
15
16namespace AudioCore::Sink {
17
18class OboeSinkStream final : public SinkStream,
19 public oboe::AudioStreamDataCallback,
20 public oboe::AudioStreamErrorCallback {
21public:
22 explicit OboeSinkStream(Core::System& system_, StreamType type_, const std::string& name_,
23 u32 system_channels_)
24 : SinkStream(system_, type_) {
25 name = name_;
26 system_channels = system_channels_;
27
28 this->OpenStream();
29 }
30
31 ~OboeSinkStream() override {
32 LOG_INFO(Audio_Sink, "Destroyed Oboe stream");
33 }
34
35 void Finalize() override {
36 this->Stop();
37 m_stream.reset();
38 }
39
40 void Start(bool resume = false) override {
41 if (!m_stream || !paused) {
42 return;
43 }
44
45 paused = false;
46
47 if (m_stream->start() != oboe::Result::OK) {
48 LOG_CRITICAL(Audio_Sink, "Error starting Oboe stream");
49 }
50 }
51
52 void Stop() override {
53 if (!m_stream || paused) {
54 return;
55 }
56
57 this->SignalPause();
58
59 if (m_stream->stop() != oboe::Result::OK) {
60 LOG_CRITICAL(Audio_Sink, "Error stopping Oboe stream");
61 }
62 }
63
64public:
65 static s32 QueryChannelCount(oboe::Direction direction) {
66 std::shared_ptr<oboe::AudioStream> temp_stream;
67 oboe::AudioStreamBuilder builder;
68
69 const auto result = ConfigureBuilder(builder, direction)->openStream(temp_stream);
70 ASSERT(result == oboe::Result::OK);
71
72 return temp_stream->getChannelCount() >= 6 ? 6 : 2;
73 }
74
75protected:
76 oboe::DataCallbackResult onAudioReady(oboe::AudioStream*, void* audio_data,
77 s32 num_buffer_frames) override {
78 const size_t num_channels = this->GetDeviceChannels();
79 const size_t frame_size = num_channels;
80 const size_t num_frames = static_cast<size_t>(num_buffer_frames);
81
82 if (type == StreamType::In) {
83 std::span<const s16> input_buffer{reinterpret_cast<const s16*>(audio_data),
84 num_frames * frame_size};
85 this->ProcessAudioIn(input_buffer, num_frames);
86 } else {
87 std::span<s16> output_buffer{reinterpret_cast<s16*>(audio_data),
88 num_frames * frame_size};
89 this->ProcessAudioOutAndRender(output_buffer, num_frames);
90 }
91
92 return oboe::DataCallbackResult::Continue;
93 }
94
95 void onErrorAfterClose(oboe::AudioStream*, oboe::Result) override {
96 LOG_INFO(Audio_Sink, "Audio stream closed, reinitializing");
97
98 if (this->OpenStream()) {
99 m_stream->start();
100 }
101 }
102
103private:
104 static oboe::AudioStreamBuilder* ConfigureBuilder(oboe::AudioStreamBuilder& builder,
105 oboe::Direction direction) {
106 // TODO: investigate callback delay issues when using AAudio
107 return builder.setPerformanceMode(oboe::PerformanceMode::LowLatency)
108 ->setAudioApi(oboe::AudioApi::OpenSLES)
109 ->setDirection(direction)
110 ->setSampleRate(TargetSampleRate)
111 ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
112 ->setFormat(oboe::AudioFormat::I16)
113 ->setFormatConversionAllowed(true)
114 ->setUsage(oboe::Usage::Game)
115 ->setBufferCapacityInFrames(TargetSampleCount * 2);
116 }
117
118 bool OpenStream() {
119 const auto direction = [&]() {
120 switch (type) {
121 case StreamType::In:
122 return oboe::Direction::Input;
123 case StreamType::Out:
124 case StreamType::Render:
125 return oboe::Direction::Output;
126 default:
127 ASSERT(false);
128 return oboe::Direction::Output;
129 }
130 }();
131
132 const auto expected_channels = QueryChannelCount(direction);
133 const auto expected_mask = [&]() {
134 switch (expected_channels) {
135 case 1:
136 return oboe::ChannelMask::Mono;
137 case 2:
138 return oboe::ChannelMask::Stereo;
139 case 6:
140 return oboe::ChannelMask::CM5Point1;
141 default:
142 ASSERT(false);
143 return oboe::ChannelMask::Unspecified;
144 }
145 }();
146
147 oboe::AudioStreamBuilder builder;
148 const auto result = ConfigureBuilder(builder, direction)
149 ->setChannelCount(expected_channels)
150 ->setChannelMask(expected_mask)
151 ->setChannelConversionAllowed(true)
152 ->setDataCallback(this)
153 ->setErrorCallback(this)
154 ->openStream(m_stream);
155 ASSERT(result == oboe::Result::OK);
156 return result == oboe::Result::OK && this->SetStreamProperties();
157 }
158
159 bool SetStreamProperties() {
160 ASSERT(m_stream);
161
162 m_stream->setBufferSizeInFrames(TargetSampleCount * 2);
163 device_channels = m_stream->getChannelCount();
164
165 const auto sample_rate = m_stream->getSampleRate();
166 const auto buffer_capacity = m_stream->getBufferCapacityInFrames();
167 const auto stream_backend =
168 m_stream->getAudioApi() == oboe::AudioApi::AAudio ? "AAudio" : "OpenSLES";
169
170 LOG_INFO(Audio_Sink, "Opened Oboe {} stream with {} channels sample rate {} capacity {}",
171 stream_backend, device_channels, sample_rate, buffer_capacity);
172
173 return true;
174 }
175
176 std::shared_ptr<oboe::AudioStream> m_stream{};
177};
178
179OboeSink::OboeSink() {
180 // TODO: This is not generally knowable
181 // The channel count is distinct based on direction and can change
182 device_channels = OboeSinkStream::QueryChannelCount(oboe::Direction::Output);
183}
184
185OboeSink::~OboeSink() = default;
186
187SinkStream* OboeSink::AcquireSinkStream(Core::System& system, u32 system_channels,
188 const std::string& name, StreamType type) {
189 SinkStreamPtr& stream = sink_streams.emplace_back(
190 std::make_unique<OboeSinkStream>(system, type, name, system_channels));
191
192 return stream.get();
193}
194
195void OboeSink::CloseStream(SinkStream* to_remove) {
196 sink_streams.remove_if([&](auto& stream) { return stream.get() == to_remove; });
197}
198
199void OboeSink::CloseStreams() {
200 sink_streams.clear();
201}
202
203f32 OboeSink::GetDeviceVolume() const {
204 if (sink_streams.empty()) {
205 return 1.0f;
206 }
207
208 return sink_streams.front()->GetDeviceVolume();
209}
210
211void OboeSink::SetDeviceVolume(f32 volume) {
212 for (auto& stream : sink_streams) {
213 stream->SetDeviceVolume(volume);
214 }
215}
216
217void OboeSink::SetSystemVolume(f32 volume) {
218 for (auto& stream : sink_streams) {
219 stream->SetSystemVolume(volume);
220 }
221}
222
223} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/oboe_sink.h b/src/audio_core/sink/oboe_sink.h
new file mode 100644
index 000000000..8f6f54ab5
--- /dev/null
+++ b/src/audio_core/sink/oboe_sink.h
@@ -0,0 +1,75 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <list>
7#include <string>
8
9#include "audio_core/sink/sink.h"
10
11namespace Core {
12class System;
13}
14
15namespace AudioCore::Sink {
16class SinkStream;
17
18class OboeSink final : public Sink {
19public:
20 explicit OboeSink();
21 ~OboeSink() override;
22
23 /**
24 * Create a new sink stream.
25 *
26 * @param system - Core system.
27 * @param system_channels - Number of channels the audio system expects.
28 * May differ from the device's channel count.
29 * @param name - Name of this stream.
30 * @param type - Type of this stream, render/in/out.
31 *
32 * @return A pointer to the created SinkStream
33 */
34 SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels,
35 const std::string& name, StreamType type) override;
36
37 /**
38 * Close a given stream.
39 *
40 * @param stream - The stream to close.
41 */
42 void CloseStream(SinkStream* stream) override;
43
44 /**
45 * Close all streams.
46 */
47 void CloseStreams() override;
48
49 /**
50 * Get the device volume. Set from calls to the IAudioDevice service.
51 *
52 * @return Volume of the device.
53 */
54 f32 GetDeviceVolume() const override;
55
56 /**
57 * Set the device volume. Set from calls to the IAudioDevice service.
58 *
59 * @param volume - New volume of the device.
60 */
61 void SetDeviceVolume(f32 volume) override;
62
63 /**
64 * Set the system volume. Comes from the audio system using this stream.
65 *
66 * @param volume - New volume of the system.
67 */
68 void SetSystemVolume(f32 volume) override;
69
70private:
71 /// List of streams managed by this sink
72 std::list<SinkStreamPtr> sink_streams{};
73};
74
75} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp
index 7c9a4e3ac..449af659d 100644
--- a/src/audio_core/sink/sink_details.cpp
+++ b/src/audio_core/sink/sink_details.cpp
@@ -7,6 +7,9 @@
7#include <vector> 7#include <vector>
8 8
9#include "audio_core/sink/sink_details.h" 9#include "audio_core/sink/sink_details.h"
10#ifdef HAVE_OBOE
11#include "audio_core/sink/oboe_sink.h"
12#endif
10#ifdef HAVE_CUBEB 13#ifdef HAVE_CUBEB
11#include "audio_core/sink/cubeb_sink.h" 14#include "audio_core/sink/cubeb_sink.h"
12#endif 15#endif
@@ -36,6 +39,16 @@ struct SinkDetails {
36 39
37// sink_details is ordered in terms of desirability, with the best choice at the top. 40// sink_details is ordered in terms of desirability, with the best choice at the top.
38constexpr SinkDetails sink_details[] = { 41constexpr SinkDetails sink_details[] = {
42#ifdef HAVE_OBOE
43 SinkDetails{
44 Settings::AudioEngine::Oboe,
45 [](std::string_view device_id) -> std::unique_ptr<Sink> {
46 return std::make_unique<OboeSink>();
47 },
48 [](bool capture) { return std::vector<std::string>{"Default"}; },
49 []() { return true; },
50 },
51#endif
39#ifdef HAVE_CUBEB 52#ifdef HAVE_CUBEB
40 SinkDetails{ 53 SinkDetails{
41 Settings::AudioEngine::Cubeb, 54 Settings::AudioEngine::Cubeb,
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index d6351e57e..617036588 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -82,16 +82,15 @@ enum class AudioEngine : u32 {
82 Cubeb, 82 Cubeb,
83 Sdl2, 83 Sdl2,
84 Null, 84 Null,
85 Oboe,
85}; 86};
86 87
87template <> 88template <>
88inline std::vector<std::pair<std::string, AudioEngine>> 89inline std::vector<std::pair<std::string, AudioEngine>>
89EnumMetadata<AudioEngine>::Canonicalizations() { 90EnumMetadata<AudioEngine>::Canonicalizations() {
90 return { 91 return {
91 {"auto", AudioEngine::Auto}, 92 {"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2},
92 {"cubeb", AudioEngine::Cubeb}, 93 {"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
93 {"sdl2", AudioEngine::Sdl2},
94 {"null", AudioEngine::Null},
95 }; 94 };
96} 95}
97 96