aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--doc/diagram.drawio18
-rw-r--r--doc/known-issues.md6
-rw-r--r--solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py23
-rw-r--r--solo-tool-project/test/midi_launchpad_mini_integrationtest.py58
5 files changed, 84 insertions, 22 deletions
diff --git a/.gitignore b/.gitignore
index cbe6874..a330487 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
venv/
**/*.egg-info
**/build
+**/*.bkp
diff --git a/doc/diagram.drawio b/doc/diagram.drawio
index 51dd11f..f900292 100644
--- a/doc/diagram.drawio
+++ b/doc/diagram.drawio
@@ -1,6 +1,6 @@
-<mxfile host="Electron" modified="2024-11-09T22:49:49.440Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.1.2 Chrome/114.0.5735.289 Electron/25.9.4 Safari/537.36" etag="Lsuc8pY-LfMLzD1PEqfs" version="22.1.2" type="device" pages="3">
+<mxfile host="Electron" modified="2024-11-10T11:22:07.508Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.1.2 Chrome/114.0.5735.289 Electron/25.9.4 Safari/537.36" etag="iuxE9_e737GxBbJf21Ic" version="22.1.2" type="device" pages="3">
<diagram id="g-wcGVps3MkI6_XAwNEs" name="Core">
- <mxGraphModel dx="846" dy="395" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
+ <mxGraphModel dx="1561" dy="946" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
@@ -369,7 +369,7 @@
</mxGraphModel>
</diagram>
<diagram id="PyNSc7ezSt42GBdjTBvd" name="Launchpad">
- <mxGraphModel dx="846" dy="395" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
+ <mxGraphModel dx="1561" dy="946" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-0" />
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-1" parent="ZtjfeE3uwfRsFhnWfLYL-0" />
@@ -406,7 +406,7 @@
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-86" value="33" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="130" y="255" width="80" height="80" as="geometry" />
</mxCell>
- <mxCell id="ZtjfeE3uwfRsFhnWfLYL-87" value="49&lt;br&gt;-1 min" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-87" value="49&lt;br&gt;-25%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="130" y="342" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-88" value="65" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
@@ -430,7 +430,7 @@
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-95" value="34" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="220" y="255" width="80" height="80" as="geometry" />
</mxCell>
- <mxCell id="ZtjfeE3uwfRsFhnWfLYL-96" value="50&lt;br&gt;-30 s" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-96" value="50&lt;br&gt;-5%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="220" y="342" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-97" value="66" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
@@ -454,7 +454,7 @@
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-104" value="35" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="310" y="255" width="80" height="80" as="geometry" />
</mxCell>
- <mxCell id="ZtjfeE3uwfRsFhnWfLYL-105" value="51&lt;br&gt;-5 s" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-105" value="51&lt;br&gt;-1%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="310" y="342" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-106" value="67" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
@@ -478,7 +478,7 @@
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-113" value="36" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="400" y="255" width="80" height="80" as="geometry" />
</mxCell>
- <mxCell id="ZtjfeE3uwfRsFhnWfLYL-114" value="52&lt;br&gt;+5 s" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-114" value="52&lt;br&gt;+1%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="400" y="342" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-115" value="68" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
@@ -502,7 +502,7 @@
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-122" value="37" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="490" y="255" width="80" height="80" as="geometry" />
</mxCell>
- <mxCell id="ZtjfeE3uwfRsFhnWfLYL-123" value="53&lt;br&gt;+30 s" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-123" value="53&lt;br&gt;+5%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="490" y="342" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-124" value="69" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
@@ -526,7 +526,7 @@
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-131" value="38" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="580" y="255" width="80" height="80" as="geometry" />
</mxCell>
- <mxCell id="ZtjfeE3uwfRsFhnWfLYL-132" value="54&lt;br&gt;+1 min" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-132" value="54&lt;br&gt;+25%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
<mxGeometry x="580" y="342" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-133" value="70" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
diff --git a/doc/known-issues.md b/doc/known-issues.md
index 2a43f53..f41bfc8 100644
--- a/doc/known-issues.md
+++ b/doc/known-issues.md
@@ -13,7 +13,7 @@
* sometimes crashes when selecting limits with MIDI controller
* MIDI controller feature requests:
- * skip ahead and behind by steps of a few seconds
+ * play head location indicator
# Closed Issues
@@ -34,10 +34,10 @@
* CLI feature requests:
* close application without crashing
- * wipe LED state when application closes
* set A and B points at current play head position (during playback)
-* GUI feature requests:
+* MIDI controller feature requests:
+ * skip ahead and behind by steps of a few seconds
* wipe LED state when application closes
# Use Cases
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
index ee77d21..fb6e385 100644
--- a/solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py
+++ b/solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py
@@ -44,7 +44,13 @@ class MidiController:
116 : self._setALimit,
117 : self._setBLimit,
48 : self._soloTool.previousSong,
- 55 : self._soloTool.nextSong
+ 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):
@@ -75,7 +81,14 @@ class MidiController:
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)
@@ -159,5 +172,11 @@ class MidiController:
# 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)
diff --git a/solo-tool-project/test/midi_launchpad_mini_integrationtest.py b/solo-tool-project/test/midi_launchpad_mini_integrationtest.py
index e7c1315..8fd09bb 100644
--- a/solo-tool-project/test/midi_launchpad_mini_integrationtest.py
+++ b/solo-tool-project/test/midi_launchpad_mini_integrationtest.py
@@ -1,4 +1,5 @@
import pytest
+from mido import Message
from solo_tool.midi_controller_launchpad_mini import MidiController
from solo_tool.solo_tool import SoloTool
@@ -10,12 +11,12 @@ LED_GREEN = 124
LED_OFF = 0
nextSongButton = 55
-plus1MinuteButton = 54
-plus30SecondsButton = 53
-plus5SecondsButton = 52
-minus5SecondsButton = 51
-minus30SecondsButton = 50
-minus1MinuteButton = 49
+fwd25PcButton = 54
+fwd5PcButton = 53
+fwd1PcButton = 52
+rwd1PcButton = 51
+rwd5PcButton = 50
+rwd25PcButton = 49
previousSongButton = 48
playPauseButton = 112
@@ -46,7 +47,6 @@ class MidiWrapperMock:
def simulateInput(self, note, velocity=127, channel=0):
if self.callback is not None:
- from mido import Message
msg = Message("note_on", note=note, velocity=velocity, channel=channel)
self.callback(msg)
@@ -365,7 +365,13 @@ def test_connectDisconnect(uut, midiWrapperMock):
(setAButton, LED_YELLOW, 0),
(setBButton, LED_YELLOW, 0),
(previousSongButton, LED_RED, 0),
- (nextSongButton, LED_GREEN, 0)
+ (rwd1PcButton, LED_RED, 0),
+ (rwd5PcButton, LED_RED, 0),
+ (rwd25PcButton, LED_RED, 0),
+ (nextSongButton, LED_GREEN, 0),
+ (fwd1PcButton, LED_GREEN, 0),
+ (fwd5PcButton, LED_GREEN, 0),
+ (fwd25PcButton, LED_GREEN, 0),
])
teardownMessages = [(int(i / 8) * 16 + (i % 8), LED_OFF, 0) for i in range(0, 64)] # clear all
@@ -436,3 +442,39 @@ def test_setAbButtons(uut, midiWrapperMock, soloTool, playerMock):
midiWrapperMock.simulateInput(nextLimitButton)
checkLimit(abLimits[0], abLimits[1])
+def test_seekButtons(uut, midiWrapperMock, soloTool, playerMock):
+ song = "test.flac"
+ soloTool.addSong(song)
+ soloTool.setSong(0)
+
+ uut.connect()
+
+ assert playerMock.position == 0.0
+
+ midiWrapperMock.simulateInput(fwd25PcButton)
+ assert playerMock.position == 0.25
+
+ midiWrapperMock.simulateInput(fwd5PcButton)
+ assert playerMock.position == 0.30
+
+ midiWrapperMock.simulateInput(fwd1PcButton)
+ assert playerMock.position == 0.31
+
+ midiWrapperMock.simulateInput(fwd25PcButton)
+ midiWrapperMock.simulateInput(fwd25PcButton)
+ midiWrapperMock.simulateInput(fwd25PcButton)
+ assert playerMock.position == 1.0
+
+ midiWrapperMock.simulateInput(rwd25PcButton)
+ assert playerMock.position == 0.75
+
+ midiWrapperMock.simulateInput(rwd5PcButton)
+ assert playerMock.position == 0.70
+
+ midiWrapperMock.simulateInput(rwd1PcButton)
+ assert playerMock.position == 0.69
+
+ midiWrapperMock.simulateInput(rwd25PcButton)
+ midiWrapperMock.simulateInput(rwd25PcButton)
+ midiWrapperMock.simulateInput(rwd25PcButton)
+ assert playerMock.position == 0.0