-
Notifications
You must be signed in to change notification settings - Fork 685
/
Copy pathsox_utils.py
116 lines (104 loc) · 3.44 KB
/
sox_utils.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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import subprocess
import sys
import warnings
def get_encoding(dtype):
encodings = {
"float32": "floating-point",
"int32": "signed-integer",
"int16": "signed-integer",
"uint8": "unsigned-integer",
}
return encodings[dtype]
def get_bit_depth(dtype):
bit_depths = {
"float32": 32,
"int32": 32,
"int16": 16,
"uint8": 8,
}
return bit_depths[dtype]
def gen_audio_file(
path,
sample_rate,
num_channels,
*,
encoding=None,
bit_depth=None,
compression=None,
attenuation=None,
duration=1,
comment_file=None,
):
"""Generate synthetic audio file with `sox` command."""
if path.endswith(".wav"):
warnings.warn("Use get_wav_data and save_wav to generate wav file for accurate result.")
command = [
"sox",
"-V3", # verbose
"--no-dither", # disable automatic dithering
"-R",
# -R is supposed to be repeatable, though the implementation looks suspicious
# and not setting the seed to a fixed value.
# https://fossies.org/dox/sox-14.4.2/sox_8c_source.html
# search "sox_globals.repeatable"
]
if bit_depth is not None:
command += ["--bits", str(bit_depth)]
command += [
"--rate",
str(sample_rate),
"--null", # no input
"--channels",
str(num_channels),
]
if compression is not None:
command += ["--compression", str(compression)]
if bit_depth is not None:
command += ["--bits", str(bit_depth)]
if encoding is not None:
command += ["--encoding", str(encoding)]
if comment_file is not None:
command += ["--comment-file", str(comment_file)]
command += [
str(path),
"synth",
str(duration), # synthesizes for the given duration [sec]
"sawtooth",
"1",
# saw tooth covers the both ends of value range, which is a good property for test.
# similar to linspace(-1., 1.)
# this introduces bigger boundary effect than sine when converted to mp3
]
if attenuation is not None:
command += ["vol", f"-{attenuation}dB"]
print(" ".join(command), file=sys.stderr)
subprocess.run(command, check=True)
def convert_audio_file(src_path, dst_path, *, encoding=None, bit_depth=None, compression=None):
"""Convert audio file with `sox` command."""
command = ["sox", "-V3", "--no-dither", "-R", str(src_path)]
if encoding is not None:
command += ["--encoding", str(encoding)]
if bit_depth is not None:
command += ["--bits", str(bit_depth)]
if compression is not None:
command += ["--compression", str(compression)]
command += [dst_path]
print(" ".join(command), file=sys.stderr)
subprocess.run(command, check=True)
def _flattern(effects):
if not effects:
return effects
if isinstance(effects[0], str):
return effects
return [item for sublist in effects for item in sublist]
def run_sox_effect(input_file, output_file, effect, *, output_sample_rate=None, output_bitdepth=None):
"""Run sox effects"""
effect = _flattern(effect)
command = ["sox", "-V", "--no-dither", input_file]
if output_bitdepth:
command += ["--bits", str(output_bitdepth)]
command += [output_file] + effect
if output_sample_rate:
command += ["rate", str(output_sample_rate)]
print(" ".join(command))
subprocess.run(command, check=True)