예제 #1
0
 def _parse_signal_sources(self):
     srcs = []
     candidates = []
     # This is internal clock source.
     for subunit_id, subunit_id_plugs in self.subunit_plugs['music'].items(
     ):
         for i, plug in subunit_id_plugs['output'].items():
             if plug['type'] == 'Sync':
                 addr = AvcCcm.get_subunit_signal_addr('music', 0, i)
                 candidates.append((addr, plug))
     # External source is available.
     for i, plug in self.unit_plugs['external']['input'].items():
         if plug['type'] in ('Sync', 'Digital', 'Clock'):
             addr = AvcCcm.get_unit_signal_addr('external', i)
             candidates.append((addr, plug))
     # SYT-match is available, but not practical.
     for i, plug in self.unit_plugs['isoc']['input'].items():
         if plug['type'] == 'Sync':
             addr = AvcCcm.get_unit_signal_addr('isoc', i)
             candidates.append((addr, plug))
     # BeBoBv3 has.
     # Inquire these are able to connect to destination.
     for params in candidates:
         addr = params[0]
         plug = params[1]
         try:
             AvcCcm.ask_signal_source(self.fcp, addr,
                                      self.signal_destination)
         except:
             continue
         srcs.append(params)
     return srcs
예제 #2
0
 def get_clock_src(self):
     plugs = self.__get_clock_plugs()
     dst = plugs['input']
     src = AvcCcm.get_signal_source(self.fcp, dst)
     addr = AvcCcm.parse_signal_addr(plugs['output'])
     if AvcCcm.compare_addrs(src, addr):
         return 'Internal'
     for name, addr in self.__CLOCK_SRCS.items():
         if AvcCcm.compare_addrs(src, AvcCcm.parse_signal_addr(addr)):
             return name
     raise OSError('Unexpected state of device.')
예제 #3
0
 def set_clock_src(self, src):
     if self.get_property('streaming'):
         raise OSError('Packet streaming started.')
     if src not in self.__CLOCK_SRCS and src != 'Internal':
         raise ValueError('Invalid argument for source of clock.')
     plugs = self.__get_clock_plugs()
     dst = plugs['input']
     if src == 'Internal':
         src = plugs['output']
     else:
         src = self.__CLOCK_SRCS[src]
     AvcCcm.set_signal_source(self.fcp, src, dst)
예제 #4
0
 def parse_signal_destination(cls, fcp, subunit_plugs):
     dst = []
     for subunit_id, subunit_id_plugs in subunit_plugs['music'].items():
         for i, plug in subunit_id_plugs['input'].items():
             if plug['type'] == 'Sync':
                 dst = AvcCcm.get_subunit_signal_addr('music', 0, i)
     return dst
예제 #5
0
 def get_stream_mode(self):
     sync_plug_ids = {
         5: '8x8',
         6: '10x10',
         7: '16x16',
     }
     plugs = self.__get_clock_plugs()
     addr = AvcCcm.parse_signal_addr(plugs['output'])
     plug_id = addr['data']['plug']
     if plug_id not in sync_plug_ids:
         raise OSError('Unexpected state of device.')
     return sync_plug_ids[plug_id]
예제 #6
0
 def __get_clock_plugs(self):
     plugs = {}
     info = AvcConnection.get_subunit_plug_info(self.fcp, 'music', 0)
     for direction in ('input', 'output'):
         for i in range(info[direction]):
             addr = BcoPlugInfo.get_subunit_addr(direction, 'music', 0, i)
             plug_type = BcoPlugInfo.get_plug_type(self.fcp, addr)
             if plug_type == 'Sync':
                 plugs[direction] = \
                     AvcCcm.get_subunit_signal_addr('music', 0, i)
                 break
         else:
             raise OSError('Unexpected state of device for clock source.')
     return plugs
예제 #7
0
    def get_avail_connections(cls, fcp, unit_plug_list, subunit_plug_list):
        src_candidates = {}
        dst_candidates = {}
        avail = {}

        for seqid, info in unit_plug_list.items():
            data = info['data']
            addr = AvcCcm.get_unit_signal_addr(data['unit-type'], data['plug'])
            if info['dir'] == 'output':
                target = dst_candidates
            else:
                target = src_candidates
            target[seqid] = addr

        for seqid, info in subunit_plug_list.items():
            data = info['data']
            addr = AvcCcm.get_subunit_signal_addr(data['subunit-type'],
                                                  data['subunit-id'],
                                                  data['plug'])
            # Inverse direction against plugs of unit.
            if info['dir'] == 'output':
                target = src_candidates
            else:
                target = dst_candidates
            target[seqid] = addr

        for dst_seqid, dst_addr in dst_candidates.items():
            try:
                curr_src_info = AvcCcm.get_signal_source(fcp, dst_addr)
            except Exception:
                curr_src_info = None

            for src_seqid, src_addr in src_candidates.items():
                try:
                    AvcCcm.ask_signal_source(fcp, src_addr, dst_addr)
                except Exception:
                    continue

                if dst_seqid not in avail:
                    avail[dst_seqid] = []

                src_info = AvcCcm.parse_signal_addr(src_addr)
                avail[dst_seqid].append((src_seqid, src_info == curr_src_info))

        return avail
예제 #8
0
class ApogeeEnsembleUnit(BebobUnit):
    __CLOCK_SRCS = {
        'Coaxial': AvcCcm.get_unit_signal_addr('external', 4),
        'Optical': AvcCcm.get_unit_signal_addr('external', 5),
        'Word-clock': AvcCcm.get_unit_signal_addr('external', 6),
    }

    def __init__(self, path):
        super().__init__(path)

        if (self.vendor_id, self.model_id) != (0x0003db, 0x01eeee):
            raise OSError('Not supported.')

        guid = self.get_property('guid')
        self.__path = Path('/tmp/hinawa-{0:08x}'.format(guid))

        if self.__path.exists() and self.__path.is_file():
            self.__load_cache()
        else:
            self.__create_cache()
            self.__set_from_cache()
            self.__save_cache()

    def __load_cache(self):
        with self.__path.open(mode='r') as f:
            self.__cache = load(f)

    def __save_cache(self):
        with self.__path.open(mode='w+') as f:
            dump(self.__cache, f)

    def __create_cache(self):
        cache = {}
        HwCmd.create_cache(cache)
        DisplayCmd.create_cache(cache)
        OptIfaceCmd.create_cache(cache)
        MicCmd.create_cache(cache)
        InputCmd.create_cache(cache)
        OutputCmd.create_cache(cache)
        MixerCmd.create_cache(cache)
        RouteCmd.create_cache(cache)
        SpdifResampleCmd.create_cache(cache)
        self.__cache = cache

    def __set_from_cache(self):
        val = HwCmd.get_cd_mode(self.__cache)
        HwCmd.set_cd_mode(self.__cache, self.fcp, val)
        val = HwCmd.get_16bit_mode(self.__cache)
        HwCmd.set_16bit_mode(self.__cache, self.fcp, val)

        val = DisplayCmd.get_illuminate(self.__cache)
        DisplayCmd.set_illuminate(self.__cache, self.fcp, val)
        val = DisplayCmd.get_mode(self.__cache)
        DisplayCmd.set_mode(self.__cache, self.fcp, val)
        val = DisplayCmd.get_target(self.__cache)
        DisplayCmd.set_target(self.__cache, self.fcp, val)
        val = DisplayCmd.get_overhold(self.__cache)
        DisplayCmd.set_overhold(self.__cache, self.fcp, val)

        for target in OptIfaceCmd.get_target_labels():
            val = OptIfaceCmd.get_mode(self.__cache, target)
            OptIfaceCmd.set_mode(self.__cache, self.fcp, target, val)

        for target in MicCmd.get_mic_labels():
            val = MicCmd.get_power(self.__cache, target)
            MicCmd.set_power(self.__cache, self.fcp, target, val)
            val = MicCmd.get_polarity(self.__cache, target)
            MicCmd.set_polarity(self.__cache, self.fcp, target, val)

        for target in InputCmd.get_in_labels():
            val = InputCmd.get_soft_limit(self.__cache, target)
            InputCmd.set_soft_limit(self.__cache, self.fcp, target, val)
            val = InputCmd.get_attr(self.__cache, target)
            InputCmd.set_attr(self.__cache, self.fcp, target, val)

        for target in OutputCmd.get_target_labels():
            val = OutputCmd.get_attr(self.__cache, target)
            OutputCmd.set_attr(self.__cache, self.fcp, target, val)

        for target in MixerCmd.get_target_labels():
            for src in MixerCmd.get_src_labels():
                vals = MixerCmd.get_src_gain(self.__cache, target, src)
                MixerCmd.set_src_gain(self.__cache, self.fcp, target, src,
                                      *vals)

        for target in RouteCmd.get_out_labels():
            src = RouteCmd.get_out_src(self.__cache, target)
            RouteCmd.set_out_src(self.__cache, self.fcp, target, src)
        for target in RouteCmd.get_cap_labels():
            src = RouteCmd.get_cap_src(self.__cache, target)
            RouteCmd.set_cap_src(self.__cache, self.fcp, target, src)
        for target in RouteCmd.get_hp_labels():
            src = RouteCmd.get_hp_src(self.__cache, target)
            RouteCmd.set_hp_src(self.__cache, self.fcp, target, src)

        params = SpdifResampleCmd.get_params(self.__cache)
        SpdifResampleCmd.set_params(self.__cache, self.fcp, *params)

    def __get_clock_plugs(self):
        plugs = {}
        info = AvcConnection.get_subunit_plug_info(self.fcp, 'music', 0)
        for direction in ('input', 'output'):
            for i in range(info[direction]):
                addr = BcoPlugInfo.get_subunit_addr(direction, 'music', 0, i)
                plug_type = BcoPlugInfo.get_plug_type(self.fcp, addr)
                if plug_type == 'Sync':
                    plugs[direction] = \
                        AvcCcm.get_subunit_signal_addr('music', 0, i)
                    break
            else:
                raise OSError('Unexpected state of device for clock source.')
        return plugs

    def get_clock_src_labels(self):
        labels = list(self.__CLOCK_SRCS.keys())
        labels.append('Internal')
        return labels

    def set_clock_src(self, src):
        if self.get_property('streaming'):
            raise OSError('Packet streaming started.')
        if src not in self.__CLOCK_SRCS and src != 'Internal':
            raise ValueError('Invalid argument for source of clock.')
        plugs = self.__get_clock_plugs()
        dst = plugs['input']
        if src == 'Internal':
            src = plugs['output']
        else:
            src = self.__CLOCK_SRCS[src]
        AvcCcm.set_signal_source(self.fcp, src, dst)

    def get_clock_src(self):
        plugs = self.__get_clock_plugs()
        dst = plugs['input']
        src = AvcCcm.get_signal_source(self.fcp, dst)
        addr = AvcCcm.parse_signal_addr(plugs['output'])
        if AvcCcm.compare_addrs(src, addr):
            return 'Internal'
        for name, addr in self.__CLOCK_SRCS.items():
            if AvcCcm.compare_addrs(src, AvcCcm.parse_signal_addr(addr)):
                return name
        raise OSError('Unexpected state of device.')

    def get_stream_mode_labels(self):
        return HwCmd.get_stream_mode_labels()

    def set_stream_mode(self, mode):
        HwCmd.set_stream_mode(mode)

    def get_stream_mode(self):
        sync_plug_ids = {
            5: '8x8',
            6: '10x10',
            7: '16x16',
        }
        plugs = self.__get_clock_plugs()
        addr = AvcCcm.parse_signal_addr(plugs['output'])
        plug_id = addr['data']['plug']
        if plug_id not in sync_plug_ids:
            raise OSError('Unexpected state of device.')
        return sync_plug_ids[plug_id]

    def reset_meters(self):
        DisplayCmd.reset_meter(self.fcp)

    def set_cd_mode(self, enable):
        if enable:
            if self.__cache['hw']['16bit'] != 'spdif-coax-out-1/2':
                raise ValueError('16bit-mode should be spdif-coax-out-1/2.')
            rate = AvcConnection.get_plug_signal_format(self.fcp, 'output', 0)
            if rate != 44100:
                raise ValueError('Sampling rate should be 44100.')
        HwCmd.set_cd_mode(self.__cache, self.fcp, enable)
        self.__save_cache()

    def get_cd_mode(self):
        return HwCmd.get_cd_mode(self.__cache)

    def get_16bit_mode_labels(self):
        return HwCmd.get_16bit_mode_labels()

    def set_16bit_mode(self, target):
        if target != 'none':
            rate = AvcConnection.get_plug_signal_format(self.fcp, 'output', 0)
            if rate not in (44100, 48000):
                raise ValueError('Sampling rate should be 44100 or 48000.')
        HwCmd.set_16bit_mode(self.__cache, self.fcp, target)
        self.__save_cache()

    def get_16bit_mode(self):
        return HwCmd.get_16bit_mode(self.__cache)

    # Hardware configurations.
    def set_display_mode(self, enable):
        DisplayCmd.set_mode(self.__cache, self.fcp, enable)
        self.__save_cache()

    def get_display_mode(self):
        return DisplayCmd.get_mode(self.__cache)

    def get_display_target_labels(self):
        return DisplayCmd.get_target_labels()

    def set_display_target(self, target):
        DisplayCmd.set_target(self.__cache, self.fcp, target)
        self.__save_cache()

    def get_display_target(self):
        return DisplayCmd.get_target(self.__cache)

    def set_display_illuminate(self, enable):
        DisplayCmd.set_illuminate(self.__cache, self.fcp, enable)
        self.__save_cache()

    def get_display_illuminate(self):
        return DisplayCmd.get_illuminate(self.__cache)

    def set_display_overhold(self, enable):
        DisplayCmd.set_overhold(self.__cache, self.fcp, enable)
        self.__save_cache()

    def get_display_overhold(self):
        return DisplayCmd.get_overhold(self.__cache)

    def get_opt_iface_target_labels(self):
        return OptIfaceCmd.get_target_labels()

    def get_opt_iface_mode_labels(self):
        return OptIfaceCmd.get_mode_labels()

    def set_opt_iface_mode(self, target, mode):
        OptIfaceCmd.set_mode(self.__cache, self.fcp, target, mode)
        self.__save_cache()

    def get_opt_iface_mode(self, target):
        return OptIfaceCmd.get_mode(self.__cache, target)

    # Knob configurations.
    def get_knob_out_labels(self):
        return KnobCmd.get_knob_out_labels()

    def set_knob_out_volume(self, target, db):
        KnobCmd.set_out_vol(self.fcp, target, db)

    def get_knob_states(self):
        return KnobCmd.get_states(self.fcp)

    # Microphone configurations.
    def get_mic_labels(self):
        return MicCmd.get_mic_labels()

    def set_polarity(self, target, invert):
        MicCmd.set_polarity(self.__cache, self.fcp, target, invert)
        self.__save_cache()

    def get_polarity(self, target):
        return MicCmd.get_polarity(self.__cache, target)

    def set_phantom_power(self, target, enable):
        MicCmd.set_power(self.__cache, self.fcp, target, enable)
        self.__save_cache()

    def get_phantom_power(self, target):
        return MicCmd.get_power(self.__cache, target)

    # Line input/output configurations.
    def get_line_in_labels(self):
        return InputCmd.get_in_labels()

    def set_soft_limit(self, target, enable):
        InputCmd.set_soft_limit(self.__cache, self.fcp, target, enable)
        self.__save_cache()

    def get_soft_limit(self, target):
        return InputCmd.get_soft_limit(self.__cache, target)

    def get_in_attr_labels(self):
        return InputCmd.get_attr_labels()

    def set_in_attr(self, target, attr):
        InputCmd.set_attr(self.__cache, self.fcp, target, attr)
        self.__save_cache()

    def get_in_attr(self, target):
        return InputCmd.get_attr(self.__cache, target)

    def get_line_out_labels(self):
        return OutputCmd.get_target_labels()

    def get_out_attr_labels(self):
        return OutputCmd.get_attr_labels()

    def set_out_attr(self, target, attr):
        OutputCmd.set_attr(self.__cache, self.fcp, target, attr)
        self.__save_cache()

    def get_out_attr(self, target):
        return OutputCmd.get_attr(self.__cache, target)

    # Route configurations.
    def get_out_labels(self):
        return RouteCmd.get_out_labels()

    def get_out_src_labels(self):
        return RouteCmd.get_out_src_labels()

    def set_out_src(self, target, src):
        RouteCmd.set_out_src(self.__cache, self.fcp, target, src)
        self.__save_cache()

    def get_out_src(self, target):
        return RouteCmd.get_out_src(self.__cache, target)

    def get_cap_labels(self):
        return RouteCmd.get_cap_labels()

    def get_cap_src_labels(self):
        return RouteCmd.get_cap_src_labels()

    def set_cap_src(self, target, src):
        RouteCmd.set_cap_src(self.__cache, self.fcp, target, src)
        self.__save_cache()

    def get_cap_src(self, target):
        return RouteCmd.get_cap_src(self.__cache, target)

    def get_hp_labels(self):
        return RouteCmd.get_hp_labels()

    def get_hp_src_labels(self):
        return RouteCmd.get_hp_src_labels()

    def set_hp_src(self, target, src):
        RouteCmd.set_hp_src(self.__cache, self.fcp, target, src)
        self.__save_cache()

    def get_hp_src(self, target):
        return RouteCmd.get_hp_src(self.__cache, target)

    # Internal multiplexer configuration.
    def get_mixer_labels(self):
        return MixerCmd.get_target_labels()

    def get_mixer_src_labels(self):
        return MixerCmd.get_src_labels()

    def set_mixer_src(self, target, src, db, balance):
        MixerCmd.set_src_gain(self.__cache, self.fcp, target, src, db, balance)
        self.__save_cache()

    def get_mixer_src(self, target, src):
        return MixerCmd.get_src_gain(self.__cache, target, src)

    # S/PDIF resampler configuration.
    def get_spdif_resample_iface_labels(self):
        return SpdifResampleCmd.get_iface_labels()

    def get_spdif_resample_direction_labels(self):
        return SpdifResampleCmd.get_direction_labels()

    def get_spdif_resample_rate_labels(self):
        return SpdifResampleCmd.get_rate_labels()

    def set_spdif_resample(self, enable, iface, direction, rate):
        SpdifResampleCmd.set_params(self.__cache, self.fcp, enable, iface,
                                    direction, rate)
        self.__save_cache()

    def get_spdif_resample(self):
        return SpdifResampleCmd.get_params(self.__cache)
예제 #9
0
 def get_clock_source(self):
     dst = AvcCcm.get_subunit_signal_addr('music', 0, 1)
     curr = AvcCcm.get_signal_source(self.unit.fcp, dst)
     for name, addr in self.__clocks.items():
         if AvcCcm.compare_addrs(curr, AvcCcm.parse_signal_addr(addr)):
             return name
예제 #10
0
 def set_clock_source(self, src):
     if self.unit.get_property('streaming'):
         raise ValueError('Packet streaming already runs.')
     dst = AvcCcm.get_subunit_signal_addr('music', 0, 1)
     addr = self.__clocks[src]
     AvcCcm.set_signal_source(self.unit.fcp, addr, dst)
예제 #11
0
class MaudioProtocolNormal(MaudioProtocolAbstract):
    __IDS = (
        0x00000a,  # Ozonic
        0x010062,  # Firewire Solo
        0x010060,  # Firewire Audiophile
        0x010046,  # Firewire 410
        # Need information.
        #   NRV10
        #   ProFire Lightbridge
    )

    __LABELS = (
        {
            'inputs': ('analog-1/2', 'analog-3/4', 'stream-1/2', 'stream-3/4'),
            'outputs': ('analog-1/2', 'analog-3/4'),
            'mixers': ('mixer-1/2', 'mixer-3/4'),
            'meters':
            ('analog-in-1', 'analog-in-2', 'analog-in-3', 'analog-in-4',
             'stream-in-1', 'stream-in-2', 'stream-in-3', 'stream-in-4',
             'analog-out-1', 'analog-out-2', 'analog-out-3', 'analog-out-4')
        },
        {
            'inputs':
            ('analog-1/2', 'digital-1/2', 'stream-1/2', 'stream-3/4'),
            'outputs': ('analog-1/2', 'digital-1/2'),
            'mixers': ('mixer-1/2', 'mixer-3/4'),
            'meters':
            ('analog-in-1', 'analog-in-2', 'digital-in-1', 'digital-in-2',
             'stream-in-1', 'stream-in-2', 'stream-in-3', 'stream-in-4',
             'analog-out-1', 'analog-out-2', 'digital-out-1', 'digital-out-2')
        },
        {
            'inputs': ('analog-1/2', 'digital-1/2', 'stream-1/2', 'stream-3/4',
                       'stream-5/6'),
            'outputs': ('analog-1/2', 'analog-3/4', 'digital-1/2'),
            'mixers': ('mixer-1/2', 'mixer-3/4', 'mixer-5/6'),
            'meters':
            ('analog-in-1', 'analog-in-2', 'digital-in-1', 'digital-in-2',
             'analog-out-1', 'analog-out-2', 'analog-out-3', 'analog-out-4',
             'digital-out-1', 'digital-out-2', 'headphone-out-1',
             'headphone-out-2', 'aux-out-1', 'aux-out-2')
        },
        {
            'inputs': ('analog-1/2', 'digital-1/2', 'stream-1/2', 'stream-3/4',
                       'stream-5/6', 'stream-7/8', 'stream-9/10'),
            'outputs': ('analog-1/2', 'analog-3/4', 'analog-5/6', 'analog-7/8',
                        'digital-1/2'),
            'mixers':
            ('mixer-1/2', 'mixer-3/4', 'mixer-5/6', 'mixer-7/8', 'mixer-9/10'),
            'meters':
            ('analog-in-1', 'analog-in-2', 'digital-in-1', 'digital-in-2',
             'analog-out-1', 'analog-out-2', 'analog-out-3', 'analog-out-4',
             'analog-out-5', 'analog-out-6', 'analog-out-7', 'analog-out-8',
             'digital-out-1', 'digital-out-2', 'headphone-out-1',
             'headphone-out-2', 'aux-out-1', 'aux-out-2')
        },
    )

    # = __LABELS['inputs']
    __INPUTS = (
        ((3, (1, 2)), (4, (1, 2)), (1, (1, 2)), (2, (1, 2))),
        ((1, (1, 2)), (2, (1, 2)), (4, (1, 2)), (3, (1, 2))),
        ((4, (1, 2)), (5, (1, 2)), (1, (1, 2)), (2, (1, 2)), (3, (1, 2))),
        ((3, (1, 2)), (4, (1, 2)), (2, (1, 2)), (1, (1, 2)), (1, (3, 4)),
         (1, (5, 6)), (1, (7, 8))),
    )

    # = __LABELS['inputs']
    __AUX__INPUTS = (
        (),
        (),
        ((9, (1, 2)), (10, (1, 2)), (6, (1, 2)), (7, (1, 2)), (8, (1, 2))),
        ((7, (1, 2)), (8, (1, 2)), (9, (1, 2)), (6, (1, 2)), (5, (1, 2)),
         (5, (3, 4)), (5, (5, 6)), (5, (7, 8))),
    )

    __AUX_OUTPUT = (
        None,
        None,
        11,
        9,
    )

    # = __LABELS['inputs']
    __MIXER_SOURCES = (
        ((2, (1, 2)), (3, (1, 2)), (0, (1, 2)), (1, (1, 2))),
        ((0, (1, 2)), (1, (1, 2)), (3, (1, 2)), (2, (1, 2))),
        ((3, (1, 2)), (4, (1, 2)), (0, (1, 2)), (1, (1, 2)), (2, (1, 2))),
        ((2, (1, 2)), (3, (1, 2)), (1, (1, 2)), (0, (1, 2)), (0, (3, 4)),
         (0, (5, 6)), (0, (7, 8))),
    )

    # = __LABELS['mixers']
    __MIXERS = (
        ((1, (1, 2)), (1, (1, 2))),
        ((1, (1, 2)), (1, (3, 4))),
        ((1, (1, 2)), (2, (1, 2)), (3, (1, 2))),
        ((1, (1, 2)), (1, (3, 4)), (1, (5, 6)), (1, (7, 8)), (1, (9, 10))),
    )

    # = __LABELS['outputs']
    __OUTPUT_SOURCES = (
        (),
        (),
        (1, 2, 3),
        (2, 3, 4, 5, 6),
    )

    # = __LABELS['outputs']
    __OUTPUTS = (
        (),
        (),
        ((12, (1, 2)), (13, (1, 2)), (14, (1, 2))),
        ((10, (1, 2)), (11, (1, 2)), (12, (1, 2)), (13, (1, 2)), (14, (1, 2))),
    )

    __HP_SOURCES = (
        (),
        (),
        ((4, (0, 1, 2, 3)), ),
        ((7, (2, 3, 4, 5, 6, 7)), ),
    )

    __HP_OUTS = (
        (),
        (),
        ((15, (1, 2)), ),
        ((15, (1, 2)), ),
    )

    __METERS = (
        48,
        52,
        60,
        76,
    )

    __CLOCKS = (
        {},
        {
            'Internal': AvcCcm.get_subunit_signal_addr('music', 0, 1),
            'S/PDIF': AvcCcm.get_unit_signal_addr('external', 1)
        },
        {
            'Internal': AvcCcm.get_subunit_signal_addr('music', 0, 1),
            'S/PDIF': AvcCcm.get_unit_signal_addr('external', 2)
        },
        {
            'Internal': AvcCcm.get_subunit_signal_addr('music', 0, 1),
            'S/PDIF': AvcCcm.get_unit_signal_addr('external', 2),
            'ADAT': AvcCcm.get_unit_signal_addr('external', 3)
        },
    )

    def __init__(self, unit, debug):
        if unit.model_id not in self.__IDS:
            raise OSError('Not supported')

        super().__init__(unit, debug)

        index = self.__IDS.index(unit.model_id)
        self.labels = self.__LABELS[index]
        self.mixers = self.__MIXERS[index]
        self.__inputs = self.__INPUTS[index]
        self.__aux_inputs = self.__AUX__INPUTS[index]
        self.__aux_output = self.__AUX_OUTPUT[index]
        self.__mixer_sources = self.__MIXER_SOURCES[index]
        self.__output_sources = self.__OUTPUT_SOURCES[index]
        self.__outputs = self.__OUTPUTS[index]
        self.__hp_sources = self.__HP_SOURCES[index]
        self.__hp_outs = self.__HP_OUTS[index]
        self.__meters = self.__METERS[index]
        self.__clocks = self.__CLOCKS[index]

    def _refer_fb_data(self, targets, index, ch):
        if index >= len(targets):
            raise ValueError('Invalid argument for function block index')
        if ch >= len(targets[index][1]):
            raise ValueError('Invalid argument for channel number')
        fb = targets[index][0]
        ch = targets[index][1][ch]
        return (fb, ch)

    def _set_volume(self, targets, index, ch, db):
        fb, ch = self._refer_fb_data(targets, index, ch)
        data = AvcAudio.build_data_from_db(db)
        AvcAudio.set_feature_volume_state(self.unit.fcp, 0, 'current', fb, ch,
                                          data)

    def _get_volume(self, targets, index, ch):
        fb, ch = self._refer_fb_data(targets, index, ch)
        data = AvcAudio.get_feature_volume_state(self.unit.fcp, 0, 'current',
                                                 fb, ch)
        return AvcAudio.parse_data_to_db(data)

    def get_input_labels(self):
        return self.labels['inputs']

    def _refer_input_data(self, target):
        if target not in self.labels['inputs']:
            raise ValueError('Invalid argument for input')
        return self.labels['inputs'].index(target)

    def set_input_gain(self, target, ch, db):
        index = self._refer_input_data(target)
        self._set_volume(self.__inputs, index, ch, db)

    def get_input_gain(self, target, ch):
        index = self._refer_input_data(target)
        return self._get_volume(self.__inputs, index, ch)

    def get_input_balance_labels(self):
        labels = []
        for label in self.labels['inputs']:
            if label.find('stream-') == 0:
                continue
            labels.append(label)
        return labels

    def set_input_balance(self, target, ch, balance):
        index = self._refer_input_data(target)
        fb, ch = self._refer_fb_data(self.__inputs, index, ch)
        data = AvcAudio.build_data_from_db(balance)
        AvcAudio.set_feature_lr_state(self.unit.fcp, 0, 'current', fb, ch,
                                      data)

    def get_input_balance(self, target, ch):
        index = self._refer_input_data(target)
        fb, ch = self._refer_fb_data(self.__inputs, index, ch)
        data = AvcAudio.get_feature_lr_state(self.unit.fcp, 0, 'current', fb,
                                             ch)
        return AvcAudio.parse_data_to_db(data)

    def get_output_labels(self):
        if len(self.__outputs) == 0:
            return ()
        return self.labels['outputs']

    def _refer_out_data(self, target):
        if target not in self.labels['outputs']:
            raise ValueError('Invalid argument for output')
        return self.labels['outputs'].index(target)

    def set_output_volume(self, target, ch, db):
        index = self._refer_out_data(target)
        self._set_volume(self.__outputs, index, ch, db)

    def get_output_volume(self, target, ch):
        index = self._refer_out_data(target)
        return self._get_volume(self.__outputs, index, ch)

    def set_aux_volume(self, ch, db):
        if ch > 2:
            raise ValueError('Invalid argument for master channel')
        fb = self.__aux_output
        data = AvcAudio.build_data_from_db(db)
        AvcAudio.set_feature_volume_state(self.unit.fcp, 0, 'current', fb, ch,
                                          data)

    def get_aux_volume(self, ch):
        if ch > 2:
            raise ValueError('Invalid argument for master channel')
        fb = self.__aux_output
        data = AvcAudio.get_feature_volume_state(self.unit.fcp, 0, 'current',
                                                 fb, ch)
        return AvcAudio.parse_data_to_db(data)

    def set_aux_balance(self, ch, balance):
        if ch > 2:
            raise ValueError('Invalid argument for master channel')
        fb = self.__aux_output
        data = AvcAudio.build_data_from_db(balance)
        ch += 1
        AvcAudio.set_feature_lr_state(self.unit.fcp, 0, 'current', fb, ch,
                                      data)

    def get_aux_balance(self, ch):
        if ch > 2:
            raise ValueError('Invalid argument for master channel')
        fb = self.__aux_output
        ch += 1
        data = AvcAudio.get_feature_lr_state(self.unit.fcp, 0, 'current', fb,
                                             ch)
        return AvcAudio.parse_data_to_db(data)

    def get_headphone_labels(self):
        labels = []
        for i in range(len(self.__hp_outs)):
            labels.append('headphone-{0}/{1}'.format(i * 2 + 1, i * 2 + 2))
        return labels

    def _refer_hp_data(self, target):
        matches = match('^headphone-([0-9]*)/([0-9]*)$', target)
        if not matches:
            raise ValueError('Invalid argument for headphone')
        left = int(matches.group(1)) // 2
        right = int(matches.group(2)) // 2
        if right != left + 1:
            raise ValueError('Invalid argument for headphone')
        return left

    def set_headphone_volume(self, target, ch, db):
        index = self._refer_hp_data(target)
        self._set_volume(self.__hp_outs, index, ch, db)

    def get_headphone_volume(self, target, ch):
        index = self._refer_hp_data(target)
        return self._get_volume(self.__hp_outs, index, ch)

    def get_aux_input_labels(self):
        if not self.__aux_output:
            return ()
        return self.labels['inputs']

    def set_aux_input(self, target, ch, db):
        index = self._refer_input_data(target)
        self._set_volume(self.__aux_inputs, index, ch, db)

    def get_aux_input(self, target, ch):
        index = self._refer_input_data(target)
        return self._get_volume(self.__aux_inputs, index, ch)

    def get_mixer_labels(self):
        return self.labels['mixers']

    def get_mixer_source_labels(self):
        return self.labels['inputs']

    def _refer_mixer_data(self, target, source):
        if source not in self.labels['inputs']:
            raise ValueError('Invalid argument for mixer input')
        if target not in self.labels['mixers']:
            raise ValueError('Invalid argument for mixer output')
        input = self.labels['inputs'].index(source)
        in_fb = self.__mixer_sources[input][0]
        in_ch = self.__mixer_sources[input][1][0]  # Use left channel.
        mixer = self.labels['mixers'].index(target)
        out_fb = self.mixers[mixer][0]
        out_ch = self.mixers[mixer][1][0]  # Use left channel.
        return (in_fb, in_ch, out_fb, out_ch)

    def set_mixer_routing(self, target, source, enable):
        in_fb, in_ch, out_fb, out_ch = self._refer_mixer_data(target, source)
        if enable:
            data = (0x00, 0x00)
        else:
            data = (0x80, 0x00)
        AvcAudio.set_processing_mixer_state(self.unit.fcp, 0, 'current',
                                            out_fb, in_fb, in_ch, out_ch, data)

    def get_mixer_routing(self, target, source):
        in_fb, in_ch, out_fb, out_ch = self._refer_mixer_data(target, source)
        data = AvcAudio.get_processing_mixer_state(self.unit.fcp, 0, 'current',
                                                   out_fb, in_fb, in_ch,
                                                   out_ch)
        return data[0] == 0x00 and data[1] == 0x00

    def get_headphone_source_labels(self, target):
        labels = []
        if len(self.__hp_sources) > 0:
            for mixer in self.labels['mixers']:
                labels.append(mixer)
            if self.__aux_output:
                labels.append("aux-1/2")
        return labels

    def set_headphone_source(self, target, source):
        index = self._refer_hp_data(target)
        if source in self.labels['mixers']:
            ch = self.labels['mixers'].index(source)
        elif source.find('aux') == 0:
            ch = len(self.labels['mixers'])
        else:
            raise ValueError('Invalid argument for output target')
        fb = self.__hp_sources[index][0]
        value = self.__hp_sources[index][1][ch]
        AvcAudio.set_selector_state(self.unit.fcp, 0, 'current', fb, value)

    def get_headphone_source(self, target):
        index = self._refer_hp_data(target)
        fb = self.__hp_sources[index][0]
        value = AvcAudio.get_selector_state(self.unit.fcp, 0, 'current', fb)
        ch = self.__hp_sources[index][1][value]
        if ch < len(self.labels['mixers']):
            return self.labels['mixers'][ch]
        return 'aux-1/2'

    def get_output_source_labels(self, target):
        index = self._refer_out_data(target)
        labels = []
        labels.append(self.labels['mixers'][index])
        if self.__aux_output:
            labels.append("aux-1/2")
        return labels

    def set_output_source(self, target, source):
        index = self._refer_out_data(target)
        if source in self.labels['mixers'][index]:
            value = 0
        elif source.find('aux') == 0:
            value = 1
        else:
            raise ValueError('Invalid argument for output target')
        fb = self.__output_sources[index]
        AvcAudio.set_selector_state(self.unit.fcp, 0, 'current', fb, value)

    def get_output_source(self, target):
        index = self._refer_out_data(target)
        fb = self.__output_sources[index]
        value = AvcAudio.get_selector_state(self.unit.fcp, 0, 'current', fb)
        if value == 1 and self.__aux_output:
            return 'aux-1/2'
        return self.labels['mixers'][index]

    # 0x0000ffff - 0x7fffffff
    # db = 20 * log10(vol / 0x80000000)
    # vol = 0, then db = -144.0
    # may differs analog-in and the others.
    def get_meters(self):
        labels = self.labels['meters']
        meters = {}
        req = Hinawa.FwReq()
        frames = [0] * 256
        data = req.transaction(self.unit.get_node(),
                               Hinawa.FwTcode.READ_BLOCK_REQUEST,
                               self._ADDR_FOR_METERING, self.__meters, frames)
        for i, name in enumerate(labels):
            meters[name] = unpack('>I', data[i * 4:(i + 1) * 4])[0]
        if len(data) > len(labels) * 4:
            meters['rotery-0'] = data[-3] & 0x0f
            meters['rotery-1'] = (data[-3] & 0xf0) >> 4
            meters['rotery-2'] = 0
            meters['switch-0'] = (data[-4] & 0xf0) >> 4
            meters['switch-1'] = data[-4] & 0x0f
            meters['rate'] = AvcConnection.SAMPLING_RATES[data[-2]]
            meters['sync'] = data[-1] & 0x0f
        return meters

    def get_clock_source_labels(self):
        return self.__clocks.keys()

    def set_clock_source(self, src):
        if self.unit.get_property('streaming'):
            raise ValueError('Packet streaming already runs.')
        dst = AvcCcm.get_subunit_signal_addr('music', 0, 1)
        addr = self.__clocks[src]
        AvcCcm.set_signal_source(self.unit.fcp, addr, dst)

    def get_clock_source(self):
        dst = AvcCcm.get_subunit_signal_addr('music', 0, 1)
        curr = AvcCcm.get_signal_source(self.unit.fcp, dst)
        for name, addr in self.__clocks.items():
            if AvcCcm.compare_addrs(curr, AvcCcm.parse_signal_addr(addr)):
                return name

    def get_sampling_rate(self):
        in_rate = AvcConnection.get_plug_signal_format(self.unit.fcp, 'input',
                                                       0)
        out_rate = AvcConnection.get_plug_signal_format(
            self.unit.fcp, 'output', 0)
        if in_rate != out_rate:
            raise OSError('Unexpected state of the unit: {0}!={1}'.format(
                in_rate, out_rate))
        return in_rate