Module project.music_gen.core

Expand source code
import functools as ft
import itertools as it

import numpy as np

import project.esn.utils as ut
from project.music_gen.data_types import *


@ut.mydataclass(init=True, repr=True)
class gNote():
    """gNote class, wraps a note and generate the output matrix for the
note

    """
    note: Note
    _generator: callable
    tempo: Tempo
    pattern_len: int = 1

    def __add__(self, other):
        return check_instance(other, gNote, lambda x: note_zipper(self, x),
                              f"cannot add!! {other}")

    def __or__(self, other):
        return check_instance(other, gNote, lambda x: note_concat(self, x),
                              f"cannot zip !! {other}")

    def __mul__(self, other):
        return check_instance(other, int, lambda x: note_replic(self, x),
                              f"cannot replic with {other}")

    def __getitem__(self, key):
        return check_instance(key, int, lambda x: note_slice(self, x),
                              f"cannot slice with {key}")

    def __floordiv__(self, other):
        return check_instance(other, tuple,
                              lambda x: note_pattern(self, other),
                              f"cannote apply the patter {other}")

    def len(self):
        return int((self.pattern_len * 4) * (self.tempo.value / len(Quarters)))

    def __invert__(self):
        return note_sampler(self)

    def __call__(self):
        return self._generator(self.note, self.pattern_len)


gNote_ = lambda note=None, pattern_len=None, tempo=None: lambda fun: (
    lambda n, p: gNote(_generator=fun, note=n, pattern_len=p, tempo=n.tempo)
) if note is None else lambda: gNote(
    _generator=fun, note=note, pattern_len=pattern_len, tempo=tempo)


def note_sampler(note: gNote):
    """returns line by line the output matrix

    """
    for x in note():
        for el in x:
            yield el

                                                      
def note_replicator(tempo: Tempo,
                    note: Abs_note,
                    quarters: list,
                    pattern_len=1):
    """simply replicate a note for the specified quarters

    """
    return note_zipper.reduce(
        [note_generator(Note(tempo, note, x), pattern_len) for x in quarters])


''' implementation of gNote operators
'''


def merge_tempos_f(tempo: Tempo):
    """merge two note with possible different tempos

    """
    @np.vectorize
    def merge_tempos(genNote: gNote):
        def inner():
            for x in genNote():
                if genNote.tempo.value < tempo.value:
                    yield np.vstack(
                        (x, [[0 for _ in range(len(Abs_note))] for _ in range(
                            int((tempo.value - genNote.tempo.value) /
                                len(Quarters)))]))
                else:
                    yield x

        return inner()

    return merge_tempos


def note_reduce(f: callable, notes: list):
    """ reducing function for notes
    """
    return ft.reduce(f, notes)


def reducer(fun):
    setattr(fun, "reduce", lambda li: note_reduce(fun, li))
    return fun


def note_map(f: callable, notes: list):
    """mapping function for notes

    """
    return map(f, notes)


def mapper(fun):
    setattr(fun, "map", lambda li: note_map(fun, li))
    return fun


def note_slice(gnote: gNote, n: int):
    """possibly reduce the lenght of the pattern

    """
    @gNote_(note="slice", pattern_len=n, tempo=gnote.tempo)
    def gsliced(note: Note, pattern_len: int):
        return it.islice(gnote(), n * len(Quarters), None)

    return gsliced()


def note_replic(gnote: gNote, n: int):
    """multiply the lenght of the pattern

    """
    @gNote_(note="replic",
            pattern_len=gnote.pattern_len * n,
            tempo=gnote.tempo)
    def greplic(note: Note, pattern_len: int):
        for _ in range(n):
            for x in gnote():
                yield x

    return greplic()


@gNote_()
def note_generator(note: Note, pattern_len: int):
    """generator for notes

    """
    for _ in range(pattern_len):
        for x in Quarters:
            yield np.array([
                np.array([
                    note.val
                    if idx == note.note.value and x == note.quarter else 0
                    for idx in range(len(Abs_note))
                ]) for t in range(int(note.tempo.value / len(Quarters)))
            ])


def note_pattern(gnote: gNote, qua_pat: tuple):
    """modify the pattern of the note given a tuple which consist on a
tuple of the Quarter in which you want to apply the pattern and the
patter itself

    """
    quarter, pattern = qua_pat
    if not isinstance(quarter, Quarters):
        quarter = Quarters(quarter)
    c_pattern = np.array([
        np.ones(len(Abs_note)) if x != 0 else np.zeros(len(Abs_note))
        for x in pattern
    ])

    @gNote_(note="pattern", pattern_len=gnote.pattern_len, tempo=gnote.tempo)
    def gpatter(note: Note, pattern_len: int):
        for i, x in enumerate(gnote()):
            yield x if (i % 4) == quarter.value else x * c_pattern

    return gpatter()


@reducer
@mapper
def note_concat(gNote_0: gNote, gNote_1: gNote):
    """concat two notes (one after another)

    """
    max_tempo = max_tempos(gNote_0.tempo, gNote_1.tempo)
    merge_t = merge_tempos_f(max_tempo)

    @gNote_(note="concated",
            pattern_len=(gNote_0.pattern_len + gNote_1.pattern_len),
            tempo=max_tempo)
    def gconcat(note: Note, pattern_len: int):
        return it.chain(*merge_t([gNote_0, gNote_1]))

    return gconcat()


@reducer
@mapper
def note_zipper(gNote_0: gNote, gNote_1: gNote):
    """zip the notes (play the 2 note in the same measure)

    """
    max_tempo = max_tempos(gNote_0.tempo, gNote_1.tempo)
    merge_t = merge_tempos_f(max_tempo)

    @gNote_(note="zipped", pattern_len=gNote_0.pattern_len, tempo=max_tempo)
    def gzipped(note: Note, pattern_len: int):
        for (x, y) in it.zip_longest(*merge_t([gNote_0, gNote_1]),
                                     fillvalue=np.zeros(
                                         (int(max_tempo.value / len(Quarters)),
                                          len(Abs_note)))):
            yield x + y

    return gzipped()

Functions

def gNote_(note=None, pattern_len=None, tempo=None)
Expand source code
gNote_ = lambda note=None, pattern_len=None, tempo=None: lambda fun: (
    lambda n, p: gNote(_generator=fun, note=n, pattern_len=p, tempo=n.tempo)
) if note is None else lambda: gNote(
    _generator=fun, note=note, pattern_len=pattern_len, tempo=tempo)
def mapper(fun)
Expand source code
def mapper(fun):
    setattr(fun, "map", lambda li: note_map(fun, li))
    return fun
def merge_tempos_f(tempo: Tempo)

merge two note with possible different tempos

Expand source code
def merge_tempos_f(tempo: Tempo):
    """merge two note with possible different tempos

    """
    @np.vectorize
    def merge_tempos(genNote: gNote):
        def inner():
            for x in genNote():
                if genNote.tempo.value < tempo.value:
                    yield np.vstack(
                        (x, [[0 for _ in range(len(Abs_note))] for _ in range(
                            int((tempo.value - genNote.tempo.value) /
                                len(Quarters)))]))
                else:
                    yield x

        return inner()

    return merge_tempos
def note_concat(gNote_0: gNote, gNote_1: gNote)

concat two notes (one after another)

Expand source code
@reducer
@mapper
def note_concat(gNote_0: gNote, gNote_1: gNote):
    """concat two notes (one after another)

    """
    max_tempo = max_tempos(gNote_0.tempo, gNote_1.tempo)
    merge_t = merge_tempos_f(max_tempo)

    @gNote_(note="concated",
            pattern_len=(gNote_0.pattern_len + gNote_1.pattern_len),
            tempo=max_tempo)
    def gconcat(note: Note, pattern_len: int):
        return it.chain(*merge_t([gNote_0, gNote_1]))

    return gconcat()
def note_generator(n, p)
Expand source code
lambda n, p: gNote(_generator=fun, note=n, pattern_len=p, tempo=n.tempo)
def note_map(f: , notes: list)

mapping function for notes

Expand source code
def note_map(f: callable, notes: list):
    """mapping function for notes

    """
    return map(f, notes)
def note_pattern(gnote: gNote, qua_pat: tuple)

modify the pattern of the note given a tuple which consist on a tuple of the Quarter in which you want to apply the pattern and the patter itself

Expand source code
def note_pattern(gnote: gNote, qua_pat: tuple):
    """modify the pattern of the note given a tuple which consist on a
tuple of the Quarter in which you want to apply the pattern and the
patter itself

    """
    quarter, pattern = qua_pat
    if not isinstance(quarter, Quarters):
        quarter = Quarters(quarter)
    c_pattern = np.array([
        np.ones(len(Abs_note)) if x != 0 else np.zeros(len(Abs_note))
        for x in pattern
    ])

    @gNote_(note="pattern", pattern_len=gnote.pattern_len, tempo=gnote.tempo)
    def gpatter(note: Note, pattern_len: int):
        for i, x in enumerate(gnote()):
            yield x if (i % 4) == quarter.value else x * c_pattern

    return gpatter()
def note_reduce(f: , notes: list)

reducing function for notes

Expand source code
def note_reduce(f: callable, notes: list):
    """ reducing function for notes
    """
    return ft.reduce(f, notes)
def note_replic(gnote: gNote, n: int)

multiply the lenght of the pattern

Expand source code
def note_replic(gnote: gNote, n: int):
    """multiply the lenght of the pattern

    """
    @gNote_(note="replic",
            pattern_len=gnote.pattern_len * n,
            tempo=gnote.tempo)
    def greplic(note: Note, pattern_len: int):
        for _ in range(n):
            for x in gnote():
                yield x

    return greplic()
def note_replicator(tempo: Tempo, note: Abs_note, quarters: list, pattern_len=1)

simply replicate a note for the specified quarters

Expand source code
def note_replicator(tempo: Tempo,
                    note: Abs_note,
                    quarters: list,
                    pattern_len=1):
    """simply replicate a note for the specified quarters

    """
    return note_zipper.reduce(
        [note_generator(Note(tempo, note, x), pattern_len) for x in quarters])
def note_sampler(note: gNote)

returns line by line the output matrix

Expand source code
def note_sampler(note: gNote):
    """returns line by line the output matrix

    """
    for x in note():
        for el in x:
            yield el
def note_slice(gnote: gNote, n: int)

possibly reduce the lenght of the pattern

Expand source code
def note_slice(gnote: gNote, n: int):
    """possibly reduce the lenght of the pattern

    """
    @gNote_(note="slice", pattern_len=n, tempo=gnote.tempo)
    def gsliced(note: Note, pattern_len: int):
        return it.islice(gnote(), n * len(Quarters), None)

    return gsliced()
def note_zipper(gNote_0: gNote, gNote_1: gNote)

zip the notes (play the 2 note in the same measure)

Expand source code
@reducer
@mapper
def note_zipper(gNote_0: gNote, gNote_1: gNote):
    """zip the notes (play the 2 note in the same measure)

    """
    max_tempo = max_tempos(gNote_0.tempo, gNote_1.tempo)
    merge_t = merge_tempos_f(max_tempo)

    @gNote_(note="zipped", pattern_len=gNote_0.pattern_len, tempo=max_tempo)
    def gzipped(note: Note, pattern_len: int):
        for (x, y) in it.zip_longest(*merge_t([gNote_0, gNote_1]),
                                     fillvalue=np.zeros(
                                         (int(max_tempo.value / len(Quarters)),
                                          len(Abs_note)))):
            yield x + y

    return gzipped()
def reducer(fun)
Expand source code
def reducer(fun):
    setattr(fun, "reduce", lambda li: note_reduce(fun, li))
    return fun

Classes

class gNote (note: Note, _generator: , tempo: Tempo, pattern_len: int = 1)

gNote class, wraps a note and generate the output matrix for the note

Expand source code
class gNote():
    """gNote class, wraps a note and generate the output matrix for the
note

    """
    note: Note
    _generator: callable
    tempo: Tempo
    pattern_len: int = 1

    def __add__(self, other):
        return check_instance(other, gNote, lambda x: note_zipper(self, x),
                              f"cannot add!! {other}")

    def __or__(self, other):
        return check_instance(other, gNote, lambda x: note_concat(self, x),
                              f"cannot zip !! {other}")

    def __mul__(self, other):
        return check_instance(other, int, lambda x: note_replic(self, x),
                              f"cannot replic with {other}")

    def __getitem__(self, key):
        return check_instance(key, int, lambda x: note_slice(self, x),
                              f"cannot slice with {key}")

    def __floordiv__(self, other):
        return check_instance(other, tuple,
                              lambda x: note_pattern(self, other),
                              f"cannote apply the patter {other}")

    def len(self):
        return int((self.pattern_len * 4) * (self.tempo.value / len(Quarters)))

    def __invert__(self):
        return note_sampler(self)

    def __call__(self):
        return self._generator(self.note, self.pattern_len)

Class variables

var pattern_len : int

Methods

def len(self)
Expand source code
def len(self):
    return int((self.pattern_len * 4) * (self.tempo.value / len(Quarters)))