diff options
| -rw-r--r-- | midi_controller_launchpad_mini.py | 16 | ||||
| -rw-r--r-- | notifier.py | 8 | ||||
| -rw-r--r-- | notifier_unittest.py | 40 | ||||
| -rw-r--r-- | solo_tool.py | 41 | ||||
| -rw-r--r-- | solo_tool_integrationtest.py | 48 | ||||
| -rw-r--r-- | solo_tool_qt.py | 6 | 
6 files changed, 98 insertions, 61 deletions
| diff --git a/midi_controller_launchpad_mini.py b/midi_controller_launchpad_mini.py index 10013b5..6790cec 100644 --- a/midi_controller_launchpad_mini.py +++ b/midi_controller_launchpad_mini.py @@ -69,21 +69,19 @@ class MidiController:      def _stop(self):          self._soloTool.stop() -    def _updatePlayPauseButton(self): -        if self._soloTool.isPlaying(): +    def _updatePlayPauseButton(self, playing): +        if playing:              self._setButtonLED(7, 0, MidiController.LED_GREEN)          else:              self._setButtonLED(7, 0, MidiController.LED_YELLOW) -    def _updateVolumeRow(self): -        volume = self._soloTool.getPlaybackVolume() +    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 = self._soloTool.getPlaybackRate() +    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 @@ -116,14 +114,14 @@ class MidiController:          self._allLEDsOff()          # volume buttons -        self._updateVolumeRow() +        self._updateVolumeRow(self._soloTool.getPlaybackVolume())          # playback rate buttons -        self._updateRateRow() +        self._updateRateRow(self._soloTool.getPlaybackRate())          # playback control          self._setButtonLED(6, 0, MidiController.LED_RED) -        self._updatePlayPauseButton() +        self._updatePlayPauseButton(self._soloTool.isPlaying())          # AB control          self._setButtonLED(6, 5, MidiController.LED_YELLOW) diff --git a/notifier.py b/notifier.py index fc91e8c..06b4434 100644 --- a/notifier.py +++ b/notifier.py @@ -17,13 +17,13 @@ class Notifier:              self._callbacks[event] = list()          self._callbacks[event].append(callback) -    def notify(self, event): +    def notify(self, event, value):          for callback in self._callbacks.get(event, list()): -            callback() +            callback(value)      def _playingStateChangedCallback(self, *args): -        self.notify(Notifier.PLAYING_STATE_EVENT) +        self.notify(Notifier.PLAYING_STATE_EVENT, self._player.isPlaying())      def _playbackVolumeChangedCallback(self, *args): -        self.notify(Notifier.PLAYBACK_VOLUME_EVENT) +        self.notify(Notifier.PLAYBACK_VOLUME_EVENT, self._player.getPlaybackVolume()) diff --git a/notifier_unittest.py b/notifier_unittest.py index 13f2285..4aff8f4 100644 --- a/notifier_unittest.py +++ b/notifier_unittest.py @@ -14,20 +14,24 @@ def uut(mockPlayer):  def test_allEvents(uut):      def checkEvent(uut, event):          callbacks = 2 -        calledFlags = [False] * 2 +        calledFlags = [False] * callbacks +        values = [None] * callbacks          def createCallback(i): -            def cb(): -                nonlocal calledFlags +            def cb(value): +                nonlocal calledFlags, values                  calledFlags[i] = True +                values[i] = value +              return cb          for i in range(0, callbacks):              uut.registerCallback(event, createCallback(i))          assert not any(calledFlags) -        uut.notify(event) +        uut.notify(event, 123)          assert all(calledFlags) +        assert values == [123] * callbacks      checkEvent(uut, Notifier.PLAYING_STATE_EVENT)      checkEvent(uut, Notifier.PLAYBACK_VOLUME_EVENT) @@ -36,33 +40,39 @@ def test_allEvents(uut):      checkEvent(uut, Notifier.CURRENT_AB_EVENT)  def test_eventWithoutRegisteredCallbacks(uut): -    uut.notify(Notifier.PLAYING_STATE_EVENT) +    uut.notify(Notifier.PLAYING_STATE_EVENT, 0)      # expect no crash  def test_eventsWithMockPlayer(uut, mockPlayer): -    def checkEvent(eventCode, simulateEvent): +    def checkEvent(eventCode, simulateEvent, expectedValue):          called = False -        def callback(): -            nonlocal called +        receivedValue = None +        def callback(value): +            nonlocal called, receivedValue              called = True +            receivedValue = value          uut.registerCallback(eventCode, callback)          assert not called          simulateEvent()          assert called +        assert receivedValue == expectedValue + +    mockPlayer.state = 1 +    mockPlayer.volume = 75 -    checkEvent(Notifier.PLAYING_STATE_EVENT, mockPlayer.simulatePlayingStateChanged) -    checkEvent(Notifier.PLAYBACK_VOLUME_EVENT, mockPlayer.simulatePlaybackVolumeChanged) +    checkEvent(Notifier.PLAYING_STATE_EVENT, mockPlayer.simulatePlayingStateChanged, True) +    checkEvent(Notifier.PLAYBACK_VOLUME_EVENT, mockPlayer.simulatePlaybackVolumeChanged, 75)  def test_singleEventNotification(uut):      playingStateCalled = False -    def playingStateCallback(): +    def playingStateCallback(value):          nonlocal playingStateCalled          playingStateCalled = True      volumeCalled = False -    def volumeCallback(): +    def volumeCallback(value):          nonlocal volumeCalled          volumeCalled = True @@ -72,18 +82,18 @@ def test_singleEventNotification(uut):      assert not playingStateCalled      assert not volumeCalled -    uut.notify(Notifier.PLAYING_STATE_EVENT) +    uut.notify(Notifier.PLAYING_STATE_EVENT, 0)      assert playingStateCalled      assert not volumeCalled      playingStateCalled = False -    uut.notify(Notifier.PLAYBACK_VOLUME_EVENT) +    uut.notify(Notifier.PLAYBACK_VOLUME_EVENT, 0)      assert not playingStateCalled      assert volumeCalled      volumeCalled = False -    uut.notify(Notifier.PLAYBACK_RATE_EVENT) +    uut.notify(Notifier.PLAYBACK_RATE_EVENT, 0)      assert not playingStateCalled      assert not volumeCalled diff --git a/solo_tool.py b/solo_tool.py index fee7e4a..4ea081d 100644 --- a/solo_tool.py +++ b/solo_tool.py @@ -30,22 +30,25 @@ class SoloTool:              self._sessionManager.addSong(path)      def setSong(self, index): -        changed = index != self._playlist.getCurrentSongIndex() +        previous = self._playlist.getCurrentSongIndex()          self._playlist.setCurrentSong(index) -        if changed: -            self._notifier.notify(Notifier.CURRENT_SONG_EVENT) +        new = self._playlist.getCurrentSongIndex() +        if previous != new: +            self._notifier.notify(Notifier.CURRENT_SONG_EVENT, new)      def nextSong(self):          previous = self._playlist.getCurrentSongIndex()          self._playlist.nextSong() -        if previous != self._playlist.getCurrentSongIndex(): -            self._notifier.notify(Notifier.CURRENT_SONG_EVENT) +        new = self._playlist.getCurrentSongIndex() +        if previous != new: +            self._notifier.notify(Notifier.CURRENT_SONG_EVENT, new)      def previousSong(self):          previous = self._playlist.getCurrentSongIndex()          self._playlist.previousSong() -        if previous != self._playlist.getCurrentSongIndex(): -            self._notifier.notify(Notifier.CURRENT_SONG_EVENT) +        new = self._playlist.getCurrentSongIndex() +        if previous != new: +            self._notifier.notify(Notifier.CURRENT_SONG_EVENT, new)      def getSongs(self):          return self._playlist.getSongs() @@ -54,10 +57,11 @@ class SoloTool:          self._abController.storeLimits(aLimit, bLimit)      def loadAbLimits(self, index): -        changed = index != self._abController.getLoadedIndex() +        previous = self._abController.getLoadedIndex()          self._abController.loadLimits(index) -        if changed: -            self._notifier.notify(Notifier.CURRENT_AB_EVENT) +        new = self._abController.getLoadedIndex() +        if previous != new: +            self._notifier.notify(Notifier.CURRENT_AB_EVENT, new)      def setAbLimits(self, aLimit, bLimit):          self._abController.setLimits(aLimit, bLimit) @@ -75,14 +79,16 @@ class SoloTool:      def nextStoredAbLimits(self):          previous = self._abController.getLoadedIndex()          self._abController.nextStoredAbLimits() -        if previous != self._abController.getLoadedIndex(): -            self._notifier.notify(Notifier.CURRENT_AB_EVENT) +        new = self._abController.getLoadedIndex() +        if previous != new: +            self._notifier.notify(Notifier.CURRENT_AB_EVENT, new)      def previousStoredAbLimits(self):          previous = self._abController.getLoadedIndex()          self._abController.previousStoredAbLimits() -        if previous != self._abController.getLoadedIndex(): -            self._notifier.notify(Notifier.CURRENT_AB_EVENT) +        new = self._abController.getLoadedIndex() +        if previous != new: +            self._notifier.notify(Notifier.CURRENT_AB_EVENT, new)      def jumpToA(self):          a = self._abController.getCurrentLimits()[0] @@ -110,10 +116,11 @@ class SoloTool:          return self._player.isPlaying()      def setPlaybackRate(self, rate): -        previousRate = self._player.getPlaybackRate() +        previous = self._player.getPlaybackRate()          self._player.setPlaybackRate(rate) -        if previousRate != rate: -            self._notifier.notify(Notifier.PLAYBACK_RATE_EVENT) +        new = self._player.getPlaybackRate() +        if previous != new: +            self._notifier.notify(Notifier.PLAYBACK_RATE_EVENT, new)      def getPlaybackRate(self):          return self._player.getPlaybackRate() diff --git a/solo_tool_integrationtest.py b/solo_tool_integrationtest.py index 386cb26..2ee4719 100644 --- a/solo_tool_integrationtest.py +++ b/solo_tool_integrationtest.py @@ -335,9 +335,11 @@ def test_playingStateNotification(uut, mockPlayer):      uut.setSong(0)      called = False -    def callback(): -        nonlocal called  +    receivedValue = None +    def callback(value): +        nonlocal called, receivedValue          called = True +        receivedValue = value      uut.registerPlayingStateCallback(callback) @@ -346,22 +348,26 @@ def test_playingStateNotification(uut, mockPlayer):      uut.play()      assert called +    assert receivedValue == True      called = False      uut.play()      assert not called      uut.pause()      assert called +    assert receivedValue == False      called = False      uut.pause()      assert not called      uut.play()      assert called +    assert receivedValue == True      called = False      uut.stop()      assert called +    assert receivedValue == False      called = False      uut.stop()      assert not called @@ -372,19 +378,22 @@ def test_playbackVolumeNotification(uut, mockPlayer):      uut.setSong(0)      called = False -    def callback(): -        nonlocal called  +    receivedValue = None +    def callback(value): +        nonlocal called, receivedValue          called = True +        receivedValue = value      uut.registerPlaybackVolumeCallback(callback)      assert not called -    uut.setPlaybackVolume(0.0) +    uut.setPlaybackVolume(0.3)      assert called +    assert receivedValue == 0.3      called = False -    uut.setPlaybackVolume(0.0) +    uut.setPlaybackVolume(0.3)      assert not called  def test_playbackRateNotification(uut, mockPlayer): @@ -393,9 +402,11 @@ def test_playbackRateNotification(uut, mockPlayer):      uut.setSong(0)      called = False -    def callback(): -        nonlocal called  +    receivedValue = None +    def callback(value): +        nonlocal called, receivedValue          called = True +        receivedValue = value      uut.registerPlaybackRateCallback(callback) @@ -403,6 +414,7 @@ def test_playbackRateNotification(uut, mockPlayer):      uut.setPlaybackRate(0.5)      assert called +    assert receivedValue == 0.5      called = False      uut.setPlaybackRate(0.5) @@ -410,9 +422,11 @@ def test_playbackRateNotification(uut, mockPlayer):  def test_currentSongNotification(uut):      called = False -    def callback(): -        nonlocal called  +    receivedValue = None +    def callback(value): +        nonlocal called, receivedValue          called = True +        receivedValue = value      uut.registerCurrentSongCallback(callback)      assert not called @@ -426,6 +440,7 @@ def test_currentSongNotification(uut):      uut.setSong(0)      assert called +    assert receivedValue == 0      called = False      uut.addSong(songs[1]) @@ -436,10 +451,12 @@ def test_currentSongNotification(uut):      uut.setSong(1)      assert called +    assert receivedValue == 1      called = False      uut.previousSong()      assert called +    assert receivedValue == 0      called = False      uut.previousSong() @@ -447,6 +464,7 @@ def test_currentSongNotification(uut):      uut.nextSong()      assert called +    assert receivedValue == 1      called = False      uut.nextSong() @@ -454,9 +472,11 @@ def test_currentSongNotification(uut):  def test_currentAbNotification(uut):      called = False -    def callback(): -        nonlocal called  +    receivedValue = None +    def callback(value): +        nonlocal called, receivedValue          called = True +        receivedValue = value      uut.registerCurrentAbLimitsCallback(callback)      assert not called @@ -476,6 +496,7 @@ def test_currentAbNotification(uut):      uut.loadAbLimits(0)      assert called +    assert receivedValue == 0      called = False      uut.loadAbLimits(0) @@ -483,10 +504,12 @@ def test_currentAbNotification(uut):      uut.loadAbLimits(1)      assert called +    assert receivedValue == 1      called = False      uut.previousStoredAbLimits()      assert called +    assert receivedValue == 0      called = False      uut.previousStoredAbLimits() @@ -494,6 +517,7 @@ def test_currentAbNotification(uut):      uut.nextStoredAbLimits()      assert called +    assert receivedValue == 1      called = False      uut.nextStoredAbLimits() diff --git a/solo_tool_qt.py b/solo_tool_qt.py index a6823e0..25a15fb 100644 --- a/solo_tool_qt.py +++ b/solo_tool_qt.py @@ -41,7 +41,7 @@ class ABListModel(QAbstractListModel):          return len(self.soloTool.getStoredAbLimits())  class MainWindow(QMainWindow, Ui_MainWindow): -    songChangeSignal = pyqtSignal() +    songChangeSignal = pyqtSignal(int)      def __init__(self, *args, **kwargs):          super(MainWindow, self).__init__(*args, **kwargs) @@ -170,14 +170,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):          self.abListModel.layoutChanged.emit()          self.clearListViewSelection(self.abListView) -    def currentSongChanged(self): +    def currentSongChanged(self, songIndex):          if self.songChangePending == CHANGE_GUI:              self.songChangePending = None          else:              assert self.songChangePending is None              self.songChangePending = CHANGE_INTERNAL -            # TODO fix this hack -            songIndex = self.soloTool._playlist.getCurrentSongIndex()              print(f"Forcing selection to {songIndex}")              i = self.playlistModel.createIndex(songIndex, 0)              self.songListView.selectionModel().select(i, QItemSelectionModel.ClearAndSelect) | 
