2010年9月26日日曜日

Pythonで少数を分数に変換する関数作った

前の記事で書いたwavファイルの速度調整用にスクリプト書いたので載せておく。

少数を分数に変換するための関数。


def Euclidean(m,n):
if m < n:
m,n = n,m
if n == 0:
return m
elif m % n == 0:
return n
else:return Euclidean(n,m%n)

def deciToFrac(deci):
i = 0
while deci % 1 != 0:
deci = deci * 10
i += 1

numer,denomi = int(deci),int(10 ** i)

gcd = Euclidean(numer,denomi)

return numer / gcd,denomi / gcd



下の関数に少数を渡すと通分された分数に変換し、分子と分母を返す。
上の関数はユークリッドの互除法。
これを使って分子、分母を通分している。


使用結果はこんな感じ。



>>> deciToFrac(2.6)
(13, 5)
>>> deciToFrac(1.2)
(6, 5)

PyAudioで早送り再生とかディレイとか

PyAudioでwavファイルを早送り再生したりディレイで再生したりしてみた。

早送りやディレイは音楽の波形データを時間軸上で伸縮させることにより実現できる。
縮ませると早く、引き伸ばすとゆっくり再生できる。

すでに音楽データを数値の配列として扱うことが出来ているわけだから、データ間を補間したり逆にデータを間引くことによりこれらを実現することができる。

試しにコーディングしてみた。


def delay(inp,rate):
outp = []

for i in range(len(inp)):
for j in range(rate):
outp.append(inp[i])

return array(outp)

def fast(inp,rate):
outp = []

for i in range(len(inp) / rate):
outp.append(inp[i * rate])

return array(outp)



引数rateは何倍に遅くしたい(or早くしたい)かを決定するためのもの。

そのまま使うと整数倍でしか再生スピードを調整できないけど、二つを組み合わせると自在に変更可能(1.1倍再生ならfast(data,11)とdelay(data,10)の組み合わせ、とか)。

簡単なコードだけど試してみたら結構面白かった。
是非お試しあれ。

※追記:上の二つの関数を再生スピード変更関数として一元化、PyAudioでwav再生するプログラムを作ってみた。


from scipy import *
import pyaudio
import wave
import sys

def changePlaySpeed(inp,rate):
outp = []
for i in range(int(len(inp) / rate)):
outp.append(inp[int(i * float(rate))])
return array(outp)

chunk = 1024

wf = wave.open("hoge.wav", 'rb')

p = pyaudio.PyAudio()

# open stream
stream = p.open(format =
p.get_format_from_width(wf.getsampwidth()),
channels = wf.getnchannels(),
rate = wf.getframerate(),
output = True)

# read data
data = wf.readframes(chunk)

# play stream
while data != '':
stream.write(data)
data = wf.readframes(chunk)
data = frombuffer(data,dtype = "int16")
if data != '':
data = changePlaySpeed(data,1.8)
data = int16(data).tostring()


stream.close()
p.terminate()



changePlaySpeed()の2番目の引数で再生スピードを変更できます(2.0だったら2倍速、0.5だったら半分のスピード)。

ゆっくり再生すると変な音が混ざった感じに聞こえる。
あまりイクナイ。

再生している音源が悪いのかしら?
データの補完方法に問題があるような気もするなぁ。

とりあえず試してみました、くらいの感覚で。

2010年9月24日金曜日

グラフの対数表示とか研究室のイベントとか

今日は所属研究室の初回ゼミ&歓迎会でした。

ということで酔ってます。

今日の更新はいつにも増して内容が薄いです。

周波数特性グラフをデシベル表示にしてみました、というだけ。


こんな感じ。





前の記事に掲載した内容とほぼ変わらす。


変更点は各周波数に対するゲインを求める式を以下のように変えたのみ。



>>> 20 * log10(f[i] / fmax)



最近リアルタイム音声処理の本とか読み始めたのでそちらについても書きたいなー。

2010年9月22日水曜日

一からデジタルフィルタ設計、Pythonで実装してみる(その2)

デジタルフィルタを一から設計してPythonで実装しよう!(タイトルまんま)
という企画の続き。

残る作業は
・数式内の定数を決める
・差分方程式を元にコーディング

たったこれだけの簡単なお仕事。


まず、数式内の定数を定める。

これは一番初めに決めた「信号から1000Hz以下の周波数成分をブっこ抜く」、という部分とデジタルフィルタの伝達関数から求めていく。

まずデジタルフィルタの伝達関数Hd(z)から周波数特性を得るため、z=e^(j2πfT)とする。
そしてその関数を用いて周波数特性を求めグラフにしたものがこちら。





遮断周波数(振幅が最大値に比べて√2/2になる周波数)が1000Hz付近になるようなRとCになっている。
R=1000、C=0.2uで決定。


これを差分方程式に代入してIIRフィルタとしてコーディングしてみる。



#coding:utf-8

from pylab import *
from scipy import *

def LPF(array):
output = []

R = 1000.
C = 0.0000002
Ts = 1 / 50000.

for i in range(len(array)):
if i == 0:
output.append(1/(R*C)*Ts*array[0])
else:
output.append(1/(R*C)*Ts*array[i] + exp(-1/(R*C)*Ts)*output[i-1])

return output

Fs = 50000.
Ts = 1 / Fs
t = arange(0,1,Ts)
f = 500

s1 = sin(2 * pi * f * t)
s2 = sin(2 * pi * 2 * f * t)
s3 = sin(2 * pi * 5 * f * t)

plot(t[0:200],LPF(s1)[0:200])
plot(t[0:200],LPF(s2)[0:200])
plot(t[0:200],LPF(s3)[0:200])
xlabel("time(s)")
legend(("500Hz","1000Hz","2500Hz"))

show()



結果がコチラ。





周波数特性と比較してみるとその通りにフィルタリングされている。

実際に周波数特性を求めたり、フィルタを作って分かったこと。
それは、アナログのローパス→インパルス不変法というデジタルフィルタの作り方だと、良い特性を持つLPFが作れないということ。
遮断周波数を過ぎてもすぐに減衰しない。

ということで実用的なデジタルフィルタを構築するには他の方式も勉強しておく必要があるようだ。

Pythonでフィルタリングツール

昨日の続き。
Python&matplotlibで移動平均フィルタリングアプリを作ってみた。
…とは言っても、かなりやっつけ仕事。

一応形にはなったので、画像とソースを張っておく。



左側がフィルタリング前の波形、右側がフィルタリング後の波形。
左下のスライダーがノイズ成分の強度調整、右下のスライダーが移動平均フィルタの強度調整。


以下が、ソースコード。


#coding:utf-8

from pylab import *
from matplotlib.widgets import Slider, Button

#FIR filter
def FIR(array,FIRCoefficient):
output = []

for i in range(len(array)):
temp = 0
for j in range(len(FIRCoefficient)):
temp += array[i - j] * FIRCoefficient[j]
output.append(temp)

return output

#Time axis
t = arange(0.0,1.0,0.0001)
f = 10
s = []

#Noise cofficient
nc = 1

#Noisy sine wave
for i in range(len(t)):
s.append(sin(2 * pi * f * t[i]) + nc * randn())

ax = subplot(121)
l, = plot(t,s)
axis([0,1,-10,10])
subplots_adjust(left=0.25, bottom=0.25)
xlabel("time(s)")
ax.set_position([0.1,0.5,0.35,0.3],"original")


#Filtered wave
FIRCoefficient = []
FIRlength = 10
for i in range(FIRlength):
FIRCoefficient.append(1/ float(FIRlength))
s2 = FIR(s,FIRCoefficient)
ax2 = subplot(122)
m, = plot(t[:len(s2)],s2)
axis([0,1,-10,10])
ax2.set_position([0.55,0.5,0.35,0.3],"original")


#Noise controll
axcolor = 'lightgoldenrodyellow'
axnoise = axes([0.1,0.3,0.32,0.03], axisbg = axcolor)
snoise = Slider(axnoise,"Noise",0.0,10.0,valinit = 1)

def update(val):
global s
nc = snoise.val
s = []
for i in range(len(t)):
s.append(sin(2 * pi * f * t[i]) + nc * randn())
s = array(s)
l.set_ydata(s)
FIRCoefficient = []
for i in range(FIRlength):
FIRCoefficient.append(1/ float(FIRlength))
s2 = FIR(s,FIRCoefficient)
m.set_ydata(s2)
draw()
snoise.on_changed(update)


#Filter length controll
axfilter = axes([0.55,0.3,0.32,0.03], axisbg = axcolor)
sfilter = Slider(axfilter,"Filter",5,30,valinit = 10)

def filtering(val):
global FIRlength
FIRlength = int(sfilter.val)
FIRCoefficient = []
#Moving average filter
for i in range(FIRlength):
FIRCoefficient.append(1/ float(FIRlength))
s2 = FIR(s,FIRCoefficient)
m.set_ydata(s2)
draw()
sfilter.on_changed(filtering)

show()


昨日作ったアプリと早いところ統合したいなぁ。
でも、その前にどっちのアプリも個別にブラッシュアップしとかないと。

2010年9月21日火曜日

PyAudioとmatplotlibで録音アプリ作った

今日、Twitterのタイムライン上に「JavaScriptでペイントアプリ作る。一日で」というのを行っている人がいたので、自分も何か作りたくなった。

ということでmatplotlibやらPyAudioの勉強がてら簡単な録音アプリをPythonで作ってみた。

速攻でコーディングしたせいでソースの見栄えが非常に悪いので(あとまだまだ不完全なので…)、GUIのスクリーンショットだけ載せておく。






左上のラジオボタンでサンプリングレートを、グラフ下部のスライダーをいじくると録音時間を設定できる。
Saveボタンで録音した音声をwav形式で保存、Recordボタンを押すと録音開始、という非常にシンプル(芸がないとも言う)なもの。


時間があったら、再生ボタンとかエフェクターとかフィルタとか色々実装してみたい。

こういうアプリを作ってると、GUIを自由にデザインして実装できるようになりたいなぁ、としみじみ思う。
matplotlibも悪くないけどやっぱりウィジェットの配置とかデザインとか一から全部手掛けたい。

Ajax(javascript)とかHTML5とかweb周りの技術触っときたいなー。

※参考にしたサイト
[1]wavファイルの保存処理
http://www.s12600.net/psy/python/02-1.html
[2]matplotlibのGUI周り
http://matplotlib.sourceforge.net/examples/widgets/

2010年9月19日日曜日

一からデジタルフィルタ設計、Pythonで実装してみる(その1)

これまで、デジタルフィルタの構成イメージを掴むため実際にPythonで実装してみた。

けど、本格的にフィルタを構築する場合、しっかりとした設計が必要になる。

ということで、今日はデジタルフィルタの設計について。



まず、大まかな設計の流れについて。
信号からある特定の周波数成分だけ抽出(あるいは除去)したいというニーズがあるとして、デジタルフィルタを設計するときは以下の6ステップを踏む必要がある。


(1)抽出したい成分の周波数を特定する
 目的とする信号とノイズを区別するため。

(2)目的に合わせてフィルタの種類(ローパス、ハイパス、バンドパスetc)を選択する
 高周波のノイズ除去ならローパス、FMラジオの電波受信みたいに特定の狭い周波数帯域だけ欲しい場合はバンドパス、など。

(3)選択したフィルタをアナログフィルタで実現した場合の伝達関数を求める
 この段階で得られた数式はs領域のもの。

(4)(3)で求めた伝達関数をデジタルフィルタの伝達関数に変換
 s-Z変換を用いて離散時間領域のシステムに変換する。

(5)(4)で得た伝達関数から差分方程式を導出
 伝達関数を式変形した後、z逆変換して導出。

(6)差分方程式を実際にコーディング


こんな感じ。
今までにやってきたフィルタの実装は(6)にあたる(設計がいかに大変な作業なのかがよく分かる…)。



練習がてら、上の6ステップに基づいて「信号から1000Hz以下の周波数だけ取り出すフィルタ」というものを1から設計してみる。


(1)周波数の特定
 上の題目から、取り出したい信号成分は1000Hz以下のもの。それ以上はノイズであるという前提条件を立てる。

(2)フィルタの選択
 「1000Hz以下の周波数だけ」とのことなので、ローパスフィルタを選択する。

(3)伝達関数導出
 回路理論の教科書を引っ張り出してローパスフィルタのアナログ回路構成と回路方程式、そして伝達関数を調べてみる。





図下部の数式は、回路方程式を立てる→ラプラス変換→H(s)導出、という流れになっている。
これで伝達関数H(s)が手に入った。

(4)s-Z変換を行う。これにはいくつか方法があるんだけど今回は「インパルス不変法」を使ってみる。
インパルス不変法はその名の通り、アナログフィルタのインパルス応答の標本値を利用する変換方式。

詳細の説明はコチラにゆずるとして、変換の過程はこんな数式。




めでたく、デジタルフィルタの伝達関数Hd(z)が手に入った。

※ここではオーソドックスな方法である、という理由からインパルス不変法を使った。けど本当は、前に決定した部分を振り返りながら、慎重にs-Z変換手法を選択しなければいけない。各変換手法にはそれぞれ得手、不得手があるからだ。
今回はローパスフィルタを試しに実装しているけど、実はインパルス不変法ではハイパスフィルタやバンドパスフィルタは実現できない
ということで、今回はサクっと設計手法を決めているけど、実際の設計では1ステップずつしっかりと進めていく必要がある。当たり前の話ではあるけれど、非常に重要かつ忘れがち…。


(5)差分方程式導出
差分方程式を求めるために、デジタルフィルタの伝達関数を式変形の後、z逆変換する。
…というと難しそうに聞こえるけど簡単で、ちょこちょこっと式変形を行うだけ。





最後に出てきた数式の中のvi(k)がFIRフィルタで実装する部分、vo(k-1)がIIRフィルタで実装する部分となる。

ここまで来たらあと少し。
いくつかの定数の値を決め、コーディングするのみ。

長くなってしまったので続きはまた今度。

※続きはコチラ

2010年9月18日土曜日

PythonでIIRフィルタ

Pythonで信号処理シリーズ。
IIRフィルタ編。

ホントはscipyのsignalモジュールにFIRフィルタとかIIRフィルタを実現するためのクラスだかメソッドだかがあるので、それを使えばいいと言う話なのだけど。
キチンと仕組みを理解してフィルタを使う、プログラミングの勉強の一環として、個人的な趣味、などの理由からフィルタをPythonで実装してみようという趣旨。


前置きはこのあたりにして、本題。


前の記事にも書いたけどIIRフィルタは出力のフィードバックをそのシステム内部に含むデジタルフィルタ。

出力のフィードバックを含むというのは、プログラムの観点から言うと「フィルタ自身が過去の出力を保存しており、適宜それを使用する仕組みを持っている」ということを意味している。

これに対して、FIRフィルタは「過去の入力を保存しておき、それを適宜使用する」といったものだった。
このFIRフィルタにフィードバック機構を備え付けたものがIIRフィルタだ。
イメージとしては

FIRフィルタ+フィードバック機構=IIRフィルタ

といったような感じ(と、自分は捉えてる)。


それを踏まえてココを参考にIIRフィルタを以下のような形で設計してみた。





中央の加算器を挟んで左半分がFIR部分、右側がIIR部分となっている。
今回はIIRフィルタの影響のみを調べたいので、FIRフィルタの係数はインプットをそのまま出力する形となっている。


んでソースは下のようなものに。



#coding:utf-8

from scipy import *
from matplotlib.pylab import *

def IIR(array):
output = []

#係数格納行列
FIRCoefficient = [1,0,0]
IIRCoefficient = [0.7,0.2,0.1]

for i in range(0,len(array)):
temp = 0

for i in range(len(array)):
temp = 0
for j in range(len(FIRCoefficient)):
temp += array[i - j] * FIRCoefficient[j]
if len(output) > j:
temp += output[i - j - 2] * IIRCoefficient[j]

output.append(temp)

return output

Fs = 100.
t = arange(0,1,1/Fs)
f = 5

inp = []

#入力信号生成(正弦波+ノイズ)
for i in range(len(t)):
inp.append(sin(2 * pi * f * t[i]) + rand())

outp = IIR(inp)

plot(t,inp,"b")
plot(t,outp,"r")
xlabel("time(s)")
show()



出力結果がコチラ。





…発振してしまっている。

どうやらIIRフィルタの係数設定はかなり難しいみたい。
FIRフィルタのときは直感的に係数を決めて、目で見て分かるくらいの効果があるフィルタを作ることができた。
でもIIRの場合、そううまくはいかないようで。

係数の設定や構成など、設計段階で考慮しなければいけないことが多いみたいだ。
ということで次回はデジタルフィルタ設計の勉強をすることにするか。

PyAudioでピッチ検出

これまで、信号処理のアルゴリズムと音声入力を取り扱う方法について色々勉強してきた。
その中からPyAudioとAMDFアルゴリズムを組み合わせて、マイクから音声を拾って音程を探り当てる、ということをやってみた。

まず、マイクに向かって「ド」の音階を出すつもりで発声してみる。





拡大してみると





それらしい波形になっている。


これにAMDFをかけたものがコチラ。





おおよそ0.0078(s)のあたりにピークが来ている。
これが声の基本周期だと考えると周波数は、1 / 0.0078 ≒ 128 (Hz)となる。

このサイトによると低い「ド」の音は131(Hz)とのこと。
そんなに大きくハズレてはいない模様。

少しホッとした。


現時点では、録音してAMDF結果を表示するまでの流れでプログラムが終了してしまうので、これをリアルタイムで出来るようにしたい。

2010年9月17日金曜日

信号処理の勉強その2

引き続き信号処理の勉強。

デジタルフィルタについて調べていた。
以下ざっくりとしたまとめ。

(1)デジタルフィルタとは
・フィルタとは信号から特定の周波数成分を抜き出す、あるいは除去するためのモノ
・離散値を処理するフィルタをデジタルフィルタという
・デジタルフィルタには線形フィルタと非線形フィルタがある(現時点ではこういうくくりがある、というぐらいで留めておく)
・デジタルフィルタは3つの基本的な構成要素から成る
 ー遅延素子:1クロック分、信号成分を遅延させて出力する素子
 ー加算器:入力された信号成分を足し合わせて出力する素子
 ー乗算器:入力された信号成分を定数倍して出力する素子

(2)FIR(Finite Inpulse Responce)フィルタ
フィードバックループが無いデジタルフィルタ。
インパルス応答が有限個のパルスで表されるからFIRフィルタ(らしい)。

(3)IIR(Infinite Inpulse Responce)フィルタ
フィードバックループを持つデジタルフィルタ。
インパルス応答が無限に続くインパルス列になるのでIIRフィルタ(らしい)。

全部ココを参考にして2、3行にまとめてみた(ザックリしすぎな感は否めない)。


ということで毎度お馴染みPythonでFIRフィルタを実装してみる。

ざっくり下のような感じで設計。
四角が遅延素子、三角が乗算器(内側の数字が係数)、丸が加算器。




んで、コーディングした結果がこちら。



#coding:utf-8

from scipy import *
from matplotlib.pylab import *

#FIRフィルタ
def FIR(array):
output = []

for i in range(2,len(array)):
output.append(array[i] * 0.7 + array[i-1] * 0.2 + array[i-2] * 0.1)

return output

Fs = 100.
t = arange(0,1,1/Fs)
f = 5

inp = []

#入力信号生成(正弦波+ノイズ)
for i in range(len(t)):
inp.append(sin(2 * pi * f * t[i]) + rand())

outp = FIR(inp)

plot(t,inp,"b")
plot(t[2:len(t)],outp,"r")
xlabel("time(s)")
show()



出力結果がコレ。




青が入力信号で赤が入力をフィルタリングした信号。

少し分かりづらいけれど、よく見るとフィルタリングを行う前と後で波形のギザギザが少なくなっていることが分かる。これは高周波のノイズがフィルタによって除去されたことを意味する。
ごくごく簡単なローパスフィルタ(低周波数の信号のみ通すフィルタ)が完成した。

ということで遅延素子を用いるとローパスフィルタを作ることが出来る、ということがこれで分かった。

2010年9月16日木曜日

キーボードチェッカー

「ハッカープログラミング大全」という本を読んでいる。

その中の「キーストロークを監視する」プログラムが面白かったのでPythonで実装してみた。
ということで公開…したかったけど、ちょっと思いとどまった。
簡単に悪用されてしまいそうなので、止めておく。
実際にパスワードを盗みだす手段として一般的なものとなっているみたい。
参考:「キーロガーの悪用が急増中」(2007年)


ただせっかく作ったのに何も残さないのは勿体無いので、参考までにPythonでキーロガーをプログラミングする上で大事そうなポイント2つ、書き出してみる。


・キーボードチェックのループは0.02s間隔で回すと実際のキーストロークと(多分)同じ間隔になる
・キーの状態は4種類(カッコの中はキーチェック関数の戻り値)
 ー押した瞬間(-32767)
 ー押しっぱなしの状態(-32768)
 ー離した瞬間(1)
 ー押されていない状態(0)


この技術、悪用するというのは問題外なので考えないとしても、色々面白い使い道がありそう。

最近、キーストロークのクセで個人認証を行う技術が発表されていたし、単純にログを取ってみるだけでも何か見えてきそうな気がする。

それ以外に思いつくものとしては、
ゲームをプレイしている人のキーボードのデータのログを取って初心者がそれを参考にするとか。
あるいはソースコードや文章のバックアップ手段として使うとか(復元がかなり面倒臭そうだけど)。
あとはキーストロークのクセを見ぬいて、「ここを改善するともっとキータッチが早くなりますよ」とか教えてくれるアプリとか。

1、3番目は難しそうだけど2番目くらいなら出回っててもおかしくないんじゃないかと。

2010年9月15日水曜日

AMDF

AMDF(Average Magnitude Deference function)を用いたピッチ検出を試してみましたよ、というお話。

AMDFは自己相関、相互相関関数のようにピッチ検出に用いられるアルゴリズムのひとつ。

(※2010/10/12追記:OEDEC2007のプレゼン資料にAMDFの分かりやすい説明が載ってます)

ココとかココ(こちらはIEEEの認証が必要)を参考にAMDFをPythonで実装してみた。


def AMDF(array,frameLength):
amdf = []
length = len(array)
for i in range(frameLength):
temp = 0
for j in range(length):
temp += abs(array[j] - array[j - i])
amdf.append(temp / length)
return amdf



この関数が正しく動作するか確認するため、下のような波形を用意する。



周波数100Hzの方形波にノイズを加えたもの。


この波形にAMDFを適用して計算した結果がこちら。



0.01sに最小値のピークが出ている。
相互相関関数と違って、AMDFでは最小値となる点が周期性を表していることが分かる。

2010年9月14日火曜日

信号処理の勉強

SciPyも無事導入出来たということで、信号処理を勉強し始めた。
ということで調べたことをメモ。

・自己相関関数
時間領域において波形の周期性を調べる際に用いられる数学的手続き。
その波形自信の畳み込み積分を計算することによって求められる。
音声のピッチ検出などに応用されている。

・相互相関関数
2つの波形の類似性を調べるための数学的手続き。
自己相関関数と同様ピッチ検出などに応用されている。
2つの波形の畳み込み積分を計算することによって求められる。

どちらもピッチ検出に用いられているけれど、相互相関を使った方が自己相関を使うよりも計算負荷が低くいのでより良いとのこと。

毎度お馴染みPythonだったら、2つともnumpy.correlate()で計算可能。

ただし、自己相関関数を求める場合は下記のようにmodeに"same"を指定する必要あり。


>>> correlate(signal,signal,"same")


ただ使うだけじゃ面白くないので相互相関関数を自分で実装してみた。
CCFは(Cross Correlation Function:相互相関関数)の略。

def CCF(array1,array2):

if len(array1) < len(array2):
array1,array2 = array2,array1

ccf = []

for i in range(len(array1) - len(array2)):
temp = 0
for j in range(len(array2)):
temp += array2[j] * array1[j - i]
ccf.append(temp / len(array2))

return ccf


この関数を用いて10Hzの正弦波(サンプリング周波数1000Hz)と、その正弦波の先頭から1周期分の波形の相互相関を求めてみる。



青が元の正弦波、赤が正弦波の先頭1周期、緑がそれら二つの波形の相互相関。

相互相関の値が0,100,200…と100の倍数で最大値をとっている。

これはつまり、この正弦波が100ms(10Hz)の周期の波形であること示している。
…ということでキチンと計算できているようだ。

SciPyのsignalモジュール

オーディオ入力にフィルタをかける必要が出てきたので、SciPyをインストールしてみた。

で、早速信号処理用モジュールscipy.signalをインポートしてみると…エラーダイアログが出てくるのみ。
IDLEがリスタートしてしまって、インポート失敗している。

自分がインストールしたバージョンは0.8.0。
しょうがないから0.7.2バージョンをインストールし直したら、何とかインポートできた。

(途中ココも参考にして書いてある通りに試してみたけど、うまくいかなかった)

自分のPCはwindowsマシン、Pythonのバージョンは2.6。
同じような環境でこんな症状が出たら0.7以下のバージョンをインストールするのがいいのかな、と思います。

2010年9月13日月曜日

PyAudioリベンジ

引き続きPyAudio。

前回、オーディオデータをフレーム単位で扱う必要がある、ということに気づいた。

そのことを意識してPyAudioのサンプルソースを見ていたら、何やら関係がありそうな記述が。


FORMAT = pyaudio.paInt16

p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
output = True,
frames_per_buffer = chunk)


どうやらオーディオストリームを開く時に、データフォーマットをint16で定義していたみたい。
きちんとソースは読まなくては…。

ということでこの場合、1フレームは16ビット=2バイトということがわかった。

ordにいちいち一つずつデータを渡すのは面倒なのでnumpyのfrombufferメソッドに型指定して渡すと


>>> a = frombuffer(data,dtype = "int16")
>>> a
array([ 1, -4, -4, ..., 0, 0, 0], dtype=int16)


dataは無音環境で録ったマイクからのデータなので、それらしい値になっている。
めでたくオーディオデータを数値として取り出すことができた。


これまでの経緯をもとに録音、表示プログラムを改良したものがこちら。

from pylab import *
from numpy import *
import pyaudio
import sys

chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 10

p = pyaudio.PyAudio()

stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
output = True,
frames_per_buffer = chunk)

inp = []

print "* recording"
for i in range(0, 44100 / chunk * RECORD_SECONDS):
data = stream.read(chunk)
inp.extend(frombuffer(data,dtype = "int16"))

print "* done"

plot(inp)
show()


出力結果は下の画像。



マイクに息を吹きかけたりかけなかったりした波形。

うまくいっているようだ。

ちなみにいろいろ調べている途中でPyAudio使って音楽ファイルにFFTかけたり、フィルタリングするコードが載ったサイトを発見した。参考までに。

オーディオデータについて

前の記事の続き。

オーディオデータがシステム内部でどういう風に扱われているのか。

pythonでwavモジュールをいじくっていたら何となく分かってきた。
以下のような形で適当なwavファイルを読み込んでデータを表示させてみた。


>>> w = wave.open("filePath","r")
>>> w.readframes(5)
'\x00\x00\x00\x00\xdb\xff\xdb\xff\xb5\xff\xb5\xff\x8f\xff\x8f\xffj\xffj\xff'


readframes()は指定されたフレーム数、データを読み出すメソッド。
で、出力されたデータを見る限り、1フレームというのは1バイトでは無いらしい。

1フレームが何バイトなのか調べてみる。


>>> r = w.readframes(5)
>>> len(r)
20


5フレーム20バイトなので1フレームは4バイト=32ビット。

よくよく考えて見ればオーディオデータの量子化ビット数が1バイトなんてことはありえないわけで。
前の記事のようなグラフが出力されるのは当たり前だ。

ということで、オーディオプログラミングではフレーム単位でデータを扱う必要があることが分かった。

2010年9月12日日曜日

PyAudio使ってみた

PyAudioを使ってオーディオ入力を取り込むプログラムをゴリゴリいじっている。取り敢えず
マイクから入力→数値データに変換→matplotlibでグラフ化してみました
というお話。

PyAudioのサイトにサンプルとして載っている、マイク入力をスピーカーからそのまま出力するプログラムを改造した。


from pylab import *
import pyaudio
import sys

chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5

p = pyaudio.PyAudio()

stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
output = True,
frames_per_buffer = chunk)

inp = []

print "* recording"
for i in range(0, 44100 / chunk * RECORD_SECONDS):
data = stream.read(chunk)
for j in range(len(data)):
for k in range(len(data[j])):
inp.append(ord(data[j][k]))
print "* done"

plot(inp)
show()



マイクから取り込んだ入力はそのまま出力すると下のようにバイトコードで出力されてしまうので、ord関数を使って数値データに変換している。

>>> data[0]
'\x00'


このプログラムを実行した結果、以下のような感じに。



…何が何だか分からない。

録音したのは無音環境だったので、ゼロ付近に張り付くグラフを予想していたんだけれど…。
単純に音の強弱が数値に変換されている訳じゃないのかな?

次はマイクから入力されたデータがシステム内部でどう取り扱われているかについて調べてみよう。