#
# Copyright (c) 2013 by Takashi Sakamoto
# Copyright (C) 2005-2008 by Pieter Palmers
#
# This file is part of FFADO
# FFADO = Free FireWire (pro-)audio drivers for Linux
#
# FFADO is based upon FreeBoB.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# from PyQt4.QtCore import Qt
# from PyQt4.QtGui import QSizePolicy, QHBoxLayout, QVBoxLayout, QGroupBox
# from PyQt4.QtGui import QWidget, QTabWidget, QLabel, QSlider, QToolButton
from ffado.import_pyqt import *

from math import log10
from ffado.config import *

import logging
log = logging.getLogger('MAudioBeBoB')

class MAudio_BeBoB_Input_Widget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        uicLoad("ffado/mixer/maudio_bebob_input", self)

class MAudio_BeBoB_Output_Widget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        uicLoad("ffado/mixer/maudio_bebob_output", self)


class MAudio_BeBoB(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

    info = {
        0x0000000a: (0, "Ozonic"),
        0x00010062: (1, "FireWire Solo"),
        0x00010060: (2, "FireWire Audiophile"),
        0x00010046: (3, "FireWire 410"),
        0x00010071: (4, "FireWire 1814"),
        0x00010091: (4, "ProjectMix I/O"),
    }

    labels = (
        {
            "inputs":   ("Analog 1/2", "Analog 3/4",
                         "Stream 1/2", "Stream 3/4"),
            "mixers":   ("Mixer 1/2", "Mixer 3/4"),
            "outputs":  ("Analog 1/2", "Analog 3/4")
        },
        {
            "inputs":   ("Analog 1/2", "Digital 1/2",
                         "Stream 1/2", "Stream 3/4"),
            "mixers":   ("Mixer 1/2", "Mixer 3/4"),
            "outputs":  ("Analog 1/2", "Digital 1/2")
        },
        {
            "inputs":   ("Analog 1/2", "Digital 1/2",
                         "Stream 1/2", "Stream 3/4", "Stream 5/6"),
            "mixers":   ("Mixer 1/2", "Mixer 3/4", "Mixer 5/6", "Aux 1/2"),
            "outputs":  ("Analog 1/2", "Analog 3/4", "Digital 1/2",
                         "Headphone 1/2")
        },
        {
            "inputs":   ("Analog 1/2", "Digital 1/2",
                         "Stream 1/2", "Stream 3/4", "Stream 5/6",
                         "Stream 7/8", "Stream 9/10"),
            "mixers":   ("Mixer 1/2", "Mixer 3/4", "Mixer 5/6", "Mixer 7/8",
                         "Mixer 9/10", "Aux 1/2"),
            "outputs":  ("Analog 1/2", "Analog 3/4", "Analog 5/6", "Analog 7/8",
                         "Digital 1/2", "Headphone 1/2")
        },
        {
            "inputs":   ("Analog 1/2", "Analog 3/4", "Analog 5/6", "Analog 7/8",
                         "Stream 1/2", "Stream 3/4", "S/PDIF 1/2",
                         "ADAT 1/2", "ADAT 3/4", "ADAT 5/6", "ADAT 7/8"),
            "mixers":   ("Mixer 1/2", "Mixer 3/4", "Aux 1/2"),
            "outputs":  ("Analog 1/2", "Analog 3/4",
                         "Headphone 1/2", "Headphone 3/4")
        }
    )

    # hardware inputs and stream playbacks
    #  format: function_id/channel_idx/panning-able
    #  NOTE: function_id = channel_idx = panning-able =  labels["inputs"]
    inputs = (
        (
            (0x03, 0x04, 0x01, 0x02),
            ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)),
            (True, True, False, False)
        ),
        (
            (0x01, 0x02, 0x04, 0x03),
            ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)),
            (True, True, False, False)
        ),
        (
            (0x04, 0x05, 0x01, 0x02, 0x03),
            ((0x01, 0x02), (0x01, 0x02),
             (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)),
            (True, True, False, False, False)
        ),
        (
            (0x03, 0x04, 0x02, 0x01, 0x01, 0x01, 0x01),
            ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02),
             (0x01, 0x02), (0x03, 0x04), (0x05, 0x06), (0x07, 0x08)),
            (True, True, False, False, False, False, False)
        ),
        (
            (0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x05, 0x06, 0x07, 0x08, 0x09),
            ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02),
             (0x01, 0x02), (0x01, 0x02), (0x01, 0x02),
             (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)),
            (True, True, True, True, False, False, True,
             True, True, True, True)
        )
    )

    # jack sources except for headphone
    #  format: function_id/source id
    #  NOTE: "function_id" = labels["output"] - "Headphone 1/2/3/4"
    #  NOTE: "source_id" = labels["mixer"]
    jack_src = (
        None,
        None,
        ((0x01, 0x02, 0x03),
         (0x00, 0x00, 0x00, 0x01)),
        ((0x02, 0x03, 0x04, 0x05, 0x06),
         (0x00, 0x00, 0x00, 0x00, 0x00, 0x01)),
        ((0x03, 0x04),
         (0x00, 0x01, 0x02))
    )

    # headphone sources
    #  format: sink id/source id
    #  NOTE: "source_id" = labels["mixer"]
    hp_src = (
        None,
        None,
        ((0x04,),
         (0x00, 0x01, 0x02, 0x03)),
        ((0x07,),
         (0x02, 0x03, 0x04, 0x05, 0x06, 0x07)),
        ((0x01, 0x02),
         (0x01, 0x02, 0x04))
    )

    # hardware outputs
    #  format: function id
    #  NOTE: "function_id" = labels["output"]
    outputs = (
        (0x05, 0x06),
        (0x02, 0x03),
        (0x0c, 0x0d, 0x0e, 0x0f),
        (0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f),
        (0x0c, 0x0d, 0x0f, 0x10)
    )

    # Mixer inputs/outputs
    #  format: function_id/out_stereo_channel_id/in_id/in_stereo_channel_id
    #  NOTE: function_id = out_stereo_channel_id = labels["mixers"]
    #  NOTE: in_id = in_stereo_channel_id = labels["inputs"]
    mixers = (
        (
            (0x01, 0x02),
            ((0x01, 0x02), (0x01, 0x02)),
             (0x02, 0x03, 0x00, 0x01),
            ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02))
        ),
        (
            (0x01, 0x01),
            ((0x01, 0x02), (0x03, 0x04)),
            (0x00, 0x01, 0x03, 0x02),
            ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02))
        ),
        (
            (0x01, 0x02, 0x03, 0x04),
            ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02)),
            (0x03, 0x04, 0x00, 0x01, 0x02),
            ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02),
             (0x01, 0x02), (0x01, 0x02))
        ),
        (
            (0x01, 0x01, 0x01, 0x01, 0x01, 0x07),
            ((0x01, 0x02), (0x03, 0x04),
             (0x05, 0x06), (0x07, 0x08), (0x09, 0x0a), (0x01, 0x02)),
            (0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00),
            ((0x01, 0x02), (0x01, 0x02),
             (0x01, 0x02), (0x01, 0x02), (0x03, 0x04),
             (0x05, 0x06), (0x07, 0x08))
        ),
        (
            (0x01, 0x02, 0x03),
            ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02)),
            (0x01, 0x01, 0x01, 0x01,
             0x02, 0x02, 0x03,
             0x04, 0x04, 0x04, 0x04),
            ((0x01, 0x02), (0x03, 0x04), (0x05, 0x06), (0x07, 0x08),
             (0x01, 0x02), (0x03, 0x04), (0x01, 0x02),
             (0x01, 0x02), (0x03, 0x04), (0x05, 0x06), (0x07, 0x08))
        )
    )

    # Aux mixer inputs/outputs
    #  format: function_id/input_id/input_stereo_channel_id
    #  NOTE: input_id = labels["inputs"]
    aux = (
        None,
        None,
        (
            0x0b,
            (0x09, 0x0a, 0x06, 0x07, 0x08),
            ((0x01, 0x02), (0x01, 0x02),
             (0x01, 0x02), (0x01, 0x02), (0x01, 0x02))
        ),
        (
            0x09,
            (0x07, 0x08, 0x06, 0x05, 0x05, 0x05, 0x05),
            ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02),
             (0x01, 0x02), (0x03, 0x04), (0x05, 0x06), (0x07, 0x08))
        ),
        (
            0x0e,
            (0x13, 0x14, 0x15, 0x16, 0x11, 0x12, 0x17, 0x18, 0x19, 0x1a, 0x1b),
            ((0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02),
             (0x01, 0x02), (0x01, 0x02), (0x01, 0x02),
             (0x01, 0x02), (0x01, 0x02), (0x01, 0x02), (0x01, 0x02))
        )
    )

    def getDisplayTitle(self):
        model = self.configrom.getModelId()
        return self.info[model][1]

    def buildMixer(self):
        self.Selectors = {}
        self.Pannings = {}
        self.Volumes = {}
        self.Mutes = {}
        self.Mixers = {}
        self.FW410HP = 0

        model = self.configrom.getModelId()
        if model not in self.info:
            return

        self.id = self.info[model][0]

        tabs_layout = QHBoxLayout(self)
        tabs = QTabWidget(self)

        self.addInputTab(tabs)
        self.addMixTab(tabs)
        if self.aux[self.id] is not None:
            self.addAuxTab(tabs)
        self.addOutputTab(tabs)

        tabs_layout.addWidget(tabs)


    def addInputTab(self, tabs):
        tab_input = QWidget(self)
        tabs.addTab(tab_input, "In")

        tab_input_layout = QHBoxLayout()
        tab_input.setLayout(tab_input_layout)

        in_labels = self.labels[self.id]["inputs"]
        in_ids = self.inputs[self.id][0]
        in_pan = self.inputs[self.id][2]

        for i in range(len(in_ids)):
            l_idx = self.inputs[self.id][1][i][0]
            r_idx = self.inputs[self.id][1][i][1]

            widget = MAudio_BeBoB_Input_Widget(tab_input)
            tab_input_layout.addWidget(widget)

            widget.name.setText(in_labels[i])

            self.Volumes[widget.l_sld] = (
                "/Mixer/Feature_Volume_%d" % in_ids[i], l_idx,
                widget.r_sld, r_idx,
                widget.link
            )
            self.Volumes[widget.r_sld] = (
                "/Mixer/Feature_Volume_%d" % in_ids[i], r_idx,
                widget.l_sld, l_idx,
                widget.link
            )
            self.Mutes[widget.mute] = (widget.l_sld, widget.r_sld)

            if not in_pan[i]:
                widget.l_pan.setDisabled(True)
                widget.r_pan.setDisabled(True)
            else:
                self.Pannings[widget.l_pan] = (
                    "/Mixer/Feature_LRBalance_%d" % in_ids[i],
                    l_idx
                )
                self.Pannings[widget.r_pan] = (
                    "/Mixer/Feature_LRBalance_%d" % in_ids[i],
                    r_idx
                )

        tab_input_layout.addStretch()


    def addMixTab(self, tabs):
        tab_mix = QWidget(self)
        tabs.addTab(tab_mix, "Mix")

        tab_layout = QHBoxLayout()
        tab_mix.setLayout(tab_layout)

        in_labels = self.labels[self.id]["inputs"]
        in_idxs = self.inputs[self.id][0]

        mix_labels = self.labels[self.id]["mixers"]
        mix_idxs = self.mixers[self.id][0]

        for i in range(len(mix_idxs)):
            if mix_labels[i] == 'Aux 1/2':
                continue

            grp = QGroupBox(tab_mix)
            grp_layout = QVBoxLayout()
            grp.setLayout(grp_layout)
            tab_layout.addWidget(grp)

            label = QLabel(grp)
            grp_layout.addWidget(label)

            label.setText(mix_labels[i])
            label.setAlignment(Qt.AlignCenter)
            label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)

            for j in range(len(in_idxs)):
                mix_in_id = self.mixers[self.id][2][j]

                button = QToolButton(grp)
                grp_layout.addWidget(button)

                button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
                button.setText('%s In' % in_labels[j])
                button.setCheckable(True)

                self.Mixers[button] = (
                    "/Mixer/EnhancedMixer_%d" % mix_idxs[i],
                    mix_in_id, j, i
                )

            grp_layout.addStretch()
        tab_layout.addStretch()

    def addAuxTab(self, tabs):
        #local functions
        def addLinkButton(parent, layout):
            button = QToolButton(parent)
            layout.addWidget(button)
            button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
            button.setText('Link')
            button.setCheckable(True)
            return button
        def addMuteButton(parent, layout):
            button = QToolButton(parent)
            layout.addWidget(button)
            button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
            button.setText('Mute')
            button.setCheckable(True)
            return button

        # local processing
        tab_aux = QWidget(self)
        tabs.addTab(tab_aux, "Aux")

        layout = QHBoxLayout()
        tab_aux.setLayout(layout)

        aux_label = self.labels[self.id]["mixers"][-1]
        aux_id = self.aux[self.id][0]

        aux_in_labels = self.labels[self.id]["inputs"]
        aux_in_ids = self.aux[self.id][1]

        for i in range(len(aux_in_ids)):
            in_ch_l = self.aux[self.id][2][i][0]
            in_ch_r = self.aux[self.id][2][i][1]

            grp = QGroupBox(tab_aux)
            grp_layout = QVBoxLayout()
            grp.setLayout(grp_layout)
            layout.addWidget(grp)

            label = QLabel(grp)
            grp_layout.addWidget(label)
            label.setText("%s\nIn" % aux_in_labels[i])
            label.setAlignment(Qt.AlignCenter)

            grp_sld = QGroupBox(grp)
            grp_sld_layout = QHBoxLayout()
            grp_sld.setLayout(grp_sld_layout)
            grp_layout.addWidget(grp_sld)

            l_sld = QSlider(grp_sld)
            grp_sld_layout.addWidget(l_sld)
            r_sld = QSlider(grp_sld)
            grp_sld_layout.addWidget(r_sld)

            button = addLinkButton(grp, grp_layout)
            self.Volumes[l_sld] = (
                "/Mixer/Feature_Volume_%d" % aux_in_ids[i], in_ch_l,
                r_sld, in_ch_r,
                button
            )
            self.Volumes[r_sld] = (
                "/Mixer/Feature_Volume_%d" % aux_in_ids[i], in_ch_r,
                l_sld, in_ch_l,
                button
            )

            button = addMuteButton(grp, grp_layout)
            self.Mutes[button] = (l_sld, r_sld)

        grp = QGroupBox(tab_aux)
        grp_layout = QVBoxLayout()
        grp.setLayout(grp_layout)
        layout.addWidget(grp)

        label = QLabel(grp)
        grp_layout.addWidget(label)
        label.setText("%s\nOut" % aux_label)
        label.setAlignment(Qt.AlignCenter)

        grp_sld = QGroupBox(grp)
        grp_sld_layout = QHBoxLayout()
        grp_sld.setLayout(grp_sld_layout)
        grp_layout.addWidget(grp_sld)

        l_sld = QSlider(grp_sld)
        grp_sld_layout.addWidget(l_sld)
        r_sld = QSlider(grp_sld)
        grp_sld_layout.addWidget(r_sld)

        button = addLinkButton(grp, grp_layout)
        self.Volumes[l_sld] = (
            "/Mixer/Feature_Volume_%d" % aux_id, 1,
            r_sld, 2,
            button
        )
        self.Volumes[r_sld] = (
            "/Mixer/Feature_Volume_%d" % aux_id, 2,
            l_sld, 1,
            button
        )

        button = addMuteButton(grp, grp_layout)
        self.Mutes[button] = (l_sld, r_sld)

        layout.addStretch()

    def addOutputTab(self, tabs):
        tab_out = QWidget(self)
        tabs.addTab(tab_out, "Out")

        layout = QHBoxLayout()
        tab_out.setLayout(layout)

        out_labels = self.labels[self.id]["outputs"]
        out_ids = self.outputs[self.id]

        mixer_labels = self.labels[self.id]["mixers"]

        if self.jack_src[self.id] is None:
            for i in range(len(out_ids)):
                label = QLabel(tab_out)
                layout.addWidget(label)
                label.setText("%s Out is fixed to %s Out" %
                              (mixer_labels[i], out_labels[i]))
            return

        mixer_ids = self.jack_src[self.id][1]


        for i in range(len(out_ids)):
            out_label = out_labels[i]
            if out_label.find('Headphone') >= 0:
                continue

            out_id = self.jack_src[self.id][0][i]

            widget = MAudio_BeBoB_Output_Widget(tab_out)
            layout.addWidget(widget)

            widget.name.setText(out_label)

            self.Volumes[widget.l_sld] = (
                "/Mixer/Feature_Volume_%d" % out_ids[i], 1,
                widget.r_sld, 2,
                widget.link
            )
            self.Volumes[widget.r_sld] = (
                "/Mixer/Feature_Volume_%d" % out_ids[i], 2,
                widget.l_sld, 1,
                widget.link
            )
            self.Mutes[widget.mute] = (widget.l_sld, widget.r_sld)

            self.Selectors[widget.cmb_src] = ("/Mixer/Selector_%d" % out_id, )

            for j in range(len(mixer_ids)):
                if i != j and j != len(mixer_ids) - 1:
                    continue
                widget.cmb_src.addItem("%s Out" % mixer_labels[j], mixer_ids[j])

        # add headphone
        hp_idx = 0
        for i in range(len(out_ids)):
            out_label = out_labels[i]
            if out_label.find('Headphone') < 0:
                continue

            hp_label = self.labels[self.id]["outputs"][i]
            hp_id = self.hp_src[self.id][0][hp_idx]
            hp_idx += 1

            mixer_labels = self.labels[self.id]["mixers"]

            widget = MAudio_BeBoB_Output_Widget(tab_out)
            layout.addWidget(widget)

            widget.name.setText(hp_label)

            mixer_labels = self.labels[self.id]["mixers"]
            mixer_ids = self.mixers[self.id][0]

            self.Volumes[widget.l_sld] = (
                "/Mixer/Feature_Volume_%d" % out_ids[i], 1,
                widget.r_sld, 2,
                widget.link
            )
            self.Volumes[widget.r_sld] = (
                "/Mixer/Feature_Volume_%d" % out_ids[i], 2,
                widget.l_sld, 1,
                widget.link
            )
            self.Mutes[widget.mute] = (widget.l_sld, widget.r_sld)

            for i in range(len(mixer_ids)):
                widget.cmb_src.addItem("%s Out" % mixer_labels[i], mixer_ids[i])

            if self.id != 3:
                self.Selectors[widget.cmb_src] = ("/Mixer/Selector_%d" % hp_id, )
            else:
                widget.cmb_src.activated.connect(self.update410HP)
                self.FW410HP = widget.cmb_src

        layout.addStretch()

    def initValues(self):
        for ctl, params in list(self.Selectors.items()):
            path = params[0]
            state = self.hw.getDiscrete(path)
            ctl.setCurrentIndex(state)
            ctl.activated.connect(self.updateSelector)

        #       Right - Center - Left
        # 0x8000 - 0x0000 - 0x0001 - 0x7FFE
        #        ..., -1, 0, +1, ...
        for ctl, params in list(self.Pannings.items()):
            path = params[0]
            idx = params[1]
            curr = self.hw.getContignuous(path, idx)
            state = -(curr / 0x7FFE) * 50 + 50
            ctl.setValue(int(state))
            ctl.valueChanged.connect(self.updatePanning)

        for ctl, params in list(self.Volumes.items()):
            path = params[0]
            idx = params[1]
            p_idx = params[3]
            link = params[4]

            db = self.hw.getContignuous(path, idx)
            vol = self.db2vol(db)
            ctl.setValue(int(vol))
            ctl.valueChanged.connect(self.updateVolume)

            # to activate link button, a pair is checked twice, sign...
            pair_db = self.hw.getContignuous(path, p_idx)
            if pair_db == db:
                link.setChecked(True)

        for ctl, params in list(self.Mutes.items()):
            ctl.clicked.connect(self.updateMute)

        for ctl, params in list(self.Mixers.items()):
            path = params[0]
            in_id = params[1]
            mix_in_idx = params[2]
            mix_out_idx = params[3]
            in_ch_l = self.mixers[self.id][3][mix_in_idx][0]
            out_ch_l = self.mixers[self.id][1][mix_out_idx][0]
            # see /libffado/src/bebob/bebob_mixer.cpp
            mux_id = self.getMultiplexedId(in_id, in_ch_l, out_ch_l)
            curr = self.hw.getContignuous(path, mux_id)
            if curr == 0:
                state = True
            else:
                state = False
            ctl.setChecked(state)
            ctl.clicked.connect(self.updateMixer)

        if self.id == 3:
            self.read410HP()

    # helper functions
    def vol2db(self, vol):
        return (log10(vol + 1) - 2) * 16384

    def db2vol(self, db):
        return pow(10, db / 16384 + 2) - 1

    def getMultiplexedId(self, in_id, in_ch_l, out_ch_l):
        # see /libffado/src/bebob/bebob_mixer.cpp
        return (in_id << 8) | (in_ch_l << 4) | (out_ch_l << 0)

    def updateSelector(self, state):
        sender = self.sender()
        path = self.Selectors[sender][0]
        log.debug("set %s to %d" % (path, state))
        self.hw.setDiscrete(path, state)

    def updatePanning(self, state):
        sender = self.sender()
        path = self.Pannings[sender][0]
        idx = self.Pannings[sender][1]
        value = (state - 50) * 0x7FFE / -50
        log.debug("set %s for %d to %d(%d)" % (path, idx, value, state))
        self.hw.setContignuous(path, value, idx)

    def updateVolume(self, vol):
        sender = self.sender()
        path = self.Volumes[sender][0]
        idx = self.Volumes[sender][1]
        pair = self.Volumes[sender][2]
        link = self.Volumes[sender][4]

        db = self.vol2db(vol)
        log.debug("set %s for %d to %d(%d)" % (path, idx, db, vol))
        self.hw.setContignuous(path, db, idx)

        if link.isChecked():
            pair.setValue(vol)

    # device remeber gain even if muted
    def updateMute(self, state):
        sender = self.sender()
        l_sld = self.Mutes[sender][0]
        r_sld = self.Mutes[sender][1]

        if state:
            db = 0x8000
        else:
            db = 0x0000
        for w in [l_sld, r_sld]:
            path = self.Volumes[w][0]
            self.hw.setContignuous(path, db)
            w.setDisabled(state)

    def updateMixer(self, checked):
        if checked:
            state = 0x0000
        else:
            state = 0x8000

        sender = self.sender()
        path = self.Mixers[sender][0]
        in_id = self.Mixers[sender][1]
        mix_in_idx = self.Mixers[sender][2]
        mix_out_idx = self.Mixers[sender][3]
        in_ch_l = self.mixers[self.id][3][mix_in_idx][0]
        out_ch_l = self.mixers[self.id][1][mix_out_idx][0]

        mux_id = self.getMultiplexedId(in_id, in_ch_l, out_ch_l)

        log.debug("set %s for 0x%04X(%d/%d/%d) to %d" %
                  (path, mux_id, in_id, in_ch_l, out_ch_l, state))
        self.hw.setContignuous(path, state, mux_id)

    def read410HP(self):
        path = "/Mixer/Selector_7"
        sel = self.hw.getDiscrete(path)
        if sel > 0:
            enbl = 5
        else:
            enbl = -1

        path = "/Mixer/EnhancedMixer_7"
        for i in range(5):
            in_id = 0
            in_ch_l = i * 2 + 1
            out_ch_l = 1
            mux_id = self.getMultiplexedId(in_id, in_ch_l, out_ch_l)
            state = self.hw.getContignuous(path, mux_id)
            if enbl < 0 and state == 0:
                enbl = i
            else:
                self.hw.setContignuous(path, 0x8000, mux_id)
        # if inconsistency between Selector and Mixer, set AUX as default
        if enbl == -1:
            self.hw.setDiscrete('/Mixer/Selector_7', 1)
            enbl = 5

        self.FW410HP.setCurrentIndex(enbl)

    def update410HP(self, state):
        # each output from mixer can be multiplexed in headphone
        # but here they are exclusive because of GUI simpleness, sigh...
        path = "/Mixer/EnhancedMixer_7"
        for i in range(5):
            in_id = 0
            in_ch_l = i * 2 + 1
            out_ch_l = 1
            mux_id = self.getMultiplexedId(in_id, in_ch_l, out_ch_l)
            if i == state:
                value = 0x0000
            else:
                value = 0x8000
            self.hw.setContignuous(path, value, mux_id)

        # Mixer/Aux is selectable exclusively
        path = "/Mixer/Selector_7"
        sel = (state == 5)
        self.hw.setDiscrete(path, sel)

