summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/hle/service/audio/audout_u.cpp195
-rw-r--r--src/core/hle/service/audio/audout_u.h12
2 files changed, 114 insertions, 93 deletions
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> {