ํธ์ง๋ฅผ ์ฝ๊ธฐ ์ด๋ ค์ด ์ฌ์ฉ์(ํธ๋ํฐ์ ๋ณผ ์ ์๋ ์ํฉ์ด๊ฑฐ๋ ๋ ธ์, ์ฅ์ ๋ฑ) ๋ค๋ Sendy(์๋) ์๋น์ค๋ฅผ ์ด์ฉํ ์ ์๋๋ก ํ ์คํธ โก๏ธ ์์ฑ์ผ๋ก ๋ณํํด์ฃผ๋ TTS ๊ธฐ๋ฅ์ ๋์ ํ๋ค.
์นด์นด์ค๋ ๊ตฌ๊ธ์์ ์ ๊ณตํ๋ TTS ์๋น์ค์ ๊ฒฝ์ฐ, ์์ฑ์ด ์ข ๋ ์์ฐ์ค๋ฝ๊ณ ํธํ์ฑ์ด ์ข๋ค๋ ์ฅ์ ์ด ์์ง๋ง ์ ๋ฃ ์๋น์ค์ธ ๊ด๊ณ๋ก ๋ฌด๋ฃ๋ก ์ฌ์ฉํ ์ ์๋ Window Web Speech API
์ ์ด์ฉํด TTS๋ฅผ ๊ตฌํํ์๋ค.
์์ฝ๊ฒ๋ ๋ฒจ๋ก๊ทธ์ ๋์์์ด ์ ๋ก๋๋์ง ์์ GIF๋ก ๋์ฒดํ๋ค. ํน ๊ถ๊ธํ ์ฌ๋์ด ์๋ค๋ฉด, ์ ์๋ ์ฌ์ดํธ๋ฅผ ๋ฐฉ๋ฌธํด์ฃผ์ธ์ ! ๐ฎ
Web Speech API ๋ ์น ์ฑ์์ ์์ฑ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ API๋ก, SpeechSynthesis(ํ ์คํธ ์์ฑ ๋ณํ) ๋ฐ SpeechRecognition(๋น๋๊ธฐ ์์ฑ ์ธ์)์ ๋ ๋ถ๋ถ์ผ๋ก ๊ตฌ์ฑ๋๋ค.
SpeechSynthesis
์ปจํธ๋กค๋ฌ ์ญํ ์ ํ๋ Interface๋ก ๋๋ฐ์ด์ค์์ ์ฌ์ฉ๊ฐ๋ฅํ ๋ชฉ์๋ฆฌ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ ์์ฑ์ ์คํผ์นํ๊ณ ์ ์งํ ์ ์๋ค.
์ฃผ์ ๋ฉ์๋ | ๊ธฐ๋ฅ |
---|---|
getVoices() | ํ์ฌ ์ฌ์ฉํ๊ณ ์๋ ๋๋ฐ์ด์ค์ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชฉ์๋ฆฌ๋ฅผ ๊ฐ์ ธ์จ๋ค. |
speak() | utterance(speech)๋ฅผ utterrace queue์ ์์ ์์ฑ์ผ๋ก ์ฝ์ ์ ์๊ฒ ํ๋ค. |
SpeechSynthesisUtterance
speech ์์ฒญ์ ํํํ๋ interface๋ก ๋ฌด์์ ์ด๋ป๊ฒ ์ฝ์์ง์ ๋ํ ์ ๋ณด๋ฅผ ํฌํจํ๊ณ ์๋ค.
์ฃผ์ ํ๋กํผํฐ | ์ค์ |
---|---|
lang | ko-KR ์ ๊ฐ์ด ์ฌ์ฉํ ์ธ์ด์ ๋ํ ์ค์ ์ผ๋ก, ๊ธฐ๋ณธ๊ฐ์ lang๊ฐ ๋๋ user-agent-default ๊ฐ์ด๋ค. |
pitch | ์คํผ์น์ pitch ๊ฐ์ผ๋ก, ๊ธฐ๋ณธ๊ฐ์ 1์ด๋ฉฐ 0 ~ 2๊น์ง ์กฐ์ ๊ฐ๋ฅํ๋ค. |
rate | ์คํผ์น์ ์๋๊ฐ์ผ๋ก ๊ธฐ๋ณธ๊ฐ์ 1์ด๋ค. 0.1์์ 10๊น์ง ์กฐ์ ํ ์ ์์ผ๋ฉฐ, ์ซ์๊ฐ ํด์๋ก ์๋๊ฐ ๋น ๋ฅด๋ค. |
voice | ์คํผ์น ๋ชฉ์๋ฆฌ ์ค์ ๊ฐ์ผ๋ก, ๋ฏธ์ค์ ์ utterance ์ธ์ด์ default ์ธ์ด ๋ชฉ์๋ฆฌ๊ฐ ๊ธฐ๋ณธ๊ฐ์ด ๋๋ค. |
window.speechSynthesis.getVoices()
๋ฉ์๋์์ ๋๋ ์ด๊ฐ ๋ฐ์ํด ์ต์ด ์ฌ์ฉ์ ๋ช ์ด ํ TTS ์์ฑ์ด ๋์ค๋๊ฒ ํ์ธ๋์๋ค. ๋ฐ๋ผ์ ํ์ด์ง ์ด๊ธฐ ๋ ๋๋ง ์ ํด๋น ๋ฉ์๋๋ ํจ๊ป ๋ ๋๋ง ๋ ์ ์๋๋ก useEffect
์ ๋ฃ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก ๋๋ ์ด ์์ด TTS ๊ธฐ๋ฅ์ ์ฌ์ฉ ํ ์ ์๊ฒ ๋์๋ค. //์์ฑ ๋ณํ ๋ชฉ์๋ฆฌ preload
useEffect(() => {
window.speechSynthesis.getVoices();
}, []);
export const getSpeech = (text) => {
let voices = [];
//๋๋ฐ์ด์ค์ ๋ด์ฅ๋ voice๋ฅผ ๊ฐ์ ธ์จ๋ค.
const setVoiceList = () => {
voices = window.speechSynthesis.getVoices();
};
setVoiceList();
if (window.speechSynthesis.onvoiceschanged !== undefined) {
//voice list์ ๋ณ๊ฒฝ๋์๋, voice๋ฅผ ๋ค์ ๊ฐ์ ธ์จ๋ค.
window.speechSynthesis.onvoiceschanged = setVoiceList;
}
const speech = (txt) => {
const lang = "ko-KR";
const utterThis = new SpeechSynthesisUtterance(txt);
//rate : speech ์๋ ์กฐ์ (๊ธฐ๋ณธ๊ฐ 1 / ์กฐ์ 0.1 ~ 10 -> ์ซ์๊ฐ ํด์๋ก ์๋๊ฐ ๋น ๋ฆ )
const rate = 0.8;
utterThis.lang = lang;
utterThis.rate = rate;
/* ํ๊ตญ์ด vocie ์ฐพ๊ธฐ
๋๋ฐ์ด์ค ๋ณ๋ก ํ๊ตญ์ด๋ ko-KR ๋๋ ko_KR๋ก voice๊ฐ ์ ์๋์ด ์๋ค.
*/
const kor_voice = voices.find(
(elem) => elem.lang === lang || elem.lang === lang.replace("-", "_")
);
//ํ๊ตญ์ด voice๊ฐ ์๋ค๋ฉด ? utterance์ ๋ชฉ์๋ฆฌ๋ฅผ ์ค์ ํ๋ค : ๋ฆฌํดํ์ฌ ๋ชฉ์๋ฆฌ๊ฐ ๋์ค์ง ์๋๋ก ํ๋ค.
if (kor_voice) {
utterThis.voice = kor_voice;
} else {
return;
}
//utterance๋ฅผ ์ฌ์(speak)ํ๋ค.
window.speechSynthesis.speak(utterThis);
};
speech(text);
};
export const pauseSpeech = () => {
window.speechSynthesis.cancel();
};
import { getSpeech, pauseSpeech } from "./TTS";
...
//์์ฑ value ์ํ
const voiceValue = `${data.content}`;
//์์ฑtts speech ๋ฒํผ
const handleSpeechButton = () => {
getSpeech(voiceValue);
};
//์์ฑtts pause ๋ฒํผ
const handlePauseButton = () => {
getSpeech(pauseSpeech());
};
//์์ฑ ๋ณํ ๋ชฉ์๋ฆฌ preload
useEffect(() => {
window.speechSynthesis.getVoices();
}, []);
return (
<>
...
<AiOutlineSound onClick={handleSpeechButton} />
<HiPause onClick={handlePauseButton} />
...
</>
)
}
tts-react
, ํน์ react-speech-kit
๋ฅผ ํตํด ๋ฆฌํฉํ ๋งํด ์ฌํญ๋ค์ ๊ฐ์ ํ ์์ ์ด๋ค.