reframe-video 0.6.26 → 0.6.27

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/dist/diff.js CHANGED
@@ -13,6 +13,30 @@ import { dirname, resolve } from "node:path";
13
13
  import { fileURLToPath } from "node:url";
14
14
 
15
15
  // ../core/src/ir.ts
16
+ var SFX_NAMES = [
17
+ "whoosh",
18
+ "swish",
19
+ "rise",
20
+ "riser",
21
+ "warp",
22
+ "tick",
23
+ "click",
24
+ "blip",
25
+ "pop",
26
+ "select",
27
+ "thud",
28
+ "boom",
29
+ "knock",
30
+ "chime",
31
+ "ding",
32
+ "coin",
33
+ "sparkle",
34
+ "shimmer",
35
+ "success",
36
+ "zap",
37
+ "error"
38
+ ];
39
+ var BGM_SYNTHS = ["ambient-pad", "lofi", "pulse", "tension", "uplift"];
16
40
  var DEFAULT_TO_DURATION = 0.5;
17
41
  var DEFAULT_TWEEN_DURATION = 0.5;
18
42
  var DEFAULT_MOTIONPATH_DURATION = 1;
@@ -662,7 +686,6 @@ function validateScene(ir) {
662
686
  }
663
687
  }
664
688
  }
665
- const SFX_NAMES = ["whoosh", "pop", "tick", "rise", "shimmer", "thud"];
666
689
  for (const [i, cue] of (ir.audio?.cues ?? []).entries()) {
667
690
  if (typeof cue.at === "string" && !labels.has(cue.at)) {
668
691
  problems.push(
@@ -698,6 +721,10 @@ function validateScene(ir) {
698
721
  if (ir.audio?.bgm?.file !== void 0 && ir.audio.bgm.synth !== void 0) {
699
722
  problems.push('audio.bgm: use either "file" or "synth", not both');
700
723
  }
724
+ const bgmSynth = ir.audio?.bgm?.synth;
725
+ if (bgmSynth !== void 0 && !BGM_SYNTHS.includes(bgmSynth)) {
726
+ problems.push(`audio.bgm.synth: unknown synth "${bgmSynth}" \u2014 valid: ${BGM_SYNTHS.join(", ")}`);
727
+ }
701
728
  if (problems.length > 0) throw new SceneValidationError(problems);
702
729
  }
703
730
  var TRANSITIONS = ["cut", "crossfade"];
package/dist/frame.js CHANGED
@@ -40,6 +40,30 @@ import { existsSync } from "node:fs";
40
40
  import { extname, isAbsolute, resolve } from "node:path";
41
41
 
42
42
  // ../core/src/ir.ts
43
+ var SFX_NAMES = [
44
+ "whoosh",
45
+ "swish",
46
+ "rise",
47
+ "riser",
48
+ "warp",
49
+ "tick",
50
+ "click",
51
+ "blip",
52
+ "pop",
53
+ "select",
54
+ "thud",
55
+ "boom",
56
+ "knock",
57
+ "chime",
58
+ "ding",
59
+ "coin",
60
+ "sparkle",
61
+ "shimmer",
62
+ "success",
63
+ "zap",
64
+ "error"
65
+ ];
66
+ var BGM_SYNTHS = ["ambient-pad", "lofi", "pulse", "tension", "uplift"];
43
67
  var DEFAULT_TO_DURATION = 0.5;
44
68
  var DEFAULT_TWEEN_DURATION = 0.5;
45
69
  var DEFAULT_MOTIONPATH_DURATION = 1;
@@ -689,7 +713,6 @@ function validateScene(ir) {
689
713
  }
690
714
  }
691
715
  }
692
- const SFX_NAMES = ["whoosh", "pop", "tick", "rise", "shimmer", "thud"];
693
716
  for (const [i, cue] of (ir.audio?.cues ?? []).entries()) {
694
717
  if (typeof cue.at === "string" && !labels.has(cue.at)) {
695
718
  problems.push(
@@ -725,6 +748,10 @@ function validateScene(ir) {
725
748
  if (ir.audio?.bgm?.file !== void 0 && ir.audio.bgm.synth !== void 0) {
726
749
  problems.push('audio.bgm: use either "file" or "synth", not both');
727
750
  }
751
+ const bgmSynth = ir.audio?.bgm?.synth;
752
+ if (bgmSynth !== void 0 && !BGM_SYNTHS.includes(bgmSynth)) {
753
+ problems.push(`audio.bgm.synth: unknown synth "${bgmSynth}" \u2014 valid: ${BGM_SYNTHS.join(", ")}`);
754
+ }
728
755
  if (problems.length > 0) throw new SceneValidationError(problems);
729
756
  }
730
757
  var TRANSITIONS = ["cut", "crossfade"];
package/dist/index.js CHANGED
@@ -1,4 +1,28 @@
1
1
  // ../core/src/ir.ts
2
+ var SFX_NAMES = [
3
+ "whoosh",
4
+ "swish",
5
+ "rise",
6
+ "riser",
7
+ "warp",
8
+ "tick",
9
+ "click",
10
+ "blip",
11
+ "pop",
12
+ "select",
13
+ "thud",
14
+ "boom",
15
+ "knock",
16
+ "chime",
17
+ "ding",
18
+ "coin",
19
+ "sparkle",
20
+ "shimmer",
21
+ "success",
22
+ "zap",
23
+ "error"
24
+ ];
25
+ var BGM_SYNTHS = ["ambient-pad", "lofi", "pulse", "tension", "uplift"];
2
26
  var DEFAULT_CROSSFADE = 0.5;
3
27
  var DEFAULT_TO_DURATION = 0.5;
4
28
  var DEFAULT_TWEEN_DURATION = 0.5;
@@ -785,7 +809,6 @@ function validateScene(ir) {
785
809
  }
786
810
  }
787
811
  }
788
- const SFX_NAMES = ["whoosh", "pop", "tick", "rise", "shimmer", "thud"];
789
812
  for (const [i, cue] of (ir.audio?.cues ?? []).entries()) {
790
813
  if (typeof cue.at === "string" && !labels.has(cue.at)) {
791
814
  problems.push(
@@ -821,6 +844,10 @@ function validateScene(ir) {
821
844
  if (ir.audio?.bgm?.file !== void 0 && ir.audio.bgm.synth !== void 0) {
822
845
  problems.push('audio.bgm: use either "file" or "synth", not both');
823
846
  }
847
+ const bgmSynth = ir.audio?.bgm?.synth;
848
+ if (bgmSynth !== void 0 && !BGM_SYNTHS.includes(bgmSynth)) {
849
+ problems.push(`audio.bgm.synth: unknown synth "${bgmSynth}" \u2014 valid: ${BGM_SYNTHS.join(", ")}`);
850
+ }
824
851
  if (problems.length > 0) throw new SceneValidationError(problems);
825
852
  }
826
853
  var TRANSITIONS = ["cut", "crossfade"];
@@ -2889,12 +2916,32 @@ function motionOp(name, target, opts = {}) {
2889
2916
 
2890
2917
  // ../core/src/audio.ts
2891
2918
  var SFX_DURATION = {
2919
+ // transition
2892
2920
  whoosh: 0.35,
2893
- pop: 0.12,
2894
- tick: 0.03,
2921
+ swish: 0.32,
2895
2922
  rise: 0.5,
2923
+ riser: 0.85,
2924
+ warp: 0.5,
2925
+ // ui
2926
+ tick: 0.03,
2927
+ click: 0.05,
2928
+ blip: 0.1,
2929
+ pop: 0.12,
2930
+ select: 0.18,
2931
+ // impact
2932
+ thud: 0.25,
2933
+ boom: 0.6,
2934
+ knock: 0.14,
2935
+ // positive
2936
+ chime: 0.7,
2937
+ ding: 0.5,
2938
+ coin: 0.3,
2939
+ sparkle: 0.6,
2896
2940
  shimmer: 0.9,
2897
- thud: 0.25
2941
+ success: 0.6,
2942
+ // alert
2943
+ zap: 0.22,
2944
+ error: 0.4
2898
2945
  };
2899
2946
  var FILE_CUE_DURATION = 0.4;
2900
2947
  function collectClipAudio(ir, duration, warnings) {
@@ -2954,7 +3001,11 @@ function resolveAudioPlan(compiled) {
2954
3001
  fadeIn: cue.fadeIn ?? 0,
2955
3002
  fadeOut: cue.fadeOut ?? 0,
2956
3003
  pan: cue.pan ?? 0,
2957
- source: cue.sfx ? { kind: "sfx", name: cue.sfx, params: cue.params ?? {} } : { kind: "file", path: cue.file }
3004
+ source: cue.sfx ? (
3005
+ // auto-vary: default the seed to the cue's order so repeated sfx differ
3006
+ // (pitch/texture); an explicit params.seed always wins.
3007
+ { kind: "sfx", name: cue.sfx, params: { seed: index, ...cue.params } }
3008
+ ) : { kind: "file", path: cue.file }
2958
3009
  });
2959
3010
  }
2960
3011
  cues.sort((a, b) => a.t - b.t);
@@ -3034,7 +3085,11 @@ function resolveCompositionAudioPlan(comp) {
3034
3085
  fadeIn: cue.fadeIn ?? 0,
3035
3086
  fadeOut: cue.fadeOut ?? 0,
3036
3087
  pan: cue.pan ?? 0,
3037
- source: cue.sfx ? { kind: "sfx", name: cue.sfx, params: cue.params ?? {} } : { kind: "file", path: cue.file }
3088
+ source: cue.sfx ? (
3089
+ // auto-vary: default the seed to the cue's order so repeated sfx differ
3090
+ // (pitch/texture); an explicit params.seed always wins.
3091
+ { kind: "sfx", name: cue.sfx, params: { seed: index, ...cue.params } }
3092
+ ) : { kind: "file", path: cue.file }
3038
3093
  });
3039
3094
  }
3040
3095
  if (!audio?.bgm && cues.length === 0 && clipAudio.length === 0) return null;
@@ -3593,6 +3648,7 @@ function sketchToTimeline(sketch, nodeIds) {
3593
3648
  return par(...steps);
3594
3649
  }
3595
3650
  export {
3651
+ BGM_SYNTHS,
3596
3652
  CAMERA_ID,
3597
3653
  CAMERA_PROPS2 as CAMERA_PROPS,
3598
3654
  CHARACTER_PRESET_NAMES,
@@ -3608,6 +3664,7 @@ export {
3608
3664
  PRESET_NAMES,
3609
3665
  PROPS_BY_TYPE,
3610
3666
  SFX_DURATION,
3667
+ SFX_NAMES,
3611
3668
  SceneValidationError,
3612
3669
  beat,
3613
3670
  cameraFit,
package/dist/labels.js CHANGED
@@ -1,6 +1,30 @@
1
1
  #!/usr/bin/env tsx
2
2
 
3
3
  // ../core/src/ir.ts
4
+ var SFX_NAMES = [
5
+ "whoosh",
6
+ "swish",
7
+ "rise",
8
+ "riser",
9
+ "warp",
10
+ "tick",
11
+ "click",
12
+ "blip",
13
+ "pop",
14
+ "select",
15
+ "thud",
16
+ "boom",
17
+ "knock",
18
+ "chime",
19
+ "ding",
20
+ "coin",
21
+ "sparkle",
22
+ "shimmer",
23
+ "success",
24
+ "zap",
25
+ "error"
26
+ ];
27
+ var BGM_SYNTHS = ["ambient-pad", "lofi", "pulse", "tension", "uplift"];
4
28
  var DEFAULT_TO_DURATION = 0.5;
5
29
  var DEFAULT_TWEEN_DURATION = 0.5;
6
30
  var DEFAULT_MOTIONPATH_DURATION = 1;
@@ -650,7 +674,6 @@ function validateScene(ir) {
650
674
  }
651
675
  }
652
676
  }
653
- const SFX_NAMES = ["whoosh", "pop", "tick", "rise", "shimmer", "thud"];
654
677
  for (const [i, cue] of (ir.audio?.cues ?? []).entries()) {
655
678
  if (typeof cue.at === "string" && !labels.has(cue.at)) {
656
679
  problems.push(
@@ -686,6 +709,10 @@ function validateScene(ir) {
686
709
  if (ir.audio?.bgm?.file !== void 0 && ir.audio.bgm.synth !== void 0) {
687
710
  problems.push('audio.bgm: use either "file" or "synth", not both');
688
711
  }
712
+ const bgmSynth = ir.audio?.bgm?.synth;
713
+ if (bgmSynth !== void 0 && !BGM_SYNTHS.includes(bgmSynth)) {
714
+ problems.push(`audio.bgm.synth: unknown synth "${bgmSynth}" \u2014 valid: ${BGM_SYNTHS.join(", ")}`);
715
+ }
689
716
  if (problems.length > 0) throw new SceneValidationError(problems);
690
717
  }
691
718
 
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import type { CompiledScene } from "./compile.js";
10
10
  import type { CompiledComposition } from "./composeComposition.js";
11
- import type { SfxName } from "./ir.js";
11
+ import type { BgmSynth, SfxName } from "./ir.js";
12
12
  /** Nominal cue lengths (s) for duck-window math; file cues use a default. */
13
13
  export declare const SFX_DURATION: Record<SfxName, number>;
14
14
  export interface ResolvedCue {
@@ -55,7 +55,7 @@ export interface AudioPlan {
55
55
  path: string;
56
56
  } | {
57
57
  kind: "synth";
58
- name: "ambient-pad";
58
+ name: BgmSynth;
59
59
  };
60
60
  gain: number;
61
61
  fadeIn: number;
@@ -412,7 +412,20 @@ export interface BehaviorIR {
412
412
  };
413
413
  };
414
414
  }
415
- export type SfxName = "whoosh" | "pop" | "tick" | "rise" | "shimmer" | "thud";
415
+ /**
416
+ * The procedural sfx palette — the single source of truth (the type, validation,
417
+ * and the render-cli synth recipes all key off this). Grouped by use:
418
+ * transition: whoosh swish rise riser warp · ui: tick click blip pop select
419
+ * impact: thud boom knock · positive: chime ding coin sparkle shimmer success
420
+ * alert: zap error
421
+ * Every cue's pitch/texture varies with its `seed` (auto-seeded by cue order), so
422
+ * repeated cues sound different; `params.pitch` is an explicit multiplier.
423
+ */
424
+ export declare const SFX_NAMES: readonly ["whoosh", "swish", "rise", "riser", "warp", "tick", "click", "blip", "pop", "select", "thud", "boom", "knock", "chime", "ding", "coin", "sparkle", "shimmer", "success", "zap", "error"];
425
+ export type SfxName = (typeof SFX_NAMES)[number];
426
+ /** Synthesized background-music beds (license-free). */
427
+ export declare const BGM_SYNTHS: readonly ["ambient-pad", "lofi", "pulse", "tension", "uplift"];
428
+ export type BgmSynth = (typeof BGM_SYNTHS)[number];
416
429
  export interface AudioCueIR {
417
430
  /** Anchor: a timeline label (the step's start) or absolute seconds. */
418
431
  at: string | number;
@@ -430,14 +443,18 @@ export interface AudioCueIR {
430
443
  fadeOut?: number;
431
444
  /** Stereo balance: -1 full left, 0 centre (default), +1 full right. */
432
445
  pan?: number;
433
- /** Synth parameter overrides (seed, duration, …) — numbers only. */
446
+ /**
447
+ * Synth parameter overrides — numbers only. `seed` varies pitch/texture
448
+ * (defaults to the cue's order so repeats differ); `pitch` is an explicit
449
+ * frequency multiplier (1 = unchanged, 2 = an octave up); `gainDb` trims level.
450
+ */
434
451
  params?: Record<string, number>;
435
452
  }
436
453
  export interface AudioIR {
437
454
  bgm?: {
438
455
  file?: string;
439
- /** License-free synthesized bed. */
440
- synth?: "ambient-pad";
456
+ /** License-free synthesized bed (see {@link BGM_SYNTHS}). */
457
+ synth?: BgmSynth;
441
458
  gain?: number;
442
459
  fadeIn?: number;
443
460
  fadeOut?: number;
@@ -558,12 +558,28 @@ audio: {
558
558
  }
559
559
  ```
560
560
 
561
- Procedural sfx names: `whoosh` `pop` `tick` `rise` `shimmer` `thud` (deterministic,
562
- seedable via `params: { seed }`). Exactly one of `sfx`/`file` per cue.
563
- **Mixing**: any cue takes `fadeIn`/`fadeOut` (seconds) and `pan` (-1 left … 0 centre …
564
- +1 right). A `video` clip's audio takes `fadeIn` and `pan` too (clip fade-out isn't
565
- supported yet a clip has no fixed length in the plan). The bed auto-ducks under cues
566
- (`bgm.duck`).
561
+ **Procedural sfx palette** (deterministic; exactly one of `sfx`/`file` per cue):
562
+
563
+ | group | names |
564
+ | --- | --- |
565
+ | transition | `whoosh` `swish` `rise` `riser` `warp` |
566
+ | ui | `tick` `click` `blip` `pop` `select` |
567
+ | impact | `thud` `boom` `knock` |
568
+ | positive | `chime` `ding` `coin` `sparkle` `shimmer` `success` |
569
+ | alert | `zap` `error` |
570
+
571
+ **Variation — repeats don't sound the same.** Each cue's `seed` shifts the sound's
572
+ PITCH (a musical step) and texture, and it **defaults to the cue's order**, so a run of
573
+ the same sfx becomes a little phrase instead of a stuck note — no setup needed. Override
574
+ explicitly with `params`: `{ sfx: "blip", params: { seed: 4 } }` (pick the variant) or
575
+ `{ sfx: "tick", params: { pitch: 1.5 } }` (an explicit frequency multiplier; `2` = octave
576
+ up). `params.gainDb` trims a single hit.
577
+
578
+ **bgm beds** (`bgm.synth`): `ambient-pad` `lofi` `pulse` `tension` `uplift` — or
579
+ `bgm.file` for your own. **Mixing**: any cue takes `fadeIn`/`fadeOut` (seconds) and `pan`
580
+ (-1 left … 0 centre … +1 right). A `video` clip's audio takes `fadeIn` and `pan` too
581
+ (clip fade-out isn't supported yet). The bed auto-ducks under cues (`bgm.duck`). See
582
+ `examples/scenes/sfx-showcase.ts` to audition the whole palette.
567
583
 
568
584
  ## Rules
569
585
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reframe-video",
3
- "version": "0.6.26",
3
+ "version": "0.6.27",
4
4
  "description": "Declarative motion graphics that AI can write and humans can tweak — human edits survive AI regeneration. Deterministic mp4 renders from a plain-data scene format.",
5
5
  "keywords": [
6
6
  "motion-graphics",