~ubuntu-branches/ubuntu/feisty/ecasound2.2/feisty

« back to all changes in this revision

Viewing changes to pyecasound/ecacontrol.py

  • Committer: Bazaar Package Importer
  • Author(s): Junichi Uekawa
  • Date: 2005-04-14 09:15:48 UTC
  • Revision ID: james.westby@ubuntu.com-20050414091548-o7kgb47z0tcunh0s
Tags: upstream-2.4.1
ImportĀ upstreamĀ versionĀ 2.4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Native python ECI (ecasound control interface) implementation
 
2
 
 
3
   Can be used to replace the C implementation 'pyecasound.so'.
 
4
"""
 
5
 
 
6
# Version: $Id: ecacontrol.py,v 1.8 2003/11/26 19:33:55 kaiv Exp $
 
7
 
 
8
authors="""Kai Vehmanen, Eric S. Tiedemann and Janne Halttunen."""
 
9
 
 
10
import re
 
11
from popen2 import Popen3
 
12
from select import select
 
13
import os
 
14
import signal
 
15
import string
 
16
import time
 
17
 
 
18
_ecasound=[]
 
19
 
 
20
type_override={}
 
21
eci_str_sync_lost= 'Connection to the processing engine was lost.\n'
 
22
 
 
23
class ECA_CONTROL_INTERFACE:
 
24
    
 
25
    def __init__(I, verbose=1):
 
26
        """Instantiate new ECI session
 
27
        
 
28
        verbose: set this false to get rid of startup-messages
 
29
        """
 
30
        I.verbose=verbose
 
31
        
 
32
        I._cmd=''
 
33
        I._type=''
 
34
        I._timeout=5 # in seconds
 
35
        I._resp={}
 
36
        I.initialize()
 
37
        
 
38
        
 
39
    def __call__(I, cmd, f=None):
 
40
        if f != None:
 
41
            val=I.command_float_arg(cmd, f)
 
42
        else:
 
43
            cmds=string.split(cmd, '\n')
 
44
            if len(cmds) > 1:
 
45
                v=[]
 
46
                for c in cmds:
 
47
                    c=string.strip(c)
 
48
                    if c:
 
49
                        v.append(I.command(c))
 
50
                    
 
51
                        if I.error():
 
52
                            raise Exception(v[-1])
 
53
                    
 
54
                val=string.join(map(str, v), '\n')
 
55
            else:
 
56
                val=I.command(cmd)
 
57
                    
 
58
        if I.error():
 
59
            raise Exception(val)
 
60
        
 
61
        return val          
 
62
 
 
63
    def _readline(I):
 
64
        return string.strip(I.eca.fromchild.readline())
 
65
        
 
66
    def _read_eca(I):
 
67
        buffer=''
 
68
        while select([I.eca.fromchild.fileno()],[],[I.eca.fromchild.fileno()],0.01)[0]:
 
69
           buffer=buffer+I.eca.fromchild.read(1)
 
70
        return buffer
 
71
    
 
72
    def _parse_response(I):
 
73
        tm=''; r=(); failcount=0
 
74
        if I.verbose > 2:
 
75
            print 'c=' + I._cmd
 
76
        while 1:
 
77
            
 
78
            s=I._read_eca()
 
79
            #print 'read s=' + s
 
80
            if s:
 
81
                if I.verbose > 3:
 
82
                    print 's=<', s, '>'
 
83
            else:
 
84
                failcount = failcount + 1
 
85
                if failcount < I._timeout * 10:
 
86
                #if failcount < 0:
 
87
                    time.sleep(0.01)
 
88
                    continue
 
89
                else:
 
90
                    print 'timeout: s=<' + s, '>, cmd=' + I._cmd + '.'
 
91
                    r=('e', eci_str_sync_lost)
 
92
                    break
 
93
            tm=tm+s
 
94
            m=expand_eiam_response(tm)
 
95
            r=parse_eiam_response(tm, m)
 
96
            if r:
 
97
                if I.verbose > 2:
 
98
                    print 'r=', r
 
99
                break
 
100
 
 
101
        if not r:
 
102
            I._resp['e']='-'        
 
103
            I._type='e'
 
104
            r=None
 
105
        else:
 
106
            I._type=r[0]
 
107
            
 
108
            if I._cmd in type_override.keys():
 
109
                I._type=type_override[I._cmd]
 
110
            
 
111
            if I._type == 'S':
 
112
                I._resp[I._type]=string.split(r[1], ',')            
 
113
            elif I._type == 'Sn':
 
114
                I._resp[I._type]=string.split(r[1], '\n')
 
115
            elif I._type == 'f':
 
116
                I._resp[I._type]=float(r[1])
 
117
            elif I._type == 'i':
 
118
                I._resp[I._type]=int(r[1])
 
119
            elif I._type == 'li':
 
120
                I._resp[I._type]=long(r[1])
 
121
            else:
 
122
                I._resp[I._type]=r[1]
 
123
 
 
124
        return I._resp[I._type]
 
125
 
 
126
    
 
127
    def initialize(I):
 
128
        """Reserve resources"""
 
129
                
 
130
##      if _ecasound is not None:
 
131
##          I.cleanup()             # exit previous ecasound session cleanly
 
132
           
 
133
        global _ecasound
 
134
 
 
135
        try:
 
136
            ecasound_binary = os.environ['ECASOUND']
 
137
        except KeyError:
 
138
            ecasound_binary = ''
 
139
 
 
140
        if ecasound_binary == '':
 
141
            ecasound_binary = 'ecasound'
 
142
 
 
143
        _ecasound.append(Popen3(ecasound_binary + ' -c -d:256 2>/dev/null', 0, 0))
 
144
        
 
145
        I.eca=_ecasound[-1]
 
146
        
 
147
        lines=''
 
148
        
 
149
        lines=lines+I._readline()+'\n'
 
150
 
 
151
        version=I._readline()
 
152
            
 
153
        s=string.find(version, 'ecasound v')
 
154
        if float(version[s+10:s+13])>=2.2:
 
155
            lines=lines+version+'\n'
 
156
        else:
 
157
            raise RuntimeError('ecasound version 2.2 required!')
 
158
        
 
159
        lines=lines+I._readline()+'\n'
 
160
        
 
161
        if I.verbose:
 
162
            print lines
 
163
            print __doc__
 
164
            print 'by', authors
 
165
            print '\n(to get rid of this message, pass zero to instance init)'
 
166
            
 
167
        I.command('int-output-mode-wellformed')
 
168
        #I._read_eca()
 
169
        #I.command('debug 256')
 
170
        
 
171
    def cleanup(I):
 
172
        """Free all reserved resources"""
 
173
        
 
174
        I.eca.tochild.write('quit\n')
 
175
 
 
176
        os.kill(I.eca.pid, signal.SIGTERM)
 
177
                
 
178
        signal.signal(signal.SIGALRM, handler)
 
179
        signal.alarm(2)
 
180
        
 
181
        try:
 
182
            return I.eca.wait()
 
183
        except:
 
184
            pass
 
185
        
 
186
        signal.alarm(0)
 
187
        os.kill(I.eca.pid, signal.SIGKILL)
 
188
        
 
189
        
 
190
    def command(I,cmd):
 
191
        """Issue an EIAM command"""
 
192
        
 
193
        cmd=string.strip(cmd)
 
194
        if cmd:
 
195
            I._cmd=cmd
 
196
            I.eca.tochild.write(cmd+'\n')
 
197
            return I._parse_response()
 
198
        
 
199
    def command_float_arg(I,cmd,f=None):
 
200
        """Issue an EIAM command
 
201
        
 
202
        This function can be used instead of command(string), 
 
203
        if the command in question requires exactly one numerical parameter."""
 
204
        
 
205
        cmd=string.strip(cmd)
 
206
        if cmd:
 
207
            I._cmd=cmd
 
208
            if f:
 
209
                I.eca.tochild.write('%s %f\n' % (cmd,f))
 
210
            else:
 
211
                I.eca.tochild.write(cmd+'\n')
 
212
            return I._parse_response()
 
213
            
 
214
    def error(I):
 
215
        """Return true if error has occured during the execution of last EIAM command"""
 
216
        
 
217
        if I._type=='e': return 1
 
218
        
 
219
    def last_error(I):
 
220
        """Return a string describing the last error"""
 
221
        
 
222
        if I.error():
 
223
            return I._resp.get('e')
 
224
        else: 
 
225
            return ''
 
226
        
 
227
    def last_float(I):
 
228
        """Return the last floating-point return value"""
 
229
        return I._resp.get('f')
 
230
    
 
231
    def last_integer(I):
 
232
        """Return the last integer return value
 
233
        
 
234
        This function is also used to return boolean values."""
 
235
        return I._resp.get('i')
 
236
    
 
237
    def last_long_integer(I):
 
238
        """Return the last long integer return value
 
239
        
 
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')
 
244
    
 
245
    def last_string(I):
 
246
        """Return the last string return value"""
 
247
        return I._resp.get('s')
 
248
    
 
249
    def last_string_list(I):
 
250
        """Return the last collection of strings (one or more strings)"""
 
251
        return I._resp.get('S')
 
252
    
 
253
    def last_type(I):
 
254
        return I._type
 
255
    
 
256
    def current_event(I):
 
257
        """** not implemented **"""
 
258
        pass
 
259
    def events_available(I): 
 
260
        """** not implemented **"""
 
261
        pass
 
262
    def next_event(I): 
 
263
        """** not implemented **"""
 
264
        pass
 
265
 
 
266
 
 
267
def handler(*args):
 
268
    print 'AARGH!'
 
269
    raise Exception, 'killing me not so softly'
 
270
 
 
271
    
 
272
expand=re.compile('256 ([0-9]{1,5}) (.+)\r\n(.*)\r\n\r\n.*', re.MULTILINE | re.S)
 
273
 
 
274
def expand_eiam_response(st):
 
275
    """Checks wheter 'str' is a valid EIAM response.
 
276
 
 
277
    @return Regex match object.
 
278
    """
 
279
 
 
280
    m = expand.search(st)
 
281
    return m
 
282
 
 
283
parse=re.compile('256 ([0-9]{1,5}) (.+)\r\n(.*)', re.MULTILINE | re.S)
 
284
 
 
285
def parse_eiam_response(st, m=None):
 
286
    """Parses a valid EIAM response.
 
287
 
 
288
    @param m Valid regex match object.
 
289
    @param str The whole EIAM response.
 
290
 
 
291
    @return tuple of return value type and value
 
292
    """
 
293
 
 
294
    if not m:
 
295
        m = parse.search(st)
 
296
        if not m:
 
297
            return ()
 
298
 
 
299
    if m and len(m.groups()) == 0:
 
300
        #print "(pyeca) Matching groups failed: %s" % str(m.groups())
 
301
        return ('e','Matching groups failed')
 
302
    
 
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.')
 
309
            
 
310
    if m:
 
311
        return (m.group(2), m.group(3))
 
312
 
 
313
    return ('e','')
 
314
 
 
315
 
 
316
class base:
 
317
    def __init__(I, eci, cmd):
 
318
        I.eci=eci
 
319
        I.cmd=string.replace(cmd, '_', '-')
 
320
        
 
321
    def __call__(I):
 
322
        return I.eci(I.cmd)
 
323
 
 
324
class string_argument(base):
 
325
    def __call__(I, s):
 
326
        return I.eci('%s %s' % (I.cmd,s))
 
327
        
 
328
 
 
329
class EIAM:
 
330
    def __init__(I, verbose=0):
 
331
        I._eci=ECA_CONTROL_INTERFACE(verbose)
 
332
        I._cmds=I._eci('int-cmd-list')
 
333
        
 
334
        for c in I._cmds:
 
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)
 
339
            else:
 
340
                I.__dict__[c]=base(I._eci,c)
 
341
 
 
342
def main():
 
343
    e=ECA_CONTROL_INTERFACE()
 
344
    print e.command('c-add huppaa')
 
345
    print e.command('c-list')
 
346
    
 
347
    print e("""
 
348
    
 
349
    c-list
 
350
    c-status
 
351
    """)
 
352
 
 
353
    print e.cleanup()
 
354
 
 
355
if __name__ == '__main__':
 
356
    main()