aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEddy Pedroni <eddy@0xf7.com>2022-01-01 19:09:01 +0100
committerEddy Pedroni <eddy@0xf7.com>2022-01-01 19:09:01 +0100
commitf7480bb96323b8466ad8e097475db2a7135c88e0 (patch)
treece588932ed3afd93ecdb21941d357c7fe56c40df
parentd28a1b91afa86b6e39ac4df24d8bada7e795b20b (diff)
Added next/previous AB limit functionality
-rw-r--r--abcontroller.py17
-rw-r--r--abcontroller_unittest.py48
-rw-r--r--midi_controller_launchpad_mini.py2
-rw-r--r--midi_launchpad_mini_integrationtest.py61
-rw-r--r--solo_tool.py6
-rw-r--r--solo_tool_integrationtest.py44
6 files changed, 168 insertions, 10 deletions
diff --git a/abcontroller.py b/abcontroller.py
index e4088b4..3604a85 100644
--- a/abcontroller.py
+++ b/abcontroller.py
@@ -35,11 +35,25 @@ class ABController:
def loadLimits(self, index):
if not self._songLimits:
return
-
+
if index >= 0 and index < len(self._songLimits):
self._currentLimits = self._songLimits[index]
self._loadedIndex = index
+ def nextStoredAbLimits(self):
+ if self._loadedIndex is None:
+ nextIndex = 0
+ else:
+ nextIndex = self._loadedIndex + 1
+ self.loadLimits(nextIndex)
+
+ def previousStoredAbLimits(self):
+ if self._loadedIndex is None:
+ previousIndex = 0
+ else:
+ previousIndex = self._loadedIndex - 1
+ self.loadLimits(previousIndex)
+
def setLimits(self, aLimit, bLimit):
self._currentLimits = _AB(aLimit, bLimit)
self._loadedIndex = None
@@ -62,3 +76,4 @@ class ABController:
def clear(self):
self.__init__(enabled=self._enabled, callback=self._setPositionCallback)
+
diff --git a/abcontroller_unittest.py b/abcontroller_unittest.py
index 4f79fd9..f9e947d 100644
--- a/abcontroller_unittest.py
+++ b/abcontroller_unittest.py
@@ -218,3 +218,51 @@ def test_defaultBehaviour():
uut = ABController()
checkDefaultLimits(uut)
+def test_nextStoredLimit():
+ song = "/path/to/song"
+ abLimits = [
+ AB(0.2, 0.4),
+ AB(0.3, 0.5)
+ ]
+
+ uut = ABController()
+ uut.setCurrentSong(song)
+ for l in abLimits:
+ uut.storeLimits(l.a, l.b)
+
+ checkDefaultLimits(uut)
+
+ uut.nextStoredAbLimits()
+ checkLimits(uut, abLimits[0].a, abLimits[0].b)
+
+ uut.nextStoredAbLimits()
+ checkLimits(uut, abLimits[1].a, abLimits[1].b)
+
+ uut.nextStoredAbLimits()
+ checkLimits(uut, abLimits[1].a, abLimits[1].b)
+
+def test_previousStoredLimit():
+ song = "/path/to/song"
+ abLimits = [
+ AB(0.2, 0.4),
+ AB(0.3, 0.5)
+ ]
+
+ uut = ABController()
+ uut.setCurrentSong(song)
+ for l in abLimits:
+ uut.storeLimits(l.a, l.b)
+
+ checkDefaultLimits(uut)
+
+ uut.previousStoredAbLimits()
+ checkLimits(uut, abLimits[0].a, abLimits[0].b)
+
+ uut.previousStoredAbLimits()
+ checkLimits(uut, abLimits[0].a, abLimits[0].b)
+
+ uut.loadLimits(1)
+ checkLimits(uut, abLimits[1].a, abLimits[1].b)
+
+ uut.previousStoredAbLimits()
+ checkLimits(uut, abLimits[0].a, abLimits[0].b)
diff --git a/midi_controller_launchpad_mini.py b/midi_controller_launchpad_mini.py
index 55c0ec0..adf18d5 100644
--- a/midi_controller_launchpad_mini.py
+++ b/midi_controller_launchpad_mini.py
@@ -14,6 +14,8 @@ class MidiController:
112 : self._playPause,
96 : self._soloTool.stop,
101 : self._soloTool.jumpToA,
+ 102 : self._soloTool.previousStoredAbLimits,
+ 103 : self._soloTool.nextStoredAbLimits,
118 : self._soloTool.previousSong,
119 : self._soloTool.nextSong
}
diff --git a/midi_launchpad_mini_integrationtest.py b/midi_launchpad_mini_integrationtest.py
index 4601c8d..8779503 100644
--- a/midi_launchpad_mini_integrationtest.py
+++ b/midi_launchpad_mini_integrationtest.py
@@ -82,6 +82,8 @@ def test_jumpToAButton(uut, midiWrapperMock, soloTool, playerMock):
assert playerMock.position == ab[0]
def test_previousAndNextSongButtons(uut, midiWrapperMock, soloTool, playerMock):
+ nextSongButton = 119
+ previousSongButton = 118
songs = [
"test.flac",
"test.mp3"
@@ -91,21 +93,66 @@ def test_previousAndNextSongButtons(uut, midiWrapperMock, soloTool, playerMock):
uut.connect()
assert playerMock.currentSong == None
- midiWrapperMock.simulateInput(119)
+ midiWrapperMock.simulateInput(nextSongButton)
assert playerMock.currentSong == songs[0]
- midiWrapperMock.simulateInput(119)
+ midiWrapperMock.simulateInput(nextSongButton)
assert playerMock.currentSong == songs[1]
- midiWrapperMock.simulateInput(118)
+ midiWrapperMock.simulateInput(previousSongButton)
assert playerMock.currentSong == songs[0]
- midiWrapperMock.simulateInput(118)
+ midiWrapperMock.simulateInput(previousSongButton)
assert playerMock.currentSong == songs[0]
-def test_unprogrammedButton(uut, midiWrapperMock):
- unusedButton = 48
+def test_previousAndNextAbButtons(uut, midiWrapperMock, soloTool, playerMock):
+ nextLimitButton = 103
+ previousLimitButton = 102
+ song = "test.flac"
+ abLimits = [
+ [0.2, 0.4],
+ [0.1, 0.3]
+ ]
+
+ soloTool.addSong(song)
+ soloTool.setSong(0)
+ soloTool.setAbLimitEnable(True)
+
+ for ab in abLimits:
+ soloTool.storeAbLimits(ab[0], ab[1])
+
+ uut.connect()
+
+ def checkLimit(aLimit, bLimit):
+ playerMock.position = bLimit - 0.1
+ soloTool.tick()
+ assert playerMock.position == bLimit - 0.1
+
+ playerMock.position = bLimit + 0.1
+ soloTool.tick()
+ assert playerMock.position == aLimit
+
+ checkLimit(0.0, 0.0)
+
+ midiWrapperMock.simulateInput(nextLimitButton)
+ checkLimit(abLimits[0][0], abLimits[0][1])
+
+ midiWrapperMock.simulateInput(nextLimitButton)
+ checkLimit(abLimits[1][0], abLimits[1][1])
+
+ midiWrapperMock.simulateInput(nextLimitButton)
+ checkLimit(abLimits[1][0], abLimits[1][1])
+
+ midiWrapperMock.simulateInput(previousLimitButton)
+ checkLimit(abLimits[0][0], abLimits[0][1])
+
+ midiWrapperMock.simulateInput(previousLimitButton)
+ checkLimit(abLimits[0][0], abLimits[0][1])
+
+def test_unassignedButton(uut, midiWrapperMock):
+ unassignedButton = 48
uut.connect()
# expect no crash
- midiWrapperMock.simulateInput(48)
+ midiWrapperMock.simulateInput(unassignedButton)
+ # XXX would be better to assert that nothing changed in the solo tool
diff --git a/solo_tool.py b/solo_tool.py
index d2147bf..6581f5e 100644
--- a/solo_tool.py
+++ b/solo_tool.py
@@ -58,6 +58,12 @@ class SoloTool:
def setAbLimitEnable(self, enable):
self._abController.setEnable(enable)
+ def nextStoredAbLimits(self):
+ self._abController.nextStoredAbLimits()
+
+ def previousStoredAbLimits(self):
+ self._abController.previousStoredAbLimits()
+
def jumpToA(self):
a = self._abController.getCurrentLimits()[0]
# XXX assumes that player.setPlaybackPosition is thread-safe!
diff --git a/solo_tool_integrationtest.py b/solo_tool_integrationtest.py
index d0123d5..6022454 100644
--- a/solo_tool_integrationtest.py
+++ b/solo_tool_integrationtest.py
@@ -96,8 +96,8 @@ def test_addAndSetAbLimits(uut, mockPlayer):
uut.addSong(song)
uut.setSong(0)
- uut.storeAbLimits(abLimits[0][0], abLimits[0][1])
- uut.storeAbLimits(abLimits[1][0], abLimits[1][1])
+ for ab in abLimits:
+ uut.storeAbLimits(ab[0], ab[1])
mockPlayer.position = 0.0
uut.tick()
@@ -206,6 +206,46 @@ def test_storeAbLimitsWithoutSong(uut, mockPlayer):
uut.tick()
assert mockPlayer.position == abLimit[0]
+def test_nextAndPreviousAbLimit(uut, mockPlayer):
+ song = "test.flac"
+ abLimits = [
+ [0.2, 0.4],
+ [0.1, 0.3]
+ ]
+
+ def checkLimit(aLimit, bLimit):
+ mockPlayer.position = bLimit - 0.1
+ uut.tick()
+ assert mockPlayer.position == bLimit - 0.1
+
+ mockPlayer.position = bLimit + 0.1
+ uut.tick()
+ assert mockPlayer.position == aLimit
+
+ uut.addSong(song)
+ uut.setSong(0)
+ uut.setAbLimitEnable(True)
+
+ for ab in abLimits:
+ uut.storeAbLimits(ab[0], ab[1])
+
+ checkLimit(0.0, 0.0) # default limits
+
+ uut.nextStoredAbLimits()
+ checkLimit(abLimits[0][0], abLimits[0][1])
+
+ uut.nextStoredAbLimits()
+ checkLimit(abLimits[1][0], abLimits[1][1])
+
+ uut.nextStoredAbLimits()
+ checkLimit(abLimits[1][0], abLimits[1][1])
+
+ uut.previousStoredAbLimits()
+ checkLimit(abLimits[0][0], abLimits[0][1])
+
+ uut.previousStoredAbLimits()
+ checkLimit(abLimits[0][0], abLimits[0][1])
+
def test_loadAndSaveSession(prepared_tmp_path):
mockPlayer = MockPlayer()
uut = SoloTool(mockPlayer)