def __init__(self, parent):
     self._parent = parent
     self.master_track = parent.song().master_track
     self.the_slider = SliderElement(MIDI_CC_TYPE, 1, 86)
     self.the_slider.add_value_listener(self._do_main_slider, True)
     self.volume_button = None
     self._set_volume_button(StateButton(True, MIDI_CC_TYPE, 1, 80))
     self.xfade_button = None
     self._set_xfade_button(StateButton(True, MIDI_CC_TYPE, 1, 99))
     self.swing_button = None
     self._set_swing_button(StateButton(True, MIDI_CC_TYPE, 1, 81))
     self.mode = KN2_MODE_VOLUME
     self.previous_mode = -1
     self.tempo_button = None
     self._set_tempo_button(StateButton(True, MIDI_CC_TYPE, 1, 82))
     self.push_button = None
     self._set_push_button(StateButton(True, MIDI_CC_TYPE, 1, 87))
     self.clipn_v_button = None
     self.clipn_h_button = None
     self._set_clipn_h_button(StateButton(True, MIDI_CC_TYPE, 1, 90))
     self._set_clipn_v_button(StateButton(True, MIDI_CC_TYPE, 1, 91))
     self.toggle_buttons = [self.volume_button,
      self.xfade_button,
      self.swing_button,
      self.tempo_button,
      self.clipn_h_button,
      self.clipn_v_button]
     self.shift_button = None
     self._set_shift_button(StateButton(True, MIDI_CC_TYPE, 1, 85))
     self.shift_on = False
     self.scroll_mod_left_button = None
     self.scroll_mod_right_button = None
     self._set_scroll_mod_left_button(ButtonElement(True, MIDI_CC_TYPE, 0, 105))
     self._set_scroll_mod_right_button(ButtonElement(True, MIDI_CC_TYPE, 0, 106))
     self._prev_mode = KN2_MODE_VOLUME
     self.lrmode = LR_CONTROL_CLIP
     self.loop_div_index = 0
     self.loop_incdex = 4.0
     self.arrow_mode_button = ColorButton(True, MIDI_CC_TYPE, 30)
     self.arrow_mode_button.add_value_listener(self.toggle_arrow_mode)
     self.arrow_mode_button.send_value(1, True)
     self.navflags = 0
     self.octave_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 70)
     self.octave_mod_button.add_value_listener(self._action_octave)
     self.scale_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 71)
     self.scale_mod_button.add_value_listener(self._action_scale)
     self.basenote_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 72)
     self.basenote_mod_button.add_value_listener(self._action_base_note)
     self.pad_to_mainknob_mode = 0
     self.octave_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 120)
     self.octave_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 121)
     self.scale_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 118)
     self.scale_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 119)
     self.basent_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 124)
     self.basent_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 125)
     self.octave_dwn_button.add_value_listener(self._action_oct_down)
     self.octave_upp_button.add_value_listener(self._action_oct_up)
     self.scale_dwn_button.add_value_listener(self._action_scale_down)
     self.scale_upp_button.add_value_listener(self._action_scale_up)
     self.basent_dwn_button.add_value_listener(self._action_base_down)
     self.basent_upp_button.add_value_listener(self._action_base_up)
     self._measure_left_click = 0
     self._measure_right_click = 0
     self.mode_assign_map = {KN2_MODE_VOLUME: (self.chg_volume,
                        0,
                        'Master Knob controls MASTER Volume',
                        KN2_MODE_CUE),
      KN2_MODE_CUE: (self.chg_cue,
                     0,
                     'Master Knob controls Cue Level',
                     KN2_MODE_VOLUME),
      KN2_MODE_TEMPO_COARSE: (self.chg_tempo,
                              3,
                              'Master Knob controls TEMPO Coarse',
                              KN2_MODE_TEMPO_FINE),
      KN2_MODE_TEMPO_FINE: (self.chg_tempo_fine,
                            3,
                            'Master Knob controls TEMPO Fine',
                            KN2_MODE_TEMPO_COARSE),
      KN2_MODE_XFADE: (self.chg_xfade,
                       1,
                       'Master Knob controls Crossfader',
                       -1),
      KN2_MODE_QUANT: (self.chg_quant,
                       2,
                       'Master Knob controls Recording Quantize',
                       KN2_MODE_CLIP_QUANT),
      KN2_MODE_CLIP_QUANT: (self.chg_clip_q,
                            2,
                            'Master Knob controls Clip Start Quantize',
                            KN2_MODE_QUANT),
      KN2_MODE_CLIPN_HOR: (self.nav_c_hor,
                           4,
                           'Master Knob Clip View horizontally',
                           -1),
      KN2_MODE_CLIPN_VER: (self.nav_c_ver,
                           5,
                           'Master Knob Clip View vertically',
                           -1),
      KN2_MODE_GENERAL: (self.chg_general,
                         -1,
                         None,
                         -1),
      KN2_P_SCALES: (self.modify_pad_scaling,
                     -1,
                     None,
                     -1)}
class MainKnobControl:
    __module__ = __name__
    __doc__ = 'Mk2 Module for Controlling Parameters with Master Knob'

    def __init__(self, parent):
        self._parent = parent
        self.master_track = parent.song().master_track
        self.the_slider = SliderElement(MIDI_CC_TYPE, 1, 86)
        self.the_slider.add_value_listener(self._do_main_slider, True)
        self.volume_button = None
        self._set_volume_button(StateButton(True, MIDI_CC_TYPE, 1, 80))
        self.xfade_button = None
        self._set_xfade_button(StateButton(True, MIDI_CC_TYPE, 1, 99))
        self.swing_button = None
        self._set_swing_button(StateButton(True, MIDI_CC_TYPE, 1, 81))
        self.mode = KN2_MODE_VOLUME
        self.previous_mode = -1
        self.tempo_button = None
        self._set_tempo_button(StateButton(True, MIDI_CC_TYPE, 1, 82))
        self.push_button = None
        self._set_push_button(StateButton(True, MIDI_CC_TYPE, 1, 87))
        self.clipn_v_button = None
        self.clipn_h_button = None
        self._set_clipn_h_button(StateButton(True, MIDI_CC_TYPE, 1, 90))
        self._set_clipn_v_button(StateButton(True, MIDI_CC_TYPE, 1, 91))
        self.toggle_buttons = [self.volume_button,
         self.xfade_button,
         self.swing_button,
         self.tempo_button,
         self.clipn_h_button,
         self.clipn_v_button]
        self.shift_button = None
        self._set_shift_button(StateButton(True, MIDI_CC_TYPE, 1, 85))
        self.shift_on = False
        self.scroll_mod_left_button = None
        self.scroll_mod_right_button = None
        self._set_scroll_mod_left_button(ButtonElement(True, MIDI_CC_TYPE, 0, 105))
        self._set_scroll_mod_right_button(ButtonElement(True, MIDI_CC_TYPE, 0, 106))
        self._prev_mode = KN2_MODE_VOLUME
        self.lrmode = LR_CONTROL_CLIP
        self.loop_div_index = 0
        self.loop_incdex = 4.0
        self.arrow_mode_button = ColorButton(True, MIDI_CC_TYPE, 30)
        self.arrow_mode_button.add_value_listener(self.toggle_arrow_mode)
        self.arrow_mode_button.send_value(1, True)
        self.navflags = 0
        self.octave_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 70)
        self.octave_mod_button.add_value_listener(self._action_octave)
        self.scale_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 71)
        self.scale_mod_button.add_value_listener(self._action_scale)
        self.basenote_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 72)
        self.basenote_mod_button.add_value_listener(self._action_base_note)
        self.pad_to_mainknob_mode = 0
        self.octave_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 120)
        self.octave_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 121)
        self.scale_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 118)
        self.scale_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 119)
        self.basent_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 124)
        self.basent_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 125)
        self.octave_dwn_button.add_value_listener(self._action_oct_down)
        self.octave_upp_button.add_value_listener(self._action_oct_up)
        self.scale_dwn_button.add_value_listener(self._action_scale_down)
        self.scale_upp_button.add_value_listener(self._action_scale_up)
        self.basent_dwn_button.add_value_listener(self._action_base_down)
        self.basent_upp_button.add_value_listener(self._action_base_up)
        self._measure_left_click = 0
        self._measure_right_click = 0
        self.mode_assign_map = {KN2_MODE_VOLUME: (self.chg_volume,
                           0,
                           'Master Knob controls MASTER Volume',
                           KN2_MODE_CUE),
         KN2_MODE_CUE: (self.chg_cue,
                        0,
                        'Master Knob controls Cue Level',
                        KN2_MODE_VOLUME),
         KN2_MODE_TEMPO_COARSE: (self.chg_tempo,
                                 3,
                                 'Master Knob controls TEMPO Coarse',
                                 KN2_MODE_TEMPO_FINE),
         KN2_MODE_TEMPO_FINE: (self.chg_tempo_fine,
                               3,
                               'Master Knob controls TEMPO Fine',
                               KN2_MODE_TEMPO_COARSE),
         KN2_MODE_XFADE: (self.chg_xfade,
                          1,
                          'Master Knob controls Crossfader',
                          -1),
         KN2_MODE_QUANT: (self.chg_quant,
                          2,
                          'Master Knob controls Recording Quantize',
                          KN2_MODE_CLIP_QUANT),
         KN2_MODE_CLIP_QUANT: (self.chg_clip_q,
                               2,
                               'Master Knob controls Clip Start Quantize',
                               KN2_MODE_QUANT),
         KN2_MODE_CLIPN_HOR: (self.nav_c_hor,
                              4,
                              'Master Knob Clip View horizontally',
                              -1),
         KN2_MODE_CLIPN_VER: (self.nav_c_ver,
                              5,
                              'Master Knob Clip View vertically',
                              -1),
         KN2_MODE_GENERAL: (self.chg_general,
                            -1,
                            None,
                            -1),
         KN2_P_SCALES: (self.modify_pad_scaling,
                        -1,
                        None,
                        -1)}

    def start_up(self):
        self._set_mode(KN2_MODE_VOLUME)
        self.arrow_mode_button.send_value(127, True)

    def toggle_arrow_mode(self, value):
        if value > 0:
            self.lrmode = (self.lrmode + 1) % 4
            self.arrow_mode_button.send_hue(LR_MODE_HUES[self.lrmode])
            self._parent.show_message('Left/Right Buttons Control:    ' + L_MODE_FUNCTION[self.lrmode] + ' / ' + R_MODE_FUNCTION[self.lrmode])

    def switch_to_matrix_mode(self):
        if self.mode != KN2_MODE_GENERAL:
            self.previous_mode = self.mode
            self._set_mode(KN2_MODE_GENERAL)

    def exit_matrix_mode(self):
        if self.mode == KN2_MODE_GENERAL:
            self._set_mode(self.previous_mode)
            self.previous_mode = -1

    def update_shift(self):
        if self.shift_on:
            self.shift_button.send_value(127, True)
        else:
            self.shift_button.send_value(0, True)

    def _set_mode(self, mode):
        if not mode in range(11):
            raise AssertionError
            self.update_shift()
            if mode == self.mode:
                return
            self._prev_mode = mode
            self.mode = mode
            self.switch_radio_buttons(self.mode_assign_map[self.mode][1])
            message = self.mode_assign_map[self.mode][2]
            message != None and self._parent.show_message(message)

    def switch_radio_buttons(self, which):
        for index in range(len(self.toggle_buttons)):
            if index == which:
                self.toggle_buttons[index].send_value(127, True)
            else:
                self.toggle_buttons[index].send_value(0, True)

    def update(self):
        self.switch_radio_buttons(self.mode_assign_map[self.mode][1])
        self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode])
        self.arrow_mode_button.send_value(127, True)

    def _do_main_slider(self, value, encoder):
        if not value in range(128):
            raise AssertionError
            if not isinstance(encoder, EncoderElement):
                raise AssertionError
                if value == 1:
                    delta = 1
                else:
                    delta = -1
                if self.pad_to_mainknob_mode != 0:
                    self.mode_assign_map[KN2_P_SCALES][0](delta)
                elif self.navflags == 0:
                    self.mode_assign_map[self.mode][0](delta)
                if self.lrmode == LR_CONTROL_CLIP:
                    self.navflags & LEFT_DOWN != 0 and self.nav_c_hor(delta)
                self.navflags & RIGHT_DOWN != 0 and self.nav_c_ver(delta)
        elif self.lrmode == LR_CONTROL_SEL:
            if self.navflags & LEFT_DOWN != 0:
                self.nav_track(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self._parent.scroll_scene(delta)
        elif self.lrmode == LR_CONTROL_DEV:
            if self.navflags & LEFT_DOWN != 0:
                self.nav_track(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self._parent.scroll_device(delta)
        elif self.lrmode == LR_CONTROL_LOOP:
            if self.navflags & LEFT_DOWN != 0:
                self.adjust_loop_start(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self.adjust_loop_length(delta)

    def modify_pad_scaling(self, delta):
        if self.pad_to_mainknob_mode & PAD_KNOB_OCTAVE != 0:
            self._parent.inc_octave(delta)
        if self.pad_to_mainknob_mode & PAD_KNOB_SCALE != 0:
            self._parent.inc_scale(delta)
        if self.pad_to_mainknob_mode & PAD_KNOB_BASEN != 0:
            self._parent.inc_base_note(delta)

    def adjust_loop_start(self, delta):
        loopval = self._parent.song().loop_start
        loopval += self.loop_incdex * delta
        if loopval < 0:
            loopval = 0
        elif loopval > 999:
            loopval = 999
        self._parent.song().loop_start = loopval

    def adjust_loop_length(self, delta):
        loopval = self._parent.song().loop_length
        loopval += self.loop_incdex * delta
        if loopval < self.loop_incdex:
            loopval = self.loop_incdex
        elif loopval > 999:
            loopval = 999
        self._parent.song().loop_length = loopval

    def chg_general(self, delta):
        self._parent._scenematrix.control_handler.mod_value(delta, self.shift_on)

    def nav_track(self, direction):
        if direction == 1:
            self._parent._a_trk_right(1)
        else:
            self._parent._a_trk_left(1)

    def nav_c_hor(self, direction):
        self._parent.move_view_horizontal(direction)

    def nav_c_ver(self, direction):
        if direction == 1:
            self._parent._session.bank_up()
        else:
            self._parent._session.bank_down()

    def chg_volume(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.volume, diff)
        else:
            self.master_track.mixer_device.volume.value = self.calc_new_parm(self.master_track.mixer_device.volume, diff)

    def chg_xfade(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.crossfader, diff)
        else:
            self.master_track.mixer_device.crossfader.value = self.calc_new_parm(self.master_track.mixer_device.crossfader, diff)

    def chg_cue(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.cue_volume, diff)
        else:
            self.master_track.mixer_device.cue_volume.value = self.calc_new_parm(self.master_track.mixer_device.cue_volume, diff)

    def repeat(self, parm, delta):
        count = 0
        while count < SHIFT_INC:
            parm.value = self.calc_new_parm(parm, delta)
            count += 1

    def calc_new_parm(self, parm, delta):
        parm_range = parm.max - parm.min
        int_val = int((parm.value - parm.min) / parm_range * PARM_RANGE + 0.1)
        inc_val = min(PARM_RANGE, max(0, int_val + delta))
        return float(inc_val) / float(PARM_RANGE) * parm_range + parm.min

    def chg_quant(self, diff):
        rec_quant = self._parent.song().midi_recording_quantization
        index = self.get_quant_index(rec_quant)
        new_index = index + diff
        if new_index >= 0 and new_index < len(QUANT_CONST):
            self._parent.song().midi_recording_quantization = QUANT_CONST[new_index]
            self._parent.show_message(QUANT_DESCR[new_index])

    def chg_clip_q(self, diff):
        quant = self._parent.song().clip_trigger_quantization
        self._parent.song().clip_trigger_quantization = max(0, min(13, quant + diff))
        self._parent.show_message('Clip Quantize ' + CLIQ_DESCR[self._parent.song().clip_trigger_quantization])

    def chg_tempo_fine(self, diff):
        if diff < 0:
            amount = -0.01
        else:
            amount = 0.01
        self.chg_tempo(amount)

    def chg_tempo(self, diff):
        self._parent.song().tempo = max(20, min(999, self._parent.song().tempo + diff))

    def get_quant_index(self, const):
        for index in range(len(QUANT_CONST)):
            if const == QUANT_CONST[index]:
                return index

        return -1

    def _action_octave(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_OCTAVE
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_OCTAVE

    def _action_scale(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_SCALE
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_SCALE

    def _action_base_note(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_BASEN
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_BASEN

    def _set_volume_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.volume_button != None:
                self.volume_button.remove_value_listener(self._action_volume)
            self.volume_button = button
            self.volume_button != None and self.volume_button.add_value_listener(self._action_volume)

    def _action_volume(self, value):
        if not self.volume_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_VOLUME and self._set_mode(KN2_MODE_VOLUME)

    def _set_xfade_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.xfade_button != None:
                self.xfade_button.remove_value_listener(self._action_xfade)
            self.xfade_button = button
            self.xfade_button != None and self.xfade_button.add_value_listener(self._action_xfade)

    def _action_xfade(self, value):
        if not self.xfade_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_XFADE and self._set_mode(KN2_MODE_XFADE)

    def _set_swing_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.swing_button != None:
                self.swing_button.remove_value_listener(self._action_swing)
            self.swing_button = button
            self.swing_button != None and self.swing_button.add_value_listener(self._action_swing)

    def _action_swing(self, value):
        if not self.swing_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_QUANT and self._set_mode(KN2_MODE_QUANT)

    def _set_tempo_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.tempo_button != None:
                self.tempo_button.remove_value_listener(self._action_tempo)
            self.tempo_button = button
            self.tempo_button != None and self.tempo_button.add_value_listener(self._action_tempo)

    def _action_tempo(self, value):
        if not self.tempo_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_TEMPO_COARSE and self._set_mode(KN2_MODE_TEMPO_COARSE)

    def _set_clipn_h_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.clipn_h_button != None:
                self.clipn_h_button.remove_value_listener(self._action_clipnh)
            self.clipn_h_button = button
            self.clipn_h_button != None and self.clipn_h_button.add_value_listener(self._action_clipnh)

    def _action_clipnh(self, value):
        if not self.clipn_h_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_CLIPN_HOR and self._set_mode(KN2_MODE_CLIPN_HOR)

    def _set_clipn_v_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.clipn_v_button != None:
                self.clipn_v_button.remove_value_listener(self._action_clipnv)
            self.clipn_v_button = button
            self.clipn_v_button != None and self.clipn_v_button.add_value_listener(self._action_clipnv)

    def _action_clipnv(self, value):
        if not self.clipn_v_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_CLIPN_VER and self._set_mode(KN2_MODE_CLIPN_VER)

    def _set_shift_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.shift_button != None:
                self.shift_button.remove_value_listener(self._action_shift)
            self.shift_button = button
            self.shift_button != None and self.shift_button.add_value_listener(self._action_shift)

    def _action_shift(self, value):
        if not self.shift_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            self.shift_on = value != 0 and not self.shift_on
            self.update_shift()

    def _set_scroll_mod_left_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.scroll_mod_left_button != None:
                self.scroll_mod_left_button.remove_value_listener(self._action_scroll_left)
            self.scroll_mod_left_button = button
            self.scroll_mod_left_button != None and self.scroll_mod_left_button.add_value_listener(self._action_scroll_left)

    def _set_scroll_mod_right_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.scroll_mod_right_button != None:
                self.scroll_mod_right_button.remove_value_listener(self._action_scroll_right)
            self.scroll_mod_right_button = button
            self.scroll_mod_right_button != None and self.scroll_mod_right_button.add_value_listener(self._action_scroll_right)

    def _action_scroll_left(self, value):
        if not self.scroll_mod_left_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.scroll_mod_left_button.send_value(127, True)
            self.navflags |= LEFT_DOWN
            self._measure_left_click = int(round(time.time() * 1000))
        else:
            self.scroll_mod_left_button.send_value(0, True)
            self.navflags &= ~LEFT_DOWN
            clicktime = int(round(time.time() * 1000)) - self._measure_left_click
            if clicktime < CLICK_TIME:
                if self._parent._modifier_down:
                    self._parent.modify_track_offset(-1)
                elif self._parent._mode == PAD_MODE:
                    self._do_lr_as_scale_mode(-1)
                elif self._parent._mode == SCENE_MODE:
                    self._parent.modify_scene_offset(-1)
                elif self._parent._mode == CLIP_MODE:
                    self._parent.move_view_horizontal(-1)
                elif self._parent._mode == CONTROL_MODE:
                    self._parent.move_view_horizontal(-1)

    def _action_scroll_right(self, value):
        if not self.scroll_mod_right_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.scroll_mod_right_button.send_value(1)
            self.navflags |= RIGHT_DOWN
            self._measure_right_click = int(round(time.time() * 1000))
        else:
            self.scroll_mod_right_button.send_value(0, True)
            self.navflags &= ~RIGHT_DOWN
            clicktime = int(round(time.time() * 1000)) - self._measure_right_click
            if clicktime < CLICK_TIME:
                if self._parent._modifier_down:
                    self._parent.modify_track_offset(1)
                elif self._parent._mode == PAD_MODE:
                    self._do_lr_as_scale_mode(1)
                elif self._parent._mode == SCENE_MODE:
                    self._parent.modify_scene_offset(1)
                elif self._parent._mode == CLIP_MODE:
                    self._parent.move_view_horizontal(1)
                elif self._parent._mode == CONTROL_MODE:
                    self._parent.move_view_horizontal(1)

    def _do_lr_as_scale_mode(self, delta):
        if self.pad_to_mainknob_mode == PAD_KNOB_SCALE:
            self._parent.inc_scale(delta)
        elif self.pad_to_mainknob_mode == PAD_KNOB_BASEN:
            self._parent.inc_base_note(delta)
        else:
            self._parent.inc_octave(delta)

    def _action_oct_down(self, value):
        if value != 0:
            self._parent.inc_octave(-1)

    def _action_oct_up(self, value):
        if value != 0:
            self._parent.inc_octave(1)

    def _action_scale_down(self, value):
        if value != 0:
            self._parent.inc_scale(-1)

    def _action_scale_up(self, value):
        if value != 0:
            self._parent.inc_scale(1)

    def _action_base_down(self, value):
        if value != 0:
            self._parent.inc_base_note(-1)

    def _action_base_up(self, value):
        if value != 0:
            self._parent.inc_base_note(1)

    def _set_push_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.push_button != None:
                self.push_button.remove_value_listener(self._action_push)
            self.push_button = button
            self.push_button != None and self.push_button.add_value_listener(self._action_push)

    def _action_push(self, value):
        if not self.push_button != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                next_mode = self.mode_assign_map[self.mode][3]
                next_mode != -1 and self._set_mode(next_mode)
            self.loop_div_index = self.lrmode == LR_CONTROL_LOOP and self.navflags != 0 and (self.loop_div_index + 1) % len(LOOP_KNOB_DIVISION)
            self._parent.show_message('Loop Selection Granularity : ' + str(LOOP_KNOB_DIVISION[self.loop_div_index]) + ' beats ')
            self.loop_incdex = LOOP_KNOB_DIVISION[self.loop_div_index]

    def remove_listener(self, control, callback):
        if control != None and control.value_has_listener(callback):
            control.remove_value_listener(callback)
        control.disconnect()

    def disconnect(self):
        self.remove_listener(self.the_slider, self._do_main_slider)
        self.remove_listener(self.arrow_mode_button, self.toggle_arrow_mode)
        self.remove_listener(self.volume_button, self._action_volume)
        self.remove_listener(self.xfade_button, self._action_xfade)
        self.remove_listener(self.swing_button, self._action_swing)
        self.remove_listener(self.clipn_h_button, self._action_clipnh)
        self.remove_listener(self.clipn_v_button, self._action_clipnv)
        self.remove_listener(self.shift_button, self._action_shift)
        self.remove_listener(self.scroll_mod_left_button, self._action_scroll_left)
        self.remove_listener(self.scroll_mod_right_button, self._action_scroll_right)
        self.remove_listener(self.push_button, self._action_push)
        self.remove_listener(self.octave_mod_button, self._action_octave)
        self.remove_listener(self.scale_mod_button, self._action_scale)
        self.remove_listener(self.basenote_mod_button, self._action_base_note)
        self.remove_listener(self.octave_dwn_button, self._action_oct_down)
        self.remove_listener(self.octave_upp_button, self._action_oct_up)
        self.remove_listener(self.scale_dwn_button, self._action_scale_down)
        self.remove_listener(self.scale_upp_button, self._action_scale_up)
        self.remove_listener(self.basent_dwn_button, self._action_base_down)
        self.remove_listener(self.basent_upp_button, self._action_base_up)
        self._parent = None
        self.master_track = None
        self.the_slider = None
        self.mode_assign_map = None
 def __init__(self, parent):
     self._parent = parent
     self.master_track = parent.song().master_track
     self.the_slider = SliderElement(MIDI_CC_TYPE, 1, 86)
     self.the_slider.add_value_listener(self._do_main_slider, True)
     self.volume_button = None
     self._set_volume_button(ButtonElement(True, MIDI_CC_TYPE, 1, 80))
     self.xfade_button = None
     self._set_xfade_button(ButtonElement(True, MIDI_CC_TYPE, 1, 99))
     self.swing_button = None
     self._set_swing_button(ButtonElement(True, MIDI_CC_TYPE, 1, 81))
     self.mode = KN2_MODE_VOLUME
     self.previous_mode = -1
     self.tempo_button = None
     self._set_tempo_button(ButtonElement(True, MIDI_CC_TYPE, 1, 82))
     self.push_button = None
     self._set_push_button(ButtonElement(True, MIDI_CC_TYPE, 1, 87))
     self.clipn_v_button = None
     self.clipn_h_button = None
     self._set_clipn_h_button(ButtonElement(True, MIDI_CC_TYPE, 1, 90))
     self._set_clipn_v_button(ButtonElement(True, MIDI_CC_TYPE, 1, 91))
     self.toggle_buttons = [self.volume_button,
      self.xfade_button,
      self.swing_button,
      self.tempo_button,
      self.clipn_h_button,
      self.clipn_v_button]
     self.shift_button = None
     self._set_shift_button(ButtonElement(True, MIDI_CC_TYPE, 1, 85))
     self.shift_on = False
     self.scroll_mod_left_button = None
     self.scroll_mod_right_button = None
     self._set_scroll_mod_left_button(ButtonElement(True, MIDI_CC_TYPE, 0, 105))
     self._set_scroll_mod_right_button(ButtonElement(True, MIDI_CC_TYPE, 0, 106))
     self._prev_mode = KN2_MODE_VOLUME
     self.lrmode = LR_CONTROL_CLIP
     self.loop_div_index = 0
     self.loop_incdex = 4.0
     self.arrow_mode_button = ColorButton(True, MIDI_CC_TYPE, 30)
     self.arrow_mode_button.add_value_listener(self.toggle_arrow_mode)
     self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode])
     self.arrow_mode_button.send_value(1)
     self.navflags = 0
     self.octave_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 70)
     self.octave_mod_button.add_value_listener(self._action_octave)
     self.scale_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 71)
     self.scale_mod_button.add_value_listener(self._action_scale)
     self.basenote_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 72)
     self.basenote_mod_button.add_value_listener(self._action_base_note)
     self.pad_to_mainknob_mode = 0
     self.octave_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 120)
     self.octave_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 121)
     self.scale_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 118)
     self.scale_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 119)
     self.basent_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 124)
     self.basent_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 125)
     self.octave_dwn_button.add_value_listener(self._action_oct_down)
     self.octave_upp_button.add_value_listener(self._action_oct_up)
     self.scale_dwn_button.add_value_listener(self._action_scale_down)
     self.scale_upp_button.add_value_listener(self._action_scale_up)
     self.basent_dwn_button.add_value_listener(self._action_base_down)
     self.basent_upp_button.add_value_listener(self._action_base_up)
     self._measure_left_click = 0
     self._measure_right_click = 0
     self.mode_assign_map = {KN2_MODE_VOLUME: (self.chg_volume,
                        0,
                        'Master Knob controls MASTER Volume',
                        KN2_MODE_CUE),
      KN2_MODE_CUE: (self.chg_cue,
                     0,
                     'Master Knob controls Cue Level',
                     KN2_MODE_VOLUME),
      KN2_MODE_TEMPO_COARSE: (self.chg_tempo,
                              3,
                              'Master Knob controls TEMPO Coarse',
                              KN2_MODE_TEMPO_FINE),
      KN2_MODE_TEMPO_FINE: (self.chg_tempo_fine,
                            3,
                            'Master Knob controls TEMPO Fine',
                            KN2_MODE_TEMPO_COARSE),
      KN2_MODE_XFADE: (self.chg_xfade,
                       1,
                       'Master Knob controls Crossfader',
                       -1),
      KN2_MODE_QUANT: (self.chg_quant,
                       2,
                       'Master Knob controls Recording Quantize',
                       KN2_MODE_CLIP_QUANT),
      KN2_MODE_CLIP_QUANT: (self.chg_clip_q,
                            2,
                            'Master Knob controls Clip Start Quantize',
                            KN2_MODE_QUANT),
      KN2_MODE_CLIPN_HOR: (self.nav_c_hor,
                           4,
                           'Master Knob Clip View horizontally',
                           -1),
      KN2_MODE_CLIPN_VER: (self.nav_c_ver,
                           5,
                           'Master Knob Clip View vertically',
                           -1),
      KN2_MODE_GENERAL: (self.chg_general,
                         -1,
                         None,
                         -1),
      KN2_P_SCALES: (self.modify_pad_scaling,
                     -1,
                     None,
                     -1)}
 def __init__(self, parent):
     self._parent = parent
     self.master_track = parent.song().master_track
     self.volume_button = None
     self._set_volume_button(StateButton(True, MIDI_CC_TYPE, 3, 110))
     self.the_slider = SliderElement(MIDI_CC_TYPE, 1, 86)
     self.the_slider.add_value_listener(self._do_main_slider, True)
     self.xfade_button = None
     self._set_xfade_button(StateButton(True, MIDI_CC_TYPE, 3, 116))
     self.swing_button = None
     self._set_swing_button(StateButton(True, MIDI_CC_TYPE, 3, 111))
     self.mode = KN2_MODE_VOLUME
     self.previous_mode = -1
     self.tempo_button = None
     self._set_tempo_button(StateButton(True, MIDI_CC_TYPE, 3, 112))
     self.push_button = None
     self._set_push_button(StateButton(True, MIDI_CC_TYPE, 1, 87))
     self.clipn_v_button = None
     self.clipn_h_button = None
     self._set_clipn_h_button(StateButton(True, MIDI_CC_TYPE, 3, 114))
     self._set_clipn_v_button(StateButton(True, MIDI_CC_TYPE, 3, 115))
     self.toggle_buttons = [
         self.volume_button,
         self.xfade_button,
         self.swing_button,
         self.tempo_button,
         self.clipn_h_button,
         self.clipn_v_button,
     ]
     self.shift_button = None
     self._set_shift_button(StateButton(True, MIDI_CC_TYPE, 3, 113))
     self.shift_on = False
     self.scroll_mod_left_button = None
     self.scroll_mod_right_button = None
     self._set_scroll_mod_left_button(StateButton(True, MIDI_CC_TYPE, 0, 105))
     self._set_scroll_mod_right_button(StateButton(True, MIDI_CC_TYPE, 0, 106))
     self._prev_mode = KN2_MODE_VOLUME
     self.lrmode = LR_CONTROL_CLIP
     self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143
     self.loop_div_index = 0
     self.loop_incdex = 4.0
     self.arrow_mode_button = ColorButton(True, MIDI_CC_TYPE, 30)
     self.arrow_mode_button.add_value_listener(self.toggle_arrow_mode)
     self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode])
     self.arrow_mode_button.send_value(127, True)
     self.navflags = 0
     self.octave_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 70)
     self.octave_mod_button.add_value_listener(self._action_octave)
     self.scale_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 71)
     self.scale_mod_button.add_value_listener(self._action_scale)
     self.basenote_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 72)
     self.basenote_mod_button.add_value_listener(self._action_base_note)
     self.keycolor_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 73)
     self.keycolor_mod_button.add_value_listener(self._action_key_color)
     self.pad_to_mainknob_mode = 0
     self._measure_left_click = 0
     self._measure_right_click = 0
     self.mode_assign_map = {
         KN2_MODE_VOLUME: (self.chg_volume, 0, "Master Knob controls MASTER Volume", KN2_MODE_CUE),
         KN2_MODE_CUE: (self.chg_cue, 0, "Master Knob controls Cue Level", KN2_MODE_VOLUME),
         KN2_MODE_TEMPO_COARSE: (self.chg_tempo, 3, "Master Knob controls TEMPO Coarse", KN2_MODE_TEMPO_FINE),
         KN2_MODE_TEMPO_FINE: (self.chg_tempo_fine, 3, "Master Knob controls TEMPO Fine", KN2_MODE_TEMPO_COARSE),
         KN2_MODE_XFADE: (self.chg_xfade, 1, "Master Knob controls Crossfader", -1),
         KN2_MODE_QUANT: (self.chg_quant, 2, "Master Knob controls Recording Quantize", KN2_MODE_CLIP_QUANT),
         KN2_MODE_CLIP_QUANT: (self.chg_clip_q, 2, "Master Knob controls Clip Start Quantize", KN2_MODE_QUANT),
         KN2_MODE_CLIPN_HOR: (self.nav_c_hor, 4, "Master Knob Clip View horizontally", -1),
         KN2_MODE_CLIPN_VER: (self.nav_c_ver, 5, "Master Knob Clip View vertically", -1),
         KN2_MODE_GENERAL: (self.chg_general, -1, None, -1),
         KN2_P_SCALES: (self.modify_pad_scaling, -1, None, -1),
     }
class MainKnobControl:
    __module__ = __name__
    __doc__ = 'Mk2 Module for Controlling Parameters with Master Knob'

    def __init__(self, parent):
        self._parent = parent
        self.master_track = parent.song().master_track
        self.the_slider = SliderElement(MIDI_CC_TYPE, 1, 86)
        self.the_slider.add_value_listener(self._do_main_slider, True)
        self.volume_button = None
        self._set_volume_button(ButtonElement(True, MIDI_CC_TYPE, 1, 80))
        self.xfade_button = None
        self._set_xfade_button(ButtonElement(True, MIDI_CC_TYPE, 1, 99))
        self.swing_button = None
        self._set_swing_button(ButtonElement(True, MIDI_CC_TYPE, 1, 81))
        self.mode = KN2_MODE_VOLUME
        self.previous_mode = -1
        self.tempo_button = None
        self._set_tempo_button(ButtonElement(True, MIDI_CC_TYPE, 1, 82))
        self.push_button = None
        self._set_push_button(ButtonElement(True, MIDI_CC_TYPE, 1, 87))
        self.clipn_v_button = None
        self.clipn_h_button = None
        self._set_clipn_h_button(ButtonElement(True, MIDI_CC_TYPE, 1, 90))
        self._set_clipn_v_button(ButtonElement(True, MIDI_CC_TYPE, 1, 91))
        self.toggle_buttons = [self.volume_button,
         self.xfade_button,
         self.swing_button,
         self.tempo_button,
         self.clipn_h_button,
         self.clipn_v_button]
        self.shift_button = None
        self._set_shift_button(ButtonElement(True, MIDI_CC_TYPE, 1, 85))
        self.shift_on = False
        self.scroll_mod_left_button = None
        self.scroll_mod_right_button = None
        self._set_scroll_mod_left_button(ButtonElement(True, MIDI_CC_TYPE, 0, 105))
        self._set_scroll_mod_right_button(ButtonElement(True, MIDI_CC_TYPE, 0, 106))
        self._prev_mode = KN2_MODE_VOLUME
        self.lrmode = LR_CONTROL_CLIP
        self.loop_div_index = 0
        self.loop_incdex = 4.0
        self.arrow_mode_button = ColorButton(True, MIDI_CC_TYPE, 30)
        self.arrow_mode_button.add_value_listener(self.toggle_arrow_mode)
        self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode])
        self.arrow_mode_button.send_value(1)
        self.navflags = 0
        self.octave_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 70)
        self.octave_mod_button.add_value_listener(self._action_octave)
        self.scale_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 71)
        self.scale_mod_button.add_value_listener(self._action_scale)
        self.basenote_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 72)
        self.basenote_mod_button.add_value_listener(self._action_base_note)
        self.pad_to_mainknob_mode = 0
        self.octave_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 120)
        self.octave_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 121)
        self.scale_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 118)
        self.scale_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 119)
        self.basent_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 124)
        self.basent_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 125)
        self.octave_dwn_button.add_value_listener(self._action_oct_down)
        self.octave_upp_button.add_value_listener(self._action_oct_up)
        self.scale_dwn_button.add_value_listener(self._action_scale_down)
        self.scale_upp_button.add_value_listener(self._action_scale_up)
        self.basent_dwn_button.add_value_listener(self._action_base_down)
        self.basent_upp_button.add_value_listener(self._action_base_up)
        self._measure_left_click = 0
        self._measure_right_click = 0
        self.mode_assign_map = {KN2_MODE_VOLUME: (self.chg_volume,
                           0,
                           'Master Knob controls MASTER Volume',
                           KN2_MODE_CUE),
         KN2_MODE_CUE: (self.chg_cue,
                        0,
                        'Master Knob controls Cue Level',
                        KN2_MODE_VOLUME),
         KN2_MODE_TEMPO_COARSE: (self.chg_tempo,
                                 3,
                                 'Master Knob controls TEMPO Coarse',
                                 KN2_MODE_TEMPO_FINE),
         KN2_MODE_TEMPO_FINE: (self.chg_tempo_fine,
                               3,
                               'Master Knob controls TEMPO Fine',
                               KN2_MODE_TEMPO_COARSE),
         KN2_MODE_XFADE: (self.chg_xfade,
                          1,
                          'Master Knob controls Crossfader',
                          -1),
         KN2_MODE_QUANT: (self.chg_quant,
                          2,
                          'Master Knob controls Recording Quantize',
                          KN2_MODE_CLIP_QUANT),
         KN2_MODE_CLIP_QUANT: (self.chg_clip_q,
                               2,
                               'Master Knob controls Clip Start Quantize',
                               KN2_MODE_QUANT),
         KN2_MODE_CLIPN_HOR: (self.nav_c_hor,
                              4,
                              'Master Knob Clip View horizontally',
                              -1),
         KN2_MODE_CLIPN_VER: (self.nav_c_ver,
                              5,
                              'Master Knob Clip View vertically',
                              -1),
         KN2_MODE_GENERAL: (self.chg_general,
                            -1,
                            None,
                            -1),
         KN2_P_SCALES: (self.modify_pad_scaling,
                        -1,
                        None,
                        -1)}

    def start_up(self):
        self._set_mode(KN2_MODE_VOLUME)
        self.arrow_mode_button.send_value(1)

    def toggle_arrow_mode(self, value):
        if value > 0:
            self.lrmode = (self.lrmode + 1) % 4
            self.arrow_mode_button.send_hue(LR_MODE_HUES[self.lrmode])
            self._parent.show_message('Left/Right Buttons Control:    ' + L_MODE_FUNCTION[self.lrmode] + ' / ' + R_MODE_FUNCTION[self.lrmode])

    def switch_to_matrix_mode(self):
        if self.mode != KN2_MODE_GENERAL:
            self.previous_mode = self.mode
            self._set_mode(KN2_MODE_GENERAL)

    def exit_matrix_mode(self):
        if self.mode == KN2_MODE_GENERAL:
            self._set_mode(self.previous_mode)
            self.previous_mode = -1

    def update_shift(self):
        if self.shift_on:
            self.shift_button.send_value(1)
        else:
            self.shift_button.send_value(0)

    def _set_mode(self, mode):
        if not mode in range(11):
            raise AssertionError
            self.update_shift()
            if mode == self.mode:
                return
            self._prev_mode = mode
            self.mode = mode
            self.switch_radio_buttons(self.mode_assign_map[self.mode][1])
            message = self.mode_assign_map[self.mode][2]
            message != None and self._parent.show_message(message)

    def switch_radio_buttons(self, which):
        for index in range(len(self.toggle_buttons)):
            if index == which:
                self.toggle_buttons[index].send_value(1)
            else:
                self.toggle_buttons[index].send_value(0)

    def update(self):
        self.switch_radio_buttons(self.mode_assign_map[self.mode][1])
        self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode])
        self.arrow_mode_button.send_value(1)

    def _do_main_slider(self, value, encoder):
        if not value in range(128):
            raise AssertionError
            if not isinstance(encoder, EncoderElement):
                raise AssertionError
                if value == 1:
                    delta = 1
                else:
                    delta = -1
                if self.pad_to_mainknob_mode != 0:
                    self.mode_assign_map[KN2_P_SCALES][0](delta)
                elif self.navflags == 0:
                    self.mode_assign_map[self.mode][0](delta)
                if self.lrmode == LR_CONTROL_CLIP:
                    self.navflags & LEFT_DOWN != 0 and self.nav_c_hor(delta)
                self.navflags & RIGHT_DOWN != 0 and self.nav_c_ver(delta)
        elif self.lrmode == LR_CONTROL_SEL:
            if self.navflags & LEFT_DOWN != 0:
                self.nav_track(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self._parent.scroll_scene(delta)
        elif self.lrmode == LR_CONTROL_DEV:
            if self.navflags & LEFT_DOWN != 0:
                self.nav_track(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self._parent.scroll_device(delta)
        elif self.lrmode == LR_CONTROL_LOOP:
            if self.navflags & LEFT_DOWN != 0:
                self.adjust_loop_start(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self.adjust_loop_length(delta)

    def modify_pad_scaling(self, delta):
        if self.pad_to_mainknob_mode & PAD_KNOB_OCTAVE != 0:
            self._parent.inc_octave(delta)
        if self.pad_to_mainknob_mode & PAD_KNOB_SCALE != 0:
            self._parent.inc_scale(delta)
        if self.pad_to_mainknob_mode & PAD_KNOB_BASEN != 0:
            self._parent.inc_base_note(delta)

    def adjust_loop_start(self, delta):
        loopval = self._parent.song().loop_start
        loopval += self.loop_incdex * delta
        if loopval < 0:
            loopval = 0
        elif loopval > 999:
            loopval = 999
        self._parent.song().loop_start = loopval

    def adjust_loop_length(self, delta):
        loopval = self._parent.song().loop_length
        loopval += self.loop_incdex * delta
        if loopval < self.loop_incdex:
            loopval = self.loop_incdex
        elif loopval > 999:
            loopval = 999
        self._parent.song().loop_length = loopval

    def chg_general(self, delta):
        self._parent._scenematrix.control_handler.mod_value(delta, self.shift_on)

    def nav_track(self, direction):
        if direction == 1:
            self._parent._a_trk_right(1)
        else:
            self._parent._a_trk_left(1)

    def nav_c_hor(self, direction):
        self._parent.move_view_horizontal(direction)

    def nav_c_ver(self, direction):
        if direction == 1:
            self._parent._session.bank_up()
        else:
            self._parent._session.bank_down()

    def chg_volume(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.volume, diff)
        else:
            self.master_track.mixer_device.volume.value = self.calc_new_parm(self.master_track.mixer_device.volume, diff)

    def chg_xfade(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.crossfader, diff)
        else:
            self.master_track.mixer_device.crossfader.value = self.calc_new_parm(self.master_track.mixer_device.crossfader, diff)

    def chg_cue(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.cue_volume, diff)
        else:
            self.master_track.mixer_device.cue_volume.value = self.calc_new_parm(self.master_track.mixer_device.cue_volume, diff)

    def repeat(self, parm, delta):
        count = 0
        while count < SHIFT_INC:
            parm.value = self.calc_new_parm(parm, delta)
            count += 1

    def calc_new_parm(self, parm, delta):
        parm_range = parm.max - parm.min
        int_val = int((parm.value - parm.min) / parm_range * PARM_RANGE + 0.1)
        inc_val = min(PARM_RANGE, max(0, int_val + delta))
        return float(inc_val) / float(PARM_RANGE) * parm_range + parm.min

    def chg_quant(self, diff):
        rec_quant = self._parent.song().midi_recording_quantization
        index = self.get_quant_index(rec_quant)
        new_index = index + diff
        if new_index >= 0 and new_index < len(QUANT_CONST):
            self._parent.song().midi_recording_quantization = QUANT_CONST[new_index]
            self._parent.show_message(QUANT_DESCR[new_index])

    def chg_clip_q(self, diff):
        quant = self._parent.song().clip_trigger_quantization
        self._parent.song().clip_trigger_quantization = max(0, min(13, quant + diff))
        self._parent.show_message('Clip Quantize ' + CLIQ_DESCR[self._parent.song().clip_trigger_quantization])

    def chg_tempo_fine(self, diff):
        if diff < 0:
            amount = -0.01
        else:
            amount = 0.01
        self.chg_tempo(amount)

    def chg_tempo(self, diff):
        self._parent.song().tempo = max(20, min(999, self._parent.song().tempo + diff))

    def get_quant_index(self, const):
        for index in range(len(QUANT_CONST)):
            if const == QUANT_CONST[index]:
                return index

        return -1

    def _action_octave(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_OCTAVE
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_OCTAVE

    def _action_scale(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_SCALE
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_SCALE

    def _action_base_note(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_BASEN
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_BASEN

    def _set_volume_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.volume_button != None:
                self.volume_button.remove_value_listener(self._action_volume)
            self.volume_button = button
            self.volume_button != None and self.volume_button.add_value_listener(self._action_volume)

    def _action_volume(self, value):
        if not self.volume_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_VOLUME and self._set_mode(KN2_MODE_VOLUME)

    def _set_xfade_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.xfade_button != None:
                self.xfade_button.remove_value_listener(self._action_xfade)
            self.xfade_button = button
            self.xfade_button != None and self.xfade_button.add_value_listener(self._action_xfade)

    def _action_xfade(self, value):
        if not self.xfade_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_XFADE and self._set_mode(KN2_MODE_XFADE)

    def _set_swing_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.swing_button != None:
                self.swing_button.remove_value_listener(self._action_swing)
            self.swing_button = button
            self.swing_button != None and self.swing_button.add_value_listener(self._action_swing)

    def _action_swing(self, value):
        if not self.swing_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_QUANT and self._set_mode(KN2_MODE_QUANT)

    def _set_tempo_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.tempo_button != None:
                self.tempo_button.remove_value_listener(self._action_tempo)
            self.tempo_button = button
            self.tempo_button != None and self.tempo_button.add_value_listener(self._action_tempo)

    def _action_tempo(self, value):
        if not self.tempo_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_TEMPO_COARSE and self._set_mode(KN2_MODE_TEMPO_COARSE)

    def _set_clipn_h_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.clipn_h_button != None:
                self.clipn_h_button.remove_value_listener(self._action_clipnh)
            self.clipn_h_button = button
            self.clipn_h_button != None and self.clipn_h_button.add_value_listener(self._action_clipnh)

    def _action_clipnh(self, value):
        if not self.clipn_h_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_CLIPN_HOR and self._set_mode(KN2_MODE_CLIPN_HOR)

    def _set_clipn_v_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.clipn_v_button != None:
                self.clipn_v_button.remove_value_listener(self._action_clipnv)
            self.clipn_v_button = button
            self.clipn_v_button != None and self.clipn_v_button.add_value_listener(self._action_clipnv)

    def _action_clipnv(self, value):
        if not self.clipn_v_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_CLIPN_VER and self._set_mode(KN2_MODE_CLIPN_VER)

    def _set_shift_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.shift_button != None:
                self.shift_button.remove_value_listener(self._action_shift)
            self.shift_button = button
            self.shift_button != None and self.shift_button.add_value_listener(self._action_shift)

    def _action_shift(self, value):
        if not self.shift_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            self.shift_on = value != 0 and not self.shift_on
            self.update_shift()

    def _set_scroll_mod_left_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.scroll_mod_left_button != None:
                self.scroll_mod_left_button.remove_value_listener(self._action_scroll_left)
            self.scroll_mod_left_button = button
            self.scroll_mod_left_button != None and self.scroll_mod_left_button.add_value_listener(self._action_scroll_left)

    def _set_scroll_mod_right_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.scroll_mod_right_button != None:
                self.scroll_mod_right_button.remove_value_listener(self._action_scroll_right)
            self.scroll_mod_right_button = button
            self.scroll_mod_right_button != None and self.scroll_mod_right_button.add_value_listener(self._action_scroll_right)

    def _action_scroll_left(self, value):
        if not self.scroll_mod_left_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.scroll_mod_left_button.send_value(1)
            self.navflags |= LEFT_DOWN
            self._measure_left_click = int(round(time.time() * 1000))
        else:
            self.scroll_mod_left_button.send_value(0)
            self.navflags &= ~LEFT_DOWN
            clicktime = int(round(time.time() * 1000)) - self._measure_left_click
            if clicktime < CLICK_TIME:
                if self._parent._modifier_down:
                    self._parent.modify_track_offset(-1)
                elif self._parent._mode == PAD_MODE:
                    self._do_lr_as_scale_mode(-1)
                elif self._parent._mode == SCENE_MODE:
                    self._parent.modify_scene_offset(-1)
                elif self._parent._mode == CLIP_MODE:
                    self._parent.move_view_horizontal(-1)
                elif self._parent._mode == CONTROL_MODE:
                    self._parent.move_view_horizontal(-1)

    def _action_scroll_right(self, value):
        if not self.scroll_mod_right_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.scroll_mod_right_button.send_value(1)
            self.navflags |= RIGHT_DOWN
            self._measure_right_click = int(round(time.time() * 1000))
        else:
            self.scroll_mod_right_button.send_value(0)
            self.navflags &= ~RIGHT_DOWN
            clicktime = int(round(time.time() * 1000)) - self._measure_right_click
            if clicktime < CLICK_TIME:
                if self._parent._modifier_down:
                    self._parent.modify_track_offset(1)
                elif self._parent._mode == PAD_MODE:
                    self._do_lr_as_scale_mode(1)
                elif self._parent._mode == SCENE_MODE:
                    self._parent.modify_scene_offset(1)
                elif self._parent._mode == CLIP_MODE:
                    self._parent.move_view_horizontal(1)
                elif self._parent._mode == CONTROL_MODE:
                    self._parent.move_view_horizontal(1)

    def _do_lr_as_scale_mode(self, delta):
        if self.pad_to_mainknob_mode == PAD_KNOB_SCALE:
            self._parent.inc_scale(delta)
        elif self.pad_to_mainknob_mode == PAD_KNOB_BASEN:
            self._parent.inc_base_note(delta)
        else:
            self._parent.inc_octave(delta)

    def _action_oct_down(self, value):
        if value != 0:
            self._parent.inc_octave(-1)

    def _action_oct_up(self, value):
        if value != 0:
            self._parent.inc_octave(1)

    def _action_scale_down(self, value):
        if value != 0:
            self._parent.inc_scale(-1)

    def _action_scale_up(self, value):
        if value != 0:
            self._parent.inc_scale(1)

    def _action_base_down(self, value):
        if value != 0:
            self._parent.inc_base_note(-1)

    def _action_base_up(self, value):
        if value != 0:
            self._parent.inc_base_note(1)

    def _set_push_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.push_button != None:
                self.push_button.remove_value_listener(self._action_push)
            self.push_button = button
            self.push_button != None and self.push_button.add_value_listener(self._action_push)

    def _action_push(self, value):
        if not self.push_button != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                next_mode = self.mode_assign_map[self.mode][3]
                next_mode != -1 and self._set_mode(next_mode)
            self.loop_div_index = self.lrmode == LR_CONTROL_LOOP and self.navflags != 0 and (self.loop_div_index + 1) % len(LOOP_KNOB_DIVISION)
            self._parent.show_message('Loop Selection Granularity : ' + str(LOOP_KNOB_DIVISION[self.loop_div_index]) + ' beats ')
            self.loop_incdex = LOOP_KNOB_DIVISION[self.loop_div_index]

    def remove_listener(self, control, callback):
        if control != None and control.value_has_listener(callback):
            control.remove_value_listener(callback)
        control.disconnect()

    def disconnect(self):
        self.remove_listener(self.the_slider, self._do_main_slider)
        self.remove_listener(self.arrow_mode_button, self.toggle_arrow_mode)
        self.remove_listener(self.volume_button, self._action_volume)
        self.remove_listener(self.xfade_button, self._action_xfade)
        self.remove_listener(self.swing_button, self._action_swing)
        self.remove_listener(self.clipn_h_button, self._action_clipnh)
        self.remove_listener(self.clipn_v_button, self._action_clipnv)
        self.remove_listener(self.shift_button, self._action_shift)
        self.remove_listener(self.scroll_mod_left_button, self._action_scroll_left)
        self.remove_listener(self.scroll_mod_right_button, self._action_scroll_right)
        self.remove_listener(self.push_button, self._action_push)
        self.remove_listener(self.octave_mod_button, self._action_octave)
        self.remove_listener(self.scale_mod_button, self._action_scale)
        self.remove_listener(self.basenote_mod_button, self._action_base_note)
        self.remove_listener(self.octave_dwn_button, self._action_oct_down)
        self.remove_listener(self.octave_upp_button, self._action_oct_up)
        self.remove_listener(self.scale_dwn_button, self._action_scale_down)
        self.remove_listener(self.scale_upp_button, self._action_scale_up)
        self.remove_listener(self.basent_dwn_button, self._action_base_down)
        self.remove_listener(self.basent_upp_button, self._action_base_up)
        self._parent = None
        self.master_track = None
        self.the_slider = None
        self.mode_assign_map = None