Python:pysoundfileを使って読み込んだwavデータをFFTをする

自分用メモです。

これ一つで取りあえずはwav読み込みとFFTのサンプルかねてるので参考になれば

import soundfile as sf
import numpy as np
import matplotlib.pyplot as plt
from scipy import fftpack

fname = 'whitenoise.wav' # mono
data, samplerate = sf.read(fname)


X = fftpack.fft(data)
freqList = fftpack.fftfreq(len(data), d=1.0/ samplerate)

amplitude = [np.sqrt(c.real ** 2 + c.imag ** 2) for c in X]  # 振幅スペクトル

# 波形を描画

# 振幅スペクトルを描画
plt.plot(freqList, amplitude, marker='.', linestyle='-',label = "fft plot")
plt.axis([0, 25000, 0, 2000])
plt.xlabel("frequency [Hz]")
plt.ylabel("amplitude")

f:id:gsmcustomeffects:20190107141330p:plain
ホワイトノイズ.wav(44100kHz)

Python:lmfitを使ったカーブフィッティング②

2020/5/6
追記この記事では実データでフィッティングを行っています。
データ自体はご自分で用意していただく形になるのでそのまま実行するだけでは動きません

あくまでlmfitの使い方の一例として捉えていただけると幸いです。
尚初期値、モデルの与え方によっては収束しない、フィッティングがうまく行かないことがあります。(理論を勉強すれば理由はわかるとおもいます。


はじめに

今回は前回の続きでlmfitを使ったフィッティングを試して行きたいと思う。
ほとんどここのパクリだがバグの修正含め解説も入れているので勘弁してほしい

Peak fitting XRD data with Python - Chris Ostrouchov

さっそくやって行こうと思うが
実用的なもので使わないと意味がないので今回はXRD(X-ray Diffraction)のピークフィッティングを例にしてフィッティングしてみたいと思います。
X-ray Diffractionは結晶性物質の解析に用いられる分析で結晶性物質の同定や結晶化度などを求めるときに使います。

以下に例としてゼオライトのXRDピークを示す。

XRDの解析ではこれらのピークをフィットしたりして使うことが多いと思います。
フィッティングにより非晶質ピークと結晶質ピークの面積がわかるのでそれらの割合から結晶化度を求めることが可能です。

全ピークフィットを行うと時間がかかりすぎるので範囲を絞って説明します。
今回は15度~21度範囲でのピークをフィッティングするスキームで進めます。


次にフィットモデルについてだがPeak-like modelsってのがLMFITには内蔵されている
XRDはピーク波形の集まりなのでそれを利用すると楽にフィットできそうである。
その中でもよく使うものを紹介しておく

GaussianModel


f(x; A, \mu, \sigma) = \frac{A}{\sigma\sqrt{2\pi}} e^{[ {-{(x-\mu)^2}/{{2\sigma}^2}}] }

LorentzianModel


f(x; A, \mu, \sigma) = \frac{A}{\pi} \big [\frac{\sigma}{(x - \mu)^2 + \sigma^2}\big ]

VoigtModel


f(x; A, \mu, \sigma, \gamma) = \frac{A \textrm{Re}/[ w(z)] }{\sigma\sqrt{2 \pi}}
where

\begin{eqnarray*}
    z &=& \frac{x-\mu +i\gamma}{\sigma\sqrt{2}} \\
    w(z) &=& e^{-z^2}{\operatorname{erfc}}(-iz)
\end{eqnarray*}


lmfitのモデル作成

まずはモデルを作る
def generate_model(spec)という関数を定義する
長いのでColabのリンクを貼っておく(リンク先のgenerate_modelって関数)
https://colab.research.google.com/drive/1At5-CseX7jhqPxjSyTk9gbIxC_dFRRU3#scrollTo=EdlAR8AoLbpa&line=26&uniqifier=1

途中でset_param_hintメソッドという新しい項目が出てきます。
これはモデルの最適化を行う際にある程度のヒントを与えるためのメソッドです。
各種説明は以下の通りです。

これ以外はPythonのビルトインメソッドの多様なのでゆっくり読み進めていけば困ることはないと思います。

次にこの関数に与える辞書リストをつくります。
いきなり作るのもあれなので
まずはピーク検出を行うための関数を作る。これにはScipyのfind_peaks_cwtメソッドを利用する。
こちらもColabのリンクで失礼する(update_spec_from_peaks)
https://colab.research.google.com/drive/1At5-CseX7jhqPxjSyTk9gbIxC_dFRRU3#scrollTo=YESpYSpYRyj6&line=2&uniqifier=1

簡単にモデルを与えてあげる

spec = {
    'x': theta[int((15-5.0)/0.02):int((21-5.0)/0.02)],
    'y': peak[int((15-5.0)/0.02):int((21-5.0)/0.02)],
    'model': [
        {
            'type': 'GaussianModel',
            'params': {'center': 26.5, 'height': 10, 'sigma': 1},
        },
        {
            'type': 'GaussianModel',
            'params': {'center': 26.14, 'height': 350, 'sigma': 0.1},
        },

        {
            'type': 'GaussianModel', 
            'params': {'center': 26.8, 'height': 10, 'sigma': 0.08},
        },         
        {
            'type': 'GaussianModel', 
            'params': {'center': 27.12, 'height': 10, 'sigma': 0.1},
        },
        {
            'type': 'GaussianModel',
            'params': {'center': 27.5, 'height': 10, 'sigma': 0.1},
        },
    ]
}

次にピーク検出したものをプロット

peaks_found = update_spec_from_peaks(spec, [0,1,2,3,4], peak_widths=(13,))
fig, ax = plt.subplots()
ax.scatter(spec['x'], spec['y'], s=4)
peaks = np.zeros((len(peaks_found),2))
j = 0
for i in peaks_found:
    
    ax.axvline(x=spec['x'][i], c='black', linestyle='dotted')
    ax.text(spec['x'][i],800, spec['x'][i])
    peaks[j,0] = spec['x'][i]
    peaks[j,1] = spec['y'][i]
    j = j+1

これを使うとある程度のピーク位置が出てくる

そのままフィッティングしてみる

model, params = generate_model(spec)
output = model.fit(spec['y'], params, x=spec['x'])
fig, gridspec = output.plot(data_kws={'markersize':  1})

仮フィッティング

だいたいあっているがフィットしていない部分もある。3,4番目のモデルが当たっていない感があるのでそこのパラメータの与え方を変えてみる

spec['model'][2]['params']['sigma'] = 0.05
spec['model'][3]['params']['sigma'] = 0.05

ベストフィット

綺麗にできたみたいなのでピークを分けてみてみる

fig, ax = plt.subplots()
ax.scatter(spec['x'], spec['y'], s=4)
components = output.eval_components(x=spec['x'])
print(len(spec['model']))
for i, model in enumerate(spec['model']):
    ax.plot(spec['x'], components[f'm{i}_'])

モデルごとのプロット

最後にベストパラメータを出して終わり

  def print_best_values(spec, output):
    model_params = {
        'GaussianModel':   ['amplitude', 'sigma'],
        'LorentzianModel': ['amplitude', 'sigma'],
        'VoigtModel':      ['amplitude', 'sigma', 'gamma']
    }
    best_values = output.best_values
    print('center    model       amplitude      sigma      ')
    for i, model in enumerate(spec['model']):
        prefix = f'm{i}_'
        values = ', '.join(f'{best_values[prefix+param]:8.3f}' for param in model_params[model["type"]])
        print(f'[{best_values[prefix+"center"]:3.3f}] {model["type"]:16}: {values}')
print_best_values(spec, output)

#--------------------------------------------------------------------------------------------
center    model       amplitude      sigma      
[17.460] GaussianModel   :  428.469,    0.129
[20.031] GaussianModel   :  253.663,    0.153
[19.180] GaussianModel   :  210.520,    0.122
[18.621] GaussianModel   :  254.223,    0.148
[15.726] GaussianModel   :  284.501,    0.123

まとめ

lmfitを使ったパラメータアシストフィットを行うことができた。
またモデルに推定ヒントを単一値を与えていたが範囲で与えることも可能なためその例も書いておいた。
今回のコード全容は以下にアップロードしているので皆さんのほうでカスタマイズしてみてほしい
https://colab.research.google.com/drive/1At5-CseX7jhqPxjSyTk9gbIxC_dFRRU3

今度はレーベンバーグマルカート法以外のメソッドで得たモデルパラメータを使った複合モデリングについて書いていきたい

Python:lmfitを使ったカーブフィッティング①

実験データのフィッティングについて頻繁に使う機会があったので自分メモとしてまとめておきます。
フィッティングを行うにあたり、Numpy , Scipyには便利なライブラリがあります。

  • Numpy :polyfit
  • Scipy:optimize.leastsq
  • Scipy:optimize.curve_fit

以上3つが結構有名です。簡単なフィッティングならScipy:optimize.curve_fitを使うのが一番楽だと思います。

Scipy:optimize.curve_fitを使った例

import numpy as np
import scipy.optimize
import matplotlib.pylab as plt

x = np.linspace(0, 2*np.pi, 100)
y1 = np.sin(x)
y2 = 0.5*np.sin(2*x)
y_noise =(y1 + y2)+ 0.08 * np.random.randn(100)

plt.scatter(x,y_noise)

parameter_initial = np.array([0.5, 1.0]) #a, b, c
# function to fit
def func(x, a, b):
    return np.sin(x) +  a*np.sin(b*x)

paramater_optimal, covariance = scipy.optimize.curve_fit(func, x, y_noise, p0=parameter_initial)
print(paramater_optimal)
fitting = func(x,paramater_optimal[0],paramater_optimal[1])

plt.plot(x,fitting,color="red")

ノイズ入り正弦波合成関数のフィッテング

このように数行でフィットまでできるので
データがそろっていてモデルがある程度絞れているならこの方法が一番早いです。

ですがちょっと複雑なことしようとするとlmfitに軍配が上がるかもしれません。

lmfitとは?

Non-Linear Least-Squares Minimization and Curve-Fitting for Pythonってサブタイトルがついてる通り非線形最小二乗法を用いたモデルフィットのためのライブラリです。

scipy.optimizeの多くの最適化方法を基にして拡張したものでLevenberg-Marquardt methodの移植からプロジェクトが始まり様々な追加がなされています。
ざっと列挙すると以下があげられる。

  • Parameter objectsを導入しモデルにアクセスしやすくした
  • フィッティングアルゴリズムの変更を容易にした
  • scipy.optimize.leastsqの信頼区間推定の改善
  • Modelクラスとのカーブフィットが改善(scipy.optimize.curve_fitの機能が拡張)
  • 一般的な線形の多くの組み込みモデルが内包されている。

lmfitを使ってフィッティングを行う

lmfitの導入ですがpipなら簡単にできます。

pip install lmfit

その他Scipy,Numpy,matplotlibなんかが必要だった気がするのでご自分に合った環境づくりをしてください。

サンプルコード

import os
import math
import random

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import optimize, signal

from lmfit import models

#gaussianを定義(ノイズ入りデータを作るためのダミーモデル)
def gaussian(x, A, μ, σ):
    return A / (σ * math.sqrt(2 * math.pi)) * np.exp(-(x-μ)**2 / (2*σ**2))
  
'''fitするためのノイズ入りデータの作成'''
g_0 = [250.0, 4.0, 5.0]
g_1 = [20.0, -5.0, 1.0]
n = 150
x = np.linspace(-10, 10, n)
# ここでは2つのガウシアンモデルを足し合わせて適当なグラフを作成
y = (gaussian(x, *g_0) + gaussian(x, *g_1)) + 0.05*np.random.randn(n)

fig, ax = plt.subplots()
ax.scatter(x, y, s=20)
'''-----------------------------------------------------------'''

'''lmfitの内蔵モデルの使用'''
model_1 = models.GaussianModel(prefix='m1_')#内包モデルのガウシアンを使用
model_2 = models.GaussianModel(prefix='m2_')#内包モデルのガウシアンを使用
model = model_1 + model_2#コンポジットモデルの作成


'''lmfitの内蔵モデルの初期パラメータセット'''
params_1 = model_1.make_params(center=3.0, sigma=1.0)#ガウシアンのパラメータセット
print(params_1)
params_2 = model_2.make_params(center=-7.0, sigma=1.0)#ガウシアンのパラメータセット
print(params_2)
params = params_1.update(params_2)#update methodにより辞書結合
'''
updateはPythonのビルトインメソッド(もとから入ってる標準関数みたいなもの
param_1とparam_2の辞書をまとめる機能がある
一般的に同名の辞書があるとその中身が上書きされてしまうがprefix='m1_'を付けているため上書きを避けることができる。

'''
output = model.fit(y, params, x=x)
fig, gridspec = output.plot(data_kws={'markersize': 5})

生データ

fittingするための元のデータ

フィッティング後

fitting後のデータ

フィッティング後の残差についても出してくれます。
fit後のベストパラメータはoutput.best_valuesに格納されます。

print(output.best_values)

'''-------------------------------------'''
{'m2_sigma': 4.995352955273267, 'm2_center': 4.000094565660837, 'm2_amplitude': 249.88570423583715, 'm1_sigma': 1.0041137260302269, 'm1_center': -4.998780785999739, 'm1_amplitude': 20.079858838765603}

ざっと書きましたがこんなもんです。
モデル指定してモデルパラメータを設定すれば簡単にフィッティングを行うことができます。
応用的な使い方やパラメータ拘束なんかは次回書こうと思います。

STM32のMCO2からPLLI2Sクロックを出力してみる

今回はSTM32のMCO(Master Clock Output)の機能を使ってPLLI2Sのクロックを出力してみる。
品質はどうなのかわからんけど主に外部オーディオコーディック用のMLCKとかに使えそうな予感

通常のMCO1に関しては以前やっている(STM32F3での実施)ので基礎に関しては以下の記事を参照
gsmcustomeffects.hatenablog.com

今回はSTM32F767を使うので上記の記事のレジスタ構成と多少異なる部分があるが基本は同じなのでリファレンスマニュアルを見るなりして対応してほしい

今回の試験環境

  • STM32F767 Nucleo144 board (評価ボード)
  • CubeMX Ver4.27(CubeF7 latest)(SDKというかHAL)
  • Atollic TrueStudio(IDE
  • RIGOL DS1054(オシロスコープ

というわけでやって行こう!

CubeMXでの作業

f:id:gsmcustomeffects:20181027232016p:plain

まずピンコンフィグ画面でMasterClockOutput2のチェックを入れる

f:id:gsmcustomeffects:20181027232647p:plain

次にクロック画面でPLLの設定とMCO2の設定をしてあげて24MHzあたりを出せるように設定してください

それが終わったらCubeから出力してください

AtollicTrueStudioでの作業

次にIDEでの作業ですがMCOの場合は何もいじらなくても初期コードだけで済みます。

まあそうはそうなんですがHALにバグ?みたいのがありましてクロックコンフィグ内の低レベルAPIを修正してあげないといけません

内容としてはSystemClock_Configの内部で呼ばれているHAL_RCCEx_PeriphCLKConfig内のI2SPLL部分です。
f:id:gsmcustomeffects:20181027233547p:plain

以上を修正したらビルドして書き込みしてください

見てみる

nucleoを使っているならここにオシロでもあててみてください

f:id:gsmcustomeffects:20181027234443p:plain

上手くできていればこのように波形が出ると思います
f:id:gsmcustomeffects:20181027234346p:plain

所感

MCO2からクロックを出力できた。

主な用途としてはエフェクターなどに使われているAK4558(32bit 192khz audio codec)のMLCKとかですかね。
f:id:gsmcustomeffects:20181027235224p:plain

24MHz与えると様々なサンプリングに対応できるのでこういったクロック出力機能は便利なんじゃないでしょうか?

bugfixへのアドバイス

最後に僕がどのような手順でデバッグしているかの話です。

まずはレジスタの値を確認する方法です。
f:id:gsmcustomeffects:20181027234233p:plain
SFRに関してはこの画面からレジスタに書き込んだりもできるので便利です。

最初行き詰ったらここを見てきちんとレジスタにされるべき設定が入っているか確認してください
もちろんこのSFRのjson定義にもバグがあるかもなので過信しすぎるのもよくないですけどね・・・・・

次に変数だったり構造体のトレースができる式タブですね
一般にはexpressionという名前で呼ばれています。

f:id:gsmcustomeffects:20181027234921p:plain

最近のマイコンではSDKで構造体を多用するのが増えていてそのメンバに対してif分岐するのが多いのでなんでこのループに入らないんだろう?とかbitシフト結果がほんとに正しいの?とかそういった疑問を実際に見れるのでHALの動作のステップ確認では便利です。

以上の二つを駆使していろいろ見てみると動作の勉強にもなりますし自身でfixできる癖がつきますので時間を溶かさないようにこういった機能を駆使してみてください。

STM32CubeProgでUARTを使ったフラッシュ書き換えを試す

STM32に関しては久しぶりですが
今回はSTからリリースされたSTM32CubeProgというソフトについてみていこうと思います。

STM32CubeProg

www.st.com

簡単に説明すると

STlink utility とかflash loaderとかdfuツールとかそういうのが一緒になってWindows,Mac ,Linuxに対応したものです。
以前できたことは一通りできます。

というわけで・・・・・

SWD接続とかDFUはいろんな方がやっているので
UARTブートローダーでもやってみようと思います。

UART bootloader

UARTからバイナリをぼこぼこ投げて書き込みできる機能。
特にまあたらしい物でもないからこんなもんで早速やって行きましょう。

今回使うものはSTM32F767 Nucleoを使うのでそれ用の設定で説明していきます。

STM32F767の場合UARTは以下のものが使えます。
f:id:gsmcustomeffects:20181020085513p:plain

んでまあ見てわかるんですが注意が必要でUART3ってSTLINKとつながってるのでそれが使えるやん?思う方もいると思うんですがところがどっこい
f:id:gsmcustomeffects:20181020085624p:plain

ブートローダーで使えるピンと違うとこからUART3を出してるのでSTLINK経由では使えません(このせいでなんでつながらないんだろうと5分位悩みました。
問題が整理できたとこでPC10,11が使えるとのことからこの辺から引っ張ってあげましょう

f:id:gsmcustomeffects:20181020090002p:plain

STLINKのTX RXピンから延ばしてもいいのですがシリアル変換器はこの辺の使ってます
f:id:gsmcustomeffects:20181020090144p:plain

次にbootloaderに遷移するための準備です。
STM32の場合は基本BOOT1とBOOT0の設定でフラッシュブートとsystem bootloaderから起動するか設定できるんですがF7の場合BOOTピン一個になりまして起動アドレス指定ができるようになりました。
f:id:gsmcustomeffects:20181020091839p:plain

初期状態でBOOTをHIGHにするとsystem bootloaderでいけるという感じですね。オプションバイトに関してはCubeProgで書き換えたり今の状態を見たりできるのでその都度確認してみてください。
ピンで言うとここです
f:id:gsmcustomeffects:20181020092138p:plain

ココまで終わるとフローチャート的にはこうなります
f:id:gsmcustomeffects:20181020092315p:plain

というわけでBOOTピンをHIGHにしてリセットしてUARTで接続してみましょう。
つながるとこのようになります。
f:id:gsmcustomeffects:20181020092536p:plain

先ほど示したオプションバイトはこのように確認できます。
f:id:gsmcustomeffects:20181020092623p:plain

書き込みはこの画面からできます
f:id:gsmcustomeffects:20181020093101p:plain

所感

というわけでかなり簡単にフラッシュの書き換えができるというわけです。
近年デバッガが安くなってきており持ってると特に使わない機能なんですが

エフェクターとか開発してると弾き手とかそういう人とやりとりする機会が結構あってアルゴリズム詰めてる時向こうが書き込み機を持ってないときとかに便利です。
あとは製品出荷後にアップデートとかしたいときに使えます。

あとF7ならDFUで書くほうが何もいらないのでいいと思いましたw

おまけ

ブートローダーについてCubeProgが自動でやってくれるので特に必要ありませんがマイコン間とかでファーム書き換えする場合は自前で打つ必要があります。
f:id:gsmcustomeffects:20181020093528p:plain

BOOTピンをHIGHにして該当UARTに0x7Fを送るとUARTブートするみたいです。
それを送るとACKがかえるみたいなのでTeratermで実験してみた。
f:id:gsmcustomeffects:20181020093833p:plain

Teratermを16進表記するとわかりやすいのでココを参考にしてみてください
shuzo-kino.hateblo.jp

デバッグmodeにできたら
信号を送信していきます。

ここでもTeratermのマクロ機能が便利です。

send $7F

とすると0x7Fを送ってくれます。

保存はこのような拡張子にします
f:id:gsmcustomeffects:20181020094134p:plain

次に接続設定です。
f:id:gsmcustomeffects:20181020094223p:plain

パリティEVEN以外は得に変わりはないです。

そうしたら上部メニューからコントロール/マクロを選択 先ほどのiniファイルを選択すると

f:id:gsmcustomeffects:20181020094332p:plain

きちんと79が帰ってきています。

そのあとはマニュアル通りにコマンドを打ってあげると書き込みできます。

ちなみにteratermのマクロはいろいろできるのでプロトコルとかそういった通信テストには便利だと思いますので覚えておくといいでしょう。

資料など

  • AN3155 Application note USART protocol used in the STM32 bootloader
  • UM2237 User manual STM32CubeProgrammer software description
  • AN2606 Application note STM32 microcontroller system memory boot mode

Python:ディレイの実装

今回はディレイを実装していきます。
いわゆる遅延系エフェクトでやまびこ効果とか言われるやつです。


ディレイの実装には参考文献に示すようにフィードバックの仕方によっていろいろな種類がありますが一般的なフィードバックありミックスありの通常のタイプをやって行きます。

f:id:gsmcustomeffects:20180820231509p:plain

まず時系列信号処理をするにあたってデータを保存するバッファを確保する必要があるのでリングバッファを使ってそれを実現します。
リングバッファの説明は参考文献の二つ目を参照してくれると理解しやすいと思う。

実装

というわけでさっそく実装していく

まずはエフェクトのパラメータであるfeedbackとmixとtimeを定義する。

delay_buffer_idx    = 0                                 #リングバッファのインデックス
feedback            = 0.4                               #フィードバック(0~1)
delay_ms            = 465                               #ディレイタイム[ms]
delay_buf_size      = int(frame_fs * delay_ms / 1000)   #バッファ確保[sample]
level                 = 0.1                               #level(0~1)

frame_fs は処理系によって異なりますが基本48000(48kHz)ぐらいだと思います。
48kHzサンプリングなので48000サンプルで1秒のディレイタイムが確保できる。
今回はPC上での実装なのでメモリはほぼ無限にあるがDSPだったり組み込みでの実装の場合限られた中で実装する必要もあるので頭に入れておくといいです。

次に入出力関係の整備を行う
ここはいつも通り入力バッファと出力バッファを確保しそれと今回使うディレイバッファを確保する。

input_buf    = in_data#wavデータだったり正弦波だったりを格納しておく
output_buf   = np.zeros(frame_size,dtype=np.float)#
delay_buffer = np.zeros(delay_buf_size, dtype=np.float)#パラメータのところで計算したサイズ分だけ確保する。

信号処理部分はコメントに示す通りの流れになる。

"""signal  processing"""
for i in range(frame_size):
    l_x = input_buf[i]*1.0+ delay_buffer[delay_buffer_idx] * level #入力(ゲインは1)+ディレイ
    delay_buffer[delay_buffer_idx] = input_buf[i] + delay_buffer[delay_buffer_idx]*feedback#入力とフェードバック分をバッファに書き込み
    delay_buffer_idx = int((delay_buffer_idx + 1) % delay_buf_size)#リングバッファをすすめる
    output_buf[i] = l_x#出力

これで処理自体は完了となる。
処理としてはメモリに入れて所定サンプル数経過後に原音と加算して出力してるだけなのでかなりコードが短い

最後に処理前後の波形を示しておきます。
f:id:gsmcustomeffects:20180821001507p:plain

音が切れてから残響効果により音が残る波形となっていることがわかる

サンプル音源

元音源

ディレイ

各種設定

"""delay param"""
delay_buffer_idx    = 0                                 #リングバッファのインデックス
feedback            = 0.4                               #フィードバック
delay_ms            = 465                               #ディレイタイム[ms]
delay_buf_size      = int(frame_fs * delay_ms / 1000)   #バッファ確保[sample]
level                 = 0.8                               #ミックス(エフェクトレベル)

コード全体

かなり走り書きなので参考程度にお願いします。

import numpy as np
import matplotlib.pyplot as plt
import create_func as cf
import wave
import audio_func as af
import struct
import scipy

wf = wave.open("GS04.wav", "r")#wav 扱うならお決まりのやつ
num_data = scipy.fromstring(wf.readframes(wf.getnframes()),dtype = "int16") / 32768.0#正規化
frame_fs = wf.getframerate()
frame_size = wf.getnframes()

"""delay param"""
delay_buffer_idx    = 0                                 #リングバッファのインデックス
feedback            = 0.4                               #フィードバック
delay_ms            = 400                              #ディレイタイム[ms]
delay_buf_size      = int(frame_fs * delay_ms / 1000)   #バッファ確保[sample]
level                 = 0.5                               #ミックス(エフェクトレベル)
if(wf.getnchannels() == 2):
    left = num_data[::2]#1 スライス
    right= num_data[1::2]
    in_data = left*0.5 + right*0.5#2ステレオモノラル化
    #1:スライスの説明
    #a[1,2,3,4,5]ていうリストがあったとして
    #a[::2]  -> 1,3,5
    #a[1::2] -> 2,4

    #2:ステレオ->モノラル化
    #もともとギターの録音で左右に同じ音ふってるのでLR分解して半分にして足せば同じようなもん

input_buf    = in_data
output_buf   = np.zeros(frame_size,dtype=np.float)
delay_buffer = np.zeros(delay_buf_size, dtype=np.float)


"""signal  processing"""
for i in range(frame_size):
    l_x = input_buf[i] + delay_buffer[delay_buffer_idx] * level #入力+ディレイ
    delay_buffer[delay_buffer_idx] = input_buf[i] + delay_buffer[delay_buffer_idx]*feedback#入力とフェードバック分をバッファに書き込み
    delay_buffer_idx = int((delay_buffer_idx + 1) % delay_buf_size)#リングバッファをすすめる
    output_buf[i] = l_x#出力

plt.subplot(211)
plt.plot(input_buf[0:frame_size])
plt.subplot(212)
plt.plot(output_buf[0:frame_size])
plt.show()

output = [int(x * 32768.0) for x in output_buf]
output = struct.pack("h" * len(output), *output)
af.play(output,wf.getsampwidth(),1,wf.getframerate()) #再生
#af.save(output, frame_fs,1,"delay2.wav")