upgrading music APIs (#12)
This commit is contained in:
parent
63047454cb
commit
5a6cbf2639
@ -321,21 +321,30 @@
|
|||||||
"motors.motorCommand": "Send break, coast or sleep commands to the motor. Has no effect in dual-motor mode.",
|
"motors.motorCommand": "Send break, coast or sleep commands to the motor. Has no effect in dual-motor mode.",
|
||||||
"motors.motorPower": "Turns on the motor at a certain percent of power. Switches to single motor mode!",
|
"motors.motorPower": "Turns on the motor at a certain percent of power. Switches to single motor mode!",
|
||||||
"motors.motorPower|param|power": "%percent of power sent to the motor. Negative power goes backward. eg: 50",
|
"motors.motorPower|param|power": "%percent of power sent to the motor. Negative power goes backward. eg: 50",
|
||||||
"music": "Generation of music tones through pin ``P0``.",
|
"music": "Generation of music tones.",
|
||||||
"music.beat": "Returns the duration of a beat in milli-seconds",
|
"music.beat": "Returns the duration of a beat in milli-seconds",
|
||||||
|
"music.beginMelody": "Starts playing a melody.\nNotes are expressed as a string of characters with this format: NOTE[octave][:duration]",
|
||||||
|
"music.beginMelody|param|melodyArray": "the melody array to play, eg: ['g5:1']",
|
||||||
|
"music.beginMelody|param|options": "melody options, once / forever, in the foreground / background",
|
||||||
|
"music.builtInMelody": "Gets the melody array of a built-in melody.",
|
||||||
"music.changeTempoBy": "Change the tempo by the specified amount",
|
"music.changeTempoBy": "Change the tempo by the specified amount",
|
||||||
"music.changeTempoBy|param|bpm": "The change in beats per minute to the tempo, eg: 20",
|
"music.changeTempoBy|param|bpm": "The change in beats per minute to the tempo, eg: 20",
|
||||||
"music.noteFrequency": "Gets the frequency of a note.",
|
"music.noteFrequency": "Gets the frequency of a note.",
|
||||||
"music.noteFrequency|param|name": "the note name",
|
"music.noteFrequency|param|name": "the note name, eg: Note.C",
|
||||||
"music.playTone": "Plays a tone through ``speaker`` for the given duration.",
|
"music.onEvent": "Registers code to run on various melody events",
|
||||||
|
"music.playTone": "Plays a tone through pin ``P0`` for the given duration.",
|
||||||
"music.playTone|param|frequency": "pitch of the tone to play in Hertz (Hz)",
|
"music.playTone|param|frequency": "pitch of the tone to play in Hertz (Hz)",
|
||||||
"music.playTone|param|ms": "tone duration in milliseconds (ms)",
|
"music.playTone|param|ms": "tone duration in milliseconds (ms)",
|
||||||
"music.rest": "Rests (plays nothing) for a specified time through pin ``P0``.",
|
"music.rest": "Rests (plays nothing) for a specified time through pin ``P0``.",
|
||||||
"music.rest|param|ms": "rest duration in milliseconds (ms)",
|
"music.rest|param|ms": "rest duration in milliseconds (ms)",
|
||||||
"music.ringTone": "Plays a tone through ``speaker``.",
|
"music.ringTone": "Plays a tone through pin ``P0``.",
|
||||||
"music.ringTone|param|frequency": "pitch of the tone to play in Hertz (Hz)",
|
"music.ringTone|param|frequency": "pitch of the tone to play in Hertz (Hz)",
|
||||||
|
"music.setPlayTone": "Sets a custom playTone function for playing melodies",
|
||||||
"music.setTempo": "Sets the tempo to the specified amount",
|
"music.setTempo": "Sets the tempo to the specified amount",
|
||||||
"music.setTempo|param|bpm": "The new tempo in beats per minute, eg: 120",
|
"music.setTempo|param|bpm": "The new tempo in beats per minute, eg: 120",
|
||||||
|
"music.speakerPlayTone": "Plays a tone through ``speaker`` for the given duration.",
|
||||||
|
"music.speakerPlayTone|param|frequency": "pitch of the tone to play in Hertz (Hz)",
|
||||||
|
"music.speakerPlayTone|param|ms": "tone duration in milliseconds (ms)",
|
||||||
"music.tempo": "Returns the tempo in beats per minute. Tempo is the speed (bpm = beats per minute) at which notes play. The larger the tempo value, the faster the notes will play.",
|
"music.tempo": "Returns the tempo in beats per minute. Tempo is the speed (bpm = beats per minute) at which notes play. The larger the tempo value, the faster the notes will play.",
|
||||||
"parseInt": "Convert A string to an integer.",
|
"parseInt": "Convert A string to an integer.",
|
||||||
"pins": "Control currents in Pins for analog/digital signals, servos, i2c, ...",
|
"pins": "Control currents in Pins for analog/digital signals, servos, i2c, ...",
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
"BaudRate.BaudRate115200|block": "115200",
|
"BaudRate.BaudRate115200|block": "115200",
|
||||||
"BaudRate.BaudRate56700|block": "57600",
|
"BaudRate.BaudRate56700|block": "57600",
|
||||||
"BaudRate.BaudRate9600|block": "9600",
|
"BaudRate.BaudRate9600|block": "9600",
|
||||||
|
"BeatFraction.Breve|block": "4",
|
||||||
|
"BeatFraction.Double|block": "2",
|
||||||
"BeatFraction.Eighth|block": "1/8",
|
"BeatFraction.Eighth|block": "1/8",
|
||||||
"BeatFraction.Half|block": "1/2",
|
"BeatFraction.Half|block": "1/2",
|
||||||
"BeatFraction.Quarter|block": "1/4",
|
"BeatFraction.Quarter|block": "1/4",
|
||||||
@ -121,10 +123,44 @@
|
|||||||
"Math.randomBoolean|block": "pick random true or false",
|
"Math.randomBoolean|block": "pick random true or false",
|
||||||
"Math.random|block": "pick random 0 to %limit",
|
"Math.random|block": "pick random 0 to %limit",
|
||||||
"Math|block": "Math",
|
"Math|block": "Math",
|
||||||
|
"Melodies.BaDing|block": "ba ding",
|
||||||
|
"Melodies.Baddy|block": "baddy",
|
||||||
|
"Melodies.Birthday|block": "birthday",
|
||||||
|
"Melodies.Blues|block": "blues",
|
||||||
|
"Melodies.Chase|block": "chase",
|
||||||
|
"Melodies.Dadadadum|block": "dadadum",
|
||||||
|
"Melodies.Entertainer|block": "entertainer",
|
||||||
|
"Melodies.Funeral|block": "funereal",
|
||||||
|
"Melodies.Funk|block": "funk",
|
||||||
|
"Melodies.JumpDown|block": "jump down",
|
||||||
|
"Melodies.JumpUp|block": "jump up",
|
||||||
|
"Melodies.Nyan|block": "nyan",
|
||||||
|
"Melodies.Ode|block": "ode",
|
||||||
|
"Melodies.PowerDown|block": "power down",
|
||||||
|
"Melodies.PowerUp|block": "power up",
|
||||||
|
"Melodies.Prelude|block": "prelude",
|
||||||
|
"Melodies.Punchline|block": "punchline",
|
||||||
|
"Melodies.Ringtone|block": "ringtone",
|
||||||
|
"Melodies.Wawawawaa|block": "wawawawaa",
|
||||||
|
"Melodies.Wedding|block": "wedding",
|
||||||
|
"MelodyOptions.ForeverInBackground|block": "forever in background",
|
||||||
|
"MelodyOptions.Forever|block": "forever",
|
||||||
|
"MelodyOptions.OnceInBackground|block": "once in background",
|
||||||
|
"MelodyOptions.Once|block": "once",
|
||||||
"Motor.AB|block": "A and B",
|
"Motor.AB|block": "A and B",
|
||||||
"MotorCommand.Break|block": "break",
|
"MotorCommand.Break|block": "break",
|
||||||
"MotorCommand.Coast|block": "coast",
|
"MotorCommand.Coast|block": "coast",
|
||||||
"MotorCommand.Sleep|block": "sleep",
|
"MotorCommand.Sleep|block": "sleep",
|
||||||
|
"MusicEvent.BackgroundMelodyEnded|block": "background melody ended",
|
||||||
|
"MusicEvent.BackgroundMelodyNotePlayed|block": "background melody note played",
|
||||||
|
"MusicEvent.BackgroundMelodyPaused|block": "background melody paused",
|
||||||
|
"MusicEvent.BackgroundMelodyRepeated|block": "background melody repeated",
|
||||||
|
"MusicEvent.BackgroundMelodyResumed|block": "background melody resumed",
|
||||||
|
"MusicEvent.BackgroundMelodyStarted|block": "background melody started",
|
||||||
|
"MusicEvent.MelodyEnded|block": "melody ended",
|
||||||
|
"MusicEvent.MelodyNotePlayed|block": "melody note played",
|
||||||
|
"MusicEvent.MelodyRepeated|block": "melody repeated",
|
||||||
|
"MusicEvent.MelodyStarted|block": "melody started",
|
||||||
"Note.CSharp3|block": "C#3",
|
"Note.CSharp3|block": "C#3",
|
||||||
"Note.CSharp4|block": "C#4",
|
"Note.CSharp4|block": "C#4",
|
||||||
"Note.CSharp5|block": "C#5",
|
"Note.CSharp5|block": "C#5",
|
||||||
@ -225,8 +261,11 @@
|
|||||||
"motors.motorPower|block": "motor on at %percent",
|
"motors.motorPower|block": "motor on at %percent",
|
||||||
"motors|block": "motors",
|
"motors|block": "motors",
|
||||||
"music.beat|block": "%fraction|beat",
|
"music.beat|block": "%fraction|beat",
|
||||||
|
"music.beginMelody|block": "start melody %melody=device_builtin_melody| repeating %options",
|
||||||
|
"music.builtInMelody|block": "%melody",
|
||||||
"music.changeTempoBy|block": "change tempo by (bpm)|%value",
|
"music.changeTempoBy|block": "change tempo by (bpm)|%value",
|
||||||
"music.noteFrequency|block": "%note",
|
"music.noteFrequency|block": "%note",
|
||||||
|
"music.onEvent|block": "music on %value",
|
||||||
"music.playTone|block": "play|tone %note=device_note|for %duration=device_beat",
|
"music.playTone|block": "play|tone %note=device_note|for %duration=device_beat",
|
||||||
"music.rest|block": "rest(ms)|%duration=device_beat",
|
"music.rest|block": "rest(ms)|%duration=device_beat",
|
||||||
"music.ringTone|block": "ring tone (Hz)|%note=device_note",
|
"music.ringTone|block": "ring tone (Hz)|%note=device_note",
|
||||||
|
119
libs/core/melodies.ts
Normal file
119
libs/core/melodies.ts
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2016 The MicroPython-on-micro:bit Developers, as listed
|
||||||
|
in the accompanying AUTHORS file
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Melodies from file microbitmusictunes.c https://github.com/bbcmicrobit/MicroPython
|
||||||
|
|
||||||
|
enum Melodies {
|
||||||
|
//% block="dadadum" blockIdentity=music.builtInMelody
|
||||||
|
Dadadadum = 0,
|
||||||
|
//% block="entertainer" blockIdentity=music.builtInMelody
|
||||||
|
Entertainer,
|
||||||
|
//% block="prelude" blockIdentity=music.builtInMelody
|
||||||
|
Prelude,
|
||||||
|
//% block="ode" blockIdentity=music.builtInMelody
|
||||||
|
Ode,
|
||||||
|
//% block="nyan" blockIdentity=music.builtInMelody
|
||||||
|
Nyan,
|
||||||
|
//% block="ringtone" blockIdentity=music.builtInMelody
|
||||||
|
Ringtone,
|
||||||
|
//% block="funk" blockIdentity=music.builtInMelody
|
||||||
|
Funk,
|
||||||
|
//% block="blues" blockIdentity=music.builtInMelody
|
||||||
|
Blues,
|
||||||
|
//% block="birthday" blockIdentity=music.builtInMelody
|
||||||
|
Birthday,
|
||||||
|
//% block="wedding" blockIdentity=music.builtInMelody
|
||||||
|
Wedding,
|
||||||
|
//% block="funereal" blockIdentity=music.builtInMelody
|
||||||
|
Funeral,
|
||||||
|
//% block="punchline" blockIdentity=music.builtInMelody
|
||||||
|
Punchline,
|
||||||
|
//% block="baddy" blockIdentity=music.builtInMelody
|
||||||
|
Baddy,
|
||||||
|
//% block="chase" blockIdentity=music.builtInMelody
|
||||||
|
Chase,
|
||||||
|
//% block="ba ding" blockIdentity=music.builtInMelody
|
||||||
|
BaDing,
|
||||||
|
//% block="wawawawaa" blockIdentity=music.builtInMelody
|
||||||
|
Wawawawaa,
|
||||||
|
//% block="jump up" blockIdentity=music.builtInMelody
|
||||||
|
JumpUp,
|
||||||
|
//% block="jump down" blockIdentity=music.builtInMelody
|
||||||
|
JumpDown,
|
||||||
|
//% block="power up" blockIdentity=music.builtInMelody
|
||||||
|
PowerUp,
|
||||||
|
//% block="power down" blockIdentity=music.builtInMelody
|
||||||
|
PowerDown,
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace music {
|
||||||
|
|
||||||
|
export function getMelody(melody: Melodies): string[] {
|
||||||
|
switch (melody) {
|
||||||
|
case Melodies.Dadadadum:
|
||||||
|
return ['r4:2', 'g', 'g', 'g', 'eb:8', 'r:2', 'f', 'f', 'f', 'd:8'];
|
||||||
|
case Melodies.Entertainer:
|
||||||
|
return ['d4:1', 'd#', 'e', 'c5:2', 'e4:1', 'c5:2', 'e4:1', 'c5:3', 'c:1', 'd', 'd#', 'e', 'c', 'd', 'e:2', 'b4:1', 'd5:2', 'c:4'];
|
||||||
|
case Melodies.Prelude:
|
||||||
|
return ['c4:1', 'e', 'g', 'c5', 'e', 'g4', 'c5', 'e', 'c4', 'e', 'g', 'c5', 'e', 'g4', 'c5', 'e', 'c4', 'd', 'g', 'd5', 'f', 'g4', 'd5', 'f', 'c4', 'd', 'g', 'd5', 'f', 'g4', 'd5', 'f', 'b3', 'd4', 'g', 'd5', 'f', 'g4', 'd5', 'f', 'b3', 'd4', 'g', 'd5', 'f', 'g4', 'd5', 'f', 'c4', 'e', 'g', 'c5', 'e', 'g4', 'c5', 'e', 'c4', 'e', 'g', 'c5', 'e', 'g4', 'c5', 'e'];
|
||||||
|
case Melodies.Ode:
|
||||||
|
return ['e4', 'e', 'f', 'g', 'g', 'f', 'e', 'd', 'c', 'c', 'd', 'e', 'e:6', 'd:2', 'd:8', 'e:4', 'e', 'f', 'g', 'g', 'f', 'e', 'd', 'c', 'c', 'd', 'e', 'd:6', 'c:2', 'c:8'];
|
||||||
|
case Melodies.Nyan:
|
||||||
|
return ['f#5:2', 'g#', 'c#:1', 'd#:2', 'b4:1', 'd5:1', 'c#', 'b4:2', 'b', 'c#5', 'd', 'd:1', 'c#', 'b4:1', 'c#5:1', 'd#', 'f#', 'g#', 'd#', 'f#', 'c#', 'd', 'b4', 'c#5', 'b4', 'd#5:2', 'f#', 'g#:1', 'd#', 'f#', 'c#', 'd#', 'b4', 'd5', 'd#', 'd', 'c#', 'b4', 'c#5', 'd:2', 'b4:1', 'c#5', 'd#', 'f#', 'c#', 'd', 'c#', 'b4', 'c#5:2', 'b4', 'c#5', 'b4', 'f#:1', 'g#', 'b:2', 'f#:1', 'g#', 'b', 'c#5', 'd#', 'b4', 'e5', 'd#', 'e', 'f#', 'b4:2', 'b', 'f#:1', 'g#', 'b', 'f#', 'e5', 'd#', 'c#', 'b4', 'f#', 'd#', 'e', 'f#', 'b:2', 'f#:1', 'g#', 'b:2', 'f#:1', 'g#', 'b', 'b', 'c#5', 'd#', 'b4', 'f#', 'g#', 'f#', 'b:2', 'b:1', 'a#', 'b', 'f#', 'g#', 'b', 'e5', 'd#', 'e', 'f#', 'b4:2', 'c#5'];
|
||||||
|
case Melodies.Ringtone:
|
||||||
|
return ['c4:1', 'd', 'e:2', 'g', 'd:1', 'e', 'f:2', 'a', 'e:1', 'f', 'g:2', 'b', 'c5:4'];
|
||||||
|
case Melodies.Funk:
|
||||||
|
return ['c2:2', 'c', 'd#', 'c:1', 'f:2', 'c:1', 'f:2', 'f#', 'g', 'c', 'c', 'g', 'c:1', 'f#:2', 'c:1', 'f#:2', 'f', 'd#'];
|
||||||
|
case Melodies.Blues:
|
||||||
|
return ['c2:2', 'e', 'g', 'a', 'a#', 'a', 'g', 'e', 'c2:2', 'e', 'g', 'a', 'a#', 'a', 'g', 'e', 'f', 'a', 'c3', 'd', 'd#', 'd', 'c', 'a2', 'c2:2', 'e', 'g', 'a', 'a#', 'a', 'g', 'e', 'g', 'b', 'd3', 'f', 'f2', 'a', 'c3', 'd#', 'c2:2', 'e', 'g', 'e', 'g', 'f', 'e', 'd'];
|
||||||
|
case Melodies.Birthday:
|
||||||
|
return ['c4:3', 'c:1', 'd:4', 'c:4', 'f', 'e:8', 'c:3', 'c:1', 'd:4', 'c:4', 'g', 'f:8', 'c:3', 'c:1', 'c5:4', 'a4', 'f', 'e', 'd', 'a#:3', 'a#:1', 'a:4', 'f', 'g', 'f:8'];
|
||||||
|
case Melodies.Wedding:
|
||||||
|
return ['c4:4', 'f:3', 'f:1', 'f:8', 'c:4', 'g:3', 'e:1', 'f:8', 'c:4', 'f:3', 'a:1', 'c5:4', 'a4:3', 'f:1', 'f:4', 'e:3', 'f:1', 'g:8'];
|
||||||
|
case Melodies.Funeral:
|
||||||
|
return ['c3:4', 'c:3', 'c:1', 'c:4', 'd#:3', 'd:1', 'd:3', 'c:1', 'c:3', 'b2:1', 'c3:4'];
|
||||||
|
case Melodies.Punchline:
|
||||||
|
return ['c4:3', 'g3:1', 'f#', 'g', 'g#:3', 'g', 'r', 'b', 'c4'];
|
||||||
|
case Melodies.Baddy:
|
||||||
|
return ['c3:3', 'r', 'd:2', 'd#', 'r', 'c', 'r', 'f#:8'];
|
||||||
|
case Melodies.Chase:
|
||||||
|
return ['a4:1', 'b', 'c5', 'b4', 'a:2', 'r', 'a:1', 'b', 'c5', 'b4', 'a:2', 'r', 'a:2', 'e5', 'd#', 'e', 'f', 'e', 'd#', 'e', 'b4:1', 'c5', 'd', 'c', 'b4:2', 'r', 'b:1', 'c5', 'd', 'c', 'b4:2', 'r', 'b:2', 'e5', 'd#', 'e', 'f', 'e', 'd#', 'e'];
|
||||||
|
case Melodies.BaDing:
|
||||||
|
return ['b5:1', 'e6:3'];
|
||||||
|
case Melodies.Wawawawaa:
|
||||||
|
return ['e3:3', 'r:1', 'd#:3', 'r:1', 'd:4', 'r:1', 'c#:8'];
|
||||||
|
case Melodies.JumpUp:
|
||||||
|
return ['c5:1', 'd', 'e', 'f', 'g'];
|
||||||
|
case Melodies.JumpDown:
|
||||||
|
return ['g5:1', 'f', 'e', 'd', 'c'];
|
||||||
|
case Melodies.PowerUp:
|
||||||
|
return ['g4:1', 'c5', 'e', 'g:2', 'e:1', 'g:3'];
|
||||||
|
case Melodies.PowerDown:
|
||||||
|
return ['g5:1', 'd#', 'c', 'g4:2', 'b:1', 'c5:3'];
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,10 +6,9 @@ namespace music {
|
|||||||
* @param frequency pitch of the tone to play in Hertz (Hz)
|
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||||
* @param ms tone duration in milliseconds (ms)
|
* @param ms tone duration in milliseconds (ms)
|
||||||
*/
|
*/
|
||||||
//% help=music/play-tone weight=90
|
//%
|
||||||
//% blockId=device_play_note block="play|tone %note=device_note|for %duration=device_beat" icon="\uf025" blockGap=8
|
|
||||||
//% parts="speaker" async useEnumVal=1
|
//% parts="speaker" async useEnumVal=1
|
||||||
void playTone(int frequency, int ms) {
|
void speakerPlayTone(int frequency, int ms) {
|
||||||
if(frequency > 0) uBit.soundmotor.soundOn(frequency);
|
if(frequency > 0) uBit.soundmotor.soundOn(frequency);
|
||||||
else uBit.soundmotor.soundOff();
|
else uBit.soundmotor.soundOff();
|
||||||
if(ms > 0) {
|
if(ms > 0) {
|
||||||
|
@ -119,15 +119,82 @@ enum BeatFraction {
|
|||||||
//% block="1/8"
|
//% block="1/8"
|
||||||
Eighth = 8,
|
Eighth = 8,
|
||||||
//% block="1/16"
|
//% block="1/16"
|
||||||
Sixteenth = 16
|
Sixteenth = 16,
|
||||||
|
//% block="2"
|
||||||
|
Double = 32,
|
||||||
|
//% block="4",
|
||||||
|
Breve = 64
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MelodyOptions {
|
||||||
|
//% block="once""
|
||||||
|
Once = 1,
|
||||||
|
//% block="forever"
|
||||||
|
Forever = 2,
|
||||||
|
//% block="once in background"
|
||||||
|
OnceInBackground = 4,
|
||||||
|
//% block="forever in background"
|
||||||
|
ForeverInBackground = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MusicEvent {
|
||||||
|
//% block="melody note played"
|
||||||
|
MelodyNotePlayed = 1,
|
||||||
|
//% block="melody started"
|
||||||
|
MelodyStarted = 2,
|
||||||
|
//% block="melody ended"
|
||||||
|
MelodyEnded = 3,
|
||||||
|
//% block="melody repeated"
|
||||||
|
MelodyRepeated = 4,
|
||||||
|
//% block="background melody note played"
|
||||||
|
BackgroundMelodyNotePlayed = MelodyNotePlayed | 0xf0,
|
||||||
|
//% block="background melody started"
|
||||||
|
BackgroundMelodyStarted = MelodyStarted | 0xf0,
|
||||||
|
//% block="background melody ended"
|
||||||
|
BackgroundMelodyEnded = MelodyEnded | 0xf0,
|
||||||
|
//% block="background melody repeated"
|
||||||
|
BackgroundMelodyRepeated = MelodyRepeated | 0xf0,
|
||||||
|
//% block="background melody paused"
|
||||||
|
BackgroundMelodyPaused = 5 | 0xf0,
|
||||||
|
//% block="background melody resumed"
|
||||||
|
BackgroundMelodyResumed = 6 | 0xf0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generation of music tones through pin ``P0``.
|
* Generation of music tones.
|
||||||
*/
|
*/
|
||||||
//% color=#DF4600 weight=98 icon="\uf025"
|
//% color=#DF4600 weight=98 icon="\uf025"
|
||||||
namespace music {
|
namespace music {
|
||||||
let beatsPerMinute: number = 120;
|
let beatsPerMinute: number = 120;
|
||||||
|
let freqTable: number[] = [];
|
||||||
|
let _playTone: (frequency: number, duration: number) => void;
|
||||||
|
const MICROBIT_MELODY_ID = 2000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays a tone through pin ``P0`` for the given duration.
|
||||||
|
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||||
|
* @param ms tone duration in milliseconds (ms)
|
||||||
|
*/
|
||||||
|
//% help=music/play-tone weight=90
|
||||||
|
//% blockId=device_play_note block="play|tone %note=device_note|for %duration=device_beat" blockGap=8
|
||||||
|
//% parts="headphone"
|
||||||
|
//% useEnumVal=1
|
||||||
|
export function playTone(frequency: number, ms: number): void {
|
||||||
|
if (_playTone) _playTone(frequency, ms);
|
||||||
|
else speakerPlayTone(frequency, ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays a tone through pin ``P0``.
|
||||||
|
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||||
|
*/
|
||||||
|
//% help=music/ring-tone weight=80
|
||||||
|
//% blockId=device_ring block="ring tone (Hz)|%note=device_note" blockGap=8
|
||||||
|
//% parts="headphone"
|
||||||
|
//% useEnumVal=1
|
||||||
|
export function ringTone(frequency: number): void {
|
||||||
|
playTone(frequency, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rests (plays nothing) for a specified time through pin ``P0``.
|
* Rests (plays nothing) for a specified time through pin ``P0``.
|
||||||
@ -135,38 +202,28 @@ namespace music {
|
|||||||
*/
|
*/
|
||||||
//% help=music/rest weight=79
|
//% help=music/rest weight=79
|
||||||
//% blockId=device_rest block="rest(ms)|%duration=device_beat"
|
//% blockId=device_rest block="rest(ms)|%duration=device_beat"
|
||||||
//% parts="speaker"
|
//% parts="headphone"
|
||||||
export function rest(ms: number): void {
|
export function rest(ms: number): void {
|
||||||
playTone(0, ms);
|
playTone(0, ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Plays a tone through ``speaker``.
|
|
||||||
* @param frequency pitch of the tone to play in Hertz (Hz)
|
|
||||||
*/
|
|
||||||
//% help=music/ring-tone weight=80
|
|
||||||
//% blockId=device_ring block="ring tone (Hz)|%note=device_note" blockGap=8
|
|
||||||
//% parts="speaker" async
|
|
||||||
//% useEnumVal=1
|
|
||||||
export function ringTone(frequency: number) {
|
|
||||||
playTone(frequency, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the frequency of a note.
|
* Gets the frequency of a note.
|
||||||
* @param name the note name
|
* @param name the note name, eg: Note.C
|
||||||
*/
|
*/
|
||||||
//% weight=50 help=music/note-frequency
|
//% weight=50 help=music/note-frequency
|
||||||
//% blockId=device_note block="%note"
|
//% blockId=device_note block="%note"
|
||||||
//% shim=TD_ID blockHidden=true
|
//% shim=TD_ID
|
||||||
//% blockFieldEditor="note_editor"
|
//% note.fieldEditor="note" note.defl="262"
|
||||||
//% useEnumVal = 1
|
//% useEnumVal=1
|
||||||
export function noteFrequency(name: Note): number {
|
export function noteFrequency(name: Note): number {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
if (beatsPerMinute <= 0) beatsPerMinute = 120;
|
if (beatsPerMinute <= 0) beatsPerMinute = 120;
|
||||||
|
if (freqTable.length == 0) freqTable = [31, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123, 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,11 +235,15 @@ namespace music {
|
|||||||
init();
|
init();
|
||||||
if (fraction == null) fraction = BeatFraction.Whole;
|
if (fraction == null) fraction = BeatFraction.Whole;
|
||||||
let beat = 60000 / beatsPerMinute;
|
let beat = 60000 / beatsPerMinute;
|
||||||
if (fraction == BeatFraction.Whole) return beat;
|
switch (fraction) {
|
||||||
else if (fraction == BeatFraction.Half) return beat / 2;
|
case BeatFraction.Half: return beat / 2;
|
||||||
else if (fraction == BeatFraction.Quarter) return beat / 4;
|
case BeatFraction.Quarter: return beat / 4;
|
||||||
else if (fraction == BeatFraction.Eighth) return beat / 8;
|
case BeatFraction.Eighth: return beat / 8;
|
||||||
else return beat / 16;
|
case BeatFraction.Sixteenth: return beat / 16;
|
||||||
|
case BeatFraction.Double: return beat * 2;
|
||||||
|
case BeatFraction.Breve: return beat * 4;
|
||||||
|
default: return beat;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -212,10 +273,166 @@ namespace music {
|
|||||||
*/
|
*/
|
||||||
//% help=music/set-tempo weight=38
|
//% help=music/set-tempo weight=38
|
||||||
//% blockId=device_set_tempo block="set tempo to (bpm)|%value"
|
//% blockId=device_set_tempo block="set tempo to (bpm)|%value"
|
||||||
|
//% bpm.min=4 bpm.max=400
|
||||||
export function setTempo(bpm: number): void {
|
export function setTempo(bpm: number): void {
|
||||||
init();
|
init();
|
||||||
if (bpm > 0) {
|
if (bpm > 0) {
|
||||||
beatsPerMinute = Math.max(1, bpm);
|
beatsPerMinute = Math.max(1, bpm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentMelody: Melody;
|
||||||
|
let currentBackgroundMelody: Melody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the melody array of a built-in melody.
|
||||||
|
* @param name the note name, eg: Note.C
|
||||||
|
*/
|
||||||
|
//% weight=50 help=music/builtin-melody
|
||||||
|
//% blockId=device_builtin_melody block="%melody"
|
||||||
|
//% blockHidden=true
|
||||||
|
export function builtInMelody(melody: Melodies): string[] {
|
||||||
|
return getMelody(melody);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers code to run on various melody events
|
||||||
|
*/
|
||||||
|
//% blockId=melody_on_event block="music on %value"
|
||||||
|
//% help=music/on-event weight=59
|
||||||
|
export function onEvent(value: MusicEvent, handler: Action) {
|
||||||
|
control.onEvent(MICROBIT_MELODY_ID, value, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, eg: ['g5:1']
|
||||||
|
* @param options melody options, once / forever, in the foreground / background
|
||||||
|
*/
|
||||||
|
//% help=music/begin-melody weight=60 blockGap=8
|
||||||
|
//% blockId=device_start_melody block="start melody %melody=device_builtin_melody| repeating %options"
|
||||||
|
//% parts="headphone"
|
||||||
|
export function beginMelody(melodyArray: string[], options: MelodyOptions = 1) {
|
||||||
|
init();
|
||||||
|
if (currentMelody != undefined) {
|
||||||
|
if (((options & MelodyOptions.OnceInBackground) == 0)
|
||||||
|
&& ((options & MelodyOptions.ForeverInBackground) == 0)
|
||||||
|
&& currentMelody.background) {
|
||||||
|
currentBackgroundMelody = currentMelody;
|
||||||
|
currentMelody = null;
|
||||||
|
control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.BackgroundMelodyPaused);
|
||||||
|
}
|
||||||
|
if (currentMelody)
|
||||||
|
control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background ? MusicEvent.BackgroundMelodyEnded : MusicEvent.MelodyEnded);
|
||||||
|
currentMelody = new Melody(melodyArray, options);
|
||||||
|
control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background ? MusicEvent.BackgroundMelodyStarted : MusicEvent.MelodyStarted);
|
||||||
|
} else {
|
||||||
|
currentMelody = new Melody(melodyArray, options);
|
||||||
|
control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background ? MusicEvent.BackgroundMelodyStarted : MusicEvent.MelodyStarted);
|
||||||
|
// Only start the fiber once
|
||||||
|
control.inBackground(() => {
|
||||||
|
while (currentMelody.hasNextNote()) {
|
||||||
|
playNextNote(currentMelody);
|
||||||
|
if (!currentMelody.hasNextNote() && currentBackgroundMelody) {
|
||||||
|
// Swap the background melody back
|
||||||
|
currentMelody = currentBackgroundMelody;
|
||||||
|
currentBackgroundMelody = null;
|
||||||
|
control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.MelodyEnded);
|
||||||
|
control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.BackgroundMelodyResumed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background ? MusicEvent.BackgroundMelodyEnded : MusicEvent.MelodyEnded);
|
||||||
|
currentMelody = null;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a custom playTone function for playing melodies
|
||||||
|
*/
|
||||||
|
//% help=music/set-play-tone
|
||||||
|
//% advanced=true
|
||||||
|
export function setPlayTone(f: (frequency: number, duration: number) => void) {
|
||||||
|
_playTone = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
function playNextNote(melody: Melody): void {
|
||||||
|
// cache elements
|
||||||
|
let currNote = melody.nextNote();
|
||||||
|
let currentPos = melody.currentPos;
|
||||||
|
let currentDuration = melody.currentDuration;
|
||||||
|
let currentOctave = melody.currentOctave;
|
||||||
|
|
||||||
|
let note: number;
|
||||||
|
let isrest: boolean = false;
|
||||||
|
let beatPos: number;
|
||||||
|
let parsingOctave: boolean = true;
|
||||||
|
|
||||||
|
for (let pos = 0; pos < currNote.length; pos++) {
|
||||||
|
let noteChar = currNote.charAt(pos);
|
||||||
|
switch (noteChar) {
|
||||||
|
case 'c': case 'C': note = 1; break;
|
||||||
|
case 'd': case 'D': note = 3; break;
|
||||||
|
case 'e': case 'E': note = 5; break;
|
||||||
|
case 'f': case 'F': note = 6; break;
|
||||||
|
case 'g': case 'G': note = 8; break;
|
||||||
|
case 'a': case 'A': note = 10; break;
|
||||||
|
case 'b': case 'B': note = 12; break;
|
||||||
|
case 'r': case 'R': isrest = true; break;
|
||||||
|
case '#': note++; break;
|
||||||
|
case 'b': note--; break;
|
||||||
|
case ':': parsingOctave = false; beatPos = pos; break;
|
||||||
|
default: if (parsingOctave) currentOctave = parseInt(noteChar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!parsingOctave) {
|
||||||
|
currentDuration = parseInt(currNote.substr(beatPos + 1, currNote.length - beatPos));
|
||||||
|
}
|
||||||
|
let beat = (60000 / beatsPerMinute) / 4;
|
||||||
|
if (isrest) {
|
||||||
|
music.rest(currentDuration * beat)
|
||||||
|
} else {
|
||||||
|
let keyNumber = note + (12 * (currentOctave - 1));
|
||||||
|
let frequency = keyNumber >= 0 && keyNumber < freqTable.length ? freqTable[keyNumber] : 0;
|
||||||
|
music.playTone(frequency, currentDuration * beat);
|
||||||
|
}
|
||||||
|
melody.currentDuration = currentDuration;
|
||||||
|
melody.currentOctave = currentOctave;
|
||||||
|
const repeating = melody.repeating && currentPos == melody.melodyArray.length - 1;
|
||||||
|
melody.currentPos = repeating ? 0 : currentPos + 1;
|
||||||
|
|
||||||
|
control.raiseEvent(MICROBIT_MELODY_ID, melody.background ? MusicEvent.BackgroundMelodyNotePlayed : MusicEvent.MelodyNotePlayed);
|
||||||
|
if (repeating)
|
||||||
|
control.raiseEvent(MICROBIT_MELODY_ID, melody.background ? MusicEvent.BackgroundMelodyRepeated : MusicEvent.MelodyRepeated);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Melody {
|
||||||
|
public melodyArray: string[];
|
||||||
|
public currentDuration: number;
|
||||||
|
public currentOctave: number;
|
||||||
|
public currentPos: number;
|
||||||
|
public repeating: boolean;
|
||||||
|
public background: boolean;
|
||||||
|
|
||||||
|
constructor(melodyArray: string[], options: MelodyOptions) {
|
||||||
|
this.melodyArray = melodyArray;
|
||||||
|
this.repeating = ((options & MelodyOptions.Forever) != 0);
|
||||||
|
this.repeating = this.repeating ? true : ((options & MelodyOptions.ForeverInBackground) != 0)
|
||||||
|
this.background = ((options & MelodyOptions.OnceInBackground) != 0);
|
||||||
|
this.background = this.background ? true : ((options & MelodyOptions.ForeverInBackground) != 0);
|
||||||
|
this.currentDuration = 4; //Default duration (Crotchet)
|
||||||
|
this.currentOctave = 4; //Middle octave
|
||||||
|
this.currentPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNextNote() {
|
||||||
|
return this.repeating || this.currentPos < this.melodyArray.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextNote(): string {
|
||||||
|
const currentNote = this.melodyArray[this.currentPos];
|
||||||
|
return currentNote;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
"led.ts",
|
"led.ts",
|
||||||
"motors.cpp",
|
"motors.cpp",
|
||||||
"music.cpp",
|
"music.cpp",
|
||||||
|
"melodies.ts",
|
||||||
"music.ts",
|
"music.ts",
|
||||||
"pins.cpp",
|
"pins.cpp",
|
||||||
"pins.ts",
|
"pins.ts",
|
||||||
|
7
libs/core/shims.d.ts
vendored
7
libs/core/shims.d.ts
vendored
@ -552,10 +552,9 @@ declare namespace music {
|
|||||||
* @param frequency pitch of the tone to play in Hertz (Hz)
|
* @param frequency pitch of the tone to play in Hertz (Hz)
|
||||||
* @param ms tone duration in milliseconds (ms)
|
* @param ms tone duration in milliseconds (ms)
|
||||||
*/
|
*/
|
||||||
//% help=music/play-tone weight=90
|
//%
|
||||||
//% blockId=device_play_note block="play|tone %note=device_note|for %duration=device_beat" icon="\uf025" blockGap=8
|
//% parts="speaker" async useEnumVal=1 shim=music::speakerPlayTone
|
||||||
//% parts="speaker" async useEnumVal=1 shim=music::playTone
|
function speakerPlayTone(frequency: number, ms: number): void;
|
||||||
function playTone(frequency: number, ms: number): void;
|
|
||||||
}
|
}
|
||||||
declare namespace pins {
|
declare namespace pins {
|
||||||
|
|
||||||
|
@ -57,7 +57,8 @@
|
|||||||
"listsBlocks": true,
|
"listsBlocks": true,
|
||||||
"functionBlocks": true,
|
"functionBlocks": true,
|
||||||
"onStartColor": "#54C9C9",
|
"onStartColor": "#54C9C9",
|
||||||
"onStartNamespace": "basic"
|
"onStartNamespace": "basic",
|
||||||
|
"onStartWeight": 54
|
||||||
},
|
},
|
||||||
"simulator": {
|
"simulator": {
|
||||||
"autoRun": true,
|
"autoRun": true,
|
||||||
|
@ -7,7 +7,7 @@ namespace pxsim {
|
|||||||
}
|
}
|
||||||
namespace pxsim.music {
|
namespace pxsim.music {
|
||||||
|
|
||||||
export function playTone(frequency: number, ms: number) {
|
export function speakerPlayTone(frequency: number, ms: number) {
|
||||||
const b = board();
|
const b = board();
|
||||||
b.speakerState.frequency = frequency;
|
b.speakerState.frequency = frequency;
|
||||||
b.speakerState.ms = ms;
|
b.speakerState.ms = ms;
|
||||||
|
Loading…
Reference in New Issue
Block a user