Pythonで正弦波を再生する方法:PyaudioとNumpyを使った音の生成とコールバック

Python
この記事は約8分で読めます。
スポンサーリンク

プログラム解説

このプログラムでは以下のような流れで処理を行っています。

  1. 正弦波の生成
  2. 音声出力インスタンスの生成
  3. ストリームを開始と音声停止待ち
  4. 音声出力処理(コールバック)
  5. 後処理

これまで音声の入力処理では、同期処理を行っていましたが、今回はコールバック処理が使えることに気が付いたのでコールバックでの処理に挑戦してみました。

正弦波の生成

まず、正弦波の生成処理です。
正弦波の生成には、numpyというライブラリを使用しています。
実際の処理は以下です。

ここでは2秒間分の44.1Khzのサンプリング周波数で1KHzの音声生成しています。
ちなみにこのプログラムで、周波数を変えるにはfreq の値を変えれば変更できます。
正弦波の具体的な方法を私はあまり知らないので、以下を参考にさせてもらっています。
ここで重要なのは生成したデータを-32767~32767内のデータに変換していることと
1サンプリングデータを16Bitに変換していることです。
もともと、生成した波形をfloatのまま使用していましたが、これをしないと音声がノイズになりました。

2.2 正弦波の WAVE ファイルを作る
2.2 正弦波の WAVE ファイルを作る
import numpy as np
def create_sinewave():
    global wave
    A     =  1    # 振幅
    freq  = 1000  # 周波数 Hz
    sec   = 2     # 信号の長さ s
    fs    = 44100 # サンプリング周波数 Hz
   
    t = np.linspace(0, sec, fs * sec +1)         # 0≦t≦time をnumsamples等分
    wave = np.sin(2 * np.pi * freq * t)          # 周波数 freq (Hz) の正弦波
    wave = np.rint(32767*wave / max(abs(wave)))  # [-32767,32767] の範囲に収める
    wave = wave.astype(np.int16)                 # 16ビット整数に型変換する
   

音声出力インスタンスの生成

続いて、音声出力インスタンスの生成です。
以下が該当部分です。
前のマイクの時と同じような処理ですが、今回は音声出力になりますので、引数の値が違います。
outputを trueに設定しています。

あと、今回大きく違うのが、stream_callbackという引数を設定していることです。
これは、音声出力タイミング(正確には次のバッファが欲しいタイミング)で呼ばれるコールバック関数を設定しています。
また、同時にframes_per_bufferという引数を設定しておけば、コールバックで設定するデータのサイズをしてできます。
ここでは、1波形分をそのまま設定しています。
(今回は、2秒間でサンプリング周波数が、44.1Khzなんで、88200データ分づつ渡すことように設定)

import pyaudio
p = pyaudio.PyAudio()
    
    stream = p.open( format = pyaudio.paInt16 ,
                     rate = 44100,
                     channels = 1, 
                     output=True,
                     frames_per_buffer = len(wave),
                     stream_callback=callback)

ストリームを開始と音声停止待ち

つついて、ストリームの開始と音声停止待ち処理です。
以下が該当部分で、2で生成したインスタンスのstart_streamというAPIを呼び出しし音声出力処理を開始しています。
※このあとから先ほど設定したコールバックが呼ばれるようになります。
あとは、音声の停止待ちです。
これは単純で、stream.is_active():というのが、ストリームが動作状態かを確認するAPIでこれが無効になるまで、待ち状態にしてあります。
※つまりここで、無限ループに入ります。

    stream.start_stream()
    
    while stream.is_active():
        time.sleep(0.1)

音声出力処理(コールバック)

続いては今回のメイン処理となります。
メインといってもデータを関数に返しているだけですが・・・
まず、frame_countには、frames_per_bufferで設定した値が入っています。
で、応答するデータはとデータのサイズを合わしているので、ただデータを応答するだけで問題ないです。
あと戻り値の第2パラメータに、pyaudio.paContinueを返してあげます。
これは、この後もまだ音声がありますよ!!ってライブラリに教えてあげるパラメータになりますので、これは絶対に入れるようにしてください。
※私は普段C言語の開発を行っているので、Pythonの気持ち悪くて使い勝手のよいところが出ました。。C言語では、戻り値に、第2パラメータを与えることは無理なので非常に気持ち悪いです。。。

def callback(in_data, frame_count, time_info, status):
    data = wave
    return (data, pyaudio.paContinue)

後処理

最後は後処理です。
最後は、動作状態にしてるStreamの停止処理と今回生成したインスタンス類の破棄をして終了です。

    stream.stop_stream()
    stream.close()
    p.terminate()
スポンサーリンク

まとめ

今回は、音声出力処理の方法しかも内製した正弦波をスピーカ出力してみましたが、いかがだったでしょうか?
音源さえ作れれば、あとは基本となるデジタル音声を理解していれば簡単に音声出力ができました。
私としては、しっかりコールバック処理により音声出力ができることが分かっただけでもちょっと感動しました。

最後に、Pythonの基礎を学びたい方は以下がおすすめです。私も持っていてたまに眺めて勉強していますものですのでぜひ購入して学習してみてください。

コメント

タイトルとURLをコピーしました