はじめに
micro:bitでは、音を鳴らすことができます。v1では外付けのスピーカー等が必要でしたが、v2からはスピーカーが内蔵されています。音楽ブロック(Music)も標準で用意されており、内蔵するメロディーを再生したり、音のブロックをつなぎ合わせてメロディーを作ったりすることもできます。
元々は、Musical notes によるメロディーの再生をブロックでコーディングできましたが、そのブロックが消え、隠しコマンドになっています(JavaScriptやPythonであればコーディング可能)。
そこで、この隠しコマンドを復活する拡張機能を作成するとともに、配列を使わずにブロックを連結してメロディーを作成する機能をその拡張機能に盛り込みました。
音楽から消えたブロック
micro:bitのリファレンスでは、 Musical notes という音符表記と文字列の配列とを使ったメロディーの作り方が紹介されています。
しかし、そのメロディーを再生するブロックが見当たりません。
begin Melodyのページで、再生方法を説明していますが、そのコード例は、JavaScriptとPythonだけです。ブロック表記もありますが、単にブロックの形をしたJavaScriptのコードです。
pxt-microbitのソースコードを調べると、音楽(music)にあった次のブロックが廃止されているようです(//% deprecated=1
)。
関数名 | 説明 | 備考 |
---|---|---|
beginMelody | 文字列の配列をメロディーとして再生する | startMelody()の呼び出し |
startMelody | 文字列の配列をメロディーとして再生する | |
playMelody | スペース区切りの文字列をメロディーとして再生する | 再生が終わるまで待つ |
ソースコードのコメントを読むと、これらのブロックはすべて廃止されている(all deprecated)と書かれています。
// Shared code between begin, start, and play Melody blocks (all deprecated), plus StringPlayable.play (NOT deprecated).
export function startMelodyInternal(melodyArray: string[], options: MelodyOptions) {
ただし、コメントにも書かれているように、 StringPlayable.play
という機能は廃止されていないようですので、Musical notes という内部的な再生機能は使えそうです(いつかは廃止になるのかもしれません)。
Melody拡張機能の開発
文字列の配列をメロディーとして再生する機能を復活させる為に、 Melody拡張機能 を開発します。
さらに、配列を使わずにブロックを連結してメロディーを作成する機能をこの拡張機能に盛り込みます。
ちょっと待った!配列再生ブロックの復活
文字列の配列をメロディーとして再生する為には、既存のmusic.beginMelody()
をそのまま Melody拡張機能 としてコピーすれば良いだけです。
/**
* Generation of musical notes.
*/
//% weight=100 color=#696969 icon="\uf1b2"
namespace melody {
/**
* Starts playing a melody.
* Notes are expressed as a string of characters with this format: NOTE[octave][:duration]
* @param melodyArray the melody array to play
* @param options melody options, once / forever, in the foreground / background
*/
//% block="start melody $melodyArray repeating %options"
//% parts="headphone"
export function startMelody(melodyArray: string[], options: MelodyOptions = 1) {
return music.startMelody(melodyArray, options);
}
}
でも、ちょっと待ってください。先ほどのコメントを読むと、何らかの理由で music.beginMelody
ブロックは廃止されているのです。それにもかかわらず、StringPlayable.play
という機能は廃止されていないのです。これは、従来のブロックからStringPlayable.play
へと移行しているのではないかと考えられます。
Playableとは
そこで、 StringPlayable.play
について、ソースコードを確認してみました。
まずは、コメントにあるように、 music.startMelodyInternal()
関数は、共通関数であり("Shared code between ...")、加えて StringPlayable.play から呼び出されているようですので、 music.startMelodyInternal()
関数を参照しているコードを検索します。すると、次のコードが見つかりました(StringArrayPlayable._play)。
export class StringArrayPlayable extends Playable {
constructor(private notes: string[], private tempo: number) {
super();
}
_play(playbackMode: PlaybackMode) {
if(this.tempo) {
music.setTempo(this.tempo);
}
if (playbackMode == PlaybackMode.InBackground) {
startMelodyInternal(this.notes, MelodyOptions.OnceInBackground);
}
else if (playbackMode == PlaybackMode.LoopingInBackground) {
startMelodyInternal(this.notes, MelodyOptions.ForeverInBackground);
}
else {
startMelodyInternal(this.notes, MelodyOptions.Once);
waitForMelodyEnd();
}
}
}
Playable
クラスを継承した StringArrayPlayable
クラスの、 _play
メソッドで、 PlaybackMode
別に、 music.startMelodyInternal()
関数を呼び出しています。
「鳴らす」ブロック
次にそれぞれの 鳴らす
ブロックを確認してみます。
JavaScript
input.onButtonPressed(Button.A, function () {
music.play(music.stringPlayable("C5 B A G F E D C ", 120), music.PlaybackMode.UntilDone)
music.play(music.tonePlayable(262, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone)
})
input.onButtonPressed(Button.AB, function () {
music._playDefaultBackground(music.builtInPlayableMelody(Melodies.Dadadadum), music.PlaybackMode.InBackground)
})
input.onButtonPressed(Button.B, function () {
music.play(music.builtinPlayableSoundEffect(soundExpression.giggle), music.PlaybackMode.UntilDone)
music.play(music.createSoundExpression(WaveShape.Sine, 5000, 0, 255, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear), music.PlaybackMode.UntilDone)
})
ブロックではわかりにくいのですが、 鳴らす
ブロックは、 music.play
もしくは music._playDefaultBackground
です。第一引数の型は、 Playable
であり、それぞれのブロックで、継承先の異なるインスタンスが指定されていることがわかります。
この中で文字列の配列をメロディとして再生するのに近そうなのが、 music.stringPlayable()
関数ですので、これを参考にします。
さらに、もう一つ興味深いのが、 music.builtInPlayableMelody()
関数です。その名の通り、予め組み込まれているメロディーを再生するためのインスタンスを生成しています。内部をより詳しく確認すると、メロディーをhex形式で圧縮して定義しています。このhex形式のメロディーをユーザーが指定できるブロックも作成します。
文字列の配列をPlayableにするarrayPlayable()
文字列の配列をPlayableにするのは、とても簡単です。 music.StringArrayPlayable
クラスのインスタンスを生成して、それを返すようにコーディングするだけです。
/**
* Generation of musical notes.
*/
//% weight=100 color=#696969 icon="\uf1b2"
namespace melody {
//% blockId="melody_array_playable"
//% block="melody $melody"
//% toolboxParent=music_playable_play
//% toolboxParentArgument=toPlay
//% duplicateShadowOnDrag
//% parts="headphone"
export function arrayPlayable(melody: string[]): music.Playable {
return new music.StringArrayPlayable(melody, undefined)
}
}
HEXをPlayableにするhexPlayable()
melodies.tsで、予め組み込まれたメロディーがhex形式で定義されています。このhex形式のデータをブロックの引数に指定できるようにします。ただし、ブロック上では、hex形式の値を直接指定することができません。そこで文字列をhexに変換するようにし、内部でhexを文字列の配列に変換後、それをPlayableにします。
これらの一連の変換は、すべて標準機能で実現できます。
/**
* Generation of musical notes.
*/
//% weight=100 color=#696969 icon="\uf1b2"
namespace melody {
//% blockId="melody_hex_playable"
//% block="melody hex $hex"
//% hex.defl="4f01540158015b0258015b03"
//% toolboxParent=music_playable_play
//% toolboxParentArgument=toPlay
//% duplicateShadowOnDrag
//% parts="headphone"
export function hexPlayable(hex: string): music.Playable {
// hex examples: https://github.com/microsoft/pxt-microbit/blob/master/libs/core/melodies.ts
return new music.StringArrayPlayable(music._bufferToMelody(Buffer.fromHex(hex)), undefined)
}
}
ブロックでメロディーを組み立てる
文字列とその配列を使ってメロディーを組み立てるのは、少し手こずる感じがあります。やぱり、標準機能のようにブロックで、音階と長さ(拍)とを指定して並べたいものです。
そこで、次のような拡張機能のブロックを作成しました。 イベントブロックの形をした melody id
ブロックの中に、 scale
ブロックを並べてメロディーを組み立てます。 繰り返し
や もし
も使えます。
再生は、 melody id
ブロックです。これも、Playableなブロックです。
scale
ブロックで注意して欲しいことがあります。このブロックが音を鳴らしているのではなく、内部で文字列の配列に要素を追加しているだけです。それを 鳴らす
ブロックで文字列の配列に変換し、メロディーとして再生しています。
メロディーidのブロック
メロディーをidで管理し、次のブロックでメロディーを宣言したり、鳴らしたりします。
ブロック | 関数名 | 説明 | 備考 |
---|---|---|---|
melody.melodyPlayable | melody.declareMelodyブロックで宣言したメロディーを鳴らす。 | メロディーのidを指定する。 | |
melody.declareMelody | メロディーを宣言する。 | メロディーにidを付与する。 | |
melody.scale | 音階(scale)を追加する。 | 内部の配列に追加する。 | |
melody.rest | 休符を追加する。 | 内部の配列に追加する。 | |
melody.note | 音符(note)を追加する。 | 内部の配列に追加する。 |
おわりに
Melody拡張機能を開発し、 Musical notes という音符表記と文字列の配列とを使ってメロディーを鳴らす機能を復活させました。
さらに、文字列の配列ではなく、ブロックを並べることにより内部的に配列を作成するブロックもMelody拡張機能に盛り込みました。
これらの拡張機能を開発するために、 Playable
クラスを中心に、ソースコードを確認しました。