@hraness/dawg 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -0
- package/DAWG.md +592 -49
- package/README.md +4 -4
- package/core/chords.ts +492 -4
- package/core/diff.ts +9 -0
- package/core/expression.ts +143 -6
- package/core/fx.ts +689 -2
- package/core/granular.ts +619 -0
- package/core/instruments.ts +442 -0
- package/core/keys.ts +955 -0
- package/core/resonators.ts +862 -0
- package/core/score.ts +529 -8
- package/core/sdk/eval-child.ts +2 -0
- package/core/sdk/print.ts +247 -16
- package/core/sdk/sync-instruments.ts +58 -0
- package/core/sdk/v1.ts +2417 -40
- package/core/sections.ts +44 -10
- package/core/strings.ts +1080 -0
- package/core/winds.ts +652 -0
- package/guides/automation.md +1 -0
- package/guides/chords.md +3 -1
- package/guides/effects.md +5 -2
- package/guides/media.md +1 -1
- package/guides/performance.md +3 -0
- package/guides/resample.md +26 -0
- package/guides/sounds.md +7 -2
- package/guides/tempo.md +1 -0
- package/package.json +1 -1
- package/src/agent/agent.ts +13 -0
- package/src/agent/chord-tools.ts +153 -1
- package/src/agent/expression-tools.ts +91 -0
- package/src/agent/granular-tools.ts +138 -0
- package/src/agent/models.ts +4 -4
- package/src/agent/ops.ts +42 -2
- package/src/agent/preview-tool.ts +7 -1
- package/src/agent/resample-tool.ts +131 -0
- package/src/agent/tools.ts +726 -17
- package/src/agent/xcb-agent.ts +13 -0
- package/src/audio/arrange.ts +42 -3
- package/src/audio/dsp/bank.ts +233 -0
- package/src/audio/dsp/envelope.ts +161 -0
- package/src/audio/dsp/fft.ts +6 -0
- package/src/audio/dsp/filters.ts +57 -0
- package/src/audio/dsp/interp.ts +112 -0
- package/src/audio/dsp/modal.ts +684 -0
- package/src/audio/dsp/onset.ts +197 -0
- package/src/audio/dsp/oversample.ts +202 -0
- package/src/audio/dsp/rng.ts +37 -0
- package/src/audio/dsp/shape.ts +81 -0
- package/src/audio/dsp/shift.ts +256 -0
- package/src/audio/dsp/stft.ts +73 -0
- package/src/audio/dsp/window.ts +77 -0
- package/src/audio/effects/chain.ts +23 -3
- package/src/audio/effects/common.ts +14 -0
- package/src/audio/effects/convolution.ts +106 -1
- package/src/audio/effects/gaze.ts +354 -0
- package/src/audio/effects/rig/cab.ts +99 -0
- package/src/audio/effects/rig/filters.ts +152 -0
- package/src/audio/effects/rig/gate.ts +39 -0
- package/src/audio/effects/rig/head.ts +487 -0
- package/src/audio/effects/rig/index.ts +170 -0
- package/src/audio/effects/rig/section.ts +78 -0
- package/src/audio/effects/rig/stomp.ts +238 -0
- package/src/audio/engine.ts +56 -6
- package/src/audio/fit.ts +447 -0
- package/src/audio/granular.ts +995 -0
- package/src/audio/instrument-check.ts +137 -0
- package/src/audio/instruments.ts +140 -0
- package/src/audio/keys/dsp.ts +323 -0
- package/src/audio/keys/electric.ts +420 -0
- package/src/audio/keys/engine.ts +485 -0
- package/src/audio/keys/organ.ts +1335 -0
- package/src/audio/keys/piano.ts +476 -0
- package/src/audio/keys/sympathetic.ts +127 -0
- package/src/audio/live-worker.ts +56 -0
- package/src/audio/live.ts +361 -15
- package/src/audio/loudness.ts +65 -20
- package/src/audio/preview.ts +17 -1
- package/src/audio/resample.ts +285 -0
- package/src/audio/resonators.ts +295 -0
- package/src/audio/sampler.ts +328 -35
- package/src/audio/samples.ts +56 -8
- package/src/audio/strings/body.ts +263 -0
- package/src/audio/strings/bow.ts +656 -0
- package/src/audio/strings/engine.ts +615 -0
- package/src/audio/strings/loop.ts +119 -0
- package/src/audio/strings/measure.test-helpers.ts +200 -0
- package/src/audio/strings/pluck.ts +354 -0
- package/src/audio/warp.ts +61 -0
- package/src/audio/wav.ts +191 -19
- package/src/audio/winds/engine.ts +302 -0
- package/src/audio/winds/filters.ts +153 -0
- package/src/audio/winds/pitch.ts +104 -0
- package/src/audio/winds/trim.ts +80 -0
- package/src/audio/winds/trims.ts +917 -0
- package/src/audio/winds/voice.ts +479 -0
- package/src/commands/expression.ts +111 -23
- package/src/commands/fit.ts +135 -0
- package/src/commands/fx.ts +35 -1
- package/src/commands/granular.ts +432 -0
- package/src/commands/help.ts +159 -5
- package/src/commands/keys.ts +629 -0
- package/src/commands/modal.ts +310 -0
- package/src/commands/resample.ts +281 -0
- package/src/commands/rig.ts +264 -0
- package/src/commands/sample.ts +52 -2
- package/src/commands/shift.ts +119 -0
- package/src/commands/string.ts +201 -0
- package/src/commands/strum.ts +473 -0
- package/src/commands/time.ts +4 -1
- package/src/commands/wind.ts +244 -0
- package/src/main.ts +339 -15
- package/src/project/check.ts +17 -0
- package/src/render.ts +49 -4
- package/src/session/presence.ts +16 -3
- package/src/tui/audition.ts +2 -2
- package/src/tui/granular-menu.ts +278 -0
- package/src/tui/menu.ts +934 -9
- package/src/tui/modal-menu.ts +145 -0
- package/src/tui/performance-menu.ts +42 -0
- package/src/tui/play-chords.ts +104 -3
- package/src/tui/play-mode.ts +1 -0
- package/src/tui/play-session.ts +70 -2
- package/src/tui/wind-menu.ts +144 -0
package/core/fx.ts
CHANGED
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
* under `track.fx.<name>` with every parameter stored once enabled.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
import { MODAL_LANE_PARAMS, WIND_LANE_PARAMS } from "./resonators.ts";
|
|
15
16
|
import { SYNTH_LANE_PARAMS } from "./synth.ts";
|
|
17
|
+
import { STRING_LANE_PARAMS } from "./strings.ts";
|
|
18
|
+
import { KEYS_LANE_PARAMS } from "./keys.ts";
|
|
19
|
+
import { GRANULAR_LANE_PARAMS } from "./granular.ts";
|
|
16
20
|
import {
|
|
17
21
|
FxValidationError,
|
|
18
22
|
normalizeParam,
|
|
@@ -48,9 +52,20 @@ export const FX_CHAIN = Object.freeze([
|
|
|
48
52
|
"vowel",
|
|
49
53
|
"crush",
|
|
50
54
|
"distort",
|
|
55
|
+
// 0.6 guitar rig (stomp box, amp head with noise gate, speaker cabinet).
|
|
56
|
+
"stomp",
|
|
57
|
+
"head",
|
|
58
|
+
"cab",
|
|
59
|
+
// 0.6.1 shoegaze (src/audio/effects/gaze.ts): a held tremolo arm, the
|
|
60
|
+
// dominant note's feedback bloom, a volume swell; double (mono in, stereo
|
|
61
|
+
// out) runs where the chain turns stereo.
|
|
62
|
+
"wobble",
|
|
63
|
+
"bloom",
|
|
64
|
+
"swell",
|
|
51
65
|
"tremolo",
|
|
52
66
|
"compressor",
|
|
53
67
|
"pan",
|
|
68
|
+
"double",
|
|
54
69
|
"phaser",
|
|
55
70
|
"chorus",
|
|
56
71
|
"leslie",
|
|
@@ -323,6 +338,300 @@ export const FX_SPECS = Object.freeze({
|
|
|
323
338
|
},
|
|
324
339
|
},
|
|
325
340
|
},
|
|
341
|
+
stomp: {
|
|
342
|
+
label: "stomp box",
|
|
343
|
+
doc: "guitar pedal before the amp: fuzz (Big Muff), face (Fuzz Face), od (Tube Screamer), rat, octave (Octavia); oversampled",
|
|
344
|
+
simple: ["type", "gain", "tone", "level"],
|
|
345
|
+
strudel: "(no Strudel equivalent; dawg guitar rig)",
|
|
346
|
+
params: {
|
|
347
|
+
type: {
|
|
348
|
+
kind: "enum",
|
|
349
|
+
values: ["fuzz", "face", "od", "rat", "octave"],
|
|
350
|
+
default: "od",
|
|
351
|
+
doc: "circuit: fuzz (Big Muff), face (Fuzz Face), od (Tube Screamer), rat (RAT), octave (Octavia)",
|
|
352
|
+
},
|
|
353
|
+
gain: {
|
|
354
|
+
kind: "number",
|
|
355
|
+
min: 0,
|
|
356
|
+
max: 10,
|
|
357
|
+
default: 5,
|
|
358
|
+
step: 0.25,
|
|
359
|
+
automate: true,
|
|
360
|
+
doc: "the pedal's gain / sustain / distortion knob, 0..10",
|
|
361
|
+
},
|
|
362
|
+
tone: {
|
|
363
|
+
kind: "number",
|
|
364
|
+
min: 0,
|
|
365
|
+
max: 1,
|
|
366
|
+
default: 0.5,
|
|
367
|
+
step: 0.05,
|
|
368
|
+
automate: true,
|
|
369
|
+
doc: "dark 0 .. bright 1",
|
|
370
|
+
},
|
|
371
|
+
level: {
|
|
372
|
+
kind: "number",
|
|
373
|
+
min: -24,
|
|
374
|
+
max: 12,
|
|
375
|
+
default: 0,
|
|
376
|
+
step: 0.5,
|
|
377
|
+
unit: "dB",
|
|
378
|
+
doc: "trim over a level-matched pedal (0 = bypass loudness)",
|
|
379
|
+
},
|
|
380
|
+
octave: {
|
|
381
|
+
kind: "number",
|
|
382
|
+
min: 0,
|
|
383
|
+
max: 1,
|
|
384
|
+
default: 0.7,
|
|
385
|
+
step: 0.05,
|
|
386
|
+
doc: "octave: blend of the octave-up (rectified) path",
|
|
387
|
+
},
|
|
388
|
+
mix: mix(1),
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
head: {
|
|
392
|
+
label: "amp head",
|
|
393
|
+
doc: "guitar amp: preamp stages, Yeh-Smith tone stack, power amp with sag, level-matched across types; optional noise gate",
|
|
394
|
+
simple: ["type", "gain", "bass", "mid", "treble"],
|
|
395
|
+
strudel: "(no Strudel equivalent; Strudel amp is linear gain)",
|
|
396
|
+
params: {
|
|
397
|
+
type: {
|
|
398
|
+
kind: "enum",
|
|
399
|
+
values: ["clean", "chime", "crunch", "lead", "high", "solid", "bass"],
|
|
400
|
+
default: "crunch",
|
|
401
|
+
doc: "clean (blackface), chime (AC30), crunch (plexi), lead (JCM800), high (modern high gain), solid (JC-120), bass (SVT)",
|
|
402
|
+
},
|
|
403
|
+
gain: {
|
|
404
|
+
kind: "number",
|
|
405
|
+
min: 0,
|
|
406
|
+
max: 10,
|
|
407
|
+
default: 5,
|
|
408
|
+
step: 0.25,
|
|
409
|
+
automate: true,
|
|
410
|
+
doc: "preamp gain 0..10 (level-matched: more gain, not more volume)",
|
|
411
|
+
},
|
|
412
|
+
bass: {
|
|
413
|
+
kind: "number",
|
|
414
|
+
min: 0,
|
|
415
|
+
max: 10,
|
|
416
|
+
default: 5,
|
|
417
|
+
step: 0.5,
|
|
418
|
+
doc: "tone stack bass 0..10",
|
|
419
|
+
},
|
|
420
|
+
mid: {
|
|
421
|
+
kind: "number",
|
|
422
|
+
min: 0,
|
|
423
|
+
max: 10,
|
|
424
|
+
default: 5,
|
|
425
|
+
step: 0.5,
|
|
426
|
+
doc: "tone stack mid 0..10",
|
|
427
|
+
},
|
|
428
|
+
treble: {
|
|
429
|
+
kind: "number",
|
|
430
|
+
min: 0,
|
|
431
|
+
max: 10,
|
|
432
|
+
default: 5,
|
|
433
|
+
step: 0.5,
|
|
434
|
+
doc: "tone stack treble 0..10",
|
|
435
|
+
},
|
|
436
|
+
presence: {
|
|
437
|
+
kind: "number",
|
|
438
|
+
min: 0,
|
|
439
|
+
max: 10,
|
|
440
|
+
default: 5,
|
|
441
|
+
step: 0.5,
|
|
442
|
+
doc: "power amp presence 0..10 (5 flat)",
|
|
443
|
+
},
|
|
444
|
+
master: {
|
|
445
|
+
kind: "number",
|
|
446
|
+
min: 0,
|
|
447
|
+
max: 10,
|
|
448
|
+
default: 5,
|
|
449
|
+
step: 0.25,
|
|
450
|
+
automate: true,
|
|
451
|
+
doc: "power amp drive 0..10",
|
|
452
|
+
},
|
|
453
|
+
sag: {
|
|
454
|
+
kind: "number",
|
|
455
|
+
min: 0,
|
|
456
|
+
max: 1,
|
|
457
|
+
default: 0.3,
|
|
458
|
+
step: 0.05,
|
|
459
|
+
optional: true,
|
|
460
|
+
doc: "power supply sag 0..1 (absent: the head type's own)",
|
|
461
|
+
},
|
|
462
|
+
gate: {
|
|
463
|
+
kind: "number",
|
|
464
|
+
min: -96,
|
|
465
|
+
max: 0,
|
|
466
|
+
default: -60,
|
|
467
|
+
step: 1,
|
|
468
|
+
unit: "dB",
|
|
469
|
+
optional: true,
|
|
470
|
+
doc: "noise gate threshold before the amp (absent: no gate)",
|
|
471
|
+
},
|
|
472
|
+
level: {
|
|
473
|
+
kind: "number",
|
|
474
|
+
min: -24,
|
|
475
|
+
max: 12,
|
|
476
|
+
default: 0,
|
|
477
|
+
step: 0.5,
|
|
478
|
+
unit: "dB",
|
|
479
|
+
doc: "output level after level matching",
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
cab: {
|
|
484
|
+
label: "speaker cabinet",
|
|
485
|
+
doc: "speaker cabinet and microphone (biquad model, no impulse response)",
|
|
486
|
+
simple: ["type", "mic", "mix"],
|
|
487
|
+
strudel: "(no Strudel equivalent; dawg guitar rig)",
|
|
488
|
+
params: {
|
|
489
|
+
type: {
|
|
490
|
+
kind: "enum",
|
|
491
|
+
values: ["1x12", "2x12", "4x12", "1x10", "open", "8x10", "1x15", "di"],
|
|
492
|
+
default: "2x12",
|
|
493
|
+
doc: "1x12, 2x12, 4x12, 1x10, open back, 8x10 and 1x15 bass, di (no speaker)",
|
|
494
|
+
},
|
|
495
|
+
mic: {
|
|
496
|
+
kind: "number",
|
|
497
|
+
min: 0,
|
|
498
|
+
max: 1,
|
|
499
|
+
default: 0.3,
|
|
500
|
+
step: 0.05,
|
|
501
|
+
doc: "microphone position: 0 centre (bright) .. 1 edge (dark)",
|
|
502
|
+
},
|
|
503
|
+
mix: mix(1),
|
|
504
|
+
},
|
|
505
|
+
},
|
|
506
|
+
wobble: {
|
|
507
|
+
label: "wobble",
|
|
508
|
+
doc: "held tremolo arm: slow seeded pitch wow and flutter of the whole track",
|
|
509
|
+
simple: ["depth", "rate", "drift"],
|
|
510
|
+
strudel: "(no Strudel equivalent; dawg shoegaze)",
|
|
511
|
+
params: {
|
|
512
|
+
depth: {
|
|
513
|
+
kind: "number",
|
|
514
|
+
min: 0,
|
|
515
|
+
max: 100,
|
|
516
|
+
default: 20,
|
|
517
|
+
step: 1,
|
|
518
|
+
unit: "c",
|
|
519
|
+
automate: true,
|
|
520
|
+
doc: "peak pitch deviation in cents",
|
|
521
|
+
},
|
|
522
|
+
rate: {
|
|
523
|
+
kind: "number",
|
|
524
|
+
min: 0.05,
|
|
525
|
+
max: 8,
|
|
526
|
+
default: 0.5,
|
|
527
|
+
step: 0.05,
|
|
528
|
+
unit: "Hz",
|
|
529
|
+
doc: "wobble rate",
|
|
530
|
+
},
|
|
531
|
+
drift: {
|
|
532
|
+
kind: "number",
|
|
533
|
+
min: 0,
|
|
534
|
+
max: 1,
|
|
535
|
+
default: 0.3,
|
|
536
|
+
step: 0.05,
|
|
537
|
+
doc: "0 periodic .. 1 wandering (seeded, so every render matches)",
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
bloom: {
|
|
542
|
+
label: "bloom",
|
|
543
|
+
doc: "feedback bloom: the top held note grows a singing harmonic",
|
|
544
|
+
simple: ["amount", "harm", "delay"],
|
|
545
|
+
strudel: "(no Strudel equivalent; dawg shoegaze)",
|
|
546
|
+
params: {
|
|
547
|
+
amount: {
|
|
548
|
+
kind: "number",
|
|
549
|
+
min: 0,
|
|
550
|
+
max: 1,
|
|
551
|
+
default: 0.5,
|
|
552
|
+
step: 0.05,
|
|
553
|
+
doc: "level of the feedback partial",
|
|
554
|
+
},
|
|
555
|
+
harm: {
|
|
556
|
+
kind: "number",
|
|
557
|
+
min: 1,
|
|
558
|
+
max: 4,
|
|
559
|
+
default: 2,
|
|
560
|
+
step: 1,
|
|
561
|
+
doc: "harmonic that feeds back: 1 the note, 2 its octave, 3 the fifth above",
|
|
562
|
+
},
|
|
563
|
+
delay: {
|
|
564
|
+
kind: "number",
|
|
565
|
+
min: 0,
|
|
566
|
+
max: 4,
|
|
567
|
+
default: 0.6,
|
|
568
|
+
step: 0.05,
|
|
569
|
+
unit: "s",
|
|
570
|
+
doc: "how long a note is held before it starts to feed back",
|
|
571
|
+
},
|
|
572
|
+
time: {
|
|
573
|
+
kind: "number",
|
|
574
|
+
min: 0.05,
|
|
575
|
+
max: 4,
|
|
576
|
+
default: 1,
|
|
577
|
+
step: 0.05,
|
|
578
|
+
unit: "s",
|
|
579
|
+
doc: "how fast the feedback grows",
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
swell: {
|
|
584
|
+
label: "swell",
|
|
585
|
+
doc: "volume swell: each strum fades in, no pick attack",
|
|
586
|
+
simple: ["time", "mix"],
|
|
587
|
+
strudel: "(no Strudel equivalent; dawg shoegaze)",
|
|
588
|
+
params: {
|
|
589
|
+
time: {
|
|
590
|
+
kind: "number",
|
|
591
|
+
min: 0.01,
|
|
592
|
+
max: 4,
|
|
593
|
+
default: 0.4,
|
|
594
|
+
step: 0.01,
|
|
595
|
+
unit: "s",
|
|
596
|
+
doc: "rise time after each onset",
|
|
597
|
+
},
|
|
598
|
+
mix: mix(1),
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
double: {
|
|
602
|
+
label: "double",
|
|
603
|
+
doc: "automatic double tracking: a seeded second take, spread left and right",
|
|
604
|
+
simple: ["time", "drift", "width"],
|
|
605
|
+
strudel: "(no Strudel equivalent; dawg shoegaze)",
|
|
606
|
+
params: {
|
|
607
|
+
time: {
|
|
608
|
+
kind: "number",
|
|
609
|
+
min: 5,
|
|
610
|
+
max: 60,
|
|
611
|
+
default: 22,
|
|
612
|
+
step: 1,
|
|
613
|
+
unit: "ms",
|
|
614
|
+
doc: "how late the second take plays",
|
|
615
|
+
},
|
|
616
|
+
drift: {
|
|
617
|
+
kind: "number",
|
|
618
|
+
min: 0,
|
|
619
|
+
max: 10,
|
|
620
|
+
default: 3,
|
|
621
|
+
step: 0.5,
|
|
622
|
+
unit: "ms",
|
|
623
|
+
doc: "how far the second take's timing wanders",
|
|
624
|
+
},
|
|
625
|
+
width: {
|
|
626
|
+
kind: "number",
|
|
627
|
+
min: 0,
|
|
628
|
+
max: 1,
|
|
629
|
+
default: 0.6,
|
|
630
|
+
step: 0.05,
|
|
631
|
+
doc: "0 centred .. 1 the two takes hard left and right",
|
|
632
|
+
},
|
|
633
|
+
},
|
|
634
|
+
},
|
|
326
635
|
tremolo: {
|
|
327
636
|
label: "tremolo",
|
|
328
637
|
doc: "volume modulation",
|
|
@@ -834,6 +1143,14 @@ export const CORE_EFFECTS: readonly EffectName[] = Object.freeze([
|
|
|
834
1143
|
"reverb",
|
|
835
1144
|
] as const);
|
|
836
1145
|
|
|
1146
|
+
/** The 0.6.1 shoegaze stages (Effects > Shoegaze), in chain order. */
|
|
1147
|
+
export const SHOEGAZE_EFFECTS: readonly EffectName[] = Object.freeze([
|
|
1148
|
+
"wobble",
|
|
1149
|
+
"bloom",
|
|
1150
|
+
"swell",
|
|
1151
|
+
"double",
|
|
1152
|
+
] as const);
|
|
1153
|
+
|
|
837
1154
|
export function isEffectName(value: unknown): value is EffectName {
|
|
838
1155
|
return (
|
|
839
1156
|
typeof value === "string" &&
|
|
@@ -903,12 +1220,21 @@ export function normalizeFx(input: unknown): TrackFx | undefined {
|
|
|
903
1220
|
}
|
|
904
1221
|
|
|
905
1222
|
/** Automation lane name of an `fx` parameter, e.g. `distort-drive`. */
|
|
906
|
-
export type FxLane =
|
|
1223
|
+
export type FxLane =
|
|
1224
|
+
`${FxName | "reverb" | "synth" | "string" | "keys" | "modal" | "grain" | "wind"}-${string}`;
|
|
907
1225
|
|
|
908
1226
|
/** Every automatable `fx` parameter as `{ lane, effect, param, spec }`. */
|
|
909
1227
|
export const FX_LANES: readonly Readonly<{
|
|
910
1228
|
lane: FxLane;
|
|
911
|
-
effect:
|
|
1229
|
+
effect:
|
|
1230
|
+
| FxName
|
|
1231
|
+
| "reverb"
|
|
1232
|
+
| "synth"
|
|
1233
|
+
| "string"
|
|
1234
|
+
| "keys"
|
|
1235
|
+
| "modal"
|
|
1236
|
+
| "grain"
|
|
1237
|
+
| "wind";
|
|
912
1238
|
param: string;
|
|
913
1239
|
spec: NumberParam;
|
|
914
1240
|
}>[] = Object.freeze([
|
|
@@ -936,6 +1262,53 @@ export const FX_LANES: readonly Readonly<{
|
|
|
936
1262
|
spec,
|
|
937
1263
|
}),
|
|
938
1264
|
),
|
|
1265
|
+
// String engine parameters (core/strings.ts), read at each note's onset.
|
|
1266
|
+
...STRING_LANE_PARAMS.map(({ param, spec }) =>
|
|
1267
|
+
Object.freeze({
|
|
1268
|
+
lane: `string-${param}` as FxLane,
|
|
1269
|
+
effect: "string" as const,
|
|
1270
|
+
param,
|
|
1271
|
+
spec,
|
|
1272
|
+
}),
|
|
1273
|
+
),
|
|
1274
|
+
// Modelled piano parameters (core/keys.ts), read at each note's onset.
|
|
1275
|
+
...KEYS_LANE_PARAMS.map(({ param, spec }) =>
|
|
1276
|
+
Object.freeze({
|
|
1277
|
+
lane: `keys-${param}` as FxLane,
|
|
1278
|
+
effect: "keys" as const,
|
|
1279
|
+
param,
|
|
1280
|
+
spec,
|
|
1281
|
+
}),
|
|
1282
|
+
),
|
|
1283
|
+
// Modal parameters (core/resonators.ts), read at each note's onset
|
|
1284
|
+
// (`modal-motordepth` every 32-sample control tick).
|
|
1285
|
+
...MODAL_LANE_PARAMS.map(({ param, spec }) =>
|
|
1286
|
+
Object.freeze({
|
|
1287
|
+
lane: `modal-${param}` as FxLane,
|
|
1288
|
+
effect: "modal" as const,
|
|
1289
|
+
param,
|
|
1290
|
+
spec,
|
|
1291
|
+
}),
|
|
1292
|
+
),
|
|
1293
|
+
// Granular parameters (core/granular.ts, 0.6.1), read every 32 frames;
|
|
1294
|
+
// grain-scoped ones are latched at each grain's onset.
|
|
1295
|
+
...GRANULAR_LANE_PARAMS.map(({ param, spec }) =>
|
|
1296
|
+
Object.freeze({
|
|
1297
|
+
lane: `grain-${param}` as FxLane,
|
|
1298
|
+
effect: "grain" as const,
|
|
1299
|
+
param,
|
|
1300
|
+
spec,
|
|
1301
|
+
}),
|
|
1302
|
+
),
|
|
1303
|
+
// Wind parameters (core/winds.ts), read every 32-sample control tick.
|
|
1304
|
+
...WIND_LANE_PARAMS.map(({ param, spec }) =>
|
|
1305
|
+
Object.freeze({
|
|
1306
|
+
lane: `wind-${param}` as FxLane,
|
|
1307
|
+
effect: "wind" as const,
|
|
1308
|
+
param,
|
|
1309
|
+
spec,
|
|
1310
|
+
}),
|
|
1311
|
+
),
|
|
939
1312
|
]);
|
|
940
1313
|
|
|
941
1314
|
/**
|
|
@@ -973,6 +1346,14 @@ export const FX_PRESETS: Readonly<
|
|
|
973
1346
|
cutoff: 400,
|
|
974
1347
|
resonance: 0.4,
|
|
975
1348
|
},
|
|
1349
|
+
wah: {
|
|
1350
|
+
type: "bpf",
|
|
1351
|
+
sync: 0.5,
|
|
1352
|
+
depth: 2,
|
|
1353
|
+
shape: "sine",
|
|
1354
|
+
cutoff: 700,
|
|
1355
|
+
resonance: 0.6,
|
|
1356
|
+
},
|
|
976
1357
|
},
|
|
977
1358
|
vowel: { a: { vowel: "a" }, o: { vowel: "o" }, ee: { vowel: "i" } },
|
|
978
1359
|
crush: {
|
|
@@ -987,6 +1368,50 @@ export const FX_PRESETS: Readonly<
|
|
|
987
1368
|
fold: { drive: 5, type: "fold", tone: 7000, mix: 0.7 },
|
|
988
1369
|
shape: { drive: 5, type: "shape", tone: 9000 },
|
|
989
1370
|
},
|
|
1371
|
+
stomp: {
|
|
1372
|
+
muff: { type: "fuzz", gain: 7, tone: 0.5 },
|
|
1373
|
+
face: { type: "face", gain: 6, tone: 0.5 },
|
|
1374
|
+
screamer: { type: "od", gain: 5, tone: 0.5, level: 3 },
|
|
1375
|
+
rat: { type: "rat", gain: 6, tone: 0.4 },
|
|
1376
|
+
octavia: { type: "octave", gain: 6, tone: 0.6, octave: 0.8 },
|
|
1377
|
+
boost: { type: "od", gain: 0, tone: 0.6, level: 6 },
|
|
1378
|
+
},
|
|
1379
|
+
head: {
|
|
1380
|
+
blackface: { type: "clean", gain: 3, treble: 6 },
|
|
1381
|
+
ac: { type: "chime", gain: 4, treble: 6 },
|
|
1382
|
+
plexi: { type: "crunch", gain: 5 },
|
|
1383
|
+
lead: { type: "lead", gain: 6, mid: 6 },
|
|
1384
|
+
modern: { type: "high", gain: 7, bass: 6, mid: 3, treble: 7, gate: -55 },
|
|
1385
|
+
jc: { type: "solid", gain: 2, treble: 6 },
|
|
1386
|
+
svt: { type: "bass", gain: 4, bass: 6 },
|
|
1387
|
+
},
|
|
1388
|
+
cab: {
|
|
1389
|
+
"1x12": { type: "1x12" },
|
|
1390
|
+
"2x12": { type: "2x12" },
|
|
1391
|
+
"4x12": { type: "4x12" },
|
|
1392
|
+
open: { type: "open" },
|
|
1393
|
+
"8x10": { type: "8x10" },
|
|
1394
|
+
"1x15": { type: "1x15" },
|
|
1395
|
+
di: { type: "di" },
|
|
1396
|
+
},
|
|
1397
|
+
wobble: {
|
|
1398
|
+
glide: { depth: 35, rate: 0.4, drift: 0.4 },
|
|
1399
|
+
tape: { depth: 12, rate: 0.8, drift: 0.6 },
|
|
1400
|
+
seasick: { depth: 60, rate: 0.25, drift: 0.2 },
|
|
1401
|
+
},
|
|
1402
|
+
bloom: {
|
|
1403
|
+
octave: { amount: 0.5, harm: 2, delay: 0.6, time: 1 },
|
|
1404
|
+
fifth: { amount: 0.4, harm: 3, delay: 0.8, time: 1.2 },
|
|
1405
|
+
ebow: { amount: 0.8, harm: 1, delay: 0.1, time: 0.6 },
|
|
1406
|
+
},
|
|
1407
|
+
swell: {
|
|
1408
|
+
slow: { time: 0.8 },
|
|
1409
|
+
violin: { time: 0.3 },
|
|
1410
|
+
},
|
|
1411
|
+
double: {
|
|
1412
|
+
tight: { time: 12, drift: 1.5, width: 0.5 },
|
|
1413
|
+
wide: { time: 28, drift: 3, width: 0.7 },
|
|
1414
|
+
},
|
|
990
1415
|
tremolo: {
|
|
991
1416
|
gentle: { sync: 1, depth: 0.3, shape: "sine" },
|
|
992
1417
|
"eighth-chop": { sync: 0.5, depth: 0.9, shape: "square" },
|
|
@@ -1060,6 +1485,268 @@ export const FX_PRESETS: Readonly<
|
|
|
1060
1485
|
},
|
|
1061
1486
|
} satisfies Partial<Record<EffectName, Record<string, FxValues>>>);
|
|
1062
1487
|
|
|
1488
|
+
/** The guitar rig stages, in chain order: what `rig reset` clears. */
|
|
1489
|
+
export const RIG_STAGES = Object.freeze(["stomp", "head", "cab"] as const);
|
|
1490
|
+
export type RigStage = (typeof RIG_STAGES)[number];
|
|
1491
|
+
|
|
1492
|
+
/**
|
|
1493
|
+
* Whole guitar rigs (`rig <name>`): each sets the rig stages it names over
|
|
1494
|
+
* their defaults and clears the ones it leaves out. A few also set one
|
|
1495
|
+
* other effect (a wah, a spring-like reverb, a chorus) the sound needs.
|
|
1496
|
+
*/
|
|
1497
|
+
export const RIG_PRESETS: Readonly<
|
|
1498
|
+
Record<string, Readonly<Partial<Record<FxName | "reverb", FxValues>>>>
|
|
1499
|
+
> = Object.freeze({
|
|
1500
|
+
clean: {
|
|
1501
|
+
head: { type: "clean", gain: 3, treble: 6 },
|
|
1502
|
+
cab: { type: "1x12" },
|
|
1503
|
+
},
|
|
1504
|
+
crunch: { head: { type: "crunch", gain: 5 }, cab: { type: "4x12" } },
|
|
1505
|
+
punk: {
|
|
1506
|
+
head: { type: "crunch", gain: 7, mid: 6, master: 6 },
|
|
1507
|
+
cab: { type: "4x12", mic: 0.2 },
|
|
1508
|
+
},
|
|
1509
|
+
ragged: {
|
|
1510
|
+
stomp: { type: "face", gain: 6, tone: 0.6 },
|
|
1511
|
+
head: { type: "chime", gain: 4 },
|
|
1512
|
+
cab: { type: "2x12" },
|
|
1513
|
+
},
|
|
1514
|
+
lead: {
|
|
1515
|
+
stomp: { type: "od", gain: 3, tone: 0.5, level: 3 },
|
|
1516
|
+
head: { type: "lead", gain: 6, mid: 6 },
|
|
1517
|
+
cab: { type: "4x12" },
|
|
1518
|
+
},
|
|
1519
|
+
metal: {
|
|
1520
|
+
stomp: { type: "od", gain: 0, tone: 0.6, level: 6 },
|
|
1521
|
+
head: { type: "high", gain: 7, bass: 6, mid: 3, treble: 7, gate: -55 },
|
|
1522
|
+
cab: { type: "4x12", mic: 0.2 },
|
|
1523
|
+
},
|
|
1524
|
+
fuzz: {
|
|
1525
|
+
stomp: { type: "fuzz", gain: 7, tone: 0.5 },
|
|
1526
|
+
head: { type: "clean", gain: 4 },
|
|
1527
|
+
cab: { type: "2x12" },
|
|
1528
|
+
},
|
|
1529
|
+
octave: {
|
|
1530
|
+
stomp: { type: "octave", gain: 6, tone: 0.6, octave: 0.8 },
|
|
1531
|
+
head: { type: "clean", gain: 3 },
|
|
1532
|
+
cab: { type: "1x12" },
|
|
1533
|
+
},
|
|
1534
|
+
funk: {
|
|
1535
|
+
head: { type: "clean", gain: 2, treble: 7, presence: 6 },
|
|
1536
|
+
cab: { type: "2x12" },
|
|
1537
|
+
autofilter: {
|
|
1538
|
+
type: "bpf",
|
|
1539
|
+
sync: 0,
|
|
1540
|
+
rate: 0.01,
|
|
1541
|
+
depth: 0,
|
|
1542
|
+
follow: 3,
|
|
1543
|
+
cutoff: 500,
|
|
1544
|
+
resonance: 0.6,
|
|
1545
|
+
},
|
|
1546
|
+
},
|
|
1547
|
+
wah: {
|
|
1548
|
+
head: { type: "crunch", gain: 4 },
|
|
1549
|
+
cab: { type: "2x12" },
|
|
1550
|
+
autofilter: {
|
|
1551
|
+
type: "bpf",
|
|
1552
|
+
sync: 0.5,
|
|
1553
|
+
depth: 2,
|
|
1554
|
+
shape: "sine",
|
|
1555
|
+
cutoff: 700,
|
|
1556
|
+
resonance: 0.6,
|
|
1557
|
+
},
|
|
1558
|
+
},
|
|
1559
|
+
bachata: {
|
|
1560
|
+
head: { type: "clean", gain: 2, mid: 6, treble: 7 },
|
|
1561
|
+
cab: { type: "1x12", mic: 0.2 },
|
|
1562
|
+
chorus: { rate: 0.8, depth: 0.25, mix: 0.3 },
|
|
1563
|
+
},
|
|
1564
|
+
spring: {
|
|
1565
|
+
head: { type: "clean", gain: 3, treble: 6 },
|
|
1566
|
+
cab: { type: "open" },
|
|
1567
|
+
reverb: { mix: 0.3, size: 0.35, fade: 1.5, predelay: 0, dim: 3500 },
|
|
1568
|
+
},
|
|
1569
|
+
bassdrive: {
|
|
1570
|
+
stomp: { type: "od", gain: 4, tone: 0.5, mix: 0.6 },
|
|
1571
|
+
head: { type: "bass", gain: 4 },
|
|
1572
|
+
cab: { type: "8x10" },
|
|
1573
|
+
},
|
|
1574
|
+
reese: {
|
|
1575
|
+
stomp: { type: "rat", gain: 3, tone: 0.3, mix: 0.5 },
|
|
1576
|
+
head: { type: "bass", gain: 6, master: 6 },
|
|
1577
|
+
cab: { type: "1x15" },
|
|
1578
|
+
},
|
|
1579
|
+
jangle: {
|
|
1580
|
+
head: { type: "chime", gain: 3, treble: 7 },
|
|
1581
|
+
cab: { type: "2x12", mic: 0.2 },
|
|
1582
|
+
compressor: { threshold: -20, ratio: 4, attack: 0.01, release: 0.15 },
|
|
1583
|
+
// 0.6.1: the double-tracked jangle of a twelve-string record. A stored
|
|
1584
|
+
// 0.6.0 jangle (no double) keeps rendering as it was; `rig jangle` again
|
|
1585
|
+
// adds it. The SDK's rig("jangle") keeps its 0.6.0 stages (see
|
|
1586
|
+
// RIG_ADDED), so the printer writes this double out explicitly.
|
|
1587
|
+
double: { time: 12, drift: 1.5, width: 0.5 },
|
|
1588
|
+
},
|
|
1589
|
+
alt: {
|
|
1590
|
+
stomp: { type: "rat", gain: 6, tone: 0.4 },
|
|
1591
|
+
head: { type: "crunch", gain: 4 },
|
|
1592
|
+
cab: { type: "4x12" },
|
|
1593
|
+
},
|
|
1594
|
+
// 0.6.1 shoegaze rigs (src/audio/effects/gaze.ts): fuzz into a held
|
|
1595
|
+
// tremolo arm, a feedback bloom, doubled takes and a long wash.
|
|
1596
|
+
shoegaze: {
|
|
1597
|
+
stomp: { type: "fuzz", gain: 7, tone: 0.45 },
|
|
1598
|
+
head: { type: "chime", gain: 4 },
|
|
1599
|
+
cab: { type: "2x12" },
|
|
1600
|
+
wobble: { depth: 25, rate: 0.4, drift: 0.4 },
|
|
1601
|
+
bloom: { amount: 0.4, harm: 2, delay: 0.6, time: 1 },
|
|
1602
|
+
double: { time: 18, drift: 2.5, width: 0.4 },
|
|
1603
|
+
postgain: { gain: 0.86 },
|
|
1604
|
+
reverb: { mix: 0.35, size: 0.9, fade: 4, predelay: 0.02, dim: 6000 },
|
|
1605
|
+
},
|
|
1606
|
+
glide: {
|
|
1607
|
+
stomp: { type: "face", gain: 5, tone: 0.5 },
|
|
1608
|
+
head: { type: "clean", gain: 4, treble: 5 },
|
|
1609
|
+
cab: { type: "2x12" },
|
|
1610
|
+
wobble: { depth: 35, rate: 0.4, drift: 0.4 },
|
|
1611
|
+
postgain: { gain: 0.8 },
|
|
1612
|
+
reverb: { mix: 0.35, size: 0.7, fade: 2.5, predelay: 0.01, dim: 6000 },
|
|
1613
|
+
},
|
|
1614
|
+
dreampop: {
|
|
1615
|
+
head: { type: "clean", gain: 2, treble: 6 },
|
|
1616
|
+
cab: { type: "1x12" },
|
|
1617
|
+
chorus: { rate: 0.6, depth: 0.3, mix: 0.35 },
|
|
1618
|
+
double: { time: 14, drift: 2, width: 0.4 },
|
|
1619
|
+
postgain: { gain: 1.45 },
|
|
1620
|
+
reverb: { mix: 0.4, size: 0.85, fade: 5, predelay: 0.03, dim: 7000 },
|
|
1621
|
+
},
|
|
1622
|
+
swell: {
|
|
1623
|
+
head: { type: "clean", gain: 3, treble: 5 },
|
|
1624
|
+
cab: { type: "1x12" },
|
|
1625
|
+
swell: { time: 0.5 },
|
|
1626
|
+
postgain: { gain: 1.25 },
|
|
1627
|
+
reverb: { mix: 0.45, size: 0.8, fade: 4, predelay: 0.02, dim: 6500 },
|
|
1628
|
+
},
|
|
1629
|
+
ebow: {
|
|
1630
|
+
stomp: { type: "od", gain: 3, tone: 0.5, level: 3 },
|
|
1631
|
+
head: { type: "lead", gain: 5, mid: 6 },
|
|
1632
|
+
cab: { type: "4x12" },
|
|
1633
|
+
swell: { time: 0.25 },
|
|
1634
|
+
bloom: { amount: 0.8, harm: 1, delay: 0.1, time: 0.6 },
|
|
1635
|
+
},
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
/**
|
|
1639
|
+
* Effects a rig preset gained after SDK `rig(name)` was fixed. `rig(name)`
|
|
1640
|
+
* leaves them out, so an older song.ts spreading `...rig("jangle")` keeps
|
|
1641
|
+
* its sound; the printer writes them next to the spread, and a stored
|
|
1642
|
+
* track with or without them still reads as that rig.
|
|
1643
|
+
*/
|
|
1644
|
+
export const RIG_ADDED: Readonly<Record<string, readonly FxName[]>> =
|
|
1645
|
+
Object.freeze({ jangle: Object.freeze(["double"] as FxName[]) });
|
|
1646
|
+
|
|
1647
|
+
export function isRigPreset(name: string): boolean {
|
|
1648
|
+
return Object.prototype.hasOwnProperty.call(RIG_PRESETS, name);
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
/**
|
|
1652
|
+
* `fx` with rig preset `name` applied: rig stages it leaves out are
|
|
1653
|
+
* removed, every stage it names is filled over its defaults, and other
|
|
1654
|
+
* effects keep their values. `"reset"` removes the rig. A preset's track
|
|
1655
|
+
* effect (the `spring` reverb) is not part of `fx`: see `rigReverb`.
|
|
1656
|
+
*/
|
|
1657
|
+
export function applyRigPreset(
|
|
1658
|
+
fx: TrackFx | undefined,
|
|
1659
|
+
name: string,
|
|
1660
|
+
): TrackFx | undefined {
|
|
1661
|
+
const out: Record<string, unknown> = { ...(fx ?? {}) };
|
|
1662
|
+
for (const stage of RIG_STAGES) delete out[stage];
|
|
1663
|
+
// A previous rig's companion (funk's envelope filter, jangle's
|
|
1664
|
+
// compressor…) goes with it, unless the user has changed it since.
|
|
1665
|
+
for (const [effect, values] of Object.entries(out))
|
|
1666
|
+
if (isRigCompanion(effect, values)) delete out[effect];
|
|
1667
|
+
if (name !== "reset") {
|
|
1668
|
+
if (!isRigPreset(name))
|
|
1669
|
+
throw new FxValidationError(
|
|
1670
|
+
`unknown rig "${name}" (rigs: ${Object.keys(RIG_PRESETS).join(", ")})`,
|
|
1671
|
+
);
|
|
1672
|
+
for (const [effect, values] of Object.entries(RIG_PRESETS[name]!))
|
|
1673
|
+
if (isFxName(effect)) out[effect] = values;
|
|
1674
|
+
}
|
|
1675
|
+
return normalizeFx(out);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
/** Normalized companion effects (not stages, not reverb) of every rig. */
|
|
1679
|
+
let companions: Map<string, string[]> | undefined;
|
|
1680
|
+
|
|
1681
|
+
/** True when `values` is exactly what some rig preset writes for `effect`. */
|
|
1682
|
+
function isRigCompanion(effect: string, values: unknown): boolean {
|
|
1683
|
+
if ((RIG_STAGES as readonly string[]).includes(effect)) return false;
|
|
1684
|
+
if (!companions) {
|
|
1685
|
+
companions = new Map();
|
|
1686
|
+
for (const preset of Object.values(RIG_PRESETS))
|
|
1687
|
+
for (const [name, raw] of Object.entries(preset)) {
|
|
1688
|
+
if (!isFxName(name) || (RIG_STAGES as readonly string[]).includes(name))
|
|
1689
|
+
continue;
|
|
1690
|
+
const normal = normalizeFx({ [name]: raw })?.[name];
|
|
1691
|
+
const list = companions.get(name) ?? [];
|
|
1692
|
+
list.push(JSON.stringify(normal));
|
|
1693
|
+
companions.set(name, list);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
return companions.get(effect)?.includes(JSON.stringify(values)) ?? false;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
/** True when `reverb` is exactly the track reverb some rig preset sets. */
|
|
1700
|
+
export function isRigReverb(reverb: unknown): boolean {
|
|
1701
|
+
if (reverb === undefined) return false;
|
|
1702
|
+
const json = JSON.stringify(reverb);
|
|
1703
|
+
return Object.keys(RIG_PRESETS).some(
|
|
1704
|
+
(name) => rigReverb(name) && JSON.stringify(rigReverb(name)) === json,
|
|
1705
|
+
);
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
/** The track reverb a rig preset sets (spring), if any. */
|
|
1709
|
+
export function rigReverb(name: string): FxValues | undefined {
|
|
1710
|
+
if (!isRigPreset(name)) return undefined;
|
|
1711
|
+
const values = (RIG_PRESETS[name] as Record<string, FxValues | undefined>)
|
|
1712
|
+
.reverb;
|
|
1713
|
+
return values === undefined
|
|
1714
|
+
? undefined
|
|
1715
|
+
: normalizeParams(TRACK_EFFECT_SPECS.reverb.params, values, "reverb");
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
/**
|
|
1719
|
+
* The rig preset whose stages and companion effects `fx` matches exactly,
|
|
1720
|
+
* if any (other effects may be present too).
|
|
1721
|
+
*/
|
|
1722
|
+
export function rigPresetOf(fx: TrackFx | undefined): string | undefined {
|
|
1723
|
+
if (!fx || !RIG_STAGES.some((stage) => fx[stage])) return undefined;
|
|
1724
|
+
for (const name of Object.keys(RIG_PRESETS)) {
|
|
1725
|
+
const want: Partial<Record<FxName, FxValues>> = {
|
|
1726
|
+
...(applyRigPreset(undefined, name) ?? {}),
|
|
1727
|
+
};
|
|
1728
|
+
for (const added of RIG_ADDED[name] ?? []) delete want[added];
|
|
1729
|
+
const keys = new Set<string>([...RIG_STAGES, ...Object.keys(want)]);
|
|
1730
|
+
if (
|
|
1731
|
+
[...keys].every(
|
|
1732
|
+
(key) =>
|
|
1733
|
+
JSON.stringify(fx[key as FxName]) ===
|
|
1734
|
+
JSON.stringify(want[key as FxName]),
|
|
1735
|
+
)
|
|
1736
|
+
)
|
|
1737
|
+
return name;
|
|
1738
|
+
}
|
|
1739
|
+
return undefined;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
/** The effects of `fx` a printed `...rig(name)` stands for. */
|
|
1743
|
+
export function rigPresetKeys(name: string): readonly FxName[] {
|
|
1744
|
+
const added = RIG_ADDED[name] ?? [];
|
|
1745
|
+
return Object.keys(RIG_PRESETS[name] ?? {}).filter(
|
|
1746
|
+
(key): key is FxName => isFxName(key) && !added.includes(key),
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1063
1750
|
export function effectPresetNames(effect: EffectName): readonly string[] {
|
|
1064
1751
|
return Object.keys(FX_PRESETS[effect] ?? {});
|
|
1065
1752
|
}
|