Index: __init__.py =================================================================== --- __init__.py (revision 78) +++ __init__.py (working copy) @@ -12,106 +12,20 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from xl import event -from xl import providers -from xl import xdg + +import os, signal, socket, subprocess import pygst pygst.require('0.10') import gst -import socket,os -from threading import Thread -import signal -import time -import array -import struct -import subprocess +from xl import common, event, providers, xdg -plugin_name = "projectM" -pcm_socket_path = os.path.join(xdg.get_plugin_data_dir(), '.projectM-exaile') -if not os.path.exists(pcm_socket_path): - open(pcm_socket_path, 'a') -projectM_bin = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'projectM-exaile') +import logging +logger = logging.getLogger(__name__) -ENABLED=0 -AUDIO_SINK=None -CONNECTED=0 -PCM_DATA_SIZE=1024 -PCM_DATA=None -projectM_proc=None +PROJECTM = None +AUDIO_SINK = None -def run_projectM(arg): - - global projectM_proc - global projectM_bin - - cmd = [projectM_bin, arg] - try: - projectM_proc = subprocess.Popen(cmd,shell=False) - if projectM_proc.pid: - print "projectM-exaile: Spawned projectM Process ID: %s" % projectM_proc.pid - else: - print "projectM-exaile: Failed to spawn projectM Process" - except: - print "projectM-exaile: Failed to spawn projectM Process" - -def kill_projectM(): - global projectM_proc - print "projectM-exaile: Trying to Kill projectM PID: %i" % projectM_proc.pid - try: - if(projectM_proc.pid): - os.kill(projectM_proc.pid,signal.SIGKILL) - print "projectM-exaile: Killed" - except: - return - -class pcm_sock_thread ( Thread ): - def __init__ (self): - Thread.__init__(self) - def run ( self ): - create_pcm_socket() - -def destroy_pcm_socket(): - global conn - global CONNECTED - conn.close() - CONNECTED=0 - -def create_pcm_socket(): - global conn - global CONNECTED,PCM_DATA_SIZE,pcm_socket_path - try: - os.remove(pcm_socket_path) - except OSError: - pass - #Bind Unix Socket for connection from projectM-exaile - s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - s.bind(pcm_socket_path) - print "projectM-exaile: Bound to %s" % pcm_socket_path - s.listen(1) - #Wait for projectM-exaile to connect - print "projectM-exaile: Waiting for connection" - conn = s.accept()[0] - CONNECTED=1 - print "projectM-exaile: projectM Connected" - -def write_pcm_socket(data): - global conn - global CONNECTED - global pcm_socket_path - if(CONNECTED==0): - return - try: - #Send Data - print "START WRITE", len(data) - conn.send(data) - print "END WRITE" - except: - print "projectM-exaile: ProjectM Disconnected" - kill_projectM() - destroy_pcm_socket() - return - def enable(exaile): if (exaile.loading): event.add_callback(_enable, 'exaile_loaded') @@ -119,46 +33,102 @@ _enable(None, exaile, None) def disable(exaile): - global ENABLED - ENABLED=0 - kill_projectM() - destroy_pcm_socket() - print('%s Disabled' % plugin_name) + teardown(exaile) def _enable(eventname, exaile, nothing): - global ENABLED - global AUDIO_SINK, pcm_socket_path - ENABLED = 1 - AUDIO_SINK = AudioSink(exaile) + global PROJECTM, AUDIO_SINK + PROJECTM = ProjectM() + AUDIO_SINK = AudioSink(PROJECTM) providers.register('playback_audio_sink', AUDIO_SINK) - sck_thrd = pcm_sock_thread() - sck_thrd.start() - run_projectM(pcm_socket_path) - print('%s Enabled' % plugin_name) + PROJECTM.spawn() def teardown(exaile): - global ENABLED - ENABLED=0 - print "projectM-exaile: Shutting down" - kill_projectM() - destroy_pcm_socket() - print "projectM-exaile: Shutdown" + global PROJECTM, AUDIO_SINK + PROJECTM.kill() +class ProjectM(object): + pcm_socket_path = os.path.join(xdg.get_plugin_data_dir(), 'projectM', 'projectM-exaile.sock') + projectm_binary = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'projectM-exaile') + def __init__(self): + self.connected = False + self.pid = None + self._sock = None + self._conn = None + + def spawn(self): + self._make_socket() + + command = [self.projectm_binary, self.pcm_socket_path] + try: + proc = subprocess.Popen(command) + if not proc.pid: + raise ValueError # TODO: get a better exception type here + except: + logger.error("Failed to spawn projectM process.") + common.log_exception() + else: + self.pid = proc.pid + logger.debug("Spawned projectM process, pid %s."%self.pid) + + def kill(self): + if not self.pid: + return + try: + os.kill(self.pid, signal.SIGKILL) + logger.debug("projectM process killed.") + except: + logger.warning("Failed to kill projectM process, pid %s."%self.pid) + else: + self.pid = None + finally: + self._close_socket() + + def send_pcm(self, data): + if not self.connected: + return + try: + self._conn.send(data) + except: + logger.warning("Disconnected from projectM") + self.kill() + + @common.threaded + def _make_socket(self): + try: + os.makedirs(os.path.dirname(self.pcm_socket_path)) + except OSError: + pass + try: + os.remove(self.pcm_socket_path) + except OSError: + pass + self._sock = sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.bind(self.pcm_socket_path) + sock.listen(1) + self._conn = sock.accept()[0] + self.connected = True + + def _close_socket(self): + self._conn.close() + self._conn = None + self._sock = None + self.connected = False + + + class AudioSink(gst.Bin): - def __init__(self, exaile): + def __init__(self, projectm): gst.Bin.__init__(self) self.name = 'projectm-audiosink' - self.arr = None - self.exaile = exaile + self.projectm = projectm + elems = [] self.convert = gst.element_factory_make("audioconvert") elems.append(self.convert) self.caps = gst.Caps("audio/x-raw-float, width=32, depth=32") - self.filter = gst.element_factory_make("capsfilter") - self.filter.props.caps = self.caps - elems.append(self.filter) self.sink = gst.element_factory_make("appsink", "projectm-audiosink") self.sink.set_property('emit-signals', True) + self.sink.set_property('caps', self.caps) self.sink.connect('new-preroll', self.new_preroll) self.sink.connect('new-buffer', self.new_buffer) elems.append(self.sink) @@ -168,12 +138,8 @@ self.add_pad(gst.GhostPad('sink', self.sinkpad)) def new_preroll(self, sink): - print "PREROLL" buf = sink.emit('pull-preroll') def new_buffer(self, sink): - print "BUFFER" - global PCM_DATA,CONNECTED buf = sink.emit('pull-buffer') - PCM_DATA = buf.data - write_pcm_socket(PCM_DATA) + self.projectm.send_pcm(buf.data)