summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2018-07-27 20:25:32 -0700
committerGravatar GitHub2018-07-27 20:25:32 -0700
commite1d66ea7942800d51aff57c44d0e9ab9ffd949dc (patch)
treeaaff4a680cafbd1111baa602af8d0d2d7297f585
parentMerge pull request #696 from DarkLordZach/romfs (diff)
parentaudout: Implement IAudioOut interface with AudioCore. (diff)
downloadyuzu-e1d66ea7942800d51aff57c44d0e9ab9ffd949dc.tar.gz
yuzu-e1d66ea7942800d51aff57c44d0e9ab9ffd949dc.tar.xz
yuzu-e1d66ea7942800d51aff57c44d0e9ab9ffd949dc.zip
Merge pull request #842 from bunnei/audio-core
Initial implementation of Audio Core
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/audio_core/CMakeLists.txt11
-rw-r--r--src/audio_core/audio_out.cpp50
-rw-r--r--src/audio_core/audio_out.h44
-rw-r--r--src/audio_core/buffer.h37
-rw-r--r--src/audio_core/stream.cpp103
-rw-r--r--src/audio_core/stream.h89
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/core.h7
-rw-r--r--src/core/hle/service/audio/audout_u.cpp195
-rw-r--r--src/core/hle/service/audio/audout_u.h12
12 files changed, 459 insertions, 94 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 85354f43e..a88551fbc 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,6 +3,7 @@ include_directories(.)
3 3
4add_subdirectory(common) 4add_subdirectory(common)
5add_subdirectory(core) 5add_subdirectory(core)
6add_subdirectory(audio_core)
6add_subdirectory(video_core) 7add_subdirectory(video_core)
7add_subdirectory(input_common) 8add_subdirectory(input_common)
8add_subdirectory(tests) 9add_subdirectory(tests)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
new file mode 100644
index 000000000..f00a55994
--- /dev/null
+++ b/src/audio_core/CMakeLists.txt
@@ -0,0 +1,11 @@
1add_library(audio_core STATIC
2 audio_out.cpp
3 audio_out.h
4 buffer.h
5 stream.cpp
6 stream.h
7)
8
9create_target_directory_groups(audio_core)
10
11target_link_libraries(audio_core PUBLIC common core)
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
new file mode 100644
index 000000000..6d418a05b
--- /dev/null
+++ b/src/audio_core/audio_out.cpp
@@ -0,0 +1,50 @@
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 "audio_core/audio_out.h"
6#include "common/assert.h"
7#include "common/logging/log.h"
8
9namespace AudioCore {
10
11/// Returns the stream format from the specified number of channels
12static Stream::Format ChannelsToStreamFormat(int num_channels) {
13 switch (num_channels) {
14 case 1:
15 return Stream::Format::Mono16;
16 case 2:
17 return Stream::Format::Stereo16;
18 case 6:
19 return Stream::Format::Multi51Channel16;
20 }
21
22 LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
23 UNREACHABLE();
24 return {};
25}
26
27StreamPtr AudioOut::OpenStream(int sample_rate, int num_channels,
28 Stream::ReleaseCallback&& release_callback) {
29 streams.push_back(std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
30 std::move(release_callback)));
31 return streams.back();
32}
33
34std::vector<u64> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
35 return stream->GetTagsAndReleaseBuffers(max_count);
36}
37
38void AudioOut::StartStream(StreamPtr stream) {
39 stream->Play();
40}
41
42void AudioOut::StopStream(StreamPtr stream) {
43 stream->Stop();
44}
45
46bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
47 return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
48}
49
50} // namespace AudioCore
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
new file mode 100644
index 000000000..a86499d10
--- /dev/null
+++ b/src/audio_core/audio_out.h
@@ -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#pragma once
6
7#include <memory>
8#include <vector>
9
10#include "audio_core/buffer.h"
11#include "audio_core/stream.h"
12#include "common/common_types.h"
13
14namespace AudioCore {
15
16using StreamPtr = std::shared_ptr<Stream>;
17
18/**
19 * Represents an audio playback interface, used to open and play audio streams
20 */
21class AudioOut {
22public:
23 /// Opens a new audio stream
24 StreamPtr OpenStream(int sample_rate, int num_channels,
25 Stream::ReleaseCallback&& release_callback);
26
27 /// Returns a vector of recently released buffers specified by tag for the specified stream
28 std::vector<u64> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count);
29
30 /// Starts an audio stream for playback
31 void StartStream(StreamPtr stream);
32
33 /// Stops an audio stream that is currently playing
34 void StopStream(StreamPtr stream);
35
36 /// Queues a buffer into the specified audio stream, returns true on success
37 bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
38
39private:
40 /// Active audio streams on the interface
41 std::vector<StreamPtr> streams;
42};
43
44} // namespace AudioCore
diff --git a/src/audio_core/buffer.h b/src/audio_core/buffer.h
new file mode 100644
index 000000000..874ec787e
--- /dev/null
+++ b/src/audio_core/buffer.h
@@ -0,0 +1,37 @@
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 <vector>
8
9#include "common/common_types.h"
10
11namespace AudioCore {
12
13/**
14 * Represents a buffer of audio samples to be played in an audio stream
15 */
16class Buffer {
17public:
18 using Tag = u64;
19
20 Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
21
22 /// Returns the raw audio data for the buffer
23 const std::vector<u8>& GetData() const {
24 return data;
25 }
26
27 /// Returns the buffer tag, this is provided by the game to the audout service
28 Tag GetTag() const {
29 return tag;
30 }
31
32private:
33 Tag tag;
34 std::vector<u8> data;
35};
36
37} // namespace AudioCore
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
new file mode 100644
index 000000000..82bff4b9e
--- /dev/null
+++ b/src/audio_core/stream.cpp
@@ -0,0 +1,103 @@
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 "common/assert.h"
6#include "common/logging/log.h"
7#include "core/core_timing.h"
8#include "core/core_timing_util.h"
9
10#include "audio_core/stream.h"
11
12namespace AudioCore {
13
14constexpr size_t MaxAudioBufferCount{32};
15
16/// Returns the sample size for the specified audio stream format
17static size_t SampleSizeFromFormat(Stream::Format format) {
18 switch (format) {
19 case Stream::Format::Mono16:
20 return 2;
21 case Stream::Format::Stereo16:
22 return 4;
23 case Stream::Format::Multi51Channel16:
24 return 12;
25 };
26
27 LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
28 UNREACHABLE();
29 return {};
30}
31
32Stream::Stream(int sample_rate, Format format, ReleaseCallback&& release_callback)
33 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)} {
34 release_event = CoreTiming::RegisterEvent(
35 "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
36}
37
38void Stream::Play() {
39 state = State::Playing;
40 PlayNextBuffer();
41}
42
43void Stream::Stop() {
44 ASSERT_MSG(false, "Unimplemented");
45}
46
47s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
48 const size_t num_samples{buffer.GetData().size() / SampleSizeFromFormat(format)};
49 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
50}
51
52void Stream::PlayNextBuffer() {
53 if (!IsPlaying()) {
54 // Ensure we are in playing state before playing the next buffer
55 return;
56 }
57
58 if (active_buffer) {
59 // Do not queue a new buffer if we are already playing a buffer
60 return;
61 }
62
63 if (queued_buffers.empty()) {
64 // No queued buffers - we are effectively paused
65 return;
66 }
67
68 active_buffer = queued_buffers.front();
69 queued_buffers.pop();
70
71 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
72}
73
74void Stream::ReleaseActiveBuffer() {
75 released_buffers.push(std::move(active_buffer));
76 release_callback();
77 PlayNextBuffer();
78}
79
80bool Stream::QueueBuffer(BufferPtr&& buffer) {
81 if (queued_buffers.size() < MaxAudioBufferCount) {
82 queued_buffers.push(std::move(buffer));
83 PlayNextBuffer();
84 return true;
85 }
86 return false;
87}
88
89bool Stream::ContainsBuffer(Buffer::Tag tag) const {
90 ASSERT_MSG(false, "Unimplemented");
91 return {};
92}
93
94std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) {
95 std::vector<Buffer::Tag> tags;
96 for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
97 tags.push_back(released_buffers.front()->GetTag());
98 released_buffers.pop();
99 }
100 return tags;
101}
102
103} // namespace AudioCore
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
new file mode 100644
index 000000000..5f43b0798
--- /dev/null
+++ b/src/audio_core/stream.h
@@ -0,0 +1,89 @@
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#include <queue>
11
12#include "audio_core/buffer.h"
13#include "common/assert.h"
14#include "common/common_types.h"
15#include "core/core_timing.h"
16
17namespace AudioCore {
18
19using BufferPtr = std::shared_ptr<Buffer>;
20
21/**
22 * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
23 */
24class Stream {
25public:
26 /// Audio format of the stream
27 enum class Format {
28 Mono16,
29 Stereo16,
30 Multi51Channel16,
31 };
32
33 /// Callback function type, used to change guest state on a buffer being released
34 using ReleaseCallback = std::function<void()>;
35
36 Stream(int sample_rate, Format format, ReleaseCallback&& release_callback);
37
38 /// Plays the audio stream
39 void Play();
40
41 /// Stops the audio stream
42 void Stop();
43
44 /// Queues a buffer into the audio stream, returns true on success
45 bool QueueBuffer(BufferPtr&& buffer);
46
47 /// Returns true if the audio stream contains a buffer with the specified tag
48 bool ContainsBuffer(Buffer::Tag tag) const;
49
50 /// Returns a vector of recently released buffers specified by tag
51 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count);
52
53 /// Returns true if the stream is currently playing
54 bool IsPlaying() const {
55 return state == State::Playing;
56 }
57
58 /// Returns the number of queued buffers
59 size_t GetQueueSize() const {
60 return queued_buffers.size();
61 }
62
63private:
64 /// Current state of the stream
65 enum class State {
66 Stopped,
67 Playing,
68 };
69
70 /// Plays the next queued buffer in the audio stream, starting playback if necessary
71 void PlayNextBuffer();
72
73 /// Releases the actively playing buffer, signalling that it has been completed
74 void ReleaseActiveBuffer();
75
76 /// Gets the number of core cycles when the specified buffer will be released
77 s64 GetBufferReleaseCycles(const Buffer& buffer) const;
78
79 int sample_rate; ///< Sample rate of the stream
80 Format format; ///< Format of the stream
81 ReleaseCallback release_callback; ///< Buffer release callback for the stream
82 State state{State::Stopped}; ///< Playback state of the stream
83 CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream
84 BufferPtr active_buffer; ///< Actively playing buffer in the stream
85 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
87};
88
89} // namespace AudioCore
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f389897d4..30d7667c8 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -309,7 +309,7 @@ add_library(core STATIC
309 309
310create_target_directory_groups(core) 310create_target_directory_groups(core)
311 311
312target_link_libraries(core PUBLIC common PRIVATE video_core) 312target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
313target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn) 313target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn)
314 314
315if (ARCHITECTURE_x86_64) 315if (ARCHITECTURE_x86_64)
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b7f4b4532..186fa46df 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -177,6 +177,7 @@ 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>();
180 telemetry_session = std::make_unique<Core::TelemetrySession>(); 181 telemetry_session = std::make_unique<Core::TelemetrySession>();
181 service_manager = std::make_shared<Service::SM::ServiceManager>(); 182 service_manager = std::make_shared<Service::SM::ServiceManager>();
182 183
@@ -228,6 +229,7 @@ void System::Shutdown() {
228 service_manager.reset(); 229 service_manager.reset();
229 telemetry_session.reset(); 230 telemetry_session.reset();
230 gpu_core.reset(); 231 gpu_core.reset();
232 audio_core.reset();
231 233
232 // Close all CPU/threading state 234 // Close all CPU/threading state
233 cpu_barrier->NotifyEnd(); 235 cpu_barrier->NotifyEnd();
diff --git a/src/core/core.h b/src/core/core.h
index c123fe401..6f4df775f 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -8,6 +8,7 @@
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"
11#include "common/common_types.h" 12#include "common/common_types.h"
12#include "core/arm/exclusive_monitor.h" 13#include "core/arm/exclusive_monitor.h"
13#include "core/core_cpu.h" 14#include "core/core_cpu.h"
@@ -131,6 +132,11 @@ public:
131 return *gpu_core; 132 return *gpu_core;
132 } 133 }
133 134
135 /// Gets the AudioCore interface
136 AudioCore::AudioOut& AudioCore() {
137 return *audio_core;
138 }
139
134 /// Gets the scheduler for the CPU core that is currently running 140 /// Gets the scheduler for the CPU core that is currently running
135 Kernel::Scheduler& CurrentScheduler() { 141 Kernel::Scheduler& CurrentScheduler() {
136 return *CurrentCpuCore().Scheduler(); 142 return *CurrentCpuCore().Scheduler();
@@ -195,6 +201,7 @@ private:
195 /// AppLoader used to load the current executing application 201 /// AppLoader used to load the current executing application
196 std::unique_ptr<Loader::AppLoader> app_loader; 202 std::unique_ptr<Loader::AppLoader> app_loader;
197 std::unique_ptr<Tegra::GPU> gpu_core; 203 std::unique_ptr<Tegra::GPU> gpu_core;
204 std::unique_ptr<AudioCore::AudioOut> audio_core;
198 std::shared_ptr<Tegra::DebugContext> debug_context; 205 std::shared_ptr<Tegra::DebugContext> debug_context;
199 Kernel::SharedPtr<Kernel::Process> current_process; 206 Kernel::SharedPtr<Kernel::Process> current_process;
200 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 207 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 1dcd84d98..a15d53ff8 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -5,8 +5,7 @@
5#include <array> 5#include <array>
6#include <vector> 6#include <vector>
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core_timing.h" 8#include "core/core.h"
9#include "core/core_timing_util.h"
10#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
12#include "core/hle/kernel/hle_ipc.h" 11#include "core/hle/kernel/hle_ipc.h"
@@ -14,17 +13,22 @@
14 13
15namespace Service::Audio { 14namespace Service::Audio {
16 15
17/// Switch sample rate frequency 16namespace ErrCodes {
18constexpr u32 sample_rate{48000}; 17enum {
19/// TODO(st4rk): dynamic number of channels, as I think Switch has support 18 ErrorUnknown = 2,
20/// to more audio channels (probably when Docked I guess) 19 BufferCountExceeded = 8,
21constexpr u32 audio_channels{2}; 20};
22/// TODO(st4rk): find a proper value for the audio_ticks 21}
23constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 500)}; 22
23constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
24constexpr int DefaultSampleRate{48000};
24 25
25class IAudioOut final : public ServiceFramework<IAudioOut> { 26class IAudioOut final : public ServiceFramework<IAudioOut> {
26public: 27public:
27 IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) { 28 IAudioOut(AudoutParams audio_params)
29 : ServiceFramework("IAudioOut"), audio_params(audio_params),
30 audio_core(Core::System::GetInstance().AudioCore()) {
31
28 static const FunctionInfo functions[] = { 32 static const FunctionInfo functions[] = {
29 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 33 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
30 {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, 34 {1, &IAudioOut::StartAudioOut, "StartAudioOut"},
@@ -32,66 +36,65 @@ public:
32 {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, 36 {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
33 {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, 37 {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
34 {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"}, 38 {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"},
35 {6, nullptr, "ContainsAudioOutBuffer"}, 39 {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
36 {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, 40 {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
37 {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, 41 {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
38 {9, nullptr, "GetAudioOutBufferCount"}, 42 {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
39 {10, nullptr, "GetAudioOutPlayedSampleCount"}, 43 {10, nullptr, "GetAudioOutPlayedSampleCount"},
40 {11, nullptr, "FlushAudioOutBuffers"}, 44 {11, nullptr, "FlushAudioOutBuffers"},
41 }; 45 };
42 RegisterHandlers(functions); 46 RegisterHandlers(functions);
43 47
44 // This is the event handle used to check if the audio buffer was released 48 // This is the event handle used to check if the audio buffer was released
45 buffer_event = 49 buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
46 Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
47
48 // Register event callback to update the Audio Buffer
49 audio_event = CoreTiming::RegisterEvent(
50 "IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) {
51 UpdateAudioBuffersCallback();
52 CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
53 });
54
55 // Start the audio event
56 CoreTiming::ScheduleEvent(audio_ticks, audio_event);
57 }
58 50
59 ~IAudioOut() { 51 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
60 CoreTiming::UnscheduleEvent(audio_event, 0); 52 [=]() { buffer_event->Signal(); });
61 } 53 }
62 54
63private: 55private:
56 struct AudioBuffer {
57 u64_le next;
58 u64_le buffer;
59 u64_le buffer_capacity;
60 u64_le buffer_size;
61 u64_le offset;
62 };
63 static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
64
64 void GetAudioOutState(Kernel::HLERequestContext& ctx) { 65 void GetAudioOutState(Kernel::HLERequestContext& ctx) {
65 LOG_DEBUG(Service_Audio, "called"); 66 LOG_DEBUG(Service_Audio, "called");
66 IPC::ResponseBuilder rb{ctx, 3}; 67 IPC::ResponseBuilder rb{ctx, 3};
67 rb.Push(RESULT_SUCCESS); 68 rb.Push(RESULT_SUCCESS);
68 rb.Push(static_cast<u32>(audio_out_state)); 69 rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
69 } 70 }
70 71
71 void StartAudioOut(Kernel::HLERequestContext& ctx) { 72 void StartAudioOut(Kernel::HLERequestContext& ctx) {
72 LOG_WARNING(Service_Audio, "(STUBBED) called"); 73 LOG_DEBUG(Service_Audio, "called");
74
75 if (stream->IsPlaying()) {
76 IPC::ResponseBuilder rb{ctx, 2};
77 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown));
78 return;
79 }
73 80
74 // Start audio 81 audio_core.StartStream(stream);
75 audio_out_state = AudioState::Started;
76 82
77 IPC::ResponseBuilder rb{ctx, 2}; 83 IPC::ResponseBuilder rb{ctx, 2};
78 rb.Push(RESULT_SUCCESS); 84 rb.Push(RESULT_SUCCESS);
79 } 85 }
80 86
81 void StopAudioOut(Kernel::HLERequestContext& ctx) { 87 void StopAudioOut(Kernel::HLERequestContext& ctx) {
82 LOG_WARNING(Service_Audio, "(STUBBED) called"); 88 LOG_DEBUG(Service_Audio, "called");
83
84 // Stop audio
85 audio_out_state = AudioState::Stopped;
86 89
87 queue_keys.clear(); 90 audio_core.StopStream(stream);
88 91
89 IPC::ResponseBuilder rb{ctx, 2}; 92 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(RESULT_SUCCESS); 93 rb.Push(RESULT_SUCCESS);
91 } 94 }
92 95
93 void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { 96 void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
94 LOG_WARNING(Service_Audio, "(STUBBED) called"); 97 LOG_DEBUG(Service_Audio, "called");
95 98
96 IPC::ResponseBuilder rb{ctx, 2, 1}; 99 IPC::ResponseBuilder rb{ctx, 2, 1};
97 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
@@ -99,101 +102,107 @@ private:
99 } 102 }
100 103
101 void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { 104 void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
102 LOG_WARNING(Service_Audio, "(STUBBED) called"); 105 LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
103 IPC::RequestParser rp{ctx}; 106 IPC::RequestParser rp{ctx};
104 107
105 const u64 key{rp.Pop<u64>()}; 108 const auto& input_buffer{ctx.ReadBuffer()};
106 queue_keys.insert(queue_keys.begin(), key); 109 ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer),
110 "AudioBuffer input is an invalid size!");
111 AudioBuffer audio_buffer{};
112 std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
113 const u64 tag{rp.Pop<u64>()};
114
115 std::vector<u8> data(audio_buffer.buffer_size);
116 Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size());
117
118 if (!audio_core.QueueBuffer(stream, tag, std::move(data))) {
119 IPC::ResponseBuilder rb{ctx, 2};
120 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
121 }
107 122
108 IPC::ResponseBuilder rb{ctx, 2}; 123 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(RESULT_SUCCESS); 124 rb.Push(RESULT_SUCCESS);
110 } 125 }
111 126
112 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { 127 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
113 LOG_WARNING(Service_Audio, "(STUBBED) called"); 128 LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
114 129 IPC::RequestParser rp{ctx};
115 // TODO(st4rk): This is how libtransistor currently implements the 130 const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
116 // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address 131 const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
117 // is used to know which buffer should be filled with data and send again to the service
118 // through AppendAudioOutBuffer. Check if this is the proper way to do it.
119 u64 key{0};
120
121 if (queue_keys.size()) {
122 key = queue_keys.back();
123 queue_keys.pop_back();
124 }
125 132
126 ctx.WriteBuffer(&key, sizeof(u64)); 133 std::vector<u64> tags{released_buffers};
134 tags.resize(max_count);
135 ctx.WriteBuffer(tags);
127 136
128 IPC::ResponseBuilder rb{ctx, 3}; 137 IPC::ResponseBuilder rb{ctx, 3};
129 rb.Push(RESULT_SUCCESS); 138 rb.Push(RESULT_SUCCESS);
130 // TODO(st4rk): This might be the total of released buffers, needs to be verified on 139 rb.Push<u32>(static_cast<u32>(released_buffers.size()));
131 // hardware
132 rb.Push<u32>(static_cast<u32>(queue_keys.size()));
133 } 140 }
134 141
135 void UpdateAudioBuffersCallback() { 142 void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
136 if (audio_out_state != AudioState::Started) { 143 LOG_DEBUG(Service_Audio, "called");
137 return; 144 IPC::RequestParser rp{ctx};
138 } 145 const u64 tag{rp.Pop<u64>()};
139 146 IPC::ResponseBuilder rb{ctx, 3};
140 if (queue_keys.empty()) { 147 rb.Push(RESULT_SUCCESS);
141 return; 148 rb.Push(stream->ContainsBuffer(tag));
142 } 149 }
143 150
144 buffer_event->Signal(); 151 void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
152 LOG_DEBUG(Service_Audio, "called");
153 IPC::ResponseBuilder rb{ctx, 3};
154 rb.Push(RESULT_SUCCESS);
155 rb.Push(static_cast<u32>(stream->GetQueueSize()));
145 } 156 }
146 157
147 enum class AudioState : u32 { 158 AudioCore::AudioOut& audio_core;
148 Started, 159 AudioCore::StreamPtr stream;
149 Stopped,
150 };
151 160
152 /// This is used to trigger the audio event callback that is going to read the samples from the 161 AudoutParams audio_params{};
153 /// audio_buffer list and enqueue the samples using the sink (audio_core).
154 CoreTiming::EventType* audio_event;
155 162
156 /// This is the evend handle used to check if the audio buffer was released 163 /// This is the evend handle used to check if the audio buffer was released
157 Kernel::SharedPtr<Kernel::Event> buffer_event; 164 Kernel::SharedPtr<Kernel::Event> buffer_event;
158
159 /// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor
160 /// uses the key as an address in the App, so we need to return when the
161 /// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because
162 /// libtransistor uses the key returned as an pointer.
163 std::vector<u64> queue_keys;
164
165 AudioState audio_out_state;
166}; 165};
167 166
168void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { 167void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
169 LOG_WARNING(Service_Audio, "(STUBBED) called"); 168 LOG_DEBUG(Service_Audio, "called");
170 IPC::RequestParser rp{ctx}; 169 IPC::RequestParser rp{ctx};
171 170
172 constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; 171 ctx.WriteBuffer(DefaultDevice);
173 ctx.WriteBuffer(audio_interface);
174 172
175 IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); 173 IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
176 174
177 rb.Push(RESULT_SUCCESS); 175 rb.Push(RESULT_SUCCESS);
178 // TODO(st4rk): We're currently returning only one audio interface (stringlist size). However, 176 rb.Push<u32>(1); // Amount of audio devices
179 // it's highly possible to have more than one interface (despite that libtransistor requires
180 // only one).
181 rb.Push<u32>(1);
182} 177}
183 178
184void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { 179void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
185 LOG_WARNING(Service_Audio, "(STUBBED) called"); 180 LOG_DEBUG(Service_Audio, "called");
186 181
187 if (!audio_out_interface) { 182 ctx.WriteBuffer(DefaultDevice);
188 audio_out_interface = std::make_shared<IAudioOut>(); 183 IPC::RequestParser rp{ctx};
184 auto params{rp.PopRaw<AudoutParams>()};
185 if (params.channel_count <= 2) {
186 // Mono does not exist for audout
187 params.channel_count = 2;
188 } else {
189 params.channel_count = 6;
189 } 190 }
191 if (!params.sample_rate) {
192 params.sample_rate = DefaultSampleRate;
193 }
194
195 // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl
196 // will likely need to be updated as well.
197 ASSERT_MSG(!audio_out_interface, "Unimplemented");
198 audio_out_interface = std::make_shared<IAudioOut>(std::move(params));
190 199
191 IPC::ResponseBuilder rb{ctx, 6, 0, 1}; 200 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
192 rb.Push(RESULT_SUCCESS); 201 rb.Push(RESULT_SUCCESS);
193 rb.Push<u32>(sample_rate); 202 rb.Push<u32>(DefaultSampleRate);
194 rb.Push<u32>(audio_channels); 203 rb.Push<u32>(params.channel_count);
195 rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); 204 rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
196 rb.Push<u32>(0); // This field is unknown 205 rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
197 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); 206 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
198} 207}
199 208
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index 847d86aa6..bc43f1f44 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -12,6 +12,18 @@ class HLERequestContext;
12 12
13namespace Service::Audio { 13namespace Service::Audio {
14 14
15struct AudoutParams {
16 s32_le sample_rate;
17 u16_le channel_count;
18 INSERT_PADDING_BYTES(2);
19};
20static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
21
22enum class AudioState : u32 {
23 Started,
24 Stopped,
25};
26
15class IAudioOut; 27class IAudioOut;
16 28
17class AudOutU final : public ServiceFramework<AudOutU> { 29class AudOutU final : public ServiceFramework<AudOutU> {