diff options
| -rw-r--r-- | doc/diagram.drawio | 4 | ||||
| -rw-r--r-- | solo-tool-project/src/solo_tool/handlers.py | 5 | ||||
| -rw-r--r-- | solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py | 4 | ||||
| -rw-r--r-- | solo-tool-project/src/solo_tool/player_vlc.py | 4 | ||||
| -rw-r--r-- | solo-tool-project/src/solo_tool/solo_tool.py | 4 | ||||
| -rw-r--r-- | solo-tool-project/test/midi_launchpad_mini_integrationtest.py | 56 | ||||
| -rw-r--r-- | solo-tool-project/test/notifier_unittest.py | 2 | ||||
| -rw-r--r-- | solo-tool-project/test/player_mock.py | 27 | ||||
| -rw-r--r-- | solo-tool-project/test/solo_tool_integrationtest.py | 30 | ||||
| -rw-r--r-- | web-project/src/solo_tool_web.py | 2 | 
10 files changed, 42 insertions, 96 deletions
| diff --git a/doc/diagram.drawio b/doc/diagram.drawio index 424570a..3c37bc7 100644 --- a/doc/diagram.drawio +++ b/doc/diagram.drawio @@ -1,4 +1,4 @@ -<mxfile host="Electron" modified="2025-02-24T11:00:29.036Z" 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="9_vGhwK_Mq_I_v5YUap0" version="22.1.2" type="device" pages="2"> +<mxfile host="Electron" modified="2025-02-25T14:57:34.998Z" 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="YTK2v8-32YjVXFJp_mJd" version="22.1.2" type="device" pages="2">    <diagram id="PyNSc7ezSt42GBdjTBvd" name="Launchpad">      <mxGraphModel dx="1562" dy="963" 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> @@ -22,7 +22,7 @@          <mxCell id="ZtjfeE3uwfRsFhnWfLYL-44" value="80" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">            <mxGeometry x="40" y="476" width="80" height="80" as="geometry" />          </mxCell> -        <mxCell id="ZtjfeE3uwfRsFhnWfLYL-45" value="96<br>stop" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1"> +        <mxCell id="ZtjfeE3uwfRsFhnWfLYL-45" value="96<br>jump to start" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">            <mxGeometry x="40" y="564" width="80" height="80" as="geometry" />          </mxCell>          <mxCell id="ZtjfeE3uwfRsFhnWfLYL-46" value="112<br>play/<br>pause" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1"> diff --git a/solo-tool-project/src/solo_tool/handlers.py b/solo-tool-project/src/solo_tool/handlers.py index 975ce8d..ab7309e 100644 --- a/solo-tool-project/src/solo_tool/handlers.py +++ b/solo-tool-project/src/solo_tool/handlers.py @@ -30,6 +30,11 @@ def seekRelative(st: SoloTool, delta: float) -> Callable[[], None]:          st.position += delta      return f +def seekAbsolute(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 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 38b7cce..08d55cd 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 @@ -32,7 +32,7 @@ class MidiController:      def _registerHandlers(self):          self._handlers = { -            96  : self._soloTool.stop, +            96  : handlers.seekAbsolute(self._soloTool, 0.0),              114 : self._soloTool.jump,              112 : handlers.playPause(self._soloTool),              118 : handlers.changeKeyPoint(self._soloTool, -1), @@ -112,7 +112,7 @@ class MidiController:          self._updateRateRow(self._soloTool.rate)          # playback control -        self._setButtonLED(6, 0, MidiController.LED_RED) +        self._setButtonLED(6, 0, MidiController.LED_YELLOW)          self._updatePlayPauseButton(self._soloTool.playing)          # Key point control diff --git a/solo-tool-project/src/solo_tool/player_vlc.py b/solo-tool-project/src/solo_tool/player_vlc.py index 283102e..dade61f 100644 --- a/solo-tool-project/src/solo_tool/player_vlc.py +++ b/solo-tool-project/src/solo_tool/player_vlc.py @@ -7,9 +7,6 @@ class Player:      def play(self):          self._player.play() -    def stop(self): -        self._player.stop() -      def pause(self):          self._player.pause() @@ -36,7 +33,6 @@ class Player:          return self._player.audio_get_volume() / 100.0      def setCurrentSong(self, path): -        self._player.stop()          media = vlc.Media(path)          self._player.set_media(media) diff --git a/solo-tool-project/src/solo_tool/solo_tool.py b/solo-tool-project/src/solo_tool/solo_tool.py index 0489517..ec4caa1 100644 --- a/solo-tool-project/src/solo_tool/solo_tool.py +++ b/solo-tool-project/src/solo_tool/solo_tool.py @@ -15,6 +15,7 @@ class SoloTool:      def _updateSong(self, index):          previousSong = self._song          self._song = index +        self._player.pause()          self._player.setCurrentSong(self._songs[index])          self._notifier.notify(Notifier.CURRENT_SONG_EVENT, index) @@ -86,9 +87,6 @@ class SoloTool:      def pause(self):          self._player.pause() -    def stop(self): -        self._player.stop() -      @property      def playing(self) -> bool:          return self._player.isPlaying() diff --git a/solo-tool-project/test/midi_launchpad_mini_integrationtest.py b/solo-tool-project/test/midi_launchpad_mini_integrationtest.py index 642733d..c731539 100644 --- a/solo-tool-project/test/midi_launchpad_mini_integrationtest.py +++ b/solo-tool-project/test/midi_launchpad_mini_integrationtest.py @@ -20,7 +20,7 @@ rwd25PcButton = 49  previousSongButton = 48  playPauseButton = 112 -stopButton = 96 +jumpToStartButton = 96  nextKeyPositionButton = 119  previousKeyPositionButton = 118 @@ -67,60 +67,34 @@ def midiWrapperMock():  def uut(soloTool, midiWrapperMock):      return MidiController(soloTool, midiWrapperMock) -def test_startStopAndPauseButtons(uut, midiWrapperMock, playerMock): +def test_startAndPauseButtons(uut, midiWrapperMock, playerMock):      uut.connect() -    assert playerMock.state == PlayerMock.STOPPED +    assert not playerMock.playing      midiWrapperMock.simulateInput(playPauseButton) -    assert playerMock.state == PlayerMock.PLAYING +    assert playerMock.playing      assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0) -    midiWrapperMock.simulateInput(stopButton) -    assert playerMock.state == PlayerMock.STOPPED -    assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0) - -    midiWrapperMock.simulateInput(playPauseButton) -    assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0) -          midiWrapperMock.simulateInput(playPauseButton) +    assert not playerMock.playing      assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0) -    assert playerMock.state == PlayerMock.PAUSED - -    midiWrapperMock.simulateInput(playPauseButton) -    assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0) -    assert playerMock.state == PlayerMock.PLAYING - -    midiWrapperMock.simulateInput(playPauseButton) -    assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0) - -    midiWrapperMock.simulateInput(stopButton) -    assert playerMock.state == PlayerMock.STOPPED  def test_startPauseButtonLed(uut, midiWrapperMock, playerMock, soloTool):      uut.connect() -    assert playerMock.state == PlayerMock.STOPPED +    assert not playerMock.playing -    playerMock.state = PlayerMock.PLAYING +    playerMock.playing = True      playerMock.simulatePlayingStateChanged()      assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0) -    playerMock.state = PlayerMock.STOPPED -    playerMock.simulatePlayingStateChanged() -    assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0) - -    playerMock.state = PlayerMock.PAUSED +    playerMock.playing = False      playerMock.simulatePlayingStateChanged()      assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_YELLOW, 0) -    playerMock.state = PlayerMock.PLAYING -    playerMock.simulatePlayingStateChanged() -    assert midiWrapperMock.getLatestMessage() == (playPauseButton, MidiController.LED_GREEN, 0) -  def test_jumpToKeyPositionButton(uut, midiWrapperMock, soloTool, playerMock):      soloTool.addSong("test.flac") -    soloTool.song = 0      uut.connect()      soloTool.keyPoint = 0.5 @@ -129,6 +103,10 @@ def test_jumpToKeyPositionButton(uut, midiWrapperMock, soloTool, playerMock):      midiWrapperMock.simulateInput(jumpToKeyPositionButton)      assert playerMock.position == 0.5 +# TODO implement +def test_jumpToStartButton(uut, midiWrapperMock, soloTool, playerMock): +    pass +  def test_previousAndNextSongButtons(uut, midiWrapperMock, soloTool, playerMock):      songs = [          "test.flac", @@ -156,7 +134,6 @@ def test_previousAndNextKeyPositionButtons(uut, midiWrapperMock, soloTool, playe      keyPoints = [0.2, 0.1]      soloTool.addSong(song) -    soloTool.song = 0      soloTool.keyPoints = keyPoints      uut.connect() @@ -318,7 +295,7 @@ 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), +            (jumpToStartButton,         LED_YELLOW, 0),              (playPauseButton,           LED_YELLOW, 0),              (jumpToKeyPositionButton,   LED_YELLOW, 0),              (previousKeyPositionButton, LED_RED, 0), @@ -357,19 +334,17 @@ def test_playingFeedbackWhenChangingSong(uut, midiWrapperMock, soloTool, playerM          soloTool.addSong(s)      uut.connect() -    soloTool.song = 0      soloTool.play() -    assert playerMock.state == PlayerMock.PLAYING +    assert playerMock.playing      assert midiWrapperMock.getLatestMessage() == (playPauseButton, LED_GREEN, 0)      soloTool.song = 1 -    assert playerMock.state == PlayerMock.STOPPED +    assert not playerMock.playing      assert midiWrapperMock.getLatestMessage() == (playPauseButton, LED_YELLOW, 0)  def test_setKeyPositionButton(uut, midiWrapperMock, soloTool, playerMock):      song = "test.flac"      soloTool.addSong(song) -    soloTool.song = 0      uut.connect() @@ -388,7 +363,6 @@ def test_setKeyPositionButton(uut, midiWrapperMock, soloTool, playerMock):  def test_seekButtons(uut, midiWrapperMock, soloTool, playerMock):      song = "test.flac"      soloTool.addSong(song) -    soloTool.song = 0      uut.connect() diff --git a/solo-tool-project/test/notifier_unittest.py b/solo-tool-project/test/notifier_unittest.py index 4ab6096..5749149 100644 --- a/solo-tool-project/test/notifier_unittest.py +++ b/solo-tool-project/test/notifier_unittest.py @@ -60,7 +60,7 @@ def test_eventsWithMockPlayer(uut, mockPlayer):          assert called          assert receivedValue == expectedValue -    mockPlayer.state = 1 +    mockPlayer.playing = True      mockPlayer.volume = 75      checkEvent(Notifier.PLAYING_STATE_EVENT, mockPlayer.simulatePlayingStateChanged, True) diff --git a/solo-tool-project/test/player_mock.py b/solo-tool-project/test/player_mock.py index 3162e0f..e9e9ead 100644 --- a/solo-tool-project/test/player_mock.py +++ b/solo-tool-project/test/player_mock.py @@ -1,10 +1,6 @@  class Player(): -    STOPPED = 0 -    PLAYING = 1 -    PAUSED = 2 -      def __init__(self): -        self.state = Player.STOPPED +        self.playing = False          self.rate = 1.0          self.position = 0.0          self.volume = 1.0 @@ -13,25 +9,19 @@ class Player():          self.playbackVolumeChangedCallback = None      def play(self): -        previousState = self.state -        self.state = Player.PLAYING -        if previousState != Player.PLAYING: -            self.playingStateChangedCallback() - -    def stop(self): -        previousState = self.state -        self.state = Player.STOPPED -        if previousState != Player.STOPPED: +        previousState = self.playing +        self.playing = True +        if previousState != self.playing:              self.playingStateChangedCallback()      def pause(self): -        previousState = self.state -        self.state = Player.PAUSED -        if previousState != Player.PAUSED: +        previousState = self.playing +        self.playing = False +        if previousState != self.playing:              self.playingStateChangedCallback()      def isPlaying(self): -        return self.state == Player.PLAYING +        return self.playing      def setPlaybackRate(self, rate):          self.rate = rate @@ -55,7 +45,6 @@ class Player():          return self.volume      def setCurrentSong(self, path): -        self.stop()          self.currentSong = path      def setPlayingStateChangedCallback(self, callback): diff --git a/solo-tool-project/test/solo_tool_integrationtest.py b/solo-tool-project/test/solo_tool_integrationtest.py index fb759b0..f8ed2f1 100644 --- a/solo-tool-project/test/solo_tool_integrationtest.py +++ b/solo-tool-project/test/solo_tool_integrationtest.py @@ -26,17 +26,14 @@ def prepared_tmp_path(tmp_path):      return tmp_path  def test_playerControls(uut, mockPlayer): -    assert mockPlayer.state == MockPlayer.STOPPED -    assert uut.playing == False +    assert not mockPlayer.playing +    assert not uut.playing      uut.play() -    assert mockPlayer.state == MockPlayer.PLAYING -    assert uut.playing == True +    assert mockPlayer.playing +    assert uut.playing      uut.pause() -    assert mockPlayer.state == MockPlayer.PAUSED -    assert uut.playing == False -    uut.stop() -    assert mockPlayer.state == MockPlayer.STOPPED -    assert uut.playing == False +    assert not mockPlayer.playing +    assert not uut.playing      assert mockPlayer.rate == 1.0      uut.rate = 0.5 @@ -110,7 +107,6 @@ def test_sanitizePlaybackVolume(uut):  def test_playingStateNotification(uut, mockPlayer):      song = "test.flac"      uut.addSong(song) -    uut.song = 0      called = False      receivedValue = None @@ -121,7 +117,7 @@ def test_playingStateNotification(uut, mockPlayer):      uut.registerPlayingStateCallback(callback) -    assert mockPlayer.state == MockPlayer.STOPPED +    assert not mockPlayer.playing      assert not called      uut.play() @@ -138,18 +134,6 @@ def test_playingStateNotification(uut, mockPlayer):      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 -  def test_playbackVolumeNotification(uut, mockPlayer):      song = "test.flac"      uut.addSong(song) diff --git a/web-project/src/solo_tool_web.py b/web-project/src/solo_tool_web.py index d0e8bf8..1c039ab 100644 --- a/web-project/src/solo_tool_web.py +++ b/web-project/src/solo_tool_web.py @@ -56,7 +56,7 @@ def main():      # Play control      with ui.button_group().classes('w-full').style('height: 90px'):          ui.button(icon='skip_previous', on_click=handlers.changeSong(st, -1)).style('flex: 1') -        ui.button(icon='stop', on_click=st.stop, color='negative').style('flex: 1') +        ui.button(icon='start', on_click=handlers.seekAbsolute(st, 0.0), color='negative').style('flex: 1')          ui.button(color='positive', on_click=handlers.playPause(st)).bind_icon_from(st, "playing", lambda playing: "pause" if playing else "play_arrow").style('flex: 2')          ui.button(icon='undo', on_click=st.jump, color='secondary').style('flex: 2')          ui.button(icon='skip_next', on_click=handlers.changeSong(st, 1)).style('flex: 1') | 
