Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ engine/models/
models/*.onnx
models/*.tar.gz

# External HQ model checkpoints downloaded during local PoC runs
/checkpoints/

# macOS
.DS_Store

Expand All @@ -28,5 +31,12 @@ models/*.tar.gz
*.f32
!test/fixtures/*.wav

# Generated evaluation corpus + reports (reproduce: maketestset / quality-eval.sh / hq-model-poc.sh)
/test/corpus/manifest.csv
/test/corpus/out/
/test/corpus/quality-report.csv
/test/corpus/hq-poc/
/test/corpus/hq-poc-report.csv

# Internal PRD spec — not published
*.docx
9 changes: 9 additions & 0 deletions LICENSES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ Crisp가 사용하는 서드파티 구성요소와 상용 배포 가능 여부.
| **tract** (tract-onnx/core/pulse) | 순수 Rust ONNX 추론 엔진 | MIT / Apache-2.0 (dual) | ✅ 가능 |
| Crisp HAL 드라이버 코드 | 가상 마이크 | 자체 작성(원본). Apple Audio Server Plug-in 아키텍처 참고 | ✅ 자체 저작 |
| Crisp 앱/엔진 코드 | UI, 실시간/파일 처리 | 자체 작성 | ✅ 자체 저작 |
| Crisp Voice Enhancer DSP | EQ/컴프레서/디에서/리미터/LUFS (PRD v0.2) | 자체 작성(RBJ EQ cookbook·ITU-R BS.1770 공개 공식 기반, 코드 원본) | ✅ 자체 저작 |
| ClearerVoice-Studio `clearvoice` + MossFormer2_SE_48K | 선택적 파일 HQ 외부 모델(Python/Torch 프로세스) | Apache-2.0(코드·가중치 README 확인) | ✅ 가능, **제품 번들 아님** |

> **Voice Enhancer(PRD v0.2)는 신규 서드파티·모델 weight를 추가하지 않는다.** 인핸서를 생성형
> 모델 대신 순수 DSP(자체 코드)로 구현했기 때문이다. ClearerVoice는 설치된 외부 명령이 있을 때만
> out-of-process 파일 HQ 옵션으로 연결하며 앱/패키지에 Python·Torch·가중치를 번들하지 않는다. PRD가 후보로 든
> LocalVQE(Apache-2.0), Resemble Enhance(MIT)는 추후 동일 `AudioProcessor` seam에 드롭인할
> 수 있으나, 그 가중치·코드 라이선스는 **탑재 시점에 별도 BOM 확인 필요**(PRD §7.2, §9 M1).
> NVIDIA RE-USE 등 비상용 모델은 출시 빌드 제외 원칙 유지.

> **ffmpeg 의존 제거됨.** 파일 처리(Phase 4)는 AVFoundation(시스템 프레임워크) + libDF로 구현해
> 외부 프로세스/ffmpeg 없이 동작한다. ffmpeg는 테스트 픽스처 생성에만 쓰이며 제품에 포함되지 않는다.
Expand Down
50 changes: 36 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# Crisp — macOS 실시간 노이즈 캔슬링
# Crisp — macOS 실시간 노이즈 캔슬링 + 보이스 인핸서

물리 마이크 입력을 **온디바이스 AI(DeepFilterNet3)** 로 실시간 노이즈 억제한 뒤, Zoom·Meet·Slack·Discord·Teams
등에서 선택할 수 있는 **가상 마이크("Noise Cancelled Microphone")** 로 출력. 로컬 파일 음성 개선도 지원.
물리 마이크 입력을 **온디바이스 AI(DeepFilterNet3)** 로 실시간 노이즈 억제하고, **DSP 보이스
인핸서**(EQ·컴프레서·디에서·리미터)로 명료도·음량을 다듬은 뒤, Zoom·Meet·Slack·Discord·Teams
등에서 선택할 수 있는 **가상 마이크("Noise Cancelled Microphone")** 로 출력. 로컬 파일 음성 개선(파일 HQ)도 지원.

**처리 모드**: Off · Noise Cancellation · Voice Enhancer · Clean + Enhance (PRD v0.2 —
[`docs/voice-enhancer.md`](docs/voice-enhancer.md)). 두 stage(denoise → enhance)는 교체 가능하며
보이스 인핸서는 생성형이 아니라 화자/발화를 보존한다.

## 빌드 & 설치 — 복사해서 실행 (또는 AI 에이전트에 붙여넣기)

Expand Down Expand Up @@ -54,20 +59,29 @@ open build/Crisp-0.1.0.pkg

## 사용법

**실시간**: 메뉴바 Crisp → 노이즈 캔슬링 ON + 입력 마이크 선택 → 회의 앱 마이크에서 "Noise Cancelled Microphone" 선택. 강도(약/보통/강)·바이패스·저지연 모드 조절 가능.
**실시간**: 메뉴바 Crisp → 실시간 처리 ON + 입력 마이크 선택 → 회의 앱 마이크에서 "Noise Cancelled Microphone" 선택.
**처리 모드**(Off/Noise/Voice/Clean+Enhance), 노이즈 강도, 인핸스 강도, 톤(자연/따뜻/밝게),
바이패스(A/B), 저지연 모드를 조절. 모드/강도/톤 전환은 클릭 없이 라이브 적용.

**파일**: 설정 창 → 파일 탭 → 오디오/비디오(wav/mp3/m4a/mp4) 드래그앤드롭 → 출력 포맷(wav/m4a) 선택 →
**강도 슬라이더(0–100 dB)** 로 단일 처리, 또는 **배치 모드**로 여러 강도(예 12/24/48/100 dB)를 한 번에 출력해 비교·저장.
**파일**: 설정 창 → 파일 탭 → 오디오/비디오(wav/mp3/m4a/mp4/mov) 드래그앤드롭 →
처리 모드 + 품질(Fast/HQ) + 톤 + 음량 정규화(-16/-18 LUFS) 선택 → **미리듣기(20초)** 로 결과 방향 확인 후
**음성 개선 시작**. 원본은 덮어쓰지 않고 wav/m4a로 저장하며 before/after LUFS·peak 리포트를 표시.
ClearerVoice가 외부 명령/venv로 설치되어 있으면 HQ에서 "HQ 모델" 선택지가 나타나고, 없으면 내장 DSP만 사용한다.

## 검증 / 테스트

```sh
swift test --package-path app # 단위·통합 테스트 (32개: DSP·파이프라인·LUFS·true-peak·외부 모델 seam)
./scripts/quality-eval.sh # 테스트셋 생성+배치 처리 → quality-report.csv (회귀 게이트)
./scripts/hq-model-poc.sh # 파일 HQ 외부 모델 PoC (CLEARERVOICE_CMD/RESEMBLE_CMD 설정 시 비교)
./poc/model/run_poc.sh # 모델 RTF·노이즈 감소
./scripts/e2e-test.sh # end-to-end: model→가상마이크→녹음 (드라이버 설치 필요)
./scripts/stability-test.sh 1800 # 30분 안정성 (crash/dropout)
BIN="$(cd app && swift build --show-bin-path)"
"$BIN/dftool" engine/models/DeepFilterNet3_onnx.tar.gz 100 build/in.f32 out.f32 chunked # 스트리밍 정확성
"$BIN/filetool" input.mp3 out.m4a m4a # 파일 처리
"$BIN/dftool" engine/models/DeepFilterNet3_onnx.tar.gz 100 build/in.f32 out.f32 chunked # 노이즈 stage 스트리밍 정확성
"$BIN/vetool" engine/models/DeepFilterNet3_onnx.tar.gz # 인핸서/파이프라인(결정성·리미터·RTF)
"$BIN/maketestset" test/corpus # PRD 8.1 테스트셋 생성(실제 자산 기반)
"$BIN/filetool" enhance input.mp3 out.wav clean hq natural podcast # 파일 Voice Enhancer 파이프라인
```

## 프로젝트 구조
Expand All @@ -78,12 +92,16 @@ app/Package.swift SwiftPM: CDeepFilter / CrispEngine / CrispApp / dftoo
Sources/CDeepFilter/ libdf C 헤더(df.h) + modulemap
Sources/CrispEngine/ 순수 DSP/모델(AVFoundation): DeepFilterSuppressor, FileEnhancer,
VirtualMicOutput(AUHAL), CoreAudioDevices ← 앱·CLI 공유
Voice Enhancer: AudioProcessing(모드/config/protocol), PipelineProcessor,
VoiceEnhancer, Biquad, Loudness · 평가: Metrics, AudioIO, TestCorpus
Sources/CrispApp/ SwiftUI 메뉴바 앱 (AppState, AudioEngineController, Views)
Sources/{dftool,filetool,mictool}/ 검증 CLI
Sources/{dftool,vetool,filetool,mictool,maketestset,evaltool}/ 검증·평가 CLI
Tests/CrispEngineTests/ XCTest (swift test): DSP·파이프라인·LUFS·메트릭·파일 end-to-end
engine/CDeepFilter/lib/ libdf.dylib (fetch-deps 생성, gitignore)
engine/models/ DeepFilterNet3 모델 (fetch-deps 생성, gitignore)
scripts/ fetch-deps / install / verify / package / e2e / stability
docs/ architecture · STATUS · test-report · install · acceptance-checklist · known-issues
test/corpus/ 평가 테스트셋 (maketestset 생성, gitignore) + README
scripts/ fetch-deps / install / verify / package / e2e / stability / quality-eval / hq-model-poc
docs/ architecture · voice-enhancer · enhance-behavior · enhance-limitations-research · hq-model-integration · STATUS · test-report
poc/model/ DeepFilterNet 클론(gitignore) + run_poc.sh
```

Expand All @@ -98,6 +116,7 @@ poc/model/ DeepFilterNet 클론(gitignore) + run_poc.sh
| 2 실시간 엔진 | capture→model→가상마이크 | ✅ end-to-end loopback |
| 3 앱 | 메뉴바/온보딩/설정 | ✅ |
| 4 파일 처리 | ffmpeg-free (AVFoundation) | ✅ mp3→m4a, mp4→wav |
| VE Voice Enhancer (v0.2) | 2-stage 파이프라인 + DSP 인핸서, 4모드, 파일 HQ | ✅ vetool 검증, 외부 HQ PoC |
| 5 패키징 | 서명/notarization/pkg | 🔶 pkg ✅, notarize는 Apple 계정 |

---
Expand All @@ -108,8 +127,10 @@ poc/model/ DeepFilterNet 클론(gitignore) + run_poc.sh

**Repo map (수정 위치):**
- 가상 마이크 동작 → `driver/CrispAudioDriver/CrispAudioDriver.c` (`Crisp_DoIOOperation`의 loopback ring)
- 모델 추론(실시간·파일 공통) → `app/Sources/CrispEngine/DeepFilterSuppressor.swift` (libdf C-API)
- 파일 처리 → `app/Sources/CrispEngine/FileEnhancer.swift` (AVAssetReader→libdf→AVAudioFile)
- 모델 추론(노이즈 stage) → `app/Sources/CrispEngine/DeepFilterSuppressor.swift` (libdf C-API)
- 보이스 인핸서(DSP) → `app/Sources/CrispEngine/VoiceEnhancer.swift` · 필터 `Biquad.swift` · LUFS `Loudness.swift`
- 파이프라인 그래프/모드 → `app/Sources/CrispEngine/{PipelineProcessor,AudioProcessing}.swift`
- 파일 처리 → `app/Sources/CrispEngine/FileEnhancer.swift` (AVAssetReader→pipeline→AVAudioFile)
- 가상 마이크 출력 → `app/Sources/CrispEngine/VirtualMicOutput.swift` (AUHAL)
- 실시간 캡처/라우팅 → `app/Sources/CrispApp/Audio/AudioEngineController.swift`
- UI/상태 → `app/Sources/CrispApp/{Views,Model}`
Expand All @@ -118,6 +139,7 @@ poc/model/ DeepFilterNet 클론(gitignore) + run_poc.sh
- 장치 UID `"CrispAudioDevice:0"` 는 드라이버(`kDevice_UID`)와 Swift(`CoreAudioDevices.crispVirtualUID`)에서 동일.
- 모델은 48 kHz mono, hop 480(=10ms). 다른 레이트는 `AVAudioConverter`로 48k 변환 후 처리.
- `DeepFilterSuppressor`는 stateful 스트리밍 — hop을 연속·순서대로 공급(carry 버퍼 보장). 검증: `dftool ... chunked` 출력이 aligned와 bit-identical.
- 두 stage(denoise→enhance)는 모든 모드에서 항상 신호 경로에 존재 — 모드는 파라미터(atten·dry/wet)만 바꿈. `VoiceEnhancer`는 `wetMix==0`에서 bit-identical 패스스루여야 함(클릭 방지). 검증: `vetool` (aligned==chunked, 패스스루, 리미터).
- 실시간/파일 경로에 ffmpeg·외부 프로세스 의존 금지 (파일도 AVFoundation).
- UI 메터 갱신은 ~15Hz throttle + `MeterState` 격리 (오디오 탭 ~100Hz → 메인스레드 폭주 방지). 되돌리지 말 것.
- HAL 드라이버는 Apple Silicon에서 서명 필수(ad-hoc 가능), 팩토리 심볼 `CrispAudioDriver_Create` export 유지(`CRISP_EXPORT`).
Expand All @@ -127,4 +149,4 @@ poc/model/ DeepFilterNet 클론(gitignore) + run_poc.sh

## 라이선스

DeepFilterNet(libDF + 모델 weight), tract: **MIT / Apache-2.0** (상용 가능). Crisp 드라이버/앱/엔진: 자체 작성. 상세: [`LICENSES.md`](LICENSES.md)
DeepFilterNet(libDF + 모델 weight), tract: **MIT / Apache-2.0** (상용 가능). Crisp 드라이버/앱/엔진(보이스 인핸서 DSP 포함): 자체 작성 — Voice Enhancer 추가로 **서드파티/모델 weight 변화 없음**. 상세: [`LICENSES.md`](LICENSES.md)
28 changes: 28 additions & 0 deletions app/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,34 @@ let package = Package(
dependencies: ["CrispEngine", "CDeepFilter"],
path: "Sources/mictool",
linkerSettings: [dfLinker]
),
// Verifies the Voice Enhancer DSP stage + pipeline (streaming determinism, RTF).
.executableTarget(
name: "vetool",
dependencies: ["CrispEngine", "CDeepFilter"],
path: "Sources/vetool",
linkerSettings: [dfLinker]
),
// Generates the evaluation corpus (PRD 8.1) from in-repo speech + noise.
.executableTarget(
name: "maketestset",
dependencies: ["CrispEngine"],
path: "Sources/maketestset",
linkerSettings: [dfLinker]
),
// Objective metrics for a processed file (PRD 8.2): LUFS, peak, RMS, SI-SDR.
.executableTarget(
name: "evaltool",
dependencies: ["CrispEngine"],
path: "Sources/evaltool",
linkerSettings: [dfLinker]
),
// Automated unit + integration tests (`swift test`).
.testTarget(
name: "CrispEngineTests",
dependencies: ["CrispEngine", "CDeepFilter"],
path: "Tests/CrispEngineTests",
linkerSettings: [dfLinker]
)
]
)
Loading