Skip to content

Instantly share code, notes, and snippets.

@connornishijima
Last active December 16, 2023 02:41
Show Gist options
  • Save connornishijima/c55ee765ad6f832085fcd5bdac8734d2 to your computer and use it in GitHub Desktop.
Save connornishijima/c55ee765ad6f832085fcd5bdac8734d2 to your computer and use it in GitHub Desktop.
"Lovely Music Generator" For CircuitPython by LIXIE LABS
# ----------------------------------------------------------------------------
# LOVELY MUSIC GENERATOR by LIXIE LABS
# https://leds.social/@lixielabs
#
# Plays random notes/chords from the Amaj7 scale in a pleasing fashion using
# audiopwmio to drive a speaker with five synthio sine voices and ADSR envelopes.
# Import dependencies
import time
import board
import synthio
import audiopwmio
import array
import math
import random
# ----------------------------------------------------------------------------
# USER VARIABLES
# You can use the speaker directly, but it's MUCH louder to use a PAM8503 3W Audio Amplifier PCB (https://www.amazon.com/ALAMSCN-Speaker-Loudspeaker-Amplifier-Replacement/dp/B0953B5KMJ/ref=sr_1_6)
SPEAKER_PWM_PIN = board.GP15
# Some of the AMaj7 scale in MIDI note numbers:
MUSICAL_SCALE = [40, 42, 44, 45, 47, 49, 50, 52, 54, 56, 57, 59, 61, 62, 64, 66, 68, 69, 71, 73, 74, 76, 78]
# How much to transpose the scale (default +2 octaves)
TRANSPOSITION = 12
# Should the ADSR envelope evolve slightly over time?
envelope_changes = True
# ----------------------------------------------------------------------------
# AUDIO SETUP SECTION
# Initialize PWM audio output on SPEAKER_PIN
audio = audiopwmio.PWMAudioOut(SPEAKER_PWM_PIN, quiescent_value = 32768)
# Render waveform data for use by synthio (single sine cycle)
synth_waveform = array.array('h')
wave_sample_count = 1024
for i in range(wave_sample_count):
sine_value = math.sin(2 * math.pi * (i / wave_sample_count)) * 0.2
adjusted_value = int(((sine_value + 1) / 2) * 65535) - 32768
synth_waveform.append(adjusted_value)
# Define the note ADSR envelope timings
adsr_envelope = synthio.Envelope(
attack_time = 0.05, # Attack time in seconds
decay_time = 0.50, # Decay time in seconds
release_time = 1.00, # Release time in seconds
attack_level = 1.00, # Peak volume of the attack phase (0.0 to 1.0)
sustain_level = 0.40 # Volume of the sustain phase relative to the attack level (0.0 to 1.0)
)
# Initialize the synthesizer
synth = synthio.Synthesizer(sample_rate=44100, envelope=adsr_envelope, waveform=synth_waveform)
# Start the audio engine
audio.play(synth)
# ----------------------------------------------------------------------------
# LOVELY MUSIC SECTION
# This stores currently playing notes
notes = [0, 0, 0, 0, 0]
# Start looping forever now
while True:
# Iterate through 5 voices
for i in range(len(notes)):
# If 1 in 10 chance:
if random.randint(0, 10) < 1:
# Play random new note from MUSICAL_SCALE + TRANSPOSITION using this voice
synth.release((notes[i]))
notes[i] = MUSICAL_SCALE[random.randint(0, len(MUSICAL_SCALE) - 1)] + TRANSPOSITION
synth.press((notes[i]))
# If 1 in 10 chance: (1 in 100 since note changes are already 1 in 10) alter the ADSR envelope
if random.randint(0, 10) < 1 and envelope_changes == True:
new_adsr_envelope = synthio.Envelope(
attack_time = (random.randint(10,100) / 500.0), # Attack time in seconds
decay_time = (random.randint(10,100) / 500.0), # Decay time in seconds
release_time = (random.randint(10,100) / 150.0), # Release time in seconds
attack_level = 1.00, # Peak volume of the attack phase (0.0 to 1.0)
sustain_level = 0.10 # Volume of the sustain phase relative to the attack level (0.0 to 1.0)
)
synth.envelope = new_adsr_envelope
# Keeps playback speed in check
time.sleep(0.4)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment