Commit Graph
16 Commits
Author SHA1 Message Date
Brandon Thomas fe573bb31f Merge pull request #416 from grovebit/transient-detection
Transient detection: find quiet and legato onsets, place them on the attack
2026-10-13 06:53:38 -04:00
Brandon Thomas e32c16b82e Merge main into #367 2026-10-13 05:11:18 -04:00
Brandon Thomas 0eee82a49c Merge main into #328 2026-10-13 04:59:30 -04:00
Brandon Thomas 9c7e5018b3 Merge pull request #333 from grovebit/strip-silence-long
Stop Strip Silence deleting clips late in long selections
2026-10-13 04:43:41 -04:00
Brandon Thomas f868c2678f Merge pull request #386 from itrycodinganditsbad/fix-240
fix(meter): keep K-weighting stable below ~3.4 kHz sample rates
2026-10-13 04:28:41 -04:00
Brandon Thomas 8c33cefe1c Merge pull request #286 from marcoarnulfo/fix/modulation-feedback-tail
fix(dsp): chorus and flanger tails follow their feedback (#242)
2026-10-13 04:27:23 -04:00
Brandon Thomas 5bb3e7b3b6 Merge pull request #285 from marcoarnulfo/fix/pitch-shift-full-range
fix(dsp): pitch_shift reaches ±48 semitones (#243)
2026-10-13 04:27:18 -04:00
grovebit 7be845abd1 Transient detection: find quiet and legato onsets, place them on the attack
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.
2026-10-13 00:46:34 +01:00
Fabio LussiandClaude Opus 5.5 81ec430341 fix(meter): keep K-weighting stable below ~3.4 kHz sample rates
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>
2026-10-12 23:45:03 +02:00
grovebit dab2bb956c Don't let Strip Silence delete clips past what one render holds
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.
2026-10-12 18:12:00 +01:00
grovebit 5026b0655d Signal Generator fills the selection with a new clip
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.
2026-10-12 17:38:17 +01:00
Marco Arnulfo 96ea49c7f6 fix(dsp): chorus and flanger tails follow their feedback (#242) 2026-10-12 14:44:57 +02:00
Marco Arnulfo ae46052cac fix(dsp): pitch_shift reaches ±48 semitones (#243) 2026-10-12 14:34:02 +02:00
Marco Arnulfo c73a8e2272 fix(eq): switches and choices redesign the filter on a running EQ (#239) 2026-10-12 12:34:35 +02:00
grovebit acd9e7d54e Play MIDI controllers through instruments
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.
2026-10-12 02:57:22 +01:00
Brandon ThomasandClaude Opus 5.5 755f02fc85 Foundation: time, audio-io, midi, dsp, model, mix, engine, playback crates
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>
2026-10-05 22:27:29 -04:00