aboutsummaryrefslogtreecommitdiffstats
path: root/solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py
diff options
context:
space:
mode:
authorEddy Pedroni <epedroni@pm.me>2024-11-09 20:35:56 +0100
committerEddy Pedroni <epedroni@pm.me>2024-11-09 20:35:56 +0100
commitcda8197669409689be291660f93cb288ab2d31b3 (patch)
tree81db9b0c7c0491e0737cbffb39af6b935c0dfeb8 /solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py
parenta2257a900d4fffd6f94b73f1c48c62370ed1d684 (diff)
Migrate to project-based structure
Diffstat (limited to 'solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py')
-rw-r--r--solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py145
1 files changed, 145 insertions, 0 deletions
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)
+