Skip to content

Instantly share code, notes, and snippets.

@haveyouwantto
Last active January 2, 2025 14:32
Show Gist options
  • Save haveyouwantto/8014511cb6adea77973e4235204bb85d to your computer and use it in GitHub Desktop.
Save haveyouwantto/8014511cb6adea77973e4235204bb85d to your computer and use it in GitHub Desktop.
ESP32 PWM
from time import sleep
from machine import Pin, PWM
import struct
# Function to calculate the frequency of a musical note based on its MIDI note number
def get_freq(note):
return int(440 * 2 ** ((note - 69) / 12.0))
# Lists for note offsets and PWM duty values
offset = [0, -24, 0, 0, 0, -12, 12, 24, 36, 0, 0, 0, 0, 0, 0, 0]
duty = [24576, 32768, 0, 0, 0, 24576, 32768, 32768, 32768, 8192, 8192, 32768, 16384, 32768, 16384, 32768]
# Class for managing sound on multiple channels
class SoundManager:
def __init__(self, *pins):
self.channels = []
self.size = len(pins)
for pin in pins:
pwm = PWM(Pin(pin))
pwm.freq(48000)
self.channels.append(pwm)
self.active = 0
def add(self, freq, inst):
if self.active < self.size:
self.channels[self.active].duty_u16(duty[inst])
self.channels[self.active].freq(freq)
self.active += 1
def clear(self):
self.active = 0
def stop(self):
for p in self.channels:
p.freq(48000)
self.active = 0
# Initialize the SoundManager with specified pins
snd = SoundManager(4, 5, 19, 22)
# Open and read the music file in binary mode
f = open('music/牛仔很忙.mcs', 'rb')
# Read the magic bytes, version, delay, and length from the file
magic = f.read(4)
version, delay, length = struct.unpack("<hfi", f.read(10))
print(version, delay, length)
# Clear any existing sounds
snd.clear()
print(1)
# Function to pause the sound for a specified time
def pause(time):
sleep(time * 0.05 * delay)
snd.stop()
# Function to play a note with a given frequency and instrument
def play(freq, inst):
snd.add(freq, inst)
try:
while True:
for i in range(length):
# Read timing, instrument, and note data from the file
timing, inst, note = struct.unpack("<hbb", f.read(4))
print(i, timing, inst, note)
if inst in (2, 3, 4): # Check if the instrument is for percussion
if timing != 0:
pause(timing)
continue
if version == 0:
play(get_freq(note + offset[inst]), inst)
if timing != 0:
pause(timing)
elif version == 1:
if timing != 0:
pause(timing)
play(get_freq(note + offset[inst]), inst)
snd.stop()
sleep(1)
f.seek(14)
except KeyboardInterrupt:
snd.stop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment