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