0
0

More than 1 year has passed since last update.

voicevox で生成された音声を zello に送る方法

Posted at

概要

この記事では voicevox で生成された音声を python を使い zello に送る方法を記載しています。また、同様の方法を voiceroid や discord.py に応用できます。

個人の趣味の範疇で利用してください

ポイント

  • zello の API では opus と呼ばれる Audio Codec を利用します。
  • この記事では windows で実験しています。
       * この記事のための一部コマンドの実行結果例は macos から貼っています。

事前準備

zello developer の登録

  • zello console からアカウントの登録を行い、開発用の API キーを発行します。
  • この開発用の API キーは3ヶ月有効です。

opus-tools のインストール

  • opus-tools をダウンロードします。
  • ダウンロードした zip ファイルを展開, フォルダ内に opusenc のバイナリ (*.exe) があることを確認します。
  • 環境変数にこのフォルダ (opusenc) へのパスを設定します。
  • powershell を開き、次のコマンドが通ることを確認します。
// TODO 差し替える
> opusenc --version
opusenc opus-tools 0.1.9 (using libopus 1.1)
Copyright (C) 2008-2013 Xiph.Org Foundation

sox のインストール

  • sox は Audio を扱う十徳ナイフです。
  • sox.exe へのパスを環境変数に設定します。
  • powershell を開き、次のコマンドが通ることを確認します。
// TODO: windows 版で差し替える
sox --version
sox:      SoX v14.4.2

voicevox の起動

voicevox をダウンロードします。ダウンロードしたファイルを展開後にフォルダ内の run.exe (voicevox.exe) で合成音声エンジンの機能を提供する web API アプリケーションを起動します。このアプリケーションは http://localhost:50021 で web サーバーを起動しています。

合成音声の送信テスト方法

  • example コードを github にアップロードしています。
  • demo_on_zello.py で動作確認ができます。
    • 環境変数 ZELLO_AUTH_TOKEN に zello console から発行した API キーをセット
    • 実行時引数の username, password, channel には zello で普段利用しているアカウントとチャンネルを設定
  • 別のデバイス、Zello アカウントからテキストメッセージをチャンネルに送ると合成音声が再生されます。

実装上のポイント

  • zello の API 仕様はこちらを参照します。
  • audio は 16bit 48k の 1ch として 20ms の opus packet を送ります
    • codec header でこれ以外を設定しても正しく再生されませんでした。
    • opusenc の振る舞いも怪しいため sox でデータを整えています
  • voicevox は 16bit 符号あり 24k の 1ch の PCM らしいので、sox で 48k に直しています。
  • voiceroid の場合は このような変換 でうまくいきます。

Raw PCM と opus エンコーディング後のファイルサイズの違い

マイク入力を扱うための備忘録のついでにマイク音源を保存した raw pcm と opus encoding 後のファイルサイズを残します。raw pcm のファイルサイズ 960k に対して opus encoding 後は 82.9k ほどに圧縮されていることがわかります。

import pyaudio
import subprocess
from subprocess import PIPE


if __name__ == '__main__':
    audio = pyaudio.PyAudio()

    sampling_rate = 48000

    stream = audio.open(format=pyaudio.paInt16, channels=1, rate=sampling_rate,
                        input=True, input_device_index=1, frames_per_buffer=int(48000/12))

    chunk_size_in_seconds = 0.2  # 200ms
    number_of_frames_per_chunk = int(sampling_rate * chunk_size_in_seconds)

    record_time_in_seconds = 10
    number_of_chunks = int((1 / chunk_size_in_seconds) * record_time_in_seconds)

    frames = b''
    for chunk_id in range(number_of_chunks):
        # NOTE: wait for `chunk_size_in_seconds`.
        frames += stream.read(num_frames=number_of_frames_per_chunk)

    command = 'sox -b 16 -e signed -c 1 -r 48000 -t raw - -b 16 -e signed -c 1 -r 48000 -t wav - | opusenc --quiet - -'
    p = subprocess.Popen(command, stdin=PIPE, stdout=PIPE, shell=True)

    opus_data = p.communicate(frames)[0]
    with open('sample.opus', 'wb') as f:
        f.write(opus_data)
sample.opus:

 File Size: 82.9k     Bit Rate: 66.3k
  Encoding: Opus          
  Channels: 1 @ 16-bit   
Samplerate: 48000Hz      
Replaygain: off         
  Duration: 00:00:10.00  

In:100%  00:00:10.00 [00:00:00.00] Out:480k  [      |      ]        Clip:0    
Done.
[app]$play sample.wav 

sample.wav:

 File Size: 960k      
  Encoding: Signed PCM    
  Channels: 1 @ 16-bit   
Samplerate: 48000Hz      
Replaygain: off         
  Duration: unknown      

In:0.00% 00:00:10.00 [00:00:00.00] Out:480k  [      |      ]        Clip:0    
Done.

余談

  • windows <=> linux 間で wav ファイルを直接やりとりせず、numpy.ndarray で変換した方がよい。
    • エンディアンの問題で再生できない
  • discord に投稿すると音が小さい
  • discord.pyFFmpegOpusAudio を始め使おうとしたが上手くいかない。
    • これが外部コマンド ffmpeg を使うが、ffmpeg のコマンド実行結果がエラーになる
    • このため demo_on_discord.py では PCMAudio を使っている。
0
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
0
0