import math from types import SimpleNamespace import torch model_module=SimpleNamespace(INSTRUMENTS=(),MOODS=(),ROLES=()) def encode_request(request, rate=16, tail=1.0, velocity_mode='original'): if velocity_mode not in ('original', 'disabled'): raise ValueError('velocity_mode must be original or disabled') tempo = request['tempo_bpm'] num, den = request['time_signature'] if not math.isfinite(tempo) or tempo <= 0 or (not math.isfinite(request['bar_origin_beats'])): raise ValueError('Invalid tempo/bar origin') if type(num) is not int or type(den) is not int or num <= 0 or (den <= 0) or den & den - 1: raise ValueError('Invalid time signature') notes = request['notes'] for note in notes: if type(note['pitch']) is not int: raise ValueError('Pitch must be an integer') if 'velocity' in note and (type(note['velocity']) is not int or not 1 <= note['velocity'] <= 127): raise ValueError('Sounding velocity must be an integer in 1..127') controls = request['controls'] lo, hi = (controls['cc_min'], controls['cc_max']) if type(lo) is not int or type(hi) is not int or (not 0 <= lo <= hi <= 127): raise ValueError('CC bounds must be integers in 0..127') if controls['instrument'] not in model_module.INSTRUMENTS: raise ValueError('Unsupported instrument family') if rate not in (16, 48) or not math.isfinite(tail) or tail < 0: raise ValueError('Invalid rate/tail') for note in notes: if not (math.isfinite(note['start']) and math.isfinite(note['end']) and (0 <= note['start'] < note['end']) and (0 <= note['pitch'] <= 127)): raise ValueError('Invalid note') enabled = velocity_mode != 'disabled' and controls['use_velocity'] and all(('velocity' in n for n in notes)) and (len({n.get('velocity') for n in notes}) > 1) note_end = max((n['end'] for n in notes), default=0) hard_end = note_end + tail if notes else 0 length = math.ceil(hard_end * rate) features = torch.zeros(length, 25) rolls = [torch.zeros(length, 128) for _ in range(3)] lead = torch.full((length,), 128, dtype=torch.long) onsets = sorted({n['start'] for n in notes}) representatives = {s: max((n['pitch'] for n in notes if n['start'] == s)) for s in onsets} group_index = {s: i for i, s in enumerate(onsets)} num, den = request['time_signature'] bar_length = num * 4 / den origin = request['bar_origin_beats'] cap = lambda x, k: min(max(x, 0), k) / k for i in range(length): t, end = (i / rate, min((i + 1) / rate, hard_end)) active = [(j, n) for j, n in enumerate(notes) if n['start'] <= t < n['end']] starts = [n for n in notes if t <= n['start'] < end] stops = [n for n in notes if t <= n['end'] < end] for roll, events in zip(rolls, ([n for _, n in active], starts, stops)): for n in events: roll[i, n['pitch']] += 1 f = features[i] f[0:3] = torch.tensor([bool(active), bool(starts), bool(stops)]) if active: _, n = max(active, key=lambda item: (item[1]['start'], item[1]['pitch'], -item[0])) lead[i] = n['pitch'] g = group_index[n['start']] f[3:10] = torch.tensor([n['velocity'] / 127 if enabled else 0, cap(t - n['start'], 8), cap(n['end'] - t, 8), cap(n['end'] - n['start'], 8), (t - n['start']) / (n['end'] - n['start']), (n['pitch'] - representatives[onsets[g - 1]]) / 12 if g else 0, (representatives[onsets[g + 1]] - n['pitch']) / 12 if g + 1 < len(onsets) else 0]) pitches = [n['pitch'] for _, n in active] f[16:18] = torch.tensor([len(active) / 4, (max(pitches) - min(pitches)) / 24]) else: previous = [n['end'] for n in notes if n['end'] <= t] following = [n['start'] for n in notes if n['start'] > t] f[10:12] = torch.tensor([cap(t - max(previous), 8) if previous else 0, cap(min(following) - t, 8) if following else 0]) f[12:16] = torch.tensor([math.sin(2 * math.pi * (t - origin)), math.cos(2 * math.pi * (t - origin)), math.sin(2 * math.pi * (t - origin) / bar_length), math.cos(2 * math.pi * (t - origin) / bar_length)]) f[18:21] = torch.tensor([cap(t, 16), cap(note_end - t, 16), cap(hard_end - t, 16)]) if starts: f[21] = (min((n['start'] for n in starts)) - t) * rate f[23] = cap(sum((n['end'] - n['start'] for n in starts)) / len(starts), 8) f[24] = sum((n['velocity'] for n in starts)) / len(starts) / 127 if enabled else 0 if stops: f[22] = (min((n['end'] for n in stops)) - t) * rate inputs = dict(lead_pitch=lead[None], pitch_roll=rolls[0][None], onset_roll=rolls[1][None], offset_roll=rolls[2][None], frame_numeric=features[None], mood_id=torch.tensor([model_module.MOODS.index(controls['mood'])]), role_id=torch.tensor([model_module.ROLES.index(controls['track_role'])]), instrument_id=torch.tensor([model_module.INSTRUMENTS.index(controls['instrument'])]), ctrl_numeric=torch.tensor([[lo / 127, hi / 127, request['tempo_bpm'] / 200, num / 8, den / 8, float(enabled)]]), mask=torch.ones(1, length, dtype=torch.bool)) return (inputs, hard_end)