Sitelet https://github.com/WorldLinkStudio/mpcsample/pull/3
Skip to content

Sample Library Browser, Step Sequencer, MIDI, Sample Recording, Effects Rack - #3

Open
ResistanceIsUseless wants to merge 13 commits into
WorldLinkStudio:mainfrom
ResistanceIsUseless:main
Open

ResistanceIsUseless wants to merge 13 commits into
WorldLinkStudio:mainfrom
ResistanceIsUseless:main

Conversation

@ResistanceIsUseless

Copy link
Copy Markdown

Sample Library Browser, Step Sequencer, MIDI, Sample Recording, Effects Rack

Adds a sample library browser for large local sample collections, bidirectional MIDI control, a step sequencer with native .xpj export, system-audio sample recording, an offline multi-effect rack, and a more reliable SD card export workflow.

Sample Library Browser

  • Scan a local folder of samples (tested with 25k+ files) in the background — fast enumeration, waveform thumbnails, on-disk cache so repeat scans are near-instant
  • Auto-tagging from folder/filename conventions (Kick, Snare, 808, Loop, Vocal, ...), plus manual tags, pack filtering, and drag-to-pad
  • Corrupt/unreadable audio files are detected and hidden instead of showing up as silent dead ends
  • Scaled to handle the 25k-file case specifically: smaller waveform cache (600→72 points, fixing a cache file that previously exceeded V8's max string length), batched progress updates instead of one store update per file

MIDI

  • Bidirectional Web MIDI with the MPC Sample hardware: pad hits in and out, correct per-bank note mapping (hardware shifts notes by 16 per bank), hardware-originated triggers aren't echoed back

Step Sequencer

  • Live/MIDI-driven pattern recording, 1–8 bars, 8th/16th/32nd resolution, per-step velocity, pattern persisted across reloads
  • Exports into the project's real, editable native MPC sequence data, not just in-app playback

Sample Recording

  • Records directly from system/app audio (any browser tab, VLC, Plex, ...) with no virtual audio cable — live level meter, waveform preview, click-to-assign to a pad

Effects Rack

  • Stack multiple effects (filter, distortion, bit crusher, chorus, delay, reverb) and render the whole chain through a sample in a single offline pass, with live preview before committing
  • Replaces the hardware's forced add-effect → resample → add-next-effect workflow for every stacked effect

Desktop / SD Card Workflow

  • Writes projects straight to the MPC SD card unzipped (.xpj + _[ProjectData]/*.wav) — no zip/unzip round-trip
  • Detects the mounted SD card by contents instead of a hardcoded volume name, so it works regardless of how the card is labeled; eject uses the detected volume too
  • Fixed a CORS issue blocking pad playback of library samples in dev
  • UI polish: draggable Sample Browser/Step Sequencer panels, hideable HUD

Testing

  • npm run typecheck and npm test pass (main + Electron test configs)
  • Effects rack and SD card detection were also smoke-tested manually against a running app and a real mounted SD card

🤖 Generated with Claude Code

web-flow and others added 13 commits September 7, 2026 20:10
…quencer

Three major features built on top of the MPC Sample desktop app:

- Sample Library Browser: scan/browse/preview/tag/drag-to-pad for large
  local sample folders. Scanning and waveform decode run in the Electron
  main process so the UI never blocks, with a disk cache keyed by
  path+size+mtime and a restricted mpc-sample:// protocol for streaming
  preview/export without loading whole files into renderer memory. Also
  fixes playback of WAVs with a trailing RIFF LIST/INFO chunk (FL Studio
  export quirk) that was corrupting the served byte range.

- Bidirectional MIDI: fixed hardware input (the MPC Sample shifts its
  outgoing note number by 16 per physical pad bank; the app only accepted
  notes 36-51, silently dropping every bank past A) and added MIDI output
  so UI-triggered pad hits echo back to the physical device (MidiInput now
  also implements MidiOutputLike; store.triggerPad/releasePad take a
  fromHardware flag to avoid echoing hardware-originated hits back out).

- Step sequencer (v1, live/MIDI-driven): program pad hits on a
  configurable bar/resolution step grid, played back via Tone.Transport
  through the existing engine + MIDI-out path. Click-to-preview on step
  activation, scroll-to-adjust per-step velocity, and pattern persistence
  to localStorage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reverse-engineered the real event schema from a user-recorded .xpj
project (note events live per-track at
sequences[].value.trackClipMaps[0][entryIdx].value.eventList.events,
960 ticks/beat, note = padIdx + 36) and wired the step sequencer's
pattern into the existing kit-export pipeline so it shows up as a
real, editable sequence on the hardware instead of only playing back
live from the app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Panels can be dragged by their header to anywhere in the window
(pointer-drag, matching the existing Knob/PitchFader idiom;
double-click resets to the docked position), composing a
--drag-x/--drag-y translate on top of each panel's existing
slide-open/centering transform rather than fighting it. The
bottom-left HUD can be hidden/restored via a small persistent tab,
with hudVisible persisted alongside the other UI settings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Uses Electron's session.setDisplayMediaRequestHandler with
useSystemPicker: true so getDisplayMedia triggers macOS's own
ScreenCaptureKit-backed picker — pick a specific window (a browser
tab, VLC, Plex) and capture just its audio. Recording taps a
dedicated native AudioContext via a ScriptProcessorNode (discovered
live that Tone.getContext().rawContext is actually Tone's
standardized-audio-context wrapper, which doesn't implement
createScriptProcessor), encodes to 16-bit PCM WAV with a new
hand-rolled encoder, and offers a waveform preview + click-to-assign
pad picker before committing the take.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ture

getDisplayMedia() worked, but every recording attempt failed with
NotAllowedError: Permission denied even after granting Screen
Recording in System Settings. Root-caused live (via CDP against the
packaged app) by logging every permission string Electron actually
checked: Chromium gates the getDisplayMedia() call itself on
"display-capture", but separately checks "media" to grant use of the
resulting audio track — and only "display-capture" was allow-listed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two more bugs found and fixed after the permission fix, both
root-caused live via Chrome DevTools Protocol attached directly to
the packaged app:

1. useSystemPicker's native macOS picker never actually returns an
   audio track (confirmed: electron/electron#44685, still open).
   Dropped it entirely in favor of granting audio: "loopback"
   directly in handleDisplayMediaRequest — capture is now always
   system-wide (no more per-window picker UI).

2. The direct loopback grant then returned an audio track that
   looked successful but was silently dead (readyState: "ended",
   zero signal, no error) — recent Chromium defaults to Apple's new
   CoreAudio Tap API for macOS loopback, which needs a different,
   newly-introduced Info.plist key (NSAudioCaptureUsageDescription)
   almost nothing has yet, and fails with no fallback when it's
   missing (electron/electron#49607). Fixed by explicitly disabling
   that path via disable-features: MacCatapLoopbackAudioForScreenShare.

Confirmed with real captured audio (readyState: "live", strong RMS
signal) and end-to-end through the actual UI: record, stop, waveform
preview, pad assignment, playback from that pad.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
getDisplayMedia's `audio: true` shorthand defaults echoCancellation,
noiseSuppression, and autoGainControl all to true, and channelCount
to mono — even for system-audio loopback. Those are voice-call DSP
tuned for mic input; applied to music/video they genuinely mangle
the signal.

Confirmed numerically with a clean 440Hz test tone: with the
defaults on, the captured signal measured 431.5Hz with amplitude
drift and discontinuities; with echoCancellation/noiseSuppression/
autoGainControl explicitly disabled and channelCount:2 requested, it
measured an exact 440.0Hz with a flat RMS envelope and zero
discontinuities.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…iles

- Find the MPC SD card by scanning /Volumes for MPC-Sample/Projects (any
  volume name, e.g. SDCARD) instead of hardcoding MPC-SD; eject uses it too.
- Shrink waveform peaks from 600 to 72 points (thumbnail is 72px). The old
  cache hit ~526 MB on a 25k-sample library, exceeding V8's max string length.
  Stale-format cache entries are re-decoded.
- Batch library decode progress into one store update per 250 ms instead of
  rebuilding the entry list per file.
- Hide unreadable/corrupt audio files from the Sample Browser.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The renderer is served from http://localhost in dev, making fetch() of
mpc-sample:// cross-origin. Register the scheme with corsEnabled and send
Access-Control-Allow-Origin on audio responses.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… pass

The hardware forces add-effect -> resample -> add-next-effect per stacked
effect. Firmware research found no MIDI/USB control surface over the
effects engine to automate that remotely (see mpc-firmware-research:
the device's /api/v1 API is real but gated behind on-device PIN pairing).

Built an in-app workaround instead: a chain of filter/distortion/
bitcrusher/chorus/delay/reverb that renders through Tone.Offline in a
single pass and writes the result back onto the pad as a new sample.

- src/audio/effects.types.ts: plain, serializable EffectInstance chain.
- src/audio/effectsChain.ts: renderEffectsChain (offline) and
  previewEffectsChain (realtime audition) share one node-building path.
- src/components/EffectsRackPanel.tsx: new panel, opened via an
  "Effects..." button in KitEditor's per-pad editor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cover everything built on this branch: sample library browser (25k+
files), auto-tagging, step sequencer with native .xpj export, system-audio
sample recording, the offline effects rack, MIDI in/out, and the direct
unzipped SD card workflow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…-browser

Sample Library Browser, Step Sequencer, MIDI, Sample Recording, Effects Rack
@vercel

vercel Bot commented Oct 2, 2026

Copy link
Copy Markdown

@ResistanceIsUseless is attempting to deploy a commit to the World Link Studio Team on Vercel.

A member of the Team first needs to authorize it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants