Compare commits

...
Author SHA1 Message Date
nmzikandPhr3d13 b65dd18224 ngs2: let CLEAR reopen a closed waveform block list
Co-authored-by: Phr3d13 <phr3d13@gmail.com>
2026-10-30 01:57:41 +01:00
nmzikandPhr3d13 2f6dccb10b ngs2: mix ports without matrix levels with the default matrix
Co-authored-by: Phr3d13 <phr3d13@gmail.com>
2026-10-30 01:57:41 +01:00
nmzikandPhr3d13 08a7d7fbed ngs2: render the reverb voice dry signal
Co-authored-by: Phr3d13 <phr3d13@gmail.com>
2026-10-30 01:57:41 +01:00
nmzikandPhr3d13 2aa0d58ffb ngs2: report voice port routing and volume
Co-authored-by: Phr3d13 <phr3d13@gmail.com>
2026-10-30 01:57:41 +01:00
nmzikandPhr3d13 9bf311c6c7 ngs2: report voice setup channels from the setup query
Co-authored-by: Phr3d13 <phr3d13@gmail.com>
2026-10-30 01:57:41 +01:00
nmzikandPhr3d13 e66a7f52b5 ngs2: apply mastering voice full band and LFE gain
Co-authored-by: Phr3d13 <phr3d13@gmail.com>
2026-10-30 01:57:41 +01:00
nmzikandPhr3d13 67f8276751 ngs2: patch to a rack handle connects to its voice 0
Co-authored-by: Phr3d13 <phr3d13@gmail.com>
2026-10-30 01:57:41 +01:00
KairosMakkiandAjakfial 3ad21e35cc Rename near identifier to avoid Windows macro clash (#1983)
Co-authored-by: Ajakfial <ajakafial@tutamail.com>
2026-10-30 00:41:22 +01:00
5 changed files with 271 additions and 13 deletions

No files matched your search

+2
View File
@@ -1162,6 +1162,8 @@ LIB_DEFINE(InitAudio_1_Ngs2) {
LIB_FUNC("AbYvTOZ8Pts", Ngs2::Ngs2VoiceRunCommands); LIB_FUNC("AbYvTOZ8Pts", Ngs2::Ngs2VoiceRunCommands);
LIB_FUNC("-TOuuAQ-buE", Ngs2::Ngs2VoiceGetState); LIB_FUNC("-TOuuAQ-buE", Ngs2::Ngs2VoiceGetState);
LIB_FUNC("rEh728kXk3w", Ngs2::Ngs2VoiceGetStateFlags); LIB_FUNC("rEh728kXk3w", Ngs2::Ngs2VoiceGetStateFlags);
LIB_FUNC("9eic4AmjGVI", Ngs2::Ngs2VoiceQueryInfo);
LIB_FUNC("WCayTgob7-o", Ngs2::Ngs2VoiceGetPortInfo);
LIB_FUNC("i0VnXM-C9fc", Ngs2::Ngs2SystemRender); LIB_FUNC("i0VnXM-C9fc", Ngs2::Ngs2SystemRender);
LIB_FUNC("l4Q2dWEH6UM", Ngs2::Ngs2SystemSetGrainSamples); LIB_FUNC("l4Q2dWEH6UM", Ngs2::Ngs2SystemSetGrainSamples);
LIB_FUNC("hyVLT2VlOYk", Ngs2::Ngs2ParseWaveformData); LIB_FUNC("hyVLT2VlOYk", Ngs2::Ngs2ParseWaveformData);
+121 -7
View File
@@ -568,6 +568,9 @@ struct Ngs2VoiceInternal {
std::vector<float> output_matrix; std::vector<float> output_matrix;
uint32_t channels = 0; uint32_t channels = 0;
uint32_t output_id = 0; uint32_t output_id = 0;
float fbw_level = 1.0f;
float lfe_level = 1.0f;
float dry_level = 0.0f;
bool rendering = false; bool rendering = false;
bool rendered = false; bool rendered = false;
bool has_samples = false; bool has_samples = false;
@@ -710,10 +713,11 @@ struct Ngs2VoiceInternal {
if (count > capacity) { if (count > capacity) {
return NGS2_ERROR_INVALID_NUM_WAVEFORM_BLOCKS; return NGS2_ERROR_INVALID_NUM_WAVEFORM_BLOCKS;
} }
EXIT_NOT_IMPLEMENTED(!accepts_blocks || (flags & ~0x17u) != 0);
const bool clear = (flags & 4u) != 0; const bool clear = (flags & 4u) != 0;
const bool only_data = (flags & 2u) != 0; const bool only_data = (flags & 2u) != 0;
const bool join = only_data && (flags & 0x10u) == 0; const bool join = only_data && (flags & 0x10u) == 0;
// CLEAR reopens a list that the previous addition closed.
EXIT_NOT_IMPLEMENTED((!accepts_blocks && !clear) || (flags & ~0x17u) != 0);
const auto retained = clear ? 0 : blocks.size(); const auto retained = clear ? 0 : blocks.size();
Block tail = retained == 0 ? Block {} : blocks.back(); Block tail = retained == 0 ? Block {} : blocks.back();
std::vector<Block> pending; std::vector<Block> pending;
@@ -1800,6 +1804,18 @@ static void Ngs2ConsumeSamples(Ngs2VoiceInternal& voice, uint32_t grain) {
voice.has_samples = output != 0; voice.has_samples = output != 0;
} }
// Mono and stereo convert at -3 dB, mono feeds the surround front center, others map 1:1.
static float Ngs2DefaultMatrixLevel(uint32_t src_channels, uint32_t src, uint32_t dst_channels,
size_t dst) {
if ((src_channels == 1 && dst_channels == 2) || (src_channels == 2 && dst_channels == 1)) {
return std::numbers::sqrt2_v<float> / 2;
}
if (src_channels == 1 && dst_channels > 2) {
return dst == 2 ? 1.0f : 0.0f;
}
return src == dst ? 1.0f : 0.0f;
}
static void Ngs2RenderVoice(Ngs2VoiceInternal& voice, const std::vector<Ngs2VoiceInternal*>& voices, static void Ngs2RenderVoice(Ngs2VoiceInternal& voice, const std::vector<Ngs2VoiceInternal*>& voices,
uint32_t grain) { uint32_t grain) {
if (voice.rendered) { if (voice.rendered) {
@@ -1848,7 +1864,11 @@ static void Ngs2RenderVoice(Ngs2VoiceInternal& voice, const std::vector<Ngs2Voic
EXIT_NOT_IMPLEMENTED(port.input >= voice.rack->NumBuffers()); EXIT_NOT_IMPLEMENTED(port.input >= voice.rack->NumBuffers());
const auto* input = source->Buffer(source->rack->PortBufferId(p), grain); const auto* input = source->Buffer(source->rack->PortBufferId(p), grain);
auto* output = voice.Buffer(port.input, grain); auto* output = voice.Buffer(port.input, grain);
// A port without a matrix, or whose matrix has no levels, uses the default mix.
const auto* matrix = port.matrix < 0 ? nullptr : &source->matrices.at(port.matrix); const auto* matrix = port.matrix < 0 ? nullptr : &source->matrices.at(port.matrix);
if (matrix != nullptr && matrix->empty()) {
matrix = nullptr;
}
const size_t output_channels = const size_t output_channels =
matrix == nullptr matrix == nullptr
? voice.channels ? voice.channels
@@ -1857,7 +1877,8 @@ static void Ngs2RenderVoice(Ngs2VoiceInternal& voice, const std::vector<Ngs2Voic
for (uint32_t src = 0; src < source->channels; ++src) { for (uint32_t src = 0; src < source->channels; ++src) {
const float level = const float level =
port.volume * (matrix == nullptr port.volume * (matrix == nullptr
? (src == dst ? 1.0f : 0.0f) ? Ngs2DefaultMatrixLevel(source->channels, src,
voice.channels, dst)
: (*matrix)[dst * source->channels + src]); : (*matrix)[dst * source->channels + src]);
for (uint32_t i = 0; i < grain; ++i) { for (uint32_t i = 0; i < grain; ++i) {
output[dst * grain + i] += input[src * grain + i] * level; output[dst * grain + i] += input[src * grain + i] * level;
@@ -1867,6 +1888,11 @@ static void Ngs2RenderVoice(Ngs2VoiceInternal& voice, const std::vector<Ngs2Voic
voice.has_samples = true; voice.has_samples = true;
} }
} }
if (voice.has_samples && voice.rack->type == Ngs2RackType::Reverb) {
for (auto& sample: voice.samples) {
sample *= voice.dry_level;
}
}
if (voice.has_samples) { if (voice.has_samples) {
const auto& custom = voice.rack->option.custom_sampler.custom_rack_option; const auto& custom = voice.rack->option.custom_sampler.custom_rack_option;
const auto rate = voice.rack->ngs->option.sample_rate; const auto rate = voice.rack->ngs->option.sample_rate;
@@ -1978,10 +2004,13 @@ int KYTY_SYSV_ABI Ngs2SystemRender(uintptr_t system_handle, const Ngs2RenderBuff
pcm = static_cast<float*>(output.buffer); pcm = static_cast<float*>(output.buffer);
} }
for (uint32_t dst = 0; dst < output.num_channels; ++dst) { for (uint32_t dst = 0; dst < output.num_channels; ++dst) {
// Output channel 3 is LFE; every other channel is full band.
const float gain = dst == 3 ? voice->lfe_level : voice->fbw_level;
for (uint32_t src = 0; src < voice->channels; ++src) { for (uint32_t src = 0; src < voice->channels; ++src) {
const float level = voice->output_matrix.empty() const float level =
? (src == dst ? 1.0f : 0.0f) gain * (voice->output_matrix.empty()
: voice->output_matrix.at(src * output.num_channels + dst); ? static_cast<float>(src == dst)
: voice->output_matrix.at(src * output.num_channels + dst));
if (level == 0.0f) { if (level == 0.0f) {
continue; continue;
} }
@@ -2339,6 +2368,17 @@ int KYTY_SYSV_ABI Ngs2RackGetVoiceHandle(uintptr_t rack_handle, uint32_t voice_i
return OK; return OK;
} }
// A patch to a rack handle connects to voice #0 of that rack.
static Ngs2VoiceInternal* Ngs2FindPatchDestination(uintptr_t handle) {
Common::LockGuard racks_lock(g_racks_mutex);
for (auto* rack = g_racks_list; rack != nullptr; rack = rack->next) {
if (reinterpret_cast<uintptr_t>(rack) == handle) {
return reinterpret_cast<Ngs2VoiceInternal*>(rack + 1);
}
}
return reinterpret_cast<Ngs2VoiceInternal*>(handle);
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity) // NOLINTNEXTLINE(readability-function-cognitive-complexity)
int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamHeader* param_list) { int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamHeader* param_list) {
PRINT_NAME(); PRINT_NAME();
@@ -2408,7 +2448,7 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH
const auto* patch = reinterpret_cast<const Ngs2VoicePatchParam*>(param); const auto* patch = reinterpret_cast<const Ngs2VoicePatchParam*>(param);
EXIT_NOT_IMPLEMENTED(patch->port >= voice->ports.size()); EXIT_NOT_IMPLEMENTED(patch->port >= voice->ports.size());
voice->ports[patch->port].dest = voice->ports[patch->port].dest =
reinterpret_cast<Ngs2VoiceInternal*>(patch->dest_handle); Ngs2FindPatchDestination(patch->dest_handle);
voice->ports[patch->port].input = patch->dest_input_id; voice->ports[patch->port].input = patch->dest_input_id;
LOGF("\t connect->port = %u\n" LOGF("\t connect->port = %u\n"
"\t connect->dest_input_id = %u\n" "\t connect->dest_input_id = %u\n"
@@ -2456,7 +2496,31 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH
} }
break; break;
} }
case 0x2001: EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::Reverb); break; case 0x2001: {
EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::Reverb);
const auto* values = reinterpret_cast<const uint32_t*>(param + 1);
switch (param->id & 0xffffu) {
case 0:
// Input and output channels, flags; channel conversion stays silent.
EXIT_NOT_IMPLEMENTED(values[0] == 0 || values[2] != 0);
voice->ResetSetupState();
voice->channels = values[1] == values[0] ? values[0] : 0;
break;
case 1: {
// The reverb parameters start with the wet and dry levels.
const auto* levels = reinterpret_cast<const float*>(values);
voice->dry_level = std::clamp(levels[1], 0.0f, 1.0f);
static std::atomic_bool wet_warned = false;
if (levels[0] != 0.0f && !wet_warned.exchange(true)) {
Log::WriteToConsoleAndLog("warning: NGS2 reverb wet signal is muted\n");
}
break;
}
default: EXIT("unsupported reverb control: 0x%08" PRIx32 "\n", param->id);
}
break;
}
case 0x3000: { case 0x3000: {
EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::Mastering); EXIT_NOT_IMPLEMENTED(voice->rack->type != Ngs2RackType::Mastering);
const auto* values = reinterpret_cast<const uint32_t*>(param + 1); const auto* values = reinterpret_cast<const uint32_t*>(param + 1);
@@ -2470,6 +2534,12 @@ int KYTY_SYSV_ABI Ngs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamH
matrix->levels + matrix->num_levels); matrix->levels + matrix->num_levels);
break; break;
} }
case 4: {
const auto* levels = reinterpret_cast<const float*>(values);
voice->fbw_level = std::clamp(levels[0], 0.0f, 4.0f);
voice->lfe_level = std::clamp(levels[1], 0.0f, 4.0f);
break;
}
case 5: voice->output_id = values[0]; break; case 5: voice->output_id = values[0]; break;
default: EXIT("unsupported mastering control: 0x%08" PRIx32 "\n", param->id); default: EXIT("unsupported mastering control: 0x%08" PRIx32 "\n", param->id);
} }
@@ -2761,6 +2831,50 @@ int KYTY_SYSV_ABI Ngs2VoiceGetStateFlags(uintptr_t voice_handle, uint32_t* state
return OK; return OK;
} }
int KYTY_SYSV_ABI Ngs2VoiceQueryInfo(uintptr_t voice_handle, uint32_t query_id, void* info,
size_t info_size) {
PRINT_NAME();
struct SetupInfo {
uint32_t channels, flags;
};
EXIT_NOT_IMPLEMENTED(voice_handle == 0 || info == nullptr);
EXIT_NOT_IMPLEMENTED(query_id != 0x4001 || info_size != sizeof(SetupInfo));
auto* voice = reinterpret_cast<Ngs2VoiceInternal*>(voice_handle);
Common::LockGuard lock(voice->rack->ngs->mutex);
*static_cast<SetupInfo*>(info) = {voice->channels, 0};
return OK;
}
int KYTY_SYSV_ABI Ngs2VoiceGetPortInfo(uintptr_t voice_handle, uint32_t port, void* info,
size_t info_size) {
PRINT_NAME();
struct PortInfo {
int32_t matrix;
float volume;
uint32_t delay_samples, input;
uintptr_t dest;
};
EXIT_NOT_IMPLEMENTED(voice_handle == 0 || info == nullptr || info_size != sizeof(PortInfo));
auto* voice = reinterpret_cast<Ngs2VoiceInternal*>(voice_handle);
Common::LockGuard lock(voice->rack->ngs->mutex);
EXIT_NOT_IMPLEMENTED(port >= voice->ports.size());
const auto& p = voice->ports[port];
*static_cast<PortInfo*>(info) = {p.matrix, p.volume, 0, p.input,
reinterpret_cast<uintptr_t>(p.dest)};
return OK;
}
int KYTY_SYSV_ABI Ngs2SystemLock(uintptr_t system_handle) { int KYTY_SYSV_ABI Ngs2SystemLock(uintptr_t system_handle) {
PRINT_NAME(); PRINT_NAME();
+4
View File
@@ -60,6 +60,10 @@ int KYTY_SYSV_ABI Ngs2VoiceRunCommands(uintptr_t voice_handle, const void* comma
int KYTY_SYSV_ABI Ngs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* state, int KYTY_SYSV_ABI Ngs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* state,
size_t state_size); size_t state_size);
int KYTY_SYSV_ABI Ngs2VoiceGetStateFlags(uintptr_t voice_handle, uint32_t* state_flags); int KYTY_SYSV_ABI Ngs2VoiceGetStateFlags(uintptr_t voice_handle, uint32_t* state_flags);
int KYTY_SYSV_ABI Ngs2VoiceQueryInfo(uintptr_t voice_handle, uint32_t query_id, void* info,
size_t info_size);
int KYTY_SYSV_ABI Ngs2VoiceGetPortInfo(uintptr_t voice_handle, uint32_t port, void* info,
size_t info_size);
int KYTY_SYSV_ABI Ngs2SystemRender(uintptr_t system_handle, const Ngs2RenderBufferInfo* buffer_info, int KYTY_SYSV_ABI Ngs2SystemRender(uintptr_t system_handle, const Ngs2RenderBufferInfo* buffer_info,
uint32_t num_buffer_info); uint32_t num_buffer_info);
int KYTY_SYSV_ABI Ngs2ParseWaveformData(const void* data, size_t data_size, Ngs2WaveformInfo* info); int KYTY_SYSV_ABI Ngs2ParseWaveformData(const void* data, size_t data_size, Ngs2WaveformInfo* info);
+138
View File
@@ -807,6 +807,76 @@ void TestPcm16RenderOutput() {
"PCM16 output retained samples from the previous render"); "PCM16 output retained samples from the previous render");
} }
void TestPatchToRackHandle() {
Ngs2Internal system;
Ngs2RackOptionUnion sampler_option {}, mastering_option {};
Ngs2FillDefaultRackOption(0x1000, &sampler_option);
Ngs2FillDefaultRackOption(0x3000, &mastering_option);
sampler_option.common.max_voices = 1;
RenderRack sampler(system, 0x1000, sampler_option.common);
RenderRack master(system, 0x3000, mastering_option.common);
auto& source = sampler.Voice();
Ngs2VoicePatchParam patch {{24, 0, 5}, 0, 0, master.handle};
Check(Ngs2VoiceControl(reinterpret_cast<uintptr_t>(&source), &patch.header) == OK &&
source.ports[0].dest == &master.Voice(),
"patch to a rack handle did not connect to its voice #0");
}
void TestMasteringGain() {
Ngs2Internal system;
system.option = Ngs2DefaultSystemOption();
system.option.num_grain_samples = 1;
Ngs2RackOptionUnion sampler_option {}, mastering_option {};
Ngs2FillDefaultRackOption(0x1000, &sampler_option);
Ngs2FillDefaultRackOption(0x3000, &mastering_option);
sampler_option.common.max_voices = 1;
RenderRack sampler(system, 0x1000, sampler_option.common);
RenderRack master(system, 0x3000, mastering_option.common);
auto& source = sampler.Voice();
auto& mastering = master.Voice();
mastering.channels = 8;
mastering.state = Ngs2VoicePlayState::Playing;
const int16_t frame[8] = {8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192};
source.SetupSampler({0x12, 8, 48000});
source.blocks.push_back({reinterpret_cast<const uint8_t*>(frame), {0, sizeof(frame), 0, 0, 1}});
source.ports[0].dest = &mastering;
source.SetEvent(1);
struct {
Ngs2VoiceParamHeader header {16, 0, 0x30000004};
float fbw = 5.0f, lfe = 0.5f;
} gain;
float pcm[8] = {};
Ngs2RenderBufferInfo output {pcm, sizeof(pcm), 0x18, 8};
const float expected[8] = {1.0f, 1.0f, 1.0f, 0.125f, 1.0f, 1.0f, 1.0f, 1.0f};
Check(Ngs2VoiceControl(reinterpret_cast<uintptr_t>(&mastering), &gain.header) == OK &&
Ngs2SystemRender(reinterpret_cast<uintptr_t>(&system), &output, 1) == OK &&
std::ranges::equal(pcm, expected),
"mastering gain was not clamped or applied per full band and LFE channel");
}
void TestReverbDryLevel() {
Fixture f(48000);
Ngs2RackInternal reverb_rack {};
reverb_rack.ngs = &f.system;
reverb_rack.type = Ngs2RackType::Reverb;
Ngs2VoiceInternal reverb;
reverb.rack = &reverb_rack;
f.voice.ports.push_back({&reverb, 0, 1.0f, -1});
const int16_t pcm = 8192;
f.Queue(&pcm, 1, 0);
struct {
Ngs2VoiceParamHeader header {24, 24, 0x20010000};
uint32_t channels[4] = {1, 1, 0, 0};
Ngs2VoiceParamHeader levels_header {96, 0, 0x20010001};
float levels[22] = {0.6f, 0.4f};
} setup;
Ngs2VoiceControl(reinterpret_cast<uintptr_t>(&reverb), &setup.header);
reverb.SetEvent(1);
Ngs2RenderVoice(reverb, {&f.voice, &reverb}, 1);
Check(reverb.has_samples && reverb.samples[0] == 0.25f * 0.4f,
"reverb did not pass its dry signal");
}
void TestCustomMasteringChannelMixer() { void TestCustomMasteringChannelMixer() {
Ngs2Internal system; Ngs2Internal system;
system.option = Ngs2DefaultSystemOption(); system.option = Ngs2DefaultSystemOption();
@@ -1026,6 +1096,35 @@ void TestMonoRateAndRouting() {
"22.05 kHz effect duration was incorrect"); "22.05 kHz effect duration was incorrect");
} }
void TestDefaultMatrix() {
struct Case {
uint32_t src_channels;
int32_t matrix;
uint32_t dst_channels;
std::vector<float> expected;
};
constexpr float minus_3db = std::numbers::sqrt2_v<float> / 2;
const int16_t pcm[] = {8192, -16384};
for (const auto& c: {Case {1, -1, 2, {0.25f * minus_3db, 0.25f * minus_3db}},
Case {1, 0, 2, {0.25f * minus_3db, 0.25f * minus_3db}},
Case {2, -1, 1, {0.25f * minus_3db + -0.5f * minus_3db}},
Case {1, -1, 6, {0, 0, 0.25f, 0, 0, 0}}}) {
Fixture f(48000, c.src_channels);
Ngs2RackInternal rack {};
rack.ngs = &f.system;
rack.type = Ngs2RackType::Mastering;
Ngs2VoiceInternal dest;
dest.rack = &rack;
dest.channels = c.dst_channels;
dest.state = Ngs2VoicePlayState::Playing;
f.voice.matrices.resize(1);
f.voice.ports.push_back({&dest, 0, 1.0f, c.matrix});
f.Queue(pcm, 1, 0);
Ngs2RenderVoice(dest, {&f.voice, &dest}, 1);
Check(dest.samples == c.expected, "default port mix produced the wrong channel levels");
}
}
void TestMatrixOutputChannels() { void TestMatrixOutputChannels() {
Fixture f(48000, 2); Fixture f(48000, 2);
Ngs2RackInternal mastering_rack {}; Ngs2RackInternal mastering_rack {};
@@ -1111,6 +1210,38 @@ void TestDeferredSamplerSetup() {
"sampler did not play after deferred stream setup"); "sampler did not play after deferred stream setup");
} }
void TestClearReopensClosedList() {
Fixture f(48000);
const int16_t first = 8192, second = -16384;
f.Queue(&first, 1, 0);
f.Queue(&second, 1, 4);
f.Render(1);
Check(f.voice.samples[0] == -0.5f, "CLEAR did not reopen a closed waveform list");
}
void TestSetupQuery() {
Fixture f(48000, 2);
uint32_t info[2] = {99, 99};
Ngs2VoiceQueryInfo(reinterpret_cast<uintptr_t>(&f.voice), 0x4001, info, sizeof(info));
Check(info[0] == 2 && info[1] == 0, "setup query did not report the voice channels");
}
void TestPortInfo() {
Fixture f(48000);
Ngs2VoiceInternal dest;
f.voice.ports.push_back({&dest, 1, 0.5f, 0});
struct {
int32_t matrix;
float volume;
uint32_t delay_samples, input;
uintptr_t dest;
} info {99, 99, 99, 99, 99};
Ngs2VoiceGetPortInfo(reinterpret_cast<uintptr_t>(&f.voice), 0, &info, sizeof(info));
Check(info.matrix == 0 && info.volume == 0.5f && info.delay_samples == 0 && info.input == 1 &&
info.dest == reinterpret_cast<uintptr_t>(&dest),
"port info did not report the port routing and volume");
}
void TestPlayStateBeforeRender() { void TestPlayStateBeforeRender() {
Fixture f(48000); Fixture f(48000);
const int16_t pcm[] = {8192, -16384}; const int16_t pcm[] = {8192, -16384};
@@ -1803,6 +1934,9 @@ int main() {
TestAtrac9Vibration(); TestAtrac9Vibration();
TestAtrac9Discrete(); TestAtrac9Discrete();
TestPcm16RenderOutput(); TestPcm16RenderOutput();
TestPatchToRackHandle();
TestMasteringGain();
TestReverbDryLevel();
TestCustomMasteringChannelMixer(); TestCustomMasteringChannelMixer();
TestCustomVoiceStateCapacity(); TestCustomVoiceStateCapacity();
TestSubmixerSetupAndRouting(); TestSubmixerSetupAndRouting();
@@ -1837,8 +1971,12 @@ int main() {
TestPcmSkipAcrossBlocks(); TestPcmSkipAcrossBlocks();
TestPcmBlocksEndWithTheirData(); TestPcmBlocksEndWithTheirData();
TestMonoRateAndRouting(); TestMonoRateAndRouting();
TestDefaultMatrix();
TestMatrixOutputChannels(); TestMatrixOutputChannels();
TestCustomPcmStillPlays(); TestCustomPcmStillPlays();
TestClearReopensClosedList();
TestSetupQuery();
TestPortInfo();
TestPlayStateBeforeRender(); TestPlayStateBeforeRender();
TestStatePublication(); TestStatePublication();
TestStopThenPlayBeforeRender(); TestStopThenPlayBeforeRender();
+6 -6
View File
@@ -25,7 +25,7 @@ TestCase TrinityBvhIntersections(bool compressed, bool bary, bool sorted,
data[NodeStart + 29] = 9u << 22; // Force opaque and flip facing. data[NodeStart + 29] = 9u << 22; // Force opaque and flip facing.
data[NodeStart + 30] = 0x98; data[NodeStart + 30] = 0x98;
data[NodeStart + 31] = 0x04123456; data[NodeStart + 31] = 0x04123456;
const std::array<float, 8> near {5,1,3,7,4,2,6,8}; const std::array<float, 8> nearDist {5,1,3,7,4,2,6,8};
const std::array<float, 8> length {1,8,3,5,7,2,4,6}; const std::array<float, 8> length {1,8,3,5,7,2,4,6};
const std::array<u32, 8> children {128,138,171,190,256,266,299,318}; const std::array<u32, 8> children {128,138,171,190,256,266,299,318};
const auto set_bits = [&](u32 base, u32 offset, u32 count, u32 value) { const auto set_bits = [&](u32 base, u32 offset, u32 count, u32 value) {
@@ -43,12 +43,12 @@ TestCase TrinityBvhIntersections(bool compressed, bool bary, bool sorted,
const u32 index = slot * 4 + i; const u32 index = slot * 4 + i;
if (compressed) { if (compressed) {
// Exact integer minima; positive maxima round outward within one quantization step. // Exact integer minima; positive maxima round outward within one quantization step.
const std::array<u32, 6> planes {0x30000, 0x30000, u32(near[index]) * 8192, const std::array<u32, 6> planes {0x30000, 0x30000, u32(nearDist[index]) * 8192,
0x10000, 0x10000, u32(near[index] + length[index]) * 8192}; 0x10000, 0x10000, u32(nearDist[index] + length[index]) * 8192};
for (u32 axis = 0; axis < 6; ++axis) set_bits(base, 80 + i * 108 + axis * 18, 18, planes[axis]); for (u32 axis = 0; axis < 6; ++axis) set_bits(base, 80 + i * 108 + axis * 18, 18, planes[axis]);
} else { } else {
data[base + i] = children[index]; data[base + i] = children[index];
const std::array<float, 6> bounds {-1,-1,near[index], 1,1,near[index] + length[index]}; const std::array<float, 6> bounds {-1,-1,nearDist[index], 1,1,nearDist[index] + length[index]};
for (u32 axis = 0; axis < 6; ++axis) data[base + 4 + i * 6 + axis] = bits(bounds[axis]); for (u32 axis = 0; axis < 6; ++axis) data[base + 4 + i * 6 + axis] = bits(bounds[axis]);
} }
} }
@@ -126,8 +126,8 @@ TestCase TrinityBvhIntersections(bool compressed, bool bary, bool sorted,
const bool xt = (children[x] & 7) < 2, yt = (children[y] & 7) < 2; const bool xt = (children[x] & 7) < 2, yt = (children[y] & 7) < 2;
if (triangles_first && xt != yt) return xt; if (triangles_first && xt != yt) return xt;
if (heuristic == 1) return length[x] > length[y]; if (heuristic == 1) return length[x] > length[y];
if (heuristic == 2) return 2 * near[x] + length[x] < 2 * near[y] + length[y]; if (heuristic == 2) return 2 * nearDist[x] + length[x] < 2 * nearDist[y] + length[y];
return near[x] < near[y]; return nearDist[x] < nearDist[y];
}; };
if (sorted) { if (sorted) {
if (wide_sort && which == 3) std::stable_sort(order.begin(), order.end(), compare); if (wide_sort && which == 3) std::stable_sort(order.begin(), order.end(), compare);