Spaces:
Running
Running
import json | |
import random | |
from dataclasses import dataclass | |
from pickle import dump | |
from typing import Dict, List, Sequence, Tuple | |
import numpy as np | |
""" | |
A setting for a parameter, with its oneHOT encoding | |
""" | |
class ParamValue: | |
name: str | |
value: float | |
encoding: List[float] | |
""" | |
A sample point - the parameter values, the oneHOT encoding and the audio | |
""" | |
class Sample: | |
# parameter_values: List[Tuple[str,float]] | |
# parameter_encoding:List[List[float]] | |
parameters: List[ParamValue] | |
# length:float=0.1 | |
# sample_rate:int = 44100 | |
# audio:np.ndarray = np.zeros(1) | |
def value_list(self) -> List[Tuple[str, float]]: | |
return [(p.name, p.value) for p in self.parameters] | |
def encode(self) -> List[float]: | |
return np.hstack([p.encoding for p in self.parameters]) | |
class Parameter: | |
def __init__(self, name: str, levels: list, id=""): | |
self.name = name | |
self.levels = levels | |
self.id = id | |
def get_levels(self) -> List[ParamValue]: | |
return [self.get_value(i) for i in range(len(self.levels))] | |
def sample(self) -> ParamValue: | |
index: int = random.choice(range(len(self.levels))) | |
return self.get_value(index) | |
def get_value(self, index: int) -> ParamValue: | |
encoding = np.zeros(len(self.levels)).astype(float) | |
encoding[index] = 1.0 | |
return ParamValue( | |
name=self.name, | |
# Actual value | |
value=self.levels[index], | |
# One HOT encoding | |
encoding=encoding, | |
) | |
def decode(self, one_hot: List[float]) -> ParamValue: | |
ind = np.array(one_hot).argmax() | |
# ind = tf.cast(tf.argmax(one_hot, axis=-1), "int32") | |
return self.get_value(ind) | |
def from_output( | |
self, current_output: List[float] | |
) -> Tuple[ParamValue, List[float]]: | |
param_data = current_output[: len(self.levels)] | |
remaining = current_output[len(self.levels) :] | |
my_val = self.decode(param_data) | |
return (my_val, remaining) | |
def to_json(self): | |
return {"name": self.name, "levels": self.levels, "id": self.id} | |
class ParameterSet: | |
def __init__(self, parameters: List[Parameter], fixed_parameters: dict = {}): | |
self.parameters = parameters | |
self.fixed_parameters = fixed_parameters | |
def sample_space(self, sample_size=2000) -> Sequence[Sample]: | |
print("Sampling {} points from parameter space".format(sample_size)) | |
dataset = [] | |
for i in range(sample_size): | |
params = [p.sample() for p in self.parameters] | |
dataset.append(Sample(params)) | |
if i % 1000 == 0: | |
print("Sampling iteration: {}".format(i)) | |
return dataset | |
# Runs through the whole parameter space, setting up parameters and calling the generation function | |
# Excuse slightly hacky recusions - sure there's a more numpy-ish way to do it! | |
def recursively_generate_all( | |
self, parameter_list: list = None, parameter_set=[], return_list=[] | |
) -> Sequence[Sample]: | |
print("Generating entire parameter space") | |
if parameter_list is None: | |
parameter_list = self.parameters | |
param = parameter_list[0] | |
remaining = parameter_list[1:] | |
for p in param.levels: | |
ps = parameter_set.copy() | |
ps.append((param.name, p)) | |
if len(remaining) == 0: | |
return_list.append(ps) | |
else: | |
self.recursively_generate_all(remaining, ps, return_list) | |
return return_list | |
def to_settings(self, p: Sample): | |
params = self.fixed_parameters.copy() | |
params.update(dict(p.value_list())) | |
return params | |
def encoding_to_settings(self, output: List[float]) -> Dict[str, float]: | |
params = self.fixed_parameters.copy() | |
for p in self.decode(output): | |
params[p.name] = p.value | |
return params | |
def decode(self, output: List[float]) -> List[ParamValue]: | |
values = [] | |
for p in self.parameters: | |
v, output = p.from_output(output) | |
values.append(v) | |
if len(output) > 0: | |
print("Leftover output!: {}".format(output)) | |
return values | |
def save(self, filename): | |
with open(filename, "wb") as file: | |
dump(self, file) | |
def save_json(self, filename): | |
dump = self.to_json() | |
with open(filename, "w") as file: | |
json.dump(dump, file, indent=2) | |
def explain(self): | |
levels = 0 | |
for p in self.parameters: | |
levels += len(p.levels) | |
return { | |
"n_variable": len(self.parameters), | |
"n_fixed": len(self.fixed_parameters), | |
"levels": levels, | |
} | |
def to_json(self): | |
return { | |
"parameters": [p.to_json() for p in self.parameters], | |
"fixed": self.fixed_parameters, | |
} | |
""" | |
Generates evenly spaced parameter values | |
paper: | |
The rest of the synthesizer parameters ranges are quantized evenly to 16 | |
classes according to the following ranges ... | |
For each parameter, the first and last classes correspond to its range limits | |
""" | |
def param_range(steps, min, max): | |
ext = float(max - min) | |
return [n * ext / (steps - 1) + min for n in range(steps)] | |