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)