mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-29 16:51:20 +00:00
ngs2: apply custom channel matrices before mastering output
Co-authored-by: Frenzied <frenziedsaint@gmail.com>
This commit is contained in:
1 parent
f71da54b9b
commit
8cfe1c4d34
2 files changed
+128
-21
No files matched your search
+85
-19
@@ -751,6 +751,26 @@ void TestCustomSideChainUserFx() {
|
||||
"user FX did not receive the side-chain buffer after the source channels");
|
||||
}
|
||||
|
||||
struct RenderRack {
|
||||
std::unique_ptr<std::byte[]> storage;
|
||||
uintptr_t handle = 0;
|
||||
|
||||
RenderRack(Ngs2Internal& system, uint32_t id, const Ngs2RackOption& option) {
|
||||
Ngs2ContextBufferInfo buffer {};
|
||||
Check(Ngs2RackQueryBufferSize(id, &option, &buffer) == OK, "rack size query failed");
|
||||
storage = std::make_unique<std::byte[]>(buffer.host_buffer_size);
|
||||
buffer.host_buffer = storage.get();
|
||||
Check(Ngs2RackCreate(reinterpret_cast<uintptr_t>(&system), id, &option, &buffer,
|
||||
&handle) == OK, "render rack creation failed");
|
||||
}
|
||||
|
||||
~RenderRack() { Check(Ngs2RackDestroy(handle, nullptr) == OK, "render rack cleanup failed"); }
|
||||
|
||||
Ngs2VoiceInternal& Voice() {
|
||||
return *reinterpret_cast<Ngs2VoiceInternal*>(reinterpret_cast<Ngs2RackInternal*>(handle) + 1);
|
||||
}
|
||||
};
|
||||
|
||||
void TestPcm16RenderOutput() {
|
||||
Ngs2Internal system;
|
||||
system.option = Ngs2DefaultSystemOption();
|
||||
@@ -760,23 +780,11 @@ void TestPcm16RenderOutput() {
|
||||
Ngs2FillDefaultRackOption(0x3000, &mastering_option);
|
||||
sampler_option.common.max_voices = 2;
|
||||
sampler_option.common.max_ports = 1;
|
||||
alignas(Ngs2RackInternal) alignas(Ngs2VoiceInternal)
|
||||
std::byte sampler_storage[sizeof(Ngs2RackInternal) + 2 * sizeof(Ngs2VoiceInternal)];
|
||||
alignas(Ngs2RackInternal) alignas(Ngs2VoiceInternal)
|
||||
std::byte mastering_storage[sizeof(Ngs2RackInternal) + sizeof(Ngs2VoiceInternal)];
|
||||
Ngs2ContextBufferInfo sampler_buffer {sampler_storage, sizeof(sampler_storage)};
|
||||
Ngs2ContextBufferInfo mastering_buffer {mastering_storage, sizeof(mastering_storage)};
|
||||
uintptr_t sampler_handle = 0, mastering_handle = 0;
|
||||
RenderRack sampler(system, 0x1000, sampler_option.common);
|
||||
RenderRack master(system, 0x3000, mastering_option.common);
|
||||
const auto system_handle = reinterpret_cast<uintptr_t>(&system);
|
||||
Check(Ngs2RackCreate(system_handle, 0x1000, &sampler_option.common, &sampler_buffer,
|
||||
&sampler_handle) == OK &&
|
||||
Ngs2RackCreate(system_handle, 0x3000, &mastering_option.common,
|
||||
&mastering_buffer, &mastering_handle) == OK,
|
||||
"PCM16 render rack creation failed");
|
||||
auto* samplers = reinterpret_cast<Ngs2VoiceInternal*>(
|
||||
reinterpret_cast<Ngs2RackInternal*>(sampler_handle) + 1);
|
||||
auto& mastering = *reinterpret_cast<Ngs2VoiceInternal*>(
|
||||
reinterpret_cast<Ngs2RackInternal*>(mastering_handle) + 1);
|
||||
auto* samplers = &sampler.Voice();
|
||||
auto& mastering = master.Voice();
|
||||
mastering.channels = 1;
|
||||
mastering.state = Ngs2VoicePlayState::Playing;
|
||||
const int16_t source[2][4] = {{16384, -16384, 32767, -32768},
|
||||
@@ -797,9 +805,66 @@ void TestPcm16RenderOutput() {
|
||||
Check(Ngs2SystemRender(system_handle, &output, 1) == OK &&
|
||||
std::ranges::all_of(output_samples, [](int16_t sample) { return sample == 0; }),
|
||||
"PCM16 output retained samples from the previous render");
|
||||
Check(Ngs2RackDestroy(mastering_handle, nullptr) == OK &&
|
||||
Ngs2RackDestroy(sampler_handle, nullptr) == OK,
|
||||
"PCM16 render rack cleanup failed");
|
||||
}
|
||||
|
||||
void TestCustomMasteringChannelMixer() {
|
||||
Ngs2Internal system;
|
||||
system.option = Ngs2DefaultSystemOption();
|
||||
system.option.num_grain_samples = 6;
|
||||
Ngs2RackOptionUnion sampler_option {};
|
||||
Ngs2FillDefaultRackOption(0x1000, &sampler_option);
|
||||
sampler_option.common.max_voices = 1;
|
||||
sampler_option.common.max_ports = 1;
|
||||
Ngs2CustomMasteringRackOption master_option {};
|
||||
auto& custom = master_option.custom_rack_option;
|
||||
custom.rack_option.size = sizeof(master_option);
|
||||
custom.rack_option.max_voices = 1;
|
||||
custom.rack_option.max_voice_channels = 6;
|
||||
custom.num_buffers = 1;
|
||||
custom.num_modules = 1;
|
||||
custom.module[0].module_id = 0x17;
|
||||
RenderRack sampler(system, 0x1000, sampler_option.common);
|
||||
RenderRack master(system, 0x4003, custom.rack_option);
|
||||
auto& source = sampler.Voice();
|
||||
auto& output = master.Voice();
|
||||
output.channels = 6;
|
||||
output.SetEvent(1);
|
||||
struct {
|
||||
Ngs2VoiceParamHeader header {5484, 0, 0x40001700};
|
||||
float levels[37][37] {};
|
||||
} matrix;
|
||||
constexpr float center = 0.70710677f;
|
||||
matrix.levels[0][0] = matrix.levels[1][1] = 1.0f;
|
||||
matrix.levels[0][2] = matrix.levels[1][2] = center;
|
||||
matrix.levels[0][4] = matrix.levels[1][5] = center;
|
||||
int16_t impulses[6][6] {};
|
||||
for (uint32_t c = 0; c < 6; ++c) impulses[c][c] = 8192;
|
||||
for (unsigned pass = 0; pass < 2; ++pass) {
|
||||
if (pass == 1) {
|
||||
std::memset(matrix.levels, 0, sizeof(matrix.levels));
|
||||
matrix.levels[0][1] = 64.0f;
|
||||
matrix.levels[1][0] = -64.0f;
|
||||
}
|
||||
Check(Ngs2VoiceControl(reinterpret_cast<uintptr_t>(&output), &matrix.header) == OK,
|
||||
"custom channel mixer control failed");
|
||||
source.SetupSampler({0x12, 6, 48000});
|
||||
source.blocks.push_back({reinterpret_cast<const uint8_t*>(impulses),
|
||||
{0, sizeof(impulses), 0, 0, 6}});
|
||||
source.ports[0].dest = &output;
|
||||
source.SetEvent(1);
|
||||
std::array<float, 14> pcm;
|
||||
pcm.fill(99.0f);
|
||||
Ngs2RenderBufferInfo buffer {pcm.data() + 1, 12 * sizeof(float), 0x18, 2};
|
||||
Check(Ngs2SystemRender(reinterpret_cast<uintptr_t>(&system), &buffer, 1) == OK,
|
||||
"custom mastering rejected stereo output");
|
||||
const std::array<float, 12> expected = pass == 0
|
||||
? std::array<float, 12> {0.25f, 0, 0, 0.25f, center / 4, center / 4,
|
||||
0, 0, center / 4, 0, 0, center / 4}
|
||||
: std::array<float, 12> {0, -8, 8, 0};
|
||||
Check(pcm.front() == 99 && pcm.back() == 99 &&
|
||||
std::equal(expected.begin(), expected.end(), pcm.begin() + 1),
|
||||
"custom mixer lost channel routing, in-place input, clamping or output bounds");
|
||||
}
|
||||
}
|
||||
|
||||
void TestPitchLoopAndSkip() {
|
||||
@@ -1700,6 +1765,7 @@ int main() {
|
||||
TestAtrac9Vibration();
|
||||
TestAtrac9Discrete();
|
||||
TestPcm16RenderOutput();
|
||||
TestCustomMasteringChannelMixer();
|
||||
TestSubmixerSetupAndRouting();
|
||||
TestCustomSideChainUserFx();
|
||||
TestWaveformBlockGeometry();
|
||||
|
||||
Reference in new issue
Block a user