オールインワンのマイコンボードM5StickC Plus(下図)を使って、ジュークボックスを作成しようと思います。
まず、M5StickC Plusが内蔵しているパッシブブザーを使って音階を演奏する関数を整備します。
Arduino UNOやArduino DUE用に作成した、play_mml()関数をM5StickC Plus用に実装(移植)して、独自のMML(Music Macro Language)で記述した楽譜を演奏させてみます。play_mml()関数の実装からスタートです。
Arduino UNO用に作成したplay_mml()関数や、DUE用に作成した三和音対応play_mml()関数は、バックグラウンド演奏が可能な様に、割り込みを使って実装していました。
Arduino UNO: 「#5-8 MMLで音楽を奏でる」
Arduino DUE: 「#13-2 MMLで三和音を演奏する」
実は、上にリンクした「#5-8 MMLで音楽を奏でる」で最初に作ったplay_mml()関数は、MMLの演奏を開始すると、ブロッキングして演奏終了まで制御が戻りませんでした。これではバックグラウンド演奏(BGM等)に使用できないので、「#6-7 Arduinoはサイコロを振る」にて、バックグラウンド演奏ができるよう、割り込みを使って並列処理(ノンブロッキング処理)するコードに改造しました。
「#6-7 Arduinoはサイコロを振る」は、使い慣れたUNOだったので、それほど難しいことはなかったのですが、「#13-2 MMLで三和音を演奏する」では、DUEのタイマユニットを直接叩いたり、割り込みハンドリングのお作法を調べたり、予備検討が大変だった記憶があります。
UNO → DUE移植時の苦労を、ここで再びM5StickC Plusで繰り返すのはシンドイのですが、幸い、M5StickC PlusのMCUであるESP32には、リアルタイムOSであるFreeRTOSが載っていて、スレッドを起動することでバックグラウンド演奏が可能なので、play_mml()関数自体は、与えられたMMLの演奏が終わるまでブロッキング処理してしまってOKなのです。
しかも今回は、M5StickC Plusライブラリに整備されたM5.Beepクラスのtone()メソッドをそのまま使って音を鳴らしますので、タイマユニットのレジスタ設定方法などについても調査する手間が省けます。
すごく楽に実装できるはずですo(1^−^)o。
以下、音階を演奏する部分の簡易版コード抜粋です。
// note
M5.Beep.tone (frq);
while (millis () - msec_start<=msec_len/2)
vTaskDelay (1/portTICK_PERIOD_MS);
M5.Beep.mute ();
よきよき。
元々、音階から求めた周波数(frq)のPWMをパッシブブザーが接続された端子に出力したいだけなので、上コード例の様に、tone()メソッドで終了って訳です。tone()メソッド〜mute()メソッドまでの間は、楽譜の音の長さに従った時間を待っているだけですから、簡単です。
BGM等の様にノンブロッキング演奏したい場合、FreeRTOSのAPIでスレッドを起動して、スレッド内でブロッキングplay_mml()関数を実行すれば良いのです。今回はBGMの演奏は必要なかったので、未実装です。以下の記事を参考に、各自スレッドを起動して並列処理して見てください。
「#24-4 FreeRTOSでマルチタスク」
play_mml()関数の占有リソースはパッシブブザー(M5.Beepクラス)です。並列処理する場合、共有リソースの管理は、しっかりしましょうね。
独自のMMLに関しては、以下の記事で仕様を定義してあります。
「#13-3 MMLで三和音を演奏する(仕上げ)」
上記事の「コアMML仕様」が、play_mml()関数が解釈するMMLの仕様となっています。
play_mml()関数を使って、M5StickC Plusのジュークボックスを作ろうとする時、演奏する音楽の楽譜をMMLとして作成して、文字列ポインタ配列に保持します。
一連のMMLの中で、テンポやキーのオフセット値などを記述できる必要があったので、コアMML仕様に対して、以下のコマンドを追加したいと思います。
コアMMLの拡張:
上の拡張コマンドは、UNO/DUE用のplay_mml()関数には未実装なので、ひとまず、M5StickC Plus用play_mml()関数のローカルルールとしておきます。
ジュークボックスと言うのは、本体内部に沢山のレコードが内蔵されていて、お金(演奏代金)を入れて曲名の記されたスイッチを押すことで、所望のレコードを再生してくれる商業装置です。多い物だと、数100枚のレコードを内蔵していたらしいです。
M5StickC Plusでジュークボックスを作る以上、少なくとも、2曲以上の音楽を再生できるようにしておく必要があります。
いくつかの音楽からMMLを起こして、文字列へのポインタ配列として保持します(以下コード例)。
const char *mml_ardious[ ]=
{
"# Ardious: theme, fly, fail",
"T2000",
"# theme",
"G58,G58,F516,E516,D516,C516",
"G58,G58,F516,E516,D516,C516",
"C58,D58,F58,G58",
"A54,R04",
"# fly",
"C616,D616,R08,R04",
"C616,D616,R08,R04",
"# fail",
"C416,A34,A#38.",
NULL
};
再生可能な音楽のリストをTFTディスプレイへメニューとして表示しておいて、選択されたメニューに割り当てた音楽を再生するようなユーザインターフェイスとします。
そこで次のステップとして、メニューアイテム名とMMLとを対にして、データベースを構築します。
const void *jbx_menu[ ]=
{
// "Menu item", parameter
"1)Ardious", mml_ardious,
"2)Bee March", mml_bee_march,
"3)Carp Streamer", mml_carp,
"4)For Elise", mml_for_elise,
"5)Parade", mml_parade,
"6)Rudolph", mml_rudolph,
"7)Sakura", mml_sakura,
"8)Silent Night", mml_silent,
"9)Twinkle", mml_twinkle,
// maximum 9 items
NULL, NULL
};
上のデータベースで、メニューとしてTFTディスプレイへ表示するアイテム名文字列と、それを選択した時に演奏すべきMMLへのポインタが対となりました。このデータベースを使ってメニューを表示します。
お気に入りの曲からMMLを起こして、皆様のオリジナルジュークボックスにカスタマイズすることもできます。
U/Iは以下の通りのシンプルなものとします。
U/I設計:
TFTディスプレイの表示領域サイズの関係で最大9曲ほどなのですが、メニューのページ切り替えなどのU/Iを設計すると、もっと曲数を増やすことも可能でしょう。その辺りの拡張は、必要に応じて皆様で実施してください。
では、完成したプログラムを以下にリンクしておきます。M5StickC Plus開発環境の立ち上げは、こちらを参照してください。
完成プログラム:m5cp_jukebox.ino
※コンパイルには、M5cp-PSライブラリが必要となります。リンクを辿ってインストールしておいてください。
MML定義モジュール:
ヘッダファイル | 曲名 | 備考 |
mml_ardious.h | Ardious |
UNO+TFTシールドで作成した自作ゲームの テーマ曲、飛行音、墜落音 |
mml_bee_march.h | みつばちマーチ |
「子犬のマーチ」という場合もあるみたい 英名は「Bee March」 |
mml_carp.h | こいのぼり | 「屋根より高い〜♪」 |
mml_for_elise.h | エリーゼのために | オルゴールなどで流れるお馴染みの曲 |
mml_parade.h | おもちゃの兵隊の観兵式 | キユーピー3分クッキング |
mml_rudolph.h | 赤鼻のトナカイ | クリスマスソング |
mml_sakura.h | さくらさくら | 「さくら、さくら〜♪」 |
mml_silent.h | きよしこの夜 | クリスマスソング |
mml_twinkle.h | きらきら星 | アルファベットA〜Zも、この曲? |
※すべてダウンロードして、プログラム(m5cp_jukebox.ino)と同じディレクトリに配置してからコンパイル&書き込んでください。
コンパイルしてM5StickC Plusに書き込むと、シリアルモニタから以下のコマンドを受け付けています。
シリアルモニタコマンド仕様:
すべて、デバッグ用・試行用のコマンドなので、ジュークボックスとしては、シリアルモニタ無しで自立します。
M5StickC Plusだけあれば、外付け回路等は必要ないので、そのまま実験を進められます。特段変わった事は無いので、実験中の様子(写真)は省略します。後ほど動画でご確認ください。
実験サンプル:
【,T2000,C44,D44,E44,R02,T1000,C44,D44,E44,T2000】
OK: short-cut (0)
M5StickC Plus用に拡張した「T」コマンドのテストをしてみました。「♪ドレミ」の2回目が倍速で演奏されたらOKです。
【,K0,A42,B42,R01,K-2,A42,B42,R01,K3,A42,B42,K0】
OK: short-cut (0)
同じく、拡張した「K」コマンドのテストです。すべて同じ「♪ラシ(A42,B42)」を演奏しているのですが、「K」コマンドでキーを変えているので、2回目、3回目の音程が少し異なって聞こえるはずです。
【,C48,D48,E44.,D48,C44,R08,C48,D48,E48,D48,C48,D42】
OK: short-cut (0)
最近、聞かなくなりましたが、昔、屋台のラーメン屋さん🍥が、こんな(↑)音楽を鳴らしながら走っていました。
さて、ここまでは、シリアルモニタからの試行用コマンドを使って、play_mml()関数に追加した新機能等の動作を検証してみました。ここから先は、M5StickC PlusのTFTディスプレイに表示したメニューやスイッチ操作などのU/Iを使って、ジュークボックスアプリケーションとしての検証をしていきます。
SW-Bでメニュー項目を選択して、SW-Aでその音楽を再生できます。再生中にSW-Bを押し続けると、MMLの切れ目で中断することができます。
ジュークボックスの動作状態を以下動画でご確認ください。
「1)Ardious」は、UNO+TFTシールドで作った自作ゲーム(Ardious)のBGMと効果音です。ゲームプレイの様子はこちらからご確認ください。
組み込み用途のOS(本ページではFreeRTOSですね)は、PCやスマホ等のOSとは、だいぶ印象が異なりますが、ものすごく大切な基本ソフトウェアなんですね。改めて、思い知らされました。