@hraness/dawg 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +126 -0
- package/DAWG.md +655 -141
- package/README.md +27 -25
- package/core/autotune.ts +1119 -0
- package/core/chords.ts +759 -23
- package/core/clips.ts +499 -0
- package/core/diff.ts +182 -99
- package/core/expression.ts +158 -6
- package/core/fx.ts +354 -32
- package/core/granular.ts +91 -0
- package/core/instruments.ts +180 -0
- package/core/keys.ts +573 -4
- package/core/loop.ts +5 -0
- package/core/lyrics.ts +297 -0
- package/core/master.ts +3 -3
- package/core/resonators.ts +306 -4
- package/core/routing.ts +165 -0
- package/core/score.ts +1181 -16
- package/core/sdk/eval-child.ts +7 -2
- package/core/sdk/eval.ts +35 -6
- package/core/sdk/print.ts +354 -6
- package/core/sdk/sync-lyrics.ts +49 -0
- package/core/sdk/v1.ts +3160 -69
- package/core/sections.ts +298 -24
- package/core/sing.ts +815 -0
- package/core/strings.ts +237 -2
- 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/core/winds.ts +652 -0
- package/guides/agent.md +29 -0
- package/guides/arrange.md +30 -0
- package/guides/audition.md +20 -14
- package/guides/automation.md +12 -6
- package/guides/chords.md +16 -14
- package/guides/effects.md +17 -15
- 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 +12 -6
- package/guides/music.md +23 -8
- package/guides/notes.md +15 -9
- package/guides/performance.md +15 -10
- package/guides/play.md +21 -13
- package/guides/project.md +25 -8
- package/guides/providers.md +20 -14
- package/guides/resample.md +32 -0
- package/guides/rhythm.md +17 -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 -12
- package/guides/styles.md +31 -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 +5 -2
- package/src/agent/agent.ts +131 -13
- package/src/agent/calibration-tools.ts +53 -0
- package/src/agent/chord-tools.ts +153 -1
- package/src/agent/clip-tools.ts +453 -0
- package/src/agent/command-agent.ts +369 -0
- package/src/agent/drum-tools.ts +2 -2
- package/src/agent/expression-tools.ts +91 -0
- package/src/agent/gateway.ts +246 -60
- package/src/agent/models.ts +53 -12
- package/src/agent/ops.ts +15 -1
- package/src/agent/pack-tools.ts +1 -1
- package/src/agent/planner.ts +13 -0
- package/src/agent/portable-schema.ts +80 -0
- package/src/agent/preview-tool.ts +5 -1
- package/src/agent/provider.ts +22 -8
- package/src/agent/resample-tool.ts +131 -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 +307 -37
- package/src/agent/usage.ts +2 -2
- package/src/agent/voice-tools.ts +925 -0
- package/src/agent/xcb-agent.ts +16 -6
- package/src/argv.ts +38 -0
- package/src/audio/analysis.ts +253 -0
- package/src/audio/arrange.ts +58 -3
- 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/doctor.ts +86 -0
- package/src/audio/dsp/bandbank.ts +138 -0
- package/src/audio/dsp/envelope.ts +171 -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/dsp/shift.ts +256 -0
- package/src/audio/effects/chain.ts +21 -4
- package/src/audio/effects/common.ts +57 -0
- package/src/audio/effects/convolution.ts +113 -5
- package/src/audio/effects/filter.ts +48 -69
- package/src/audio/effects/formant.ts +263 -0
- package/src/audio/effects/gaze.ts +354 -0
- package/src/audio/engine.ts +206 -33
- package/src/audio/fit.ts +80 -3
- package/src/audio/granular.ts +352 -21
- package/src/audio/instrument-check.ts +67 -47
- package/src/audio/instruments.ts +36 -3
- package/src/audio/keys/calibration.ts +56 -0
- package/src/audio/keys/electric.ts +427 -0
- package/src/audio/keys/engine.ts +156 -20
- package/src/audio/keys/organ.ts +1335 -0
- package/src/audio/keys/piano.ts +72 -8
- package/src/audio/keys/sympathetic.ts +127 -0
- package/src/audio/kits.ts +135 -6
- package/src/audio/live-worker.ts +3 -1
- package/src/audio/live.ts +247 -26
- package/src/audio/native.ts +615 -0
- package/src/audio/preview.ts +45 -3
- package/src/audio/render-worker.ts +2 -0
- package/src/audio/renderer.ts +2 -0
- package/src/audio/resample.ts +286 -0
- package/src/audio/resonators.ts +10 -2
- package/src/audio/sampler.ts +252 -24
- package/src/audio/samples.ts +46 -3
- package/src/audio/sing/analysis.ts +193 -0
- package/src/audio/sing/engine.ts +949 -0
- package/src/audio/strings/body.ts +29 -16
- package/src/audio/strings/bow.ts +699 -0
- package/src/audio/strings/engine.ts +274 -22
- package/src/audio/strings/measure.test-helpers.ts +2 -0
- 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 +608 -85
- package/src/audio/winds/engine.ts +306 -0
- package/src/audio/winds/filters.ts +153 -0
- package/src/audio/winds/pitch.ts +104 -0
- package/src/audio/winds/trim.ts +104 -0
- package/src/audio/winds/trims.ts +917 -0
- package/src/audio/winds/trims1.ts +297 -0
- package/src/audio/winds/voice.ts +492 -0
- 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 +44 -29
- 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 +104 -23
- package/src/commands/formant.ts +221 -0
- package/src/commands/fx.ts +105 -33
- package/src/commands/grammar.ts +558 -0
- package/src/commands/granular.ts +7 -2
- package/src/commands/help.ts +652 -339
- package/src/commands/history.ts +18 -0
- package/src/commands/keys.ts +293 -17
- package/src/commands/modal.ts +67 -4
- 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 +130 -0
- package/src/commands/progression.ts +170 -0
- package/src/commands/resample.ts +281 -0
- package/src/commands/rhythm.ts +3 -0
- package/src/commands/rig.ts +8 -25
- package/src/commands/sample.ts +49 -2
- package/src/commands/shift.ts +119 -0
- package/src/commands/sing.ts +478 -0
- package/src/commands/string.ts +28 -2
- package/src/commands/strum.ts +485 -0
- 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 +616 -0
- package/src/commands/vocal.ts +147 -0
- package/src/commands/vocoder.ts +627 -0
- package/src/commands/wind.ts +244 -0
- package/src/fs/durable.ts +50 -0
- package/src/lang/glossary.ts +493 -0
- package/src/launch-args.ts +163 -0
- package/src/main.ts +1521 -255
- package/src/media/cli.ts +20 -3
- package/src/media/import.ts +3 -1
- package/src/project/check.ts +26 -3
- 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 +36 -1
- package/src/session/daemon.ts +3 -0
- package/src/session/meta.ts +14 -0
- package/src/session/origin.ts +154 -0
- package/src/session/port.ts +22 -4
- package/src/session/presence.ts +50 -7
- package/src/session/protocol.ts +5 -1
- package/src/session/rebase.ts +18 -5
- package/src/session/receipt.ts +258 -0
- package/src/session/store.ts +65 -43
- package/src/tui/arrange-menu.ts +65 -31
- package/src/tui/audition.ts +1 -1
- package/src/tui/euclid.ts +18 -13
- package/src/tui/fader.ts +228 -41
- package/src/tui/granular-menu.ts +2 -4
- 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 +1377 -193
- package/src/tui/modal-menu.ts +38 -11
- package/src/tui/performance-menu.ts +46 -1
- package/src/tui/play-chords.ts +108 -5
- package/src/tui/play-mode.ts +16 -1
- package/src/tui/play-session.ts +81 -16
- package/src/tui/sing-menu.ts +278 -0
- package/src/tui/style-menu.ts +104 -0
- package/src/tui/vocoder-menu.ts +244 -0
- package/src/tui/wind-menu.ts +144 -0
- package/src/version.ts +8 -0
- package/src/web/fetch.ts +115 -29
- package/tui/activity.ts +180 -9
- package/tui/app.ts +274 -40
- package/tui/clip-row.ts +132 -0
- package/tui/delight.ts +144 -0
- package/tui/drawer.ts +70 -22
- package/tui/frame-gate.ts +76 -0
- package/tui/grammar.ts +112 -63
- package/tui/guide.ts +42 -4
- package/tui/highway.ts +269 -25
- package/tui/hints.ts +192 -0
- package/tui/input.ts +60 -9
- package/tui/keys.ts +1 -1
- package/tui/play-strip.ts +68 -14
- package/tui/prompt.ts +1 -1
- package/tui/screen.ts +144 -14
- package/tui/theme.ts +27 -0
package/src/audio/keys/engine.ts
CHANGED
|
@@ -16,14 +16,21 @@
|
|
|
16
16
|
* - Post: the family's body EQ per channel (the minimal per-track post
|
|
17
17
|
* hook), then the track volume, before the existing effects chain.
|
|
18
18
|
*/
|
|
19
|
-
import type
|
|
20
|
-
import {
|
|
19
|
+
import { pedalStateAt, type PerformedNote } from "../../../core/expression.ts";
|
|
20
|
+
import {
|
|
21
|
+
isElectricFamily,
|
|
22
|
+
isPianoFamily,
|
|
23
|
+
resolvedKeys,
|
|
24
|
+
type ElectricFamily,
|
|
25
|
+
} from "../../../core/keys.ts";
|
|
21
26
|
import type { Track } from "../../../core/score.ts";
|
|
22
27
|
import { noteHz } from "../../../core/tuning.ts";
|
|
23
28
|
import type { FxLane } from "../../../core/fx.ts";
|
|
24
29
|
import { interpolateAutomation } from "../effects/common.ts";
|
|
25
30
|
import type { EngineContext, InstrumentEngine } from "../instruments.ts";
|
|
26
31
|
import { Biquad2, clamp, CONTROL, LN1000 } from "./dsp.ts";
|
|
32
|
+
import { ORGAN_ENGINES } from "./organ.ts";
|
|
33
|
+
import { keysTrim } from "./calibration.ts";
|
|
27
34
|
import {
|
|
28
35
|
damperT60,
|
|
29
36
|
physicalKey,
|
|
@@ -34,6 +41,15 @@ import {
|
|
|
34
41
|
trackSeedOf,
|
|
35
42
|
type PianoParams,
|
|
36
43
|
} from "./piano.ts";
|
|
44
|
+
import {
|
|
45
|
+
clavReleaseT60,
|
|
46
|
+
electricPost,
|
|
47
|
+
electricVoice,
|
|
48
|
+
tineReleaseT60,
|
|
49
|
+
type ElectricParams,
|
|
50
|
+
type KeysVoice,
|
|
51
|
+
} from "./electric.ts";
|
|
52
|
+
import { Sympathetic } from "./sympathetic.ts";
|
|
37
53
|
|
|
38
54
|
export const KEYS_LIMITS = Object.freeze({
|
|
39
55
|
/** Per-track polyphony cap (voice stealing beyond it). */
|
|
@@ -98,6 +114,39 @@ export function pianoParamsAt(
|
|
|
98
114
|
};
|
|
99
115
|
}
|
|
100
116
|
|
|
117
|
+
/** Electric voice parameters for a note: the resolved keys plus lanes. */
|
|
118
|
+
export function electricParamsAt(
|
|
119
|
+
track: Track,
|
|
120
|
+
values: Values,
|
|
121
|
+
tick: number,
|
|
122
|
+
kind: ElectricFamily,
|
|
123
|
+
): ElectricParams {
|
|
124
|
+
const lane = (name: string) => {
|
|
125
|
+
const points = track.fxAutomation?.[`keys-${name}` as FxLane];
|
|
126
|
+
const base = num(values, name);
|
|
127
|
+
return points && points.length > 0
|
|
128
|
+
? interpolateAutomation(points, tick, base)
|
|
129
|
+
: base;
|
|
130
|
+
};
|
|
131
|
+
const pickup = values.pickup;
|
|
132
|
+
return {
|
|
133
|
+
kind,
|
|
134
|
+
hardness: clamp(lane("hardness"), 0, 1),
|
|
135
|
+
touch: clamp(lane("touch"), 0, 1),
|
|
136
|
+
decay: clamp(lane("decay"), 0.1, 4),
|
|
137
|
+
release: clamp(lane("release"), 0.1, 4),
|
|
138
|
+
width: num(values, "width"),
|
|
139
|
+
bark: num(values, "bark"),
|
|
140
|
+
bell: num(values, "bell"),
|
|
141
|
+
tone: clamp(lane("tone"), 0, 12000),
|
|
142
|
+
pickup:
|
|
143
|
+
pickup === "neck" || pickup === "bridge" || pickup === "out"
|
|
144
|
+
? pickup
|
|
145
|
+
: "both",
|
|
146
|
+
mute: num(values, "mute"),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
101
150
|
/** Body voicing name for a track (the stored `body`, else the family's). */
|
|
102
151
|
export function bodyOf(values: Values): string {
|
|
103
152
|
const body = values.body;
|
|
@@ -105,7 +154,9 @@ export function bodyOf(values: Values): string {
|
|
|
105
154
|
}
|
|
106
155
|
|
|
107
156
|
type Live = {
|
|
108
|
-
voice:
|
|
157
|
+
voice: KeysVoice;
|
|
158
|
+
/** Calibration 1 level trim (1 for legacy songs). */
|
|
159
|
+
trim: number;
|
|
109
160
|
/** Sample index the voice started at. */
|
|
110
161
|
start: number;
|
|
111
162
|
/** Sample index of key-up. */
|
|
@@ -161,10 +212,14 @@ export function renderKeysTrack(
|
|
|
161
212
|
const sr = context.sampleRate;
|
|
162
213
|
const total = left.length;
|
|
163
214
|
const values = resolvedKeys(track.instrument ?? "grand", track.keys);
|
|
215
|
+
const electric = isElectricFamily(track.instrument)
|
|
216
|
+
? track.instrument
|
|
217
|
+
: undefined;
|
|
164
218
|
const trackSeed = trackSeedOf(track.id);
|
|
165
219
|
const seedTick = context.seedTick ?? 0;
|
|
166
220
|
const window = Math.round(KEYS_LIMITS.tailSeconds * sr);
|
|
167
221
|
const taper = Math.round(KEYS_LIMITS.taperSeconds * sr);
|
|
222
|
+
const calibrated = (context.score.calibration ?? 0) >= 1;
|
|
168
223
|
const onsets: Onset[] = [];
|
|
169
224
|
notes.forEach((note) => {
|
|
170
225
|
const hz = noteHz(note.pitch, note.cents, context.tuning);
|
|
@@ -204,18 +259,37 @@ export function renderKeysTrack(
|
|
|
204
259
|
victim.stolen = true;
|
|
205
260
|
}
|
|
206
261
|
const note = onset.note;
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
262
|
+
// Una corda (0.6.1) at the onset: down is the full shift, half half.
|
|
263
|
+
const softState = track.softPedal
|
|
264
|
+
? pedalStateAt(track.softPedal, note.startTick)
|
|
265
|
+
: "up";
|
|
266
|
+
const soft = softState === "down" ? 1 : softState === "half" ? 0.5 : 0;
|
|
267
|
+
const noteOn = {
|
|
268
|
+
pitch: note.pitch,
|
|
269
|
+
hz: onset.hz,
|
|
270
|
+
velocity: clamp(note.velocity, 0, 1),
|
|
271
|
+
trackSeed,
|
|
272
|
+
noteSeed: `${track.id}:${note.id}:${note.startTick + seedTick}`,
|
|
273
|
+
...(soft > 0 ? { soft } : {}),
|
|
274
|
+
...(calibrated ? { calibration: 1 } : {}),
|
|
275
|
+
};
|
|
276
|
+
const trim = calibrated
|
|
277
|
+
? keysTrim(
|
|
278
|
+
track.keys?.preset ?? track.instrument ?? "grand",
|
|
279
|
+
note.pitch,
|
|
280
|
+
)
|
|
281
|
+
: 1;
|
|
282
|
+
const voice: KeysVoice = electric
|
|
283
|
+
? electricVoice(
|
|
284
|
+
noteOn,
|
|
285
|
+
electricParamsAt(track, values, note.startTick, electric),
|
|
286
|
+
sr,
|
|
287
|
+
)
|
|
288
|
+
: new PianoVoice(
|
|
289
|
+
noteOn,
|
|
290
|
+
pianoParamsAt(track, values, note.startTick),
|
|
291
|
+
sr,
|
|
292
|
+
);
|
|
219
293
|
const performance = note.performance;
|
|
220
294
|
// The `vib`/`vibmod` wow (lofi) unless the note brings its own vibrato.
|
|
221
295
|
const vib = num(values, "vib");
|
|
@@ -229,6 +303,7 @@ export function renderKeysTrack(
|
|
|
229
303
|
bend && wow ? (t: number) => bend(t) + wow(t) : (bend ?? wow);
|
|
230
304
|
live.push({
|
|
231
305
|
voice,
|
|
306
|
+
trim,
|
|
232
307
|
start: onset.start,
|
|
233
308
|
off: onset.off,
|
|
234
309
|
end: onset.off + window,
|
|
@@ -268,7 +343,7 @@ export function renderKeysTrack(
|
|
|
268
343
|
gain *= Math.max(0, x.fade) / x.fadeLength;
|
|
269
344
|
x.fade -= 1;
|
|
270
345
|
}
|
|
271
|
-
const y = block[s]! * gain;
|
|
346
|
+
const y = block[s]! * gain * x.trim;
|
|
272
347
|
left[index]! += y * gl;
|
|
273
348
|
right[index]! += y * gr;
|
|
274
349
|
}
|
|
@@ -277,6 +352,50 @@ export function renderKeysTrack(
|
|
|
277
352
|
if (!alive) live.splice(k, 1);
|
|
278
353
|
}
|
|
279
354
|
}
|
|
355
|
+
if (electric) {
|
|
356
|
+
// The electric post: suitcase vibrato or reed tremolo, then volume.
|
|
357
|
+
const param = electric === "wurli" ? "trem" : "vibe";
|
|
358
|
+
const points = track.fxAutomation?.[`keys-${param}` as FxLane];
|
|
359
|
+
const base = num(values, param);
|
|
360
|
+
const tickOf = (sample: number) =>
|
|
361
|
+
context.warp
|
|
362
|
+
? context.warp.tick(sample)
|
|
363
|
+
: sample / context.samplesPerTick;
|
|
364
|
+
const depthAt =
|
|
365
|
+
points && points.length > 0
|
|
366
|
+
? (sample: number) =>
|
|
367
|
+
interpolateAutomation(points, tickOf(sample), base)
|
|
368
|
+
: () => base;
|
|
369
|
+
const rate = electric === "wurli" ? 5.6 : num(values, "vibehz") || 4;
|
|
370
|
+
const startSample = Math.round(seedTick * context.samplesPerTick);
|
|
371
|
+
electricPost(left, right, electric, rate, depthAt, sr, startSample);
|
|
372
|
+
applyVolume(left, right, track, context);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
// Sympathetic resonance (0.6.1): only with `sym` and sustain pedal events.
|
|
376
|
+
const sym = num(values, "sym");
|
|
377
|
+
const pedal = track.pedal;
|
|
378
|
+
if (sym > 0 && pedal?.some((event) => event.state !== "up")) {
|
|
379
|
+
const bank = new Sympathetic(
|
|
380
|
+
(pitch) => noteHz(pitch, undefined, context.tuning),
|
|
381
|
+
sym,
|
|
382
|
+
sr,
|
|
383
|
+
);
|
|
384
|
+
// Pedal changes as samples; the bank reads them in order.
|
|
385
|
+
const changes = pedal.map((event) => ({
|
|
386
|
+
at: context.warp
|
|
387
|
+
? context.warp.sample(event.tick)
|
|
388
|
+
: event.tick * context.samplesPerTick,
|
|
389
|
+
down: event.state !== "up",
|
|
390
|
+
}));
|
|
391
|
+
let cursor = 0;
|
|
392
|
+
let down = false;
|
|
393
|
+
bank.process(left, right, (sample) => {
|
|
394
|
+
while (cursor < changes.length && changes[cursor]!.at <= sample)
|
|
395
|
+
down = changes[cursor++]!.down;
|
|
396
|
+
return down;
|
|
397
|
+
});
|
|
398
|
+
}
|
|
280
399
|
// The per-track post hook: body EQ, then the track volume.
|
|
281
400
|
const body = bodyOf(values);
|
|
282
401
|
const eqL = pianoBody(body, sr);
|
|
@@ -329,6 +448,14 @@ function applyVolume(
|
|
|
329
448
|
export function keysReleaseSeconds(track: Track, hz: number): number {
|
|
330
449
|
if (!(hz > 0) || !Number.isFinite(hz)) return 0.01;
|
|
331
450
|
const values = resolvedKeys(track.instrument ?? "grand", track.keys);
|
|
451
|
+
if (isElectricFamily(track.instrument)) {
|
|
452
|
+
const release = clamp(num(values, "release"), 0.1, 4);
|
|
453
|
+
const t60 =
|
|
454
|
+
track.instrument === "clav"
|
|
455
|
+
? clavReleaseT60(release)
|
|
456
|
+
: tineReleaseT60(physicalKey(hz), release);
|
|
457
|
+
return Math.max(0.01, (3 / (2 * LN1000)) * t60);
|
|
458
|
+
}
|
|
332
459
|
const p = pianoParamsAt(track, values, 0);
|
|
333
460
|
const key = physicalKey(hz);
|
|
334
461
|
if (undamped(key)) return KEYS_LIMITS.tailSeconds;
|
|
@@ -353,9 +480,18 @@ function engineFor(id: string): InstrumentEngine {
|
|
|
353
480
|
};
|
|
354
481
|
}
|
|
355
482
|
|
|
356
|
-
/**
|
|
483
|
+
/**
|
|
484
|
+
* One engine per keys family (the registry dispatches by instrument id):
|
|
485
|
+
* the pianos, then the electric keys and the organs (0.6.1).
|
|
486
|
+
*/
|
|
357
487
|
export const KEYS_ENGINES: readonly InstrumentEngine[] = Object.freeze(
|
|
358
|
-
[
|
|
359
|
-
.filter((id) =>
|
|
360
|
-
|
|
488
|
+
[
|
|
489
|
+
...["grand", "upright", "felt", "honkytonk", "prepared"].filter((id) =>
|
|
490
|
+
isPianoFamily(id),
|
|
491
|
+
),
|
|
492
|
+
...["epiano", "wurli", "clav"].filter((id) => isElectricFamily(id)),
|
|
493
|
+
]
|
|
494
|
+
.map(engineFor)
|
|
495
|
+
// f061-organ: tonewheel, combo and pipe (src/audio/keys/organ.ts).
|
|
496
|
+
.concat(ORGAN_ENGINES),
|
|
361
497
|
);
|