Commit Graph
838 Commits
Author SHA1 Message Date
grovebit cf9cced90d Create MIDI clips with the Pencil, Consolidate and the MIDI Editor
Fixes #282: on an empty MIDI or instrument track there was no UI way to
get a MIDI clip, so notes couldn't be entered by hand. `midi.new_clip`
existed but had no menu item, key or tool.

- Consolidate now works on MIDI and instrument tracks. The MIDI in the
  selection becomes one clip covering the whole selection, gaps
  included. It keeps the sound: muted clips stay silent, and notes are
  cut where their clip ended. An empty selection gives an empty clip,
  as in Pro Tools. Audio tracks are unchanged.
- The Pencil draws a MIDI clip on an empty part of a MIDI or instrument
  track: a click makes one bar, a drag covers the bars it crosses, with
  an outline while dragging. Clips start and end on barlines, the rule
  Pro Tools uses for MIDI it creates. A clip stops at its neighbour
  rather than overwriting it, and clicks on existing clips add nothing.
  The new clip is selected, so the MIDI Editor shows it.
- With an empty MIDI or instrument track selected, the MIDI Editor says
  so, and a click adds a one-bar clip at the insertion point (or a clip
  over the selection).
2026-10-12 14:49:51 +01:00
Marco Arnulfo e5d5996f7d fix(event): Constant tempo applies only over its range (#238) 2026-10-12 15:36:40 +02:00
Marco Arnulfo 93ddd419f5 fix(track): Clear Trim Automation removes the trim lane (#233) 2026-10-12 15:24:32 +02:00
Marco Arnulfo c85952ecc3 fix(edit): cut clip gain keeps the removed gain for paste (#230) 2026-10-12 15:17:52 +02:00
Marco Arnulfo 5f4cc96578 fix(midi-editor): Backspace in a text field leaves the selected notes alone (#223) 2026-10-12 15:13:18 +02:00
Marco Arnulfo fe919e5d57 fix(clip): Elastic Properties stretches the clip by the requested ratio (#227) 2026-10-12 14:46:05 +02: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 35e9d63bee fix(markers): markers.edit applies the documented name (#236) 2026-10-12 14:30:57 +02:00
Marco Arnulfo 9144570b6e fix(video): H.264 parameter sets check their arithmetic before range checks (#251, #252) 2026-10-12 12:38:03 +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
Marco Arnulfo f792eae580 fix(midi-editor): Quantize snaps to the grid the editor shows (#226) 2026-10-12 12:32:41 +02:00
Marco Arnulfo 0d18612a87 fix(edit-window): Pencil clicks on plugin lanes use the plugin's range (#224) 2026-10-12 12:30:02 +02:00
Marco Arnulfo bff9ca1390 fix(window): Hide All Floating Windows also hides Automation (#253) 2026-10-12 12:18:00 +02:00
Marco Arnulfo 89ff513b78 fix(video-universe): filmstrip thumbnails use the clip's own movie time (#225) 2026-10-12 12:15:28 +02:00
Marco Arnulfo 7d173d3a25 fix(midi-editor): a selected video clip is not a MIDI target (#258) 2026-10-12 12:13:21 +02:00
Marco Arnulfo 4c4414d006 fix(window): keep the MIDI Editor open on Bring to Front (#254) 2026-10-12 11:58:36 +02:00
Marco Arnulfo 67158020f0 fix(control): apply the engine.commands filter to UI commands (#255) 2026-10-12 11:56:06 +02:00
Brandon ThomasandClaude Opus 5.5 df0c4790b7 Release prep: 0.6.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:44:38 -04:00
Marco Arnulfo 30372886cf fix(track): vca: null clears a VCA assignment, as documented (#237) 2026-10-12 11:03:28 +02:00
Marco Arnulfo 217bea462d fix(dsp): a huge finite sample rate is no pitch, not an overflow (#241) 2026-10-12 10:59:23 +02:00
TheMojoMan 5e1c247eda fix(ui): horizontally scroll the Edit toolbar
Groups clipped when the window was narrower than the toolbar; wrap the whole row in a horizontal ScrollArea so every tool stays reachable.
2026-10-12 10:10:00 +02:00
NetterSilver faf2a13468 Add my contributor entry 2026-10-12 09:07:02 +02:00
NetterSilver 9f1a62470d Reject invalid --rate and --bit-depth in convert (#256)
convert ignored a --rate that did not parse and mapped any unknown
--bit-depth to 24-bit, then reported success. Both options are now
validated before the input is read; an invalid value prints which
option was wrong and exits non-zero without writing output. Omitted
options keep their defaults.
2026-10-12 08:54:26 +02:00
NetterSilver e1e15cf3ab Compute timecode frame counts in i128 and clamp (#248)
Timecode::to_frames multiplied caller-supplied hours in i64 without a
check, so "9223372036854775807:00:00:00" panicked under overflow checks
and wrapped without them. The count is now computed in i128 and clamped
to i64, and samples_at_frame clamps instead of returning 0 on overflow,
so huge labels saturate toward the right end of the timeline.
2026-10-12 08:53:51 +02:00
NetterSilver cfce845f70 Saturate the bar offset for bars before 1 (#249)
bar_start_tick subtracted 1 from the bar before its saturating multiply,
so the minimum i64 bar panicked under overflow checks. Parsing
"-9223372036854775808|01|000" as Bars|Beats now returns a saturated
negative position instead.
2026-10-12 08:52:59 +02:00
Jafz2001 87b348fecc Complete routing folder management 2026-10-11 23:50:06 -03: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
grovebit 33ad8264c8 Give plugins the session's tempo and transport
Hosted plugins never learned the session's tempo or where the song was:
- CLAP got a null transport;
- VST3 got a fixed 120 BPM 4/4 context that never played and counted
  processed samples as the song position;
- Audio Units had no host callbacks.
Tempo-synced delays, LFOs and arpeggiators ran at 120 BPM and ignored
the transport.

Before each block the mix engine now hands every insert, instrument and
master-fader plugin a soundcraft_dsp::Transport (Plugin::set_transport):
play and record state, tempo and meter at the block start, and the
timeline position in samples, seconds and quarter notes, with the
current bar. Built-in plugins ignore it.
- CLAP: a clap_event_transport (fixed-point beats and seconds, bar start
  and number, time signature, play and record flags).
- VST3: ProcessContext tempo, time signature, project time in samples and
  quarter notes, bar position, kPlaying and kRecording. Continuous time
  stays the processed-sample count.
- AU: kAudioUnitProperty_HostCallbacks (beat and tempo, musical time
  location, transport state 1 and 2), read from atomics the instance
  owns and frees after disposing the unit.

Loop (cycle) ranges aren't reported yet: playback keeps the loop range
to itself.
2026-10-12 02:56:55 +01:00
grovebit 75fae5535f Share the session with the UI instead of copying it every frame
The Edit window copied the whole session once per frame for the rulers and
twice for every visible track (track_row and lane_interaction), plus once more
for each track shown as playlists and once for the Timecode 2 ruler. The MIDI
editor copied it once per frame, and every Edit row and Mix strip copied its
track. A copy includes every plugin's parameters and saved state: in a 23-track
session with three plugins per track (200 parameters and 64 KB of state each),
one copy took 0.2 ms and each Edit window frame 15.6 ms, which is paid on every
frame of playback, dragging and scrolling.

These now hold the engine's Arc<Session> (session_arc). It is still a snapshot:
a command run later in the frame copies on write, as it already does to keep
its undo step. timebase_ruler takes the frame rate as a parameter, so the
Timecode 2 ruler no longer needs a copy with another frame rate.

Median frame times in a release build, with 21 of 23 tracks on screen:

- The demo session with its tracks tripled: Edit window from 1.25 to 0.68 ms.
- Plus 5,000 volume automation points per audio track: from 2.03 to 0.68 ms.
- Plus three plugins per track: Edit window from 15.6 to 0.69 ms, Mix window
  from 1.14 to 0.71 ms.

The Edit window (every ruler, a playlist lane and an automation lane), the MIDI
editor and the Mix window render byte-identical images before and after.
2026-10-12 02:31:02 +01:00
Brandon Thomas acb08ff5f0 Merge pull request #108 from scottcampbelldata/sampler-instrument
Add a built-in Sampler instrument
2026-10-11 21:06:08 -04:00
Fabio LussiandClaude Opus 5.5 092b3cc890 Consolidate covers the whole edit selection
Consolidate clamped its range to the track's last clip end plus one
second, so on an empty track a 0-5 s selection made a 1 s clip and a
2-7 s selection made nothing. It now renders, clears and places exactly
the selection, and refuses a selection too long to render instead of
placing a shorter clip. Prepare DPE keeps the old per-track bound.

Fixes #164

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:02:56 +02:00
Brandon Thomas b9dec547df Merge remote-tracking branch 'origin/main' into sampler 2026-10-11 21:02:27 -04:00
Brandon Thomas 525f00b719 Merge pull request #157 from grovebit/unsaved-changes
Ask before discarding unsaved changes; Save opens Save As for new sessions
2026-10-11 20:59:57 -04:00
Brandon Thomas 48c3f65e12 Merge pull request #160 from grovebit/menu-fixes
Clip > Rename opens its dialog; Undo and Redo name their step; the Window menu's keys work
2026-10-11 20:59:52 -04:00
Brandon Thomas 6754f04a05 Merge pull request #192 from grovebit/idle-repaint
Let the desktop app sleep when idle or covered
2026-10-11 20:59:48 -04:00
Brandon Thomas 7f2a6977b5 Merge pull request #56 from grovebit/trackpad-scrolling
Trackpad-friendly Edit window: smooth panning, pinch and Cmd+scroll zoom
2026-10-11 20:59:45 -04:00
Brandon Thomas c5a8fd2f52 Merge pull request #49 from grovebit/drag-selection
Fix drag selection and the insertion point in the Edit window
2026-10-11 20:59:41 -04:00
Brandon Thomas 7c7e8deaaa Merge pull request #158 from natocool2/fix/web-files
Web: open, import, save and export files in the browser (fixes #63, fixes #64)
2026-10-11 20:59:37 -04:00
Brandon ThomasandClaude Opus 5.5 6347aad211 Rebuild on the amended drag-selection (#49)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:57:17 -04:00
Brandon ThomasandClaude Opus 5.5 c5efedd740 Merge drag-selection (#49) into trackpad
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:57:06 -04:00
Brandon ThomasandClaude Opus 5.5 ef0ce90b66 Merge origin/main into drag-selection
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:56:43 -04:00
Brandon ThomasandClaude Opus 5.5 2ec2698331 Grid moves snap the grabbed clip, not whatever is under the pointer now
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:56:37 -04:00
Brandon ThomasandClaude Opus 5.5 f855836af1 Idle test: playing repaints at the adaptive meter rate from #207
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:51:00 -04:00
Brandon ThomasandClaude Opus 5.5 7a1035a3d5 Merge origin/main into sampler-instrument
#181 already pushes instrument params each block, so drop the duplicate
apply_instrument_params. Import Session Data now brings a Sampler's sample
along and remaps its source id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:45:12 -04:00
Brandon Thomas 480b0b2dd7 Merge pull request #211 from storytold/fmt-send-doc
rustfmt the mix.send spec
2026-10-11 20:43:32 -04:00
Brandon ThomasandClaude Opus 5.5 b01abf6dd7 rustfmt the mix.send spec
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:43:27 -04:00
Brandon ThomasandClaude Opus 5.5 d37386721a Merge origin/main into unsaved-changes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:42:42 -04:00
Brandon ThomasandClaude Opus 5.5 1c52ad1c6e Merge origin/main into idle-repaint
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:42:34 -04:00
Brandon ThomasandClaude Opus 5.5 e7e4a19cfa Merge origin/main into fix/web-files
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:42:26 -04:00