record-player
record-player is the Rust audio engine behind BITNEEDLE. It turns decoded
programme audio and hand motion into the sound of a record on a deck: platter
and slipmat motion, scratching, timed controls, and record-surface behavior.
You give the engine bounded PCM windows and sample-timed controls. It gives you host-rate audio, metering, and serializable deck state. Source decoding, audio callbacks, and UI belong to the host application.
ScratchAcousticDsp is the renderer. It owns the deck, the record's acoustic
behavior, and the scratch gate in one host-rate path, so a settled record at
nominal speed passes the programme through untouched while a hand on the
record reads it the way a needle would.
Entry points
ScratchAcousticDsp— the production renderer. Bounded PCM windows in, host-rate audio out.ScratchGestureMapper— converts timestamped pointer samples into sample-timed mechanical controls. The C ABI wraps this and this alone.PlayerEngine(exported asWasmPlayerEngineon the web target) — serializable transport and deck state, events, host commands, and view state.DeckMechanicalState,ScratchGate, and theVinylVfx*processor are the pieces the renderer is built from.
The workspace contains two crates:
record-player— the Rust library and its optional WASM exports;record-player-capi— ABI version 5, for the scratch gesture mapper. The header isinclude/record_player.h.
How BITNEEDLE consumes it
- Phone.
bitneedle-native-corewraps oneScratchAcousticDspinrecord_player_bridge.rsand exposes it through thebitneedle_native_record_player_*C ABI; theDeckSwift package drives that. The live pointer tracker is BITNEEDLE's own. - Browser.
web.mkbuilds this crate's WASM, and the AudioWorklet and take-render worker instantiateScratchAcousticDspdirectly.
PlayerEngine and the gesture-mapper C ABI ship for hosts that drive the deck
through them. Consume a revision rather than copying DSP, mechanics, preset
behavior, or gesture policy out of this repository into an application.
What the engine models
ScratchAcousticDsp models:
- platter, record, motor, bearing, slipmat, and hand-contact motion;
- playback, braking, free spin, pitch, seek, needle lift, and run-out;
- the Baby, Stab, Chirp, Transform, Flare, Crab, Orbit, and Drum scratch presets;
- click timing, scratch-gate state, crossfader automation, and deterministic replay;
- locked grooves, groove wear, pressing defects, stylus effects, and surface foley;
- velocity gain and RIAA speed tilt away from nominal speed, so a settled 1× pass is bit-exact;
- bounded window requests, output metering, and real-time recovery diagnostics.
Real-time contract
The audio path is #![forbid(unsafe_code)] and uses bounded, preallocated
storage; under cargo test an allocation guard fails a test that allocates on
it. Timed controls use absolute frames. The audio callback is for rendering,
so keep source decoding, whole-record allocation, network work, and UI work on
the host's other threads.
Build and test
Run the complete Rust workspace:
Build the WASM the browser instantiates:
Check the native ABI and header:
Evidence and design records
docs/RENDERER_ARCHITECTURE_DECISION_LOG.mdrecords the renderer's design and the transparent-playback rule.docs/SCRATCH_FEEL_DECISION_LOG.mdis the scratch-feel investigation and its live A/B evidence.docs/REFERENCE_ENGINE_GAP_AUDIT.mdcompares this engine against the reference implementation.docs/PERCEPTUAL_ACCURACY_REPORT.mdseparates confirmed audible behavior from work that still needs listening evidence.docs/PHYSICS_INVESTIGATION_LOG.mdtracks model evidence, uncertainty, and rejected assumptions.
The SL-1200MK7 deck mechanics profile is a seed profile. Unit tests establish deterministic software behavior; hardware measurements and independent listening tests remain the reference.