mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 19:07:01 +02:00
audio: parse NGS2 ATRAC9 waveform metadata
This commit is contained in:
+145
-10
@@ -21,6 +21,8 @@
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "libatrac9.h"
|
||||
|
||||
namespace Libs::Audio {
|
||||
|
||||
namespace {
|
||||
@@ -1360,6 +1362,17 @@ namespace Ngs2 {
|
||||
|
||||
LIB_NAME("Ngs2", "Ngs2");
|
||||
|
||||
constexpr int32_t NGS2_ERROR_INVALID_OUT_ADDRESS =
|
||||
static_cast<int32_t>(0x804a8010u);
|
||||
constexpr int32_t NGS2_ERROR_INVALID_WAVEFORM_DATA =
|
||||
static_cast<int32_t>(0x804a8430u);
|
||||
constexpr int32_t NGS2_ERROR_INVALID_WAVEFORM_FORMAT =
|
||||
static_cast<int32_t>(0x804a8431u);
|
||||
constexpr int32_t NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT =
|
||||
static_cast<int32_t>(0x804a8432u);
|
||||
|
||||
constexpr uint32_t NGS2_WAVEFORM_TYPE_ATRAC9 = 0x40;
|
||||
|
||||
struct Ngs2SystemOption {
|
||||
size_t size = 0;
|
||||
char name[64] = {};
|
||||
@@ -1773,6 +1786,9 @@ static_assert(sizeof(Ngs2VoiceState) == 8);
|
||||
static_assert(sizeof(Ngs2SubmixerVoiceState) == 20);
|
||||
static_assert(sizeof(Ngs2CustomMasteringVoiceState) == 16);
|
||||
static_assert(sizeof(Ngs2SamplerVoiceState) == 56);
|
||||
static_assert(sizeof(Ngs2WaveformFormat) == 24);
|
||||
static_assert(sizeof(Ngs2WaveformBlock) == 40);
|
||||
static_assert(sizeof(Ngs2WaveformInfo) == 232);
|
||||
|
||||
static uint32_t Ngs2GetStateFlags(const Ngs2VoiceInternal* voice) {
|
||||
switch (voice->state) {
|
||||
@@ -2443,24 +2459,143 @@ int KYTY_SYSV_ABI Ngs2SystemRender(uintptr_t system_handle, const Ngs2RenderBuff
|
||||
return OK;
|
||||
}
|
||||
|
||||
static uint16_t Ngs2ReadLe16(const uint8_t* data) {
|
||||
return static_cast<uint16_t>(data[0]) | (static_cast<uint16_t>(data[1]) << 8u);
|
||||
}
|
||||
|
||||
static uint32_t Ngs2ReadLe32(const uint8_t* data) {
|
||||
return static_cast<uint32_t>(data[0]) | (static_cast<uint32_t>(data[1]) << 8u) |
|
||||
(static_cast<uint32_t>(data[2]) << 16u) | (static_cast<uint32_t>(data[3]) << 24u);
|
||||
}
|
||||
|
||||
static bool Ngs2FourCcEquals(const uint8_t* data, const char* four_cc) {
|
||||
return std::memcmp(data, four_cc, 4) == 0;
|
||||
}
|
||||
|
||||
static int Ngs2ParseAtrac9Riff(const void* data, size_t data_size, Ngs2WaveformInfo* info) {
|
||||
static constexpr uint8_t ATRAC9_GUID[16] = {0xd2, 0x42, 0xe1, 0x47, 0xba, 0x36,
|
||||
0x8d, 0x4d, 0x88, 0xfc, 0x61, 0x65,
|
||||
0x4f, 0x8c, 0x83, 0x6c};
|
||||
const auto* bytes = static_cast<const uint8_t*>(data);
|
||||
if (bytes == nullptr || data_size < 12) {
|
||||
return NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
}
|
||||
if (!Ngs2FourCcEquals(bytes, "RIFF") || !Ngs2FourCcEquals(bytes + 8, "WAVE")) {
|
||||
return NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
|
||||
}
|
||||
|
||||
const uint64_t riff_end64 = 8ull + Ngs2ReadLe32(bytes + 4);
|
||||
if (riff_end64 < 12 || riff_end64 > data_size) {
|
||||
return NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
}
|
||||
const auto riff_end = static_cast<size_t>(riff_end64);
|
||||
|
||||
const uint8_t* format = nullptr;
|
||||
const uint8_t* fact = nullptr;
|
||||
size_t waveform_offset = 0;
|
||||
uint32_t waveform_size = 0;
|
||||
|
||||
for (size_t offset = 12; offset + 8 <= riff_end;) {
|
||||
const auto* chunk = bytes + offset;
|
||||
const auto chunk_size = static_cast<size_t>(Ngs2ReadLe32(chunk + 4));
|
||||
const auto payload = offset + 8;
|
||||
if (chunk_size > riff_end - payload) {
|
||||
return NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
}
|
||||
|
||||
if (Ngs2FourCcEquals(chunk, "fmt ")) {
|
||||
if (chunk_size < 52) {
|
||||
return NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
|
||||
}
|
||||
format = bytes + payload;
|
||||
} else if (Ngs2FourCcEquals(chunk, "fact")) {
|
||||
if (chunk_size < 12) {
|
||||
return NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
|
||||
}
|
||||
fact = bytes + payload;
|
||||
} else if (Ngs2FourCcEquals(chunk, "data")) {
|
||||
if (payload > std::numeric_limits<uint32_t>::max()) {
|
||||
return NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
}
|
||||
waveform_offset = payload;
|
||||
waveform_size = static_cast<uint32_t>(chunk_size);
|
||||
}
|
||||
|
||||
const uint64_t next = static_cast<uint64_t>(payload) + chunk_size + (chunk_size & 1u);
|
||||
if (next > riff_end) {
|
||||
return NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
}
|
||||
offset = static_cast<size_t>(next);
|
||||
}
|
||||
|
||||
if (format == nullptr || fact == nullptr || waveform_offset == 0) {
|
||||
return NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
|
||||
}
|
||||
if (Ngs2ReadLe16(format) != 0xfffe ||
|
||||
std::memcmp(format + 24, ATRAC9_GUID, sizeof(ATRAC9_GUID)) != 0) {
|
||||
return NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
|
||||
}
|
||||
|
||||
std::array<uint8_t, ATRAC9_CONFIG_DATA_SIZE> config {};
|
||||
std::memcpy(config.data(), format + 44, config.size());
|
||||
if (config[0] != 0xfe || (config[1] & 1u) != 0 || ((config[1] >> 1u) & 7u) >= 6u) {
|
||||
return NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
|
||||
}
|
||||
|
||||
Atrac9CodecInfo codec {};
|
||||
void* decoder = Atrac9GetHandle();
|
||||
const bool valid_codec =
|
||||
decoder != nullptr && Atrac9InitDecoder(decoder, config.data()) == 0 &&
|
||||
Atrac9GetCodecInfo(decoder, &codec) == 0 && codec.channels > 0 &&
|
||||
codec.samplingRate > 0 && codec.superframeSize > 0 && codec.framesInSuperframe > 0 &&
|
||||
codec.frameSamples > 0 && codec.superframeSize % codec.framesInSuperframe == 0;
|
||||
if (decoder != nullptr) {
|
||||
Atrac9ReleaseHandle(decoder);
|
||||
}
|
||||
const uint64_t frame_samples =
|
||||
static_cast<uint64_t>(codec.frameSamples) * static_cast<uint64_t>(codec.framesInSuperframe);
|
||||
if (!valid_codec || codec.channels != Ngs2ReadLe16(format + 2) ||
|
||||
codec.samplingRate != static_cast<int>(Ngs2ReadLe32(format + 4)) ||
|
||||
codec.superframeSize != Ngs2ReadLe16(format + 12) ||
|
||||
frame_samples != Ngs2ReadLe16(format + 18) ||
|
||||
frame_samples > std::numeric_limits<uint32_t>::max()) {
|
||||
return NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
|
||||
}
|
||||
|
||||
info->format.waveform_type = NGS2_WAVEFORM_TYPE_ATRAC9;
|
||||
info->format.num_channels = static_cast<uint32_t>(codec.channels);
|
||||
info->format.sample_rate = static_cast<uint32_t>(codec.samplingRate);
|
||||
std::memcpy(&info->format.config_data, config.data(), config.size());
|
||||
info->data_offset = static_cast<uint32_t>(waveform_offset);
|
||||
info->data_size = waveform_size;
|
||||
info->num_samples = Ngs2ReadLe32(fact);
|
||||
info->audio_unit_size = static_cast<uint32_t>(codec.superframeSize /
|
||||
codec.framesInSuperframe);
|
||||
info->num_audio_unit_samples = static_cast<uint32_t>(codec.frameSamples);
|
||||
info->num_audio_unit_per_frame = static_cast<uint32_t>(codec.framesInSuperframe);
|
||||
info->audio_frame_size = static_cast<uint32_t>(codec.superframeSize);
|
||||
info->num_audio_frame_samples = static_cast<uint32_t>(frame_samples);
|
||||
info->num_delay_samples = Ngs2ReadLe32(fact + 4);
|
||||
info->num_blocks = 1;
|
||||
info->blocks[0].data_offset = waveform_offset;
|
||||
info->blocks[0].data_size = waveform_size;
|
||||
info->blocks[0].num_skip_samples = Ngs2ReadLe32(fact + 8);
|
||||
info->blocks[0].num_samples = info->num_samples;
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI Ngs2ParseWaveformData(const void* data, size_t data_size,
|
||||
Ngs2WaveformInfo* info) {
|
||||
PRINT_NAME();
|
||||
LOGF("\t data = 0x%016" PRIx64 ", data_size = 0x%016" PRIx64 "\n",
|
||||
reinterpret_cast<uint64_t>(data), static_cast<uint64_t>(data_size));
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(info == nullptr);
|
||||
if (info == nullptr) {
|
||||
return NGS2_ERROR_INVALID_OUT_ADDRESS;
|
||||
}
|
||||
|
||||
std::memset(info, 0, sizeof(Ngs2WaveformInfo));
|
||||
info->format.waveform_type = 0x80;
|
||||
info->format.num_channels = 1;
|
||||
info->format.sample_rate = 48000;
|
||||
info->data_size =
|
||||
static_cast<uint32_t>(std::min<size_t>(data_size, std::numeric_limits<uint32_t>::max()));
|
||||
info->num_audio_unit_samples = 1;
|
||||
info->num_audio_unit_per_frame = 1;
|
||||
info->num_audio_frame_samples = 1;
|
||||
return OK;
|
||||
return Ngs2ParseAtrac9Riff(data, data_size, info);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI Ngs2CalcWaveformBlock(const Ngs2WaveformFormat* format, uint32_t sample_pos,
|
||||
|
||||
Reference in New Issue
Block a user