diff options
6 files changed, 131 insertions, 129 deletions
| diff --git a/solo-tool-project/src/solo_tool/handlers.py b/solo-tool-project/src/solo_tool/handlers.py new file mode 100644 index 0000000..13e982b --- /dev/null +++ b/solo-tool-project/src/solo_tool/handlers.py @@ -0,0 +1,21 @@ +from collections.abc import Callable + +from solo_tool.solo_tool import SoloTool + +def changeSong(st: SoloTool, delta: int) -> Callable[[], None]: +    def f(): +        if st.song is None: +            st.song = 0 +        else: +            st.song += delta +    return f + +def seekRelative(st: SoloTool, delta: float) -> Callable[[], None]: +    def f(): +        st.position += delta +    return f + +def positionToKeyPoint(st: SoloTool) -> Callable[[], None]: +    def f(): +        st.keyPoint = st.position +    return f 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 4fde8fc..3dc8ec6 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 @@ -1,5 +1,5 @@  from .midi_wrapper_mido import MidiWrapper -from .solo_tool_controller import SoloToolController +from . import handlers  class MidiController:      DEVICE_NAME = "Launchpad Mini MIDI 1" @@ -20,7 +20,6 @@ class MidiController:      def __init__(self, soloTool, midiWrapperOverride=None):          self._soloTool = soloTool -        self._soloToolController = SoloToolController(soloTool)          if midiWrapperOverride is not None:              self._midiWrapper = midiWrapperOverride          else: @@ -28,31 +27,25 @@ class MidiController:          self._registerHandlers()          self._soloTool.registerPlayingStateCallback(self._updatePlayPauseButton) -        self._soloTool.registerPlaybackVolumeCallback(self._updateVolumeRow) -        self._soloTool.registerPlaybackRateCallback(self._updateRateRow) -        self._soloTool.registerAbLimitEnabledCallback(self._updateToggleAbLimitEnableButton) - -        self._aLimit = 0.0 -        self._bLimit = 0.0 +        self._soloTool.registerVolumeCallback(self._updateVolumeRow) +        self._soloTool.registerRateCallback(self._updateRateRow)      def _registerHandlers(self):          self._handlers = {              96  : self._soloTool.stop, -            114 : self._soloTool.jumpToA, +            114 : self._soloTool.jump,              112 : self._playPause, -            98  : self._toggleAbLimitEnable, -            118 : self._soloTool.previousStoredAbLimits, -            119 : self._soloTool.nextStoredAbLimits, -            116 : self._setALimit, -            117 : self._setBLimit, -            48  : self._soloToolController.previousSong, -            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._soloToolController.nextSong, +            #118 : self._soloTool.previousStoredAbLimits, +            #119 : self._soloTool.nextStoredAbLimits, +            117 : handlers.positionToKeyPoint(self._soloTool), +            48  : handlers.changeSong(self._soloTool, -1),  +            49  : handlers.seekRelative(self._soloTool, -0.25), +            50  : handlers.seekRelative(self._soloTool, -0.05), +            51  : handlers.seekRelative(self._soloTool, -0.01), +            52  : handlers.seekRelative(self._soloTool, 0.01), +            53  : handlers.seekRelative(self._soloTool, 0.05), +            54  : handlers.seekRelative(self._soloTool, 0.25), +            55  : handlers.changeSong(self._soloTool, 1),           }          for i in range(0, 8): @@ -84,24 +77,6 @@ class MidiController:          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) - -    def _setBLimit(self): -        self._bLimit = self._soloTool.getPlaybackPosition() -        self._soloTool.setAbLimits(self._aLimit, self._bLimit) -     -    def _toggleAbLimitEnable(self): -        self._soloTool.setAbLimitEnable(not self._soloTool.isAbLimitEnabled()) -      def _updatePlayPauseButton(self, playing):          if playing:              self._setButtonLED(7, 0, MidiController.LED_GREEN) @@ -128,12 +103,12 @@ class MidiController:      def _createSetPlaybackRateCallback(self, rate):          def f(): -            self._soloTool.setPlaybackRate(rate) +            self._soloTool.rate = rate          return f      def _createSetPlaybackVolumeCallback(self, volume):          def f(): -            self._soloTool.setPlaybackVolume(volume) +            self._soloTool.volume = volume          return f      def _setButtonLED(self, row, col, colour): @@ -153,23 +128,19 @@ class MidiController:          self._allLEDsOff()          # volume buttons -        self._updateVolumeRow(self._soloTool.getPlaybackVolume()) +        self._updateVolumeRow(self._soloTool.volume)          # playback rate buttons -        self._updateRateRow(self._soloTool.getPlaybackRate()) +        self._updateRateRow(self._soloTool.rate)          # 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(7, 2, MidiController.LED_YELLOW)          self._setButtonLED(7, 6, MidiController.LED_RED)          self._setButtonLED(7, 7, MidiController.LED_GREEN) -        self._setButtonLED(7, 4, MidiController.LED_YELLOW)          self._setButtonLED(7, 5, MidiController.LED_YELLOW)          # Song control diff --git a/solo-tool-project/src/solo_tool/solo_tool.py b/solo-tool-project/src/solo_tool/solo_tool.py index 884721b..97c3495 100644 --- a/solo-tool-project/src/solo_tool/solo_tool.py +++ b/solo-tool-project/src/solo_tool/solo_tool.py @@ -40,9 +40,10 @@ class SoloTool:      @song.setter      def song(self, new: int) -> None: -        if new is None or new < 0 or new >= len(self._songs): -            raise ValueError() -        if new != self._song: +        if new is not None \ +           and new >= 0 \ +           and new < len(self._songs) \ +           and new != self._song:              self._updateSong(new)      @property @@ -53,9 +54,7 @@ class SoloTool:      @keyPoints.setter      def keyPoints(self, new: list[float]) -> None: -        if new is None: -            raise ValueError() -        if self._song is not None: +        if new is not None and self._song is not None:              sanitized = sorted(list(set([p for p in new if SoloTool._keyPointValid(p)])))              self._keyPoints[self._song] = sanitized @@ -65,9 +64,7 @@ class SoloTool:      @keyPoint.setter      def keyPoint(self, new: float) -> None: -        if not SoloTool._keyPointValid(new): -            raise ValueError() -        if self._song is not None and new != self._keyPoint: +        if self._song is not None and SoloTool._keyPointValid(new) and new != self._keyPoint:              self._keyPoint = new              self._notifier.notify(Notifier.CURRENT_KEY_POINT_EVENT, new) @@ -92,9 +89,7 @@ class SoloTool:      @rate.setter      def rate(self, new: float) -> None: -        if new is None or new <= 0.0: -            raise ValueError() -        if new != self._player.getPlaybackRate(): +        if new is not None and new >= 0.0 and new != self._player.getPlaybackRate():              self._player.setPlaybackRate(new)              self._notifier.notify(Notifier.PLAYBACK_RATE_EVENT, new) @@ -104,9 +99,7 @@ class SoloTool:      @volume.setter      def volume(self, new: float) -> None: -        if new is None or new < 0.0: -            raise ValueError() -        if new != self._player.getPlaybackVolume(): +        if new is not None and new >= 0.0 and new != self._player.getPlaybackVolume():              self._player.setPlaybackVolume(new)              self._notifier.notify(Notifier.PLAYBACK_VOLUME_EVENT, new) @@ -116,11 +109,9 @@ class SoloTool:      @position.setter      def position(self, new: float) -> None: -        if new is None or new < 0.0 or new >= 1.0: -            raise ValueError()          # TODO stop playback before changing position? -        if new != self._player.getPlaybackPosition(): -            self._player.setPlaybackPosition(new) +        if new is not None and new != self._player.getPlaybackPosition(): +            self._player.setPlaybackPosition(min(max(0.0, new), 1.0))      def registerPlayingStateCallback(self, callback):          self._notifier.registerCallback(Notifier.PLAYING_STATE_EVENT, callback) diff --git a/solo-tool-project/test/midi_launchpad_mini_integrationtest.py b/solo-tool-project/test/midi_launchpad_mini_integrationtest.py index ec41ab2..9588f9f 100644 --- a/solo-tool-project/test/midi_launchpad_mini_integrationtest.py +++ b/solo-tool-project/test/midi_launchpad_mini_integrationtest.py @@ -1,8 +1,6 @@  import pytest  from mido import Message -pytestmark = pytest.mark.skip(reason="not yet implemented") -  from solo_tool.midi_controller_launchpad_mini import MidiController  from solo_tool.solo_tool import SoloTool  from player_mock import Player as PlayerMock @@ -125,7 +123,7 @@ def test_jumpToKeyPositionButton(uut, midiWrapperMock, soloTool, playerMock):      soloTool.song = 0      uut.connect() -    soloTool.keyPosition = 0.5 +    soloTool.keyPoint = 0.5      assert playerMock.position == 0.0      midiWrapperMock.simulateInput(jumpToKeyPositionButton) @@ -147,6 +145,9 @@ def test_previousAndNextSongButtons(uut, midiWrapperMock, soloTool, playerMock):      midiWrapperMock.simulateInput(nextSongButton)      assert playerMock.currentSong == songs[1] +    midiWrapperMock.simulateInput(nextSongButton) +    assert playerMock.currentSong == songs[1] +      midiWrapperMock.simulateInput(previousSongButton)      assert playerMock.currentSong == songs[0] @@ -155,27 +156,27 @@ def test_previousAndNextSongButtons(uut, midiWrapperMock, soloTool, playerMock):  def test_previousAndNextKeyPositionButtons(uut, midiWrapperMock, soloTool, playerMock):      song = "test.flac" -    keyPositions = [0.2, 0.1] +    keyPoints = [0.2, 0.1]      soloTool.addSong(song)      soloTool.song = 0 -    soloTool.keyPositions = keyPositions +    soloTool.keyPoints = keyPoints      uut.connect() -    assert soloTool.keyPosition == 0.0 +    assert soloTool.keyPoint == 0.0      midiWrapperMock.simulateInput(nextKeyPositionButton) -    soloTool.keyPosition == 0.1 +    soloTool.keyPoint == 0.1      midiWrapperMock.simulateInput(nextKeyPositionButton) -    soloTool.keyPosition == 0.2 +    soloTool.keyPoint == 0.2      midiWrapperMock.simulateInput(previousKeyPositionButton) -    soloTool.keyPosition == 0.1 +    soloTool.keyPoint == 0.1      midiWrapperMock.simulateInput(previousKeyPositionButton) -    soloTool.keyPosition == 0.1 +    soloTool.keyPoint == 0.1  def test_playbackRateButtons(uut, midiWrapperMock, soloTool, playerMock):      playbackRateOptions = { @@ -233,9 +234,10 @@ def test_playbackRateLeds(uut, midiWrapperMock, soloTool, playerMock):      assert playerMock.rate == 1.0      for t, (rate, leds) in enumerate(playbackRateOptions): +        print(t)          midiWrapperMock.sentMessages.clear() -        soloTool.setPlaybackRate(rate) +        soloTool.rate = rate          assert playerMock.rate == rate          for i, colour in enumerate(leds): @@ -299,7 +301,7 @@ def test_playbackVolumeLeds(uut, midiWrapperMock, soloTool, playerMock):      for t, (volume, leds) in enumerate(playbackVolumeOptions):          midiWrapperMock.sentMessages.clear() -        soloTool.setPlaybackVolume(volume) +        soloTool.volume = volume          assert playerMock.volume == volume          for i, colour in enumerate(leds): @@ -319,22 +321,20 @@ def test_connectDisconnect(uut, midiWrapperMock):          [(i, LED_GREEN, 0) for i in range(0, 6)] +                      # volume row          [(i, LED_YELLOW, 0) for i in range(16, 22)] +                   # playback rate row          [ -            (stopButton,          LED_RED, 0), -            (playPauseButton,     LED_YELLOW, 0), -            (abToggleButton,      LED_RED, 0), -            (jumpToAButton,       LED_YELLOW, 0), -            (previousLimitButton, LED_RED, 0), -            (nextLimitButton,     LED_GREEN, 0), -            (setAButton,          LED_YELLOW, 0), -            (setBButton,          LED_YELLOW, 0), -            (previousSongButton,  LED_RED, 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), +            (stopButton,                LED_RED, 0), +            (playPauseButton,           LED_YELLOW, 0), +            (jumpToKeyPositionButton,   LED_YELLOW, 0), +            (previousKeyPositionButton, LED_RED, 0), +            (nextKeyPositionButton,     LED_GREEN, 0), +            (setKeyPositionButton,      LED_YELLOW, 0), +            (previousSongButton,        LED_RED, 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 @@ -378,11 +378,11 @@ def test_setKeyPositionButton(uut, midiWrapperMock, soloTool, playerMock):      playerMock.position = 0.3      midiWrapperMock.simulateInput(setKeyPositionButton) -    assert soloTool.keyPosition == 0.3 +    assert soloTool.keyPoint == 0.3      playerMock.position = 0.5      midiWrapperMock.simulateInput(setKeyPositionButton) -    assert soloTool.keyPosition == 0.5 +    assert soloTool.keyPoint == 0.5      playerMock.position = 0.7      midiWrapperMock.simulateInput(jumpToKeyPositionButton) diff --git a/solo-tool-project/test/solo_tool_controller_integrationtest.py b/solo-tool-project/test/solo_tool_controller_integrationtest.py index e39e5f9..8eb09f9 100644 --- a/solo-tool-project/test/solo_tool_controller_integrationtest.py +++ b/solo-tool-project/test/solo_tool_controller_integrationtest.py @@ -2,11 +2,11 @@ import pathlib  import shutil  import pytest -pytestmark = pytest.mark.skip(reason="not yet implemented") -  from solo_tool.solo_tool_controller import SoloToolController  from solo_tool.solo_tool import SoloTool +pytestmark = pytest.mark.skip(reason="not yet implemented") +  @pytest.fixture  def prepared_tmp_path(tmp_path):      testFiles = [ diff --git a/solo-tool-project/test/solo_tool_integrationtest.py b/solo-tool-project/test/solo_tool_integrationtest.py index 94d5cef..2a818ed 100644 --- a/solo-tool-project/test/solo_tool_integrationtest.py +++ b/solo-tool-project/test/solo_tool_integrationtest.py @@ -54,35 +54,58 @@ def test_playerControls(uut, mockPlayer):      assert uut.volume == 0.5  def test_sanitizePlaybackRate(uut): -    # Valid rates are > 0.0 -    with pytest.raises(ValueError): -        uut.rate = -0.1 +    # Initial value +    assert uut.rate == 1.0 -    with pytest.raises(ValueError): -        uut.rate = 0.0 +    # Valid rates are >= 0.0, invalid is ignored +    uut.rate = -0.1 +    assert uut.rate == 1.0 + +    uut.rate = 0.0 +    assert uut.rate == 0.0 +     +    uut.rate = 0.0001 +    assert uut.rate == 0.0001 -    uut.rate = 1.0      uut.rate = 150.0 +    assert uut.rate == 150.0  def test_sanitizePlaybackPosition(uut): -    # Valid positions are in [0, 1) -    with pytest.raises(ValueError): -        uut.position = -0.1 +    # Initial value +    assert uut.position == 0.0 + +    # Valid positions are in [0, 1], invalid is limited +    uut.position = 0.2 +    assert uut.position == 0.2 + +    uut.position = -0.1 +    assert uut.position == 0.0 + +    uut.position = 1.0 +    assert uut.position == 1.0 -    uut.position = 0.0 -    uut.position = 0.999 +    uut.position = 0.4 +    assert uut.position == 0.4 -    with pytest.raises(ValueError): -        uut.position = 1.0 +    uut.position = 1.5 +    assert uut.position == 1.0  def test_sanitizePlaybackVolume(uut): -    # Valid volumes are >= 0.0 -    with pytest.raises(ValueError): -        uut.volume = -0.1 +    # Initial value +    assert uut.volume == 1.0 + +    # Valid volumes are >= 0.0, invalid is ignored +    uut.volume = -0.1 +    assert uut.volume == 1.0      uut.volume = 0.0 +    assert uut.volume == 0.0 +      uut.volume = 1.0 +    assert uut.volume == 1.0 +      uut.volume = 150.0 +    assert uut.volume == 150.0  def test_addAndSelectSongs(uut, mockPlayer):      songs = [ @@ -112,17 +135,14 @@ def test_addAndSelectSongs(uut, mockPlayer):          assert uut.song == i      # The current song cannot be de-selected -    with pytest.raises(ValueError): -        uut.song = None +    uut.song = None      assert uut.song == len(uut.songs) - 1      # Non-existent songs cannot be selected -    with pytest.raises(ValueError): -        uut.song = -1 +    uut.song = -1      assert uut.song == len(uut.songs) - 1 -    with pytest.raises(ValueError): -        uut.song = 2 +    uut.song = 2      assert uut.song == len(uut.songs) - 1  def test_addAndJumpToKeyPoints(uut, mockPlayer): @@ -174,22 +194,21 @@ def test_sanitizeKeyPoint(uut):      # Key point and key point list cannot be none      uut.keyPoint = 0.5 -    with pytest.raises(ValueError): -        uut.keyPoint = None +    uut.keyPoint = None      assert uut.keyPoint == 0.5 -    with pytest.raises(ValueError): -        uut.keyPoints = None +    uut.keyPoints = None      assert uut.keyPoints == [0.1, 0.2, 0.4]      # Valid key points are in [0, 1) -    with pytest.raises(ValueError): -        uut.keyPoint = -0.1 +    uut.keyPoint = -0.1 +    assert uut.keyPoint == 0.5 -    with pytest.raises(ValueError): -        uut.keyPoint = 1.0 +    uut.keyPoint = 1.0 +    assert uut.keyPoint == 0.5      uut.keyPoint = 0.999 +    assert uut.keyPoint == 0.999  def test_keyPointsPerSong(uut, mockPlayer):      songs = [ | 
