1
0

More than 1 year has passed since last update.

GPTと音声で会話するサンプルプログラム

Posted at

TL;DR

音声でGPTと会話するアプリを、node.jsで作ってみました。
音声データはSocket.ioでサーバーと通信し、テキストとの変換にはGoogleのAPIを使っています。

┌───────┐              ┌─────────┐       ┌──────────────┐
│       │localhost:3000│ Client  │       │Google        │
│Browser├──────────────┤         │   ┌───┤Text to Speech│
│       │   html,js,css│React App│   │   │Speech to Text│
└────┬──┘              └─────────┘   │   └──────────────┘
     │                               │
     │                 ┌─────────┐   │   ┌──────────────┐
     │   localhost:3001│ Server  │   │   │OpenAI        │
     └─────────────────┤         ├───┴───┤              │
             Audio Data│ Express │       │gpt-3.5       │
                       └─────────┘       └──────────────┘

最終的なコードは以下にあります。実行方法はREADMEを参照してください。

各技術要素の説明

各技術要素について、簡単に説明していきます。

Socket.ioを利用したリアルタイム通信

サーバーとクライアント間でリアルタイム通信を実現するために、Socket.ioを使用しています。Socket.ioは、WebSocketをベースにしていますが、WebSocketが利用できない環境ではロングポーリングなどの別の通信方式を利用できます。

サーバー側のhttpServer.listen()までの処理で、HTTPサーバーを立ち上げています。
クライアントとの通信が確立できた後の処理は、io.on("connection",...)のハンドラ内で行います。

サーバー側
const PORT = process.env.PORT || 3001;
const httpServer = createServer();
const io = new Server(httpServer, {
  cors: {
    origin: "*", // Allow all origins
    methods: ["GET", "POST"],
  },
});

httpServer.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

io.on("connection", (socket) => {
  console.log("Client connected.");
  // ...
});

クライアント側ではio()でサーバーとの接続を行います。
データ送信は.emit()を使い、データ受信時の処理は.on()に記載します。

Reactの再レンダリングでioが再初期化されたりしないように、useRefを使って状態を保持しています。

クライアント側
import { io } from "socket.io-client";
const SERVER_URL = "http://localhost:3001";
const socketRef = useRef();
socketRef.current = io(SERVER_URL);

音声の録音とストリーミング送信

navigator.mediaDevices.getUserMediaを使用して、ユーザーのマイクからデータを取得します。
音声データの転送にはMediaRecorderを使い、定期的に(ストリーミングで)サーバーへ送信するため、start()の引数にタイムスライスを渡しています。これによって、この場合では100ms時間ごとに、音声データをサーバーへ転送します。

const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
audioRef.current = new MediaRecorder(stream);
audioRef.current.ondataavailable = (e) => {
  if (e.data.size > 0) {
    socketRef.current.emit("clientAudio", e.data);
  }
};
audioRef.current.start(100);

サーバーから受け取った音声の再生

クライアントは、サーバーから送られてくる音声データ(GPTによって生成されたテキストを音声合成したもの)を受信し、再生します。AudioContextを使用して音声データをデコードし、audioContext.destinationに対して出力することで音声を再生します。

socketRef.current.on('serverAudio', async (data) => {
  const audioContext = new AudioContext();
  const audioBuffer = await audioContext.decodeAudioData(data);
  const bufferSource = audioContext.createBufferSource();
  bufferSource.buffer = audioBuffer;
  bufferSource.connect(audioContext.destination);
  bufferSource.start();
});

サーバーでのAPI呼び出し

GoogleのAPIを使って音声とテキストの変換をする部分や、OpenAIのAPIで回答を得る部分については、呼び出している以上の内容がないため、説明は省略します。

以上。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0