mirror of
https://github.com/nad4-su/meeting-minutes.git
synced 2026-10-11 15:50:13 +09:00
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>
This commit is contained in:
1 parent
9af00c3016
commit
d2cb5b0009
8 files changed
+546
-19
No files matched your search
@@ -0,0 +1,45 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
LOCAL_SPEAKER,
|
||||
REMOTE_SPEAKER,
|
||||
listAttendees,
|
||||
resolveSpeakerName,
|
||||
} from '@/lib/speakers'
|
||||
|
||||
describe('resolveSpeakerName', () => {
|
||||
it('기본 화자에 한국어 표기를 붙인다', () => {
|
||||
expect(resolveSpeakerName(LOCAL_SPEAKER)).toBe('나')
|
||||
expect(resolveSpeakerName(REMOTE_SPEAKER)).toBe('상대')
|
||||
})
|
||||
|
||||
it('사용자가 지정한 이름이 우선한다', () => {
|
||||
const names = { [REMOTE_SPEAKER]: '김지훈' }
|
||||
expect(resolveSpeakerName(REMOTE_SPEAKER, names)).toBe('김지훈')
|
||||
expect(resolveSpeakerName(LOCAL_SPEAKER, names)).toBe('나')
|
||||
})
|
||||
|
||||
it('공백뿐인 이름은 무시한다', () => {
|
||||
expect(resolveSpeakerName(LOCAL_SPEAKER, { [LOCAL_SPEAKER]: ' ' })).toBe('나')
|
||||
})
|
||||
|
||||
it('모르는 식별자는 그대로 돌려준다', () => {
|
||||
expect(resolveSpeakerName('spk_3')).toBe('spk_3')
|
||||
})
|
||||
})
|
||||
|
||||
describe('listAttendees', () => {
|
||||
it('중복을 제거하고 순서를 유지한다', () => {
|
||||
expect(
|
||||
listAttendees([REMOTE_SPEAKER, LOCAL_SPEAKER, REMOTE_SPEAKER]),
|
||||
).toEqual(['상대', '나'])
|
||||
})
|
||||
|
||||
it('지정된 이름을 반영한다', () => {
|
||||
expect(
|
||||
listAttendees([LOCAL_SPEAKER, REMOTE_SPEAKER], {
|
||||
[LOCAL_SPEAKER]: '배철승',
|
||||
[REMOTE_SPEAKER]: '김지훈',
|
||||
}),
|
||||
).toEqual(['배철승', '김지훈'])
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user