M5Stackでゆみみみっくす! その2

M5Stackで音を鳴らしたい!

せっかくM5Stackで動くゆみみみっくすデモを作ったのだから、やっぱりサウンドを鳴らしてみたいと思うわけよ!(^-^)

 

でも私はM5Stackで音を鳴らしたことがない。

そういえばM5Stack Core2を手に入れて、初めて起動した時に音が鳴ってたような…。

それ以来、聞いたことがないのでどんな音だったのかも忘れてしまった!(^_^;

 

今回はやった事ないことに挑戦するので、ちょっとだけ手間取りそうだよ!

ちなみにこのブログで書かれている事は、全てM5Stack Core2 で試してる!

M5Stack BASICやFireなどではやり方が違う可能性もあるのでご注意ください!

 

音のデータを用意する

何はともあれ音のデータが必要だ!

サウンドのフォーマットはたくさんあるけれども、M5Stackで鳴らせるのはWAVフォーマットくらいかも? 詳しい説明は省くけれど、WAVは生音のデータで、このまま鳴らす事が出来る。MP3とかAACは複雑な計算をしないと音が再生できない。

 

動画(mp4)ファイルからWAVデータを作り出すのは、ffmpegコマンドを利用すれば良い。

 

それぞれのオプションはちゃんと理解していないけれど、適当にサンプルとして出せればいいから…と思って変換してみた!(^^;

でも……44.1KHzのステレオWAVなんて再生できるんだろうか…滝汗

ダメだったら出力し直してみるとして、まずはこれでテストだ!(^^;;

 

SDカードにあるデータを鳴らす!

まずはサンプルがどこかにあるはずなので、それを探してみる!

多くのサンプルはArduino IDEのファイル→スケッチ例にあるのだけど、サウンドに関するものは見当たらないなぁ…。

 

だいぶいろんなところを探してみて、ようやくそれっぽいプログラムを見つけた!

M5StackでWAVファイルを再生するプログラムのようだ!

これをそのまま利用させてもらおう!(すみません…)

 

ファイル名が /wav/samba.wav になっている部分を /wav/yumimi.wav に変更。

これで試してみたが……淡い期待をしていたけれども、残念ながら鳴らない(^^;;

動作を見てみると、どうも演奏しようとした瞬間に終了してるように見える。

 

やっぱり用意したサウンドファイル(.wav)がダメなんのだろうな…と思い作り直し。

まずは一番チープな音にしてテストしてみることに!

このオプションでモノラル 8KHzのWAVファイルが作られたようだ!

 

このファイルをSDカードにコピーしM5Stackを再起動したところ、無事に鳴った!(^^)

サンプリングレートが高くないので電話でしゃべってるくらいの音になってるけれど、原曲を知っていれば聞き取ることは出来る…くらいだろうか(^^;;;

 

鳴らし方を調べている時に気がついたのだが、どうやらライブラリ的にはmp3やAACなどのフォーマットも鳴らせるようだ。ぜひどこかで試してみたいぞ!(^^)

 

メモリにあるデータを鳴らす!

出来上がったプログラム(SDカードから読み込みながら鳴らす)をそのままゆみみみっくすのデモプログラムに入れると、上手く鳴らない可能性が高い。

 

ゆみみみっくす動画では、絵を表示するために頻繁かつ長い時間SDカードをアクセスする。SDカードへのアクセスは排他的に行われる(だろう)から、おそらく絵のデータを読み込み始めるとサウンドが止まる。

 

絵と違い、サウンドは1フレームでも途切れるとはっきり分かる。人間の目は途中のコマが抜けていても、目(脳みそ?)が勝手に補間してくれるのだが、耳はそうではない。

この手のプログラムで最優先されるのはサウンドなのだ!

 

そしてサンプリングレートを8KHzにしたのもワケがある。

ゆみみみっくすの動画は3分34秒あり、秒数換算で214秒。214x8000バイトx16bitPCMで3424KBとなり、このサイズならばM5Stack Core2のPSRAMに丸々入る計算だ。

PSRAMのアクセスは遅いという印象が(私には)あるけれど、SDカードからの読み込みに比べたら爆速だ!(^-^)/

 

ということは、SDカードから読み込みつつではなく、メモリにあるデータを再生する関数があれば、目的は達成出来ることになる!

いろいろと探したりこねくり回したりしてみて目的を達成出来るプログラムになった!

 

最終的にゆみみみっくすを動かしているプログラムの一部を公開します。

↑これが宣言部分。

↑これがサウンド初期化と、駆動部分。

初期化の関数内で、確保されたヒープにwavファイルを一気に読み込んでいる。

3.4MBのファイルを3秒台の時間で読み切るM5Stack素敵!(^^)

 

Core0AudioはxTaskCreatePinnedToCore関数で登録されてコア0で実行される。

コア1は映像を描画するためにフル稼働!

逆にコア0が余ってたのでちょうど良かった!(^^)

 

なお、実験している最中はAudioFileSourcePROGMEMを利用していたが、あとになってAudioFileSourceSPIRAMBufferという関数があるのを見つけた。

もしかしてコッチの方が良かったりする???(@_@;;

 

映像の時間に合わせる

せっかく音が鳴っても、映像とシンクロしてなかったら残念すぎる!(T-T)

 

画像データは圧縮効率によりファイルサイズが変わり、SDカードから読み込む時間やデータ展開の時間がバラバラになっている。

具体的な数字をあげると、最短で30ms、最長で120msくらい掛かってた。

これだけの違いがあると、工夫無しで音楽と合わせるのは不可能だ。

 

なんとかして音楽と合わせなければならないが、実はそんなに難しくない(^^;

先ほど書いた通り、サウンドは途切れなく鳴らさないとバレるけど、映像は1コマ抜けても気づかれない事が多いのだ!

 

そこで、描画する時間が処理落ちしていた時には、意図的にコマ落ちをさせる処理を入れた。

1990年代のゲームでは良くやっていた手法だ!

 

ゆみみみっくすの映像は1/30秒を基準にデータが用意されていた(Youtubeの制限かも?)。

1秒=1000ミリ秒を30で割ると、1枚の絵に掛けられる時間は33.33ミリ秒だ。

この時間に処理が間に合えば33.33ミリ秒まで待つ、間に合わなければ66.66ミリ秒まで待つ…という処理をいれつつ、1枚の映像をスキップする。

 

これで映像と音楽がずれる事がなくなる!

 

コマ落ちがどどーんと発生した時(多くは複雑な絵の場合)は、少し見た目が目立つけれど、それでも音楽と映像があってる方が良いのは分かるだろう。

 

実際のところ、映像の描画には多くの時間がかかっており、33ミリ秒で描画が終わる事はほぼ無い。せっかく用意された6411枚の画像の、実に半分以上は描画されていない(^^;;

 

これで本当に終わり…?

思いつきで作ってしまったゆみみみっくすデモだったが、ワリと満足度の高いものが出来上がった気がする!やっぱり音楽入ると違うね!(^-^)

 

またしてもツイートしてから気がついたのだが、プログラム的にゆみみみっくすに依存しているところがないんですよね…。

制限があるとすれば、サウンドデータが4MBを超えられない(260秒くらいが限度)くらいかも。CPUコアを1つ専有する事を考えたら、MP3とかACCでの演奏が出来るかも知れないので、もっと長い音楽を再生する事も出来るかも!?

 

ということは、Youtubeの映像とか、mp4ファイルになっている映像であれば M5Stack Core2で再生出来るって事かー……ほほぅ……(-_-;;

 

ではまた次回!(^-^)ノ