[Node] VoskとVOICEVOXを使ってChatGPTと音声で会話する

2023.03.06

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Introduction

先日、ChatGPTがAPIの提供を開始しました。
周りでもChatGPTのAPIをつかっていろいろつくっている人がいるので、
私もVoskとVOICESBOXを使用して、 音声でChatGPTに話しかけて返答を読み上げてくれる
スクリプトを(雑に)つくってみました。

Environment

今回試した環境は以下のとおりです。

  • MacBook Pro (13-inch, M1, 2020)
  • OS : MacOS 12.4
  • VOICEBOX : 0.14.5
  • Node : v18.2.0

Try

やってることはシンプルです。

Voskで音声入力して内容をChatGPTになげ、かえってきた答えをVOICEBOXにPOSTします。
次にVOICEBOXからのレスポンスをwavに保存して、afplayで再生するという雑仕様。
(VOICEBOXのところとか再生の部分はいい方法がいくらでもあるはず)

とりあえず動く状態なレベルですが、やってみます。

まずはVOICEBOXのインストールです。
ここからダウンロードしてインストール&起動します。
VOICEBOXはhttpサーバとして起動しているので、RESTでアクセスできます。

APIの使い方は、http://localhost:50021/docsにアクセスすれば
詳しい説明があります。

次はOpenAIのAPIを使うための準備です。
このへんとかこのへんをみて API Keyの発行をしましょう。
後述するプログラムで使います。

nodeプログラムで使うモジュールをnpmでインストールします。

% mkdir example && cd example
% npm install vosk web-audio-api openai axios

次に、ここからVoskのモデル(vosk-model-small-ja-0.22)を
ダウンロードして解凍し、example直下に置いておきます。
そして下記のjavascriptプログラム(main.js)を作成します。

//main.js
const vosk = require("vosk");
const { Configuration, OpenAIApi } = require("openai");
const { default: axios } = require("axios");
const fs = require("fs");
const { execSync } = require("child_process");

var mic = require("mic");

const conf = new Configuration({
  apiKey: "<api key>",
});
const openai = new OpenAIApi(conf);

const SAMPLE_RATE = 16000
const MODEL_PATH = "./vosk-model-small-ja-0.22";

vosk.setLogLevel(0);
const model = new vosk.Model(MODEL_PATH);
const rec = new vosk.Recognizer({model: model, sampleRate: SAMPLE_RATE});

var micInstance = mic({
    rate: String(SAMPLE_RATE),
    channels: '1',
    debug: false,
    device: 'default',    
});

var micInputStream = micInstance.getAudioStream();

async function ask(prompt) {
    const completion = await openai.createChatCompletion({
        model: "gpt-3.5-turbo",
        messages: [{role: "user", content: prompt}],
      });

      return completion;
}

var play_flag = false;

micInputStream.on('data', data => {
    if (rec.acceptWaveform(data)) {
        let question = rec.result().text;
        console.log("question=" + question);

        if(question !== null && question.length > 3 && play_flag == false) {
            play_flag = true;
            //chatGPTに質問を投げる
            ask(question).then((result) => {
                let prompt = result.data.choices[0].message.content;
                // VOICEVOXでchatGPTからの返答を音声合成
                axios.post(`http://127.0.0.1:50021/audio_query?speaker=1&text="${prompt}"`)
                .then((queryRes) => {
                    axios.post(`http://127.0.0.1:50021/synthesis?speaker=1`, queryRes.data, {
                        responseType: "arraybuffer",
                    })
                    .then((res) => {
                        //VOICEBOXで生成した音声データをwavで出力してafplayで再生
                        fs.writeFileSync("tmp_voice.wav", res.data);
                        execSync("afplay tmp_voice.wav");
                        play_flag = false;
                    });
                });
            })
            .catch((e) => {
                console.error(e.message);
                throw e;
            });    
        } else {
            console.log("do not exec ask & play);
        }
    }
});

micInputStream.on('audioProcessExitComplete', function() {
    console.log(rec.finalResult());
    rec.free();
    model.free();
});

process.on('SIGINT', function() {
    console.log("SIGINT");
    micInstance.stop();
});

micInstance.start();

スクリプトの記述ができたら起動してみます。

% node main.js

マイクにむかってしゃべってみれば、その内容がchatGPTにPOSTされ、
返答がVOICEBOXで音声になって再生されます。

Summary

今回はchatGPTと音声入力を組み合わせてみました。
動作が遅いとか、アルファベットがそのまま読まれたりとか
いろいろ問題はありますが、とりあえず音声で会話っぽくなってる感じです。

References