Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
[36, 35, None, None, 75, 56, None, 44],
],
)
class Melodic(enum.Enum):
CHROMATIC = 1
DIATONIC = 2
MAGIC = 3
Drumkit = enum.Enum("Drumkit", list(DRUMKIT_MAPPINGS.keys()))
@persistent_attrs(root=48,
drumkit_mapping=Drumkit.FOUR_EIGHT,
melodic_mapping=Melodic.CHROMATIC)
class NotePicker(Gridget):
def __init__(self, grid, channel):
self.grid = grid
self.surface = Surface(grid.surface)
for button in "UP DOWN LEFT RIGHT".split():
self.surface[button] = palette.CHANNEL[channel]
self.channel = channel
persistent_attrs_init(self, "{}__{}".format(self.grid.grid_name, channel))
self.led2note = {}
self.note2leds = collections.defaultdict(list)
devicechain = self.grid.griode.devicechains[self.grid.channel]
if devicechain.instrument.is_drumkit:
self.mapping = self.drumkit_mapping
from looper import Looper, LoopController
from gridgets import MENU, Menu
from mixer import Faders, Mixer
import notes
from palette import palette
from persistence import cache, persistent_attrs, persistent_attrs_init
from pickers import ColorPicker, InstrumentPicker, NotePicker, ScalePicker
import scales
log_format = "[%(levelname)s] %(filename)s:%(lineno)d %(funcName)s() -> %(message)s"
log_level = os.environ.get("LOG_LEVEL", "INFO").upper()
logging.basicConfig(level=log_level, format=log_format)
@persistent_attrs(key=notes.C, scale=scales.MAJOR)
class Griode(object):
def __init__(self):
persistent_attrs_init(self)
self.synth = Fluidsynth()
self.devicechains = [DeviceChain(self, i) for i in range(16)]
self.grids = []
self.cpu = CPU(self)
self.clock = Clock(self)
self.looper = Looper(self)
self.mixer = Mixer(self)
self.detect_devices()
# FIXME: probably make this configurable somehow (env var...?)
if False:
from termpad import ASCIIGrid
self.grids.append(ASCIIGrid(self, 0, 1))
# Unmap the widget(s) that was "owning" that led
if led in self.surface_map:
self.surface_map[led].surface.parent.mask.remove(led)
# Update the map
self.surface_map[led] = gridget
# Map the new widget
gridget.surface.parent.mask.add(led)
# Draw it
self.surface[led] = gridget.surface[led]
def tick(self, tick):
pass
##############################################################################
@persistent_attrs(font_index=0, group_index=0, instr_index=0, bank_index=0)
class DeviceChain(object):
def __init__(self, griode, channel):
self.griode = griode
self.channel = channel
persistent_attrs_init(self, str(channel))
self.latch = Latch(self)
self.arpeggiator = Arpeggiator(self)
self.program_change()
# The variables `..._index` indicate which instrument is currently selected.
# Note: perhaps this instrument does not exist. In that case, the
# `instrument` property below will fallback to an (existing) one.
@property
def instrument(self):
fonts = self.griode.synth.fonts
def output(self, message):
message.channel = self.channel
devicechain = self.sequencer.griode.devicechains[self.channel]
devicechain.send(message)
# Send event to notepickers so that they can update LEDs if necessary
for grid in self.sequencer.griode.grids:
notepicker = grid.notepickers[self.channel]
notepicker.send(message, self)
# Update widgets (FIXME: this expensive, can we do better?)
for grid in self.sequencer.griode.grids:
controller = grid.sequencercontrollers[self.channel]
controller.draw()
@persistent_attrs(notes=intervaltree.IntervalTree(), duration=24*8)
class Loop(object):
def __init__(self, looper, number):
self.looper = looper
persistent_attrs_init(
self, "C{:02}L{:02}".format(looper.channel, number))
class Note(object):
def __init__(self, note, velocity):
self.note = note
self.velocity = velocity
def __repr__(self):
return ("Note(note={}, velocity={})"
class MotifMode(enum.Enum):
DISABLED = 1 # Do not use the motif, just spell out notes in buffer
SCALE = 2 # Use the motif, mapping steps to the current scale
BUFFER = 3 # Use the motif, mapping steps to the notes buffer
class ScaleKey(enum.Enum): # Which note will be the root of the scale?
FIRST = 1 # - the first note in the buffer
LAST = 2 # - the last note in the buffer
LOWER = 3 # - the lowest note in the buffer
HIGHER = 4 # - the highest note in the buffer
NEXT = 5 # - the next note in the buffer
@persistent_attrs(
enabled=False, interval=6, pattern_length=4,
pattern=[[4, 3, [0]], [1, 2, [0]], [3, 1, [0]], [1, 2, [0]]],
note_order=NoteOrder.FIRST,
motif_mode=MotifMode.DISABLED,
scale_key=ScaleKey.FIRST,
)
class Arpeggiator(object):
def __init__(self, devicechain):
self.devicechain = devicechain
persistent_attrs_init(self, str(devicechain.channel))
self.notes = []
self.next_note = 0 # That's a position in self.notes
self.direction = 1 # Always 1, except when in BOUNCING mode
self.latch_notes = set()
self.playing = []
elif (any(x!=y for x, y in zip(self.teacher_notes, student_notes))
or
len(student_notes) > len(self.teacher_notes)
or
self.student_loop.next_tick >= 2*self.tick_interval):
# Bzzzt wrong
logging.info("Bzzzt try again!")
logging.info("Teacher notes: {}"
.format(self.teacher_notes))
logging.info("Student notes: {}"
.format(student_notes))
self.flash(colors.RED)
self.teacher()
@persistent_attrs(beats_per_bar=4)
class Looper(object):
def __init__(self, griode):
self.griode = griode
persistent_attrs_init(self)
self.playing = False
self.last_tick = 0 # Last (=current) tick
self.loops_playing = set() # Contains instances of Loop
self.loops_recording = set() # Also instances of Loop
self.notes_recording = {} # note -> (Note(), tick_when_started)
self.notes_playing = [] # (stop_tick, channel, note)
self.loops = {}
self.teacher = Teacher(self)
for row in range(1, 9):
for column in range(1, 9):
self.loops[row, column] = Loop(self, (row, column))
from gridgets import Gridget, Surface
from palette import palette
from persistence import persistent_attrs, persistent_attrs_init
NUMBERS = """
### # ### ### # # ### ### ### ### ###
# # # # # # # # # # # # # #
# # # ### ### ### ### ### # ### ###
# # # # # # # # # # # # #
### # ### ### # ### ### # ### ###
""".strip().split("\n")
@persistent_attrs(bpm=120)
class Clock(object):
def __init__(self, griode):
self.griode = griode
persistent_attrs_init(self)
self.tick = 0 # 24 ticks per quarter note
self.next = time.time()
self.cues = []
def cue(self, when, func, args):
self.cues.append((self.tick+when, func, args))
def callback(self):
expired_cues = [cue for cue in self.cues if cue[0] <= self.tick]
for when, func, args in expired_cues:
func(*args)
import enum
import logging
import mido
from gridgets import Gridget, Surface
from palette import palette
from persistence import persistent_attrs, persistent_attrs_init
class Page(enum.Enum):
VOLUME = 1
CHORUS = 2
REVERB = 3
@persistent_attrs(volume=16*[96], chorus=16*[0], reverb=16*[0])
class Mixer(object):
def __init__(self, griode):
self.griode = griode
persistent_attrs_init(self)
# FIXME don't duplicate the CC mappings
for cc, array in [
(7, self.volume),
(91, self.chorus),
(93, self.reverb),
]:
for channel, value in enumerate(array):
m = mido.Message("control_change", control=cc, value=value)
self.griode.devicechains[channel].send(m)
klass = Keyboard
if klass is not None:
# FIXME find a better way than this for hotplug!
if not initial:
logging.info("Detected hotplug of new device: {}".format(port_name))
time.sleep(4)
self.grids.append(klass(self, port_name))
for port_name in configured_ports - detected_ports:
# Removing a device
logging.info("Device {} is no longer plugged. Removing it."
.format(port_name))
self.grids = [g for g in self.grids if g.grid_name != port_name]
##############################################################################
@persistent_attrs(channel=0)
class Grid(object):
def __init__(self, griode, grid_name):
self.griode = griode
self.grid_name = grid_name
persistent_attrs_init(self, grid_name)
self.surface_map = {} # maps leds to gridgets
self.colorpicker = ColorPicker(self)
self.faders = Faders(self)
self.bpmsetter = BPMSetter(self)
self.notepickers = [NotePicker(self, i) for i in range(16)]
self.instrumentpickers = [InstrumentPicker(self, i) for i in range(16)]
self.scalepicker = ScalePicker(self)
self.arpconfigs = [ArpConfig(self, i) for i in range(16)]
self.latchconfigs = [LatchConfig(self, i) for i in range(16)]
self.loopcontroller = LoopController(self)
import mido
from gridgets import Surface
from palette import palette
from persistence import persistent_attrs, persistent_attrs_init
@persistent_attrs(enabled=False)
class Latch(object):
def __init__(self, devicechain):
self.devicechain = devicechain
persistent_attrs_init(self, str(devicechain.channel))
self.notes = set()
def send(self, message):
if self.enabled and message.type == "note_on":
note = message.note
if message.velocity > 0:
if note not in self.notes:
self.notes.add(note)
self.output(message)
else:
self.notes.remove(note)