@hraness/dawg 0.6.1 → 0.8.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.
- package/CHANGELOG.md +118 -0
- package/DAWG.md +454 -140
- package/README.md +30 -26
- package/core/autotune.ts +1119 -0
- package/core/chords.ts +271 -23
- package/core/clips.ts +499 -0
- package/core/diff.ts +184 -104
- package/core/expression.ts +15 -0
- package/core/fx.ts +99 -29
- package/core/ids.ts +52 -0
- package/core/instruments.ts +19 -0
- package/core/keys.ts +3 -3
- package/core/loop.ts +8 -0
- package/core/lyrics.ts +297 -0
- package/core/master.ts +3 -3
- package/core/range.ts +581 -0
- package/core/resonators.ts +16 -2
- package/core/routing.ts +165 -0
- package/core/score.ts +955 -14
- package/core/sdk/eval-child.ts +7 -2
- package/core/sdk/eval.ts +35 -6
- package/core/sdk/print.ts +287 -5
- package/core/sdk/sync-lyrics.ts +49 -0
- package/core/sdk/v1.ts +2132 -46
- package/core/sections.ts +480 -35
- package/core/sing.ts +815 -0
- package/core/style-provenance.ts +80 -0
- package/core/styles/africa-mena-southasia.ts +2893 -0
- package/core/styles/americas.ts +3810 -0
- package/core/styles/art.ts +4993 -0
- package/core/styles/base.ts +123 -0
- package/core/styles/cycles.ts +106 -0
- package/core/styles/electronic.ts +2723 -0
- package/core/styles/europe-asia-pacific.ts +2838 -0
- package/core/styles/excerpt.ts +29 -0
- package/core/styles/gamelan.ts +283 -0
- package/core/styles/generate.ts +2199 -0
- package/core/styles/index.ts +515 -0
- package/core/styles/parts.ts +106 -0
- package/core/styles/pop.ts +3189 -0
- package/core/styles/rock.ts +2993 -0
- package/core/styles/roots.ts +4175 -0
- package/core/styles/schema.ts +429 -0
- package/core/styles/taxonomy.ts +940 -0
- package/core/styles/validate.ts +528 -0
- package/core/tempo.ts +32 -2
- package/core/tuning.ts +19 -3
- package/core/vocoder.ts +524 -0
- package/guides/agent.md +29 -0
- package/guides/arrange.md +31 -0
- package/guides/audio.md +31 -0
- package/guides/audition.md +20 -14
- package/guides/automation.md +12 -7
- package/guides/chords.md +15 -15
- package/guides/effects.md +17 -16
- package/guides/faders.md +20 -16
- package/guides/files.md +13 -9
- package/guides/getting-started.md +15 -11
- package/guides/keys.md +19 -15
- package/guides/media.md +17 -12
- package/guides/mix.md +15 -7
- package/guides/music.md +23 -8
- package/guides/notes.md +15 -9
- package/guides/panes.md +32 -0
- package/guides/performance.md +15 -13
- package/guides/play.md +21 -13
- package/guides/project.md +26 -8
- package/guides/providers.md +20 -14
- package/guides/resample.md +15 -9
- package/guides/rhythm.md +18 -13
- package/guides/sessions.md +17 -7
- package/guides/show-me.md +31 -0
- package/guides/sound.md +25 -9
- package/guides/sounds.md +15 -13
- package/guides/styles.md +31 -0
- package/guides/tape.md +32 -0
- package/guides/tempo.md +15 -10
- package/guides/tracks.md +15 -10
- package/guides/tuning.md +32 -0
- package/guides/voice.md +31 -0
- package/guides/web-search.md +18 -8
- package/native/prebuilt/darwin-arm64/libdawg_sink.dylib +0 -0
- package/native/prebuilt/darwin-x64/libdawg_sink.dylib +0 -0
- package/native/prebuilt/linux-arm64/libdawg_sink.so +0 -0
- package/native/prebuilt/linux-x64/libdawg_sink.so +0 -0
- package/native/prebuilt/manifest.json +21 -0
- package/package.json +6 -2
- package/src/agent/agent.ts +130 -18
- package/src/agent/calibration-tools.ts +53 -0
- package/src/agent/clip-tools.ts +453 -0
- package/src/agent/command-agent.ts +378 -0
- package/src/agent/drum-tools.ts +2 -2
- package/src/agent/expression-tools.ts +1 -1
- package/src/agent/gateway.ts +246 -60
- package/src/agent/models.ts +53 -12
- package/src/agent/ops.ts +27 -1
- package/src/agent/pack-tools.ts +1 -1
- package/src/agent/planner.ts +25 -0
- package/src/agent/portable-schema.ts +80 -0
- package/src/agent/preview-tool.ts +4 -1
- package/src/agent/provider.ts +22 -8
- package/src/agent/range-tools.ts +216 -0
- package/src/agent/rhythm-tools.ts +1 -1
- package/src/agent/section-tools.ts +1 -1
- package/src/agent/show-me.ts +497 -0
- package/src/agent/steer.ts +15 -0
- package/src/agent/style-tools.ts +217 -0
- package/src/agent/tool-error.ts +12 -0
- package/src/agent/tools.ts +110 -23
- package/src/agent/usage.ts +2 -2
- package/src/agent/voice-tools.ts +925 -0
- package/src/agent/xcb-agent.ts +13 -10
- package/src/argv.ts +38 -0
- package/src/audio/analysis.ts +253 -0
- package/src/audio/arrange.ts +49 -5
- package/src/audio/audio-command.ts +218 -0
- package/src/audio/autotune-engine.ts +101 -0
- package/src/audio/autotune.ts +640 -0
- package/src/audio/clips.ts +240 -0
- package/src/audio/devices.ts +264 -0
- package/src/audio/doctor.ts +157 -0
- package/src/audio/dsp/bandbank.ts +138 -0
- package/src/audio/dsp/envelope.ts +10 -0
- package/src/audio/dsp/follow.ts +120 -0
- package/src/audio/dsp/formant.ts +427 -0
- package/src/audio/dsp/glottal.ts +243 -0
- package/src/audio/dsp/interp.ts +7 -2
- package/src/audio/dsp/lpc.ts +50 -0
- package/src/audio/dsp/periodicity.ts +59 -0
- package/src/audio/dsp/pitch.ts +995 -0
- package/src/audio/dsp/psola.ts +199 -0
- package/src/audio/effects/chain.ts +3 -1
- package/src/audio/effects/common.ts +43 -0
- package/src/audio/effects/convolution.ts +7 -4
- package/src/audio/effects/filter.ts +48 -69
- package/src/audio/effects/formant.ts +263 -0
- package/src/audio/engine.ts +360 -40
- package/src/audio/fit.ts +35 -3
- package/src/audio/instrument-check.ts +59 -43
- package/src/audio/instruments.ts +4 -0
- package/src/audio/keys/calibration.ts +56 -0
- package/src/audio/keys/electric.ts +8 -1
- package/src/audio/keys/engine.ts +13 -1
- package/src/audio/keys/piano.ts +22 -2
- package/src/audio/kits.ts +135 -6
- package/src/audio/live.ts +114 -20
- package/src/audio/native.ts +615 -0
- package/src/audio/preview.ts +30 -2
- package/src/audio/render-worker.ts +2 -0
- package/src/audio/renderer.ts +2 -0
- package/src/audio/resample.ts +2 -1
- package/src/audio/sampler.ts +85 -4
- package/src/audio/samples.ts +20 -2
- package/src/audio/sing/analysis.ts +193 -0
- package/src/audio/sing/engine.ts +949 -0
- package/src/audio/strings/bow.ts +48 -5
- package/src/audio/strings/engine.ts +8 -2
- package/src/audio/synth/oscillators.ts +31 -21
- package/src/audio/synth/voice.ts +34 -1
- package/src/audio/vocoder/bank.ts +314 -0
- package/src/audio/vocoder/carrier.ts +165 -0
- package/src/audio/vocoder/control.ts +68 -0
- package/src/audio/vocoder/detect.ts +50 -0
- package/src/audio/vocoder/index.ts +304 -0
- package/src/audio/vocoder/talkbox.ts +143 -0
- package/src/audio/wav.ts +500 -77
- package/src/audio/winds/engine.ts +5 -1
- package/src/audio/winds/trim.ts +28 -4
- package/src/audio/winds/trims1.ts +297 -0
- package/src/audio/winds/voice.ts +15 -2
- package/src/auth/cli.ts +38 -36
- package/src/auth/credentials.ts +30 -1
- package/src/auth/login.ts +15 -9
- package/src/auth/tui.ts +19 -10
- package/src/commands/arrange.ts +82 -32
- package/src/commands/autotune.ts +421 -0
- package/src/commands/calibration.ts +74 -0
- package/src/commands/clips.ts +887 -0
- package/src/commands/drums.ts +3 -2
- package/src/commands/edit.ts +11 -4
- package/src/commands/expression.ts +1 -1
- package/src/commands/formant.ts +221 -0
- package/src/commands/fx.ts +101 -32
- package/src/commands/grammar.ts +560 -0
- package/src/commands/help.ts +774 -366
- package/src/commands/history.ts +139 -10
- package/src/commands/keys.ts +25 -8
- package/src/commands/modal.ts +1 -1
- package/src/commands/music.ts +1 -1
- package/src/commands/nearest.ts +53 -0
- package/src/commands/pack.ts +9 -2
- package/src/commands/param-range.ts +56 -0
- package/src/commands/parses.ts +135 -0
- package/src/commands/progression.ts +170 -0
- package/src/commands/range.ts +763 -0
- package/src/commands/resample.ts +13 -10
- package/src/commands/rhythm.ts +3 -0
- package/src/commands/rig.ts +3 -24
- package/src/commands/sample.ts +11 -1
- package/src/commands/sing.ts +474 -0
- package/src/commands/strum.ts +13 -1
- package/src/commands/style.ts +415 -0
- package/src/commands/time.ts +6 -3
- package/src/commands/tuning.ts +3 -3
- package/src/commands/vocal-pitch.ts +617 -0
- package/src/commands/vocal.ts +147 -0
- package/src/commands/vocoder.ts +627 -0
- package/src/commands/wind.ts +2 -2
- package/src/fs/durable.ts +50 -0
- package/src/identity/actor.ts +127 -0
- package/src/lang/glossary.ts +511 -0
- package/src/launch-args.ts +267 -0
- package/src/main.ts +2508 -308
- package/src/media/cli.ts +20 -3
- package/src/media/import.ts +3 -1
- package/src/project/check.ts +21 -2
- package/src/project/clip-pins.ts +72 -0
- package/src/project/init.ts +23 -8
- package/src/project/sync.ts +418 -86
- package/src/render.ts +20 -1
- package/src/session/client.ts +107 -3
- package/src/session/clipboard.ts +79 -0
- package/src/session/daemon.ts +199 -5
- package/src/session/live-host.ts +232 -0
- package/src/session/meta.ts +14 -0
- package/src/session/origin.ts +174 -0
- package/src/session/port.ts +109 -14
- package/src/session/presence.ts +34 -4
- package/src/session/protocol.ts +365 -7
- package/src/session/rebase.ts +63 -12
- package/src/session/receipt.ts +265 -0
- package/src/session/shared-live.ts +131 -0
- package/src/session/store.ts +126 -45
- package/src/tui/arrange-menu.ts +225 -23
- package/src/tui/audition.ts +1 -1
- package/src/tui/euclid.ts +113 -27
- package/src/tui/fader.ts +282 -42
- package/src/tui/granular-menu.ts +2 -4
- package/src/tui/knob-fields.ts +107 -0
- package/src/tui/knob-map.ts +198 -0
- package/src/tui/menu-clips.ts +297 -0
- package/src/tui/menu-time.ts +20 -13
- package/src/tui/menu-voice.ts +405 -0
- package/src/tui/menu.ts +987 -179
- package/src/tui/modal-menu.ts +6 -6
- package/src/tui/performance-menu.ts +5 -2
- package/src/tui/play-chords.ts +4 -2
- package/src/tui/play-mode.ts +15 -1
- package/src/tui/play-session.ts +243 -28
- package/src/tui/sing-menu.ts +278 -0
- package/src/tui/style-menu.ts +104 -0
- package/src/tui/tape-mode.ts +580 -0
- package/src/tui/tape-view.ts +263 -0
- package/src/tui/vocoder-menu.ts +244 -0
- package/src/tui/wind-menu.ts +3 -3
- package/src/version.ts +8 -0
- package/src/web/fetch.ts +115 -29
- package/tui/activity.ts +180 -9
- package/tui/app.ts +491 -84
- package/tui/clip-row.ts +132 -0
- package/tui/delight.ts +193 -0
- package/tui/drawer.ts +233 -27
- package/tui/frame-gate.ts +76 -0
- package/tui/grammar.ts +143 -65
- package/tui/guide.ts +50 -5
- package/tui/highway.ts +309 -30
- package/tui/hints.ts +197 -0
- package/tui/hits.ts +7 -1
- package/tui/input.ts +60 -9
- package/tui/keys.ts +1 -1
- package/tui/knobs.ts +268 -0
- package/tui/play-strip.ts +80 -14
- package/tui/prompt.ts +1 -1
- package/tui/screen.ts +151 -18
- package/tui/tape.ts +439 -0
- package/tui/text.ts +35 -2
- package/tui/theme.ts +46 -1
package/src/tui/euclid.ts
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* runs through the normal prompt path, so every change is one revision and
|
|
10
10
|
* one undo step and the row's detail teaches the command.
|
|
11
11
|
*
|
|
12
|
-
* Keys: ↑↓
|
|
12
|
+
* Keys: ↑↓ knob (● drum ▲ pulses ■ rotate ◆ velocity) · ←→ (h l + -) turn ·
|
|
13
|
+
* tab / shift-tab (] [) every parameter ·
|
|
13
14
|
* digits type a value · enter add/edit · space loop · x off · f freeze ·
|
|
14
15
|
* esc back.
|
|
15
16
|
*
|
|
@@ -18,7 +19,18 @@
|
|
|
18
19
|
* `a` flips A/B, `c` solo ↔ in context, Enter keeps the staged changes as
|
|
19
20
|
* one undo step and Esc reverts them.
|
|
20
21
|
*/
|
|
21
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
HINTS,
|
|
24
|
+
KEY_BACKSPACE,
|
|
25
|
+
KEY_BACKTAB,
|
|
26
|
+
KEY_DOWN,
|
|
27
|
+
KEY_ENTER,
|
|
28
|
+
KEY_LEFT,
|
|
29
|
+
KEY_RESET,
|
|
30
|
+
KEY_RIGHT,
|
|
31
|
+
KEY_TAB,
|
|
32
|
+
KEY_UP,
|
|
33
|
+
} from "../../tui/grammar.ts";
|
|
22
34
|
import { DRUM_VOICES, isDrumInstrument } from "../../core/drums.ts";
|
|
23
35
|
import {
|
|
24
36
|
DIVISIONS,
|
|
@@ -37,6 +49,8 @@ import {
|
|
|
37
49
|
import type { PickerItem } from "../../tui/app.ts";
|
|
38
50
|
import { auditionKey, type AuditionKey } from "./audition.ts";
|
|
39
51
|
import type { MenuAudition } from "./menu.ts";
|
|
52
|
+
import { KNOB_MAPS } from "./knob-map.ts";
|
|
53
|
+
import { knobGlyph, KNOBS, nextKnob, type KnobIndex } from "../../tui/knobs.ts";
|
|
40
54
|
|
|
41
55
|
export type EuclidContext = Readonly<{
|
|
42
56
|
/** The score shown: the staged one while auditioning. */
|
|
@@ -284,20 +298,38 @@ export function ringText(row: RhythmRow | undefined): string {
|
|
|
284
298
|
.join("");
|
|
285
299
|
}
|
|
286
300
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
301
|
+
/**
|
|
302
|
+
* The four knobs (KNOB_MAPS.euclid): blue `drum` picks the row, the others
|
|
303
|
+
* name EUCLID_PARAMS fields.
|
|
304
|
+
*/
|
|
305
|
+
const EUCLID_KNOBS = KNOB_MAPS.euclid!;
|
|
306
|
+
|
|
307
|
+
/** After the strip, the knob keys, then the way out; fitHint drops the knob keys first. */
|
|
308
|
+
const KNOB_KEYS = "↑↓ knob · ←→ turn · esc back · ? keys";
|
|
309
|
+
|
|
310
|
+
/** The knob that turns `param`, if one does. */
|
|
311
|
+
function knobOfParam(param: number): KnobIndex | undefined {
|
|
312
|
+
const at = EUCLID_KNOBS.indexOf(EUCLID_PARAMS[param]!.field);
|
|
313
|
+
return at < 0 ? undefined : (at as KnobIndex);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** EUCLID_PARAMS index a knob turns; undefined for the drum knob. */
|
|
317
|
+
function paramOfKnob(knob: KnobIndex): number | undefined {
|
|
318
|
+
const field = EUCLID_KNOBS[knob];
|
|
319
|
+
const at = EUCLID_PARAMS.findIndex((param) => param.field === field);
|
|
320
|
+
return at < 0 ? undefined : at;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** Tab ] next field, shift-tab [ previous (the brackets are the old keys). */
|
|
324
|
+
const isNextField = (value: string) => KEY_TAB.has(value) || value === "]";
|
|
325
|
+
const isPrevField = (value: string) => KEY_BACKTAB.has(value) || value === "[";
|
|
296
326
|
|
|
297
327
|
export class EuclidEditor {
|
|
298
328
|
private visible = false;
|
|
299
329
|
private lane = 0;
|
|
300
330
|
private param = 0;
|
|
331
|
+
/** True while the blue knob (which drum) has focus, not a field. */
|
|
332
|
+
private onDrum = true;
|
|
301
333
|
private entry: string | undefined;
|
|
302
334
|
/** Where Esc returns to (`menu` when opened from `/menu`). */
|
|
303
335
|
private origin: string | undefined;
|
|
@@ -322,6 +354,10 @@ export class EuclidEditor {
|
|
|
322
354
|
this.origin = origin;
|
|
323
355
|
const lanes = this.lanes(context);
|
|
324
356
|
const at = voice ? lanes.findIndex((lane) => lane.voice === voice) : -1;
|
|
357
|
+
// Opened on a named drum, the green knob (pulses) has focus; otherwise
|
|
358
|
+
// the blue one, to pick the drum.
|
|
359
|
+
this.onDrum = at < 0;
|
|
360
|
+
if (!this.onDrum) this.param = paramOfKnob(1) ?? 0;
|
|
325
361
|
if (at >= 0) this.lane = at;
|
|
326
362
|
else {
|
|
327
363
|
// Land on the first voice that already has a row.
|
|
@@ -347,9 +383,14 @@ export class EuclidEditor {
|
|
|
347
383
|
return lanes[Math.min(this.lane, lanes.length - 1)]?.voice;
|
|
348
384
|
}
|
|
349
385
|
|
|
350
|
-
/** Parameter under the cursor. */
|
|
386
|
+
/** Parameter under the cursor (`drum` on the blue knob). */
|
|
351
387
|
get selectedParam(): string {
|
|
352
|
-
return EUCLID_PARAMS[this.param]!.field;
|
|
388
|
+
return this.onDrum ? "drum" : EUCLID_PARAMS[this.param]!.field;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** The knob with focus; undefined on a field no knob turns. */
|
|
392
|
+
get selectedKnob(): KnobIndex | undefined {
|
|
393
|
+
return this.onDrum ? 0 : knobOfParam(this.param);
|
|
353
394
|
}
|
|
354
395
|
|
|
355
396
|
key(value: string, context: EuclidContext): EuclidResult {
|
|
@@ -371,7 +412,8 @@ export class EuclidEditor {
|
|
|
371
412
|
if (KEY_ENTER.has(value)) {
|
|
372
413
|
const text = this.entry.trim();
|
|
373
414
|
this.entry = undefined;
|
|
374
|
-
if (!lane || !/^-?[0-9./tT]+$/.test(text))
|
|
415
|
+
if (this.onDrum || !lane || !/^-?[0-9./tT]+$/.test(text))
|
|
416
|
+
return { type: "handled" };
|
|
375
417
|
return {
|
|
376
418
|
type: "run",
|
|
377
419
|
command: `euclid ${lane.voice} ${param.field} ${text}`,
|
|
@@ -387,21 +429,45 @@ export class EuclidEditor {
|
|
|
387
429
|
return audition?.dirty ? { type: "revert" } : { type: "close" };
|
|
388
430
|
const loopKey = audition ? auditionKey(value) : undefined;
|
|
389
431
|
if (loopKey) return { type: "loop", key: loopKey };
|
|
432
|
+
// ↑↓ pick a knob (design §8.7); from a field no knob turns, ↑ lands
|
|
433
|
+
// on orange and ↓ on blue.
|
|
390
434
|
if (KEY_UP.has(value) || KEY_DOWN.has(value)) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
435
|
+
const up = KEY_UP.has(value);
|
|
436
|
+
const from = this.selectedKnob;
|
|
437
|
+
const knob =
|
|
438
|
+
from === undefined
|
|
439
|
+
? up
|
|
440
|
+
? 3
|
|
441
|
+
: 0
|
|
442
|
+
: nextKnob(from, up ? -1 : 1, (index) =>
|
|
443
|
+
index === 0 ? true : paramOfKnob(index) !== undefined,
|
|
444
|
+
);
|
|
445
|
+
this.onDrum = knob === 0;
|
|
446
|
+
if (!this.onDrum) this.param = paramOfKnob(knob) ?? this.param;
|
|
395
447
|
return { type: "handled" };
|
|
396
448
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
449
|
+
// Tab walks every field (the full list behind the knobs).
|
|
450
|
+
if (isNextField(value) || isPrevField(value)) {
|
|
451
|
+
const direction = isNextField(value) ? 1 : -1;
|
|
452
|
+
if (this.onDrum) {
|
|
453
|
+
this.onDrum = false;
|
|
454
|
+
this.param = direction > 0 ? 0 : EUCLID_PARAMS.length - 1;
|
|
455
|
+
} else
|
|
456
|
+
this.param =
|
|
457
|
+
(this.param + direction + EUCLID_PARAMS.length) %
|
|
458
|
+
EUCLID_PARAMS.length;
|
|
401
459
|
return { type: "handled" };
|
|
402
460
|
}
|
|
403
461
|
if (!lane) return { type: "handled" };
|
|
404
462
|
if (value === " ") return { type: "audition", voice: lane.voice };
|
|
463
|
+
if (this.onDrum && (KEY_LEFT.has(value) || KEY_RIGHT.has(value))) {
|
|
464
|
+
// The blue knob turns which drum row has focus.
|
|
465
|
+
if (lanes.length)
|
|
466
|
+
this.lane =
|
|
467
|
+
(this.lane + (KEY_RIGHT.has(value) ? 1 : -1) + lanes.length) %
|
|
468
|
+
lanes.length;
|
|
469
|
+
return { type: "handled" };
|
|
470
|
+
}
|
|
405
471
|
if (KEY_LEFT.has(value) || KEY_RIGHT.has(value)) {
|
|
406
472
|
const direction = KEY_RIGHT.has(value) ? 1 : -1;
|
|
407
473
|
const row: RhythmRow = lane.row ?? { voice: lane.voice };
|
|
@@ -422,7 +488,7 @@ export class EuclidEditor {
|
|
|
422
488
|
audition: lane.voice,
|
|
423
489
|
};
|
|
424
490
|
}
|
|
425
|
-
if (/^[0-9]$/.test(value)) {
|
|
491
|
+
if (/^[0-9]$/.test(value) && !this.onDrum) {
|
|
426
492
|
this.entry = value;
|
|
427
493
|
return { type: "handled" };
|
|
428
494
|
}
|
|
@@ -434,10 +500,10 @@ export class EuclidEditor {
|
|
|
434
500
|
command: `euclid ${lane.voice} ${RHYTHM_DEFAULTS.pulses} ${RHYTHM_DEFAULTS.steps}`,
|
|
435
501
|
audition: lane.voice,
|
|
436
502
|
};
|
|
437
|
-
this.entry = "";
|
|
503
|
+
if (!this.onDrum) this.entry = "";
|
|
438
504
|
return { type: "handled" };
|
|
439
505
|
}
|
|
440
|
-
if (
|
|
506
|
+
if (KEY_RESET.has(value))
|
|
441
507
|
return lane.row
|
|
442
508
|
? { type: "run", command: `euclid ${lane.voice} off` }
|
|
443
509
|
: { type: "handled" };
|
|
@@ -449,6 +515,23 @@ export class EuclidEditor {
|
|
|
449
515
|
return { type: "handled" };
|
|
450
516
|
}
|
|
451
517
|
|
|
518
|
+
/**
|
|
519
|
+
* The knob strip for the hint row: `●›kick ▲ pulses 4 ■ rotate 0 ◆ velocity
|
|
520
|
+
* 0.8`, the focused knob marked `›`. Glyphs carry which knob is which.
|
|
521
|
+
*/
|
|
522
|
+
private strip(drum: string | undefined, row: RhythmRow | undefined): string {
|
|
523
|
+
const focused = this.selectedKnob;
|
|
524
|
+
return KNOBS.map((knob) => {
|
|
525
|
+
const mark = knob.index === focused ? "›" : " ";
|
|
526
|
+
if (knob.index === 0) return `${knob.glyph}${mark}${drum ?? "·"}`;
|
|
527
|
+
const at = paramOfKnob(knob.index);
|
|
528
|
+
if (at === undefined) return `${knob.glyph} ·`;
|
|
529
|
+
const param = EUCLID_PARAMS[at]!;
|
|
530
|
+
const shown = row ? formatValue(param.value(row)) : "—";
|
|
531
|
+
return `${knob.glyph}${mark}${param.label} ${shown}`;
|
|
532
|
+
}).join(" ");
|
|
533
|
+
}
|
|
534
|
+
|
|
452
535
|
view(context: EuclidContext): EuclidView {
|
|
453
536
|
const lanes = this.lanes(context);
|
|
454
537
|
const index = Math.max(0, Math.min(this.lane, lanes.length - 1));
|
|
@@ -484,7 +567,10 @@ export class EuclidEditor {
|
|
|
484
567
|
const voice = lanes[index]?.voice ?? "";
|
|
485
568
|
const row = lanes[index]?.row;
|
|
486
569
|
const value = row ? formatValue(param.value(row)) : "—";
|
|
487
|
-
|
|
570
|
+
const focus = this.onDrum
|
|
571
|
+
? `${knobGlyph(0, true)} drum`
|
|
572
|
+
: `${param.label} ${value}`;
|
|
573
|
+
let title = `rhythm › ${context.trackId}${voice ? ` · ${voice}` : ""} · ${focus}`;
|
|
488
574
|
if (this.entry !== undefined) title += ` · ${param.label}: ${this.entry}▏`;
|
|
489
575
|
if (audition?.dirty) title = `● ${title}`;
|
|
490
576
|
if (audition?.status && this.entry === undefined)
|
|
@@ -494,7 +580,7 @@ export class EuclidEditor {
|
|
|
494
580
|
? HINTS.typing
|
|
495
581
|
: audition && (audition.looping || audition.dirty)
|
|
496
582
|
? audition.hint
|
|
497
|
-
:
|
|
583
|
+
: `${this.strip(lanes[index]?.label.trim(), row)} · ${KNOB_KEYS}`;
|
|
498
584
|
return {
|
|
499
585
|
title,
|
|
500
586
|
items: items.length
|