Microbit Bird Song2

In the previous post about getting microbits singing to each other I mentioned that using the speech API wasn't a good choice due to the limitations of it's enunciation. Here's a better version using the music DSL to better emulate the beeps and boops of a microbit bird flock.

Code

Frogs

In the early development of this, I discovered a good way to emulate a set of bullfrogs croaking away is to stick with a "C1:1" note and repeat that every loop.

from microbit import *
import music

while True:

    music.play("C1:1")
    sleep(1000)

Flashing this to several devices was really effective in giving the frog vibe.

Birds

Here each bird starts off with it's own song generated at the start, then randomly sings part or all of the song each time around the loop. The length, note and octave are randomly assigned, each bird picks an octave then notes stay around that particular register so we don't get too many jarring songs with notes from extreme low and high octaves playing together.

from microbit import *
import random
import music

display.show(Image.DUCK)

note_names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
base_octave = random.randint(2, 8)
note_lengths = [1, 2, 4, 8]
song = []

for x in range(random.randint(1, 8)):
    note_name = random.choice(note_names)
    octave = base_octave + random.randint(-1, 1)
    length = random.choice(note_lengths)
    song.append("{}{}:{}".format(note_name, octave, length))

sound_threshold = 100  # 255 max
microphone.sound_level()  # discard first

while True:

    display.set_pixel(2, 2, 9)

    for x in range(random.randint(1, len(song))):
        music.play(song[x])

    display.set_pixel(2, 2, 0)

    # listen to surrounding noise
    if microphone.sound_level() >= sound_threshold:
        time_to_next_call = 500
    else:
        time_to_next_call = random.randint(1000, 5000)

    sleep(time_to_next_call)

Having several microbits in this bird orchestra still feels a bit like starting a 90s dialup modem but some songs can be genuinely nice or at least interesting. One thing that helped the musicality is keeping the note lengths to the more standard 1, 2, 4, and 8 note durations rather than just a random number between 1 and 8.

Update

After playing around with this in actual demonstrations, I added a new refinement which was to separate the calls into different kinds of "bird". Randomly we pick either a low frequency "owl", a medium frequency "songbird" or a high frequency "cheep".

from microbit import *
import random
import music

def generate_song(song_length, note_names, note_lengths, base_octave):
    song = []
    
    for x in range(song_length):
        note_name = random.choice(note_names)
        octave = base_octave + random.randint(-1, 1)
        length = random.choice(note_lengths)  
        song.append("{}{}:{}".format(note_name, octave, length))
    
    return song
    
display.show(Image.DUCK)

note_names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
note_lengths = [1, 2, 4, 8]
song = []

bird = random.randint(1, 3)

if bird == 1:
    # low voiced owl?
    song = generate_song(2, note_names, note_lengths, 2)
    display.show(Image.ASLEEP)
    music.set_tempo(bpm=60)
elif bird == 2:
    # song bird
    song = generate_song(random.randint(1, 8), note_names, note_lengths, random.randint(2, 8))
    display.show(Image.MUSIC_QUAVER)
else: 
    # high voiced cheep
    song = generate_song(2, note_names, note_lengths, 7)
    display.show(Image.DUCK)
    music.set_tempo(bpm=180)
    
sound_threshold = 100  # 255 max
microphone.sound_level()  # discard first

while True:

    display.set_pixel(2, 2, 9)

    for x in range(random.randint(1, len(song))):
        music.play(song[x])

    display.set_pixel(2, 2, 0)

    # listen to surrounding noise
    if microphone.sound_level() >= sound_threshold:
        time_to_next_call = 500
    else:
        time_to_next_call = random.randint(1000, 5000)

    sleep(time_to_next_call)

I separated out a new function to generate a full song with a given song length and a given octave basis.