-
🎤Native Speech Recognition📢Web/JS30 2020. 11. 23. 19:23반응형
브라우저에 이렇게 다양한 기능이 내장되어있는 줄은 몰랐다.
오늘은 Speech Recognition이라는 글로벌 변수에 대해 알아보자.
간단히 말하자면, 음성 인식 기능이다.
0. 일단, 이 기능을 수행하려면 서버가 필요하다.
그냥 로컬 파일로 실행했을 때는 실행되지 않는 기능이므로
localhost로 간단한 서버를 만들어 실행해보도록 하자.
1. 우선, 브라우저마다 변수 이름이 다르기 때문에 다음과 같이 설정해준다.
파이어폭스의 경우 window.SpeechRecognition이지만 크롬의 경우에는 window.webkitSpeechRecognition이다.
window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
2. SpeechRecognition 객체를 하나 만들고, interimResults 속성을 true로 설정해준다.
interimResults는 번역하면 대충 '중간 결과, 임시 결과' 정도로 해석할 수 있는데,
만약 이 속성이 false일 경우 사용자가 한 문장을 모두 말하고 쉴 때 브라우저가 음성을 인식하고 출력한다.
즉, 한 문장씩 띄어서 말해야 한다.
한 문장 말하고 쉬고, 브라우저가 좌르륵 인식하고 출력하고
그 다음 한 문장 말하고 쉬면 브라우저가 좌르륵 인식하고 출력하고 이런 식.
우리는 우리가 말하는 것과 동시에 브라우저가 음성을 인식하여 출력하기를 원하기 때문에
interimResults를 true로 만들어준다.
const recognition = new SpeechRecognition(); recognition.interimResults = true;
3. 위의 사진에서 볼 수 있듯이, 우리가 말한 음성을 인식하여 화면에 글자로 출력할 것이기 때문에
p 태그를 만들어서 'words'라는 공간(위 사진에서 노트 모양의 div 공간)에다가 넣어줄 것이다.
let p = document.createElement('p'); const words = document.querySelector('.words'); words.appendChild(p);
4. 그 다음은 앞에서 만들어준 SpeechRecognition 변수인 'recognition'에 이벤트를 붙여줘야 한다.
SpeechRecognition 서비스가 결과를 return할 때 발생하는 이벤트인 'result' 이벤트를 사용하면 된다.
이 이벤트를 console.log로 출력해보자.
recognition.addEventListener('result', e => { console.log(e); }
SpeechRecognitionEvent 객체는 여러 겹으로 둘둘 둘러쌓여있다.
여러 개 중 하나를 골라, results라는 property를 살펴보자.
5. 참고로, index가 달려있고 해서 배열과 비슷한 형태를 띠고 있는데 사실 배열은 아니다.
prototype을 살펴보면 map이나 forEach같은 메서드가 없다는 것을 알 수 있다.
다루기 편하게 Array.from을 사용해서 배열로 변환해주면 된다.
recognition.addEventListener('result', e => { const transcript = Array.from(e.results) .map(result => result[0]) .map(result => result.transcript) .join(''); });
둘둘 둘러쌓여있는 객체를 배열 메서드를 사용해서 위와 같이 풀어줄 수 있다.
Array.from 이후에는 우리가 말한 단어들이 배열의 형태로 존재하기 때문에
map 메서드를 통해서 SpeechRecognitionResult 등의 껍질을 벗겨주고
그 안에 있는 transcript를 꺼내주면 된다.
join은 배열의 형태로 존재하는 문자열들을 하나로 이어붙이기 위함.
내부 변수들을 살펴보면,
transcript는 우리의 음성을 인식한 내용이고,
confidence는 정확도라고 생각하면 된다.
isFinal은 우리가 한 문장을 끝냈는지를 의미한다.
참고로 SpeechRecognition의 result 이벤트는 한 문장을 모두 말할 때까지 유지되었다가,
문장이 끝났다고 생각되면 이벤트가 종료된다.
6. 그래서 생기는 문제점 중 하나가, 한 번 말을 하고 나서 공백을 가지면
문장이 끝났다고 생각되어 result 이벤트가 종료된다.
그래서, 쉬었다가 다시 말해도 이미 result 이벤트가 종료된 상태이기 때문에 음성을 인식하지 않는다는 것.
이를 해결하기 위해서는, 음성 인식이 종료되는 시점에 다시 음성 인식을 시작해줘야 한다.
recognition.addEventListener('end', recognition.start);
요렇게 해주면, recognition이 종료되었을 때 다시 recognition.start를 통해 음성 인식을 시작하게 된다.
그래서, 말을 하는 중간중간에 휴지기를 가져도 끊이지 않고 음성 인식이 가능해진다.
7. 맨 처음에 페이지가 로드될 때 음성 인식을 시작하기 위해 recognition.start()를 호출해주면 된다.
recognition.start();
참고로 이 화면에서 배열의 형태로 문자열이 존재하는 이유는
아직 join('')을 호출하기 전에 사진을 찍었기 때문..
8. 이제, 인식된 음성을 p 태그의 content로 만들어서 화면에 출력하면 된다(textContent 사용).
recognition.addEventListener('result', e => { const transcript = Array.from(e.results) .map(result => result[0]) .map(result => result.transcript) .join(''); p.textContent = transcript; }
근데, 이렇게 하면 문제가 하나 있는데
여러 문장을 말하게 될 경우, 그 문장의 수만큼 p 태그가 생기는 게 아니라
처음에 생성된 p 태그의 내용이 변할 뿐이라는 것.
그래서 p 태그는 하나만 존재하고, 그 내용만 계속해서 바뀌게 된다.
이를 해결하기 위해서, 매 result 이벤트마다 p 태그를 새로 만들어서 화면에 박아주면 된다.
isFinal 속성이 등장한 이유는, 한 문장이 끝나고 나면 p 태그를 새로 만들어주기 위해서.
(매 문장마다 p 태그를 새로 생성해야 하니까.)
recognition.addEventListener('result', e => { const transcript = Array.from(e.results) .map(result => result[0]) .map(result => result.transcript) .join(''); p.textContent = transcript; if(e.results[0].isFinal) { p = document.createElement('p'); words.appendChild(p); } });
9. transcript 또한 문자열이므로 includes 메서드를 사용할 수 있다.
includes 메서드를 사용하면, 특정 단어가 인식되었을 때 특정 함수를 호출하는 등의 기능을 만드는 것이 가능하다.
"시리야, 날씨 알려줘" 라고 했을 때 날씨를 알려주는 외부 함수를 호출한다던가.
다양한 용도로 사용할 수 있는 기능인 듯 하다.
정확도도 생각보다 좋은 것 같고.
if(transcript.includes('날씨')) { console.log('오늘의 날씨는 직접 알아보세요.'); }
10. 위 사진에서 콘솔창을 자세히 보면, 한 번 말한 문장이 엄청 여러 번 인식되는 것을 볼 수 있다.
이는 debounce같은 것을 사용하여, 호출 빈도수를 조절하는 등의 방법으로 해결할 수 있다.
전체 자바스크립트 코드
<script> window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; const recognition = new SpeechRecognition(); recognition.interimResults = true; let p = document.createElement('p'); const words = document.querySelector('.words'); words.appendChild(p); recognition.addEventListener('result', e => { const transcript = Array.from(e.results) .map(result => result[0]) .map(result => result.transcript) .join(''); p.textContent = transcript; if(e.results[0].isFinal) { p = document.createElement('p'); words.appendChild(p); } if(transcript.includes('날씨')) { console.log('오늘의 날씨는 직접 알아보세요.'); } }); recognition.addEventListener('end', recognition.start); recognition.start(); </script>
+) 마이크가 해당 탭에서 인식되고 있는 상태인지를 확인할 것.
다른 탭에서 마이크가 사용되고 있는 경우, 음성 인식이 되지 않을 수도 있다.
반응형'Web > JS30' 카테고리의 다른 글
📢 Speech Synthesis 🔊 (0) 2020.12.23 Following Along Links (0) 2020.11.29 ⌚Tally String Times with Reduce⏱ (0) 2020.11.20 📊 Sort Without Articles 🎢 (0) 2020.11.19 🤩 CSS Text Shadow Mouse Move Effect 😎 (0) 2020.11.18