from .midi_wrapper_mido import MidiWrapper class MidiController: DEVICE_NAME = "Launchpad Mini MIDI 1" LIGHT_CONTROL_CHANNEL = 0 LED_GREEN = 124 LED_YELLOW = 126 LED_RED = 3 LED_OFF = 0 BUTTON_MATRIX = [[x for x in range(y * 16, y * 16 + 8)] for y in range(0,8)] # address as [row][col] MIN_PLAYBACK_RATE = 0.5 MAX_PLAYBACK_RATE = 1.2 PLAYBACK_RATE_STEP = 0.1 MIN_PLAYBACK_VOLUME = 0.5 MAX_PLAYBACK_VOLUME = 1.2 PLAYBACK_VOLUME_STEP = 0.1 def __init__(self, soloTool, midiWrapperOverride=None): self._soloTool = soloTool if midiWrapperOverride is not None: self._midiWrapper = midiWrapperOverride else: self._midiWrapper = MidiWrapper() self._registerHandlers() self._soloTool.registerPlayingStateCallback(self._updatePlayPauseButton) self._soloTool.registerPlaybackVolumeCallback(self._updateVolumeRow) self._soloTool.registerPlaybackRateCallback(self._updateRateRow) self._soloTool.registerAbLimitEnabledCallback(self._updateToggleAbLimitEnableButton) self._aLimit = 0.0 self._bLimit = 0.0 def _registerHandlers(self): self._handlers = { 96 : self._soloTool.stop, 114 : self._soloTool.jumpToA, 112 : self._playPause, 98 : self._toggleAbLimitEnable, 118 : self._soloTool.previousStoredAbLimits, 119 : self._soloTool.nextStoredAbLimits, 116 : self._setALimit, 117 : self._setBLimit, 48 : self._soloTool.previousSong, 49 : self._createSeekHandler(-0.25), 50 : self._createSeekHandler(-0.05), 51 : self._createSeekHandler(-0.01), 52 : self._createSeekHandler(0.01), 53 : self._createSeekHandler(0.05), 54 : self._createSeekHandler(0.25), 55 : self._soloTool.nextSong, } for i in range(0, 8): volume = round(MidiController.MIN_PLAYBACK_VOLUME + MidiController.PLAYBACK_VOLUME_STEP * i, 1) self._handlers[i] = self._createSetPlaybackVolumeCallback(volume) for i, button in enumerate(range(16, 24)): rate = round(MidiController.MIN_PLAYBACK_RATE + MidiController.PLAYBACK_RATE_STEP * i, 1) self._handlers[button] = self._createSetPlaybackRateCallback(rate) def connect(self): self._midiWrapper.connect(MidiController.DEVICE_NAME, self._callback) self._initialiseButtonLEDs() def disconnect(self): self._allLEDsOff() self._midiWrapper.disconnect() def _callback(self, msg): if msg.type != "note_on" or msg.velocity < 127: return if msg.note in self._handlers: handler = self._handlers[msg.note]() def _playPause(self): if self._soloTool.isPlaying(): self._soloTool.pause() else: self._soloTool.play() def _createSeekHandler(self, delta): def f(): newPosition = self._soloTool.getPlaybackPosition() + delta newPosition = min(1.0, max(0.0, newPosition)) self._soloTool.setPlaybackPosition(newPosition) return f def _setALimit(self): self._aLimit = self._soloTool.getPlaybackPosition() self._soloTool.setAbLimits(self._aLimit, self._bLimit) def _setBLimit(self): self._bLimit = self._soloTool.getPlaybackPosition() self._soloTool.setAbLimits(self._aLimit, self._bLimit) def _toggleAbLimitEnable(self): self._soloTool.setAbLimitEnable(not self._soloTool.isAbLimitEnabled()) def _updatePlayPauseButton(self, playing): if playing: self._setButtonLED(7, 0, MidiController.LED_GREEN) else: self._setButtonLED(7, 0, MidiController.LED_YELLOW) def _updateToggleAbLimitEnableButton(self, enabled): if enabled: self._setButtonLED(6, 2, MidiController.LED_GREEN) else: self._setButtonLED(6, 2, MidiController.LED_RED) def _updateVolumeRow(self, volume): t1 = int(round(volume / MidiController.PLAYBACK_VOLUME_STEP, 1)) t2 = int(round(MidiController.MIN_PLAYBACK_VOLUME / MidiController.PLAYBACK_VOLUME_STEP, 1)) lastColumnLit = t1 - t2 + 1 self._lightRowUntilColumn(0, lastColumnLit, MidiController.LED_GREEN) def _updateRateRow(self, rate): t1 = int(round(rate / MidiController.PLAYBACK_RATE_STEP, 1)) t2 = int(round(MidiController.MIN_PLAYBACK_RATE / MidiController.PLAYBACK_RATE_STEP, 1)) lastColumnLit = t1 - t2 + 1 self._lightRowUntilColumn(1, lastColumnLit, MidiController.LED_YELLOW) def _createSetPlaybackRateCallback(self, rate): def f(): self._soloTool.setPlaybackRate(rate) return f def _createSetPlaybackVolumeCallback(self, volume): def f(): self._soloTool.setPlaybackVolume(volume) return f def _setButtonLED(self, row, col, colour): self._midiWrapper.sendMessage(MidiController.BUTTON_MATRIX[row][col], colour, MidiController.LIGHT_CONTROL_CHANNEL) def _lightRowUntilColumn(self, row, column, litColour): colours = [litColour] * column + [MidiController.LED_OFF] * (8 - column) for col in range(0, 8): self._setButtonLED(row, col, colours[col]) def _allLEDsOff(self): for row in range(0, 8): for col in range(0, 8): self._setButtonLED(row, col, MidiController.LED_OFF) def _initialiseButtonLEDs(self): self._allLEDsOff() # volume buttons self._updateVolumeRow(self._soloTool.getPlaybackVolume()) # playback rate buttons self._updateRateRow(self._soloTool.getPlaybackRate()) # playback control self._setButtonLED(6, 0, MidiController.LED_RED) self._updatePlayPauseButton(self._soloTool.isPlaying()) # AB repeat toggle self._updateToggleAbLimitEnableButton(self._soloTool.isAbLimitEnabled()) # AB control self._setButtonLED(7, 2, MidiController.LED_YELLOW) self._setButtonLED(7, 6, MidiController.LED_RED) self._setButtonLED(7, 7, MidiController.LED_GREEN) self._setButtonLED(7, 4, MidiController.LED_YELLOW) self._setButtonLED(7, 5, MidiController.LED_YELLOW) # Song control self._setButtonLED(3, 0, MidiController.LED_RED) self._setButtonLED(3, 1, MidiController.LED_RED) self._setButtonLED(3, 2, MidiController.LED_RED) self._setButtonLED(3, 3, MidiController.LED_RED) self._setButtonLED(3, 4, MidiController.LED_GREEN) self._setButtonLED(3, 5, MidiController.LED_GREEN) self._setButtonLED(3, 6, MidiController.LED_GREEN) self._setButtonLED(3, 7, MidiController.LED_GREEN)