2022.5.29
前回のプログラムで「オクターブ変更」ができるようにました。今回は音の長さ(音長)の処理を追加してみます。
以前のルールを思い出すと、音程を確定してから音長を定義することになっています。そして音長には付点も含まれるので、少々ややこしいですが、やるしかありません。以下のようなMMLを解析できるようにしましょう。
Z="C4C4.C16C16."
おさらいになりますが、IchigoJamで電子楽器を操作#6で最初の音程取得はしています。さらに半音の処理も終わっているため、音程は確定しています。と言うことは、この後に音長処理をすれば良いことが分かりますね。プログラムの方も途中まで作っています。
140 IF 47<M AND M<58 GOTO 210・・・数字だったら210行へ
では210行から作りますが、どのように作ったらいいのでしょうか?数字が音長なのは分かっていますが、楽譜の場合、1分音符、2分音符、4分音符、8分音符・・・と数字が増えるほど短い音になります。それも半分ずつです。(ここでは全音符を1分音符と言います)1、2、4、8と2倍増えると2分の1の長さになる?ちょっと算数をしてみましょう。
2分音符は1分音符の半分の長さだから、2で割ればいい。
4分音符は2分音符の半分の長さだから、さらに2で割ればいい。
8分音符は4分音符の半分の長さだから、さらに2で割ればいい。
と言うことは1分音符を4で割れば4分音符で、1分音符を8で割れば8分音符。
仮に1分音符の長さを「8」として、数式で書くと、8÷1=8、8÷2=4、8÷4=2、8÷8=1になります。このことから「1分音符長さ÷MML音長=音の長さ」になることが分かりました。
では1分音符の長さとは何でしょうか?
今まで触れてきませんでしたが「テンポ」に相当します。音楽の場合は4分音符を基準するのですが、早さを示すのでだいたい同じ意味合いです。曲によって変わるので、ここではルールだけ気にしていきましょう。
では、MMLから数値を取得しているので、さっそく先ほどの数式にあてはめたいところですが、この数値はキャラクターコードになっています。そのため計算できるようにちょっと細工が必要で・・・
L=M-48・・・これでキャラクターコード48が0になる
W=8/L・・・音の長さを計算で求める
変数LはLength(長さの意味)で作りました。変数WはWAIT命令で待機するため、このように命名しました。
このようなプログラムになります。
MMLに書いた0は「文字の0」なのでキャラクターコードでは48で、0~9は順番になっているため、取得したキャラクターコードから48を引けば文字から数値の変換が可能です。
さて、まだ問題があります。音長は2桁の場合もあるので、C1とC16を別なものと判断しなければいけません。そのため次の文字もチェックしますが、すでにほかの行でやってることと同じです。このような方法で次の文字を取得できます。
A=A+1:M=PEEK(Z+A)・・・次の文字を読み込む
C16と言った音符を仮定すると、最初に1を取得して次に6を取得することになります。変数Lには16と入れておきたい。さてどうしましょう?
C32とC64を考えるとピンとくるかもしれませんね。
最初に1、次に6でC16、最初に3、次に2でC32、最初に6、次に4でC64
最初の数は10の位、次の数は1の位になっています。と言うことは1回目の値を10倍すれば良さそうですね。
200 L=0・・・初期値を0としておく
210 L=(L*10)+(M-48)・・・音長を加算する
220 A=A+1:M=PEEK(Z+A)・・・次の文字を読み込む
230 IF 47<M AND M<58 GOTO 210・・・数値だったら210行へ
無駄なカッコがありますが見やすいかな?と思って付けてます。
MMLデータが「C16」だとすれば、最初の音長は1なので変数Mにはキャラクターコード49が入ってきます。最初の変数Lは0なので10倍しても0です。そこにM-48を入れるので、結果として変数Lは1になります。
次のMMLデータを読み込んで数値だったら同じ処理を行います。変数Lは1になっているため10倍して10、そこに次の音長6(C16の6が読み込まれる)が加算され、変数Lは16になります。
これ、3桁でも4桁でも同じ処理なんですよね。このように、ルールが決まるとデータ(ここで言う桁数)が変わってもプログラムに変更はありません。ルール決めの大切さと言うか便利さが分かってきたと思います。
かなり頑張っていますが、まだ終わりません。付点を忘れてはいませんか?。付点に相当する文字「.」はキャラクターコード46です。これを見つけて処理をさせましょう。
音楽のお勉強になってしまいますが、付点とはこのような意味合いになっています。
C2.は2分音符と4分音符を足した長さ、C4.は4分音符と8分音符を足した長さ
これも算数でできそうですよね。数値で確定した音の長さの半分伸ばせばいいんです。だから最初に音の長さを確定してから、半分の長さを足しましょう。
W=8/L・・・数値部分の音の長さを確定する
IF M==46 W=W+W/2
これだけです。急に答えが出てますが、当然、すぐにこのようなプログラムは作れません。紙に書いて規則性を確認して、何回も繰り返して、ここまでシンプルなプログラムになりました。ルールからプログラムの変換はとても大変です。でも、これができると楽しくなるし、失敗しても面白い動きを見せてくれることもあります。
話がそれてしまいました。まとめましょう。
10 BPS 312500:UART 3・・・MIDIの設定
20 Z="CDEC4D4E4"・・・MMLデータ
30 LET[65],9,11,0,2,4,5,7・・・ノートナンバー
40 A=0・・・MMLを読み込む位置を0(先頭)にする
41 O=60・・・オクターブを決める
42 T=128・・・テンポを決める
50 M=PEEK(Z+A)・・・MMLを1文字読み込む
60 IF M==0 END・・・MMLが終わったら終了
70 IF (64<M AND M<71) OR R==114 GOTO 100・・・MMLがA~G,Rだったら100行へ
80 O=O+((M==62)-(M==60))*12・・・</>だったらオクターブを変える
90 A=A+1:GOTO 50・・・読み込む位置を1つずらして50行へ
100 '英字が見つかった
110 N=[M]・・・ノートナンバーとりあえず確定
120 A=A+1:M=PEEK(Z+A)・・・次の文字を読み込む
130 IF M==43 OR M==45 N=N+(M==43)-(M==45):GOTO 120・・・+/-だったらノートナンバーを半音変えて120行へ
140 IF 47<M AND M<58 L=0:GOTO 210・・・数字だったら210行へ
150 'ここから発音
160 ? CHR$(#90,N+O,127)・・・鍵盤を押す
170 WAIT W・・・待つ
180 ? CHR$(#80,N+O,127)・・・鍵盤を離す
190 GOTO 50・・・50行へ
200 ' 音長を決める
210 L=(L*10)+(M-48)・・・音長を加算する
220 A=A+1:M=PEEK(Z+A)・・・次の文字を読み込む
230 IF 47<M AND M<58 GOTO 210・・・数値だったら210行へ
240 W=T/L・・・数値部分の音の長さを確定する
250 IF M==46 W=W+W/2・・・付点なら音を半分伸ばす
260 GOTO 150・・・150行へ
動かなかったらごめんなさい
これで完成ですね。でも何か問題がありそう・・・
次回はどうしようかな~
