1
"""Native python ECI (ecasound control interface) implementation
3
Can be used to replace the C implementation 'pyecasound.so'.
6
# Version: $Id: ecacontrol.py,v 1.8 2003/11/26 19:33:55 kaiv Exp $
8
authors="""Kai Vehmanen, Eric S. Tiedemann and Janne Halttunen."""
11
from popen2 import Popen3
12
from select import select
21
eci_str_sync_lost= 'Connection to the processing engine was lost.\n'
23
class ECA_CONTROL_INTERFACE:
25
def __init__(I, verbose=1):
26
"""Instantiate new ECI session
28
verbose: set this false to get rid of startup-messages
34
I._timeout=5 # in seconds
39
def __call__(I, cmd, f=None):
41
val=I.command_float_arg(cmd, f)
43
cmds=string.split(cmd, '\n')
49
v.append(I.command(c))
52
raise Exception(v[-1])
54
val=string.join(map(str, v), '\n')
64
return string.strip(I.eca.fromchild.readline())
68
while select([I.eca.fromchild.fileno()],[],[I.eca.fromchild.fileno()],0.01)[0]:
69
buffer=buffer+I.eca.fromchild.read(1)
72
def _parse_response(I):
73
tm=''; r=(); failcount=0
84
failcount = failcount + 1
85
if failcount < I._timeout * 10:
90
print 'timeout: s=<' + s, '>, cmd=' + I._cmd + '.'
91
r=('e', eci_str_sync_lost)
94
m=expand_eiam_response(tm)
95
r=parse_eiam_response(tm, m)
108
if I._cmd in type_override.keys():
109
I._type=type_override[I._cmd]
112
I._resp[I._type]=string.split(r[1], ',')
113
elif I._type == 'Sn':
114
I._resp[I._type]=string.split(r[1], '\n')
116
I._resp[I._type]=float(r[1])
118
I._resp[I._type]=int(r[1])
119
elif I._type == 'li':
120
I._resp[I._type]=long(r[1])
122
I._resp[I._type]=r[1]
124
return I._resp[I._type]
128
"""Reserve resources"""
130
## if _ecasound is not None:
131
## I.cleanup() # exit previous ecasound session cleanly
136
ecasound_binary = os.environ['ECASOUND']
140
if ecasound_binary == '':
141
ecasound_binary = 'ecasound'
143
_ecasound.append(Popen3(ecasound_binary + ' -c -d:256 2>/dev/null', 0, 0))
149
lines=lines+I._readline()+'\n'
151
version=I._readline()
153
s=string.find(version, 'ecasound v')
154
if float(version[s+10:s+13])>=2.2:
155
lines=lines+version+'\n'
157
raise RuntimeError('ecasound version 2.2 required!')
159
lines=lines+I._readline()+'\n'
165
print '\n(to get rid of this message, pass zero to instance init)'
167
I.command('int-output-mode-wellformed')
169
#I.command('debug 256')
172
"""Free all reserved resources"""
174
I.eca.tochild.write('quit\n')
176
os.kill(I.eca.pid, signal.SIGTERM)
178
signal.signal(signal.SIGALRM, handler)
187
os.kill(I.eca.pid, signal.SIGKILL)
191
"""Issue an EIAM command"""
193
cmd=string.strip(cmd)
196
I.eca.tochild.write(cmd+'\n')
197
return I._parse_response()
199
def command_float_arg(I,cmd,f=None):
200
"""Issue an EIAM command
202
This function can be used instead of command(string),
203
if the command in question requires exactly one numerical parameter."""
205
cmd=string.strip(cmd)
209
I.eca.tochild.write('%s %f\n' % (cmd,f))
211
I.eca.tochild.write(cmd+'\n')
212
return I._parse_response()
215
"""Return true if error has occured during the execution of last EIAM command"""
217
if I._type=='e': return 1
220
"""Return a string describing the last error"""
223
return I._resp.get('e')
228
"""Return the last floating-point return value"""
229
return I._resp.get('f')
232
"""Return the last integer return value
234
This function is also used to return boolean values."""
235
return I._resp.get('i')
237
def last_long_integer(I):
238
"""Return the last long integer return value
240
Long integers are used to pass values like 'length_in_samples'
241
and 'length_in_bytes'. It's implementation specific whether there's
242
any real difference between integers and long integers."""
243
return I._resp.get('li')
246
"""Return the last string return value"""
247
return I._resp.get('s')
249
def last_string_list(I):
250
"""Return the last collection of strings (one or more strings)"""
251
return I._resp.get('S')
256
def current_event(I):
257
"""** not implemented **"""
259
def events_available(I):
260
"""** not implemented **"""
263
"""** not implemented **"""
269
raise Exception, 'killing me not so softly'
272
expand=re.compile('256 ([0-9]{1,5}) (.+)\r\n(.*)\r\n\r\n.*', re.MULTILINE | re.S)
274
def expand_eiam_response(st):
275
"""Checks wheter 'str' is a valid EIAM response.
277
@return Regex match object.
280
m = expand.search(st)
283
parse=re.compile('256 ([0-9]{1,5}) (.+)\r\n(.*)', re.MULTILINE | re.S)
285
def parse_eiam_response(st, m=None):
286
"""Parses a valid EIAM response.
288
@param m Valid regex match object.
289
@param str The whole EIAM response.
291
@return tuple of return value type and value
299
if m and len(m.groups()) == 0:
300
#print "(pyeca) Matching groups failed: %s" % str(m.groups())
301
return ('e','Matching groups failed')
303
if m and len(m.groups()) == 3:
304
#print 'received=', len(m.group(3)), ', expected=', m.group(1)
305
if int(m.group(1)) != len(m.group(3)):
306
print '(pyeca) Response length error. Received ', len(m.group(3)), ', expected for ', m.group(1), '.'
307
#print 'g=', m.group(3)
308
return ('e', 'Response length error.')
311
return (m.group(2), m.group(3))
317
def __init__(I, eci, cmd):
319
I.cmd=string.replace(cmd, '_', '-')
324
class string_argument(base):
326
return I.eci('%s %s' % (I.cmd,s))
330
def __init__(I, verbose=0):
331
I._eci=ECA_CONTROL_INTERFACE(verbose)
332
I._cmds=I._eci('int-cmd-list')
335
c=string.replace(c, '-', '_')
336
if string.count(c, 'add') \
337
or string.count(c, 'select'):
338
I.__dict__[c]=string_argument(I._eci,c)
340
I.__dict__[c]=base(I._eci,c)
343
e=ECA_CONTROL_INTERFACE()
344
print e.command('c-add huppaa')
345
print e.command('c-list')
355
if __name__ == '__main__':