Squashed from work: - score: a headless music engraving, playback and notation engine - score: the notation app — pianist mode, editing, playback - piano_model: it was a plucked string by construction, and 20 voicings - score: one document you can pan, zoom and navigate - piano_model: the body tap was a click, and the objective was rewarding noise - score: add the sound panel and library modules - piano_model: a second engine, and the attack that finally sounded right - score: two instruments, reverb and brightness — and the rest of the panel gone - score model: a note remembers how it was struck, and the score remembers the pedal - score import: keep the velocities and the pedal the file was carrying - score playback: play the performance, not a flattened copy of it - score ui: the music list moves to the sidebar, and the view stops fighting itself - score: the application ships its font and eight performances - piano_model: a limiter that rides the music, so the knee stops shaping chords - piano_model: the forte bell was the treble's dynamic slope, and the bass was dying at its own prompt rate - piano_model: the bridge decides each partial's decay, and a fixed multiplier cannot say that - piano_model: each partial gets its own two coupled modes, from the eigen algebra - piano_model: a median that fell between the peaks made every bass partial a drain - score-ai: LocalBroker — the seam's in-process implementation over the session engine (aicore P8) - client + chat dispatcher: the dead wire comes out (aicore P7/P8) - score_pdf: the score model grew a pedal map — the pdf importer initialises it - libs: the zero-warning sweep — stitch casts say what they mean, xatlas keeps upstream's surface quietly - score app: the shipped-piece test speaks the PERFORMANCES table - zero-warning sweep, round three — the model lanes and the deep examples
91 lines
3.1 KiB
Rust
91 lines
3.1 KiB
Rust
// Performance measurements (ignored by default; run explicitly, in release):
|
|
// cargo test -p makepad-piano-model --release -- --ignored perf_ --nocapture
|
|
// Reports realtime headroom for full 88-key polyphony on the scalar and SIMD
|
|
// kernels, per-voice cost, and multicore scaling of the offline path.
|
|
|
|
mod common;
|
|
|
|
use common::*;
|
|
use makepad_piano_model::{Piano, PianoEvent::*};
|
|
use std::time::Instant;
|
|
|
|
/// Strike all 88 keys, pedal down, re-strike every 1.5 s so every voice and
|
|
/// every sympathetic bank stays busy for the whole run.
|
|
fn full_load_script(seconds: f64) -> Vec<Ev> {
|
|
let mut script = vec![ev(0.0, Sustain { value: 1.0 })];
|
|
let mut t = 0.0;
|
|
while t < seconds - 0.1 {
|
|
for key in 21..=108u8 {
|
|
script.push(ev(t + (key as f64 - 21.0) * 0.0001, NoteOn { key, velocity: 110 }));
|
|
}
|
|
t += 1.5;
|
|
}
|
|
script
|
|
}
|
|
|
|
fn run_load(p: &mut Piano, seconds: f64, block: usize, workers: usize) -> f64 {
|
|
let script = full_load_script(seconds);
|
|
let total = (seconds * FS as f64) as usize;
|
|
let start = Instant::now();
|
|
if workers <= 1 {
|
|
let (l, r) = render(p, &script, total, block);
|
|
assert!(l[total - 1].is_finite() && r[total - 1].is_finite());
|
|
} else {
|
|
let (l, r) = render_mt(p, &script, total, block, workers);
|
|
assert!(l[total - 1].is_finite() && r[total - 1].is_finite());
|
|
}
|
|
start.elapsed().as_secs_f64()
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn perf_polyphony() {
|
|
let seconds = 10.0;
|
|
for (name, scalar) in [("simd", false), ("scalar", true)] {
|
|
let mut p = Piano::new(FS);
|
|
p.set_force_scalar(scalar);
|
|
let path = p.kernel_path();
|
|
let wall = run_load(&mut p, seconds, 512, 1);
|
|
let xrt = seconds / wall;
|
|
println!(
|
|
"{name} ({path:?}): 88 keys ringing + pedal down + full fx: {wall:.3} s wall for {seconds} s audio = {xrt:.1}x realtime ({:.1}% of one core, {:.1} us/voice/block-of-512)",
|
|
100.0 / xrt,
|
|
wall / (seconds * FS as f64 / 512.0) / 88.0 * 1e6
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn perf_single_voice() {
|
|
for (name, scalar) in [("simd", false), ("scalar", true)] {
|
|
let mut p = Piano::new(FS);
|
|
p.set_force_scalar(scalar);
|
|
let script = vec![ev(0.0, NoteOn { key: 24, velocity: 110 })]; // worst-case voice (128x2 modes)
|
|
let total = (10.0 * FS as f64) as usize;
|
|
let start = Instant::now();
|
|
let (l, _) = render(&mut p, &script, total, 512);
|
|
let wall = start.elapsed().as_secs_f64();
|
|
assert!(l[total - 1].is_finite());
|
|
println!("{name}: single A0 voice 10 s: {wall:.3} s wall ({:.2}% of one core incl. shared fx)", wall * 10.0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn perf_multicore() {
|
|
let seconds = 10.0;
|
|
let mut base = 0.0;
|
|
for workers in [1usize, 2, 4, 8] {
|
|
let mut p = Piano::new(FS);
|
|
let wall = run_load(&mut p, seconds, 512, workers);
|
|
if workers == 1 {
|
|
base = wall;
|
|
}
|
|
println!(
|
|
"offline multicore, {workers} workers: {wall:.3} s wall = {:.1}x realtime (speedup {:.2}x)",
|
|
seconds / wall,
|
|
base / wall
|
|
);
|
|
}
|
|
}
|