Melody events (#391)

* support for custom playTone

* added music.onEvent, music.setPlayTone

* updated docs

* updated sample
This commit is contained in:
Peli de Halleux 2017-04-25 10:14:34 -07:00 committed by GitHub
parent 53ddd4485d
commit 54ae7a4fda
7 changed files with 178 additions and 13 deletions

View File

@ -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)

View 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)
```

View File

@ -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)
```

View 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)

View File

@ -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.",

View File

@ -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",

View File

@ -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 {