Last active
October 14, 2020 00:56
-
-
Save darrylsloan/76fc4410e85215b15133e85190ee0238 to your computer and use it in GitHub Desktop.
Music sequencer for the BBC micro:bit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# MUSIC SEQUENCER by Darryl Sloan (30/11/2017) | |
# Step 1: press A to toggle the first note of the desired scale, then B to select. | |
# Step 2: press A to toggle the time signature (3/4, 4/4 or 5/4), then B to select. | |
# Step 3: press A to toggle the desired number of patterns (1 to 4), then B to select. | |
# Press A to move the range of notes selected. Press B to randomise the selected notes. | |
# The direction of tilt determines which scale the random notes conform to: | |
# Left = minor; right = major; forward = minor pentatonic; backward = major pentatonic. | |
# While holding A during the first note of the sequence, tilting up/down increases/decreases the tempo, | |
# tilting left/right selects a lower/higher octave. | |
# While holding A, tap B to show the notes in English for transcription purposes. | |
from microbit import * | |
import music | |
import random | |
octave = 3 | |
tempo_adjust = 90 | |
music.set_tempo(bpm = tempo_adjust) | |
selection = 0 | |
pattern = 0 | |
all_notes = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | |
major = (0, 2, 4, 5, 7, 9, 11) | |
minor = (0, 2, 3, 5, 7, 8, 10) | |
major_pent = (0, 2, 4, 7, 9) | |
minor_pent = (0, 3, 5, 7, 10) | |
scale_major = ["", "", "", "", "", "", ""] | |
scale_minor = ["", "", "", "", "", "", ""] | |
scale_major_pent = ["", "", "", "", ""] | |
scale_minor_pent = ["", "", "", "", ""] | |
# User selects scale | |
scale = 0 | |
display.scroll("Scale") | |
while True: | |
display.show(all_notes[scale]) | |
if button_a.was_pressed(): | |
scale += 1 | |
if scale == 12: | |
scale = 0 | |
if button_b.was_pressed(): | |
break | |
for i in range(7): | |
scale_major[i] = all_notes[major[i]] | |
scale_minor[i] = all_notes[minor[i]] | |
for i in range(5): | |
scale_major_pent[i] = all_notes[major_pent[i]] | |
scale_minor_pent[i] = all_notes[minor_pent[i]] | |
notes = scale_major | |
# User selects time signature | |
beat = 4 | |
display.scroll("Beat") | |
while True: | |
display.show(str(beat)) | |
if button_a.was_pressed(): | |
beat += 1 | |
if beat == 6: | |
beat = 3 | |
riff_length = beat*4 | |
if button_b.was_pressed(): | |
break | |
# User selects quantity of patterns | |
patterns = 1 | |
display.scroll("Patterns") | |
while True: | |
display.show(str(patterns)) | |
if button_a.was_pressed(): | |
patterns += 1 | |
if patterns == 5: | |
patterns = 1 | |
if button_b.was_pressed(): | |
break | |
# Load example riff | |
sequence = [] | |
default = [all_notes[scale]+"4"] | |
for i in range(beat-1): | |
default += [all_notes[scale]+"3"] | |
for i in range(patterns*4): | |
sequence += default | |
while True: | |
x, y = 0, 0 | |
for i in range(riff_length): | |
# Refresh display | |
display.clear() | |
display.set_pixel(pattern, 4, 9) | |
for highlight in range(beat): | |
display.set_pixel(highlight, selection, 5) | |
display.set_pixel(x, y, 9) | |
# User reveals notes in English | |
if button_a.is_pressed() and button_b.was_pressed(): | |
for p in range(patterns): | |
text = "" | |
for q in range(8): | |
display.show(str(p+1)) | |
sleep(200) | |
display.clear() | |
sleep(200) | |
for j in range(riff_length): | |
text += sequence[j+(pattern*riff_length)]+" " | |
display.scroll(text) | |
# User adjusts octave or tempo | |
if i == 0 and button_a.is_pressed(): | |
if accelerometer.get_x() < 0 and abs(accelerometer.get_y()) < abs(accelerometer.get_x()) and octave > 1: | |
octave -= 1 | |
if accelerometer.get_x() > 0 and abs(accelerometer.get_y()) < abs(accelerometer.get_x()) and octave < 6: | |
octave += 1 | |
if accelerometer.get_y() < 0 and abs(accelerometer.get_x()) < abs(accelerometer.get_y()) and tempo_adjust < 200: | |
tempo_adjust += 10 | |
if accelerometer.get_y() > 0 and abs(accelerometer.get_x()) < abs(accelerometer.get_y()) and tempo_adjust > 60: | |
tempo_adjust -= 10 | |
music.set_tempo(bpm = tempo_adjust) | |
# User changes current selection | |
if button_a.was_pressed(): | |
selection += 1 | |
if selection == 4: | |
selection = 0 | |
# User randomises selected notes | |
if button_b.was_pressed(): | |
if accelerometer.get_x() < 0 and abs(accelerometer.get_y()) < abs(accelerometer.get_x()): | |
notes = scale_minor | |
if accelerometer.get_x() > 0 and abs(accelerometer.get_y()) < abs(accelerometer.get_x()): | |
notes = scale_major | |
if accelerometer.get_y() < 0 and abs(accelerometer.get_x()) < abs(accelerometer.get_y()): | |
notes = scale_minor_pent | |
if accelerometer.get_y() > 0 and abs(accelerometer.get_x()) < abs(accelerometer.get_y()): | |
notes = scale_major_pent | |
for r in range(selection*beat, selection*beat+beat): | |
sequence[r+(pattern*riff_length)] = random.choice(notes)+str(random.randint(octave, octave+1)) | |
# Play current note | |
music.play(sequence[i+(pattern*riff_length)]+":1") | |
# Move playhead forward by 1 | |
x += 1 | |
if x == beat: | |
x = 0 | |
y += 1 | |
if y == 4: | |
y = 0 | |
# Move pattern forward by 1 | |
if x == 0 and y == 0: | |
pattern += 1 | |
if pattern == patterns: | |
pattern = 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is so awesome, I can't believe how complete this program is for something that fits on a Micro:Bit! I'm going to have so much fun playing with this, thank you :)