Pythonで正弦波を生成して音を出す方法:PyaudioとNumpyを使った音声出力の解説

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

これまでは入力音声の制御について、記事にしてきましたが、今回は逆に音声を出力する方法を纏めてみます。
Wave音源をそのまま出力するのは面白くないなと思ったので、プログラム上で正弦波を生成して出力するプログラムにしてみました。
いや入力音声が知りたいんだけど!!って 方は以下にいくつか記事を書いていますのでチェックしてみて下さい。

↓は、ちょっと毛色が違って入力音声を文字起こしする方法についてもまとめていますので見てみてください。

使用ライブラリ

まず、今回使用したライブラリは以下の2つです。

  • pyaudio
  • numpy

この二つはこれまでに使用したことがあるので、インストール方法は画像がありません。すいません。
インストールには以下のコマンドを使用して下さい。XXXに上記のライブラリ名を入れればインストール可能です。

pip install XXX

プログラムと実行結果

ではいつも通りプログラブの全体です。

import pyaudio
import time
import numpy as np

wave = 0
totalcounter = 0

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ビット整数に型変換する
   

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

def main():
    create_sinewave()  # サイン波の生成
    p = pyaudio.PyAudio()
    
    stream = p.open( format = pyaudio.paInt16 ,
                     rate = 44100,
                     channels = 1, 
                     output=True,
                     frames_per_buffer = len(wave),
                     stream_callback=callback)

    stream.start_stream()
    
    while stream.is_active():
        time.sleep(0.1)
    
    stream.stop_stream()
    stream.close()
    p.terminate()
    
if __name__ == '__main__':
    main()

実行結果は以下です。
といっても何も表示されていないですが、実行してしばらくすると1Khzの音声が出力されます。
※1Khzは耳で確認しているので正確ではないかもしれません。。
あと終了処理をいれていないので、強制終了しています。。

続いてはプログラムの解説ですが、解説は次ページにて記載します。

プログラム解説

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

  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をコピーしました