entangled-worlds-orbiters-shared 2.14.0 → 2.15.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.
@@ -33,7 +33,7 @@ export declare function readRecipes(value: unknown): Recipes | null;
33
33
  * How this package composes a program from a shape. Raise it when the same shape would compose to
34
34
  * different text: a test keeps a fingerprint of the composed text beside it and fails until then.
35
35
  */
36
- export declare const COMPOSER_VERSION = 1;
36
+ export declare const COMPOSER_VERSION = 2;
37
37
  /**
38
38
  * Where a build's compiled programs are filed: the recipes, the shrink step and the composer
39
39
  * together. A file under it is never replaced, so a release keeps the programs it was given.
@@ -41,7 +41,7 @@ export function readRecipes(value) {
41
41
  * How this package composes a program from a shape. Raise it when the same shape would compose to
42
42
  * different text: a test keeps a fingerprint of the composed text beside it and fails until then.
43
43
  */
44
- export const COMPOSER_VERSION = 1;
44
+ export const COMPOSER_VERSION = 2;
45
45
  /**
46
46
  * Where a build's compiled programs are filed: the recipes, the shrink step and the composer
47
47
  * together. A file under it is never replaced, so a release keeps the programs it was given.
@@ -30,9 +30,10 @@
30
30
  *
31
31
  * A head rack that plays notes composes as lanes (`lib/ps_notes.lib`):
32
32
  *
33
- * process = nt.entry(8) // the lanes the allocator writes
33
+ * process = nt.chainEntry(8) // the lanes the allocator writes
34
34
  * : hgroup("s0", nt.bypass(8, 1, library("pitch.dsp")[SLOT=0;].notes(8)))
35
35
  * : hgroup("tap0", nt.tap(8)) // the notes leaving s0
36
+ * : nt.toPlay(8) // the beats stay behind
36
37
  * : hgroup("s1", library("synth.dsp")[SLOT=1;].play(8)) // a note voice per lane
37
38
  * : hgroup("s2", component("filter.dsp"));
38
39
  *
@@ -41,6 +42,11 @@
41
42
  * instrument plays every lane that reaches it. `SLOT` is the module's place, its part of the seed;
42
43
  * a module that adds lanes (a structural `lanesOut`) also gets `LANES_OUT`, its lanes out per lane in,
43
44
  * since `notes(n)` is told only n.
45
+ *
46
+ * Between the note modules a lane carries a fourth signal, the beat its note started on (what Random
47
+ * and Velocity draw from): `nt.chainEntry` writes it, `nt.toPlay` drops it, so an instrument takes
48
+ * three signals a lane whether or not anything sits in front of it. An instrument alone keeps the
49
+ * plain `nt.entry`.
44
50
  */
45
51
  /** The most note lanes one head plays: what reaches the instrument after every note module has multiplied them. */
46
52
  export declare const MAX_NOTE_LANES = 16;
@@ -30,9 +30,10 @@
30
30
  *
31
31
  * A head rack that plays notes composes as lanes (`lib/ps_notes.lib`):
32
32
  *
33
- * process = nt.entry(8) // the lanes the allocator writes
33
+ * process = nt.chainEntry(8) // the lanes the allocator writes
34
34
  * : hgroup("s0", nt.bypass(8, 1, library("pitch.dsp")[SLOT=0;].notes(8)))
35
35
  * : hgroup("tap0", nt.tap(8)) // the notes leaving s0
36
+ * : nt.toPlay(8) // the beats stay behind
36
37
  * : hgroup("s1", library("synth.dsp")[SLOT=1;].play(8)) // a note voice per lane
37
38
  * : hgroup("s2", component("filter.dsp"));
38
39
  *
@@ -41,6 +42,11 @@
41
42
  * instrument plays every lane that reaches it. `SLOT` is the module's place, its part of the seed;
42
43
  * a module that adds lanes (a structural `lanesOut`) also gets `LANES_OUT`, its lanes out per lane in,
43
44
  * since `notes(n)` is told only n.
45
+ *
46
+ * Between the note modules a lane carries a fourth signal, the beat its note started on (what Random
47
+ * and Velocity draw from): `nt.chainEntry` writes it, `nt.toPlay` drops it, so an instrument takes
48
+ * three signals a lane whether or not anything sits in front of it. An instrument alone keeps the
49
+ * plain `nt.entry`.
44
50
  */
45
51
  /** The most note lanes one head plays: what reaches the instrument after every note module has multiplied them. */
46
52
  export const MAX_NOTE_LANES = 16;
@@ -100,7 +106,10 @@ function noteModules(slots, head) {
100
106
  let lanes = wholeAtLeastOne(instrument.polyphony, `slot ${head}: polyphony`);
101
107
  // A tap after every note module: the notes leaving it, for the meters and screens. The entry has
102
108
  // none: the main thread knows what it sends in (the keys, MIDI in, the clip's notes reported back).
103
- const parts = [`nt.entry(${lanes})`];
109
+ // Note modules in front: lanes carry their beats up to the instrument. An instrument alone takes the
110
+ // plain entry, its committed program unchanged.
111
+ const chain = head > 0;
112
+ const parts = [chain ? `nt.chainEntry(${lanes})` : `nt.entry(${lanes})`];
104
113
  slots.slice(0, head).forEach((slot, index) => {
105
114
  const notes = slot.notes;
106
115
  if (!notes || notes.kind === 'instrument') {
@@ -117,6 +126,8 @@ function noteModules(slots, head) {
117
126
  if (lanes > MAX_NOTE_LANES) {
118
127
  throw new Error(`[voiceProgram] ${lanes} note lanes reach the instrument; it plays at most ${MAX_NOTE_LANES}`);
119
128
  }
129
+ if (chain)
130
+ parts.push(`nt.toPlay(${lanes})`);
120
131
  return { parts, lanes };
121
132
  }
122
133
  /** The instrument at `head`, playing `lanes` lanes: the last part of a note chain. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "entangled-worlds-orbiters-shared",
3
- "version": "2.14.0",
3
+ "version": "2.15.0",
4
4
  "description": "Shared library for entangled worlds orbiters - contains core world rendering, moons system, and UI components",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -59,7 +59,7 @@ export function readRecipes(value: unknown): Recipes | null {
59
59
  * How this package composes a program from a shape. Raise it when the same shape would compose to
60
60
  * different text: a test keeps a fingerprint of the composed text beside it and fails until then.
61
61
  */
62
- export const COMPOSER_VERSION = 1;
62
+ export const COMPOSER_VERSION = 2;
63
63
 
64
64
  /**
65
65
  * Where a build's compiled programs are filed: the recipes, the shrink step and the composer
@@ -30,9 +30,10 @@
30
30
  *
31
31
  * A head rack that plays notes composes as lanes (`lib/ps_notes.lib`):
32
32
  *
33
- * process = nt.entry(8) // the lanes the allocator writes
33
+ * process = nt.chainEntry(8) // the lanes the allocator writes
34
34
  * : hgroup("s0", nt.bypass(8, 1, library("pitch.dsp")[SLOT=0;].notes(8)))
35
35
  * : hgroup("tap0", nt.tap(8)) // the notes leaving s0
36
+ * : nt.toPlay(8) // the beats stay behind
36
37
  * : hgroup("s1", library("synth.dsp")[SLOT=1;].play(8)) // a note voice per lane
37
38
  * : hgroup("s2", component("filter.dsp"));
38
39
  *
@@ -41,6 +42,11 @@
41
42
  * instrument plays every lane that reaches it. `SLOT` is the module's place, its part of the seed;
42
43
  * a module that adds lanes (a structural `lanesOut`) also gets `LANES_OUT`, its lanes out per lane in,
43
44
  * since `notes(n)` is told only n.
45
+ *
46
+ * Between the note modules a lane carries a fourth signal, the beat its note started on (what Random
47
+ * and Velocity draw from): `nt.chainEntry` writes it, `nt.toPlay` drops it, so an instrument takes
48
+ * three signals a lane whether or not anything sits in front of it. An instrument alone keeps the
49
+ * plain `nt.entry`.
44
50
  */
45
51
  /** The most note lanes one head plays: what reaches the instrument after every note module has multiplied them. */
46
52
  export const MAX_NOTE_LANES = 16;
@@ -126,7 +132,10 @@ function noteModules(slots: readonly VoiceSlot[], head: number): { parts: string
126
132
  let lanes = wholeAtLeastOne(instrument.polyphony, `slot ${head}: polyphony`);
127
133
  // A tap after every note module: the notes leaving it, for the meters and screens. The entry has
128
134
  // none: the main thread knows what it sends in (the keys, MIDI in, the clip's notes reported back).
129
- const parts = [`nt.entry(${lanes})`];
135
+ // Note modules in front: lanes carry their beats up to the instrument. An instrument alone takes the
136
+ // plain entry, its committed program unchanged.
137
+ const chain = head > 0;
138
+ const parts = [chain ? `nt.chainEntry(${lanes})` : `nt.entry(${lanes})`];
130
139
  slots.slice(0, head).forEach((slot, index) => {
131
140
  const notes = slot.notes;
132
141
  if (!notes || notes.kind === 'instrument') {
@@ -143,6 +152,7 @@ function noteModules(slots: readonly VoiceSlot[], head: number): { parts: string
143
152
  if (lanes > MAX_NOTE_LANES) {
144
153
  throw new Error(`[voiceProgram] ${lanes} note lanes reach the instrument; it plays at most ${MAX_NOTE_LANES}`);
145
154
  }
155
+ if (chain) parts.push(`nt.toPlay(${lanes})`);
146
156
  return { parts, lanes };
147
157
  }
148
158