feat(libSceAjm): implement sceAjmBatchJobDecodeSplit and sceAjmBatchJobSetResampleParametersEx
sceAjmBatchJobDecodeSplit queues a split run job with the stream sideband, as sceAjmBatchJobDecode does for a single buffer pair. sceAjmBatchJobSetResampleParametersEx with a zero ratio change per sample sets the ratio as sceAjmBatchJobSetResampleParameters does; a non-zero change throws, since its ramp is unknown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
57f96fd2b3
commit
e8209307e9
|
|
@ -14,18 +14,6 @@ static void AjmStub(const char* name) {
|
|||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceAjmBatchJobDecodeSplit(AjmBatchInfo* info, uint32_t instance, const AjmBuffer* input_buffers, size_t input_buffers_num, const AjmBuffer* output_buffers, size_t output_buffers_num, void* result) {
|
||||
(void)info;
|
||||
(void)instance;
|
||||
(void)input_buffers;
|
||||
(void)input_buffers_num;
|
||||
(void)output_buffers;
|
||||
(void)output_buffers_num;
|
||||
(void)result;
|
||||
AjmStub(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAjmBatchJobEncode(AjmBatchInfo* info, uint32_t instance, const void* pcm_input, size_t pcm_input_size, void* bitstream_output, size_t bitstream_output_size, void* result) {
|
||||
(void)info;
|
||||
(void)instance;
|
||||
|
|
@ -38,17 +26,6 @@ int APS5_VABI sceAjmBatchJobEncode(AjmBatchInfo* info, uint32_t instance, const
|
|||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAjmBatchJobSetResampleParametersEx(AjmBatchInfo* info, uint32_t instance, float ratio_start, float ratio_change_per_sample, uint32_t flags, void* result) {
|
||||
(void)info;
|
||||
(void)instance;
|
||||
(void)ratio_start;
|
||||
(void)ratio_change_per_sample;
|
||||
(void)flags;
|
||||
(void)result;
|
||||
AjmStub(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* APS5_VABI sceAjmStrError(int error) {
|
||||
(void)error;
|
||||
AjmStub(__func__);
|
||||
|
|
|
|||
|
|
@ -1340,6 +1340,11 @@ int APS5_VABI sceAjmBatchJobSetResampleParameters(AjmBatchInfo* info, uint32_t i
|
|||
return Append(info, header, nullptr, nullptr);
|
||||
}
|
||||
|
||||
int APS5_VABI sceAjmBatchJobSetResampleParametersEx(AjmBatchInfo* info, uint32_t instance, float ratio_start, float ratio_change_per_sample, uint32_t flags, void* result) {
|
||||
if (ratio_change_per_sample != 0.0f) throw std::runtime_error("sceAjmBatchJobSetResampleParametersEx: a ratio change per sample is unsupported");
|
||||
return sceAjmBatchJobSetResampleParameters(info, instance, ratio_start, flags, result);
|
||||
}
|
||||
|
||||
int APS5_VABI sceAjmBatchJobGetResampleInfo(AjmBatchInfo* info, uint32_t instance, void* result) {
|
||||
return Append(info, MakeHeader(JobKind::GetResampleInfo, instance, result, sizeof(SidebandResult) + sizeof(SidebandResampleInfo)), nullptr, nullptr);
|
||||
}
|
||||
|
|
@ -1348,6 +1353,10 @@ int APS5_VABI sceAjmBatchJobDecode(AjmBatchInfo* info, uint32_t instance, const
|
|||
return sceAjmBatchJobRun(info, instance, SIDEBAND_STREAM, bitstream_input, bitstream_input_size, pcm_output, pcm_output_size, result, sizeof(SidebandResult) + sizeof(SidebandStream));
|
||||
}
|
||||
|
||||
int APS5_VABI sceAjmBatchJobDecodeSplit(AjmBatchInfo* info, uint32_t instance, const AjmBuffer* input_buffers, size_t input_buffers_num, const AjmBuffer* output_buffers, size_t output_buffers_num, void* result) {
|
||||
return sceAjmBatchJobRunSplit(info, instance, SIDEBAND_STREAM, input_buffers, input_buffers_num, output_buffers, output_buffers_num, result, sizeof(SidebandResult) + sizeof(SidebandStream));
|
||||
}
|
||||
|
||||
int APS5_VABI sceAjmBatchJobDecodeSingle(AjmBatchInfo* info, uint32_t instance, const void* bitstream_input, size_t bitstream_input_size, void* pcm_output, size_t pcm_output_size, void* result) {
|
||||
return sceAjmBatchJobRun(info, instance, SIDEBAND_STREAM, bitstream_input, bitstream_input_size, pcm_output, pcm_output_size, result, sizeof(SidebandResult) + sizeof(SidebandStream));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ int APS5_VABI sceAjmBatchCancel(std::uint32_t, std::uint32_t);
|
|||
int APS5_VABI sceAjmBatchJobClearContext(AjmBatchInfo*, std::uint32_t, void*);
|
||||
int APS5_VABI sceAjmBatchJobSetResampleParameters(AjmBatchInfo*, std::uint32_t, float, std::uint32_t, void*);
|
||||
int APS5_VABI sceAjmBatchJobGetResampleInfo(AjmBatchInfo*, std::uint32_t, void*);
|
||||
int APS5_VABI sceAjmBatchJobSetResampleParametersEx(AjmBatchInfo*, std::uint32_t, float, float, std::uint32_t, void*);
|
||||
int APS5_VABI sceAjmBatchJobDecodeSplit(AjmBatchInfo*, std::uint32_t, const AjmBuffer*, std::size_t, const AjmBuffer*, std::size_t, void*);
|
||||
}
|
||||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
|
@ -367,6 +369,28 @@ void TestDecodeSingle(std::uint32_t context) {
|
|||
Require(sceAjmInstanceDestroy(context, instance) == 0);
|
||||
}
|
||||
|
||||
void TestDecodeSplit(std::uint32_t context) {
|
||||
std::uint32_t whole = 0;
|
||||
std::uint32_t split = 0;
|
||||
Require(sceAjmInstanceCreate(context, 0, 0, &whole) == 0 && sceAjmInstanceCreate(context, 0, 0, &split) == 0);
|
||||
std::vector<std::int16_t> reference(1152);
|
||||
DecodeSideband expected{};
|
||||
RunDecode(context, whole, MP3_MONO, sizeof(MP3_MONO), reference.data(), reference.size() * sizeof(std::int16_t), expected);
|
||||
std::vector<std::int16_t> first(512);
|
||||
std::vector<std::int16_t> second(640);
|
||||
const AjmBuffer input{const_cast<std::uint8_t*>(MP3_MONO), sizeof(MP3_MONO)};
|
||||
const AjmBuffer outputs[2] = {{first.data(), first.size() * sizeof(std::int16_t)}, {second.data(), second.size() * sizeof(std::int16_t)}};
|
||||
std::vector<std::uint8_t> batch(0x100);
|
||||
AjmBatchInfo info{};
|
||||
DecodeSideband sideband{};
|
||||
Require(sceAjmBatchInitialize(batch.data(), batch.size(), &info) == 0);
|
||||
Require(sceAjmBatchJobDecodeSplit(&info, split, &input, 1, outputs, 2, &sideband) == 0);
|
||||
Submit(context, info);
|
||||
Require(expected.result == 0 && sideband.result == 0 && sideband.inputConsumed == expected.inputConsumed && sideband.outputWritten == expected.outputWritten && sideband.totalDecodedSamples == expected.totalDecodedSamples);
|
||||
Require(std::equal(first.begin(), first.end(), reference.begin()) && std::equal(second.begin(), second.end(), reference.begin() + 512));
|
||||
Require(sceAjmInstanceDestroy(context, whole) == 0 && sceAjmInstanceDestroy(context, split) == 0);
|
||||
}
|
||||
|
||||
void TestGaplessDecode(std::uint32_t context) {
|
||||
std::uint32_t instance = 0;
|
||||
Require(sceAjmInstanceCreate(context, 0, 0, &instance) == 0);
|
||||
|
|
@ -921,6 +945,44 @@ void TestResampleMp3(std::uint32_t context) {
|
|||
for (const std::uint32_t instance : {plain, faster, slower}) Require(sceAjmInstanceDestroy(context, instance) == 0);
|
||||
}
|
||||
|
||||
void TestResampleParametersEx(std::uint32_t context) {
|
||||
std::uint32_t plain = 0;
|
||||
std::uint32_t extended = 0;
|
||||
Require(sceAjmInstanceCreate(context, 0, 0, &plain) == 0 && sceAjmInstanceCreate(context, 0, 0, &extended) == 0);
|
||||
std::vector<std::int16_t> plainPcm(512);
|
||||
std::vector<std::int16_t> extendedPcm(512);
|
||||
for (const bool ex : {false, true}) {
|
||||
std::vector<std::uint8_t> batch(4096);
|
||||
AjmBatchInfo info{};
|
||||
std::int64_t setResult[2] = {-1, -1};
|
||||
DecodeSideband sideband{};
|
||||
auto& pcm = ex ? extendedPcm : plainPcm;
|
||||
const auto instance = ex ? extended : plain;
|
||||
Require(sceAjmBatchInitialize(batch.data(), batch.size(), &info) == 0);
|
||||
if (ex) Require(sceAjmBatchJobSetResampleParametersEx(&info, instance, 2.0f, 0.0f, 1, setResult) == 0);
|
||||
else Require(sceAjmBatchJobSetResampleParameters(&info, instance, 2.0f, 1, setResult) == 0);
|
||||
Require(sceAjmBatchJobDecode(&info, instance, MP3_MONO, sizeof(MP3_MONO), pcm.data(), pcm.size() * sizeof(std::int16_t), &sideband) == 0);
|
||||
Submit(context, info);
|
||||
Require(setResult[0] == 0 && sideband.result == 0);
|
||||
}
|
||||
Require(extendedPcm == plainPcm);
|
||||
Require(GetResampleInfo(context, extended).ratio == 2.0f && GetResampleInfo(context, extended).samples == GetResampleInfo(context, plain).samples);
|
||||
|
||||
std::vector<std::uint8_t> batch(4096);
|
||||
AjmBatchInfo info{};
|
||||
std::int64_t result[2] = {-1, -1};
|
||||
Require(sceAjmBatchInitialize(batch.data(), batch.size(), &info) == 0);
|
||||
for (const float bad : {0.0f, -1.0f, std::nanf(""), INFINITY}) Require(sceAjmBatchJobSetResampleParametersEx(&info, extended, bad, 0.0f, 1, result) == static_cast<int>(0x80930005));
|
||||
bool rampThrows = false;
|
||||
try {
|
||||
static_cast<void>(sceAjmBatchJobSetResampleParametersEx(&info, extended, 1.0f, 0.001f, 1, result));
|
||||
} catch (const std::runtime_error&) {
|
||||
rampThrows = true;
|
||||
}
|
||||
Require(rampThrows && info.offset == 0);
|
||||
Require(sceAjmInstanceDestroy(context, plain) == 0 && sceAjmInstanceDestroy(context, extended) == 0);
|
||||
}
|
||||
|
||||
const std::uint8_t AT9_MONO_SILENT_SUPERFRAME[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x80, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
|
@ -1042,6 +1104,7 @@ int main() {
|
|||
TestMp3(context);
|
||||
TestOpus(context);
|
||||
TestDecodeSingle(context);
|
||||
TestDecodeSplit(context);
|
||||
TestGaplessDecode(context);
|
||||
TestCodecInfo(context);
|
||||
TestGetInfo(context);
|
||||
|
|
@ -1053,6 +1116,7 @@ int main() {
|
|||
TestControlOpus(context);
|
||||
TestResampleOpus(context);
|
||||
TestResampleMp3(context);
|
||||
TestResampleParametersEx(context);
|
||||
TestResampleAt9(context);
|
||||
TestAt9GaplessSegments(context);
|
||||
TestAt9RunDecodesWholeInput(context);
|
||||
|
|
|
|||
|
|
@ -207,6 +207,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [NGS2 port matrix](../../core/libs/prx/libSceNgs2.native/src/Render.cpp) (libSceNgs2.native) - a port without a matrix, or with a selected matrix whose levels were never set, mixes through a default channel map: mono reaches both stereo channels at -3 dB and the 5.1 and 7.1 centre, stereo folds to mono at -3 dB per side, and wider sources fold down with 0.5 and -3 dB gains. The map and the equivalence of an unset matrix with no matrix were measured from the PS4 firmware 11.00 `libSceNgs2.sprx`, not yet from the PS5 library. That library only accepts submixer and mastering voices with 1, 2, 6 or 8 channels, so the map into 3, 4, 5 or 7 channels throws. A matrix set with fewer levels than the destination needs still mixes only the rows it has
|
||||
- [sceAjmBatchJobGetCodecInfo, sceAjmBatchJobGetGaplessDecode](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native) - the `RUN_GET_CODEC_INFO` flag (0x800), the ATRAC9 codec info layout and its next frame size are taken from the [shadPS4](https://github.com/shadps4-emu/shadPS4) and [RPCSX](https://github.com/RPCSX/rpcsx) reimplementations, not confirmed on a PS5 title; a run job without buffers returns result 0, as in shadPS4. The gapless sideband reports the total and skip counts the title set and does not count them down: RPCSX does the same, shadPS4 counts them down and BryKytyPS5 returns its current counts
|
||||
- [AJM Opus decoder](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native, codec 24) - the initialize parameter layout (`u32 channels, u32 sample rate, u32`, seen as `2, 48000, 0`) and the little-endian `u16` byte count before each packet come from one title (The Smurfs Dreams); only 48000 Hz and a zero third word are accepted
|
||||
- [sceAjmBatchJobSetResampleParametersEx](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native) - the `(info, instance, float ratio_start, float ratio_change_per_sample, flags, result)` signature is inferred from the name and from `sceAjmBatchJobSetResampleParameters`; with a zero change per sample it sets the ratio as `sceAjmBatchJobSetResampleParameters` does (Watch Dogs: Legion, PPSA01500, calls it so). How a non-zero change ramps the ratio is unknown and throws
|
||||
- [sceAjmBatchJobControl](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native) - RESET (bit 13), INITIALIZE (bit 14) and the sideband input order (gapless decode, then initialize parameters) come from [shadPS4](https://github.com/shadps4-emu/shadPS4) (PS4); other flags throw
|
||||
- [sceAjmBatchJobControl](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native) - RESET is assumed to clear the context as sceAjmBatchJobClearContext does
|
||||
- [sceAjmBatchJobControl](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native) - initialize parameters are 8 bytes for ATRAC9, 12 for Opus and none for MP3, as one title passes them; other codecs and sizes, and an output other than the 8-byte result, throw
|
||||
|
|
|
|||
Loading…
Reference in New Issue