@hraness/dawg 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/DAWG.md +343 -116
  3. package/README.md +27 -25
  4. package/core/autotune.ts +1119 -0
  5. package/core/chords.ts +271 -23
  6. package/core/clips.ts +499 -0
  7. package/core/diff.ts +182 -104
  8. package/core/expression.ts +15 -0
  9. package/core/fx.ts +99 -29
  10. package/core/instruments.ts +19 -0
  11. package/core/keys.ts +3 -3
  12. package/core/loop.ts +5 -0
  13. package/core/lyrics.ts +297 -0
  14. package/core/master.ts +3 -3
  15. package/core/resonators.ts +16 -2
  16. package/core/routing.ts +165 -0
  17. package/core/score.ts +832 -13
  18. package/core/sdk/eval-child.ts +7 -2
  19. package/core/sdk/eval.ts +35 -6
  20. package/core/sdk/print.ts +276 -3
  21. package/core/sdk/sync-lyrics.ts +49 -0
  22. package/core/sdk/v1.ts +1849 -42
  23. package/core/sections.ts +292 -26
  24. package/core/sing.ts +815 -0
  25. package/core/style-provenance.ts +80 -0
  26. package/core/styles/africa-mena-southasia.ts +2893 -0
  27. package/core/styles/americas.ts +3810 -0
  28. package/core/styles/art.ts +4993 -0
  29. package/core/styles/base.ts +123 -0
  30. package/core/styles/cycles.ts +106 -0
  31. package/core/styles/electronic.ts +2723 -0
  32. package/core/styles/europe-asia-pacific.ts +2838 -0
  33. package/core/styles/excerpt.ts +29 -0
  34. package/core/styles/gamelan.ts +283 -0
  35. package/core/styles/generate.ts +2199 -0
  36. package/core/styles/index.ts +515 -0
  37. package/core/styles/parts.ts +106 -0
  38. package/core/styles/pop.ts +3189 -0
  39. package/core/styles/rock.ts +2993 -0
  40. package/core/styles/roots.ts +4175 -0
  41. package/core/styles/schema.ts +429 -0
  42. package/core/styles/taxonomy.ts +940 -0
  43. package/core/styles/validate.ts +528 -0
  44. package/core/tempo.ts +32 -2
  45. package/core/tuning.ts +19 -3
  46. package/core/vocoder.ts +524 -0
  47. package/guides/agent.md +29 -0
  48. package/guides/arrange.md +30 -0
  49. package/guides/audition.md +20 -14
  50. package/guides/automation.md +12 -7
  51. package/guides/chords.md +15 -15
  52. package/guides/effects.md +17 -16
  53. package/guides/faders.md +20 -16
  54. package/guides/files.md +13 -9
  55. package/guides/getting-started.md +15 -11
  56. package/guides/keys.md +19 -15
  57. package/guides/media.md +17 -12
  58. package/guides/mix.md +12 -6
  59. package/guides/music.md +23 -8
  60. package/guides/notes.md +15 -9
  61. package/guides/performance.md +15 -13
  62. package/guides/play.md +21 -13
  63. package/guides/project.md +25 -8
  64. package/guides/providers.md +20 -14
  65. package/guides/resample.md +15 -9
  66. package/guides/rhythm.md +17 -13
  67. package/guides/sessions.md +17 -7
  68. package/guides/show-me.md +31 -0
  69. package/guides/sound.md +25 -9
  70. package/guides/sounds.md +15 -13
  71. package/guides/styles.md +31 -0
  72. package/guides/tempo.md +15 -10
  73. package/guides/tracks.md +15 -10
  74. package/guides/tuning.md +32 -0
  75. package/guides/voice.md +31 -0
  76. package/guides/web-search.md +18 -8
  77. package/native/prebuilt/darwin-arm64/libdawg_sink.dylib +0 -0
  78. package/native/prebuilt/darwin-x64/libdawg_sink.dylib +0 -0
  79. package/native/prebuilt/linux-arm64/libdawg_sink.so +0 -0
  80. package/native/prebuilt/linux-x64/libdawg_sink.so +0 -0
  81. package/native/prebuilt/manifest.json +21 -0
  82. package/package.json +5 -2
  83. package/src/agent/agent.ts +126 -14
  84. package/src/agent/calibration-tools.ts +53 -0
  85. package/src/agent/clip-tools.ts +453 -0
  86. package/src/agent/command-agent.ts +369 -0
  87. package/src/agent/drum-tools.ts +2 -2
  88. package/src/agent/expression-tools.ts +1 -1
  89. package/src/agent/gateway.ts +246 -60
  90. package/src/agent/models.ts +53 -12
  91. package/src/agent/ops.ts +12 -1
  92. package/src/agent/pack-tools.ts +1 -1
  93. package/src/agent/planner.ts +13 -0
  94. package/src/agent/portable-schema.ts +80 -0
  95. package/src/agent/preview-tool.ts +4 -1
  96. package/src/agent/provider.ts +22 -8
  97. package/src/agent/rhythm-tools.ts +1 -1
  98. package/src/agent/section-tools.ts +1 -1
  99. package/src/agent/show-me.ts +497 -0
  100. package/src/agent/steer.ts +15 -0
  101. package/src/agent/style-tools.ts +217 -0
  102. package/src/agent/tool-error.ts +12 -0
  103. package/src/agent/tools.ts +108 -23
  104. package/src/agent/usage.ts +2 -2
  105. package/src/agent/voice-tools.ts +925 -0
  106. package/src/agent/xcb-agent.ts +11 -7
  107. package/src/argv.ts +38 -0
  108. package/src/audio/analysis.ts +253 -0
  109. package/src/audio/arrange.ts +37 -3
  110. package/src/audio/autotune-engine.ts +101 -0
  111. package/src/audio/autotune.ts +640 -0
  112. package/src/audio/clips.ts +240 -0
  113. package/src/audio/doctor.ts +86 -0
  114. package/src/audio/dsp/bandbank.ts +138 -0
  115. package/src/audio/dsp/envelope.ts +10 -0
  116. package/src/audio/dsp/follow.ts +120 -0
  117. package/src/audio/dsp/formant.ts +427 -0
  118. package/src/audio/dsp/glottal.ts +243 -0
  119. package/src/audio/dsp/interp.ts +7 -2
  120. package/src/audio/dsp/lpc.ts +50 -0
  121. package/src/audio/dsp/periodicity.ts +59 -0
  122. package/src/audio/dsp/pitch.ts +995 -0
  123. package/src/audio/dsp/psola.ts +199 -0
  124. package/src/audio/effects/chain.ts +3 -1
  125. package/src/audio/effects/common.ts +43 -0
  126. package/src/audio/effects/convolution.ts +7 -4
  127. package/src/audio/effects/filter.ts +48 -69
  128. package/src/audio/effects/formant.ts +263 -0
  129. package/src/audio/engine.ts +160 -28
  130. package/src/audio/fit.ts +35 -3
  131. package/src/audio/instrument-check.ts +59 -43
  132. package/src/audio/instruments.ts +4 -0
  133. package/src/audio/keys/calibration.ts +56 -0
  134. package/src/audio/keys/electric.ts +8 -1
  135. package/src/audio/keys/engine.ts +13 -1
  136. package/src/audio/keys/piano.ts +22 -2
  137. package/src/audio/kits.ts +135 -6
  138. package/src/audio/live.ts +114 -20
  139. package/src/audio/native.ts +615 -0
  140. package/src/audio/preview.ts +30 -2
  141. package/src/audio/render-worker.ts +2 -0
  142. package/src/audio/renderer.ts +2 -0
  143. package/src/audio/resample.ts +2 -1
  144. package/src/audio/sampler.ts +85 -4
  145. package/src/audio/samples.ts +20 -2
  146. package/src/audio/sing/analysis.ts +193 -0
  147. package/src/audio/sing/engine.ts +949 -0
  148. package/src/audio/strings/bow.ts +48 -5
  149. package/src/audio/strings/engine.ts +8 -2
  150. package/src/audio/synth/oscillators.ts +31 -21
  151. package/src/audio/synth/voice.ts +34 -1
  152. package/src/audio/vocoder/bank.ts +314 -0
  153. package/src/audio/vocoder/carrier.ts +165 -0
  154. package/src/audio/vocoder/control.ts +68 -0
  155. package/src/audio/vocoder/detect.ts +50 -0
  156. package/src/audio/vocoder/index.ts +304 -0
  157. package/src/audio/vocoder/talkbox.ts +143 -0
  158. package/src/audio/wav.ts +500 -77
  159. package/src/audio/winds/engine.ts +5 -1
  160. package/src/audio/winds/trim.ts +28 -4
  161. package/src/audio/winds/trims1.ts +297 -0
  162. package/src/audio/winds/voice.ts +15 -2
  163. package/src/auth/cli.ts +38 -36
  164. package/src/auth/credentials.ts +30 -1
  165. package/src/auth/login.ts +15 -9
  166. package/src/auth/tui.ts +19 -10
  167. package/src/commands/arrange.ts +44 -29
  168. package/src/commands/autotune.ts +421 -0
  169. package/src/commands/calibration.ts +74 -0
  170. package/src/commands/clips.ts +887 -0
  171. package/src/commands/drums.ts +3 -2
  172. package/src/commands/edit.ts +11 -4
  173. package/src/commands/expression.ts +1 -1
  174. package/src/commands/formant.ts +221 -0
  175. package/src/commands/fx.ts +101 -32
  176. package/src/commands/grammar.ts +558 -0
  177. package/src/commands/help.ts +610 -380
  178. package/src/commands/history.ts +18 -0
  179. package/src/commands/keys.ts +8 -8
  180. package/src/commands/modal.ts +1 -1
  181. package/src/commands/music.ts +1 -1
  182. package/src/commands/nearest.ts +53 -0
  183. package/src/commands/pack.ts +9 -2
  184. package/src/commands/param-range.ts +56 -0
  185. package/src/commands/parses.ts +130 -0
  186. package/src/commands/progression.ts +170 -0
  187. package/src/commands/rhythm.ts +3 -0
  188. package/src/commands/rig.ts +3 -24
  189. package/src/commands/sing.ts +478 -0
  190. package/src/commands/strum.ts +13 -1
  191. package/src/commands/style.ts +415 -0
  192. package/src/commands/time.ts +6 -3
  193. package/src/commands/tuning.ts +3 -3
  194. package/src/commands/vocal-pitch.ts +616 -0
  195. package/src/commands/vocal.ts +147 -0
  196. package/src/commands/vocoder.ts +627 -0
  197. package/src/commands/wind.ts +2 -2
  198. package/src/fs/durable.ts +50 -0
  199. package/src/lang/glossary.ts +493 -0
  200. package/src/launch-args.ts +163 -0
  201. package/src/main.ts +1397 -253
  202. package/src/media/cli.ts +20 -3
  203. package/src/media/import.ts +3 -1
  204. package/src/project/check.ts +21 -2
  205. package/src/project/clip-pins.ts +72 -0
  206. package/src/project/init.ts +23 -8
  207. package/src/project/sync.ts +418 -86
  208. package/src/render.ts +20 -1
  209. package/src/session/daemon.ts +3 -0
  210. package/src/session/meta.ts +14 -0
  211. package/src/session/origin.ts +154 -0
  212. package/src/session/port.ts +22 -4
  213. package/src/session/presence.ts +34 -4
  214. package/src/session/protocol.ts +5 -1
  215. package/src/session/rebase.ts +18 -5
  216. package/src/session/receipt.ts +258 -0
  217. package/src/session/store.ts +65 -43
  218. package/src/tui/arrange-menu.ts +65 -31
  219. package/src/tui/audition.ts +1 -1
  220. package/src/tui/euclid.ts +18 -13
  221. package/src/tui/fader.ts +228 -41
  222. package/src/tui/granular-menu.ts +2 -4
  223. package/src/tui/menu-clips.ts +297 -0
  224. package/src/tui/menu-time.ts +20 -13
  225. package/src/tui/menu-voice.ts +405 -0
  226. package/src/tui/menu.ts +759 -177
  227. package/src/tui/modal-menu.ts +6 -6
  228. package/src/tui/performance-menu.ts +5 -2
  229. package/src/tui/play-chords.ts +4 -2
  230. package/src/tui/play-mode.ts +15 -1
  231. package/src/tui/play-session.ts +47 -7
  232. package/src/tui/sing-menu.ts +278 -0
  233. package/src/tui/style-menu.ts +104 -0
  234. package/src/tui/vocoder-menu.ts +244 -0
  235. package/src/tui/wind-menu.ts +3 -3
  236. package/src/version.ts +8 -0
  237. package/src/web/fetch.ts +115 -29
  238. package/tui/activity.ts +180 -9
  239. package/tui/app.ts +274 -40
  240. package/tui/clip-row.ts +132 -0
  241. package/tui/delight.ts +144 -0
  242. package/tui/drawer.ts +70 -22
  243. package/tui/frame-gate.ts +76 -0
  244. package/tui/grammar.ts +112 -63
  245. package/tui/guide.ts +42 -4
  246. package/tui/highway.ts +269 -25
  247. package/tui/hints.ts +192 -0
  248. package/tui/input.ts +60 -9
  249. package/tui/keys.ts +1 -1
  250. package/tui/play-strip.ts +68 -14
  251. package/tui/prompt.ts +1 -1
  252. package/tui/screen.ts +144 -14
  253. package/tui/theme.ts +27 -0
package/src/audio/live.ts CHANGED
@@ -28,6 +28,9 @@ import { isOrganFamily } from "../../core/keys.ts";
28
28
  import type { LiveFullReply, LiveFullRequest } from "./live-worker.ts";
29
29
  import { isBowed, liveStringTrack } from "./strings/engine.ts";
30
30
  import { resolveString } from "../../core/strings.ts";
31
+ import { singTrack } from "../../core/sing.ts";
32
+ import { warmGlottal } from "./dsp/glottal.ts";
33
+ import { withSingHorizon } from "./sing/engine.ts";
31
34
 
32
35
  /** A rendered live note: interleaved stereo 16-bit PCM. */
33
36
  export type LiveNotePcm = Readonly<{
@@ -99,7 +102,18 @@ export class LiveSynth {
99
102
  /** f061-organ: the live organ clock's origin (performance.now ms). */
100
103
  private readonly organEpoch = performance.now();
101
104
 
102
- public constructor(private readonly sampleRate: number) {}
105
+ /**
106
+ * `fastTail: false` always renders the whole tail window (tests compare
107
+ * the release-sized buffer against it).
108
+ */
109
+ public constructor(
110
+ private readonly sampleRate: number,
111
+ private readonly options: Readonly<{
112
+ fastTail?: boolean;
113
+ /** Called with each one-note render's buffer length, in frames. */
114
+ onRender?: (frames: number) => void;
115
+ }> = {},
116
+ ) {}
103
117
 
104
118
  public get rate(): number {
105
119
  return this.sampleRate;
@@ -237,7 +251,11 @@ export class LiveSynth {
237
251
  // Organs too: wheels, rotors and drive cost 4-8 ms per note-second.
238
252
  const windowed =
239
253
  request.full !== true &&
240
- (hasRig(track) || organTick !== undefined || bowedTrack(track)) &&
254
+ (hasRig(track) ||
255
+ organTick !== undefined ||
256
+ bowedTrack(track) ||
257
+ hasFormant(track) ||
258
+ singTrack(track)) &&
241
259
  seconds > LIVE_RIG_WINDOW_SECONDS &&
242
260
  !this.cache.has(key);
243
261
  if (windowed) {
@@ -282,24 +300,8 @@ export class LiveSynth {
282
300
  : 0;
283
301
  // Fitted sample windows over 8 s fit in the background (silent until
284
302
  // ready, never at the wrong pitch); shorter ones fit synchronously.
285
- const audio = withLiveFit(() =>
286
- renderScorePcm(single, {
287
- sampleRate: this.sampleRate,
288
- // The renderer's shortest one-shot is 1 s; the window is cut below.
289
- maxSeconds: windowed
290
- ? 1
291
- : Math.max(MAX_LIVE_NOTE_SECONDS + 4, seconds + tail),
292
- ...(request.samples ? { samples: request.samples } : {}),
293
- ...(organTick ? { seedTick: organTick } : {}),
294
- ...(quantChord ? { quantChord } : {}),
295
- }),
296
- );
297
- const frames = windowed
298
- ? Math.min(
299
- audibleFrames(audio.pcm),
300
- Math.round(LIVE_RIG_WINDOW_SECONDS * this.sampleRate),
301
- )
302
- : audibleFrames(audio.pcm);
303
+ const windowFrames = Math.round(LIVE_RIG_WINDOW_SECONDS * this.sampleRate);
304
+ const horizon = windowed ? windowFrames : Infinity;
303
305
  const release = liveEngine?.releaseSeconds
304
306
  ? liveEngine.releaseSeconds(
305
307
  track,
@@ -311,6 +313,44 @@ export class LiveSynth {
311
313
  ),
312
314
  )
313
315
  : tail;
316
+ const renderFor = (maxSeconds: number) => {
317
+ const out = withLiveFit(() =>
318
+ withSingHorizon(horizon, () =>
319
+ renderScorePcm(single, {
320
+ sampleRate: this.sampleRate,
321
+ maxSeconds,
322
+ ...(request.samples ? { samples: request.samples } : {}),
323
+ ...(organTick ? { seedTick: organTick } : {}),
324
+ ...(quantChord ? { quantChord } : {}),
325
+ }),
326
+ ),
327
+ );
328
+ this.options.onRender?.(out.frames);
329
+ return out;
330
+ };
331
+ const fullSeconds = Math.max(MAX_LIVE_NOTE_SECONDS + 4, seconds + tail);
332
+ // A damped key decays 60 dB per 4.6 x its release (a linear fade of the
333
+ // same energy), so 12 x release is past -150 dB: silent in 16 bits. The
334
+ // renderer is causal, so the short buffer is the full one's prefix; a
335
+ // note still sounding at its end falls back to the full window.
336
+ const shortSeconds =
337
+ !windowed &&
338
+ this.options.fastTail !== false &&
339
+ liveFastTail(track, liveEngine !== undefined)
340
+ ? seconds + Math.min(tail, LIVE_TAIL_RELEASES * release) + 0.1
341
+ : Infinity;
342
+ let audio =
343
+ // The renderer's shortest one-shot is 1 s; the window is cut below.
344
+ windowed ? renderFor(1) : renderFor(Math.min(fullSeconds, shortSeconds));
345
+ if (
346
+ shortSeconds < fullSeconds &&
347
+ audibleFrames(audio.pcm) >
348
+ audio.frames - Math.round(LIVE_TAIL_GUARD_SECONDS * this.sampleRate)
349
+ )
350
+ audio = renderFor(fullSeconds);
351
+ const frames = windowed
352
+ ? Math.min(audibleFrames(audio.pcm), windowFrames)
353
+ : audibleFrames(audio.pcm);
314
354
  const ringOut = !windowed && liveEngine?.ringOut?.(track) === true;
315
355
  const pcm = audio.pcm.subarray(0, frames * RENDER_CHANNELS);
316
356
  if (ringOut) fadeCutTail(pcm, frames, this.sampleRate);
@@ -346,6 +386,30 @@ export class LiveSynth {
346
386
  }
347
387
  }
348
388
 
389
+ /**
390
+ * Pays a voice's one-off costs before a key can sound (formant.md 4.5): the
391
+ * sing engine builds its LF glottal tables (about 50 ms) on first use and
392
+ * its inner loops need a pass to compile, so the first sing key of a session
393
+ * would miss the 15 ms first-window budget by 4x. Every other voice has the
394
+ * same shape at a smaller scale: a cold first press of a grand, a bowed
395
+ * cello, a granular swarm or a track with effects costs 2-4x a warm one
396
+ * (docs/perf.md, `live.*`). Play mode calls this when it opens on a track,
397
+ * so the first key costs what later keys do. The warm render goes
398
+ * through a throwaway synth, so no cache entry or state is left behind.
399
+ */
400
+ export function warmLive(
401
+ score: TrackScore,
402
+ trackId: string,
403
+ sampleRate: number,
404
+ ): void {
405
+ const track = score.tracks.find((candidate) => candidate.id === trackId);
406
+ if (!track) return;
407
+ if (singTrack(track)) warmGlottal();
408
+ const synth = new LiveSynth(sampleRate);
409
+ for (const pitch of [48, 60])
410
+ synth.render({ score, trackId, pitch, velocity: 0.8, seconds: 0.3 });
411
+ }
412
+
349
413
  /**
350
414
  * The track as it sounds live: audible regardless of mute and solo, and
351
415
  * without track `time`, so a key sounds once, as played, not re-placed.
@@ -379,6 +443,15 @@ function bowedTrack(track: Track): boolean {
379
443
  );
380
444
  }
381
445
 
446
+ /**
447
+ * Whether the track plays through the 0.7 formant fx. It is not causal (one
448
+ * STFT frame of lookahead), but the window renders a 1 s source, well past
449
+ * the 0.75 s window plus one frame, so the window is an exact prefix.
450
+ */
451
+ function hasFormant(track: Track): boolean {
452
+ return Boolean(track.fx?.formant);
453
+ }
454
+
382
455
  /** Whether the track plays through a guitar rig stage. */
383
456
  function hasRig(track: Track): boolean {
384
457
  return Boolean(track.fx?.stomp || track.fx?.head || track.fx?.cab);
@@ -425,6 +498,27 @@ export function fadeCutTail(
425
498
  }
426
499
  }
427
500
 
501
+ /** A short live render ends at 12 x the engine's release past the note. */
502
+ const LIVE_TAIL_RELEASES = 12;
503
+ /** Audio within this much of a short render's end means it was cut. */
504
+ const LIVE_TAIL_GUARD_SECONDS = 0.05;
505
+
506
+ /**
507
+ * Whether a live note may render a buffer sized by its engine's release
508
+ * rather than the whole tail window: only a 0.6 engine's dry track (echoes
509
+ * and reverb tails are not bounded by the key's release).
510
+ */
511
+ function liveFastTail(track: Track, engine: boolean): boolean {
512
+ return (
513
+ engine &&
514
+ track.delay === undefined &&
515
+ track.reverb === undefined &&
516
+ (track.fx === undefined || Object.keys(track.fx).length === 0) &&
517
+ (track.fxAutomation === undefined ||
518
+ Object.keys(track.fxAutomation).length === 0)
519
+ );
520
+ }
521
+
428
522
  function audibleFrames(pcm: Int16Array): number {
429
523
  let last = pcm.length - 1;
430
524
  while (last >= 0 && pcm[last] === 0) last -= 1;