polyphon 0.0.0-stage → 0.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 (174) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +374 -2
  3. package/README.zh-TW.md +375 -0
  4. package/dist/engine/automation.d.ts +24 -0
  5. package/dist/engine/automation.d.ts.map +1 -0
  6. package/dist/engine/automation.js +55 -0
  7. package/dist/engine/automation.js.map +1 -0
  8. package/dist/engine/compile.d.ts +36 -0
  9. package/dist/engine/compile.d.ts.map +1 -0
  10. package/dist/engine/compile.js +103 -0
  11. package/dist/engine/compile.js.map +1 -0
  12. package/dist/engine/jukebox.d.ts +113 -0
  13. package/dist/engine/jukebox.d.ts.map +1 -0
  14. package/dist/engine/jukebox.js +287 -0
  15. package/dist/engine/jukebox.js.map +1 -0
  16. package/dist/engine/mixer.d.ts +29 -0
  17. package/dist/engine/mixer.d.ts.map +1 -0
  18. package/dist/engine/mixer.js +141 -0
  19. package/dist/engine/mixer.js.map +1 -0
  20. package/dist/engine/output.d.ts +14 -0
  21. package/dist/engine/output.d.ts.map +1 -0
  22. package/dist/engine/output.js +51 -0
  23. package/dist/engine/output.js.map +1 -0
  24. package/dist/engine/playback.d.ts +97 -0
  25. package/dist/engine/playback.d.ts.map +1 -0
  26. package/dist/engine/playback.js +311 -0
  27. package/dist/engine/playback.js.map +1 -0
  28. package/dist/engine/prepare.d.ts +6 -0
  29. package/dist/engine/prepare.d.ts.map +1 -0
  30. package/dist/engine/prepare.js +12 -0
  31. package/dist/engine/prepare.js.map +1 -0
  32. package/dist/engine/render.d.ts +14 -0
  33. package/dist/engine/render.d.ts.map +1 -0
  34. package/dist/engine/render.js +60 -0
  35. package/dist/engine/render.js.map +1 -0
  36. package/dist/index.d.ts +17 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +25 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/instruments/bowed.d.ts +357 -0
  41. package/dist/instruments/bowed.d.ts.map +1 -0
  42. package/dist/instruments/bowed.js +502 -0
  43. package/dist/instruments/bowed.js.map +1 -0
  44. package/dist/instruments/builtin.d.ts +261 -0
  45. package/dist/instruments/builtin.d.ts.map +1 -0
  46. package/dist/instruments/builtin.js +160 -0
  47. package/dist/instruments/builtin.js.map +1 -0
  48. package/dist/instruments/definition.d.ts +46 -0
  49. package/dist/instruments/definition.d.ts.map +1 -0
  50. package/dist/instruments/definition.js +5 -0
  51. package/dist/instruments/definition.js.map +1 -0
  52. package/dist/instruments/dsp/ambience.d.ts +17 -0
  53. package/dist/instruments/dsp/ambience.d.ts.map +1 -0
  54. package/dist/instruments/dsp/ambience.js +146 -0
  55. package/dist/instruments/dsp/ambience.js.map +1 -0
  56. package/dist/instruments/dsp/core.d.ts +22 -0
  57. package/dist/instruments/dsp/core.d.ts.map +1 -0
  58. package/dist/instruments/dsp/core.js +100 -0
  59. package/dist/instruments/dsp/core.js.map +1 -0
  60. package/dist/instruments/dsp/drums.d.ts +112 -0
  61. package/dist/instruments/dsp/drums.d.ts.map +1 -0
  62. package/dist/instruments/dsp/drums.js +566 -0
  63. package/dist/instruments/dsp/drums.js.map +1 -0
  64. package/dist/instruments/dsp/modal.d.ts +30 -0
  65. package/dist/instruments/dsp/modal.d.ts.map +1 -0
  66. package/dist/instruments/dsp/modal.js +54 -0
  67. package/dist/instruments/dsp/modal.js.map +1 -0
  68. package/dist/instruments/dsp/piano.d.ts +9 -0
  69. package/dist/instruments/dsp/piano.d.ts.map +1 -0
  70. package/dist/instruments/dsp/piano.js +230 -0
  71. package/dist/instruments/dsp/piano.js.map +1 -0
  72. package/dist/instruments/dsp/pluck.d.ts +53 -0
  73. package/dist/instruments/dsp/pluck.d.ts.map +1 -0
  74. package/dist/instruments/dsp/pluck.js +195 -0
  75. package/dist/instruments/dsp/pluck.js.map +1 -0
  76. package/dist/instruments/dsp/reverb.d.ts +16 -0
  77. package/dist/instruments/dsp/reverb.d.ts.map +1 -0
  78. package/dist/instruments/dsp/reverb.js +47 -0
  79. package/dist/instruments/dsp/reverb.js.map +1 -0
  80. package/dist/instruments/dsp/staccato.d.ts +18 -0
  81. package/dist/instruments/dsp/staccato.d.ts.map +1 -0
  82. package/dist/instruments/dsp/staccato.js +147 -0
  83. package/dist/instruments/dsp/staccato.js.map +1 -0
  84. package/dist/instruments/play.d.ts +21 -0
  85. package/dist/instruments/play.d.ts.map +1 -0
  86. package/dist/instruments/play.js +140 -0
  87. package/dist/instruments/play.js.map +1 -0
  88. package/dist/instruments/registry.d.ts +63 -0
  89. package/dist/instruments/registry.d.ts.map +1 -0
  90. package/dist/instruments/registry.js +50 -0
  91. package/dist/instruments/registry.js.map +1 -0
  92. package/dist/instruments/samples.d.ts +30 -0
  93. package/dist/instruments/samples.d.ts.map +1 -0
  94. package/dist/instruments/samples.js +109 -0
  95. package/dist/instruments/samples.js.map +1 -0
  96. package/dist/instruments/voices.d.ts +76 -0
  97. package/dist/instruments/voices.d.ts.map +1 -0
  98. package/dist/instruments/voices.js +1061 -0
  99. package/dist/instruments/voices.js.map +1 -0
  100. package/dist/midi.d.ts +15 -0
  101. package/dist/midi.d.ts.map +1 -0
  102. package/dist/midi.js +280 -0
  103. package/dist/midi.js.map +1 -0
  104. package/dist/notation/format.d.ts +12 -0
  105. package/dist/notation/format.d.ts.map +1 -0
  106. package/dist/notation/format.js +102 -0
  107. package/dist/notation/format.js.map +1 -0
  108. package/dist/notation/harmony.d.ts +83 -0
  109. package/dist/notation/harmony.d.ts.map +1 -0
  110. package/dist/notation/harmony.js +191 -0
  111. package/dist/notation/harmony.js.map +1 -0
  112. package/dist/notation/melody.d.ts +25 -0
  113. package/dist/notation/melody.d.ts.map +1 -0
  114. package/dist/notation/melody.js +138 -0
  115. package/dist/notation/melody.js.map +1 -0
  116. package/dist/notation/part.d.ts +34 -0
  117. package/dist/notation/part.d.ts.map +1 -0
  118. package/dist/notation/part.js +15 -0
  119. package/dist/notation/part.js.map +1 -0
  120. package/dist/notation/transform.d.ts +28 -0
  121. package/dist/notation/transform.d.ts.map +1 -0
  122. package/dist/notation/transform.js +67 -0
  123. package/dist/notation/transform.js.map +1 -0
  124. package/dist/song.d.ts +140 -0
  125. package/dist/song.d.ts.map +1 -0
  126. package/dist/song.js +45 -0
  127. package/dist/song.js.map +1 -0
  128. package/dist/tempo.d.ts +36 -0
  129. package/dist/tempo.d.ts.map +1 -0
  130. package/dist/tempo.js +81 -0
  131. package/dist/tempo.js.map +1 -0
  132. package/dist/theory.d.ts +70 -0
  133. package/dist/theory.d.ts.map +1 -0
  134. package/dist/theory.js +183 -0
  135. package/dist/theory.js.map +1 -0
  136. package/dist/transcribe.d.ts +81 -0
  137. package/dist/transcribe.d.ts.map +1 -0
  138. package/dist/transcribe.js +433 -0
  139. package/dist/transcribe.js.map +1 -0
  140. package/package.json +57 -4
  141. package/src/engine/automation.ts +65 -0
  142. package/src/engine/compile.ts +124 -0
  143. package/src/engine/jukebox.ts +322 -0
  144. package/src/engine/mixer.ts +166 -0
  145. package/src/engine/output.ts +59 -0
  146. package/src/engine/playback.ts +349 -0
  147. package/src/engine/prepare.ts +15 -0
  148. package/src/engine/render.ts +71 -0
  149. package/src/index.ts +28 -0
  150. package/src/instruments/bowed.ts +660 -0
  151. package/src/instruments/builtin.ts +180 -0
  152. package/src/instruments/definition.ts +52 -0
  153. package/src/instruments/dsp/ambience.ts +142 -0
  154. package/src/instruments/dsp/core.ts +97 -0
  155. package/src/instruments/dsp/drums.ts +565 -0
  156. package/src/instruments/dsp/modal.ts +76 -0
  157. package/src/instruments/dsp/piano.ts +226 -0
  158. package/src/instruments/dsp/pluck.ts +240 -0
  159. package/src/instruments/dsp/reverb.ts +56 -0
  160. package/src/instruments/dsp/staccato.ts +141 -0
  161. package/src/instruments/play.ts +151 -0
  162. package/src/instruments/registry.ts +100 -0
  163. package/src/instruments/samples.ts +119 -0
  164. package/src/instruments/voices.ts +1168 -0
  165. package/src/midi.ts +285 -0
  166. package/src/notation/format.ts +96 -0
  167. package/src/notation/harmony.ts +233 -0
  168. package/src/notation/melody.ts +133 -0
  169. package/src/notation/part.ts +42 -0
  170. package/src/notation/transform.ts +78 -0
  171. package/src/song.ts +170 -0
  172. package/src/tempo.ts +108 -0
  173. package/src/theory.ts +208 -0
  174. package/src/transcribe.ts +487 -0
@@ -0,0 +1,113 @@
1
+ import type { Song } from '../song.ts';
2
+ import { Playback } from './playback.ts';
3
+ /** A song, or a function that loads one (`() => import('./songs/title.ts')` works: a module's default export is used) */
4
+ export type SongSource = Song | (() => Promise<Song | {
5
+ default: Song;
6
+ }>);
7
+ export interface JukeboxOptions {
8
+ /** Use this AudioContext (default: one is created on first use) */
9
+ context?: AudioContext;
10
+ /** Where the music goes (default: the speakers) */
11
+ destination?: AudioNode;
12
+ /** Songs that can be played by name */
13
+ songs?: Record<string, SongSource>;
14
+ /** 0-1 (default 1) */
15
+ volume?: number;
16
+ }
17
+ export interface PlayOptions {
18
+ /** Seconds to fade the previous song out (default 0.9) */
19
+ fadeOut?: number;
20
+ /** Seconds to fade this song in (default: 0.05 if it has an intro, otherwise 1.2) */
21
+ fadeIn?: number;
22
+ /** Start this many seconds into the song */
23
+ from?: number;
24
+ /** How many times the body plays before the song ends (default: forever, or once if `song.loop` is false) */
25
+ loops?: number;
26
+ }
27
+ export interface DuckOptions {
28
+ /** Seconds to reach the ducked level (default 0: instantly) */
29
+ attack?: number;
30
+ /** Seconds to stay there (default 0) */
31
+ hold?: number;
32
+ /** Seconds to come back (default 0.35) */
33
+ release?: number;
34
+ }
35
+ export type JukeboxState = 'idle' | 'loading' | 'playing' | 'paused';
36
+ export declare class Jukebox {
37
+ private ctx;
38
+ private readonly destination?;
39
+ private readonly songs;
40
+ private readonly loaded;
41
+ /** The music buses, each a volume and a duck gain: [into the output stage, around it for songs mastered loud] */
42
+ private buses;
43
+ private volumeLevel;
44
+ private timer;
45
+ /** Incremented whenever the current song changes: a song still loading only starts if nothing replaced it */
46
+ private token;
47
+ /** The song asked for last (it may still be loading) */
48
+ private wanted;
49
+ private pending;
50
+ private song;
51
+ /** The loop count the current song was asked to play with */
52
+ private loops;
53
+ private active;
54
+ private pausedAt;
55
+ /** Mute states to carry into the next playback of the same song (after a seek or a pause) */
56
+ private carry;
57
+ /** Preloads queue up behind each other, so they never compete with each other for the main thread */
58
+ private preloads;
59
+ constructor(options?: JukeboxOptions);
60
+ /** The audio context (created on first use; it starts on the first click, tap or key press if the browser requires one) */
61
+ get context(): AudioContext;
62
+ /** The song playing, paused, or about to start (null when idle) */
63
+ get current(): Song | null;
64
+ /** The current playback, for positions, durations and per-track mixing (a new one starts after every seek or resume) */
65
+ get playback(): Playback | null;
66
+ get state(): JukeboxState;
67
+ /** Seconds into the current song */
68
+ get position(): number;
69
+ get volume(): number;
70
+ set volume(value: number);
71
+ /**
72
+ * Play a song (by name, or a song / loader), crossfading from the one before.
73
+ * Asking for the song that is already playing does nothing; asking for a paused one resumes it.
74
+ * Resolves with the playback once it starts, or null if another song was asked for before this one was ready.
75
+ */
76
+ play(song: string | SongSource, options?: PlayOptions): Promise<Playback | null>;
77
+ /** Fade the music out and go silent */
78
+ stop(fade?: number): void;
79
+ /** Pause the current song (resume() continues from the same point) */
80
+ pause(): void;
81
+ /** Continue a paused song */
82
+ resume(): void;
83
+ /** Jump to a point in the current song (seconds) */
84
+ seek(seconds: number): void;
85
+ /** Render a song's samples ahead of time, so playing it later starts without a wait */
86
+ preload(song: string | SongSource): Promise<void>;
87
+ /** Play a short song once on top of whatever is playing (victory jingles, fanfares). Its samples are rendered on the spot. */
88
+ playOnce(song: Song): Playback;
89
+ /**
90
+ * Lower the music for a moment, for example under a big sound effect or a line of dialogue.
91
+ *
92
+ * @example
93
+ * jukebox.duck(-10); // a hit: drop 10 dB, recover over 0.35 s
94
+ * jukebox.duck(-30, { attack: 0.12, hold: 4, release: 0.6 }); // near silence for a cutscene beat
95
+ */
96
+ duck(db: number, { attack, hold, release }?: DuckOptions): void;
97
+ /** Bring ducked music back now */
98
+ unduck(release?: number): void;
99
+ /** Resume the audio context; call from a click or key handler if you want sound to start on that exact gesture */
100
+ unlock(): Promise<void>;
101
+ private begin;
102
+ private start;
103
+ /** Fade out what is playing and cancel anything still loading; the current song is remembered (for resume and seek) */
104
+ private halt;
105
+ private resolve;
106
+ private load;
107
+ /** Where a song goes in: songs mastered loud have their own limiter, so they go around the output stage */
108
+ private busFor;
109
+ /** Build the music buses (volume → duck → output stage, and volume → duck straight out) and arrange for the context to start on the first user gesture */
110
+ private connect;
111
+ private runClock;
112
+ }
113
+ //# sourceMappingURL=jukebox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jukebox.d.ts","sourceRoot":"","sources":["../../src/engine/jukebox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,287 @@
1
+ // The Jukebox: the one object a game needs. It owns the clock, crossfades between songs, prepares samples ahead of time,
2
+ // and handles the browser's audio unlock. One song plays at a time; jingles can be layered on top.
3
+ import { createOutputStage, mastersItself } from './output.js';
4
+ import { Playback } from './playback.js';
5
+ import { prepareSong, prepareSongSync } from './prepare.js';
6
+ /** How far ahead notes are scheduled: a little while visible, much more in a background tab (whose timers are throttled to about once a second) */
7
+ const AHEAD = 0.25;
8
+ const AHEAD_HIDDEN = 1.6;
9
+ export class Jukebox {
10
+ ctx;
11
+ destination;
12
+ songs;
13
+ loaded = new Map();
14
+ /** The music buses, each a volume and a duck gain: [into the output stage, around it for songs mastered loud] */
15
+ buses = [];
16
+ volumeLevel;
17
+ timer = null;
18
+ /** Incremented whenever the current song changes: a song still loading only starts if nothing replaced it */
19
+ token = 0;
20
+ /** The song asked for last (it may still be loading) */
21
+ wanted = null;
22
+ pending = null;
23
+ song = null;
24
+ /** The loop count the current song was asked to play with */
25
+ loops;
26
+ active = null;
27
+ pausedAt = null;
28
+ /** Mute states to carry into the next playback of the same song (after a seek or a pause) */
29
+ carry = new Set();
30
+ /** Preloads queue up behind each other, so they never compete with each other for the main thread */
31
+ preloads = Promise.resolve();
32
+ constructor(options = {}) {
33
+ this.ctx = options.context ?? null;
34
+ this.destination = options.destination;
35
+ this.songs = options.songs ?? {};
36
+ this.volumeLevel = options.volume ?? 1;
37
+ }
38
+ /** The audio context (created on first use; it starts on the first click, tap or key press if the browser requires one) */
39
+ get context() {
40
+ if (!this.ctx)
41
+ this.ctx = new AudioContext();
42
+ if (!this.buses.length)
43
+ this.connect(this.ctx);
44
+ return this.ctx;
45
+ }
46
+ /** The song playing, paused, or about to start (null when idle) */
47
+ get current() {
48
+ return this.song;
49
+ }
50
+ /** The current playback, for positions, durations and per-track mixing (a new one starts after every seek or resume) */
51
+ get playback() {
52
+ return this.active;
53
+ }
54
+ get state() {
55
+ if (this.pausedAt !== null)
56
+ return 'paused';
57
+ if (this.pending)
58
+ return 'loading';
59
+ return this.active?.playing ? 'playing' : 'idle';
60
+ }
61
+ /** Seconds into the current song */
62
+ get position() {
63
+ if (this.pausedAt !== null)
64
+ return this.pausedAt;
65
+ return this.active ? this.active.position().seconds : 0;
66
+ }
67
+ get volume() {
68
+ return this.volumeLevel;
69
+ }
70
+ set volume(value) {
71
+ this.volumeLevel = Math.min(1, Math.max(0, value));
72
+ if (this.ctx)
73
+ for (const { volume } of this.buses)
74
+ volume.gain.setTargetAtTime(this.volumeLevel, this.ctx.currentTime, 0.02);
75
+ }
76
+ /**
77
+ * Play a song (by name, or a song / loader), crossfading from the one before.
78
+ * Asking for the song that is already playing does nothing; asking for a paused one resumes it.
79
+ * Resolves with the playback once it starts, or null if another song was asked for before this one was ready.
80
+ */
81
+ play(song, options = {}) {
82
+ const source = this.resolve(song);
83
+ if (source === this.wanted) {
84
+ if (this.pausedAt !== null) {
85
+ this.resume();
86
+ return Promise.resolve(this.active);
87
+ }
88
+ if (this.pending)
89
+ return this.pending;
90
+ if (this.active?.playing)
91
+ return Promise.resolve(this.active);
92
+ }
93
+ this.halt(options.fadeOut ?? 0.9);
94
+ this.carry = new Set();
95
+ this.wanted = source;
96
+ this.loops = options.loops;
97
+ this.pending = this.begin(source, this.token, options);
98
+ return this.pending;
99
+ }
100
+ /** Fade the music out and go silent */
101
+ stop(fade = 0.9) {
102
+ this.halt(fade);
103
+ this.wanted = null;
104
+ this.song = null;
105
+ if (this.timer !== null)
106
+ clearInterval(this.timer);
107
+ this.timer = null;
108
+ }
109
+ /** Pause the current song (resume() continues from the same point) */
110
+ pause() {
111
+ if (!this.active?.playing)
112
+ return;
113
+ const at = this.active.position().seconds;
114
+ this.carry = mutedTracks(this.active);
115
+ this.halt(0.12);
116
+ this.pausedAt = at;
117
+ }
118
+ /** Continue a paused song */
119
+ resume() {
120
+ if (this.pausedAt === null || !this.song)
121
+ return;
122
+ const from = this.pausedAt;
123
+ this.pausedAt = null;
124
+ this.start(this.song, { fadeIn: 0.05, from });
125
+ }
126
+ /** Jump to a point in the current song (seconds) */
127
+ seek(seconds) {
128
+ if (this.pausedAt !== null) {
129
+ this.pausedAt = Math.max(0, seconds);
130
+ return;
131
+ }
132
+ if (!this.song || !this.active)
133
+ return;
134
+ this.carry = mutedTracks(this.active);
135
+ this.active.stop(0.12);
136
+ this.start(this.song, { fadeIn: 0.08, from: seconds });
137
+ }
138
+ /** Render a song's samples ahead of time, so playing it later starts without a wait */
139
+ preload(song) {
140
+ const source = this.resolve(song);
141
+ const ctx = this.context;
142
+ const job = this.preloads.then(() => this.load(source)).then((def) => prepareSong(ctx, def));
143
+ this.preloads = job.catch(() => undefined);
144
+ return job;
145
+ }
146
+ /** Play a short song once on top of whatever is playing (victory jingles, fanfares). Its samples are rendered on the spot. */
147
+ playOnce(song) {
148
+ const ctx = this.context;
149
+ prepareSongSync(ctx, song);
150
+ const active = new Playback(ctx, this.busFor(song), song, { at: ctx.currentTime + 0.03, loops: 1 });
151
+ active.schedule(active.startTime + active.duration);
152
+ return active;
153
+ }
154
+ /**
155
+ * Lower the music for a moment, for example under a big sound effect or a line of dialogue.
156
+ *
157
+ * @example
158
+ * jukebox.duck(-10); // a hit: drop 10 dB, recover over 0.35 s
159
+ * jukebox.duck(-30, { attack: 0.12, hold: 4, release: 0.6 }); // near silence for a cutscene beat
160
+ */
161
+ duck(db, { attack = 0, hold = 0, release = 0.35 } = {}) {
162
+ if (!this.ctx)
163
+ return;
164
+ const t = this.ctx.currentTime;
165
+ const level = Math.pow(10, db / 20);
166
+ for (const { duck } of this.buses) {
167
+ const g = duck.gain;
168
+ g.cancelScheduledValues(t);
169
+ g.setValueAtTime(attack > 0 ? g.value : level, t);
170
+ if (attack > 0)
171
+ g.linearRampToValueAtTime(level, t + attack);
172
+ g.setValueAtTime(level, t + attack + hold);
173
+ g.linearRampToValueAtTime(1, t + attack + hold + release);
174
+ }
175
+ }
176
+ /** Bring ducked music back now */
177
+ unduck(release = 0.3) {
178
+ if (!this.ctx)
179
+ return;
180
+ const t = this.ctx.currentTime;
181
+ for (const { duck } of this.buses) {
182
+ const g = duck.gain;
183
+ g.cancelScheduledValues(t);
184
+ g.setValueAtTime(g.value, t);
185
+ g.linearRampToValueAtTime(1, t + release);
186
+ }
187
+ }
188
+ /** Resume the audio context; call from a click or key handler if you want sound to start on that exact gesture */
189
+ unlock() {
190
+ return this.context.resume();
191
+ }
192
+ async begin(source, token, options) {
193
+ try {
194
+ const ctx = this.context;
195
+ const song = await this.load(source);
196
+ if (token !== this.token)
197
+ return null;
198
+ this.song = song;
199
+ await prepareSong(ctx, song);
200
+ if (token !== this.token)
201
+ return null;
202
+ return this.start(song, { fadeIn: options.fadeIn ?? (song.intro?.length ? 0.05 : 1.2), from: options.from });
203
+ }
204
+ finally {
205
+ if (token === this.token)
206
+ this.pending = null;
207
+ }
208
+ }
209
+ start(song, options) {
210
+ const ctx = this.context;
211
+ const active = new Playback(ctx, this.busFor(song), song, { ...options, loops: this.loops, at: ctx.currentTime + 0.08 });
212
+ for (const t of active.tracks)
213
+ if (this.carry.has(`${t.section}|${t.name}`))
214
+ t.muted = true;
215
+ this.carry = new Set();
216
+ this.active = active;
217
+ this.runClock();
218
+ return active;
219
+ }
220
+ /** Fade out what is playing and cancel anything still loading; the current song is remembered (for resume and seek) */
221
+ halt(fade) {
222
+ this.token++;
223
+ this.pending = null;
224
+ this.pausedAt = null;
225
+ this.active?.stop(fade);
226
+ this.active = null;
227
+ }
228
+ resolve(song) {
229
+ if (typeof song !== 'string')
230
+ return song;
231
+ const source = this.songs[song];
232
+ if (!source)
233
+ throw new Error(`Unknown song "${song}". Known: ${Object.keys(this.songs).join(', ') || '(none)'}`);
234
+ return source;
235
+ }
236
+ load(source) {
237
+ if (typeof source !== 'function')
238
+ return Promise.resolve(source);
239
+ let p = this.loaded.get(source);
240
+ if (!p) {
241
+ p = source().then((m) => ('default' in m ? m.default : m));
242
+ p.catch(() => this.loaded.delete(source));
243
+ this.loaded.set(source, p);
244
+ }
245
+ return p;
246
+ }
247
+ /** Where a song goes in: songs mastered loud have their own limiter, so they go around the output stage */
248
+ busFor(song) {
249
+ return this.buses[mastersItself(song) ? 1 : 0].volume;
250
+ }
251
+ /** Build the music buses (volume → duck → output stage, and volume → duck straight out) and arrange for the context to start on the first user gesture */
252
+ connect(ctx) {
253
+ const out = this.destination ?? ctx.destination;
254
+ this.buses = [createOutputStage(ctx, out), out].map((to) => {
255
+ const volume = ctx.createGain();
256
+ volume.gain.value = this.volumeLevel;
257
+ const duck = ctx.createGain();
258
+ volume.connect(duck).connect(to);
259
+ return { volume, duck };
260
+ });
261
+ if (ctx.state !== 'suspended' || typeof window === 'undefined')
262
+ return;
263
+ const events = ['pointerdown', 'keydown', 'touchend'];
264
+ const unlock = () => void ctx.resume();
265
+ for (const e of events)
266
+ window.addEventListener(e, unlock, { capture: true });
267
+ ctx.addEventListener('statechange', () => {
268
+ if (ctx.state === 'running')
269
+ for (const e of events)
270
+ window.removeEventListener(e, unlock, { capture: true });
271
+ });
272
+ }
273
+ runClock() {
274
+ if (this.timer !== null)
275
+ return;
276
+ this.timer = setInterval(() => {
277
+ if (!this.active || !this.ctx)
278
+ return;
279
+ const hidden = typeof document !== 'undefined' && document.hidden;
280
+ this.active.schedule(this.ctx.currentTime + (hidden ? AHEAD_HIDDEN : AHEAD));
281
+ }, 25);
282
+ }
283
+ }
284
+ function mutedTracks(active) {
285
+ return new Set(active.tracks.filter((t) => t.muted).map((t) => `${t.section}|${t.name}`));
286
+ }
287
+ //# sourceMappingURL=jukebox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jukebox.js","sourceRoot":"","sources":["../../src/engine/jukebox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { type Room } from '../instruments/dsp/reverb.ts';
2
+ import { type Song } from '../song.ts';
3
+ import { type Pump } from './automation.ts';
4
+ export interface Mixer {
5
+ /** The song's output (its fades are applied here) */
6
+ output: GainNode;
7
+ /** Closes for the song's silences: after the reverb and delay, so their tails stop dead too */
8
+ gate: GainNode;
9
+ /** One strip per track, per section: [intro tracks, body tracks] */
10
+ strips: [Strip[], Strip[]];
11
+ }
12
+ /** A track's channel on the console */
13
+ export interface Strip {
14
+ /** Where its notes go: the track's level (and mute) */
15
+ input: GainNode;
16
+ /** After its filters and ducking, before pan and effects */
17
+ output: AudioNode;
18
+ /** Its duck gain, when it ducks under another track */
19
+ pump?: Pump;
20
+ /** Its filters' cutoffs that sweep, with their breakpoints */
21
+ sweeps: {
22
+ cutoff: AudioParam;
23
+ points: [number, number][];
24
+ }[];
25
+ }
26
+ export declare function createMixer(ctx: BaseAudioContext, song: Song, destination: AudioNode): Mixer;
27
+ /** Impulse response of a room (each room is computed once per sample rate) */
28
+ export declare function impulseBuffer(ctx: BaseAudioContext, room: Room): AudioBuffer;
29
+ //# sourceMappingURL=mixer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mixer.d.ts","sourceRoot":"","sources":["../../src/engine/mixer.ts"],"names":[],"mappings":"AAQA,OAAO,EAAW,KAAK,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAkB,KAAK,IAAI,EAAc,MAAM,YAAY,CAAC;AACnE,OAAO,EAAyB,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAGnE,MAAM,WAAW,KAAK;IACpB,qDAAqD;IACrD,MAAM,EAAE,QAAQ,CAAC;IACjB,+FAA+F;IAC/F,IAAI,EAAE,QAAQ,CAAC;IACf,oEAAoE;IACpE,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;CAC5B;AAED,uCAAuC;AACvC,MAAM,WAAW,KAAK;IACpB,uDAAuD;IACvD,KAAK,EAAE,QAAQ,CAAC;IAChB,4DAA4D;IAC5D,MAAM,EAAE,SAAS,CAAC;IAClB,uDAAuD;IACvD,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,8DAA8D;IAC9D,MAAM,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;KAAE,EAAE,CAAC;CAC9D;AAKD,wBAAgB,WAAW,CAAC,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,GAAG,KAAK,CAwC5F;AA4ED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,GAAG,WAAW,CAW5E"}
@@ -0,0 +1,141 @@
1
+ // Each song brings its own mixing console: per-track level, filters, ducking, pan and effect sends, a convolution reverb, a ping-pong delay
2
+ // and the mastering (EQ, glue compression, and a limiter of its own for songs mastered loud).
3
+ //
4
+ // track input ── filters ── duck ─┬─ pan ───────────────────────┐
5
+ // ├─ reverb send ── reverb ─────┤
6
+ // └─ delay send ─── delay ──────┼── master ── EQ ── glue ── (loud stage) ── gate ── output
7
+ // └→ reverb │
8
+ import { impulse } from '../instruments/dsp/reverb.js';
9
+ import { DEFAULT_REVERB } from '../song.js';
10
+ import { breakpoints, cutoffAt } from './automation.js';
11
+ import { createLoudStage } from './output.js';
12
+ /** Q of the two stages of a 24 dB/octave filter, in dB as Web Audio reads it for low- and high-pass: Butterworth, with a touch of resonance */
13
+ const FILTER_Q = [-5.3, 4];
14
+ export function createMixer(ctx, song, destination) {
15
+ // The delay and the ducking keep time with the opening tempo
16
+ const stepSeconds = 60 / song.bpm / 4;
17
+ const output = ctx.createGain();
18
+ output.connect(destination);
19
+ const gate = ctx.createGain();
20
+ gate.connect(output);
21
+ const master = createMaster(ctx, song, gate);
22
+ const reverb = createReverb(ctx, song, master);
23
+ const delay = song.delay ? createDelay(ctx, song.delay, stepSeconds, master, reverb) : null;
24
+ const strip = (track) => {
25
+ const input = ctx.createGain();
26
+ input.gain.value = track.gain ?? 1;
27
+ let out = input;
28
+ const sweeps = [];
29
+ for (const type of ['lowpass', 'highpass']) {
30
+ const setting = track[type];
31
+ if (setting === undefined)
32
+ continue;
33
+ const points = breakpoints(typeof setting === 'number' ? { 0: setting } : setting);
34
+ for (const q of FILTER_Q) {
35
+ const f = biquad(ctx, type, cutoffAt(points, 0), q);
36
+ out = out.connect(f);
37
+ if (points.length > 1)
38
+ sweeps.push({ cutoff: f.frequency, points });
39
+ }
40
+ }
41
+ let pump;
42
+ if (track.duck) {
43
+ const duck = ctx.createGain();
44
+ out = out.connect(duck);
45
+ const { depth = 0.7, release = 4 } = typeof track.duck === 'string' ? {} : track.duck;
46
+ pump = { gain: duck.gain, depth, release: release * stepSeconds };
47
+ }
48
+ const pan = ctx.createStereoPanner();
49
+ pan.pan.value = track.pan ?? 0;
50
+ out.connect(pan).connect(master);
51
+ send(ctx, out, reverb, track.reverb ?? 0.25);
52
+ if (delay && track.delay)
53
+ send(ctx, out, delay, track.delay);
54
+ return { input, output: out, pump, sweeps };
55
+ };
56
+ return { output, gate, strips: [(song.intro ?? []).map(strip), song.tracks.map(strip)] };
57
+ }
58
+ function send(ctx, from, to, amount) {
59
+ const g = ctx.createGain();
60
+ g.gain.value = amount;
61
+ from.connect(g).connect(to);
62
+ }
63
+ /** Master bus: overall level, then (each only when asked for) the mastering EQ, glue compression and the loud stage */
64
+ function createMaster(ctx, song, output) {
65
+ const master = ctx.createGain();
66
+ const m = song.master ?? {};
67
+ master.gain.value = (song.gain ?? 1) * Math.pow(10, (m.trim ?? 0) / 20);
68
+ let out = master;
69
+ if (m.warm || m.cut) {
70
+ // Cut the rumble below 30 Hz first, so adding body does not eat the dynamics
71
+ const hp = biquad(ctx, 'highpass', 30, 0.6);
72
+ const warm = biquad(ctx, 'peaking', 150, 0.7, m.warm ?? 0);
73
+ const cut = biquad(ctx, 'peaking', 1300, 0.9, -(m.cut ?? 0));
74
+ out = out.connect(hp).connect(warm).connect(cut);
75
+ }
76
+ // Glue: the more of it, the lower the threshold and the higher the ratio; the slow attack lets the drums' hits through
77
+ if (m.glue)
78
+ out = out.connect(new DynamicsCompressorNode(ctx, { threshold: -4 - 12 * m.glue, knee: 6, ratio: 2 + 2 * m.glue, attack: 0.02, release: 0.25 }));
79
+ out.connect(m.loud === undefined ? output : createLoudStage(ctx, m.loud, output));
80
+ return master;
81
+ }
82
+ function biquad(ctx, type, frequency, q, gain = 0) {
83
+ const f = ctx.createBiquadFilter();
84
+ f.type = type;
85
+ f.frequency.value = frequency;
86
+ f.Q.value = q;
87
+ f.gain.value = gain;
88
+ return f;
89
+ }
90
+ /** Convolution reverb with the song's room; returns its input */
91
+ function createReverb(ctx, song, master) {
92
+ const room = song.reverb ?? DEFAULT_REVERB;
93
+ const rev = ctx.createConvolver();
94
+ rev.normalize = false;
95
+ rev.buffer = impulseBuffer(ctx, room);
96
+ const ret = ctx.createGain();
97
+ ret.gain.value = room.mix ?? 1;
98
+ rev.connect(ret).connect(master);
99
+ return rev;
100
+ }
101
+ /** Tempo-synced ping-pong delay (left, then right); its echoes also feed a little into the reverb. Returns its input */
102
+ function createDelay(ctx, d, stepSeconds, master, reverb) {
103
+ const time = Math.min(1.9, d.steps * stepSeconds);
104
+ const input = ctx.createGain();
105
+ const left = ctx.createDelay(2);
106
+ const right = ctx.createDelay(2);
107
+ left.delayTime.value = time;
108
+ right.delayTime.value = time;
109
+ const tone = biquad(ctx, 'lowpass', d.tone ?? 2800, 1);
110
+ const feedback = ctx.createGain();
111
+ feedback.gain.value = d.feedback ?? 0.35;
112
+ const ret = ctx.createGain();
113
+ ret.gain.value = d.mix ?? 0.8;
114
+ const panL = ctx.createStereoPanner();
115
+ const panR = ctx.createStereoPanner();
116
+ panL.pan.value = -0.7;
117
+ panR.pan.value = 0.7;
118
+ input.connect(tone).connect(left);
119
+ left.connect(panL).connect(ret);
120
+ left.connect(right);
121
+ right.connect(panR).connect(ret);
122
+ right.connect(feedback).connect(left);
123
+ ret.connect(master);
124
+ send(ctx, ret, reverb, 0.3);
125
+ return input;
126
+ }
127
+ const irCache = new Map();
128
+ /** Impulse response of a room (each room is computed once per sample rate) */
129
+ export function impulseBuffer(ctx, room) {
130
+ const key = `${ctx.sampleRate}|${room.size}|${room.damp}|${room.preDelay}|${room.early}|${room.bloom}`;
131
+ let b = irCache.get(key);
132
+ if (!b) {
133
+ const [l, r] = impulse(ctx.sampleRate, room);
134
+ b = ctx.createBuffer(2, l.length, ctx.sampleRate);
135
+ b.getChannelData(0).set(l);
136
+ b.getChannelData(1).set(r);
137
+ irCache.set(key, b);
138
+ }
139
+ return b;
140
+ }
141
+ //# sourceMappingURL=mixer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mixer.js","sourceRoot":"","sources":["../../src/engine/mixer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { Song } from '../song.ts';
2
+ /** Songs are mixed to sound right at this bus level */
3
+ export declare const MIX_LEVEL = 0.36;
4
+ /** Level and limiter in front of a destination; returns the stage's input */
5
+ export declare function createOutputStage(ctx: BaseAudioContext, destination: AudioNode): GainNode;
6
+ /** Songs mastered loud end in a limiter of their own, so they skip the output stage */
7
+ export declare function mastersItself(song: Song): boolean;
8
+ /**
9
+ * The final stage of a song mastered loud: `loud` dB of drive into a brickwall limiter (songs peak around full scale before the
10
+ * bus level, so that is about how much their loudest moments are turned down), then a soft clipper that rounds off whatever gets
11
+ * past it, under the ceiling. Returns its input.
12
+ */
13
+ export declare function createLoudStage(ctx: BaseAudioContext, loud: number, destination: AudioNode): GainNode;
14
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/engine/output.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,uDAAuD;AACvD,eAAO,MAAM,SAAS,OAAO,CAAC;AAE9B,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,GAAG,QAAQ,CAQzF;AAED,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAEjD;AAKD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,GAAG,QAAQ,CAarG"}