audio_core: Replace AAC decoders with single FAAD2-based decoder. (#7098)
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								externals/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								externals/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							@@ -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")
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										102
									
								
								externals/faad2/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								externals/faad2/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										1
									
								
								externals/faad2/faad2
									
									
									
									
										vendored
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								externals/faad2/faad2
									
									
									
									
										vendored
									
									
										Submodule
									
								
							 Submodule externals/faad2/faad2 added at 3918dee560
									
								
							@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -1,79 +0,0 @@
 | 
			
		||||
// Copyright 2019 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
#include <array>
 | 
			
		||||
#include "adts.h"
 | 
			
		||||
#include "common/bit_field.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
constexpr std::array<u32, 16> freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
 | 
			
		||||
                                            16000, 12000, 11025, 8000,  7350,  0,     0,     0};
 | 
			
		||||
constexpr std::array<u8, 8> channel_table = {0, 1, 2, 3, 4, 5, 6, 8};
 | 
			
		||||
 | 
			
		||||
struct ADTSHeader {
 | 
			
		||||
    union {
 | 
			
		||||
        std::array<u8, 7> 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<bool>(header.mpeg2);
 | 
			
		||||
    // bit 17 to 18
 | 
			
		||||
    out.profile = static_cast<u8>(header.profile) + 1;
 | 
			
		||||
    // bit 19 to 22
 | 
			
		||||
    out.samplerate_idx = static_cast<u8>(header.samplerate_idx);
 | 
			
		||||
    out.samplerate = header.samplerate_idx > 15 ? 0 : freq_table[header.samplerate_idx];
 | 
			
		||||
    // bit 24 to 26
 | 
			
		||||
    out.channel_idx = static_cast<u8>(header.channel_idx);
 | 
			
		||||
    out.channels = (header.channel_idx > 7) ? 0 : channel_table[header.channel_idx];
 | 
			
		||||
    // bit 55 to 56
 | 
			
		||||
    out.framecount = static_cast<u8>(header.frame_count + 1);
 | 
			
		||||
    // bit 31 to 43
 | 
			
		||||
    out.length = static_cast<u32>(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
 | 
			
		||||
@@ -1,264 +0,0 @@
 | 
			
		||||
// Copyright 2023 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <AudioToolbox/AudioToolbox.h>
 | 
			
		||||
#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<BinaryMessage> ProcessRequest(const BinaryMessage& request);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::optional<BinaryMessage> Initalize(const BinaryMessage& request);
 | 
			
		||||
    std::optional<BinaryMessage> 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<BinaryMessage> 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<BinaryMessage> 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<u16>(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<u16>(request.header.cmd));
 | 
			
		||||
        BinaryMessage response = request;
 | 
			
		||||
        response.header.result = ResultStatus::Success;
 | 
			
		||||
        return response;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Got unknown binary request: {}",
 | 
			
		||||
                  static_cast<u16>(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<Float64>(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<Impl*>(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<BinaryMessage> 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<std::vector<s16>, 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<u32>(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<Impl>(memory)) {}
 | 
			
		||||
 | 
			
		||||
AudioToolboxDecoder::~AudioToolboxDecoder() = default;
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> AudioToolboxDecoder::ProcessRequest(const BinaryMessage& request) {
 | 
			
		||||
    return impl->ProcessRequest(request);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioToolboxDecoder::IsValid() const {
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore::HLE
 | 
			
		||||
							
								
								
									
										186
									
								
								src/audio_core/hle/faad2_decoder.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								src/audio_core/hle/faad2_decoder.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,186 @@
 | 
			
		||||
// Copyright 2023 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <neaacdec.h>
 | 
			
		||||
#include "audio_core/hle/faad2_decoder.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore::HLE {
 | 
			
		||||
 | 
			
		||||
class FAAD2Decoder::Impl {
 | 
			
		||||
public:
 | 
			
		||||
    explicit Impl(Memory::MemorySystem& memory);
 | 
			
		||||
    ~Impl();
 | 
			
		||||
    std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request);
 | 
			
		||||
    bool IsValid() const {
 | 
			
		||||
        return decoder != nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::optional<BinaryMessage> Initalize(const BinaryMessage& request);
 | 
			
		||||
 | 
			
		||||
    std::optional<BinaryMessage> 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<BinaryMessage> 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<u16>(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<u16>(request.header.cmd));
 | 
			
		||||
        BinaryMessage response = request;
 | 
			
		||||
        response.header.result = ResultStatus::Success;
 | 
			
		||||
        return response;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Got unknown binary request: {}",
 | 
			
		||||
                  static_cast<u16>(request.header.cmd));
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> FAAD2Decoder::Impl::Initalize(const BinaryMessage& request) {
 | 
			
		||||
    BinaryMessage response = request;
 | 
			
		||||
    response.header.result = ResultStatus::Success;
 | 
			
		||||
    return response;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> 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<std::vector<s16>, 2> out_streams;
 | 
			
		||||
 | 
			
		||||
    while (data_len > 0) {
 | 
			
		||||
        NeAACDecFrameInfo frame_info;
 | 
			
		||||
        auto curr_sample_buffer =
 | 
			
		||||
            static_cast<s16*>(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<u32_le>(out_streams[0].size());
 | 
			
		||||
 | 
			
		||||
    return response;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FAAD2Decoder::FAAD2Decoder(Memory::MemorySystem& memory) : impl(std::make_unique<Impl>(memory)) {}
 | 
			
		||||
 | 
			
		||||
FAAD2Decoder::~FAAD2Decoder() = default;
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> FAAD2Decoder::ProcessRequest(const BinaryMessage& request) {
 | 
			
		||||
    return impl->ProcessRequest(request);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FAAD2Decoder::IsValid() const {
 | 
			
		||||
    return impl->IsValid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore::HLE
 | 
			
		||||
@@ -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<BinaryMessage> ProcessRequest(const BinaryMessage& request) override;
 | 
			
		||||
    bool IsValid() const override;
 | 
			
		||||
 | 
			
		||||
@@ -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<BinaryMessage> ProcessRequest(const BinaryMessage& request);
 | 
			
		||||
    bool IsValid() const {
 | 
			
		||||
        return decoder != nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::optional<BinaryMessage> Initalize(const BinaryMessage& request);
 | 
			
		||||
 | 
			
		||||
    std::optional<BinaryMessage> 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<BinaryMessage> 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<BinaryMessage> 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<u16>(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<u16>(request.header.cmd));
 | 
			
		||||
        BinaryMessage response = request;
 | 
			
		||||
        response.header.result = ResultStatus::Success;
 | 
			
		||||
        return response;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Got unknown binary request: {}",
 | 
			
		||||
                  static_cast<u16>(request.header.cmd));
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> 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<std::vector<s16>, 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<Impl>(memory)) {}
 | 
			
		||||
 | 
			
		||||
FDKDecoder::~FDKDecoder() = default;
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> FDKDecoder::ProcessRequest(const BinaryMessage& request) {
 | 
			
		||||
    return impl->ProcessRequest(request);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FDKDecoder::IsValid() const {
 | 
			
		||||
    return impl->IsValid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore::HLE
 | 
			
		||||
@@ -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<BinaryMessage> ProcessRequest(const BinaryMessage& request) override;
 | 
			
		||||
    bool IsValid() const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    class Impl;
 | 
			
		||||
    std::unique_ptr<Impl> impl;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore::HLE
 | 
			
		||||
@@ -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<BinaryMessage> ProcessRequest(const BinaryMessage& request);
 | 
			
		||||
    bool IsValid() const {
 | 
			
		||||
        return have_ffmpeg_dl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::optional<BinaryMessage> Initalize(const BinaryMessage& request);
 | 
			
		||||
 | 
			
		||||
    void Clear();
 | 
			
		||||
 | 
			
		||||
    std::optional<BinaryMessage> 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<AVCodecContext, AVCodecContextDeleter> av_context;
 | 
			
		||||
    std::unique_ptr<AVCodecParserContext, AVCodecParserContextDeleter> parser;
 | 
			
		||||
    std::unique_ptr<AVPacket, AVPacketDeleter> av_packet;
 | 
			
		||||
    std::unique_ptr<AVFrame, AVFrameDeleter> decoded_frame;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
FFMPEGDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) {
 | 
			
		||||
    have_ffmpeg_dl = FFmpeg::LoadFFmpeg();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FFMPEGDecoder::Impl::~Impl() = default;
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> FFMPEGDecoder::Impl::ProcessRequest(const BinaryMessage& request) {
 | 
			
		||||
    if (request.header.codec != DecoderCodec::DecodeAAC) {
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Got wrong codec {}", static_cast<u16>(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<u16>(request.header.cmd));
 | 
			
		||||
        BinaryMessage response = request;
 | 
			
		||||
        response.header.result = ResultStatus::Success;
 | 
			
		||||
        return response;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Got unknown binary request: {}",
 | 
			
		||||
                  static_cast<u16>(request.header.cmd));
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> 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<BinaryMessage> 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<std::vector<u8>, 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<int>(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<u32>(decoded_frame->ch_layout.nb_channels);
 | 
			
		||||
#else
 | 
			
		||||
                auto num_channels = static_cast<u32>(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<s16>(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<Impl>(memory)) {}
 | 
			
		||||
 | 
			
		||||
FFMPEGDecoder::~FFMPEGDecoder() = default;
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> FFMPEGDecoder::ProcessRequest(const BinaryMessage& request) {
 | 
			
		||||
    return impl->ProcessRequest(request);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FFMPEGDecoder::IsValid() const {
 | 
			
		||||
    return impl->IsValid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore::HLE
 | 
			
		||||
@@ -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<BinaryMessage> ProcessRequest(const BinaryMessage& request) override;
 | 
			
		||||
    bool IsValid() const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    class Impl;
 | 
			
		||||
    std::unique_ptr<Impl> impl;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore::HLE
 | 
			
		||||
@@ -8,23 +8,15 @@
 | 
			
		||||
#include <boost/serialization/vector.hpp>
 | 
			
		||||
#include <boost/serialization/weak_ptr.hpp>
 | 
			
		||||
#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<std::function<std::unique_ptr<HLE::DecoderBase>(Memory::MemorySystem&)>>
 | 
			
		||||
    decoder_backends = {
 | 
			
		||||
#if defined(HAVE_MF)
 | 
			
		||||
        [](Memory::MemorySystem& memory) -> std::unique_ptr<HLE::DecoderBase> {
 | 
			
		||||
            return std::make_unique<HLE::WMFDecoder>(memory);
 | 
			
		||||
        },
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(HAVE_AUDIOTOOLBOX)
 | 
			
		||||
        [](Memory::MemorySystem& memory) -> std::unique_ptr<HLE::DecoderBase> {
 | 
			
		||||
            return std::make_unique<HLE::AudioToolboxDecoder>(memory);
 | 
			
		||||
        },
 | 
			
		||||
#endif
 | 
			
		||||
#if ANDROID
 | 
			
		||||
        [](Memory::MemorySystem& memory) -> std::unique_ptr<HLE::DecoderBase> {
 | 
			
		||||
            return std::make_unique<HLE::MediaNDKDecoder>(memory);
 | 
			
		||||
        },
 | 
			
		||||
#endif
 | 
			
		||||
        [](Memory::MemorySystem& memory) -> std::unique_ptr<HLE::DecoderBase> {
 | 
			
		||||
            return std::make_unique<HLE::FDKDecoder>(memory);
 | 
			
		||||
        },
 | 
			
		||||
        [](Memory::MemorySystem& memory) -> std::unique_ptr<HLE::DecoderBase> {
 | 
			
		||||
            return std::make_unique<HLE::FFMPEGDecoder>(memory);
 | 
			
		||||
            return std::make_unique<HLE::FAAD2Decoder>(memory);
 | 
			
		||||
        },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,253 +0,0 @@
 | 
			
		||||
// Copyright 2019 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <media/NdkMediaCodec.h>
 | 
			
		||||
#include <media/NdkMediaError.h>
 | 
			
		||||
#include <media/NdkMediaFormat.h>
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#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<BinaryMessage> ProcessRequest(const BinaryMessage& request);
 | 
			
		||||
 | 
			
		||||
    bool SetMediaType(const AudioCore::ADTSData& adts_data);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::optional<BinaryMessage> Initalize(const BinaryMessage& request);
 | 
			
		||||
    std::optional<BinaryMessage> Decode(const BinaryMessage& request);
 | 
			
		||||
 | 
			
		||||
    Memory::MemorySystem& memory;
 | 
			
		||||
    std::unique_ptr<AMediaCodec, AMediaCodecRelease> 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<BinaryMessage> 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<u8>((adts_data.profile << 3) | (adts_data.samplerate_idx >> 1));
 | 
			
		||||
    csd_0[1] =
 | 
			
		||||
        static_cast<u8>(((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<BinaryMessage> MediaNDKDecoder::Impl::ProcessRequest(const BinaryMessage& request) {
 | 
			
		||||
    if (request.header.codec != DecoderCodec::DecodeAAC) {
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "AAC Decoder cannot handle such codec: {}",
 | 
			
		||||
                  static_cast<u16>(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<u16>(request.header.cmd));
 | 
			
		||||
        BinaryMessage response = request;
 | 
			
		||||
        response.header.result = ResultStatus::Success;
 | 
			
		||||
        return response;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Got unknown binary request: {}",
 | 
			
		||||
                  static_cast<u16>(request.header.cmd));
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> 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<std::vector<u16>, 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<Impl>(memory)) {}
 | 
			
		||||
 | 
			
		||||
MediaNDKDecoder::~MediaNDKDecoder() = default;
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> MediaNDKDecoder::ProcessRequest(const BinaryMessage& request) {
 | 
			
		||||
    return impl->ProcessRequest(request);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool MediaNDKDecoder::IsValid() const {
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore::HLE
 | 
			
		||||
@@ -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<BinaryMessage> ProcessRequest(const BinaryMessage& request) override;
 | 
			
		||||
    bool IsValid() const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    class Impl;
 | 
			
		||||
    std::unique_ptr<Impl> impl;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore::HLE
 | 
			
		||||
@@ -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<BinaryMessage> ProcessRequest(const BinaryMessage& request);
 | 
			
		||||
    bool IsValid() const {
 | 
			
		||||
        return is_valid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::optional<BinaryMessage> Initalize(const BinaryMessage& request);
 | 
			
		||||
 | 
			
		||||
    std::optional<BinaryMessage> Decode(const BinaryMessage& request);
 | 
			
		||||
 | 
			
		||||
    MFOutputState DecodingLoop(AudioCore::ADTSData adts_header,
 | 
			
		||||
                               std::array<std::vector<u8>, 2>& out_streams);
 | 
			
		||||
 | 
			
		||||
    bool transform_initialized = false;
 | 
			
		||||
    bool format_selected = false;
 | 
			
		||||
 | 
			
		||||
    Memory::MemorySystem& memory;
 | 
			
		||||
 | 
			
		||||
    unique_mfptr<IMFTransform> 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<BinaryMessage> WMFDecoder::Impl::ProcessRequest(const BinaryMessage& request) {
 | 
			
		||||
    if (request.header.codec != DecoderCodec::DecodeAAC) {
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Got unknown codec {}", static_cast<u16>(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<u16>(request.header.cmd));
 | 
			
		||||
        BinaryMessage response = request;
 | 
			
		||||
        response.header.result = ResultStatus::Success;
 | 
			
		||||
        return response;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Got unknown binary request: {}",
 | 
			
		||||
                  static_cast<u16>(request.header.cmd));
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> 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<std::vector<u8>, 2>& out_streams) {
 | 
			
		||||
    std::optional<std::vector<f32>> 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<s16>(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<BinaryMessage> 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<std::vector<u8>, 2> out_streams;
 | 
			
		||||
    unique_mfptr<IMFSample> sample;
 | 
			
		||||
    MFInputState input_status = MFInputState::OK;
 | 
			
		||||
    MFOutputState output_status = MFOutputState::OK;
 | 
			
		||||
    std::optional<ADTSMeta> 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<Impl>(memory)) {}
 | 
			
		||||
 | 
			
		||||
WMFDecoder::~WMFDecoder() = default;
 | 
			
		||||
 | 
			
		||||
std::optional<BinaryMessage> WMFDecoder::ProcessRequest(const BinaryMessage& request) {
 | 
			
		||||
    return impl->ProcessRequest(request);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool WMFDecoder::IsValid() const {
 | 
			
		||||
    return impl->IsValid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore::HLE
 | 
			
		||||
@@ -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<BinaryMessage> ProcessRequest(const BinaryMessage& request) override;
 | 
			
		||||
    bool IsValid() const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    class Impl;
 | 
			
		||||
    std::unique_ptr<Impl> impl;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore::HLE
 | 
			
		||||
@@ -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<IMFTransform> MFDecoderInit(GUID audio_format) {
 | 
			
		||||
 | 
			
		||||
    HRESULT hr = S_OK;
 | 
			
		||||
    MFT_REGISTER_TYPE_INFO reg{};
 | 
			
		||||
    GUID category = MFT_CATEGORY_AUDIO_DECODER;
 | 
			
		||||
    IMFActivate** activate;
 | 
			
		||||
    unique_mfptr<IMFTransform> 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<void**>(static_cast<IMFTransform**>(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<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignment,
 | 
			
		||||
                                     LONGLONG duration) {
 | 
			
		||||
    HRESULT hr = S_OK;
 | 
			
		||||
    unique_mfptr<IMFMediaBuffer> buf;
 | 
			
		||||
    unique_mfptr<IMFSample> 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<IMFMediaType> 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<IMFMediaType> 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<ADTSMeta> 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<MFOutputState, unique_mfptr<IMFSample>> 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<IMFSample> 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<std::vector<f32>> CopySampleToBuffer(IMFSample* sample) {
 | 
			
		||||
    unique_mfptr<IMFMediaBuffer> buffer;
 | 
			
		||||
    HRESULT hr = S_OK;
 | 
			
		||||
    std::optional<std::vector<f32>> output;
 | 
			
		||||
    std::vector<f32> 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<HMODULE, LibraryDeleter> mf_dll{nullptr};
 | 
			
		||||
std::unique_ptr<HMODULE, LibraryDeleter> 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<LPSTR>(&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<LPSTR>(&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<HRESULT(ULONG, DWORD)>(mfplat_dll.get(), "MFStartup");
 | 
			
		||||
    if (!MFStartup) {
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Cannot load function MFStartup");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MFShutdown = Symbol<HRESULT(void)>(mfplat_dll.get(), "MFShutdown");
 | 
			
		||||
    if (!MFShutdown) {
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Cannot load function MFShutdown");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MFShutdownObject = Symbol<HRESULT(IUnknown*)>(mf_dll.get(), "MFShutdownObject");
 | 
			
		||||
    if (!MFShutdownObject) {
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Cannot load function MFShutdownObject");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MFCreateAlignedMemoryBuffer = Symbol<HRESULT(DWORD, DWORD, IMFMediaBuffer**)>(
 | 
			
		||||
        mfplat_dll.get(), "MFCreateAlignedMemoryBuffer");
 | 
			
		||||
    if (!MFCreateAlignedMemoryBuffer) {
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Cannot load function MFCreateAlignedMemoryBuffer");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MFCreateSample = Symbol<HRESULT(IMFSample**)>(mfplat_dll.get(), "MFCreateSample");
 | 
			
		||||
    if (!MFCreateSample) {
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Cannot load function MFCreateSample");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MFTEnumEx =
 | 
			
		||||
        Symbol<HRESULT(GUID, UINT32, const MFT_REGISTER_TYPE_INFO*, const MFT_REGISTER_TYPE_INFO*,
 | 
			
		||||
                       IMFActivate***, UINT32*)>(mfplat_dll.get(), "MFTEnumEx");
 | 
			
		||||
    if (!MFTEnumEx) {
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Cannot load function MFTEnumEx");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MFCreateMediaType = Symbol<HRESULT(IMFMediaType**)>(mfplat_dll.get(), "MFCreateMediaType");
 | 
			
		||||
    if (!MFCreateMediaType) {
 | 
			
		||||
        LOG_ERROR(Audio_DSP, "Cannot load function MFCreateMediaType");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Symbol<HRESULT(ULONG, DWORD)> MFStartup;
 | 
			
		||||
Symbol<HRESULT(void)> MFShutdown;
 | 
			
		||||
Symbol<HRESULT(IUnknown*)> MFShutdownObject;
 | 
			
		||||
Symbol<HRESULT(DWORD, DWORD, IMFMediaBuffer**)> MFCreateAlignedMemoryBuffer;
 | 
			
		||||
Symbol<HRESULT(IMFSample**)> MFCreateSample;
 | 
			
		||||
Symbol<HRESULT(GUID, UINT32, const MFT_REGISTER_TYPE_INFO*, const MFT_REGISTER_TYPE_INFO*,
 | 
			
		||||
               IMFActivate***, UINT32*)>
 | 
			
		||||
    MFTEnumEx;
 | 
			
		||||
Symbol<HRESULT(IMFMediaType**)> MFCreateMediaType;
 | 
			
		||||
 | 
			
		||||
} // namespace MFDecoder
 | 
			
		||||
@@ -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 <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <comdef.h>
 | 
			
		||||
#include <mfapi.h>
 | 
			
		||||
#include <mferror.h>
 | 
			
		||||
#include <mfidl.h>
 | 
			
		||||
#include <mftransform.h>
 | 
			
		||||
 | 
			
		||||
#include "adts.h"
 | 
			
		||||
 | 
			
		||||
namespace MFDecoder {
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
struct Symbol {
 | 
			
		||||
    Symbol() = default;
 | 
			
		||||
    Symbol(HMODULE dll, const char* name) {
 | 
			
		||||
        if (dll) {
 | 
			
		||||
            ptr_symbol = reinterpret_cast<T*>(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<HRESULT(ULONG, DWORD)> MFStartup;
 | 
			
		||||
extern Symbol<HRESULT(void)> MFShutdown;
 | 
			
		||||
extern Symbol<HRESULT(IUnknown*)> MFShutdownObject;
 | 
			
		||||
extern Symbol<HRESULT(DWORD, DWORD, IMFMediaBuffer**)> MFCreateAlignedMemoryBuffer;
 | 
			
		||||
extern Symbol<HRESULT(IMFSample**)> MFCreateSample;
 | 
			
		||||
extern Symbol<HRESULT(GUID, UINT32, const MFT_REGISTER_TYPE_INFO*, const MFT_REGISTER_TYPE_INFO*,
 | 
			
		||||
                      IMFActivate***, UINT32*)>
 | 
			
		||||
    MFTEnumEx;
 | 
			
		||||
extern Symbol<HRESULT(IMFMediaType**)> MFCreateMediaType;
 | 
			
		||||
 | 
			
		||||
enum class MFOutputState { FatalError, OK, NeedMoreInput, NeedReconfig, HaveMoreData };
 | 
			
		||||
enum class MFInputState { FatalError, OK, NotAccepted };
 | 
			
		||||
 | 
			
		||||
// utility functions / templates
 | 
			
		||||
template <class T>
 | 
			
		||||
struct MFRelease {
 | 
			
		||||
    void operator()(T* pointer) const {
 | 
			
		||||
        pointer->Release();
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
struct MFRelease<IMFTransform> {
 | 
			
		||||
    void operator()(IMFTransform* pointer) const {
 | 
			
		||||
        MFShutdownObject(pointer);
 | 
			
		||||
        pointer->Release();
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// wrapper facilities for dealing with pointers
 | 
			
		||||
template <typename T>
 | 
			
		||||
using unique_mfptr = std::unique_ptr<T, MFRelease<T>>;
 | 
			
		||||
 | 
			
		||||
template <typename SmartPtr, typename RawPtr>
 | 
			
		||||
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 <typename SmartPtr>
 | 
			
		||||
auto Amp(SmartPtr& smart_ptr) {
 | 
			
		||||
    return AmpImpl<SmartPtr, decltype(smart_ptr.get())>(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<IMFTransform> MFDecoderInit(GUID audio_format = MFAudioFormat_AAC);
 | 
			
		||||
unique_mfptr<IMFSample> 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<ADTSMeta> 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<MFOutputState, unique_mfptr<IMFSample>> ReceiveSample(IMFTransform* transform,
 | 
			
		||||
                                                                 DWORD out_stream_id);
 | 
			
		||||
std::optional<std::vector<f32>> CopySampleToBuffer(IMFSample* sample);
 | 
			
		||||
 | 
			
		||||
} // namespace MFDecoder
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
// Copyright 2023 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#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<Common::DynamicLibrary> fdk_aac;
 | 
			
		||||
 | 
			
		||||
#define LOAD_SYMBOL(library, name)                                                                 \
 | 
			
		||||
    any_failed = any_failed || (name = library->GetSymbol<name##_func>(#name)) == nullptr
 | 
			
		||||
 | 
			
		||||
bool LoadFdkAac() {
 | 
			
		||||
    if (fdk_aac) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fdk_aac = std::make_unique<Common::DynamicLibrary>("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
 | 
			
		||||
@@ -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 <fdk-aac/aacdecoder_lib.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,77 +0,0 @@
 | 
			
		||||
// Copyright 2023 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <catch2/catch_test_macros.hpp>
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "audio_core/hle/adts.h"
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
constexpr std::array<u32, 16> freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
 | 
			
		||||
                                            16000, 12000, 11025, 8000,  7350,  0,     0,     0};
 | 
			
		||||
constexpr std::array<u8, 8> 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<u8, 7> adts_header;
 | 
			
		||||
        std::string adts_header_string = "ADTS Header: ";
 | 
			
		||||
        for (auto& it : adts_header) {
 | 
			
		||||
            it = static_cast<u8>(rand());
 | 
			
		||||
            adts_header_string.append(fmt::format("{:2X} ", it));
 | 
			
		||||
        }
 | 
			
		||||
        INFO(adts_header_string);
 | 
			
		||||
 | 
			
		||||
        AudioCore::ADTSData out_old_impl =
 | 
			
		||||
            ParseADTS_Old(reinterpret_cast<const unsigned char*>(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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user