From f7480bb96323b8466ad8e097475db2a7135c88e0 Mon Sep 17 00:00:00 2001 From: Eddy Pedroni Date: Sat, 1 Jan 2022 19:09:01 +0100 Subject: Added next/previous AB limit functionality --- abcontroller.py | 17 +++++++++- abcontroller_unittest.py | 48 ++++++++++++++++++++++++++ midi_controller_launchpad_mini.py | 2 ++ midi_launchpad_mini_integrationtest.py | 61 ++++++++++++++++++++++++++++++---- solo_tool.py | 6 ++++ solo_tool_integrationtest.py | 44 ++++++++++++++++++++++-- 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) -- cgit v1.2.3