1# Generate WT files from various formulae 2 3import math 4import numpy as np 5import matplotlib.pyplot as plt 6 7 8def savewt(fn, samples): 9 with open(fn, "wb") as outf: 10 outf.write(b'vawt') 11 outf.write(int(len(samples[0]) / 2).to_bytes(4, byteorder='little')) 12 outf.write(len(samples).to_bytes(2, byteorder='little')) 13 outf.write(bytes([4, 0])) 14 for d in samples: 15 outf.write(d) 16 17 18def readwt(fn): 19 data = [] 20 with open(fn, "rb") as f: 21 ident = f.read(4) 22 wavsz = int.from_bytes(f.read(4), 'little') 23 wavct = int.from_bytes(f.read(2), 'little') 24 flags = int.from_bytes(f.read(2), 'little') 25 print("{} waves of size {}".format(wavct, wavsz)) 26 for i in range(wavct): 27 raw = f.read(wavsz * 2) 28 d = [] 29 for j in range(0, wavsz * 2, 2): 30 ls = raw[j] 31 ms = raw[j + 1] 32 if(ms >= 128): 33 ms -= 256 34 v = ls + ms * 256 35 d.append(v / 32768.0) 36 data.append(np.array(d)) 37 return data 38 39 40def npftoi15bin(fsamples): 41 """Given an array of float samples in range [-1,1] return an array of bytes representing the 2^15 int""" 42 res = [] 43 for f in fsamples: 44 v = f * 16384.0 45 ls = int(v % 256) 46 ms = int(v / 256) 47 if(v < 0 and ls != 0): 48 ms -= 1 49 if(ls == 256): 50 ls = 0 51 if(ms < 0): 52 ms += 256 53 if(ms == 256): 54 ms = 0 55 res.append(ls) 56 res.append(ms) 57 return bytearray(res) 58 59 60def generatewt(filename, genf, res, tables): 61 """Given res as a function of npline,int -> npline to generate the nth table, 62 genereate the wt file""" 63 line = np.linspace(0, 1, res, endpoint=False) 64 dat = [] 65 for i in range(tables): 66 di = genf(line, i + 1) 67 dat.append(npftoi15bin(di)) 68 savewt(filename, dat) 69 70 71def comparewt(fn1, fn2): 72 d1 = readwt(fn1) 73 l1 = np.linspace(0, 1, len(d1[0]), endpoint=False) 74 75 d2 = readwt(fn2) 76 l2 = np.linspace(0, 1, len(d2[0]), endpoint=False) 77 78 if(len(d1) != len(d2)): 79 print(" Lenghts don't match", len(d1), " ", len(d2)) 80 return 81 82 for i in range(len(d1)): 83 e1 = d1[i] 84 e2 = d2[i] 85 plt.plot(l1, e1) 86 plt.plot(l2, e2) 87 plt.show() 88 89 90def comparereswt(fn): 91 lr = "resources/data/wavetables/" + fn + ".wt" 92 hr = "resources/data/wavetables/" + fn + " HQ.wt" 93 comparewt(lr, hr) 94 95 96def sinepower(ls, n): 97 return np.sin(2.0 * math.pi * ls)**(4 * (n - 1) + 1) 98 99 100def sinepd(ls, n): 101 if(n == 1): 102 coeff = 1 103 elif(n == 2): 104 coeff = 1.5 105 else: 106 coeff = n - 1 107 108 return np.sin(2.0 * math.pi * (ls ** (coeff))) 109 110 111def sinehalf(ls, n): 112 return np.sin(math.pi * ls * n) 113 114 115def sinefm2(x, n): 116 c = np.sin(2 * math.pi * x * 2) 117 r = np.sin(2 * math.pi * x + (n - 1) * c / 2) 118 return r 119 120 121def sinefm3(x, n): 122 c = np.sin(2 * math.pi * x * 3) 123 r = np.sin(2 * math.pi * x + (n - 1) * c / 2) 124 return r 125 126 127def sinewindowed(x, n): 128 xc = x - 0.5 + 1 / (2 * len(x)) 129 w = np.exp(-18.0 * (xc ** 2)) 130 r = np.sin((-1)**n * 2.0 * math.pi * n * x) * w 131 return r 132 133 134def squarewindowed(x, n): 135 xc = x - 0.5 + 1 / (2 * len(x)) 136 w = np.exp(-18.0 * (xc ** 2)) 137 sq = np.where(np.sin((-1)**n * 2.0 * math.pi * n * x) < 0, -1, 1) 138 r = sq * w 139 return r 140 141 142def make_high_quality_of(name, genf): 143 infn = "resources/data/wavetables/" + name + ".wt" 144 outfn = "resources/data/wavetables/" + name + " HQ.wt" 145 ind = readwt(infn) 146 147 generatewt(outfn, genf, 512, len(ind)) 148 149 150make_high_quality_of("generated/Sine PD", sinepd) 151make_high_quality_of("generated/Sine Half", sinehalf) 152make_high_quality_of("generated/Sine Power", sinepower) 153make_high_quality_of("generated/Sine FM 2x", sinefm2) 154make_high_quality_of("generated/Sine FM 3x", sinefm3) 155make_high_quality_of("generated/Sine Windowed", sinewindowed) 156make_high_quality_of("generated/Square Windowed", squarewindowed) 157