From 513de2b51f2a8e22c7a93b0834fc090597e7c017 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 28 Aug 2026 12:47:57 +0900 Subject: [PATCH] docs: add quick start and user guides --- README.md | 188 +++++++++++++++++-- apps/acoustic-camera/README.md | 91 +++++++++- apps/acoustic-camera/index.html | 13 +- apps/acoustic-camera/styles.css | 3 + apps/dataset-builder/README.md | 64 ++++++- assets/array-geometries/README.md | 39 +++- assets/sample-scenes/README.md | 11 +- docs/benchmarks/README.md | 7 +- docs/data-contracts.md | 39 +++- docs/execution-plan.md | 54 +++--- docs/index.html | 192 ++++++++++++++++++++ docs/styles.css | 291 ++++++++++++++++++++++++++++++ index.html | 47 ++++- packages/ui/app-shell.css | 120 +++++++++++- tests/integration/README.md | 29 ++- tests/unit/README.md | 25 ++- 16 files changed, 1148 insertions(+), 65 deletions(-) create mode 100644 docs/index.html create mode 100644 docs/styles.css diff --git a/README.md b/README.md index 649972f..475d23f 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,175 @@ # micarrsim -192채널 MEMS 마이크 배열의 배치와 음향 영상 성능을 평가하기 위한 WebGPU 기반 시뮬레이터입니다. +192채널 MEMS 마이크 배열의 배치와 자유음장 DAS 음향 영상을 비교하는 로컬 브라우저 +시뮬레이터입니다. Dataset Builder에서 재현 가능한 음향 장면을 만들고 Acoustic Camera에서 +CPU 또는 WebGPU로 배열 응답, acoustic map과 위치 검출 지표를 계산합니다. -현재는 설계 단계입니다. 프로젝트 목표는 [`goal.md`](goal.md), 구현 로드맵과 기술 결정은 -[`docs/execution-plan.md`](docs/execution-plan.md)를 참고하세요. +별도 프로그램이나 패키지를 설치하지 않습니다. 모든 앱과 계산은 이 저장소와 브라우저 안에서 +실행됩니다. -## 디렉터리 +## 실행 환경 -```text -apps/ - dataset-builder/ 음원 합성 및 ground truth 생성 UI - acoustic-camera/ 전파, DAC 빔포밍, 평가 및 시각화 UI -packages/ - acoustics-core/ 공통 좌표계, 음향 모델, 평가 로직 - webgpu-kernels/ WGSL 기반 steering/DAC 연산 -schemas/ 배열·장면·실험 JSON Schema -assets/ - array-geometries/ 마이크 배열 예제 - sample-scenes/ 재배포 가능한 소규모 예제 장면 -tests/ - unit/ 수치 모델 단위 테스트 - integration/ 앱 간 데이터 계약과 기준 시나리오 테스트 -docs/ 설계 및 실행 계획 +- Windows 10/11 +- 최신 Microsoft Edge 또는 Google Chrome +- 저장소에 포함된 Windows PowerShell과 브라우저 외 추가 런타임 불필요 +- WebGPU가 없거나 장치를 열 수 없어도 CPU backend로 실행 가능 + +## 가장 간단하게 시작하기 + +1. 저장소 최상위의 `start.cmd`를 더블클릭합니다. +2. 기본 브라우저에서 micarrsim 시작 화면이 열립니다. +3. 사용을 마치면 함께 열린 콘솔에서 `Ctrl+C`를 눌러 로컬 서버를 종료합니다. + +브라우저가 자동으로 열리지 않으면 을 직접 여세요. HTML module과 +Worker를 사용하므로 `index.html` 파일을 탐색기에서 직접 열어서는 안 됩니다. + +다른 포트를 쓰거나 브라우저를 자동으로 열지 않으려면 명령 프롬프트에서 실행합니다. + +```bat +start.cmd -Port 4174 -NoBrowser ``` -원본 DCASE/MIMII 데이터와 생성된 대용량 결과는 저장소에 커밋하지 않습니다. +PowerShell에서 서버를 직접 실행할 수도 있습니다. + +```powershell +powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\scripts\serve.ps1 -Port 4174 -NoBrowser +``` + +이 명령의 `ExecutionPolicy Bypass`는 실행 중인 PowerShell 프로세스에만 적용되며 시스템 정책을 +영구 변경하지 않습니다. 해당 명령을 사용했다면 을 여세요. + +실행 후 브라우저용 빠른 시작과 문제 해결은 에서도 볼 수 있습니다. + +### 30초 기본 예제 + +1. 시작 화면에서 **Acoustic Camera**를 엽니다. +2. 기본 192채널 rectangular 배열, single-tone scene과 50×50 experiment가 준비될 때까지 + 기다립니다. +3. **기본 예제 실행**을 누릅니다. +4. 계산이 끝나면 overlay와 평가 지표를 확인하고 **Result JSON**을 누릅니다. + +실제 완료 시간은 브라우저와 장치에 따라 다릅니다. CPU fallback에서는 WebGPU보다 오래 걸릴 수 +있습니다. + +## 내 장면으로 전체 흐름 실행하기 + +### 1. Dataset Builder에서 Scene 만들기 + +1. 시작 화면에서 **Dataset Builder**를 엽니다. +2. 선택적으로 배경 이미지와 오디오를 추가합니다. +3. 음원을 추가해 canvas 또는 x/y/z 입력으로 위치를 정하고, tone·noise·audio asset과 ambient를 + 설정합니다. +4. 상태가 **저장 가능한 장면**이면 **Scene JSON 저장**을 누릅니다. + +**Preview WAV 저장**은 source와 ambient를 공간 정보 없이 하나의 mono channel로 합친 청취 +확인용 파일입니다. 192채널 마이크 신호나 beamforming 결과가 아닙니다. + +### 2. Acoustic Camera에서 실행하기 + +1. 홈으로 돌아가 **Acoustic Camera**를 엽니다. +2. 다음 순서로 입력합니다: **Scene → Array → Experiment → Assets**. +3. **Scene**에서 Builder가 저장한 `.scene.json`을 선택합니다. +4. 기본 배열을 사용하지 않을 때만 **Array**에서 `.array.json`을 선택합니다. +5. 별도 실험 문서를 사용할 때만 **Experiment**에서 `.experiment.json`을 선택합니다. +6. Scene에 이미지나 audio asset이 있다면 **Assets**에서 Builder에 사용했던 원본 파일을 다시 + 선택합니다. 여러 파일을 한 번에 선택할 수 있습니다. +7. 입력 검증이 통과하면 backend와 scan grid를 정하고 실행합니다. +8. 완료 후 **Result JSON**을 저장합니다. + +Array와 Scene을 먼저 불러와야 Experiment의 `arrayId`와 `sceneId` 관계를 안전하게 검증할 수 +있습니다. Scene을 다시 불러오면 이전 asset 연결은 해제됩니다. + +## Scene JSON과 로컬 assets + +Scene JSON에는 원본 이미지나 오디오 byte가 들어가지 않습니다. 대신 asset id, 종류, 파일명과 +SHA-256 manifest를 저장합니다. 브라우저는 새 페이지나 세션에서 로컬 파일 접근 권한을 유지할 수 +없으므로 원본 파일을 다시 선택해야 합니다. + +- Camera는 SHA-256으로 선택한 파일을 manifest에 다시 연결합니다. +- 같은 이름이라도 내용이 달라 hash가 일치하지 않으면 거절합니다. +- 로컬 asset 한 파일은 최대 128 MiB, 연결된 파일 합계는 최대 512 MiB입니다. +- 실제 decode되어 메모리에 유지되는 오디오 합계는 최대 192 MiB입니다. +- Scene JSON에는 원본 파일명이 기록될 수 있으므로 파일을 외부에 공유하기 전에 확인하세요. + +## Backend와 scan grid + +- **Auto (GPU→CPU)**: 권장값입니다. WebGPU adapter나 device를 사용할 수 없거나 지원되는 장치 + 한계를 넘으면 CPU로 계속합니다. +- **WebGPU 강제**: GPU 경로만 사용합니다. 장치를 열 수 없거나 한계를 넘으면 fallback하지 않고 + 오류를 표시합니다. +- **CPU 강제**: 비교와 호환성 확인을 위해 CPU DAS만 사용합니다. + +WebGPU 경로도 신호 합성과 STFT 일부는 CPU에서 수행합니다. grid의 point 수는 50×50을 1로 보면 +100×100은 4배, 200×200은 16배입니다. 처음에는 50×50을 사용하고 해상도가 필요할 때만 높이세요. +실행 시간은 microphone 수, scene 길이, FFT, 대역 폭과 장치에 함께 좌우됩니다. + +## Result JSON + +Result JSON은 계약 버전 `1.0.0`의 `micarrsim.result` 문서입니다. 주요 내용은 다음과 같습니다. + +- 실행한 `experimentId`, `arrayId`, `sceneId`, 실제 `backend`와 생성 시각 +- Array·Scene·Experiment의 canonical SHA-256인 `inputHash` +- 정규화 전 peak, 적용 scale과 clipping 수를 담은 `signalStats` +- 전체·신호 생성·beamforming 시간인 `timingMs` +- 주파수 대역별 선형 power `rawMap`, 표시용 `displayMap`, 검출 `peaks` +- 위치 오차, precision/recall, truth 반경 내 energy, peak-to-sidelobe, main-lobe 폭을 계산하기 + 위한 평가 파라미터와 `metrics` + +평가에서 tone source는 tone 주파수가 포함되는 band의 ground truth로만 사용합니다. Audio asset과 +noise source는 스펙트럼을 미리 알 수 없으므로 모든 band의 ground truth로 취급합니다. 이 가정은 +결과를 실제 데이터와 해석할 때 고려해야 합니다. + +## 개인정보와 모델의 한계 + +서버는 `127.0.0.1` loopback에만 열리고 외부 서비스로 파일을 업로드하거나 telemetry를 보내지 +않습니다. 선택한 파일은 브라우저 메모리에서 읽으며 생성한 JSON/WAV는 브라우저 다운로드로만 +저장됩니다. + +현재 음향 모델은 직접 경로와 거리 감쇠를 사용하는 자유음장 모델입니다. 반사, 잔향, 회절, +차폐, 바람, 실제 MEMS 비선형과 장착 구조는 모델링하지 않습니다. Beamformer는 conventional +DAS이며 UI의 DAMAS와 CLEAN-SC는 아직 사용할 수 없습니다. 따라서 결과는 배열·grid·대역의 상대 +비교와 소프트웨어 검증용이며, 현장 성능 인증이나 안전 판단을 대신하지 않습니다. + +## 배열 예제 + +- `assets/array-geometries/rectangular-16x12.array.json`: 0.45 m 정사각 aperture의 16×12 기준 배열 +- `assets/array-geometries/fermat-spiral-192.array.json`: 0.45 m 원형 aperture의 192채널 Fermat spiral + +Camera의 **비교 배열** 선택으로 두 fixture를 같은 scene과 experiment에서 연속 실행할 수 있습니다. +상세한 생성법은 [`assets/array-geometries/README.md`](assets/array-geometries/README.md)를 참고하세요. + +## 테스트와 benchmark + +서버를 실행한 뒤 다음 주소를 브라우저에서 엽니다. + +- 전체 browser test runner: +- WebGPU scaling benchmark: + +포트를 바꿨다면 URL의 포트도 같은 값으로 바꾸세요. 테스트 범위는 +[`tests/unit/README.md`](tests/unit/README.md)와 +[`tests/integration/README.md`](tests/integration/README.md), 기준 실측은 +[`docs/benchmarks/README.md`](docs/benchmarks/README.md)에 설명되어 있습니다. + +## 저장 형식과 개발 문서 + +- [`docs/data-contracts.md`](docs/data-contracts.md): Array·Scene·Experiment·Result 계약 +- [`schemas/README.md`](schemas/README.md): JSON Schema 구성 +- [`docs/execution-plan.md`](docs/execution-plan.md): 구현 단계와 기술 결정 +- [`goal.md`](goal.md): 프로젝트 목표 + +원본 DCASE/MIMII 데이터, 사용자가 선택한 assets와 생성된 대용량 결과는 저장소에 커밋하지 +않습니다. + +## 디렉터리 구조 + +```text +apps/ Dataset Builder와 Acoustic Camera UI +assets/ 192채널 배열과 synthetic Scene/Experiment fixture +benchmarks/ 현재 브라우저에서 실행하는 WebGPU scaling 측정 +docs/ 사용자 안내, 데이터 계약, 실행 계획과 기준 실측 +packages/ 음향 수치 모델, 계약 검증, UI, WebGPU kernel +schemas/ Array·Scene·Experiment·Result JSON Schema +scripts/ 무설치 로컬 서버와 fixture 생성기 +tests/ 브라우저 unit·integration·end-to-end test +start.cmd Windows 원클릭 실행 진입점 +``` diff --git a/apps/acoustic-camera/README.md b/apps/acoustic-camera/README.md index 2be0f5f..cb72d96 100644 --- a/apps/acoustic-camera/README.md +++ b/apps/acoustic-camera/README.md @@ -1,3 +1,92 @@ # Acoustic Camera -scene과 마이크 배열을 불러와 자유음장 전파, WGSL DAS 빔포밍, overlay 및 평가를 수행하는 앱입니다. +Scene과 마이크 배열로 자유음장 microphone signal을 합성하고, STFT와 conventional DAS를 통해 +주파수 대역별 acoustic map과 ground-truth 평가 지표를 만드는 브라우저 앱입니다. + +## 열기와 기본 예제 + +저장소의 `start.cmd`를 실행한 뒤 시작 화면에서 **Acoustic Camera**를 선택하거나 +을 엽니다. `index.html`을 직접 열지 마세요. + +가장 빠른 확인 방법은 다음과 같습니다. + +1. 기본 fixture가 준비될 때까지 기다립니다. +2. 192채널 rectangular 배열, single-tone scene과 50×50 grid를 그대로 둡니다. +3. **기본 예제 실행**을 누릅니다. +4. 완료 후 overlay, 위치 오차와 timing을 확인하고 **Result JSON**을 저장합니다. + +## 사용자 입력 불러오기 + +문서는 다음 순서로 불러오는 것이 안전합니다. + +1. **Scene**: `.scene.json` +2. **Array**: 선택 사항인 `.array.json`; 또는 **비교 배열** preset 사용 +3. **Experiment**: 선택 사항인 `.experiment.json` +4. **Assets**: Scene이 참조하는 원본 이미지와 오디오 + +Scene을 불러오면 현재 Experiment의 `sceneId`와 scan plane을 Scene에 맞추고 기존 asset 연결을 +해제합니다. Array를 불러오면 현재 Experiment의 `arrayId`를 맞춥니다. 별도 Experiment는 현재 +Scene과 Array를 참조해야 하므로 마지막에 불러오세요. + +Scene JSON에는 원본 파일 byte가 없습니다. **Assets**에서 Builder에 사용한 파일을 다시 선택하면 +Camera가 SHA-256을 계산해 manifest에 연결합니다. 같은 이름이라도 내용이 바뀌면 거절됩니다. +브라우저를 다시 열거나 Scene을 바꿀 때도 재선택해야 합니다. + +- asset 한 파일: 최대 128 MiB +- 연결된 asset 합계: 최대 512 MiB +- decode되어 메모리에 유지되는 오디오 합계: 최대 192 MiB + +파일은 외부로 업로드되지 않고 브라우저 메모리에서만 처리됩니다. + +## Backend 선택 + +- **Auto (GPU→CPU)**: 권장. WebGPU를 먼저 시도하고 adapter/device 부재나 지원되는 장치 한계가 + 있으면 CPU로 계속합니다. +- **WebGPU 강제**: GPU만 사용하며 운용 오류가 나도 CPU로 fallback하지 않습니다. +- **CPU 강제**: CPU 수치 기준이나 WebGPU가 없는 환경에서 사용합니다. + +WebGPU가 감지돼도 실제 장치는 실행 시 확인합니다. 계산 중 **계산 취소**를 누르면 Worker를 +종료하며, 이미 완료된 이전 결과가 있으면 그대로 남습니다. + +## Grid, STFT와 대역 + +50×50은 2,500 points입니다. 이를 1로 보면 100×100은 4배, 200×200은 16배 points를 계산합니다. +처음에는 50×50을 권장합니다. 실행량은 microphone 수, scene 길이, FFT/hop과 선택 band의 bin +수에도 비례하므로 실제 시간 배율은 장치마다 다릅니다. + +각 band는 id, 최소/최대 Hz와 overlay 색을 가집니다. Nyquist를 넘거나 겹치는 id 등 계약 오류는 +실행 전에 표시됩니다. 현재 후처리는 raw DAS만 지원하며 DAMAS와 CLEAN-SC 항목은 비활성화되어 +있습니다. + +## 화면과 배열 비교 + +- acoustic overlay의 투명도와 표시할 band를 선택할 수 있습니다. +- **Ground truth**를 켜면 source 위치와 검출 peak를 비교할 수 있습니다. +- rectangular와 Fermat spiral preset을 같은 Scene/Experiment에서 연속 실행하면 **배열 비교 기록**에 + backend, timing과 평가 요약이 남습니다. +- 입력을 바꾼 뒤 화면에 남은 결과는 이전 실행 snapshot이며 UI가 이전 설정 결과임을 표시합니다. + +## Result JSON + +**Result JSON**은 검증을 통과한 `micarrsim.result` 문서만 저장합니다. + +- `experimentId`, `arrayId`, `sceneId`, `createdAt`, 실제 `backend` +- 실행 입력 전체의 canonical SHA-256인 `inputHash` +- `signalStats`: 정규화 전 peak, 적용 scale, clipped sample 수 +- `timingMs`: 전체, signal generation과 beamforming 시간 +- band별 `rawMap` 선형 power, `displayMap`, 검출 `peaks` +- 평가 설정과 `peakToSidelobeDb`, `energyInsideTruthRatio`, `precision`, `recall`, + `mainLobeWidthM` + +Tone source는 그 주파수가 band 범위 안에 있을 때만 해당 band의 ground truth입니다. Audio asset과 +noise source는 스펙트럼을 사전에 판별하지 않으므로 모든 band의 ground truth로 평가합니다. Source +z가 scan z와 다르면 평면에 투영한 x/y를 사용하므로 UI 경고와 위치 오차를 함께 해석하세요. + +## 해석 한계 + +신호 모델은 직접 경로와 거리 감쇠를 사용하는 자유음장입니다. 반사, 잔향, 회절, 차폐, 바람, +센서 비선형과 장착 구조는 포함하지 않습니다. 배열 JSON의 sensitivity/phase calibration은 적용하지만 +결과는 상대 배열 비교와 알고리즘 검증용이며 실측 인증을 대신하지 않습니다. + +WebGPU 성능을 별도로 측정하려면 서버 실행 후 +을 여세요. diff --git a/apps/acoustic-camera/index.html b/apps/acoustic-camera/index.html index 161eb67..f6dad7e 100644 --- a/apps/acoustic-camera/index.html +++ b/apps/acoustic-camera/index.html @@ -31,11 +31,14 @@

음향 장면

장면을 불러오고 있습니다.

-
- - - - +
+
+ + + + +
+

권장 순서 · Scene → Array → Experiment → Assets

diff --git a/apps/acoustic-camera/styles.css b/apps/acoustic-camera/styles.css index f4a8241..c84f3e6 100644 --- a/apps/acoustic-camera/styles.css +++ b/apps/acoustic-camera/styles.css @@ -17,7 +17,9 @@ .workspace-heading .eyebrow, .section-heading .eyebrow, .array-heading .eyebrow { margin-bottom: 7px; } .workspace-heading h1 { margin-bottom: 4px; font-size: clamp(1.9rem, 4vw, 3.2rem); letter-spacing: -.055em; } .workspace-meta, .section-copy { margin: 0; color: var(--muted); font-size: .76rem; line-height: 1.55; } +.workspace-import { display: grid; gap: 7px; justify-items: end; } .workspace-tools { justify-content: flex-end; } +.import-order { margin: 0; color: var(--muted); font-size: .64rem; } .scene-canvas-card { position: relative; overflow: hidden; min-height: 520px; border: 1px solid var(--line); border-radius: 22px; background: #07110f; box-shadow: var(--shadow); } #scene-canvas { display: block; width: 100%; height: clamp(520px, 64vh, 760px); } .canvas-message { position: absolute; top: 32px; left: 50%; display: grid; width: min(440px, calc(100% - 48px)); gap: 6px; color: var(--muted); text-align: center; pointer-events: none; transform: translateX(-50%); } @@ -109,6 +111,7 @@ .camera-header, .workspace-heading { align-items: stretch; } .camera-header { position: static; display: flex; flex-direction: column; } .header-status, .header-actions, .workspace-tools { justify-content: flex-start; width: 100%; } + .workspace-import { justify-items: start; } .header-actions .button, .workspace-tools .button { flex: 1 1 calc(50% - 5px); } .scene-workspace { padding: 18px; } .workspace-heading { flex-direction: column; } diff --git a/apps/dataset-builder/README.md b/apps/dataset-builder/README.md index 31db6fb..c0f7b6c 100644 --- a/apps/dataset-builder/README.md +++ b/apps/dataset-builder/README.md @@ -1,3 +1,65 @@ # Dataset Builder -음원, ambient audio, 이미지상의 위치를 조합해 scene audio와 ground truth manifest를 만드는 앱입니다. +이미지 평면에 source ground truth를 배치하고 tone, noise, 로컬 audio asset과 ambient를 조합해 +재현 가능한 `micarrsim.scene` JSON을 만드는 브라우저 앱입니다. + +## 열기 + +저장소의 `start.cmd`를 실행한 뒤 시작 화면에서 **Dataset Builder**를 선택하거나 +을 엽니다. `index.html`을 직접 열면 module과 Worker가 +동작하지 않습니다. + +## 기본 사용법 + +1. **배경 이미지**를 선택하거나 이미지 없이 grid를 사용합니다. +2. **오디오 추가**로 source 또는 ambient에 사용할 파일을 연결합니다. +3. **+ 음원 추가**를 누르고 카드를 선택한 뒤 canvas를 클릭하거나 x/y/z를 입력합니다. +4. source 종류를 설정합니다. + - `Tone`: 주파수와 위상을 갖는 합성 정현파 + - `Noise`: seed로 재현되는 white 또는 pink noise + - `Audio asset`: 선택한 파일의 channel을 사용하며 짧은 파일은 반복 가능 +5. 필요하면 ambient 종류와 gain을 설정합니다. +6. 검증 카드가 **저장 가능한 장면**이면 **Scene JSON 저장**을 누릅니다. + +길이, sample rate, 음속, seed와 mix 정규화도 Scene JSON에 저장됩니다. 이미지가 있으면 표시 +평면의 x/y 범위, z와 image pixel 크기가 ground truth 좌표 변환에 사용됩니다. + +## 미리 듣기와 Preview WAV + +**미리 듣기**는 현재 mix를 브라우저에서 재생합니다. **Preview WAV 저장**은 모든 source와 +ambient를 공간 정보 없이 합친 mono PCM16 확인용 파일입니다. 마이크별 전파 지연이 적용된 +192채널 녹음이나 acoustic-camera 결과가 아닙니다. + +클리핑이 예상되면 저장 전에 경고합니다. 출력이 너무 크면 scene 길이 또는 sample rate를 줄이거나 +source gain과 mix 정규화를 조절하세요. + +## Scene JSON과 assets + +Scene JSON은 오디오·이미지 원본을 포함하지 않고 asset manifest만 기록합니다. 각 descriptor에는 +id, 종류, 파일명과 SHA-256이 들어갑니다. + +- 파일 한 개는 최대 128 MiB, 연결 파일 합계는 최대 512 MiB입니다. +- decode된 오디오의 합계는 최대 192 MiB입니다. +- Scene을 다시 불러오면 브라우저 권한이 없으므로 원본 이미지와 오디오를 다시 선택해야 합니다. +- 같은 이름의 파일도 SHA-256이 manifest와 다르면 기존 asset으로 연결되지 않습니다. +- 사용 중인 asset은 먼저 source, ambient 또는 image 참조를 바꾼 뒤 제거할 수 있습니다. + +원본 파일은 외부로 업로드되지 않고 브라우저 메모리에서만 처리됩니다. 단, JSON에는 파일명이 +남을 수 있으므로 공유 전에 확인하세요. + +## Acoustic Camera로 넘기기 + +1. Scene JSON과 사용한 원본 assets를 함께 보관합니다. +2. Acoustic Camera에서 **Scene**으로 JSON을 선택합니다. +3. 사용자 문서가 있다면 **Array**, **Experiment** 순서로 선택합니다. +4. **Assets**에서 원본 이미지와 오디오를 다시 선택해 SHA-256 연결 상태를 확인합니다. +5. 검증이 통과하면 시뮬레이션을 실행합니다. + +안전한 전체 import 순서는 **Scene → Array → Experiment → Assets**입니다. 자세한 계산과 Result +설명은 [`../acoustic-camera/README.md`](../acoustic-camera/README.md)를 참고하세요. + +## 모델 범위 + +Builder의 mix는 재현 가능한 source recipe와 위치 ground truth를 만드는 단계입니다. 자유음장 전파, +마이크 calibration과 DAS는 Acoustic Camera에서 수행합니다. 실제 방의 반사·잔향·차폐를 Builder가 +추정하지 않습니다. diff --git a/assets/array-geometries/README.md b/assets/array-geometries/README.md index 05fb812..06a6900 100644 --- a/assets/array-geometries/README.md +++ b/assets/array-geometries/README.md @@ -1,3 +1,40 @@ # Array Geometries -재현 가능한 마이크 배열 JSON 예제를 둡니다. +Camera에서 즉시 사용하고 테스트에서 재현할 수 있는 계약 버전 `1.0.0`의 192채널 Array JSON +fixture입니다. 좌표 단위는 m이고 모든 microphone은 배열 평면 `z = 0`에 있습니다. `+x`는 오른쪽, +`+y`는 위, `+z`는 scene 방향입니다. + +## 포함된 배열 + +### `rectangular-16x12.array.json` + +- 16 columns × 12 rows = 192 microphones +- 폭과 높이가 각각 0.45 m인 정사각 aperture +- x/y 양 끝을 포함해 균일한 간격으로 배치 +- 기본 single-tone experiment와 CPU/WebGPU 기준 fixture가 참조하는 baseline + +### `fermat-spiral-192.array.json` + +- 192 microphones +- 반지름 0.225 m, 지름 0.45 m인 원형 aperture +- golden angle과 면적 균등 반지름으로 만든 Fermat spiral +- rectangular baseline과 sidelobe·집중도·위치 오차를 비교하기 위한 preset + +Acoustic Camera의 **비교 배열**에서 두 배열을 선택해 같은 Scene/Experiment 결과를 세션 기록에 +나란히 추가할 수 있습니다. + +## fixture 다시 생성하기 + +저장소 최상위에서 다음 명령을 실행하면 두 JSON을 결정론적으로 다시 생성합니다. + +```powershell +powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\scripts\generate-array-fixture.ps1 +``` + +이 스크립트는 기존 `rectangular-16x12.array.json`과 `fermat-spiral-192.array.json`을 덮어씁니다. +`ExecutionPolicy Bypass`는 이 PowerShell 프로세스에만 적용됩니다. 생성 뒤 +에서 schema와 192채널 배치 검증을 실행하세요. + +사용자 배열은 [`../../schemas/array.schema.json`](../../schemas/array.schema.json) 구조를 따르고 +고유한 `id`, aperture와 microphone별 `[x, y, z]`를 가져야 합니다. 선택적인 `sensitivityDb`와 +`phaseOffsetDeg`는 합성 channel 편차와 beamforming 역 calibration에 사용됩니다. diff --git a/assets/sample-scenes/README.md b/assets/sample-scenes/README.md index 06ebcbb..82fcde9 100644 --- a/assets/sample-scenes/README.md +++ b/assets/sample-scenes/README.md @@ -1,3 +1,12 @@ # Sample Scenes -재배포 가능한 최소 synthetic scene과 fixture를 둡니다. +재배포 가능한 최소 synthetic fixture입니다. 외부 asset이 없어 서버를 시작한 직후 Camera에서 +바로 실행할 수 있습니다. + +- `single-tone.scene.json`: 48 kHz, 0.25 s, 1 kHz tone 하나를 z=2 m 평면에 배치한 Scene +- `single-tone.experiment.json`: rectangular 192채널 배열, 900–1100 Hz band, 50×50 scan, + FFT 512 / hop 256, Auto backend를 쓰는 Experiment + +두 문서는 계약 버전 `1.0.0`이며 기본 Camera 실행, CPU golden test, CPU/WebGPU 비교와 benchmark가 +같은 조건을 공유하도록 고정되어 있습니다. fixture를 바꿀 때는 의 +전체 테스트와 benchmark 입력 의미를 함께 확인하세요. diff --git a/docs/benchmarks/README.md b/docs/benchmarks/README.md index 003193f..83605e5 100644 --- a/docs/benchmarks/README.md +++ b/docs/benchmarks/README.md @@ -18,6 +18,7 @@ distance 생성, CPU STFT가 포함된다. 200×200은 64 MiB steering buffer 쓰지 않는다. 같은 환경의 자동 검증에서 192채널·50×50 GPU raw map은 CPU raw map 대비 peak-normalized RMS -오차 `3.008e-5`를 기록했고, 모든 37개 테스트를 통과했다. 상세 원자료는 -`webgpu-intel-gen9-2026-08-28.json`에 있다. 사용자는 `/benchmarks/webgpu-scaling.html`에서 자신의 -장치 결과를 다시 측정해 JSON으로 저장할 수 있다. +오차 `3.008e-5`를 기록했고, 측정 당시 제공되던 37개 테스트를 모두 통과했다. 이후 end-to-end와 +서버 보안 테스트가 추가되었으므로 현재 전체 suite 상태는 `/tests/`에서 별도로 확인한다. 상세 +원자료는 `webgpu-intel-gen9-2026-08-28.json`에 있다. 사용자는 +`/benchmarks/webgpu-scaling.html`에서 자신의 장치 결과를 다시 측정해 JSON으로 저장할 수 있다. diff --git a/docs/data-contracts.md b/docs/data-contracts.md index 6fc7142..f79abb5 100644 --- a/docs/data-contracts.md +++ b/docs/data-contracts.md @@ -11,14 +11,27 @@ micarrsim의 저장 형식은 JSON Schema Draft 2020-12와 추가 관계 검증 - `micarrsim.experiment`: scan plane/grid, 주파수 대역, STFT와 DAS 설정 - `micarrsim.result`: raw/display acoustic map, 복수 peak와 source 매칭, 평가·성능 정보 -scene JSON에는 192채널 원시 sample을 넣지 않는다. 합성 tone/noise는 seed가 포함된 recipe로 -재생성하고, 녹음 파일과 이미지는 manifest 기준의 안전한 상대 경로로 참조한다. 사용자 컴퓨터의 -절대 경로, URL, `..` 경로는 저장하지 않는다. +scene JSON에는 192채널 원시 sample이나 원본 asset byte를 넣지 않는다. 합성 tone/noise는 seed가 +포함된 recipe로 재생성하고, 녹음 파일과 이미지는 `id`, `kind`, `fileName`, `mimeType`, +`sha256` manifest로 참조한다. 사용자 컴퓨터의 절대 경로, URL, `..` 경로는 저장하지 않는다. array의 `sensitivityDb`와 `phaseOffsetDeg`는 각 마이크 수신 channel에 적용되는 편차다. 합성기는 이를 신호에 주입하고, beamformer는 알려진 calibration 값의 역 gain/phase를 적용한다. 따라서 두 값이 0이 아닌 배열도 오류 없이 조용히 무시되지 않는다. +## 로컬 asset 재연결 + +브라우저 보안상 Scene JSON을 새 페이지에서 열었을 때 이전 로컬 파일 권한은 복원되지 않는다. +Camera는 사용자가 다시 선택한 파일을 SHA-256으로 계산해 같은 descriptor에만 연결한다. 이름이 +같아도 byte가 다르면 거절하고, 원본은 브라우저 메모리에서만 decode하며 서버에 업로드하지 않는다. + +- 파일 하나: 최대 128 MiB +- 한 Scene에 연결된 원본 파일 합계: 최대 512 MiB +- 동시에 유지하는 decode 오디오 channel 합계: 최대 192 MiB + +Builder가 내보낸 Scene JSON과 사용한 원본 이미지·오디오는 함께 보관해야 한다. Preview WAV는 +scene recipe를 mono로 합친 청취 확인용 파일이며 마이크 배열의 192채널 수신 신호가 아니다. + ## 오디오와 map 정규화 `scene.mix.normalization`은 마이크 신호 합성 전에 적용할 오디오 mix 정책이다. `none`은 원래 @@ -32,6 +45,25 @@ raw DAS map을 덮어쓰지 않는다. map은 row-major 배열이다. 첫 행은 `yMax`, 마지막 행은 `yMin`, 각 행은 `xMin`에서 `xMax` 방향이다. `values.length`는 반드시 `rows × columns`와 같아야 한다. +## Result 평가 정보 + +각 band의 `peaks`는 위치와 power 값, 선택적 `sourceId`, `localizationErrorM`, +`localizationErrorPixels`를 저장한다. `metrics`에는 다음 유한값만 기록한다. + +- `peakToSidelobeDb` +- `energyInsideTruthRatio` +- `precision`, `recall` +- `mainLobeWidthM` + +`evaluation`은 지표를 다시 해석할 수 있도록 truth 반경, source 매칭 tolerance, peak 검출 +threshold와 최소 거리, sidelobe 제외 반경, half-power 비율, dynamic range, 최대 검출 수를 +버전과 함께 저장한다. `environment`에는 실제 backend, microphone/grid 수, scan bounds와 z, +가능한 경우 GPU adapter 및 fallback 정보를 기록한다. + +대역별 ground truth는 tone의 주파수가 band 안에 있을 때 그 tone source를 포함한다. noise와 +asset source는 현재 모든 band의 truth로 간주한다. 즉 녹음 asset의 실제 spectrum energy를 +대역별로 판정하는 계약은 아직 없으며, 이 가정은 실제 데이터 결과를 해석할 때 고려해야 한다. + ## 재현성 hash `result.inputHash`는 실행에 사용한 `{ array, scene, experiment }`를 객체 key 사전순으로 재귀 @@ -48,6 +80,7 @@ SHA-256을 계산해 scene의 해당 asset `sha256` 필드를 채운 상태로 c - asset 종류와 안전한 상대 경로 - Nyquist 및 시간·scan 범위 - map shape와 source peak 매칭 +- band별 evaluation 파라미터와 허용 metric - 현재 비스트리밍 CPU backend의 512 MiB channel-buffer 예산 지원하지 않는 schemaVersion은 묵시적으로 변환하지 않고 오류로 알린다. diff --git a/docs/execution-plan.md b/docs/execution-plan.md index 5f05f12..7b796aa 100644 --- a/docs/execution-plan.md +++ b/docs/execution-plan.md @@ -1,5 +1,10 @@ # micarrsim 실행 계획 +> 구현 상태 · 2026-08-28 +> +> 단계 0–4는 완료되어 설치 없는 브라우저 앱, CPU/WebGPU DAS, Builder→Camera asset 재연결, +> 배열 비교, 평가·Result 내보내기와 53개 자동 테스트로 검증했다. 단계 5는 향후 고도화 범위다. + ## 1. 목표와 성공 기준 이 프로젝트는 정방형 평면에 배치할 192개 MEMS 마이크의 배열을 비교하기 위한 재현 가능한 @@ -8,7 +13,7 @@ 1. 기계음 데이터와 ambient audio를 조합해 장면 및 ground truth를 생성한다. 2. 배열 JSON과 장면 정보를 사용해 각 마이크에 도달하는 신호를 합성한다. -3. WGSL로 conventional delay-and-sum(DAS/DAC) 빔포밍을 수행한다. +3. WGSL로 conventional delay-and-sum(DAS) 빔포밍을 수행한다. 4. 주파수 구간별 acoustic image를 ground truth 위에 표시하고 정량 평가한다. 5. 배열, 거리, 음원, 주파수, grid 해상도를 바꾼 실험을 저장하고 비교한다. @@ -18,10 +23,12 @@ CPU 기준 구현과 GPU 결과의 수치 오차가 합의된 허용 범위 안 ## 2. 기술 접근 결정 -TypeScript 기반의 브라우저 앱 2개와 WebGPU/WGSL을 권장한다. 브라우저는 배열·장면 편집, -오디오 미리 듣기, 이미지 overlay에 적합하고 WebGPU는 192 × scan point × frequency bin 형태의 -병렬 연산에 잘 맞는다. WGSL 커널을 독립 패키지로 두면 이후 GLSL 또는 네이티브 GPU 구현으로 -옮길 때 데이터 배치와 수식을 재사용하기 쉽다. +최종 구현은 빌드 단계와 패키지 설치가 없는 브라우저 네이티브 ES module 2개와 WebGPU/WGSL을 +선택했다. 초기 계획의 TypeScript 권고보다 Windows 사용자가 `start.cmd` 하나로 바로 실행하는 +목표를 우선한 결정이다. JSON Schema와 브라우저 자동 테스트가 런타임 계약을 보완한다. +브라우저는 배열·장면 편집, 오디오 미리 듣기, 이미지 overlay에 적합하고 WebGPU는 +192 × scan point × frequency bin 형태의 병렬 연산에 잘 맞는다. WGSL 커널은 독립 package로 +두어 데이터 배치와 수식을 CPU 기준 구현과 대조할 수 있게 했다. 데이터셋 전체를 브라우저에 적재하지 않는다. 사용자가 로컬 파일을 선택해 장면을 만들고, 결과를 manifest와 오디오 파일로 내보내는 방식을 우선 구현한다. 대규모 batch 전처리가 필요해질 @@ -105,7 +112,7 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로 ## 8. 단계별 구현 -### 단계 0 — 기반과 계약 +### 단계 0 — 기반과 계약 · 완료 - workspace, formatter, linter, unit test, WebGPU feature detection 구성 - 네 가지 JSON Schema와 최소 예제 작성 @@ -114,7 +121,7 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로 완료 조건: 예제 JSON이 schema validation을 통과하고 두 앱이 동일 manifest를 읽는다. -### 단계 1 — CPU 기준 모델 +### 단계 1 — CPU 기준 모델 · 완료 - 자유음장 구면 전파, 분수 지연, STFT, steering, DAS의 CPU reference 구현 - 단일 음원의 지연·감쇠·peak 위치를 analytic result와 비교 @@ -122,15 +129,15 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로 완료 조건: 자동화된 golden test가 통과하고 sign/좌표 convention이 문서화된다. -### 단계 2 — Dataset Builder +### 단계 2 — Dataset Builder · 완료 - 이미지와 음원 파일 불러오기, 위치·시간·gain 편집, ambient mix와 미리 듣기 -- deterministic seed, clipping 경고, manifest/audio 내보내기 +- deterministic seed, clipping 경고, Scene JSON과 mono Preview WAV 내보내기 - 데이터셋 adapter 인터페이스와 synthetic adapter 구현 완료 조건: 만든 scene을 다시 열었을 때 음원, 위치, 파형, 정답이 동일하다. -### 단계 3 — WebGPU DAS +### 단계 3 — WebGPU DAS · 완료 - CPU와 동일한 buffer layout 및 WGSL kernel 구현 - frequency band별 map 생성, device limit 대응, CPU fallback @@ -138,7 +145,7 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로 완료 조건: CPU/GPU map 오차 기준을 통과하고 192채널 50×50 실행 timing이 기록된다. -### 단계 4 — Acoustic Camera UI와 평가 +### 단계 4 — Acoustic Camera UI와 평가 · 완료 - scene/ground truth/acoustic overlay, 배열 geometry, parameter panel 구현 - 결과 metric, 실행 환경, 입력 hash와 map 내보내기 @@ -146,7 +153,7 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로 완료 조건: 사용자가 코드 수정 없이 배열 JSON 두 개를 같은 장면에서 비교할 수 있다. -### 단계 5 — 실제 데이터와 고도화 +### 단계 5 — 실제 데이터와 고도화 · 향후 - 라이선스 검토를 통과한 DCASE/MIMII adapter와 batch workflow 추가 - microphone calibration·noise 모델 검토 @@ -164,15 +171,18 @@ flattening이 모델 타당성을 훼손하는지 청취 및 spectrum 통계로 - 실제 AC와 simulation gap: 감도, 위상, 위치 오차를 이후 calibration/noise model로 주입 가능하게 한다. - post-processing이 기본 결과를 가림: raw DAS map과 후처리 map을 항상 별도로 보존한다. -## 10. 첫 구현 백로그 +## 10. 구현 결과와 다음 백로그 -1. TypeScript workspace와 두 앱 shell을 생성한다. -2. array/scene/experiment/result schema와 fixture를 만든다. -3. 좌표 변환, 거리, 구면 감쇠, 주파수 phase delay 단위 테스트를 작성한다. -4. CPU DAS golden implementation과 synthetic scene을 만든다. -5. WGSL kernel 및 CPU/GPU 비교 harness를 만든다. -6. scene canvas, array view, parameter controls를 연결한다. -7. benchmark와 평가 지표를 결과 manifest에 기록한다. +- [x] 설치 없는 ES module workspace와 두 앱 shell +- [x] array/scene/experiment/result schema와 synthetic fixture +- [x] 좌표·거리·구면 감쇠·분수 지연·STFT 단위 테스트 +- [x] CPU DAS golden implementation +- [x] WGSL kernel, CPU/GPU 비교와 50²/100²/200² benchmark +- [x] scene canvas, array view, parameter controls와 Result export +- [x] Builder→Camera SHA-256 asset 재연결 왕복 테스트 +- [ ] DCASE/MIMII 라이선스 검토와 adapter +- [ ] 잔향·calibration·noise model +- [ ] DAMAS/CLEAN-SC 후처리 -각 단계는 정확성 test를 먼저 통과시킨 뒤 UI 또는 최적화를 진행한다. 초기 milestone에서는 잔향, -DAMAS/CLEAN-SC, 대규모 dataset batch 변환을 완료 조건으로 삼지 않는다. +정확성 test를 먼저 통과시킨 뒤 UI 또는 최적화를 진행한다는 원칙은 유지한다. 단계 5 기능은 +자유음장 DAS 결과를 바꾸지 않는 선택형 확장으로 추가한다. diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..d3cb8ad --- /dev/null +++ b/docs/index.html @@ -0,0 +1,192 @@ + + + + + + + micarrsim 사용 설명서 + + + + +
+ ← micarrsim 홈 + +
+ +
+
+

Local user guide

+

설치 없이 시작하는
음향 카메라 실험

+

+ Windows 10/11과 최신 Chrome 또는 Edge만 있으면 됩니다. 모든 입력과 계산은 이 + 컴퓨터의 브라우저 안에서 처리되며 외부 서버로 업로드되지 않습니다. +

+
+ +
+
01
+
+

30초 확인

+

기본 결과 하나 저장하기

+
    +
  1. start.cmd를 더블클릭합니다.
  2. +
  3. 홈에서 Acoustic Camera를 엽니다.
  4. +
  5. ▶ 기본 예제 실행을 누릅니다.
  6. +
  7. 완료되면 Result JSON을 눌러 다운로드 폴더에 저장합니다.
  8. +
+ +
+
+ +
+
02
+
+

5분 전체 흐름

+

내 장면을 만들고 Camera에서 실행하기

+
    +
  1. + Dataset Builder에서 이미지와 오디오를 + 선택하고 음원 위치, gain, 시간, ambient를 설정합니다. +
  2. +
  3. Scene JSON 저장을 누르고 사용한 원본 파일과 함께 보관합니다.
  4. +
  5. Scene 저장 후 Camera 열기를 누릅니다.
  6. +
  7. + Camera에서 파일을 Scene → Array → Experiment → Assets 순서로 + 가져옵니다. 내 Array/Experiment가 없다면 내장 기본값을 그대로 둡니다. +
  8. +
  9. + Assets에서 Builder에 사용한 원본 이미지와 오디오를 한 번에 다시 선택합니다. + 준비 상태가 모두 초록색인지 확인합니다. +
  10. +
  11. Auto (GPU→CPU)로 실행한 뒤 Result JSON을 저장합니다.
  12. +
+ + + +
+
128 MiB원본 파일 하나
+
512 MiB연결 파일 합계
+
192 MiBdecode된 오디오 합계
+
+
+
+ +
+
03
+
+

Compute backend

+

속도와 격자 선택

+
+ + + + + + + +
선택동작권장 상황
Auto (GPU→CPU)WebGPU를 먼저 시도하고 불가능하면 CPU 사용기본 권장
WebGPU 강제GPU 장치가 없거나 실패하면 fallback 없이 오류GPU 비교·측정
CPU 강제동일한 자유음장 DAS를 CPU에서 계산호환성 확인·기준 비교
+
+

+ 먼저 50×50을 권장합니다. 100×100은 4배, 200×200은 16배의 scan point를 계산합니다. + 실행 중에는 취소할 수 있으며, 취소하거나 실패해도 이전 완료 결과는 유지됩니다. +

+
+
+ +
+
04
+
+

Result JSON

+

무엇이 저장되나요?

+
+
rawMap / displayMap
선형 power 원본과 화면 표시용 acoustic map
+
peaks
검출 위치, source 매칭, m·pixel 위치 오차
+
metrics
energy 비율, precision/recall, sidelobe, main-lobe 폭
+
evaluation
검출 threshold, 반경, tolerance 등 지표 계산 조건
+
timing / environment
backend, grid·scan 범위, 장치 정보와 구간별 시간
+
inputHash
Array·Scene·Experiment 입력을 묶은 SHA-256 재현성 식별자
+
+ +
+
+ +
+
05
+
+

Troubleshooting

+

막혔을 때 확인할 것

+
+ 화면이 비거나 module/Worker 오류가 납니다. +

index.html을 직접 열지 말고 반드시 start.cmd로 실행하세요.

+
+
+ 4173 포트를 사용할 수 없다고 나옵니다. +

start.cmd -Port 4174처럼 다른 포트를 지정하고 http://127.0.0.1:4174/을 여세요.

+
+
+ Experiment를 가져올 수 없습니다. +

Scene과 Array를 먼저 가져오세요. Experiment의 sceneId와 arrayId가 현재 문서와 같아야 합니다.

+
+
+ Assets가 준비되지 않습니다. +

Builder에서 사용한 것과 바이트가 같은 원본 파일을 다시 선택하세요. 파일명뿐 아니라 SHA-256도 일치해야 합니다.

+
+
+ WebGPU가 없거나 실행이 너무 느립니다. +

Backend를 Auto 또는 CPU로 두고 50×50 grid부터 실행하세요. 최신 Chrome/Edge와 그래픽 드라이버도 확인하세요.

+
+
+
+ +
+
06
+
+

Scope & privacy

+

현재 모델의 범위

+

+ micarrsim 1.0은 점 음원, 구면 감쇠·분수 지연을 쓰는 자유음장 모델과 conventional + DAS를 제공합니다. 실내 반사·잔향, 실제 마이크 위치 오차와 비선형성, DAMAS, + CLEAN-SC, DCASE/MIMII 자동 adapter는 현재 범위에 포함되지 않습니다. 결과는 실제 + 장비의 절대 성능 보증이 아니라 같은 조건에서 배열을 비교하는 재현 가능한 기준입니다. +

+

+ 원본 asset은 브라우저 메모리에서만 읽고 서버나 외부 네트워크로 보내지 않습니다. + 다만 Scene JSON에는 원본 파일명이 남을 수 있으므로 외부 공유 전 확인하세요. +

+
+
+ +
+ 전체 README설치·실행·구조·제약 + Builder 설명서source·ambient·Preview WAV + Camera 설명서backend·결과·비교 + 데이터 계약JSON Schema와 재현성 + 실행 계획구현 상태와 향후 단계 + 브라우저 테스트단위·통합 테스트 실행 + WebGPU 측정현재 장치 benchmark +
+
+ + diff --git a/docs/styles.css b/docs/styles.css new file mode 100644 index 0000000..925d707 --- /dev/null +++ b/docs/styles.css @@ -0,0 +1,291 @@ +.guide-header { + position: sticky; + z-index: 10; + top: 0; + display: flex; + align-items: center; + justify-content: space-between; + min-height: 64px; + padding: 12px clamp(18px, 4vw, 48px); + border-bottom: 1px solid var(--line); + background: rgb(8 17 15 / 92%); + backdrop-filter: blur(18px); +} + +.guide-brand, +.guide-header nav a { + color: inherit; + font-size: 0.82rem; + font-weight: 700; + text-decoration: none; +} + +.guide-header nav { + display: flex; + flex-wrap: wrap; + gap: 18px; + color: var(--muted); +} + +.guide-shell { + width: min(980px, calc(100% - 40px)); + margin: 0 auto; + padding: clamp(48px, 8vw, 96px) 0 72px; +} + +.guide-hero { + max-width: 820px; + margin-bottom: clamp(64px, 10vw, 112px); +} + +.guide-hero h1 { + margin-bottom: 24px; + font-size: clamp(2.8rem, 8vw, 6.4rem); + letter-spacing: -0.07em; + line-height: 0.98; +} + +.guide-hero > p:last-child, +.guide-copy { + color: #c8d9d3; + line-height: 1.8; +} + +.guide-hero > p:last-child { + max-width: 720px; + font-size: clamp(1rem, 2vw, 1.2rem); +} + +.guide-section { + display: grid; + grid-template-columns: 70px minmax(0, 1fr); + gap: clamp(18px, 4vw, 42px); + padding: clamp(36px, 7vw, 68px) 0; + border-top: 1px solid var(--line); + scroll-margin-top: 72px; +} + +.section-number { + color: var(--mint); + font-size: 0.75rem; + font-weight: 760; + letter-spacing: 0.12em; +} + +.guide-section h2 { + margin-bottom: 28px; + font-size: clamp(1.8rem, 4vw, 3rem); + letter-spacing: -0.05em; +} + +.guide-steps { + display: grid; + gap: 14px; + margin: 0; + padding-left: 1.35rem; + color: #c8d9d3; + line-height: 1.75; +} + +.guide-steps li { + padding-left: 8px; +} + +.guide-steps a, +.callout a { + color: var(--mint); +} + +code, +kbd { + border: 1px solid var(--line); + border-radius: 6px; + color: var(--text); + background: rgb(255 255 255 / 5%); + font-family: "Cascadia Code", Consolas, monospace; +} + +code { + padding: 0.12em 0.4em; +} + +kbd { + padding: 2px 6px; + box-shadow: 0 2px 0 var(--line); + font-size: 0.78rem; +} + +.callout { + margin-top: 26px; + padding: 18px 20px; + border-left: 3px solid var(--cyan); + border-radius: 0 12px 12px 0; + color: var(--muted); + background: rgb(88 199 232 / 7%); + line-height: 1.7; +} + +.callout--important { + border-color: var(--amber); + background: rgb(255 197 110 / 7%); +} + +.callout strong { + color: var(--text); +} + +.limit-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; + margin-top: 14px; +} + +.limit-grid div { + padding: 18px; + border: 1px solid var(--line); + border-radius: 12px; + background: rgb(255 255 255 / 2%); +} + +.limit-grid strong, +.limit-grid span { + display: block; +} + +.limit-grid strong { + color: var(--mint); + font-size: 1.2rem; +} + +.limit-grid span { + margin-top: 5px; + color: var(--muted); + font-size: 0.78rem; +} + +.guide-table-wrap { + overflow-x: auto; + border: 1px solid var(--line); + border-radius: 14px; +} + +.guide-table-wrap table { + width: 100%; + border-collapse: collapse; + color: #c8d9d3; + font-size: 0.88rem; +} + +.guide-table-wrap th, +.guide-table-wrap td { + padding: 14px 16px; + border-bottom: 1px solid var(--line); + text-align: left; + vertical-align: top; +} + +.guide-table-wrap th { + color: var(--text); + background: rgb(255 255 255 / 4%); +} + +.guide-table-wrap tr:last-child td { + border-bottom: 0; +} + +.result-list { + display: grid; + gap: 10px; + margin: 0; +} + +.result-list div { + display: grid; + grid-template-columns: minmax(150px, 0.4fr) minmax(0, 1fr); + gap: 18px; + padding: 14px 0; + border-bottom: 1px solid var(--line); +} + +.result-list dt { + color: var(--mint); + font-family: "Cascadia Code", Consolas, monospace; + font-size: 0.86rem; +} + +.result-list dd { + margin: 0; + color: var(--muted); + line-height: 1.6; +} + +details { + padding: 16px 0; + border-bottom: 1px solid var(--line); +} + +summary { + cursor: pointer; + font-weight: 700; +} + +details p { + margin: 12px 0 0; + color: var(--muted); + line-height: 1.7; +} + +.reference-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + padding-top: 48px; + border-top: 1px solid var(--line); +} + +.reference-grid a { + padding: 20px; + border: 1px solid var(--line); + border-radius: 14px; + background: var(--surface); + text-decoration: none; +} + +.reference-grid a:hover, +.reference-grid a:focus-visible { + border-color: var(--mint); + outline: none; +} + +.reference-grid strong, +.reference-grid span { + display: block; +} + +.reference-grid span { + margin-top: 8px; + color: var(--muted); + font-size: 0.78rem; + line-height: 1.5; +} + +@media (max-width: 720px) { + .guide-header nav { + display: none; + } + + .guide-section { + grid-template-columns: 1fr; + } + + .limit-grid, + .reference-grid { + grid-template-columns: 1fr; + } + + .result-list div { + grid-template-columns: 1fr; + gap: 6px; + } +} diff --git a/index.html b/index.html index 84bc302..4d17a8e 100644 --- a/index.html +++ b/index.html @@ -25,6 +25,26 @@ +
+
+
+

Local workflow

+

파일이 흐르는 방식

+
+ 왜 원본을 다시 선택하나요? +
+
    +
  1. Dataset Builder음원과 위치 만들기
  2. +
  3. Scene JSON + 원본 assets같이 보관하기
  4. +
  5. Acoustic Camera배열·DAS 실행하기
  6. +
  7. Result JSONmap·지표 저장하기
  8. +
+

+ 원본 파일은 JSON에 포함되거나 외부로 업로드되지 않습니다. Camera에서 같은 파일을 다시 + 선택하면 SHA-256으로 확인한 뒤 연결합니다. +

+
+
01 @@ -32,7 +52,7 @@

Dataset Builder

기기 이미지에 음원을 배치하고 ambient를 혼합해 재현 가능한 장면을 만듭니다.

- 장면 만들기 → + 새 Scene 만들기 →
@@ -41,14 +61,33 @@

Acoustic Camera

배열 응답을 계산하고 주파수 대역별 DAS 결과를 ground truth와 비교합니다.

- 시뮬레이션 열기 → + 기본 예제로 바로 실행 →
+
+
+

30초 확인

+

설정 없이 첫 결과 만들기

+

Acoustic Camera를 열어 ▶ 기본 예제 실행을 누르고, 완료되면 Result JSON을 저장하세요.

+ 기본 예제 열기 → +
+
+

내 데이터

+

Builder부터 끝까지

+

Scene을 저장한 뒤 Camera에서 Scene → Array → Experiment → Assets 순서로 가져오면 참조 오류를 줄일 수 있습니다.

+ 5분 안내 보기 → +
+
+ diff --git a/packages/ui/app-shell.css b/packages/ui/app-shell.css index 35dbdac..b5afdcf 100644 --- a/packages/ui/app-shell.css +++ b/packages/ui/app-shell.css @@ -114,11 +114,92 @@ p { background: rgb(255 197 110 / 8%); } +.workflow-card { + margin-top: clamp(42px, 7vw, 72px); + padding: clamp(22px, 4vw, 34px); + border: 1px solid var(--line); + border-radius: 22px; + background: rgb(255 255 255 / 2%); +} + +.workflow-card__heading { + display: flex; + align-items: end; + justify-content: space-between; + gap: 24px; +} + +.workflow-card__heading h2, +.quick-start-card h2 { + margin-bottom: 0; + letter-spacing: -0.035em; +} + +.workflow-card__heading a, +.quick-start-card a { + color: var(--mint); + font-size: 0.84rem; + font-weight: 700; + text-decoration: none; +} + +.workflow-list { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; + margin: 24px 0 16px; + padding: 0; + list-style: none; + counter-reset: workflow; +} + +.workflow-list li { + position: relative; + min-height: 104px; + padding: 18px; + border: 1px solid rgb(101 242 191 / 18%); + border-radius: 14px; + background: rgb(101 242 191 / 5%); + counter-increment: workflow; +} + +.workflow-list li::before { + display: block; + margin-bottom: 18px; + color: var(--mint); + content: "0" counter(workflow); + font-size: 0.7rem; + font-weight: 760; + letter-spacing: 0.1em; +} + +.workflow-list strong, +.workflow-list span { + display: block; +} + +.workflow-list strong { + font-size: 0.9rem; +} + +.workflow-list span { + margin-top: 6px; + color: var(--muted); + font-size: 0.78rem; +} + +.workflow-note { + margin-bottom: 0; + color: var(--muted); + font-size: 0.82rem; + line-height: 1.65; +} + .launcher-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; - margin-top: clamp(56px, 9vw, 104px); + margin-top: 18px; } .launcher-card { @@ -181,6 +262,30 @@ p { color: var(--cyan); } +.quick-start-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 18px; + margin-top: 18px; +} + +.quick-start-card { + padding: clamp(22px, 4vw, 32px); + border: 1px solid var(--line); + border-radius: 20px; + background: var(--surface); +} + +.quick-start-card p:not(.eyebrow) { + margin: 18px 0; + color: var(--muted); + line-height: 1.7; +} + +.quick-start-card strong { + color: var(--text); +} + .landing-footer { display: flex; gap: 10px; @@ -259,10 +364,21 @@ p { } @media (max-width: 720px) { - .launcher-grid { + .launcher-grid, + .quick-start-grid, + .workflow-list { grid-template-columns: 1fr; } + .workflow-card__heading { + align-items: start; + flex-direction: column; + } + + .workflow-list li { + min-height: auto; + } + .launcher-card { min-height: 260px; } diff --git a/tests/integration/README.md b/tests/integration/README.md index 61b300e..521dc52 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -1,3 +1,30 @@ # Integration Tests -두 앱 사이 manifest 계약과 end-to-end golden scenario를 검증합니다. +실제 브라우저 API, Worker, 다운로드와 앱 간 데이터 계약을 함께 검증합니다. + +## 실행 + +1. 저장소의 `start.cmd`를 실행합니다. +2. 최신 Edge 또는 Chrome에서 을 엽니다. +3. 모든 항목이 끝날 때까지 페이지를 열어 둡니다. + +Unit과 integration test가 같은 페이지에서 실행됩니다. WebGPU 장치 초기화와 192채널 baseline은 +수 초 이상 걸릴 수 있습니다. 페이지 제목과 요약이 `PASS`인지 확인하고 실패 항목의 stack을 +확인하세요. `index.html`을 직접 열면 HTTP, module과 Worker 테스트가 올바르게 동작하지 않습니다. + +## 범위 + +- `cpu-baseline.test.js`: 192채널·50×50 synthetic fixture의 CPU DAS와 위치 기준 +- `webgpu.test.js`: 실제 adapter가 있으면 CPU/GPU map 오차, peak와 강제 bin/point batching; + adapter가 없으면 Auto CPU fallback +- `server.test.js`: 숨김 저장소 경로 차단, GET/HEAD 제한과 보안 response headers +- `builder-ui.test.js`: 이미지·오디오 연결, canvas 편집, Preview WAV/Scene 다운로드, SHA 재연결과 + 잘못된 import 보존 +- `camera-ui.test.js`: Dedicated Worker CPU/WebGPU 실행, 취소, Result schema, rectangular/Fermat 비교 +- `workflow-ui.test.js`: Dataset Builder의 Scene JSON과 원본 assets를 Acoustic Camera에 넘겨 + Result JSON을 저장하는 end-to-end 흐름 + +WebGPU adapter가 없는 환경에서는 장치가 필요한 비교 항목을 건너뛰거나 Auto fallback을 검증하므로 +CPU-only PASS도 가능합니다. 실제 장치의 scaling과 benchmark JSON 저장은 +에서 별도로 실행하세요. 기존 실측 조건과 +해석은 [`../../docs/benchmarks/README.md`](../../docs/benchmarks/README.md)에 있습니다. diff --git a/tests/unit/README.md b/tests/unit/README.md index cbd1df0..432b40c 100644 --- a/tests/unit/README.md +++ b/tests/unit/README.md @@ -1,3 +1,26 @@ # Unit Tests -음향 수식, 좌표 변환, CPU/GPU 수치 일치 테스트를 둡니다. +브라우저에서 실행되는 결정론적 수치·계약 테스트입니다. 별도 Node.js test runner가 필요하지 +않습니다. + +## 실행 + +1. 저장소의 `start.cmd`를 실행합니다. +2. 최신 Edge 또는 Chrome에서 을 엽니다. +3. 페이지 상단의 통과/실패 합계와 각 테스트 결과를 확인합니다. + +Unit과 integration module은 같은 browser runner에서 순서대로 실행됩니다. 다시 실행하려면 페이지를 +새로 고치세요. 다른 포트를 사용했다면 URL의 포트도 바꾸며, `tests/index.html`을 파일로 직접 열면 +안 됩니다. + +## 범위 + +- `contracts.test.js`: Array·Scene·Experiment·Result schema, 관계 검증, 안전한 asset 경로와 fixture +- `acoustics.test.js`: seed 재현성, mix/정규화, 자유음장 전파, calibration, STFT와 CPU DAS +- `builder.test.js`: Builder scene model, source/asset 조작, 오디오 크기와 WAV 사전 검증 +- `evaluation.test.js`: local peak, truth matching, 위치 오차, energy, sidelobe와 main-lobe 지표 +- `camera.test.js`: canonical input hash, workload 추정, 거리 이동, Result 생성과 schema 검증 +- `webgpu.test.js`: WGSL buffer packing, bin/point batching, 장치 한계와 typed CPU fallback 정책 + +WebGPU unit test 대부분은 실제 GPU 없이 planner와 binary layout을 검증합니다. 실제 GPU 수치 일치는 +integration test에서 adapter가 있을 때 수행합니다.