Audio/AudioControl: Lots of fixes master
g0dil [Tue, 12 May 2009 08:00:40 +0000 (08:00 +0000)]
.gitignore
Joyboard.py
Keyboard.py
Logger.py
Metronome.py
config.py
start.sh

index 9334190..b60b44b 100644 (file)
@@ -1,2 +1,6 @@
 *.pyc
 winmove
+conf
+loops
+tm
+songs
index ef06b87..ab0621e 100644 (file)
@@ -33,7 +33,7 @@ class JSEvent:
     TYPE_BUTTON = 0x01
     TYPE_AXIS = 0x02
     TYPE_INIT = 0x80
-    
+
     def __init__(self, data):
         self.time, self.value, self.type, self.number = struct.unpack(self.STRUCT,data)
 
@@ -106,16 +106,86 @@ class Source(Events.EventSource):
             return [Events.ControlEvent(self.context(), 'p%d' % number, value)]
         return []
 
+# New Controller state machine:
+#
+# First, we work in two steps: The controller gets an internal
+# controller value which is an integer between 0 and 100. All
+# operations change this value which is then converted to the output
+# value.
+#
+# The controller position is managed as a signed integer between -2
+# and 2.
+#
+# The current state is managed in a state variable. There are the
+# following states:
+#
+#   INIT    In this state, the controller is waiting for the initial
+#           parameter value.
+#
+#   RUNNING The value is currently being changed. The speed and
+#           direction are given by the controller position. If the
+#           current controller position changes to 0 or to the
+#           opposite sign, the state is switched to WAIT.
+#
+#   WAIT    The state machine waits for the controller to enter the
+#           center position. Only when the controller has been in the
+#           center position for some time, it will enter the IDLE
+#           state. It may however go to RUNNING, if the controller
+#           position is again changed to have the same sign t had
+#           before entering WAIT. On entering this state, the
+#           controller position is checked. If the position is 0 now
+#           or if it is changed to become 0, a timeout is started. If
+#           the controller is not changed within this timeout, we
+#           enter IDLE. Any change of the controlle value terminates
+#           thiw timeout.
+#
+#   SWAIT   Wait at a stop. The controller will stay in this state
+#           until the controller position is changed to 0 or the opposite
+#           sign. It then enters the WAIT state.
+#
+#   IDLE    In this state, nothing happens, however, if the controller
+#           position changes from 0, the state machine enters the
+#           running state.
+#
+# Events are:
+#
+#   ev_controllerChanged  The controller position changes
+#
+#   ev_timeout            The timeout handle got called
+#
+# The stop positions are handled by converting them to appropriate
+# integer values. When such a value is reached or crossed, the value
+# is set to the stop value and the state machine enters the SWAIT
+# state.
+#
+# When converting the integer value to the return float value, we
+# first check, wether the integer value is one of the stop values. In
+# this case, the associated float value is returned otherweise ahe
+# integer is scaled to the correct float range.
 
 class Controller(Views.View):
 
+    ########################################
+    # external API
+
     def __init__(self, context, name, x, y, dx, dy, keylist, dispatcher,
                  controller, source, low, high):
         Views.View.__init__(self, context, name, x, y, dx, dy)
+
+        # External value interface
         self._valueEvent = None
         self._setCommand = None
         self._valueCommand = None
+
+        # Parameter information
         self._parameter = None
+        self._min = None
+        self._max = None
+        self._stops = None
+        self._rawStops = None
+        self._steps = 40
+
+        # Controller keymap stuff
         self._keylist = keylist
         self._keymap = Bindings.KeyMap()
         if source:
@@ -125,19 +195,14 @@ class Controller(Views.View):
         self._keylist.prepend(self._keymap)
         if source:
             source.registerController(controller, low, high)
+
+        # Working variables
         self._dispatcher = dispatcher
-        self._min = None
-        self._max = None
-        self._stops = None
         self._value = None
+        self._rawValue = None
         self._controlValue = None
-
-    def updateView(self, bindings):
-        pass
-
-    def init(self):
-        Views.View.init(self)
-        self._redraw()
+        self._controlSign = None
+        self._state = None
 
     def assign(self, parameter,  setCommand, valueCommand, valueEvent, min, max, stops=[]):
         self._keylist.removeMap(self._keymap)
@@ -150,79 +215,202 @@ class Controller(Views.View):
         self._min = min
         self._max = max
         self._stops = stops
+        self._rawStops = [ self._rawFromCooked(x) for x in self._stops ]
         if self._valueEvent:
             self._keymap.add( Bindings.Binding( self._valueEvent, '',
                                                 Actions.Command('updateValue',
                                                                 self._updateValue)) )
         self._keylist.prepend(self._keymap)
         self._value = None
-        if self._valueEvent:
-            self._valueCommand()
-        else:
-            self._value = self._valueCommand()
+        self._rawValue = 0
+        self._controlValue = None
+        self._controlSign = None
+        self._state = self.S_INIT
+        self._redraw()
+        self._getValue()
+
+    def stepValue(self, direction):
+        Logger.log('ctl','direction = %d' % direction)
+        self._rawValue += max(-1,min(1,direction))
+        if self._rawValue > self._steps:
+            self._rawValue = self._steps
+        elif self._rawValue < 0:
+            self._rawValue = 0
+
+        Logger.log('ctl','rawValue = %d' % self._rawValue)
+        self._setValue(self._cookedFromRaw(self._rawValue))
+
+        return self._rawValue in self._rawStops
+
+    ########################################
+    # Base class interface implementation
+
+    def updateView(self, bindings):
+        pass
+
+    def init(self):
+        Views.View.init(self)
         self._redraw()
 
+    ########################################
+    # External event callbacks
+
     def _updateValue(self, binding):
+        # Called to return the value after executing the valueCommand
+        # (when valueCommand initiates an async value get like via
+        # osc)
         event = self._dispatcher.currentEvent()
-        self._value = event.value
+        self.__updateValue(event.value)
+        self._updateWindow()
+
+    def __updateValue(self, value):
+        self._value = value
+        if self._state == self.S_INIT:
+            self._rawValue = self._rawFromCooked(self._value)
+            self._setState(self.S_SWAIT)
         self._redrawValue()
+        self._redrawState()
+        self._updateWindow()
 
     def _controllerChanged(self, binding):
+        # Called, whenever the controller position changes
         event = self._dispatcher.currentEvent()
-        Logger.log('ctl',"value = %d" % event.value)
-        self._controlValue = event.value
-        if self._controlValue >= 999 or self._controlValue <= 1:
-            self._dispatcher.setIdleCallback(self._changeValue,75)
-        elif self._controlValue >= 700 or self._controlValue <= 200:
-            self._dispatcher.setIdleCallback(self._changeValue,200)
+        if event.value >= 999:
+            newControlValue = 2
+        elif event.value >= 700:
+            newControlValue = 1
+        elif event.value >= 200:
+            newControlValue = 0
+        elif event.value >= 1:
+            newControlValue = -1
         else:
-            self._dispatcher.unsetIdleCallback()
-        self._redrawController()
+            newControlValue = -2
+        Logger.log('ctl',"controlValue = %d" % newControlValue)
+        if self._controlValue is None or self._controlValue != newControlValue:
+            self._controlValue = newControlValue
+            self._ev_controllerChanged()
 
-    def _changeValue(self):
-        if self._value is None: return
-        if self._controlValue > 500:
-            self.stepValue(+1)
-        else:
-            self.stepValue(-1)
+    ########################################
+    # Internal API
 
-    def stepValue(self, direction):
-        if direction > 0:
-            newValue = self._value + (self._max - self._min) / (3000/50)
-            crossed = [ x for x in self._stops if x > self._value*1.0001 and x <= newValue ]
-        elif direction < 0:
-            newValue = self._value - (self._max - self._min) / (3000/50)
-            crossed = [ x for x in self._stops if x < self._value/1.0001 and x >= newValue ]
-        if newValue >= self._max:
-            crossed = [ self._max ]
-        elif newValue <= self._min:
-            crossed = [ self._min ]
-        if crossed:
-            newValue = crossed[0]
-            self._dispatcher.unsetIdleCallback()
-            self._setCommand(newValue)
-            # Hmm ... why does value_command not work sometimes ??
-            self._value = newValue
-            self._redrawValue()
+    def _setValue(self, value):
+        self._setCommand(value)
+        self._getValue()
+
+    def _getValue(self):
+        if self._valueEvent:
+            self._valueCommand()
         else:
-            self._setCommand(newValue)
-            if self._valueEvent:
-                self._valueCommand()
+            self.__updateValue(self._valueCommand())
+
+    def _rawFromCooked(self, value):
+        return min(self._steps,max(0,int(float(self._steps)*(value-self._min)/(self._max-self._min)+.5)))
+
+    def _cookedFromRaw(self, value):
+        try:
+            return self._stops[ self._rawStops.index(value) ]
+        except ValueError:
+            return (self._min*(self._steps-value) + self._max*value)/self._steps
+
+    ########################################
+    # State machine
+
+    S_INIT = 0
+    S_RUNNING = 1
+    S_WAIT = 2
+    S_SWAIT = 3
+    S_IDLE = 4
+
+    def _setState(self, state):
+        self._state = state
+        self._redrawState()
+
+    def _ev_controllerChanged(self):
+        self._redrawController()
+
+        if self._state in (self.S_IDLE, self.S_RUNNING):
+            if abs(self._controlValue)==1:
+                self._dispatcher.setIdleCallback(self._ev_timeout,200)
+                self._setState(self.S_RUNNING)
+            elif self._controlValue != 0:
+                self._dispatcher.setIdleCallback(self._ev_timeout,75)
+                self._setState(self.S_RUNNING)
+            else:
+                self._dispatcher.unsetIdleCallback()
+                if self._state == self.S_RUNNING:
+                    self._setState(self.S_WAIT)
+                    self._ev_controllerChanged()
+                else:
+                    self._setState(self.S_IDLE)
+
+        elif self._state == self.S_SWAIT:
+            if self._controlValue == 0 or self._controlSign and self._controlSign*self._controlValue < 0:
+                self._setState(self.S_WAIT)
+                self._ev_controllerChanged()
+
+        elif self._state == self.S_WAIT:
+            if self._controlValue == 0:
+                self._dispatcher.setIdleCallback(self._ev_timeout,300)
+            else:
+                self._dispatcher.unsetIdleCallback()
+                if self._controlSign and self._controlSign*self._controlValue > 0:
+                    self._setState(self.S_RUNNING)
+                    self._ev_controllerChanged()
+
+        self._updateWindow()
+
+    def _ev_timeout(self):
+        if self._state == self.S_RUNNING:
+            self._controlSign = max(-1, min(1, self._controlValue))
+            if self._controlSign == 0:
+                # Cannot happen but ...
+                self._setState(self.S_IDLE)
             else:
-                self._value = self._valueCommand()
-                self._redrawValue()
+                if self.stepValue(self._controlSign):
+                    self._setState(self.S_SWAIT)
+
+        elif self._state == self.S_WAIT:
+            self._setState(self.S_IDLE)
+            self._dispatcher.unsetIdleCallback()
+
+        self._updateWindow()
+
+    ########################################
+    # Drawing
+
+    # +----------+
+    # |   Name   |
+    # |  -       |
+    # |  |       |
+    # |  |-1.234 |
+    # |-S|       |
+    # |  |       |
+    # |  |       |
+    # |  -       |
+    # +----------+
+
 
     def _redraw(self):
         height, width = self.win().getmaxyx()
         if self._parameter is not None:
             self.win().addstr(1,2,self._parameter[:width-4].ljust(width-4), curses.A_BOLD)
-        self._redrawValue(False)
+        self.win().addch(3,3,curses.ACS_TTEE)
+        self.win().addch(height-3,3,curses.ACS_BTEE)
+        self.win().addch(height/2,1,'-')
+        self._redrawValue()
+        self._redrawController()
+        self._updateWindow()
+
+    def _redrawState(self):
         self._redrawController()
 
+    def _updateWindow(self):
+        self.win().refresh()
+
     def _flt(self, value, width):
         return ('%.3f' % value)[:width].ljust(width)
 
-    def _redrawValue(self, refresh=True):
+    def _redrawValue(self):
         height, width = self.win().getmaxyx()
         pos = None
         if self._value is not None:
@@ -247,25 +435,17 @@ class Controller(Views.View):
             self.win().addstr(height-3, 5, "".ljust(width-7))
         if self._min is not None:
             self.win().addstr(height-2,2,self._flt(self._min,width-7))
-        
-        if refresh:
-            self.win().refresh()
 
     def _redrawController(self, refresh=True):
+        if self._controlValue is None: return
         height, width = self.win().getmaxyx()
-        if self._controlValue is not None and self._controlValue >= 700:
-            self.win().addch(3,3,curses.ACS_UARROW)
-        else:
-            self.win().addch(3,3,curses.ACS_TTEE)
-        if self._controlValue is not None and self._controlValue <= 200:
-            self.win().addch(height-3,3,curses.ACS_DARROW)
-        else:
-            self.win().addch(height-3,3,curses.ACS_BTEE)
-            
-        if refresh:
-            self.win().refresh()
-    
-
+        pos = height - 3 - int( (self._controlValue + 2) * (height-6) / 4 + .5 )
+        for row in range(3,height-2):
+            if row == pos:
+                self.win().addch(row,2,('N','R','W','S','I')[self._state])
+            else:
+                self.win().addch(row,2,' ')
+        
 class StepController(Actions.Action):
 
     def __init__(self, name, controller, direction):
@@ -275,7 +455,7 @@ class StepController(Actions.Action):
 
     def __call__(self, binding):
         self._controller.stepValue(self._direction)
-        
+
 
 def register( viewmanager,
               dispatcher,
index 3f1ff24..f3a8b79 100644 (file)
@@ -13,9 +13,10 @@ class Source(Events.EventSource):
 
 class View(Views.View):
 
-    def __init__(self, context, label, x, y, dx, dy, size=9):
-        Views.View.__init__(self, context, label, x, y, dx, dy)
+    def __init__(self, context, label, x, y, dx, dy, size=9, ignore_keys = ()):
+        Views.View.__init__(self, context, label, x, y, dx, dy )
         self._size = size
+        self._ignore_keys = ignore_keys
 
     def updateView(self, bindings):
         self.win().clear()
@@ -32,6 +33,7 @@ class View(Views.View):
                 keyname = '%sC-%s' % (keyname[:-2],keyname[-1:].lower())
             if curses.ascii.isupper(ord(keyname[-1:])):
                 keyname = '%sS-%s' % (keyname[:-1],keyname[-1:].lower())
+            if keyname in self._ignore_keys : continue
             self.win().addstr(row, column, '%-6s %s'
                               % (keyname, bindings[key].label[:self._size]))
             row += 1
@@ -50,6 +52,7 @@ def register( viewmanager,
               y,
               dx,
               dy,
-              size=9):
-    viewmanager.registerView( View(context, label, x,y,dx,dy,size) )
+              size = 9,
+              ignore_keys = ()):
+    viewmanager.registerView( View(context, label, x,y,dx,dy,size,ignore_keys) )
     dispatcher.registerSource( Source(context, viewmanager.win()) )
index 93bc12e..c0c7894 100644 (file)
--- a/Logger.py
+++ b/Logger.py
@@ -26,6 +26,7 @@ class Logger(object):
         text = '[%s] (%s) %s' %  (time.strftime("%H:%M:%S",time.localtime()),src, msg)
         if self._logfile:
             self._logfile.write(text+"\n")
+            self._logfile.flush()
         lines = self._wrapper.wrap(text)
         lines = lines[max(0,len(lines)-self._textwin.getmaxyx()[0]):]
         self._textwin.scroll(len(lines))
index b527f4e..297e7e1 100644 (file)
@@ -9,7 +9,7 @@ class Metronome(Views.View):
         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('ao-add jack_auto,system')
         self._eci('cop-add -pn:metronome,120')
         self._eci('cop-add -ea:1')
         self._eci('engine-launch')
index 76ea832..9196350 100644 (file)
--- a/config.py
+++ b/config.py
@@ -19,11 +19,9 @@ global_map = Bindings.KeyMap()
 #
 # Display size: 88x22
 
-#Logger.init(main.viewmanager, 38, 0, 37, 10, 'audiocontroller.log')
-Logger.init(main.viewmanager, 0, 17, 65, 5)
+#Logger.init(main.viewmanager, 0, 17, 88, 5, 'audiocontroller.log')
+Logger.init(main.viewmanager, 0, 17, 88, 5)
 
-jb = None
-ctl = None
 jb = Joyboard.register(
     viewmanager = main.viewmanager,
     dispatcher = main.dispatcher,
@@ -67,9 +65,10 @@ Keyboard.register(
     label            = 'Key Bindings',
     x                =  0,
     y                =  0,
-    dx               = 52,
+    dx               = 25,
     dy               = 10,
-    size             =  7
+    size             = 11,
+    ignore_keys      = ( '0','1','2','3','4','5','6','7','8','9','0','a','b','c','d','e' )
 )
 
 ###########################################################################
@@ -137,13 +136,16 @@ looper_main_map.add ( Binding( Event('jb0',4),  'Redo',   Action['looper_redo']
 looper_main_map.add ( Binding( Event('jb0',5),  'Mute',   Action['looper_mute'] ) )
 looper_main_map.add ( Binding( Event('jb0',6),  'Trig',   Action['looper_trigger'] ) )
 looper_main_map.add ( Binding( Event('jb0',7),  'Once',   Action['looper_oneshot'] ) )
-looper_main_map.add ( Binding( Event('jb0',8),  'Ins',    Action['looper_insert'] ) )
-looper_main_map.add ( Binding( Event('jb0',9),  'Repl',   Action['looper_replace'] ) )
+#looper_main_map.add ( Binding( Event('jb0',8),  'Ins',    Action['looper_insert'] ) )
+#looper_main_map.add ( Binding( Event('jb0',9),  'Repl',   Action['looper_replace'] ) )
+looper_main_map.add( Binding( Event('jb0',8), 'Ctl Up', Action['controller_increment'] ) )
+looper_main_map.add( Binding( Event('jb0',9), 'Ctl Dn', Action['controller_decrement'] ) )
 looper_main_map.add ( Binding( Event('jb0',12), 'Undo A', Action['looper_undo_all'] ) )
 looper_main_map.add ( Binding( Event('jb0',13), 'Redo A', Action['looper_redo_all'] ) )
 looper_main_map.add ( Binding( Event('jb0',14), 'Subst',  Action['looper_substitute'] ) )
 
 
+
 looper_param_map = Bindings.KeyMap( 'Parameters' )
 Action.register( Actions.ChangeBindingsRelative('looper_set_param_map', 1, [looper_param_map] ) )
 looper_main_map.add ( Binding( Event('jb0',11), '[Param]', Action['looper_set_param_map'] ) )
@@ -171,12 +173,15 @@ Action.register( Looper.AssignController( 'looper_parm_rate', looper, ctl, 'Rate
 looper_param_map.add( Binding( Event('jb0',5),  '(Feedb)', Action['looper_parm_feedback'] ) )
 looper_param_map.add( Binding( Event('jb0',6),  '(Dry)',   Action['looper_parm_dry'] ) )
 looper_param_map.add( Binding( Event('jb0',7),  '(Wet)',   Action['looper_parm_wet'] ) )
-looper_param_map.add( Binding( Event('jb0',8),  '(Gain)',  Action['looper_parm_igain'] ) )
-looper_param_map.add( Binding( Event('jb0',9),  '(Rec T)', Action['looper_parm_rec_thresh'] ) )
+#looper_param_map.add( Binding( Event('jb0',8),  '(Gain)',  Action['looper_parm_igain'] ) )
+#looper_param_map.add( Binding( Event('jb0',9),  '(Rec T)', Action['looper_parm_rec_thresh'] ) )
 looper_param_map.add( Binding( Event('jb0',12), '',        Actions.Nop() ) )
-looper_param_map.add( Binding( Event('jb0',13), 'Rev',     Action['looper_reverse'] ) )
+#looper_param_map.add( Binding( Event('jb0',13), 'Rev',     Action['looper_reverse'] ) )
 looper_param_map.add( Binding( Event('jb0',14), '(Rate)',  Action['looper_parm_rate'] ) )
 
+looper_param_map.add( Binding( Event('jb0',12),  '(Gain)',  Action['looper_parm_igain'] ) )
+looper_param_map.add( Binding( Event('jb0',13),  '(Rec T)', Action['looper_parm_rec_thresh'] ) )
+
 looper_param_map.add( Binding( Event('jb0',11), '[Main]',    Action['unset_this_map'] ) )
 
 # Initialize looper: enable 'round' and set quantize to 'cycle'
@@ -195,11 +200,11 @@ mixer = Mixer.register(
     oscserver = main.oscserver,
 
     context  = 'mix',
-    label    = 'Mixer',
-    x        = 65,
-    y        = 17,
-    dx       = 23,
-    dy       = 5,
+    label    = 'Master',
+    x        = 25,
+    y        = 0,
+    dx       = 25,
+    dy       = 4,
 
     channels = ( 'Guitar', 'Voice' ),
     remote  = ('127.0.0.1', 9901),
@@ -210,27 +215,42 @@ gain = Mixer.register(
     oscserver = main.oscserver,
 
     context  = 'gain',
-    label    = 'Gain',
-    x        = 52,
-    y        =  7,
-    dx       = 23,
+    label    = 'Lead Gain',
+    x        = 25,
+    y        =  4,
+    dx       = 25,
     dy       = 3,
 
     channels = ( 'Guitar', ),
     remote  = ('127.0.0.1', 9902),
 )
 
+monitor = Mixer.register(
+    viewmanager = main.viewmanager,
+    oscserver = main.oscserver,
+
+    context  = 'mix',
+    label    = 'Monitor',
+    x        = 50,
+    y        = 0,
+    dx       = 25,
+    dy       = 4,
+
+    channels = ( 'Guitar', 'Voice' ),
+    remote  = ('127.0.0.1', 9903),
+)
+
 tm = TimeMachine.register(
     viewmanager = main.viewmanager,
 
     context = 'tm',
     name    = 'TimeMachine',
-    x       = 52,
-    y       =  4,
-    dx      = 23,
+    x       = 25,
+    y       =  7,
+    dx      = 25,
     dy      =  3,
 
-    ports   = ('minimixer:out_left', 'minimixer:out_right'),
+    ports   = ('master:out_left', 'master:out_left'),
     dir     = 'tm',
     buffer  =  1)
 
@@ -240,19 +260,38 @@ metronome = Metronome.register(
 
     context = 'mt',
     label   = 'Metronome',
-    x       = 52,
-    y       = 0,
-    dx      = 23,
-    dy      = 4,
+    x       = 50,
+    y       = 7,
+    dx      = 25,
+    dy      = 3,
 )
 
-Action.register( Mixer.AssignController  ( 'mixer_guitar_level', mixer, ctl, 'Guitar', 1 ) )
-Action.register( Mixer.ToggleMuteChannel ( 'mixer_mute_guitar',  mixer, 1 ) )
-Action.register( Mixer.AssignController  ( 'mixer_voice_level',  mixer, ctl, 'Voice', 2 ) )
-Action.register( Mixer.ToggleMuteChannel ( 'mixer_mute_voice',   mixer, 2 ) )
-Action.register( Mixer.AssignController  ( 'mixer_master_level', mixer, ctl, 'Master', 0 ) )
-Action.register( Mixer.ToggleMuteAll     ( 'mixer_mute_all',     mixer ) )
-Action.register( Mixer.CycleVolume       ( 'mixer_cycle_gain',   gain, 1, ( 0.0, 2.0, 4.0 ) ) )
+# Action.register( Mixer.AssignController  ( 'mixer_master_level', mixer, ctl, 'Master', 0 ) )
+Action.register( Mixer.AssignController  ( 'master_guitar_level', mixer, ctl, 'Mast Guit', 1 ) )
+Action.register( Mixer.AssignController  ( 'master_voice_level',  mixer, ctl, 'Mast Voc', 2 ) )
+Action.register( Mixer.ToggleMuteChannel ( 'mixer_mute_guitar',   mixer, 1 ) )
+Action.register( Mixer.ToggleMuteChannel ( 'mixer_mute_voice',    mixer, 2 ) )
+Action.register( Mixer.ToggleMuteAll     ( 'mixer_mute_all',      mixer ) )
+
+Action.register( Mixer.CycleVolume       ( 'mixer_cycle_gain',   gain, 1, ( 0.0, 2.5, 5.0 ) ) )
+
+Action.register( Mixer.AssignController  ( 'monitor_guitar_level', monitor, ctl, 'Mon Guit.', 1 ) )
+Action.register( Mixer.AssignController  ( 'monitor_voice_level',  monitor, ctl, 'Mon Voc', 2, -6, 12, [ 0, 6 ] ) )
+
+master_level_alt_map = Bindings.KeyMap()
+Action.register( Actions.ChangeBindingsRelative( 'set_master_level_alt_map', 1, [master_level_alt_map] ) )
+Action.register( Actions.Macro( 'master_set_guitar_level',
+                                [ 'master_guitar_level', 'set_master_level_alt_map' ] ) )
+Action.register( Actions.Macro( 'master_set_voice_level',
+                                [ 'master_voice_level', 'unset_this_map' ] ) )
+
+monitor_level_alt_map = Bindings.KeyMap()
+Action.register( Actions.ChangeBindingsRelative( 'set_monitor_level_alt_map', 1, [monitor_level_alt_map] ) )
+Action.register( Actions.Macro( 'monitor_set_voice_level',
+                                [ 'monitor_voice_level', 'set_monitor_level_alt_map' ] ) )
+Action.register( Actions.Macro( 'monitor_set_guitar_level',
+                                [ 'monitor_guitar_level', 'unset_this_map' ] ) )
+
 
 Action.register( TimeMachine.ToggleRecord( 'tm_rec_toggle', tm ) )
 
@@ -264,12 +303,18 @@ mixer_map.add( Binding( Event('jb0',4),  'Redo',    Action['looper_redo'] ) )
 mixer_map.add( Binding( Event('jb0',5),  'Un All',  Action['looper_undo_all'] ) )
 
 mixer_map.add( Binding( Event('jb0',6), 'Lead',     Action['mixer_cycle_gain'] ) )
-mixer_map.add( Binding( Event('jb0',7), 'Mute G',   Action['mixer_mute_guitar'] ) )
-mixer_map.add( Binding( Event('jb0',8), 'Mute V',   Action['mixer_mute_voice'] ) )
-mixer_map.add( Binding( Event('jb0',9), 'Mute',     Action['mixer_mute_all'] ) )
+#mixer_map.add( Binding( Event('jb0',7), 'Mute G',   Action['mixer_mute_guitar'] ) )
+#mixer_map.add( Binding( Event('jb0',8), 'Mute V',   Action['mixer_mute_voice'] ) )
+#mixer_map.add( Binding( Event('jb0',9), 'Mute',     Action['mixer_mute_all'] ) )
+mixer_map.add( Binding( Event('jb0',7), 'Mute',     Action['mixer_mute_all'] ) )
+mixer_map.add( Binding( Event('jb0',8), 'Ctl Up', Action['controller_increment'] ) )
+mixer_map.add( Binding( Event('jb0',9), 'Ctl Dn', Action['controller_decrement'] ) )
 
-mixer_map.add( Binding( Event('jb0',13), '(Vol G)', Action['mixer_guitar_level'] ) )
-mixer_map.add( Binding( Event('jb0',14), '(Vol V)', Action['mixer_voice_level'] ) )
+mixer_map.add( Binding( Event('jb0',13), '(Mas G)', Action['master_set_guitar_level'] ) )
+master_level_alt_map.add( Binding( Event('jb0',13), '(Mas V)', Action['master_set_voice_level'] ) )
+
+mixer_map.add( Binding( Event('jb0',14), '(Mon V)', Action['monitor_set_voice_level'] ) )
+monitor_level_alt_map.add( Binding( Event('jb0',14), '(Mon G)', Action['monitor_set_guitar_level'] ) )
 
 mixer_map.add( Binding( Event('jb0',12), 'TM Rec',  Action['tm_rec_toggle'] ) )
 
@@ -294,7 +339,8 @@ metronome_map.add( Binding( Event('jb0', 11), '[Main]', Action['unset_this_map']
 mixer_map.add( Binding( Event('jb0', 11), '[Metr]', Action['set_metronome_map'] ) )
 
 mixer.mute(2)
-mixer.assignController( ctl, 'Guitar', 1 )
+mixer.assignController( ctl, 'Mas Guit', 1 )
+monitor.set(2, 6)
     
 ###########################################################################
 # Jukebox
@@ -330,12 +376,15 @@ Action.register( Actions.Macro( 'player_looper_cancel',
 Action.register( Actions.Macro( 'player_stop_loop',
                                 [ 'looper_undo_all', 'player_pause' ] ) )
 
-for i in range(7):
+for i in range(5):
     Action.register( AlsaPlayer.Jump  ( 'player_jump_%d' % i, player, i+1 ) )
     Action.register( Actions.Macro( 'player_set_track_%d' % i,
                                     [ 'player_jump_%d' % i, 'player_set_stop_map' ] ) )
     player_map.add( Binding( Event('jb0',i+3), 'Tr %d' % (i+1), Action['player_set_track_%d' % i] ) )
 
+player_map.add( Binding( Event('jb0',8), 'Ctl Up', Action['controller_increment'] ) )
+player_map.add( Binding( Event('jb0',9), 'Ctl Dn', Action['controller_decrement'] ) )
+
 player_map.add( Binding( Event('jb0',  0), 'L Rec', Action['player_start_loop_rec'] ) )
 player_map.add( Binding( Event('jb0',  1), 'L Stp', Action['player_stop_loop'] ) )
 player_map.add( Binding( Event('jb0', 11), 'Back', Action['player_skip_rev'] ) )
@@ -358,13 +407,13 @@ def player_queue_tm_recordings(binding):
         wav = os.path.splitext(f)[0] + '.wav'
         if f.endswith('.w64'):
             if not os.path.exists('tm/' + wav):
-                os.waitpid(subprocess.Popen(["sndfile-convert","-pcm32",f,wav],cwd='tm').pid,0)
+                os.waitpid(subprocess.Popen(["sndfile-convert","-pcm16",f,wav],cwd='tm').pid,0)
                 wavs.append(wav)
         elif f.endswith('.wav'):
             wavs.append(f)
     wavs.sort()
     wavs.reverse()
-    for wav in wavs[:7]:
+    for wav in wavs[:5]:
         player.add('tm/' + wav)
         player.stop()
 
index 6a15566..6f98972 100755 (executable)
--- a/start.sh
+++ b/start.sh
@@ -47,11 +47,15 @@ start() {
     move $x $y $w $h "$name"
 }
 
+dcop amarok MainApplication-Interface quit
+sleep 1
 lsof -n | grep dev | grep -e snd -e dsp | awk '{print $2}' | xargs -r kill
+sleep 1
 
 # Make sure nothing is running
-killall slgui
+pidof -x -o $$ start.sh | xargs -r kill
 killall sooperlooper
+killall slgui
 killall meterbridge
 killall qjackctl
 killall jack-rack
@@ -61,16 +65,17 @@ killall jackd
 killall audiocontroller
 killall alsaplayer
 
-amixer sset Master 100% on
+amixer sset Master 67%,100% on
 amixer sset PCM 100% on
 amixer sset Capture 50% on
-amixer sset 'Capture Mux' 0,1
+amixer sset 'Capture Mux' 1,0
 
 start 0 25 496 100 "JACK Audio Connection Kit [(default)] Started." \
     qjackctl
 
 sooperlooper -l 1 -c 2 -t 600 &
 clients="$clients $!"
+sleep 1
 
 cd loops
 start 582 25 794 210 "SooperLooper" \
@@ -82,40 +87,46 @@ start 0 153 496 428 "JACK Rack (voice) - voice.rack" \
     jack-rack -c 1 -s voice voice.rack
 cd ..
 
-start 88 609 408 98 "AlsaPlayer" \
+start 0 609 496 516 "AlsaPlayer" \
     alsaplayer -r -l 0 -d sooperlooper:common_in_1,sooperlooper:common_in_2
 clients="$clients $!"
-move 0 735 496 390 "Queue"
+#move 0 735 496 390 "Queue"
 alsaplayer --stop
 alsaplayer --volume .25
 
 
 start 502 25 74 210 "dpm meter" \
-    meterbridge -r 0 -t dpm -n meter alsa_pcm:capture_1 jack_rack_voice:out_1
+    meterbridge -r 0 -t dpm -n meter alsa_pcm:capture_2 jack_rack_voice:out_1
 
 x=`expr $offset + 1382`
 xterm -fn 6x12 -bg black -fg white -cr white -geometry 88x17+${x}+25 +sb -title Mixer \
     -e alsamixer -V all &
 clients="$clients $!"
 
-jackminimix -c 2 -p 9901 &
+jackminimix -c 2 -p 9901 -n master &
 clients="$clients $!"
+sleep 1
 
 jackminimix -c 1 -p 9902 -n inputgain &
 clients="$clients $!"
+sleep 1
 
+jackminimix -c 2 -p 9903 -n monitor &
+clients="$clients $!"
 sleep 1
 
-jack_connect alsa_pcm:capture_1 inputgain:in1_left
+jack_connect alsa_pcm:capture_2 inputgain:in1_left
 jack_connect inputgain:out_left sooperlooper:common_in_1
 jack_connect inputgain:out_left sooperlooper:common_in_2
-jack_connect sooperlooper:common_out_1 minimixer:in1_left
-jack_connect sooperlooper:common_out_2 minimixer:in1_right
-jack_connect alsa_pcm:capture_2 jack_rack_voice:in_1
-jack_connect jack_rack_voice:out_1 minimixer:in2_left
-jack_connect jack_rack_voice:out_1 minimixer:in2_right
-jack_connect minimixer:out_left alsa_pcm:playback_1
-jack_connect minimixer:out_right alsa_pcm:playback_2
+jack_connect sooperlooper:common_out_1 master:in1_left
+jack_connect sooperlooper:common_out_2 master:in1_left
+jack_connect sooperlooper:common_out_1 monitor:in1_left
+jack_connect sooperlooper:common_out_2 monitor:in1_left
+jack_connect alsa_pcm:capture_1 jack_rack_voice:in_1
+jack_connect jack_rack_voice:out_1 master:in2_left
+jack_connect jack_rack_voice:out_1 monitor:in2_left
+jack_connect master:out_left system:playback_2
+jack_connect monitor:out_left system:playback_1
 
 x=`expr $offset + 502`
 xterm -fn '-dejavu-dejavu sans mono-medium-r-normal--*-260-75-75-m-0-iso10646-1' \