[251021] fix: audioContext삭제,디버그화면 개선

🕐 커밋 시간: 2025. 10. 21. 20:52:42

📊 변경 통계:
  • 총 파일: 1개
  • 추가: +29줄
  • 삭제: -113줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.jsx

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.jsx (javascript):
    🔄 Modified: SpotlightContainerDecorator(), clearAllTimers()

Performance: 코드 최적화로 성능 개선 기대
This commit is contained in:
2025-10-21 20:52:43 +09:00
parent bf14fd308e
commit 1abe8db65e

View File

@@ -33,8 +33,8 @@ const OverlayContainer = SpotlightContainerDecorator(
const SpottableMicButton = Spottable('div');
// Debug mode constant
const DEBUG_MODE = process.env.NODE_ENV === 'development';
// Debug mode constant - 항상 디버그 화면 표시
const DEBUG_MODE = true;
// Voice overlay 모드 상수
export const VOICE_MODES = {
@@ -81,10 +81,6 @@ const DEFAULT_SUGGESTIONS = [
// false로 설정하면 이 기능은 완전히 비활성화됩니다
const ENABLE_WAKE_WORD = false;
// 🔧 실험적 기능: Beep Sound on Listening Start
// false로 설정하면 Beep 소리가 재생되지 않습니다
const ENABLE_BEEP_SOUND = true;
// Utility function to clear a single timer ref (timeout)
const clearTimerRef = (timerRef) => {
if (timerRef.current) {
@@ -127,7 +123,6 @@ const VoiceInputOverlay = ({
const dispatch = useDispatch();
const lastFocusedElement = useRef(null);
const listeningTimerRef = useRef(null);
const audioContextRef = useRef(null);
const interimTextRef = useRef(''); // Interim text 추적용 ref
// Timer refs for cleanup
@@ -193,81 +188,6 @@ const VoiceInputOverlay = ({
}
}, [currentMode, isVisible]);
// 🔊 Beep 소리 재생 함수 - zero dependencies
const playBeep = useCallback(() => {
if (!ENABLE_BEEP_SOUND) return;
try {
// AudioContext 지원 여부 확인 (TV 환경에서는 지원되지 않을 수 있음)
const AudioContextClass = window.AudioContext || window.webkitAudioContext;
if (!AudioContextClass) {
if (DEBUG_MODE) {
console.warn(
'[VoiceInputOverlay.v2] AudioContext not supported in this environment, skipping beep'
);
}
return;
}
// AudioContext 생성 (재사용)
if (!audioContextRef.current) {
try {
audioContextRef.current = new AudioContextClass();
} catch (contextErr) {
if (DEBUG_MODE) {
console.warn('[VoiceInputOverlay.v2] Failed to create AudioContext:', contextErr);
}
return;
}
}
const audioContext = audioContextRef.current;
// null 또는 undefined 체크
if (!audioContext) {
if (DEBUG_MODE) {
console.warn('[VoiceInputOverlay.v2] AudioContext is null or undefined, skipping beep');
}
return;
}
// AudioContext 메서드 존재 여부 확인
if (
typeof audioContext.createOscillator !== 'function' ||
typeof audioContext.createGain !== 'function'
) {
if (DEBUG_MODE) {
console.warn('[VoiceInputOverlay.v2] AudioContext methods not available, skipping beep');
}
return;
}
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = 800; // 800Hz (높은 피치)
oscillator.type = 'sine'; // 부드러운 소리
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime); // 볼륨 30%
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1); // 페이드아웃
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.1); // 0.1초 재생
if (DEBUG_MODE) {
console.log('🔊 [VoiceInputOverlay.v2] Beep sound played successfully');
}
} catch (err) {
// 어떤 오류가 발생하더라도 앱이 멈추지 않도록 조용히 처리
if (DEBUG_MODE) {
console.warn('[VoiceInputOverlay.v2] Failed to play beep sound (non-critical):', err);
}
}
}, []);
// 🔍 검색 기록 저장 함수 (성능 최적화: stable reference)
const addToSearchHistory = useCallback((searchText) => {
if (!searchText || searchText.trim().length < 3) return;
@@ -674,25 +594,11 @@ const VoiceInputOverlay = ({
};
}, [isVisible, mode, dispatch]);
// Cleanup all timers and AudioContext on component unmount
// Cleanup all timers on component unmount
useEffect(() => {
return () => {
// Clear all timer refs
clearAllTimers(allTimerRefs);
// Close AudioContext to free audio resources
if (audioContextRef.current) {
try {
if (typeof audioContextRef.current.close === 'function') {
audioContextRef.current.close();
}
} catch (err) {
if (DEBUG_MODE) {
console.warn('[VoiceInputOverlay.v2] Failed to close AudioContext:', err);
}
}
audioContextRef.current = null;
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -997,9 +903,6 @@ const VoiceInputOverlay = ({
// ✅ Interim text ref 초기화
interimTextRef.current = '';
// 🔊 Beep 소리 재생
playBeep();
// 기존 타이머 정리
clearTimerRef(listeningTimerRef);
clearTimerRef(silenceDetectionTimerRef);
@@ -1025,7 +928,6 @@ const VoiceInputOverlay = ({
[
currentMode,
handleClose,
playBeep,
startListening,
stopListening,
onSearchChange,
@@ -1104,10 +1006,8 @@ const VoiceInputOverlay = ({
handleMicBlur,
]);
// Memoize debug UI (only render when DEBUG_MODE is true)
// Memoize debug UI (always render for TV debugging)
const debugUI = useMemo(() => {
if (!DEBUG_MODE) return null;
return (
<div
style={{
@@ -1116,24 +1016,114 @@ const VoiceInputOverlay = ({
right: 10,
color: '#fff',
zIndex: 10000,
backgroundColor: 'rgba(0,0,0,0.7)',
padding: '10px',
borderRadius: '5px',
fontSize: '14px',
backgroundColor: 'rgba(0,0,0,0.9)',
padding: '16px',
borderRadius: '8px',
fontSize: '18px',
minWidth: '350px',
lineHeight: '1.6',
fontFamily: 'monospace',
}}
>
<div>Voice Version: {voiceVersion}</div>
<div>Input Mode: {voiceInputMode || 'None'}</div>
<div>Current Mode: {currentMode}</div>
<div
style={{
fontWeight: 'bold',
marginBottom: '12px',
borderBottom: '2px solid #fff',
paddingBottom: '8px',
fontSize: '20px',
}}
>
🔊 Voice Input Debug Info
</div>
<div>
Voice Version: <span style={{ color: '#4CAF50' }}>{voiceVersion}</span>
</div>
<div>
Input Mode: <span style={{ color: '#2196F3' }}>{voiceInputMode || 'None'}</span>
</div>
<div>
Current Mode: <span style={{ color: '#FF9800' }}>{currentMode}</span>
</div>
<div>
isSupported:{' '}
{isSupported ? (
<span style={{ color: '#4CAF50' }}> YES</span>
) : (
<span style={{ color: '#F44336' }}> NO</span>
)}
</div>
{voiceVersion === VOICE_VERSION.WEB_SPEECH && (
<>
<div>isListening: {isListening ? '🎤 YES' : '❌ NO'}</div>
<div>Interim: {interimText || 'N/A'}</div>
<div>
isListening:{' '}
{isListening ? (
<span style={{ color: '#4CAF50' }}>🎤 YES</span>
) : (
<span style={{ color: '#F44336' }}> NO</span>
)}
</div>
<div>
Interim:{' '}
<span style={{ color: '#9C27B0', wordBreak: 'break-all' }}>
"{interimText || 'N/A'}"
</span>
</div>
<div>
Ref Text:{' '}
<span style={{ color: '#9C27B0', wordBreak: 'break-all' }}>
"{interimTextRef.current || 'N/A'}"
</span>
</div>
<div>
Countdown:{' '}
<span style={{ color: '#FF5722', fontSize: '20px', fontWeight: 'bold' }}>
{countdown}s
</span>
</div>
<div>
STT Result:{' '}
<span style={{ color: '#607D8B', wordBreak: 'break-all' }}>
"{lastSTTText || 'None'}"
</span>
</div>
<div>
STT Time:{' '}
<span style={{ color: '#795548' }}>
{sttTimestamp ? new Date(sttTimestamp).toLocaleTimeString() : 'None'}
</span>
</div>
</>
)}
<div
style={{
marginTop: '12px',
fontSize: '14px',
color: '#ccc',
borderTop: '1px solid #666',
paddingTop: '8px',
}}
>
Timers:{' '}
{listeningTimerRef.current ? (
<span style={{ color: '#FF5722' }}> LISTENING ACTIVE</span>
) : (
<span style={{ color: '#666' }}>None</span>
)}
</div>
</div>
);
}, [voiceVersion, voiceInputMode, currentMode, isListening, interimText]);
}, [
voiceVersion,
voiceInputMode,
currentMode,
isListening,
interimText,
isSupported,
countdown,
lastSTTText,
sttTimestamp,
]);
return (
<TFullPopup