Attiny85 & Arduinoで、和音を鳴らせるオリジナルメロディーカードを作る話—前編 電子オルゴール部分を作る—

さて、バースデーカードなんかで、開いたりボタンを押したりすると、音楽が鳴るのがありますよね? あれを、好きな曲で作りたいなって思いました。

こんなカード👇

とりあえず音楽を鳴らす仕掛けが出来たので、前編としてご紹介します。

メロディを鳴らすガジェット(?)を作りたい場合、Arduinoのtone()関数を使う方法がありますが、Arduinoは大きくてカードに収まらないうえ、tone()は単音しか出せないので面白味がありません。

www.musashinodenpa.com

欲しい機能はこんな感じです。

  • 小さいマイコンで動く
  • 省電力
  • 和音を鳴らせる
  • 楽譜データが作りやすい

こういうのには大抵先人がいるもので、探してみると早速ぴったりのものが見つかりました。

弘前大学の小山先生が公開されている、mymelo2というプログラムです。

koyama.verse.jp

これが素晴らしい出来で、8ピンの小さなマイコンAttiny85(Digisparkでも!)で動かせて、3音までの和音を鳴らせるうえ、ボタンをつければ再生/停止を操作できて、再生後はちゃんと省電力モードに入ってくれます。

akizukidenshi.com

さらに、楽譜データを簡単に作れるインフラまで用意してくれていて、至れり尽くせりです。

というわけで、このプログラムを少し自分好みにカスタマイズして、使わせてもらおうと思います。

楽譜データの変換ツールをMac対応にする

さて、mymelo2では、ドレミ表記で書いた楽譜データを、付属の変換ツール(mymelo.wsf)でArduinoのヘッダーファイルに変換して使います。

ところが、変換ツールはWSHというWindows向けのスクリプトで作られているので、そのままではMacで利用することができません。 ja.wikipedia.org

うーん、Macでも動かしたい。

じつはこの変換スクリプト、中身がほぼJavaScriptなので、変換ロジックをWebアプリに移植すれば、マルチプラットフォーム対応に改造できそうです。

Google App Scriptに移植しよう

Webアプリと言いましたが、後の運用のために、Google App Script(GAS)へ移植することにしました。

gsuite.google.co.jp

GASもほぼJavaScriptなので、ほとんどそのままで動かせるはずです。

GASの使い方については、探せばすぐ出てくるので割愛しますが、使い始めはこの辺りが参考になると思います。

liginc.co.jp

変換処理を抜き出す

まずはmymelo.wsfから、26行~364行あたりの変換処理を、ゴソッとコピーしてきます。

ついでに、45行からの4行は不要なので、削除しておきましょう。

var sh = new ActiveXObject("Shell.Application");
var ado = new ActiveXObject("ADODB.Stream");
var fso = new ActiveXObject("Scripting.FileSystemObject");
var mymelo = fso.GetFile(WScript.ScriptFullName).ParentFolder;

データの入出力部分を改造

入力部分

まず、コピーした処理をまるごと関数に入れて、曲名と入力文字列は引数で渡す形にしておきます。

function convert(song_name, input) {
    <上でコピーした処理>
}

曲名を設定している行を探し、引数で受けたものを代入するように書き換えます。

// 変更前
var s = "// " + fso.getFileName(mmlfile) + "\n";

// 変更後
var s = "// " + song_name + "\n";

ファイル読み込み部分も、同じように探して書き換えます。

// 変更前
var S = readFile(mmlfile);

// 変更後
var S = input;

出力部分

出力処理は、353行~362行辺りの減衰矩形波/正弦波の確認フォーム処理に組み込まれていますが、とりあえず正弦波の決め打ちにしつつ、出力先を返り値にしておきます。

s += "#define SINE_WAVE\n";
var output = s + SW + "0};\n" + gakufu + "0};\n";

console.log(output);
return output;

ついでに長音符改行時の挙動を修正

こんなふうに、長音符の途中に改行が入ると、変換がうまくいかないという問題がありました。

ドーーーー
ーーー

アウフタクトを多用する曲だと、小節単位で改行できなくて不便です。

入力文字列の整形のところに、行頭が長音符なら改行を削除する処理を追加すれば、このエラーを回避することができました。

.replace(/\n\s*[ー^]/g, "^")  // 行頭にーor^があれば次行と合体

WSH特有の関数を置き換える

といっても、メッセージ表示部分だけなので簡単です。

WScript.Echo()を、全部console.log()に置き換えて、WScript.Quit()を削除すればOKです。

スプレッドシートを楽譜エディターにする

さて、mymelo2では、テキストで手軽に楽譜データを作ることができますが、音符によって文字数が違うので、複雑なリズムを打ち込むのには少し不便です。

`ド`レ`ミ`レーシソ`ドーシラソーラシラ
ーシ`ドソーファミファ+ーミファ+ソ+ーラシラ

そこで、Excelなどのスプレッドシートを使って、1セル1音で打ち込むという方法を考えました。

A B C D E F G H I J K L M N O P
`ド `レ `ミ `レ `ド
`ド ファ ファ+ ファ+ ソ+

これなら、音の長さと見た目が一致するので、直感的にリズムを打ち込んでいくことができます。ピアノロールUIの縦軸を圧縮した感じですね。

具体的には、Googleスプレッドシートで楽譜を作り、GASでヘッダファイルに変換するというワークフローにします。

www.google.com

変換ツールをGASに移植しておいたのは、このためです。

スプレッドシートにすることで、小節ごとに罫線を付けたり色分けしたりもできるので、フレーズの編集が劇的に楽になります。

楽譜データを変換する

さて、ここからがGASの本領発揮、Googleスプレッドシートと、Googleドライブを組み合わせて、入出力を追加します。

あらかじめ、Googleドライブにmymeloのスケッチフォルダーをアップロードして、mymelo.hファイルのIDを取得しておきましょう。

アップロードしたmymelo.hファイルを右クリックし、「共有可能なリンクを取得」で表示されるURLの、「/file/d/」に続くランダムな文字列部分がIDです。

そうしたら、main()にスプレッドシートからの文字列読み込みと、ドライブへのファイル書き出し処理を追加します。

function main() {
    // アクティブなシートを取得
    var sheet = SpreadsheetApp.getActiveSheet();
    // データ範囲を取得
    var range = sheet.getDataRange();
    // 範囲内の値を取得
    var values = range.getValues();
    // 2次元配列が来るので、区切り文字なしで文字列に結合
    var score = values.map(value => value.join('')).join('\n')

    // 変換処理、曲名はシート名から取る
    var output = convert(sheet.getSheetName(), score);

    // Googleドライブのmymelo.hに上書き保存
    var file = DriveApp.getFileById('調べておいたmymelo.hのID');
    file.setContent(output);
}

あとは楽譜のシートを開いておいてから、main()を実行すれば、mymelo.hが更新されるはずです。

初回実行時のみ、「このアプリは確認されていません」というダイアログが表示されるので、「詳細」→「安全ではないページに移動」→「許可」で、実行権限を与えてあげてください。

と、ここまで書いておいてナンですが、書き出した後のステップは、まだうまい方法を模索中です。

Googleドライブ上のArduinoスケッチを、直接Arduino IDEで開けるといいんだけどなぁ、できないかなぁ。

とりあえず良い方法が見つかるまでは、でき上がったヘッダーファイルをダウンロードして使うことにしましょう。

ディストーションをかける

さて、でき上がったものを鳴らしてみると、正弦波ではちょっと音がマイルドすぎました。

かといって矩形波では少し安っぽい感があるので、ちょっと力技っぽいですが、Excelで波形を加工してディストーションをかけ、パンチのある音を目指します。

mymelo2の波形生成に使われている波形テーブルは、8bit×256ステップのなので、こんな感じで。

index sin result
0~256の連番 =(SIN(RADIANS(([@index]/256*360)-90))+1) *128 =(MAX(variable[sat],MIN((256-variable[sat]),[@sin]))-variable[sat])*(256/(256-variable[sat]*2))
  • index列に用意した連番を元に、sin列に8bitの正弦波を作って整数領域に移動

  • 別途variableテーブルを用意して、sat列に閾値を入れる

  • result列で、閾値ぶん上下をクリップ

できたテーブルは、カンマ区切りにしてココに入れます↓

// 正弦波(値:0~255, データ数:256)
const char W[] PROGMEM= { この中身 }

何度か鳴らしながら閾値を調整して、結局少し鈍らせた矩形波という感じになりました。

選ぶスピーカーによっても、鳴り具合が違うと思うので、色々試してみようと思います。

試奏してみる

ということで、ひとまず環境が整ったので、何曲か書いてみました。Digisparkを使うと、簡単に書き換えて試せるので、編曲が捗ります。

Billie Eilish / Bad Guy

King Gnu / Sorrows

Iron Man 3 / Can you dig it

LiSA / 紅蓮華

これは楽しい...!

後編へつづく

カードに仕立てるための材料待ちなので、続きは後編をおまちください。

今日はここまで!