Created
October 7, 2020 00:28
-
-
Save NoraCodes/3c838d3f55903867574eb7ee5fc94141 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
## Sol DrumBud v0.1 ## | |
## Leonora Tindall <[email protected]> ## | |
## Licensed GPLv.3 ## | |
A configurable drum trigger helper for Sol. | |
Use it to trigger 8 more drums from the Beatstep Pro, or trigger your complex | |
Boolean logic rhythms from 8 more directions. | |
Provides 8 percussion triggers on the Sol's outputs. | |
By default works with MIDI channel 10 and notes 44 to 51 | |
(the Beatstep Pro's upper row). | |
If using with ORCA, please recall that what is often called MIDI Ch 1 | |
is really Ch 0, and ORCA uses the real MIDI number. | |
Throughout the source code, I will refer to MIDI channels starting at 1. | |
""" | |
# ----------------- Config ---------------------------------------------------# | |
MIDI_CHANNEL = 10 | |
# Keys (numbers on the left) are MIDI note values indexed from 0. | |
# Values (strings on the right) are output names. | |
CHANNEL_CONFIG = { | |
44: "a", | |
45: 1, | |
46: "b", | |
47: 2, | |
48: "c", | |
49: 3, | |
50: "d", | |
51: 4 | |
} | |
# ----------------- Script ---------------------------------------------------# | |
import winterbloom_sol as sol | |
from winterbloom_sol import trigger | |
import winterbloom_smolmidi as midi | |
CHANNEL_NUMBERS = [1, 2, 3, 4] | |
CHANNEL_NAMES = ["a", "b", "c", "d"] | |
class CvAsGateState: | |
"Contains the state of a CV channel's trigger value, to be updated by Trigger.step()" | |
def __init__(self): | |
self.value = False | |
self.trigger = trigger.Trigger(self) | |
self.retrigger = trigger.Retrigger(self) | |
def step(self): | |
self.trigger.step() | |
self.retrigger.step() | |
def voltage(self): | |
if self.value: | |
return 5.0 | |
else: | |
return 0.0 | |
# Channel correspondance setup | |
pseudogates = {} | |
for cv_output in CHANNEL_NAMES: | |
pseudogates[cv_output] = CvAsGateState() | |
def loop(last, state, outputs): | |
# Always update the state of the pseudogates | |
global pseudogates | |
for channel_name, pseudogate in pseudogates.items(): | |
pseudogate.step() | |
outputs.set_cv(channel_name, pseudogate.voltage()) | |
# Check whether or not there's a new MIDI message to process. | |
if state.message is last.message or state.message is None: | |
return # No change! | |
# Messages without a channel are useless to us. | |
if state.message.channel is None: | |
print(f"ignoring message no channel") | |
return # Don't care. | |
ch = state.message.channel + 1 # MIDI channels are indexed from 0 | |
# Messages on a channel we're not configured for aren't useful. | |
if ch is not MIDI_CHANNEL: | |
print(f"ignoring message wrong channel {ch}") | |
return # Don't care. | |
if state.message.type is midi.NOTE_ON and state.message.data[0] in CHANNEL_CONFIG.keys(): | |
output = CHANNEL_CONFIG[state.message.data[0]] | |
if type(output) is int and output in CHANNEL_NUMBERS: | |
print(f"trigger real gate {output}") | |
outputs.trigger_gate(output) | |
elif type(output) is str and len(output) is 1 and output in CHANNEL_NAMES: | |
print(f"trigger pseudogate {output}") | |
pseudogates[output].trigger.trigger() | |
else: | |
print("bad configuration {output} is neither int from 1 to 4 nor string from 'a' to 'd'") | |
else: | |
print(f"ignoring message wrong type {state.message.type} or note {state.message.data[0]}") | |
sol.run(loop) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment