관련지식
javascript, TTS, speechSynthesis, SpeechSynthesisUtterance

TTS 라는 기술이 있습니다. 프로그램이 텍스트를 읽어주는 것으로, 과거에는 운전중인 사람을 위해 책을 읽어주거나 시각장애인을 위해 웹텍스트를 읽어주는 리더기에서 사용되었습니다. 이 TTS 라는 기술은 절대 쉬운 기술이 아닙니다. 단순하게 한글자씩 녹음을 해서 연속재생을 한다면 옛날 영화에서나 보던 기계가 말하는 느낌이 나기 때문입니다. 따라서 단어나 문장에 맞게 톤, 억양, 연음 등을 세심하게 조정되어야 하는 노하우가 필요하고, 그것이 곧 프로그램의 수준이 되었습니다. 하지만 지금은 브라우저에서 별도의 플러그인 없이도 상당한 고급수준의 TTS 기능을 사용할수 있게 되었습니다.(물론 모든 브라우저가 지원되는것은 아닙니다.)

Speech Synthesis API

이 기능을 사용하려면 먼저 브라우저에서 지원되는지 확인해야 합니다.

유명 브라우저는 다수 지원하고 있습니다. 그러나 브라우저의 버그까지는 확인이 안되네요. 버그 때문에 이상하게 동작될수도 있습니다. 일단 그러한 부분은 넘어가겠습니다.

먼저 데모를 보겠습니다. 아래 데모에 들어가면 원하는 언어를 셀렉트 박스에서 선택하고, 해당 언어를 텍스트박스에 입력후 엔터를 치면 텍스트를 읽는 음성을 들을수 있습니다. 따라서 브라우저에서 지원하지 않는 언어는 사용할수 없습니다.

데모
http://mdn.github.io/web-speech-api/speak-easy-synthesis/

목소리 가져오기

  1. var voices = [];
  2. function setVoiceList() {
  3. voices = window.speechSynthesis.getVoices();
  4. }
  5. setVoiceList();
  6. if (window.speechSynthesis.onvoiceschanged !== undefined) {
  7. window.speechSynthesis.onvoiceschanged = setVoiceList;
  8. }

데모와 mozilla에 있는 소스를 약간만 수정한 형태입니다. 이부분에 대해선 브라우저 별로 기능이 확실히 차이가 생기는것 같은데, 만약 위와 같은 형태가 아니라 ‘window.speechSynthesis.getVoices()’ 을 바로 호출 하는 형태로 사용한다면 조금 당황스러운 상황을 겪으실수도 있습니다.

speech() 함수 생성

이번엔 음성 재생 부분을 구현할 차례입니다. 브라우저에서 window.speechSynthesis을 제공해야만 사용이 가능하므로 브라우저 체크가 필요합니다. 크롬이나 파이어폭스는 모바일 버전에서도 지원하지만 OS 내장브라우저는 거의 지원하지 못합니다. 따라서 네이버 브라우저나 SNS 프로그램 내에서 웹페이지를 보여주는 웹뷰에선 지원 못할것입니다.

  1. if(!window.speechSynthesis) {
  2. alert("음성 재생을 지원하지 않는 브라우저입니다. 크롬, 파이어폭스 등의 최신 브라우저를 이용하세요");
  3. return;
  4. }

음성 읽기에 대한 정보를 담은 객체를 생성해야 합니다. 영어 텍스트의 경우 언어셋을 한글로 선택해도 간단한 단어는 어느 정도 읽히지만(콩글리쉬 형태로 읽힙니다), 한글 텍스트를 영어로 선택할 경우엔 외국인이 한국말 하는것 같은 발음을 합니다.

  1. var lang = 'ko-KR';
  2. var utterThis = new SpeechSynthesisUtterance(txt);
  3. var voiceFound = false;
  4. for(var i = 0; i < voices.length ; i++) {
  5. if(voices[i].lang.indexOf(lang) >= 0 || voices[i].lang.indexOf(lang.replace('-', '_')) >= 0) {
  6. utterThis.voice = voices[i];
  7. voiceFound = true;
  8. }
  9. }
  10. if(!voiceFound) {
  11. alert('voice not found');
  12. return;
  13. }
  14. utterThis.lang = lang;
  15. utterThis.pitch = 1;
  16. utterThis.rate = 1; //속도

말하는 속도나 목소리의 높이도 변경 가능하지만 변경하지 않는것이 가장 자연스럽습니다. 위 코드에서 ‘ko-KR’을 ‘ko_KR’ 로 바꿔서 비교하는 이유는 모바일 브라우저에서 언어셋이 ‘ko_KR’ 인 경우가 있기 때문입니다.

정리

기능을 만들고 계속 사용 해본 결과 윈도우의 크롬에서는 어느정도 안정적인 사용이 가능했지만, 안드로이드의 크롬이나 파이어폭스에서는 재생이 안되는 증상이 종종 발생했습니다. 실제로 검색을 해보면 몇년전부터 다양한 오류가 접수되고 있습니다. 브라우저를 완전히 종료후에 다시 실행하면 재생이 가능하지만, 크롬에서 재생이 안될때 파이어폭스에서도 재생이 안되는 것을 보면 OS의 오류 가능성도 있을것 같습니다.

그러나 플러그인 없이 무료로 높은 수준의 TTS 기능을 사용할수 있다는 것은 상당히 매력적입니다. 점점 더 좋아지겠죠?

최종 샘플

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>CSS Template</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. </head>
  8. <body>
  9. <input id="code_html" type="text" value="삼겹살" autocomplete="off" readonly="">
  10. <input type="button" value="말하기"/>
  11. <input id="code_direct" type="text" value="족발" autocomplete="off" readonly="">
  12. <input type="button" value="말하기"/>
  13. <input id="code_reddit" type="text" value="수육" autocomplete="off" readonly="">
  14. <input type="button" value="말하기"/>
  15. <script>
  16. var voices = [];
  17. function setVoiceList() {
  18. voices = window.speechSynthesis.getVoices();
  19. }
  20. setVoiceList();
  21. if (window.speechSynthesis.onvoiceschanged !== undefined) {
  22. window.speechSynthesis.onvoiceschanged = setVoiceList;
  23. }
  24. function speech(txt) {
  25. if(!window.speechSynthesis) {
  26. alert("음성 재생을 지원하지 않는 브라우저입니다. 크롬, 파이어폭스 등의 최신 브라우저를 이용하세요");
  27. return;
  28. }
  29. var lang = 'ko-KR';
  30. var utterThis = new SpeechSynthesisUtterance(txt);
  31. utterThis.onend = function (event) {
  32. console.log('end');
  33. };
  34. utterThis.onerror = function(event) {
  35. console.log('error', event);
  36. };
  37. var voiceFound = false;
  38. for(var i = 0; i < voices.length ; i++) {
  39. if(voices[i].lang.indexOf(lang) >= 0 || voices[i].lang.indexOf(lang.replace('-', '_')) >= 0) {
  40. utterThis.voice = voices[i];
  41. voiceFound = true;
  42. }
  43. }
  44. if(!voiceFound) {
  45. alert('voice not found');
  46. return;
  47. }
  48. utterThis.lang = lang;
  49. utterThis.pitch = 1;
  50. utterThis.rate = 1; //속도
  51. window.speechSynthesis.speak(utterThis);
  52. }
  53. document.addEventListener("click", function(e) {
  54. var t = e.target;
  55. var input = t.previousElementSibling;
  56. speech(input.value);
  57. });
  58. </script>
  59. </body>
  60. </html>

참고 자료
https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API/Using_the_Web_Speech_API