note_length_groupings

this module privides constant note-length (rhythm) groupings pauses should be implemented as a gate on the trigger and are not implemented in the groupings

reconsider specifying pauses with negative values(to-do: check implications on other modules)

roqba.static.note_length_groupings.analyze_grouping(g)[source]

transform the grouping into a binary pattern for every beat, i.e.:

>>> analyze_grouping([1,2,1,3])
[1, 1, 0, 1, 1, 0, 0]
roqba.static.note_length_groupings.badly_formeD(meter_length, to_check)[source]

checks if a grouping is well-formed

checks if the sum of items equals the specified target length

roqba.static.note_length_groupings.cut_grouping_to_size(grouping, size)[source]

cut the grouping to the required size

it will sanitize the last entry if necessary this function is called in case a non-existing size is required

roqba.static.note_length_groupings.get_grouping(meter, mode, check=True)[source]

returns the groupings for a given meter and mode

well_formedness is checked by default, “default”-mode will combine first, second and terns-modes

"""this module privides constant note-length (rhythm) groupings
pauses should be implemented as a gate on the trigger and
are not implemented in the groupings

reconsider specifying pauses with negative values(to-do:
check implications on other modules)
"""

DEFAULT_METER_LENGTH = (8, (4, 4))

groupings = {
    (8, (4, 4)): {
        "first": [
            # smallest units
            # 8: 8x1
            [[1, 1, 1, 1, 1, 1, 1, 1]] * 3,
            # 7: 6x1 & 1x2
            [[2, 1, 1, 1, 1, 1, 1]] * 6,
            [[1, 2, 1, 1, 1, 1, 1]] * 4,
            [[1, 1, 2, 1, 1, 1, 1]] * 6,
            [[1, 1, 1, 2, 1, 1, 1]] * 4,
            [[1, 1, 1, 1, 2, 1, 1]] * 6,
            [[1, 1, 1, 1, 1, 2, 1]] * 4,
            [[1, 1, 1, 1, 1, 1, 2]] * 6,
            # 6: 5x1 & 1x3
            [[3, 1, 1, 1, 1, 1]] * 2,
            [[1, 3, 1, 1, 1, 1]] * 2,
            [[1, 1, 3, 1, 1, 1]] * 2,
            [[1, 1, 1, 3, 1, 1]] * 2,
            [[1, 1, 1, 1, 3, 1]] * 2,
            [[1, 1, 1, 1, 1, 3]] * 2,
            # 5: 1x4 & 1x4
            [[4, 1, 1, 1, 1]] * 2,
            [[1, 4, 1, 1, 1]] * 2,
            [[1, 1, 4, 1, 1]] * 2,
            [[1, 1, 1, 4, 1]] * 2,
            [[1, 1, 1, 1, 4]] * 2,
            # 4: 1x5 3x1
            [[5, 1, 1, 1]] * 2,
            [[1, 5, 1, 1]] * 2,
            [[1, 1, 5, 1]] * 2,
            [[1, 1, 1, 5]] * 2,
            # 3: 1x6 & 2x1
            [[6, 1, 1]] * 2,
            [[1, 6, 1]] * 2,
            [[1, 1, 6]] * 2,
            # 2: 1x7 & 1x1
            [[7, 1]] * 2,
            [[1, 7]] * 2,
            # 1: 1x8
            [[8]] * 1],
        "second": [
            # second smallest units:
            # 5 3x2 & 2x1
            [[1, 1, 2, 2, 2]] * 8,
            [[2, 1, 1, 2, 2]] * 8,
            [[2, 2, 1, 1, 2]] * 8,
            [[2, 2, 2, 1, 1]] * 8,
            # 4: 4x2
            [[2, 2, 2, 2]] * 10,
            # 3: 1x4 & 2x2
            [[4, 2, 2]] * 8,
            [[2, 4, 2]] * 8,
            [[2, 2, 4]] * 8,
            # 2 1x6 & 1x2
            [[6, 2]] * 6,
            [[2, 6]] * 6],
        "terns": [
            # second smallest units:
            #  2x3 & 2x1
            [[3, 3, 1, 1]] * 8,
            [[3, 1, 1, 3]] * 3,
            [[1, 1, 3, 3]] * 8,
            #  2x3 & 2x1
            [[2, 3, 3]] * 8,
            [[3, 2, 3]] * 3,
            [[3, 3, 2]] * 8,
            # 2 x (2, 1) & 2x1
            [[2, 2, 1, 2, 1]] * 8,
            [[2, 1, 2, 2, 1]] * 5,
            [[2, 1, 2, 1, 2]] * 8],
        "heavy": [
            # 5 3x2 & 2x1
            [[2, 1, 1, 2, 2]] * 3,
            [[2, 2, 1, 1, 2]] * 3,
            [[2, 2, 2, 1, 1]] * 3,

            # 6 2x2 & 4x1
            [[2, 1, 1, 2, 1, 1]] * 3,
            [[1, 1, 2, 1, 1, 2]] * 3,
            # 4: 4x2
            [[2, 2, 2, 2]] * 5,
            # 3: 1x4 & 2x2
            [[4, 2, 2]] * 8,
            [[2, 4, 2]] * 8,
            [[2, 2, 4]] * 8,
            # 5 1x4 & 1x2 & 2x1
            [[4, 2, 1, 1]] * 3,
            [[2, 1, 1, 4]] * 3,
            [[1, 1, 4, 2]] * 3,
            [[4, 1, 1, 2]] * 3,
            [[1, 1, 2, 4]] * 3,
            [[4, 1, 1, 2]] * 3,
            # 2 1x6 & 1x2
            [[6, 2]] * 6,
            [[2, 6]] * 6]},
    (8, (2, 2, 2, 2)): {
        "heavy": [
            [[2, 2, 2, 2]] * 17,
            [[4, 2, 2]] * 7,
            [[2, 4, 2]] * 7,
            [[2, 2, 4]] * 7,
        ],
        "first": [
            [[1, 1, 1, 1, 1, 1, 1, 1]] * 3,
            [[2, 1, 1, 1, 1, 1, 1]] * 10,
            [[2, 1, 1, 2, 1, 1]] * 10],
        "second": [
            [[2, 2, 1, 1, 1, 1]] * 10,
            [[2, 1, 1, 2, 2]] * 10],
        "terns": [
            [[2, 1, 2, 1, 1, 1]] * 10,
            [[2, 1, 1, 1, 2, 1]] * 10,
            [[1, 1, 1, 2, 1, 2]] * 10]
    },
    (8, (3, 3, 2)): {
        "heavy": [
            [[3, 3, 2]] * 20,
            [[3, 3, 1, 1]] * 5,
            [[2, 1, 2, 1, 2]] * 5,
            [[2, 1, 2, 1, 1, 1]] * 5,
            [[1, 1, 1, 2, 1, 1, 1]] * 5
        ]
    },
    (5, (2, 3)): {
        "heavy": [
            [[5]] * 7,
            [[2, 3]] * 20,
            [[2, 2, 1]] * 5,

            [[1, 1, 3]] * 5,
            [[1, 1, 2, 1]] * 5,
            [[1, 1, 1, 1, 1]] * 5,
            [[2, 1, 1, 1]] * 5
        ]
    },
    (5, (3, 2)): {
        "heavy": [
            [[5]] * 7,
            [[3, 2]] * 20,
            [[2, 1, 2]] * 5,

            [[1, 1, 1, 2]] * 5,
            [[1, 1, 1, 1, 1]] * 5,
            [[2, 1, 1, 1]] * 5]
    },
    (6, (3, 3)): {
        "heavy": [
            [[6]] * 3,
            [[3, 3]] * 15,
            [[3, 2, 1]] * 3,
            [[2, 1, 2, 1]] * 6,
            [[3, 1, 1, 1]] * 3,
            [[1, 1, 1, 3]] * 3,
            [[2, 2, 2]] * 4]
    },
    (6, (2, 2, 2)): {
        "heavy": [
            [[6]] * 3,
            [[2, 2, 2]] * 15,
            [[3, 1, 2]] * 6,
            [[1, 1, 2, 2]] * 3,
            [[2, 1, 1, 2]] * 3,
            [[2, 2, 1, 1]] * 3,
            [[2, 2, 1, 1]] * 3,
            [[1, 1, 1, 1, 2]] * 4,
            [[2, 1, 1, 1, 1]] * 4,
            [[1, 1, 2, 1, 1]] * 4]
    },
    (7, (3, 2, 2)): {
        "heavy": [
            [[5, 2]] * 7,
            [[3, 2, 2]] * 20,
            [[2, 1, 2, 2]] * 7,

            [[1, 1, 1, 2, 2]] * 5,
            [[1, 1, 1, 1, 1, 1, 1]] * 5,
            [[2, 1, 1, 1, 1, 1]] * 7,
            [[1, 1, 1, 1, 1, 2]] * 5,
            [[2, 1, 2, 1, 1]] * 7]
    },
    (9, (3, 3, 3)): {
        "heavy": [
            [[6, 3]] * 7,
            [[3, 6]] * 7,
            [[3, 3, 3]] * 30,
            [[2, 1, 2, 1, 2, 1]] * 7,
            [[1, 1, 1, 1, 1, 1, 1, 1, 1]] * 5,
            [[2, 1, 1, 1, 1, 1, 1, 1]] * 5,
            [[2, 1, 2, 1, 1, 1, 1]] * 7,
            [[3, 3, 1, 1, 1]] * 5,
            [[3, 2, 1, 1, 2]] * 7]
    },
    (11, (3, 3, 3, 2)): {
        "heavy": [
            [[6, 3, 2]] * 7,
            [[3, 6, 2]] * 7,
            [[3, 3, 3, 2]] * 30,
            [[2, 1, 2, 1, 2, 1, 2]] * 7,

            [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] * 5,
            [[2, 1, 1, 1, 1, 1, 1, 1, 2]] * 5,
            [[2, 1, 2, 1, 1, 1, 1, 2]] * 7,
            [[3, 3, 1, 1, 1, 1, 1]] * 5,
            [[3, 2, 1, 1, 2, 2]] * 7]
    },
    (12, (3, 3, 2, 2, 2)): {
        "heavy": [
            [[3, 3, 2, 2, 2]] * 17,
            [[3, 3, 6]] * 7,
            [[3, 3, 4, 2]] * 10,
            [[2, 1, 2, 1, 2, 2, 2]] * 7,
            [[1, 1, 1, 2, 1, 2, 1, 1, 2]] * 7,
            [[1, 2, 2, 1, 2, 2, 2]] * 7,
            [[2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] * 5],
        "first": [
            [[2, 1, 2, 1, 2, 2, 1, 1]] * 10,
            [[1, 1, 1, 2, 1, 1, 1, 2, 1, 1]] * 10,
            [[2, 1, 2, 1, 1, 1, 2, 1, 1]] * 10],
        "second": [
            [[2, 1, 2, 1, 2, 2, 2]] * 10,
            [[3, 3, 2, 2, 2]] * 10],
        "terns": [
            [[1, 1, 2, 1, 1, 2, 1, 2, 1]] * 10,
            [[1, 2, 1, 2, 1, 2, 1, 2]] * 10,
            [[2, 1, 2, 1, 2, 1, 2, 1]] * 10]
    },
    (12, (2, 2, 2, 3, 3)): {
        "heavy": [
            [[2, 2, 2, 3, 3]] * 7,
            [[6, 3, 3]] * 7,
            [[4, 2, 3, 3]] * 30,
            [[2, 2, 2, 2, 1, 2, 1]] * 7,
            [[2, 2, 2, 1, 2, 2, 1]] * 7,
            [[2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] * 5],
        "first": [
            [[1, 1, 2, 1, 1, 2, 1, 2, 1]] * 15,
            [[2, 1, 1, 1, 1, 2, 1, 2, 1]] * 5,
            [[1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1]] * 10],
        "terns": [
            [[1, 1, 2, 1, 1, 2, 1, 2, 1]] * 10,
            [[1, 2, 1, 2, 1, 2, 1, 2]] * 10,
            [[2, 1, 2, 1, 2, 1, 2, 1]] * 10]
    },
    (12, (1, 2, 2, 1, 2, 2, 2)): {
        "heavy": [
            [[1, 2, 2, 1, 2, 2, 2]] * 20,
            [[3, 3, 2, 2, 2]] * 7],
        "first": [
            [[1, 1, 1, 2, 1, 2, 1, 2, 1]] * 10,
            [[1, 1, 1, 1, 1, 1, 2, 2, 2]] * 10,
            [[1, 2, 2, 1, 1, 1, 1, 1, 2]] * 10],
        "terns": [
            [[2, 1, 2, 1, 2, 1, 2, 1]] * 10,
            [[1, 2, 1, 2, 1, 2, 1, 2]] * 10,
            [[3, 3, 1, 1, 1, 3]] * 10]
    },
    (15, (3, 3, 2, 3, 2, 2)): {
        "heavy": [
            [[3, 3, 2, 3, 2, 2]] * 20,
            [[3, 3, 1, 1, 3, 1, 1, 2]] * 5,
            [[2, 1, 2, 1, 2, 2, 1, 2, 2]] * 25,
        ],
        "first": [
            [[2, 1, 2, 1, 2, 2, 1, 2, 1, 1]] * 25,
         ],
        "second": [
            [[2, 1, 1, 1, 1, 2, 2, 1, 2, 2]] * 25,
         ],
        "terns": [
            [[3, 3, 2, 3, 2, 2]] * 20,
        ]
    },
    (23, (3, 3, 2, 3, 3, 2, 3, 2, 2)): {
        "heavy": [
            [[3, 3, 2, 3, 3, 2, 3, 2, 2]] * 20,
        ],
        "first": [
            [[3, 2, 1, 1, 1, 3, 2, 1, 2, 3, 2, 1, 1]] * 20,
         ],
        "second": [
            [[3, 2, 1, 1, 1, 3, 2, 1, 2, 3, 2, 1, 1]] * 20,
         ],
        "terns": [
            [[2, 1, 3, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2]] * 20,
        ]
    },
    (24, (1, 2, 2, 1, 2, 2, 2, 1, 2, 2, 1, 2, 2, 2)): {
        "heavy": [
            [[2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1]] * 20,
            [[3, 3, 3, 3, 3, 3, 3, 3]] * 7],
        "first": [
            [[1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1]] * 10,
            [[1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2]] * 10,
            [[1, 2, 2, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 2]] * 10],
        "terns": [
            [[2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1]] * 10,
            [[1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2]] * 10,
            [[3, 3, 1, 1, 1, 3, 3, 3, 1, 1, 1, 3]] * 10]
    },
    (24, (2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1)): {
        "heavy": [
            [[2, 1, 3, 2, 1, 3, 2, 1, 2, 1, 2, 1, 2, 1]] * 20,
            [[3, 3, 3, 3, 3, 3, 3, 3]] * 7],
        "first": [
            [[3, 3, 3, 3, 3, 3, 3, 3]] * 7,
            [[2, 1, 2, 6, 1, 2, 1, 2, 1, 2, 1, 2, 1]] * 20],
        "second": [
            [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
              1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] * 7],
        "terns": [
            [[1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1, 2, 1]] * 10],
    },
    (30, (3, 2, 2, 3, 2, 2, 3, 2, 2, 3, 2, 2, 2)): {
        "heavy": [
            [[2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2]] * 20,
            [[3, 4, 3, 4, 3, 4, 3, 4, 2]] * 7,
            [[3, 2, 2, 3, 2, 2, 3, 2, 2, 3, 2, 2, 2]] * 7],
        "first": [
            [[1, 1, 1, 2, 2, 3, 2, 1, 1, 1, 1, 1, 2, 2, 3, 2, 2, 1, 1]] * 7],
        "second": [
            [[3, 4, 7, 1, 1, 1, 2, 1, 1, 3, 2, 1, 1, 2]] * 3],
        "terns": [
            [[3, 3, 1, 3, 3, 1, 3, 3, 1, 3, 3, 3]] * 10],
    }
}


def get_grouping(meter, mode, check=True):
    '''returns the groupings for a given meter and mode

    well_formedness is checked by default, "default"-mode
    will combine first, second and terns-modes
    '''
    mode = None if mode == "default" else mode
    meter_length = meter if type(meter) == int else meter[0]
    res = _assemble(meter, mode, meter_length=meter_length)
    if check:
        if badly_formeD(meter_length, res):
            raise RuntimeError(
                "badly formed rhythm grouping. length: {}\nmode: {}\npattern: {}".format(
                    meter_length, mode, res))
    return res


def _assemble(id, which=None, fallback=True, meter_length=DEFAULT_METER_LENGTH):
    '''assembles note-length groupings.

    it is called during the loading of the module'''
    if id not in groupings.keys():
        raise RuntimeError("KeyError: specified meter not found.")
    target = groupings[id]

    if which:
        if which not in target.keys():
            if fallback:
                res = groupings[DEFAULT_METER_LENGTH][which]
                return cut_grouping_to_size(sum(res, []), meter_length)
            else:
                raise RuntimeError("non-existing meter mode")
        else:
            return sum(target[which], [])
    else:
        return (_assemble(id, "first", fallback, meter_length) +
                _assemble(id, "second", fallback, meter_length) +
                _assemble(id, "terns", fallback, meter_length))


def cut_grouping_to_size(grouping, size):
    '''cut the grouping to the required size

    it will sanitize the last entry if necessary
    this function is called in case a non-existing size is required'''
    res = []
    for group in grouping:
        new = []
        for i in group:
            new.append(i)
            sum_ = sum(new)
            if sum_ >= size:
                new.pop()
                diff = size - sum(new)
                if diff != 0:
                    new.append(abs(diff))
                break
        if sum_ < size:
            new.append(size - sum_)
        res.append(new)
    return res

DEFAULT_NOTE_LENGTH_GROUPINGS = _assemble(DEFAULT_METER_LENGTH)
DEFAULT_FAST_GROUPINGS = _assemble(DEFAULT_METER_LENGTH, "first")
DEFAULT_TERNARY_GROUPINGS = _assemble(DEFAULT_METER_LENGTH, "terns")
DEFAULT_SLOWER_GROUPINGS = _assemble(DEFAULT_METER_LENGTH, "heavy")


def analyze_grouping(g):
    """transform the grouping into a binary pattern for every beat, i.e.:

    >>> analyze_grouping([1,2,1,3])
    [1, 1, 0, 1, 1, 0, 0]
    """
    res = []
    for item in g:
        first = True
        for n in xrange(item):
            if first:
                res.append(1)
            else:
                res.append(0)
            first = False
    return res


def badly_formeD(meter_length, to_check):
    '''checks if a grouping is well-formed

    checks if the sum of items equals the specified target length'''
    odd = filter(lambda x: sum(x) != meter_length, to_check)
    return bool(odd)

if badly_formeD(DEFAULT_METER_LENGTH[0], DEFAULT_NOTE_LENGTH_GROUPINGS):
    raise RuntimeError('''not all note length groupings are well-formed:
            \n{0}\n\nin:{1}'''.format(badly_formeD(DEFAULT_METER_LENGTH,
                                      DEFAULT_NOTE_LENGTH_GROUPINGS),
                                      DEFAULT_NOTE_LENGTH_GROUPINGS))