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/bin.js CHANGED
@@ -10,10 +10,34 @@ 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
+ "rise",
21
+ "riser",
22
+ "warp",
23
+ "tick",
24
+ "click",
25
+ "blip",
26
+ "pop",
27
+ "select",
28
+ "thud",
29
+ "boom",
30
+ "knock",
31
+ "chime",
32
+ "ding",
33
+ "coin",
34
+ "sparkle",
35
+ "shimmer",
36
+ "success",
37
+ "zap",
38
+ "error"
39
+ ];
40
+ BGM_SYNTHS = ["ambient-pad", "lofi", "pulse", "tension", "uplift"];
17
41
  DEFAULT_TO_DURATION = 0.5;
18
42
  DEFAULT_TWEEN_DURATION = 0.5;
19
43
  DEFAULT_MOTIONPATH_DURATION = 1;
@@ -647,7 +671,6 @@ function validateScene(ir) {
647
671
  }
648
672
  }
649
673
  }
650
- const SFX_NAMES = ["whoosh", "pop", "tick", "rise", "shimmer", "thud"];
651
674
  for (const [i, cue] of (ir.audio?.cues ?? []).entries()) {
652
675
  if (typeof cue.at === "string" && !labels.has(cue.at)) {
653
676
  problems.push(
@@ -683,6 +706,10 @@ function validateScene(ir) {
683
706
  if (ir.audio?.bgm?.file !== void 0 && ir.audio.bgm.synth !== void 0) {
684
707
  problems.push('audio.bgm: use either "file" or "synth", not both');
685
708
  }
709
+ const bgmSynth = ir.audio?.bgm?.synth;
710
+ if (bgmSynth !== void 0 && !BGM_SYNTHS.includes(bgmSynth)) {
711
+ problems.push(`audio.bgm.synth: unknown synth "${bgmSynth}" \u2014 valid: ${BGM_SYNTHS.join(", ")}`);
712
+ }
686
713
  if (problems.length > 0) throw new SceneValidationError(problems);
687
714
  }
688
715
  function validateComposition(comp) {
@@ -718,6 +745,7 @@ var EASE_SET, FX_PROPS, BLEND_MODES, IMAGE_FITS, COMMON_PROPS, CAMERA_PROPS, PRO
718
745
  var init_validate = __esm({
719
746
  "../core/src/validate.ts"() {
720
747
  "use strict";
748
+ init_ir();
721
749
  init_interpolate();
722
750
  EASE_SET = new Set(EASE_NAMES);
723
751
  FX_PROPS = ["blur", "shadowColor", "shadowBlur", "shadowX", "shadowY", "blend"];
@@ -1395,7 +1423,11 @@ function resolveAudioPlan(compiled) {
1395
1423
  fadeIn: cue.fadeIn ?? 0,
1396
1424
  fadeOut: cue.fadeOut ?? 0,
1397
1425
  pan: cue.pan ?? 0,
1398
- source: cue.sfx ? { kind: "sfx", name: cue.sfx, params: cue.params ?? {} } : { kind: "file", path: cue.file }
1426
+ source: cue.sfx ? (
1427
+ // auto-vary: default the seed to the cue's order so repeated sfx differ
1428
+ // (pitch/texture); an explicit params.seed always wins.
1429
+ { kind: "sfx", name: cue.sfx, params: { seed: index, ...cue.params } }
1430
+ ) : { kind: "file", path: cue.file }
1399
1431
  });
1400
1432
  }
1401
1433
  cues.sort((a, b) => a.t - b.t);
@@ -1438,12 +1470,32 @@ var init_audio = __esm({
1438
1470
  "../core/src/audio.ts"() {
1439
1471
  "use strict";
1440
1472
  SFX_DURATION = {
1473
+ // transition
1441
1474
  whoosh: 0.35,
1442
- pop: 0.12,
1443
- tick: 0.03,
1475
+ swish: 0.32,
1444
1476
  rise: 0.5,
1477
+ riser: 0.85,
1478
+ warp: 0.5,
1479
+ // ui
1480
+ tick: 0.03,
1481
+ click: 0.05,
1482
+ blip: 0.1,
1483
+ pop: 0.12,
1484
+ select: 0.18,
1485
+ // impact
1486
+ thud: 0.25,
1487
+ boom: 0.6,
1488
+ knock: 0.14,
1489
+ // positive
1490
+ chime: 0.7,
1491
+ ding: 0.5,
1492
+ coin: 0.3,
1493
+ sparkle: 0.6,
1445
1494
  shimmer: 0.9,
1446
- thud: 0.25
1495
+ success: 0.6,
1496
+ // alert
1497
+ zap: 0.22,
1498
+ error: 0.4
1447
1499
  };
1448
1500
  FILE_CUE_DURATION = 0.4;
1449
1501
  }
@@ -1739,19 +1791,21 @@ function hash01(n, seed) {
1739
1791
  h = (h ^ h >>> 16) >>> 0;
1740
1792
  return h / 4294967295;
1741
1793
  }
1794
+ function seedPitch(seed) {
1795
+ const i = (Math.round(seed) % PITCH_STEPS.length + PITCH_STEPS.length) % PITCH_STEPS.length;
1796
+ return Math.pow(2, PITCH_STEPS[i] / 12);
1797
+ }
1742
1798
  function buffer(duration) {
1743
1799
  const n = Math.round(duration * SAMPLE_RATE);
1744
1800
  return { out: new Float32Array(n), n };
1745
1801
  }
1746
- function whoosh(seed) {
1802
+ function whoosh(seed, pitch) {
1747
1803
  const dur2 = 0.35;
1748
1804
  const { out, n } = buffer(dur2);
1749
- let lp = 0;
1750
- let lp2 = 0;
1805
+ let lp = 0, lp2 = 0;
1751
1806
  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);
1807
+ const t = i / SAMPLE_RATE, u = t / dur2;
1808
+ const center = 1200 * pitch * Math.pow(0.25, u);
1755
1809
  const alpha = Math.min(1, TAU * center / SAMPLE_RATE);
1756
1810
  lp += alpha * (noise(i, seed) - lp);
1757
1811
  lp2 += alpha * 0.5 * (lp - lp2);
@@ -1760,103 +1814,363 @@ function whoosh(seed) {
1760
1814
  }
1761
1815
  return out;
1762
1816
  }
1763
- function pop(seed) {
1817
+ function swish(seed, pitch) {
1818
+ const dur2 = 0.32;
1819
+ const { out, n } = buffer(dur2);
1820
+ let lp = 0, lp2 = 0;
1821
+ for (let i = 0; i < n; i++) {
1822
+ const t = i / SAMPLE_RATE, u = t / dur2;
1823
+ const center = 2600 * pitch * Math.pow(0.2, u);
1824
+ const alpha = Math.min(1, TAU * center / SAMPLE_RATE);
1825
+ lp += alpha * (noise(i, seed) - lp);
1826
+ lp2 += alpha * 0.5 * (lp - lp2);
1827
+ const env = u < 0.15 ? u / 0.15 : expDecay(t - 0.15 * dur2, dur2 * 0.85, 5);
1828
+ out[i] = (lp - lp2) * env * 2.4;
1829
+ }
1830
+ return out;
1831
+ }
1832
+ function rise(_seed, pitch) {
1833
+ const dur2 = 0.5;
1834
+ const { out, n } = buffer(dur2);
1835
+ let phase = 0;
1836
+ for (let i = 0; i < n; i++) {
1837
+ const t = i / SAMPLE_RATE, u = t / dur2;
1838
+ const freq = 220 * pitch * Math.pow(4, u);
1839
+ phase += TAU * freq / SAMPLE_RATE;
1840
+ const env = Math.sin(Math.PI * Math.min(1, u * 1.05)) ** 1.5;
1841
+ out[i] = (Math.sin(phase) + 0.3 * Math.sin(2 * phase)) * env * 0.45;
1842
+ }
1843
+ return out;
1844
+ }
1845
+ function riser(seed, pitch) {
1846
+ const dur2 = 0.85;
1847
+ const { out, n } = buffer(dur2);
1848
+ let lp = 0, phase = 0;
1849
+ for (let i = 0; i < n; i++) {
1850
+ const t = i / SAMPLE_RATE, u = t / dur2;
1851
+ const center = 200 * pitch * Math.pow(12, u);
1852
+ const alpha = Math.min(1, TAU * center / SAMPLE_RATE);
1853
+ lp += alpha * (noise(i, seed) - lp);
1854
+ const freq = 120 * pitch * Math.pow(6, u);
1855
+ phase += TAU * freq / SAMPLE_RATE;
1856
+ const env = Math.pow(u, 1.6);
1857
+ out[i] = (lp * 1.6 + Math.sin(phase) * 0.5) * env * 0.9;
1858
+ }
1859
+ return out;
1860
+ }
1861
+ function warp(_seed, pitch) {
1862
+ const dur2 = 0.5;
1863
+ const { out, n } = buffer(dur2);
1864
+ let phase = 0;
1865
+ for (let i = 0; i < n; i++) {
1866
+ const t = i / SAMPLE_RATE, u = t / dur2;
1867
+ const bend = Math.sin(Math.PI * u);
1868
+ const vib = 1 + 0.4 * Math.sin(TAU * 18 * t);
1869
+ const freq = 300 * pitch * (1 + 2.5 * bend) * vib;
1870
+ phase += TAU * freq / SAMPLE_RATE;
1871
+ const env = Math.sin(Math.PI * u) ** 0.8;
1872
+ out[i] = square(phase) * env * 0.5;
1873
+ }
1874
+ return out;
1875
+ }
1876
+ function tick(seed, pitch) {
1877
+ const dur2 = 0.03;
1878
+ const { out, n } = buffer(dur2);
1879
+ for (let i = 0; i < n; i++) {
1880
+ const t = i / SAMPLE_RATE;
1881
+ const sine = t < 4e-3 ? Math.sin(TAU * 4e3 * pitch * t) : 0;
1882
+ out[i] = (sine * 0.6 + noise(i, seed) * 0.35) * expDecay(t, dur2, 8);
1883
+ }
1884
+ return out;
1885
+ }
1886
+ function click(seed, pitch) {
1887
+ const dur2 = 0.05;
1888
+ const { out, n } = buffer(dur2);
1889
+ let lp = 0;
1890
+ for (let i = 0; i < n; i++) {
1891
+ const t = i / SAMPLE_RATE;
1892
+ lp += 0.5 * (noise(i, seed) - lp);
1893
+ const sine = Math.sin(TAU * 1500 * pitch * t);
1894
+ out[i] = (sine * 0.5 + lp * 0.6) * expDecay(t, dur2, 11);
1895
+ }
1896
+ return out;
1897
+ }
1898
+ function blip(_seed, pitch) {
1899
+ const dur2 = 0.1;
1900
+ const { out, n } = buffer(dur2);
1901
+ let phase = 0;
1902
+ for (let i = 0; i < n; i++) {
1903
+ const t = i / SAMPLE_RATE, u = t / dur2;
1904
+ phase += TAU * 880 * pitch / SAMPLE_RATE;
1905
+ const env = Math.min(1, u * 12) * Math.min(1, (1 - u) * 6);
1906
+ out[i] = square(phase) * env * 0.5;
1907
+ }
1908
+ return out;
1909
+ }
1910
+ function pop(seed, pitch) {
1764
1911
  const dur2 = 0.12;
1765
1912
  const { out, n } = buffer(dur2);
1766
1913
  let phase = 0;
1767
1914
  for (let i = 0; i < n; i++) {
1768
1915
  const t = i / SAMPLE_RATE;
1769
- const freq = 600 * Math.pow(150 / 600, t / 0.08);
1916
+ const freq = 600 * pitch * Math.pow(0.25, t / 0.08);
1770
1917
  phase += TAU * freq / SAMPLE_RATE;
1771
1918
  const transient = t < 2e-3 ? noise(i, seed) * 0.5 : 0;
1772
1919
  out[i] = (Math.sin(phase) + transient) * expDecay(t, dur2, 6) * 0.8;
1773
1920
  }
1774
1921
  return out;
1775
1922
  }
1776
- function tick(seed) {
1777
- const dur2 = 0.03;
1923
+ function select(_seed, pitch) {
1924
+ const dur2 = 0.18;
1925
+ const { out, n } = buffer(dur2);
1926
+ let phase = 0;
1927
+ for (let i = 0; i < n; i++) {
1928
+ const t = i / SAMPLE_RATE, u = t / dur2;
1929
+ const freq = (t < 0.08 ? 620 : 930) * pitch;
1930
+ phase += TAU * freq / SAMPLE_RATE;
1931
+ const env = Math.min(1, u * 16) * Math.min(1, (1 - u) * 5);
1932
+ out[i] = (Math.sin(phase) + 0.25 * Math.sin(2 * phase)) * env * 0.5;
1933
+ }
1934
+ return out;
1935
+ }
1936
+ function thud(seed, pitch) {
1937
+ const dur2 = 0.25;
1778
1938
  const { out, n } = buffer(dur2);
1939
+ let phase = 0, lp = 0;
1779
1940
  for (let i = 0; i < n; i++) {
1780
1941
  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);
1942
+ const freq = 90 * pitch * Math.pow(0.5, t / 0.15);
1943
+ phase += TAU * freq / SAMPLE_RATE;
1944
+ lp += 0.02 * (noise(i, seed) - lp);
1945
+ const attack = t < 0.01 ? lp * 3 : 0;
1946
+ out[i] = (Math.sin(phase) * 0.9 + attack) * expDecay(t, dur2, 5);
1783
1947
  }
1784
1948
  return out;
1785
1949
  }
1786
- function rise(seed) {
1950
+ function boom(seed, pitch) {
1951
+ const dur2 = 0.6;
1952
+ const { out, n } = buffer(dur2);
1953
+ let phase = 0, lp = 0;
1954
+ for (let i = 0; i < n; i++) {
1955
+ const t = i / SAMPLE_RATE;
1956
+ const freq = 70 * pitch * Math.pow(0.5, t / 0.3);
1957
+ phase += TAU * freq / SAMPLE_RATE;
1958
+ lp += 0.06 * (noise(i, seed) - lp);
1959
+ const body = t < 0.06 ? lp * 2.5 * (1 - t / 0.06) : 0;
1960
+ out[i] = (Math.sin(phase) * 1 + body) * expDecay(t, dur2, 3.2);
1961
+ }
1962
+ return out;
1963
+ }
1964
+ function knock(seed, pitch) {
1965
+ const dur2 = 0.14;
1966
+ const { out, n } = buffer(dur2);
1967
+ let phase = 0, lp = 0;
1968
+ for (let i = 0; i < n; i++) {
1969
+ const t = i / SAMPLE_RATE;
1970
+ const freq = 220 * pitch * Math.pow(0.7, t / 0.05);
1971
+ phase += TAU * freq / SAMPLE_RATE;
1972
+ lp += 0.3 * (noise(i, seed) - lp);
1973
+ const tap = t < 5e-3 ? lp : 0;
1974
+ out[i] = (Math.sin(phase) * 0.8 + tap * 0.6) * expDecay(t, dur2, 9);
1975
+ }
1976
+ return out;
1977
+ }
1978
+ function chime(seed, pitch) {
1979
+ const dur2 = 0.7;
1980
+ const { out, n } = buffer(dur2);
1981
+ const f0 = 800 * pitch;
1982
+ const partials = [
1983
+ { f: f0, a: 1, k: 4 },
1984
+ { f: f0 * 2.76, a: 0.5, k: 5.5 },
1985
+ { f: f0 * 5.4, a: 0.28, k: 7 },
1986
+ { f: f0 * 8.9, a: 0.13, k: 9 }
1987
+ ];
1988
+ for (let i = 0; i < n; i++) {
1989
+ const t = i / SAMPLE_RATE;
1990
+ let s = 0;
1991
+ for (const p of partials) s += Math.sin(TAU * p.f * t) * p.a * expDecay(t, dur2, p.k);
1992
+ const strike = t < 3e-3 ? noise(i, seed) * 0.3 : 0;
1993
+ out[i] = (s / 1.9 + strike) * 0.6;
1994
+ }
1995
+ return out;
1996
+ }
1997
+ function ding(_seed, pitch) {
1787
1998
  const dur2 = 0.5;
1788
1999
  const { out, n } = buffer(dur2);
1789
- let phase = 0;
2000
+ const f0 = 1200 * pitch;
1790
2001
  for (let i = 0; i < n; i++) {
1791
2002
  const t = i / SAMPLE_RATE;
1792
- const u = t / dur2;
1793
- const freq = 220 * Math.pow(880 / 220, u);
2003
+ const s = Math.sin(TAU * f0 * t) + 0.4 * Math.sin(TAU * f0 * 2 * t) + 0.2 * Math.sin(TAU * f0 * 3.01 * t);
2004
+ out[i] = s / 1.6 * expDecay(t, dur2, 4.5) * 0.6;
2005
+ }
2006
+ return out;
2007
+ }
2008
+ function coin(_seed, pitch) {
2009
+ const dur2 = 0.3;
2010
+ const { out, n } = buffer(dur2);
2011
+ let phase = 0;
2012
+ for (let i = 0; i < n; i++) {
2013
+ const t = i / SAMPLE_RATE, u = t / dur2;
2014
+ const freq = (t < 0.06 ? 988 : 1319) * pitch;
1794
2015
  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;
2016
+ const env = Math.min(1, u * 30) * expDecay(Math.max(0, t - 0.06), dur2, 3.5);
2017
+ out[i] = square(phase) * env * 0.55;
2018
+ }
2019
+ return out;
2020
+ }
2021
+ function sparkle(seed, pitch) {
2022
+ const dur2 = 0.6;
2023
+ const { out, n } = buffer(dur2);
2024
+ const steps = [1, 1.5, 2, 3, 4, 5, 6];
2025
+ const base = 1200 * pitch;
2026
+ for (let i = 0; i < n; i++) {
2027
+ const t = i / SAMPLE_RATE, u = t / dur2;
2028
+ let s = 0;
2029
+ for (let g = 0; g < steps.length; g++) {
2030
+ const on = u * steps.length - g;
2031
+ if (on > 0 && on < 3) {
2032
+ const ge = Math.exp(-on * 1.8);
2033
+ s += Math.sin(TAU * base * steps[g] * t + hash01(g, seed) * TAU) * ge;
2034
+ }
2035
+ }
2036
+ out[i] = s / 2.4 * Math.sin(Math.PI * u) ** 0.5 * 0.5;
2037
+ }
2038
+ return out;
2039
+ }
2040
+ function success(_seed, pitch) {
2041
+ const dur2 = 0.6;
2042
+ const { out, n } = buffer(dur2);
2043
+ const notes = [523.25, 659.25, 783.99].map((f) => f * pitch);
2044
+ let phase = 0, cur = 0;
2045
+ for (let i = 0; i < n; i++) {
2046
+ const t = i / SAMPLE_RATE, u = t / dur2;
2047
+ const idx = Math.min(notes.length - 1, Math.floor(u * 3.2));
2048
+ if (idx !== cur) cur = idx;
2049
+ phase += TAU * notes[cur] / SAMPLE_RATE;
2050
+ const local = u * 3.2 - idx;
2051
+ const env = Math.min(1, local * 12) * Math.min(1, (1 - Math.min(1, local)) * 4 + 0.2);
2052
+ out[i] = (Math.sin(phase) + 0.3 * Math.sin(2 * phase)) * env * 0.42;
1797
2053
  }
1798
2054
  return out;
1799
2055
  }
1800
- function shimmer(seed) {
2056
+ function shimmer(seed, pitch) {
1801
2057
  const dur2 = 0.9;
1802
2058
  const { out, n } = buffer(dur2);
1803
2059
  const partials = Array.from({ length: 5 }, (_, p) => ({
1804
- freq: 2e3 + hash01(p, seed + 7) * 2e3,
2060
+ freq: (2e3 + hash01(p, seed + 7) * 2e3) * pitch,
1805
2061
  am: 0.5 + hash01(p, seed + 8) * 1.5,
1806
2062
  phase: hash01(p, seed + 9) * TAU
1807
2063
  }));
1808
2064
  for (let i = 0; i < n; i++) {
1809
- const t = i / SAMPLE_RATE;
1810
- const u = t / dur2;
2065
+ const t = i / SAMPLE_RATE, u = t / dur2;
1811
2066
  const env = Math.sin(Math.PI * u) ** 1.2;
1812
2067
  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
- }
2068
+ 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
2069
  out[i] = s / 5 * env * 0.5;
1817
2070
  }
1818
2071
  return out;
1819
2072
  }
1820
- function thud(seed) {
1821
- const dur2 = 0.25;
2073
+ function zap(seed, pitch) {
2074
+ const dur2 = 0.22;
1822
2075
  const { out, n } = buffer(dur2);
1823
2076
  let phase = 0;
1824
- let lp = 0;
1825
2077
  for (let i = 0; i < n; i++) {
1826
- const t = i / SAMPLE_RATE;
1827
- const freq = 90 * Math.pow(45 / 90, t / 0.15);
2078
+ const t = i / SAMPLE_RATE, u = t / dur2;
2079
+ const freq = 1600 * pitch * Math.pow(0.12, u);
1828
2080
  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);
2081
+ const grit = noise(i, seed) * 0.25;
2082
+ out[i] = (square(phase) + grit) * expDecay(t, dur2, 4.5) * 0.5;
2083
+ }
2084
+ return out;
2085
+ }
2086
+ function error(_seed, pitch) {
2087
+ const dur2 = 0.4;
2088
+ const { out, n } = buffer(dur2);
2089
+ let phase = 0;
2090
+ for (let i = 0; i < n; i++) {
2091
+ const t = i / SAMPLE_RATE, u = t / dur2;
2092
+ const freq = (t < 0.16 ? 311 : 233) * pitch;
2093
+ phase += TAU * freq / SAMPLE_RATE;
2094
+ const seg = t < 0.16 ? u / 0.4 : (u - 0.4) / 0.6;
2095
+ const env = Math.min(1, seg * 18) * Math.min(1, (1 - seg) * 6);
2096
+ out[i] = square(phase) * env * 0.5;
1832
2097
  }
1833
2098
  return out;
1834
2099
  }
1835
2100
  function synthSfx(name, params = {}) {
1836
- const samples = RECIPES[name](params.seed ?? 0);
2101
+ const seed = params.seed ?? 0;
2102
+ const pitch = (params.pitch ?? 1) * seedPitch(seed);
2103
+ const samples = RECIPES[name](seed, pitch);
1837
2104
  if (params.gainDb) {
1838
2105
  const g = Math.pow(10, params.gainDb / 20);
1839
2106
  for (let i = 0; i < samples.length; i++) samples[i] *= g;
1840
2107
  }
2108
+ let peak = 0;
2109
+ for (let i = 0; i < samples.length; i++) peak = Math.max(peak, Math.abs(samples[i]));
2110
+ if (peak > 0.95) {
2111
+ const g = 0.95 / peak;
2112
+ for (let i = 0; i < samples.length; i++) samples[i] *= g;
2113
+ }
1841
2114
  return samples;
1842
2115
  }
1843
- function synthAmbientPad(duration, seed = 0) {
2116
+ function pad(freqs, duration, seed, opts) {
1844
2117
  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 }
2118
+ const voices = freqs.flatMap((f, v) => [
2119
+ { 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 },
2120
+ { 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
2121
  ]);
1849
2122
  for (let i = 0; i < n; i++) {
1850
2123
  const t = i / SAMPLE_RATE;
1851
2124
  let s = 0;
1852
2125
  for (const voice of voices) {
1853
2126
  s += Math.sin(TAU * voice.freq * t + voice.phase) * (0.75 + 0.25 * Math.sin(TAU * voice.am * t));
2127
+ if (opts.bright > 0) s += opts.bright * Math.sin(TAU * voice.freq * 2 * t + voice.phase);
1854
2128
  }
1855
- out[i] = s / voices.length * 0.7;
2129
+ out[i] = s / voices.length * opts.gain;
1856
2130
  }
1857
2131
  return out;
1858
2132
  }
1859
- var noise, TAU, expDecay, RECIPES;
2133
+ function synthAmbientPad(duration, seed = 0) {
2134
+ return pad([110, 165, 220], duration, seed, { amBase: 0.05, bright: 0, gain: 0.7 });
2135
+ }
2136
+ function synthLofi(duration, seed = 0) {
2137
+ return pad([130.81, 164.81, 196, 246.94], duration, seed, { amBase: 0.04, bright: 0.04, gain: 0.62 });
2138
+ }
2139
+ function synthPulse(duration, _seed = 0) {
2140
+ const { out, n } = buffer(duration);
2141
+ const beat2 = 2.2;
2142
+ for (let i = 0; i < n; i++) {
2143
+ const t = i / SAMPLE_RATE;
2144
+ const ph = t * beat2 % 1;
2145
+ const gate = Math.exp(-ph * 5) * 0.9 + 0.1;
2146
+ const s = Math.sin(TAU * 82 * t) + 0.6 * Math.sin(TAU * 123 * t) + 0.3 * Math.sin(TAU * 246 * t);
2147
+ out[i] = s / 1.9 * gate * 0.6;
2148
+ }
2149
+ return out;
2150
+ }
2151
+ function synthTension(duration, seed = 0) {
2152
+ const { out, n } = buffer(duration);
2153
+ const base = [98, 104, 110];
2154
+ for (let i = 0; i < n; i++) {
2155
+ const t = i / SAMPLE_RATE;
2156
+ const drift = 1 + 0.03 * (t / Math.max(1e-3, duration));
2157
+ let s = 0;
2158
+ for (let v = 0; v < base.length; v++) {
2159
+ const f = base[v] * drift * (1 + (hash01(v, seed) - 0.5) * 6e-3);
2160
+ s += Math.sin(TAU * f * t + hash01(v, seed + 1) * TAU);
2161
+ }
2162
+ const swell = 0.6 + 0.4 * Math.sin(TAU * 0.08 * t);
2163
+ out[i] = s / base.length * swell * 0.6;
2164
+ }
2165
+ return out;
2166
+ }
2167
+ function synthUplift(duration, seed = 0) {
2168
+ return pad([196, 246.94, 293.66, 392], duration, seed, { amBase: 0.07, bright: 0.1, gain: 0.6 });
2169
+ }
2170
+ function synthBgm(name, duration, seed = 0) {
2171
+ return BGM_RECIPES[name](duration, seed);
2172
+ }
2173
+ var noise, TAU, expDecay, square, PITCH_STEPS, RECIPES, BGM_RECIPES;
1860
2174
  var init_synth = __esm({
1861
2175
  "../render-cli/src/audio/synth.ts"() {
1862
2176
  "use strict";
@@ -1864,13 +2178,37 @@ var init_synth = __esm({
1864
2178
  noise = (n, seed) => hash01(n, seed) * 2 - 1;
1865
2179
  TAU = Math.PI * 2;
1866
2180
  expDecay = (t, dur2, k = 5) => Math.exp(-k * t / dur2);
2181
+ square = (ph) => (Math.sin(ph) + 0.33 * Math.sin(3 * ph) + 0.2 * Math.sin(5 * ph)) / 1.4;
2182
+ PITCH_STEPS = [0, 2, 4, 7, 9, 12, 5, -3, 16, -5];
1867
2183
  RECIPES = {
1868
2184
  whoosh,
1869
- pop,
1870
- tick,
2185
+ swish,
1871
2186
  rise,
2187
+ riser,
2188
+ warp,
2189
+ tick,
2190
+ click,
2191
+ blip,
2192
+ pop,
2193
+ select,
2194
+ thud,
2195
+ boom,
2196
+ knock,
2197
+ chime,
2198
+ ding,
2199
+ coin,
2200
+ sparkle,
1872
2201
  shimmer,
1873
- thud
2202
+ success,
2203
+ zap,
2204
+ error
2205
+ };
2206
+ BGM_RECIPES = {
2207
+ "ambient-pad": synthAmbientPad,
2208
+ lofi: synthLofi,
2209
+ pulse: synthPulse,
2210
+ tension: synthTension,
2211
+ uplift: synthUplift
1874
2212
  };
1875
2213
  }
1876
2214
  });
@@ -1925,7 +2263,7 @@ async function resolveBgmFile(source, duration, sceneDir) {
1925
2263
  }
1926
2264
  throw new Error(`bgm file "${p}" not found`);
1927
2265
  }
1928
- return writeCached(`ambient-pad-${duration.toFixed(2)}`, () => synthAmbientPad(duration));
2266
+ return writeCached(`${source.name}-${duration.toFixed(2)}`, () => synthBgm(source.name, duration));
1929
2267
  }
1930
2268
  var ROOT, VENDORED, CACHE;
1931
2269
  var init_sfx = __esm({