음성인식 API(Web Speech API): 나만의 시리를 만들 수 있을까?

음성인식 API에 대해서 이미 알고 있으시면 가볍게 스킵하셔도 됩니다. ^^;
브라우저에는 우리가 기본적으로 많이 사용하는 API(geolocation, Web Notification 등등)이외에도 아주 다양한 API들이 존재한다. 이러한 API는 표준으로 등록 되서 모든 브라우저에서 공통으로 제공하는 API 뿐만 아니라 아직 표준으로 등록 되지 않은 드래프트 상태의 API까지 다양하다.
오늘은 그 중에서 웹 음성인식 API(Web Speech API)에 대해서 알아보도록 하자.
이 글을 설명 하기 위해 간단한 데모를 만들었으니, 관심이 있다면 아래에서 확인 해보자.
데모: https://fresh-web.github.io/web-speech/
소스: https://github.com/fresh-web/web-speech
모질라 예제 소스: https://github.com/mdn/web-speech-api/

음성인식 API(Web Speech API)는 크게 SpeechSynthesis (Text-to-Speech)와 SpeechRecognition (Asynchronous Speech Recognition) 두가지로 나뉜다.
SpeechSynthesis는 텍스트를 음성으로 변환하는 API이고, SpeechRecognition은 음성을 텍스트로 변환하는 API이다.
SpeechSynthesis의 경우 IE를 제외한 대부분의 모던 브라우저에서는 지원 되지만 SpeechRecognition은 현재 메이저 브라우저에선 크롬만 지원된다.
노트: 크롬에서 음성인식 API는 서버 엔진을 통해서 제공 되기 때문에 오프라인에서는 지원되지 않는다.
브라우저 지원 현황: https://caniuse.com/#search=speech
Web Speech API Spec: https://w3c.github.io/speech-api/speechapi.html
사실 SpeechRecognition API의 경우 구글 크롬에선 추가된지 오래되었지만 아직까지 드래프트 단계이다.
이번 시간에는 SpeechSynthesis와 SpeechRecognition 중에 SpeechRecognition API에 대해 구체적으로 알아보도록 하겠다.

SpeechRecognition 인터페이스는 프로퍼티(Properties), 메소드(Methods), 이벤트 헨들러(Event handlers) 총 부분으로 구성되어 있다.
먼저, SpeechRecognition의 프로퍼티는 총 6개가 있다:
  • grammars: 현재 SpeechRecognition에서 인식 할 문법을 나타내는 SpeechGrammar 객체의 컬렉션을 가져오거나 설정한다.
  • lang: 현재 언어를 세팅하거나 가져온다. 값이 없다면 html의 lang 속성 또는 user-agent의 언어 값으로 매칭 된다.
  • continuous: 음성을 인식 될때 마다 결과를 받을 것인지, 아니면 한개의 결과만 받을 것인지를 설정 할 수 있다. 기본값은 false 이다.
  • interimResults: 아직 끝나지 않은 상태의 음성을 받을 것인지 아닌지를 설정한다. 기본값은 false이다.
  • maxAlternatives: 가능성있는 음성인식의 결과 값들을 최대 몇개까지 받을 것인지를 설정한다. 기본값은 1이다.
  • serviceURI: 음성인식 서비스를 기본 내장 서비스가 아닌 다른 서비스를 이용하여 받고 싶을 때 설정하는 값이다.
메소드는 3가지가 있는데 이름만 봐도 알만하다:
  • start(): 음성인식을 시작한다.
  • stop(): 음성인식을 멈춘다. SpeechRecognitionResult 객체가 event 객체에 포함되서 리턴된다.
  • abort(): 음성인식을 중단한다. 음성인식을 중단하면 SpeechRecognitionResult 객체가 리턴되지 않는다.
이벤트 핸들러는 총 11개가 있는데, 핸들러 이름만 봐도 어떤 역할을 하는지 이해가 될 거라고 생각해서 모두 설명하지는 않고, onresult, onnomatch와 onerror에 대해서만 설명 하도록 하겠다:
  • onaudiostart
  • onsoundstart
  • onspeechstart
  • onsoundend
  • onaudioend
  • onresult
  • onnomatch
  • onerror
  • onstart
  • onend

onresult

음성인식 서비스가 결과를 리턴하면 실행되는 이벤트이다. 리턴되는 결과 값은 EventTarget 객체 인데, 이 객체의 results 속성으로 SpeechRecognitionResultList 객체가 들어있고, 이 객체 안에는 다시 SpeechRecognitionResult 객체가 배열 같은 형태로 들어 있다. 그래서 음성을 인식하여 text로 변환된 값은 event.results[0][0] 이런식으로 접근하여 값을 가져 올 수 있다. 예제를 보면 더 명확하게 이해가 될 것이다:
this.recognition.onresult = event => { const text = event.results[0][0].transcript; console.log('transcript', text); // text 변수에 인식된 음성을 text 형태로 변환한 문자가 들어있다. };
당연히 리턴된 값이 객체이기 때문에 transcript이외에도 다른 값들도 포함되어 있다.
SpeechRecognitionResult의 객체 형태 예제:
{
{transcript: "텍스트", confidence: 0.8404663801193237},
isFinal:true
length:1
}

onerror

음성인식이 되지 않고 error를 뱉으면 실행되는 이벤트이다. EventTarget 객체의 error 속성 값은 아래와 같다:
  • no-speech
  • aborted
  • audio-capture
  • network
  • service-not-allowed
  • bad-grammar
  • language-not-supported

onnomatch

음성 인식 서비스가 제대로된 인식없이 최종 결과를 반환하면 실행된다. confidence 값이 낮을 때 발생 할 수 있다.

만들어보기

자, 기본적인 api의 정의와 사용법을 알았으니, 샘플 코드를 만들어보자. 이번 예제는 간단하게 버튼을 눌러서 음성인식을 시작하고, 이벤트 헨들러가 호출되면 그 값을 화면에 보여주는 간단한 예제를 만들어 보자. (예제는 리액트로 만들었는데, 리액트와는 전혀 상관이 없다. 간단한 소스 이기 때문에 리액트를 모르더라도 충분히 이해 할 수 있을 것이다. 아래의 예제소스는 여기에서도 확인 할 수 있다.)
먼저 SpeechRecognition 인터페이스를 초기화 한다:
const Recognition = window.SpeechRecognition || window.webkitSpeechRecognition; if (!Recognition) { alert('Speech Recognition API is not supported in this browser, try chrome'); return; } this.recognition = new Recognition();
한국어를 인식 할 수 있도록 언어(lang) 속성 값을 한국어로 지정해준다:
this.recognition.lang = 'ko-KR';

이벤트 핸들러를 등록해서 호출되는 이벤트에 따라 테스트를 화면에 노출 하도록 한:
this.recognition.onresult = event => {
const text = event.results[0][0].transcript;
console.log('transcript', text);
this.setState({ text });
};

this.recognition.onspeechend = () => {
console.log('stopped');
this.setState({ show: true });
};

this.recognition.onnomatch = event => {
console.log('no match');
this.setState({ text: "Sorry, can't hear" });
};

this.recognition.onstart = () => {
this.setState({
listening: true,
});
};

this.recognition.onend = () => {
console.log('end');
this.setState({
listening: false,
});
this.end();
};

this.recognition.onerror = event => {
console.log('error', event);
this.setState({
show: true,
text: event.error,
});
};
이벤트를 등록하고 리턴된 결과값이 있으면 그 텍스트를 화면에 뿌려준다:
<main className="demo-1">
{this.state.show ? (
<Word text={this.state.text} onClose={this.handleClose} />
) : (
<ListenerButton
onStart={this.start}
onEnd={this.end}
disabled={this.state.listening}
buttonText={
this.state.listening ? 'Listening...' : 'Click me to listen'
}
/>
)}
</main>
Web Speech API에 대해서 알아봤다. 아직 당연히 드래프트 상태이고, 브라우저 지원도 크롬만 되지만 이러한 API가 정식으로 표준 API로 등록 된다면, 이런 API를 활용해서 앱에서만 가능했던 음성 챗봇이라던지, 정말 시리(Siri) 같은 것들을 브라우저에서 볼 수 있지 않을까 상상해본다.
참조:
https://davidwalsh.name/speech-recognition
https://w3c.github.io/speech-api/speechapi.html
https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition