Updated original repo to work with Ubuntu 24.04

This commit is contained in:
Ruben Ramirez 2025-03-18 14:35:01 -04:00
parent c7f3237b43
commit 5218c39bb7
21 changed files with 369 additions and 97 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
mido
threading

View file

@ -2,3 +2,56 @@
sample sequencer for raspberry pi
Video Example here! https://youtu.be/zX5hSGyLj7c
The sequencer has 4 polyphony and allows the user to store and cue up 6 different sequences
that they can alternate between in real time, and supports the ability to change between different samples.
The Asterisk * and the Period. both operate as function keys.
MAINFUNCTIONS
[8] - Toggle Metronome on and off
[9] - Toggle Recording Mode on and off
[Enter] - Play/Pause sequence
[0] - Delete Current Note in Sequence
[Num Lock and *] - Shut down
[MIDI Note and .] - Don't quantize to even numbers
TEMPO FUNCTIONS
[+] - Course Speed Up BPM
[-] - Course Slow Down BPM
[+ and *] - Rapidly Speed Up BPM
[- and *] - Rapidly Slow Down BPM
[+ and .] - Fine Speed Up BPM
[- and .] - Fine Slow Down BPM
SEQUENCE FUNCTIONS
[1-6] Recall Sequence 1-6
[1-6 and .] Store Sequence 1-6
[0 and .] Clear Current Sequence
CHANGING SAMPLE FOLDERS
[1-9 and *] - Change to Sample Pack in Folders 1-9
[1-9 and * and .] - Change to Sample Pack in Folders 10-18
SEQUENCE TIPS:
-Clear the current sequence [0 and .] and store it to any sequences you want cleared out of memory.
-Recall a sequence and store it to a different number to copy it.

79
audiohelp.py Normal file
View file

@ -0,0 +1,79 @@
import pygame
import sys
import os
import time
def test_audio():
print("=== Pygame Audio System Test ===")
print("Initializing Pygame Audio System...")
# Initialize pygame mixer with specific settings
try:
pygame.mixer.pre_init(22050, -16, 8, 512)
pygame.init()
print("✓ Pygame initialized successfully")
except Exception as e:
print(f"✗ Failed to initialize pygame: {e}")
return False
# Check mixer initialization
try:
print("\nChecking audio configuration...")
mixer_settings = pygame.mixer.get_init()
if mixer_settings:
freq, format, channels = mixer_settings
print(f"✓ Sample rate: {freq}Hz")
print(f"✓ Format: {format}")
print(f"✓ Channels: {channels}")
else:
print("✗ Mixer not initialized")
return False
except Exception as e:
print(f"✗ Failed to get mixer info: {e}")
return False
# Try to load and play the metronome sound
try:
print("\nTesting sound playback...")
if not os.path.exists("metronome.wav"):
print("✗ Could not find metronome.wav")
return False
test_sound = pygame.mixer.Sound("metronome.wav")
print("✓ Metronome sound loaded successfully")
print("\nPlaying metronome sound on loop...")
print("Can you hear the sound? (y/n)")
# Play sound on loop
channel = test_sound.play(-1) # -1 means loop indefinitely
response = input().lower()
channel.stop() # Stop the sound
if response == 'y':
print("✓ Audio test successful!")
return True
else:
print("✗ Audio test failed - no sound heard")
return False
except Exception as e:
print(f"✗ Failed to play test sound: {e}")
return False
if __name__ == "__main__":
success = test_audio()
if success:
print("\n✓ Audio system is working correctly!")
else:
print("\n✗ Audio system test failed. Please check your audio configuration.")
print("Troubleshooting tips:")
print("1. Check if your speakers/headphones are connected and turned on")
print("2. Check if system volume is unmuted and turned up")
print("3. Verify metronome.wav exists in the same directory as this script")
print("4. Try running 'pygame.mixer.quit()' and reinitializing if audio stops working")
print("\nPress Enter to exit...")
input()

72
midihelp.py Normal file → Executable file
View file

@ -1,9 +1,69 @@
import threading
import mido
import time
import sys
def test_midi():
print("=== MIDI Connection Test ===")
# Check available ports
try:
ins = mido.get_input_names()
print("\nAvailable MIDI input ports:")
for i, port in enumerate(ins):
print(f"{i + 1}. {port}")
if not ins:
print("No MIDI input ports found!")
return False
# Look for LPD8
lpd8_ports = [port for port in ins if 'LPD8' in port]
if lpd8_ports:
print("\nFound LPD8 device:", lpd8_ports[0])
port_name = lpd8_ports[0]
else:
print("\nLPD8 not found. Please select a port number:")
choice = input("> ")
try:
port_name = ins[int(choice) - 1]
except (ValueError, IndexError):
print("Invalid selection!")
return False
# Try to open the port
print(f"\nAttempting to connect to: {port_name}")
with mido.open_input(port_name) as inport:
print("✓ Successfully opened MIDI port")
print("\nNow listening for MIDI messages...")
print("Press pads/knobs on your controller (Ctrl+C to exit)")
try:
while True:
msg = inport.poll()
if msg:
print(f"Received: {msg}")
time.sleep(0.1) # Small delay to prevent CPU hogging
except KeyboardInterrupt:
print("\nTest completed!")
return True
except Exception as e:
print(f"\nError: {e}")
print("\nTroubleshooting tips:")
print("1. Make sure your MIDI device is properly connected")
print("2. Try unplugging and reconnecting the USB cable")
print("3. Check if device shows up in your system's MIDI devices")
print("4. Ensure no other applications are using the MIDI port")
return False
ins = mido.get_input_names()
print(ins)
inport = mido.open_input(ins[0])
for msg in inport:
print(msg)
if __name__ == "__main__":
success = test_midi()
if success:
print("\n✓ MIDI system is working correctly!")
else:
print("\n✗ MIDI test failed. Please check your MIDI configuration.")
print("\nPress Enter to exit...")
input()

View file

@ -31,18 +31,34 @@ class ThreadedMidi(threading.Thread):
threading.Thread.__init__(self, *args, **kwargs)
self.daemon = True
self.start()
def run(self):
# get mido going
ins = mido.get_input_names()
print(ins)
inport = mido.open_input(ins[0])
for msg in inport:
print(msg)
if msg.type != "program_change":
if msg.type == "note_on":
playSound(msg.note)
else:
toggleSeq()
print("Available MIDI inputs:", ins)
# Look specifically for the LPD8
lpd8_ports = [port for port in ins if 'LPD8' in port]
if not lpd8_ports:
print("Warning: LPD8 not found in MIDI devices")
return
try:
print(f"Attempting to connect to {lpd8_ports[0]}")
inport = mido.open_input(lpd8_ports[0])
print("Successfully connected to LPD8")
print("Waiting for MIDI messages... (Press pads or knobs on LPD8)")
for msg in inport:
print(f"Received MIDI message: {msg}")
if msg.type != "program_change":
if msg.type == "note_on":
playSound(msg.note)
else:
toggleSeq()
except Exception as e:
print(f"Error in MIDI handling: {e}")
##FUNCTIONS
def playSound(note):
@ -78,12 +94,18 @@ def playSound(note):
def makeNoise(note):
j = 0;
j = 0
while j < len(noteList):
if note==noteList[j]:
cha[j].play(s[j])
break;
j = j + 1
if note == noteList[j]:
try:
if s[j] and cha[j]: # Check if sound and channel exist
cha[j].play(s[j])
else:
print(f"Warning: Missing sound or channel for note {note}")
except Exception as e:
print(f"Error playing note {note}: {e}")
break
j = j + 1
def toggleSeq():
#Func turns on and off sequence
@ -139,8 +161,18 @@ def setWavs(pathnm):
##INIT PYGAME MIXER
pygame.mixer.pre_init(22050, -16, 8, 512)
pygame.init()
# Initialize with explicit settings and error handling
try:
pygame.mixer.pre_init(22050, -16, 8, 512)
pygame.init()
mixer_settings = pygame.mixer.get_init()
if mixer_settings:
print(f"Audio initialized - Sample rate: {mixer_settings[0]}Hz, Format: {mixer_settings[1]}, Channels: {mixer_settings[2]}")
else:
print("Warning: Mixer not properly initialized")
except Exception as e:
print(f"Error initializing audio: {e}")
sys.exit(1)
swing = 0
setMidi = False
@ -148,21 +180,27 @@ met = True
##GET WAVES FROM CURRENT FOLDER
chnWav = False
foldNum = 1 #CHANGE TO NUMBER OF SUBFOLDERS
paths = [0] * foldNum
paths = [] # Initialize as empty list
extension = '/*.wav'
iya = 0
for root, dirs, files in os.walk("."):
# Change from "." to "samples" directory
for root, dirs, files in os.walk("samples"):
for dir in dirs:
print(dir)
paths[iya] = (dir + extension)
paths.append(os.path.join("samples", dir + extension)) # Use os.path.join for proper path handling
iya = iya + 1
# If no directories found, use samples directory itself
if not paths:
paths = [os.path.join("samples", extension[1:])] # Remove leading slash from extension
wavs = glob.glob(paths[0])
wavs = sorted(wavs)
mets = glob.glob('*.wav')
##MIDI NOTE NUMBERS TO PLAY
noteList = [36, 38, 40, 41, 43, 45, 47, 48, 50, 52, 53, 55, 57, 59, 60, 62]
noteList = [36, 37, 38, 39, 40, 41, 42, 43]
# noteList = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
##SEQUENCER SIZE
seqSize = 32
##SEQUENCES
@ -186,91 +224,131 @@ cha = [0] * 16
metro = pygame.mixer.Sound(mets[0])
for i in range(len(wavs)):
if i > 15: break
if i > 15:
break
else:
print(str(i))
if i > 7 :
cha[i] = pygame.mixer.Channel(i - 8);
else:
cha[i] = pygame.mixer.Channel(i);
s[i] = pygame.mixer.Sound(wavs[i])
try:
print(f"Loading sound {i}: {wavs[i]}")
if i > 7:
cha[i] = pygame.mixer.Channel(i - 8)
else:
cha[i] = pygame.mixer.Channel(i)
s[i] = pygame.mixer.Sound(wavs[i])
except Exception as e:
print(f"Error loading sound {i}: {e}")
s[i] = None
cha[i] = None
##START RUNNING MIDI
tm = ThreadedMidi(name='Play-Thread')
re = True
pygame.display.set_mode()
screen = pygame.display.set_mode((800, 600), pygame.RESIZABLE)
pygame.display.set_caption("Sample Sequencer")
# Add this helper function at the top with other functions
def print_key_press(key_name, action):
print(f"Key press: {key_name} - {action}")
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_KP_PLUS:
if chnWav == True: d.time = d.time + 15
else:
if re == False: d.time = d.time + 1
else: d.time = d.time + 5
# Print raw key info for debugging
print(f"Raw key event - key: {event.key}, unicode: {event.unicode}")
# Main Functions
if event.key in [pygame.K_KP8, pygame.K_8]:
print_key_press("8", "Toggle Metronome")
if not chnWav: # Only if not in sample change mode
met = not met
print(f"Metronome: {'ON' if met else 'OFF'}")
if event.key == pygame.K_KP_MINUS:
if chnWav == True: d.time = d.time - 15
else:
if re == False: d.time = d.time - 1
else: d.time = d.time - 5
if event.key == pygame.K_KP_ENTER:
elif event.key in [pygame.K_KP9, pygame.K_9]:
print_key_press("9", "Toggle Recording Mode")
if not chnWav:
OVRHT = not OVRHT
print(f"Recording Mode: {'ON' if OVRHT else 'OFF'}")
elif event.key in [pygame.K_KP_ENTER, pygame.K_RETURN]:
print_key_press("Enter", "Play/Pause sequence")
toggleSeq()
d.tick = 31
if event.key == pygame.K_KP_MULTIPLY:
chnWav = True
if event.key == pygame.K_NUMLOCK:
if chnWav == True:
call("sudo shutdown -h now", shell=True)
pygame.quit()
sys.exit()
if event.key == pygame.K_KP0:
if re == False:
seq[0] = [0] * seqSize;
seq[1] = [0] * seqSize;
seq[2] = [0] * seqSize;
seq[3] = [0] * seqSize;
else:
elif event.key in [pygame.K_KP0, pygame.K_0]:
print_key_press("0", "Delete Current Note")
if re: # When not in store mode
delCur = True
if event.key == pygame.K_KP_PERIOD:
# Tempo Functions
elif event.key in [pygame.K_KP_PLUS, pygame.K_PLUS, pygame.K_EQUALS]:
if chnWav:
print_key_press("+ *", "Rapidly Speed Up BPM")
d.time = d.time + 15
elif not re:
print_key_press("+", "Course Speed Up BPM")
d.time = d.time + 1
else:
print_key_press("+ .", "Fine Speed Up BPM")
d.time = d.time + 5
print(f"New BPM: {d.time}")
elif event.key in [pygame.K_KP_MINUS, pygame.K_MINUS]:
if chnWav:
print_key_press("- *", "Rapidly Slow Down BPM")
d.time = d.time - 15
elif not re:
print_key_press("-", "Course Slow Down BPM")
d.time = d.time - 1
else:
print_key_press("- .", "Fine Slow Down BPM")
d.time = d.time - 5
print(f"New BPM: {d.time}")
# Function key toggles
elif event.key in [pygame.K_KP_MULTIPLY, pygame.K_ASTERISK]:
print_key_press("*", "Function key pressed")
chnWav = True
elif event.key in [pygame.K_KP_PERIOD, pygame.K_PERIOD]:
print_key_press(".", "Function key pressed")
re = False
if event.key == pygame.K_KP8:
if chnWav == True: setWavs(8)
else:
if met == True: met = False
else: met = True
if event.key == pygame.K_KP9:
if chnWav == True: setWavs(9)
else:
if OVRHT == True: OVRHT = False
else: OVRHT = True
if event.key == pygame.K_KP1:
if chnWav == True: setWavs(1)
else: recall(1,re)
if event.key == pygame.K_KP2:
if chnWav == True: setWavs(2)
else: recall(2,re)
if event.key == pygame.K_KP3:
if chnWav == True: setWavs(3)
else: recall(3,re)
if event.key == pygame.K_KP4:
if chnWav == True: setWavs(4)
else: recall(4,re)
if event.key == pygame.K_KP5:
if chnWav == True: setWavs(5)
else: recall(5,re)
if event.key == pygame.K_KP6:
if chnWav == True: setWavs(6)
else: recall(6,re)
if event.key == pygame.K_KP7:
if chnWav == True: setWavs(7)
# Sequence and Sample Pack Functions (1-6 for sequences, 1-9 for sample packs)
for num in range(1, 10):
if event.key in [getattr(pygame, f'K_KP{num}'), getattr(pygame, f'K_{num}')]:
if chnWav:
print_key_press(f"{num} *", f"Change to Sample Pack {num}")
setWavs(num)
elif num <= 6: # Sequence operations only work for 1-6
if re:
print_key_press(f"{num}", f"Recall Sequence {num}")
recall(num, True)
else:
print_key_press(f"{num} .", f"Store Sequence {num}")
recall(num, False)
# Shutdown combination
if event.key == pygame.K_NUMLOCK and chnWav:
print_key_press("Num Lock + *", "Shutdown triggered")
call("touch numlock.test", shell=True)
# call("sudo shutdown -h now", shell=True)
# pygame.quit()
# sys.exit()
if event.type == pygame.KEYUP:
if event.key == pygame.K_KP_PERIOD:
if event.key in [pygame.K_KP_PERIOD, pygame.K_PERIOD]:
print_key_press(".", "Function key released")
re = True
if event.key == pygame.K_KP0:
if event.key in [pygame.K_KP0, pygame.K_0]:
print_key_press("0", "Delete key released")
delCur = False
if event.key == pygame.K_KP_MULTIPLY:
if event.key in [pygame.K_KP_MULTIPLY, pygame.K_ASTERISK]:
print_key_press("*", "Function key released")
chnWav = False
if event.type == pygame.VIDEORESIZE:
screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
print(f"Window resized to {event.w}x{event.h}")
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()