git.haldean.org symrep / 219f29d
add audio module with to_wav method (untested) Will Haldean Brown 6 years ago
6 changed file(s) with 102 addition(s) and 51 deletion(s). Raw diff Collapse all Expand all
11 *.pyc
22 *.dot
33 *.png
4 *.wav
0 from base import *
1 from . import audio
0 import array
1 import math
2 import struct
3 import symrep
4
5 def sine(freq):
6 return symrep.Node(
7 "sin", lambda t: math.sin(t * freq(t) / (2 * math.pi)), [freq])
8
9 def to_wav(root, sample_rate, length, stream):
10 num_samples = int(math.ceil(length * sample_rate))
11 print 'generating', num_samples, 'samples'
12 bits_per_sample = 32
13
14 def sample_t():
15 i = t = 0
16 while i < num_samples:
17 yield t
18 i += 1
19 t += 1 / sample_rate
20
21 data = array.array("f", (root(t) for t in sample_t()))
22 print 'data is', len(data), 'samples'
23 import pprint; pprint.pprint(data[:20])
24
25 # begin file header
26 stream.write("RIFF")
27 stream.write(struct.pack("<I",
28 # length of file is length of remaining bytes in the file header, length
29 # of the format header, length of the data header and the data itself
30 4 + 24 + 8 + len(data)))
31 stream.write("WAVE")
32
33 # begin fmt block header
34 stream.write("fmt ")
35 stream.write(struct.pack("<IHHIIHH",
36 16, # length of format block
37 1, # use PCM encoding
38 1, # number of channels
39 sample_rate,
40 int((sample_rate * bits_per_sample) / 8.),
41 bits_per_sample / 8,
42 bits_per_sample,
43 ))
44
45 # begin data header
46 stream.write("data")
47 stream.write(struct.pack("<I", len(data)))
48 data.tofile(stream)
49
50 if __name__ == "__main__":
51 with open("test.wav", "w") as f:
52 to_wav(sine(symrep.const(440)), 41000, 10, f)
0 def const(val):
1 return Node("const {}".format(val), lambda _: val, [])
2
3 def sum(n1, n2):
4 return Node("add", lambda t: n1(t) + n2(t), [n1, n2])
5
6 def product(n1, n2):
7 return Node("product", lambda t: n1(t) * n2(t), [n1, n2])
8
9 class Node(object):
10 _next_id = 0
11
12 def __init__(self, name, func, deps):
13 self.id = Node._next_id
14 Node._next_id += 1
15
16 self.name = name
17 self.func = func
18 self.deps = deps
19
20 def __call__(self, t):
21 return self.func(t)
22
23 def collect_nodes(root):
24 return set([root]).union(
25 reduce(set.union, map(collect_nodes, root.deps), set()))
26
27 def collect_edges(root):
28 edges = [(root.id, dep.id) for dep in root.deps]
29 for dep in root.deps:
30 edges.extend(collect_edges(dep))
31 return edges
32
33 def to_dot(root, stream, name="symrep"):
34 stream.write("digraph {name} {{\n".format(name=name))
35 for node in collect_nodes(root):
36 stream.write("{id} [label=\"{name}\"];\n".format(
37 id=node.id, name=node.name))
38 for n1, n2 in collect_edges(root):
39 stream.write("{n2} -> {n1};\n".format(n1=n1, n2=n2))
40 stream.write("}\n")
+0
-47
symrep.py less more
0 import math
1
2 def const(val):
3 return Node("const {}".format(val), lambda _: val, [])
4
5 def sum(n1, n2):
6 return Node("add", lambda t: n1(t) + n2(t), [n1, n2])
7
8 def product(n1, n2):
9 return Node("product", lambda t: n1(t) * n2(t), [n1, n2])
10
11 def sine(freq):
12 return Node(
13 "sin", lambda t: math.sin(t * freq(t) / (2 * math.pi)), [freq])
14
15 class Node(object):
16 _next_id = 0
17
18 def __init__(self, name, func, deps):
19 self.id = Node._next_id
20 Node._next_id += 1
21
22 self.name = name
23 self.func = func
24 self.deps = deps
25
26 def __call__(self, t):
27 return self.func(t)
28
29 def collect_nodes(root):
30 return set([root]).union(
31 reduce(set.union, map(collect_nodes, root.deps), set()))
32
33 def collect_edges(root):
34 edges = [(root.id, dep.id) for dep in root.deps]
35 for dep in root.deps:
36 edges.extend(collect_edges(dep))
37 return edges
38
39 def to_dot(root, stream, name="symrep"):
40 stream.write("digraph {name} {{\n".format(name=name))
41 for node in collect_nodes(root):
42 stream.write("{id} [label=\"{name}\"];\n".format(
43 id=node.id, name=node.name))
44 for n1, n2 in collect_edges(root):
45 stream.write("{n2} -> {n1};\n".format(n1=n1, n2=n2))
46 stream.write("}\n")
00 import symrep
1 import symrep.audio
12 import unittest
23
34
2728 self.assertEqual(n(0), -2)
2829
2930 def test_sine(self):
30 n = symrep.sine(symrep.const(1))
31 n = symrep.audio.sine(symrep.const(1))
3132 self.assertEqual(n(0), 0)
3233 self.assertEqual(n(0.25), 1)
3334 self.assertEqual(n(0.5), 0)
3637
3738 def test_collect_nodes(self):
3839 n1 = symrep.const(1)
39 n2 = symrep.sine(n1)
40 n2 = symrep.audio.sine(n1)
4041 n3 = symrep.sum(n1, n2)
4142 n4 = symrep.const(-2)
4243 n5 = symrep.product(n3, n4)
4849
4950 def test_dot(self):
5051 n = symrep.product(
51 symrep.sine(symrep.const(2)),
52 symrep.audio.sine(symrep.const(2)),
5253 symrep.product(
53 symrep.sine(symrep.const(3)),
54 symrep.audio.sine(symrep.const(3)),
5455 symrep.sum(
5556 symrep.const(1),
5657 symrep.const(1),