summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/service/audio/hwopus.cpp135
-rw-r--r--src/core/hle/service/audio/hwopus.h1
3 files changed, 132 insertions, 6 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f05e02cfb..3386c2231 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -318,7 +318,7 @@ add_library(core STATIC
318create_target_directory_groups(core) 318create_target_directory_groups(core)
319 319
320target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 320target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
321target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn) 321target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static opus unicorn)
322 322
323if (ARCHITECTURE_x86_64) 323if (ARCHITECTURE_x86_64)
324 target_sources(core PRIVATE 324 target_sources(core PRIVATE
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 844df382c..371cd4997 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring>
6#include <opus.h>
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/hle_ipc.h" 9#include "core/hle/kernel/hle_ipc.h"
@@ -9,19 +11,142 @@
9 11
10namespace Service::Audio { 12namespace Service::Audio {
11 13
14struct OpusDeleter {
15 void operator()(void* ptr) const {
16 operator delete(ptr);
17 }
18};
19
20class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
21public:
22 IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoder, OpusDeleter> decoder, u32 sample_rate,
23 u32 channel_count)
24 : ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)),
25 sample_rate(sample_rate), channel_count(channel_count) {
26 static const FunctionInfo functions[] = {
27 {0, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
28 {1, nullptr, "SetContext"},
29 {2, nullptr, "DecodeInterleavedForMultiStream"},
30 {3, nullptr, "SetContextForMultiStream"},
31 {4, nullptr, "Unknown4"},
32 {5, nullptr, "Unknown5"},
33 {6, nullptr, "Unknown6"},
34 {7, nullptr, "Unknown7"},
35 };
36 RegisterHandlers(functions);
37 }
38
39private:
40 void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
41 u32 consumed = 0;
42 u32 sample_count = 0;
43 std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
44 if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples)) {
45 IPC::ResponseBuilder rb{ctx, 2};
46 // TODO(ogniK): Use correct error code
47 rb.Push(ResultCode(-1));
48 return;
49 }
50 IPC::ResponseBuilder rb{ctx, 4};
51 rb.Push(RESULT_SUCCESS);
52 rb.Push<u32>(consumed);
53 rb.Push<u32>(sample_count);
54 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
55 }
56
57 bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input,
58 std::vector<opus_int16>& output) {
59 size_t raw_output_sz = output.size() * sizeof(opus_int16);
60 if (sizeof(OpusHeader) > input.size())
61 return false;
62 OpusHeader hdr{};
63 std::memcpy(&hdr, input.data(), sizeof(OpusHeader));
64 if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) {
65 return false;
66 }
67 auto frame = input.data() + sizeof(OpusHeader);
68 auto decoded_sample_count = opus_packet_get_nb_samples(
69 frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)),
70 static_cast<opus_int32>(sample_rate));
71 if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz)
72 return false;
73 auto out_sample_count =
74 opus_decode(decoder.get(), frame, hdr.sz, output.data(),
75 (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0);
76 if (out_sample_count < 0)
77 return false;
78 sample_count = out_sample_count;
79 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
80 return true;
81 }
82
83 struct OpusHeader {
84 u32_be sz; // Needs to be BE for some odd reason
85 INSERT_PADDING_WORDS(1);
86 };
87 static_assert(sizeof(OpusHeader) == 0x8, "OpusHeader is an invalid size");
88
89 std::unique_ptr<OpusDecoder, OpusDeleter> decoder;
90 u32 sample_rate;
91 u32 channel_count;
92};
93
94static size_t WorkerBufferSize(u32 channel_count) {
95 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
96 return opus_decoder_get_size(static_cast<int>(channel_count));
97}
98
12void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { 99void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
13 LOG_WARNING(Service_Audio, "(STUBBED) called"); 100 IPC::RequestParser rp{ctx};
101 auto sample_rate = rp.Pop<u32>();
102 auto channel_count = rp.Pop<u32>();
103 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
104 sample_rate == 12000 || sample_rate == 8000,
105 "Invalid sample rate");
106 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
107 u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count));
108 LOG_DEBUG(Audio, "called worker_buffer_sz={}", worker_buffer_sz);
109
14 IPC::ResponseBuilder rb{ctx, 3}; 110 IPC::ResponseBuilder rb{ctx, 3};
15 rb.Push(RESULT_SUCCESS); 111 rb.Push(RESULT_SUCCESS);
16 rb.Push<u32>(0x4000); 112 rb.Push<u32>(worker_buffer_sz);
113}
114
115void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
116 IPC::RequestParser rp{ctx};
117 auto sample_rate = rp.Pop<u32>();
118 auto channel_count = rp.Pop<u32>();
119 auto buffer_sz = rp.Pop<u32>();
120 LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate,
121 channel_count, buffer_sz);
122 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
123 sample_rate == 12000 || sample_rate == 8000,
124 "Invalid sample rate");
125 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
126
127 size_t worker_sz = WorkerBufferSize(channel_count);
128 ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large");
129 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
130 static_cast<OpusDecoder*>(operator new(worker_sz))};
131 if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
132 IPC::ResponseBuilder rb{ctx, 2};
133 // TODO(ogniK): Use correct error code
134 rb.Push(ResultCode(-1));
135 return;
136 }
137
138 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
139 rb.Push(RESULT_SUCCESS);
140 rb.PushIpcInterface<IHardwareOpusDecoderManager>(std::move(decoder), sample_rate,
141 channel_count);
17} 142}
18 143
19HwOpus::HwOpus() : ServiceFramework("hwopus") { 144HwOpus::HwOpus() : ServiceFramework("hwopus") {
20 static const FunctionInfo functions[] = { 145 static const FunctionInfo functions[] = {
21 {0, nullptr, "Initialize"}, 146 {0, &HwOpus::OpenOpusDecoder, "OpenOpusDecoder"},
22 {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, 147 {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"},
23 {2, nullptr, "InitializeMultiStream"}, 148 {2, nullptr, "OpenOpusDecoderForMultiStream"},
24 {3, nullptr, "GetWorkBufferSizeMultiStream"}, 149 {3, nullptr, "GetWorkBufferSizeForMultiStream"},
25 }; 150 };
26 RegisterHandlers(functions); 151 RegisterHandlers(functions);
27} 152}
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h
index 090b8c825..5258d59f3 100644
--- a/src/core/hle/service/audio/hwopus.h
+++ b/src/core/hle/service/audio/hwopus.h
@@ -14,6 +14,7 @@ public:
14 ~HwOpus() = default; 14 ~HwOpus() = default;
15 15
16private: 16private:
17 void OpenOpusDecoder(Kernel::HLERequestContext& ctx);
17 void GetWorkBufferSize(Kernel::HLERequestContext& ctx); 18 void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
18}; 19};
19 20