sox_utils.py 2.44 KB
Newer Older
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
import subprocess


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,
):
    """Generate synthetic audio file with `sox` command."""
moto's avatar
moto committed
29
30
31
    if path.endswith('.wav'):
        raise RuntimeError(
            'Use get_wav_data and save_wav to generate wav file for accurate result.')
32
33
    command = [
        'sox',
34
35
36
37
38
39
40
41
42
43
        '-V3',  # verbose
        '-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 += [
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
        '--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)]
    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))
    subprocess.run(command, check=True)
moto's avatar
moto committed
66
67
68
69
70
71


def convert_audio_file(
        src_path, dst_path,
        *, bit_depth=None, compression=None):
    """Convert audio file with `sox` command."""
72
    command = ['sox', '-V3', '-R', str(src_path)]
moto's avatar
moto committed
73
74
75
76
77
78
79
    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))
    subprocess.run(command, check=True)