Download inference/encoder.py from KGAudioLab/instrument-aire-models: direct link, hf CLI and curl.
- Browser
- Download file 5.33 kB
-
https://huggingface.co/KGAudioLab/instrument-aire-models/resolve/main/inference/encoder.py
- Command line
-
hf download hf://KGAudioLab/instrument-aire-models/inference/encoder.py
-
curl -L -o encoder.py https://huggingface.co/KGAudioLab/instrument-aire-models/resolve/main/inference/encoder.py
5.33 kB
| 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) | |