From b08d4e46c88cfbe7d9a07c6e02c8aa192d072530 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Wed, 11 Jan 2012 23:43:46 +0100 Subject: [PATCH] Add support for asynchronous audiosinks. With playlists working as they should --- examples/jukebox.py | 30 ++++++++++++++++++++++++------ spotify/audiosink/__init__.py | 1 + spotify/audiosink/gstreamer.py | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/examples/jukebox.py b/examples/jukebox.py index 179affaa..20c51e28 100755 --- a/examples/jukebox.py +++ b/examples/jukebox.py @@ -1,10 +1,10 @@ #!/usr/bin/env python - +import os +import sys import cmd -import traceback import time +import traceback import threading -import os from spotify import ArtistBrowser, Link, ToplistBrowser from spotify.audiosink import import_audio_sink @@ -12,6 +12,7 @@ SpotifyContainerManager) AudioSink = import_audio_sink() +container_loaded = threading.Event() class JukeboxUI(cmd.Cmd, threading.Thread): @@ -26,6 +27,7 @@ def __init__(self, jukebox): self.results = False def run(self): + container_loaded.wait() try: self.cmdloop() except Exception, e: @@ -264,7 +266,8 @@ def tracks_removed(self, p, t, u): ## container calllbacks ## class JukeboxContainerManager(SpotifyContainerManager): def container_loaded(self, c, u): - print 'Container loaded !' + container_loaded.set() + container_loaded.clear() def playlist_added(self, c, p, i, u): print 'Container: playlist "%s" added.' % p.name() @@ -285,14 +288,20 @@ class Jukebox(SpotifySessionManager): def __init__(self, *a, **kw): SpotifySessionManager.__init__(self, *a, **kw) self.audio = AudioSink() + if self.audio.async: + self.audio.backend = self + self.audio.setup() self.ui = JukeboxUI(self) self.ctr = None self.playing = False self._queue = [] self.playlist_manager = JukeboxPlaylistManager() self.container_manager = JukeboxContainerManager() + self.track_playing = None print "Logging in, please wait..." + def new_track_playing(self, track): + self.track_playing = track def logged_in(self, session, error): if error: @@ -313,6 +322,7 @@ def logged_out(self, session): def load_track(self, track): if self.playing: self.stop() + self.new_track_playing(track) self.session.load(track) print "Loading %s" % track.name() @@ -323,8 +333,10 @@ def load(self, playlist, track): pl = self.ctr[playlist] elif playlist == len(self.ctr): pl = self.starred - self.session.load(pl[track]) - print "Loading %s from %s" % (pl[track].name(), pl.name()) + spot_track = pl[track] + self.new_track_playing(spot_track) + self.session.load(spot_track) + print "Loading %s from %s" % (spot_track.name(), pl.name()) def load_playlist(self, playlist): if self.playing: @@ -335,6 +347,8 @@ def load_playlist(self, playlist): pl = self.starred print "Loading playlist %s" % pl.name() if len(pl): + print "Loading %s from %s" % (pl[0].name(), pl.name()) + self.new_track_playing(pl[0]) self.session.load(pl[0]) for i, track in enumerate(pl): if i == 0: @@ -345,6 +359,7 @@ def queue(self, playlist, track): if self.playing: self._queue.append((playlist, track)) else: + print 'Loading %s', track.name() self.load(playlist, track) self.play() @@ -373,6 +388,9 @@ def next(self): self.stop() def end_of_track(self, sess): + if self.audio.async: + self.audio.emit_EOS() + return print "track ends." self.next() diff --git a/spotify/audiosink/__init__.py b/spotify/audiosink/__init__.py index 62e1ccef..bb5913c6 100644 --- a/spotify/audiosink/__init__.py +++ b/spotify/audiosink/__init__.py @@ -57,6 +57,7 @@ class BaseAudioSink(object): def __init__(self): self._call_cache = {} + self.async = False def music_delivery(self, session, frames, frame_size, num_frames, sample_type, sample_rate, channels): diff --git a/spotify/audiosink/gstreamer.py b/spotify/audiosink/gstreamer.py index 3a64e4c8..f24b1f00 100644 --- a/spotify/audiosink/gstreamer.py +++ b/spotify/audiosink/gstreamer.py @@ -1,3 +1,4 @@ +import threading import gobject import gst import sys @@ -35,12 +36,41 @@ def __init__(self): } caps = gst.caps_from_string(caps_string) self._pipeline = gst.parse_launch(' ! '.join([ - 'appsrc name="application_src"', + 'appsrc name="application_src" block=true', 'audioconvert', 'autoaudiosink', ])) self._source = self._pipeline.get_by_name('application_src') self._source.set_property('caps', caps) + self.async = True + self.backend = None + self.mainloop = None + self.mainloop_thread = threading.Thread(target=self.start_glib) + self.mainloop_thread.setDaemon(True) + self.mainloop_thread.start() + + + def start_glib(self): + self.mainloop = gobject.MainLoop() + self.mainloop.run() + print 'Mainloop running' + + def setup(self): + self._setup_message_processor() + + def _setup_message_processor(self): + bus = self._pipeline.get_bus() + bus.add_signal_watch() + bus.connect('message', self._on_message) + + def _on_message(self, bus, message): + if message.type == gst.MESSAGE_EOS: + print 'track ended' + self._pipeline.set_state(gst.STATE_NULL) + self.backend.next() + + def emit_EOS(self): + self._source.emit('end-of-stream') def music_delivery(self, session, frames, frame_size, num_frames, sample_type, sample_rate, channels): @@ -58,9 +88,11 @@ def music_delivery(self, session, frames, frame_size, num_frames, return num_frames def start(self): + self._pipeline.set_state(gst.STATE_READY) self._pipeline.set_state(gst.STATE_PLAYING) def stop(self): + self._pipeline.set_state(gst.STATE_READY) self._pipeline.set_state(gst.STATE_NULL) def pause(self):