Skip to content

Commit

Permalink
using new PitchedPCMSynth to power drum machine
Browse files Browse the repository at this point in the history
  • Loading branch information
bwhitman committed May 24, 2024
1 parent 1c091c8 commit efeee38
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 34 deletions.
38 changes: 6 additions & 32 deletions tulip/fs/app/drums/drums.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

from tulip import UIScreen, UIElement, pal_to_lv, lv_depad, lv, frame_callback, ticks_ms, seq_add_callback, seq_remove_callback, seq_ppq, ticks_ms
import amy
import midi
from patches import drumkit


# A single drum machine switch with LED
class DrumSwitch(UIElement):
Expand Down Expand Up @@ -176,37 +179,6 @@ def set_preset(self, idx):
def cb(self, e):
pass

drumkit = [
(89, "Maraca"),
(39, "Kick"),
(45, "Snare"),
(52, "Snare2"),
(51, "Snare3"),
(41, "Snare4"),
(53, "Closed Hat"),
(56, "Open Hat"),
(61, "Low Tom"),
(94, "Clap"),
(69, "Cowbell"),
(74, "Congo Low"),
(82, "Clave"),
(60, "Block"),
(60, "Roll"),
(60, "Hit"),
(60, "Crash"),
(60, "Shell"),
(60, "Chimes"),
(60, "Seashore"),
(60, "Pwr Snare"),
(60, "Hi Tom"),
(66, "Shamisen"),
(66, "Koto"),
(72, "Steel"),
(60, "Pwr Kick"),
(66, "Marimba"),
(60, "Frets"),
(60, "Std Kick")
]

# Called from tulip's sequencer
def beat_callback(t):
Expand All @@ -221,7 +193,8 @@ def beat_callback(t):
note_for_pitch = None
if row.get_pitch() != 0.5:
note_for_pitch = int(base_note + (row.get_pitch() - 0.5)*24.0)
amy.send(osc=50+i+2, wave=amy.PCM, patch=row.get_preset(), note=note_for_pitch, vel=row.get_vel()*2, pan=row.get_pan(), time=t)
app.synth.note_on(note_for_pitch, row.get_vel()*2, row.get_preset(), t)
#amy.send(osc=50+i+2, wave=amy.PCM, patch=row.get_preset(), note=note_for_pitch, vel=row.get_vel()*2, pan=row.get_pan(), time=t)
app.current_beat = (app.current_beat+1) % 16

def quit(screen):
Expand All @@ -230,6 +203,7 @@ def quit(screen):
def run(screen):
global app
app = screen # we can use the screen obj passed in as a general "store stuff here" class, as well as inspect the UI
app.synth = midi.config.synth_per_channel[10]
app.current_beat = 0
app.offset_y = 10
app.set_bg_color(0)
Expand Down
44 changes: 42 additions & 2 deletions tulip/shared/py/midi.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def add_synth(self, channel, patch, polyphony):
self.synth_per_channel[channel].release_voices()
del self.synth_per_channel[channel]
if channel == 10:
synth = DrumSynth(num_voices=polyphony)
synth = PitchedPCMSynth(num_voices=polyphony)
else:
synth = Synth(voice_source=VoiceSource(), num_voices=polyphony)
if patch is not None:
Expand Down Expand Up @@ -305,6 +305,46 @@ def release_voices(self):
self.voice_source.release_voices()



class PitchedPCMSynth:
def __init__(self, num_voices=10):
self.oscs = list(range(amy.AMY_OSCS - num_voices, amy.AMY_OSCS))
self.next_osc = 0
self.pcm_patch_to_osc = {}
# Fields used by UI
self.amy_voices = self.oscs # Actually osc numbers not amy voices.

def note_on(self, note, velocity, pcm_patch, time=None):
osc = self.pcm_patch_to_osc.get(pcm_patch, None)
if osc is None:
osc = self.oscs[self.next_osc]
self.next_osc = (self.next_osc + 1) % len(self.oscs)
self.pcm_patch_to_osc[pcm_patch] = osc
amy.send(time=time, osc=osc, wave=amy.PCM, note=note,
patch=pcm_patch, vel=velocity)

def note_off(self, note, pcm_patch, time=None):
# Drums don't really need note-offs, but handle them anyway.
try:
osc = self.pcm_patch_to_osc[note]
amy.send(time=time, osc=osc, vel=0)
del self.pcm_patch_to_osc[note]
except KeyError:
# We didn't recognize the patch; never mind.
pass

# Rest of Synth protocol doesn't do anything for drums.
def sustain(self, state):
pass

def program_change(self, patch_number):
pass

def control_change(self, control, value):
pass



class DrumSynth:
"""Simplified Synth for Drum channel (10)."""
PCM_PATCHES = 29
Expand All @@ -321,7 +361,7 @@ def note_on(self, note, velocity, time=None):
osc = self.oscs[self.next_osc]
self.next_osc = (self.next_osc + 1) % len(self.oscs)
amy.send(time=time, osc=osc, wave=amy.PCM,
patch=note % self.PCM_PATCHES, vel=velocity, freq=0)
patch=note % DrumSynth.PCM_PATCHES, vel=velocity, freq=0)
self.note_to_osc[note] = osc

def note_off(self, note, time=None):
Expand Down
33 changes: 33 additions & 0 deletions tulip/shared/py/patches.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
# patches.py
# maybe one day automated list of patches in AMY

# Drumkit is [base_midi_note, name]
drumkit = [
(89, "Maraca"),
(39, "Kick"),
(45, "Snare"),
(52, "Snare2"),
(51, "Snare3"),
(41, "Snare4"),
(53, "Closed Hat"),
(56, "Open Hat"),
(61, "Low Tom"),
(94, "Clap"),
(69, "Cowbell"),
(74, "Congo Low"),
(82, "Clave"),
(60, "Block"),
(60, "Roll"),
(60, "Hit"),
(60, "Crash"),
(60, "Shell"),
(60, "Chimes"),
(60, "Seashore"),
(60, "Pwr Snare"),
(60, "Hi Tom"),
(66, "Shamisen"),
(66, "Koto"),
(72, "Steel"),
(60, "Pwr Kick"),
(66, "Marimba"),
(60, "Frets"),
(60, "Std Kick")
]

patches = [
"A11 Brass Set 1",
"A12 Brass Swell",
Expand Down

0 comments on commit efeee38

Please sign in to comment.