3. Music_advanced


3.1. Christmas tunes

Jingle bells chorus:
from microbit import *
import music

notes = [
    'E4:4', 'E4:4', 'E4:8',
    'E4:4', 'E4:4', 'E4:8',
    'E4:4', 'G4:4', 'C4:6', 'D4:2',
    'E4:12', 'R:4',
    'F4:4', 'F4:4', 'F4:6', 'F4:2',
    'F4:4', 'E4:4', 'E4:4', 'E4:2', 'E4:2',
    'E4:4', 'D4:4', 'D4:4', 'E4:4',
    'D4:8', 'G4:8',
    'E4:4', 'E4:4', 'E4:8',
    'E4:4', 'E4:4', 'E4:8',
    'E4:4', 'G4:4', 'C4:6', 'D4:2',
    'E4:12', 'R:4',
    'F4:4', 'F4:4', 'F4:6', 'F4:2',
    'F4:4', 'E4:4', 'E4:4', 'E4:2', 'E4:2',
    'G4:4', 'G4:4', 'F4:4', 'D4:4',
    'C4:16'
]

while True:
    if button_a.was_pressed():
        music.stop()
        music.set_tempo(ticks=8, bpm=120)
        music.play(notes, wait=False)
    elif button_b.was_pressed():
        music.stop()
    sleep(1000)
../_images/jingle_bells.png

Exercise

  1. Write the notes for this song.

../_images/we_wish_you.png

Exercise

  1. Write the notes for this hymn.

../_images/silent_night.png

Exercise

  1. Write the notes for this hymn.

../_images/we_three_kings.png

3.2. Christmas tunes

Download the python file merry.py module.

3.3. Random notes Task

Design code to generate random notes, using separate lists of possibilities for each feature of a note: note name, octave, length.
Use random.choice(list_name) to choose a random element from a list.
Make sure the octave is a string. e.g octave 4 can be converted to “4” using str(4).
Make sure the note length is a string. e.g 2 can be converted to “2” using str(2).
Build the full note specification by concatenating each part.
i.e. note name + octave + “:” + length
e.g. c4:2
Create a list of notes, a list of octaves and a list of durations that are to be used.
Create a definition that takes as parameters the list of notes, the list of octaves and the list of durations, then randomly chooses one from each, joins them together and returns a full note.
Then, play the randomly generated notes.

Tasks

  1. Random notes Scaffold:

    from microbit import *
    import random
    import music
    
    notes = ['c', 'd', 'e', 'f', 'g', 'a', 'b']
    octaves = [3, 4, 5]
    durations = [1, 2, 4, 8]
    
    def get_random_note(notes, octaves, durations):
        note = random.choice(......)
        # convert numbers to strings so they ccan be joined
        octave = str(random.choice(.......))
        duration = str(random.choice(.......))
        full_note = ..... + ...... + ":" + ......
        return .......
    
    while True:
        random_note = get_random_note(....., ......, ...... )
        music.play(.....)
    
from microbit import *
import random
import music

notes = ['c', 'd', 'e', 'f', 'g', 'a', 'b']
octaves = [3, 4, 5]
durations = [1, 2, 4, 8]

def get_random_note(notes, octaves, durations):
    note = random.choice(notes)
    # convert numbers to strings so they ccan be joined
    octave = str(random.choice(octaves))
    duration = str(random.choice(durations))
    full_note = note + octave + ":" + duration
    return full_note

while True:
    random_note = get_random_note(notes, octaves, durations)
    music.play(random_note)

Tasks

  1. Include a note modification parameter to sharpen, flatten or leave the note unchanged.

  2. Create a function, get_random_notes, to return a random list of notes, given the number of notes. Use a for-loop to add the random notes to a list within the function. Play the notes when the A-button is pressed.

  3. Rewrite the function, get_random_notes, to use list comprehension.

Include a note modification parameter to sharpen, flatten or leave the note unchanged.

from microbit import *
import random
import music


notes = ["c", "d", "e", "f", "g", "a", "b"]
modifiers = ["#", "b", ""]
octaves = [3, 4, 4, 4, 4, 5]
durations = [2, 3, 4, 2, 3, 4, 2, 3, 4, 8]


def get_random_note(notes, modifiers, octaves, durations):
    note = random.choice(notes)
    modifier = random.choice(modifiers)
    octave = str(random.choice(octaves))
    duration = str(random.choice(durations))
    full_note = note + modifier + octave + ":" + duration
    return full_note


while True:
    random_note = get_random_note(notes, modifiers, octaves, durations)
    music.play(random_note)

Create a function, get_random_notes, to return a random list of notes, given the number of notes. Use a for-loop to add the random notes to a list within the function. Play the notes when the A-button is pressed.

from microbit import *
import random
import music

notes = ["c", "d", "e", "f", "g", "a", "b"]
octaves = [3, 4, 4, 4, 4, 5]
durations = [2, 3, 4, 2, 3, 4, 2, 3, 4, 8]


def get_random_note(notes, octaves, durations):
    note = random.choice(notes)
    octave = str(random.choice(octaves))
    duration = str(random.choice(durations))
    full_note = note + octave + ":" + duration
    return full_note


def get_random_notes(note_count):
    random_notes = []
    for i in range(note_count):
        random_notes.append(get_random_note(notes, octaves, durations))
    return random_notes


while True:
    if button_a.is_pressed():
        random_notes = get_random_notes(10)
        music.play(random_notes)
        sleep(2000)

Rewrite the function, get_random_notes, to use list comprehension.

from microbit import *
import random
import music

notes = ["c", "d", "e", "f", "g", "a", "b"]
octaves = [3, 4, 4, 4, 4, 5]
durations = [2, 3, 4, 2, 3, 4, 2, 3, 4, 8]


def get_random_note(notes, octaves, durations):
    note = random.choice(notes)
    octave = str(random.choice(octaves))
    duration = str(random.choice(durations))
    full_note = note + octave + ":" + duration
    return full_note


def get_random_notes(note_count):
    return [get_random_note(notes, octaves, durations) for i in range(note_count)]


while True:
    random_notes = get_random_notes(10)
    music.play(random_notes)
    sleep(2000)

3.4. Scales generator

Design code to generate the notes in a major scale, given the key and the octave.
Research the intervals for major and natural minor scales and place them in lists, major_steps and minor_steps. Define a function, get_scale_steps(key_type), that returns the scale intervals for a Major or minor key based on passing “M” for major and “m” for minor.
Define a function, get_2oct, to return 2 octaves of notes, starting at c, given the start octave and the notes. Use list comprehension to take each note and add the octave to it as a string. e.g “c” and 4 are joined to become “c4”.
Define a function, get_key_notes, to return the notes in a key given the key. Research the keys that have sharps in them (see: circle of fifths) and make a list of them, sharp_keys. Check to see if the key is in that list and return a list of all possible notes, starting at c, that include sharps, [“c”, “c#”, …], or return the list of notes that includes flats, [“c”, “db”, …].
Define a function, get_scale(key_note, key_type, octave), that returns the list of notes that include sharps or flats based on the key_note and key_type. Then build 2 octaves of notes based on those notes and the octave. Use the index method to get the index of the key_note in that 2 octave list. Then iterate through the scale_steps, adding the scale step interval to the index and append that note to the scale. Return the one octave scale.
Test the definitions by playing some scales that include a scale with a sharp, and a scale with a flat, as well as both major and minor scales.
Scaffold for the task:
from microbit import *
import music


def get_scale_steps(key_type):
    major_steps = [2, ......, 1]
    minor_steps = [2, ......, 2]
    if key_type == "M":
        return major_steps
    else:
        return ......


def get_2oct(octave, notes):
    notes_oct1 = [i + str(octave) for i in notes]
    notes_oct2 = [..... + str(octave + ....) for .... in ........]
    notes_2oct = notes_oct1 + notes_oct2
    return notes_2oct


def get_key_notes(key_note, key_type):
    major_sharp_keys = ["c", "g", "d", "a", "e", "b", "f#", "c#"]
    # major_flat_keys = ["c", "f", "bb", "eb", "ab", "db", "gb", "cb"]
    minor_sharp_keys = ["a", "e", "b", "f#", "c#", "g#", "d#", "a#"]
    # minor_flat_keys = ["a", "d", "g", "c", "f", "bb", "eb", "ab"]
    if key_type == "M":
        sharp_keys = ......
    else:
        sharp_keys = .....
    sharp_key_notes = ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"]
    flat_key_notes = ["c", "db", "d", "eb", "e", "f", "gb", "g", "ab", "a", "bb", "b"]
    if key_note in ......:
        return sharp_key_notes
    else:
        return flat_key_notes


def get_scale(key_note, key_type, octave):
    scale_steps = get_scale_steps(......)
    notes = get_key_notes(......)
    notes2oct = get_2oct(....., ........)
    note_index = notes2oct.index(..... + str(.....))
    scale = [notes2oct[.........]]
    for i in scale_steps:
        note_index += i
        scale.append(notes2oct[........])
    return scale


while True:
    if button_a.is_pressed():
        scale_notes = get_scale("g", "M", 4)
        # print(scale_notes)
        music.play(scale_notes)
        sleep(1000)
        scale_notes = get_scale("e", "m", 4)
        # print(scale_notes)
        music.play(scale_notes)
        sleep(1000)
    elif button_b.is_pressed():
        scale_notes = get_scale("f", "M", 4)
        # print(scale_notes)
        music.play(scale_notes)
        sleep(1000)
        scale_notes = get_scale("d", "m", 4)
        # print(scale_notes)
        music.play(scale_notes)
        sleep(1000)
from microbit import *
import music


def get_scale_steps(key_type):
    major_steps = [2, 2, 1, 2, 2, 2, 1]
    minor_steps = [2, 1, 2, 2, 1, 2, 2]
    if key_type == "M":
        return major_steps
    else:
        return minor_steps


def get_2oct(octave, notes):
    notes_oct1 = [i + str(octave) for i in notes]
    notes_oct2 = [i + str(octave + 1) for i in notes]
    notes_2oct = notes_oct1 + notes_oct2
    return notes_2oct


def get_key_notes(key_note, key_type):
    major_sharp_keys = ["c", "g", "d", "a", "e", "b", "f#", "c#"]
    # major_flat_keys = ["c", "f", "bb", "eb", "ab", "db", "gb", "cb"]
    minor_sharp_keys = ["a", "e", "b", "f#", "c#", "g#", "d#", "a#"]
    # minor_flat_keys = ["a", "d", "g", "c", "f", "bb", "eb", "ab"]
    if key_type == "M":
        sharp_keys = major_sharp_keys
    else:
        sharp_keys = minor_sharp_keys
    sharp_key_notes = ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"]
    flat_key_notes = ["c", "db", "d", "eb", "e", "f", "gb", "g", "ab", "a", "bb", "b"]
    if key_note in sharp_keys:
        return sharp_key_notes
    else:
        return flat_key_notes


def get_scale(key_note, key_type, octave):
    scale_steps = get_scale_steps(key_type)
    notes = get_key_notes(key_note, key_type)
    notes2oct = get_2oct(octave, notes)
    note_index = notes2oct.index(key_note + str(octave))
    scale = [notes2oct[note_index]]
    for i in scale_steps:
        note_index += i
        scale.append(notes2oct[note_index])
    return scale


while True:
    if button_a.is_pressed():
        scale_notes = get_scale("g", "M", 4)
        # print(scale_notes)
        music.play(scale_notes)
        sleep(1000)
        scale_notes = get_scale("e", "m", 4)
        # print(scale_notes)
        music.play(scale_notes)
        sleep(1000)
    elif button_b.is_pressed():
        scale_notes = get_scale("f", "M", 4)
        # print(scale_notes)
        music.play(scale_notes)
        sleep(1000)
        scale_notes = get_scale("d", "m", 4)
        # print(scale_notes)
        music.play(scale_notes)
        sleep(1000)

Tasks

  1. Create a function, get_random_notes(notes, note_count=5), to return a random list of notes, given the notes and the number of notes, with default 5. Use list comprehension to generate the list. Play 5 random notes from the g major and 5 from the a minor scales on button pressing.

from microbit import *
import music
import random


def get_scale_steps(key_type):
    major_steps = [2, 2, 1, 2, 2, 2, 1]
    minor_steps = [2, 1, 2, 2, 1, 2, 2]
    if key_type == "M":
        return major_steps
    else:
        return minor_steps


def get_2oct(octave, notes):
    notes_oct1 = [i + str(octave) for i in notes]
    notes_oct2 = [i + str(octave + 1) for i in notes]
    notes_2oct = notes_oct1 + notes_oct2
    return notes_2oct


def get_key_notes(key_note, key_type):
    major_sharp_keys = ["c", "g", "d", "a", "e", "b", "f#", "c#"]
    # major_flat_keys = ["c", "f", "bb", "eb", "ab", "db", "gb", "cb"]
    minor_sharp_keys = ["a", "e", "b", "f#", "c#", "g#", "d#", "a#"]
    # minor_flat_keys = ["a", "d", "g", "c", "f", "bb", "eb", "ab"]
    if key_type == "M":
        sharp_keys = major_sharp_keys
    else:
        sharp_keys = minor_sharp_keys
    sharp_key_notes = ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"]
    flat_key_notes = ["c", "db", "d", "eb", "e", "f", "gb", "g", "ab", "a", "bb", "b"]
    if key_note in sharp_keys:
        return sharp_key_notes
    else:
        return flat_key_notes


def get_scale(key_note, key_type, octave):
    scale_steps = get_scale_steps(key_type)
    notes = get_key_notes(key_note, key_type)
    notes2oct = get_2oct(octave, notes)
    note_index = notes2oct.index(key_note + str(octave))
    scale = [notes2oct[note_index]]
    for i in scale_steps:
        note_index += i
        scale.append(notes2oct[note_index])
    return scale


def get_random_notes(notes, note_count=5):
    random_notes = [random.choice(notes) for i in range(note_count)]
    return random_notes

while True:
    if button_a.is_pressed():
        random_notes = get_random_notes(get_scale("g", "M", 4), 5)
        print(random_notes)
        music.play(random_notes)
        sleep(1000)
    elif button_b.is_pressed():
        random_notes = get_random_notes(get_scale("a", "m", 4), 5)
        print(random_notes)
        music.play(random_notes)
        sleep(1000)

Exercises

  1. Set up two microbits and send a key from one to the other and have it play on the receiver.

  2. Create a dictionary of keys and their notes and save it to a file to be accessed on the microbit.


3.5. Accelerometer based notes

The code below uses the accelerometer to choose the note and the note duration.
The scale function is used to scale the tilting range to the length of the notes list and the length of the durations list.
The pitches used are based on the E minor scale.
from microbit import *
import music

accelerometer.set_range(1)

play_notes = ["E3", "F#3", "G3", "A3", "B3",
        "E4", "F#4", "G4", "A4", "B4",
        "E5", "F#5", "G5", "A5", "B5",
        "E6", "F#6", "G6", "A6", "B6"]
play_durations = ["1", "2", "4", "8", "16"]
durationlen = len(play_durations)
notelen = len(play_notes)

play_music = True
while True:
    #use A to toggle music
    if button_a.was_pressed():
        play_music = not play_music
    if not play_music:
        continue
    #get accelerometer readings
    xreading = abs(accelerometer.get_x())
    yreading = abs(accelerometer.get_y())
    # use above 1023 incase some micorbits give slightly higher readings
    scaled_x = scale(xreading, from_=(-1200, 1200), to=(-notelen +1, notelen -1))
    scaled_y = scale(yreading, from_=(-1200, 1200), to=(-durationlen +1, durationlen -1))
    #get a note based on tilt
    music.play(play_notes[scaled_x] + ":" + play_durations[scaled_y])

Exercise

  1. Use the accelerometer to control 8 notes of a scale over just one octave.