mirror of
				https://git.suyu.dev/suyu/suyu.git
				synced 2025-11-04 12:34:39 +08:00 
			
		
		
		
	Implemented various hwopus functions (#853)
This commit is contained in:
		
							parent
							
								
									05ee92a357
								
							
						
					
					
						commit
						a483e5e28d
					
				
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@ -22,3 +22,6 @@
 | 
			
		||||
[submodule "unicorn"]
 | 
			
		||||
    path = externals/unicorn
 | 
			
		||||
    url = https://github.com/yuzu-emu/unicorn
 | 
			
		||||
[submodule "opus"]
 | 
			
		||||
	path = externals/opus
 | 
			
		||||
	url = https://github.com/ogniK5377/opus.git
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								externals/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								externals/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							@ -50,3 +50,7 @@ if (ARCHITECTURE_x86_64)
 | 
			
		||||
    target_include_directories(xbyak INTERFACE ./xbyak/xbyak)
 | 
			
		||||
    target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Opus
 | 
			
		||||
add_subdirectory(opus)
 | 
			
		||||
target_include_directories(opus INTERFACE ./opus/include)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								externals/opus
									
									
									
									
										vendored
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								externals/opus
									
									
									
									
										vendored
									
									
										Submodule
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Subproject commit b2871922a12abb49579512d604cabc471a59ad91
 | 
			
		||||
@ -318,7 +318,7 @@ add_library(core STATIC
 | 
			
		||||
create_target_directory_groups(core)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
 | 
			
		||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn)
 | 
			
		||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static opus unicorn)
 | 
			
		||||
 | 
			
		||||
if (ARCHITECTURE_x86_64)
 | 
			
		||||
    target_sources(core PRIVATE
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,8 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <opus.h>
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "core/hle/ipc_helpers.h"
 | 
			
		||||
#include "core/hle/kernel/hle_ipc.h"
 | 
			
		||||
@ -9,19 +11,142 @@
 | 
			
		||||
 | 
			
		||||
namespace Service::Audio {
 | 
			
		||||
 | 
			
		||||
struct OpusDeleter {
 | 
			
		||||
    void operator()(void* ptr) const {
 | 
			
		||||
        operator delete(ptr);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
 | 
			
		||||
public:
 | 
			
		||||
    IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoder, OpusDeleter> decoder, u32 sample_rate,
 | 
			
		||||
                                u32 channel_count)
 | 
			
		||||
        : ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)),
 | 
			
		||||
          sample_rate(sample_rate), channel_count(channel_count) {
 | 
			
		||||
        static const FunctionInfo functions[] = {
 | 
			
		||||
            {0, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
 | 
			
		||||
            {1, nullptr, "SetContext"},
 | 
			
		||||
            {2, nullptr, "DecodeInterleavedForMultiStream"},
 | 
			
		||||
            {3, nullptr, "SetContextForMultiStream"},
 | 
			
		||||
            {4, nullptr, "Unknown4"},
 | 
			
		||||
            {5, nullptr, "Unknown5"},
 | 
			
		||||
            {6, nullptr, "Unknown6"},
 | 
			
		||||
            {7, nullptr, "Unknown7"},
 | 
			
		||||
        };
 | 
			
		||||
        RegisterHandlers(functions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        u32 consumed = 0;
 | 
			
		||||
        u32 sample_count = 0;
 | 
			
		||||
        std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
 | 
			
		||||
        if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples)) {
 | 
			
		||||
            IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
            // TODO(ogniK): Use correct error code
 | 
			
		||||
            rb.Push(ResultCode(-1));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 4};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
        rb.Push<u32>(consumed);
 | 
			
		||||
        rb.Push<u32>(sample_count);
 | 
			
		||||
        ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input,
 | 
			
		||||
                                   std::vector<opus_int16>& output) {
 | 
			
		||||
        size_t raw_output_sz = output.size() * sizeof(opus_int16);
 | 
			
		||||
        if (sizeof(OpusHeader) > input.size())
 | 
			
		||||
            return false;
 | 
			
		||||
        OpusHeader hdr{};
 | 
			
		||||
        std::memcpy(&hdr, input.data(), sizeof(OpusHeader));
 | 
			
		||||
        if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        auto frame = input.data() + sizeof(OpusHeader);
 | 
			
		||||
        auto decoded_sample_count = opus_packet_get_nb_samples(
 | 
			
		||||
            frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)),
 | 
			
		||||
            static_cast<opus_int32>(sample_rate));
 | 
			
		||||
        if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz)
 | 
			
		||||
            return false;
 | 
			
		||||
        auto out_sample_count =
 | 
			
		||||
            opus_decode(decoder.get(), frame, hdr.sz, output.data(),
 | 
			
		||||
                        (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0);
 | 
			
		||||
        if (out_sample_count < 0)
 | 
			
		||||
            return false;
 | 
			
		||||
        sample_count = out_sample_count;
 | 
			
		||||
        consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct OpusHeader {
 | 
			
		||||
        u32_be sz; // Needs to be BE for some odd reason
 | 
			
		||||
        INSERT_PADDING_WORDS(1);
 | 
			
		||||
    };
 | 
			
		||||
    static_assert(sizeof(OpusHeader) == 0x8, "OpusHeader is an invalid size");
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<OpusDecoder, OpusDeleter> decoder;
 | 
			
		||||
    u32 sample_rate;
 | 
			
		||||
    u32 channel_count;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static size_t WorkerBufferSize(u32 channel_count) {
 | 
			
		||||
    ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
 | 
			
		||||
    return opus_decoder_get_size(static_cast<int>(channel_count));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    LOG_WARNING(Service_Audio, "(STUBBED) called");
 | 
			
		||||
    IPC::RequestParser rp{ctx};
 | 
			
		||||
    auto sample_rate = rp.Pop<u32>();
 | 
			
		||||
    auto channel_count = rp.Pop<u32>();
 | 
			
		||||
    ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
 | 
			
		||||
                   sample_rate == 12000 || sample_rate == 8000,
 | 
			
		||||
               "Invalid sample rate");
 | 
			
		||||
    ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
 | 
			
		||||
    u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count));
 | 
			
		||||
    LOG_DEBUG(Audio, "called worker_buffer_sz={}", worker_buffer_sz);
 | 
			
		||||
 | 
			
		||||
    IPC::ResponseBuilder rb{ctx, 3};
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    rb.Push<u32>(0x4000);
 | 
			
		||||
    rb.Push<u32>(worker_buffer_sz);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    IPC::RequestParser rp{ctx};
 | 
			
		||||
    auto sample_rate = rp.Pop<u32>();
 | 
			
		||||
    auto channel_count = rp.Pop<u32>();
 | 
			
		||||
    auto buffer_sz = rp.Pop<u32>();
 | 
			
		||||
    LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate,
 | 
			
		||||
              channel_count, buffer_sz);
 | 
			
		||||
    ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
 | 
			
		||||
                   sample_rate == 12000 || sample_rate == 8000,
 | 
			
		||||
               "Invalid sample rate");
 | 
			
		||||
    ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
 | 
			
		||||
 | 
			
		||||
    size_t worker_sz = WorkerBufferSize(channel_count);
 | 
			
		||||
    ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large");
 | 
			
		||||
    std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
 | 
			
		||||
        static_cast<OpusDecoder*>(operator new(worker_sz))};
 | 
			
		||||
    if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
        // TODO(ogniK): Use correct error code
 | 
			
		||||
        rb.Push(ResultCode(-1));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    rb.PushIpcInterface<IHardwareOpusDecoderManager>(std::move(decoder), sample_rate,
 | 
			
		||||
                                                     channel_count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HwOpus::HwOpus() : ServiceFramework("hwopus") {
 | 
			
		||||
    static const FunctionInfo functions[] = {
 | 
			
		||||
        {0, nullptr, "Initialize"},
 | 
			
		||||
        {0, &HwOpus::OpenOpusDecoder, "OpenOpusDecoder"},
 | 
			
		||||
        {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"},
 | 
			
		||||
        {2, nullptr, "InitializeMultiStream"},
 | 
			
		||||
        {3, nullptr, "GetWorkBufferSizeMultiStream"},
 | 
			
		||||
        {2, nullptr, "OpenOpusDecoderForMultiStream"},
 | 
			
		||||
        {3, nullptr, "GetWorkBufferSizeForMultiStream"},
 | 
			
		||||
    };
 | 
			
		||||
    RegisterHandlers(functions);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ public:
 | 
			
		||||
    ~HwOpus() = default;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void OpenOpusDecoder(Kernel::HLERequestContext& ctx);
 | 
			
		||||
    void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user