Pythonでsin波を鳴らして遊んでみた。

Pythonで公衆電話とか昔の電話にあった「ピッ」「ポッ」「パッ」って感じの音を鳴らしみました。

 

環境
  • MacBookAir(mid 2013)
  • Python 2.7(pyenvでインストールしたanaconda-2.1.0)
  • pyaudio
    pyaudioのインストールはこちらの記事を参考にさせていただきました。
    [MacでPyAudioを使ってみる. | miettalの日記]途中、ライブラリのシンボリックリンクを作成する説明(sudo ln -s うんぬん)があるのですが、こちらを行わなくても利用できたので、実行しませんでした。
DTMF(ピッポッパの音)について
DTMF(英: Dual-Tone Multi-Frequency)は、0から9までの数字と、*、#、A、B、C、Dの記号の計16種類の符号を、低群・高群の2つの音声周波数帯域の合成信号音で送信する方法である。別名「トーン信号」「プッシュ信号」とも呼ばれ、その信号音は人間の可聴域にあるため日本語では「ピ、ポ、パ」とも擬音語表記される。 プッシュ式電話回線での電話番号の送出、その他音声回線での数字入力(例・コールセンターでの着信後の項目選択)などで用いられる。DTMF | Wikipedia

通信の際にそれぞれの数字や記号に2つの周波数の合成はを対応させたものです。それぞれの対応は下記の表のようになります。

高群[Hz]
1209 1336 1477

[Hz]

697 1 2 3
770 4 5 6
852 7 8 9
941 * 0 #

つまり、それぞれの数値に対応した2つの周波数を足し合わせたsin波を出力することによてあの「ピッポッパッ」という音を鳴らすことができます。例えば、”5″の場合は1336[Hz]と770[Hz]の合成波で表現されます。

 

実装

pythonでsin波を出力する方法については、以下の記事を参考にさせていただきました。とてもわかりやすいソースコードだったので、これらについての解説は省略したいと思います。
[正弦波の合成 | 人工知能に関する断創録] こちらのソースコードのうち、一番上にあるソースコードを参考にさせていただきました。

 

その他、自分で行った変更とか

数値に対応した周波数のペアを返す関数

# 数値に対応したdtmfの周波数ペアを返す
# 引数は、文字を受け取る
# A,B,C,Dには未対応
def dtmf(number):
  freq_row = ( 697, 770, 852, 941)
  freq_col = ( 1209, 1336, 1477, 1633)

  if(number == '0'):
    row = 3
    col = 1
  elif(number == '#'):
    row = 3
    col = 2
  elif(number == '*'):
    row = 3
    col = 0
  else:
    num = int(number)-1
    row = int(num/3)
    col = int(num%3)

  return ( freq_row[row], freq_col[col])

DTMFは0~9の数字と#,*,A~Dの16種類に対応した周波数が用意されていますが、今回はA-Dの文字は使わないので省きました。一つ目の戻り値が低群の周波数、2つめが高群の周波数となっています。

次にsin波を作成する関数を編集します。(最終的に作成したソースは後ほど↓↓)
createSinWave関数の引数の周波数を2つにして、さらにsin波を作成する際に二つの波の合成波を用います。また、合成波にした際に振幅が大きいときにその成分が切られてしまうことを避けるためにcreateSinWave関数に渡す際の引数Aの値を1.0->0.5に変更しました。
また、使用に合うようにmain関数を変更しました。

#7行目
def createSineWave (A, f0, fs, length):
#変更後
def createSinWave (A, f0, f1, fs, length):

#13行目
s = A * np.sin(2 * np.pi * f0 * n / fs)
#変更後
s = A * (np.sin(2 * np.pi * f0 * n / fs) + np.sin(2* np.pi * f1 * n / fs))

これで、createSineWave関数は2つの波の合成から出力されるようになりました。

完成したソースコードはこちら。

#coding: utf-8
# dtmf.py
# 電話のピッポッパッって音を鳴らしてみる。

import wave
import struct
import numpy as np
from pylab import *

def createSineWave (A, f0, f1, fs, length):
 """振幅A、基本周波数f0、サンプリング周波数 fs、
 長さlength秒の正弦波を作成して返す"""
 data = []
 # [-1.0, 1.0]の小数値が入った波を作成
 for n in arange(length * fs): # nはサンプルインデックス
 s = A * (np.sin(2 * np.pi * f0 * n / fs) + np.sin(2* np.pi * f1 * n / fs))
 # 振幅が大きい時はクリッピング
 if s > 1.0: s = 1.0
 if s < -1.0: s = -1.0
 data.append(s)
 # [-32768, 32767]の整数値に変換
 data = [int(x * 32767.0) for x in data]
 # plot(data[0:100]); show()
 # バイナリに変換
 data = struct.pack("h" * len(data), *data) # listに*をつけると引数展開される
 return data

def play (data, fs, bit):
 import pyaudio
 # ストリームを開く
 p = pyaudio.PyAudio()
 stream = p.open(format=pyaudio.paInt16,
 channels=1,
 rate=int(fs),
 output= True)
 # チャンク単位でストリームに出力し音声を再生
 chunk = 1024
 sp = 0 # 再生位置ポインタ
 buffer = data[sp:sp+chunk]
 while buffer != '':
 stream.write(buffer)
 sp = sp + chunk
 buffer = data[sp:sp+chunk]
 stream.close()
 p.terminate()

# 数値に対応したdtmfの周波数ペアを返す
# 引数は、文字を受け取る
# A,B,C,Dには未対応
def dtmf(number):
 freq_row = ( 697, 770, 852, 941)
 freq_col = ( 1209, 1336, 1477, 1633)

 if(number == '0'):
 row = 3
 col = 1
 elif(number == '#'):
 row = 3
 col = 2
 elif(number == '*'):
 row = 3
 col = 0
 else:
 num = int(number)-1
 row = int(num/3)
 col = int(num%3)

 return ( freq_row[row], freq_col[col])

if __name__ == "__main__" :
 # numberList = ( 1, 2, 3, 4, 5, 6, 7, 8, 9, '*', 0, '#')

 number = raw_input("number or * or # :")
 numberList = list(number)

 for num in numberList:
   freq = dtmf(num)
   data = createSineWave(0.5, freq[0], freq[1], 8000.0, 0.5)
   print("%c %d[Hz]+%d[Hz]" % (str(num), freq[0], freq[1]))
   play(data, 8000, 16)

実行例(177#を入力した場合)

shell% python dtmf.py
--> number or * or # :177#
--> 1 697[Hz]+1209[Hz] # ♪
--> 7 852[Hz]+1209[Hz] # ♪
--> 7 852[Hz]+1209[Hz] # ♪
--> # 941[Hz]+1477[Hz] # ♪

それぞれの音が0.5秒ずつ鳴ります。

1出力波形

“1”出力波形

1出力波形 フーリエ変換

“1”出力波形 フーリエ変換

どうやら、正しく出力されていそうです。音も、それっぽい音…なのかな。。。

 

まとめ

今回は、 Pythonでsin波を作成して音を出力してみました。学校の授業で最近pythonを用いたfft(高速フーリエ変換)などを利用したので馴染みやすい分野でした。

この電話のピッポッパッとい音は2つの周波数から作成されているということは、数年前にみた名探偵コナンの映画で知りました。どこで得た知識が役に立つのかわりませんねぇ〜w
映画だと、受話器を外してこの周波数の音を鳴らせば電話がかけられるみたいな描写があったのですが、実際どうなんでしょう?公衆電話を使って177あたりで試してみたいですね。

どうでもいい話ですが、自分は「ピッポッパッ」だと思っていたのですが、Wikipediaでは「ピ、ポ、パ」と表現されていましたね。これが普通なのでしょうか?w

 

以上、Pythonでsin波を出力するでした。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください