detect_transients (Tab to Transient, Separate at Transients, Beat Detective)
missed most onsets that weren't the loudest around, and put notes that start
over a ringing note 25-35 ms early, in the previous note's tail. Its
threshold was a share of the loudest flux in the analysed audio, so quiet
hits vanished next to loud ones and the result changed with where the
analysis started. It placed each onset on the first sample reaching a
quarter of the frame's peak, which a ringing note already does.
- Vibrato-suppressed spectral flux: 21 ms windows 5.3 ms apart, log
magnitudes on 24 bands per octave from 30 Hz to 20 kHz, each band compared
with the loudest of its neighbours in the frame before, so vibrato and
chorus don't count. Channels are analysed as the sum of their energies,
so stereo content that cancels in mono still counts.
- A peak must be the highest within 30 ms and exceed the mean flux from
100 ms before to 70 ms after by a fixed amount (2.5 at sensitivity 0.5),
so quiet hits count and the result doesn't depend on where the audio
starts.
- A fast release smears energy between a note's partials, which looks like
a rise. A detection also needs a 3 dB step in its high-passed energy, or
total energy that grows in its frame or the next.
- Each onset goes to the step edge of the high-passed energy (the sample
where the next 3 ms gain most over the 6 ms before), then back to where
the energy crosses the midpoint between the two: 0.3-0.5 ms before
percussive attacks.
On 29 synthetic cases with known onsets (drums with ghost notes; drum-synth
grooves dry and through reverb, compression and limiting; 180 bpm; flams and
claps; legato piano; plucked bass and guitar; strums; 808 kicks; hand drums;
synth bass, leads, pads and staccato notes; string swells; a vibrato pad;
quiet hits over noise and hum; stereo pairs; formant syllables), the
F-measure went from 0.658 to 0.977. It found 1398 onsets instead of 870,
with 5 false ones instead of 315; all 5 are vowels starting after a
consonant. Still missed: hats under a crash, ghost notes under a louder
hit's tail, and slow string swells.
The old offline_meters test bursts stopped dead at -37 dB, an audible click
that now counts as a transient; they ring out instead.
The shelf corner (~1682 Hz) sits at or above Nyquist for rates under
~3.4 kHz. The bilinear prewarp tan() then wraps negative (unstable
poles, +750 LUFS readings, -144 integrated) or aliases. Hold the
design frequency below Nyquist (0.49 * sr, as Coeffs::design does).
Coefficients at 8 kHz and above are unchanged.
Fixes#240
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Strip Silence rendered the whole selection at once, and a render stops
at 2^29 samples over all channels: 93 minutes of stereo at 48 kHz, 46
at 96 kHz. Everything past that read as silence, so the clips there
were deleted. A 105-minute selection on a stereo track lost its clip at
100 minutes.
It now renders at most 2^24 samples at a time, only where unmuted audio
clips play, into a streaming version of the silence detector
(`SilenceScan`, which `non_silent_ranges` now runs on). A window takes
at most 64 MB instead of up to 2 GiB, and more than a day of clips is
refused rather than scanned for minutes. `render_clips` now skips clips
outside its range before looking up their clip effects, so a track of
5,000 clips strips as fast as before. Results are unchanged on the demo
sessions and on 10 minutes of tone bursts that cross window edges.
Fixes#220: Render in the Signal Generator window did nothing on a time
selection over an empty part of a track. AudioSuite only processed the
clips under the selection, so with none it stopped with "select audio
clips first", and that went to the status line only.
- A generator (soundcraft_dsp::is_generator, today only the Signal
Generator) ignores its input, so it no longer needs clips. With a
time selection it renders a new clip over exactly the selection on
each selected audio track, over whatever was there, as Pro Tools'
Signal Generator does. Before, it replaced whole clips under the
selection and left the empty parts empty.
- Other tracks in the selection are skipped. With no audio track in
it, or no time selection and no clips, it reports why.
- Explicit `clips` still process those clips. Effects behave as before.
- The AudioSuite window's hint says what Render will do for a
generator.
Imported MIDI keeps its controller events: sustain pedal and other CCs,
pitch bend, channel and poly pressure, program changes. Playback sent
instruments only note-ons and note-offs, so a piano part lost its pedal
and bends went flat.
- Plugin::midi takes any other MIDI 1.0 channel message at a sample
offset. The mix engine sends each clip's controllers at their sample,
after the Real-Time Properties delay (and transpose, for poly
pressure), on channel 1 like the notes.
- After a seek, a stop or a loop wrap, an instrument first gets Reset All
Controllers (CC 121), then the last value each controller had on its
track before the playhead. A pedal held before the start point is
still down.
- CLAP: raw MIDI events when the note port speaks the MIDI dialect.
- VST3: CCs, aftertouch and pitch bend become parameter changes at their
offset, through the plugin's IMidiMapping. It is asked once in prepare,
on the main thread, and there is one point per parameter per block.
Poly pressure is a PolyPressureEvent. Program changes have no VST3
mapping and are dropped.
- AU: MusicDeviceMIDIEvent for instruments and music effects.
- The built-in synth follows the sustain pedal, pitch bend (2 semitones
each way), Reset All Controllers and All Notes Off.
- The sampler ignores the pedal, pitch bend and Reset All Controllers
for now.
The AU check of MIDI volume lives inside the existing DLS test. Creating
two DLSMusicDevice instances at once on two threads crashes inside the
unit, with or without this change. Two instances created one after
another render in parallel fine.
Workspace for SoundCraft, a clean-room pure-Rust Pro Tools-class DAW:
timebases and tempo maps, WAV/AIFF/FLAC/symphonia audio IO with peaks,
SMF and MIDI ops, 26 original DSP plugins, the session model, the mix
engine, ~180 registered commands with undo, a cpal player, and the
agent rules (AGENTS.md, ATTRIBUTION.md).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>