haskellで周波数スペクトルを描画したはなし
大学のHな授業の課題が自由課題だったので適当に深夜テンションでつくりました。
demo
はすけるでがんばったっ pic.twitter.com/vGv1sNS4HW
— こくたん (@cocu_tan) February 9, 2016
haskellで周波数スペクトル描画したやつ、意外と綺麗に波形でてるのわかる(でもなんでか線対称にでてる pic.twitter.com/lFw88SpNOH
— こくたん (@cocu_tan) February 11, 2016
用いたライブラリ
- pure-fft
- 離散フーリエ変換のため
- WAVE
- .wav を読み込むため
- hscurses
- 可視化のため
- pulse-simple
- wavを再生するためにpulse audioのbinding
- pipes
- fftと可視化が重いのでマルチスレッド化
やったこと
だいたいこんなかんじ
`.wav`を`WAVE`で読み込み ↓[[int32]] ↓(時間xチャンネル) chunksOfでchunkに分ける ↓[[[int32]]] ↓(chunkNum x chunkSize x channelNum) pipesでstream化 ⇓[[int32]] ⇓ひとつのchunk ⇓(chunkSize x channelNum) 音量調節(map `div` 3) ⇓[[int32]] pulseaudioがLRLRLR…のような[int32]で受け取るのでチャンネルをconcat ⇓[int32] ⇓(chunkSize*channelNum) pulseaudioにwrite(Pipes.chainで) ⇓[int32] 別スレッドにstreamをつなげる ⇛ plotterOutput plotterInput ⇓[int32] ⇓(chunkSize*channelNum) chunkNum次元をdftすると遅いので16個ごとに取って雑に次元削減してベクトル作成 ↓[int32] ↓(chunkSize*channelNum/16) dftしてスペクトルの絶対値を取る ⇓[int32] ⇓(chunkSize*channelNum/16) ncursesでplotする
問題点
- ウンコード
- なぜか周波数に対して左右対称になってる
- 使用してるライブラリがおかしいのか自分の実装がおかしいのか
- なおすのめんどくさい
- (追記:サンプリング周波数/2で左右対称になるのは
ナイキストの定理
というらしい。なぜそうなるだろう。。。(勉強不足))
悩んだ点
- haskellは遅延評価なので、pipes使わないとbufferが遅延評価されてブツブツになる
- dft遅いのでリアルタイム処理るの大変
- 型があわない
- Monad書き慣れてないので、んんんっっっっ
- 型があわない
- pulseaudioのspecの型指定
Int16
と入力型Int32
と合ってなかったのに動いて謎な挙動した- モノラルだと(bitrateを倍にすると)問題なく聴こえる
- ステレオにすると右耳だけ聞こえる
- 割る2すると右耳は変わらず、左がノイズが聴こえる
- ↑この現象に遭遇してやっと型が違うことにきづいた
- 型があわない
- 型があわない
- 型があわない
- 型があわない
- 型があわない
本当に顧客が望んでいたこと
- ウェーブレット変換
- cmusみたいな便利なplayer
深夜テンションはこわい。敬具。