2024年のアドベントカレンダー6日目の記事です。
音声入力を実施したい#2
5日目に引き続き音声入力処理を作成します。
音声入力をするにあたってリアルタイムで発声内容を描画していくことも考えましたが、テストと同じように回答した後に採点したほうが(つくりやすくて)いいかなと思ったのでリアルタイムでの描画は行わないこととしました。
それに伴い、web speech apiの制御はuseStateではなくuseRefを利用することにしました。
(useStateとuseRefの違いは再レンダリングの有無くらいのざっくり知識しかないのでこの選択が正しいかは不明です)
ドキュメントを見た感じだと以下のイベントがあれば動かせそうだったため、useRefを利用してそのあたりの制御を行っていきます。
1.start
2.error
3.result
4.end
また、結果を格納するstateも追加して最終的に以下のようなコードで落ち着きました。
App.js
import './App.css';
import { Provider } from './components/ui/provider';
import { Box, AbsoluteCenter, Heading, VStack, HStack } from '@chakra-ui/react';
import { useState, useRef } from 'react';
import { Button } from './components/ui/button';
function App() {
const [question, setQuestion] = useState('あとですぷしからしゅとくするようにします');
const [answer, setAnswer] = useState('ここがおんせいにゅうりょくでかわるところ');
const [isRecording, setIsRecording] = useState(false);
const recognitionRef = useRef(null);
const startRecording = () => {
if (!('webkitSpeechRecognition' in window)) {
alert('なんかだめみたい。おじさんにきいてみて');
return;
}
recognitionRef.current = new window.webkitSpeechRecognition();
recognitionRef.current.lang = 'ja-JP';
recognitionRef.current.continuous = false;
recognitionRef.current.interimResults = false;
recognitionRef.current.onstart = () => {
setIsRecording(true);
};
recognitionRef.current.onresult = (event) => {
const transcript = event.results[0][0].transcript;
setAnswer(transcript);
};
recognitionRef.current.onerror = (event) => {
console.error(event.error);
};
recognitionRef.current.onend = () => {
setIsRecording(false);
};
recognitionRef.current.start();
};
const stopRecording = () => {
if (recognitionRef.current) {
recognitionRef.current.stop();
}
};
return (
<Provider>
<Box bg="blue.100" w="100vw" h="100vh">
<AbsoluteCenter>
<VStack gap="20">
<Heading size="5xl">
{question}
</Heading>
<HStack gap="20">
<Button size="2xl" variant="outline" borderColor="green.600" color="green.600"onClick={isRecording ? stopRecording : startRecording}>
{isRecording ? "こたえた" : "こたえる"}
</Button>
<Button size="2xl" variant="outline" borderColor="orange.600" color="orange.600">
わからない
</Button>
</HStack>
<Heading size="5xl">
{answer}
</Heading>
</VStack>
</AbsoluteCenter>
</Box>
</Provider>
);
}
export default App;
結果として音声入力自体は無事に動きました。
ただし回答が自動的に漢字になってしまったりするので、ここは最終的にすべて平仮名で保持されるように変換する必要があり、今後どこかで改修しようかとおもいます。