def finalize_midi_sequence(ptrn, name="Track 1", inst=0, channel=0, tsig=(4, 4), bpm=120): """ Makes a geenrated midi sequnce valid for most players by adding TimeSignature, SetTempo and EndOfTrack events. """ has_tsig = False has_tempo = False has_end = False for evnt in utils.evnt_gen(ptrn): if isinstance(evnt, midi.SetTempoEvent): has_tsig = True if isinstance(evnt, midi.TimeSignatureEvent): has_tempo = True if isinstance(evnt, midi.EndOfTrackEvent): has_end = True if not has_tsig: ptrn[0].insert(0, midi.TimeSignatureEvent(tick=0)) ptrn[0][0].data[:2] = tsig ptrn[0][0].data[2:] = 24, 8 if not has_tempo: ptrn[0].insert(0, midi.SetTempoEvent(tick=0)) ptrn[0][0].set_bpm(bpm) if not has_end: ptrn[0].append(midi.EndOfTrackEvent(tick=0)) ptrn[0].insert(0, midi.TrackNameEvent(tick=0, text=name, data=[])) ptrn[0].insert( 0, midi.ProgramChangeEvent(tick=0, channel=channel, data=[inst])) return ptrn
def has_channels(x, typs=(midi.NoteOnEvent, midi.NoteOffEvent), channels=[9]): """ Returns True if any midi.events types in the tuple 'typs' have channels contained in the tuple 'channels' """ for evnt in utils.evnt_gen(x): if isinstance(evnt, typs) and evnt.channel in channels: return True
def is_equal_midi_sequences(a, b): """ Returns True if a and b are sequences containing equivalent midi.events objects else False. The type, .data and .tick of the evnts are compared. """ if type(a) != type(b): return False a = list(utils.evnt_gen(a)) b = list(utils.evnt_gen(b)) lena, lenb = len(a), len(b) if lena != lenb: return False else: for i in range(lena): evnt0, evnt1 = a[i], b[i] if (type(evnt0) != type(evnt1) or evnt0.data != evnt1.data or evnt0.tick != evnt1.tick): return False return True
def is_evnt_typ_uniq(x, typ): """ Returns True if x contains a single instance of typ """ uniq = False for evnt in utils.evnt_gen(x): if isinstance(evnt, typ): if uniq: return not uniq uniq = True return uniq
def has_insts(x, insts: tuple) -> bool: """ Args: x - list/midi.Pattern/midi.Track/MidiObj insts - int, midi standard numbers for instruments Returns: boolean - True if ptrn contains any types in typ """ insts = [i - 1 for i in insts] for evnt in utils.evnt_gen(x): if isinstance(evnt, midi.ProgramChangeEvent) and evnt.data[0] in insts: return True
def has_evnt_typ(x, typ=()) -> bool: """ Args: x - list/midi.Pattern/midi.Track/MidiObj typ - tuple(type(midi.event),..., type(midi.event)) Returns: boolean - True if ptrn contains any types in typ """ for evnt in utils.evnt_gen(x): if isinstance(evnt, typ): return True return False
def quantize_typ_attr(x, q, typ, func): """ Reduces the number of different available velocity values Args: x - MidiObj or midi.Pattern typ - midi.events type, i.e. midi.NoteOnEvent q - int, quantization factor """ for evnt in utils.evnt_gen(x): if isinstance(evnt, typ): evnt.data[1] = utils.quantize(func(evnt), q) return x
def get_evnt_info(x, evnt_typ, attrs=["tick", "data"], func=None): """ Returns a 2-tuple containing the count (as an int) and a list containing the value of each entry for an attribute of a particular event type. """ count, data = 0, [] for evnt in utils.evnt_gen(x): if isinstance(evnt, evnt_typ): datum = [getattr(evnt, attr) for attr in attrs] if func is not None: datum = func(datum) data.append(datum) count += 1 return count, data
def has_only_inst(x, to_keep: set) -> bool: """ Args: x - list/midi.Pattern/midi.Track/MidiObj to_keep - set{int} midi standard numbers for desired instruments all shifted by -1 Returns: bool - True if isinstance(evnt, typ) and not condition(evnt) """ all_insts = set(range(128)) to_remove = all_insts - to_keep for evnt in utils.evnt_gen(x): if isinstance(evnt, midi.ProgramChangeEvent) and evnt.data[0] in to_remove: return False return True
def has_tsig(x, tsigs=[(4, 4)]) -> bool: """ Args: x - list/midi.Pattern/midi.Track/MidiObj tsigs - list[tuple(int, int),... tuple(int, int)], time signatures encoded as a 2-tuple(numerator, denominator) Returns: boolean - True if ptrn contains any time signatures contained in tsigs """ for evnt in utils.evnt_gen(x): if isinstance(evnt, midi.TimeSignatureEvent): data = list(evnt.data[:2]) data[1] = utils.nptf(data[1]) data = tuple(data) if any([ts == data for ts in tsigs]): return True
def filter_data_of_insts(data, to_remove=set()): """ Remove midi.Pattern or MidiObj from an iterable data container if they contain instrument numbers in 'to_remove'. These numbers are same as midi standard -1. """ ret = [] for x in data: keep = True for evnt in utils.evnt_gen(x): if isinstance( evnt, midi.ProgramChangeEvent) and evnt.data[0] in to_remove: keep = False break if keep: ret.append(x) return ret
def which_insts(x, insts: tuple, uniq: bool = True) -> list: """ Args: x - list/midi.Pattern/midi.Track/MidiObj insts - int, midi standard numbers for instruments Returns: boolean - True if ptrn contains any types in typ """ ret = [] insts = [i - 1 for i in insts] for evnt in utils.evnt_gen(x): if isinstance(evnt, midi.ProgramChangeEvent): for i in insts: if i == evnt.data[0]: ret.append(i + 1) if uniq: ret = list(set(ret)) return ret
def get_min_max(x, typs=(), func=lambda x: x.tick): """ Get the minimum and maximum values for an event type. Args: x - midi.Pattern/midi.Track or python sequence containing midi.events typs - tuple, contains types of events to analyze func - callable function to expose event attribute, default is lambda x: x.tick Returns: min, max - int, int """ min_ = np.inf max_ = -np.inf for evnt in utils.evnt_gen(x): if isinstance(evnt, typs): min_ = min(min_, func(evnt)) max_ = max(max_, func(evnt)) return min_, max_
def max_simultaneous_notes(x): """ Returns the maximum number of simultaneous notes in a ptrn """ state = {} n_max = 0 for evnt in utils.evnt_gen(x): on = off = False if isinstance(evnt, midi.NoteOnEvent): on = True elif isinstance(evnt, midi.NoteOffEvent): off = True if on or off: note, vel = evnt.data[:2] if on and not vel or off: state[note] = max(state.get(note, 0) - 1, 0) elif on: state[note] = state.get(note, 0) + 1 n = sum(list(state.values())) n_max = max(n, n_max) return n_max
def time_split(x): """ Breaks up a list or midi.Track containing midi.events into a nested list where each sublist represents events which occur simultaneously. """ if isinstance(x, midi.Pattern) or any( [isinstance(trck, (list, midi.Track)) for trck in x]): x = [ trck for trck in x if utils.safe_len(trck) and isinstance(trck, (list, midi.Track)) ] x = consolidate_trcks(x) if not len(x): return [] ret = [] sub = [] for evnt in utils.evnt_gen(x): if evnt.tick != 0 and sub: ret.append(sub) sub = [] sub.append(evnt) ret.append(sub) return ret
def categorize_input(x, q, n_time, off_mode, time_encoder, ekwa, **kwargs): """ This function is the inverse of categorize_output. This function will encode a midi sequence in the form of a midi.Track/midi.Pattern (with 1 track) or a MuGen.utils.MidiObj into a encoded/serialized sequence of integers. A midi.events object, often referred to in this code as 'evnt' will be encoded as an integer, an 'avnt'. The evnt/avnt terminology is used to imitate efferent/afferent neuron terminology in neuroscience. Args: x - list/midi.Track/midi.Pattern, iterable containing midi.Events q - int, quantization factor of velocity n_time - int, number of bits in one-hot encoding that are used for time slips off_mode - bool, True if NoteOffEvents are included in input sequence time_encoder - int, a function to encode milliseconds ekwa - dict, keyword arguments for the encoder n_vel - int, number of bits in one-hot encoding that are used for velocity, this value is dependent on q q_map - dict, maps quantized velocities to encoding p_map - dict, maps added pulses/note divisions to encoding asarray - bool, default=False, whether to return output as a numpy array dtype - str, default='int', data type of the returned array bpm - int, default=120, tempo in Beats Per Minute, initial tempo of the input sequence, is updated dynamically as tempo changed in input sequence sort_velocity - bool, default=False, whether velocity values should be in sorted order sort_pitch = bool, default=False, whether pitch values should be in sorted order sort_pulse = bool, default=False, whether pulse values should be in sorted order Vars: n_pitch - int, number of bits in one-hot encoding that are used for pitch i.e. NoteOnEvents, if off_mode is True an additional 128 bits are used for NoteOffEvents otherwise x should only contain NoteOnEvents with velocity=0 to signify NoteOffEvents n_t_p - int, n_time + n_pitch n_t_p_v - int, n_time + n_pitch + n_vel n_vocab - int, n_time + n_pitch + n_vel + n_pulse, total number of bits used for one-hot encoding mspt - float, default=None, milliseconds per tick, updated dynamically as we iterate through the input sequence (x) tick - int, holds the current tick as we iterate through the input sequence (x) timestep - list, as we iterate through the input sequence all events that occur simultaneously, in a single 'timestep', will be collected here before being processed/encoded/serialized Returns: ret - list[int]/np.ndarray[int] - encoded and serialized midi sequence """ if isinstance(x, utils.MidiObj): x = x.ptrn n_vel = kwargs.get("n_vel", utils.dynamic_order(128, q)) q_map = kwargs.get("q_map", maps.create_q_map(128, q)) p_map = kwargs.get("p_map", None) asarray = kwargs.get("asarray", False) dtype = kwargs.get("dtype", "int") sort_velocity = kwargs.get("sort_velocity", False) sort_pitch = kwargs.get("sort_pitch", False) sort_pulse = kwargs.get("sort_pulse", False) bpm = kwargs.get("bpm", 120) # beats per minute n_pitch = 128 * 2 if off_mode else 128 n_pulse = len(p_map.keys()) if p_map is not None else 0 n_t_p = n_time + n_pitch n_t_p_v = n_time + n_pitch + n_vel n_vocab = n_time + n_pitch + n_vel + n_pulse # n_t_p_v_p mspt = None # miliseconds per tick tick = 0 timestep = [] # timestep/time bubble ret = [] # output sequence t_encoder = lambda x: time_encoder(x, **ekwa) args = [ret, timestep, tick, bpm, mspt] static_args = ( q, n_time, n_t_p, n_t_p_v, q_map, p_map, t_encoder, sort_velocity, sort_pitch, sort_pulse, ) _processor = lambda x: process_timestep(*x, *static_args) for evnt in utils.evnt_gen(x): if evnt.tick != 0: args = _processor(args) args[2] = tick = evnt.tick args[1].append(evnt) ret = _processor(args)[0] if asarray: ret = np.array(ret, dtype=dtype) return ret
def display_evnt_typs(x): """ A quick count of each unique event type in a midi.Track or midi.Pattern """ typs = [type(evnt) for evnt in utils.evnt_gen(x)] return counter(typs)