Commit graph

15 commits

Author SHA1 Message Date
w1n5t0n
4240b03c1c feat(sequencer): mono/poly voice mode
Mono mode (default) kills the previous note before firing a new one —
classic monophonic synth behavior. Poly mode retriggers same-pitch
notes without killing others. All active notes are released on mode
switch.
2026-04-06 23:59:04 +01:00
w1n5t0n
4aedc92bf9 feat(shapeseq): unified/dual MLP mode switching
Unified mode: a single MLP drives both timbre and sequence — the timbre
MLP's outputs are partitioned and a configurable slice feeds the
sequencer pipeline via setSequenceOutputsFromTimbre().

Dual mode (default): separate MLPs for timbre and sequence with
independent training. The sequencer owns its own IML instance.
2026-04-06 23:57:41 +01:00
w1n5t0n
9b08918b1e feat(shapeseq): freeze-as-algorithm/pattern + delta controller
Two freeze modes for capturing and preserving musical state:

- Algorithm freeze: snapshot all primitive params + seeds. Individual
  params can be selectively unfrozen for ML-driven exploration via
  DeltaController (applies MLP deltas to frozen base values with
  per-param boundary enforcement).

- Pattern freeze: snapshot the realized note pattern. The sequencer
  bypasses the primitive chain entirely and loops the frozen pattern.

Extracts _runPipeline() from setSequenceInputs() for reuse.
2026-04-06 23:55:23 +01:00
w1n5t0n
122bdb9843 feat(sequencer): generation counter forces re-eval on config changes
The dirty-check that skips re-evaluation when inputs haven't changed now
also checks a generation counter. Config changes (tempo, step count,
projection, chain edits) bump the counter, ensuring the pipeline re-runs
even when joystick position is static.
2026-04-06 23:51:57 +01:00
w1n5t0n
5796d8fd35 feat(seq-iml): configurable output count with scaled architecture
The sequence MLP output count is now configurable (default 16). Hidden
layer widths scale with the output count to keep inference cheap while
providing sufficient capacity. The sequencer exposes setOutputCount()
to resize the MLP at runtime.
2026-04-06 23:45:55 +01:00
w1n5t0n
4cea170795 refactor(projection): simplify to 3 global knobs
Replace the composable transform chain (VelocityCurve, GateThreshold,
RangeMap, OctaveFolder, StutterMap, presets) with 3 straightforward
post-chain knobs: velocityCurve, gateThreshold, and pitchRange. The
old system was over-engineered for a research prototype — the new API
is easier to wire to UI controls and reason about.
2026-04-06 23:41:30 +01:00
w1n5t0n
af4f19bc72 refactor(PitchWalker): redesign as processor, add reEvalOnLoop
PitchWalker was classified as a generator but always consumed the
incoming pattern's triggers. Reclassify as a processor so it modifies
the pattern produced by an upstream generator (clonePattern instead of
createPattern). Add reEvalOnLoop flag on the Primitive base class; when
set, the sequencer re-evaluates the chain at each loop start so
stateful primitives produce evolving patterns across loops.
2026-04-06 23:37:32 +01:00
w1n5t0n
b4b4025f59 feat(shapeseq): polyrhythm — per-generator step counts with LCM tiling
Generators can now have independent step counts. When merging patterns
of different lengths, both are tiled to their LCM. Chain exposes
setGeneratorStepCount/getGeneratorStepCount for polyrhythmic control.
2026-04-06 23:31:45 +01:00
w1n5t0n
aec58537db feat(pattern): add midiNote field + trigger-aware merge
Replace the pitch*127 encoding hack with a dedicated midiNote field on
pattern steps. IntervalLock now writes integer MIDI notes to step.midiNote
while preserving the original [0,1] pitch for other consumers. The clock
and sequencer bridge pass midiNote through the event pipeline.

Also reworks mergePatterns to be trigger-aware: when only one generator
fires at a step, use its values directly instead of averaging with the
other generator's defaults.
2026-04-06 23:28:01 +01:00
w1n5t0n
3f4ffcd4f9 refactor(event-bus): extract to shared module
The EventBus was duplicated inside shapeseq/ but is used by other
playground code too. Move the canonical implementation to js/event-bus.js
and turn the shapeseq copy into a re-export shim.
2026-04-06 23:18:47 +01:00
w1n5t0n
d40711eb77 fix(shapeseq): fix 6 bugs found in fresh-eyes review
1. Pitch pipeline: IntervalLock stores midiNote/127, sequencer now
   converts back via pitch*127. Removed pitch RangeMap from projection
   presets (was double-mapping already-quantized values).

2. Clock step duration: changed from quarter-note grid (60/bpm) to
   16th-note grid (60/bpm/4). 8 steps at 120 BPM now = 1 second.

3. Method name: drawWeights → randomiseWeights (matching WasmIML API).

4. Dirty-check: setSequenceInputs now skips re-evaluation when inputs
   haven't changed (epsilon 1e-5). Avoids 60fps chain evaluation.

5. SwingGroove: swingGrid param now functional — selects 8th note
   (period 2), 16th note (period 4), or triplet (period 3) swing grid.

6. IntervalLock base octave: starts from C3 (MIDI 48) instead of C0,
   so default output is in a playable range.
2026-03-24 11:42:25 +02:00
w1n5t0n
027510a7e4 feat(shapeseq): implement Layer 3 — chain builder UI and sequencer core
chain-ui.js (1002 lines): Vertical stack chain builder (pedalboard-
style) with expandable primitive cards, category-colored borders,
drag-to-reorder via pointer events, add primitive palette modal,
NISPS live-param pulsing indicator, generator combine mode toggle.
All styles inline matching dark glass theme.

sequencer.js (304 lines): ShapeSeqEngine orchestrator wiring the
full pipeline: input routing → sequence MLP (16 outputs) → param
mapping → chain evaluation → projection → clock scheduling → C15
bridge. Default chain: Euclidean→ProbGate→PitchWalker→IntervalLock→
VelocityShaper. Tracks active notes to prevent orphaned noteOns.
2026-03-24 02:10:04 +02:00
w1n5t0n
efcf6d2c06 feat(shapeseq): implement Layer 2 — all 8 primitives and chain runner
primitives.js (505 lines): All 8 sequencing primitives extending
Primitive base class:
- EuclideanRhythm: Bjorklund-distributed trigger patterns
- ProbabilityGate: PRNG-based trigger filtering + accent assignment
- PitchWalker: stateful constrained random walk with gravity
- Ratchet: probabilistic step subdivision (1-4x)
- SwingGroove: alternating-step timing offsets (max triplet feel)
- DensityMorph: trigger placement with clustering control
- IntervalLock: 11-scale pitch quantizer (chromatic→diminished)
- VelocityShaper: 5 curve types with depth/phase control
Includes PRIMITIVE_REGISTRY for chain builder UI.

chain.js (319 lines): Sequential pipeline chain evaluator.
Buckets primitives by category (generators→processors→converters→
timing), forks deterministic PRNG per primitive, merges multiple
generators via additive (OR) or multiplicative (AND) mode
(configurable in real time). Flat param distribution by chain
position. Full state serialization for freeze support.
2026-03-24 02:06:17 +02:00
w1n5t0n
c8015da35a feat(shapeseq): implement Layer 1 modules
Four modules that build on the Layer 0 foundation:

- primitive.js: base class with category system (generator/processor/
  timing/converter), param schema validation, boundary enforcement
  (clamp/wrap/scaled), symbolic process() interface, state management
  for freeze, and applyBoundary() utility for delta control
- clock.js: AudioContext lookahead scheduling (25ms interval, 100ms
  window) replacing setTimeout arpeggiator. Reads pattern descriptions
  for per-step swing (timeOffset) and ratchet (subdivisions). Emits
  seq.noteOn/noteOff/step/loopStart via event bus.
- projection.js: composable transform chain with 5 transforms
  (VelocityCurve, GateThreshold, RangeMap, OctaveFolder, StutterMap)
  and 3 presets (expressive, percussive, fullRange). Pitch quantization
  deliberately excluded (handled by Interval Lock primitive).
- step-viz.js: Canvas2D circular step visualizer with even angular
  spacing for any step count. Pitch→radius, velocity→node size,
  accent→color. 60fps-friendly with pre-allocated coordinate buffers.
  Tap interaction for step toggling.
2026-03-24 02:02:01 +02:00
w1n5t0n
e3b94d644f feat(shapeseq): implement Layer 0 foundation modules
Five independent modules with no interdependencies:

- event-bus.js: namespaced pub/sub (seq.*/ml.*/ui.*) with wildcard
  subscriptions and automatic AudioContext/performance.now timestamps
- prng.js: seedable mulberry32 PRNG with pure-functional API, fork()
  for independent per-primitive streams, serializable state for freeze
- pattern.js: symbolic pattern description data structure with
  create/clone/merge/validate, additive and multiplicative merge modes
- param-map.js: maps fixed 16-output MLP to variable-count primitive
  params via linear interpolation, with optional per-param min/max scaling
- seq-iml.js: factory for second WasmIML instance (2 inputs, 16 outputs,
  [16,16,16] hidden layers) following existing imlJoy/imlHand pattern

All modules are ES modules with no build step. PRNG, pattern, and
param-map are port-ready (explicit state, typed arrays, no closures).
2026-03-24 01:57:59 +02:00