rlo-engine 1.0.3 → 1.1.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.
Files changed (166) hide show
  1. package/README.md +15 -4
  2. package/dist/Core/AudioMath.d.ts +29 -0
  3. package/dist/Core/AudioMath.d.ts.map +1 -0
  4. package/dist/Core/AudioMath.js +64 -0
  5. package/dist/Core/AudioMath.js.map +1 -0
  6. package/dist/Core/InstrumentMap.d.ts +36 -0
  7. package/dist/Core/InstrumentMap.d.ts.map +1 -0
  8. package/dist/Core/InstrumentMap.js +62 -0
  9. package/dist/Core/InstrumentMap.js.map +1 -0
  10. package/dist/Core/RLOCore.d.ts +49 -0
  11. package/dist/Core/RLOCore.d.ts.map +1 -0
  12. package/dist/Core/RLOCore.js +188 -0
  13. package/dist/Core/RLOCore.js.map +1 -0
  14. package/dist/Core/SequenceBuilder.d.ts +23 -0
  15. package/dist/Core/SequenceBuilder.d.ts.map +1 -0
  16. package/dist/Core/SequenceBuilder.js +31 -0
  17. package/dist/Core/SequenceBuilder.js.map +1 -0
  18. package/dist/Instruments/Analog/AnalogSynthBase.d.ts +17 -7
  19. package/dist/Instruments/Analog/AnalogSynthBase.d.ts.map +1 -1
  20. package/dist/Instruments/Analog/AnalogSynthBase.js +27 -16
  21. package/dist/Instruments/Analog/AnalogSynthBase.js.map +1 -1
  22. package/dist/Instruments/Analog/BassSynth.d.ts +12 -5
  23. package/dist/Instruments/Analog/BassSynth.d.ts.map +1 -1
  24. package/dist/Instruments/Analog/BassSynth.js +17 -10
  25. package/dist/Instruments/Analog/BassSynth.js.map +1 -1
  26. package/dist/Instruments/Analog/ChiptuneSynth.d.ts +11 -4
  27. package/dist/Instruments/Analog/ChiptuneSynth.d.ts.map +1 -1
  28. package/dist/Instruments/Analog/ChiptuneSynth.js +12 -5
  29. package/dist/Instruments/Analog/ChiptuneSynth.js.map +1 -1
  30. package/dist/Instruments/Analog/FMSynth.d.ts +11 -4
  31. package/dist/Instruments/Analog/FMSynth.d.ts.map +1 -1
  32. package/dist/Instruments/Analog/FMSynth.js +14 -7
  33. package/dist/Instruments/Analog/FMSynth.js.map +1 -1
  34. package/dist/Instruments/Analog/FormantSynth.d.ts +11 -4
  35. package/dist/Instruments/Analog/FormantSynth.d.ts.map +1 -1
  36. package/dist/Instruments/Analog/FormantSynth.js +12 -5
  37. package/dist/Instruments/Analog/FormantSynth.js.map +1 -1
  38. package/dist/Instruments/Analog/LeadSynth.d.ts +11 -4
  39. package/dist/Instruments/Analog/LeadSynth.d.ts.map +1 -1
  40. package/dist/Instruments/Analog/LeadSynth.js +14 -7
  41. package/dist/Instruments/Analog/LeadSynth.js.map +1 -1
  42. package/dist/Instruments/Analog/OrganSynth.d.ts +11 -4
  43. package/dist/Instruments/Analog/OrganSynth.d.ts.map +1 -1
  44. package/dist/Instruments/Analog/OrganSynth.js +17 -10
  45. package/dist/Instruments/Analog/OrganSynth.js.map +1 -1
  46. package/dist/Instruments/Analog/PadSynth.js +7 -7
  47. package/dist/Instruments/Analog/PadSynth.js.map +1 -1
  48. package/dist/Instruments/Analog/ReeseBassSynth.d.ts +11 -4
  49. package/dist/Instruments/Analog/ReeseBassSynth.d.ts.map +1 -1
  50. package/dist/Instruments/Analog/ReeseBassSynth.js +13 -6
  51. package/dist/Instruments/Analog/ReeseBassSynth.js.map +1 -1
  52. package/dist/Instruments/Analog/StringSynth.js +4 -4
  53. package/dist/Instruments/Analog/StringSynth.js.map +1 -1
  54. package/dist/Instruments/Analog/WoodwindSynth.d.ts +11 -4
  55. package/dist/Instruments/Analog/WoodwindSynth.d.ts.map +1 -1
  56. package/dist/Instruments/Analog/WoodwindSynth.js +19 -12
  57. package/dist/Instruments/Analog/WoodwindSynth.js.map +1 -1
  58. package/dist/Instruments/CoreSynthBase.d.ts +31 -24
  59. package/dist/Instruments/CoreSynthBase.d.ts.map +1 -1
  60. package/dist/Instruments/CoreSynthBase.js +53 -34
  61. package/dist/Instruments/CoreSynthBase.js.map +1 -1
  62. package/dist/Instruments/Decay/AdditiveSynth.d.ts +12 -5
  63. package/dist/Instruments/Decay/AdditiveSynth.d.ts.map +1 -1
  64. package/dist/Instruments/Decay/AdditiveSynth.js +9 -2
  65. package/dist/Instruments/Decay/AdditiveSynth.js.map +1 -1
  66. package/dist/Instruments/Decay/BrassSynth.d.ts +11 -4
  67. package/dist/Instruments/Decay/BrassSynth.d.ts.map +1 -1
  68. package/dist/Instruments/Decay/BrassSynth.js +22 -15
  69. package/dist/Instruments/Decay/BrassSynth.js.map +1 -1
  70. package/dist/Instruments/Decay/ChromaticPercussionSynth.d.ts +12 -5
  71. package/dist/Instruments/Decay/ChromaticPercussionSynth.d.ts.map +1 -1
  72. package/dist/Instruments/Decay/ChromaticPercussionSynth.js +16 -9
  73. package/dist/Instruments/Decay/ChromaticPercussionSynth.js.map +1 -1
  74. package/dist/Instruments/Decay/DecaySynthBase.d.ts +14 -8
  75. package/dist/Instruments/Decay/DecaySynthBase.d.ts.map +1 -1
  76. package/dist/Instruments/Decay/DecaySynthBase.js +22 -15
  77. package/dist/Instruments/Decay/DecaySynthBase.js.map +1 -1
  78. package/dist/Instruments/Decay/ElectricGuitarSynth.d.ts +12 -5
  79. package/dist/Instruments/Decay/ElectricGuitarSynth.d.ts.map +1 -1
  80. package/dist/Instruments/Decay/ElectricGuitarSynth.js +19 -12
  81. package/dist/Instruments/Decay/ElectricGuitarSynth.js.map +1 -1
  82. package/dist/Instruments/Decay/EthnicSynth.d.ts +12 -5
  83. package/dist/Instruments/Decay/EthnicSynth.d.ts.map +1 -1
  84. package/dist/Instruments/Decay/EthnicSynth.js +13 -6
  85. package/dist/Instruments/Decay/EthnicSynth.js.map +1 -1
  86. package/dist/Instruments/Decay/GuitarSynth.d.ts +12 -5
  87. package/dist/Instruments/Decay/GuitarSynth.d.ts.map +1 -1
  88. package/dist/Instruments/Decay/GuitarSynth.js +14 -7
  89. package/dist/Instruments/Decay/GuitarSynth.js.map +1 -1
  90. package/dist/Instruments/Decay/KarplusSynth.d.ts +7 -0
  91. package/dist/Instruments/Decay/KarplusSynth.d.ts.map +1 -1
  92. package/dist/Instruments/Decay/KarplusSynth.js +16 -8
  93. package/dist/Instruments/Decay/KarplusSynth.js.map +1 -1
  94. package/dist/Instruments/Decay/PianoSynth.d.ts +12 -5
  95. package/dist/Instruments/Decay/PianoSynth.d.ts.map +1 -1
  96. package/dist/Instruments/Decay/PianoSynth.js +19 -12
  97. package/dist/Instruments/Decay/PianoSynth.js.map +1 -1
  98. package/dist/Instruments/Decay/SlapBassSynth.d.ts +12 -5
  99. package/dist/Instruments/Decay/SlapBassSynth.d.ts.map +1 -1
  100. package/dist/Instruments/Decay/SlapBassSynth.js +13 -6
  101. package/dist/Instruments/Decay/SlapBassSynth.js.map +1 -1
  102. package/dist/Instruments/ISynthInstrument.d.ts +6 -0
  103. package/dist/Instruments/ISynthInstrument.d.ts.map +1 -1
  104. package/dist/Instruments/Speciality/DrumSynth.d.ts +7 -0
  105. package/dist/Instruments/Speciality/DrumSynth.d.ts.map +1 -1
  106. package/dist/Instruments/Speciality/DrumSynth.js +30 -21
  107. package/dist/Instruments/Speciality/DrumSynth.js.map +1 -1
  108. package/dist/Instruments/Speciality/SoundEffectsSynth.d.ts +7 -0
  109. package/dist/Instruments/Speciality/SoundEffectsSynth.d.ts.map +1 -1
  110. package/dist/Instruments/Speciality/SoundEffectsSynth.js +20 -12
  111. package/dist/Instruments/Speciality/SoundEffectsSynth.js.map +1 -1
  112. package/dist/Instruments/Synthesizer.d.ts +5 -0
  113. package/dist/Instruments/Synthesizer.d.ts.map +1 -1
  114. package/dist/Instruments/Synthesizer.js +7 -1
  115. package/dist/Instruments/Synthesizer.js.map +1 -1
  116. package/dist/Players/RLOGameEngine.d.ts +35 -0
  117. package/dist/Players/RLOGameEngine.d.ts.map +1 -0
  118. package/dist/Players/RLOGameEngine.js +98 -0
  119. package/dist/Players/RLOGameEngine.js.map +1 -0
  120. package/dist/Players/RLOMusicPlayer.d.ts +35 -0
  121. package/dist/Players/RLOMusicPlayer.d.ts.map +1 -0
  122. package/dist/Players/RLOMusicPlayer.js +167 -0
  123. package/dist/Players/RLOMusicPlayer.js.map +1 -0
  124. package/dist/RLO-Transpiler.d.ts +37 -15
  125. package/dist/RLO-Transpiler.d.ts.map +1 -1
  126. package/dist/RLO-Transpiler.js +139 -117
  127. package/dist/RLO-Transpiler.js.map +1 -1
  128. package/dist/compiler.d.ts +50 -31
  129. package/dist/compiler.d.ts.map +1 -1
  130. package/dist/compiler.js +165 -230
  131. package/dist/compiler.js.map +1 -1
  132. package/dist/crush.d.ts +1 -5
  133. package/dist/crush.d.ts.map +1 -1
  134. package/dist/crush.js +10 -3
  135. package/dist/crush.js.map +1 -1
  136. package/dist/index.d.ts +15 -2
  137. package/dist/index.d.ts.map +1 -1
  138. package/dist/index.js +14 -3
  139. package/dist/index.js.map +1 -1
  140. package/dist/midi-parser.d.ts +14 -0
  141. package/dist/midi-parser.d.ts.map +1 -1
  142. package/dist/midi-parser.js +21 -0
  143. package/dist/midi-parser.js.map +1 -1
  144. package/dist/rlo-engine.min.js +441 -439
  145. package/dist/rlo-engine.min.js.map +1 -1
  146. package/dist/rlo-engine.min.umd.js +1 -1
  147. package/dist/rlo-engine.min.umd.js.map +1 -1
  148. package/dist/types.d.ts +21 -3
  149. package/dist/types.d.ts.map +1 -1
  150. package/dist/types.js +4 -0
  151. package/dist/types.js.map +1 -1
  152. package/dist/vite.config.d.ts.map +1 -1
  153. package/dist/vite.config.js +16 -3
  154. package/dist/vite.config.js.map +1 -1
  155. package/dist/vite.config.js13k.d.ts.map +1 -1
  156. package/dist/vite.config.js13k.js +1 -0
  157. package/dist/vite.config.js13k.js.map +1 -1
  158. package/package.json +1 -1
  159. package/dist/Instruments/InstrumentFactory.d.ts +0 -2
  160. package/dist/Instruments/InstrumentFactory.d.ts.map +0 -1
  161. package/dist/Instruments/InstrumentFactory.js +0 -4
  162. package/dist/Instruments/InstrumentFactory.js.map +0 -1
  163. package/dist/RLO-Player.d.ts +0 -298
  164. package/dist/RLO-Player.d.ts.map +0 -1
  165. package/dist/RLO-Player.js +0 -724
  166. package/dist/RLO-Player.js.map +0 -1
package/README.md CHANGED
@@ -4,6 +4,17 @@ An optimized compiler and procedural Web Audio engine designed specifically for
4
4
 
5
5
  RLO converts sequence files (like MIDI) into 1D numerical arrays, bypassing the need for external parsing libraries or audio samples. Audio is synthesized dynamically at runtime using pure mathematics and the native Web Audio API.
6
6
 
7
+
8
+ Demo PWA game using this library : https://github.com/SiliconStreetDev1/NeonBlitz
9
+
10
+ 🎮 **[PLAY NEON BLITZ DIRECTLY IN YOUR BROWSER HERE!](https://siliconstreetdev1.github.io/NeonBlitz/)** 🎮
11
+
12
+
13
+ See JS13ksize working example:
14
+ https://github.com/SiliconStreetDev1/NeonBlitzjs13k
15
+
16
+ 📜 **[View Changelog (v1.1.0 Update)](./CHANGELOG.md)**
17
+
7
18
  ### 🚀 Two Ways to Use the Engine
8
19
 
9
20
  This library uses a **Hybrid Architecture**. You can either install it directly via NPM for standard development, or clone it as a build-pipeline boilerplate to achieve high compression for JS13K competitions.
@@ -126,7 +137,7 @@ If you are using `RLOMusicPlayer`, it handles fetching and decompression automat
126
137
  The `.rlo` files generated by the CLI compiler are **gzipped binary buffers**. Here is the standard boilerplate to fetch, unzip, and decode them using native browser APIs:
127
138
 
128
139
  ```javascript
129
- import { RLOTranspiler } from "rlo-engine";
140
+ import { decodeBinary } from "rlo-engine";
130
141
 
131
142
  async function loadAndPlayRLO(url, engine) {
132
143
  const res = await fetch(url);
@@ -137,7 +148,7 @@ async function loadAndPlayRLO(url, engine) {
137
148
  const buffer = await new Response(decompressedStream).arrayBuffer();
138
149
 
139
150
  // 2. Decode the binary buffer into the RloData object format
140
- const trackData = RLOTranspiler._decodeBinary(buffer);
151
+ const trackData = decodeBinary(buffer);
141
152
 
142
153
  // 3. Play it!
143
154
  engine.playMusic(trackData); // Use playSequence(trackData) if using RLOCore
@@ -148,7 +159,7 @@ async function loadAndPlayRLO(url, engine) {
148
159
 
149
160
  ## Paradigm B: The JS13k Boilerplate (High Compression)
150
161
 
151
- For size-coding and JS13k developers. By cloning the repo and using the custom build pipeline, you bypass ES module boundaries, allowing the Terser minifier to mangle internal properties and strip out any synthesizers you don't actively use.
162
+ For size-coding and JS13k developers. By cloning the repo and using the custom build pipeline, you bypass ES module boundaries, allowing the Terser minifier to mangle internal properties and strip out any synthesizers you don't actively use. **The current highly-optimized core engine sits at ~2.2KB Gzipped.**
152
163
 
153
164
  ### 1. Clone & Install
154
165
 
@@ -356,7 +367,7 @@ export class MyCustomSynth extends CoreSynthBase {
356
367
  const gain = this._gain(ctx, 0, masterGain);
357
368
 
358
369
  // 2. Create a sound wave (Oscillator)
359
- const osc = this._osc(ctx, Osc.Square, freq, gain);
370
+ const osc = this._osc(ctx, "square", freq, gain);
360
371
 
361
372
  // 3. Mathematical Volume Envelope (ADSR)
362
373
  applyEnvelope(gain.gain, time, duration, {
@@ -0,0 +1,29 @@
1
+ export interface ADSREnvelope {
2
+ attack?: number;
3
+ decay?: number;
4
+ sustain?: number;
5
+ release?: number;
6
+ peak?: number;
7
+ }
8
+ /**
9
+ * Mathematically applies an Attack-Decay-Sustain-Release (ADSR) volume envelope to an AudioParam.
10
+ *
11
+ * @reason Why we use explicit `setValueAtTime` and `linearRampToValueAtTime`:
12
+ * Web Audio nodes often suffer from clicking/popping if values jump instantaneously.
13
+ * By using linear ramps, we ensure smooth zero-crossing transitions. Furthermore,
14
+ * the logic elegantly handles edge-cases where the note duration is shorter than
15
+ * the Attack or Decay phase by dynamically calculating partial peaks and
16
+ * preemptively jumping to the Release phase, preventing sustained hanging notes.
17
+ */
18
+ export declare function applyEnvelope(gainParam: AudioParam, now: number, duration: number, opts: ADSREnvelope): void;
19
+ /**
20
+ * Converts a scientific pitch notation string (e.g., "C4", "F#3") to frequency in Hz.
21
+ *
22
+ * @reason Why this is included but conditionally compiled:
23
+ * This parser is highly convenient for developers writing sequences manually via the API,
24
+ * allowing `Note("C4")` instead of `261.63`. However, the transpiler strips string notes
25
+ * during binary compilation, so we wrap this in `__ENABLE_NOTE_PARSER__` to allow Rollup
26
+ * to entirely remove the Regex from the JS13K build if unused.
27
+ */
28
+ export declare function Note(pitch: string | number): number;
29
+ //# sourceMappingURL=AudioMath.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioMath.d.ts","sourceRoot":"","sources":["../../Core/AudioMath.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,UAAU,EACrB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,YAAY,GACjB,IAAI,CAwBN;AAKD;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAYnD"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Mathematically applies an Attack-Decay-Sustain-Release (ADSR) volume envelope to an AudioParam.
3
+ *
4
+ * @reason Why we use explicit `setValueAtTime` and `linearRampToValueAtTime`:
5
+ * Web Audio nodes often suffer from clicking/popping if values jump instantaneously.
6
+ * By using linear ramps, we ensure smooth zero-crossing transitions. Furthermore,
7
+ * the logic elegantly handles edge-cases where the note duration is shorter than
8
+ * the Attack or Decay phase by dynamically calculating partial peaks and
9
+ * preemptively jumping to the Release phase, preventing sustained hanging notes.
10
+ */
11
+ export function applyEnvelope(gainParam, now, duration, opts) {
12
+ const a = opts.attack ?? 0.05;
13
+ const d = opts.decay ?? 0.1;
14
+ const s = opts.sustain ?? 0.8;
15
+ const r = opts.release ?? 0.1;
16
+ const peak = opts.peak ?? 1.0;
17
+ gainParam.setValueAtTime(0, now);
18
+ const realDur = Math.max(0, duration);
19
+ if (realDur <= a) {
20
+ const partialPeak = peak * (realDur / (a || 1));
21
+ gainParam.linearRampToValueAtTime(partialPeak, now + realDur * 0.5);
22
+ gainParam.linearRampToValueAtTime(0, now + realDur);
23
+ }
24
+ else if (realDur <= a + d) {
25
+ gainParam.linearRampToValueAtTime(peak, now + a);
26
+ gainParam.linearRampToValueAtTime(0, now + realDur);
27
+ }
28
+ else {
29
+ gainParam.linearRampToValueAtTime(peak, now + a);
30
+ gainParam.linearRampToValueAtTime(peak * s, now + a + d);
31
+ const releaseStart = Math.max(now + a + d, now + realDur - r);
32
+ gainParam.setValueAtTime(peak * s, releaseStart);
33
+ gainParam.linearRampToValueAtTime(0, now + realDur);
34
+ }
35
+ }
36
+ const hasNoteParser = typeof __ENABLE_NOTE_PARSER__ !== "undefined" ? __ENABLE_NOTE_PARSER__ : true;
37
+ /**
38
+ * Converts a scientific pitch notation string (e.g., "C4", "F#3") to frequency in Hz.
39
+ *
40
+ * @reason Why this is included but conditionally compiled:
41
+ * This parser is highly convenient for developers writing sequences manually via the API,
42
+ * allowing `Note("C4")` instead of `261.63`. However, the transpiler strips string notes
43
+ * during binary compilation, so we wrap this in `__ENABLE_NOTE_PARSER__` to allow Rollup
44
+ * to entirely remove the Regex from the JS13K build if unused.
45
+ */
46
+ export function Note(pitch) {
47
+ if (typeof pitch === "number")
48
+ return pitch;
49
+ if (!hasNoteParser)
50
+ return 0;
51
+ const match = pitch.match(/^([a-gA-G])([#b]?)(\d)$/);
52
+ if (!match)
53
+ return 0;
54
+ const offsets = { C: -9, D: -7, E: -5, F: -4, G: -2, A: 0, B: 2 };
55
+ const [, note, accidental, octave] = match;
56
+ let semitone = offsets[note.toUpperCase()];
57
+ if (accidental === "#")
58
+ semitone++;
59
+ if (accidental === "b")
60
+ semitone--;
61
+ semitone += (parseInt(octave, 10) - 4) * 12;
62
+ return Number((440 * Math.pow(2, semitone / 12)).toFixed(2));
63
+ }
64
+ //# sourceMappingURL=AudioMath.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioMath.js","sourceRoot":"","sources":["../../Core/AudioMath.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { ISynthInstrument } from "../Instruments/ISynthInstrument.js";
2
+ /**
3
+ * Helper synth that produces zero sound, used as a fallback or placeholder.
4
+ */
5
+ export declare const SilentSynth: ISynthInstrument;
6
+ /**
7
+ * Creates a raw mapping between explicit MIDI Program IDs (0-128) and Synthesizer instances.
8
+ *
9
+ * @reason Used primarily by the `crush` JS13k build where only 2 or 3 synths are bundled,
10
+ * avoiding the need for complex fuzzy-matching loops.
11
+ */
12
+ export declare function createDirectMap(assignments: {
13
+ synth: ISynthInstrument;
14
+ ids: number[];
15
+ }[]): ISynthInstrument[];
16
+ /**
17
+ * Populates a 128-slot Array mapping MIDI Program IDs to Synthesizer instances.
18
+ *
19
+ * @reason The NPM library comes with 16 default synths. Standard MIDI files might
20
+ * request Program ID 44 (Contrabass), but we only have `StringSynth` mapped to 40-55.
21
+ * This function uses a fuzzy "closest neighbor" algorithm to automatically fill
22
+ * any missing gaps (e.g. mapping ID 44 to the nearest available synth ID). This guarantees
23
+ * that imported MIDI files will always produce sound, even if the user hasn't explicitly
24
+ * instantiated all 128 GM instruments.
25
+ */
26
+ export declare function createInstrumentMap(modules: {
27
+ synth: ISynthInstrument;
28
+ start: number;
29
+ end: number;
30
+ }[]): ISynthInstrument[];
31
+ export declare function extendInstrumentMap(baseMap: ISynthInstrument[], overrides: {
32
+ synth: ISynthInstrument;
33
+ start: number;
34
+ end: number;
35
+ }[]): ISynthInstrument[];
36
+ //# sourceMappingURL=InstrumentMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InstrumentMap.d.ts","sourceRoot":"","sources":["../../Core/InstrumentMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,gBAEzB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,GAAG,gBAAgB,EAAE,CAM7G;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,gBAAgB,EAAE,CAuB1H;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,SAAS,EAAE;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,gBAAgB,EAAE,CAMzJ"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Helper synth that produces zero sound, used as a fallback or placeholder.
3
+ */
4
+ export const SilentSynth = {
5
+ _playNote: () => { },
6
+ };
7
+ /**
8
+ * Creates a raw mapping between explicit MIDI Program IDs (0-128) and Synthesizer instances.
9
+ *
10
+ * @reason Used primarily by the `crush` JS13k build where only 2 or 3 synths are bundled,
11
+ * avoiding the need for complex fuzzy-matching loops.
12
+ */
13
+ export function createDirectMap(assignments) {
14
+ const mapArray = new Array(129).fill(null);
15
+ assignments.forEach((a) => {
16
+ a.ids.forEach((id) => (mapArray[id] = a.synth));
17
+ });
18
+ return mapArray;
19
+ }
20
+ /**
21
+ * Populates a 128-slot Array mapping MIDI Program IDs to Synthesizer instances.
22
+ *
23
+ * @reason The NPM library comes with 16 default synths. Standard MIDI files might
24
+ * request Program ID 44 (Contrabass), but we only have `StringSynth` mapped to 40-55.
25
+ * This function uses a fuzzy "closest neighbor" algorithm to automatically fill
26
+ * any missing gaps (e.g. mapping ID 44 to the nearest available synth ID). This guarantees
27
+ * that imported MIDI files will always produce sound, even if the user hasn't explicitly
28
+ * instantiated all 128 GM instruments.
29
+ */
30
+ export function createInstrumentMap(modules) {
31
+ const mapArray = new Array(129).fill(null);
32
+ modules.forEach((m) => {
33
+ for (let i = m.start; i <= m.end; i++)
34
+ mapArray[i] = m.synth;
35
+ });
36
+ for (let i = 0; i < 129; i++) {
37
+ if (mapArray[i] === null) {
38
+ let closest = null;
39
+ let minDiff = Infinity;
40
+ for (let j = 0; j < 129; j++) {
41
+ if (mapArray[j] !== null) {
42
+ const diff = Math.abs(i - j);
43
+ if (diff < minDiff) {
44
+ minDiff = diff;
45
+ closest = mapArray[j];
46
+ }
47
+ }
48
+ }
49
+ mapArray[i] = closest;
50
+ }
51
+ }
52
+ return mapArray;
53
+ }
54
+ export function extendInstrumentMap(baseMap, overrides) {
55
+ const newMap = [...baseMap];
56
+ overrides.forEach((m) => {
57
+ for (let i = m.start; i <= m.end; i++)
58
+ newMap[i] = m.synth;
59
+ });
60
+ return newMap;
61
+ }
62
+ //# sourceMappingURL=InstrumentMap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InstrumentMap.js","sourceRoot":"","sources":["../../Core/InstrumentMap.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAqB;IAC3C,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;CACpB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,WAAyD;IACvF,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QACxB,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAkE;IACpG,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QACpB,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE;YAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACzB,IAAI,OAAO,GAAG,IAAI,CAAC;YACnB,IAAI,OAAO,GAAG,QAAQ,CAAC;YACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC7B,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;oBACzB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBAC7B,IAAI,IAAI,GAAG,OAAO,EAAE,CAAC;wBACnB,OAAO,GAAG,IAAI,CAAC;wBACf,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;oBACxB,CAAC;gBACH,CAAC;YACH,CAAC;YACD,QAAQ,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAA2B,EAAE,SAAoE;IACnI,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;IAC5B,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QACtB,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE;YAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;IAC7D,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { RloData } from "../types.js";
2
+ import { ISynthInstrument } from "../Instruments/ISynthInstrument.js";
3
+ /**
4
+ * Options for playing a compiled RLO sequence.
5
+ */
6
+ export interface PlaySequenceOptions {
7
+ loop?: boolean;
8
+ fadeInTime?: number;
9
+ playbackRate?: number;
10
+ volume?: number;
11
+ }
12
+ /**
13
+ * Base Engine Core responsible for the precise scheduling and timing of Web Audio nodes.
14
+ *
15
+ * @reason Why we use a Worker for the metronome:
16
+ * In modern browsers, `setTimeout` or `setInterval` on the main thread is throttled
17
+ * to ~1000ms if the user switches browser tabs (to save battery).
18
+ * By offloading the timing loop to a Web Worker, we guarantee that background tabs
19
+ * continue to schedule audio nodes perfectly at 50ms intervals without skipping a beat.
20
+ */
21
+ export declare class RLOCore {
22
+ protected _ctx: AudioContext;
23
+ protected _isPlaying: boolean;
24
+ protected _timer: ReturnType<typeof setTimeout> | null;
25
+ protected _workerTimer: Worker | null;
26
+ protected _sequenceId: number;
27
+ protected _trkT: number;
28
+ protected _trkD: number;
29
+ protected _activeNodes: AudioNode[];
30
+ protected _volume: number;
31
+ protected _seekTarget: number | null;
32
+ playbackRate: number;
33
+ protected _instrumentMap: ISynthInstrument[];
34
+ protected _workerUrl: string | null;
35
+ constructor(audioContext: AudioContext, instrumentMap?: ISynthInstrument[]);
36
+ protected get _now(): number;
37
+ setVolume(vol: number): void;
38
+ seek(timeInSeconds: number): void;
39
+ stop(): void;
40
+ dispose(): void;
41
+ protected _createGain(): GainNode;
42
+ protected _applyLinearFade(gain: AudioParam, target: number, time: number): void;
43
+ protected _createCompressor(ctx?: AudioContext): DynamicsCompressorNode;
44
+ protected _createRouting(fadeInTime?: number): {
45
+ destination: AudioNode;
46
+ };
47
+ playSequence(track: RloData, loopOrOpts?: boolean | PlaySequenceOptions, oldFadeInTime?: number): void;
48
+ }
49
+ //# sourceMappingURL=RLOCore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RLOCore.d.ts","sourceRoot":"","sources":["../../Core/RLOCore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAGtE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAKD;;;;;;;;GAQG;AACH,qBAAa,OAAO;IAClB,SAAS,CAAC,IAAI,EAAE,YAAY,CAAC;IAC7B,SAAS,CAAC,UAAU,EAAE,OAAO,CAAS;IACtC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,IAAI,CAAQ;IAC9D,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC7C,SAAS,CAAC,WAAW,EAAE,MAAM,CAAK;IAClC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAK;IAC5B,SAAS,CAAC,KAAK,EAAE,MAAM,CAAK;IAC5B,SAAS,CAAC,YAAY,EAAE,SAAS,EAAE,CAAM;IACzC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAO;IAChC,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAQ;IAErC,YAAY,EAAE,MAAM,CAAO;IAElC,SAAS,CAAC,cAAc,EAAE,gBAAgB,EAAE,CAAC;IAC7C,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;gBAE/B,YAAY,EAAE,YAAY,EAAE,aAAa,GAAE,gBAAgB,EAAO;IAgB9E,SAAS,KAAK,IAAI,IAAI,MAAM,CAE3B;IAEM,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI5B,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI;IAMjC,IAAI,IAAI,IAAI;IAcZ,OAAO,IAAI,IAAI;IAYtB,SAAS,CAAC,WAAW,IAAI,QAAQ;IAIjC,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKhF,SAAS,CAAC,iBAAiB,CAAC,GAAG,GAAE,YAAwB,GAAG,sBAAsB;IAUlF,SAAS,CAAC,cAAc,CAAC,UAAU,GAAE,MAAU,GAAG;QAAE,WAAW,EAAE,SAAS,CAAA;KAAE;IAQrE,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,GAAE,OAAO,GAAG,mBAA0B,EAAE,aAAa,GAAE,MAAU,GAAG,IAAI;CAkGvH"}
@@ -0,0 +1,188 @@
1
+ import { Synthesizer } from "../Instruments/Synthesizer.js";
2
+ const hasWorkerMetronome = typeof __ENABLE_WORKER_METRONOME__ !== "undefined" ? __ENABLE_WORKER_METRONOME__ : false;
3
+ /**
4
+ * Base Engine Core responsible for the precise scheduling and timing of Web Audio nodes.
5
+ *
6
+ * @reason Why we use a Worker for the metronome:
7
+ * In modern browsers, `setTimeout` or `setInterval` on the main thread is throttled
8
+ * to ~1000ms if the user switches browser tabs (to save battery).
9
+ * By offloading the timing loop to a Web Worker, we guarantee that background tabs
10
+ * continue to schedule audio nodes perfectly at 50ms intervals without skipping a beat.
11
+ */
12
+ export class RLOCore {
13
+ constructor(audioContext, instrumentMap = []) {
14
+ this._isPlaying = false;
15
+ this._timer = null;
16
+ this._workerTimer = null;
17
+ this._sequenceId = 0;
18
+ this._trkT = 0;
19
+ this._trkD = 0;
20
+ this._activeNodes = [];
21
+ this._volume = 0.5;
22
+ this._seekTarget = null;
23
+ this.playbackRate = 1.0;
24
+ this._workerUrl = null;
25
+ this._ctx = audioContext;
26
+ this._instrumentMap = instrumentMap;
27
+ if (hasWorkerMetronome) {
28
+ const blob = new Blob([
29
+ "let t=null;self.onmessage=e=>{let d=e.data;if(d=='start'){if(t)clearInterval(t);t=setInterval(()=>self.postMessage('tick'),50)}else if(d=='stop'&&t){clearInterval(t);t=null}};"
30
+ ], { type: "application/javascript" });
31
+ this._workerUrl = URL.createObjectURL(blob);
32
+ this._workerTimer = new Worker(this._workerUrl);
33
+ }
34
+ }
35
+ get _now() {
36
+ return this._ctx.currentTime;
37
+ }
38
+ setVolume(vol) {
39
+ this._volume = vol;
40
+ }
41
+ seek(timeInSeconds) {
42
+ if (this._trkD > 0) {
43
+ this._seekTarget = Math.max(0, timeInSeconds) % this._trkD;
44
+ }
45
+ }
46
+ stop() {
47
+ this._isPlaying = false;
48
+ this._sequenceId++;
49
+ if (this._timer)
50
+ clearTimeout(this._timer);
51
+ if (hasWorkerMetronome && this._workerTimer)
52
+ this._workerTimer.postMessage("stop");
53
+ const nodesToClean = this._activeNodes;
54
+ this._activeNodes = [];
55
+ while (nodesToClean.length) {
56
+ try {
57
+ nodesToClean.pop().disconnect();
58
+ }
59
+ catch (e) { }
60
+ }
61
+ }
62
+ dispose() {
63
+ this.stop();
64
+ if (hasWorkerMetronome && this._workerTimer) {
65
+ this._workerTimer.terminate();
66
+ this._workerTimer = null;
67
+ }
68
+ if (hasWorkerMetronome && this._workerUrl) {
69
+ URL.revokeObjectURL(this._workerUrl);
70
+ this._workerUrl = null;
71
+ }
72
+ }
73
+ _createGain() {
74
+ return this._ctx.createGain();
75
+ }
76
+ _applyLinearFade(gain, target, time) {
77
+ gain.setValueAtTime(time > 0 ? 0 : target, this._now);
78
+ if (time > 0)
79
+ gain.linearRampToValueAtTime(target, this._now + time);
80
+ }
81
+ _createCompressor(ctx = this._ctx) {
82
+ const c = ctx.createDynamicsCompressor();
83
+ c.threshold.value = -24;
84
+ c.knee.value = 12;
85
+ c.ratio.value = 8;
86
+ c.attack.value = 0.001;
87
+ c.release.value = 0.25;
88
+ return c;
89
+ }
90
+ _createRouting(fadeInTime = 0) {
91
+ const gain = this._createGain();
92
+ this._applyLinearFade(gain.gain, this._volume, fadeInTime);
93
+ gain.connect(this._ctx.destination);
94
+ this._activeNodes.push(gain);
95
+ return { destination: gain };
96
+ }
97
+ playSequence(track, loopOrOpts = true, oldFadeInTime = 0) {
98
+ let loop = true;
99
+ let fadeInTime = oldFadeInTime;
100
+ if (typeof loopOrOpts === "object") {
101
+ loop = loopOrOpts.loop ?? true;
102
+ fadeInTime = loopOrOpts.fadeInTime ?? 0;
103
+ this.playbackRate = loopOrOpts.playbackRate ?? this.playbackRate;
104
+ if (loopOrOpts.volume !== undefined)
105
+ this.setVolume(loopOrOpts.volume);
106
+ }
107
+ else {
108
+ loop = loopOrOpts;
109
+ }
110
+ this.stop();
111
+ if (this._ctx.state === "suspended")
112
+ this._ctx.resume();
113
+ this._isPlaying = true;
114
+ const currentSequenceId = ++this._sequenceId;
115
+ this._trkD = track.durationSecs;
116
+ const { destination } = this._createRouting(fadeInTime);
117
+ const synthesizer = new Synthesizer(this._ctx, destination, this._instrumentMap);
118
+ const lookaheadTime = 0.5;
119
+ this._trkT = -0.05 * this.playbackRate;
120
+ let lastScheduleTime = this._now;
121
+ let loopOffsetSecs = 0;
122
+ let notePtr = 0;
123
+ const notes = track.notes;
124
+ const len = notes.length;
125
+ const schedule = () => {
126
+ if (!this._isPlaying || this._sequenceId !== currentSequenceId)
127
+ return;
128
+ if (len === 0)
129
+ return; // Prevent infinite loop on empty tracks
130
+ const currentPhysicalTime = this._now;
131
+ const deltaPhysical = currentPhysicalTime - lastScheduleTime;
132
+ lastScheduleTime = currentPhysicalTime;
133
+ if (this._seekTarget !== null) {
134
+ this._trkT = this._seekTarget;
135
+ loopOffsetSecs = 0;
136
+ notePtr = 0;
137
+ while (notePtr < len && notes[notePtr + 1] < this._seekTarget) {
138
+ notePtr += 5;
139
+ }
140
+ this._seekTarget = null;
141
+ // Duck the volume briefly to hide the 0.5s lookahead buffer overlap
142
+ const duckGain = destination;
143
+ if (duckGain.gain) {
144
+ duckGain.gain.cancelScheduledValues(currentPhysicalTime);
145
+ duckGain.gain.setValueAtTime(0.001, currentPhysicalTime);
146
+ duckGain.gain.exponentialRampToValueAtTime(this._volume, currentPhysicalTime + 0.5);
147
+ }
148
+ }
149
+ else {
150
+ this._trkT += deltaPhysical * this.playbackRate;
151
+ }
152
+ const loopDurationSecs = track.durationSecs;
153
+ while (notePtr < len) {
154
+ const noteTrackTime = loopOffsetSecs + notes[notePtr + 1];
155
+ if (noteTrackTime >= this._trkT + lookaheadTime * this.playbackRate)
156
+ break;
157
+ // The "Lag Chord" drop threshold:
158
+ // If a thread freeze occurred, drop notes that are more than 150ms in the past
159
+ if (this._trkT - noteTrackTime <= 0.15 * this.playbackRate) {
160
+ const notePhysicalTime = currentPhysicalTime + Math.max(0, (noteTrackTime - this._trkT) / this.playbackRate);
161
+ synthesizer._playNote(notes[notePtr + 4], notePhysicalTime, notes[notePtr], notes[notePtr + 2] / this.playbackRate, notes[notePtr + 3]);
162
+ }
163
+ notePtr += 5;
164
+ }
165
+ if (notePtr >= len) {
166
+ if (loop) {
167
+ notePtr = 0;
168
+ loopOffsetSecs += loopDurationSecs;
169
+ schedule();
170
+ }
171
+ else {
172
+ if (hasWorkerMetronome && this._workerTimer)
173
+ this._workerTimer.postMessage("stop");
174
+ }
175
+ return;
176
+ }
177
+ if (!hasWorkerMetronome || !this._workerTimer) {
178
+ this._timer = setTimeout(schedule, 50);
179
+ }
180
+ };
181
+ if (hasWorkerMetronome && this._workerTimer) {
182
+ this._workerTimer.onmessage = () => schedule();
183
+ this._workerTimer.postMessage("start");
184
+ }
185
+ schedule();
186
+ }
187
+ }
188
+ //# sourceMappingURL=RLOCore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RLOCore.js","sourceRoot":"","sources":["../../Core/RLOCore.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAa5D,MAAM,kBAAkB,GAAG,OAAO,2BAA2B,KAAK,WAAW,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,KAAK,CAAC;AAEpH;;;;;;;;GAQG;AACH,MAAM,OAAO,OAAO;IAiBlB,YAAY,YAA0B,EAAE,gBAAoC,EAAE;QAfpE,eAAU,GAAY,KAAK,CAAC;QAC5B,WAAM,GAAyC,IAAI,CAAC;QACpD,iBAAY,GAAkB,IAAI,CAAC;QACnC,gBAAW,GAAW,CAAC,CAAC;QACxB,UAAK,GAAW,CAAC,CAAC;QAClB,UAAK,GAAW,CAAC,CAAC;QAClB,iBAAY,GAAgB,EAAE,CAAC;QAC/B,YAAO,GAAW,GAAG,CAAC;QACtB,gBAAW,GAAkB,IAAI,CAAC;QAErC,iBAAY,GAAW,GAAG,CAAC;QAGxB,eAAU,GAAkB,IAAI,CAAC;QAGzC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;QAEpC,IAAI,kBAAkB,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,IAAI,IAAI,CACnB;gBACE,iLAAiL;aAClL,EACD,EAAE,IAAI,EAAE,wBAAwB,EAAE,CACnC,CAAC;YACF,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YAC5C,IAAI,CAAC,YAAY,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAED,IAAc,IAAI;QAChB,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;IAC/B,CAAC;IAEM,SAAS,CAAC,GAAW;QAC1B,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC;IACrB,CAAC;IAEM,IAAI,CAAC,aAAqB;QAC/B,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7D,CAAC;IACH,CAAC;IAEM,IAAI;QACT,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,IAAI,IAAI,CAAC,MAAM;YAAE,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,kBAAkB,IAAI,IAAI,CAAC,YAAY;YAAE,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAEnF,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;QACvC,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QAEvB,OAAO,YAAY,CAAC,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC;gBAAC,YAAY,CAAC,GAAG,EAAG,CAAC,UAAU,EAAE,CAAC;YAAC,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC,CAAA,CAAC;QACxD,CAAC;IACH,CAAC;IAEM,OAAO;QACZ,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,kBAAkB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YAC5C,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,IAAI,kBAAkB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC1C,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACrC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;IACH,CAAC;IAES,WAAW;QACnB,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;IAChC,CAAC;IAES,gBAAgB,CAAC,IAAgB,EAAE,MAAc,EAAE,IAAY;QACvE,IAAI,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,IAAI,GAAG,CAAC;YAAE,IAAI,CAAC,uBAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACvE,CAAC;IAES,iBAAiB,CAAC,MAAoB,IAAI,CAAC,IAAI;QACvD,MAAM,CAAC,GAAG,GAAG,CAAC,wBAAwB,EAAE,CAAC;QACzC,CAAC,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;QACxB,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAClB,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;QAClB,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;QACvB,CAAC,CAAC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;QACvB,OAAO,CAAC,CAAC;IACX,CAAC;IAES,cAAc,CAAC,aAAqB,CAAC;QAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAChC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAC3D,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACpC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAC/B,CAAC;IAEM,YAAY,CAAC,KAAc,EAAE,aAA4C,IAAI,EAAE,gBAAwB,CAAC;QAC7G,IAAI,IAAI,GAAG,IAAI,CAAC;QAChB,IAAI,UAAU,GAAG,aAAa,CAAC;QAC/B,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;YACnC,IAAI,GAAG,UAAU,CAAC,IAAI,IAAI,IAAI,CAAC;YAC/B,UAAU,GAAG,UAAU,CAAC,UAAU,IAAI,CAAC,CAAC;YACxC,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,CAAC;YACjE,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS;gBAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACzE,CAAC;aAAM,CAAC;YACN,IAAI,GAAG,UAAU,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,WAAW;YAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAExD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,MAAM,iBAAiB,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC;QAC7C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC;QAEhC,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QACxD,MAAM,WAAW,GAAG,IAAI,WAAW,CACjC,IAAI,CAAC,IAAI,EACT,WAAuB,EACvB,IAAI,CAAC,cAAc,CACpB,CAAC;QAEF,MAAM,aAAa,GAAG,GAAG,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC;QACvC,IAAI,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC;QACjC,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;QAEzB,MAAM,QAAQ,GAAG,GAAG,EAAE;YACpB,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,WAAW,KAAK,iBAAiB;gBAAE,OAAO;YACvE,IAAI,GAAG,KAAK,CAAC;gBAAE,OAAO,CAAC,wCAAwC;YAE/D,MAAM,mBAAmB,GAAG,IAAI,CAAC,IAAI,CAAC;YACtC,MAAM,aAAa,GAAG,mBAAmB,GAAG,gBAAgB,CAAC;YAC7D,gBAAgB,GAAG,mBAAmB,CAAC;YAEvC,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;gBAC9B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC;gBAC9B,cAAc,GAAG,CAAC,CAAC;gBACnB,OAAO,GAAG,CAAC,CAAC;gBACZ,OAAO,OAAO,GAAG,GAAG,IAAI,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;oBAC9D,OAAO,IAAI,CAAC,CAAC;gBACf,CAAC;gBACD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;gBAExB,oEAAoE;gBACpE,MAAM,QAAQ,GAAG,WAAuB,CAAC;gBACzC,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;oBACjB,QAAQ,CAAC,IAAI,CAAC,qBAAqB,CAAC,mBAAmB,CAAC,CAAC;oBACzD,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;oBACzD,QAAQ,CAAC,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,GAAG,CAAC,CAAC;gBACvF,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,KAAK,IAAI,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC;YAClD,CAAC;YAED,MAAM,gBAAgB,GAAG,KAAK,CAAC,YAAY,CAAC;YAE5C,OAAO,OAAO,GAAG,GAAG,EAAE,CAAC;gBACrB,MAAM,aAAa,GAAG,cAAc,GAAG,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;gBAE1D,IAAI,aAAa,IAAI,IAAI,CAAC,KAAK,GAAG,aAAa,GAAG,IAAI,CAAC,YAAY;oBAAE,MAAM;gBAE3E,kCAAkC;gBAClC,+EAA+E;gBAC/E,IAAI,IAAI,CAAC,KAAK,GAAG,aAAa,IAAI,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;oBAC3D,MAAM,gBAAgB,GAAG,mBAAmB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC;oBAC7G,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,gBAAgB,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC1I,CAAC;gBACD,OAAO,IAAI,CAAC,CAAC;YACf,CAAC;YAED,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;gBACnB,IAAI,IAAI,EAAE,CAAC;oBACT,OAAO,GAAG,CAAC,CAAC;oBACZ,cAAc,IAAI,gBAAgB,CAAC;oBACnC,QAAQ,EAAE,CAAC;gBACb,CAAC;qBAAM,CAAC;oBACN,IAAI,kBAAkB,IAAI,IAAI,CAAC,YAAY;wBAAE,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;gBACrF,CAAC;gBACD,OAAO;YACT,CAAC;YACD,IAAI,CAAC,kBAAkB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;gBAC9C,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YACzC,CAAC;QACH,CAAC,CAAC;QACF,IAAI,kBAAkB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YAC5C,IAAI,CAAC,YAAY,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;YAC/C,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACzC,CAAC;QACD,QAAQ,EAAE,CAAC;IACb,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ import { RloData } from "../types.js";
2
+ /**
3
+ * Fluent API for constructing RLO sequences programmatically without using a compiler.
4
+ *
5
+ * @reason This class directly pushes `[frequency, time, duration, velocity, instrument]`
6
+ * tuples into a flat `number[]` array. By avoiding intermediary JS Objects `{ note: 'C4', dur: 1 }`
7
+ * we completely eliminate Garbage Collection (GC) pauses during sequence generation,
8
+ * making it incredibly fast for procedural music generation at runtime.
9
+ */
10
+ export declare class RLOSequenceBuilder {
11
+ private _notes;
12
+ private _duration;
13
+ addNote(opts: {
14
+ instrument: number;
15
+ pitch: string | number;
16
+ duration: number;
17
+ time?: number;
18
+ velocity?: number;
19
+ }): this;
20
+ setDuration(secs: number): this;
21
+ compile(): RloData;
22
+ }
23
+ //# sourceMappingURL=SequenceBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SequenceBuilder.d.ts","sourceRoot":"","sources":["../../Core/SequenceBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAGtC;;;;;;;GAOG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,SAAS,CAAa;IAEvB,OAAO,CAAC,IAAI,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;KAAE,GAAG,IAAI;IAQxH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI/B,OAAO,IAAI,OAAO;CAG1B"}
@@ -0,0 +1,31 @@
1
+ import { Note } from "./AudioMath.js";
2
+ /**
3
+ * Fluent API for constructing RLO sequences programmatically without using a compiler.
4
+ *
5
+ * @reason This class directly pushes `[frequency, time, duration, velocity, instrument]`
6
+ * tuples into a flat `number[]` array. By avoiding intermediary JS Objects `{ note: 'C4', dur: 1 }`
7
+ * we completely eliminate Garbage Collection (GC) pauses during sequence generation,
8
+ * making it incredibly fast for procedural music generation at runtime.
9
+ */
10
+ export class RLOSequenceBuilder {
11
+ constructor() {
12
+ this._notes = [];
13
+ this._duration = 0;
14
+ }
15
+ addNote(opts) {
16
+ const time = opts.time !== undefined ? opts.time : this._duration;
17
+ const freq = Note(opts.pitch);
18
+ const vel = opts.velocity ?? 1.0;
19
+ this._notes.push(freq, time, opts.duration, vel, opts.instrument);
20
+ this._duration = Math.max(this._duration, time + opts.duration);
21
+ return this;
22
+ }
23
+ setDuration(secs) {
24
+ this._duration = secs;
25
+ return this;
26
+ }
27
+ compile() {
28
+ return { durationSecs: this._duration, notes: this._notes };
29
+ }
30
+ }
31
+ //# sourceMappingURL=SequenceBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SequenceBuilder.js","sourceRoot":"","sources":["../../Core/SequenceBuilder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEtC;;;;;;;GAOG;AACH,MAAM,OAAO,kBAAkB;IAA/B;QACU,WAAM,GAAa,EAAE,CAAC;QACtB,cAAS,GAAW,CAAC,CAAC;IAiBhC,CAAC;IAfQ,OAAO,CAAC,IAAyG;QACtH,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;QAClE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,IAAI,GAAG,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAClE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IACM,WAAW,CAAC,IAAY;QAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IACM,OAAO;QACZ,OAAO,EAAE,YAAY,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAC9D,CAAC;CACF"}
@@ -1,17 +1,27 @@
1
1
  import { CoreSynthBase } from "../CoreSynthBase.js";
2
2
  export type AnalogCfg = {
3
- v: number;
4
- a: number;
5
- r: number;
3
+ _peakVelocity: number;
4
+ _attackTimeSeconds: number;
5
+ _releaseTimeSeconds: number;
6
+ _maxDurationSeconds?: number;
6
7
  };
7
8
  /**
8
- * Abstract base class for sustained analog synthesizers.
9
- * Handles GainNode creation, Nyquist safety limits, and ADSR volume envelopes.
9
+ * Synthesizer instrument implementation.
10
+ *
11
+ * @reason Acoustic Design:
12
+ * Encapsulates the specific Web Audio node routing and ADSR parameters
13
+ * required to physically model this instrument within the 13KB limit.
14
+ *
15
+ * @reason Separation of Sustained vs Decaying Physics:
16
+ * Synthesizers like an Organ will sustain at peak volume infinitely as long as the key is held,
17
+ * whereas a Piano will physically decay to silence even if the key remains held down.
18
+ * This base class encapsulates the mathematical ADSR envelope logic specifically for
19
+ * infinite-sustain physics, ensuring derived classes only need to define the timbre (Oscillators/Filters).
10
20
  */
11
21
  export declare abstract class AnalogSynthBase extends CoreSynthBase {
12
- protected _c: AnalogCfg;
22
+ protected _envelopeConfig: AnalogCfg;
13
23
  _playNote(ctx: AudioContext, masterGain: GainNode, time: number, freq: number, duration: number, velocity: number): void;
14
- protected _cfg(d: number): AnalogCfg;
24
+ protected _getEnvelopeConfig(_decayTimeSeconds: number): AnalogCfg;
15
25
  /**
16
26
  * Set up the specific oscillators and filters for this instrument.
17
27
  * You must start() and stop() your own oscillators.
@@ -1 +1 @@
1
- {"version":3,"file":"AnalogSynthBase.d.ts","sourceRoot":"","sources":["../../../Instruments/Analog/AnalogSynthBase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,MAAM,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D;;;GAGG;AACH,8BAAsB,eAAgB,SAAQ,aAAa;IACzD,SAAS,CAAC,EAAE,EAAE,SAAS,CAA+B;IAE/C,SAAS,CACd,GAAG,EAAE,YAAY,EACjB,UAAU,EAAE,QAAQ,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,IAAI;IA+BP,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS;IAIpC;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,eAAe,CAChC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,SAAS,GAAG,IAAI;CACpB"}
1
+ {"version":3,"file":"AnalogSynthBase.d.ts","sourceRoot":"","sources":["../../../Instruments/Analog/AnalogSynthBase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAe,MAAM,qBAAqB,CAAC;AAEjE,MAAM,MAAM,SAAS,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzI;;;;;;;;;;;;GAYG;AACH,8BAAsB,eAAgB,SAAQ,aAAa;IACzD,SAAS,CAAC,eAAe,EAAE,SAAS,CAAyG;IAEtI,SAAS,CACd,GAAG,EAAE,YAAY,EACjB,UAAU,EAAE,QAAQ,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,IAAI;IAgCP,SAAS,CAAC,kBAAkB,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS;IAIlE;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,eAAe,CAChC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,SAAS,GAAG,IAAI;CACpB"}