@_arguments.accept([_UNIT_TYPES], add_varargs=True)
def Chain(units):
    """
    Units connected in series.
    """
    # handle a single list argument differently for backward compatibility
    if len(units) == 1 and type(units[0]) == list:
        units = units[0]

    return _Chain(units) @_arguments.accept([_UNIT_TYPES], add_varargs=True,
                   kwargs={'remove_duplicates': (True, False, None)})
def Fork(units, **kwargs):
    """
    Units connected in parallel.
    """
    remove_duplicates = (kwargs['remove_duplicates']
                            if 'remove_duplicates' in kwargs else None)

    # handle a single list argument differently for backward compatibility
    if len(units) == 1 and type(units[0]) == list:
        units = units[0]

    return _Fork(units, remove_duplicates) _UNIT_TYPES = (_Unit, list, dict, _constants._EventType) @_unitrepr.accept(_arguments.flatten(_util.port_number), add_varargs=True)
def PortFilter(ports):
    """
    PortFilter(ports, ...)

    Filter events by port name or number. The *ports* argument can be a single
    port or a list of multiple ports.
    """
    return _Filter(_mididings.PortFilter(map(_util.actual, ports))) @_unitrepr.accept(_arguments.flatten(_util.channel_number), add_varargs=True)
def ChannelFilter(channels):
    """
    ChannelFilter(channels, ...)

    Filter events by channel number. The *channels* argument can be a single
    channel number or a list of multiple channel numbers.
    System events (which don't carry channel information) are discarded.
    """
    return _Filter(_mididings.ChannelFilter(map(_util.actual, channels))) @_overload.mark(
    """
    KeyFilter(note_range)
    KeyFilter(lower, upper)
    KeyFilter(lower=...)
    KeyFilter(upper=...)
    KeyFilter(notes=...)

    Filter note events by key (note number or note range). All other events
    are let through.
    The last form expects its argument to be a list of individual note names
    or numbers.
    """
)
@_unitrepr.accept(_util.note_range)
def KeyFilter(note_range):
    return _Filter(_mididings.KeyFilter(note_range[0], note_range[1], [])) @_overload.mark(
    """
    VelocityFilter(value)
    VelocityFilter(lower=...)
    VelocityFilter(upper=...)
    VelocityFilter(lower, upper)

    Filter note-on events by velocity. All other events are let through.
    """
)
@_unitrepr.accept(_util.velocity_value)
def VelocityFilter(value):
    return _Filter(_mididings.VelocityFilter(value, value + 1)) @_unitrepr.accept(_arguments.flatten(_util.ctrl_number), add_varargs=True)
def CtrlFilter(ctrls):
    """
    CtrlFilter(ctrls, ...)

    Filter control change events by controller number. The *ctrls* argument
    can be a single controller or a list of multiple controller numbers.
    All other events are discarded.
    """
    return _Filter(_mididings.CtrlFilter(ctrls)) @_overload.mark(
    """
    CtrlValueFilter(value)
    CtrlValueFilter(lower=...)
    CtrlValueFilter(upper=...)
    CtrlValueFilter(lower, upper)

    Filter control change events by controller value. All other events are
    discarded.
    """
)
@_unitrepr.accept(_util.ctrl_value)
def CtrlValueFilter(value):
    return _Filter(_mididings.CtrlValueFilter(value, value + 1)) @_unitrepr.accept(_arguments.flatten(_util.program_number), add_varargs=True)
def ProgramFilter(programs):
    """
    ProgramFilter(programs, ...)

    Filter program change events by program number. The *programs* argument
    can be a single program number or a list of program numbers.
    All other events are discarded.
    """
    return _Filter(_mididings.ProgramFilter(map(_util.actual, programs))) @_overload.mark(
    r"""
    SysExFilter(sysex)
    SysExFilter(manufacturer=...)

    Filter system exclusive events by the data they contain, specified as a
    string or as a sequence of integers.
    If *sysex* does not end with ``F7``, partial matches that start with the
    given data bytes are accepted.

    Alternatively, a sysex manufacturer id can be specified, which may be
    either a string or a sequence of integers, with a length of one or three
    bytes.

    All non-sysex events are discarded.
    """
)
@_unitrepr.accept(_functools.partial(_util.sysex_data, allow_partial=True))
def SysExFilter(sysex):
    partial = (sysex[-1] != '\xf7')
    return _Filter(_mididings.SysExFilter(sysex, partial))
from mididings.units.base import Chain, Fork, _UNIT_TYPES
from mididings.units.filters import (
        PortFilter, ChannelFilter, KeyFilter, VelocityFilter,
        CtrlFilter, CtrlValueFilter, ProgramFilter, SysExFilter)
import mididings.overload as _overload
import mididings.arguments as _arguments
import mididings.util as _util
    Split events by input port, with *mapping* being a dictionary of the form
    ``{ports: patch, ...}``.
    """
    return _make_split(PortFilter, mapping)


@_arguments.accept({
    _arguments.nullable(_arguments.flatten(_util.channel_number, tuple)):
        _UNIT_TYPES
})
def ChannelSplit(mapping):
    """
    ChannelSplit(mapping)

    Split events by input channel, with *mapping* being a dictionary of
    the form ``{channels: patch, ...}``.
    """
    return _make_split(ChannelFilter, mapping)


@_overload.mark(
    """
    KeySplit(threshold, patch_lower, patch_upper)
    KeySplit(mapping)

    Split events by key. Non-note events are sent to all patches.

    The first version splits at a single threshold. The second version allows
    an arbitrary number of (possibly overlapping) note ranges, with *mapping*
    being a dictionary of the form ``{note_range: patch, ...}``.
    """
)
@_arguments.accept(_util.note_limit, _UNIT_TYPES, _UNIT_TYPES)
def KeySplit(threshold, patch_lower, patch_upper):
    return _make_threshold(KeyFilter(0, threshold),
                           patch_lower, patch_upper)

@_overload.mark
@_arguments.accept({_arguments.nullable(_util.note_range): _UNIT_TYPES})
def KeySplit(mapping):
    return _make_split(KeyFilter, mapping)


@_overload.mark(
    """
    VelocitySplit(threshold, patch_lower, patch_upper)
    VelocitySplit(mapping)

    Split events by note-on velocity. Non-note events are sent to all patches.

    The first version splits at a single threshold. The second version allows
    an arbitrary number of (possibly overlapping) value ranges, with
    *mapping* being a dictionary of the form ``{(lower, upper): patch, ...}``.
    """
)
@_arguments.accept(_util.velocity_limit, _UNIT_TYPES, _UNIT_TYPES)
def VelocitySplit(threshold, patch_lower, patch_upper):
    return _make_threshold(VelocityFilter(0, threshold),
                           patch_lower, patch_upper)

@_overload.mark
@_arguments.accept({_arguments.nullable(_util.velocity_range): _UNIT_TYPES})
def VelocitySplit(mapping):
    return _make_split(VelocityFilter, mapping, unpack=True)


@_arguments.accept({
    _arguments.nullable(_arguments.flatten(_util.ctrl_number, tuple)):
        _UNIT_TYPES
})
def CtrlSplit(mapping):
    """
    CtrlSplit(mapping)

    Split events by controller number, with *mapping* being a dictionary of
    the form ``{ctrls: patch, ...}``.
    Non-control-change events are discarded.
    """
    return _make_split(CtrlFilter, mapping)


@_overload.mark(
    """
    CtrlValueSplit(threshold, patch_lower, patch_upper)
    CtrlValueSplit(mapping)

    Split events by controller value.

    The first version splits at a single threshold. The second version allows
    an arbitrary number of (possibly overlapping) value ranges, with *mapping*
    being a dictionary of the form ``{value: patch, ...}`` or
    ``{(lower, upper): patch, ...}``.

    Non-control-change events are discarded.

    """
)
@_arguments.accept(_util.ctrl_limit, _UNIT_TYPES, _UNIT_TYPES)
def CtrlValueSplit(threshold, patch_lower, patch_upper):
    return _make_threshold(CtrlValueFilter(0, threshold),
                           patch_lower, patch_upper)

@_overload.mark
@_arguments.accept({_arguments.nullable(_util.ctrl_range): _UNIT_TYPES})
def CtrlValueSplit(mapping):
    return _make_split(CtrlValueFilter, mapping, unpack=True)


@_arguments.accept({
    _arguments.nullable(_arguments.flatten(_util.program_number, tuple)):
        _UNIT_TYPES
})
def ProgramSplit(mapping):
    """
    ProgramSplit(mapping)

    Split events by program number, with *mapping* being a dictionary of the
    form ``{programs: patch, ...}``.
    Non-program-change events are discarded.
    """

    return _make_split(ProgramFilter, mapping)