Melody events (#391)
* support for custom playTone * added music.onEvent, music.setPlayTone * updated docs * updated sample
This commit is contained in:
parent
53ddd4485d
commit
54ae7a4fda
@ -10,8 +10,11 @@ music.beat(BeatFraction.Whole);
|
||||
music.tempo();
|
||||
music.changeTempoBy(20);
|
||||
music.setTempo(120);
|
||||
music.onEvent(MusicEvent.MelodyNotePlayed, () => {});
|
||||
music.setPlayTone((freq, ms) => {});
|
||||
```
|
||||
|
||||
### See Also
|
||||
|
||||
[playTone](/reference/music/play-tone), [ringTone](/reference/music/ring-tone), [rest](/reference/music/rest), [beat](/reference/music/beat), [tempo](/reference/music/tempo), [changeTempoBy](/reference/music/change-tempo-by), [setTempo](/reference/music/set-tempo)
|
||||
[playTone](/reference/music/play-tone), [ringTone](/reference/music/ring-tone), [rest](/reference/music/rest), [beat](/reference/music/beat), [tempo](/reference/music/tempo), [changeTempoBy](/reference/music/change-tempo-by), [setTempo](/reference/music/set-tempo),
|
||||
[setPlayTone](/reference/music/set-play-tone), [onEvent](/reference/music/on-event)
|
||||
|
51
docs/reference/music/on-event.md
Normal file
51
docs/reference/music/on-event.md
Normal file
@ -0,0 +1,51 @@
|
||||
# On Event
|
||||
|
||||
Raises events for melodies or music events.
|
||||
|
||||
```sig
|
||||
music.onEvent(MusicEvent.MelodyNotePlayed, () => {})
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
* ``value`` the kind of event
|
||||
* ``handler`` the code to run when the event is raised.
|
||||
|
||||
### Example
|
||||
|
||||
This example prints all the events to the serial output.
|
||||
|
||||
```blocks
|
||||
music.onEvent(MusicEvent.MelodyRepeated, () => {
|
||||
serial.writeLine("melody repeated")
|
||||
})
|
||||
music.onEvent(MusicEvent.MelodyEnded, () => {
|
||||
serial.writeLine("melody ended")
|
||||
})
|
||||
music.onEvent(MusicEvent.MelodyStarted, () => {
|
||||
serial.writeLine("melody started")
|
||||
})
|
||||
music.onEvent(MusicEvent.MelodyRepeated, () => {
|
||||
serial.writeLine("background melody repeated")
|
||||
})
|
||||
music.onEvent(MusicEvent.BackgroundMelodyStarted, () => {
|
||||
serial.writeLine("background started")
|
||||
})
|
||||
music.onEvent(MusicEvent.BackgroundMelodyEnded, () => {
|
||||
serial.writeLine("background ended")
|
||||
})
|
||||
music.onEvent(MusicEvent.BackgroundMelodyPaused, () => {
|
||||
serial.writeLine("background paused")
|
||||
})
|
||||
music.onEvent(MusicEvent.BackgroundMelodyResumed, () => {
|
||||
serial.writeLine("background resumed")
|
||||
})
|
||||
music.onEvent(MusicEvent.BackgroundMelodyRepeated, () => {
|
||||
serial.writeLine("background repeated")
|
||||
})
|
||||
input.onButtonPressed(Button.A, () => {
|
||||
music.beginMelody(music.builtInMelody(Melodies.BaDing), MelodyOptions.Once)
|
||||
})
|
||||
music.setTempo(100)
|
||||
music.beginMelody(music.builtInMelody(Melodies.Ringtone), MelodyOptions.ForeverInBackground)
|
||||
```
|
@ -2,10 +2,12 @@
|
||||
|
||||
Play a musical tone through pin ``P0`` of the @boardname@ for as long as you say.
|
||||
|
||||
## Simulator
|
||||
### ~ hint
|
||||
|
||||
This function only works on the @boardname@ and in some browsers.
|
||||
|
||||
### ~
|
||||
|
||||
```sig
|
||||
music.playTone(440, 120)
|
||||
```
|
||||
|
38
docs/reference/music/set-play-tone.md
Normal file
38
docs/reference/music/set-play-tone.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Set Play Tone
|
||||
|
||||
Replaces the implementation of the [music play tone](/reference/music/play-tone).
|
||||
|
||||
|
||||
```sig
|
||||
music.setPlayTone((freq, ms) => {})
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
* ``f`` the replacement function
|
||||
|
||||
### Example
|
||||
|
||||
This example send the frequency and duration over radio
|
||||
and plays it on the remote @boardname@.
|
||||
|
||||
```typescript
|
||||
input.onButtonPressed(Button.A, () => {
|
||||
music.playTone(440, 120)
|
||||
led.toggle(0, 0)
|
||||
})
|
||||
radio.onDataPacketReceived( ({ receivedNumber }) => {
|
||||
const freq = receivedNumber >> 16;
|
||||
const duration = receivedNumber & 0xffff;
|
||||
music.playTone(freq, duration);
|
||||
})
|
||||
input.onButtonPressed(Button.B, () => {
|
||||
music.setPlayTone((frequency: number, duration: number) => {
|
||||
radio.sendNumber((frequency << 16) | (duration & 0xffff));
|
||||
})
|
||||
})
|
||||
```
|
||||
### See also
|
||||
|
||||
[rest](/reference/music/rest), [ring tone](/reference/music/ring-tone) , [tempo](/reference/music/tempo), [set tempo](/reference/music/set-tempo),
|
||||
[change tempo by](/reference/music/change-tempo-by)
|
@ -164,15 +164,16 @@
|
||||
"led.unplot": "Turn off the specified LED using x, y coordinates (x is horizontal, y is vertical). (0,0) is upper left.",
|
||||
"led.unplot|param|x": "TODO",
|
||||
"led.unplot|param|y": "TODO",
|
||||
"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.beginMelody": "Starts playing a melody through pin ``P0``.\nNotes are expressed as a string of characters with this format: NOTE[octave][:duration]",
|
||||
"music.beginMelody": "Starts playing a melody.\nNotes are expressed as a string of characters with this format: NOTE[octave][:duration]",
|
||||
"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|param|bpm": "The change in beats per minute to the tempo, eg: 20",
|
||||
"music.noteFrequency": "Gets the frequency of a note.",
|
||||
"music.noteFrequency|param|name": "the note name, eg: Note.C",
|
||||
"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|ms": "tone duration in milliseconds (ms)",
|
||||
@ -180,6 +181,7 @@
|
||||
"music.rest|param|ms": "rest duration in milliseconds (ms)",
|
||||
"music.ringTone": "Plays a tone through pin ``P0``.",
|
||||
"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|param|bpm": "The new tempo in beats per minute, eg: 120",
|
||||
"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.",
|
||||
|
@ -149,6 +149,16 @@
|
||||
"MelodyOptions.Forever|block": "forever",
|
||||
"MelodyOptions.OnceInBackground|block": "once in background",
|
||||
"MelodyOptions.Once|block": "once",
|
||||
"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.CSharp4|block": "C#4",
|
||||
"Note.CSharp5|block": "C#5",
|
||||
@ -234,10 +244,11 @@
|
||||
"led.unplot|block": "unplot|x %x|y %y",
|
||||
"led|block": "led",
|
||||
"music.beat|block": "%fraction|beat",
|
||||
"music.beginMelody|block": "start|melody %melody=device_builtin_melody| repeating %options",
|
||||
"music.beginMelody|block": "start melody %melody=device_builtin_melody| repeating %options",
|
||||
"music.builtInMelody|block": "%melody",
|
||||
"music.changeTempoBy|block": "change tempo by (bpm)|%value",
|
||||
"music.noteFrequency|block": "%note",
|
||||
"music.onEvent|block": "music on %value",
|
||||
"music.playTone|block": "play|tone %note=device_note|for %duration=device_beat",
|
||||
"music.rest|block": "rest(ms)|%duration=device_beat",
|
||||
"music.ringTone|block": "ring tone (Hz)|%note=device_note",
|
||||
|
@ -137,13 +137,38 @@ enum MelodyOptions {
|
||||
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=#D83B01 weight=98 icon="\uf025"
|
||||
namespace music {
|
||||
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.
|
||||
@ -155,7 +180,8 @@ namespace music {
|
||||
//% parts="headphone"
|
||||
//% useEnumVal = 1
|
||||
export function playTone(frequency: number, ms: number): void {
|
||||
pins.analogPitch(frequency, ms);
|
||||
if (_playTone) _playTone(frequency, ms);
|
||||
else pins.analogPitch(frequency, ms);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -167,7 +193,7 @@ namespace music {
|
||||
//% parts="headphone"
|
||||
//% useEnumVal = 1
|
||||
export function ringTone(frequency: number): void {
|
||||
pins.analogPitch(frequency, 0);
|
||||
playTone(frequency, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -270,25 +296,40 @@ namespace music {
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts playing a melody through pin ``P0``.
|
||||
* 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 melody the melody array to play, eg: ['g5:1']
|
||||
* @param options melody options, once / forever, in the foreground / background
|
||||
*/
|
||||
//% help=music/begin-melody weight=60
|
||||
//% blockId=device_start_melody block="start|melody %melody=device_builtin_melody| repeating %options"
|
||||
//% 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 = MelodyOptions.Once) {
|
||||
init();
|
||||
if (currentMelody != undefined) {
|
||||
if (((options & MelodyOptions.OnceInBackground) == 0)
|
||||
&& ((options & MelodyOptions.ForeverInBackground) == 0)
|
||||
&& currentMelody.background == true) {
|
||||
&& 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()) {
|
||||
@ -297,13 +338,25 @@ namespace music {
|
||||
// Swap the background melody back
|
||||
currentMelody = currentBackgroundMelody;
|
||||
currentBackgroundMelody = null;
|
||||
control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.MelodyEnded);
|
||||
control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.BackgroundMelodyResumed);
|
||||
}
|
||||
}
|
||||
currentMelody = null;
|
||||
control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background ? MusicEvent.BackgroundMelodyEnded : MusicEvent.MelodyEnded);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
@ -346,7 +399,12 @@ namespace music {
|
||||
}
|
||||
melody.currentDuration = currentDuration;
|
||||
melody.currentOctave = currentOctave;
|
||||
melody.currentPos = melody.repeating == true && currentPos == melody.melodyArray.length - 1 ? 0 : currentPos + 1;
|
||||
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user