/
main.py
91 lines (81 loc) · 4.02 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import sys
from .formatter import formatters
from . import WavDecoder
import argparse
import wave
import logging
# returns either 'wav' or 'aiff'
# on Python versions > 3.13 will use python-filetype instead of the deprecated sndhdr
def get_file_type(filename):
try:
import sndhdr
kind = sndhdr.what(filename)
if kind is None:
return None
return kind[0]
except ImportError:
# no sndhdr means we're on python > 3.12
# https://peps.python.org/pep-0594/#sndhdr
import filetype
logging.warning("sndhdr is deprecated, using python-filetype instead")
kind = filetype.guess(filename)
if kind is None:
return None
if kind.mime == "audio/x-wav":
return 'wav'
elif kind.mime == "audio/x-aiff":
return 'aiff'
return None
def main():
aparser = argparse.ArgumentParser(description=("Convert WAV and AIFF files "
"to vector (SVG, PostScript,"
" CVS) graphics."),
epilog="The output is sent to stdout.")
aparser.add_argument("filename", help="The WAV file to read")
aparser.add_argument("--format", "-f", default="SVG", type=str,
choices=formatters.keys(),
help="The output format, one of: SVG, CSV, PostScript. Default is SVG.")
aparser.add_argument("--width", default=1000,
type=int, help=("Maximum width of generated SVG "
"(graphic will be scaled down to "
"this size in px)"))
aparser.add_argument("--height", default=500,
type=int, help="Maximum height of generated SVG (graphic will be scaled down to this size in px). Note that this scales according to the highest possible amplitude (given the sample bit depth), not the highest amplitude that actually occurs in the data.")
aparser.add_argument("--stream", metavar="BS", default=0, type=int,
help=("Stream the input file size in chunks (of BS "
"number of frames at a time) and process/format "
"each chunk separately. Useful for conserving "
"memory when processing large files, but note "
"that multi-channel paths will be split up into "
"BS-sized chunks. By default BS=0, which causes "
"the entire file to be read into memory before "
"processing."))
aparser.add_argument("--downtoss", default=1,
type=int, help="Downsample by keeping only 1 out of every N samples.", metavar="N")
aparser.add_argument("--log", dest="loglevel",
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR',
'CRITICAL'], help="Set the logging level.",
default='ERROR', type=str)
args = aparser.parse_args()
# setup logging
logging.basicConfig(level=logging.getLevelName(args.loglevel))
# Test whether WAV or AIFF
decoder_class = wave
sndtype = get_file_type(args.filename)
if sndtype is None:
logging.error(
"Unknown file type (should be either WAV or AIFF): %s" % args.filename)
sys.exit(1)
logging.debug("sndtype: ", sndtype)
if sndtype == 'aiff' or sndtype == 'aifc':
import aifc
decoder_class = aifc
# setup decoder and formatter
decoder = WavDecoder(args.filename, decoder_class=decoder_class, bs=args.stream,
max_width=args.width, max_height=args.height,
downtoss=args.downtoss)
formatter_class = formatters[args.format]
logging.debug("formatter_class: %s" % formatter_class)
formatter = formatter_class(decoder)
# decode and format
formatter.output(sys.stdout)