Audio/AudioControl: Lots of fixes
[audiocontrol.git] / Joyboard.py
1 import Views, Events, Logger, Bindings, Actions
2 from Views import EventWidget
3 import time, os, struct, curses
4
5 class View(Views.WidgetView):
6
7     def __init__(self, context, label, numeric_switches, alpha_switches, x, y, dx=0, size=11):
8         size += 1
9         dx = max(dx,size*numeric_switches+3)
10         delta = dx - size*numeric_switches - 3
11         shift = delta / 2
12         delta = dx - size*numeric_switches - 2*shift
13         Views.WidgetView.__init__(self, context, label, x, y, dx, 7)
14
15         split = numeric_switches // 2
16         for i in range(split):
17             self.add( EventWidget(i,str(i+1)[-1:],size*i+1+shift,4,size) )
18         for i in range(split,numeric_switches):
19             self.add( EventWidget(i,str(i+1)[-1:],size*i+shift+delta,4,size) )
20         split = max(0,alpha_switches-(numeric_switches-split))
21         offset = size//2+(numeric_switches-alpha_switches-1)*size
22         for i in range(split):
23             self.add( EventWidget(i+numeric_switches, chr(ord('A')+i), size*i+1+offset+shift,1,size) )
24         for i in range(split, alpha_switches):
25             self.add( EventWidget(i+numeric_switches, chr(ord('A')+i), size*i+delta+offset+shift,1,size) )
26
27
28 class JSEvent:
29
30     STRUCT = "IhBB"
31     STRUCT_len = struct.calcsize(STRUCT)
32
33     TYPE_BUTTON = 0x01
34     TYPE_AXIS = 0x02
35     TYPE_INIT = 0x80
36
37     def __init__(self, data):
38         self.time, self.value, self.type, self.number = struct.unpack(self.STRUCT,data)
39
40     def __str__(self):
41         return "%s(time=%d, value=%d,type=%d (%s)), number=%d)" % (
42             str(self.__class__),self.time, self.value, self.type, self.decode_type(), self.number)
43
44     def decode_type(self):
45         return ('','INIT|')[(self.type & self.TYPE_INIT) and 1 or 0] \
46                + ('','BUTTON','AXIS')[self.type & ~self.TYPE_INIT]
47
48     def readFrom(klass,f):
49         return klass(os.read(f.fileno(),klass.STRUCT_len))
50     readFrom=classmethod(readFrom)
51
52     def readMultipleFrom(klass,f,maxevents=256):
53         data = os.read(f.fileno(),maxevents*klass.STRUCT_len)
54         rv = []
55         while data:
56             rv.append(klass(data[:klass.STRUCT_len]))
57             data = data[klass.STRUCT_len:]
58         return rv
59     readMultipleFrom=classmethod(readMultipleFrom)
60
61
62 class Source(Events.EventSource):
63
64     def __init__(self, joydev, context, bits=None, mindelay=100):
65         Events.EventSource.__init__(self, file(joydev), context)
66         self._bits = bits
67         self._lastevent = 0
68         self._mindelay = mindelay
69         self._controllers = {}
70
71     def readEvents(self):
72         n = 0
73         lev = self._lastevent
74         if self._bits:
75             time.sleep(0.0005)
76             jsevents = JSEvent.readMultipleFrom(self.fd())
77             for event in jsevents:
78                 if event.type == JSEvent.TYPE_AXIS:
79                     return self._controller(event.number, event.value)
80                 if event.type == JSEvent.TYPE_BUTTON and event.value == 1:
81                     self._lastevent = event.time
82                     if event.time - lev < self._mindelay : return []
83                     n = n | self._bits[event.number]
84             if n == 0 : return []
85             n -= 1
86         else:
87             event = JSEvent.readFrom(self.fd())
88             if event.type == JSEvent.TYPE_AXIS:
89                 return self._controller(event.number, event.value)
90             if event.type == JSEvent.TYPE_BUTTON and event.value == 1:
91                 self._lastevent = event.time
92                 if event.time - lev < self._mindelay : return []
93                 n = event.number
94             else:
95                 return []
96         return [Events.Event(self.context(), n)]
97
98     def registerController(self, controller, low, high):
99         self._controllers[controller] = { 'low': low, 'high': high }
100
101     def _controller(self, number, value):
102         controller = self._controllers.get(number)
103         if controller:
104             value = (value - controller['low']) * 1000 / (controller['high']-controller['low'])
105             value = max(min(value,1000),0)
106             return [Events.ControlEvent(self.context(), 'p%d' % number, value)]
107         return []
108
109 # New Controller state machine:
110 #
111 # First, we work in two steps: The controller gets an internal
112 # controller value which is an integer between 0 and 100. All
113 # operations change this value which is then converted to the output
114 # value.
115 #
116 # The controller position is managed as a signed integer between -2
117 # and 2.
118 #
119 # The current state is managed in a state variable. There are the
120 # following states:
121 #
122 #   INIT    In this state, the controller is waiting for the initial
123 #           parameter value.
124 #
125 #   RUNNING The value is currently being changed. The speed and
126 #           direction are given by the controller position. If the
127 #           current controller position changes to 0 or to the
128 #           opposite sign, the state is switched to WAIT.
129 #
130 #   WAIT    The state machine waits for the controller to enter the
131 #           center position. Only when the controller has been in the
132 #           center position for some time, it will enter the IDLE
133 #           state. It may however go to RUNNING, if the controller
134 #           position is again changed to have the same sign t had
135 #           before entering WAIT. On entering this state, the
136 #           controller position is checked. If the position is 0 now
137 #           or if it is changed to become 0, a timeout is started. If
138 #           the controller is not changed within this timeout, we
139 #           enter IDLE. Any change of the controlle value terminates
140 #           thiw timeout.
141 #
142 #   SWAIT   Wait at a stop. The controller will stay in this state
143 #           until the controller position is changed to 0 or the opposite
144 #           sign. It then enters the WAIT state.
145 #
146 #   IDLE    In this state, nothing happens, however, if the controller
147 #           position changes from 0, the state machine enters the
148 #           running state.
149 #
150 # Events are:
151 #
152 #   ev_controllerChanged  The controller position changes
153 #
154 #   ev_timeout            The timeout handle got called
155 #
156 # The stop positions are handled by converting them to appropriate
157 # integer values. When such a value is reached or crossed, the value
158 # is set to the stop value and the state machine enters the SWAIT
159 # state.
160 #
161 # When converting the integer value to the return float value, we
162 # first check, wether the integer value is one of the stop values. In
163 # this case, the associated float value is returned otherweise ahe
164 # integer is scaled to the correct float range.
165
166 class Controller(Views.View):
167
168     ########################################
169     # external API
170
171     def __init__(self, context, name, x, y, dx, dy, keylist, dispatcher,
172                  controller, source, low, high):
173         Views.View.__init__(self, context, name, x, y, dx, dy)
174
175         # External value interface
176         self._valueEvent = None
177         self._setCommand = None
178         self._valueCommand = None
179
180         # Parameter information
181         self._parameter = None
182         self._min = None
183         self._max = None
184         self._stops = None
185         self._rawStops = None
186         self._steps = 40
187
188         # Controller keymap stuff
189         self._keylist = keylist
190         self._keymap = Bindings.KeyMap()
191         if source:
192             self._keymap.add( Bindings.Binding(Events.Event(source.context(), 'p%d' % controller), '',
193                                                Actions.Command('controllerChanged',
194                                                                self._controllerChanged)) )
195         self._keylist.prepend(self._keymap)
196         if source:
197             source.registerController(controller, low, high)
198
199         # Working variables
200         self._dispatcher = dispatcher
201         self._value = None
202         self._rawValue = None
203         self._controlValue = None
204         self._controlSign = None
205         self._state = None
206
207     def assign(self, parameter,  setCommand, valueCommand, valueEvent, min, max, stops=[]):
208         self._keylist.removeMap(self._keymap)
209         if self._valueEvent is not None:
210             self._keymap.unbind(self._valueEvent)
211         self._parameter = parameter
212         self._valueEvent = valueEvent
213         self._setCommand = setCommand
214         self._valueCommand = valueCommand
215         self._min = min
216         self._max = max
217         self._stops = stops
218         self._rawStops = [ self._rawFromCooked(x) for x in self._stops ]
219         if self._valueEvent:
220             self._keymap.add( Bindings.Binding( self._valueEvent, '',
221                                                 Actions.Command('updateValue',
222                                                                 self._updateValue)) )
223         self._keylist.prepend(self._keymap)
224         self._value = None
225         self._rawValue = 0
226         self._controlValue = None
227         self._controlSign = None
228         self._state = self.S_INIT
229         self._redraw()
230         self._getValue()
231
232     def stepValue(self, direction):
233         Logger.log('ctl','direction = %d' % direction)
234         self._rawValue += max(-1,min(1,direction))
235         if self._rawValue > self._steps:
236             self._rawValue = self._steps
237         elif self._rawValue < 0:
238             self._rawValue = 0
239
240         Logger.log('ctl','rawValue = %d' % self._rawValue)
241         self._setValue(self._cookedFromRaw(self._rawValue))
242
243         return self._rawValue in self._rawStops
244
245     ########################################
246     # Base class interface implementation
247
248     def updateView(self, bindings):
249         pass
250
251     def init(self):
252         Views.View.init(self)
253         self._redraw()
254
255     ########################################
256     # External event callbacks
257
258     def _updateValue(self, binding):
259         # Called to return the value after executing the valueCommand
260         # (when valueCommand initiates an async value get like via
261         # osc)
262         event = self._dispatcher.currentEvent()
263         self.__updateValue(event.value)
264         self._updateWindow()
265
266     def __updateValue(self, value):
267         self._value = value
268         if self._state == self.S_INIT:
269             self._rawValue = self._rawFromCooked(self._value)
270             self._setState(self.S_SWAIT)
271         self._redrawValue()
272         self._redrawState()
273         self._updateWindow()
274
275     def _controllerChanged(self, binding):
276         # Called, whenever the controller position changes
277         event = self._dispatcher.currentEvent()
278         if event.value >= 999:
279             newControlValue = 2
280         elif event.value >= 700:
281             newControlValue = 1
282         elif event.value >= 200:
283             newControlValue = 0
284         elif event.value >= 1:
285             newControlValue = -1
286         else:
287             newControlValue = -2
288         Logger.log('ctl',"controlValue = %d" % newControlValue)
289         if self._controlValue is None or self._controlValue != newControlValue:
290             self._controlValue = newControlValue
291             self._ev_controllerChanged()
292
293     ########################################
294     # Internal API
295
296     def _setValue(self, value):
297         self._setCommand(value)
298         self._getValue()
299
300     def _getValue(self):
301         if self._valueEvent:
302             self._valueCommand()
303         else:
304             self.__updateValue(self._valueCommand())
305
306     def _rawFromCooked(self, value):
307         return min(self._steps,max(0,int(float(self._steps)*(value-self._min)/(self._max-self._min)+.5)))
308
309     def _cookedFromRaw(self, value):
310         try:
311             return self._stops[ self._rawStops.index(value) ]
312         except ValueError:
313             return (self._min*(self._steps-value) + self._max*value)/self._steps
314
315     ########################################
316     # State machine
317
318     S_INIT = 0
319     S_RUNNING = 1
320     S_WAIT = 2
321     S_SWAIT = 3
322     S_IDLE = 4
323
324     def _setState(self, state):
325         self._state = state
326         self._redrawState()
327
328     def _ev_controllerChanged(self):
329         self._redrawController()
330
331         if self._state in (self.S_IDLE, self.S_RUNNING):
332             if abs(self._controlValue)==1:
333                 self._dispatcher.setIdleCallback(self._ev_timeout,200)
334                 self._setState(self.S_RUNNING)
335             elif self._controlValue != 0:
336                 self._dispatcher.setIdleCallback(self._ev_timeout,75)
337                 self._setState(self.S_RUNNING)
338             else:
339                 self._dispatcher.unsetIdleCallback()
340                 if self._state == self.S_RUNNING:
341                     self._setState(self.S_WAIT)
342                     self._ev_controllerChanged()
343                 else:
344                     self._setState(self.S_IDLE)
345
346         elif self._state == self.S_SWAIT:
347             if self._controlValue == 0 or self._controlSign and self._controlSign*self._controlValue < 0:
348                 self._setState(self.S_WAIT)
349                 self._ev_controllerChanged()
350
351         elif self._state == self.S_WAIT:
352             if self._controlValue == 0:
353                 self._dispatcher.setIdleCallback(self._ev_timeout,300)
354             else:
355                 self._dispatcher.unsetIdleCallback()
356                 if self._controlSign and self._controlSign*self._controlValue > 0:
357                     self._setState(self.S_RUNNING)
358                     self._ev_controllerChanged()
359
360         self._updateWindow()
361
362     def _ev_timeout(self):
363         if self._state == self.S_RUNNING:
364             self._controlSign = max(-1, min(1, self._controlValue))
365             if self._controlSign == 0:
366                 # Cannot happen but ...
367                 self._setState(self.S_IDLE)
368             else:
369                 if self.stepValue(self._controlSign):
370                     self._setState(self.S_SWAIT)
371
372         elif self._state == self.S_WAIT:
373             self._setState(self.S_IDLE)
374             self._dispatcher.unsetIdleCallback()
375
376         self._updateWindow()
377
378     ########################################
379     # Drawing
380
381     # +----------+
382     # |   Name   |
383     # |  -       |
384     # |  |       |
385     # |  |-1.234 |
386     # |-S|       |
387     # |  |       |
388     # |  |       |
389     # |  -       |
390     # +----------+
391
392
393     def _redraw(self):
394         height, width = self.win().getmaxyx()
395         if self._parameter is not None:
396             self.win().addstr(1,2,self._parameter[:width-4].ljust(width-4), curses.A_BOLD)
397         self.win().addch(3,3,curses.ACS_TTEE)
398         self.win().addch(height-3,3,curses.ACS_BTEE)
399         self.win().addch(height/2,1,'-')
400         self._redrawValue()
401         self._redrawController()
402         self._updateWindow()
403
404     def _redrawState(self):
405         self._redrawController()
406
407     def _updateWindow(self):
408         self.win().refresh()
409
410     def _flt(self, value, width):
411         return ('%.3f' % value)[:width].ljust(width)
412
413     def _redrawValue(self):
414         height, width = self.win().getmaxyx()
415         pos = None
416         if self._value is not None:
417             pos = height - 3 - int( (self._value - self._min) * (height-6)
418                                     / (self._max - self._min) + .5 )
419         if self._max is not None:
420             self.win().addstr(2,2, self._flt(self._max,width-7))
421         if pos is not None and pos == 3:
422             self.win().addstr(pos, 5, self._flt(self._value,width-7), curses.A_BOLD)
423         else:
424             self.win().addstr(3, 5, "".ljust(width-7))
425         for row in range(4,height-3):
426             if pos is not None and row == pos:
427                 self.win().addch(pos,3,curses.ACS_PLUS)
428                 self.win().addstr(pos, 5, self._flt(self._value,width-7), curses.A_BOLD)
429             else:
430                 self.win().addch(row,3,curses.ACS_VLINE)
431                 self.win().addstr(row, 5, "".ljust(width-7))
432         if pos is not None and pos == height-3:
433             self.win().addstr(pos, 5, self._flt(self._value,width-7), curses.A_BOLD)
434         else:
435             self.win().addstr(height-3, 5, "".ljust(width-7))
436         if self._min is not None:
437             self.win().addstr(height-2,2,self._flt(self._min,width-7))
438
439     def _redrawController(self, refresh=True):
440         if self._controlValue is None: return
441         height, width = self.win().getmaxyx()
442         pos = height - 3 - int( (self._controlValue + 2) * (height-6) / 4 + .5 )
443         for row in range(3,height-2):
444             if row == pos:
445                 self.win().addch(row,2,('N','R','W','S','I')[self._state])
446             else:
447                 self.win().addch(row,2,' ')
448         
449 class StepController(Actions.Action):
450
451     def __init__(self, name, controller, direction):
452         Actions.Action.__init__(self, name)
453         self._controller = controller
454         self._direction = direction
455
456     def __call__(self, binding):
457         self._controller.stepValue(self._direction)
458
459
460 def register( viewmanager,
461               dispatcher,
462               context,
463               label,
464               numeric_switches,
465               alpha_switches,
466               x,
467               y,
468               dx,
469               size,
470               device,
471               bits = None,
472               controllers = []):
473     viewmanager.registerView( View(context, label, numeric_switches, alpha_switches, x, y, dx, size) )
474     source = None
475     if device is not None:
476         source = Source(device, context, bits)
477         dispatcher.registerSource( source )
478     return source
479
480 def registerController( viewmanager, dispatcher, keylist, source, context, name, x, y, dx, dy,
481                         controller, low, high ):
482     controller = Controller(context, name, x, y, dx, dy, keylist, dispatcher,
483                                          controller, source, low, high)
484     viewmanager.registerView( controller )
485     return controller