Skip to content
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ Called when SpeechRecognition stops listening.

#### onResult

`function(string)`
`function(transcript: string, final: string, interim: string)`

Called when SpeechRecognition has a result. It is called with a string containing a transcript of the recognized speech.
Called when SpeechRecognition has a result. It is called with a string containing a transcript of the entire recognized speech, a string containing the final transcript of the most recent recognized speech, and a string containing the interim transcript of current speech.

### Returns

Expand All @@ -171,6 +171,14 @@ Call to make the browser start listening for input. Every time it processes a re
`boolean` _(default: true)_
SpeechRecognition can provide realtime results as it's trying to figure out the best match for the input. Set to false if you only want the final result.

- **continuous**
`boolean` _(default: false)_
The continuous property of the SpeechRecognition interface controls whether continuous results are returned for each recognition, or only a single result.

- **nonStop**
`boolean` _(default: true)_
When set to `true` SpeechRecognition will not stop automatically after inactivity.

#### stop

`function()`
Expand Down
2 changes: 2 additions & 0 deletions examples/src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { render } from 'react-dom';
import SpeechSynthesisExample from './useSpeechSynthesis';
import SpeechRecognitionExample from './useSpeechRecognition';
import SpeechRecognitionContinuousExample from './useSpeechRecognitionContinuous';
import { GlobalStyles, Row, GitLink, Title } from './shared';
import gh from './images/github.png';

Expand All @@ -17,6 +18,7 @@ const App = () => (
<Row>
<SpeechSynthesisExample />
<SpeechRecognitionExample />
<SpeechRecognitionContinuousExample />
</Row>
<GitLink>
<img alt="Github" src={gh} />
Expand Down
23 changes: 21 additions & 2 deletions examples/src/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,35 @@ export const Container = styled.div`
}

select,
textarea {
textarea,
.textarea {
font-size: 16px;
margin-bottom: 12px;
width: 100%;
}

textarea {
textarea,
.textarea {
border: 1px solid darkgrey;
border-radius: 10px;
padding: 8px;
resize: none;
}

.textarea {
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
min-height: 72px;
background-color: white;
font-family: monospace;
}

.final {
color: #666;
}

.interim {
color: black;
}
`;
110 changes: 110 additions & 0 deletions examples/src/useSpeechRecognitionContinuous.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React, { useState } from 'react';
import { useSpeechRecognition } from '../../src';
import { Container } from './shared';

const languageOptions = [
{ label: 'Cambodian', value: 'km-KH' },
{ label: 'Deutsch', value: 'de-DE' },
{ label: 'English', value: 'en-AU' },
{ label: 'Farsi', value: 'fa-IR' },
{ label: 'Français', value: 'fr-FR' },
{ label: 'Italiano', value: 'it-IT' },
{ label: '普通话 (中国大陆) - Mandarin', value: 'zh' },
{ label: 'Portuguese', value: 'pt-BR' },
{ label: 'Español', value: 'es-MX' },
{ label: 'Svenska - Swedish', value: 'sv-SE' },
];

const Example = () => {
const [lang, setLang] = useState('en-AU');
const [final, setFinal] = useState('');
const [interim, setInterim] = useState('');
const [blocked, setBlocked] = useState(false);

const onEnd = () => {
setFinal(prevState => `${prevState}${interim} `);
setInterim('');
};

const onResult = (_, finalTranscript, interimTranscript) => {
setInterim(interimTranscript);
setFinal(prevState => `${prevState}${finalTranscript}`);
};

const changeLang = (event) => {
setLang(event.target.value);
};

const onError = (event) => {
if (event.error === 'not-allowed') {
setBlocked(true);
}
};

const { listen, listening, stop, supported } = useSpeechRecognition({
onResult,
onEnd,
onError,
});

const toggle = listening
? stop
: () => {
setBlocked(false);
listen({
continuous: true,
nonStop: false,
lang,
});
};

return (
<Container>
<form id="continuous-recognition-form">
<h2>Continuous Recognition</h2>
{!supported && (
<p>
Oh no, it looks like your browser doesn&#39;t support Speech
Recognition.
</p>
)}
{supported && (
<React.Fragment>
<p>
{`Click 'Listen' and start speaking.
SpeechRecognition will provide a transcript of what you are saying.`}
</p>
<label htmlFor="lang">Language</label>
<select
form="speech-recognition-form"
id="lang"
value={lang}
onChange={changeLang}
>
{languageOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
<label>Transcript</label>
<div className="textarea">
{final && <span className="final">{final}</span>}
{interim && <span className="interim">{interim}</span>}
</div>
<button disabled={blocked} type="button" onClick={toggle}>
{listening ? 'Stop' : 'Listen'}
</button>
{blocked && (
<p style={{ color: 'red' }}>
The microphone is blocked for this site in your browser.
</p>
)}
</React.Fragment>
)}
</form>
</Container>
);
};

export default Example;
32 changes: 26 additions & 6 deletions src/useSpeechRecognition.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,24 @@ const useSpeechRecognition = (props = {}) => {
const [supported, setSupported] = useState(false);

const processResult = (event) => {
const transcript = Array.from(event.results)
.map((result) => result[0])
.map((result) => result.transcript)
.join('');
const merge = (arr) => (
arr.map((result) => result[0])
.map((result) => result.transcript)
.join('')
);

onResult(transcript);
const results = Array.from(event.results);
const changed = results.splice(event.resultIndex)

const transcript = merge(results);
const final = merge(
changed.filter((result) => result.isFinal)
);
const interim = merge(
changed.filter((result) => !result.isFinal)
);

onResult(transcript, final, interim);
};

const handleError = (event) => {
Expand All @@ -60,6 +72,7 @@ const useSpeechRecognition = (props = {}) => {
continuous = false,
maxAlternatives = 1,
grammars,
nonStop = true,
} = args;
setListening(true);
recognition.current.lang = lang;
Expand All @@ -73,7 +86,14 @@ const useSpeechRecognition = (props = {}) => {
}
// SpeechRecognition stops automatically after inactivity
// We want it to keep going until we tell it to stop
recognition.current.onend = () => recognition.current.start();
recognition.current.onend = () => {
setListening(false);
onEnd();
if (nonStop) {
setListening(true);
recognition.current.start();
}
}
recognition.current.start();
}, [listening, supported, recognition]);

Expand Down