From 31bd021985fbe3cf1a069ca3398b1a43d52a1583 Mon Sep 17 00:00:00 2001 From: Thomas Buck Date: Thu, 15 Feb 2024 18:49:07 +0100 Subject: [PATCH] add argparse to render script, add makefile. --- Makefile | 28 +++++++++++++++++++++ README.md | 3 +++ render.py | 73 +++++++++++++++++++++++++++++++------------------------ 3 files changed, 72 insertions(+), 32 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0cf0377 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +inputs = $(wildcard *.svg) + +outputs = $(inputs:%.svg=output/%_44100.wav) +outputs += $(inputs:%.svg=output/%_48000.wav) +outputs += $(inputs:%.svg=output/%_96000.wav) +outputs += $(inputs:%.svg=output/%_192000.wav) + +.PHONY: all +all: output ${outputs} + +output: + mkdir output + +output/%_44100.wav: %.svg + ./render.py -s 44100 -o $(@:%.svg=%_44100.wav) $< + +output/%_48000.wav: %.svg + ./render.py -s 48000 -o $(@:%.svg=%_48000.wav) $< + +output/%_96000.wav: %.svg + ./render.py -s 96000 -o $(@:%.svg=%_96000.wav) $< + +output/%_192000.wav: %.svg + ./render.py -s 192000 -o $(@:%.svg=%_192000.wav) $< + +.PHONY: clean +clean: + rm -rf output diff --git a/README.md b/README.md index ce30116..34292ff 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,6 @@ Please see [my blog post for more details](https://www.xythobuz.de/osci_music_pl Quick start instructions can be found in the top comment of the Python script in this repo. ssh osci-music "sudo systemctl disable --now osci.service" && scp osci-pi.py osci-music:~ && ssh osci-music "sudo systemctl enable --now osci.service" + +Also include a simple script to convert single SVG paths to audio. +Run the included makefile to convert the SVG files in this directory. diff --git a/render.py b/render.py index 44004fe..35bf408 100755 --- a/render.py +++ b/render.py @@ -23,15 +23,10 @@ import sys import wave +import argparse from svgpathtools import svg2paths -samplerate = 44100 #192000 -volume_percent = 70 -path_steps = 10 -default_duration = 5.0 -default_outfile = "out.wav" - -def read_image(filename): +def read_image(filename, path_steps, volume_percent): paths, attributes = svg2paths(filename) path = paths[0] if len(paths) > 1: @@ -72,16 +67,25 @@ def read_image(filename): p.append(v) return p + def add_segment(p1, p2, f): + p = interpolate(p1, p2, f) + add_point(p) + for n in range(0, len(points) - 1): - p1 = points[n] - p2 = points[n + 1] for step in range(0, path_steps): - p = interpolate(p1, p2, step / path_steps) - add_point(p) - add_point(points[len(points) - 1]) + add_segment(points[n], points[n + 1], step / path_steps) + + #add_point(points[len(points) - 1]) + + for n in range(len(points) - 2, -1, -1): + for step in range(0, path_steps): + add_segment(points[n + 1], points[n], step / path_steps) + + add_point(points[0]) + return data -def write_waveform(data, filename): +def write_waveform(data, filename, samplerate): with wave.open(filename, "w") as f: f.setnchannels(2) f.setsampwidth(2) @@ -89,26 +93,31 @@ def write_waveform(data, filename): f.writeframes(data) def main(): - if len(sys.argv) <= 1: - print("Usage:") - print("\t" + sys.argv[0] + " image.png [out.wav] [seconds]") - sys.exit(1) - - if len(sys.argv) == 3: - duration = float(sys.argv[2]) - outfile = default_outfile - if len(sys.argv) >= 4: - duration = float(sys.argv[2]) - outfile = sys.argv[3] - else: - duration = default_duration - outfile = default_outfile - - wave = read_image(sys.argv[1]) + parser = argparse.ArgumentParser( + prog=sys.argv[0], + description='Render SVG path to vector XY audio file', + epilog='Made by Thomas Buck . Licensed as GPLv3.') + + parser.add_argument("input", help="Input SVG image file path.") + parser.add_argument("-o", "--output", dest="output", default="out.wav", + help="Output wav sound file path. Defaults to 'out.wav'.") + parser.add_argument("-t", "--time", dest="time", default=5.0, type=float, + help="Length of sound file in seconds. Defaults to 5s.") + parser.add_argument("-s", "--samplerate", dest="samplerate", default=44100, type=int, + help="Samplerate of output file in Hz. Defaults to 44.1kHz.") + parser.add_argument("-v", "--volume", dest="volume", default=100.0, type=float, + help="Volume of output file in percent. Defaults to 100%%.") + parser.add_argument("-i", "--interpolate", dest="interpolate", default=10, type=int, + help="Steps on interpolated paths. Defaults to 10.") + + args = parser.parse_args() + print(args) + + wave = read_image(args.input, args.interpolate, args.volume) samplecount = int(len(wave) / 2 / 2) # stereo, int16 - drawrate = samplerate / samplecount - drawcount = drawrate * duration + drawrate = args.samplerate / samplecount + drawcount = drawrate * args.time print("len={} samples={} drawrate={:.2f} count={:.2f}".format(len(wave), samplecount, drawrate, drawcount)) data = bytearray() @@ -116,7 +125,7 @@ def main(): data.extend(wave) print("len={}".format(len(data))) - write_waveform(bytes(data), outfile) + write_waveform(bytes(data), args.output, args.samplerate) if __name__ == "__main__": main()