LoginSignup
0
0

はじめに

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

Musical notes(配列)の再生
g909.png

音楽から消えたブロック

micro:bitのリファレンスでは、 Musical notes という音符表記と文字列の配列とを使ったメロディーの作り方が紹介されています。

しかし、そのメロディーを再生するブロックが見当たりません。
begin Melodyのページで、再生方法を説明していますが、そのコード例は、JavaScriptとPythonだけです。ブロック表記もありますが、単にブロックの形をしたJavaScriptのコードです。

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

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

ソースコードのコメントを読むと、これらのブロックはすべて廃止されている(all deprecated)と書かれています。

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 という内部的な再生機能は使えそうです(いつかは廃止になるのかもしれません)。

Melody拡張機能の開発

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

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

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

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

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

Playableとは

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

まずは、コメントにあるように、 music.startMelodyInternal() 関数は、共通関数であり("Shared code between ...")、加えて StringPlayable.play から呼び出されているようですので、 music.startMelodyInternal() 関数を参照しているコードを検索します。すると、次のコードが見つかりました(StringArrayPlayable._play)。

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

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

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

HEXをPlayableにするhexPlayable()

melodies.tsで、予め組み込まれたメロディーがhex形式で定義されています。このhex形式のデータをブロックの引数に指定できるようにします。ただし、ブロック上では、hex形式の値を直接指定することができません。そこで文字列をhexに変換するようにし、内部でhexを文字列の配列に変換後、それをPlayableにします。
これらの一連の変換は、すべて標準機能で実現できます。

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

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

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

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

g3199.png

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

メロディーidのブロック

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

ブロック 関数名 説明 備考

g6666.png

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

g7461.png

melody.declareMelody メロディーを宣言する。 メロディーにidを付与する。

g6700.png

melody.scale 音階(scale)を追加する。 内部の配列に追加する。

g6728.png

melody.rest 休符を追加する。 内部の配列に追加する。

g6760.png

melody.note 音符(note)を追加する。 内部の配列に追加する。

おわりに

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0