refactor(bridge): split fx bridge, stabilize VST3 host chain, drop old install engines
- remove diagnostic canaries from audio hot path (crackle fix) and stuck C5 note raw-MIDI test (Vst2Instrument::init noteOff) - split native bridge into daw_vst_bridge (VSTi) + fx_vst_bridge (VST FX) - VST3: processData prepare before setActive, zero input buses, accept both .vstpreset and bridge-state blob in RenderJob presets - native_render/vst_engine: bridge-exe discovery + render_fx_chain + /fx-gui - delete old install/ engine binaries (install via sidecar+resources now) - add BUILD_LAYOUT.md, build_selfcheck, tools/ diagnostics
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
// tools/test_midi_polyphony.js — runnable check cho Phase A (dedup/legato/polyphony).
|
||||
// Đọc app.jsx, extract 2 hàm thuần, eval, assert spec §II.1 + §II.5.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const src = fs.readFileSync(path.join(__dirname, '..', 'app', 'static', 'js', 'app.jsx'), 'utf8');
|
||||
|
||||
const start = src.indexOf('const _cleanNativeNotes = (notes) => {');
|
||||
const end = src.indexOf('const _nativeTrackKey', start);
|
||||
if (start < 0 || end < 0) { console.error('FAIL: khong tim thay 2 ham'); process.exit(1); }
|
||||
const code = src.slice(start, end) +
|
||||
';globalThis._cleanNativeNotes=_cleanNativeNotes;' +
|
||||
'globalThis._scaleNativePolyphony=_scaleNativePolyphony;';
|
||||
// eslint-disable-next-line no-eval
|
||||
(0, eval)(code); // indirect eval: const khai báo ở global scope
|
||||
|
||||
let fails = 0;
|
||||
const assert = (cond, msg) => { if (!cond) { fails++; console.error('FAIL: ' + msg); } else { console.log('ok: ' + msg); } };
|
||||
const near = (a, b, eps) => Math.abs(a - b) < (eps || 1e-9);
|
||||
|
||||
// 1. 8-note chord cùng lúc → tổng peak ≈ 0.3*8/sqrt(8) ≤ 1.0
|
||||
const chord = [];
|
||||
for (let i = 0; i < 8; i++) chord.push({ pitch: 60 + i, start_beat: 0, duration_beats: 2, velocity: 0.3 });
|
||||
const sc = _scaleNativePolyphony(_cleanNativeNotes(chord));
|
||||
const sum = sc.reduce((a, n) => a + n.velocity, 0);
|
||||
assert(sc.length === 8, 'chord giu 8 note');
|
||||
assert(near(sum, 8 * 0.3 / Math.sqrt(8)), 'tong peak ' + sum.toFixed(4) + ' ~= ' + (8 * 0.3 / Math.sqrt(8)).toFixed(4));
|
||||
assert(sum <= 1.0, 'tong peak <= 1.0');
|
||||
|
||||
// 2. Solo → giữ nguyên velocity
|
||||
const solo = _scaleNativePolyphony([{ pitch: 60, start_beat: 0, duration_beats: 1, velocity: 0.8 }]);
|
||||
assert(near(solo[0].velocity, 0.8), 'solo velocity giu nguyen 0.8');
|
||||
|
||||
// 3. Dedup: cùng pitch + start_beat → gộp, velocity max
|
||||
const dup = _cleanNativeNotes([
|
||||
{ pitch: 60, start_beat: 1, duration_beats: 2, velocity: 0.5 },
|
||||
{ pitch: 60, start_beat: 1, duration_beats: 3, velocity: 0.7 },
|
||||
]);
|
||||
assert(dup.length === 1, 'dedup gop 2 note');
|
||||
assert(near(dup[0].velocity, 0.7), 'dedup giu velocity cao 0.7');
|
||||
assert(near(dup[0].duration_beats, 3), 'dedup giu duration dai nhat');
|
||||
|
||||
// 4. Legato: note trước cắt 0.01 beats trước note sau cùng pitch
|
||||
const leg = _cleanNativeNotes([
|
||||
{ pitch: 60, start_beat: 0, duration_beats: 4, velocity: 0.8 },
|
||||
{ pitch: 60, start_beat: 2, duration_beats: 1, velocity: 0.8 },
|
||||
]);
|
||||
assert(leg.length === 2, 'legato giu 2 note');
|
||||
assert(near(leg[0].duration_beats, 2 - 0.01, 1e-6), 'note dau cat con ' + leg[0].duration_beats);
|
||||
assert(near(leg[1].duration_beats, 1), 'note sau giu nguyen');
|
||||
|
||||
// 5. Nối tiếp khác pitch → không cắt
|
||||
const seq = _cleanNativeNotes([
|
||||
{ pitch: 60, start_beat: 0, duration_beats: 4, velocity: 0.8 },
|
||||
{ pitch: 64, start_beat: 2, duration_beats: 1, velocity: 0.8 },
|
||||
]);
|
||||
assert(near(seq[0].duration_beats, 4), 'khac pitch khong bi cat');
|
||||
|
||||
console.log(fails ? 'FAILED: ' + fails : 'ALL PASS');
|
||||
process.exit(fails ? 1 : 0);
|
||||
Reference in New Issue
Block a user