見出し画像

ゆるプロ!番外編「PyAudioで録音するときに応答(無音)を待つ判定」

ゆるいプログラミング講座、略して「ゆるプロ!」
これは気軽に気楽に試せるゆるーいプログラミング学習コンテンツである。今回のお題は「PyAudioで録音するときに応答(無音)を待つ判定」


一言)

以前、スピーキングのでデモを作ったのだが、任意のタイミングで語り掛け(音声の録音)を終了するにはどうしたらいいか考えたのだが始まり。結果的に無音が数秒続いたら話すことが完了したとみなすことにした。その実装を書く

例)PyAudioで録音するときに応答(無音)を待つ判定

import pyaudio
import numpy

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
WAITSECONDS=2
INPUT_DEVICE_INDEX=2 #Micは2番だった
   
audio = pyaudio.PyAudio()

stream = audio.open(format=FORMAT,
                    channels=CHANNELS,
                    rate=RATE,
                    input=True,
                    frames_per_buffer=CHUNK,
                    input_device_index=INPUT_DEVICE_INDEX)

print("話しかけてください...")

frames = []

cnt = 0
while True:
    # 音声データの読み込み
    data = stream.read(CHUNK)
    frames.append(data)
    # 音量の算出
    x = numpy.frombuffer(data, dtype="int16") / 32768.0
    # 音量が閾値以下であれば、無音とみなす
    if x.max() < 0.05:
        cnt += 1
        print("無音")   #後程コメントアウト
    else:
        cnt = 0
        print("有音")   #後程でコメントアウト
    # 無音状態が一定時間続いた場合に、無音状態と判定
    if cnt > WAITSECONDS / (CHUNK / RATE):
        cnt=0
        break

print("会話の終了")

stream.stop_stream()
stream.close()
audio.terminate()

他人のコードをみて自分も理解が追い付いていないのだが、Google Bardに解釈を求めたところ少し理解が進んだ(面白い)、要は下記の計算がミソとなる、32768は16bitの0を含めた正の桁数であるがこれで音量が計算できる

x = numpy.frombuffer(data, dtype="int16") / 32768.0

CHUNK:録音する音声データのサイズ(バイト単位)
RATE:録音する音声データのサンプリングレート
WAITSECONDS:無音状態が続く時間を判定する基準(秒)

Google Bardより

1024のCHUNK(データ量)で最大音量を測っています。
1秒あたりの音声データの約2.3%を読み込むCHUNKの値が1024の場合、1024個の音量の値が連続して並んでいます。
これらの音量の値の最大値を算出することで、音量を算出することができます。

例えば、音量の値が以下のようなものだった場合、配列の要素は以下のようになります。
[-32768, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767]
この場合、配列の要素の最大値は32767なので、音量は最大となります。

また、音量の値が以下のようなものだった場合、配列の要素は以下のようになります。
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
この場合、配列の要素の最大値は0なので、音量は無音となります。

このように、1024のCHUNK(データ量)で最大音量を測ることで、音量を算出することができます。

Google Bardより

配列を32768で割ると、-1から1までの範囲の値に変換されます。
[-1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]

音量のdBと配列の値の関係は、以下の計算式で表されます。
dB = 20 * log10(音量)
音量は、-1から1までの範囲の値です。

ログ10は、底が10の常用対数です。
したがって、音量が-1だった場合、dBは以下のようになります。
dB = 20 * log10(-1)
dB = -20 dB

また、音量が1だった場合、dBは以下のようになります。
dB = 20 * log10(1)
dB = 0 dB

このように、音量が-1から1までの範囲の値の場合、dBは-20 dBから0 dBまでの範囲の値となります。

0.05が配列の最大値の場合、音量は0.05となります。
音量が0.05の場合、dBは以下のようになります。
dB = 20 * log10(0.05)
dB = -14.14 dB
したがって、0.05が配列の最大値の場合、dBは-14.14 dBとなります。
なお、人間の耳で聞こえる音量の閾値は、-60 dB程度とされています。

したがって、0.05が配列の最大値の場合、音量は人間の耳で聞くことができますが、聞こえにくい音量となります。

Google Bardより

結果)

設定した音量より入力の音声が低ければ無音と判断するようになった

実行結果

感想)

今回はちょっと緩くはなかったな・・・。まあ、自分が入用だったからてのはあるが、科学をAIに聞いて学べるのは面白かった

参考)

numpy.frombuffer

参考にしたブログ

無音の表現について勉強させてもらった元ネタ

おわり!