#!/usr/bin/env python """Jack Module Player v0.1b""" author='Janne Halttunen' author_email='jhalttun@pp.htv.fi' requires="""Extended Module Player: http://xmp.sourceforge.net/ Ecasound: http://eca.cx/ """ import os, sys, signal, glob from popen2 import Popen4 def get_channels(mfile, verbose=0): xmpin,xmp=os.popen4('xmp %s --load-only -c' % mfile, 'r', 0) lines=xmp.readlines() xmp.close() xmpin.close() for l in lines: if verbose: print l.strip() if l[:8]=='Channels': splist=filter(None, l.split(' ')) return int(splist[2]) def get_jack_ports(): jin,jack=os.popen4('jack_lsp', 'r', 0) lines=jack.readlines() jack.close() jin.close() ports=[] for l in lines: ports.append(l.strip()) return ports usage=""" usage: jmp.py [options] module-file(s)""" help="""%s jmp.py options: -b, --buffer set buffersize -r, --rate set sample rate -c, --channels override module's number of channels -N, --names name individual channels (or channels sticked) with comma separated list of names e.g. bassdrum,hihat,snare -S, --stick stick individual channels together with colon separated list of channel-numbers e.g. 0-3,5:6,7:8 (channel-numbering starts at 0) -l, --looping loop module -n, --noconnect don't connect jack-ports to alsa-playback -q, --quiet -v, --verbose """ % usage def main(): args={} next='' args['rate']='48000' args['buffer']='1024' args['looping']='' args['file']=[] for a in sys.argv[1:]: if a in ('-b','--buffer'): next='buffer' elif a in ('-r', '--rate'): next='rate' elif a in ('-c', '--channels'): next='channels' elif a in ('-N', '--names'): next='names' elif a in ('-S', '--stick'): next='sticks' elif a in ('-n', '--noconnect'): args['noconnect']=1 elif a in ('-l', '--looping'): args['looping']='-l' elif a in ('-q', '--quiet'): args['quiet']=1 elif a in ('-v', '--verbose'): args['verbose']=1 elif next: args[next]=a elif a in ('-h','--help'): print __doc__ print help sys.exit() else: args['file']+=glob.glob(a) if not args.get('quiet'): print __doc__ if not args.get('file'): print usage sys.exit() if args.get('channels'): ch=int(args['channels']) else: ch=0 for f in args['file']: c=get_channels(f, args.get('verbose')) if c>ch: ch=c if args.get('names'): args['name']=args['names'].split(',') else: args['name']=[] args['name']+= ['xmp']*(ch-len(args['name'])) if args.get('sticks'): args['stick']=args['sticks'].split(':') else: args['stick']=[] if args['stick']: if not args['stick'][-1]: args['stick']=args['stick'][:-1] for i in range(int(args['stick'][-1][-1]), ch-1): args['stick'].append(str(i+1)) else: for i in range(ch): args['stick'].append(str(i)) ch=len(args['stick']) if ch: print 'channels: %d' % ch if not args.get('looping'): print 'not looping..' for i in range(ch): if not os.path.exists('/tmp/xmp%d.raw' % (i+1)): os.mkfifo('/tmp/xmp%d.raw' % (i+1)) xmp=[] for i in range(ch): xmp.append(Popen4('xmp %s -m %s -f%s -q --solo %s -o /tmp/xmp%d.raw &' % (' '.join(args['file']), args['looping'], args['rate'], args['stick'][i], (i+1)), 0 )) # os.popen('xmp %s -m %s -f%s -q --solo %s -o /tmp/xmp%d.raw 2>&1 &' % (args['file'], args['looping'], args['rate'], args['stick'][i], (i+1)), 'r' ) eline='ecasound -b %s -f s16_le,1,%s ' % (args['buffer'], args['rate']) for i in range(ch): eline+='-a x%d -i /tmp/xmp%d.raw -o jack_generic,%s ' % (i+1, i+1, args['name'][i]) eline+='2>&1 -c' eca=Popen4(eline, 0) eca.tochild.write('cs-connect\n') while 1: try: line=eca.fromchild.readline().strip() if not args.get('quiet') \ and args.get('verbose') \ and line.strip(): print line if line.count('Connected chainsetup'): eca.tochild.write('start\n') if not args.get('noconnect'): ports=get_jack_ports() if not args.get('quiet'): print 'connecting ports' for p in ports: if p.count('ecasound') \ and filter(p.count, args['name']): if not args.get('quiet'): print 'connecting %s to playback' % p os.popen('jack_connect %s alsa_pcm:playback_1' % p, 'r') os.popen('jack_connect %s alsa_pcm:playback_2' % p, 'r') if not args.get('quiet'): print 'done.' args['noconnect']=1 elif line.count('DBC_REQUIRE failed') \ or line.count('engine has shut down') \ or line.count('engine unexpectedly shutdown'): if not args.get('quiet'): print 'exiting..' os.kill(eca.pid, signal.SIGTERM) os.popen4('killall xmp') break elif line.count('Unable to open JACK-client'): if not args.get('quiet'): print 'cannot connect jack, exiting..' os.kill(eca.pid, signal.SIGTERM) os.popen4('killall xmp') break except: if not args.get('quiet'): print 'break' os.kill(eca.pid, signal.SIGTERM) os.popen4('killall xmp') raise else: raise RuntimeError, 'No channels acquired!' if __name__=='__main__': try: main() except KeyboardInterrupt: pass except: print '\nNOTE! external programs required:\n' print requires raise