Browse Source

another attempt, using svg path for rendering to audio

Thomas Buck 10 months ago
parent
commit
f98f6890ad
3 changed files with 89 additions and 42 deletions
  1. 41
    42
      render.py
  2. BIN
      toolbox.png
  3. 48
    0
      toolbox.svg

+ 41
- 42
render.py View File

@@ -20,49 +20,45 @@
20 20
 
21 21
 import sys
22 22
 import wave
23
-
24
-import pyaudio
25
-from PIL import Image
23
+from svgpathtools import svg2paths
26 24
 
27 25
 samplerate = 44100 #192000
28 26
 default_duration = 5.0
29
-
30
-def read_image(image):
31
-    print("image: width={} height={} total={}".format(image.width, image.height, image.width * image.height))
32
-
33
-    # resize coordinates for conversion to amplitude values
34
-    max_len = max(image.width, image.height)
35
-    fact = 32767 / max_len
36
-    sw, sh = int(image.width * fact), int(image.height * fact)
37
-    print("amplitude: width={} height={}".format(sw, sh))
27
+default_outfile = "out.wav"
28
+
29
+def read_image(filename):
30
+    paths, attributes = svg2paths(filename)
31
+    path = paths[0]
32
+    if len(paths) > 1:
33
+        print("WARNING: multiple paths in file. will just draw first one.")
34
+
35
+    print("paths={} segments={}".format(len(paths), len(path)))
36
+
37
+    points = [[path[0].start.real, path[0].start.imag]]
38
+    p_min = [points[0][0], points[0][1]]
39
+    p_max = [points[0][0], points[0][1]]
40
+    for segment in path:
41
+        p = [segment.end.real, segment.end.imag]
42
+        for i in range(0, 2):
43
+            if p[i] < p_min[i]:
44
+                p_min[i] = p[i]
45
+            if p[i] > p_max[i]:
46
+                p_max[i] = p[i]
47
+        points.append(p)
48
+    print("min={} max={}".format(p_min, p_max))
38 49
 
39 50
     data = bytearray()
40
-    for x in range(0, image.width):
41
-        for y in range(0, image.height):
42
-            if image.getpixel((x, y))[3] > 127:
43
-                xc, yc = int((x - (image.width / 2)) * fact), int((y - (image.height / 2)) * fact)
44
-                data.extend(yc.to_bytes(2, byteorder="little", signed=True))
45
-                data.extend(xc.to_bytes(2, byteorder="little", signed=True))
51
+    for n in range(0, len(points)):
52
+        for i in range(0, 2):
53
+            v = points[n][i]
54
+            v -= p_min[i]
55
+            v /= p_max[i] - p_min[i]
56
+            c = int((v * 2 - 1) * (32767 / 100 * 70))
57
+            data.extend(c.to_bytes(2, byteorder="little", signed=True))
46 58
     return data
47 59
 
48
-def play_waveform(data):
49
-    pa = pyaudio.PyAudio()
50
-
51
-    # int16
52
-    stream = pa.open(format=pa.get_format_from_width(2),
53
-                    channels=2,
54
-                    rate=samplerate,
55
-                    output=True)
56
-
57
-    stream.write(data, int(len(data) / 4), True)
58
-
59
-    stream.stop_stream()
60
-    stream.close()
61
-
62
-    pa.terminate()
63
-
64
-def write_waveform(data):
65
-    with wave.open("out.wav", "w") as f:
60
+def write_waveform(data, filename):
61
+    with wave.open(filename, "w") as f:
66 62
         f.setnchannels(2)
67 63
         f.setsampwidth(2)
68 64
         f.setframerate(samplerate)
@@ -71,16 +67,20 @@ def write_waveform(data):
71 67
 def main():
72 68
     if len(sys.argv) <= 1:
73 69
         print("Usage:")
74
-        print("\t" + sys.argv[0] + " image.png [seconds]")
70
+        print("\t" + sys.argv[0] + " image.png [out.wav] [seconds]")
75 71
         sys.exit(1)
76 72
 
77
-    if len(sys.argv) >= 3:
73
+    if len(sys.argv) == 3:
74
+        duration = float(sys.argv[2])
75
+        outfile = default_outfile
76
+    if len(sys.argv) >= 4:
78 77
         duration = float(sys.argv[2])
78
+        outfile = sys.argv[3]
79 79
     else:
80 80
         duration = default_duration
81
+        outfile = default_outfile
81 82
 
82
-    with Image.open(sys.argv[1]) as image:
83
-        wave = read_image(image)
83
+    wave = read_image(sys.argv[1])
84 84
 
85 85
     samplecount = int(len(wave) / 2 / 2) # stereo, int16
86 86
     drawrate = samplerate / samplecount
@@ -92,8 +92,7 @@ def main():
92 92
         data.extend(wave)
93 93
     print("len={}".format(len(data)))
94 94
 
95
-    #play_waveform(bytes(data))
96
-    write_waveform(bytes(data))
95
+    write_waveform(bytes(data), outfile)
97 96
 
98 97
 if __name__ == "__main__":
99 98
     main()

BIN
toolbox.png View File


+ 48
- 0
toolbox.svg View File

@@ -0,0 +1,48 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+<svg
5
+   width="18.108976mm"
6
+   height="21.025101mm"
7
+   viewBox="0 0 18.108976 21.025101"
8
+   version="1.1"
9
+   id="svg1"
10
+   inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
11
+   sodipodi:docname="toolbox.svg"
12
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
14
+   xmlns="http://www.w3.org/2000/svg"
15
+   xmlns:svg="http://www.w3.org/2000/svg">
16
+  <sodipodi:namedview
17
+     id="namedview1"
18
+     pagecolor="#505050"
19
+     bordercolor="#eeeeee"
20
+     borderopacity="1"
21
+     inkscape:showpageshadow="0"
22
+     inkscape:pageopacity="0"
23
+     inkscape:pagecheckerboard="0"
24
+     inkscape:deskcolor="#d1d1d1"
25
+     inkscape:document-units="mm"
26
+     inkscape:zoom="9.7401438"
27
+     inkscape:cx="34.291075"
28
+     inkscape:cy="39.732473"
29
+     inkscape:window-width="1920"
30
+     inkscape:window-height="1020"
31
+     inkscape:window-x="0"
32
+     inkscape:window-y="420"
33
+     inkscape:window-maximized="1"
34
+     inkscape:current-layer="layer1" />
35
+  <defs
36
+     id="defs1" />
37
+  <g
38
+     inkscape:label="Layer 1"
39
+     inkscape:groupmode="layer"
40
+     id="layer1"
41
+     transform="translate(-45.755199,-45.487921)">
42
+    <path
43
+       inkscape:connector-curvature="0"
44
+       id="path32"
45
+       style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.0352778;stroke-opacity:1"
46
+       d="m 54.809672,66.488838 -0.0032,-0.0019 -0.0033,0.0019 -0.0032,-0.0057 -9.011829,-5.198633 h -0.0153 l 0.0029,-10.567212 9.035378,-5.20901 9.028924,5.208587 v 4.160979 h -0.01739 l 0.01739,0.03006 -1.862843,1.074597 1.862843,1.074702 -5.64e-4,9.88e-4 5.64e-4,2.82e-4 v 4.202042 h -0.0088 l 0.0088,0.01517 -9.027266,5.207453 -0.0032,0.0057 m 0.897926,-11.088264 v 0 l 0.01475,8.37463 6.246777,-3.580133 v -2.057153 l -1.864043,-1.075267 -1.899073,1.095516 V 55.99825 l 3.763116,-2.170818 v -2.029848 l -7.160824,-4.130957 -7.164416,4.133003 v 8.395441 l 6.19276,3.569109 v -7.281566 l -1.492427,0.863212 -1.871098,-1.079429 5.594315,-3.236454 1.871909,1.079994 -2.231742,1.290637" />
47
+  </g>
48
+</svg>

Loading…
Cancel
Save