diff --git a/tulip/fs/app/drums/drums.py b/tulip/fs/app/drums/drums.py index 14c07a457..0d0f7f11f 100644 --- a/tulip/fs/app/drums/drums.py +++ b/tulip/fs/app/drums/drums.py @@ -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): @@ -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): @@ -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): @@ -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) diff --git a/tulip/shared/py/midi.py b/tulip/shared/py/midi.py index 3f3f8f38a..ab01a193b 100644 --- a/tulip/shared/py/midi.py +++ b/tulip/shared/py/midi.py @@ -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: @@ -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 @@ -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): diff --git a/tulip/shared/py/patches.py b/tulip/shared/py/patches.py index e380be960..bdf481169 100644 --- a/tulip/shared/py/patches.py +++ b/tulip/shared/py/patches.py @@ -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",