はじめに
前回、テキストベースでラズパイにインストールしたローカルLLM(QWEN)を実行するところまでやりました。3Bモデルなので、そこそこ会話できるレベルですが、ローカルで完結するのがうれしいところです。
今回は、音声関係をまとめました。
事前インストール関係
音声関係には、音声のテキスト化、および、テキストの音声化、のソフトウェアをインストールします。
音声認識には、Whisper を使います。前回インストール済みの場合はパスしても大丈夫です。
git clone https://github.com/ggerganov/whisper.cpp
cd whisper.cpp
cmake -B build
cmake --build build -j4
音声認識用モデル(軽量版である base)を取得します。
bash models/download-ggml-model.sh base
text2speech に使う open-jtalk をインストールします。前回は紹介していないので、インストールしてください。
sudo apt install -y open-jtalk open-jtalk-mecab-naist-jdic
音声データを取得します。
sudo apt install -y open-jtalk-mecab-naist-jdic hts-voice-nitech-jp-atr503-m001
ラズパイで録音し、その音声ファイルを再生する
USB接続でマイクとスピーカーをつなぎます。今回はヘッドセットを使用しました。
マイクを認識しているかを以下で確認します。
arecord -l
以下のように USB機器確認 card 2 、デバイス情報 device 0 などが表示されればOKです。
card 2: Device [USB PnP Sound Device], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
録音テストします。
arecord -D plughw:2,0 -f S16_LE -r 16000 -d 5 test.wav
ーD でデバイスを指定します。-D plughw:2,0 はカード番号2・デバイス番号0のデバイスを使います。これがないと、エラーが出ました。
-f でフォーマットを指定します。 -f s16_LE の意味は以下の通りです。
S は Signed(符号付き)、16 は 16bit、LE は Little Endian でメモリへの格納順序する(
Raspberry Pi / Intel系では標準です)
-r でサンプリングレートを指定、ここでは 16000 で 16kHz です。CD の44kHz よりはレートを抑えてファイルサイズの増加を抑制しています。
-d は duration(録音時間)オプションです。-d 5 で5秒間録音します。
-d オプションを付けない場合は、Ctrl-C で録音を停止させます。
その後ろの test.wav というファイルネームで保存されます。
実行すると、以下のような表示がでます。
Recording WAVE 'test.wav' : Signed 16 bit Little Endian, Rate 16000 Hz, Mono
録音したファイルを再生します。スピーカーまたはヘッドフォンで音が聞こえればOKです。
最初は aplay test.wav を使いましたが、ラズパイ 5 ではエラーが出て再生できませんでした。
pi5 は pw-play の方がいいみたいです。
pw-play test.wav
音声認識のテスト
音声を認識する whisper の動作確認をしました。
動作確認に使用したスクリプトは消してしまったのでありませんが、
特にトラブルなく確認できました。
テキストを読み上げるテスト
テキストを読み上げる open_jtalk の動作確認をします。以下を実行すると、「こんにちは」の音声が test.wav に出力されます。
echo "こんにちは" | open_jtalk \
-x /var/lib/mecab/dic/open-jtalk/naist-jdic \
-m /usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice \
-ow test.wav
再生してみます(pw-play は pipeware環境ですラズパイ5は対応済みです)
pw-play test.wav
別な文章もしゃべらせました
echo "こんにちは。今日は調子がいいですね。" | open_jtalk \
-x /var/lib/mecab/dic/open-jtalk/naist-jdic \
-m /usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice \
-ow test.wav && pw-play test.wav
音声でローカルLLMに話しかけます
エディター(以下は nano の場合)を起動し、新規ファイル voice_chat.sh を編集します。
cd ~
nano voice_chat.sh
ファイルの中身は以下です。コピー&ペーストして voice_chat.sh の名前で保存します。
nano の場合、保存は、Ctrl + O Enter Ctrl + X です。
#!/bin/bash
WHISPER=~/whisper.cpp/build/bin/whisper-cli
WMODEL=~/whisper.cpp/models/ggml-base.bin
LLAMA=~/llama.cpp/build/bin/llama-cli
LMODEL=~/llama.cpp/models/Qwen2.5-3B-Instruct-Q4_K_M.gguf
VOICE=/usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice
DICT=/var/lib/mecab/dic/open-jtalk/naist-jdic
while true
do
echo "Enterで録音開始(終了 Ctrl+C)"
read
arecord -D plughw:2,0 -d 5 -f S16_LE -r 16000 input.wav
TEXT=$($WHISPER -m $WMODEL -f input.wav -l ja 2>/dev/null \
| sed 's/\[.*\] //' | grep -P '[ぁ-んァ-ヶ一-龠]' | tail -n 1)
echo "あなた: $TEXT"
script -q -c "$LLAMA -m $LMODEL \
-p '<|im_start|>user\n${TEXT}に日本語で1文で返答してください。<|im_end|>\n<|im_start|>assistant\n' \
-n 100 \
--single-turn" /tmp/llama_out.txt
REPLY=$(grep -P '[ぁ-んァ-ヶ一-龠]' /tmp/llama_out.txt | tail -n 1)
echo "AI: $REPLY"
echo "$REPLY" | open_jtalk \
-x $DICT \
-m $VOICE \
-ow reply.wav
pw-play reply.wav
done
できあがったファイル voice_chat.sh に実行権限を付与します。
chmod +x voice_chat.sh
実行します。
./voice_chat.sh
Enterで録音開始(終了 Ctrl+C) が表示されるので、Enterキーを押下して 5秒間 しゃべります。
すると、その内容がテキストで表示されます。その後、AI の返答が表示され、それが音声で流れます。
ただし、余計な文字が表示され、WARNING が出ます。色々調整しましたが、WARNING が出ないようになりませんでした。動作はするので、このままにしています。
音声のテキスト化精度が悪い場合は、whisper の音声モデル base を small にします。
やり方は以下の通りです。ただし、応答時間が若干長くなります。
cd ~/whisper.cpp
bash models/download-ggml-model.sh small
さらに、スクリプトの1行を以下のように変更します。
WMODEL=~/whisper.cpp/models/ggml-small.bin
LLAMA SERVER を使う
llama-cli だと、WARNINGが出たり、必要のない表示がでたり、するので llama-server を試してみました。
一つ目のターミナルをサーバー用にして、以下を実行します。
~/llama.cpp/build/bin/llama-server -m ~/llama.cpp/models/Qwen2.5-3B-Instruct-Q4_K_M.gguf
立上げ時に、多量の表示がされます。
次に、エディターで以下のスクリプトをコピペして ./voice_chat01.sh の名前で保存します。
#!/bin/bash
WHISPER=~/whisper.cpp/build/bin/whisper-cli
WMODEL=~/whisper.cpp/models/ggml-base.bin
VOICE=/usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice
DICT=/var/lib/mecab/dic/open-jtalk/naist-jdic
while true
do
echo "Enterで録音開始(終了 Ctrl+C)"
read
arecord -D plughw:2,0 -d 5 -f S16_LE -r 16000 input.wav
TEXT=$($WHISPER -m $WMODEL -f input.wav -l ja 2>/dev/null \
| sed 's/\[.*\] //' | grep -P '[ぁ-んァ-ヶ一-龠]' | tail -n 1)
echo "あなた: $TEXT"
REPLY=$(curl -s http://localhost:8080/completion \
-d "{\"prompt\":\"<|im_start|>user\n${TEXT}に日本語で1文で返答してください。<|im_end|>\n<|im_start|>assistant\n\",\"n_predict\":100}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['content'])")
echo "AI: $REPLY"
echo "$REPLY" | open_jtalk \
-x $DICT \
-m $VOICE \
-ow reply.wav
pw-play reply.wav
done
できあがったファイル voice_chat01.sh に実行権限を付与します。
cd ~
chmod +x voice_chat01.sh
サーバーとは別のターミナルで実行します。
./voice_chat01.sh
無駄な表示も WARNING も表示されなくなりました。
ただし、llama-cli より応答が遅くなります。
サーバー起動を修正すると、少し早くなります。
~/llama.cpp/build/bin/llama-server \
-m ~/llama.cpp/models/Qwen2.5-3B-Instruct-Q4_K_M.gguf \
-t 4
追加したオプション -t 4 で、ラズパイ5の4コアをフル活用します。
スクリプト voice_chat01.sh は、返答を 1文 に制限しているので、回答は淡泊です。
長くしたい場合は、スクリプト中の REPLY= の行を以下で書き換えてください。
n_predict を 2倍の 200 にしたので、回答文が長くなります。
#!/bin/bash
WHISPER=~/whisper.cpp/build/bin/whisper-cli
WMODEL=~/whisper.cpp/models/ggml-base.bin
VOICE=/usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice
DICT=/var/lib/mecab/dic/open-jtalk/naist-jdic
while true
do
echo "Enterで録音開始(終了 Ctrl+C)"
read
arecord -D plughw:2,0 -d 5 -f S16_LE -r 16000 input.wav
TEXT=$($WHISPER -m $WMODEL -f input.wav -l ja 2>/dev/null \
| sed 's/\[.*\] //' | grep -P '[ぁ-んァ-ヶ一-龠]' | tail -n 1)
echo "あなた: $TEXT"
REPLY=$(curl -s http://localhost:8080/completion \
-d "{\"prompt\":\"<|im_start|>user\n${TEXT}<|im_end|>\n<|im_start|>assistant\n\",\"n_predict\":200}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['content'])")
echo "AI: $REPLY"
echo "$REPLY" | open_jtalk \
-x $DICT \
-m $VOICE \
-ow reply.wav
pw-play reply.wav
done
おわりに
音声の認識精度向上など色々修正していくと、反応が遅くなります。発話も途中でゆっくりになったりします。ラズパイ単独の場合のメリット(ネットワーク不要)もあるので、使う条件をうまく設定するといいと思います。