Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,11 @@ add_library(core STATIC
hle/service/audio/hardware_opus_decoder_manager.h
hle/service/audio/hardware_opus_decoder.cpp
hle/service/audio/hardware_opus_decoder.h
hle/service/audio/nx2_hardware_audio_codec_client.cpp
hle/service/audio/nx2_hardware_audio_codec_client.h
hle/service/audio/nx2_hardware_audio_codec_manager.cpp
hle/service/audio/nx2_hardware_audio_codec_manager.h
hle/service/audio/nx2_hardware_audio_codec_types.h
hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h
hle/service/bcat/bcat.cpp
Expand Down
8 changes: 8 additions & 0 deletions src/core/hle/service/audio/hardware_opus_decoder_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "core/hle/service/audio/hardware_opus_decoder.h"
#include "core/hle/service/audio/hardware_opus_decoder_manager.h"
#include "core/hle/service/audio/nx2_hardware_audio_codec_client.h"
#include "core/hle/service/cmif_serialization.h"

namespace Service::Audio {
Expand All @@ -26,13 +27,20 @@ IHardwareOpusDecoderManager::IHardwareOpusDecoderManager(Core::System& system_)
{7, D<&IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStreamEx>, "GetWorkBufferSizeForMultiStreamEx"},
{8, D<&IHardwareOpusDecoderManager::GetWorkBufferSizeExEx>, "GetWorkBufferSizeExEx"},
{9, D<&IHardwareOpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx>, "GetWorkBufferSizeForMultiStreamExEx"},
{100, D<&IHardwareOpusDecoderManager::OpenHardwareAudioCodecClient>, "OpenHardwareAudioCodecClient"},
};
// clang-format on
RegisterHandlers(functions);
}

IHardwareOpusDecoderManager::~IHardwareOpusDecoderManager() = default;

Result IHardwareOpusDecoderManager::OpenHardwareAudioCodecClient(
Out<SharedPointer<INx2HardwareAudioCodecClient>> out_client) {
*out_client = std::make_shared<INx2HardwareAudioCodecClient>(system);
R_SUCCEED();
}

Result IHardwareOpusDecoderManager::OpenHardwareOpusDecoder(
Out<SharedPointer<IHardwareOpusDecoder>> out_decoder, OpusParameters params, u32 tmem_size,
InCopyHandle<Kernel::KTransferMemory> tmem_handle) {
Expand Down
3 changes: 3 additions & 0 deletions src/core/hle/service/audio/hardware_opus_decoder_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
namespace Service::Audio {

class IHardwareOpusDecoder;
class INx2HardwareAudioCodecClient;

using AudioCore::OpusDecoder::OpusMultiStreamParameters;
using AudioCore::OpusDecoder::OpusMultiStreamParametersEx;
Expand All @@ -25,6 +26,8 @@ class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusD
~IHardwareOpusDecoderManager() override;

private:
Result OpenHardwareAudioCodecClient(
Out<SharedPointer<INx2HardwareAudioCodecClient>> out_client);
Result OpenHardwareOpusDecoder(Out<SharedPointer<IHardwareOpusDecoder>> out_decoder,
OpusParameters params, u32 tmem_size,
InCopyHandle<Kernel::KTransferMemory> tmem_handle);
Expand Down
109 changes: 109 additions & 0 deletions src/core/hle/service/audio/nx2_hardware_audio_codec_client.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "audio_core/adsp/apps/opus/opus_decode_object.h"
#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
#include "common/alignment.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/audio/errors.h"
#include "core/hle/service/audio/nx2_hardware_audio_codec_client.h"
#include "core/hle/service/audio/nx2_hardware_audio_codec_manager.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/memory.h"

namespace Service::Audio {

INx2HardwareAudioCodecClient::INx2HardwareAudioCodecClient(Core::System& system_)
: ServiceFramework{system_, "nx2:hwacod:client"} {
static const FunctionInfo functions[] = {
{0, D<&INx2HardwareAudioCodecClient::GetInstanceWorkBufferSize>,
"GetInstanceWorkBufferSize"},
{1, D<&INx2HardwareAudioCodecClient::GetManagerWorkBufferSize>, "GetManagerWorkBufferSize"},
{10, D<&INx2HardwareAudioCodecClient::OpenManager>, "OpenManager"},
};
RegisterHandlers(functions);
}

Result INx2HardwareAudioCodecClient::GetInstanceWorkBufferSize(
Out<u32> out_size,
InLargeData<Nx2HardwareAudioCodecParameter, BufferAttr_HipcPointer> parameter) {
using namespace AudioCore::ADSP::OpusDecoder;
const auto& p = *parameter;
R_UNLESS(p.sample_rate == 8000 || p.sample_rate == 12000 || p.sample_rate == 16000 ||
p.sample_rate == 24000 || p.sample_rate == 48000,
ResultInvalidOpusSampleRate);
u32 size{};
switch (p.codec) {
case 1:
R_UNLESS(p.channel_count == 1 || p.channel_count == 2, ResultInvalidOpusChannelCount);
size = OpusDecodeObject::GetWorkBufferSize(p.channel_count);
break;
case 2:
R_UNLESS(p.channel_count > 0 && p.channel_count <= 255 && p.stream_count > 0 &&
p.stream_count <= 255 && p.stereo_stream_count <= p.stream_count &&
p.stream_count + p.stereo_stream_count <= 255,
ResultInvalidOpusChannelCount);
size =
OpusMultiStreamDecodeObject::GetWorkBufferSize(p.stream_count, p.stereo_stream_count);
break;
default:
R_THROW(ResultNotSupported);
}
// Keep guest allocations independent of optional neural features in the host libopus.
*out_size = Common::AlignUp(size, 64);
LOG_DEBUG(Service_Audio, "hwacod codec={} rate={} channels={} instance size={:#x}", p.codec,
p.sample_rate, p.channel_count, *out_size);
R_SUCCEED();
}

Result INx2HardwareAudioCodecClient::GetManagerWorkBufferSize(Out<u32> out_size, u32 io_count) {
R_UNLESS(io_count > 0 && io_count <= 0xffff, ResultInvalidArgument);
// DSP state is host-owned; reserve one guest page. The SDK sizes the task rings separately.
*out_size = 0x1000;
R_SUCCEED();
}

Result INx2HardwareAudioCodecClient::OpenManager(
Out<u32> out_id, Out<SharedPointer<INx2HardwareAudioCodecManager>> out_manager,
OutCopyHandle<Kernel::KReadableEvent> out_event,
Nx2HardwareAudioCodecSharedMemory p, InCopyHandle<Kernel::KTransferMemory> transfer_memory,
InCopyHandle<Kernel::KProcess> process, u64 transfer_size, u64 shared_address, u64 shared_size,
u64 properties) {
R_UNLESS(transfer_memory && process && transfer_memory->GetOwner() == process.Get(),
ResultInvalidHandle);
R_UNLESS(p.version == 1 && p.instance_count > 0 && p.instance_count <= 0xffff &&
p.io_count > 0 && p.io_count <= 0xffff && next_manager_id <= 0xffff,
ResultInvalidArgument);
R_UNLESS(transfer_size <= transfer_memory->GetSize() &&
p.transfer_memory_size <= transfer_size && p.shared_memory_size <= shared_size,
ResultInsufficientBuffer);
const auto fits = [](u64 offset, u64 size, u64 total) {
return offset <= total && size <= total - offset;
};
const u64 io_size = u64{p.io_count} * (u64{p.input_capacity} + p.output_capacity);
const u64 ring_size = 0x80 + (u64{p.io_count} + 1) * 0x40;
R_UNLESS(p.input_capacity > 0 && p.input_capacity <= 1024 * 1024 && p.output_capacity > 0 &&
p.output_capacity <= 4 * 1024 * 1024 &&
fits(p.io_data_offset, p.io_data_size, p.shared_memory_size) &&
io_size <= p.io_data_size &&
fits(p.completion_ring_offset, p.completion_ring_size, p.shared_memory_size) &&
ring_size <= p.completion_ring_size,
ResultInvalidAddressInfo);
R_UNLESS(process->GetMemory().IsValidVirtualAddressRange(shared_address, shared_size),
ResultInvalidAddressInfo);
auto manager = std::make_shared<INx2HardwareAudioCodecManager>(system, process.Get(), p,
shared_address, next_manager_id);
*out_id = next_manager_id++;
*out_event = &manager->GetCompletionEvent();
*out_manager = std::move(manager);
LOG_INFO(Service_Audio,
"hwacod manager {} instances={} buffers={} input={:#x} output={:#x} "
"shared={:#x}+{:#x} ring={:#x} properties={:#x}",
*out_id, p.instance_count, p.io_count, p.input_capacity, p.output_capacity,
shared_address, shared_size, p.completion_ring_offset, properties);
R_SUCCEED();
}

} // namespace Service::Audio
40 changes: 40 additions & 0 deletions src/core/hle/service/audio/nx2_hardware_audio_codec_client.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "core/hle/service/audio/nx2_hardware_audio_codec_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Kernel {
class KProcess;
class KReadableEvent;
class KTransferMemory;
} // namespace Kernel

namespace Service::Audio {

class INx2HardwareAudioCodecManager;

class INx2HardwareAudioCodecClient final : public ServiceFramework<INx2HardwareAudioCodecClient> {
public:
explicit INx2HardwareAudioCodecClient(Core::System& system);

private:
Result GetInstanceWorkBufferSize(
Out<u32> out_size,
InLargeData<Nx2HardwareAudioCodecParameter, BufferAttr_HipcPointer> parameter);
Result GetManagerWorkBufferSize(Out<u32> out_size, u32 io_count);
Result OpenManager(Out<u32> out_id,
Out<SharedPointer<INx2HardwareAudioCodecManager>> out_manager,
OutCopyHandle<Kernel::KReadableEvent> out_event,
Nx2HardwareAudioCodecSharedMemory parameter,
InCopyHandle<Kernel::KTransferMemory> transfer_memory,
InCopyHandle<Kernel::KProcess> process, u64 transfer_size,
u64 shared_address, u64 shared_size, u64 properties);

u32 next_manager_id{1};
};

} // namespace Service::Audio
Loading