From cda8197669409689be291660f93cb288ab2d31b3 Mon Sep 17 00:00:00 2001 From: Eddy Pedroni Date: Sat, 9 Nov 2024 20:35:56 +0100 Subject: Migrate to project-based structure --- .../solo_tool/midi_controller_launchpad_mini.py | 145 +++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py (limited to 'solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py') diff --git a/solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py b/solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py new file mode 100644 index 0000000..961127c --- /dev/null +++ b/solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py @@ -0,0 +1,145 @@ +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) + + def _registerHandlers(self): + self._handlers = { + 96 : self._soloTool.stop, + 99 : self._soloTool.jumpToA, + 112 : self._playPause, + 101 : self._toggleAbLimitEnable, + 102 : self._soloTool.previousStoredAbLimits, + 103 : self._soloTool.nextStoredAbLimits, + 118 : self._soloTool.previousSong, + 119 : 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.setCallback(self._callback) + self._midiWrapper.connect(MidiController.DEVICE_NAME) + self._initialiseButtonLEDs() + + 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 _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, 5, MidiController.LED_GREEN) + else: + self._setButtonLED(6, 5, 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(6, 3, MidiController.LED_YELLOW) + self._setButtonLED(6, 6, MidiController.LED_RED) + self._setButtonLED(6, 7, MidiController.LED_GREEN) + + # Song control + self._setButtonLED(7, 6, MidiController.LED_RED) + self._setButtonLED(7, 7, MidiController.LED_GREEN) + -- cgit v1.2.3