108 lines
4.2 KiB
Python
108 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Copyright (C) 2022 harrysentonbury
|
|
# GNU General Public License v3.0
|
|
|
|
# Plots soundfiles to look like a page of a newspaper or something.
|
|
# example:-
|
|
# python3 sound2newspaper.py -i ./audio/go.wav -x 1080 -y 720
|
|
|
|
import argparse
|
|
from audio2numpy import open_audio
|
|
import numpy as np
|
|
from PIL import Image
|
|
import scipy.io.wavfile as wf
|
|
|
|
|
|
parser = argparse.ArgumentParser(prog="Wavies")
|
|
parser.add_argument("-x", "--xpixels", nargs="?", type=int, default=1080,
|
|
help="Witdth of image, default: 1080")
|
|
parser.add_argument("-y", "--ypixels", nargs="?", type=int, default=1000,
|
|
help="Height of image, will be rounded down to nearest line number, default: 1000")
|
|
parser.add_argument("-d", "--draw_both_channels", action="store_false",
|
|
help="If stereo, draw both channels on same line")
|
|
parser.add_argument("-l", "--leap", nargs="?", type=int, default=1,
|
|
help="Amount of samples to step, default: 1")
|
|
parser.add_argument("-i", "--infile", nargs="?", help="Input as [path to filename.wav]", type=str)
|
|
parser.add_argument("-o", "--outfile", nargs="?", help="Output as <path to filename.png>", type=str)
|
|
parser.add_argument("-c", "--colorbackground", nargs="?", type=int, default=0,
|
|
help="Backgount color 0->255, default: 0 (black)")
|
|
parser.add_argument("-f", "--foreground", nargs="*", type=int, default=[-1],
|
|
help="Foreground color, will make two tone")
|
|
parser.add_argument("-s", "--startposition", nargs="?", type=int, default=0,
|
|
help="Start position sample number, default: 0")
|
|
args = parser.parse_args()
|
|
if args.xpixels < 1 or args.ypixels < 25:
|
|
parser.exit("xpixels or ypixels size to low")
|
|
if not args.infile:
|
|
parser.exit("Must supply infile")
|
|
if (leap := args.leap) < 1:
|
|
parser.exit("Leap must be positive integer")
|
|
|
|
|
|
def builder(iter, s, e):
|
|
for i, p in enumerate(range(s, e)):
|
|
if len(fg) != 3 and fg[0] >= 0:
|
|
oscillogram_0[y[p] + (line * iter), i, :] = np.uint8(fg[0])
|
|
elif len(fg) == 3:
|
|
oscillogram_0[y[p] + (line * iter), i, :] = (
|
|
np.uint8(fg[0]),
|
|
np.uint8(fg[1]),
|
|
np.uint8(fg[2])
|
|
)
|
|
else:
|
|
oscillogram_0[y[p] + (line * iter), i, :] = (
|
|
np.uint8(255 - (iter * 6.5)),
|
|
np.uint8(),
|
|
np.uint8(iter * 6.5)
|
|
)
|
|
|
|
|
|
try:
|
|
line = 25
|
|
length = args.xpixels
|
|
fg = args.foreground
|
|
|
|
if args.infile.endswith(".mp3"):
|
|
data, sample_rate = open_audio(args.infile)
|
|
else:
|
|
sample_rate, data = wf.read(args.infile)
|
|
data = np.float64(data)
|
|
sound_start = 0 if (start := args.startposition) < 0 or start >= np.size(data, axis=0) else start
|
|
print(f"Infile sample size and shape: {data.shape}")
|
|
if len(data.shape) == 1 or not args.draw_both_channels:
|
|
if np.size(data, axis=0) % length != 0:
|
|
if len(data.shape) == 1:
|
|
extra = np.zeros((length - (np.size(data, axis=0) % length), ))
|
|
else:
|
|
extra = np.zeros((length - (np.size(data, axis=0) % length), 2))
|
|
data = np.concatenate((data, extra))
|
|
y = data[sound_start::leap]
|
|
else:
|
|
if np.size(data, axis=0) % length != 0:
|
|
extra = np.zeros((length - (np.size(data, axis=0) % length), 2))
|
|
data = np.concatenate((data, extra))
|
|
y = data[sound_start::leap, 0]
|
|
|
|
y = ((y / np.max(np.abs(y)) + 1) * (line / 2 - 1))
|
|
y = np.round(y).astype(int)
|
|
max_lines = np.size(y, axis=0) // length
|
|
numit = args.ypixels // line
|
|
iterations = max_lines if numit > max_lines else numit
|
|
print(f"Number of plot lines: {iterations}")
|
|
|
|
oscillogram_0 = np.zeros((iterations * line, length, 3), dtype=np.uint8) + args.colorbackground
|
|
for i in range(iterations):
|
|
builder(i, i * length, (i * length) + length)
|
|
|
|
h, w, d = oscillogram_0.shape
|
|
print(f"Image size: {w} x {h}")
|
|
|
|
new_image = Image.fromarray(oscillogram_0, "RGB")
|
|
new_image.show()
|
|
if args.outfile:
|
|
new_image.save(args.outfile)
|
|
print("image saved")
|
|
except Exception as e:
|
|
parser.exit(f"{type(e).__name__} : {str(e)}")
|