173 lines
8.4 KiB
Markdown
173 lines
8.4 KiB
Markdown
# ARCHITECTURE
|
||
|
||
## 1. 기술 스택 (확정)
|
||
| 영역 | 선택 | 이유 |
|
||
|---|---|---|
|
||
| 런타임 | **.NET 8** | 최신 LTS, 네이티브 Windows |
|
||
| UI | **WPF + WPF-UI (lepoco)** | Fluent Design System / Mica·다크모드 / WinUI 감성. 텍스트 UI 아님 |
|
||
| 차트 | **LiveCharts2** | 애니메이션되는 아름다운 EQ 커브. (대안: ScottPlot=정적·경량) |
|
||
| MVVM | **CommunityToolkit.Mvvm** | 소스제너레이터 기반, 보일러플레이트 최소 |
|
||
| DB | **SQLite (Microsoft.Data.Sqlite)** | 로컬 프로파일 인덱스 |
|
||
| AI | **Anthropic REST (HttpClient)** 또는 `Anthropic.SDK` NuGet | 구조화 출력(tool use) |
|
||
| 패키징 | **MSIX 또는 velopack/single-file** | 배포. 라이선스 제외 옵션과 연동 |
|
||
|
||
> WPF-UI가 Mica 배경 + Fluent 컨트롤 + 다크테마를 제공해 목업(`mockups/main_window.html`)의
|
||
> 룩앤필을 네이티브로 재현할 수 있다. 그래프는 LiveCharts2의 `CartesianChart`로 로그 X축(주파수)
|
||
> + 선형 Y축(dB), 영역 그라데이션 채우기.
|
||
|
||
## 2. 컴포넌트 구성
|
||
```
|
||
DansoriEQ.sln
|
||
├─ DansoriEQ.App (WPF, WPF-UI) ── Views/ViewModels, 그래프, 프롬프트 독
|
||
├─ DansoriEQ.Core (class lib)
|
||
│ ├─ Profiles/ ProfileRepository, AutoEqParser, DbUpdater
|
||
│ ├─ Eq/ EqState, Filter(model), ApoRenderer, PreampCalculator
|
||
│ ├─ Ai/ ClaudeClient, EqPromptBuilder, EqDeltaSchema
|
||
│ └─ Apo/ ApoLocator, ApoConfigInstaller(승격 1회), IncludeWriter
|
||
└─ DansoriEQ.Setup (elevated helper, 최초 1회 UAC — 아래 4장)
|
||
```
|
||
|
||
## 3. 데이터 흐름
|
||
```
|
||
프로파일 선택 → ProfileRepository.Load(id) → EqState(base)
|
||
→ ApoRenderer → IncludeWriter.Write(ai_eq.txt) → APO 라이브 리로드 → 그래프 갱신
|
||
|
||
프롬프트 입력 → EqPromptBuilder(현재 EqState + 헤드폰 메타 + 요청)
|
||
→ ClaudeClient(tool use, EqDeltaSchema) → EqDelta(JSON)
|
||
→ EqState.Apply(delta) → PreampCalculator → ApoRenderer → IncludeWriter → APO
|
||
→ AI 설명 텍스트를 응답 패널에 표시 / 히스토리 스택 push
|
||
```
|
||
|
||
## 4. APO 연동 & 저권한 전략 (중요)
|
||
문제: APO의 `config.txt`는 `C:\Program Files\EqualizerAPO\config\` 아래라 쓰기에 승격 필요.
|
||
매번 승격은 "저권한" 원칙 위반.
|
||
|
||
**채택 방식 — "1회 승격 설정, 이후 무승격":**
|
||
1. **최초 실행 설정 마법사**(DansoriEQ.Setup, UAC 1회):
|
||
- APO 설치 경로 탐지: 레지스트리 `HKLM\SOFTWARE\EqualizerAPO` → `InstallPath`.
|
||
- 사용자 쓰기 가능한 EQ 파일 준비. **권장:** `config` 폴더 내 `ai_eq.txt`를 만들고
|
||
**그 파일(또는 폴더)에 현재 사용자 쓰기 ACL 부여**(icacls). 그리고 `config.txt`에
|
||
`Include: ai_eq.txt` 한 줄 추가(중복 방지 검사).
|
||
- 완료 플래그 저장(`%LOCALAPPDATA%\DansoriEQ\setup.json`).
|
||
2. **이후 실행**: 앱은 `ai_eq.txt`만 갱신 → **승격 불필요**. APO가 저장 즉시 리로드.
|
||
|
||
> ⚠️ **M1에서 검증할 스파이크:** APO `Include:`가 절대경로를 허용하는지 확인.
|
||
> - 허용 O → `ai_eq.txt`를 `%APPDATA%\DansoriEQ\`에 두고 절대경로 Include(ACL 불필요, 가장 깔끔).
|
||
> - 허용 X(상대경로만) → 위 4-1의 config 폴더 내 파일 + ACL 부여 방식으로.
|
||
> 두 경로 모두 "1회 승격, 이후 무승격"을 만족. 스파이크 결과로 하나 확정.
|
||
|
||
APO 필터 문법(렌더 타깃):
|
||
```
|
||
Preamp: -6.0 dB
|
||
Filter 1: ON PK Fc 3000 Hz Gain 3.0 dB Q 1.20
|
||
Filter 2: ON LSC Fc 90 Hz Gain 4.0 dB Q 0.70
|
||
Filter 3: ON HSC Fc 10000 Hz Gain -1.5 dB Q 0.70
|
||
```
|
||
- 타입 매핑: PK=peaking, LSC=low-shelf, HSC=high-shelf, (LP/HP 확장 가능).
|
||
- **Preamp = -(양의 게인 총합) 또는 최소 -(최대 양의 게인) − 여유 1dB.** 클리핑 방지 필수.
|
||
|
||
## 5. 프로파일 DB & 라이선스 분리
|
||
### 폴더 레이아웃
|
||
```
|
||
data/
|
||
├─ profiles/
|
||
│ ├─ open/ ← 재배포 허용(퍼미시브) 소스. 배포 빌드에 포함
|
||
│ └─ restricted/ ← 재배포 불가 소스. 배포 빌드에서 제외
|
||
├─ manifest.json ← 소스별 {name, license, distributable, url, commit}
|
||
└─ dansorieq.db ← SQLite 인덱스(open+restricted 모두 인덱싱)
|
||
```
|
||
### 원칙
|
||
- **수집(개발 중 1회):** AutoEQ repo를 zip/clone로 확보 → 파싱 → 소스 라이선스에 따라
|
||
`open/` 또는 `restricted/`로 분류 저장 → SQLite에 upsert.
|
||
- **UI:** DB를 조회할 때 **라이선스 구분 없이 병합**해서 보여준다(IEM/헤드폰만 구분).
|
||
- **배포:** 패키징 시 `--exclude-restricted` 옵션 → `restricted/` 폴더와 해당 DB 행 제외.
|
||
(개인 사용 로컬 빌드는 전체 포함.)
|
||
- DB 스키마:
|
||
```sql
|
||
CREATE TABLE headphone(
|
||
id INTEGER PRIMARY KEY,
|
||
name TEXT NOT NULL,
|
||
brand TEXT,
|
||
type TEXT CHECK(type IN ('iem','headphone')) NOT NULL,
|
||
source TEXT, -- 측정/소스명
|
||
license TEXT, -- 예: 'CC-BY', 'restricted'
|
||
distributable INTEGER, -- 0/1 (UI에선 필터하지 않음)
|
||
target TEXT, -- 예: 'Harman IE 2019'
|
||
preamp_db REAL,
|
||
filters_json TEXT, -- [{type,fc,gain,q}]
|
||
raw_text TEXT, -- 원본 ParametricEQ.txt
|
||
updated_at TEXT
|
||
);
|
||
CREATE INDEX ix_hp_type ON headphone(type);
|
||
CREATE INDEX ix_hp_name ON headphone(name);
|
||
```
|
||
### 앱 내 업데이트
|
||
- "DB 업데이트" → 최신 소스(zip) 재확보 → 파싱 → **upsert(변경분만)** → manifest commit/날짜 갱신.
|
||
- UI 하단에 `N profiles · 최신: YYYY-MM-DD` 표시.
|
||
|
||
## 6. AI 레이어 스키마 (구조화 출력)
|
||
`EqDelta` (Claude가 tool use로 반환):
|
||
```json
|
||
{
|
||
"preamp_db": -6.0,
|
||
"filters": [
|
||
{ "type": "PK", "fc": 3000, "gain": 3.0, "q": 1.2, "enabled": true },
|
||
{ "type": "LSC", "fc": 90, "gain": 4.0, "q": 0.7, "enabled": true }
|
||
],
|
||
"explanation": "보컬 존재감을 위해 3kHz +3dB…",
|
||
"replace": false // false=현재 상태에 병합, true=전체 교체
|
||
}
|
||
```
|
||
시스템 프롬프트 요지:
|
||
- 역할: 파라메트릭 EQ 전문가. APO 제약(타입 PK/LSC/HSC, Fc 20–20000Hz, Gain ±12dB 권장,
|
||
Q 0.3–6) 안에서만 출력.
|
||
- 입력으로 **현재 EqState + 헤드폰 모델/타입 + 사용자 요청** 제공 → 요청을 delta로 반영.
|
||
- 오디오 상식 힌트(보컬≈2–5kHz, 저음 단단히≈40–80Hz 무게+150–250Hz 뭉침 컷, 치찰음≈6–8kHz).
|
||
- **Preamp 안전값 계산 규칙 명시.**
|
||
- 런타임 모델: 기본 **Claude Sonnet**(요청당 2–5k 토큰), 옵션으로 Opus.
|
||
|
||
## 7. 오픈 이슈 → DEV_PLAN 참조
|
||
- APO Include 절대경로 허용 여부(스파이크).
|
||
- AutoEQ repo 구조/소스 라이선스 최신 상태 확인(수집 스크립트에서).
|
||
- 다중 출력 장치별 APO config 타깃팅(고급 옵션, MVP 이후).
|
||
|
||
## 8. EQ 프리셋 공유 포맷 (.tweq)
|
||
사용자가 만든 EQ를 다른 사용자와 공유하기 위한 파일. **JSON**, 확장자 `.tweq`.
|
||
```json
|
||
{
|
||
"format": "dansorieq.eq",
|
||
"version": 1,
|
||
"app_version": "0.1.0",
|
||
"created_utc": "2026-07-01T12:00:00Z",
|
||
"author_note": "밝고 보컬 중심 튜닝",
|
||
"target": {
|
||
"name": "Truthear x Crinacle Zero",
|
||
"brand": "Truthear",
|
||
"type": "iem",
|
||
"source": "AutoEQ",
|
||
"target_curve": "Harman IE 2019"
|
||
},
|
||
"eq": {
|
||
"preamp_db": -6.0,
|
||
"filters": [
|
||
{ "type": "PK", "fc": 3000, "gain": 3.0, "q": 1.2, "enabled": true }
|
||
]
|
||
},
|
||
"history": [
|
||
{ "role": "user", "text": "보컬 가까이, 저음 단단히", "ts": "..." },
|
||
{ "role": "ai", "text": "3kHz +3dB, 180Hz -2.5dB…", "ts": "..." }
|
||
]
|
||
}
|
||
```
|
||
- **내보내기**: 현재 EqState + 대상 기기 메타 + 대화 히스토리(간략)를 직렬화해 저장.
|
||
- **가져오기**: 검증(format/version, 값 범위 클램프, 크기 상한) → **정보 패널(대상 기기 / 히스토리 / 작성자 노트) 표시** → EQ 적용.
|
||
- **라이선스 안전**: 파일엔 측정 데이터가 아니라 **기기 이름/메타만** 포함 → 재배포 문제 없음.
|
||
- **보안**: history 텍스트는 **표시 전용**(실행/링크 처리 안 함), 항목 수·길이 상한, API 키 미포함.
|
||
|
||
## 9. 설정 & Claude API 키 관리
|
||
- **별도 설정 화면**(WPF-UI NavigationView: Home/EQ ↔ Settings).
|
||
- 섹션: [AI · API 키] / [테마] / [APO] / [데이터베이스].
|
||
- **API 키**: 등록 / 삭제 / 교체, "연결 테스트", **DPAPI 암호화 저장**(`%LOCALAPPDATA%\DansoriEQ`).
|
||
- **모델 선택**: 기본 Claude Sonnet / 옵션 Opus.
|
||
- 키 미등록 시 프롬프트 독 비활성 + 등록 안내.
|