Merge pull request #236 from adriadam10/feat/audiodec
feat(libSceAudiodec): decode MP3, M4AAC and ATRAC9
This commit is contained in:
commit
409c41cc6a
|
|
@ -666,6 +666,59 @@ struct AudiodecCtrl {
|
|||
AudiodecPcmItem* pPcmItem;
|
||||
};
|
||||
|
||||
struct AudiodecParamAt9 {
|
||||
std::uint32_t ui_size;
|
||||
std::int32_t i_bw_pcm;
|
||||
std::uint8_t ui_config_data[4];
|
||||
};
|
||||
|
||||
struct AudiodecAt9Info {
|
||||
std::uint32_t ui_size;
|
||||
std::uint32_t ui_channel;
|
||||
std::uint32_t ui_bitrate;
|
||||
std::uint32_t ui_sampling_rate;
|
||||
std::uint32_t ui_super_frame_size;
|
||||
std::uint32_t ui_frames_in_super_frame;
|
||||
std::uint32_t ui_next_frame_size;
|
||||
std::uint32_t ui_frame_samples;
|
||||
std::int32_t i_result;
|
||||
};
|
||||
|
||||
struct AudiodecParamMp3 {
|
||||
std::uint32_t ui_size;
|
||||
std::int32_t i_bw_pcm;
|
||||
};
|
||||
|
||||
struct AudiodecMp3Info {
|
||||
std::uint32_t ui_size;
|
||||
std::uint32_t ui_header;
|
||||
std::uint8_t uc_crc;
|
||||
std::uint8_t uc_mode;
|
||||
std::uint8_t uc_mode_extension;
|
||||
std::uint8_t uc_copyright;
|
||||
std::uint8_t uc_original;
|
||||
std::uint8_t uc_emphasis;
|
||||
std::uint8_t uc_reserved[2];
|
||||
std::int32_t i_result;
|
||||
};
|
||||
|
||||
struct AudiodecParamM4aac {
|
||||
std::uint32_t ui_size;
|
||||
std::int32_t i_bw_pcm;
|
||||
std::uint32_t ui_config_number;
|
||||
std::uint32_t ui_sampling_freq_index;
|
||||
std::uint32_t ui_max_channels;
|
||||
std::uint32_t ui_enable_heaac;
|
||||
};
|
||||
|
||||
struct AudiodecM4aacInfo {
|
||||
std::uint32_t ui_size;
|
||||
std::uint32_t ui_sampling_freq;
|
||||
std::uint32_t ui_number_of_channels;
|
||||
std::uint32_t ui_heaac;
|
||||
std::int32_t i_result;
|
||||
};
|
||||
|
||||
struct VoiceInitParam {
|
||||
std::int32_t app_type;
|
||||
std::uint64_t on_event;
|
||||
|
|
|
|||
|
|
@ -5,42 +5,6 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
int32_t APS5_VABI sceAudiodecClearContext(int32_t handle) {
|
||||
(void)handle;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecCreateDecoder(AudiodecCtrl* ctrl, uint32_t codec_type) {
|
||||
(void)ctrl;
|
||||
(void)codec_type;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecDecode(int32_t handle, AudiodecCtrl* ctrl) {
|
||||
(void)handle;
|
||||
(void)ctrl;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecDeleteDecoder(int32_t handle) {
|
||||
(void)handle;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecInitLibrary(uint32_t codec_type) {
|
||||
(void)codec_type;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecTermLibrary(uint32_t codec_type) {
|
||||
(void)codec_type;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
APS5_DUMMY_FUN
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
add_library(libSceAudiodec SHARED EXCLUDE_FROM_ALL
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Codecs.cpp
|
||||
)
|
||||
target_include_directories(libSceAudiodec PRIVATE
|
||||
${LIBS_INCLUDE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/LibAtrac9/C/src
|
||||
)
|
||||
target_link_libraries(libSceAudiodec PRIVATE atrac9 ffmpeg libc libkernel)
|
||||
set_target_properties(libSceAudiodec PROPERTIES CXX_EXTENSIONS OFF)
|
||||
configure_windows_unwind(libSceAudiodec)
|
||||
|
||||
if(MINGW)
|
||||
foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static)
|
||||
string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(guest_audiodec_tests ${LIBS_INCLUDE_DIR}/tests/GuestAudiodec.cpp)
|
||||
target_include_directories(guest_audiodec_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_audiodec_tests PRIVATE libSceAudiodec libc)
|
||||
configure_windows_unwind(guest_audiodec_tests)
|
||||
add_test(NAME guest_audiodec COMMAND guest_audiodec_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_audiodec PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAudiodec>")
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -1,46 +1,250 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libSceAudiodec/src/Codecs.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::int32_t ERROR_API_FAIL = static_cast<std::int32_t>(0x807F0000);
|
||||
constexpr std::int32_t ERROR_INVALID_TYPE = static_cast<std::int32_t>(0x807F0001);
|
||||
constexpr std::int32_t ERROR_ARG = static_cast<std::int32_t>(0x807F0002);
|
||||
constexpr std::int32_t ERROR_INVALID_PARAM_SIZE = static_cast<std::int32_t>(0x807F0004);
|
||||
constexpr std::int32_t ERROR_INVALID_BSI_INFO_SIZE = static_cast<std::int32_t>(0x807F0005);
|
||||
constexpr std::int32_t ERROR_INVALID_AU_INFO_SIZE = static_cast<std::int32_t>(0x807F0006);
|
||||
constexpr std::int32_t ERROR_INVALID_PCM_ITEM_SIZE = static_cast<std::int32_t>(0x807F0007);
|
||||
constexpr std::int32_t ERROR_INVALID_CTRL_POINTER = static_cast<std::int32_t>(0x807F0008);
|
||||
constexpr std::int32_t ERROR_INVALID_PARAM_POINTER = static_cast<std::int32_t>(0x807F0009);
|
||||
constexpr std::int32_t ERROR_INVALID_BSI_INFO_POINTER = static_cast<std::int32_t>(0x807F000A);
|
||||
constexpr std::int32_t ERROR_INVALID_AU_INFO_POINTER = static_cast<std::int32_t>(0x807F000B);
|
||||
constexpr std::int32_t ERROR_INVALID_PCM_ITEM_POINTER = static_cast<std::int32_t>(0x807F000C);
|
||||
constexpr std::int32_t ERROR_INVALID_AU_POINTER = static_cast<std::int32_t>(0x807F000D);
|
||||
constexpr std::int32_t ERROR_INVALID_PCM_POINTER = static_cast<std::int32_t>(0x807F000E);
|
||||
constexpr std::int32_t ERROR_INVALID_HANDLE = static_cast<std::int32_t>(0x807F000F);
|
||||
constexpr std::int32_t ERROR_INVALID_WORD_LENGTH = static_cast<std::int32_t>(0x807F0010);
|
||||
constexpr std::int32_t ERROR_INVALID_AU_SIZE = static_cast<std::int32_t>(0x807F0011);
|
||||
constexpr std::int32_t ERROR_INVALID_PCM_SIZE = static_cast<std::int32_t>(0x807F0012);
|
||||
constexpr std::int32_t ERROR_M4AAC_INVALID_SAMPLING_FREQ = static_cast<std::int32_t>(0x807F0300);
|
||||
constexpr std::int32_t ERROR_M4AAC_INVALID_ENABLE_HEAAC = static_cast<std::int32_t>(0x807F0302);
|
||||
constexpr std::int32_t ERROR_M4AAC_INVALID_CONFIG_NUMBER = static_cast<std::int32_t>(0x807F0303);
|
||||
constexpr std::int32_t ERROR_M4AAC_INVALID_MAX_CHANNELS = static_cast<std::int32_t>(0x807F0304);
|
||||
constexpr std::int32_t ERROR_AT9_INVALID_CONFIG_DATA = static_cast<std::int32_t>(0x807F1000);
|
||||
|
||||
constexpr std::uint32_t TYPE_AT9 = 1;
|
||||
constexpr std::uint32_t TYPE_MP3 = 2;
|
||||
constexpr std::uint32_t TYPE_M4AAC = 3;
|
||||
constexpr std::uint32_t M4AAC_CONFIG_ADTS = 1;
|
||||
constexpr std::uint32_t M4AAC_CONFIG_RAW = 2;
|
||||
constexpr std::int32_t M4AAC_RESULT_DECODE_ERROR = -4;
|
||||
constexpr std::int32_t M4AAC_RESULT_INSUFFICIENT_DATA = -5;
|
||||
|
||||
static_assert(sizeof(AudiodecAuInfo) == 24 && sizeof(AudiodecPcmItem) == 24 && sizeof(AudiodecCtrl) == 32);
|
||||
static_assert(sizeof(AudiodecParamAt9) == 12 && sizeof(AudiodecAt9Info) == 36);
|
||||
static_assert(sizeof(AudiodecParamMp3) == 8 && sizeof(AudiodecMp3Info) == 20);
|
||||
static_assert(sizeof(AudiodecParamM4aac) == 24 && sizeof(AudiodecM4aacInfo) == 20);
|
||||
|
||||
struct Instance {
|
||||
std::uint32_t codecType = 0;
|
||||
std::unique_ptr<Audiodec::Decoder> decoder;
|
||||
Audiodec::At9Format at9{};
|
||||
std::uint32_t channels = 0;
|
||||
std::uint32_t sampleRate = 0;
|
||||
};
|
||||
|
||||
std::mutex g_mutex;
|
||||
std::array<std::uint32_t, 4> g_initialized{};
|
||||
std::array<Instance, 64> g_instances{};
|
||||
|
||||
bool validType(std::uint32_t codecType) {
|
||||
return codecType == TYPE_AT9 || codecType == TYPE_MP3 || codecType == TYPE_M4AAC;
|
||||
}
|
||||
|
||||
std::int32_t wordSizeOf(const void* param) {
|
||||
return static_cast<const AudiodecParamMp3*>(param)->i_bw_pcm;
|
||||
}
|
||||
|
||||
std::int32_t validateWordSize(std::int32_t wordSize) {
|
||||
if (wordSize == Audiodec::WORD_SIZE_16BIT || wordSize == Audiodec::WORD_SIZE_FLOAT) return 0;
|
||||
if (wordSize == 0) NotImplemented_nid_no_patch("libSceAudiodec 24-bit PCM output");
|
||||
return ERROR_INVALID_WORD_LENGTH;
|
||||
}
|
||||
|
||||
std::int32_t validateCtrl(const AudiodecCtrl* ctrl, std::uint32_t codecType) {
|
||||
if (!ctrl) return ERROR_INVALID_CTRL_POINTER;
|
||||
if (!ctrl->pParam) return ERROR_INVALID_PARAM_POINTER;
|
||||
if (!ctrl->pBsiInfo) return ERROR_INVALID_BSI_INFO_POINTER;
|
||||
const std::uint32_t paramSize = *static_cast<const std::uint32_t*>(ctrl->pParam);
|
||||
const std::uint32_t infoSize = *static_cast<const std::uint32_t*>(ctrl->pBsiInfo);
|
||||
switch (codecType) {
|
||||
case TYPE_AT9:
|
||||
if (paramSize != sizeof(AudiodecParamAt9)) return ERROR_INVALID_PARAM_SIZE;
|
||||
if (infoSize != sizeof(AudiodecAt9Info)) return ERROR_INVALID_BSI_INFO_SIZE;
|
||||
break;
|
||||
case TYPE_MP3:
|
||||
if (paramSize != sizeof(AudiodecParamMp3)) return ERROR_INVALID_PARAM_SIZE;
|
||||
if (infoSize != sizeof(AudiodecMp3Info)) return ERROR_INVALID_BSI_INFO_SIZE;
|
||||
break;
|
||||
default:
|
||||
if (paramSize < sizeof(AudiodecParamM4aac)) return ERROR_INVALID_PARAM_SIZE;
|
||||
if (infoSize != sizeof(AudiodecM4aacInfo)) return ERROR_INVALID_BSI_INFO_SIZE;
|
||||
break;
|
||||
}
|
||||
return validateWordSize(wordSizeOf(ctrl->pParam));
|
||||
}
|
||||
|
||||
std::int32_t validateBuffers(const AudiodecCtrl* ctrl) {
|
||||
if (!ctrl->pAuInfo) return ERROR_INVALID_AU_INFO_POINTER;
|
||||
if (!ctrl->pPcmItem) return ERROR_INVALID_PCM_ITEM_POINTER;
|
||||
if (ctrl->pAuInfo->ui_size != sizeof(AudiodecAuInfo)) return ERROR_INVALID_AU_INFO_SIZE;
|
||||
if (ctrl->pPcmItem->ui_size != sizeof(AudiodecPcmItem)) return ERROR_INVALID_PCM_ITEM_SIZE;
|
||||
if (!ctrl->pAuInfo->p_au_addr) return ERROR_INVALID_AU_POINTER;
|
||||
if (!ctrl->pPcmItem->p_pcm_addr) return ERROR_INVALID_PCM_POINTER;
|
||||
if (ctrl->pAuInfo->ui_au_size == 0) return ERROR_INVALID_AU_SIZE;
|
||||
if (ctrl->pPcmItem->ui_pcm_size == 0) return ERROR_INVALID_PCM_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fillAt9Info(AudiodecAt9Info* info, const Audiodec::At9Format& format) {
|
||||
const std::uint32_t superframeSamples = format.frameSamples * format.framesInSuperframe;
|
||||
info->ui_channel = format.channels;
|
||||
info->ui_bitrate = static_cast<std::uint32_t>(static_cast<std::uint64_t>(format.superframeSize) * 8 * format.sampleRate / superframeSamples);
|
||||
info->ui_sampling_rate = format.sampleRate;
|
||||
info->ui_super_frame_size = format.superframeSize;
|
||||
info->ui_frames_in_super_frame = format.framesInSuperframe;
|
||||
info->ui_next_frame_size = format.superframeSize;
|
||||
info->ui_frame_samples = format.frameSamples;
|
||||
info->i_result = 0;
|
||||
}
|
||||
|
||||
void fillMp3Info(AudiodecMp3Info* info, const Audiodec::Mp3Header& header) {
|
||||
info->ui_header = header.word;
|
||||
info->uc_crc = header.crc;
|
||||
info->uc_mode = header.mode;
|
||||
info->uc_mode_extension = header.modeExtension;
|
||||
info->uc_copyright = header.copyright;
|
||||
info->uc_original = header.original;
|
||||
info->uc_emphasis = header.emphasis;
|
||||
info->uc_reserved[0] = 0;
|
||||
info->uc_reserved[1] = 0;
|
||||
info->i_result = 0;
|
||||
}
|
||||
|
||||
std::int32_t createAac(const AudiodecParamM4aac* param, AudiodecM4aacInfo* info, Instance& instance) {
|
||||
if (param->ui_config_number != M4AAC_CONFIG_ADTS && param->ui_config_number != M4AAC_CONFIG_RAW) return ERROR_M4AAC_INVALID_CONFIG_NUMBER;
|
||||
const std::uint32_t sampleRate = Audiodec::AacSampleRate(param->ui_sampling_freq_index);
|
||||
if (param->ui_config_number == M4AAC_CONFIG_RAW && sampleRate == 0) return ERROR_M4AAC_INVALID_SAMPLING_FREQ;
|
||||
if (param->ui_max_channels > 8) return ERROR_M4AAC_INVALID_MAX_CHANNELS;
|
||||
if (param->ui_enable_heaac > 1) return ERROR_M4AAC_INVALID_ENABLE_HEAAC;
|
||||
const std::uint32_t channels = param->ui_max_channels == 0 ? 2 : param->ui_max_channels;
|
||||
instance.decoder = Audiodec::CreateAac(param->i_bw_pcm, param->ui_config_number == M4AAC_CONFIG_ADTS, param->ui_sampling_freq_index, channels);
|
||||
instance.channels = channels;
|
||||
instance.sampleRate = sampleRate;
|
||||
info->ui_sampling_freq = sampleRate;
|
||||
info->ui_number_of_channels = channels;
|
||||
info->ui_heaac = 0;
|
||||
info->i_result = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Instance* find(std::int32_t handle) {
|
||||
if (handle <= 0 || static_cast<std::size_t>(handle) > g_instances.size()) return nullptr;
|
||||
Instance& instance = g_instances[static_cast<std::size_t>(handle - 1)];
|
||||
return instance.decoder ? &instance : nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int32_t APS5_VABI sceAudiodecClearContext(int32_t handle) {
|
||||
(void)handle;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecCreateDecoder(AudiodecCtrl* ctrl, uint32_t codec_type) {
|
||||
(void)ctrl;
|
||||
(void)codec_type;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecDecode(int32_t handle, AudiodecCtrl* ctrl) {
|
||||
(void)handle;
|
||||
(void)ctrl;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecDeleteDecoder(int32_t handle) {
|
||||
(void)handle;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecInitLibrary(uint32_t codec_type) {
|
||||
(void)codec_type;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (!validType(codec_type)) return ERROR_INVALID_TYPE;
|
||||
std::lock_guard lock(g_mutex);
|
||||
++g_initialized[codec_type];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecTermLibrary(uint32_t codec_type) {
|
||||
(void)codec_type;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (!validType(codec_type)) return ERROR_INVALID_TYPE;
|
||||
std::lock_guard lock(g_mutex);
|
||||
if (g_initialized[codec_type] > 0) --g_initialized[codec_type];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecCreateDecoder(AudiodecCtrl* ctrl, uint32_t codec_type) {
|
||||
if (!validType(codec_type)) return ERROR_INVALID_TYPE;
|
||||
if (const std::int32_t result = validateCtrl(ctrl, codec_type); result != 0) return result;
|
||||
std::lock_guard lock(g_mutex);
|
||||
if (g_initialized[codec_type] == 0) return ERROR_ARG;
|
||||
for (std::size_t i = 0; i < g_instances.size(); ++i) {
|
||||
Instance& instance = g_instances[i];
|
||||
if (instance.decoder) continue;
|
||||
instance = {};
|
||||
instance.codecType = codec_type;
|
||||
if (codec_type == TYPE_AT9) {
|
||||
const auto* param = static_cast<const AudiodecParamAt9*>(ctrl->pParam);
|
||||
instance.decoder = Audiodec::CreateAt9(param->i_bw_pcm, param->ui_config_data, instance.at9);
|
||||
if (!instance.decoder) return ERROR_AT9_INVALID_CONFIG_DATA;
|
||||
fillAt9Info(static_cast<AudiodecAt9Info*>(ctrl->pBsiInfo), instance.at9);
|
||||
} else if (codec_type == TYPE_MP3) {
|
||||
instance.decoder = Audiodec::CreateMp3(static_cast<const AudiodecParamMp3*>(ctrl->pParam)->i_bw_pcm);
|
||||
} else if (const std::int32_t result = createAac(static_cast<const AudiodecParamM4aac*>(ctrl->pParam), static_cast<AudiodecM4aacInfo*>(ctrl->pBsiInfo), instance); result != 0) {
|
||||
instance = {};
|
||||
return result;
|
||||
}
|
||||
return static_cast<int32_t>(i + 1);
|
||||
}
|
||||
return ERROR_API_FAIL;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecDeleteDecoder(int32_t handle) {
|
||||
std::lock_guard lock(g_mutex);
|
||||
Instance* instance = find(handle);
|
||||
if (!instance) return ERROR_INVALID_HANDLE;
|
||||
*instance = {};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecClearContext(int32_t handle) {
|
||||
std::lock_guard lock(g_mutex);
|
||||
Instance* instance = find(handle);
|
||||
if (!instance) return ERROR_INVALID_HANDLE;
|
||||
instance->decoder->Reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t APS5_VABI sceAudiodecDecode(int32_t handle, AudiodecCtrl* ctrl) {
|
||||
std::lock_guard lock(g_mutex);
|
||||
Instance* instance = find(handle);
|
||||
if (!instance) return ERROR_INVALID_HANDLE;
|
||||
if (const std::int32_t result = validateCtrl(ctrl, instance->codecType); result != 0) return result;
|
||||
if (const std::int32_t result = validateBuffers(ctrl); result != 0) return result;
|
||||
|
||||
const Audiodec::DecodeResult decoded = instance->decoder->Decode(static_cast<const std::uint8_t*>(ctrl->pAuInfo->p_au_addr), ctrl->pAuInfo->ui_au_size,
|
||||
static_cast<std::uint8_t*>(ctrl->pPcmItem->p_pcm_addr), ctrl->pPcmItem->ui_pcm_size);
|
||||
if (instance->codecType == TYPE_M4AAC) {
|
||||
auto* info = static_cast<AudiodecM4aacInfo*>(ctrl->pBsiInfo);
|
||||
info->i_result = decoded.status == Audiodec::DecodeStatus::InvalidData ? M4AAC_RESULT_DECODE_ERROR
|
||||
: decoded.status == Audiodec::DecodeStatus::PartialInput ? M4AAC_RESULT_INSUFFICIENT_DATA : 0;
|
||||
if (decoded.channels != 0) {
|
||||
info->ui_sampling_freq = decoded.sampleRate;
|
||||
info->ui_number_of_channels = decoded.channels;
|
||||
info->ui_heaac = decoded.highEfficiency;
|
||||
}
|
||||
}
|
||||
if (decoded.status == Audiodec::DecodeStatus::NotEnoughRoom) return ERROR_INVALID_PCM_SIZE;
|
||||
if (decoded.status != Audiodec::DecodeStatus::Ok) {
|
||||
ctrl->pAuInfo->ui_au_size = 0;
|
||||
ctrl->pPcmItem->ui_pcm_size = 0;
|
||||
return ERROR_API_FAIL;
|
||||
}
|
||||
if (instance->codecType == TYPE_MP3) fillMp3Info(static_cast<AudiodecMp3Info*>(ctrl->pBsiInfo), decoded.mp3);
|
||||
if (instance->codecType == TYPE_AT9) fillAt9Info(static_cast<AudiodecAt9Info*>(ctrl->pBsiInfo), instance->at9);
|
||||
ctrl->pAuInfo->ui_au_size = static_cast<std::uint32_t>(decoded.consumed);
|
||||
ctrl->pPcmItem->ui_pcm_size = static_cast<std::uint32_t>(decoded.produced);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,252 @@
|
|||
#include "prx/libSceAudiodec/src/Codecs.hpp"
|
||||
|
||||
#include "libatrac9.h"
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/log.h>
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Audiodec {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::uint32_t AAC_SAMPLE_RATES[13] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350};
|
||||
|
||||
void writeSample(float value, std::int32_t wordSize, std::uint8_t* out) {
|
||||
if (wordSize == WORD_SIZE_FLOAT) {
|
||||
std::memcpy(out, &value, sizeof(value));
|
||||
return;
|
||||
}
|
||||
const auto sample = static_cast<std::int16_t>(std::clamp(std::lrint(value * 32768.0), -32768L, 32767L));
|
||||
std::memcpy(out, &sample, sizeof(sample));
|
||||
}
|
||||
|
||||
class FfmpegDecoder : public Decoder {
|
||||
public:
|
||||
FfmpegDecoder(AVCodecID id, std::int32_t wordSize, std::vector<std::uint8_t> extradata = {}) : wordSize(wordSize), extradata(std::move(extradata)), id(id) {
|
||||
static const bool quiet = (av_log_set_level(AV_LOG_QUIET), true);
|
||||
(void)quiet;
|
||||
open();
|
||||
}
|
||||
|
||||
~FfmpegDecoder() override { avcodec_free_context(&context); }
|
||||
|
||||
void Reset() override { avcodec_flush_buffers(context); }
|
||||
|
||||
protected:
|
||||
DecodeResult decodePacket(const std::uint8_t* data, std::size_t size, std::uint8_t* pcm, std::size_t pcmSize) {
|
||||
AVPacket* packet = av_packet_alloc();
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
if (!packet || !frame) {
|
||||
av_packet_free(&packet);
|
||||
av_frame_free(&frame);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
packet->data = const_cast<std::uint8_t*>(data);
|
||||
packet->size = static_cast<int>(size);
|
||||
DecodeResult result{};
|
||||
result.consumed = size;
|
||||
if (avcodec_send_packet(context, packet) < 0) result.status = DecodeStatus::InvalidData;
|
||||
while (result.status == DecodeStatus::Ok && avcodec_receive_frame(context, frame) == 0) {
|
||||
const auto format = static_cast<AVSampleFormat>(frame->format);
|
||||
if (format != AV_SAMPLE_FMT_FLTP && format != AV_SAMPLE_FMT_S16P) throw std::runtime_error("libSceAudiodec: FFmpeg sample format " + std::to_string(frame->format) + " is not converted");
|
||||
const auto channels = static_cast<std::size_t>(frame->ch_layout.nb_channels);
|
||||
const auto samples = static_cast<std::size_t>(frame->nb_samples);
|
||||
const std::size_t sampleBytes = wordSize == WORD_SIZE_FLOAT ? 4 : 2;
|
||||
if (result.produced + samples * channels * sampleBytes > pcmSize) {
|
||||
result.status = DecodeStatus::NotEnoughRoom;
|
||||
} else {
|
||||
for (std::size_t sample = 0; sample < samples; ++sample) {
|
||||
for (std::size_t channel = 0; channel < channels; ++channel) {
|
||||
const float value = format == AV_SAMPLE_FMT_FLTP ? reinterpret_cast<const float*>(frame->extended_data[channel])[sample]
|
||||
: reinterpret_cast<const std::int16_t*>(frame->extended_data[channel])[sample] / 32768.0f;
|
||||
writeSample(value, wordSize, pcm + result.produced);
|
||||
result.produced += sampleBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
result.channels = static_cast<std::uint32_t>(channels);
|
||||
result.sampleRate = static_cast<std::uint32_t>(frame->sample_rate);
|
||||
result.highEfficiency = context->profile == AV_PROFILE_AAC_HE || context->profile == AV_PROFILE_AAC_HE_V2;
|
||||
av_frame_unref(frame);
|
||||
}
|
||||
av_packet_free(&packet);
|
||||
av_frame_free(&frame);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
void open() {
|
||||
const AVCodec* codec = avcodec_find_decoder(id);
|
||||
context = codec ? avcodec_alloc_context3(codec) : nullptr;
|
||||
if (!context) throw std::runtime_error("libSceAudiodec: cannot allocate an FFmpeg decoder");
|
||||
if (!extradata.empty()) {
|
||||
context->extradata = static_cast<std::uint8_t*>(av_mallocz(extradata.size() + AV_INPUT_BUFFER_PADDING_SIZE));
|
||||
if (!context->extradata) throw std::bad_alloc();
|
||||
std::memcpy(context->extradata, extradata.data(), extradata.size());
|
||||
context->extradata_size = static_cast<int>(extradata.size());
|
||||
}
|
||||
if (avcodec_open2(context, codec, nullptr) < 0) throw std::runtime_error("libSceAudiodec: cannot open an FFmpeg decoder");
|
||||
}
|
||||
|
||||
std::int32_t wordSize;
|
||||
std::vector<std::uint8_t> extradata;
|
||||
AVCodecID id;
|
||||
AVCodecContext* context = nullptr;
|
||||
};
|
||||
|
||||
class Mp3Decoder final : public FfmpegDecoder {
|
||||
public:
|
||||
explicit Mp3Decoder(std::int32_t wordSize) : FfmpegDecoder(AV_CODEC_ID_MP3, wordSize) {}
|
||||
|
||||
DecodeResult Decode(const std::uint8_t* data, std::size_t size, std::uint8_t* pcm, std::size_t pcmSize) override {
|
||||
Mp3Header header{};
|
||||
if (!ParseMp3Header(data, size, header)) return {DecodeStatus::InvalidData};
|
||||
if (header.frameBytes > size) return {DecodeStatus::PartialInput};
|
||||
DecodeResult result = decodePacket(data, header.frameBytes, pcm, pcmSize);
|
||||
result.mp3 = header;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class AacDecoder final : public FfmpegDecoder {
|
||||
public:
|
||||
AacDecoder(std::int32_t wordSize, bool adts, std::vector<std::uint8_t> extradata) : FfmpegDecoder(AV_CODEC_ID_AAC, wordSize, std::move(extradata)), adts(adts) {}
|
||||
|
||||
DecodeResult Decode(const std::uint8_t* data, std::size_t size, std::uint8_t* pcm, std::size_t pcmSize) override {
|
||||
std::size_t frameBytes = size;
|
||||
if (adts) {
|
||||
if (size < 7 || data[0] != 0xFF || (data[1] & 0xF6) != 0xF0) return {DecodeStatus::InvalidData};
|
||||
frameBytes = static_cast<std::size_t>((data[3] & 3) << 11 | data[4] << 3 | data[5] >> 5);
|
||||
if (frameBytes < 7) return {DecodeStatus::InvalidData};
|
||||
if (frameBytes > size) return {DecodeStatus::PartialInput};
|
||||
}
|
||||
return decodePacket(data, frameBytes, pcm, pcmSize);
|
||||
}
|
||||
|
||||
private:
|
||||
bool adts;
|
||||
};
|
||||
|
||||
class At9Decoder final : public Decoder {
|
||||
public:
|
||||
At9Decoder(std::int32_t wordSize, const std::uint8_t (&config)[4]) : wordSize(wordSize) {
|
||||
std::memcpy(this->config, config, sizeof(this->config));
|
||||
Reset();
|
||||
}
|
||||
|
||||
~At9Decoder() override { Atrac9ReleaseHandle(handle); }
|
||||
|
||||
void Reset() override {
|
||||
if (handle) Atrac9ReleaseHandle(handle);
|
||||
handle = Atrac9GetHandle();
|
||||
if (!handle || Atrac9InitDecoder(handle, config) != 0) throw std::runtime_error("libSceAudiodec: cannot initialize the ATRAC9 decoder");
|
||||
Atrac9GetCodecInfo(handle, &info);
|
||||
}
|
||||
|
||||
DecodeResult Decode(const std::uint8_t* data, std::size_t size, std::uint8_t* pcm, std::size_t pcmSize) override {
|
||||
const auto superframeSize = static_cast<std::size_t>(info.superframeSize);
|
||||
if (size < superframeSize) return {DecodeStatus::PartialInput};
|
||||
const std::size_t sampleBytes = wordSize == WORD_SIZE_FLOAT ? 4 : 2;
|
||||
const std::size_t frameBytes = static_cast<std::size_t>(info.frameSamples) * info.channels * sampleBytes;
|
||||
if (frameBytes * info.framesInSuperframe > pcmSize) return {DecodeStatus::NotEnoughRoom};
|
||||
DecodeResult result{};
|
||||
std::size_t offset = 0;
|
||||
for (int frame = 0; frame < info.framesInSuperframe; ++frame) {
|
||||
int used = 0;
|
||||
const int status = wordSize == WORD_SIZE_FLOAT
|
||||
? Atrac9DecodeF32(handle, data + offset, reinterpret_cast<float*>(pcm + result.produced), &used, 0)
|
||||
: Atrac9Decode(handle, data + offset, reinterpret_cast<short*>(pcm + result.produced), &used, 0);
|
||||
if (status != 0 || used <= 0 || offset + static_cast<std::size_t>(used) > superframeSize) {
|
||||
Reset();
|
||||
return {DecodeStatus::InvalidData};
|
||||
}
|
||||
offset += static_cast<std::size_t>(used);
|
||||
result.produced += frameBytes;
|
||||
}
|
||||
result.consumed = superframeSize;
|
||||
result.channels = static_cast<std::uint32_t>(info.channels);
|
||||
result.sampleRate = static_cast<std::uint32_t>(info.samplingRate);
|
||||
return result;
|
||||
}
|
||||
|
||||
const Atrac9CodecInfo& Info() const { return info; }
|
||||
|
||||
private:
|
||||
std::int32_t wordSize;
|
||||
std::uint8_t config[4];
|
||||
void* handle = nullptr;
|
||||
Atrac9CodecInfo info{};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
bool ParseMp3Header(const std::uint8_t* data, std::size_t size, Mp3Header& header) {
|
||||
if (size < 4) return false;
|
||||
const std::uint32_t word = std::uint32_t{data[0]} << 24 | std::uint32_t{data[1]} << 16 | std::uint32_t{data[2]} << 8 | data[3];
|
||||
const auto version = word >> 19 & 3;
|
||||
const auto layer = word >> 17 & 3;
|
||||
const auto bitrateIndex = word >> 12 & 15;
|
||||
const auto rateIndex = word >> 10 & 3;
|
||||
if ((word & 0xFFE00000u) != 0xFFE00000u || version == 1 || layer != 1 || bitrateIndex == 0 || bitrateIndex == 15 || rateIndex == 3) return false;
|
||||
static constexpr std::uint32_t rates[4][3] = {{11025, 12000, 8000}, {0, 0, 0}, {22050, 24000, 16000}, {44100, 48000, 32000}};
|
||||
static constexpr std::uint32_t mpeg1Kbps[16] = {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0};
|
||||
static constexpr std::uint32_t mpeg2Kbps[16] = {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0};
|
||||
const bool mpeg1 = version == 3;
|
||||
const std::uint32_t sampleRate = rates[version][rateIndex];
|
||||
const std::uint32_t bitrate = (mpeg1 ? mpeg1Kbps : mpeg2Kbps)[bitrateIndex] * 1000;
|
||||
header.word = word;
|
||||
header.frameBytes = (mpeg1 ? 144 : 72) * bitrate / sampleRate + (word >> 9 & 1);
|
||||
header.crc = (word >> 16 & 1) == 0;
|
||||
header.mode = static_cast<std::uint8_t>(word >> 6 & 3);
|
||||
header.modeExtension = static_cast<std::uint8_t>(word >> 4 & 3);
|
||||
header.copyright = static_cast<std::uint8_t>(word >> 3 & 1);
|
||||
header.original = static_cast<std::uint8_t>(word >> 2 & 1);
|
||||
header.emphasis = static_cast<std::uint8_t>(word & 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::uint32_t AacSampleRate(std::uint32_t index) {
|
||||
return index < 13 ? AAC_SAMPLE_RATES[index] : 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<Decoder> CreateMp3(std::int32_t wordSize) {
|
||||
return std::make_unique<Mp3Decoder>(wordSize);
|
||||
}
|
||||
|
||||
std::unique_ptr<Decoder> CreateAac(std::int32_t wordSize, bool adts, std::uint32_t samplingFreqIndex, std::uint32_t channels) {
|
||||
std::vector<std::uint8_t> config;
|
||||
if (!adts) {
|
||||
const std::uint32_t lowComplexity = 2;
|
||||
config = {static_cast<std::uint8_t>(lowComplexity << 3 | samplingFreqIndex >> 1),
|
||||
static_cast<std::uint8_t>((samplingFreqIndex & 1) << 7 | channels << 3)};
|
||||
}
|
||||
return std::make_unique<AacDecoder>(wordSize, adts, std::move(config));
|
||||
}
|
||||
|
||||
std::unique_ptr<Decoder> CreateAt9(std::int32_t wordSize, const std::uint8_t (&config)[4], At9Format& format) {
|
||||
if (!ValidAt9Config(config)) return nullptr;
|
||||
auto decoder = std::make_unique<At9Decoder>(wordSize, config);
|
||||
const auto& info = decoder->Info();
|
||||
format = {static_cast<std::uint32_t>(info.channels), static_cast<std::uint32_t>(info.samplingRate), static_cast<std::uint32_t>(info.superframeSize),
|
||||
static_cast<std::uint32_t>(info.framesInSuperframe), static_cast<std::uint32_t>(info.frameSamples)};
|
||||
return decoder;
|
||||
}
|
||||
|
||||
bool ValidAt9Config(const std::uint8_t (&config)[4]) {
|
||||
void* handle = Atrac9GetHandle();
|
||||
std::uint8_t copy[4];
|
||||
std::memcpy(copy, config, sizeof(copy));
|
||||
const bool valid = handle && Atrac9InitDecoder(handle, copy) == 0;
|
||||
if (handle) Atrac9ReleaseHandle(handle);
|
||||
return valid;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEAUDIODEC_SRC_CODECS_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEAUDIODEC_SRC_CODECS_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace Audiodec {
|
||||
|
||||
constexpr std::int32_t WORD_SIZE_16BIT = 1;
|
||||
constexpr std::int32_t WORD_SIZE_FLOAT = 2;
|
||||
|
||||
enum class DecodeStatus {
|
||||
Ok,
|
||||
InvalidData,
|
||||
PartialInput,
|
||||
NotEnoughRoom,
|
||||
};
|
||||
|
||||
struct Mp3Header {
|
||||
std::uint32_t word;
|
||||
std::size_t frameBytes;
|
||||
bool crc;
|
||||
std::uint8_t mode;
|
||||
std::uint8_t modeExtension;
|
||||
std::uint8_t copyright;
|
||||
std::uint8_t original;
|
||||
std::uint8_t emphasis;
|
||||
};
|
||||
|
||||
struct DecodeResult {
|
||||
DecodeStatus status = DecodeStatus::Ok;
|
||||
std::size_t consumed = 0;
|
||||
std::size_t produced = 0;
|
||||
std::uint32_t channels = 0;
|
||||
std::uint32_t sampleRate = 0;
|
||||
bool highEfficiency = false;
|
||||
Mp3Header mp3{};
|
||||
};
|
||||
|
||||
struct At9Format {
|
||||
std::uint32_t channels;
|
||||
std::uint32_t sampleRate;
|
||||
std::uint32_t superframeSize;
|
||||
std::uint32_t framesInSuperframe;
|
||||
std::uint32_t frameSamples;
|
||||
};
|
||||
|
||||
class Decoder {
|
||||
public:
|
||||
virtual ~Decoder() = default;
|
||||
virtual DecodeResult Decode(const std::uint8_t* data, std::size_t size, std::uint8_t* pcm, std::size_t pcmSize) = 0;
|
||||
virtual void Reset() = 0;
|
||||
};
|
||||
|
||||
bool ParseMp3Header(const std::uint8_t* data, std::size_t size, Mp3Header& header);
|
||||
std::uint32_t AacSampleRate(std::uint32_t index);
|
||||
bool ValidAt9Config(const std::uint8_t (&config)[4]);
|
||||
std::unique_ptr<Decoder> CreateMp3(std::int32_t wordSize);
|
||||
std::unique_ptr<Decoder> CreateAac(std::int32_t wordSize, bool adts, std::uint32_t samplingFreqIndex, std::uint32_t channels);
|
||||
std::unique_ptr<Decoder> CreateAt9(std::int32_t wordSize, const std::uint8_t (&config)[4], At9Format& format);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
extern "C" {
|
||||
std::int32_t APS5_VABI sceAudiodecInitLibrary(std::uint32_t);
|
||||
std::int32_t APS5_VABI sceAudiodecTermLibrary(std::uint32_t);
|
||||
std::int32_t APS5_VABI sceAudiodecCreateDecoder(AudiodecCtrl*, std::uint32_t);
|
||||
std::int32_t APS5_VABI sceAudiodecDeleteDecoder(std::int32_t);
|
||||
std::int32_t APS5_VABI sceAudiodecDecode(std::int32_t, AudiodecCtrl*);
|
||||
std::int32_t APS5_VABI sceAudiodecClearContext(std::int32_t);
|
||||
}
|
||||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
namespace {
|
||||
|
||||
const std::uint8_t AAC_ADTS[] = {
|
||||
0xFF, 0xF1, 0x4C, 0x80, 0x0F, 0x9F, 0xFC, 0xDC, 0x00, 0x4C, 0x61, 0x76, 0x63, 0x36, 0x33, 0x2E, 0x31, 0x2E, 0x31, 0x30, 0x32, 0x00, 0x42, 0x4D,
|
||||
0x9F, 0xFF, 0xFF, 0xE1, 0x40, 0xC9, 0xA1, 0xA2, 0xF4, 0x44, 0x23, 0x8E, 0xEA, 0x91, 0x95, 0xA9, 0x2E, 0xE5, 0xDC, 0x92, 0x3B, 0x65, 0xC0, 0xCE,
|
||||
0xCF, 0x8E, 0x0C, 0xEC, 0xEC, 0xEC, 0xEC, 0xED, 0x59, 0x49, 0x61, 0xBC, 0xA3, 0x44, 0xEA, 0x53, 0xA7, 0x08, 0x22, 0x06, 0x36, 0x6C, 0xD9, 0xB0,
|
||||
0x64, 0x48, 0xA5, 0x06, 0x36, 0x6C, 0x18, 0x19, 0x10, 0x30, 0x31, 0xB3, 0x66, 0xCD, 0x83, 0x22, 0x45, 0x29, 0xB3, 0x72, 0xCB, 0x14, 0x51, 0x45,
|
||||
0x14, 0x51, 0x45, 0x14, 0x51, 0x45, 0x40, 0x37, 0xD1, 0x78, 0x8E, 0x3B, 0xAA, 0x46, 0x56, 0xA4, 0xBB, 0x97, 0x72, 0x48, 0xED, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0E, 0xFF, 0xF1, 0x4C, 0x80, 0x18, 0xFF, 0xFC, 0x21, 0x48, 0x6C, 0xFE, 0x7E, 0x7E, 0x7E, 0x47, 0x51, 0x4B, 0xAA, 0xC9, 0x71,
|
||||
0x64, 0xB8, 0xB2, 0x5C, 0x3F, 0xFE, 0xD7, 0xFD, 0xFE, 0xB8, 0xD3, 0xBF, 0x5D, 0x7F, 0xFD, 0xFF, 0xFA, 0xFD, 0xFA, 0xE3, 0x8D, 0x7C, 0x7F, 0xFE,
|
||||
0x4F, 0xFA, 0xFD, 0xFA, 0xE3, 0x8D, 0x7E, 0xFF, 0xFF, 0x5F, 0xFD, 0xBE, 0xFD, 0x71, 0xD7, 0x01, 0xB7, 0x49, 0xCD, 0xA0, 0x90, 0x90, 0x96, 0xAF,
|
||||
0x1B, 0x1C, 0xBC, 0xFB, 0x50, 0x49, 0xFB, 0x71, 0x11, 0x44, 0x26, 0x28, 0x81, 0xA2, 0x53, 0x71, 0x2B, 0xE9, 0x11, 0x4E, 0x3E, 0x9C, 0xF1, 0x14,
|
||||
0xE3, 0xE9, 0xF4, 0x88, 0xA7, 0x1F, 0x4F, 0xA5, 0x8B, 0x0C, 0x9E, 0x62, 0x69, 0x10, 0xD3, 0x20, 0xBE, 0x58, 0x4A, 0x6D, 0x72, 0x38, 0xAE, 0x2C,
|
||||
0x4C, 0x51, 0x03, 0x44, 0xA6, 0xE2, 0x57, 0xD2, 0x22, 0x9C, 0x7D, 0x39, 0xE2, 0x29, 0xC7, 0xD3, 0xE9, 0x11, 0x4E, 0x3E, 0x9F, 0x42, 0x38, 0x42,
|
||||
0x93, 0xE5, 0x39, 0x62, 0x28, 0xA2, 0x88, 0xA2, 0x8A, 0x28, 0xA3, 0x87, 0x6A, 0x1D, 0xE8, 0x77, 0xA1, 0xDE, 0x9F, 0xFF, 0x6B, 0xFE, 0xFF, 0x5C,
|
||||
0x69, 0xDF, 0xAE, 0xBF, 0xFE, 0xFF, 0xFD, 0x7E, 0xFD, 0x70, 0x7F, 0x8F, 0xFF, 0xC9, 0xFF, 0x5F, 0xBF, 0x5C, 0x1F, 0xF3, 0xFF, 0xF5, 0xFF, 0xDB,
|
||||
0xEF, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xF1, 0x4C, 0x80, 0x0B, 0xFF, 0xFC, 0x21, 0x77, 0x4F, 0xFF, 0xFF, 0xF0,
|
||||
0x24, 0x0A, 0xFA, 0x67, 0x34, 0xD7, 0x8E, 0xF7, 0xBC, 0xFF, 0xB7, 0xFE, 0xFA, 0xFA, 0x74, 0xD5, 0xAE, 0xE3, 0xBE, 0x4B, 0x40, 0xEF, 0xBF, 0xBF,
|
||||
0xBC, 0x2F, 0xFC, 0x7C, 0x7C, 0x66, 0x40, 0xF8, 0xF8, 0xF8, 0x03, 0xDF, 0xDE, 0x11, 0xF1, 0xF0, 0xD8, 0xCF, 0xEF, 0xA0, 0x64, 0xF8, 0x6C, 0x67,
|
||||
0xF7, 0x84, 0x64, 0xF8, 0x6C, 0x67, 0xF7, 0x29, 0x01, 0xBE, 0x99, 0xCD, 0x35, 0xE3, 0xBD, 0xEF, 0x3F, 0xED, 0xFF, 0xBE, 0xBE, 0x9D, 0x35, 0x6B,
|
||||
0xB8, 0xEF, 0x92, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xF1, 0x4C, 0x80, 0x0C, 0xFF, 0xFC, 0x21, 0x17, 0x4F, 0xFF, 0xFF, 0xF8, 0x26,
|
||||
0x0A, 0x88, 0x5D, 0x20, 0xA5, 0x56, 0x67, 0x3F, 0xF6, 0xFF, 0xDF, 0xAE, 0x2E, 0xE5, 0xC9, 0x72, 0x48, 0xE9, 0x90, 0x31, 0xAF, 0x1A, 0xF1, 0xAF,
|
||||
0xBA, 0xBE, 0xE5, 0xE0, 0x23, 0x2A, 0xCA, 0xBC, 0x6B, 0xC6, 0xBC, 0x6B, 0xC6, 0xBC, 0x6B, 0xC6, 0xBC, 0x6B, 0xC6, 0xB4, 0x69, 0x28, 0xA4, 0x46,
|
||||
0x92, 0x8A, 0x44, 0x69, 0x28, 0xA4, 0x46, 0x91, 0x1D, 0x24, 0x69, 0x11, 0xA4, 0x47, 0x30, 0x38, 0xF0, 0xE9, 0x05, 0x2A, 0xB3, 0x39, 0xFF, 0xB7,
|
||||
0xFE, 0xFD, 0x71, 0x77, 0x2E, 0x4B, 0x92, 0x1D, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0xFF, 0xF1, 0x4C, 0x80, 0x0C, 0xFF, 0xFC,
|
||||
0x21, 0x17, 0x4F, 0xFF, 0xFF, 0xF8, 0x28, 0x59, 0xC4, 0xAD, 0x28, 0x99, 0x29, 0xAE, 0xF7, 0xF5, 0x6A, 0xB9, 0x2D, 0x25, 0xC7, 0x5C, 0x92, 0x02,
|
||||
0x13, 0x42, 0x16, 0x62, 0x16, 0x67, 0x76, 0x6D, 0xDA, 0xA9, 0x9A, 0xBF, 0xE3, 0xC9, 0x27, 0x5E, 0x8A, 0xE4, 0xA6, 0x6A, 0xE4, 0xA5, 0x1C, 0x73,
|
||||
0x35, 0x9B, 0x46, 0x6D, 0x19, 0xB4, 0x39, 0xA3, 0x36, 0x8C, 0xCD, 0x38, 0xD3, 0x8D, 0x66, 0x69, 0xC6, 0xB3, 0x68, 0xCD, 0xA3, 0x36, 0x8C, 0xDA,
|
||||
0x33, 0x50, 0x8E, 0x34, 0xA2, 0x64, 0xA6, 0xBB, 0xDF, 0xD5, 0xAA, 0xE4, 0xB4, 0x97, 0x1D, 0x72, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C,
|
||||
};
|
||||
|
||||
const std::uint8_t MP3_MONO[] = {
|
||||
0xFF, 0xFB, 0x14, 0xC4, 0x00, 0x00, 0x03, 0xB0, 0x21, 0x5E, 0xF4, 0x30, 0x80, 0x30, 0x98, 0x88, 0xA9, 0x83, 0x34, 0x70, 0x00, 0x18, 0x89, 0xCB,
|
||||
0x20, 0x00, 0x3B, 0xBB, 0xB9, 0x9F, 0xD7, 0x0E, 0x06, 0x06, 0x06, 0x2C, 0x3E, 0xEF, 0x83, 0xE7, 0xF1, 0x00, 0x63, 0x83, 0xFD, 0x4E, 0xFC, 0xFF,
|
||||
0x29, 0xE1, 0xF7, 0xD9, 0x87, 0x7E, 0xFF, 0x65, 0x9E, 0x21, 0xAF, 0x46, 0x58, 0xBB, 0x46, 0x58, 0xBA, 0x64, 0x40, 0x21, 0xB2, 0x6C, 0xFA, 0x6C,
|
||||
0xF8, 0x42, 0x0E, 0x7C, 0x1E, 0x8F, 0x0D, 0xBF, 0x1B, 0x02, 0xBC, 0x24, 0x0D, 0x37, 0xCA, 0x9D, 0x52, 0xFD, 0x77, 0xE0, 0x16, 0xBA, 0x02, 0x04,
|
||||
0xFF, 0xFB, 0x14, 0xC4, 0x03, 0x83, 0xC4, 0xE0, 0x2B, 0x28, 0x1D, 0xE4, 0x80, 0x20, 0x5D, 0x95, 0x60, 0x41, 0x70, 0x16, 0xC8, 0xD3, 0x02, 0x00,
|
||||
0x2F, 0x12, 0x02, 0xD0, 0x40, 0x1D, 0x98, 0x4D, 0x06, 0x99, 0x84, 0xF2, 0x68, 0x98, 0xBF, 0x86, 0xD1, 0x84, 0xC8, 0x18, 0x2A, 0xA9, 0x79, 0x50,
|
||||
0x85, 0xDE, 0x14, 0x88, 0x9A, 0xF8, 0x44, 0x02, 0xB0, 0x60, 0x62, 0x5E, 0x11, 0xE9, 0xDC, 0x18, 0x65, 0x8F, 0xFF, 0x7F, 0xFC, 0xB3, 0xF8, 0x58,
|
||||
0xEF, 0xFF, 0xFF, 0xE1, 0x95, 0xF8, 0x44, 0x02, 0xB0, 0x60, 0x62, 0x5E, 0x11, 0xE9, 0xDC, 0x18, 0x65, 0x8F, 0xFF, 0x7F, 0xFC, 0xB3, 0xF8, 0x58,
|
||||
0xFF, 0xFB, 0x14, 0xC4, 0x09, 0x83, 0xC2, 0xEC, 0xAB, 0x02, 0x0B, 0x80, 0xB6, 0x40, 0x5D, 0x95, 0x60, 0x41, 0x70, 0x16, 0xC8, 0xEF, 0xFF, 0xFF,
|
||||
0xE1, 0x9F, 0x08, 0x80, 0x56, 0x0C, 0x0C, 0x4B, 0xC2, 0x3D, 0x3B, 0x83, 0x0C, 0xB1, 0xFF, 0xEF, 0xFF, 0x96, 0x7F, 0x0B, 0x1D, 0xFF, 0xFF, 0xFC,
|
||||
0x31, 0xF8, 0x44, 0x02, 0xB0, 0x60, 0x62, 0x5E, 0x11, 0xE9, 0xDC, 0x18, 0x65, 0x8F, 0xFF, 0x7F, 0xFC, 0xB3, 0xF8, 0x58, 0xEF, 0xFF, 0xFF, 0xE1,
|
||||
0x9F, 0x08, 0x80, 0x56, 0x0C, 0x0C, 0x4B, 0xC2, 0x3D, 0x3B, 0x83, 0x0C, 0xB1, 0xFF, 0xEF, 0xFF, 0x96, 0x7F, 0x0B, 0x1D, 0xFF, 0xFF, 0xFC, 0x31,
|
||||
0xFF, 0xFB, 0x14, 0xC4, 0x17, 0x83, 0xC2, 0xEC, 0xAB, 0x02, 0x0B, 0x80, 0xB6, 0x40, 0x5D, 0x95, 0x60, 0x41, 0x70, 0x16, 0xC8, 0x96, 0x55, 0x8D,
|
||||
0xBF, 0xE9, 0xD8, 0x50, 0x01, 0xA6, 0x17, 0x80, 0x34, 0x61, 0x00, 0x17, 0xC6, 0x15, 0x40, 0xD4, 0x61, 0x56, 0x25, 0x66, 0x3E, 0xA4, 0x92, 0x62,
|
||||
0xC3, 0xED, 0x26, 0x23, 0xA5, 0x5C, 0x63, 0x10, 0x01, 0xC6, 0x0F, 0x20, 0x9E, 0x60, 0x62, 0x08, 0x26, 0x06, 0xE0, 0x12, 0xD6, 0x22, 0x02, 0x6C,
|
||||
0x93, 0xC7, 0x04, 0x3E, 0xBF, 0xFE, 0xBF, 0xFF, 0x72, 0x62, 0x28, 0x22, 0xD1, 0x2F, 0xEF, 0xFD, 0xF1, 0x15, 0x09, 0x83, 0x08, 0x8C, 0x00, 0xFE,
|
||||
0xFF, 0xFB, 0x14, 0xC4, 0x25, 0x80, 0x07, 0x74, 0x2D, 0x16, 0x15, 0xE7, 0x80, 0x00, 0xF2, 0x0C, 0xE8, 0x83, 0x36, 0xB0, 0x00, 0xC4, 0x42, 0x82,
|
||||
0xC0, 0x86, 0x24, 0x2A, 0x02, 0x11, 0xFF, 0x02, 0x70, 0xF6, 0x07, 0x80, 0x9B, 0xCB, 0xCD, 0x0D, 0x00, 0x7C, 0x49, 0x1D, 0xBF, 0xD3, 0xDE, 0x3B,
|
||||
0x52, 0x36, 0xFE, 0xFA, 0xF3, 0x66, 0xB5, 0x13, 0xDF, 0x06, 0x84, 0xA1, 0x2F, 0xCB, 0x05, 0x41, 0x5F, 0xF5, 0x2A, 0x4C, 0x41, 0x4D, 0x45, 0x34,
|
||||
0x2E, 0x30, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xFF, 0xFB, 0x14, 0xC4, 0x0E, 0x83, 0xC0, 0x00, 0x01, 0xA4, 0x1C, 0x00, 0x00, 0x20, 0x00, 0x00, 0x34, 0x80, 0x00, 0x00, 0x04, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
};
|
||||
|
||||
constexpr std::int32_t errorApiFail = static_cast<std::int32_t>(0x807F0000);
|
||||
constexpr std::int32_t errorInvalidType = static_cast<std::int32_t>(0x807F0001);
|
||||
constexpr std::int32_t errorArg = static_cast<std::int32_t>(0x807F0002);
|
||||
constexpr std::int32_t errorInvalidParamSize = static_cast<std::int32_t>(0x807F0004);
|
||||
constexpr std::int32_t errorInvalidBsiInfoSize = static_cast<std::int32_t>(0x807F0005);
|
||||
constexpr std::int32_t errorInvalidCtrlPointer = static_cast<std::int32_t>(0x807F0008);
|
||||
constexpr std::int32_t errorInvalidAuInfoPointer = static_cast<std::int32_t>(0x807F000B);
|
||||
constexpr std::int32_t errorInvalidHandle = static_cast<std::int32_t>(0x807F000F);
|
||||
constexpr std::int32_t errorInvalidWordLength = static_cast<std::int32_t>(0x807F0010);
|
||||
constexpr std::int32_t errorInvalidAuSize = static_cast<std::int32_t>(0x807F0011);
|
||||
constexpr std::int32_t errorInvalidPcmSize = static_cast<std::int32_t>(0x807F0012);
|
||||
constexpr std::int32_t errorInvalidConfigNumber = static_cast<std::int32_t>(0x807F0303);
|
||||
constexpr std::int32_t errorInvalidAt9Config = static_cast<std::int32_t>(0x807F1000);
|
||||
|
||||
struct Streams {
|
||||
AudiodecAuInfo au{sizeof(AudiodecAuInfo), nullptr, 0};
|
||||
AudiodecPcmItem pcm{sizeof(AudiodecPcmItem), nullptr, 0};
|
||||
std::vector<std::uint8_t> output = std::vector<std::uint8_t>(16384);
|
||||
};
|
||||
|
||||
std::int32_t Decode(std::int32_t handle, AudiodecCtrl& ctrl, Streams& streams, const std::uint8_t* data, std::size_t size, std::uint32_t room = 16384) {
|
||||
streams.au.p_au_addr = const_cast<std::uint8_t*>(data);
|
||||
streams.au.ui_au_size = static_cast<std::uint32_t>(size);
|
||||
streams.pcm.p_pcm_addr = streams.output.data();
|
||||
streams.pcm.ui_pcm_size = room;
|
||||
ctrl.pAuInfo = &streams.au;
|
||||
ctrl.pPcmItem = &streams.pcm;
|
||||
return sceAudiodecDecode(handle, &ctrl);
|
||||
}
|
||||
|
||||
int ZeroCrossings(const std::int16_t* samples, std::size_t count, std::size_t stride) {
|
||||
int crossings = 0;
|
||||
for (std::size_t i = stride; i < count * stride; i += stride) crossings += (samples[i - stride] < 0) != (samples[i] < 0);
|
||||
return crossings;
|
||||
}
|
||||
|
||||
std::size_t AdtsLength(const std::uint8_t* frame) {
|
||||
return static_cast<std::size_t>((frame[3] & 3) << 11 | frame[4] << 3 | frame[5] >> 5);
|
||||
}
|
||||
|
||||
void TestAac(bool adts) {
|
||||
AudiodecParamM4aac param{sizeof(param), 1, adts ? 1u : 2u, 3, 2, 0};
|
||||
AudiodecM4aacInfo info{sizeof(info), 0, 0, 0, 0};
|
||||
AudiodecCtrl ctrl{¶m, &info, nullptr, nullptr};
|
||||
const std::int32_t handle = sceAudiodecCreateDecoder(&ctrl, 3);
|
||||
Require(handle > 0 && info.ui_sampling_freq == 48000 && info.ui_number_of_channels == 2);
|
||||
Streams streams;
|
||||
int crossings = 0;
|
||||
for (std::size_t offset = 0, frame = 0; offset < sizeof(AAC_ADTS); offset += AdtsLength(AAC_ADTS + offset), ++frame) {
|
||||
const std::size_t length = AdtsLength(AAC_ADTS + offset);
|
||||
const std::uint8_t* data = adts ? AAC_ADTS + offset : AAC_ADTS + offset + 7;
|
||||
const std::size_t size = adts ? sizeof(AAC_ADTS) - offset : length - 7;
|
||||
Require(Decode(handle, ctrl, streams, data, size) == 0);
|
||||
Require(streams.au.ui_au_size == (adts ? length : length - 7));
|
||||
Require(streams.pcm.ui_pcm_size == 1024 * 2 * 2 && info.i_result == 0);
|
||||
Require(info.ui_sampling_freq == 48000 && info.ui_number_of_channels == 2 && info.ui_heaac == 0);
|
||||
if (frame == 2) crossings = ZeroCrossings(reinterpret_cast<const std::int16_t*>(streams.output.data()), 1024, 2);
|
||||
}
|
||||
Require(crossings >= 38 && crossings <= 46);
|
||||
Require(Decode(handle, ctrl, streams, AAC_ADTS, sizeof(AAC_ADTS), 1024) == errorInvalidPcmSize);
|
||||
if (adts) {
|
||||
Require(Decode(handle, ctrl, streams, AAC_ADTS, 50) == errorApiFail && info.i_result == -5);
|
||||
Require(Decode(handle, ctrl, streams, AAC_ADTS + 1, 50) == errorApiFail && info.i_result == -4);
|
||||
}
|
||||
Require(sceAudiodecClearContext(handle) == 0);
|
||||
Require(sceAudiodecDeleteDecoder(handle) == 0);
|
||||
}
|
||||
|
||||
void TestMp3() {
|
||||
AudiodecParamMp3 param{sizeof(param), 2};
|
||||
AudiodecMp3Info info{};
|
||||
info.ui_size = sizeof(info);
|
||||
AudiodecCtrl ctrl{¶m, &info, nullptr, nullptr};
|
||||
const std::int32_t handle = sceAudiodecCreateDecoder(&ctrl, 2);
|
||||
Require(handle > 0);
|
||||
Streams streams;
|
||||
std::size_t offset = 0;
|
||||
int frames = 0;
|
||||
float peak = 0;
|
||||
while (offset < sizeof(MP3_MONO)) {
|
||||
Require(Decode(handle, ctrl, streams, MP3_MONO + offset, sizeof(MP3_MONO) - offset) == 0);
|
||||
Require(streams.au.ui_au_size == 144 * 32000 / 48000 + (MP3_MONO[offset + 2] >> 1 & 1));
|
||||
Require(streams.pcm.ui_pcm_size == 1152 * 4);
|
||||
Require(info.ui_header >> 21 == 0x7FF && info.uc_mode == 3 && info.i_result == 0);
|
||||
for (std::size_t i = 0; i < 1152; ++i) peak = std::fmax(peak, std::fabs(reinterpret_cast<const float*>(streams.output.data())[i]));
|
||||
offset += streams.au.ui_au_size;
|
||||
++frames;
|
||||
}
|
||||
Require(offset == sizeof(MP3_MONO) && frames == 6 && peak > 0.1f && peak < 0.15f);
|
||||
Require(Decode(handle, ctrl, streams, MP3_MONO, 10) == errorApiFail);
|
||||
Require(Decode(handle, ctrl, streams, MP3_MONO + 1, 100) == errorApiFail);
|
||||
Require(sceAudiodecDeleteDecoder(handle) == 0);
|
||||
}
|
||||
|
||||
void TestAt9() {
|
||||
AudiodecParamAt9 param{sizeof(param), 1, {0xFE, 0x72, 0x1F, 0xF0}};
|
||||
AudiodecAt9Info info{};
|
||||
info.ui_size = sizeof(info);
|
||||
AudiodecCtrl ctrl{¶m, &info, nullptr, nullptr};
|
||||
const std::int32_t handle = sceAudiodecCreateDecoder(&ctrl, 1);
|
||||
Require(handle > 0);
|
||||
Require(info.ui_channel == 2 && info.ui_sampling_rate == 48000 && info.ui_frame_samples == 256);
|
||||
Require(info.ui_super_frame_size == 1024 && info.ui_frames_in_super_frame == 4 && info.ui_bitrate == 384000);
|
||||
Streams streams;
|
||||
const std::vector<std::uint8_t> silence(1024);
|
||||
Require(Decode(handle, ctrl, streams, silence.data(), 1023) == errorApiFail);
|
||||
Require(Decode(handle, ctrl, streams, silence.data(), silence.size(), 4095) == errorInvalidPcmSize);
|
||||
Require(Decode(handle, ctrl, streams, silence.data(), silence.size()) == errorApiFail);
|
||||
Require(sceAudiodecDeleteDecoder(handle) == 0);
|
||||
param.ui_config_data[0] = 0xFD;
|
||||
Require(sceAudiodecCreateDecoder(&ctrl, 1) == errorInvalidAt9Config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
Require(sceAudiodecInitLibrary(4) == errorInvalidType);
|
||||
Require(sceAudiodecTermLibrary(0) == errorInvalidType);
|
||||
|
||||
AudiodecParamMp3 mp3{sizeof(mp3), 1};
|
||||
AudiodecMp3Info mp3Info{};
|
||||
mp3Info.ui_size = sizeof(mp3Info);
|
||||
AudiodecCtrl ctrl{&mp3, &mp3Info, nullptr, nullptr};
|
||||
Require(sceAudiodecCreateDecoder(&ctrl, 2) == errorArg);
|
||||
for (std::uint32_t type = 1; type <= 3; ++type) Require(sceAudiodecInitLibrary(type) == 0);
|
||||
Require(sceAudiodecCreateDecoder(nullptr, 2) == errorInvalidCtrlPointer);
|
||||
Require(sceAudiodecCreateDecoder(&ctrl, 5) == errorInvalidType);
|
||||
mp3.ui_size = 12;
|
||||
Require(sceAudiodecCreateDecoder(&ctrl, 2) == errorInvalidParamSize);
|
||||
mp3.ui_size = sizeof(mp3);
|
||||
mp3Info.ui_size = 16;
|
||||
Require(sceAudiodecCreateDecoder(&ctrl, 2) == errorInvalidBsiInfoSize);
|
||||
mp3Info.ui_size = sizeof(mp3Info);
|
||||
mp3.i_bw_pcm = 3;
|
||||
Require(sceAudiodecCreateDecoder(&ctrl, 2) == errorInvalidWordLength);
|
||||
mp3.i_bw_pcm = 1;
|
||||
AudiodecParamM4aac aac{sizeof(aac), 1, 3, 3, 2, 0};
|
||||
AudiodecM4aacInfo aacInfo{sizeof(aacInfo), 0, 0, 0, 0};
|
||||
AudiodecCtrl aacCtrl{&aac, &aacInfo, nullptr, nullptr};
|
||||
Require(sceAudiodecCreateDecoder(&aacCtrl, 3) == errorInvalidConfigNumber);
|
||||
|
||||
const std::int32_t handle = sceAudiodecCreateDecoder(&ctrl, 2);
|
||||
Require(handle > 0);
|
||||
Streams streams;
|
||||
Require(sceAudiodecDecode(handle, &ctrl) == errorInvalidAuInfoPointer);
|
||||
Require(Decode(handle, ctrl, streams, MP3_MONO, 0) == errorInvalidAuSize);
|
||||
Require(Decode(handle, ctrl, streams, MP3_MONO, sizeof(MP3_MONO), 0) == errorInvalidPcmSize);
|
||||
Require(Decode(handle + 1, ctrl, streams, MP3_MONO, sizeof(MP3_MONO)) == errorInvalidHandle);
|
||||
Require(sceAudiodecDeleteDecoder(handle) == 0);
|
||||
Require(sceAudiodecDeleteDecoder(handle) == errorInvalidHandle);
|
||||
Require(sceAudiodecClearContext(handle) == errorInvalidHandle);
|
||||
|
||||
TestAac(true);
|
||||
TestAac(false);
|
||||
TestMp3();
|
||||
TestAt9();
|
||||
for (std::uint32_t type = 1; type <= 3; ++type) Require(sceAudiodecTermLibrary(type) == 0);
|
||||
}
|
||||
|
|
@ -46,4 +46,5 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- The libc [SSE4a trap emulation](../../core/libs/prx/libc/src/specifics/windows/Sse4aEmulation.hpp) on Windows is superseded by `--to-intel` and remains only until the relinked title has been verified without it.
|
||||
- `--to-intel` guest module trampolines are covered only by a synthetic relinker test; no game title has been verified with them on Linux or Windows.
|
||||
- [sceKeyboardGetKey2Char](../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp) (libSceKeyboard) translates only the 101-key (US) arrangement and throws for the 106-key (Japanese) one; Ctrl and Alt do not change the character.
|
||||
- [libSceAudiodec](../../core/libs/prx/libSceAudiodec/Export.cpp) throws for the 24-bit PCM word size (`iBwPcm` 0): its sample layout is unknown. ATRAC9 decoding is covered only by configuration and error tests, as no ATRAC9 encoder is available for a fixture.
|
||||
- [libScePlayerInvitationDialog](../../core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.cpp) simulates dialog completion without displaying UI or sending invitations; its parameter ABI remains unverified.
|
||||
|
|
|
|||
Loading…
Reference in New Issue