kasaframe 0.1.5 → 0.1.7
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/NOTICE +13 -0
- package/README.md +2 -2
- package/docs/AGENTS.md +1 -0
- package/package.json +1 -1
- package/packages/adapters/src/project.ts +2 -0
- package/packages/check/package.json +1 -0
- package/packages/check/src/check.ts +8 -2
- package/packages/check/src/rules/frames.ts +4 -5
- package/packages/cli/src/commands/direct.ts +24 -4
- package/packages/cli/src/commands/make.ts +15 -5
- package/packages/cli/src/commands/render.ts +34 -7
- package/packages/cli/src/mix.ts +66 -4
- package/packages/cli/src/segments.ts +1 -1
- package/packages/cli/src/sfx.ts +429 -0
- package/packages/core/src/frame.ts +7 -4
- package/packages/core/src/index.ts +3 -1
- package/packages/core/src/project.ts +19 -0
- package/packages/core/src/sfx.ts +70 -0
- package/packages/director/src/assemble.ts +15 -6
- package/packages/director/src/fallback.ts +14 -2
- package/packages/director/src/index.ts +1 -1
- package/packages/director/src/pattern.ts +2 -0
- package/packages/director/src/patterns/fill/cells.ts +5 -2
- package/packages/director/src/scene.ts +17 -5
- package/packages/director/src/transitions.ts +21 -8
- package/packages/motion/src/enter.ts +7 -1
- package/packages/motion/src/impact.ts +5 -2
- package/packages/motion/src/moves/dress.ts +15 -2
- package/packages/motion/src/moves/menu.ts +11 -5
- package/packages/motion/src/transition.ts +3 -1
- package/packages/motion/src/transition3d/index.ts +3 -1
- package/packages/motion/src/transition3d/live.ts +5 -3
- package/packages/patterns/src/bars/rank.ts +6 -7
- package/packages/patterns/src/before-after/shrink.ts +3 -4
- package/packages/patterns/src/before-after/strike.ts +4 -5
- package/packages/patterns/src/code/terminal.ts +25 -16
- package/packages/patterns/src/end/lockup.ts +13 -10
- package/packages/patterns/src/end/slam.ts +14 -8
- package/packages/patterns/src/hook-number/counter.ts +15 -9
- package/packages/patterns/src/hook-number/slam.ts +6 -8
- package/packages/patterns/src/kit.ts +364 -59
- package/packages/patterns/src/limit/gauge.ts +19 -13
- package/packages/patterns/src/quote/karaoke.ts +17 -12
- package/packages/patterns/src/sfx.ts +80 -0
- package/packages/patterns/src/steps/path.ts +15 -7
- package/packages/patterns/src/term-reveal/chips.ts +24 -10
- package/packages/patterns/src/term-reveal/expand.ts +17 -13
- package/packages/patterns/src/timeline/axis.ts +79 -25
- package/packages/patterns/src/title/mask.ts +15 -10
- package/packages/patterns/src/title/slam.ts +1 -1
- package/packages/patterns/src/types.ts +1 -0
- package/packages/plan/src/brief.ts +5 -2
- package/packages/themes/src/index.ts +22 -0
- package/packages/themes/src/slots.ts +32 -2
- package/packages/themes/src/types.ts +28 -0
- package/sfx/README.md +82 -0
- package/sfx/click/appear.wav +0 -0
- package/sfx/click/burst.wav +0 -0
- package/sfx/click/drop.wav +0 -0
- package/sfx/click/end.wav +0 -0
- package/sfx/click/hop.wav +0 -0
- package/sfx/click/land.wav +0 -0
- package/sfx/click/select.wav +0 -0
- package/sfx/click/stick.wav +0 -0
- package/sfx/click/tick.wav +0 -0
- package/sfx/click/type.wav +0 -0
- package/sfx/click/whoosh.wav +0 -0
- package/sfx/pop/appear.wav +0 -0
- package/sfx/pop/burst.wav +0 -0
- package/sfx/pop/drop.wav +0 -0
- package/sfx/pop/end.wav +0 -0
- package/sfx/pop/hop.wav +0 -0
- package/sfx/pop/land.wav +0 -0
- package/sfx/pop/select.wav +0 -0
- package/sfx/pop/stick.wav +0 -0
- package/sfx/pop/tick.wav +0 -0
- package/sfx/pop/type.wav +0 -0
- package/sfx/pop/whoosh.wav +0 -0
- package/sfx/tags.json +545 -0
- package/sfx/ui/appear.wav +0 -0
- package/sfx/ui/burst.wav +0 -0
- package/sfx/ui/drop.wav +0 -0
- package/sfx/ui/end.wav +0 -0
- package/sfx/ui/hop.wav +0 -0
- package/sfx/ui/land.wav +0 -0
- package/sfx/ui/select.wav +0 -0
- package/sfx/ui/stick.wav +0 -0
- package/sfx/ui/tick.wav +0 -0
- package/sfx/ui/type.wav +0 -0
- package/sfx/ui/whoosh.wav +0 -0
- package/themes/kawaii-sticker/effects.ts +15 -3
- package/themes/kawaii-sticker/theme.json +1 -0
- package/themes/og-measure/THEME.md +13 -2
- package/themes/og-measure/effects.ts +76 -59
- package/themes/og-measure/sample/scenes/end.ts +2 -2
- package/themes/og-measure/sample/scenes/gather.ts +2 -10
- package/themes/og-measure/sample/scenes/models.ts +2 -10
- package/themes/og-measure/sample/scenes/one.ts +2 -1
- package/themes/og-measure/sample/scenes/price.ts +4 -16
- package/themes/og-measure/slots.ts +29 -0
- package/themes/og-measure/still.png +0 -0
- package/themes/og-measure/style.json +3 -0
- package/themes/og-measure/theme.json +15 -1
- package/themes/og-measure/variants/snow-blue.json +3 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { freezeLimits } from "@kasaframe/core";
|
|
2
3
|
import { captionMode } from "@kasaframe/motion/captions";
|
|
3
4
|
import { bundleFor, MENU, type Dress } from "@kasaframe/motion/menu";
|
|
4
5
|
import { questionsMarkdown, type Question } from "@kasaframe/gen";
|
|
5
|
-
import { loadTheme } from "@kasaframe/themes";
|
|
6
|
+
import { loadTheme, themeFreezeSec } from "@kasaframe/themes";
|
|
6
7
|
import type { GenSpec, ShotSpec } from "./types.ts";
|
|
7
8
|
import { join } from "node:path";
|
|
8
9
|
import { fileURLToPath } from "node:url";
|
|
@@ -125,6 +126,8 @@ export function projectBrief(dir: string): string {
|
|
|
125
126
|
const rules = purposeRules(kf.template);
|
|
126
127
|
const tplFile = join(repo, "templates", kf.template, "template.json");
|
|
127
128
|
const tplMotion = existsSync(tplFile) ? (JSON.parse(readFileSync(tplFile, "utf8")).motion ?? {}) : {};
|
|
129
|
+
// kf check 와 같은 기준 — 틀이 style.json 을 덮고, 테마는 오류 기준을 늘리기만 한다(늘린 만큼은 경고로 남는다)
|
|
130
|
+
const freeze = freezeLimits({ ...(style.motion ?? {}), ...tplMotion }.maxFreezeSec, themeFreezeSec(kf.theme));
|
|
128
131
|
const colors = Object.keys(style.colors ?? {}).map((k: string) => `--kf-${k.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`);
|
|
129
132
|
const core = signatures("core");
|
|
130
133
|
const motion = signatures("motion");
|
|
@@ -135,7 +138,7 @@ export function projectBrief(dir: string): string {
|
|
|
135
138
|
...questionsMarkdown(plan.asks ?? [], (flags) => `npx kf make --answer ${dir} ${flags}`),
|
|
136
139
|
...narrationSection(dir, kf, plan),
|
|
137
140
|
`**이 영상의 규칙 — 용도 ${kf.template}(틀이 기본값을 덮는다, kf check 가 같은 값으로 잡는다)**`,
|
|
138
|
-
`1. 화면은 ${
|
|
141
|
+
`1. 화면은 ${freeze.error}초(60fps ${Math.round(freeze.error * 60)}프레임) 넘게 멈추지 않는다${freeze.error > freeze.warn ? `(테마 ${kf.theme} 가 늘린 기준 — ${freeze.warn}초 넘는 멈춤도 경고로 남는다)` : ""} — 끝 카드·큰 숫자·로고도 예외 없이 화면 안 움직임(숫자 굴러가기·칸 채우기·반짝임·글자 흐름·바탕 흐름)으로 계속 살아 있게.`,
|
|
139
142
|
`2. 같은 구도는 ${rules.staticSec.warn}초 넘기지 않는다(${rules.staticSec.error}초 넘으면 오류) — 새 요소·칸 채우기·숫자 키우기·컷·전환으로 바꾼다.`,
|
|
140
143
|
rules.beatMove === "every-beat"
|
|
141
144
|
? "3. 미세하게 흔들리는 것만으로 때우지 않는다 — 눈에 보이는 큰 움직임이 박마다 하나 이상."
|
|
@@ -39,6 +39,9 @@ export function checkTheme(dir: string): string[] {
|
|
|
39
39
|
if (m.status === "approved" && !m.approvedAt) bad.push("approved 인데 approvedAt 이 없다");
|
|
40
40
|
if (!Array.isArray(m.purposes) || !m.purposes.length) bad.push("theme.json purposes 가 비었다");
|
|
41
41
|
if (!Array.isArray(m.aspect) || !m.aspect.length) bad.push("theme.json aspect 가 비었다");
|
|
42
|
+
if (m.transitions !== undefined && (!Array.isArray(m.transitions) || !m.transitions.length || m.transitions.some((x) => typeof x !== "string" || !x))) bad.push("theme.json transitions 는 전환 이름 배열(비우려면 칸을 지운다)");
|
|
43
|
+
const freeze = m.motion?.maxFreezeSec;
|
|
44
|
+
if (m.motion !== undefined && (typeof m.motion !== "object" || (freeze !== undefined && !(typeof freeze === "number" && freeze > 0 && freeze <= 1)))) bad.push("theme.json motion.maxFreezeSec 는 0 보다 크고 1 이하인 초(멈춤 오류 기준 — 틀·style.json 값보다 늘리기만 한다)");
|
|
42
45
|
const n = m.effects?.length ?? 0;
|
|
43
46
|
if (n < 5 || n > 10) bad.push(`효과는 5–10개 — 지금 ${n}개`);
|
|
44
47
|
for (const e of m.effects ?? []) if (!["value", "layer", "rig"].includes(e.kind)) bad.push(`효과 ${e.name} kind 는 value|layer|rig`);
|
|
@@ -150,6 +153,25 @@ export const cellPath = (id: string): string => {
|
|
|
150
153
|
return join(cellsDir, world, `${variant ?? "base"}.png`);
|
|
151
154
|
};
|
|
152
155
|
|
|
156
|
+
// 세계의 theme.json — 변주는 세계 것을 따른다. 없는 테마(npm 묶음에 안 든 것 포함)면 null
|
|
157
|
+
function worldManifest(id: string | null | undefined): Partial<ThemeManifest> | null {
|
|
158
|
+
if (!id) return null;
|
|
159
|
+
const file = join(themesDir, splitThemeId(id).world, "theme.json");
|
|
160
|
+
return existsSync(file) ? readJson<Partial<ThemeManifest>>(file) : null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** 테마가 허용한 장면 사이 전환(theme.json transitions). 정하지 않았거나 없는 테마면 null(다 허용) */
|
|
164
|
+
export function themeTransitions(id: string | null | undefined): string[] | null {
|
|
165
|
+
const t = worldManifest(id)?.transitions;
|
|
166
|
+
return Array.isArray(t) ? t : null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** 테마가 정한 멈춤 오류 기준(theme.json motion.maxFreezeSec, 초) — 쓰는 쪽은 freezeLimits 로 틀·style.json 값과 견준다. 없으면 null */
|
|
170
|
+
export function themeFreezeSec(id: string | null | undefined): number | null {
|
|
171
|
+
const v = worldManifest(id)?.motion?.maxFreezeSec;
|
|
172
|
+
return typeof v === "number" && v > 0 ? v : null;
|
|
173
|
+
}
|
|
174
|
+
|
|
153
175
|
/** 3D 무대(rig 효과)를 쓰는 테마인가 — 묻기 보기·견본 시트의 2D/3D 표시 */
|
|
154
176
|
export const themeDims = (t: Pick<ThemeManifest, "effects">): "2D" | "3D" => (t.effects.some((e) => e.kind === "rig") ? "3D" : "2D");
|
|
155
177
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
|
-
// 페이지(Vite) 쪽. 장면 문법 패턴은 테마 이름을 모른다 — 일반 자리
|
|
2
|
+
// 페이지(Vite) 쪽. 장면 문법 패턴은 테마 이름을 모른다 — 일반 자리 일곱을 부르고, 테마가 themes/<id>/slots.ts 에서 자기 효과로 채운다.
|
|
3
3
|
// effects.ts 와 따로 두는 까닭: effects.ts 내보내기는 theme.json 효과 목록과 같아야 한다(테마 시험).
|
|
4
4
|
import type { SceneCtx, Style } from "@kasaframe/core";
|
|
5
5
|
import type { DropBurstState } from "@kasaframe/motion";
|
|
@@ -7,6 +7,31 @@ import { worldOf } from "./name.ts";
|
|
|
7
7
|
|
|
8
8
|
export type Paint = (t: number) => void;
|
|
9
9
|
|
|
10
|
+
/** 짚는 칸 자리 — 칸을 깔 상자(왼쪽 위·크기, 패턴이 정한 좌표 px) */
|
|
11
|
+
export interface MarkBox {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
w: number;
|
|
15
|
+
h: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MarkPaint {
|
|
19
|
+
/** 칸이 덮은 글의 색 — 비우면 글색을 그대로 둔다(칸이 옅게 글 뒤에 깔리는 테마) */
|
|
20
|
+
ink?: string;
|
|
21
|
+
/**
|
|
22
|
+
* 짚기 방식 — true 면 장면의 요점(주인공·그 순간 목소리가 말하는 값)에 내려앉아 머물고 다음 요점에서만 옮긴다. 비우면 박 정지점마다
|
|
23
|
+
* 주인공과 나머지를 번갈아 훑는다(내용이 다 선 뒤 쉬지 않는 몫을 짚개가 진다). 머무는 테마는 쉬는 구간을 멈춤 기준(theme.json
|
|
24
|
+
* motion.maxFreezeSec)과 내용 움직임으로 감당해야 한다
|
|
25
|
+
*/
|
|
26
|
+
dwell?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* u(0..1)만큼 간 칸 자리 — from 칸(앞 요점)에서 to 칸(새 요점)으로 옮겨 가는 중. from 이 null 이면 to 자리에 처음 나타나는 중.
|
|
29
|
+
* u 는 옮김 동안 고르게 흐르고 u=1 이면 to 그대로 — 머무는 짚개(dwell)는 0.3초 안팎에 옮겨 다음 요점까지 서고, 훑는 짚개는 박 정지점 사이를 내내 옮긴다.
|
|
30
|
+
* 감속·모양 바꿈(높이·칠 짙기)은 테마 몫이고 el 칠을 그 크기에 맞춰 바꿔도 된다
|
|
31
|
+
*/
|
|
32
|
+
at(from: MarkBox | null, to: MarkBox, u: number): MarkBox;
|
|
33
|
+
}
|
|
34
|
+
|
|
10
35
|
/**
|
|
11
36
|
* 모든 슬롯은 같은 t 면 같은 화면이고, 난수는 seed 로만. 비운 슬롯은 패턴이 제 기본으로 그린다.
|
|
12
37
|
* 화면 전체를 덮는 3D(three)는 surface 에서만 — 크롬이 WebGL 문맥을 몇 개만 살려 두므로 나머지 슬롯은 DOM·캔버스 2D 로.
|
|
@@ -33,9 +58,14 @@ export interface ThemeSlots {
|
|
|
33
58
|
trace?(el: HTMLElement, o: { ctx: SceneCtx; points: readonly (readonly [number, number])[]; at: number; dur: number; seed: string; tone?: "base" | "accent" }): Paint;
|
|
34
59
|
/** 드롭 터짐 — 그 테마의 드롭 연출. 돌려준 shake 는 패턴이 화면 틀에, punch 는 주인공에 준다. 드롭 번호로만 */
|
|
35
60
|
drop?(el: HTMLElement, o: { ctx: SceneCtx; drop: number }): (t: number) => DropBurstState;
|
|
61
|
+
/**
|
|
62
|
+
* 짚음 — 패턴이 「지금 볼 것」(장면의 요점·목소리가 말하는 값)을 짚는 칸(짚개·반전 짚음)의 모양. el = 패턴이 지은 빈 칸(position:absolute, 글 뒤에 깔린다).
|
|
63
|
+
* 칸 칠(바탕·테두리·모서리)은 여기서 하고, 자리·크기는 패턴이 프레임마다 돌려받은 at 으로 준다. 비우면 강조색 꽉 찬 칸 + onAccent 글
|
|
64
|
+
*/
|
|
65
|
+
mark?(el: HTMLElement, o: { ctx: SceneCtx; seed: string }): MarkPaint;
|
|
36
66
|
}
|
|
37
67
|
|
|
38
|
-
export const SLOT_NAMES = ["surface", "panel", "land", "write", "trace", "drop"] as const satisfies readonly (keyof ThemeSlots)[];
|
|
68
|
+
export const SLOT_NAMES = ["surface", "panel", "land", "write", "trace", "drop", "mark"] as const satisfies readonly (keyof ThemeSlots)[];
|
|
39
69
|
|
|
40
70
|
const mods = import.meta.glob("../../../themes/*/slots.ts", { eager: true }) as Record<string, { slots?: ThemeSlots }>;
|
|
41
71
|
|
|
@@ -29,6 +29,13 @@ export interface ThemeManifest {
|
|
|
29
29
|
purposes: string[];
|
|
30
30
|
/** 견본이 서는 화면비. 앞이 견본의 화면비 */
|
|
31
31
|
aspect: string[];
|
|
32
|
+
/** 장면 사이 전환 허용 목록 — 덮기(none·fade·brushWipe·irisBlades·flood)와 이음매(dive·portal·… · gl:<이름>) 이름. 없으면 다 허용, 모르는 이름은 허용 안 한 것과 같다 */
|
|
33
|
+
transitions?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* 검사 기준 — maxFreezeSec 은 멈춤 오류 기준(초). 실제 오류 기준은 틀·style.json 값과 이것 중 큰 쪽이라 테마는 늘리기만 하고,
|
|
36
|
+
* 경고는 틀·style.json 값 그대로 남는다(core freezeLimits). 화면을 살려 두려고 칸을 돌리지 않고 쉬는 구간을 두는 세계가 쓴다
|
|
37
|
+
*/
|
|
38
|
+
motion?: { maxFreezeSec?: number };
|
|
32
39
|
effects: ThemeEffectInfo[];
|
|
33
40
|
/** 테마 폴더 기준 파일 이름. 기본 still.png · sample.mp4 */
|
|
34
41
|
still?: string;
|
|
@@ -37,6 +44,27 @@ export interface ThemeManifest {
|
|
|
37
44
|
vary?: ThemeVary;
|
|
38
45
|
/** 고르기 태그 — 분위기·밝기·용도·2D/3D */
|
|
39
46
|
tags?: ThemeTags;
|
|
47
|
+
/** 효과음 결 — 없으면 기본 결(ui)·기본 크기 */
|
|
48
|
+
sfx?: ThemeSfx;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* theme.json sfx — 그 세계가 내는 소리 결. 렌더가 장면 움직임에서 뽑은 소리 자리에 이 결의 파일을 깐다(레포 sfx/<set>/<역할>.wav).
|
|
53
|
+
* kf.json sfx 가 칸마다 덮고, kf.json sfx: false 나 kf render --sfx off 면 끈다
|
|
54
|
+
*/
|
|
55
|
+
export interface ThemeSfx {
|
|
56
|
+
/** 소리 결 — sfx/tags.json 의 sets 이름(pop 뽁·통 · click 딸깍·째깍 · ui 기본) */
|
|
57
|
+
set: string;
|
|
58
|
+
/** 둘레 곡(소리 자리 앞뒤 0.2초) 크기 대비 dB — 0 이면 곡과 비슷하게. 역할마다 렌더의 역할 크기(드롭 +2 · 째깍 -4 …)를 더한다 */
|
|
59
|
+
db?: number;
|
|
60
|
+
/** 1초에 몇 개까지(2초 창으로 센다 — 0.5 면 2초에 하나) — 넘으면 드롭·끝·휙·착지… 차례로 남긴다 */
|
|
61
|
+
perSec?: number;
|
|
62
|
+
/** 이 역할만 낸다 — 없으면 다 */
|
|
63
|
+
only?: string[];
|
|
64
|
+
/** 잔소리(낱낱이 나타남·숫자 째깍·타자)도 낸다 — 기본은 장면 바뀜·드롭·주인공 착지·짚개가 핵심 값에 닿는 순간만 */
|
|
65
|
+
chatter?: boolean;
|
|
66
|
+
/** 소리마다 곡을 이만큼(dB, 기본 3) 100ms 남짓 눌러 자리를 낸다 */
|
|
67
|
+
duck?: number;
|
|
40
68
|
}
|
|
41
69
|
|
|
42
70
|
export type TextureKind = "none" | "grain" | "paper" | "noise" | "scan" | "halftone";
|
package/sfx/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# 효과음 — 렌더가 장면 움직임 자리에 까는 짧은 소리
|
|
2
|
+
|
|
3
|
+
곡·목소리 위 세 번째 층. 장면 코드를 안 고쳐도 라이브러리 부품이 움직임이 시작하는 순간을 `__kf.events` 에 `sfx` 로 남기고(`@kasaframe/core` `cue`), `kf render` 가 **뜻 있는 자리만** 적게 골라 테마가 고른 **결**의 파일을 둘레 곡과 비슷한 크기로 깔고, 그 순간 곡을 짧게 눌러 자리를 낸다. 곡과 달리 npm 묶음에 실린다(전부 CC0).
|
|
4
|
+
|
|
5
|
+
- 결(`sets`)마다 역할 하나에 파일 하나 — `sfx/<결>/<역할>.wav`(48kHz 모노 16비트, 꼭대기 -1dBFS). 원본·겹친 소리·라이선스·`lead`(파일 시작 → 꼭대기 초)·`level`(100ms 창 RMS 최댓값 dBFS — 섞을 때 크기를 맞추는 기준)는 `tags.json`.
|
|
6
|
+
- **곡에 안 묻히는 건 2–6kHz 다.** 600–2000Hz 에 몸이 있는 소리는 6dB 키워도 곡 아래였다(렌더 보고서의 가려짐). 그래서 몸이 낮은 소리(뽁·통·쿵)는 그 대역의 짧은 어택을 꼭대기에 맞춰 겹쳐 짓는다(아래 표의 `+`).
|
|
7
|
+
- 테마가 결과 크기를 고른다 — `theme.json` 의 `sfx: { set, db, perSec, only, chatter, duck }`. 없으면 `ui` 결·둘레 곡 -2dB·1초 1개·잔소리 없음·곡 3dB 누름. 프로젝트 `kf.json` 의 `sfx` 가 칸마다 덮고, `sfx: false` 나 `kf render|make --sfx off` 면 끈다(끄면 소리는 효과음 이전과 바이트까지 같다).
|
|
8
|
+
- 다른 폴더를 쓰려면 `KF_SFX_DIR=<폴더>` — 그 폴더에도 같은 꼴의 `tags.json`.
|
|
9
|
+
- 다시 짓기: Kenney 묶음 zip 여섯(`https://kenney.nl/assets/<ui-audio|interface-sounds|impact-sounds|digital-audio|rpg-audio|casino-audio>`)을 한 폴더에 풀고 `node tools/sfx/build.ts --src <그 폴더>`. 묶음마다 `License.txt` 에서 CC0 를 확인하고 없으면 멈춘다.
|
|
10
|
+
|
|
11
|
+
## 결
|
|
12
|
+
|
|
13
|
+
| 결 | 소리 | 쓰는 세계 |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `pop` | 물방울 뽁·나무 통·둥근 쿵에 또렷한 톡 어택 | kawaii-sticker(둘레 곡과 같은 크기, 1초에 하나까지) |
|
|
16
|
+
| `click` | 역할마다 작은 스위치·시계 소리만(전부 2–6kHz) | og-measure(딸깍·째깍만, 둘레 곡 -4dB, 2초에 하나까지) |
|
|
17
|
+
| `ui` | 가벼운 UI 소리 — 퉁김·휙·주먹 쿵에 또렷한 어택 | 결을 안 고른 세계 |
|
|
18
|
+
|
|
19
|
+
## 역할 — 누가 저절로 남기나
|
|
20
|
+
|
|
21
|
+
기본으로 까는 것은 뜻 있는 자리 — 장면 바뀜·드롭·주인공 착지·짚개가 요점에 닿는 순간. 부품이 잔소리(`minor`)로 남긴 것(표의 「잔소리」)은 `chatter: true` 일 때만 깐다.
|
|
22
|
+
|
|
23
|
+
| 역할 | 뜻 | 저절로 남기는 부품 |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `appear` | 나타남 | 잔소리 — motion `enter`(rise·scaleIn·maskWipe·slide, fade·blurIn 은 소리 없이) · 패턴 판(panel) · og-measure 칸이 서는 순간 |
|
|
26
|
+
| `stick` | 톡 붙기 | kawaii 주인공 스티커(폭 400px 넘는 것 — 작은 이름표는 잔소리) · `stickOn` · 잔소리 — motion `enter("pop")` |
|
|
27
|
+
| `land` | 착지 | 패턴 `land` 슬롯·`slam`·숫자 굴림이 앉을 때 · og-measure 숫자 꽂힘 · 잔소리 — motion `enter("drop")` |
|
|
28
|
+
| `hop` | 통 뛰기 | 잔소리 — kawaii 스티커 `hops` |
|
|
29
|
+
| `whoosh` | 휙 전환 | 이음매(`transition3d` — 화면이 여럿이면 넘어갈 때마다) · `transition`(whip·zoomThrough·slide) · 컷 카메라(shotCam·trackCam) · 잔소리 — 떼어짐·테이프 |
|
|
30
|
+
| `tick` | 숫자 째깍 | 잔소리 — 패턴 숫자 굴림·카운터 · og-measure 계기판·점 |
|
|
31
|
+
| `select` | 짚기 딸깍 | 패턴 짚개가 요점으로 옮겨 갈 때 · og-measure 읽는 칸이 새 값에 닿을 때 |
|
|
32
|
+
| `type` | 타자 | 잔소리 — 패턴 `write` 슬롯(0.09초마다) · og-measure 로그 줄 |
|
|
33
|
+
| `drop` | 드롭 쿵 | motion `dropBurst`(slap·패턴 drop 슬롯) · beats.json 드롭 |
|
|
34
|
+
| `burst` | 팡 터짐 | kawaii `stickerBurst` |
|
|
35
|
+
| `end` | 끝 마무리 | og-measure 끝 도장 |
|
|
36
|
+
|
|
37
|
+
손으로 지은 움직임은 장면에서 `ctx.sfx("<역할>", 장면 초)` — 잔소리로 치지 않는다.
|
|
38
|
+
|
|
39
|
+
## 섞기
|
|
40
|
+
|
|
41
|
+
- 밀도: 같은 역할은 0.1초 안에 하나, 아무 소리끼리 0.25초 띄우고, 아무 2초 창에 `perSec`×2 개(적어도 하나)까지. 넘치면 드롭 > 끝 > 휙 > 착지 > 붙기·팡 > 뛰기 > 짚기 > 나타남 > 째깍 > 타자 차례로 남기고, 드롭 앞뒤 0.35초는 드롭만.
|
|
42
|
+
- 크기: 둘레 곡(소리 자리 앞뒤 0.2초) 크기 + `db` + 역할 크기(드롭 +2 … 째깍·타자 -4), 목소리가 있으면 4dB 더 낮게. 역할마다 저역을 걷는다(드롭 60Hz·그 밖 150–300Hz — 킥·베이스와 안 겹치게).
|
|
43
|
+
- 자리 내기: 소리마다 곡을 `duck` dB(기본 3) 소리 시작 10ms 앞부터 꼭대기 100ms 뒤까지 누른다.
|
|
44
|
+
- 가려짐: 소리가 차지하는 대역(제 가장 센 대역에서 10dB 안쪽) 가운데 곡을 가장 넘는 대역에서, 소리 순간(꼭대기 앞 10ms·뒤 90ms) 효과음 ÷ 눌린 곡이 4dB 가 안 되면 6dB 까지 더 키운다. 렌더 보고서 `sfx.hits` 에 소리마다 `band`·`smr`·`boost`·`over`(넓은 대역 크기 − 둘레 곡).
|
|
45
|
+
|
|
46
|
+
<!-- 파일 표 — tools/sfx/build.ts 가 짓는다 -->
|
|
47
|
+
|
|
48
|
+
| 파일 | 역할 | 길이 | 원본 | 라이선스 |
|
|
49
|
+
|---|---|---|---|---|
|
|
50
|
+
| `pop/appear.wav` | 나타남 | 0.15초 | Kenney Interface Sounds (1.0) — `drop_004.ogg` + Kenney Interface Sounds (1.0) — `select_001.ogg`(-6dB) | CC0 1.0 |
|
|
51
|
+
| `pop/stick.wav` | 톡 붙기 | 0.15초 | Kenney Interface Sounds (1.0) — `drop_002.ogg` + Kenney Interface Sounds (1.0) — `tick_004.ogg`(-4dB) | CC0 1.0 |
|
|
52
|
+
| `pop/land.wav` | 착지 | 0.12초 | Kenney Impact Sounds (1.0) — `impactWood_light_000.ogg` + Kenney Casino Audio (1.1) — `chip-lay-1.ogg`(-4dB) | CC0 1.0 |
|
|
53
|
+
| `pop/hop.wav` | 통 뛰기 | 0.30초 | Kenney Digital Audio — `phaseJump1.ogg` + Kenney UI SFX Set — `click5.ogg`(-6dB) | CC0 1.0 |
|
|
54
|
+
| `pop/whoosh.wav` | 휙 전환 | 0.25초 | Kenney Interface Sounds (1.0) — `maximize_002.ogg` | CC0 1.0 |
|
|
55
|
+
| `pop/tick.wav` | 숫자 째깍 | 0.02초 | Kenney Interface Sounds (1.0) — `tick_002.ogg` | CC0 1.0 |
|
|
56
|
+
| `pop/select.wav` | 짚기 딸깍 | 0.04초 | Kenney Interface Sounds (1.0) — `select_001.ogg` | CC0 1.0 |
|
|
57
|
+
| `pop/type.wav` | 타자 | 0.03초 | Kenney UI SFX Set — `click5.ogg` | CC0 1.0 |
|
|
58
|
+
| `pop/drop.wav` | 드롭 쿵 | 0.52초 | Kenney Impact Sounds (1.0) — `impactSoft_heavy_000.ogg` + Kenney Impact Sounds (1.0) — `impactMetal_light_000.ogg`(-3dB) | CC0 1.0 |
|
|
59
|
+
| `pop/burst.wav` | 팡 터짐 | 0.19초 | Kenney Digital Audio — `powerUp2.ogg` + Kenney Casino Audio (1.1) — `chips-collide-1.ogg`(-4dB) | CC0 1.0 |
|
|
60
|
+
| `pop/end.wav` | 끝 마무리 | 0.32초 | Kenney Interface Sounds (1.0) — `confirmation_003.ogg` | CC0 1.0 |
|
|
61
|
+
| `click/appear.wav` | 나타남 | 0.09초 | Kenney UI SFX Set — `click1.ogg` | CC0 1.0 |
|
|
62
|
+
| `click/stick.wav` | 톡 붙기 | 0.01초 | Kenney Interface Sounds (1.0) — `click_003.ogg` | CC0 1.0 |
|
|
63
|
+
| `click/land.wav` | 착지 | 0.14초 | Kenney Interface Sounds (1.0) — `switch_001.ogg` | CC0 1.0 |
|
|
64
|
+
| `click/hop.wav` | 통 뛰기 | 0.06초 | Kenney Interface Sounds (1.0) — `toggle_004.ogg` | CC0 1.0 |
|
|
65
|
+
| `click/whoosh.wav` | 휙 전환 | 0.22초 | Kenney Interface Sounds (1.0) — `scroll_004.ogg` | CC0 1.0 |
|
|
66
|
+
| `click/tick.wav` | 숫자 째깍 | 0.02초 | Kenney Interface Sounds (1.0) — `tick_001.ogg` | CC0 1.0 |
|
|
67
|
+
| `click/select.wav` | 짚기 딸깍 | 0.04초 | Kenney Interface Sounds (1.0) — `select_001.ogg` | CC0 1.0 |
|
|
68
|
+
| `click/type.wav` | 타자 | 0.05초 | Kenney UI SFX Set — `switch12.ogg` | CC0 1.0 |
|
|
69
|
+
| `click/drop.wav` | 드롭 쿵 | 0.21초 | Kenney RPG Audio — `metalLatch.ogg` | CC0 1.0 |
|
|
70
|
+
| `click/burst.wav` | 팡 터짐 | 0.13초 | Kenney Casino Audio (1.1) — `chip-lay-1.ogg` | CC0 1.0 |
|
|
71
|
+
| `click/end.wav` | 끝 마무리 | 0.11초 | Kenney Interface Sounds (1.0) — `toggle_002.ogg` | CC0 1.0 |
|
|
72
|
+
| `ui/appear.wav` | 나타남 | 0.13초 | Kenney Interface Sounds (1.0) — `pluck_001.ogg` + Kenney Interface Sounds (1.0) — `tick_004.ogg`(-6dB) | CC0 1.0 |
|
|
73
|
+
| `ui/stick.wav` | 톡 붙기 | 0.15초 | Kenney Interface Sounds (1.0) — `drop_001.ogg` + Kenney Interface Sounds (1.0) — `tick_004.ogg`(-6dB) | CC0 1.0 |
|
|
74
|
+
| `ui/land.wav` | 착지 | 0.15초 | Kenney Impact Sounds (1.0) — `impactGeneric_light_000.ogg` + Kenney Casino Audio (1.1) — `chip-lay-1.ogg`(-4dB) | CC0 1.0 |
|
|
75
|
+
| `ui/hop.wav` | 통 뛰기 | 0.14초 | Kenney Interface Sounds (1.0) — `pluck_002.ogg` + Kenney UI SFX Set — `click5.ogg`(-6dB) | CC0 1.0 |
|
|
76
|
+
| `ui/whoosh.wav` | 휙 전환 | 0.25초 | Kenney Interface Sounds (1.0) — `maximize_001.ogg` | CC0 1.0 |
|
|
77
|
+
| `ui/tick.wav` | 숫자 째깍 | 0.01초 | Kenney Interface Sounds (1.0) — `click_003.ogg` | CC0 1.0 |
|
|
78
|
+
| `ui/select.wav` | 짚기 딸깍 | 0.23초 | Kenney Interface Sounds (1.0) — `select_003.ogg` | CC0 1.0 |
|
|
79
|
+
| `ui/type.wav` | 타자 | 0.03초 | Kenney UI SFX Set — `click5.ogg` | CC0 1.0 |
|
|
80
|
+
| `ui/drop.wav` | 드롭 쿵 | 0.45초 | Kenney Impact Sounds (1.0) — `impactPunch_heavy_000.ogg` + Kenney Impact Sounds (1.0) — `impactMetal_light_000.ogg`(-3dB) | CC0 1.0 |
|
|
81
|
+
| `ui/burst.wav` | 팡 터짐 | 0.16초 | Kenney Casino Audio (1.1) — `chips-collide-1.ogg` | CC0 1.0 |
|
|
82
|
+
| `ui/end.wav` | 끝 마무리 | 0.14초 | Kenney Interface Sounds (1.0) — `switch_001.ogg` | CC0 1.0 |
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/sfx/pop/drop.wav
ADDED
|
Binary file
|
package/sfx/pop/end.wav
ADDED
|
Binary file
|
package/sfx/pop/hop.wav
ADDED
|
Binary file
|
package/sfx/pop/land.wav
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/sfx/pop/tick.wav
ADDED
|
Binary file
|
package/sfx/pop/type.wav
ADDED
|
Binary file
|
|
Binary file
|