@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/agent/xcb-agent.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { newId } from "../../core/ids.ts";
|
|
1
2
|
import { GRANULAR_PARAMS } from "../../core/granular.ts";
|
|
2
3
|
import { compositionBrief } from "./brief.ts";
|
|
3
4
|
import { CHORD_PROCESS } from "../../core/chords.ts";
|
|
@@ -44,25 +45,28 @@ export type TextAgentTurnOptions = Readonly<{
|
|
|
44
45
|
export const TEXT_AGENT_SYSTEM_PROMPT = [
|
|
45
46
|
"You are dawg, a loop composer inside a terminal music workstation.",
|
|
46
47
|
"You edit the score by returning operations. Each op is validated and applied immediately, in order, as its own revision.",
|
|
47
|
-
|
|
48
|
+
'Times are in beats from the loop start (0-based), while musicians count from 1: in 4/4 "beats 2 and 4" are beats 1 and 3 of each bar here, "the and of 1" is 0.5, and bar n starts at (n-1)×beats per bar. Keep notes inside loopBeats unless you extend the loop first.',
|
|
48
49
|
"Prefer a few well-formed ops (one add_notes op per track part) over many tiny ones.",
|
|
49
|
-
'For drums, create a track with instrument "kit" and prefer set_rhythm (Euclidean rows: pulses over steps, rotate, repeats for rolls, accent, probability, swing) so the beat stays editable as parameters; for a
|
|
50
|
+
'For drums, create a track with instrument "kit" and prefer set_rhythm (Euclidean rows: pulses over steps, rotate, repeats for rolls, accent, probability, swing) so the beat stays editable as parameters; for a style groove start from apply_drum_pattern (list_drum_patterns) and pick a sound with set_drum_kit. Drum pitches select voices, so do not use add_notes for beats.',
|
|
50
51
|
"Song structure: list_sections shows named sections (bars count from 1); edit_section marks, adds, duplicates, moves, renames, deletes, mutes, varies (transpose/gain) and loops them; set_form sets the play order with repeats (intro verse chorus*2 outro); add_transition generates builds (riser, snare roll, filter sweep, uplifter), drops (cut plus impact) and drum fills at section boundaries.",
|
|
51
52
|
CHORD_PROCESS,
|
|
52
53
|
"Synth voices: set_synth shapes a synth track with Strudel params (ADSR, lpf/lpenv filter envelopes, fm/fmh, supersaw unison/detune, vib, penv, noise; z_* ZzFX sounds take slide/pitchJump/lfo/zcrush…) or a preset (pad lead pluck bass keys bell…).",
|
|
53
|
-
"Plucked strings: set_string makes a track a physical-model string (nylon steel electric jangle ebass slap upright motown sitar tanpura harpsichord lute oud setar tar santur dulcimer koto harp banjo tres requinto) and overrides params (ring bright damp pos mute buzz body sym). The bare words sitar, ebass, pluck keep their older voices; use set_string for the
|
|
54
|
+
"Plucked strings: set_string makes a track a physical-model string (nylon steel electric jangle ebass slap upright motown sitar tanpura harpsichord lute oud setar tar santur dulcimer koto harp banjo tres requinto) and overrides params (ring bright damp pos mute buzz body sym). The bare words sitar, ebass, pluck keep their older voices; use set_string for the modeled ones.",
|
|
54
55
|
"Bowed strings: set_string preset violin|viola|cello|contrabass|fiddle|erhu|kamancheh|violins|violas|cellos|contrabasses|pizz|trem plays a bowed physical model (bow params pressure speed attack vib vibmod vibdelay tremhz sord dyn). Overlapping or legato notes slur in one bow. The bare words cello, contrabass and strings keep their older voices; use set_string (or the words violin, viola, fiddle, erhu, violins, cellos) for the bowed ones. Sampler voices take vel [lo, hi] velocity layers and rr round-robin groups via set_sample.",
|
|
55
|
-
"Pianos: set_instrument piano (or grand upright felt honkytonk prepared, ballad, lofi) loads a
|
|
56
|
-
"Electric keys: set_instrument epiano (rhodes; suitcase stereo vibe, dyno bright 80s), wurli, clav (funkclav); set_keys tweaks bark bell tone vibe (epiano), trem (wurli), pickup neck|bridge|both|out and mute (clav). Soft and sostenuto pedals: set_piano_pedals on any
|
|
57
|
-
'Organs: set_instrument tonewheel (hammond b3 gospel jazzorgan), combo (farfisa vox) or pipe (church flutes cornet reeds celeste) loads a
|
|
56
|
+
"Pianos: set_instrument piano (or grand upright felt honkytonk prepared, ballad, lofi) loads a modeled piano with real hammers, stretched inharmonic strings, dampers and the sustain pedal; set_keys tweaks it (hardness touch decay release felt stretch body…, automate as keys-<param> lanes; sym 0.5 adds sympathetic bloom under the sustain pedal for pedalled Romantic and Impressionist playing). A track already stored as instrument piano keeps the legacy tone until you set it again.",
|
|
57
|
+
"Electric keys: set_instrument epiano (rhodes; suitcase stereo vibe, dyno bright 80s), wurli, clav (funkclav); set_keys tweaks bark bell tone vibe (epiano), trem (wurli), pickup neck|bridge|both|out and mute (clav). Soft and sostenuto pedals: set_piano_pedals on any modeled piano.",
|
|
58
|
+
'Organs: set_instrument tonewheel (hammond b3 gospel jazzorgan), combo (farfisa vox) or pipe (church flutes cornet reeds celeste) loads a modeled organ; instrument organ stays the legacy sine. set_keys drawbars "888800008" (16\' to 1\'), registers "08880" (combo), stops ["principal8","octave4"] or plenum|flutes|cornet|reeds|strings|full (pipe), rotary slow|fast|stop, params perc 3rd percdecay percvol click scanner c3 drive chiff wind trem (each family takes only its own rows); automate keys-rotary and keys-drive lanes.',
|
|
58
59
|
"Mallets and bells: set_instrument vibes (or marimba via modal, xylophone glock celesta chimes kalimba mbira steelpan bowl gong timpani) plays the modal engine; set_modal changes mallet (yarn…brass), hardness, position, ring, damp, motor/motordepth (vibes tremolo), buzz, click. Plain instrument marimba stays the legacy tone.",
|
|
59
60
|
"Gamelan: set_modal preset saron demung slenthem gangsa gender bonang kenong kethuk kempul (gong too) with tuning slendro or pelog; for ombak (the shimmer) put the same part on two gangsa tracks and set_modal pair <other track> on one: it sounds ombak Hz (default 6) above its partner. Frame drums: daf bodhran tabla.",
|
|
60
61
|
"Winds and brass: set_instrument flute recorder whistle ney shakuhachi panpipe suling bansuri clarinet bassclarinet oboe bassoon sax altosax barisax trumpet harmon plunger trombone tuba horn plays the wind engine; set_wind changes breath, bright, mute (open straight cup harmon plunger), players (2..8 spreads a section over chord tones), growl, vib. Lines slur unless re-tongued (gaps or articulations). Plain instrument wind stays the legacy tone.",
|
|
62
|
+
"Voices (0.7): set_instrument aah ooh choir chorale (or set_sing preset oohchoir airy glass lament soprano basso drone) plays the built-in singing voice, a synthetic glottal source through formants (never a cloned voice); set_sing params voice (auto soprano alto tenor bass), vowel (a e i o u or a>o), voices 2..8 (an ensemble), breath, vib, jitter, bright; set_vowels puts vowels on notes. Throat singing: khoomei, sygyt (whistle) and kargyraa (sub growl) hold a drone (set_sing params drone D2) and each note picks a harmonic of it.",
|
|
63
|
+
"Vocoder: a vocoder is a carrier track whose vocoder.src is the modulator (usually the user's vocal). make one with create_track (instrument vocoder), then set_vocoder with src set to the vocal, and mute the vocal; talkbox suits funk and soul, choir and glass suit hyperpop, robot and smear suit electronic or ambient. Vocode only the user's own or licensed audio, never an identifiable third party's speech unless the user confirms they hold the rights.",
|
|
61
64
|
"Sampler voices: set_sample sets Strudel sample controls per voice (begin end speed unit loop loopBegin loopEnd clip/legato fit loopAt accelerate squiz cut); splice = slices with fit.",
|
|
62
65
|
"Fitting samples (0.6): fit_sample sets a voice's own bpm (or len in beats) so it follows the song's tempo map; fitmode beats for drums and speech (hits stay sharp), tones for pads and vocals (pitch kept), repitch (default) like tape.",
|
|
63
|
-
"Effects (set_fx
|
|
66
|
+
"Effects (set_fx and set_automation): the brief's effects list is the fixed chain order with each effect's simple params. set_fx turns an effect on with good defaults, loads a preset or sets params by dawg or Strudel name. Lanes: volume, pan, filter, resonance, delay-feedback, delay-mix, wt and <effect>-<param> (e.g. autofilter-cutoff).",
|
|
64
67
|
"Guitar rig (set_rig): rig loads a whole rig (clean crunch punk ragged lead metal fuzz octave funk wah bachata spring bassdrive reese jangle alt) on a guitar or bass track; stomp, head and cab tweak the pedal, amp (level-matched, gate in dB) and cabinet. Tracks named jangle punk funk ragged gtr-lead gtr-metal bachata start with a guitar voice and that rig. `amp` is Strudel gain; the guitar amp is head.",
|
|
65
68
|
"Shoegaze: rig shoegaze|glide|dreampop|swell|ebow (or a track named shoegaze); set_fx wobble (tremolo-arm bend: depth cents, rate, drift), bloom (feedback swell on the top note), swell (volume swell: time), double (second take: time, drift, width); reverb ir builtin:reverse|gate|spring. Strumming: set_guitar (tune dadgad, capo, hand, ring), strum_chords (chords or the track's own, strokes folk|pop|punk… or D-DU-UDU, speed ms), write_chords perform guitar.",
|
|
69
|
+
"Formant (0.7): set_formant shift -12..12 st moves a track's formants at constant pitch (negative deeper or bigger, positive smaller; 2-4 natural), mix 0..1, preset deep giant bright tiny, off; lanes formant-shift formant-mix. The vowel filter is set_fx vowel, whose to and morph 0..1 glide between two vowels (lane vowel-morph).",
|
|
66
70
|
"Previewing: preview_sound renders a track, or candidate sound tool calls (changes: [{tool, args}]), without committing, returns loudness, brightness and a comparison, and plays it once in the user's window. Use it when choosing between sounds (tables, presets, effect amounts), say how it sounds, then commit with the normal tools.",
|
|
67
71
|
"Wavetable synth: set_wavetable picks a table (built-ins offline, Strudel wt_ sets fetched once) and scans it by position wt.",
|
|
68
72
|
`Granular: set_granular makes a track a grain cloud (presets cloud hold sparkle swarm stutter microloop backwards dust) of a synth source (src synth:pad, the default; synth:<name>@<note>) or one of its own sampler voices, and overrides params (${Object.keys(GRANULAR_PARAMS).join(" ")}; repeat and hold latch the head for beat repeats, begin and end pick a region, root takes a MIDI number or note name); off returns to the previous voice; the same seed gives the same grains. Grain play: sync "1/16" puts grain onsets on the tempo grid, quant scale|chord snaps grain pitches, mono true glides legato notes through one cloud, pedal true freezes the head while the sustain pedal is down; set_automation grain-<param> lanes (grain-pos, grain-scan, grain-pitch …) move a parameter over beats. Resample: the resample tool renders a track, orbit or the mix (a section or bars) to a pinned WAV and adds a sampler track, or with grain a granular track, that plays it; set_sample shift (semitones, length kept, formant 0 keeps a voice's character) and fadeInTime/fadeTime shape a sampler voice.`,
|
|
@@ -80,6 +84,7 @@ export function renderToolCatalog(
|
|
|
80
84
|
tools: readonly AgentTool[] = AGENT_TOOLS,
|
|
81
85
|
): string {
|
|
82
86
|
return tools
|
|
87
|
+
.filter((tool) => !tool.hidden)
|
|
83
88
|
.map(
|
|
84
89
|
(tool) =>
|
|
85
90
|
`- ${tool.name}: ${tool.description}\n args schema: ${JSON.stringify(tool.parameters)}`,
|
|
@@ -121,11 +126,9 @@ export async function runTextAgentTurn(
|
|
|
121
126
|
};
|
|
122
127
|
const tools = options.tools ?? AGENT_TOOLS;
|
|
123
128
|
const catalog = renderToolCatalog(tools);
|
|
124
|
-
const nonce = Math.random().toString(36).slice(2, 6);
|
|
125
129
|
const newNoteId =
|
|
126
130
|
options.newNoteId ??
|
|
127
|
-
((trackId: string,
|
|
128
|
-
`${trackId.slice(0, 40)}-${revision}-${nonce}${index}`);
|
|
131
|
+
((trackId: string, _revision: number, _index: number) => newId(trackId));
|
|
129
132
|
const startedAt = Date.now();
|
|
130
133
|
const deadline = turnDeadline(limits.timeoutMs);
|
|
131
134
|
const signal = options.signal
|
package/src/argv.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** The small argv contract plain subcommands share (no dependencies). */
|
|
2
|
+
|
|
3
|
+
export type SimpleArgv =
|
|
4
|
+
| { kind: "help" }
|
|
5
|
+
| { kind: "run"; positionals: readonly string[] }
|
|
6
|
+
| { kind: "error"; problem: string };
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The contract `dawg init`, `dawg check` and `dawg sessions` share: argv
|
|
10
|
+
* after the subcommand. `--help`/`-h` anywhere asks for usage; any other
|
|
11
|
+
* option, or more than `maxPositionals` words, is an error (exit 2) before
|
|
12
|
+
* the command does anything.
|
|
13
|
+
*/
|
|
14
|
+
export function parseSimpleArgv(
|
|
15
|
+
rest: readonly string[],
|
|
16
|
+
maxPositionals: number,
|
|
17
|
+
): SimpleArgv {
|
|
18
|
+
if (rest.some((arg) => arg === "--help" || arg === "-h"))
|
|
19
|
+
return { kind: "help" };
|
|
20
|
+
const positionals: string[] = [];
|
|
21
|
+
for (const arg of rest) {
|
|
22
|
+
if (arg === "--")
|
|
23
|
+
return { kind: "error", problem: "unexpected argument · --" };
|
|
24
|
+
if (arg.startsWith("-") && arg.length > 1)
|
|
25
|
+
return { kind: "error", problem: `unknown option · ${clip(arg)}` };
|
|
26
|
+
positionals.push(arg);
|
|
27
|
+
}
|
|
28
|
+
if (positionals.length > maxPositionals)
|
|
29
|
+
return {
|
|
30
|
+
kind: "error",
|
|
31
|
+
problem: `unexpected argument · ${clip(positionals[maxPositionals]!)}`,
|
|
32
|
+
};
|
|
33
|
+
return { kind: "run", positionals };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function clip(value: string): string {
|
|
37
|
+
return value.length > 40 ? `${value.slice(0, 40)}…` : value;
|
|
38
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pitch-analysis cache (0.7). `pitchCurve` tracks a decoded sample once
|
|
3
|
+
* and keeps the canonical curve in a small in-memory LRU and on disk at
|
|
4
|
+
* `.dawg/analysis/<sha256>.<voice>.v<tracker version>.f0` (format DWF0).
|
|
5
|
+
*
|
|
6
|
+
* DWF0 holds exactly the canonical curve `trackPitch` returns (f0 Float32,
|
|
7
|
+
* prob and aperiodic u8), so a cache hit renders the same bytes as a cache
|
|
8
|
+
* miss. Layout, little endian:
|
|
9
|
+
*
|
|
10
|
+
* 0 "DWF0" magic
|
|
11
|
+
* 4 u16 DWF0 version, u16 tracker version
|
|
12
|
+
* 8 u32 frames
|
|
13
|
+
* 12 f64 hop seconds, 20 f64 t0 seconds
|
|
14
|
+
* 28 f32[frames] f0 Hz (0 unvoiced)
|
|
15
|
+
* … u8[frames] prob, u8[frames] aperiodic (1/255 steps)
|
|
16
|
+
* … 8 bytes: the sha256 prefix of every byte before it
|
|
17
|
+
*
|
|
18
|
+
* About 1.2 KB per audio second. Writes go to `<name>.<pid>.tmp` and are
|
|
19
|
+
* renamed into place; write errors are swallowed (the cache is an
|
|
20
|
+
* optimisation). Reads validate magic, versions, length and checksum, and
|
|
21
|
+
* any mismatch is a miss. Analysis stays inside the project.
|
|
22
|
+
*/
|
|
23
|
+
import { createHash } from "node:crypto";
|
|
24
|
+
import { mkdir, readFile, rename, utimes, writeFile } from "node:fs/promises";
|
|
25
|
+
import { join } from "node:path";
|
|
26
|
+
import {
|
|
27
|
+
cacheUsage,
|
|
28
|
+
pruneLru,
|
|
29
|
+
type CacheUsage,
|
|
30
|
+
type PruneResult,
|
|
31
|
+
} from "./cache.ts";
|
|
32
|
+
import {
|
|
33
|
+
PITCH_TRACKER_VERSION,
|
|
34
|
+
trackPitchAsync,
|
|
35
|
+
type PitchCurve,
|
|
36
|
+
type PitchVoice,
|
|
37
|
+
} from "./dsp/pitch.ts";
|
|
38
|
+
|
|
39
|
+
export const DWF0_VERSION = 1;
|
|
40
|
+
const HEAD = 28;
|
|
41
|
+
const CHECK = 8;
|
|
42
|
+
const MAGIC = [0x44, 0x57, 0x46, 0x30] as const;
|
|
43
|
+
|
|
44
|
+
/** Disk budget for `.dawg/analysis` (curves are small; 64 MB is ~15 h). */
|
|
45
|
+
export const ANALYSIS_CACHE_BYTES = 64 * 1024 * 1024;
|
|
46
|
+
/** In-memory curves kept (least recently used go first). */
|
|
47
|
+
export const ANALYSIS_MEMORY_ENTRIES = 64;
|
|
48
|
+
|
|
49
|
+
/** Every file the analysis cache writes. */
|
|
50
|
+
export const ANALYSIS_FILE = /^[0-9a-f]{64}\.\w+\.v\d+\.f0$/;
|
|
51
|
+
|
|
52
|
+
function checksum(bytes: Uint8Array, length: number): Uint8Array {
|
|
53
|
+
return createHash("sha256").update(bytes.subarray(0, length)).digest();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The DWF0 bytes of a curve. */
|
|
57
|
+
export function encodeCurve(curve: PitchCurve): Uint8Array {
|
|
58
|
+
const n = curve.f0.length;
|
|
59
|
+
const size = HEAD + 6 * n + CHECK;
|
|
60
|
+
const bytes = new Uint8Array(size);
|
|
61
|
+
const view = new DataView(bytes.buffer);
|
|
62
|
+
bytes.set(MAGIC);
|
|
63
|
+
view.setUint16(4, DWF0_VERSION, true);
|
|
64
|
+
view.setUint16(6, PITCH_TRACKER_VERSION, true);
|
|
65
|
+
view.setUint32(8, n, true);
|
|
66
|
+
view.setFloat64(12, curve.hop, true);
|
|
67
|
+
view.setFloat64(20, curve.t0, true);
|
|
68
|
+
for (let f = 0; f < n; f += 1)
|
|
69
|
+
view.setFloat32(HEAD + 4 * f, curve.f0[f]!, true);
|
|
70
|
+
bytes.set(curve.prob, HEAD + 4 * n);
|
|
71
|
+
bytes.set(curve.aperiodic, HEAD + 5 * n);
|
|
72
|
+
bytes.set(checksum(bytes, size - CHECK).subarray(0, CHECK), size - CHECK);
|
|
73
|
+
return bytes;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The curve in DWF0 `bytes`, or undefined for anything not exactly valid. */
|
|
77
|
+
export function decodeCurve(bytes: Uint8Array): PitchCurve | undefined {
|
|
78
|
+
if (bytes.length < HEAD + CHECK) return undefined;
|
|
79
|
+
for (let i = 0; i < 4; i += 1) if (bytes[i] !== MAGIC[i]) return undefined;
|
|
80
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
81
|
+
if (view.getUint16(4, true) !== DWF0_VERSION) return undefined;
|
|
82
|
+
if (view.getUint16(6, true) !== PITCH_TRACKER_VERSION) return undefined;
|
|
83
|
+
const n = view.getUint32(8, true);
|
|
84
|
+
if (bytes.length !== HEAD + 6 * n + CHECK) return undefined;
|
|
85
|
+
const sum = checksum(bytes, bytes.length - CHECK);
|
|
86
|
+
for (let i = 0; i < CHECK; i += 1)
|
|
87
|
+
if (bytes[bytes.length - CHECK + i] !== sum[i]) return undefined;
|
|
88
|
+
const hop = view.getFloat64(12, true);
|
|
89
|
+
const t0 = view.getFloat64(20, true);
|
|
90
|
+
if (!(hop > 0) || !Number.isFinite(t0)) return undefined;
|
|
91
|
+
const f0 = new Float32Array(n);
|
|
92
|
+
for (let f = 0; f < n; f += 1) f0[f] = view.getFloat32(HEAD + 4 * f, true);
|
|
93
|
+
return {
|
|
94
|
+
t0,
|
|
95
|
+
hop,
|
|
96
|
+
f0,
|
|
97
|
+
prob: bytes.slice(HEAD + 4 * n, HEAD + 5 * n),
|
|
98
|
+
aperiodic: bytes.slice(HEAD + 5 * n, HEAD + 6 * n),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The cache file name of a sample's curve. */
|
|
103
|
+
export function analysisFileName(sha256: string, voice: PitchVoice): string {
|
|
104
|
+
return `${sha256}.${voice}.v${PITCH_TRACKER_VERSION}.f0`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** `<projectRoot>/.dawg/analysis`. */
|
|
108
|
+
export function analysisDir(projectRoot: string): string {
|
|
109
|
+
return join(projectRoot, ".dawg", "analysis");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** What `pitchCurve` tracks: a decoded sample (mono mixdown) and its hash. */
|
|
113
|
+
export type AnalysisSource = Readonly<{
|
|
114
|
+
sha256: string;
|
|
115
|
+
sampleRate: number;
|
|
116
|
+
mono: ArrayLike<number>;
|
|
117
|
+
}>;
|
|
118
|
+
|
|
119
|
+
export type PitchCurveOptions = Readonly<{
|
|
120
|
+
voice?: PitchVoice;
|
|
121
|
+
/** Project root; without one only the memory cache is used. */
|
|
122
|
+
projectRoot?: string;
|
|
123
|
+
/** Overrides `<projectRoot>/.dawg/analysis`. */
|
|
124
|
+
dir?: string;
|
|
125
|
+
/** Disk budget (default ANALYSIS_CACHE_BYTES). */
|
|
126
|
+
maxBytes?: number;
|
|
127
|
+
/** Fraction of frames tracked so far, on a cache miss. */
|
|
128
|
+
progress?: (done: number) => void;
|
|
129
|
+
}>;
|
|
130
|
+
|
|
131
|
+
const memory = new Map<string, PitchCurve>();
|
|
132
|
+
const counters = { tracks: 0, diskHits: 0, memoryHits: 0 };
|
|
133
|
+
|
|
134
|
+
/** Tracking and cache-hit counts, for tests and the doctor. */
|
|
135
|
+
export function analysisCounters(): Readonly<typeof counters> {
|
|
136
|
+
return { ...counters };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Drops the in-memory curves (tests; the disk cache stays). */
|
|
140
|
+
export function clearAnalysisMemory(): void {
|
|
141
|
+
memory.clear();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function remember(key: string, curve: PitchCurve): void {
|
|
145
|
+
memory.delete(key);
|
|
146
|
+
memory.set(key, curve);
|
|
147
|
+
while (memory.size > ANALYSIS_MEMORY_ENTRIES)
|
|
148
|
+
memory.delete(memory.keys().next().value!);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** The memory-cached curve of a sample, without tracking. */
|
|
152
|
+
export function cachedPitchCurve(
|
|
153
|
+
sha256: string,
|
|
154
|
+
voice: PitchVoice = "auto",
|
|
155
|
+
): PitchCurve | undefined {
|
|
156
|
+
const key = analysisFileName(sha256, voice);
|
|
157
|
+
const hit = memory.get(key);
|
|
158
|
+
if (hit) remember(key, hit);
|
|
159
|
+
return hit;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The pitch curve of a decoded sample: memory, then disk, then tracked and
|
|
164
|
+
* written back. Every path returns identical curves.
|
|
165
|
+
*/
|
|
166
|
+
export async function pitchCurve(
|
|
167
|
+
source: AnalysisSource,
|
|
168
|
+
options: PitchCurveOptions = {},
|
|
169
|
+
): Promise<PitchCurve> {
|
|
170
|
+
const voice = options.voice ?? "auto";
|
|
171
|
+
const name = analysisFileName(source.sha256, voice);
|
|
172
|
+
const hit = memory.get(name);
|
|
173
|
+
if (hit) {
|
|
174
|
+
counters.memoryHits += 1;
|
|
175
|
+
remember(name, hit);
|
|
176
|
+
return hit;
|
|
177
|
+
}
|
|
178
|
+
const dir =
|
|
179
|
+
options.dir ??
|
|
180
|
+
(options.projectRoot ? analysisDir(options.projectRoot) : undefined);
|
|
181
|
+
if (dir) {
|
|
182
|
+
const path = join(dir, name);
|
|
183
|
+
const bytes = await readFile(path).catch(() => undefined);
|
|
184
|
+
const curve = bytes ? decodeCurve(new Uint8Array(bytes)) : undefined;
|
|
185
|
+
if (curve) {
|
|
186
|
+
counters.diskHits += 1;
|
|
187
|
+
const now = new Date();
|
|
188
|
+
await utimes(path, now, now).catch(() => undefined);
|
|
189
|
+
remember(name, curve);
|
|
190
|
+
return curve;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
counters.tracks += 1;
|
|
194
|
+
const curve = await trackPitchAsync(
|
|
195
|
+
Float64Array.from(source.mono),
|
|
196
|
+
source.sampleRate,
|
|
197
|
+
{ voice },
|
|
198
|
+
options.progress,
|
|
199
|
+
);
|
|
200
|
+
remember(name, curve);
|
|
201
|
+
if (dir) await writeCurve(dir, name, curve, options.maxBytes, source.sha256);
|
|
202
|
+
return curve;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function writeCurve(
|
|
206
|
+
dir: string,
|
|
207
|
+
name: string,
|
|
208
|
+
curve: PitchCurve,
|
|
209
|
+
maxBytes = ANALYSIS_CACHE_BYTES,
|
|
210
|
+
keep: string,
|
|
211
|
+
): Promise<void> {
|
|
212
|
+
try {
|
|
213
|
+
await mkdir(dir, { recursive: true });
|
|
214
|
+
const path = join(dir, name);
|
|
215
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
216
|
+
await writeFile(temporary, encodeCurve(curve));
|
|
217
|
+
await rename(temporary, path);
|
|
218
|
+
await pruneAnalysisCache(dir, maxBytes, new Set([keep]));
|
|
219
|
+
} catch {
|
|
220
|
+
/* the cache is an optimisation; a read-only project still renders */
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Deletes least recently used curves until `dir` fits `maxBytes`, never
|
|
226
|
+
* the ones whose sha256 is in `keep` (the open project's samples).
|
|
227
|
+
*/
|
|
228
|
+
export async function pruneAnalysisCache(
|
|
229
|
+
dir: string,
|
|
230
|
+
maxBytes = ANALYSIS_CACHE_BYTES,
|
|
231
|
+
keep: ReadonlySet<string> = new Set(),
|
|
232
|
+
): Promise<PruneResult> {
|
|
233
|
+
return pruneLru(dir, ANALYSIS_FILE, maxBytes, (name) =>
|
|
234
|
+
keep.has(name.slice(0, 64)),
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Files and bytes in the project's analysis cache, for `/pack cache`. */
|
|
239
|
+
export async function analysisCacheStatus(
|
|
240
|
+
projectRoot: string,
|
|
241
|
+
): Promise<CacheUsage & { max: number }> {
|
|
242
|
+
return {
|
|
243
|
+
...(await cacheUsage(analysisDir(projectRoot), ANALYSIS_FILE)),
|
|
244
|
+
max: ANALYSIS_CACHE_BYTES,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** The `dawg media doctor` line for the project's analysis cache. */
|
|
249
|
+
export async function analysisDoctorLine(projectRoot: string): Promise<string> {
|
|
250
|
+
const s = await analysisCacheStatus(projectRoot);
|
|
251
|
+
const mb = (bytes: number): string => (bytes / (1024 * 1024)).toFixed(1);
|
|
252
|
+
return `· analysis cache .dawg/analysis · ${s.files} file${s.files === 1 ? "" : "s"} · ${mb(s.bytes)} MB of ${mb(s.max)} MB (least recently used pruned) · pitch tracker v${PITCH_TRACKER_VERSION}`;
|
|
253
|
+
}
|
package/src/audio/arrange.ts
CHANGED
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
* that carries held notes and tails across the seam, so memory stays
|
|
9
9
|
* bounded however long the form plays (up to MAX_SONG_SECONDS).
|
|
10
10
|
*/
|
|
11
|
+
import { withClipLengths } from "./clips.ts";
|
|
12
|
+
import { vocoderPrerollSeconds } from "../../core/vocoder.ts";
|
|
11
13
|
import {
|
|
12
14
|
SCORE_LIMITS,
|
|
13
15
|
ScoreValidationError,
|
|
14
16
|
TrackScore,
|
|
15
17
|
type Note,
|
|
18
|
+
type Section,
|
|
16
19
|
withNoteCap,
|
|
17
20
|
} from "../../core/score.ts";
|
|
18
21
|
import {
|
|
@@ -101,8 +104,17 @@ export function sectionCues(
|
|
|
101
104
|
}));
|
|
102
105
|
}
|
|
103
106
|
|
|
104
|
-
/**
|
|
105
|
-
|
|
107
|
+
/**
|
|
108
|
+
* The bars playback loops: the loop range (as an unnamed section with no
|
|
109
|
+
* mutes or variations), else the section `loopSection` names. A range is
|
|
110
|
+
* cut to the song's bars; one wholly past the end loops nothing.
|
|
111
|
+
*/
|
|
112
|
+
export function loopedSection(score: TrackScore): Section | undefined {
|
|
113
|
+
if (score.loop) {
|
|
114
|
+
const startBar = Math.min(score.loop.startBar, score.bars - 1);
|
|
115
|
+
const bars = Math.max(1, Math.min(score.loop.bars, score.bars - startBar));
|
|
116
|
+
return { name: "loop", startBar, bars };
|
|
117
|
+
}
|
|
106
118
|
return score.loopSection === undefined
|
|
107
119
|
? undefined
|
|
108
120
|
: findSection(score, score.loopSection);
|
|
@@ -175,6 +187,8 @@ export function renderArranged(
|
|
|
175
187
|
score: TrackScore,
|
|
176
188
|
options: RenderOptions = {},
|
|
177
189
|
): RenderedAudio {
|
|
190
|
+
// Clips cut at sections, forms and windows need their files' real ends.
|
|
191
|
+
score = withClipLengths(score, options.samples);
|
|
178
192
|
if (
|
|
179
193
|
!(options.loop && loopedSection(score)) &&
|
|
180
194
|
formSegments(score).length === 0 &&
|
|
@@ -331,8 +345,13 @@ function renderWindows(
|
|
|
331
345
|
const slowBar = ((beatsPerBar * 60) / Math.min(...bpms)) * fermataStretch;
|
|
332
346
|
const frameAt = (bar: number) =>
|
|
333
347
|
Math.round(secondsAtTick(timeline, bar * ticks) * sampleRate);
|
|
334
|
-
|
|
335
|
-
const
|
|
348
|
+
// A vocoder's envelopes need 5 releases (+0.1 s) to settle (0.7).
|
|
349
|
+
const overhang = Math.max(
|
|
350
|
+
OVERHANG_SECONDS,
|
|
351
|
+
...score.tracks.map((track) => vocoderPrerollSeconds(track.vocoder)),
|
|
352
|
+
);
|
|
353
|
+
const ring = Math.max(1, Math.ceil(overhang / fastBar));
|
|
354
|
+
const ringTicks = Math.ceil((overhang / fastBar) * ticks);
|
|
336
355
|
const budget = Math.max(1, Math.floor(WINDOW_BUDGET_SECONDS / slowBar));
|
|
337
356
|
const reach = budget - 1;
|
|
338
357
|
const most = Math.max(1, Math.floor(WINDOW_SECONDS / slowBar));
|
|
@@ -347,6 +366,10 @@ function renderWindows(
|
|
|
347
366
|
/** Frames at the end that fade out under the next window. */
|
|
348
367
|
fadeOut: number;
|
|
349
368
|
}[] = [];
|
|
369
|
+
const nonFinite: { samples: number; tracks: string[] } = {
|
|
370
|
+
samples: 0,
|
|
371
|
+
tracks: [],
|
|
372
|
+
};
|
|
350
373
|
for (let start = 0; start < totalBars;) {
|
|
351
374
|
const startTick = start * ticks;
|
|
352
375
|
// Every note up to the next window, in render order: tracks sum in the
|
|
@@ -429,6 +452,11 @@ function renderWindows(
|
|
|
429
452
|
secondsAtTick(timeline, pre * ticks),
|
|
430
453
|
),
|
|
431
454
|
});
|
|
455
|
+
if (audio.nonFinite) {
|
|
456
|
+
nonFinite.samples += audio.nonFinite.samples;
|
|
457
|
+
for (const id of audio.nonFinite.tracks)
|
|
458
|
+
if (!nonFinite.tracks.includes(id)) nonFinite.tracks.push(id);
|
|
459
|
+
}
|
|
432
460
|
// A restarted note's phase differs from the previous window's, so the
|
|
433
461
|
// seam crossfades over the previous window's last frames instead of
|
|
434
462
|
// stepping; seams without a restart join sample for sample.
|
|
@@ -498,6 +526,15 @@ function renderWindows(
|
|
|
498
526
|
master,
|
|
499
527
|
options.loop === true,
|
|
500
528
|
);
|
|
529
|
+
const report =
|
|
530
|
+
nonFinite.samples > 0
|
|
531
|
+
? {
|
|
532
|
+
nonFinite: {
|
|
533
|
+
samples: nonFinite.samples,
|
|
534
|
+
tracks: [...nonFinite.tracks],
|
|
535
|
+
},
|
|
536
|
+
}
|
|
537
|
+
: {};
|
|
501
538
|
if (mastered)
|
|
502
539
|
return {
|
|
503
540
|
sampleRate,
|
|
@@ -505,6 +542,13 @@ function renderWindows(
|
|
|
505
542
|
frames: length,
|
|
506
543
|
pcm: mastered.pcm,
|
|
507
544
|
master: mastered.report,
|
|
545
|
+
...report,
|
|
508
546
|
};
|
|
509
|
-
return {
|
|
547
|
+
return {
|
|
548
|
+
sampleRate,
|
|
549
|
+
channels: RENDER_CHANNELS,
|
|
550
|
+
frames: length,
|
|
551
|
+
pcm,
|
|
552
|
+
...report,
|
|
553
|
+
};
|
|
510
554
|
}
|