Audio/AudioControl: Add Metronome
[audiocontrol.git] / Metronome.py
diff --git a/Metronome.py b/Metronome.py
new file mode 100644 (file)
index 0000000..b527f4e
--- /dev/null
@@ -0,0 +1,195 @@
+import Actions, OSC, Events, Views, curses
+import eci, time
+
+class Metronome(Views.View):
+
+    def __init__(self, context, label, x, y, dx, dy, dispatcher):
+        Views.View.__init__(self, context, label, x, y, dx, dy)
+        self._dispatcher = dispatcher
+        self._eci = eci.ECI(1)
+        self._eci('-G:jack,metronome,notransport')
+        self._eci('ai-add null')
+        self._eci('ao-add jack_auto,alsa_pcm')
+        self._eci('cop-add -pn:metronome,120')
+        self._eci('cop-add -ea:1')
+        self._eci('engine-launch')
+        self._bpm = 120
+        self._volume = 1
+        self._running = False
+        self._tap = None
+
+    def updateView(self, bindings):
+        pass
+
+    def init(self):
+        Views.View.init(self)
+        self._redraw(0)
+
+    def _redraw(self, refresh=1):
+        self.win().addstr(1,2,'%3d bpm' % self._bpm)
+        if self._running:
+            self.win().addstr(1,10,'Running',curses.A_BOLD)
+        else:
+            self.win().addstr(1,10,'Off    ')
+        self.win().addstr(1,18,'%3.1f' % self._volume)
+        if refresh:
+            self.win().refresh()
+        
+    def toggle(self):
+        self._running = not self._running
+        if self._running:
+            self._eci('start')
+        else:
+            self._eci('stop')
+        self._redraw()
+
+    def tap(self):
+        t = time.time()
+        if self._tap and self._tap < t and self._tap > t-2:
+            self.setBPM(60.0 / (t - self._tap))
+        self._tap = t
+
+    def setVolume(self, value):
+        self._volume = value
+        self._eci('cop-select 2')
+        self._eci('copp-select 1')
+        self._eci('copp-set', self._volume)
+        self._redraw()
+
+    def setBPM(self, value):
+        self._bpm = int(value)
+        self._eci('cop-select 1')
+        self._eci('copp-select 1')
+        self._eci('copp-set', self._bpm)
+        self._redraw()
+
+    def stepBPM(self, dir, min=30, max=360):
+        step = abs(dir)
+        bpm = self._bpm/step * step
+        if self._bpm == bpm:
+            bpm += dir
+        elif dir>0:
+            bpm += 5
+        if bpm<min : bpm = min
+        if bpm>max : bpm = max
+        self.setBPM(bpm)
+
+    class VolumeSetter(object):
+
+        def __init__(self, metronome):
+            self._metronome = metronome
+
+        def __call__(self, value):
+            self._metronome.setVolume(value)
+
+    class VolumeGetter(object):
+
+        def __init__(self, metronome):
+            self._metronome = metronome
+
+        def __call__(self):
+            return self._metronome._volume
+            
+    def assignVolumeController(self, controller, title, min, max, steps=[]):
+        controller.assign(title,
+                          self.VolumeSetter(self),
+                          self.VolumeGetter(self),
+                          None,
+                          min, max, steps)
+
+    class BPMSetter(object):
+
+        def __init__(self, metronome):
+            self._metronome = metronome
+
+        def __call__(self, value):
+            self._metronome.setBPM(value)
+
+    class BPMGetter(object):
+
+        def __init__(self, metronome):
+            self._metronome = metronome
+
+        def __call__(self):
+            return self._metronome._bpm
+
+    def assignBPMController(self, controller, title, min, max, steps=[]):
+        controller.assign(title,
+                          self.BPMSetter(self),
+                          self.BPMGetter(self),
+                          None,
+                          min, max, steps)
+
+
+class Toggle(Actions.Action):
+
+    def __init__(self, name, m):
+        Actions.Action.__init__(self, name)
+        self._m = m
+
+    def __call__(self, binding):
+        self._m.toggle()
+
+class Tap(Actions.Action):
+
+    def __init__(self, name, m):
+        Actions.Action.__init__(self, name)
+        self._m = m
+
+    def __call__(self, binding):
+        self._m.tap()
+
+class AssignVolumeController(Actions.Action):
+
+    def __init__(self, name, metronome, controller, title, min, max, steps=[]):
+        Actions.Action.__init__(self, name)
+        self._metronome = metronome
+        self._controller = controller
+        self._title = title
+        self._min = min
+        self._max = max
+        self._steps = steps
+
+    def __call__(self, binding):
+        self._metronome.assignVolumeController(self._controller,
+                                               self._title,
+                                               self._min,
+                                               self._max,
+                                               self._steps)
+
+class AssignBPMController(Actions.Action):
+
+    def __init__(self, name, metronome, controller, title, min, max, steps=[]):
+        Actions.Action.__init__(self, name)
+        self._metronome = metronome
+        self._controller = controller
+        self._title = title
+        self._min = min
+        self._max = max
+        self._steps = steps
+
+    def __call__(self, binding):
+        self._metronome.assignBPMController(self._controller,
+                                            self._title,
+                                            self._min,
+                                            self._max,
+                                            self._steps)
+
+class StepBPM(Actions.Action):
+
+    def __init__(self, name, m, dir):
+        Actions.Action.__init__(self, name)
+        self._m = m
+        self._dir = dir
+
+    def __call__(self, binding):
+        self._m.stepBPM(self._dir)
+
+def register( viewmanager,
+              dispatcher,
+              context,
+              label,
+              x, y, dx, dy ):
+    metronome = Metronome(context, label, x, y, dx, dy, dispatcher)
+    viewmanager.registerView( metronome )
+    return metronome