sitelink1 | https://developer.mozilla.org/en-US/docs...hSynthesis |
---|---|
sitelink2 | https://developer.mozilla.org/en-US/docs...Speech_API |
sitelink3 | |
sitelink4 | |
extra_vars5 | |
extra_vars6 |
- 기술 키워드
- SpeechSynthesis(Text-to-Speech)
- SpeechRecognition(Asynchronous Speech Recognition.)
- 샘플 다운로드 : stt_tts.7z
* 2025.09.08 STT_TTS 샘플 추가 (from ChatGPT)
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>STT + TTS Sample (webkitSpeechRecognition + speechSynthesis)</title>
<style>
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans KR", "Apple SD Gothic Neo", "Malgun Gothic", sans-serif; max-width: 900px; margin: 28px auto; padding: 0 18px; color:#111; }
h1 { font-size: 1.4rem; margin-bottom: 8px; }
.card { border-radius: 12px; padding: 16px; box-shadow: 0 6px 18px rgba(0,0,0,0.06); margin-bottom: 16px; background: #fff; }
label { display:block; margin:8px 0 4px; font-weight:600; }
button { padding: 8px 12px; margin-right:8px; border-radius:8px; border:1px solid #ddd; background:#f7f7f7; cursor:pointer; }
button.primary { background:#2563eb; color:white; border-color:#2563eb; }
textarea { width:100%; min-height:90px; padding:8px; border-radius:8px; border:1px solid #ddd; resize:vertical; }
pre { white-space:pre-wrap; word-break:break-word; background:#f8f9fb; padding:12px; border-radius:8px; border:1px solid #eee; }
.controls { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.small { font-size:0.9rem; color:#555; }
</style>
</head>
<body>
<h1>브라우저 STT + TTS 샘플</h1>
<div class="card">
<strong>설명</strong>
<p class="small">왼쪽은 마이크로부터 음성을 받아 텍스트로 변환(STT), 오른쪽은 텍스트를 음성으로 재생(TTS)합니다. Chrome 기반 브라우저에서 웹 API를 사용합니다.</p>
</div>
<div class="card" id="sttCard">
<h2>▶ STT (Speech-to-Text)</h2>
<div class="controls">
<button id="startRec" class="primary">녹음 시작</button>
<button id="stopRec">중지</button>
<label style="margin-left:8px;">
<input type="checkbox" id="continuous" checked /> 연속 인식
</label>
<label>
<input type="checkbox" id="interim" checked /> 중간 결과 보여주기(interim)
</label>
</div>
<label>인식 결과 (최종 + 중간):</label>
<pre id="transcript" aria-live="polite">—</pre>
<label>상태:</label>
<div id="sttStatus" class="small">초기화 대기</div>
<div style="margin-top:10px" class="small">
<strong>참고:</strong> 브라우저 권한(마이크 허용)이 필요합니다. Safari/Firefox/Edge는 구현 차이가 있을 수 있습니다.
</div>
</div>
<div class="card" id="ttsCard">
<h2>▶ TTS (Text-to-Speech)</h2>
<label for="ttsText">읽을 텍스트:</label>
<textarea id="ttsText">안녕하세요. 이것은 브라우저 기반 TTS 테스트입니다.</textarea>
<label>음성 선택:</label>
<select id="voiceSelect"></select>
<div class="controls" style="margin-top:8px">
<label>속도: <input id="rate" type="range" min="0.5" max="2" value="1" step="0.1"></label>
<label>높낮이: <input id="pitch" type="range" min="0" max="2" value="1" step="0.1"></label>
<label>볼륨: <input id="volume" type="range" min="0" max="1" value="1" step="0.1"></label>
</div>
<div style="margin-top:10px">
<button id="speakBtn" class="primary">읽기</button>
<button id="cancelBtn">중지</button>
</div>
<div style="margin-top:12px" class="small">
<strong>브라우저 음성 목록:</strong> 아래에서 음성을 선택하세요. (한국어 음성이 없으면 시스템/브라우저에 따라 보이지 않을 수 있음)
</div>
</div>
<script>
// --------------------
// STT (webkitSpeechRecognition) 설정
// --------------------
const transcriptEl = document.getElementById('transcript');
const sttStatus = document.getElementById('sttStatus');
const startRecBtn = document.getElementById('startRec');
const stopRecBtn = document.getElementById('stopRec');
const continuousCheckbox = document.getElementById('continuous');
const interimCheckbox = document.getElementById('interim');
// 브라우저 호환 처리: 표준 SpeechRecognition 이 있으면 사용, 없으면 webkitSpeechRecognition
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
let recognizer = null;
let finalTranscript = '';
if (!SpeechRecognition) {
sttStatus.textContent = '이 브라우저는 SpeechRecognition API를 지원하지 않습니다.';
startRecBtn.disabled = true;
stopRecBtn.disabled = true;
} else {
recognizer = new SpeechRecognition();
recognizer.lang = 'ko-KR'; // 기본 한국어
recognizer.interimResults = true;
recognizer.maxAlternatives = 1;
recognizer.continuous = true;
recognizer.onstart = () => {
sttStatus.textContent = '음성 인식 시작 (마이크 사용 중) — 말하세요.';
};
recognizer.onerror = (e) => {
sttStatus.textContent = '오류: ' + (e.error || JSON.stringify(e));
};
recognizer.onend = () => {
sttStatus.textContent = '음성 인식이 중지되었습니다.';
};
recognizer.onresult = (event) => {
let interim = '';
for (let i = event.resultIndex; i < event.results.length; ++i) {
const res = event.results[i];
if (res.isFinal) {
finalTranscript += res[0].transcript;
} else {
interim += res[0].transcript;
}
}
transcriptEl.textContent = (finalTranscript + (interimCheckbox.checked ? ('\n(중간) ' + interim) : ''));
};
}
startRecBtn.addEventListener('click', async () => {
if (!recognizer) return;
// 사용자 미디어 권한을 명시적으로 요청해 상태를 빨리 알 수 있게 함
try {
await navigator.mediaDevices.getUserMedia({ audio: true });
} catch (err) {
sttStatus.textContent = '마이크 접근 거부됨: ' + err.message;
return;
}
finalTranscript = '';
transcriptEl.textContent = '... listening ...';
recognizer.interimResults = interimCheckbox.checked;
recognizer.continuous = continuousCheckbox.checked;
recognizer.lang = 'ko-KR';
recognizer.start();
});
stopRecBtn.addEventListener('click', () => {
if (!recognizer) return;
recognizer.stop();
});
// --------------------
// TTS (speechSynthesis) 설정
// --------------------
const voiceSelect = document.getElementById('voiceSelect');
const ttsText = document.getElementById('ttsText');
const speakBtn = document.getElementById('speakBtn');
const cancelBtn = document.getElementById('cancelBtn');
const rateInput = document.getElementById('rate');
const pitchInput = document.getElementById('pitch');
const volumeInput = document.getElementById('volume');
function populateVoices() {
const voices = speechSynthesis.getVoices();
voiceSelect.innerHTML = '';
voices.forEach((v, i) => {
const opt = document.createElement('option');
opt.value = i;
opt.textContent = `${v.name} — ${v.lang}${v.default ? ' — default' : ''}`;
voiceSelect.appendChild(opt);
});
if (voices.length === 0) {
voiceSelect.innerHTML = '<option>사용 가능한 음성이 없습니다.</option>';
}
}
// voiceschanged 이벤트는 음성 로딩이 완료되면 발생함
window.speechSynthesis.onvoiceschanged = populateVoices;
populateVoices();
speakBtn.addEventListener('click', () => {
if (!('speechSynthesis' in window)) {
alert('이 브라우저는 speechSynthesis를 지원하지 않습니다.');
return;
}
const text = ttsText.value.trim();
if (!text) {
alert('읽을 텍스트를 입력하세요.');
return;
}
const utter = new SpeechSynthesisUtterance(text);
const voices = speechSynthesis.getVoices();
const idx = parseInt(voiceSelect.value, 10);
if (!isNaN(idx) && voices[idx]) utter.voice = voices[idx];
utter.rate = parseFloat(rateInput.value);
utter.pitch = parseFloat(pitchInput.value);
utter.volume = parseFloat(volumeInput.value);
utter.onstart = () => { speakBtn.disabled = true; sttStatus.textContent = 'TTS 재생 중...'; };
utter.onend = () => { speakBtn.disabled = false; sttStatus.textContent = 'TTS 재생 완료.'; };
utter.onerror = (e) => { speakBtn.disabled = false; sttStatus.textContent = 'TTS 오류: ' + e.error; };
// 재생 전에 큐 비우기(원하면 주석 처리 가능)
window.speechSynthesis.cancel();
window.speechSynthesis.speak(utter);
});
cancelBtn.addEventListener('click', () => {
window.speechSynthesis.cancel();
sttStatus.textContent = 'TTS 재생 취소됨.';
speakBtn.disabled = false;
});
// 키보드 단축(예: Ctrl+Enter -> 읽기)
ttsText.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
speakBtn.click();
}
});
</script>
</body>
</html>
* 아래의 예시는 2025.04.14 에 Copilot 이 만들어준 STT 의 샘플인데 너무 잘 작동한다.
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Speech API Example</title>
</head>
<body>
<h1>Web Speech API Example</h1>
<button id="start-recognition">Start Recognition</button>
<p id="output">Speech will appear here...</p>
<script>
const startButton = document.getElementById('start-recognition');
const output = document.getElementById('output');
// Check if the browser supports Web Speech API
if (!('webkitSpeechRecognition' in window)) {
output.textContent = 'Web Speech API is not supported in this browser.';
} else {
const recognition = new webkitSpeechRecognition();
recognition.lang = 'ko-KR'; // Set language
recognition.interimResults = false; // Show only final results
recognition.maxAlternatives = 1;
startButton.addEventListener('click', () => {
recognition.start();
output.textContent = 'Listening...';
});
recognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
output.textContent = `You said: ${transcript}`;
};
recognition.onerror = (event) => {
output.textContent = `Error occurred: ${event.error}`;
};
recognition.onend = () => {
output.textContent += ' (Recognition ended)';
};
}
</script>