diff --git a/.gitmodules b/.gitmodules index bfd9c9a7f2..1320809b5f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -79,6 +79,9 @@ [submodule "sirit"] path = externals/sirit url = https://github.com/yuzu-emu/sirit +[submodule "faad2"] + path = externals/faad2/faad2 + url = https://github.com/knik0/faad2 [submodule "library-headers"] path = externals/library-headers url = https://github.com/citra-emu/ext-library-headers.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 418272108f..1344d06e5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,9 +81,6 @@ CMAKE_DEPENDENT_OPTION(ENABLE_LIBUSB "Enable libusb for GameCube Adapter support option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) -CMAKE_DEPENDENT_OPTION(ENABLE_MF "Use Media Foundation decoder (preferred over FFmpeg)" ON "WIN32" OFF) -CMAKE_DEPENDENT_OPTION(ENABLE_AUDIOTOOLBOX "Use AudioToolbox decoder (preferred over FFmpeg)" ON "APPLE" OFF) - CMAKE_DEPENDENT_OPTION(CITRA_ENABLE_BUNDLE_TARGET "Enable the distribution bundling target." ON "NOT ANDROID AND NOT IOS" OFF) # Compile options diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 0c38444881..802a6be513 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -156,24 +156,12 @@ endif() # Open Source Archives add_subdirectory(open_source_archives) +# faad2 +add_subdirectory(faad2 EXCLUDE_FROM_ALL) + # Dynamic library headers add_library(library-headers INTERFACE) -if (USE_SYSTEM_FDK_AAC_HEADERS) - find_path(SYSTEM_FDK_AAC_INCLUDES NAMES fdk-aac/aacdecoder_lib.h) - if (SYSTEM_FDK_AAC_INCLUDES STREQUAL "SYSTEM_FDK_AAC_INCLUDES-NOTFOUND") - message(WARNING "System fdk-aac headers not found. Falling back on bundled headers.") - else() - message(STATUS "Using system fdk_aac headers.") - target_include_directories(library-headers SYSTEM INTERFACE ${SYSTEM_FDK_AAC_INCLUDES}) - set(FOUND_FDK_AAC_HEADERS ON) - endif() -endif() -if (NOT FOUND_FDK_AAC_HEADERS) - message(STATUS "Using bundled fdk_aac headers.") - target_include_directories(library-headers SYSTEM INTERFACE ./library-headers/fdk-aac/include) -endif() - if (USE_SYSTEM_FFMPEG_HEADERS) find_path(SYSTEM_FFMPEG_INCLUDES NAMES libavutil/avutil.h) if (SYSTEM_FFMPEG_INCLUDES STREQUAL "SYSTEM_FFMPEG_INCLUDES-NOTFOUND") diff --git a/externals/cmake-modules/CitraHandleSystemLibs.cmake b/externals/cmake-modules/CitraHandleSystemLibs.cmake index c14b1831d2..777a5d7fa1 100644 --- a/externals/cmake-modules/CitraHandleSystemLibs.cmake +++ b/externals/cmake-modules/CitraHandleSystemLibs.cmake @@ -15,7 +15,6 @@ option(USE_SYSTEM_DYNARMIC "Use the system dynarmic (instead of the bundled one) option(USE_SYSTEM_FMT "Use the system fmt (instead of the bundled one)" OFF) option(USE_SYSTEM_XBYAK "Use the system xbyak (instead of the bundled one)" OFF) option(USE_SYSTEM_INIH "Use the system inih (instead of the bundled one)" OFF) -option(USE_SYSTEM_FDK_AAC_HEADERS "Use the system fdk-aac headers (instead of the bundled one)" OFF) option(USE_SYSTEM_FFMPEG_HEADERS "Use the system FFmpeg headers (instead of the bundled one)" OFF) option(USE_SYSTEM_GLSLANG "Use the system glslang and SPIR-V libraries (instead of the bundled ones)" OFF) option(USE_SYSTEM_ZSTD "Use the system Zstandard library (instead of the bundled one)" OFF) @@ -36,7 +35,6 @@ CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_DYNARMIC "Disable system Dynarmic" OFF "US CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_FMT "Disable system fmt" OFF "USE_SYSTEM_LIBS" OFF) CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_XBYAK "Disable system xbyak" OFF "USE_SYSTEM_LIBS" OFF) CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_INIH "Disable system inih" OFF "USE_SYSTEM_LIBS" OFF) -CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_FDK_AAC_HEADERS "Disable system fdk_aac" OFF "USE_SYSTEM_LIBS" OFF) CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_FFMPEG_HEADERS "Disable system ffmpeg" OFF "USE_SYSTEM_LIBS" OFF) CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_GLSLANG "Disable system glslang" OFF "USE_SYSTEM_LIBS" OFF) CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_ZSTD "Disable system Zstandard" OFF "USE_SYSTEM_LIBS" OFF) @@ -57,7 +55,6 @@ set(LIB_VAR_LIST FMT XBYAK INIH - FDK_AAC_HEADERS FFMPEG_HEADERS GLSLANG ZSTD diff --git a/externals/faad2/CMakeLists.txt b/externals/faad2/CMakeLists.txt new file mode 100644 index 0000000000..ac73f13d18 --- /dev/null +++ b/externals/faad2/CMakeLists.txt @@ -0,0 +1,102 @@ +# Copy source to build directory for some modifications. +set(FAAD2_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/faad2/libfaad") +if (NOT EXISTS "${FAAD2_SOURCE_DIR}") + file(COPY faad2/libfaad/ DESTINATION "${FAAD2_SOURCE_DIR}/") + + # These are fixed defines for some reason and not controllable with compile flags. + file(READ "${FAAD2_SOURCE_DIR}/common.h" FAAD2_COMMON_H) + # Disable SBR decoding since we don't want it for AAC-LC. + string(REGEX REPLACE "#define SBR_DEC" "" FAAD2_COMMON_H "${FAAD2_COMMON_H}") + # Disable PS decoding. This can cause mono to be upmixed to stereo, which we don't want. + string(REGEX REPLACE "#define PS_DEC" "" FAAD2_COMMON_H "${FAAD2_COMMON_H}") + file(WRITE "${FAAD2_SOURCE_DIR}/common.h" "${FAAD2_COMMON_H}") +endif() + +# Source list from faad2/libfaad/Makefile.am, cut down to just what we need for AAC-LC. +add_library(faad2 STATIC EXCLUDE_FROM_ALL + "${FAAD2_SOURCE_DIR}/bits.c" + "${FAAD2_SOURCE_DIR}/cfft.c" + "${FAAD2_SOURCE_DIR}/common.c" + "${FAAD2_SOURCE_DIR}/decoder.c" + "${FAAD2_SOURCE_DIR}/drc.c" + "${FAAD2_SOURCE_DIR}/error.c" + "${FAAD2_SOURCE_DIR}/filtbank.c" + "${FAAD2_SOURCE_DIR}/huffman.c" + "${FAAD2_SOURCE_DIR}/is.c" + "${FAAD2_SOURCE_DIR}/mdct.c" + "${FAAD2_SOURCE_DIR}/mp4.c" + "${FAAD2_SOURCE_DIR}/ms.c" + "${FAAD2_SOURCE_DIR}/output.c" + "${FAAD2_SOURCE_DIR}/pns.c" + "${FAAD2_SOURCE_DIR}/pulse.c" + "${FAAD2_SOURCE_DIR}/specrec.c" + "${FAAD2_SOURCE_DIR}/syntax.c" + "${FAAD2_SOURCE_DIR}/tns.c" +) +target_include_directories(faad2 PUBLIC faad2/include PRIVATE "${FAAD2_SOURCE_DIR}") + +# Configure compile definitions. + +# Read version from autoconf script for configuring constant. +file(READ faad2/configure.ac CONFIGURE_SCRIPT) +string(REGEX MATCH "AC_INIT\\(faad2, ([0-9.]+)\\)" _ ${CONFIGURE_SCRIPT}) +set(FAAD_VERSION ${CMAKE_MATCH_1}) +message(STATUS "Building faad2 version ${FAAD_VERSION}") + +# Check for functions and headers. +include(CheckFunctionExists) +include(CheckIncludeFiles) +check_function_exists(getpwuid HAVE_GETPWUID) +check_function_exists(lrintf HAVE_LRINTF) +check_function_exists(memcpy HAVE_MEMCPY) +check_function_exists(strchr HAVE_STRCHR) +check_function_exists(strsep HAVE_STRSEP) +check_include_files(dlfcn.h HAVE_DLFCN_H) +check_include_files(errno.h HAVE_ERRNO_H) +check_include_files(float.h HAVE_FLOAT_H) +check_include_files(inttypes.h HAVE_INTTYPES_H) +check_include_files(IOKit/IOKitLib.h HAVE_IOKIT_IOKITLIB_H) +check_include_files(limits.h HAVE_LIMITS_H) +check_include_files(mathf.h HAVE_MATHF_H) +check_include_files(stdint.h HAVE_STDINT_H) +check_include_files(stdio.h HAVE_STDIO_H) +check_include_files(stdlib.h HAVE_STDLIB_H) +check_include_files(strings.h HAVE_STRINGS_H) +check_include_files(string.h HAVE_STRING_H) +check_include_files(sysfs/libsysfs.h HAVE_SYSFS_LIBSYSFS_H) +check_include_files(sys/stat.h HAVE_SYS_STAT_H) +check_include_files(sys/time.h HAVE_SYS_TIME_H) +check_include_files(sys/types.h HAVE_SYS_TYPES_H) +check_include_files(unistd.h HAVE_UNISTD_H) + +# faad2 uses a relative include for its config.h which breaks under CMake. +# We can use target_compile_definitions to pass on the configuration instead. +target_compile_definitions(faad2 PRIVATE + -DFAAD_VERSION=${FAAD_VERSION} + -DPACKAGE_VERSION=\"${FAAD_VERSION}\" + -DSTDC_HEADERS + -DHAVE_GETPWUID=${HAVE_GETPWUID} + -DHAVE_LRINTF=${HAVE_LRINTF} + -DHAVE_MEMCPY=${HAVE_MEMCPY} + -DHAVE_STRCHR=${HAVE_STRCHR} + -DHAVE_STRSEP=${HAVE_STRSEP} + -DHAVE_DLFCN_H=${HAVE_DLFCN_H} + -DHAVE_ERRNO_H=${HAVE_ERRNO_H} + -DHAVE_FLOAT_H=${HAVE_FLOAT_H} + -DHAVE_INTTYPES_H=${HAVE_INTTYPES_H} + -DHAVE_IOKIT_IOKITLIB_H=${HAVE_IOKIT_IOKITLIB_H} + -DHAVE_LIMITS_H=${HAVE_LIMITS_H} + -DHAVE_MATHF_H=${HAVE_MATHF_H} + -DHAVE_STDINT_H=${HAVE_STDINT_H} + -DHAVE_STDIO_H=${HAVE_STDIO_H} + -DHAVE_STDLIB_H=${HAVE_STDLIB_H} + -DHAVE_STRINGS_H=${HAVE_STRINGS_H} + -DHAVE_STRING_H=${HAVE_STRING_H} + -DHAVE_SYSFS_LIBSYSFS_H=${HAVE_SYSFS_LIBSYSFS_H} + -DHAVE_SYS_STAT_H=${HAVE_SYS_STAT_H} + -DHAVE_SYS_TIME_H=${HAVE_SYS_TIME_H} + -DHAVE_SYS_TYPES_H=${HAVE_SYS_TYPES_H} + -DHAVE_UNISTD_H=${HAVE_UNISTD_H} + # Only compile for AAC-LC decoding. + -DLC_ONLY_DECODER +) diff --git a/externals/faad2/faad2 b/externals/faad2/faad2 new file mode 160000 index 0000000000..3918dee560 --- /dev/null +++ b/externals/faad2/faad2 @@ -0,0 +1 @@ +Subproject commit 3918dee56063500d0aa23d6c3c94b211ac471a8c diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 46967735d7..2146df6fb3 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -4,15 +4,11 @@ add_library(audio_core STATIC codec.h dsp_interface.cpp dsp_interface.h - hle/adts.h - hle/adts_reader.cpp hle/common.h hle/decoder.cpp hle/decoder.h - hle/fdk_decoder.cpp - hle/fdk_decoder.h - hle/ffmpeg_decoder.cpp - hle/ffmpeg_decoder.h + hle/faad2_decoder.cpp + hle/faad2_decoder.h hle/filter.cpp hle/filter.h hle/hle.cpp @@ -48,36 +44,7 @@ add_library(audio_core STATIC create_target_directory_groups(audio_core) target_link_libraries(audio_core PUBLIC citra_common citra_core) -target_link_libraries(audio_core PRIVATE SoundTouch teakra) - -if(ENABLE_MF) - target_sources(audio_core PRIVATE - hle/wmf_decoder.cpp - hle/wmf_decoder.h - hle/wmf_decoder_utils.cpp - hle/wmf_decoder_utils.h - ) - # We dynamically load the required symbols from mf.dll and mfplat.dll but mfuuid is not a dll - # just a static library of GUIDS so include that one directly. - target_link_libraries(audio_core PRIVATE mfuuid.lib) - target_compile_definitions(audio_core PUBLIC HAVE_MF) -elseif(ENABLE_AUDIOTOOLBOX) - target_sources(audio_core PRIVATE - hle/audiotoolbox_decoder.cpp - hle/audiotoolbox_decoder.h - ) - find_library(AUDIOTOOLBOX AudioToolbox) - target_link_libraries(audio_core PRIVATE ${AUDIOTOOLBOX}) - target_compile_definitions(audio_core PUBLIC HAVE_AUDIOTOOLBOX) -endif() - -if(ANDROID) - target_sources(audio_core PRIVATE - hle/mediandk_decoder.cpp - hle/mediandk_decoder.h - ) - target_link_libraries(audio_core PRIVATE mediandk) -endif() +target_link_libraries(audio_core PRIVATE faad2 SoundTouch teakra) if(ENABLE_SDL2) target_link_libraries(audio_core PRIVATE SDL2::SDL2) diff --git a/src/audio_core/hle/adts.h b/src/audio_core/hle/adts.h deleted file mode 100644 index 3729a81da0..0000000000 --- a/src/audio_core/hle/adts.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. -#pragma once - -#include "common/common_types.h" - -namespace AudioCore { - -struct ADTSData { - u8 header_length = 0; - bool mpeg2 = false; - u8 profile = 0; - u8 channels = 0; - u8 channel_idx = 0; - u8 framecount = 0; - u8 samplerate_idx = 0; - u32 length = 0; - u32 samplerate = 0; -}; - -ADTSData ParseADTS(const u8* buffer); - -// last two bytes of MF AAC decoder user data -// see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types -u16 MFGetAACTag(const ADTSData& input); - -} // namespace AudioCore diff --git a/src/audio_core/hle/adts_reader.cpp b/src/audio_core/hle/adts_reader.cpp deleted file mode 100644 index d3dc7942e5..0000000000 --- a/src/audio_core/hle/adts_reader.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. -#include -#include "adts.h" -#include "common/bit_field.h" - -namespace AudioCore { -constexpr std::array freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, - 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; -constexpr std::array channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; - -struct ADTSHeader { - union { - std::array raw{}; - BitFieldBE<52, 12, u64> sync_word; - BitFieldBE<51, 1, u64> mpeg2; - BitFieldBE<49, 2, u64> layer; - BitFieldBE<48, 1, u64> protection_absent; - BitFieldBE<46, 2, u64> profile; - BitFieldBE<42, 4, u64> samplerate_idx; - BitFieldBE<41, 1, u64> private_bit; - BitFieldBE<38, 3, u64> channel_idx; - BitFieldBE<37, 1, u64> originality; - BitFieldBE<36, 1, u64> home; - BitFieldBE<35, 1, u64> copyright_id; - BitFieldBE<34, 1, u64> copyright_id_start; - BitFieldBE<21, 13, u64> frame_length; - BitFieldBE<10, 11, u64> buffer_fullness; - BitFieldBE<8, 2, u64> frame_count; - }; -}; - -ADTSData ParseADTS(const u8* buffer) { - ADTSHeader header; - memcpy(header.raw.data(), buffer, sizeof(header.raw)); - - // sync word 0xfff - if (header.sync_word != 0xfff) { - return {}; - } - - ADTSData out{}; - // bit 16 = no CRC - out.header_length = header.protection_absent ? 7 : 9; - out.mpeg2 = static_cast(header.mpeg2); - // bit 17 to 18 - out.profile = static_cast(header.profile) + 1; - // bit 19 to 22 - out.samplerate_idx = static_cast(header.samplerate_idx); - out.samplerate = header.samplerate_idx > 15 ? 0 : freq_table[header.samplerate_idx]; - // bit 24 to 26 - out.channel_idx = static_cast(header.channel_idx); - out.channels = (header.channel_idx > 7) ? 0 : channel_table[header.channel_idx]; - // bit 55 to 56 - out.framecount = static_cast(header.frame_count + 1); - // bit 31 to 43 - out.length = static_cast(header.frame_length); - - return out; -} - -// last two bytes of MF AAC decoder user data -// Audio object type (5 bits) -// Sample rate profile (4 bits) -// Channel configuration profile (4 bits) -// Frame length flag (1 bit) -// Depends on core coder (1 bit) -// Extension flag (1 bit) -u16 MFGetAACTag(const ADTSData& input) { - u16 tag = 0; - - tag |= input.profile << 11; - tag |= input.samplerate_idx << 7; - tag |= input.channel_idx << 3; - - return tag; -} -} // namespace AudioCore diff --git a/src/audio_core/hle/audiotoolbox_decoder.cpp b/src/audio_core/hle/audiotoolbox_decoder.cpp deleted file mode 100644 index 122b4f21ab..0000000000 --- a/src/audio_core/hle/audiotoolbox_decoder.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2023 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include "audio_core/audio_types.h" -#include "audio_core/hle/adts.h" -#include "audio_core/hle/audiotoolbox_decoder.h" - -namespace AudioCore::HLE { - -static constexpr auto bytes_per_sample = sizeof(s16); -static constexpr auto aac_frames_per_packet = 1024; -static constexpr auto error_out_of_data = -1932; - -class AudioToolboxDecoder::Impl { -public: - explicit Impl(Memory::MemorySystem& memory); - ~Impl(); - std::optional ProcessRequest(const BinaryMessage& request); - -private: - std::optional Initalize(const BinaryMessage& request); - std::optional Decode(const BinaryMessage& request); - - void Clear(); - bool InitializeDecoder(AudioCore::ADTSData& adts_header); - - static OSStatus DataFunc(AudioConverterRef in_audio_converter, u32* io_number_data_packets, - AudioBufferList* io_data, - AudioStreamPacketDescription** out_data_packet_description, - void* in_user_data); - - Memory::MemorySystem& memory; - - AudioCore::ADTSData adts_config; - AudioStreamBasicDescription output_format = {}; - AudioConverterRef converter = nullptr; - - u8* curr_data = nullptr; - u32 curr_data_len = 0; - - AudioStreamPacketDescription packet_description; -}; - -AudioToolboxDecoder::Impl::Impl(Memory::MemorySystem& memory_) : memory(memory_) {} - -std::optional AudioToolboxDecoder::Impl::Initalize(const BinaryMessage& request) { - BinaryMessage response = request; - response.header.result = ResultStatus::Success; - - Clear(); - return response; -} - -AudioToolboxDecoder::Impl::~Impl() { - Clear(); -} - -void AudioToolboxDecoder::Impl::Clear() { - curr_data = nullptr; - curr_data_len = 0; - - adts_config = {}; - output_format = {}; - - if (converter) { - AudioConverterDispose(converter); - converter = nullptr; - } -} - -std::optional AudioToolboxDecoder::Impl::ProcessRequest( - const BinaryMessage& request) { - if (request.header.codec != DecoderCodec::DecodeAAC) { - LOG_ERROR(Audio_DSP, "AudioToolbox AAC Decoder cannot handle such codec: {}", - static_cast(request.header.codec)); - return {}; - } - - switch (request.header.cmd) { - case DecoderCommand::Init: { - return Initalize(request); - } - case DecoderCommand::EncodeDecode: { - return Decode(request); - } - case DecoderCommand::Shutdown: - case DecoderCommand::SaveState: - case DecoderCommand::LoadState: { - LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", - static_cast(request.header.cmd)); - BinaryMessage response = request; - response.header.result = ResultStatus::Success; - return response; - } - default: - LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", - static_cast(request.header.cmd)); - return {}; - } -} - -bool AudioToolboxDecoder::Impl::InitializeDecoder(AudioCore::ADTSData& adts_header) { - if (converter) { - if (adts_config.channels == adts_header.channels && - adts_config.samplerate == adts_header.samplerate) { - return true; - } else { - Clear(); - } - } - - AudioStreamBasicDescription input_format = { - .mSampleRate = static_cast(adts_header.samplerate), - .mFormatID = kAudioFormatMPEG4AAC, - .mFramesPerPacket = aac_frames_per_packet, - .mChannelsPerFrame = adts_header.channels, - }; - - u32 bytes_per_frame = input_format.mChannelsPerFrame * bytes_per_sample; - output_format = { - .mSampleRate = input_format.mSampleRate, - .mFormatID = kAudioFormatLinearPCM, - .mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked, - .mBytesPerPacket = bytes_per_frame, - .mFramesPerPacket = 1, - .mBytesPerFrame = bytes_per_frame, - .mChannelsPerFrame = input_format.mChannelsPerFrame, - .mBitsPerChannel = bytes_per_sample * 8, - }; - - auto status = AudioConverterNew(&input_format, &output_format, &converter); - if (status != noErr) { - LOG_ERROR(Audio_DSP, "Could not create AAC audio converter: {}", status); - Clear(); - return false; - } - - adts_config = adts_header; - return true; -} - -OSStatus AudioToolboxDecoder::Impl::DataFunc( - AudioConverterRef in_audio_converter, u32* io_number_data_packets, AudioBufferList* io_data, - AudioStreamPacketDescription** out_data_packet_description, void* in_user_data) { - auto impl = reinterpret_cast(in_user_data); - if (!impl || !impl->curr_data || impl->curr_data_len == 0) { - *io_number_data_packets = 0; - return error_out_of_data; - } - - io_data->mNumberBuffers = 1; - io_data->mBuffers[0].mNumberChannels = 0; - io_data->mBuffers[0].mDataByteSize = impl->curr_data_len; - io_data->mBuffers[0].mData = impl->curr_data; - *io_number_data_packets = 1; - - if (out_data_packet_description != nullptr) { - impl->packet_description.mStartOffset = 0; - impl->packet_description.mVariableFramesInPacket = 0; - impl->packet_description.mDataByteSize = impl->curr_data_len; - *out_data_packet_description = &impl->packet_description; - } - - impl->curr_data = nullptr; - impl->curr_data_len = 0; - - return noErr; -} - -std::optional AudioToolboxDecoder::Impl::Decode(const BinaryMessage& request) { - BinaryMessage response{}; - response.header.codec = request.header.codec; - response.header.cmd = request.header.cmd; - response.decode_aac_response.size = request.decode_aac_request.size; - - if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || - request.decode_aac_request.src_addr + request.decode_aac_request.size > - Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", - request.decode_aac_request.src_addr); - return {}; - } - - const auto data = - memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); - auto adts_header = AudioCore::ParseADTS(data); - curr_data = data + adts_header.header_length; - curr_data_len = request.decode_aac_request.size - adts_header.header_length; - - if (!InitializeDecoder(adts_header)) { - return std::nullopt; - } - - // Up to 2048 samples, up to 2 channels each - s16 decoder_output[4096]; - AudioBufferList out_buffer{1, - {{ - output_format.mChannelsPerFrame, - sizeof(decoder_output), - decoder_output, - }}}; - - u32 num_packets = sizeof(decoder_output) / output_format.mBytesPerPacket; - auto status = AudioConverterFillComplexBuffer(converter, DataFunc, this, &num_packets, - &out_buffer, nullptr); - if (status != noErr && status != error_out_of_data) { - LOG_ERROR(Audio_DSP, "Could not decode AAC data: {}", status); - Clear(); - return std::nullopt; - } - - // De-interleave samples. - std::array, 2> out_streams; - auto num_frames = num_packets * output_format.mFramesPerPacket; - for (u32 frame = 0; frame < num_frames; frame++) { - for (u32 ch = 0; ch < output_format.mChannelsPerFrame; ch++) { - out_streams[ch].push_back( - decoder_output[(frame * output_format.mChannelsPerFrame) + ch]); - } - } - - curr_data = nullptr; - curr_data_len = 0; - - response.decode_aac_response.sample_rate = - GetSampleRateEnum(static_cast(output_format.mSampleRate)); - response.decode_aac_response.num_channels = output_format.mChannelsPerFrame; - response.decode_aac_response.num_samples = num_frames; - - // transfer the decoded buffer from vector to the FCRAM - for (std::size_t ch = 0; ch < out_streams.size(); ch++) { - if (!out_streams[ch].empty()) { - auto byte_size = out_streams[ch].size() * bytes_per_sample; - auto dst = ch == 0 ? request.decode_aac_request.dst_addr_ch0 - : request.decode_aac_request.dst_addr_ch1; - if (dst < Memory::FCRAM_PADDR || - dst + byte_size > Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch{} {:08x}", ch, dst); - return {}; - } - std::memcpy(memory.GetFCRAMPointer(dst - Memory::FCRAM_PADDR), out_streams[ch].data(), - byte_size); - } - } - - return response; -} - -AudioToolboxDecoder::AudioToolboxDecoder(Memory::MemorySystem& memory) - : impl(std::make_unique(memory)) {} - -AudioToolboxDecoder::~AudioToolboxDecoder() = default; - -std::optional AudioToolboxDecoder::ProcessRequest(const BinaryMessage& request) { - return impl->ProcessRequest(request); -} - -bool AudioToolboxDecoder::IsValid() const { - return true; -} - -} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/faad2_decoder.cpp b/src/audio_core/hle/faad2_decoder.cpp new file mode 100644 index 0000000000..795c6b0325 --- /dev/null +++ b/src/audio_core/hle/faad2_decoder.cpp @@ -0,0 +1,186 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "audio_core/hle/faad2_decoder.h" + +namespace AudioCore::HLE { + +class FAAD2Decoder::Impl { +public: + explicit Impl(Memory::MemorySystem& memory); + ~Impl(); + std::optional ProcessRequest(const BinaryMessage& request); + bool IsValid() const { + return decoder != nullptr; + } + +private: + std::optional Initalize(const BinaryMessage& request); + + std::optional Decode(const BinaryMessage& request); + + Memory::MemorySystem& memory; + + NeAACDecHandle decoder = nullptr; +}; + +FAAD2Decoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { + decoder = NeAACDecOpen(); + if (decoder == nullptr) { + LOG_CRITICAL(Audio_DSP, "Could not open FAAD2 decoder."); + return; + } + + auto config = NeAACDecGetCurrentConfiguration(decoder); + config->defObjectType = LC; + config->outputFormat = FAAD_FMT_16BIT; + if (!NeAACDecSetConfiguration(decoder, config)) { + LOG_CRITICAL(Audio_DSP, "Could not configure FAAD2 decoder."); + NeAACDecClose(decoder); + decoder = nullptr; + return; + } + + LOG_INFO(Audio_DSP, "Created FAAD2 AAC decoder."); +} + +FAAD2Decoder::Impl::~Impl() { + if (decoder) { + NeAACDecClose(decoder); + decoder = nullptr; + + LOG_INFO(Audio_DSP, "Destroyed FAAD2 AAC decoder."); + } +} + +std::optional FAAD2Decoder::Impl::ProcessRequest(const BinaryMessage& request) { + if (request.header.codec != DecoderCodec::DecodeAAC) { + LOG_ERROR(Audio_DSP, "FAAD2 AAC Decoder cannot handle such codec: {}", + static_cast(request.header.codec)); + return {}; + } + + switch (request.header.cmd) { + case DecoderCommand::Init: { + return Initalize(request); + } + case DecoderCommand::EncodeDecode: { + return Decode(request); + } + case DecoderCommand::Shutdown: + case DecoderCommand::SaveState: + case DecoderCommand::LoadState: { + LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", + static_cast(request.header.cmd)); + BinaryMessage response = request; + response.header.result = ResultStatus::Success; + return response; + } + default: + LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", + static_cast(request.header.cmd)); + return {}; + } +} + +std::optional FAAD2Decoder::Impl::Initalize(const BinaryMessage& request) { + BinaryMessage response = request; + response.header.result = ResultStatus::Success; + return response; +} + +std::optional FAAD2Decoder::Impl::Decode(const BinaryMessage& request) { + BinaryMessage response{}; + response.header.codec = request.header.codec; + response.header.cmd = request.header.cmd; + response.decode_aac_response.size = request.decode_aac_request.size; + // This is a hack to continue games when a failure occurs. + response.decode_aac_response.sample_rate = DecoderSampleRate::Rate48000; + response.decode_aac_response.num_channels = 2; + response.decode_aac_response.num_samples = 1024; + + if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || + request.decode_aac_request.src_addr + request.decode_aac_request.size > + Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { + LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", + request.decode_aac_request.src_addr); + return response; + } + u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); + u32 data_len = request.decode_aac_request.size; + + unsigned long sample_rate; + u8 num_channels; + auto init_result = NeAACDecInit(decoder, data, data_len, &sample_rate, &num_channels); + if (init_result < 0) { + LOG_ERROR(Audio_DSP, "Could not initialize FAAD2 AAC decoder for request: {}", init_result); + return response; + } + + // Advance past the frame header if needed. + data += init_result; + data_len -= init_result; + + std::array, 2> out_streams; + + while (data_len > 0) { + NeAACDecFrameInfo frame_info; + auto curr_sample_buffer = + static_cast(NeAACDecDecode(decoder, &frame_info, data, data_len)); + if (curr_sample_buffer == nullptr || frame_info.error != 0) { + LOG_ERROR(Audio_DSP, "Failed to decode AAC buffer using FAAD2: {}", frame_info.error); + return response; + } + + // Split the decode result into channels. + u32 num_samples = frame_info.samples / frame_info.channels; + for (u32 sample = 0; sample < num_samples; sample++) { + for (u32 ch = 0; ch < frame_info.channels; ch++) { + out_streams[ch].push_back(curr_sample_buffer[(sample * frame_info.channels) + ch]); + } + } + + data += frame_info.bytesconsumed; + data_len -= frame_info.bytesconsumed; + } + + // Transfer the decoded buffer from vector to the FCRAM. + for (std::size_t ch = 0; ch < out_streams.size(); ch++) { + if (out_streams[ch].empty()) { + continue; + } + auto byte_size = out_streams[ch].size() * sizeof(s16); + auto dst = ch == 0 ? request.decode_aac_request.dst_addr_ch0 + : request.decode_aac_request.dst_addr_ch1; + if (dst < Memory::FCRAM_PADDR || + dst + byte_size > Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { + LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch{} {:08x}", ch, dst); + return response; + } + std::memcpy(memory.GetFCRAMPointer(dst - Memory::FCRAM_PADDR), out_streams[ch].data(), + byte_size); + } + + // Set the output frame info. + response.decode_aac_response.sample_rate = GetSampleRateEnum(sample_rate); + response.decode_aac_response.num_channels = num_channels; + response.decode_aac_response.num_samples = static_cast(out_streams[0].size()); + + return response; +} + +FAAD2Decoder::FAAD2Decoder(Memory::MemorySystem& memory) : impl(std::make_unique(memory)) {} + +FAAD2Decoder::~FAAD2Decoder() = default; + +std::optional FAAD2Decoder::ProcessRequest(const BinaryMessage& request) { + return impl->ProcessRequest(request); +} + +bool FAAD2Decoder::IsValid() const { + return impl->IsValid(); +} + +} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/audiotoolbox_decoder.h b/src/audio_core/hle/faad2_decoder.h similarity index 73% rename from src/audio_core/hle/audiotoolbox_decoder.h rename to src/audio_core/hle/faad2_decoder.h index dcfe486d19..d67473376f 100644 --- a/src/audio_core/hle/audiotoolbox_decoder.h +++ b/src/audio_core/hle/faad2_decoder.h @@ -8,10 +8,10 @@ namespace AudioCore::HLE { -class AudioToolboxDecoder final : public DecoderBase { +class FAAD2Decoder final : public DecoderBase { public: - explicit AudioToolboxDecoder(Memory::MemorySystem& memory); - ~AudioToolboxDecoder() override; + explicit FAAD2Decoder(Memory::MemorySystem& memory); + ~FAAD2Decoder() override; std::optional ProcessRequest(const BinaryMessage& request) override; bool IsValid() const override; diff --git a/src/audio_core/hle/fdk_decoder.cpp b/src/audio_core/hle/fdk_decoder.cpp deleted file mode 100644 index a29d659b70..0000000000 --- a/src/audio_core/hle/fdk_decoder.cpp +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "audio_core/hle/fdk_decoder.h" -#include "common/dynamic_library/fdk-aac.h" - -using namespace DynamicLibrary; - -namespace AudioCore::HLE { - -class FDKDecoder::Impl { -public: - explicit Impl(Memory::MemorySystem& memory); - ~Impl(); - std::optional ProcessRequest(const BinaryMessage& request); - bool IsValid() const { - return decoder != nullptr; - } - -private: - std::optional Initalize(const BinaryMessage& request); - - std::optional Decode(const BinaryMessage& request); - - void Clear(); - - Memory::MemorySystem& memory; - - HANDLE_AACDECODER decoder = nullptr; -}; - -FDKDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { - if (!FdkAac::LoadFdkAac()) { - return; - } - - // allocate an array of LIB_INFO structures - // if we don't pre-fill the whole segment with zeros, when we call `aacDecoder_GetLibInfo` - // it will segfault, upon investigation, there is some code in fdk_aac depends on your initial - // values in this array - LIB_INFO decoder_info[FDK_MODULE_LAST] = {}; - // get library information and fill the struct - if (FdkAac::aacDecoder_GetLibInfo(decoder_info) != 0) { - LOG_ERROR(Audio_DSP, "Failed to retrieve fdk_aac library information!"); - return; - } - - LOG_INFO(Audio_DSP, "Using fdk_aac version {} (build date: {})", decoder_info[0].versionStr, - decoder_info[0].build_date); - - // choose the input format when initializing: 1 layer of ADTS - decoder = FdkAac::aacDecoder_Open(TRANSPORT_TYPE::TT_MP4_ADTS, 1); - // set maximum output channel to two (stereo) - // if the input samples have more channels, fdk_aac will perform a downmix - AAC_DECODER_ERROR ret = FdkAac::aacDecoder_SetParam(decoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2); - if (ret != AAC_DEC_OK) { - // unable to set this parameter reflects the decoder implementation might be broken - // we'd better shuts down everything - FdkAac::aacDecoder_Close(decoder); - decoder = nullptr; - LOG_ERROR(Audio_DSP, "Unable to set downmix parameter: {}", ret); - return; - } -} - -std::optional FDKDecoder::Impl::Initalize(const BinaryMessage& request) { - BinaryMessage response = request; - response.header.result = ResultStatus::Success; - - if (decoder) { - LOG_INFO(Audio_DSP, "FDK Decoder initialized"); - Clear(); - } else { - LOG_ERROR(Audio_DSP, "Decoder not initialized"); - } - - return response; -} - -FDKDecoder::Impl::~Impl() { - if (decoder) { - FdkAac::aacDecoder_Close(decoder); - } -} - -void FDKDecoder::Impl::Clear() { - s16 decoder_output[8192]; - // flush and re-sync the decoder, discarding the internal buffer - // we actually don't care if this succeeds or not - // FLUSH - flush internal buffer - // INTR - treat the current internal buffer as discontinuous - // CONCEAL - try to interpolate and smooth out the samples - if (decoder) { - FdkAac::aacDecoder_DecodeFrame(decoder, decoder_output, 8192, - AACDEC_FLUSH & AACDEC_INTR & AACDEC_CONCEAL); - } -} - -std::optional FDKDecoder::Impl::ProcessRequest(const BinaryMessage& request) { - if (request.header.codec != DecoderCodec::DecodeAAC) { - LOG_ERROR(Audio_DSP, "FDK AAC Decoder cannot handle such codec: {}", - static_cast(request.header.codec)); - return {}; - } - - switch (request.header.cmd) { - case DecoderCommand::Init: { - return Initalize(request); - } - case DecoderCommand::EncodeDecode: { - return Decode(request); - } - case DecoderCommand::Shutdown: - case DecoderCommand::SaveState: - case DecoderCommand::LoadState: { - LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", - static_cast(request.header.cmd)); - BinaryMessage response = request; - response.header.result = ResultStatus::Success; - return response; - } - default: - LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", - static_cast(request.header.cmd)); - return {}; - } -} - -std::optional FDKDecoder::Impl::Decode(const BinaryMessage& request) { - BinaryMessage response{}; - response.header.codec = request.header.codec; - response.header.cmd = request.header.cmd; - response.decode_aac_response.size = request.decode_aac_request.size; - - if (!decoder) { - LOG_DEBUG(Audio_DSP, "Decoder not initalized"); - // This is a hack to continue games that are not compiled with the aac codec - response.decode_aac_response.num_channels = 2; - response.decode_aac_response.num_samples = 1024; - return response; - } - - if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || - request.decode_aac_request.src_addr + request.decode_aac_request.size > - Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", - request.decode_aac_request.src_addr); - return {}; - } - u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); - - std::array, 2> out_streams; - - u32 data_size = request.decode_aac_request.size; - - // decoding loops - AAC_DECODER_ERROR result = AAC_DEC_OK; - // Up to 2048 samples, up to 2 channels each - s16 decoder_output[4096]; - // note that we don't free this pointer as it is automatically freed by fdk_aac - CStreamInfo* stream_info; - // how many bytes to be queued into the decoder, decrementing from the buffer size - u32 buffer_remaining = data_size; - // alias the data_size as an u32 - u32 input_size = data_size; - - while (buffer_remaining) { - // queue the input buffer, fdk_aac will automatically slice out the buffer it needs - // from the input buffer - result = FdkAac::aacDecoder_Fill(decoder, &data, &input_size, &buffer_remaining); - if (result != AAC_DEC_OK) { - // there are some issues when queuing the input buffer - LOG_ERROR(Audio_DSP, "Failed to enqueue the input samples"); - return std::nullopt; - } - // get output from decoder - result = FdkAac::aacDecoder_DecodeFrame(decoder, decoder_output, - sizeof(decoder_output) / sizeof(s16), 0); - if (result == AAC_DEC_OK) { - // get the stream information - stream_info = FdkAac::aacDecoder_GetStreamInfo(decoder); - // fill the stream information for binary response - response.decode_aac_response.sample_rate = GetSampleRateEnum(stream_info->sampleRate); - response.decode_aac_response.num_channels = stream_info->numChannels; - response.decode_aac_response.num_samples = stream_info->frameSize; - // fill the output - // the sample size = frame_size * channel_counts - for (int sample = 0; sample < stream_info->frameSize; sample++) { - for (int ch = 0; ch < stream_info->numChannels; ch++) { - out_streams[ch].push_back( - decoder_output[(sample * stream_info->numChannels) + ch]); - } - } - } else if (result == AAC_DEC_TRANSPORT_SYNC_ERROR) { - // decoder has some synchronization problems, try again with new samples, - // using old samples might trigger this error again - continue; - } else { - LOG_ERROR(Audio_DSP, "Error decoding the sample: {}", result); - return std::nullopt; - } - } - - // transfer the decoded buffer from vector to the FCRAM - for (std::size_t ch = 0; ch < out_streams.size(); ch++) { - if (!out_streams[ch].empty()) { - auto byte_size = out_streams[ch].size() * sizeof(s16); - auto dst = ch == 0 ? request.decode_aac_request.dst_addr_ch0 - : request.decode_aac_request.dst_addr_ch1; - if (dst < Memory::FCRAM_PADDR || - dst + byte_size > Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch{} {:08x}", ch, dst); - return {}; - } - std::memcpy(memory.GetFCRAMPointer(dst - Memory::FCRAM_PADDR), out_streams[ch].data(), - byte_size); - } - } - - return response; -} - -FDKDecoder::FDKDecoder(Memory::MemorySystem& memory) : impl(std::make_unique(memory)) {} - -FDKDecoder::~FDKDecoder() = default; - -std::optional FDKDecoder::ProcessRequest(const BinaryMessage& request) { - return impl->ProcessRequest(request); -} - -bool FDKDecoder::IsValid() const { - return impl->IsValid(); -} - -} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/fdk_decoder.h b/src/audio_core/hle/fdk_decoder.h deleted file mode 100644 index 65d6e8cd3e..0000000000 --- a/src/audio_core/hle/fdk_decoder.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "audio_core/hle/decoder.h" - -namespace AudioCore::HLE { - -class FDKDecoder final : public DecoderBase { -public: - explicit FDKDecoder(Memory::MemorySystem& memory); - ~FDKDecoder() override; - std::optional ProcessRequest(const BinaryMessage& request) override; - bool IsValid() const override; - -private: - class Impl; - std::unique_ptr impl; -}; - -} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/ffmpeg_decoder.cpp b/src/audio_core/hle/ffmpeg_decoder.cpp deleted file mode 100644 index 58dbfb3e92..0000000000 --- a/src/audio_core/hle/ffmpeg_decoder.cpp +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "audio_core/hle/ffmpeg_decoder.h" -#include "common/dynamic_library/ffmpeg.h" - -using namespace DynamicLibrary; - -namespace AudioCore::HLE { - -class FFMPEGDecoder::Impl { -public: - explicit Impl(Memory::MemorySystem& memory); - ~Impl(); - std::optional ProcessRequest(const BinaryMessage& request); - bool IsValid() const { - return have_ffmpeg_dl; - } - -private: - std::optional Initalize(const BinaryMessage& request); - - void Clear(); - - std::optional Decode(const BinaryMessage& request); - - struct AVPacketDeleter { - void operator()(AVPacket* packet) const { - FFmpeg::av_packet_free(&packet); - } - }; - - struct AVCodecContextDeleter { - void operator()(AVCodecContext* context) const { - FFmpeg::avcodec_free_context(&context); - } - }; - - struct AVCodecParserContextDeleter { - void operator()(AVCodecParserContext* parser) const { - FFmpeg::av_parser_close(parser); - } - }; - - struct AVFrameDeleter { - void operator()(AVFrame* frame) const { - FFmpeg::av_frame_free(&frame); - } - }; - - bool initalized = false; - bool have_ffmpeg_dl; - - Memory::MemorySystem& memory; - - const AVCodec* codec; - std::unique_ptr av_context; - std::unique_ptr parser; - std::unique_ptr av_packet; - std::unique_ptr decoded_frame; -}; - -FFMPEGDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { - have_ffmpeg_dl = FFmpeg::LoadFFmpeg(); -} - -FFMPEGDecoder::Impl::~Impl() = default; - -std::optional FFMPEGDecoder::Impl::ProcessRequest(const BinaryMessage& request) { - if (request.header.codec != DecoderCodec::DecodeAAC) { - LOG_ERROR(Audio_DSP, "Got wrong codec {}", static_cast(request.header.codec)); - return {}; - } - - switch (request.header.cmd) { - case DecoderCommand::Init: { - return Initalize(request); - } - case DecoderCommand::EncodeDecode: { - return Decode(request); - } - case DecoderCommand::Shutdown: - case DecoderCommand::SaveState: - case DecoderCommand::LoadState: { - LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", - static_cast(request.header.cmd)); - BinaryMessage response = request; - response.header.result = ResultStatus::Success; - return response; - } - default: - LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", - static_cast(request.header.cmd)); - return {}; - } -} - -std::optional FFMPEGDecoder::Impl::Initalize(const BinaryMessage& request) { - if (initalized) { - Clear(); - } - - BinaryMessage response = request; - response.header.result = ResultStatus::Success; - - if (!have_ffmpeg_dl) { - return response; - } - - av_packet.reset(FFmpeg::av_packet_alloc()); - - codec = FFmpeg::avcodec_find_decoder(AV_CODEC_ID_AAC); - if (!codec) { - LOG_ERROR(Audio_DSP, "Codec not found\n"); - return response; - } - - parser.reset(FFmpeg::av_parser_init(codec->id)); - if (!parser) { - LOG_ERROR(Audio_DSP, "Parser not found\n"); - return response; - } - - av_context.reset(FFmpeg::avcodec_alloc_context3(codec)); - if (!av_context) { - LOG_ERROR(Audio_DSP, "Could not allocate audio codec context\n"); - return response; - } - - if (FFmpeg::avcodec_open2(av_context.get(), codec, nullptr) < 0) { - LOG_ERROR(Audio_DSP, "Could not open codec\n"); - return response; - } - - initalized = true; - return response; -} - -void FFMPEGDecoder::Impl::Clear() { - if (!have_ffmpeg_dl) { - return; - } - - av_context.reset(); - parser.reset(); - decoded_frame.reset(); - av_packet.reset(); -} - -std::optional FFMPEGDecoder::Impl::Decode(const BinaryMessage& request) { - BinaryMessage response{}; - response.header.codec = request.header.codec; - response.header.cmd = request.header.cmd; - response.decode_aac_response.size = request.decode_aac_request.size; - - if (!initalized) { - LOG_DEBUG(Audio_DSP, "Decoder not initalized"); - // This is a hack to continue games that are not compiled with the aac codec - response.decode_aac_response.num_channels = 2; - response.decode_aac_response.num_samples = 1024; - return response; - } - - if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || - request.decode_aac_request.src_addr + request.decode_aac_request.size > - Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", - request.decode_aac_request.src_addr); - return {}; - } - u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); - - std::array, 2> out_streams; - - std::size_t data_size = request.decode_aac_request.size; - while (data_size > 0) { - if (!decoded_frame) { - decoded_frame.reset(FFmpeg::av_frame_alloc()); - if (!decoded_frame) { - LOG_ERROR(Audio_DSP, "Could not allocate audio frame"); - return {}; - } - } - - int ret = FFmpeg::av_parser_parse2(parser.get(), av_context.get(), &av_packet->data, - &av_packet->size, data, static_cast(data_size), - AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); - if (ret < 0) { - LOG_ERROR(Audio_DSP, "Error while parsing"); - return {}; - } - data += ret; - data_size -= ret; - - ret = FFmpeg::avcodec_send_packet(av_context.get(), av_packet.get()); - if (ret < 0) { - LOG_ERROR(Audio_DSP, "Error submitting the packet to the decoder"); - return {}; - } - - if (av_packet->size) { - while (ret >= 0) { - ret = FFmpeg::avcodec_receive_frame(av_context.get(), decoded_frame.get()); - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) - break; - else if (ret < 0) { - LOG_ERROR(Audio_DSP, "Error during decoding"); - return {}; - } - int bytes_per_sample = FFmpeg::av_get_bytes_per_sample(av_context->sample_fmt); - if (bytes_per_sample < 0) { - LOG_ERROR(Audio_DSP, "Failed to calculate data size"); - return {}; - } - -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100) - auto num_channels = static_cast(decoded_frame->ch_layout.nb_channels); -#else - auto num_channels = static_cast(decoded_frame->channels); -#endif - - ASSERT(num_channels <= out_streams.size()); - - std::size_t size = bytes_per_sample * (decoded_frame->nb_samples); - - response.decode_aac_response.sample_rate = - GetSampleRateEnum(decoded_frame->sample_rate); - response.decode_aac_response.num_channels = num_channels; - response.decode_aac_response.num_samples += decoded_frame->nb_samples; - - // FFmpeg converts to 32 signed floating point PCM, we need s16 PCM so we need to - // convert it - f32 val_float; - for (std::size_t current_pos(0); current_pos < size;) { - for (std::size_t channel(0); channel < num_channels; channel++) { - std::memcpy(&val_float, decoded_frame->data[channel] + current_pos, - sizeof(val_float)); - val_float = std::clamp(val_float, -1.0f, 1.0f); - s16 val = static_cast(0x7FFF * val_float); - out_streams[channel].push_back(val & 0xFF); - out_streams[channel].push_back(val >> 8); - } - current_pos += sizeof(val_float); - } - } - } - } - - if (out_streams[0].size() != 0) { - if (request.decode_aac_request.dst_addr_ch0 < Memory::FCRAM_PADDR || - request.decode_aac_request.dst_addr_ch0 + out_streams[0].size() > - Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch0 {:08x}", - request.decode_aac_request.dst_addr_ch0); - return {}; - } - std::memcpy( - memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch0 - Memory::FCRAM_PADDR), - out_streams[0].data(), out_streams[0].size()); - } - - if (out_streams[1].size() != 0) { - if (request.decode_aac_request.dst_addr_ch1 < Memory::FCRAM_PADDR || - request.decode_aac_request.dst_addr_ch1 + out_streams[1].size() > - Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch1 {:08x}", - request.decode_aac_request.dst_addr_ch1); - return {}; - } - std::memcpy( - memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch1 - Memory::FCRAM_PADDR), - out_streams[1].data(), out_streams[1].size()); - } - return response; -} - -FFMPEGDecoder::FFMPEGDecoder(Memory::MemorySystem& memory) : impl(std::make_unique(memory)) {} - -FFMPEGDecoder::~FFMPEGDecoder() = default; - -std::optional FFMPEGDecoder::ProcessRequest(const BinaryMessage& request) { - return impl->ProcessRequest(request); -} - -bool FFMPEGDecoder::IsValid() const { - return impl->IsValid(); -} - -} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/ffmpeg_decoder.h b/src/audio_core/hle/ffmpeg_decoder.h deleted file mode 100644 index 6c356776aa..0000000000 --- a/src/audio_core/hle/ffmpeg_decoder.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "audio_core/hle/decoder.h" - -namespace AudioCore::HLE { - -class FFMPEGDecoder final : public DecoderBase { -public: - explicit FFMPEGDecoder(Memory::MemorySystem& memory); - ~FFMPEGDecoder() override; - std::optional ProcessRequest(const BinaryMessage& request) override; - bool IsValid() const override; - -private: - class Impl; - std::unique_ptr impl; -}; - -} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/hle.cpp b/src/audio_core/hle/hle.cpp index ecb2fb81c3..4a44da5ca3 100644 --- a/src/audio_core/hle/hle.cpp +++ b/src/audio_core/hle/hle.cpp @@ -8,23 +8,15 @@ #include #include #include "audio_core/audio_types.h" -#include "common/archives.h" -#ifdef HAVE_MF -#include "audio_core/hle/wmf_decoder.h" -#elif HAVE_AUDIOTOOLBOX -#include "audio_core/hle/audiotoolbox_decoder.h" -#elif ANDROID -#include "audio_core/hle/mediandk_decoder.h" -#endif #include "audio_core/hle/common.h" #include "audio_core/hle/decoder.h" -#include "audio_core/hle/fdk_decoder.h" -#include "audio_core/hle/ffmpeg_decoder.h" +#include "audio_core/hle/faad2_decoder.h" #include "audio_core/hle/hle.h" #include "audio_core/hle/mixers.h" #include "audio_core/hle/shared_memory.h" #include "audio_core/hle/source.h" #include "audio_core/sink.h" +#include "common/archives.h" #include "common/assert.h" #include "common/common_types.h" #include "common/hash.h" @@ -121,26 +113,8 @@ private: static std::vector(Memory::MemorySystem&)>> decoder_backends = { -#if defined(HAVE_MF) [](Memory::MemorySystem& memory) -> std::unique_ptr { - return std::make_unique(memory); - }, -#endif -#if defined(HAVE_AUDIOTOOLBOX) - [](Memory::MemorySystem& memory) -> std::unique_ptr { - return std::make_unique(memory); - }, -#endif -#if ANDROID - [](Memory::MemorySystem& memory) -> std::unique_ptr { - return std::make_unique(memory); - }, -#endif - [](Memory::MemorySystem& memory) -> std::unique_ptr { - return std::make_unique(memory); - }, - [](Memory::MemorySystem& memory) -> std::unique_ptr { - return std::make_unique(memory); + return std::make_unique(memory); }, }; diff --git a/src/audio_core/hle/mediandk_decoder.cpp b/src/audio_core/hle/mediandk_decoder.cpp deleted file mode 100644 index a6b39f7e57..0000000000 --- a/src/audio_core/hle/mediandk_decoder.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include - -#include -#include - -#include "audio_core/hle/adts.h" -#include "audio_core/hle/mediandk_decoder.h" - -namespace AudioCore::HLE { - -struct AMediaCodecRelease { - void operator()(AMediaCodec* codec) const { - AMediaCodec_stop(codec); - AMediaCodec_delete(codec); - }; -}; - -class MediaNDKDecoder::Impl { -public: - explicit Impl(Memory::MemorySystem& memory); - ~Impl(); - std::optional ProcessRequest(const BinaryMessage& request); - - bool SetMediaType(const AudioCore::ADTSData& adts_data); - -private: - std::optional Initalize(const BinaryMessage& request); - std::optional Decode(const BinaryMessage& request); - - Memory::MemorySystem& memory; - std::unique_ptr decoder; - // default: 2 channles, 48000 samplerate - AudioCore::ADTSData mADTSData{ - /*header_length*/ 7, /*mpeg2*/ false, /*profile*/ 2, - /*channels*/ 2, /*channel_idx*/ 2, /*framecount*/ 0, - /*samplerate_idx*/ 3, /*length*/ 0, /*samplerate*/ 48000}; -}; - -MediaNDKDecoder::Impl::Impl(Memory::MemorySystem& memory_) : memory(memory_) { - SetMediaType(mADTSData); -} - -MediaNDKDecoder::Impl::~Impl() = default; - -std::optional MediaNDKDecoder::Impl::Initalize(const BinaryMessage& request) { - BinaryMessage response = request; - response.header.result = ResultStatus::Success; - return response; -} - -bool MediaNDKDecoder::Impl::SetMediaType(const AudioCore::ADTSData& adts_data) { - const char* mime = "audio/mp4a-latm"; - if (decoder && mADTSData.profile == adts_data.profile && - mADTSData.channel_idx == adts_data.channel_idx && - mADTSData.samplerate_idx == adts_data.samplerate_idx) { - return true; - } - decoder.reset(AMediaCodec_createDecoderByType(mime)); - if (decoder == nullptr) { - return false; - } - - u8 csd_0[2]; - csd_0[0] = static_cast((adts_data.profile << 3) | (adts_data.samplerate_idx >> 1)); - csd_0[1] = - static_cast(((adts_data.samplerate_idx << 7) & 0x80) | (adts_data.channel_idx << 3)); - AMediaFormat* format = AMediaFormat_new(); - AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mime); - AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, adts_data.samplerate); - AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, adts_data.channels); - AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_IS_ADTS, 1); - AMediaFormat_setBuffer(format, "csd-0", csd_0, sizeof(csd_0)); - - media_status_t status = AMediaCodec_configure(decoder.get(), format, NULL, NULL, 0); - if (status != AMEDIA_OK) { - AMediaFormat_delete(format); - decoder.reset(); - return false; - } - - status = AMediaCodec_start(decoder.get()); - if (status != AMEDIA_OK) { - AMediaFormat_delete(format); - decoder.reset(); - return false; - } - - AMediaFormat_delete(format); - mADTSData = adts_data; - return true; -} - -std::optional MediaNDKDecoder::Impl::ProcessRequest(const BinaryMessage& request) { - if (request.header.codec != DecoderCodec::DecodeAAC) { - LOG_ERROR(Audio_DSP, "AAC Decoder cannot handle such codec: {}", - static_cast(request.header.codec)); - return {}; - } - - switch (request.header.cmd) { - case DecoderCommand::Init: { - return Initalize(request); - } - case DecoderCommand::EncodeDecode: { - return Decode(request); - } - case DecoderCommand::Shutdown: - case DecoderCommand::SaveState: - case DecoderCommand::LoadState: { - LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", - static_cast(request.header.cmd)); - BinaryMessage response = request; - response.header.result = ResultStatus::Success; - return response; - } - default: - LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", - static_cast(request.header.cmd)); - return {}; - } -} - -std::optional MediaNDKDecoder::Impl::Decode(const BinaryMessage& request) { - BinaryMessage response{}; - response.header.codec = request.header.codec; - response.header.cmd = request.header.cmd; - response.decode_aac_response.size = request.decode_aac_request.size; - response.decode_aac_response.num_samples = 1024; - - if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || - request.decode_aac_request.src_addr + request.decode_aac_request.size > - Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", - request.decode_aac_request.src_addr); - return response; - } - - const u8* data = - memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); - ADTSData adts_data = AudioCore::ParseADTS(data); - SetMediaType(adts_data); - response.decode_aac_response.sample_rate = GetSampleRateEnum(adts_data.samplerate); - response.decode_aac_response.num_channels = adts_data.channels; - if (!decoder) { - LOG_ERROR(Audio_DSP, "Missing decoder for profile: {}, channels: {}, samplerate: {}", - adts_data.profile, adts_data.channels, adts_data.samplerate); - return {}; - } - - // input - constexpr int timeout = 160; - std::size_t buffer_size = 0; - u8* buffer = nullptr; - ssize_t buffer_index = AMediaCodec_dequeueInputBuffer(decoder.get(), timeout); - if (buffer_index < 0) { - LOG_ERROR(Audio_DSP, "Failed to enqueue the input samples: {}", buffer_index); - return response; - } - buffer = AMediaCodec_getInputBuffer(decoder.get(), buffer_index, &buffer_size); - if (buffer_size < request.decode_aac_request.size) { - return response; - } - std::memcpy(buffer, data, request.decode_aac_request.size); - media_status_t status = AMediaCodec_queueInputBuffer(decoder.get(), buffer_index, 0, - request.decode_aac_request.size, 0, 0); - if (status != AMEDIA_OK) { - LOG_WARNING(Audio_DSP, "Try queue input buffer again later!"); - return response; - } - - // output - AMediaCodecBufferInfo info; - std::array, 2> out_streams; - buffer_index = AMediaCodec_dequeueOutputBuffer(decoder.get(), &info, timeout); - switch (buffer_index) { - case AMEDIACODEC_INFO_TRY_AGAIN_LATER: - LOG_WARNING(Audio_DSP, "Failed to dequeue output buffer: timeout!"); - break; - case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED: - LOG_WARNING(Audio_DSP, "Failed to dequeue output buffer: buffers changed!"); - break; - case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED: { - AMediaFormat* format = AMediaCodec_getOutputFormat(decoder.get()); - LOG_WARNING(Audio_DSP, "output format: {}", AMediaFormat_toString(format)); - AMediaFormat_delete(format); - buffer_index = AMediaCodec_dequeueOutputBuffer(decoder.get(), &info, timeout); - } - default: { - int offset = info.offset; - buffer = AMediaCodec_getOutputBuffer(decoder.get(), buffer_index, &buffer_size); - while (offset < info.size) { - for (int channel = 0; channel < response.decode_aac_response.num_channels; channel++) { - u16 pcm_data; - std::memcpy(&pcm_data, buffer + offset, sizeof(pcm_data)); - out_streams[channel].push_back(pcm_data); - offset += sizeof(pcm_data); - } - } - AMediaCodec_releaseOutputBuffer(decoder.get(), buffer_index, info.size != 0); - } - } - - // transfer the decoded buffer from vector to the FCRAM - size_t stream0_size = out_streams[0].size() * sizeof(u16); - if (stream0_size != 0) { - if (request.decode_aac_request.dst_addr_ch0 < Memory::FCRAM_PADDR || - request.decode_aac_request.dst_addr_ch0 + stream0_size > - Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch0 {:08x}", - request.decode_aac_request.dst_addr_ch0); - return response; - } - std::memcpy( - memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch0 - Memory::FCRAM_PADDR), - out_streams[0].data(), stream0_size); - } - - size_t stream1_size = out_streams[1].size() * sizeof(u16); - if (stream1_size != 0) { - if (request.decode_aac_request.dst_addr_ch1 < Memory::FCRAM_PADDR || - request.decode_aac_request.dst_addr_ch1 + stream1_size > - Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch1 {:08x}", - request.decode_aac_request.dst_addr_ch1); - return response; - } - std::memcpy( - memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch1 - Memory::FCRAM_PADDR), - out_streams[1].data(), stream1_size); - } - return response; -} - -MediaNDKDecoder::MediaNDKDecoder(Memory::MemorySystem& memory) - : impl(std::make_unique(memory)) {} - -MediaNDKDecoder::~MediaNDKDecoder() = default; - -std::optional MediaNDKDecoder::ProcessRequest(const BinaryMessage& request) { - return impl->ProcessRequest(request); -} - -bool MediaNDKDecoder::IsValid() const { - return true; -} - -} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/mediandk_decoder.h b/src/audio_core/hle/mediandk_decoder.h deleted file mode 100644 index 344c367a51..0000000000 --- a/src/audio_core/hle/mediandk_decoder.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. -#pragma once - -#include "audio_core/hle/decoder.h" - -namespace AudioCore::HLE { - -class MediaNDKDecoder final : public DecoderBase { -public: - explicit MediaNDKDecoder(Memory::MemorySystem& memory); - ~MediaNDKDecoder() override; - std::optional ProcessRequest(const BinaryMessage& request) override; - bool IsValid() const override; - -private: - class Impl; - std::unique_ptr impl; -}; - -} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/wmf_decoder.cpp b/src/audio_core/hle/wmf_decoder.cpp deleted file mode 100644 index 09043793b7..0000000000 --- a/src/audio_core/hle/wmf_decoder.cpp +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "audio_core/hle/wmf_decoder.h" -#include "audio_core/hle/wmf_decoder_utils.h" - -namespace AudioCore::HLE { - -using namespace MFDecoder; - -class WMFDecoder::Impl { -public: - explicit Impl(Memory::MemorySystem& memory); - ~Impl(); - std::optional ProcessRequest(const BinaryMessage& request); - bool IsValid() const { - return is_valid; - } - -private: - std::optional Initalize(const BinaryMessage& request); - - std::optional Decode(const BinaryMessage& request); - - MFOutputState DecodingLoop(AudioCore::ADTSData adts_header, - std::array, 2>& out_streams); - - bool transform_initialized = false; - bool format_selected = false; - - Memory::MemorySystem& memory; - - unique_mfptr transform; - DWORD in_stream_id = 0; - DWORD out_stream_id = 0; - bool is_valid = false; - bool mf_started = false; - bool coinited = false; -}; - -WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) { - // Attempt to load the symbols for mf.dll - if (!InitMFDLL()) { - LOG_CRITICAL(Audio_DSP, - "Unable to load mf.dll. AAC audio through media foundation unavailable"); - return; - } - - HRESULT hr = S_OK; - hr = CoInitialize(NULL); - // S_FALSE will be returned when COM has already been initialized - if (hr != S_OK && hr != S_FALSE) { - ReportError("Failed to start COM components", hr); - } else { - coinited = true; - } - - // lite startup is faster and all what we need is included - hr = MFDecoder::MFStartup(MF_VERSION, MFSTARTUP_LITE); - if (hr != S_OK) { - // Do you know you can't initialize MF in test mode or safe mode? - ReportError("Failed to initialize Media Foundation", hr); - } else { - mf_started = true; - } - - LOG_INFO(Audio_DSP, "Media Foundation activated"); - - // initialize transform - transform = MFDecoderInit(); - if (transform == nullptr) { - LOG_CRITICAL(Audio_DSP, "Can't initialize decoder"); - return; - } - - hr = transform->GetStreamIDs(1, &in_stream_id, 1, &out_stream_id); - if (hr == E_NOTIMPL) { - // if not implemented, it means this MFT does not assign stream ID for you - in_stream_id = 0; - out_stream_id = 0; - } else if (FAILED(hr)) { - ReportError("Decoder failed to initialize the stream ID", hr); - return; - } - transform_initialized = true; - is_valid = true; -} - -WMFDecoder::Impl::~Impl() { - if (transform_initialized) { - MFFlush(transform.get()); - // delete the transform object before shutting down MF - // otherwise access violation will occur - transform.reset(); - } - if (mf_started) { - MFDecoder::MFShutdown(); - } - if (coinited) { - CoUninitialize(); - } -} - -std::optional WMFDecoder::Impl::ProcessRequest(const BinaryMessage& request) { - if (request.header.codec != DecoderCodec::DecodeAAC) { - LOG_ERROR(Audio_DSP, "Got unknown codec {}", static_cast(request.header.codec)); - return std::nullopt; - } - - switch (request.header.cmd) { - case DecoderCommand::Init: { - LOG_INFO(Audio_DSP, "WMFDecoder initializing"); - return Initalize(request); - } - case DecoderCommand::EncodeDecode: { - return Decode(request); - } - case DecoderCommand::Shutdown: - case DecoderCommand::SaveState: - case DecoderCommand::LoadState: { - LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}", - static_cast(request.header.cmd)); - BinaryMessage response = request; - response.header.result = ResultStatus::Success; - return response; - } - default: - LOG_ERROR(Audio_DSP, "Got unknown binary request: {}", - static_cast(request.header.cmd)); - return std::nullopt; - } -} - -std::optional WMFDecoder::Impl::Initalize(const BinaryMessage& request) { - BinaryMessage response = request; - response.header.result = ResultStatus::Success; - - format_selected = false; // select format again if application request initialize the DSP - return response; -} - -MFOutputState WMFDecoder::Impl::DecodingLoop(AudioCore::ADTSData adts_header, - std::array, 2>& out_streams) { - std::optional> output_buffer; - - while (true) { - auto [output_status, output] = ReceiveSample(transform.get(), out_stream_id); - - // 0 -> okay; 3 -> okay but more data available (buffer too small) - if (output_status == MFOutputState::OK || output_status == MFOutputState::HaveMoreData) { - output_buffer = CopySampleToBuffer(output.get()); - - // the following was taken from ffmpeg version of the decoder - f32 val_f32; - for (std::size_t i = 0; i < output_buffer->size();) { - for (std::size_t channel = 0; channel < adts_header.channels; channel++) { - val_f32 = std::clamp(output_buffer->at(i), -1.0f, 1.0f); - s16 val = static_cast(0x7FFF * val_f32); - out_streams[channel].push_back(val & 0xFF); - out_streams[channel].push_back(val >> 8); - // i is incremented on per channel basis - i++; - } - } - } - - // If we return OK here, the decoder won't be in a state to receive new data and will fail - // on the next call; instead treat it like the HaveMoreData case - if (output_status == MFOutputState::OK) - continue; - - // for status = 2, reset MF - if (output_status == MFOutputState::NeedReconfig) { - format_selected = false; - return MFOutputState::NeedReconfig; - } - - // for status = 3, try again with new buffer - if (output_status == MFOutputState::HaveMoreData) - continue; - - // according to MS document, this is not an error (?!) - if (output_status == MFOutputState::NeedMoreInput) - return MFOutputState::NeedMoreInput; - - return MFOutputState::FatalError; // return on other status - } - - return MFOutputState::FatalError; -} - -std::optional WMFDecoder::Impl::Decode(const BinaryMessage& request) { - BinaryMessage response{}; - response.header.codec = request.header.codec; - response.header.cmd = request.header.cmd; - response.decode_aac_response.size = request.decode_aac_request.size; - response.decode_aac_response.num_channels = 2; - response.decode_aac_response.num_samples = 1024; - - if (!transform_initialized) { - LOG_DEBUG(Audio_DSP, "Decoder not initialized"); - // This is a hack to continue games when decoder failed to initialize - return response; - } - - if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR || - request.decode_aac_request.src_addr + request.decode_aac_request.size > - Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}", - request.decode_aac_request.src_addr); - return std::nullopt; - } - const u8* data = - memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); - - std::array, 2> out_streams; - unique_mfptr sample; - MFInputState input_status = MFInputState::OK; - MFOutputState output_status = MFOutputState::OK; - std::optional adts_meta = DetectMediaType(data, request.decode_aac_request.size); - - if (!adts_meta) { - LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream"); - return response; - } - - response.decode_aac_response.sample_rate = GetSampleRateEnum(adts_meta->ADTSHeader.samplerate); - response.decode_aac_response.num_channels = adts_meta->ADTSHeader.channels; - - if (!format_selected) { - LOG_DEBUG(Audio_DSP, "New ADTS stream: channels = {}, sample rate = {}", - adts_meta->ADTSHeader.channels, adts_meta->ADTSHeader.samplerate); - SelectInputMediaType(transform.get(), in_stream_id, adts_meta->ADTSHeader, - adts_meta->AACTag, 14); - SelectOutputMediaType(transform.get(), out_stream_id); - SendSample(transform.get(), in_stream_id, nullptr); - // cache the result from detect_mediatype and call select_*_mediatype only once - // This could increase performance very slightly - transform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0); - format_selected = true; - } - - sample = CreateSample(data, request.decode_aac_request.size, 1, 0); - sample->SetUINT32(MFSampleExtension_CleanPoint, 1); - - while (true) { - input_status = SendSample(transform.get(), in_stream_id, sample.get()); - output_status = DecodingLoop(adts_meta->ADTSHeader, out_streams); - - if (output_status == MFOutputState::FatalError) { - // if the decode issues are caused by MFT not accepting new samples, try again - // NOTICE: you are required to check the output even if you already knew/guessed - // MFT didn't accept the input sample - if (input_status == MFInputState::NotAccepted) { - // try again - continue; - } - - LOG_ERROR(Audio_DSP, "Errors occurred when receiving output"); - return response; - } else if (output_status == MFOutputState::NeedReconfig) { - // flush the transform - MFFlush(transform.get()); - // decode again - return this->Decode(request); - } - - break; // jump out of the loop if at least we don't have obvious issues - } - - if (out_streams[0].size() != 0) { - if (request.decode_aac_request.dst_addr_ch0 < Memory::FCRAM_PADDR || - request.decode_aac_request.dst_addr_ch0 + out_streams[0].size() > - Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch0 {:08x}", - request.decode_aac_request.dst_addr_ch0); - return std::nullopt; - } - std::memcpy( - memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch0 - Memory::FCRAM_PADDR), - out_streams[0].data(), out_streams[0].size()); - } - - if (out_streams[1].size() != 0) { - if (request.decode_aac_request.dst_addr_ch1 < Memory::FCRAM_PADDR || - request.decode_aac_request.dst_addr_ch1 + out_streams[1].size() > - Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { - LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch1 {:08x}", - request.decode_aac_request.dst_addr_ch1); - return std::nullopt; - } - std::memcpy( - memory.GetFCRAMPointer(request.decode_aac_request.dst_addr_ch1 - Memory::FCRAM_PADDR), - out_streams[1].data(), out_streams[1].size()); - } - - return response; -} - -WMFDecoder::WMFDecoder(Memory::MemorySystem& memory) : impl(std::make_unique(memory)) {} - -WMFDecoder::~WMFDecoder() = default; - -std::optional WMFDecoder::ProcessRequest(const BinaryMessage& request) { - return impl->ProcessRequest(request); -} - -bool WMFDecoder::IsValid() const { - return impl->IsValid(); -} - -} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/wmf_decoder.h b/src/audio_core/hle/wmf_decoder.h deleted file mode 100644 index 73b83acbff..0000000000 --- a/src/audio_core/hle/wmf_decoder.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "audio_core/hle/decoder.h" - -namespace AudioCore::HLE { - -class WMFDecoder final : public DecoderBase { -public: - explicit WMFDecoder(Memory::MemorySystem& memory); - ~WMFDecoder() override; - std::optional ProcessRequest(const BinaryMessage& request) override; - bool IsValid() const override; - -private: - class Impl; - std::unique_ptr impl; -}; - -} // namespace AudioCore::HLE diff --git a/src/audio_core/hle/wmf_decoder_utils.cpp b/src/audio_core/hle/wmf_decoder_utils.cpp deleted file mode 100644 index 6cdf73f690..0000000000 --- a/src/audio_core/hle/wmf_decoder_utils.cpp +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. -#include "common/logging/log.h" -#include "common/string_util.h" -#include "wmf_decoder_utils.h" - -namespace MFDecoder { - -// utility functions -void ReportError(std::string msg, HRESULT hr) { - if (SUCCEEDED(hr)) { - return; - } - LPWSTR err; - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, hr, - // hardcode to use en_US because if any user had problems with this - // we can help them w/o translating anything - // default is to use the language currently active on the operating system - MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPWSTR)&err, 0, nullptr); - if (err != nullptr) { - LOG_CRITICAL(Audio_DSP, "{}: {}", msg, Common::UTF16ToUTF8(err)); - LocalFree(err); - } - LOG_CRITICAL(Audio_DSP, "{}: {:08x}", msg, hr); -} - -unique_mfptr MFDecoderInit(GUID audio_format) { - - HRESULT hr = S_OK; - MFT_REGISTER_TYPE_INFO reg{}; - GUID category = MFT_CATEGORY_AUDIO_DECODER; - IMFActivate** activate; - unique_mfptr transform; - UINT32 num_activate; - - reg.guidMajorType = MFMediaType_Audio; - reg.guidSubtype = audio_format; - - hr = MFTEnumEx(category, - MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER, - ®, nullptr, &activate, &num_activate); - if (FAILED(hr) || num_activate < 1) { - ReportError("Failed to enumerate decoders", hr); - CoTaskMemFree(activate); - return nullptr; - } - LOG_INFO(Audio_DSP, "Windows(R) Media Foundation found {} suitable decoder(s)", num_activate); - for (unsigned int n = 0; n < num_activate; n++) { - hr = activate[n]->ActivateObject( - IID_IMFTransform, - reinterpret_cast(static_cast(Amp(transform)))); - if (FAILED(hr)) - transform = nullptr; - activate[n]->Release(); - if (SUCCEEDED(hr)) - break; - } - if (transform == nullptr) { - ReportError("Failed to initialize MFT", hr); - CoTaskMemFree(activate); - return nullptr; - } - CoTaskMemFree(activate); - return transform; -} - -unique_mfptr CreateSample(const void* data, DWORD len, DWORD alignment, - LONGLONG duration) { - HRESULT hr = S_OK; - unique_mfptr buf; - unique_mfptr sample; - - hr = MFCreateSample(Amp(sample)); - if (FAILED(hr)) { - ReportError("Unable to allocate a sample", hr); - return nullptr; - } - // Yes, the argument for alignment is the actual alignment - 1 - hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, Amp(buf)); - if (FAILED(hr)) { - ReportError("Unable to allocate a memory buffer for sample", hr); - return nullptr; - } - if (data) { - BYTE* buffer; - // lock the MediaBuffer - // this is actually not a thread-safe lock - hr = buf->Lock(&buffer, nullptr, nullptr); - if (FAILED(hr)) { - ReportError("Unable to lock down MediaBuffer", hr); - return nullptr; - } - - std::memcpy(buffer, data, len); - - buf->SetCurrentLength(len); - buf->Unlock(); - } - - sample->AddBuffer(buf.get()); - hr = sample->SetSampleDuration(duration); - if (FAILED(hr)) { - // MFT will take a guess for you in this case - ReportError("Unable to set sample duration, but continuing anyway", hr); - } - - return sample; -} - -bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, - const AudioCore::ADTSData& adts, const UINT8* user_data, - UINT32 user_data_len, GUID audio_format) { - HRESULT hr = S_OK; - unique_mfptr t; - - // actually you can get rid of the whole block of searching and filtering mess - // if you know the exact parameters of your media stream - hr = MFCreateMediaType(Amp(t)); - if (FAILED(hr)) { - ReportError("Unable to create an empty MediaType", hr); - return false; - } - - // basic definition - t->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); - t->SetGUID(MF_MT_SUBTYPE, audio_format); - - t->SetUINT32(MF_MT_AAC_PAYLOAD_TYPE, 1); - t->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, adts.channels); - t->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, adts.samplerate); - // 0xfe = 254 = "unspecified" - t->SetUINT32(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 254); - t->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1); - t->SetBlob(MF_MT_USER_DATA, user_data, user_data_len); - hr = transform->SetInputType(in_stream_id, t.get(), 0); - if (FAILED(hr)) { - ReportError("failed to select input types for MFT", hr); - return false; - } - - return true; -} - -bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format) { - HRESULT hr = S_OK; - UINT32 tmp; - unique_mfptr type; - - // If you know what you need and what you are doing, you can specify the conditions instead of - // searching but it's better to use search since MFT may or may not support your output - // parameters - for (DWORD i = 0;; i++) { - hr = transform->GetOutputAvailableType(out_stream_id, i, Amp(type)); - if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) { - return true; - } - if (FAILED(hr)) { - ReportError("failed to get output types for MFT", hr); - return false; - } - - hr = type->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &tmp); - - if (FAILED(hr)) - continue; - // select PCM-16 format - if (tmp == 32) { - hr = type->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, 1); - if (FAILED(hr)) { - ReportError("failed to set MF_MT_AUDIO_BLOCK_ALIGNMENT for MFT on output stream", - hr); - return false; - } - hr = transform->SetOutputType(out_stream_id, type.get(), 0); - if (FAILED(hr)) { - ReportError("failed to select output types for MFT", hr); - return false; - } - return true; - } else { - continue; - } - - return false; - } - - ReportError("MFT: Unable to find preferred output format", E_NOTIMPL); - return false; -} - -std::optional DetectMediaType(const u8* buffer, std::size_t len) { - if (len < 7) { - return std::nullopt; - } - - AudioCore::ADTSData tmp; - ADTSMeta result; - // see https://docs.microsoft.com/en-us/windows/desktop/api/mmreg/ns-mmreg-heaacwaveinfo_tag - // for the meaning of the byte array below - - // it might be a good idea to wrap the parameters into a struct - // and pass that struct into the function but doing that will lead to messier code - // const UINT8 aac_data[] = { 0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x11, 0x90 - // }; first byte: 0: raw aac 1: adts 2: adif 3: latm/laos - UINT8 aac_tmp[] = {0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x00, 0x00}; - uint16_t tag = 0; - - tmp = AudioCore::ParseADTS(buffer); - if (tmp.length == 0) { - return std::nullopt; - } - - tag = MFGetAACTag(tmp); - aac_tmp[12] |= (tag & 0xff00) >> 8; - aac_tmp[13] |= (tag & 0x00ff); - std::memcpy(&(result.ADTSHeader), &tmp, sizeof(AudioCore::ADTSData)); - std::memcpy(&(result.AACTag), aac_tmp, 14); - return result; -} - -void MFFlush(IMFTransform* transform) { - HRESULT hr = transform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0); - if (FAILED(hr)) { - ReportError("MFT: Flush command failed", hr); - } - hr = transform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0); - if (FAILED(hr)) { - ReportError("Failed to end streaming for MFT", hr); - } -} - -MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample) { - HRESULT hr = S_OK; - - if (in_sample) { - hr = transform->ProcessInput(in_stream_id, in_sample, 0); - if (hr == MF_E_NOTACCEPTING) { - return MFInputState::NotAccepted; // try again - } else if (FAILED(hr)) { - ReportError("MFT: Failed to process input", hr); - return MFInputState::FatalError; - } // FAILED(hr) - } else { - hr = transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0); - if (FAILED(hr)) { - ReportError("MFT: Failed to drain when processing input", hr); - } - } - - return MFInputState::OK; -} - -std::tuple> ReceiveSample(IMFTransform* transform, - DWORD out_stream_id) { - HRESULT hr; - MFT_OUTPUT_DATA_BUFFER out_buffers; - MFT_OUTPUT_STREAM_INFO out_info; - DWORD status = 0; - unique_mfptr sample; - bool mft_create_sample = false; - - hr = transform->GetOutputStreamInfo(out_stream_id, &out_info); - - if (FAILED(hr)) { - ReportError("MFT: Failed to get stream info", hr); - return std::make_tuple(MFOutputState::FatalError, std::move(sample)); - } - mft_create_sample = (out_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) || - (out_info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES); - - while (true) { - status = 0; - - if (!mft_create_sample) { - sample = CreateSample(nullptr, out_info.cbSize, out_info.cbAlignment); - if (!sample.get()) { - ReportError("MFT: Unable to allocate memory for samples", hr); - return std::make_tuple(MFOutputState::FatalError, std::move(sample)); - } - } - - out_buffers.dwStreamID = out_stream_id; - out_buffers.pSample = sample.get(); - - hr = transform->ProcessOutput(0, 1, &out_buffers, &status); - - if (!FAILED(hr)) { - break; - } - - if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { - // Most likely reasons: data corrupted; your actions not expected by MFT - return std::make_tuple(MFOutputState::NeedMoreInput, std::move(sample)); - } - - if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { - ReportError("MFT: stream format changed, re-configuration required", hr); - return std::make_tuple(MFOutputState::NeedReconfig, std::move(sample)); - } - - break; - } - - if (out_buffers.dwStatus & MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) { - // this status is also unreliable but whatever - return std::make_tuple(MFOutputState::HaveMoreData, std::move(sample)); - } - - if (out_buffers.pSample == nullptr) { - ReportError("MFT: decoding failure", hr); - return std::make_tuple(MFOutputState::FatalError, std::move(sample)); - } - - return std::make_tuple(MFOutputState::OK, std::move(sample)); -} - -std::optional> CopySampleToBuffer(IMFSample* sample) { - unique_mfptr buffer; - HRESULT hr = S_OK; - std::optional> output; - std::vector output_buffer; - BYTE* data; - DWORD len = 0; - - hr = sample->GetTotalLength(&len); - if (FAILED(hr)) { - ReportError("Failed to get the length of sample buffer", hr); - return std::nullopt; - } - - hr = sample->ConvertToContiguousBuffer(Amp(buffer)); - if (FAILED(hr)) { - ReportError("Failed to get sample buffer", hr); - return std::nullopt; - } - - hr = buffer->Lock(&data, nullptr, nullptr); - if (FAILED(hr)) { - ReportError("Failed to lock the buffer", hr); - return std::nullopt; - } - - output_buffer.resize(len / sizeof(f32)); - std::memcpy(output_buffer.data(), data, len); - output = output_buffer; - - // if buffer unlock fails, then... whatever, we have already got data - buffer->Unlock(); - return output; -} - -namespace { - -struct LibraryDeleter { - using pointer = HMODULE; - void operator()(HMODULE h) const { - if (h != nullptr) - FreeLibrary(h); - } -}; - -std::unique_ptr mf_dll{nullptr}; -std::unique_ptr mfplat_dll{nullptr}; - -} // namespace - -bool InitMFDLL() { - - mf_dll.reset(LoadLibrary(TEXT("mf.dll"))); - if (!mf_dll) { - DWORD error_message_id = GetLastError(); - LPSTR message_buffer = nullptr; - size_t size = - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, error_message_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&message_buffer), 0, nullptr); - - std::string message(message_buffer, size); - - LocalFree(message_buffer); - LOG_ERROR(Audio_DSP, "Could not load mf.dll: {}", message); - return false; - } - - mfplat_dll.reset(LoadLibrary(TEXT("mfplat.dll"))); - if (!mfplat_dll) { - DWORD error_message_id = GetLastError(); - LPSTR message_buffer = nullptr; - size_t size = - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, error_message_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&message_buffer), 0, nullptr); - - std::string message(message_buffer, size); - - LocalFree(message_buffer); - LOG_ERROR(Audio_DSP, "Could not load mfplat.dll: {}", message); - return false; - } - - MFStartup = Symbol(mfplat_dll.get(), "MFStartup"); - if (!MFStartup) { - LOG_ERROR(Audio_DSP, "Cannot load function MFStartup"); - return false; - } - - MFShutdown = Symbol(mfplat_dll.get(), "MFShutdown"); - if (!MFShutdown) { - LOG_ERROR(Audio_DSP, "Cannot load function MFShutdown"); - return false; - } - - MFShutdownObject = Symbol(mf_dll.get(), "MFShutdownObject"); - if (!MFShutdownObject) { - LOG_ERROR(Audio_DSP, "Cannot load function MFShutdownObject"); - return false; - } - - MFCreateAlignedMemoryBuffer = Symbol( - mfplat_dll.get(), "MFCreateAlignedMemoryBuffer"); - if (!MFCreateAlignedMemoryBuffer) { - LOG_ERROR(Audio_DSP, "Cannot load function MFCreateAlignedMemoryBuffer"); - return false; - } - - MFCreateSample = Symbol(mfplat_dll.get(), "MFCreateSample"); - if (!MFCreateSample) { - LOG_ERROR(Audio_DSP, "Cannot load function MFCreateSample"); - return false; - } - - MFTEnumEx = - Symbol(mfplat_dll.get(), "MFTEnumEx"); - if (!MFTEnumEx) { - LOG_ERROR(Audio_DSP, "Cannot load function MFTEnumEx"); - return false; - } - - MFCreateMediaType = Symbol(mfplat_dll.get(), "MFCreateMediaType"); - if (!MFCreateMediaType) { - LOG_ERROR(Audio_DSP, "Cannot load function MFCreateMediaType"); - return false; - } - - return true; -} - -Symbol MFStartup; -Symbol MFShutdown; -Symbol MFShutdownObject; -Symbol MFCreateAlignedMemoryBuffer; -Symbol MFCreateSample; -Symbol - MFTEnumEx; -Symbol MFCreateMediaType; - -} // namespace MFDecoder diff --git a/src/audio_core/hle/wmf_decoder_utils.h b/src/audio_core/hle/wmf_decoder_utils.h deleted file mode 100644 index e515528d0b..0000000000 --- a/src/audio_core/hle/wmf_decoder_utils.h +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "adts.h" - -namespace MFDecoder { - -template -struct Symbol { - Symbol() = default; - Symbol(HMODULE dll, const char* name) { - if (dll) { - ptr_symbol = reinterpret_cast(GetProcAddress(dll, name)); - } - } - - operator T*() const { - return ptr_symbol; - } - - explicit operator bool() const { - return ptr_symbol != nullptr; - } - - T* ptr_symbol = nullptr; -}; - -// Runtime load the MF symbols to prevent mf.dll not found errors on citra load -extern Symbol MFStartup; -extern Symbol MFShutdown; -extern Symbol MFShutdownObject; -extern Symbol MFCreateAlignedMemoryBuffer; -extern Symbol MFCreateSample; -extern Symbol - MFTEnumEx; -extern Symbol MFCreateMediaType; - -enum class MFOutputState { FatalError, OK, NeedMoreInput, NeedReconfig, HaveMoreData }; -enum class MFInputState { FatalError, OK, NotAccepted }; - -// utility functions / templates -template -struct MFRelease { - void operator()(T* pointer) const { - pointer->Release(); - }; -}; - -template <> -struct MFRelease { - void operator()(IMFTransform* pointer) const { - MFShutdownObject(pointer); - pointer->Release(); - }; -}; - -// wrapper facilities for dealing with pointers -template -using unique_mfptr = std::unique_ptr>; - -template -class AmpImpl { -public: - AmpImpl(SmartPtr& smart_ptr) : smart_ptr(smart_ptr) {} - ~AmpImpl() { - smart_ptr.reset(raw_ptr); - } - - operator RawPtr*() { - return &raw_ptr; - } - -private: - SmartPtr& smart_ptr; - RawPtr raw_ptr = nullptr; -}; - -template -auto Amp(SmartPtr& smart_ptr) { - return AmpImpl(smart_ptr); -} - -// convient function for formatting error messages -void ReportError(std::string msg, HRESULT hr); - -// data type for transferring ADTS metadata between functions -struct ADTSMeta { - AudioCore::ADTSData ADTSHeader; - u8 AACTag[14]; -}; - -// exported functions - -/// Loads the symbols from mf.dll at runtime. Returns false if the symbols can't be loaded -bool InitMFDLL(); -unique_mfptr MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); -unique_mfptr CreateSample(const void* data, DWORD len, DWORD alignment = 1, - LONGLONG duration = 0); -bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, - const AudioCore::ADTSData& adts, const UINT8* user_data, - UINT32 user_data_len, GUID audio_format = MFAudioFormat_AAC); -std::optional DetectMediaType(const u8* buffer, std::size_t len); -bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, - GUID audio_format = MFAudioFormat_PCM); -void MFFlush(IMFTransform* transform); -MFInputState SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample); -std::tuple> ReceiveSample(IMFTransform* transform, - DWORD out_stream_id); -std::optional> CopySampleToBuffer(IMFSample* sample); - -} // namespace MFDecoder diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 78c8f6c55d..b4abd328c6 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -76,8 +76,6 @@ add_library(citra_common STATIC construct.h dynamic_library/dynamic_library.cpp dynamic_library/dynamic_library.h - dynamic_library/fdk-aac.cpp - dynamic_library/fdk-aac.h dynamic_library/ffmpeg.cpp dynamic_library/ffmpeg.h error.cpp diff --git a/src/common/dynamic_library/fdk-aac.cpp b/src/common/dynamic_library/fdk-aac.cpp deleted file mode 100644 index 48f8ca2cc0..0000000000 --- a/src/common/dynamic_library/fdk-aac.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2023 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include - -#include "common/dynamic_library/dynamic_library.h" -#include "common/dynamic_library/fdk-aac.h" -#include "common/logging/log.h" - -namespace DynamicLibrary::FdkAac { - -aacDecoder_GetLibInfo_func aacDecoder_GetLibInfo; -aacDecoder_Open_func aacDecoder_Open; -aacDecoder_Close_func aacDecoder_Close; -aacDecoder_SetParam_func aacDecoder_SetParam; -aacDecoder_GetStreamInfo_func aacDecoder_GetStreamInfo; -aacDecoder_DecodeFrame_func aacDecoder_DecodeFrame; -aacDecoder_Fill_func aacDecoder_Fill; - -static std::unique_ptr fdk_aac; - -#define LOAD_SYMBOL(library, name) \ - any_failed = any_failed || (name = library->GetSymbol(#name)) == nullptr - -bool LoadFdkAac() { - if (fdk_aac) { - return true; - } - - fdk_aac = std::make_unique("fdk-aac", 2); - if (!fdk_aac->IsLoaded()) { - LOG_WARNING(Common, "Could not dynamically load libfdk-aac: {}", fdk_aac->GetLoadError()); - fdk_aac.reset(); - return false; - } - - auto any_failed = false; - LOAD_SYMBOL(fdk_aac, aacDecoder_GetLibInfo); - LOAD_SYMBOL(fdk_aac, aacDecoder_Open); - LOAD_SYMBOL(fdk_aac, aacDecoder_Close); - LOAD_SYMBOL(fdk_aac, aacDecoder_SetParam); - LOAD_SYMBOL(fdk_aac, aacDecoder_GetStreamInfo); - LOAD_SYMBOL(fdk_aac, aacDecoder_DecodeFrame); - LOAD_SYMBOL(fdk_aac, aacDecoder_Fill); - - if (any_failed) { - LOG_WARNING(Common, "Could not find all required functions in libfdk-aac."); - fdk_aac.reset(); - return false; - } - - LOG_INFO(Common, "Successfully loaded libfdk-aac."); - return true; -} - -} // namespace DynamicLibrary::FdkAac diff --git a/src/common/dynamic_library/fdk-aac.h b/src/common/dynamic_library/fdk-aac.h deleted file mode 100644 index a80e237eb9..0000000000 --- a/src/common/dynamic_library/fdk-aac.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2023 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -extern "C" { -#include -} - -namespace DynamicLibrary::FdkAac { - -typedef INT (*aacDecoder_GetLibInfo_func)(LIB_INFO* info); -typedef HANDLE_AACDECODER (*aacDecoder_Open_func)(TRANSPORT_TYPE transportFmt, UINT nrOfLayers); -typedef void (*aacDecoder_Close_func)(HANDLE_AACDECODER self); -typedef AAC_DECODER_ERROR (*aacDecoder_SetParam_func)(const HANDLE_AACDECODER self, - const AACDEC_PARAM param, const INT value); -typedef CStreamInfo* (*aacDecoder_GetStreamInfo_func)(HANDLE_AACDECODER self); -typedef AAC_DECODER_ERROR (*aacDecoder_DecodeFrame_func)(HANDLE_AACDECODER self, INT_PCM* pTimeData, - const INT timeDataSize, const UINT flags); -typedef AAC_DECODER_ERROR (*aacDecoder_Fill_func)(HANDLE_AACDECODER self, UCHAR* pBuffer[], - const UINT bufferSize[], UINT* bytesValid); - -extern aacDecoder_GetLibInfo_func aacDecoder_GetLibInfo; -extern aacDecoder_Open_func aacDecoder_Open; -extern aacDecoder_Close_func aacDecoder_Close; -extern aacDecoder_SetParam_func aacDecoder_SetParam; -extern aacDecoder_GetStreamInfo_func aacDecoder_GetStreamInfo; -extern aacDecoder_DecodeFrame_func aacDecoder_DecodeFrame; -extern aacDecoder_Fill_func aacDecoder_Fill; - -bool LoadFdkAac(); - -} // namespace DynamicLibrary::FdkAac diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 42adecd282..026d0cf23a 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -12,7 +12,6 @@ add_executable(tests core/memory/vm_manager.cpp precompiled_headers.h audio_core/hle/hle.cpp - audio_core/hle/adts_reader.cpp audio_core/lle/lle.cpp audio_core/audio_fixures.h audio_core/decoder_tests.cpp diff --git a/src/tests/audio_core/hle/adts_reader.cpp b/src/tests/audio_core/hle/adts_reader.cpp deleted file mode 100644 index 8971c8b4a4..0000000000 --- a/src/tests/audio_core/hle/adts_reader.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2023 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include - -#include "audio_core/hle/adts.h" - -namespace { -constexpr std::array freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, - 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; -constexpr std::array channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; - -AudioCore::ADTSData ParseADTS_Old(const unsigned char* buffer) { - u32 tmp = 0; - AudioCore::ADTSData out{}; - - // sync word 0xfff - tmp = (buffer[0] << 8) | (buffer[1] & 0xf0); - if ((tmp & 0xffff) != 0xfff0) { - out.length = 0; - return out; - } - // bit 16 = no CRC - out.header_length = (buffer[1] & 0x1) ? 7 : 9; - out.mpeg2 = (buffer[1] >> 3) & 0x1; - // bit 17 to 18 - out.profile = (buffer[2] >> 6) + 1; - // bit 19 to 22 - tmp = (buffer[2] >> 2) & 0xf; - out.samplerate_idx = tmp; - out.samplerate = (tmp > 15) ? 0 : freq_table[tmp]; - // bit 24 to 26 - tmp = ((buffer[2] & 0x1) << 2) | ((buffer[3] >> 6) & 0x3); - out.channel_idx = tmp; - out.channels = (tmp > 7) ? 0 : channel_table[tmp]; - - // bit 55 to 56 - out.framecount = (buffer[6] & 0x3) + 1; - - // bit 31 to 43 - tmp = (buffer[3] & 0x3) << 11; - tmp |= (buffer[4] << 3) & 0x7f8; - tmp |= (buffer[5] >> 5) & 0x7; - - out.length = tmp; - - return out; -} -} // namespace - -TEST_CASE("ParseADTS fuzz", "[audio_core][hle]") { - for (u32 i = 0; i < 0x10000; i++) { - std::array adts_header; - std::string adts_header_string = "ADTS Header: "; - for (auto& it : adts_header) { - it = static_cast(rand()); - adts_header_string.append(fmt::format("{:2X} ", it)); - } - INFO(adts_header_string); - - AudioCore::ADTSData out_old_impl = - ParseADTS_Old(reinterpret_cast(adts_header.data())); - AudioCore::ADTSData out = AudioCore::ParseADTS(adts_header.data()); - - REQUIRE(out_old_impl.length == out.length); - REQUIRE(out_old_impl.channels == out.channels); - REQUIRE(out_old_impl.channel_idx == out.channel_idx); - REQUIRE(out_old_impl.framecount == out.framecount); - REQUIRE(out_old_impl.header_length == out.header_length); - REQUIRE(out_old_impl.mpeg2 == out.mpeg2); - REQUIRE(out_old_impl.profile == out.profile); - REQUIRE(out_old_impl.samplerate == out.samplerate); - REQUIRE(out_old_impl.samplerate_idx == out.samplerate_idx); - } -}