218 lines
14 KiB
HTML
218 lines
14 KiB
HTML
<!doctype html>
|
||
<html lang="ko">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<meta name="description" content="micarrsim 음향 장면 및 ground truth 생성기" />
|
||
<title>Dataset Builder · micarrsim</title>
|
||
<link rel="stylesheet" href="/packages/ui/app-shell.css" />
|
||
<link rel="stylesheet" href="/apps/dataset-builder/styles.css" />
|
||
</head>
|
||
<body>
|
||
<div class="app-shell builder-shell">
|
||
<header class="app-header builder-header">
|
||
<a class="app-brand" href="/">
|
||
<strong>micarrsim</strong>
|
||
<span>Dataset Builder</span>
|
||
</a>
|
||
<nav class="builder-workflow" id="builder-workflow" aria-label="Scene 제작 순서">
|
||
<ol>
|
||
<li data-workflow-step="input"><span>1</span>입력</li>
|
||
<li data-workflow-step="edit"><span>2</span>배치·편집</li>
|
||
<li data-workflow-step="validate"><span>3</span>검증</li>
|
||
<li data-workflow-step="save"><span>4</span>저장</li>
|
||
</ol>
|
||
</nav>
|
||
<div class="header-actions">
|
||
<button class="button button--ghost" id="preview-button" type="button">▶ 미리 듣기</button>
|
||
<button class="button button--primary" id="export-button" type="button">Scene JSON 저장</button>
|
||
<details class="header-more">
|
||
<summary class="button button--ghost" aria-label="추가 작업">더보기</summary>
|
||
<div class="header-more__menu">
|
||
<button class="button button--ghost" id="wav-button" type="button" title="공간정보 없이 모든 음원을 합친 mono 확인용 WAV입니다.">Preview WAV 저장</button>
|
||
<a class="button button--ghost" href="/apps/acoustic-camera/" title="새 Camera 세션을 엽니다. 기존 Camera에서 편집을 시작했다면 원래 Camera 탭으로 돌아가 Scene JSON을 선택하세요.">새 Acoustic Camera 열기 →</a>
|
||
</div>
|
||
</details>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="builder-layout" id="builder-layout" data-split-layout>
|
||
<section class="scene-workspace" id="builder-workspace" aria-label="장면 편집 화면">
|
||
<div class="workspace-heading">
|
||
<div>
|
||
<p class="eyebrow">01 · 입력과 배치</p>
|
||
<h1 id="workspace-title">음향 장면</h1>
|
||
</div>
|
||
<div class="workspace-tools">
|
||
<label class="button button--ghost file-button">
|
||
Scene 불러오기
|
||
<input id="scene-input" type="file" accept="application/json,.json" />
|
||
</label>
|
||
<label class="button button--ghost file-button">
|
||
배경 이미지
|
||
<input id="image-input" type="file" accept="image/*" />
|
||
</label>
|
||
<button class="button button--danger is-hidden" id="remove-image-button" type="button">이미지 해제</button>
|
||
<label class="button button--ghost file-button" title="Scene을 다시 열었을 때 원본 파일을 SHA-256으로 재연결합니다.">
|
||
원본 재연결
|
||
<input id="audio-input" type="file" accept="audio/*,.wav,.flac,.mp3,.ogg,.m4a" multiple />
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="canvas-card">
|
||
<canvas id="scene-canvas" aria-label="이미지 평면과 음원 ground truth"></canvas>
|
||
<div class="canvas-empty" id="canvas-empty">
|
||
<strong>음원을 선택하고 canvas를 클릭해 위치를 지정하세요.</strong>
|
||
<span>배경 이미지는 선택 사항입니다.</span>
|
||
</div>
|
||
<div class="canvas-badge" id="canvas-badge">x −0.60…0.60 m · y −0.45…0.45 m</div>
|
||
</div>
|
||
|
||
<div class="source-strip-heading">
|
||
<div>
|
||
<p class="eyebrow">02 · 음원 편집</p>
|
||
<h2>음원과 이동 경로</h2>
|
||
<p>카드를 선택한 뒤 canvas를 클릭하면 현재 위치 또는 선택한 경로점이 이동합니다.</p>
|
||
</div>
|
||
<div class="source-actions">
|
||
<button class="button button--ghost" id="add-source-button" type="button">+ 합성 음원</button>
|
||
<label class="button button--primary file-button">
|
||
+ 오디오 파일 음원
|
||
<input id="audio-source-input" type="file" accept="audio/*,.wav,.flac,.mp3,.ogg,.m4a" multiple />
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="source-strip" id="source-list" aria-label="음원 목록"></div>
|
||
|
||
<section class="asset-shelf">
|
||
<div>
|
||
<h2>연결된 원본 파일</h2>
|
||
<details class="asset-help">
|
||
<summary>파일 연결 도움말</summary>
|
||
<p>원본은 업로드되지 않고 브라우저에서만 처리됩니다. Scene을 다시 열었다면 위의 <strong>원본 재연결</strong>에서 같은 파일을 선택하세요. PCM WAV를 권장합니다.</p>
|
||
</details>
|
||
</div>
|
||
<div class="asset-list" id="asset-list"></div>
|
||
</section>
|
||
</section>
|
||
|
||
<div
|
||
class="split-handle"
|
||
id="builder-split-handle"
|
||
role="separator"
|
||
tabindex="0"
|
||
aria-label="장면 설정 패널 너비 조절"
|
||
aria-controls="builder-controls"
|
||
aria-orientation="vertical"
|
||
data-split-resizer
|
||
data-storage-key="micarrsim.builder.control-panel-width"
|
||
title="드래그하거나 방향키로 너비 조절 · Enter/더블클릭으로 초기화"
|
||
></div>
|
||
|
||
<aside class="control-panel" id="builder-controls" data-split-panel aria-label="장면 파라미터">
|
||
<div class="runtime-line">
|
||
<span class="status-chip status-chip--neutral" data-runtime-status>런타임 확인 중…</span>
|
||
<span class="save-state" id="save-state">기본 장면</span>
|
||
</div>
|
||
|
||
<section class="validation-card" aria-live="polite">
|
||
<div class="validation-dot" id="validation-dot"></div>
|
||
<div><strong id="validation-title">검증 대기</strong><p id="validation-message">장면을 준비하고 있습니다.</p></div>
|
||
</section>
|
||
|
||
<section class="control-section control-section--primary" id="source-editor-section">
|
||
<div class="section-title"><span>02</span><h2>선택 음원 편집</h2></div>
|
||
<div id="no-source-message" class="inline-note">음원을 선택하세요.</div>
|
||
<div id="source-fields">
|
||
<label class="field field--wide"><span>이름</span><input id="source-name" type="text" maxlength="120" /></label>
|
||
<div class="field-grid field-grid--three">
|
||
<label class="field"><span>x (m)</span><input id="source-x" type="number" step="0.01" /></label>
|
||
<label class="field"><span>y (m)</span><input id="source-y" type="number" step="0.01" /></label>
|
||
<label class="field"><span>거리 z (m)</span><input id="source-z" type="number" min="0.01" step="0.1" /></label>
|
||
</div>
|
||
<div class="field-grid field-grid--three">
|
||
<label class="field"><span>Gain (dB)</span><input id="source-gain" type="number" min="-120" max="24" step="1" /></label>
|
||
<label class="field"><span>시작 (s)</span><input id="source-start" type="number" min="0" step="0.01" /></label>
|
||
<label class="field"><span>길이 (s)</span><input id="source-duration" type="number" min="0.01" step="0.01" /></label>
|
||
</div>
|
||
<section class="trajectory-editor" aria-label="음원 이동 경로">
|
||
<label class="check-field trajectory-toggle"><input id="source-motion" type="checkbox" /><span>시간에 따라 음원 이동</span></label>
|
||
<p class="section-note">켜면 현재 위치부터 Scene 끝까지의 기본 경로가 만들어집니다.</p>
|
||
<div id="trajectory-fields" class="is-hidden">
|
||
<label class="field field--wide trajectory-preview">
|
||
<span>경로 미리보기 · <output id="preview-time-value" for="preview-time">0.00 s</output></span>
|
||
<input id="preview-time" type="range" min="0" max="1" step="0.01" value="0" />
|
||
</label>
|
||
<p class="section-note">경로점을 선택하고 canvas를 클릭해 x/y를 옮기세요.</p>
|
||
<div class="trajectory-list" id="trajectory-list"></div>
|
||
<button class="button button--ghost button--full" id="add-keyframe-button" type="button">+ 경로점 추가</button>
|
||
</div>
|
||
</section>
|
||
<label class="field field--wide"><span>신호 종류</span><select id="source-type"><option value="tone">Tone</option><option value="noise">Noise</option><option value="asset">Audio asset</option></select></label>
|
||
<div id="tone-fields"><label class="field field--wide"><span>주파수 (Hz)</span><input id="tone-frequency" type="number" min="1" step="1" /></label></div>
|
||
<div id="noise-fields"><label class="field field--wide"><span>Noise color</span><select id="noise-color"><option value="white">White</option><option value="pink">Pink</option></select></label></div>
|
||
<div id="asset-fields">
|
||
<label class="field field--wide"><span>Audio asset</span><select id="source-asset"></select></label>
|
||
<div class="field-grid">
|
||
<label class="field"><span>Channel</span><input id="source-channel" type="number" min="0" max="255" step="1" /></label>
|
||
<label class="check-field"><input id="source-loop" type="checkbox" /><span>짧은 파일 반복</span></label>
|
||
</div>
|
||
</div>
|
||
<button class="button button--danger button--full" id="remove-source-button" type="button">선택 음원 삭제</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="control-section">
|
||
<div class="section-title"><span>설정</span><h2>장면 기본값</h2></div>
|
||
<label class="field field--wide">
|
||
<span>이름</span>
|
||
<input id="scene-name" type="text" maxlength="120" />
|
||
</label>
|
||
<label class="field field--wide">
|
||
<span>Scene ID</span>
|
||
<input id="scene-id" type="text" maxlength="120" pattern="[a-z0-9][a-z0-9._-]*" />
|
||
</label>
|
||
<label class="field field--wide"><span>길이 (s)</span><input id="scene-duration" type="number" min="0.05" max="300" step="0.05" /></label>
|
||
<details class="inline-disclosure">
|
||
<summary>고급 음향·믹스 설정</summary>
|
||
<div class="field-grid">
|
||
<label class="field"><span>Sample rate</span><input id="sample-rate" type="number" min="8000" max="192000" step="1" /></label>
|
||
<label class="field"><span>음속 (m/s)</span><input id="speed-of-sound" type="number" min="250" max="400" step="0.1" /></label>
|
||
<label class="field"><span>Seed</span><input id="scene-seed" type="number" min="0" max="4294967295" step="1" /></label>
|
||
<label class="field"><span>Mix 정규화</span><select id="mix-normalization"><option value="none">없음</option><option value="peak">Peak</option></select></label>
|
||
<label class="field"><span>Target peak</span><input id="target-peak" type="number" min="0.01" max="1" step="0.01" /></label>
|
||
</div>
|
||
</details>
|
||
</section>
|
||
|
||
<details class="control-section disclosure-section">
|
||
<summary class="section-summary"><span>범위</span><strong>표시 평면</strong><small>고급</small></summary>
|
||
<div class="field-grid field-grid--compact">
|
||
<label class="field"><span>x min</span><input id="plane-x-min" type="number" step="0.05" /></label>
|
||
<label class="field"><span>x max</span><input id="plane-x-max" type="number" step="0.05" /></label>
|
||
<label class="field"><span>y min</span><input id="plane-y-min" type="number" step="0.05" /></label>
|
||
<label class="field"><span>y max</span><input id="plane-y-max" type="number" step="0.05" /></label>
|
||
<label class="field"><span>plane z (m)</span><input id="plane-z" type="number" min="0.01" step="0.1" /></label>
|
||
</div>
|
||
<p class="section-note">x/y는 canvas 범위이며 배경이 있으면 image plane으로 저장됩니다.</p>
|
||
</details>
|
||
|
||
<details class="control-section disclosure-section">
|
||
<summary class="section-summary"><span>배경음</span><strong>Ambient</strong><small>선택 사항</small></summary>
|
||
<label class="field field--wide"><span>종류</span><select id="ambient-type"><option value="none">없음</option><option value="white">White noise</option><option value="pink">Pink noise</option><option value="asset">Audio asset</option></select></label>
|
||
<label class="field field--wide" id="ambient-asset-field"><span>Audio asset</span><select id="ambient-asset"></select></label>
|
||
<div class="field-grid" id="ambient-asset-options">
|
||
<label class="field"><span>Channel</span><input id="ambient-channel" type="number" min="0" max="255" step="1" /></label>
|
||
<label class="check-field"><input id="ambient-loop" type="checkbox" /><span>짧은 파일 반복</span></label>
|
||
</div>
|
||
<label class="field field--wide"><span>Gain (dB)</span><input id="ambient-gain" type="number" min="-120" max="24" step="1" /></label>
|
||
</details>
|
||
</aside>
|
||
</main>
|
||
</div>
|
||
<script type="module" src="/packages/ui/runtime-status.js"></script>
|
||
<script type="module" src="/apps/dataset-builder/main.js"></script>
|
||
</body>
|
||
</html>
|