From eac4ed68407dbc880c352c2245e766e322ebfcec Mon Sep 17 00:00:00 2001 From: Derrick Liu Date: Thu, 28 Mar 2019 13:02:49 -0700 Subject: [PATCH 1/7] Use Pyglet playback when iTunes mode is false --- spotbox/spotbox.py | 2 +- spotbox/spotboxplayback.py | 28 ++++++++++++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/spotbox/spotbox.py b/spotbox/spotbox.py index ef3d5bd..1094e42 100644 --- a/spotbox/spotbox.py +++ b/spotbox/spotbox.py @@ -39,6 +39,6 @@ playback = sbplayback.iTunesPlayback() else: # this class does nothing for load/play/stop/etc, just pass for each - playback = sbplayback.Playback() + playback = sbplayback.PygletPlayback() spotboxgui = SpotboxTkInterface(configuration, datasheets, playback) spotboxgui.run_continuously() diff --git a/spotbox/spotboxplayback.py b/spotbox/spotboxplayback.py index fb7a9f2..e18b377 100644 --- a/spotbox/spotboxplayback.py +++ b/spotbox/spotboxplayback.py @@ -2,8 +2,9 @@ """SPOTBOX media playback backend""" -# Note-- non-iTunes mode is not yet fully implemented. +import os +from spotboxconfig import folder_configuration class Playback: """A playback object, allows for audio files to be loaded, played back, @@ -74,28 +75,35 @@ def play(self, spotnumber): class PygletPlayback(Playback): + """Provide playback support using the Pyglet library. + + See https://bitbucket.org/pyglet/pyglet/overview""" def __init__(self): - import pyglet - self.playerarray = [] + self.players = {} def initialize_one_player(self, spotnumber): - pygplayer = self.pyglet.media.Player() - self.playerarray.append(pygplayer) + import pyglet + pygplayer = pyglet.media.Player() + self.players[spotnumber] = pygplayer def stop(self): - for player in self.playerarray: + for spot_number, player in self.players.iteritems(): player.pause() def load(self, spotnumber, filepath): + import pyglet # at moment, throws "NOT A WAVE" exception, even for .wav print filepath - media = self.pyglet.media.load(filepath, streaming=False) - del self.playerarray[spotnumber] - self.playerarray[spotnumber].queue(media) + media = pyglet.media.load(os.path.join(folder_configuration['MEDIADIRECTORY'], filepath)) + + self.initialize_one_player(spotnumber) + self.players[spotnumber].queue(media) + return True def play(self, spotnumber): - self.playerarray[spotnumber].play() + self.players[spotnumber].play() + if __name__ == '__main__': pass From 7d604c416b685cb0688ad3c42b76ec3864d1eeab Mon Sep 17 00:00:00 2001 From: Derrick Liu Date: Thu, 28 Mar 2019 13:08:37 -0700 Subject: [PATCH 2/7] Stop should reset the playback transport to 0 --- spotbox/spotboxplayback.py | 1 + 1 file changed, 1 insertion(+) diff --git a/spotbox/spotboxplayback.py b/spotbox/spotboxplayback.py index e18b377..4061115 100644 --- a/spotbox/spotboxplayback.py +++ b/spotbox/spotboxplayback.py @@ -90,6 +90,7 @@ def initialize_one_player(self, spotnumber): def stop(self): for spot_number, player in self.players.iteritems(): player.pause() + player.seek(0) def load(self, spotnumber, filepath): import pyglet From 68d55cf206f818c77b3a1d77c55eab0d8d8e1e5b Mon Sep 17 00:00:00 2001 From: Derrick Liu Date: Thu, 28 Mar 2019 13:15:34 -0700 Subject: [PATCH 3/7] Work around Pyglet issue with OpenGL if seeking a non-started track --- spotbox/spotboxplayback.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spotbox/spotboxplayback.py b/spotbox/spotboxplayback.py index 4061115..576dad9 100644 --- a/spotbox/spotboxplayback.py +++ b/spotbox/spotboxplayback.py @@ -90,7 +90,10 @@ def initialize_one_player(self, spotnumber): def stop(self): for spot_number, player in self.players.iteritems(): player.pause() - player.seek(0) + + # Need to check to avoid OpenGL access exception (ugh) + if player._get_time() > 0: + player.seek(0.0) def load(self, spotnumber, filepath): import pyglet From 7e1689d05aec0bb0c8b8a4c48c54bc409229422f Mon Sep 17 00:00:00 2001 From: Derrick Liu Date: Thu, 28 Mar 2019 13:16:03 -0700 Subject: [PATCH 4/7] Stop all spots before trying to play a new one Assuming you don't want to layer spots on top of each other :\ --- spotbox/spotboxgui.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spotbox/spotboxgui.py b/spotbox/spotboxgui.py index 73fe2a5..63d2a2c 100644 --- a/spotbox/spotboxgui.py +++ b/spotbox/spotboxgui.py @@ -158,6 +158,8 @@ def _loadspot(self, spottext, spotnumber): self._menus.filetimetoload) def _playspot(self, spotnumber): + self._stop() + self._playback.play(spotnumber) countdowntostart = self.countdownarray[spotnumber] for countdown in self.countdownarray: From e694a689fc68dd6287aa937139ab99a9c92e8c6f Mon Sep 17 00:00:00 2001 From: Derrick Liu Date: Thu, 28 Mar 2019 13:32:19 -0700 Subject: [PATCH 5/7] Fix multiple playback issues (end of file, multiple players, etc) When Pyglet finishes playback, it will dequeue the source media. Calling `play` again will immediately pause, because there's no source media in the queue. This fix addresses that issue by checking the player queue state and requeueing the media if it expired out of the queue. This also addresses an issue where pressing play on two spots will allow you to play them simultaneously. Since the UI doesn't show that, I fixed the behavior to always stop playback. I also changed the behavior to make sure we start playback from the beginning, since the expected behavior of a "stop" button is to stop audio and reset the transport to the beginning of the track for future playback, not resume playback from the original timestamp. --- spotbox/spotboxplayback.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/spotbox/spotboxplayback.py b/spotbox/spotboxplayback.py index 576dad9..464f97a 100644 --- a/spotbox/spotboxplayback.py +++ b/spotbox/spotboxplayback.py @@ -81,10 +81,16 @@ class PygletPlayback(Playback): def __init__(self): self.players = {} + self.media = {} def initialize_one_player(self, spotnumber): import pyglet pygplayer = pyglet.media.Player() + + if spotnumber in self.players: + # Make sure to free up queues and player resources + self.players[spotnumber].delete() + self.players[spotnumber] = pygplayer def stop(self): @@ -92,21 +98,28 @@ def stop(self): player.pause() # Need to check to avoid OpenGL access exception (ugh) - if player._get_time() > 0: + if player.time > 0: player.seek(0.0) def load(self, spotnumber, filepath): import pyglet # at moment, throws "NOT A WAVE" exception, even for .wav - print filepath - media = pyglet.media.load(os.path.join(folder_configuration['MEDIADIRECTORY'], filepath)) + self.media[spotnumber] = pyglet.media.load(os.path.join(folder_configuration['MEDIADIRECTORY'], filepath)) - self.initialize_one_player(spotnumber) - self.players[spotnumber].queue(media) return True def play(self, spotnumber): - self.players[spotnumber].play() + player = self.players[spotnumber] + + if player.source is None: + # re-enqueue media + player.queue(self.media[spotnumber]) + + if player.time > 0: + # play from beginning + player.seek(0) + + player.play() if __name__ == '__main__': From 831ee3e71ed099ce6dad43c9a16aa68cb5c82c8c Mon Sep 17 00:00:00 2001 From: Derrick Liu Date: Thu, 28 Mar 2019 13:37:34 -0700 Subject: [PATCH 6/7] Amend comment to reflect AVBin requirements --- spotbox/spotboxplayback.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spotbox/spotboxplayback.py b/spotbox/spotboxplayback.py index 464f97a..3ae10bb 100644 --- a/spotbox/spotboxplayback.py +++ b/spotbox/spotboxplayback.py @@ -103,7 +103,8 @@ def stop(self): def load(self, spotnumber, filepath): import pyglet - # at moment, throws "NOT A WAVE" exception, even for .wav + + # To support non-WAV files, you may need to install AVBin (http://avbin.github.io/AVbin) self.media[spotnumber] = pyglet.media.load(os.path.join(folder_configuration['MEDIADIRECTORY'], filepath)) return True From 7d8fd3016e00ade49343b0946662fbb8129aac50 Mon Sep 17 00:00:00 2001 From: Derrick Liu Date: Thu, 28 Mar 2019 13:38:32 -0700 Subject: [PATCH 7/7] Better documentation --- CONTRIBUTING.md | 4 ++++ README.md | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ce9a10a..209b306 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,6 +7,10 @@ You need to have: - Python 2.7 (for now, sorry 😢) and `pip` installed - The `pipenv` tool ([See here for installation instructions](https://pipenv.readthedocs.io/en/latest/install/#installing-pipenv)) +You should consider installing: + +- Python 3.6 or above (ideally 3.7 or above) + ## Setting up your environment - Clone this repo diff --git a/README.md b/README.md index 644316c..e27a49d 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,25 @@ back short audio tracks. Written by Mark Mollineaux (2013) +## Installation and running + +### Prerequisites + +You need the following: + +- Python 2.7 and `pip` +- Recommended: AVBin for playing spotbox files (https://avbin.github.io/AVbin/Download.html) + +Use `pip install` to install: + +- `pip install pyglet` + +### Steps to install + +- Download the spotbox code from GitHub (clone or zip) +- Navigate to the repository +- Create a shortcut, script, or alias to run `python spotbox.py`. + ## Contributing Read the [contribution guidelines](./CONTRIBUTING.md) to help out.