feat: M26a vinyl rip — record real records into the library#1
Open
kLOsk wants to merge 11 commits into
Open
Conversation
…ter bar User-owned organization (schema v8): - Star ratings: `tracks.user_rating` (0–5), a clickable star column; overrides imported per-source ratings. iTunes `Rating` (0–100 → 0–5) now imported. - Colour labels: `tracks.color`, a fixed 8-swatch palette; the chosen colour tints the track row and shows as a bordered box in the Color column. - Favourites strip: 8 quick-access slots above the library; drag a Dub crate or imported playlist in, click to load, right-click to clear. Imported favourites are stored by `(source, name-path)` so they survive a re-scan. Dynamic filter bar (client-side, over the in-memory current view): - A configurable, collapsible row of Serato-style filter boxes below the favourites: categorical value lists with counts (Genre/Key/Color/Source/ Artist/Album/Year/Version/Composer), auto BPM tempo buckets, and a ≥N-star Rating threshold. Multi-select = OR within a box, AND across boxes; value lists cascade (counts narrow as other boxes are picked). Boxes derive from the current view and rebuild on source switch; the enabled-set + collapsed flag persist, selections are transient. `recomputeSortedTracks()` now sorts the filtered subset, so the AppKit table inherits filtering at one choke point. Fixes: - Apple Music tracks rendered untitled: the browse query joined iTunes only for ratings, not the §8.1 display chain. iTunes is now a first-class display source (title/artist/album/genre/year/comment/composer/track#, source label, sort keys). Regression test added. - Colour-column swatch was invisible: a Shape inside a `.borderlessButton` menu label is dropped by that style. The swatch now renders as normal cell content with the palette menu as a clear overlay. FFI: rating/color on `LibraryTrack`; `set_user_rating` / `set_track_color`; favourite slot list/set/clear/tracks + `LibraryFavoriteSlot`. The filter bar is Swift-only. Tests: dub-library 223, dub-ffi 60, dub-cli 150 green; clippy clean. macOS app builds; dogfooded on the rig. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014AqZ6CVZ2wXvRisoTg7vWg
- Introduced key lock feature for decks, allowing pitch to be held while tempo follows the platter. This includes a toggle for enabling/disabling key lock and selecting the time-stretch engine (resampler or WSOLA). - Added UI components for key lock control, including live A/B selection and pitch adjustment for testing. - Updated the engine's command structure to support key lock operations and integrated telemetry for key lock state. - Enhanced the library to include new dependencies for time-stretching and key lock functionality. Tests: Verified functionality across the engine and UI components, ensuring key lock engages correctly and the UI reflects the current state.
- Introduced a new echo-out feature that allows users to engage a 1-beat, 100% wet echo effect on decks, enhancing performance capabilities. - Updated the engine to support echo-out commands, including engaging, releasing, and setting parameters for the echo effect. - Enhanced the UI to include an echo-out toggle button, which is conditionally displayed based on user preferences. - Added necessary telemetry to track the echo state, ensuring accurate feedback in the UI and engine. Tests: Verified the functionality of the echo-out feature across the engine and UI components, ensuring correct engagement and disengagement behavior.
Adds the dub siren, the app's signature FX (PRD §6.3). Simple mode is a grid of classic preset one-shots (siren, alarm, laser, bomb, machine gun, lickshot, UFO, riser) fired by pad or keyboard (Z X C V B N M ,). - dub-dsp: SirenVoice — an additive generator (oscillator + pitch LFO + one-shot pitch sweep + filtered noise + exp-decay envelope + percussive retrigger + slap-back delay + lo-fi sample-rate/bit crush). Preset bank resolved off-RT; RT-safe (no alloc/locks, denormal-protected, xorshift noise). SN76477/GS1-inspired; a faithful chip port follows for Expert mode. - dub-engine: per-deck siren rendered after echo-out (so echo's dry-mute can't swallow it); fire-by-id command + precomputed patch bank; siren_state telemetry; optional beat-matched echo override. - dub-ffi: fire_siren_preset / release_siren / siren_preset_names (FFI 47). - apple: SIREN preset grid (Performance + Prep), keyboard triggers, Preferences toggles (enable + beat-match echo), siren colour token. Test plan: - cargo test -p dub-dsp / -p dub-engine / -p dub-ffi (pass; incl. RT-safety under assert_no_alloc, audibility + crush regression tests). - cargo clippy --workspace --all-targets -D warnings (clean); cargo fmt. - make rt-audit (no RT alloc); make app (BUILD SUCCEEDED). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014AqZ6CVZ2wXvRisoTg7vWg
…-FX DSP
The dub siren is now a self-contained instrument (voice + its own PT2399
echo + volume) rendered after the deck echo-out and the FX rack, following
the device-independent three-mode contract (Simple tap / Advanced DUB
super-knob / Expert individual controls + unit selector).
Units:
- GS1 — Rigsmith/Honsitak-style 8-shot ROM chip (dub-dsp::Hk628, +um3561)
- DS01E — Benidub analog-osc siren: 4 MODE tones on the generic SirenVoice,
PITCH/RATE/TRIGGER mirroring the real unit
- SN76477 — MAME-faithful complex-sound-generator port
Shared PT2399 dub-echo chip (dub-dsp::Pt2399): variable-delay pitch-dive,
degrading feedback, companding hiss, delay-dependent grit, resonant TPT-SVF
FILTER (dark LP -> open -> thin HP, log-mapped LUT) and momentary ECHO CUT.
Vintage-FX DSP chain also landed (spring Schroeder reverb / RE-201 tape /
Big Knob HPF / Mu-Tron phaser) with RT-safe macro->coefficient LUTs, parked
behind the deferred deck-role FX channel (ControlMode::Fx lands dormant).
All siren/FX levels trimmed to the -14 LUFS track target; every macro
precomputes its transcendentals off-RT so apply_command stays pure.
Engine: siren_unit/benidub_patches/sn76477_presets state, siren render pass,
command + handle trios. FFI 47->52: SirenUnit, set_siren_unit/_voice/
_controls/_dub_macro, siren_unit_preset_names, ControlMode::Fx. Swift:
per-unit pad relabel, DUB super-knob, Prep-surface Expert panel.
Docs: SHIPPED (M14/M15/M16), UI-BACKLOG §5 (deferred siren/FX work),
LESSONS (DSP/FX RT-safety + render-order), roadmap dashboard.
Removed accidentally-committed insta .pending-snap review artifacts and
gitignored them.
Deferred: siren-sound fine-tuning -> polish phase; Performance-surface /
in-deck Expert panel -> later; FX rack as a deck-role FX channel -> post-release.
Test plan:
- cargo test -p dub-dsp -p dub-engine -p dub-ffi (green)
- cargo clippy -p dub-dsp -p dub-engine -p dub-ffi --all-targets -D warnings (clean)
- cargo fmt
- make app (BUILD SUCCEEDED earlier this session; no code change since)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0187AfZpbDCNe7mKYfrK979r
- Implemented a streaming decode-ahead mechanism for track loading, allowing playback to start while the tail of the track continues to decode in the background. This change significantly reduces the time before playback begins, especially for longer tracks. - Updated the `load_track` function to decode a few seconds of audio synchronously, while the full buffer is pre-allocated, improving responsiveness. - Introduced a new `SampleStore` structure to manage interleaved sample storage, supporting both one-shot and streaming decodes without compromising safety or performance. - Enhanced the analysis pipeline to operate in a lock-free manner, allowing for concurrent track analysis without blocking library access, thus improving overall application performance. - Added tests to ensure the new streaming functionality and analysis improvements work as expected without introducing regressions. This refactor aligns with the PRD principles of maintaining performance while ensuring reliability and responsiveness during track loading and analysis.
- Updated the README to reflect the new schema version (v9) and the addition of 18 tables. - Revised the PRD to clarify the status of customizable columns and the data plumbing for per-source disagreement views. - Adjusted the roadmap to indicate the re-scoped M11d-columns milestone, focusing on the remaining data plumbing tasks. - Corrected the current applied schema version in the LIBRARY-SCHEMA documentation to v9. - Enhanced documentation for the new features including hot cues, reverse loops, and external library importers.
…ttach M26a. ThruSource gains with_record_tap(): full-quality interleaved stereo, pre-gain (a deck-gain ride must not print into the rip archive), carrying the underrun zero-fill so the recorded timeline stays continuous; drop-on-overflow like the M8/M9 mono taps, one push_slice memcpy per block, alloc-free under assert_no_alloc. EngineHandle::attach_thru_source_with_taps(ThruTaps) replaces the combinatorial attach-variant growth: the four existing variants now delegate to one shared trunk (deck check, config validate, reclaim, source build, command push), so a new tap can never fork the attach semantics. Returns the raw record consumer (ThruTapHandles) — the engine knows nothing about ripping. Record ring sized 4 s stereo to absorb capture-worker file-I/O stalls. Test plan: cargo test -p dub-engine (266 tests: 9 new record-tap unit tests incl. alloc-free with all three taps, 5 new with_taps handle tests incl. full input-ring-to-record-ring path; all existing attach-variant tests unchanged and green); clippy -D warnings clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018eHhvnnd8i7C7jvK9Qu8pY
…ctors, dub rip CLI
M26a: record a real record side, split it, and land tagged,
pre-analyzed FLAC tracks in the library. Fully offline by design
(recognition arrives with M26c in a separate leaf crate).
dub-rip (new): RipSession state machine (Armed -> Recording ->
Stopped{Manual|MaxDuration|InputLost}); off-RT capture worker
draining the engine record tap into a crash-safe 32-bit-float WAV
spill while building a chunk-to-frame-aligned live peak envelope
(dub-peaks Decimator); validated manual split plan (min segment 5 s,
proptest-tiled); atomic rip.json manifest (tmp+fsync+rename on every
mutation); idempotent commit — per segment: slice PCM -> FLAC ->
Vorbis tags -> import_file -> pre-analyze via
analyze_compute_with_track (fingerprint/grid/key/LUFS/waveform
populated at import); lossless side.flac archive; spill deleted only
after full success. Producer loss mid-rip fail-safe-stops with the
captured audio intact.
dub-encode (new): 24-bit FLAC via flacenc (pure Rust, Apache-2.0) +
Picard-convention Vorbis/PICTURE tagging via metaflac (MIT) — the
dep graph stays fully permissive; MP3/LAME (LGPL) evaluated and
deferred. Works around flacenc 0.5.1 shrinking StreamInfo
min_block_size on the final short frame (symphonia rejects the file
as variable-blocksize; forced min == max per libFLAC convention).
dub-library: public import_file() extracted from the folder importer
(single per-file path; UnsupportedExtension/ImportFailed variants)
and analyze_compute_with_track() so analysis can run on in-RAM PCM.
dub-cli: `dub rip` — headless dogfood (Enter starts/stops or
--duration; --splits/--titles/--artist/--album/--genre/--year;
--library; session dirs under ~/Music/Dub/Rips/<timestamp>).
Test plan: cargo test --workspace green (dub-rip: 15 unit + 3
integration incl. full capture->split->commit->retry pipeline and
producer-drop salvage; dub-encode: 12 incl. symphonia round-trips +
tag read-back; dub-library: 235 incl. new import_file/
analyze_compute_with_track suites); clippy -D warnings + fmt clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018eHhvnnd8i7C7jvK9Qu8pY
… -> 54) M26a Swift increment A. FFI (crates/dub-ffi/src/rip.rs, polling-only per house style): DubRipSession (start/stop/cancel, 10 Hz status, generation counter for callback-free refetch, packed 12-byte envelope chunks wire-compatible with peaks_extend, split CRUD with stable ids, segments + metadata, confirm_encode_and_import on a named dub-rip-commit worker with coarse job_progress, session_dir); start_thru_for_rip wires the deck-0 record tap at Thru-attach time (the input consumer is take-once — a tap cannot be bolted on later); cancel refuses once any segment imported (the FLACs live in the session dir and the library references them by path — retry is the recovery path); list_raw_input_devices exposes the pre-classifier HAL input list for the DEBUG built-in-soundcard fallback. Swift: Preferences "Vinyl recording" toggle; PREP/PERF override switch in the status strip (signed-off exception to hardware auto-detect — override clears on interface unplug); PrepRipBar (record button, elapsed, level meter with clip flash, stop); RipLiveOverview (incremental growing waveform, shared OverviewDecimator extracted from TrackOverviewView); RipSplitMarkerOverlay (drag with local echo + 30 Hz throttle, double-click add, context-click remove, arrow-key nudge, boundary audition); RipReviewPanel (editable metadata cards, per-segment encode/import progress dots, idempotent retry, two-step discard). DEBUG builds record from the built-in mic when the Developer mode override is engaged (mono duplicated to both slots via channel map); Release stays DJ-interface-only. Rip availability check kept cheap — HAL enumeration deferred to the record click (SwiftUI re-evaluates at deck-poll rate). Test plan: cargo test -p dub-ffi (76 tests incl. 13 rip-session tests: state mapping, split CRUD rollback, envelope packing, full commit -> Done with library assertions, cancel-refusal after import, closed-library failure); clippy -D warnings clean; make app BUILD SUCCEEDED; make snapshot — 12 new RipSnapshotTests green (PrepRipBar/RipReviewPanel/RipSegmentCard/marker-overlay states); manually verified end-to-end on the built-in soundcard (record -> split -> encode -> import). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018eHhvnnd8i7C7jvK9Qu8pY
PRD: new §5.2.7 (vinyl rip feature spec incl. the Preferences-gated PREP/PERF override as a documented auto-detect exception, the FLAC-not-MP3 rationale, review-always flow, and the DEBUG built-in fallback status) + the M26 row in §12.2 with a/b/c sub-milestones and the M21 seeding synergy. LICENSE-DEPENDENCIES: flacenc (Apache-2.0, incl. the min/max block-size symphonia workaround), metaflac (MIT), serde/serde_json promoted workspace-wide; explicit statement that MP3/LAME (LGPL) was evaluated and deferred so the fully-permissive posture is unchanged; ureq noted as the planned M26c addition. LIBRARY-SCHEMA: rip session artifacts (rip.json manifest v1, session dir layout, atomic write + idempotent-retry semantics, Picard-style Vorbis tagging — schema stays v9); fingerprint algorithm claim corrected (dedupe uses the chromaprint TEST1 preset, not AcoustID's TEST2 — M26c computes TEST2 transiently). AGENTS: dub-encode + dub-rip in the repo layout, dep-list additions, `rip` in the dub-cli command list; stale dub-stretch "Rubber Band FFI" description fixed (M14 shipped pure-Rust WSOLA); same TEST1 fingerprint correction. SHIPPED: M26a entry + refreshed currently-shipped header (was stuck at M11d.8). LESSONS: new Vinyl rip section (tap-at-attach-time, flacenc block-size gotcha, classifier hides the built-in mic, spill-to-disk rationale, cancel-refusal after import) + three stale bullets corrected (device-picker invariant gains the signed-off M26 exception; GPLv3 bullet now reflects the permissive-graph reality; snapshot tests exist). UI-BACKLOG: new §6 with the deliberate M26a gaps (R-39 live encode progress, R-40 recovery banner, R-41 capture deck pane, R-42 Keychain token, R-43 Performance-mode rip + monitor mute). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018eHhvnnd8i7C7jvK9Qu8pY
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Drop the needle, get split, tagged, pre-analyzed FLAC tracks in the library. First increment of the M26 vinyl-rip milestone (PRD §5.2.7): capture + manual splits + encode + import, fully offline by design.
ThruSource(pre-gain, underrun zero-fill, drop-on-overflow, alloc-free) +attach_thru_source_with_tapsoptions-struct API (existing variants delegate to one shared core).dub-rip(new): session state machine, crash-safe WAV spill + chunk-aligned live envelope, validated split plan, atomicrip.jsonmanifest, idempotent commit (FLAC → Vorbis tags → import → pre-analysis), lossless side archive.dub-encode(new): 24-bit FLAC (flacenc) + Picard-convention tagging (metaflac) — dep graph stays fully permissive; MP3/LAME evaluated and deferred. Includes the flacenc min/max-block-size workaround (symphonia rejects its default output as variable-blocksize).dub-library: publicimport_file+analyze_compute_with_track.dub ripheadless dogfood.DubRipSessionpolling surface,start_thru_for_rip(tap wired at attach time), Preferences "Vinyl recording" toggle, PREP/PERF override switch (signed-off auto-detect exception), rip bar + live waveform + draggable split markers + review panel with per-segment progress. DEBUG-only built-in-soundcard fallback for no-rig dogfooding.Deferred (tracked in UI-BACKLOG §6 + PRD): M26b auto gap detection / auto start-stop / recovery; M26c recognition (AcoustID + MusicBrainz + Discogs).
Test plan
cargo test --workspace: 1350 tests green (new: 9 record-tap + 5 with_taps engine tests incl.assert_no_alloc; 18 dub-rip incl. full capture→split→commit→retry pipeline + producer-drop salvage; 12 dub-encode symphonia round-trips + tag read-back; 6 dub-library import/analyze refactor tests; 13 FFI session tests incl. cancel-refusal-after-import).cargo clippy --all-targets -- -D warnings+cargo fmt --check+./scripts/check-docs.shclean.make appBUILD SUCCEEDED;make snapshot— 12 newRipSnapshotTestsgreen.mainseparately; once it lands, this PR's diff reduces to the four rip commits.🤖 Generated with Claude Code
https://claude.ai/code/session_018eHhvnnd8i7C7jvK9Qu8pY