Javascript

Javascript30 - day23 Speech Synthesis

joy_lee 2021. 4. 27. 20:26

목표

SpeechSynthesis를 사용해 입력한 문장을 읽어주는 기능을 구현한다.

음성의 종류, 속도, 높이를 조절할 수 있도록 한다.

 

 

새롭게 알게된 것들

SpeechSynthesisUtterance

utterance = 발화
무엇을 말할 것인지를 의미함 / 6가지 속성을 가진다.

lang 언어
pitch 소리의 높이
rate 속도
text 읽을 내용
voice 음성의 종류
volume 소리의 크기

 

developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance

 

SpeechSynthesis

web speech API를 컨트롤 할 수 있는 인터페이스.

장치에서 사용가능한 합성 음성에 대한 정보를 검색하고, 음성을 일시중지 및 기타 명령 수행하는데 사용함

 

사용가능한 속성들

속성 이름 반환값 true인 경우
paused boolean 일시정지 상태
pending boolean 대기열에 아직 읽지 않은 메시지가 있는 경우
speaking boolean 현재 음성이 말하는 중
(일시정지상태 포함)

메소드

메소드 이름 기능
cancel() 발화 대기열에서 모든 발화 제거
(말하던 음성도 멈추고, utterance 제거됨)
getVoice() 현재 장치에서 사용가능한 모든 음성을 나타내는 개체 목록 반환
pause() 일시정지한다
resume() 일시정지상태에서 다시 음성을 시작한다
speak() utterance를 발화 대기열에 추가하고, 재생한다.

사용 방법

speak() 함수는 SpeechSynthesisUtterance를 인자로 받는다.

이미 재생중인 utterance가 없고, speechSynthesis가 pause되어있지 않으면 요청된 utterance는 바로 재생된다.

이미 재생중인 utterance가 있거나 pause상태라면 바로 재생되지 않고 queue에 저장되었다가 후에 재생된다.

 

getVoices()는 SpeechSynthesis가 사용할 수 있는 SpeechSynthesisVoice 목록을 반환한다.

사용할 수 있는 voice 목록이 초기화되면 voiceschanged 이벤트가 호출되는데, 이 이벤트가 호출되어야 getVoices 이벤트가 정상적으로 사용할 수 있는 voice 목록을 리턴한다.

 

SpeechSynthesisVoice

getVoices()를 통해 가지고 온 SpeechSynthesisVoice가 가지는 속성은 아래와 같다.

default boolean 현재 응용 프로그램의 기본 음성 = true
lang language tag 언어의 나라
localService boolean local speech synthesizer service에 의해 만들어진 음성 = true
name string 목소리의 이름(한 나라의 언어에도 여러 성우가 있을 수 있다)
voiceURI type of URI URI 유형 및 위치

참고 사이트)

developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis

blog.seulgi.kim/2016/08/web-speechsynthesis-tts-api.html

 

 

 

내가 작성한 코드

 

1
2
3
4
5
6
7
function setVoiceList() {
    voices = window.SpeechSynthesis.getVoices();
}
setVoiceList();
if (window.speechSynthesis.onvoicechanged !== undefined) {
    window.speechSynthesis.onvoiceschanged = setVoiceList;
}
cs

인터넷에서 찾아서 getVoices()를 실행했지만 아무것도 return되지 않았다.

 

 

영상을 보며 만든 코드

 

1
2
3
4
5
6
7
8
const msg = new SpeechSynthesisUtterance();
let voices = [];
const voicesDropdown = document.querySelector('[name="voice"]');
const options = document.querySelectorAll('[type="range"], [name="text"]');
const speakButton = document.querySelector('#speak');
const stopButton = document.querySelector('#stop');
 
msg.text = document.querySelector('[name="text"]').value;
cs

body의 여러가지 버튼과 range input을 가져온다.

msg라는 이름으로 만든 SpeechSynthesisUtterance에 text를 넣어준다.

 

 

1
2
3
4
5
6
7
8
9
function populateVoices() {
    voices = this.getVoices();
    voicesDropdown.innerHTML = voices
    // .filter(voice => voice.lang.includes('en'))
       .map(voice => `<option value="${voice.name}">${voice.name} (${voice.lang})</option>`)
       .join('');
}
 
speechSynthesis.addEventListener('voiceschanged', populateVoices);
cs

사용할 수 있는 voice 목록이 초기화되어 voiceschanged 이벤트를 호출하면, populateVoices를 통해 speechSynthesisVoice들을 가져오고, voices에 넣어준다.

voices를 map을 통해 speechsynthesisVoice의 name, lang을 가져와 voice를 고르는 select tag안에 넣어준다(voiceDropdown에).

filter를 통해 원하는 언어의 음성만 가져올 수도 있다.

 

 

1
2
3
4
5
6
7
8
9
function setVoice() {
    // msg.voice = this.value 불가능
    // e.target.value 를 사용할 수 있지만 이미 option 태그에 value를 사용해서 이름을 적어놔서 this.value로 쉽게 voice name을 가져올 수 있다.
    msg.voice = voices.find(voice => voice.name === this.value);
    toggle();
}
 
 
voicesDropdown.addEventListener('change', setVoice);
cs

voicesDropdown에서 음성을 고르면 setVoice를 통해 msg utterance의 voice를 바꿔준다.

dripdown에서 가져오는 option의 value값은 speechSynthesisvoice가 아닌, voice의 이름일 뿐이다.

voices에 저장한 Utterance의 voice에 사용자가 고른 speechSynthesisVoice를 대입해준다.

toggle을 통해 음성이 바로 재생되도록 한다.

 

 

1
2
3
4
5
6
7
8
9
10
function toggle(startOver = true) {
    // 음성이 나오고 있을 때 바꿔도 바로 시작하도록 한다
    speechSynthesis.cancel();
    if (startOver) {
        speechSynthesis.speak(msg);
    }
}
 
speakButton.addEventListener('click', toggle);
stopButton.addEventListener('click', () => toggle(false));
cs

toggle 함수는 음성을 재생하거나, 멈추는 기능을 한다.

speechSynthesis.cancel()을 통해 재생되고 있는 음성이 있다면 멈추고, 바로 시작되도록 한다.

startOver를 통해 speak를 실행할지 결정한다.

굳이 따로 음성을 멈추는 함수를 만들지 않고, startOver = false인 경우엔 재생되지 않도록 했기 때문에 stopButton에도 동일한 함수로 멈추는 기능을 구현할 수 있다.

 

toggle에 아무런 변수가 입력되지 않는 경우는 startOver=true로 초기화했기 때문에 toggle이라고만 써도 된다.

하지만 false를 입력하고 싶은 경우엔, toggle(false)만 작성하면 제대로 기능을 하지 못한다.

arrow function을 사용해 () => toggle(false)로 작성해주어야 startOver = false로 입력할 수 있다.

 

1
2
3
4
5
6
function setOption() {
    msg[this.name= this.value;
    toggle();
}
 
options.forEach(option => option.addEventListener('change', setOption));
cs

options에는 rate, pitch, text를 입력받는 input들이 있다.

change event가 생성될 때마다 setOption을 통해 msg utterance의 속성을 바꿔주도록 작성한다.

input의 name을 utterance의 속성과 똑같이 적어주었으므로, msg[this.name]을 통해 속성에 쉽게 접근할 수 있다.

this.value로 선택한 값을 입력한후, toggle()을 실행해 바로 음성이 재생되도록 한다.

 

 

새롭게 깨달은 점

speechsynthesis를 처음 사용해봐서 검색만으로는 기능을 구현하기 힘들었다. 검색할 때 예시를 더 잘 찾아보는게 좋을 것 같다.

.map().join()은 유용하게 쓰이는 경우가 많다.

한 가지 함수로 상반된 기능이나(toggle) 비슷한 기능(setOption)을 할 수 있도록 작성하면 더 간단하게 코드를 작성할 수 있다. 같이 구현할 수 있는 것인지 생각해보고 코드를 작성하는 것이 좋을 것 같다.

(혼자 코드를 작성했다면 voiceStart, voiceStop, setRate, setPicth...등으로 여러개를 만들었을 것 같다.)

Object의 속성에 접근할 때, 속성의 이름이 변수를 통해 전달된다면 Object[this.name]으로 []안에 변수를 넣으면 사용가능하다