fix:화자분리 정확도 개선 - 이중 녹음 방식 적용
DynamicsCompressor가 모든 화자의 음량을 동일하게 압축하여 화자 음성 특성이 파괴되는 문제 해결: - MediaRecorder 1 (처리된 스트림): 실시간 Web Speech API용 - MediaRecorder 2 (원본 스트림): GCS 업로드 → 화자분리용 - 원본 오디오가 화자 음성 특성을 보존하여 분리 정확도 향상 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -279,6 +279,8 @@ function MeetingDetail({ meetingId, onBack, showToast }) {
|
||||
// refs
|
||||
const mediaRecorderRef = useRef(null);
|
||||
const audioChunksRef = useRef([]);
|
||||
const rawRecorderRef = useRef(null); // 원본 오디오 녹음 (화자분리용)
|
||||
const rawAudioChunksRef = useRef([]); // 원본 오디오 청크
|
||||
const recognitionRef = useRef(null);
|
||||
const streamRef = useRef(null);
|
||||
const timerRef = useRef(null);
|
||||
@@ -392,15 +394,24 @@ function MeetingDetail({ meetingId, onBack, showToast }) {
|
||||
|
||||
const processedStream = destination.stream;
|
||||
|
||||
// MediaRecorder (처리된 스트림 사용)
|
||||
const mimeType = MediaRecorder.isTypeSupported('audio/webm;codecs=opus')
|
||||
? 'audio/webm;codecs=opus' : 'audio/webm';
|
||||
|
||||
// MediaRecorder 1: 처리된 스트림 → 실시간 STT 품질 향상용
|
||||
const recorder = new MediaRecorder(processedStream, { mimeType, audioBitsPerSecond: 128000 });
|
||||
audioChunksRef.current = [];
|
||||
recorder.ondataavailable = (e) => { if (e.data.size > 0) audioChunksRef.current.push(e.data); };
|
||||
recorder.start();
|
||||
mediaRecorderRef.current = recorder;
|
||||
|
||||
// MediaRecorder 2: 원본 스트림 → GCS 업로드 (화자분리용)
|
||||
// DynamicsCompressor 없는 원본 음성이 화자 음성 특성을 보존하여 분리 정확도 향상
|
||||
const rawRecorder = new MediaRecorder(stream, { mimeType, audioBitsPerSecond: 128000 });
|
||||
rawAudioChunksRef.current = [];
|
||||
rawRecorder.ondataavailable = (e) => { if (e.data.size > 0) rawAudioChunksRef.current.push(e.data); };
|
||||
rawRecorder.start();
|
||||
rawRecorderRef.current = rawRecorder;
|
||||
|
||||
// Web Speech API
|
||||
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
||||
if (SpeechRecognition) {
|
||||
@@ -491,7 +502,20 @@ function MeetingDetail({ meetingId, onBack, showToast }) {
|
||||
});
|
||||
}
|
||||
|
||||
const audioBlob = new Blob(audioChunksRef.current, { type: 'audio/webm' });
|
||||
// 원본 MediaRecorder 중지 (화자분리용)
|
||||
const rawRecorder = rawRecorderRef.current;
|
||||
if (rawRecorder && rawRecorder.state !== 'inactive') {
|
||||
await new Promise(resolve => {
|
||||
rawRecorder.onstop = resolve;
|
||||
rawRecorder.stop();
|
||||
});
|
||||
}
|
||||
|
||||
// 화자분리용: 원본(무가공) 오디오 우선, 없으면 처리된 오디오 폴백
|
||||
const rawBlob = rawAudioChunksRef.current.length > 0
|
||||
? new Blob(rawAudioChunksRef.current, { type: 'audio/webm' })
|
||||
: null;
|
||||
const audioBlob = rawBlob || new Blob(audioChunksRef.current, { type: 'audio/webm' });
|
||||
const duration = recordingTime;
|
||||
|
||||
setSaving(true);
|
||||
|
||||
Reference in New Issue
Block a user