micro:bit で、自分の好きな曲を演奏するには
な感じにコツコツと楽譜を入力しないとならぬ
拍数なども試し試しで入力するのである
個人的には、鍵盤をつかって演奏したら、演奏したイメージでコードを生成して欲しいというワガママがむくむくと
じゃ Delphi で作ってみるべと思うのである
下調べ
どのようなコードが生成されているか、入力すべき値などを確認する
生成するべき JavaScript のコードを確認する
上記の scrach ベースのコードを JavaScript で確認すると
music.setTempo(120) --> テンポの設定 120bpm
music.playTone(262, music.beat(BeatFraction.Half)) --> 真ん中のド 1/2拍
music.playTone(330, music.beat(BeatFraction.Half)) --> ミ
music.playTone(349, music.beat(BeatFraction.Half)) --> ファ
music.playTone(392, music.beat(BeatFraction.Whole)) --> ソ 1拍
music.rest(music.beat(BeatFraction.Quater)) --> 休符 1/4拍
となる
音符のヘルツを確認する
playtone で指定されるのは音符のヘルツ(四捨五入)の値なので、ヘルツの値を確認した
C0 は 低いド、C1 は真ん中のド、C2 は高いドを表しています。
| 音階 | C0 | C0# | D0 | D0# |E0 | F0 | F0# | G0 | G0# | A0 | A0# | B0 |
|----|----|----|----|----|----|----|----|----|----|----|----|----|----|
|ヘルツ|131|139|147|165|156|175|185|196|208|220|233|247|
| 音階 | C1 | C1# | D1 | D1# |E1 | F1 | F1# | G1 | G1# | A1 | A1# | B1 |
|----|----|----|----|----|----|----|----|----|----|----|----|----|----|
|ヘルツ|262|277|294|311|330|349|370|392|415|440|466|494|
| 音階 | C2 | C2# | D2 | D2# |E2 | F2 | F2# | G2 | G2# | A2 | A2# | B2 |
|----|----|----|----|----|----|----|----|----|----|----|----|----|----|
|ヘルツ|523|554|587|622|659|698|740|784|831|880|932|988|
拍数の記述を確認する
BeatFreaction で指定できる拍数は 1, 1/2, 1/4, 1/8, 1/16, 2, 4 でそれぞれの記述を確認した
拍数 | 記述 |
---|---|
1 | Whole |
1/2 | Half |
1/4 | Quarter |
1/8 | Eighth |
1/16 | Sixteenth |
2 | Double |
4 | Breve |
テンポと拍数と秒数の関係を確かめる
60bpm のときの 1 泊は 1 秒
120bpm のときの 1 拍は 0.5 秒
指定可能なテンポの値は 4bpm から 400bpm の間 (デフォルト 120bpm)
となると ミリ秒で差を出して 1 秒 =1000 ミリ秒なので 1000 で割って、基本の 60bpm をかけて、設定されている bpm で割るで計算あってるかな?
1000 (1秒) / 1000 * 60 だと 60 になるから 60 bpm のときは 1 だし、120 bpm は 0.5 になるから OK (のはず)
Delphi で作ってみる
といっても唐突には作れないので、仕様を決める
作るアプリケーションの仕様を考える
- 初期バージョンなので、曲の途中ではテンポを変えない
- テンポは最初に決定する
- 鍵盤は1オクターブ分だけ用意する
- オクターブの高低はスイッチ的な要素で決定する
- 鍵盤を押している間の時間で拍数を算出する
- 鍵盤を離して次の鍵盤を押すまでの間が 1/16 拍以上であれば休符を入れる
- 和音は考えない
- 生成したコードは画面上でコピーして micro:bit のエディタにペーストができればいい(自分しか使わないから)
実際に作ってみた
今回はプロトタイプなので、あくまで鍵盤のキーと拍数がそこそこ合っていて、文字列が生成できればよい
今回はプロトタイプなので休符とオクターブの高低は考えていない
今回はベタの if 文で判定した
ボタンの操作は同じなので Button1MouseUp / Button1MouseDown にすべてのボタンの処理を集約し、ボタンの種類は TButton(sender).Text; で判断(Cとか C#とかの文字列が入ります)
できあがったプロトタイプ画面
コードはこちら
// プロトタイプなのでエラーなどは全く考慮していないベタな感じで書いてます
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Edit,
FMX.EditBox, FMX.NumberBox, FMX.StdCtrls, FMX.Controls.Presentation,
FMX.ScrollBox, FMX.Memo, FMX.Layouts, System.Math, System.DateUtils;
type
TForm1 = class(TForm)
Layout1: TLayout;
Layout2: TLayout;
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
Button6: TButton;
Button7: TButton;
Button8: TButton;
Button9: TButton;
Button10: TButton;
Button11: TButton;
Button12: TButton;
Label1: TLabel;
NumberBox1: TNumberBox;
procedure NumberBox1Change(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure Button1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Button1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
private
{ private 宣言 }
const setTempo = 'music.setTempo(';
const playTone = 'music.playTone(';
const rest = 'music.rest(';
const beat = 'music.beat(';
const whole = 'BeatFraction.Whole';
const half = 'BeatFraction.Half';
const quarter = 'BeatFraction.Quarter';
const Eighth = 'BeatFraction.Eighth';
const Sixteenth = 'BeatFraction.Sixteenth';
const B_Double = 'BeatFraction.Double';
const Breve = 'BeatFraction.Breve';
const e_1 = ')';
const e_2 = '))';
procedure generate_setTempo;
procedure generate_playTone;
procedure generate_rest;
public
{ public 宣言 }
timeCount: Double;
StartTime, EndTime: TDateTime;
Kenban, Hz, Haku: string;
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
{ TForm1 }
procedure TForm1.Button1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
StartTime := Now;
EndTime := 0;
Kenban := TButton(sender).Text;
end;
procedure TForm1.Button1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
EndTime := Now;
timeCount := ((MilliSecondsBetween(EndTime, StartTime) / 1000) * 60) / NumberBox1.Value;
generate_playTone;
StartTime := 0;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
generate_setTempo;
end;
procedure TForm1.generate_setTempo;
var
javaStrings: string;
begin
javaStrings := setTempo + NumberBox1.Text + e_1;
Memo1.Lines.Add(javaStrings);
end;
procedure TForm1.generate_playTone;
var
javaString: string;
cnt: double;
begin
if Kenban = 'C' then Hz := '262';
if Kenban = 'C#'then Hz := '277';
if Kenban = 'D' then HZ := '294';
if Kenban = 'D#'then HZ := '311';
if Kenban = 'E' then Hz := '330';
if Kenban = 'F' then Hz := '349';
if Kenban = 'F#'then Hz := '370';
if Kenban = 'G' then Hz := '392';
if Kenban = 'G#'then Hz := '415';
if Kenban = 'A' then Hz := '440';
if Kenban = 'A#'then Hz := '466';
if Kenban = 'B' then Hz := '494';
cnt := Roundto(timeCount, -2);
if cnt <= (1/16) then haku := Sixteenth;
if (cnt <= (1/8)) and (cnt > (1/16)) then haku := Eighth;
if (cnt <= (1/4)) and (cnt > (1/8)) then haku := quarter;
if (cnt <= (1/2)) and (cnt > (1/4)) then haku := half;
if (cnt <= 1) and (cnt > (1/2)) then haku := whole;
if (cnt <= 2) and (cnt > 1) then haku := B_Double;
if cnt > 2 then haku := Breve;
javaString := playTone + Hz + ', ' + beat + haku + e_2;
Memo1.Lines.Add(javaString);
end;
procedure TForm1.generate_rest;
var
javaString: string;
begin
javaString := rest + beat + half + e_2;
Memo1.Lines.Add(javaString)
end;
procedure TForm1.NumberBox1Change(Sender: TObject);
begin
generate_setTempo;
end;
end.
あとがき
プロトタイプだけど、micro:bit側での操作は恐ろしく楽になった
もうちょっとスマートに書くのと、鍵盤をそれらしく表現して完成させるのは次回
しかし、こんな拡張ボードがあったのか....これは直接演奏するときですが、音源を作りたいのよね
https://www.switch-science.com/catalog/4030/