micro:bitの音楽から消えたブロックがある

# はじめに

micro:bitでは、音を鳴らすことができます。v1では外付けのスピーカー等が必要でしたが、v2からはスピーカーが内蔵されています。音楽ブロック（Music）も標準で用意されており、内蔵するメロディーを再生したり、音のブロックをつなぎ合わせてメロディーを作ったりすることもできます。
元々は、[Musical notes](https://makecode.microbit.org/reference/music/making-melodies#musical-notes) によるメロディーの再生をブロックでコーディングできましたが、そのブロックが消え、隠しコマンドになっています(JavaScriptやPythonであればコーディング可能)。
そこで、この隠しコマンドを復活する拡張機能を作成するとともに、配列を使わずにブロックを連結してメロディーを作成する機能をその拡張機能に盛り込みました。

**Musical notes(配列)の再生**
![g909.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/eb67bddd-8a13-43f1-e17e-9d1ff209eeed.png)

# 音楽から消えたブロック

micro:bitのリファレンスでは、 [Musical notes](https://makecode.microbit.org/reference/music/making-melodies#musical-notes) という音符表記と文字列の配列とを使ったメロディーの作り方が紹介されています。

https://makecode.microbit.org/reference/music/making-melodies

しかし、そのメロディーを再生するブロックが見当たりません。
[begin Melody](https://makecode.microbit.org/reference/music/begin-melody)のページで、再生方法を説明していますが、そのコード例は、JavaScriptとPythonだけです。ブロック表記もありますが、単にブロックの形をしたJavaScriptのコードです。

https://makecode.microbit.org/reference/music/begin-melody

[pxt-microbit](https://github.com/microsoft/pxt-microbit)のソースコードを調べると、音楽(music)にあった次のブロックが廃止されているようです(`//% deprecated=1`)。

|関数名|説明|備考|
|------|----|----|
|beginMelody|**文字列の配列**をメロディーとして再生する|startMelody()の呼び出し|
|startMelody|**文字列の配列**をメロディーとして再生する||
|playMelody|**スペース区切りの文字列**をメロディーとして再生する|再生が終わるまで待つ|

[ソースコードのコメント](https://github.com/microsoft/pxt-microbit/blob/master/libs/core/music.ts#L407)を読むと、これらのブロックはすべて廃止されている(**all deprecated**)と書かれています。

```ts:music.ts
// 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](https://makecode.microbit.org/reference/music/making-melodies#musical-notes) という内部的な再生機能は使えそうです（いつかは廃止になるのかもしれません）。

# Melody拡張機能の開発

**文字列の配列をメロディーとして再生する機能**を復活させる為に、 **Melody拡張機能** を開発します。
さらに、配列を使わずに**ブロックを連結してメロディーを作成する機能**をこの拡張機能に盛り込みます。

## ちょっと待った！配列再生ブロックの復活

文字列の配列をメロディーとして再生する為には、既存の`music.beginMelody()`をそのまま **Melody拡張機能** としてコピーすれば良いだけです。

```ts:custom.ts
/**
 * 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);
    }
}
```

**コーディング例**
![g1204.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/d1af5d4e-d7df-5ce5-c80c-6e443d4d27c3.png)

https://makecode.microbit.org/_9EzRYWCXmW6r

でも、ちょっと待ってください。先ほどのコメントを読むと、何らかの理由で `music.beginMelody` ブロックは廃止されているのです。それにもかかわらず、`StringPlayable.play` という機能は廃止されていないのです。これは、従来のブロックから`StringPlayable.play`へと移行しているのではないかと考えられます。

## Playableとは

そこで、 `StringPlayable.play` について、ソースコードを確認してみました。

まずは、コメントにあるように、 `music.startMelodyInternal()` 関数は、共通関数であり(**"Shared code between ..."**)、加えて **StringPlayable.play** から呼び出されているようですので、 `music.startMelodyInternal()` 関数を参照しているコードを検索します。すると、次のコードが見つかりました([StringArrayPlayable._play](https://github.com/microsoft/pxt-microbit/blob/master/libs/core/playable.ts#L44))。

```ts:playable.ts [L.L.39-59]
    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()` 関数を呼び出しています。

## 「鳴らす」ブロック

次にそれぞれの `鳴らす` ブロックを確認してみます。

**ブロック**
![g968.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/085300d8-fba6-5ada-50f2-5cfe90c60ed3.png)

**JavaScript**
```ts
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` クラスのインスタンスを生成して、それを返すようにコーディングするだけです。

```ts:custom.ts
/**
 * 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)
    }
}
```

**コーディング例**
![g1336.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/254e84c5-a612-93e1-1052-81140e63958b.png)

https://makecode.microbit.org/_h28JF6YDELzk

## HEXをPlayableにするhexPlayable()

[melodies.ts](https://github.com/microsoft/pxt-microbit/blob/master/libs/core/melodies.ts#L79)で、予め組み込まれたメロディーがhex形式で定義されています。このhex形式のデータをブロックの引数に指定できるようにします。ただし、ブロック上では、hex形式の値を直接指定することができません。そこで文字列をhexに変換するようにし、内部でhexを文字列の配列に変換後、それをPlayableにします。
これらの一連の変換は、すべて標準機能で実現できます。

```ts:custom.ts
/**
 * 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)
    }
}
```
**コーディング例**
![g2154.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/bc1dbe99-f801-f4ac-6d09-1328efcd1fb0.png)

https://makecode.microbit.org/_1EaX9EDDk65J

# ブロックでメロディーを組み立てる

文字列とその配列を使ってメロディーを組み立てるのは、少し手こずる感じがあります。やぱり、標準機能のようにブロックで、音階と長さ（拍）とを指定して並べたいものです。

そこで、次のような拡張機能のブロックを作成しました。 イベントブロックの形をした `melody id` ブロックの中に、 `scale` ブロックを並べてメロディーを組み立てます。 `繰り返し` や `もし` も使えます。
再生は、 `melody id` ブロックです。これも、Playableなブロックです。

![g3199.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/377793b2-de71-d90d-8f96-164d874a841c.png)

https://makecode.microbit.org/_CKP4UHaRzHC6

`scale` ブロックで注意して欲しいことがあります。このブロックが音を鳴らしているのではなく、内部で文字列の配列に要素を追加しているだけです。それを `鳴らす` ブロックで文字列の配列に変換し、メロディーとして再生しています。

## メロディーidのブロック

メロディーをidで管理し、次のブロックでメロディーを宣言したり、鳴らしたりします。

<table>
<tr><th>ブロック</th><th>関数名</th><th>説明</th><th>備考</th></tr>
<tr><td>

![g6666.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/ded929cb-c440-ec08-1794-d938b4f65ff9.png)

</td><td>
melody.melodyPlayable
</td><td>
melody.declareMelodyブロックで宣言したメロディーを鳴らす。
</td><td>
メロディーのidを指定する。
</td></tr>
<tr><td>

![g7461.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/ade85d7b-3c35-ebbf-403c-bf89c3e4abd4.png)

</td><td>
melody.declareMelody
</td><td>
メロディーを宣言する。
</td><td>
メロディーにidを付与する。
</td></tr>
<tr><td>

![g6700.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/f9b18c78-bcee-b0db-550b-a6227a7bc293.png)

</td><td>
melody.scale
</td><td>
音階(scale)を追加する。
</td><td>
内部の配列に追加する。
</td></tr>
<tr><td>

![g6728.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/de7ae550-3d4f-b4fe-c3db-40c10c3c2770.png)

</td><td>
melody.rest
</td><td>
休符を追加する。
</td><td>
内部の配列に追加する。
</td></tr>
<tr><td>

![g6760.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/37112306-a728-5725-9365-8245eb57eeca.png)

</td><td>
melody.note
</td><td>
音符(note)を追加する。
</td><td>
内部の配列に追加する。
</td></tr>
</table>

# おわりに

Melody拡張機能を開発し、 [Musical notes](https://makecode.microbit.org/reference/music/making-melodies#musical-notes) という音符表記と文字列の配列とを使ってメロディーを鳴らす機能を復活させました。
さらに、文字列の配列ではなく、ブロックを並べることにより内部的に配列を作成するブロックもMelody拡張機能に盛り込みました。
これらの拡張機能を開発するために、 `Playable` クラスを中心に、ソースコードを確認しました。