Skip to content

Commit

Permalink
First cut at multi-track sampler, added new wav files
Browse files Browse the repository at this point in the history
  • Loading branch information
skafreak committed Jul 22, 2023
1 parent b9fc95d commit d1638a3
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 72 deletions.
Binary file added Software/Testing/Kick.wav
Binary file not shown.
Binary file added Software/Testing/Snare.wav
Binary file not shown.
Binary file added Software/Testing/Tom.wav
Binary file not shown.
192 changes: 120 additions & 72 deletions Software/Testing/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,28 @@

# Setup audio
audio = audiobusio.I2SOut(board.GP0, board.GP1, board.GP2)
num_voices = 1
mixer = audiomixer.Mixer(voice_count=num_voices, sample_rate=22050, channel_count=1, bits_per_sample=16, samples_signed=True)
mixer.voice[0].level = .2
wave_file = open("/sd/StreetChicken.wav", "rb")
wav = audiocore.WaveFile(wave_file)

num_voices = 4
mixer = audiomixer.Mixer(voice_count=num_voices, sample_rate=16000, channel_count=1, bits_per_sample=16, samples_signed=True)


# File sequencer class
# Sets up a sequence track to play a .wav file sample at regular intervals
# 8 step sequence tells whether to play
class fileSequencer:
class file_sequence:
def __init__(self):
self.clk_src = 'int'
self.bpm = 120
self.fname = '/sd/StreetChicken.wav'
self.sequence = [[True, 1],[False, 0],[True, .2],[False, 0],[True, .4],[False, 0],[True, 1],[False, 0]]
self.fname = ''
self.sequence = [[False, .5],[True, .5],[True, .5],[True, .5],[True, .5],[True, .5],[True, .5],[True, .5]]

def select_file(self):
# Show valid files, select with encoder knob/button
print('valid files: ' + str(os.listdir()))
fname = 'kick.wav'

self.fname = fname
def set_bpm(self):
# Display bpm on screen, select with enc knob/button
screen_input = 120
self.bpm = screen_input

# Set and store a sequence with True/False indicating steps to play on and int being a velocity value between 0-1
def set_sequence(self):
# Enter sequence mode, set with keys, exit with encoder
key_input = [[False, 0],[False, 0],[False, 0],[False, 0],[False, 0],[False, 0],[False, 0],[False, 0]]
key_input = [[False, 0],[False, 0],[True, .5],[False, 0],[False, 0],[True, .5],[False, 0],[True, .5]]
self.sequence = key_input

# Velocity will affect playback volume of a given sample in the sequence to add more movement to the sound
Expand All @@ -56,6 +45,36 @@ def set_velocity(self):
# Use encoder to select volume, and press to advance
pass

class run_sequencer:
def __init__(self):
self.clk_src = 'int'
self.bpm = 120
self.active_sequences = []
self.sequence_step = 0
self.wav_files = []
self.loaded_wavs = []
self.step = 0
self.play_music = False

# Add sequences to the active set
def add_sequence(self,new_sequence):
self.active_sequences.append(new_sequence)

# Set BPM (needs integration with input, required)
def set_bpm(self):
# Display bpm on screen, select with enc knob/button
screen_input = 120
self.bpm = screen_input

# Write sequence data to file to reload after power on/off (future)
def save_sequence(self):
pass

# Load sequence from file to reload after power on/off (future)
def load_sequence(self):
pass

### Select where clock is coming from (not critical, but high priority nice-to-have)
def set_clk_src(self):
clk_options = ['ext, midi, int']
# Display clk_options on screen, scroll/select
Expand All @@ -68,50 +87,62 @@ def set_clk_src(self):
elif clk_src == 'int':
pass

def play_sequence(self):
# Calculate time for step
step_length = int(1000 // (self.bpm / 60))
step_start = ticks_ms()
step = 0
step_end = step_start + step_length
# Load wav file
wave_file = open(self.fname, "rb");
#wav = audiocore.WaveFile(wave_file)

seq_loop = True
while seq_loop == True:
# Helpful output for troubleshooting
#print('Step ' + str(step) + ': ' + str(self.sequence[step][0]) + ', ' + str(self.sequence[step][1]))

# Play notes that are set to true in seq array
if self.sequence[step][0] == True:
wav = audiocore.WaveFile(wave_file)
def play_step(self):

# Set volume based on 'velocity' parameter
mixer.voice[0].level = self.sequence[step][1]
mixer.voice[0].play(wav)
# Calculate step duration
self.step_start = ticks_ms()
self.step_end = self.step_start + self.step_length

# Wait for beat duration and watch for stop
while ticks_ms() < step_end:
# Loop through active sequences and play indicated steps
for index, item in enumerate(self.active_sequences):
if item.sequence[self.step][0] == True:
# Set volume based on input value
mixer.voice[index].level = item.sequence[self.step][1]
mixer.voice[index].play(self.loaded_wavs[index])

# Wait for step duration and poll for input
while ticks_ms() < self.step_end:
key_event = keys.events.get()
if key_event and key_event.pressed:
if audio.playing == True:
audio.stop()
seq_loop = False


# End audio at end of beat duration
mixer.voice[0].stop()
key = key_event.key_number
### Update to play/pause button for final hardware
if key == 0:
for index, item in enumerate(self.active_sequences):
mixer.voice[index].stop
self.play_music = False

# Increment step and restart sequence if needed
if step < 7:
step +=1
# Stop output at end of step duration
else:
step = 0
step_start = step_end
step_end = step_start + step_length
for index, item in enumerate(self.active_sequences):
mixer.voice[index].stop

# Update step
def step_update(self):
if self.step < 7:
self.step +=1
else:
self.step = 0

def play_sequence(self):

# Loop through active sequences and load wav files
for item in self.active_sequences:

# Load wav files to play
self.wav_files.append(open(item.fname, "rb"))
self.loaded_wavs.append(audiocore.WaveFile(self.wav_files[-1]))
time.sleep(.5)

# Main sequencer loop
# Calculate step duration
self.step_length = int(1000 // (self.bpm / 60))

self.play_music = True
audio.play(mixer)
while self.play_music == True:
self.play_step()
self.step_update()

# MIDI Functions
def send_note_on(note, octv):
Expand Down Expand Up @@ -244,7 +275,7 @@ def update(self, machine):
display.show(text_area)
mode_select = False
while mode_select == False:
# Some code here to use an encoder to scroll through menu options, press to select one
### Some code here to use an encoder to scroll through menu options, press to select one
mode = 'flashy'
enc_buttons_event = enc_buttons.events.get()
if enc_buttons_event and enc_buttons_event.pressed:
Expand Down Expand Up @@ -309,30 +340,47 @@ def update(self, machine):
text = "Sampler"
text_area = label.Label(terminalio.FONT, text=text, color=0xFFFF00, x=2, y=15)
display.show(text_area)
sequencer_play = True

# Pressing encoder will start looping the sequencer
while sequencer_play == True:
# Run Sampler
text = "Sampler"
text_area = label.Label(terminalio.FONT, text=text, color=0xFFFF00, x=2, y=15)
display.show(text_area)

# Load sequencer class
sequencer = run_sequencer()

# Menu mode
while sequencer.play_music == False:
### Add menu and programming portions here

### Change button to play/pause button on final hardware
key_event = keys.events.get()
if key_event and key_event.pressed:
key = key_event.key_number

### Update to play/pause button for final hardware
if key == 0:
audio.play(mixer)
tracks = []
test = fileSequencer
test().set_sequence()
test().play_sequence()
enc_buttons_event = enc_buttons.events.get()
if enc_buttons_event and enc_buttons_event.pressed:
machine.go_to_state('menu')
sequencer_play = False

'''
else:
print('exit')
machine.go_to_state('menu')
sequencer_play = False
'''
sequencer.play_music = True

# Play mode
while sequencer.play_music == True:

# Add active sequences
### Testing code, replace with menu select and save/load
sequencer.add_sequence(file_sequence())
sequencer.active_sequences[0].fname = 'Snare.wav'

sequencer.add_sequence(file_sequence())
sequencer.active_sequences[1].fname = 'Tom.wav'
sequencer.active_sequences[1].set_sequence()

sequencer.add_sequence(file_sequence())
sequencer.active_sequences[2].fname = 'Kick.wav'
sequencer.active_sequences[2].sequence = [[True, .5],[False, .5],[True, .5],[False, .5],[True, .5],[False, .5],[True, .5],[False, .5]]
### End testing code

# Play
sequencer.play_sequence()

class MIDIState(State):

Expand Down

0 comments on commit d1638a3

Please sign in to comment.