Commit Graph
3 Commits
Author SHA1 Message Date
csbaeandClaude Opus 5 4af0a03de5 feat: 대면 회의 화자 분리 (로컬 diarization)
마이크 한 트랙에 여러 사람이 섞여 들어오는 대면 회의에서 화자를 나눈다.
pyannote-segmentation-3.0 + 3D-Speaker CAM++ 를 ONNX Runtime으로 로컬
Next.js 프로세스에서 실행한다. GPU도 파이썬도 필요 없고 오디오가 외부로
나가지 않는다.

실측 (Apple Silicon, 2스레드, 4인 한국어 아닌 샘플 57초):
- 처리 1.5초 → RTF 0.03, 실시간의 33배
- 자동 추정은 화자 5명(과다 계수), 참석자 수를 주면 4명(정확)
  → 참석자 수 입력을 1급 기능으로 노출

구성:
- lib/diarization.ts — sherpa-onnx-node 래퍼, 모델 캐시, 참석자 수 힌트
- lib/audio-session.ts — 녹음 중 PCM 조각을 세션별로 누적 (메모리)
- api/diarize/chunk — 4초짜리 Int16 PCM 조각 업로드
- api/diarize — 누적 오디오 전체 분석, GET은 모델 설치 여부 확인
- public/pcm-worklet.js — 16kHz Int16 PCM 추출 AudioWorklet
- hooks/useDiarization.ts — 수집·분석 상태 관리
- transcript-formatter: assignSpeakers() — 시간 겹침으로 화자 배정.
  겹치는 구간이 없으면 라벨을 비운다 (틀린 라벨보다 없는 편이 낫다)
- LiveRecorder: 사용 안 함 / 원격 회의 / 대면 회의 3모드 + 참석자 수
- scripts/setup-diarization.mjs — 모델 34MB 내려받기 (npm run setup:diarization)

설계 변경: 롤링 재-diarization 대신 녹음 종료 시 1회 전체 분석.
구간을 잘라 반복하면 실행마다 화자 번호가 달라져 이어 붙일 수 없고,
누적분 재분석은 비용이 회의 길이에 비례해 커진다. 대신 실시간 화자 표시는 없다.

모델은 라이선스와 용량 때문에 저장소에 넣지 않고 gitignore 한다.

테스트 176 → 184.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 10:47:10 +09:00
csbaeandClaude Opus 5 a8cfab8a4a fix: 두 트랙을 확보했을 때만 화자를 라벨링
대면 회의처럼 시스템 오디오가 없는 상황에서 화자 분리를 켜면,
마이크에 섞여 들어온 상대 발언까지 "나"로 기록되는 문제가 있었다.
라벨이 틀리는 것은 라벨이 없는 것보다 나쁘다.

capture.mode가 'dual-stream'일 때만 speaker를 붙이고, 마이크 단독으로
폴백하면 라벨 없이 기존 형식으로 기록한다.

설정 UI에도 대면 회의에서는 동작하지 않는다는 점을 명시했다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 18:29:58 +09:00
csbaeandClaude Opus 5 d2cb5b0009 feat: dual-stream 캡처로 원격 회의 화자 분리
내 마이크와 시스템(탭) 오디오를 별도 트랙으로 잡고, 트랙마다 인식기를
붙인다. 트랙이 곧 화자이므로 추론도 모델 다운로드도 없이 화자가 갈린다.

Chrome이 SpeechRecognition.start(audioTrack)를 지원하는 덕분에 가능하다.
트랙을 명시적으로 넘기므로 기본 마이크를 두고 경합할 일도 없다.

- speakers.ts 신설 — 화자 식별자와 표시 이름 해석
- TranscriptChunk.speaker 추가, formatTranscriptChunks가 "화자: 발화"로 출력
- mergeSpeakerChunks — 트랙별 청크를 하나의 시간축으로 병합.
  여러 인식기가 같은 timeOrigin을 공유해야 순서가 맞는다
- mergeAdjacentChunks가 화자 경계를 넘어 합치지 않도록 수정
- useSpeechRecognition: audioTrack / speaker / timeOrigin / lang 옵션.
  인자 없이 호출하면 기존 동작 그대로다
- useDualStreamCapture 신설 — getUserMedia + getDisplayMedia.
  video는 즉시 끊고 오디오만 쓴다. 시스템 오디오 확보에 실패하면
  마이크 단독으로 폴백하고 화자 분리가 꺼졌음을 안내한다
- LiveRecorder: 화자 분리 토글, 화자 이름 입력, 전사 목록 화자 색상 구분

원격 다자 회의와 대면 회의는 원격 트랙에 diarization이 필요하며 후속 작업이다.

테스트 162 → 176.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 18:27:00 +09:00