#!/usr/bin/env python
-
+#version 1.1, with some sanity checking and better 'initial' cleanup of existing instances
import os
import sys
import psutil
import signal
from time import sleep
-from subprocess import Popen
+from subprocess import run, Popen, TimeoutExpired
+from getpass import getuser
class gs:
#debug = 1 spawn and exit
- #debug = 2 spawn waiting messages
+ #debug = 2 spawn-waiting messages
#debug = 3 all messages
+ #debug = 4 too many messages
debug = 0
+ curdt = 0
+ rtprio={
+ 'jackd' :41,
+ 'alsa_out_1' :41,
+ 'alsa_in_1' :41,
+ 'jalv_1' :31,
+ 'jalv_2' :31,
+ 'jalv_3' :31,
+ 'jalv_4' :31,
+ 'jalv_5' :31,
+ 'alsa_out_2' :21,
+ 'alsa_out_3' :21,
+ 'alsa_out_4' :21,
+ 'pulseaudio' :21,
+ 'qjackctl' :0,
+ 'kmix' :0,
+ 'conky' :0,
+ }
class daemon:
def __init__(self):
- self.basepath='/home/andrew/.soundstage/'
+ self.basepath='/home/%s/.soundstage/'%getuser()
self.pidfile='%s%s.pid'%(self.basepath,self.__class__.__name__)
self.statusfile='%s%s.status'%(self.basepath,self.__class__.__name__)
self.logfile='%s%s.log'%(self.basepath,self.__class__.__name__)
- self.arguments=[]
- self.depends=[]
- self.postrun=[]
- self.child=None
+ self.arguments=self.depends=self.postrun=[]
+ self.child=self.hardclean=None
def start(self):
- child=os.fork()
- if child: return
+ try:
+ oops=open(self.pidfile,'r')
+ pid=oops.read()
+ oops.close()
+ child=psutil.Process(int(pid))
+ child.terminate()
+ try:
+ child.wait(timeout=3)
+ except psutil.Timeout:
+ child.kill()
+ except:
+ pass
+ if os.fork():return
self.writefile(self.pidfile,os.getpid())
signal.signal(signal.SIGINT, self.exit)
signal.signal(signal.SIGUSR1, self.exit)
self.cleanup()
def check_depends(self):
- if gs.debug > 1 :print ('Checking depends for %s' %self.name)
+ if gs.debug > 1 :print ('Checking depends for %s:%s'%(self.__class__.__name__,self.depends))
+
for requirement in self.depends:
- while self.depends:
- if gs.debug > 2 :print ('checking for %s%s' %(self.basepath,requirement))
- if self.readfile('%s%s.status'%(self.basepath,requirement)) == 'Running...':
+ while True:
+ if gs.debug > 2 :print ('Checking %s%s for %s ... ' %(self.basepath,requirement,self.__class__.__name__),end='',flush=True)
+ depstatus=self.readfile('%s%s.status'%(self.basepath,requirement))
+ if gs.debug > 2 :print (depstatus)
+ if depstatus == 'Running...':
break
- if gs.debug > 2: print ('%s not running yet...' %requirement)
- #sleep(.15)
+ if gs.debug > 2: print ('%s not yet running for %s...'%(requirement,self.__class__.__name__))
+
def monitor_depends(self):
- if gs.debug > 1 :print ('Montoring depends for %s' %self.name)
status=True
for requirement in self.depends:
+ if gs.debug > 1 :print ('Montoring dep %s for %s' %(requirement,self.__class__.__name__))
if self.readfile('%s%s.status'%(self.basepath,requirement)) != 'Running...':
- if gs.debug > 0 :print('we have a broken dependency %s'%requirement)
+ if gs.debug > 0 :print('%s has a broken dependency %s'%(self.__class__.__name__,requirement))
status=False
return status
def spawn_daemon(self):
- if gs.debug != 0 :print('Spawning %s Daemon...'%self.name)
+ if gs.debug != 0 :print('Spawning %s Daemon...'%self.__class__.__name__)
+ self.killcheck()
self.child_log=open(self.logfile,'w')
self.child=Popen(self.arguments, stderr=self.child_log, stdout=self.child_log)
- #sleep(.5)
for run_me in self.postrun:
if gs.debug > 1:print('Running assist: %s'%run_me)
- assistant=assists[run_me]()
+ assistant=assists[run_me](gs.rtprio[self.__class__.__name__],self.child.pid)
assistant.run()
- #sleep(.5)
self.writefile(self.statusfile,'Running...')
+ if gs.debug > 0:print('%s started with pid:%s'%(psutil.Process(self.child.pid).name(),self.child.pid))
def monitor(self):
- if gs.debug != 0 :print('Running %s monitor...'%self.name)
+ if gs.debug != 0 :print('Running %s monitor...'%self.__class__.__name__)
while self.monitor_depends():
status=self.child.poll()
- if gs.debug > 2 :print('%s status: %s' %(self.name, status))
+ if gs.debug > 3 :print('%s child status: %s' %(self.__class__.__name__, status))
if status==None and self.monitor_depends():
sleep(5)
else:
break
def cleanup(self):
- if gs.debug != 0 :print('Running %s cleanup...'%self.name)
+ if gs.debug != 0 :print('Running %s cleanup...'%self.__class__.__name__)
self.writefile(self.statusfile,'Stopping...')
if self.child:
self.child.terminate()
- if not self.child.poll():
+ try:
+ self.child.wait(timeout=10)
+ except TimeoutExpired:
self.child.kill()
self.child_log.close()
+ self.killcheck()
self.writefile(self.statusfile,'Stopped.')
- def exit(self,sig,fan):
- if gs.debug > 2 :print('%s caught exit signal...'%self.name)
- #try:
+ def exit(self,sig,frame):
+ if gs.debug > 2 :print('%s caught exit signal...'%self.__class__.__name__)
self.cleanup()
os.remove(self.pidfile)
- #except:
- # pass
- sys.exit()
+ sys.exit(0)
+
+ def killcheck(self):
+ username=getuser()
+ if self.arguments[0]=='/usr/bin/chrt':
+ args=self.arguments[2:]
+ else:
+ args=self.arguments
+ for proc in psutil.process_iter():
+ if username==proc.username() and(args==proc.cmdline() or(self.__class__.__name__==proc.name() and self.hardclean)):
+ proc.terminate()
+ try:
+ proc.wait(timeout=3)
+ except psutil.TimeoutExpired:
+ proc.kill()
def readfile(self,filename):
data=''
- #try:
with open(filename, 'r') as file:
data=file.read()
- #except:
- # pass
return data
def writefile(self,filename,data):
- #try:
with open(filename, 'w') as file:
result=file.write(str(data))
- #except:
- # pass
return result
class jackd(daemon):
def __init__(self):
super().__init__()
- self.arguments=['/usr/bin/chrt','89','/usr/bin/jackd','-dalsa','-dhw:Generic','-r48000','-p1024','-n2']
- self.postrun=['volumes_3','tv_on']
+ self.hardclean=True
+ self.arguments=['/usr/bin/jackd','-R','-P41','-dalsa','-Chw:1,0','-Phw:1,0','-r48000','-p1024','-n2']
+# self.arguments=['/usr/bin/chrt','55','/usr/bin/jackd','-ddummy','-C0','-P0','-r48000','-p1024']
+ self.postrun=['volumes_3','tv_on','chrt']
class qjackctl(daemon):
def __init__(self):
super().__init__()
+ self.hardclean=True
self.arguments=['/usr/bin/qjackctl']
- self.depends=['jackd','pulse']
+ self.depends=['jackd']
-class pulse(daemon):
+class pulseaudio(daemon):
def __init__(self):
super().__init__()
- self.arguments=['/usr/bin/pulseaudio','--fail']
+ self.hardclean=True
+ self.arguments=['/usr/bin/pulseaudio']
self.depends=['jackd']
- self.postrun=['volumes_1','chrt','volumes_2']
-
- def killall(self):
- for proc in psutil.process_iter():
- if proc.name()=='pulseaudio':
- os.kill(int(proc.pid),signal.SIGKILL)
-
- def spawn_daemon(self):
- if gs.debug != 0 :print('Spawning %s Daemon...'%self.name)
- self.killall()
- self.child_log=open(self.logfile,'w')
- self.child=Popen(self.arguments, stderr=self.child_log, stdout=self.child_log)
- #sleep(.5)
- for run_me in self.postrun:
- if gs.debug > 1:print('Running assist: %s'%run_me)
- if run_me=='chrt':
- assistant=assists[run_me](self.child.pid)
- else:
- assistant=assists[run_me]()
- assistant.run()
- #sleep(.5)
- self.writefile(self.statusfile,'Running...')
+ self.postrun=['volumes_1','volumes_2'] #,'chrt']
class jalv_1(daemon):
def __init__(self):
super().__init__()
- self.arguments=['/usr/bin/chrt','84','/usr/bin/jalv.gtk','-l','/home/andrew/.eq_files/FrontSpeakers/','--jack-name','EQ Front']
+ self.arguments=['/usr/bin/jalv.gtk','-l','/home/%s/.eq_files/FrontSpeakers/'%getuser(),'--jack-name','EQ Front']
self.depends=['jackd']
- self.postrun=['xdotool_1']
+ self.postrun=['xdotool_1','chrt']
class jalv_2(daemon):
def __init__(self):
super().__init__()
- self.arguments=['/usr/bin/chrt','84','/usr/bin/jalv.gtk','-l','/home/andrew/.eq_files/AuxSpeakers/','--jack-name','EQ Centre']
+ self.arguments=['/usr/bin/jalv.gtk','-l','/home/%s/.eq_files/AuxSpeakers/'%getuser(),'--jack-name','EQ Centre']
self.depends=['jackd','jalv_1']
- self.postrun=['xdotool_2']
+ self.postrun=['xdotool_2','chrt']
class jalv_3(daemon):
def __init__(self):
super().__init__()
- self.arguments=['/usr/bin/chrt','84','/usr/bin/jalv.gtk','-l','/home/andrew/.eq_files/RearSpeakers/','--jack-name','EQ Rear']
+ self.arguments=['/usr/bin/jalv.gtk','-l','/home/%s/.eq_files/RearSpeakers/'%getuser(),'--jack-name','EQ Rear']
self.depends=['jackd','jalv_1','jalv_2']
- self.postrun=['xdotool_3']
+ self.postrun=['xdotool_3','chrt']
class jalv_4(daemon):
def __init__(self):
super().__init__()
- self.arguments=['/usr/bin/chrt','84','/usr/bin/jalv.gtk','-l','/home/andrew/.eq_files/Headphones/','--jack-name','EQ Headphones']
+ self.arguments=['/usr/bin/jalv.gtk','-l','/home/%s/.eq_files/Headphones/'%getuser(),'--jack-name','EQ Headphones']
self.depends=['jackd','jalv_1','jalv_2','jalv_3']
- self.postrun=['xdotool_4','xdotool_5','xdotool_6']
+ self.postrun=['xdotool_4','chrt']
+
+class jalv_5(daemon):
+ def __init__(self):
+ super().__init__()
+ self.arguments=['/usr/bin/jalv.gtk','-l','/home/%s/.eq_files/Microphone/'%getuser(),'--jack-name','EQ Capture']
+ self.depends=['jackd','jalv_1','jalv_2','jalv_3','jalv_4']
+ self.postrun=['xdotool_5','xdotool_6','xdotool_7','chrt']
class alsa_out_1(daemon):
def __init__(self):
super().__init__()
- self.arguments=['/usr/bin/chrt','84','/usr/bin/alsa_out','-j','HDMI_Audio_TV','-d','hw:0,11','-c','2']
+ self.arguments=['/usr/bin/alsa_out','-j','ALSA Speakers','-d','hw:1,0','-c','8']
self.depends=['jackd']
-
+ self.postrun=['chrt']
+
class alsa_out_2(daemon):
def __init__(self):
super().__init__()
- self.arguments=['/usr/bin/chrt','84','/usr/bin/alsa_out','-j','HDMI_Left_Monitor','-d','hw:0,7','-c','2']
+ self.arguments=['/usr/bin/alsa_out','-j','HDMI_Audio_TV','-d','hw:0,11','-c','2']
self.depends=['jackd']
-
+ self.postrun=['chrt']
+
class alsa_out_3(daemon):
def __init__(self):
super().__init__()
- self.arguments=['/usr/bin/chrt','84','/usr/bin/alsa_out','-j','HDMI_Right_Monitor','-d','hw:0,10','-c','2']
+ self.arguments=['/usr/bin/alsa_out','-j','HDMI_Left_Monitor','-d','hw:0,7','-c','2']
+ self.depends=['jackd']
+ self.postrun=['chrt']
+
+class alsa_out_4(daemon):
+ def __init__(self):
+ super().__init__()
+ self.arguments=['/usr/bin/alsa_out','-j','HDMI_Right_Monitor','-d','hw:0,10','-c','2']
+ self.depends=['jackd']
+ self.postrun=['chrt']
+
+class alsa_in_1(daemon):
+ def __init__(self):
+ super().__init__()
+ self.arguments=['/usr/bin/alsa_in','-j','ALSA Capture 1','-d','hw:1,0','-c','2']
self.depends=['jackd']
+ self.postrun=['chrt']
class kmix(daemon):
def __init__(self):
super().__init__()
+ self.hardclean=True
self.arguments=['/usr/bin/kmix']
- self.depends=['pulse']
+ self.depends=['pulseaudio']
self.postrun=['welcome']
class conky(daemon):
def __init__(self):
super().__init__()
+ self.hardclean=True
self.arguments=['/usr/bin/conky']
self.depends=['jalv_2']
daemons={
- 'jackd' :jackd ,
+ 'jackd' :jackd,
'qjackctl' :qjackctl,
- 'pulse' :pulse ,
- 'jalv_1' :jalv_1 ,
- 'jalv_2' :jalv_2 ,
- 'jalv_3' :jalv_3 ,
- 'jalv_4' :jalv_4 ,
- 'alsa_out_1' :alsa_out_1 ,
- 'alsa_out_2' :alsa_out_2 ,
- 'alsa_out_3' :alsa_out_3 ,
- 'kmix' :kmix ,
- 'conky' :conky ,
-}
+ 'pulseaudio' :pulseaudio,
+ 'jalv_1' :jalv_1,
+ 'jalv_2' :jalv_2,
+ 'jalv_3' :jalv_3,
+ 'jalv_4' :jalv_4,
+ 'jalv_5' :jalv_5,
+# 'alsa_out_1' :alsa_out_1,
+ 'alsa_out_2' :alsa_out_2,
+ 'alsa_out_3' :alsa_out_3,
+ 'alsa_out_4' :alsa_out_4,
+# 'alsa_in_1' :alsa_in_1,
+ 'kmix' :kmix,
+ 'conky' :conky,
+ }
+
class assist:
- def __init__(self):
+ def __init__(self,*arg):
self.args=[]
def run(self):
return_code='none'
f=open('/dev/null','w')
- while return_code != 0:
+ tries=10
+ while return_code != 0 and tries > 0 :
+ if tries < 10:
+ if gs.debug >2:print('%s didn\'t start, retrying'%' '.join(self.args))
+ sleep(.375)
child=Popen(self.args,stderr=f,stdout=f)
child.wait()
return_code=child.returncode
+ tries-=1
f.close()
return return_code
class xdotool_1(assist):
- def __init__(self):
+ def __init__(self,*arg):
self.args=['xdotool','search','--name','EQ4Q Stereo','set_window','--name','Front Speakers','windowmove','1920','0']
class xdotool_2(assist):
- def __init__(self):
+ def __init__(self,*arg):
self.args=['xdotool','search','--name','EQ4Q Stereo','set_window','--name','Aux Speakers','windowmove','2110','0']
class xdotool_3(assist):
- def __init__(self):
+ def __init__(self,*arg):
self.args=['xdotool','search','--name','EQ4Q Stereo','set_window','--name','Rear Speakers','windowmove','2300','0']
class xdotool_4(assist):
- def __init__(self):
+ def __init__(self,*arg):
self.args=['xdotool','search','--name','EQ10Q Stereo','set_window','--name','Headphones','windowmove','2324','0']
class xdotool_5(assist):
- def __init__(self):
- pass
+ def __init__(self,*arg):
+ self.args=['xdotool','search','--name','EQ10Q Stereo','set_window','--name','Microphone','windowmove','2514','0']
+class xdotool_6(assist):
def run(self):
- windows=['Headphones','Rear Speakers','Aux Speakers','Front Speakers']
+ windows=['Microphone','Headphones','Rear Speakers','Aux Speakers','Front Speakers']
for window in windows:
+ sleep(.125)
Popen(['xdotool','search','--name',window,'windowactivate'])
sleep(.125)
#Popen(['xdotool','search','--name',window,'windowminimize'])
-class xdotool_6(assist):
- def __init__(self):
+class xdotool_7(assist):
+ def __init__(self,*arg):
sleep(.5)
- self.args=['xdotool','set_desktop','0']
+ self.args=['xdotool','set_desktop',gs.curdt]
class volumes_1(assist):
- def __init__(self):
- self.args=['/usr/bin/pactl','set-sink-volume','0','35%']
+ def __init__(self,*arg):
+ self.args=['/usr/bin/pactl','set-sink-volume','0','45%']
class volumes_2(assist):
- def __init__(self):
+ def __init__(self,*arg):
self.args=['/usr/bin/pactl','set-sink-volume','2','100%']
class volumes_3(assist):
- def __init__(self):
- self.args=['/usr/sbin/alsactl','restore','-f','/home/andrew/.asound.state']
+ def __init__(self,*arg):
+ self.args=['/usr/sbin/alsactl','restore','-f','/home/%s/.asound.state'%getuser()]
class welcome(assist):
- def __init__(self):
+ def __init__(self,*arg):
self.args=['/usr/bin/ogg123','/usr/share/sounds/Oxygen-Sys-Log-In-Long.ogg']
-
-class DoAsYouAreFuckingToldPulseaudio(assist):
- def __init__(self,pid):
- self.args=['/usr/bin/chrt','--pid','75',str(pid)]
- def run(self):
- f=open('/dev/null','w')
- child=Popen(self.args, stdout=f,stderr=f)
- child.wait()
- f.close()
- return child.poll()
+class chrt(assist):
+ def __init__(self,*arg):
+ self.args=['/usr/bin/chrt','--pid',str(arg[0]),str(arg[1])]
-class tv_on(assist):# aka DoAsYouAreFuckingToldXorgServer
- def __init__(self):
- self.args=['/home/andrew/bin/TV-On']
+class tv_on(assist):
+ def __init__(self,*arg):
+ self.args=['/home/%s/bin/TV-On'%getuser()]
assists={
- 'xdotool_1' :xdotool_1 ,
- 'xdotool_2' :xdotool_2 ,
- 'xdotool_3' :xdotool_3 ,
- 'xdotool_4' :xdotool_4 ,
- 'xdotool_5' :xdotool_5 ,
- 'xdotool_6' :xdotool_6 ,
- 'volumes_1' :volumes_1 ,
- 'volumes_2' :volumes_2 ,
- 'volumes_3' :volumes_3 ,
- 'welcome' :welcome ,
- 'chrt' :DoAsYouAreFuckingToldPulseaudio,
- 'tv_on' :tv_on,
+ 'xdotool_1' :xdotool_1,
+ 'xdotool_2' :xdotool_2,
+ 'xdotool_3' :xdotool_3,
+ 'xdotool_4' :xdotool_4,
+ 'xdotool_5' :xdotool_5,
+ 'xdotool_6' :xdotool_6,
+ 'xdotool_7' :xdotool_7,
+ 'volumes_1' :volumes_1,
+ 'volumes_2' :volumes_2,
+ 'volumes_3' :volumes_3,
+ 'welcome' :welcome,
+ 'chrt' :chrt,
+ 'tv_on' :tv_on,
}
def start():
except:
print('Error: Couldn\'t stop %s!' %stop_me)
+def statussingle(how_am_i):
+ current=daemons[how_am_i]()
+ try:
+ print('%s status: %s'%(how_am_i,current.readfile(current.statusfile)))
+ except:
+ print('%s status: Unknown'%how_am_i)
+
def stop():
for stop_me in daemons:
try:
pidfile=open(current.pidfile,'r')
pid=pidfile.read()
pidfile.close()
- Popen(['kill','-SIGUSR1',pid])
+ os.kill(int(pid),signal.SIGUSR1)
except FileNotFoundError:
pass
init()
print('%s status: %s'%(current.__class__.__name__,status))
def usage():
- print('Usage: %s [start|stop|status|init]'%sys.argv[0])
+ print('Usage: %s <module name> [start|stop|status|init|restart]'%sys.argv[0])
sys.exit(1)
-
+
def advusage():
- print('Advanced Usage: %s module [module name] [start|stop]'%sys.argv[0])
+ print('Advanced Usage: %s [module name] [start|stop|status|restart]'%sys.argv[0])
sys.exit(1)
def init():
pass
current.writefile(current.statusfile,'Stopped.')
+def killall():
+ stop()
+ for kill_me in daemons:
+ current=daemons[kill_me]()
+ current.hardclean=True
+ current.killcheck()
+
+gs.curdt=run(['xdotool','get_desktop'],capture_output=True,encoding='UTF-8').stdout[0]
try:
arg=sys.argv[1]
except IndexError:
usage()
-try:
- module=sys.argv[2]
-except IndexError:
- pass
-
-try:
- action=sys.argv[3]
-except IndexError:
- action='start'
+if arg in daemons:
+ module=arg
+ arg='module'
+ try:
+ action=sys.argv[2]
+ except IndexError:
+ advusage()
if arg=='help':
usage()
stopsingle(module)
sleep(5)
startsingle(module)
+ elif action=='status':
+ statussingle(module)
else:
advusage()
elif arg=='stop':
start()
elif arg=='init':
init()
+elif arg=='killall':
+ killall()
else:
usage()