experimentations/sound2newspaper.py

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)}")