File size: 5,333 Bytes
345ccf6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 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)
|