reframe-video 0.6.26 → 0.6.28

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/bin.js CHANGED
@@ -10,10 +10,46 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // ../core/src/ir.ts
13
- var DEFAULT_TO_DURATION, DEFAULT_TWEEN_DURATION, DEFAULT_MOTIONPATH_DURATION, DEFAULT_STILL_DURATION;
13
+ var SFX_NAMES, BGM_SYNTHS, DEFAULT_TO_DURATION, DEFAULT_TWEEN_DURATION, DEFAULT_MOTIONPATH_DURATION, DEFAULT_STILL_DURATION;
14
14
  var init_ir = __esm({
15
15
  "../core/src/ir.ts"() {
16
16
  "use strict";
17
+ SFX_NAMES = [
18
+ "whoosh",
19
+ "swish",
20
+ "swoosh",
21
+ "rise",
22
+ "riser",
23
+ "warp",
24
+ "tick",
25
+ "click",
26
+ "blip",
27
+ "pop",
28
+ "select",
29
+ "thud",
30
+ "boom",
31
+ "knock",
32
+ "sub",
33
+ "chime",
34
+ "ding",
35
+ "coin",
36
+ "sparkle",
37
+ "shimmer",
38
+ "success",
39
+ "zap",
40
+ "error",
41
+ "glitch",
42
+ "static",
43
+ "scan",
44
+ "powerup",
45
+ "powerdown",
46
+ "snare",
47
+ "hat",
48
+ "bubble",
49
+ "notify",
50
+ "camera"
51
+ ];
52
+ BGM_SYNTHS = ["ambient-pad", "lofi", "pulse", "tension", "uplift"];
17
53
  DEFAULT_TO_DURATION = 0.5;
18
54
  DEFAULT_TWEEN_DURATION = 0.5;
19
55
  DEFAULT_MOTIONPATH_DURATION = 1;
@@ -647,7 +683,6 @@ function validateScene(ir) {
647
683
  }
648
684
  }
649
685
  }
650
- const SFX_NAMES = ["whoosh", "pop", "tick", "rise", "shimmer", "thud"];
651
686
  for (const [i, cue] of (ir.audio?.cues ?? []).entries()) {
652
687
  if (typeof cue.at === "string" && !labels.has(cue.at)) {
653
688
  problems.push(
@@ -683,6 +718,10 @@ function validateScene(ir) {
683
718
  if (ir.audio?.bgm?.file !== void 0 && ir.audio.bgm.synth !== void 0) {
684
719
  problems.push('audio.bgm: use either "file" or "synth", not both');
685
720
  }
721
+ const bgmSynth = ir.audio?.bgm?.synth;
722
+ if (bgmSynth !== void 0 && !BGM_SYNTHS.includes(bgmSynth)) {
723
+ problems.push(`audio.bgm.synth: unknown synth "${bgmSynth}" \u2014 valid: ${BGM_SYNTHS.join(", ")}`);
724
+ }
686
725
  if (problems.length > 0) throw new SceneValidationError(problems);
687
726
  }
688
727
  function validateComposition(comp) {
@@ -718,6 +757,7 @@ var EASE_SET, FX_PROPS, BLEND_MODES, IMAGE_FITS, COMMON_PROPS, CAMERA_PROPS, PRO
718
757
  var init_validate = __esm({
719
758
  "../core/src/validate.ts"() {
720
759
  "use strict";
760
+ init_ir();
721
761
  init_interpolate();
722
762
  EASE_SET = new Set(EASE_NAMES);
723
763
  FX_PROPS = ["blur", "shadowColor", "shadowBlur", "shadowX", "shadowY", "blend"];
@@ -1395,7 +1435,11 @@ function resolveAudioPlan(compiled) {
1395
1435
  fadeIn: cue.fadeIn ?? 0,
1396
1436
  fadeOut: cue.fadeOut ?? 0,
1397
1437
  pan: cue.pan ?? 0,
1398
- source: cue.sfx ? { kind: "sfx", name: cue.sfx, params: cue.params ?? {} } : { kind: "file", path: cue.file }
1438
+ source: cue.sfx ? (
1439
+ // auto-vary: default the seed to the cue's order so repeated sfx differ
1440
+ // (pitch/texture); an explicit params.seed always wins.
1441
+ { kind: "sfx", name: cue.sfx, params: { seed: index, ...cue.params } }
1442
+ ) : { kind: "file", path: cue.file }
1399
1443
  });
1400
1444
  }
1401
1445
  cues.sort((a, b) => a.t - b.t);
@@ -1438,12 +1482,46 @@ var init_audio = __esm({
1438
1482
  "../core/src/audio.ts"() {
1439
1483
  "use strict";
1440
1484
  SFX_DURATION = {
1485
+ // transition
1441
1486
  whoosh: 0.35,
1442
- pop: 0.12,
1443
- tick: 0.03,
1487
+ swish: 0.32,
1488
+ swoosh: 0.35,
1444
1489
  rise: 0.5,
1490
+ riser: 0.85,
1491
+ warp: 0.5,
1492
+ // ui
1493
+ tick: 0.03,
1494
+ click: 0.05,
1495
+ blip: 0.1,
1496
+ pop: 0.12,
1497
+ select: 0.18,
1498
+ // impact
1499
+ thud: 0.25,
1500
+ boom: 0.6,
1501
+ knock: 0.14,
1502
+ sub: 0.7,
1503
+ // positive
1504
+ chime: 0.7,
1505
+ ding: 0.5,
1506
+ coin: 0.3,
1507
+ sparkle: 0.6,
1445
1508
  shimmer: 0.9,
1446
- thud: 0.25
1509
+ success: 0.6,
1510
+ // alert
1511
+ zap: 0.22,
1512
+ error: 0.4,
1513
+ // tech
1514
+ glitch: 0.3,
1515
+ static: 0.18,
1516
+ scan: 0.45,
1517
+ powerup: 0.4,
1518
+ powerdown: 0.5,
1519
+ // rhythm / foley
1520
+ snare: 0.18,
1521
+ hat: 0.05,
1522
+ bubble: 0.16,
1523
+ notify: 0.45,
1524
+ camera: 0.18
1447
1525
  };
1448
1526
  FILE_CUE_DURATION = 0.4;
1449
1527
  }
@@ -1739,19 +1817,21 @@ function hash01(n, seed) {
1739
1817
  h = (h ^ h >>> 16) >>> 0;
1740
1818
  return h / 4294967295;
1741
1819
  }
1820
+ function seedPitch(seed) {
1821
+ const i = (Math.round(seed) % PITCH_STEPS.length + PITCH_STEPS.length) % PITCH_STEPS.length;
1822
+ return Math.pow(2, PITCH_STEPS[i] / 12);
1823
+ }
1742
1824
  function buffer(duration) {
1743
1825
  const n = Math.round(duration * SAMPLE_RATE);
1744
1826
  return { out: new Float32Array(n), n };
1745
1827
  }
1746
- function whoosh(seed) {
1828
+ function whoosh(seed, pitch) {
1747
1829
  const dur2 = 0.35;
1748
1830
  const { out, n } = buffer(dur2);
1749
- let lp = 0;
1750
- let lp2 = 0;
1831
+ let lp = 0, lp2 = 0;
1751
1832
  for (let i = 0; i < n; i++) {
1752
- const t = i / SAMPLE_RATE;
1753
- const u = t / dur2;
1754
- const center = 1200 * Math.pow(300 / 1200, u);
1833
+ const t = i / SAMPLE_RATE, u = t / dur2;
1834
+ const center = 1200 * pitch * Math.pow(0.25, u);
1755
1835
  const alpha = Math.min(1, TAU * center / SAMPLE_RATE);
1756
1836
  lp += alpha * (noise(i, seed) - lp);
1757
1837
  lp2 += alpha * 0.5 * (lp - lp2);
@@ -1760,103 +1840,527 @@ function whoosh(seed) {
1760
1840
  }
1761
1841
  return out;
1762
1842
  }
1763
- function pop(seed) {
1843
+ function swish(seed, pitch) {
1844
+ const dur2 = 0.32;
1845
+ const { out, n } = buffer(dur2);
1846
+ let lp = 0, lp2 = 0;
1847
+ for (let i = 0; i < n; i++) {
1848
+ const t = i / SAMPLE_RATE, u = t / dur2;
1849
+ const center = 2600 * pitch * Math.pow(0.2, u);
1850
+ const alpha = Math.min(1, TAU * center / SAMPLE_RATE);
1851
+ lp += alpha * (noise(i, seed) - lp);
1852
+ lp2 += alpha * 0.5 * (lp - lp2);
1853
+ const env = u < 0.15 ? u / 0.15 : expDecay(t - 0.15 * dur2, dur2 * 0.85, 5);
1854
+ out[i] = (lp - lp2) * env * 2.4;
1855
+ }
1856
+ return out;
1857
+ }
1858
+ function rise(_seed, pitch) {
1859
+ const dur2 = 0.5;
1860
+ const { out, n } = buffer(dur2);
1861
+ let phase = 0;
1862
+ for (let i = 0; i < n; i++) {
1863
+ const t = i / SAMPLE_RATE, u = t / dur2;
1864
+ const freq = 220 * pitch * Math.pow(4, u);
1865
+ phase += TAU * freq / SAMPLE_RATE;
1866
+ const env = Math.sin(Math.PI * Math.min(1, u * 1.05)) ** 1.5;
1867
+ out[i] = (Math.sin(phase) + 0.3 * Math.sin(2 * phase)) * env * 0.45;
1868
+ }
1869
+ return out;
1870
+ }
1871
+ function riser(seed, pitch) {
1872
+ const dur2 = 0.85;
1873
+ const { out, n } = buffer(dur2);
1874
+ let lp = 0, phase = 0;
1875
+ for (let i = 0; i < n; i++) {
1876
+ const t = i / SAMPLE_RATE, u = t / dur2;
1877
+ const center = 200 * pitch * Math.pow(12, u);
1878
+ const alpha = Math.min(1, TAU * center / SAMPLE_RATE);
1879
+ lp += alpha * (noise(i, seed) - lp);
1880
+ const freq = 120 * pitch * Math.pow(6, u);
1881
+ phase += TAU * freq / SAMPLE_RATE;
1882
+ const env = Math.pow(u, 1.6);
1883
+ out[i] = (lp * 1.6 + Math.sin(phase) * 0.5) * env * 0.9;
1884
+ }
1885
+ return out;
1886
+ }
1887
+ function warp(_seed, pitch) {
1888
+ const dur2 = 0.5;
1889
+ const { out, n } = buffer(dur2);
1890
+ let phase = 0;
1891
+ for (let i = 0; i < n; i++) {
1892
+ const t = i / SAMPLE_RATE, u = t / dur2;
1893
+ const bend = Math.sin(Math.PI * u);
1894
+ const vib = 1 + 0.4 * Math.sin(TAU * 18 * t);
1895
+ const freq = 300 * pitch * (1 + 2.5 * bend) * vib;
1896
+ phase += TAU * freq / SAMPLE_RATE;
1897
+ const env = Math.sin(Math.PI * u) ** 0.8;
1898
+ out[i] = square(phase) * env * 0.5;
1899
+ }
1900
+ return out;
1901
+ }
1902
+ function tick(seed, pitch) {
1903
+ const dur2 = 0.03;
1904
+ const { out, n } = buffer(dur2);
1905
+ for (let i = 0; i < n; i++) {
1906
+ const t = i / SAMPLE_RATE;
1907
+ const sine = t < 4e-3 ? Math.sin(TAU * 4e3 * pitch * t) : 0;
1908
+ out[i] = (sine * 0.6 + noise(i, seed) * 0.35) * expDecay(t, dur2, 8);
1909
+ }
1910
+ return out;
1911
+ }
1912
+ function click(seed, pitch) {
1913
+ const dur2 = 0.05;
1914
+ const { out, n } = buffer(dur2);
1915
+ let lp = 0;
1916
+ for (let i = 0; i < n; i++) {
1917
+ const t = i / SAMPLE_RATE;
1918
+ lp += 0.5 * (noise(i, seed) - lp);
1919
+ const sine = Math.sin(TAU * 1500 * pitch * t);
1920
+ out[i] = (sine * 0.5 + lp * 0.6) * expDecay(t, dur2, 11);
1921
+ }
1922
+ return out;
1923
+ }
1924
+ function blip(_seed, pitch) {
1925
+ const dur2 = 0.1;
1926
+ const { out, n } = buffer(dur2);
1927
+ let phase = 0;
1928
+ for (let i = 0; i < n; i++) {
1929
+ const t = i / SAMPLE_RATE, u = t / dur2;
1930
+ phase += TAU * 880 * pitch / SAMPLE_RATE;
1931
+ const env = Math.min(1, u * 12) * Math.min(1, (1 - u) * 6);
1932
+ out[i] = square(phase) * env * 0.5;
1933
+ }
1934
+ return out;
1935
+ }
1936
+ function pop(seed, pitch) {
1764
1937
  const dur2 = 0.12;
1765
1938
  const { out, n } = buffer(dur2);
1766
1939
  let phase = 0;
1767
1940
  for (let i = 0; i < n; i++) {
1768
1941
  const t = i / SAMPLE_RATE;
1769
- const freq = 600 * Math.pow(150 / 600, t / 0.08);
1942
+ const freq = 600 * pitch * Math.pow(0.25, t / 0.08);
1770
1943
  phase += TAU * freq / SAMPLE_RATE;
1771
1944
  const transient = t < 2e-3 ? noise(i, seed) * 0.5 : 0;
1772
1945
  out[i] = (Math.sin(phase) + transient) * expDecay(t, dur2, 6) * 0.8;
1773
1946
  }
1774
1947
  return out;
1775
1948
  }
1776
- function tick(seed) {
1777
- const dur2 = 0.03;
1949
+ function select(_seed, pitch) {
1950
+ const dur2 = 0.18;
1951
+ const { out, n } = buffer(dur2);
1952
+ let phase = 0;
1953
+ for (let i = 0; i < n; i++) {
1954
+ const t = i / SAMPLE_RATE, u = t / dur2;
1955
+ const freq = (t < 0.08 ? 620 : 930) * pitch;
1956
+ phase += TAU * freq / SAMPLE_RATE;
1957
+ const env = Math.min(1, u * 16) * Math.min(1, (1 - u) * 5);
1958
+ out[i] = (Math.sin(phase) + 0.25 * Math.sin(2 * phase)) * env * 0.5;
1959
+ }
1960
+ return out;
1961
+ }
1962
+ function thud(seed, pitch) {
1963
+ const dur2 = 0.25;
1778
1964
  const { out, n } = buffer(dur2);
1965
+ let phase = 0, lp = 0;
1779
1966
  for (let i = 0; i < n; i++) {
1780
1967
  const t = i / SAMPLE_RATE;
1781
- const sine = t < 4e-3 ? Math.sin(TAU * 4e3 * t) : 0;
1782
- out[i] = (sine * 0.6 + noise(i, seed) * 0.35) * expDecay(t, dur2, 8);
1968
+ const freq = 90 * pitch * Math.pow(0.5, t / 0.15);
1969
+ phase += TAU * freq / SAMPLE_RATE;
1970
+ lp += 0.02 * (noise(i, seed) - lp);
1971
+ const attack = t < 0.01 ? lp * 3 : 0;
1972
+ out[i] = (Math.sin(phase) * 0.9 + attack) * expDecay(t, dur2, 5);
1973
+ }
1974
+ return out;
1975
+ }
1976
+ function boom(seed, pitch) {
1977
+ const dur2 = 0.6;
1978
+ const { out, n } = buffer(dur2);
1979
+ let phase = 0, lp = 0;
1980
+ for (let i = 0; i < n; i++) {
1981
+ const t = i / SAMPLE_RATE;
1982
+ const freq = 70 * pitch * Math.pow(0.5, t / 0.3);
1983
+ phase += TAU * freq / SAMPLE_RATE;
1984
+ lp += 0.06 * (noise(i, seed) - lp);
1985
+ const body = t < 0.06 ? lp * 2.5 * (1 - t / 0.06) : 0;
1986
+ out[i] = (Math.sin(phase) * 1 + body) * expDecay(t, dur2, 3.2);
1987
+ }
1988
+ return out;
1989
+ }
1990
+ function knock(seed, pitch) {
1991
+ const dur2 = 0.14;
1992
+ const { out, n } = buffer(dur2);
1993
+ let phase = 0, lp = 0;
1994
+ for (let i = 0; i < n; i++) {
1995
+ const t = i / SAMPLE_RATE;
1996
+ const freq = 220 * pitch * Math.pow(0.7, t / 0.05);
1997
+ phase += TAU * freq / SAMPLE_RATE;
1998
+ lp += 0.3 * (noise(i, seed) - lp);
1999
+ const tap = t < 5e-3 ? lp : 0;
2000
+ out[i] = (Math.sin(phase) * 0.8 + tap * 0.6) * expDecay(t, dur2, 9);
1783
2001
  }
1784
2002
  return out;
1785
2003
  }
1786
- function rise(seed) {
2004
+ function chime(seed, pitch) {
2005
+ const dur2 = 0.7;
2006
+ const { out, n } = buffer(dur2);
2007
+ const f0 = 800 * pitch;
2008
+ const partials = [
2009
+ { f: f0, a: 1, k: 4 },
2010
+ { f: f0 * 2.76, a: 0.5, k: 5.5 },
2011
+ { f: f0 * 5.4, a: 0.28, k: 7 },
2012
+ { f: f0 * 8.9, a: 0.13, k: 9 }
2013
+ ];
2014
+ for (let i = 0; i < n; i++) {
2015
+ const t = i / SAMPLE_RATE;
2016
+ let s = 0;
2017
+ for (const p of partials) s += Math.sin(TAU * p.f * t) * p.a * expDecay(t, dur2, p.k);
2018
+ const strike = t < 3e-3 ? noise(i, seed) * 0.3 : 0;
2019
+ out[i] = (s / 1.9 + strike) * 0.6;
2020
+ }
2021
+ return out;
2022
+ }
2023
+ function ding(_seed, pitch) {
1787
2024
  const dur2 = 0.5;
1788
2025
  const { out, n } = buffer(dur2);
1789
- let phase = 0;
2026
+ const f0 = 1200 * pitch;
1790
2027
  for (let i = 0; i < n; i++) {
1791
2028
  const t = i / SAMPLE_RATE;
1792
- const u = t / dur2;
1793
- const freq = 220 * Math.pow(880 / 220, u);
2029
+ const s = Math.sin(TAU * f0 * t) + 0.4 * Math.sin(TAU * f0 * 2 * t) + 0.2 * Math.sin(TAU * f0 * 3.01 * t);
2030
+ out[i] = s / 1.6 * expDecay(t, dur2, 4.5) * 0.6;
2031
+ }
2032
+ return out;
2033
+ }
2034
+ function coin(_seed, pitch) {
2035
+ const dur2 = 0.3;
2036
+ const { out, n } = buffer(dur2);
2037
+ let phase = 0;
2038
+ for (let i = 0; i < n; i++) {
2039
+ const t = i / SAMPLE_RATE, u = t / dur2;
2040
+ const freq = (t < 0.06 ? 988 : 1319) * pitch;
1794
2041
  phase += TAU * freq / SAMPLE_RATE;
1795
- const env = Math.sin(Math.PI * Math.min(1, u * 1.05)) ** 1.5;
1796
- out[i] = (Math.sin(phase) + 0.3 * Math.sin(2 * phase)) * env * 0.45;
2042
+ const env = Math.min(1, u * 30) * expDecay(Math.max(0, t - 0.06), dur2, 3.5);
2043
+ out[i] = square(phase) * env * 0.55;
1797
2044
  }
1798
2045
  return out;
1799
2046
  }
1800
- function shimmer(seed) {
2047
+ function sparkle(seed, pitch) {
2048
+ const dur2 = 0.6;
2049
+ const { out, n } = buffer(dur2);
2050
+ const steps = [1, 1.5, 2, 3, 4, 5, 6];
2051
+ const base = 1200 * pitch;
2052
+ for (let i = 0; i < n; i++) {
2053
+ const t = i / SAMPLE_RATE, u = t / dur2;
2054
+ let s = 0;
2055
+ for (let g = 0; g < steps.length; g++) {
2056
+ const on = u * steps.length - g;
2057
+ if (on > 0 && on < 3) {
2058
+ const ge = Math.exp(-on * 1.8);
2059
+ s += Math.sin(TAU * base * steps[g] * t + hash01(g, seed) * TAU) * ge;
2060
+ }
2061
+ }
2062
+ out[i] = s / 2.4 * Math.sin(Math.PI * u) ** 0.5 * 0.5;
2063
+ }
2064
+ return out;
2065
+ }
2066
+ function success(_seed, pitch) {
2067
+ const dur2 = 0.6;
2068
+ const { out, n } = buffer(dur2);
2069
+ const notes = [523.25, 659.25, 783.99].map((f) => f * pitch);
2070
+ let phase = 0, cur = 0;
2071
+ for (let i = 0; i < n; i++) {
2072
+ const t = i / SAMPLE_RATE, u = t / dur2;
2073
+ const idx = Math.min(notes.length - 1, Math.floor(u * 3.2));
2074
+ if (idx !== cur) cur = idx;
2075
+ phase += TAU * notes[cur] / SAMPLE_RATE;
2076
+ const local = u * 3.2 - idx;
2077
+ const env = Math.min(1, local * 12) * Math.min(1, (1 - Math.min(1, local)) * 4 + 0.2);
2078
+ out[i] = (Math.sin(phase) + 0.3 * Math.sin(2 * phase)) * env * 0.42;
2079
+ }
2080
+ return out;
2081
+ }
2082
+ function shimmer(seed, pitch) {
1801
2083
  const dur2 = 0.9;
1802
2084
  const { out, n } = buffer(dur2);
1803
2085
  const partials = Array.from({ length: 5 }, (_, p) => ({
1804
- freq: 2e3 + hash01(p, seed + 7) * 2e3,
2086
+ freq: (2e3 + hash01(p, seed + 7) * 2e3) * pitch,
1805
2087
  am: 0.5 + hash01(p, seed + 8) * 1.5,
1806
2088
  phase: hash01(p, seed + 9) * TAU
1807
2089
  }));
1808
2090
  for (let i = 0; i < n; i++) {
1809
- const t = i / SAMPLE_RATE;
1810
- const u = t / dur2;
2091
+ const t = i / SAMPLE_RATE, u = t / dur2;
1811
2092
  const env = Math.sin(Math.PI * u) ** 1.2;
1812
2093
  let s = 0;
1813
- for (const part of partials) {
1814
- s += Math.sin(TAU * part.freq * t + part.phase) * (0.6 + 0.4 * Math.sin(TAU * part.am * t));
1815
- }
2094
+ for (const part of partials) s += Math.sin(TAU * part.freq * t + part.phase) * (0.6 + 0.4 * Math.sin(TAU * part.am * t));
1816
2095
  out[i] = s / 5 * env * 0.5;
1817
2096
  }
1818
2097
  return out;
1819
2098
  }
1820
- function thud(seed) {
1821
- const dur2 = 0.25;
2099
+ function zap(seed, pitch) {
2100
+ const dur2 = 0.22;
2101
+ const { out, n } = buffer(dur2);
2102
+ let phase = 0;
2103
+ for (let i = 0; i < n; i++) {
2104
+ const t = i / SAMPLE_RATE, u = t / dur2;
2105
+ const freq = 1600 * pitch * Math.pow(0.12, u);
2106
+ phase += TAU * freq / SAMPLE_RATE;
2107
+ const grit = noise(i, seed) * 0.25;
2108
+ out[i] = (square(phase) + grit) * expDecay(t, dur2, 4.5) * 0.5;
2109
+ }
2110
+ return out;
2111
+ }
2112
+ function error(_seed, pitch) {
2113
+ const dur2 = 0.4;
1822
2114
  const { out, n } = buffer(dur2);
1823
2115
  let phase = 0;
2116
+ for (let i = 0; i < n; i++) {
2117
+ const t = i / SAMPLE_RATE, u = t / dur2;
2118
+ const freq = (t < 0.16 ? 311 : 233) * pitch;
2119
+ phase += TAU * freq / SAMPLE_RATE;
2120
+ const seg = t < 0.16 ? u / 0.4 : (u - 0.4) / 0.6;
2121
+ const env = Math.min(1, seg * 18) * Math.min(1, (1 - seg) * 6);
2122
+ out[i] = square(phase) * env * 0.5;
2123
+ }
2124
+ return out;
2125
+ }
2126
+ function swoosh(seed, pitch) {
2127
+ const dur2 = 0.35;
2128
+ const { out, n } = buffer(dur2);
2129
+ let lp = 0, lp2 = 0;
2130
+ for (let i = 0; i < n; i++) {
2131
+ const t = i / SAMPLE_RATE, u = t / dur2;
2132
+ const center = 300 * pitch * Math.pow(7.3, u);
2133
+ const alpha = Math.min(1, TAU * center / SAMPLE_RATE);
2134
+ lp += alpha * (noise(i, seed) - lp);
2135
+ lp2 += alpha * 0.5 * (lp - lp2);
2136
+ const env = Math.sin(Math.PI * u) ** 0.9;
2137
+ out[i] = (lp - lp2) * env * 2.4;
2138
+ }
2139
+ return out;
2140
+ }
2141
+ function glitch(seed, pitch) {
2142
+ const dur2 = 0.3;
2143
+ const { out, n } = buffer(dur2);
2144
+ const cell = Math.round(SAMPLE_RATE * 0.012);
2145
+ let phase = 0;
2146
+ for (let i = 0; i < n; i++) {
2147
+ const t = i / SAMPLE_RATE, u = t / dur2;
2148
+ const c = Math.floor(i / cell);
2149
+ const on = hash01(c, seed) > 0.4 ? 1 : 0;
2150
+ const freq = 400 * pitch * (1 + Math.floor(hash01(c, seed + 1) * 6));
2151
+ phase += TAU * freq / SAMPLE_RATE;
2152
+ const crush = Math.round(square(phase) * 4) / 4;
2153
+ out[i] = (crush * 0.7 + noise(i, seed + c) * 0.3) * on * (1 - u * 0.3) * 0.55;
2154
+ }
2155
+ return out;
2156
+ }
2157
+ function staticHit(seed, pitch) {
2158
+ const dur2 = 0.18;
2159
+ const { out, n } = buffer(dur2);
1824
2160
  let lp = 0;
2161
+ const alpha = Math.min(0.8, TAU * 3e3 * pitch / SAMPLE_RATE);
2162
+ for (let i = 0; i < n; i++) {
2163
+ const t = i / SAMPLE_RATE, u = t / dur2;
2164
+ lp += alpha * (noise(i, seed) - lp);
2165
+ const env = Math.min(1, u * 30) * expDecay(t, dur2, 5);
2166
+ out[i] = (noise(i, seed) - lp) * env * 1.4;
2167
+ }
2168
+ return out;
2169
+ }
2170
+ function scan(seed, pitch) {
2171
+ const dur2 = 0.45;
2172
+ const { out, n } = buffer(dur2);
2173
+ let phase = 0;
2174
+ for (let i = 0; i < n; i++) {
2175
+ const t = i / SAMPLE_RATE, u = t / dur2;
2176
+ const step = Math.floor(u * 6) / 6;
2177
+ const freq = 500 * pitch * Math.pow(3, step);
2178
+ phase += TAU * freq / SAMPLE_RATE;
2179
+ const cell = u * 6 % 1;
2180
+ const env = Math.min(1, cell * 12) * Math.min(1, (1 - cell) * 4) * 0.7;
2181
+ out[i] = square(phase) * env * 0.5;
2182
+ }
2183
+ return out;
2184
+ }
2185
+ function powerup(seed, pitch) {
2186
+ const dur2 = 0.4;
2187
+ const { out, n } = buffer(dur2);
2188
+ const notes = [392, 523, 659, 784, 1046].map((f) => f * pitch);
2189
+ let phase = 0;
2190
+ for (let i = 0; i < n; i++) {
2191
+ const t = i / SAMPLE_RATE, u = t / dur2;
2192
+ const idx = Math.min(notes.length - 1, Math.floor(u * notes.length));
2193
+ phase += TAU * notes[idx] / SAMPLE_RATE;
2194
+ const local = u * notes.length - idx;
2195
+ const env = Math.min(1, local * 14) * Math.min(1, (1 - Math.min(1, local)) * 4 + 0.3);
2196
+ out[i] = (Math.sin(phase) + 0.25 * Math.sin(2 * phase)) * env * 0.45;
2197
+ }
2198
+ return out;
2199
+ }
2200
+ function powerdown(seed, pitch) {
2201
+ const dur2 = 0.5;
2202
+ const { out, n } = buffer(dur2);
2203
+ let phase = 0;
2204
+ for (let i = 0; i < n; i++) {
2205
+ const t = i / SAMPLE_RATE, u = t / dur2;
2206
+ const freq = 700 * pitch * Math.pow(0.18, u);
2207
+ phase += TAU * freq / SAMPLE_RATE;
2208
+ const env = (1 - u) ** 0.8;
2209
+ out[i] = (square(phase) * 0.7 + Math.sin(phase) * 0.3) * env * 0.5;
2210
+ }
2211
+ return out;
2212
+ }
2213
+ function sub(seed, pitch) {
2214
+ const dur2 = 0.7;
2215
+ const { out, n } = buffer(dur2);
2216
+ let phase = 0;
1825
2217
  for (let i = 0; i < n; i++) {
1826
2218
  const t = i / SAMPLE_RATE;
1827
- const freq = 90 * Math.pow(45 / 90, t / 0.15);
2219
+ const freq = 80 * pitch * Math.pow(0.45, t / 0.4);
1828
2220
  phase += TAU * freq / SAMPLE_RATE;
1829
- lp += 0.02 * (noise(i, seed) - lp);
1830
- const attack = t < 0.01 ? lp * 3 : 0;
1831
- out[i] = (Math.sin(phase) * 0.9 + attack) * expDecay(t, dur2, 5);
2221
+ const click2 = t < 4e-3 ? noise(i, seed) * 0.4 : 0;
2222
+ out[i] = (Math.sin(phase) + click2) * expDecay(t, dur2, 3) * 0.95;
2223
+ }
2224
+ return out;
2225
+ }
2226
+ function snare(seed, pitch) {
2227
+ const dur2 = 0.18;
2228
+ const { out, n } = buffer(dur2);
2229
+ let phase = 0;
2230
+ for (let i = 0; i < n; i++) {
2231
+ const t = i / SAMPLE_RATE;
2232
+ phase += TAU * 180 * pitch / SAMPLE_RATE;
2233
+ const body = Math.sin(phase) * 0.5 * expDecay(t, dur2, 16);
2234
+ const rattle = noise(i, seed) * expDecay(t, dur2, 7);
2235
+ out[i] = (body + rattle * 0.8) * 0.7;
2236
+ }
2237
+ return out;
2238
+ }
2239
+ function hat(seed, pitch) {
2240
+ const dur2 = 0.05;
2241
+ const { out, n } = buffer(dur2);
2242
+ let lp = 0;
2243
+ const alpha = Math.min(0.85, TAU * 2500 * pitch / SAMPLE_RATE);
2244
+ for (let i = 0; i < n; i++) {
2245
+ const t = i / SAMPLE_RATE;
2246
+ lp += alpha * (noise(i, seed) - lp);
2247
+ out[i] = (noise(i, seed) - lp) * expDecay(t, dur2, 14) * 0.9;
2248
+ }
2249
+ return out;
2250
+ }
2251
+ function bubble(seed, pitch) {
2252
+ const dur2 = 0.16;
2253
+ const { out, n } = buffer(dur2);
2254
+ let phase = 0;
2255
+ for (let i = 0; i < n; i++) {
2256
+ const t = i / SAMPLE_RATE, u = t / dur2;
2257
+ const freq = 400 * pitch * Math.pow(3, u);
2258
+ phase += TAU * freq / SAMPLE_RATE;
2259
+ const env = Math.sin(Math.PI * u) ** 1.1;
2260
+ out[i] = Math.sin(phase) * env * 0.6;
2261
+ }
2262
+ return out;
2263
+ }
2264
+ function notify(seed, pitch) {
2265
+ const dur2 = 0.45;
2266
+ const { out, n } = buffer(dur2);
2267
+ const f0 = 880 * pitch;
2268
+ for (let i = 0; i < n; i++) {
2269
+ const t = i / SAMPLE_RATE;
2270
+ const a = Math.sin(TAU * f0 * t) * expDecay(t, dur2, 5);
2271
+ const b = t > 0.09 ? Math.sin(TAU * f0 * 1.5 * t) * expDecay(t - 0.09, dur2, 4.5) : 0;
2272
+ out[i] = (a * 0.55 + b * 0.6) * 0.55;
2273
+ }
2274
+ return out;
2275
+ }
2276
+ function camera(seed, pitch) {
2277
+ const dur2 = 0.18;
2278
+ const { out, n } = buffer(dur2);
2279
+ let lp = 0;
2280
+ for (let i = 0; i < n; i++) {
2281
+ const t = i / SAMPLE_RATE;
2282
+ lp += 0.45 * (noise(i, seed) - lp);
2283
+ const k1 = t < 0.02 ? expDecay(t, 0.02, 6) : 0;
2284
+ const k2 = t > 0.08 && t < 0.12 ? expDecay(t - 0.08, 0.04, 6) : 0;
2285
+ const sine = Math.sin(TAU * 2200 * pitch * t);
2286
+ out[i] = (lp * 0.7 + sine * 0.3) * (k1 + k2) * 1.3;
1832
2287
  }
1833
2288
  return out;
1834
2289
  }
1835
2290
  function synthSfx(name, params = {}) {
1836
- const samples = RECIPES[name](params.seed ?? 0);
2291
+ const seed = params.seed ?? 0;
2292
+ const pitch = (params.pitch ?? 1) * seedPitch(seed);
2293
+ const samples = RECIPES[name](seed, pitch);
1837
2294
  if (params.gainDb) {
1838
2295
  const g = Math.pow(10, params.gainDb / 20);
1839
2296
  for (let i = 0; i < samples.length; i++) samples[i] *= g;
1840
2297
  }
2298
+ let peak = 0;
2299
+ for (let i = 0; i < samples.length; i++) peak = Math.max(peak, Math.abs(samples[i]));
2300
+ if (peak > 0.95) {
2301
+ const g = 0.95 / peak;
2302
+ for (let i = 0; i < samples.length; i++) samples[i] *= g;
2303
+ }
1841
2304
  return samples;
1842
2305
  }
1843
- function synthAmbientPad(duration, seed = 0) {
2306
+ function pad(freqs, duration, seed, opts) {
1844
2307
  const { out, n } = buffer(duration);
1845
- const voices = [110, 165, 220].flatMap((f, v) => [
1846
- { freq: f * (1 + (hash01(v, seed + 3) - 0.5) * 4e-3), am: 0.05 + hash01(v, seed + 4) * 0.08, phase: hash01(v, seed + 5) * TAU },
1847
- { freq: f * (1 - (hash01(v, seed + 6) - 0.5) * 4e-3), am: 0.05 + hash01(v, seed + 7) * 0.08, phase: hash01(v, seed + 8) * TAU }
2308
+ const voices = freqs.flatMap((f, v) => [
2309
+ { freq: f * (1 + (hash01(v, seed + 3) - 0.5) * 4e-3), am: opts.amBase + hash01(v, seed + 4) * 0.08, phase: hash01(v, seed + 5) * TAU },
2310
+ { freq: f * (1 - (hash01(v, seed + 6) - 0.5) * 4e-3), am: opts.amBase + hash01(v, seed + 7) * 0.08, phase: hash01(v, seed + 8) * TAU }
1848
2311
  ]);
1849
2312
  for (let i = 0; i < n; i++) {
1850
2313
  const t = i / SAMPLE_RATE;
1851
2314
  let s = 0;
1852
2315
  for (const voice of voices) {
1853
2316
  s += Math.sin(TAU * voice.freq * t + voice.phase) * (0.75 + 0.25 * Math.sin(TAU * voice.am * t));
2317
+ if (opts.bright > 0) s += opts.bright * Math.sin(TAU * voice.freq * 2 * t + voice.phase);
1854
2318
  }
1855
- out[i] = s / voices.length * 0.7;
2319
+ out[i] = s / voices.length * opts.gain;
2320
+ }
2321
+ return out;
2322
+ }
2323
+ function synthAmbientPad(duration, seed = 0) {
2324
+ return pad([110, 165, 220], duration, seed, { amBase: 0.05, bright: 0, gain: 0.7 });
2325
+ }
2326
+ function synthLofi(duration, seed = 0) {
2327
+ return pad([130.81, 164.81, 196, 246.94], duration, seed, { amBase: 0.04, bright: 0.04, gain: 0.62 });
2328
+ }
2329
+ function synthPulse(duration, _seed = 0) {
2330
+ const { out, n } = buffer(duration);
2331
+ const beat2 = 2.2;
2332
+ for (let i = 0; i < n; i++) {
2333
+ const t = i / SAMPLE_RATE;
2334
+ const ph = t * beat2 % 1;
2335
+ const gate = Math.exp(-ph * 5) * 0.9 + 0.1;
2336
+ const s = Math.sin(TAU * 82 * t) + 0.6 * Math.sin(TAU * 123 * t) + 0.3 * Math.sin(TAU * 246 * t);
2337
+ out[i] = s / 1.9 * gate * 0.6;
1856
2338
  }
1857
2339
  return out;
1858
2340
  }
1859
- var noise, TAU, expDecay, RECIPES;
2341
+ function synthTension(duration, seed = 0) {
2342
+ const { out, n } = buffer(duration);
2343
+ const base = [98, 104, 110];
2344
+ for (let i = 0; i < n; i++) {
2345
+ const t = i / SAMPLE_RATE;
2346
+ const drift = 1 + 0.03 * (t / Math.max(1e-3, duration));
2347
+ let s = 0;
2348
+ for (let v = 0; v < base.length; v++) {
2349
+ const f = base[v] * drift * (1 + (hash01(v, seed) - 0.5) * 6e-3);
2350
+ s += Math.sin(TAU * f * t + hash01(v, seed + 1) * TAU);
2351
+ }
2352
+ const swell = 0.6 + 0.4 * Math.sin(TAU * 0.08 * t);
2353
+ out[i] = s / base.length * swell * 0.6;
2354
+ }
2355
+ return out;
2356
+ }
2357
+ function synthUplift(duration, seed = 0) {
2358
+ return pad([196, 246.94, 293.66, 392], duration, seed, { amBase: 0.07, bright: 0.1, gain: 0.6 });
2359
+ }
2360
+ function synthBgm(name, duration, seed = 0) {
2361
+ return BGM_RECIPES[name](duration, seed);
2362
+ }
2363
+ var noise, TAU, expDecay, square, PITCH_STEPS, RECIPES, BGM_RECIPES;
1860
2364
  var init_synth = __esm({
1861
2365
  "../render-cli/src/audio/synth.ts"() {
1862
2366
  "use strict";
@@ -1864,13 +2368,49 @@ var init_synth = __esm({
1864
2368
  noise = (n, seed) => hash01(n, seed) * 2 - 1;
1865
2369
  TAU = Math.PI * 2;
1866
2370
  expDecay = (t, dur2, k = 5) => Math.exp(-k * t / dur2);
2371
+ square = (ph) => (Math.sin(ph) + 0.33 * Math.sin(3 * ph) + 0.2 * Math.sin(5 * ph)) / 1.4;
2372
+ PITCH_STEPS = [0, 2, 4, 7, 9, 12, 5, -3, 16, -5];
1867
2373
  RECIPES = {
1868
2374
  whoosh,
1869
- pop,
1870
- tick,
2375
+ swish,
2376
+ swoosh,
1871
2377
  rise,
2378
+ riser,
2379
+ warp,
2380
+ tick,
2381
+ click,
2382
+ blip,
2383
+ pop,
2384
+ select,
2385
+ thud,
2386
+ boom,
2387
+ knock,
2388
+ sub,
2389
+ chime,
2390
+ ding,
2391
+ coin,
2392
+ sparkle,
1872
2393
  shimmer,
1873
- thud
2394
+ success,
2395
+ zap,
2396
+ error,
2397
+ glitch,
2398
+ static: staticHit,
2399
+ scan,
2400
+ powerup,
2401
+ powerdown,
2402
+ snare,
2403
+ hat,
2404
+ bubble,
2405
+ notify,
2406
+ camera
2407
+ };
2408
+ BGM_RECIPES = {
2409
+ "ambient-pad": synthAmbientPad,
2410
+ lofi: synthLofi,
2411
+ pulse: synthPulse,
2412
+ tension: synthTension,
2413
+ uplift: synthUplift
1874
2414
  };
1875
2415
  }
1876
2416
  });
@@ -1925,7 +2465,7 @@ async function resolveBgmFile(source, duration, sceneDir) {
1925
2465
  }
1926
2466
  throw new Error(`bgm file "${p}" not found`);
1927
2467
  }
1928
- return writeCached(`ambient-pad-${duration.toFixed(2)}`, () => synthAmbientPad(duration));
2468
+ return writeCached(`${source.name}-${duration.toFixed(2)}`, () => synthBgm(source.name, duration));
1929
2469
  }
1930
2470
  var ROOT, VENDORED, CACHE;
1931
2471
  var init_sfx = __esm({