@m4l-jweb/bridge 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.ts +48 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -188,6 +188,8 @@ export const CHAIN_OUT = {
|
|
|
188
188
|
buffer_play: "buffer_play",
|
|
189
189
|
/** UI -> samples: `buffer_stop` - stop the preview. */
|
|
190
190
|
buffer_stop: "buffer_stop",
|
|
191
|
+
/** UI -> instrument: `voice_play <pitch> <vel> <durMs> <channels>` - play one poly~ voice. */
|
|
192
|
+
voice_play: "voice_play",
|
|
191
193
|
} as const;
|
|
192
194
|
|
|
193
195
|
/**
|
|
@@ -406,6 +408,52 @@ export function stopSample(): void {
|
|
|
406
408
|
outlet(CHAIN_OUT.buffer_stop);
|
|
407
409
|
}
|
|
408
410
|
|
|
411
|
+
/* ------------------------------------------------------------------ *
|
|
412
|
+
* Instrument - the `instrument` chain ([poly~] voices)
|
|
413
|
+
* ------------------------------------------------------------------ */
|
|
414
|
+
|
|
415
|
+
/** One played note, handed to the `instrument` chain's [poly~]. The app times it; Max allocates a voice. */
|
|
416
|
+
export interface Voice {
|
|
417
|
+
/**
|
|
418
|
+
* Which slot's buffer to play, as a 0-based INDEX into the device's `slots` (the
|
|
419
|
+
* order declared in the manifest). The instrument is a keymap of named buffers, so a
|
|
420
|
+
* note names its sample; the app owns the slot order and passes the index.
|
|
421
|
+
*/
|
|
422
|
+
slot: number;
|
|
423
|
+
/**
|
|
424
|
+
* Playback RATE. 1 plays the buffer at its recorded pitch; 2 is an octave up, 0.5 an
|
|
425
|
+
* octave down. EXPLICIT, so the app decides whether a note plays a dedicated sample
|
|
426
|
+
* (rate 1) or a repitched one - the chain does no pitch arithmetic.
|
|
427
|
+
*/
|
|
428
|
+
rate: number;
|
|
429
|
+
/** 1-127. Scaled to amplitude in the voice. */
|
|
430
|
+
velocity: number;
|
|
431
|
+
/** How long to HOLD the voice, in ms - after which [poly~] frees it for re-use. */
|
|
432
|
+
durationMs: number;
|
|
433
|
+
/**
|
|
434
|
+
* That slot's channel count, as reported by `loadSample`'s `buffer_ready`. A mono
|
|
435
|
+
* buffer (1) is folded to both ears in the voice; pass what you measured, not what
|
|
436
|
+
* you hoped for. Defaults to 2 (no fold).
|
|
437
|
+
*/
|
|
438
|
+
channels?: number;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Play one note through the `instrument` chain's [poly~].
|
|
443
|
+
*
|
|
444
|
+
* Requires the note's slot already loaded (via `loadSample`, the same call the sampler
|
|
445
|
+
* uses). Polyphony and voice-stealing are Max's job: send as many overlapping notes as
|
|
446
|
+
* you like - across any slots - and [poly~] hands each to a free voice, or steals the
|
|
447
|
+
* oldest. A chord is several `playVoice()` calls in the same tick.
|
|
448
|
+
*
|
|
449
|
+
* The note is a control-plane message, not audio: it says which sample, at what rate,
|
|
450
|
+
* how loud and how long to hold the voice, and Max makes the sound sample-accurately.
|
|
451
|
+
* Pass the `channels` you learned from `loadSample` so a mono sample folds to both ears.
|
|
452
|
+
*/
|
|
453
|
+
export function playVoice(v: Voice): void {
|
|
454
|
+
outlet(CHAIN_OUT.voice_play, v.slot, v.rate, v.velocity, v.durationMs, v.channels ?? 2);
|
|
455
|
+
}
|
|
456
|
+
|
|
409
457
|
/**
|
|
410
458
|
* Max splits messages on commas and semicolons, so any structured payload -
|
|
411
459
|
* JSON, code, a filesystem path - must be encoded before it crosses the bridge.
|