MPDとRustとRaspberryPiで遊んでみる7

前回からちょっと間が空きました。うーん Rust とちょっとずつ仲良くなっているよーな気がします。 最終的には、Rust で MPDフロントエンドを作ろうと思っており、鋭意進めております。

ですが、ちょっと脇道にそれて実験をしてみました。

MPD の fifo 出力で遊んでみる。

MPD が再生しているときに、その PCM データをネームドパイプに出力できるよーです。

ってことは、それを使ってスペアナが作れるかも!

っと思い立ったら、モー止まらなくなりました。

で、ひたすらグーグル先生にお尋ね回りまして、いろいろ拾いまくった挙句なんとか形になりました。

github.com

やったぜ!

パラパラしたりパタパタしたり、右チャンネルが消えてしまったりしてますが、 レートの低い GIF にしたためです。実際もちょっとマシにアニメします。

いやぁ、大変でした。

何が大変って、FFTなぞさっぱり分からない人間がどーやったらかっちょいい感じにリアルタイムでピコピコ動く物を作るのかってのが、いやはや難題でした。

FFT高速フーリエ変換)あるいは、DFT(離散フーリエ変換)という物があるのは知っていたのですが、どーやって使うのかが、さっぱりわからなかったのです。

WEVEデータ -> FFT -> スペクトル

これは理解できます。ただ、この「スペクトル」となったデータ列を、どーやったら見えるものにできるのか?という点に関して、なかなかバシッと答えを書いている記事が見つけられなかったのです。

FFT の理屈を説明する記事や、スペクトルをグラフ化している記事は多々あるのです。しかし、グラフ化していても、それの数値の単位やスケールなどバラバラで、値の大小は分かるのですが、それがバシッと何なのか?というてんでは、皆さん興味が無いよーで、単位もバラバラ、どういう範囲で値がとれるのかも不明。その値の加工方法もさっぱりで、なかなかそこから先どーすればいいかがわかりませんでした。

私のよーな無学のエセ錬金術師にとっては、鶏が卵を産むように、卵(と鶏)にしか興味がなく、その間プロセスは正直そこまで大事ではないです。いわゆるこんな感じ。

鶏         -> どーでもいい -> 卵
WEVEデータ ->       FFT      -> スペクトル

もっとひどく言えば、桶屋が儲かった話では、桶屋にしか興味がないわけで、風がどーなったかはまぁ...

あとは、卵の料理の仕方なのですが、それが分からん!

イメージとしては『周波数ごとに、0 から 100 までの幅を持つ値が取りたい』のです。

結局それを調べるために、何日も検索し続けたりしてたんですが、なんかピカっと光る記事は見つけられませんでした。挙句には、何とかヒントをと Interface (インターフェース) 2016年 6月号 まで買って読んだのですが、うーん。でした。

しかし、逆に言えば、たいした決まりは無いのかもしれない。と思い立った瞬間、『好きなように加工してしまえ』と半ばやけくそでテキトーな倍率や補正関数をかけてみたら、なんかちょっとソレっぽい動きをする数値がとれるよーになり、いけるかもーとなりました。

で、そこまで行くと可視化したいわけですが、ここでも詰まります。

パッと可視化したいわけですが、相手が音楽だけにリアルタイムで変化する可視化となります。 ふつうは GUI ってことになりますが、そーすると GUIライブラリだとかのお勉強で、また遠回りとなります。 そーでなくても Linux, Windows とか GTK, Qt, etc... と、最初の壁がちょっと高いわけです。しかもまだRust不慣れだし。

何とかならんもんか...

GUIツールキットは避けたいなと思い、画像を細々と出して、ffmpeg で動画にしてはどーかと思いました。 しかしこれも、なかなかメンドクサイ。PNGを作るとしても、描画処理かかなければならないですからね。なにがしかのライブラリの世話になることになり、そこそこ手間です。Rust不慣れだし。

SVGを出力して、画像にするのは外部プログラム使うという手を思いつきました。

で、やってみたのですが、今度は音楽と同期させるのがむつかしいことに気が付きました。 そこそこ出音と合う動画をつくろうとしたら、出音の経過時間に対してピッタリあった画像を生成しなければいけないのですが、相手が単なる waveファイルなら、まだ計算できるものの、mpd の fifo は、生の PCMデータがひたすら流れ続けているだけでどこが最初かすらよくわからな物ですので、ずれたらどれだけズレたのかすら分かんないわけです。(実際プログラムが完成したら、そーでもなかったわけですが...)

自分のプログラムのバグでずれているのか、そもそもズレを認識するにはどーすればいいか、などなどよけーややこしそーだなぁ。とこの案は却下としました。

やっぱりリアルタイムでアニメしないとダメだ。第一そのほーが見てて楽しいじゃないか!音楽に合わせてピコピコうごかしたいじゃないか!

GUIがだめなら、CUIか。ncurses か。rust で ncurses も、そこそこメンドクサイ。だって ncurses つかったことないし...

っとそんなところに、こちらの記事

meganehouser.github.io

この記事と、この方のプログラムが、正に私にとっての福音でした。 そーか、ANSIエスケープシーケンスで、コンソールに出力しまくればそこそこアニメできるんだ!

この方のソースコードを拝借( git clone ) して動かしてみたら、結構動くじゃない~ アニメ速度を決める定数をかなり早くしても、そこそこ動くんです。15fpsくらいは行けてるみたいでした。 これは十分です。コードもチョー短いし。なんたって、単なるながーい文字列つくって、標準出力にだすだけですから、知識は不要です。

いけるぜ!ビバ・コンソールアプリ!

で、出力周りをととのえて、えいや!と動かしたのですが、全然動かんかったり、動きが右と左でちがってたりして、( u8バッファから i16バッファへの積み替えの際に、2バイトずつ読むとこを1バイトしかずらしてなかった) そもそも最初にもどって、スペクトルのデータの数値の単位がよーわからんので、バーが全部てっぺんに当たってどーすればいいんじゃーってなったり...

でもなんか色々やっているうちに、だんだんソレっぽい動きをしてきて、何とか見れるよーになってきました。

で、ソレっぽいのは分かったのですが、ソレがあっているのはどーすれば検証できるのか?答えがないのに...波ってむっつかしー。

うーん。音で調べるしかないか。

で、普通の音楽データを使うと、わけわからなので、サイン波を使ったわけです。

1khz のサイン波の wave ファイルを MPD に再生させながら、作ったプログラムをみてみたら、おおーそれっぽい!

で調子にのって、

  • いろいろな周波数のサイン波の wave ファイル (周波数帯ごとの出力確認)
  • ステレオ(2ch) だけど、左だけ、右だけ無音の wave ファイル (左右の出力確認)
  • 一定間隔で 無音になる wave ファイル (出てる音と描画のタイミングの確認)
  • ふつうのノイズの wave ファイル (各周波数帯の出力が同じレベルかの確認)
  • ピンクノイズの wave ファイル (聴感的に各周波数帯の出力が良い感じなのかの確認)

これらを作ったうえで、MPD に再生させながら色々調整していきました。もー理論とかじゃなく、見た目重視で色々パラメーター変えながら地道に。最終的には上3つはデバックに、下二つはレベルメーターの動きの調整に役に立ちました。

waveファイルの作成はこれが最高でした。

soundengine.jp

これの WaveGenerator が簡単にサイン波 waveファイルが作れましたし、音自体の加工もこれで簡単にできました。

とくに、「一定間隔で 無音になる wave 」が意外と役に立ちました。これがあったため、MPDの出力と同期させるように調整するのが、はかどりました。

ただ、まだ最後の山があって、MPDさん。waveファイルだと出音とピッタリ同期したPCMデータを出すのに、mp3 だと PCMデータと出音が 0.5秒くらいズレるんです。そう。上の「一定間隔で 無音になる wave 」を ffmpegでmp3 に変換して食べさせると思いっきりズレるんです。

で、PCMデータが遅れるんなら、デコードとかがあってしゃーないのかなとか思うのですが、音が遅れて出てくるんです。えっ?

だって、既にデコードしたデータがあって、fifoに出力もしているのに、そのままALSAにぶち込むだけで音出るよね?そのあと0.5秒も遅らせてから出すのってなんで? waveは遅れないのになんで?わざわざmp3だけディレイしてんの?

環境・設定の問題なのかと色々考えたり確認したのですが、いやー...

少し遅れるんならまだあれですが、0.5秒って相当です。手拍子がみんなと反対になるくらい相当です。行進がみんなと反対になるくらい相当です。(いますけどね。そういう方...私もリズム感が特別いいわけでは無いのであれですがね。)

まぁ、しゃーないので、こっち側で 0.5秒ほど遅らせてアニメするようにしました。ただ、waveだと遅れないってことは、ライブラリに mp3とwaveが混在してたら、どっちかでは必ずずれるんですよねぇ...。まぁ mp3だけにしておけば、いいっちゃいいんですがね...。

色々ありましたが、出来たコードみたら「こんなもんか」なのですが、作るって大変だなとしみじみ思います。

でもうふふ。出来たものを見てるのは楽しい。昔の(また昔話)カーステレオの派手な奴みたいに、好きな音楽に合わせてピコピコ動くのは、見てて飽きないですねぇ。

よし、これに合わせて、LEDをチカチカさせれば....などという欲望がわいてきました。(ってか最初っからそれが目的で...)

んま、Rustもわりと色々を使ってみて、武器として使えるライブラリも着々と増えてきたので当初よりコーディングスピードは上がってきましたね。だんだん慣れていければいいなぁ。