Spaces:
Runtime error
Runtime error
Delete add_reverb_to_file.py
Browse files- add_reverb_to_file.py +0 -152
add_reverb_to_file.py
DELETED
@@ -1,152 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
Add reverb to an audio file using Pedalboard.
|
3 |
-
|
4 |
-
The audio file is read in chunks, using nearly no memory.
|
5 |
-
This should be one of the fastest possible ways to add reverb to a file
|
6 |
-
while also using as little memory as possible.
|
7 |
-
|
8 |
-
On my laptop, this runs about 58x faster than real-time
|
9 |
-
(i.e.: processes a 60-second file in ~1 second.)
|
10 |
-
|
11 |
-
Requirements: `pip install PySoundFile tqdm pedalboard`
|
12 |
-
Note that PySoundFile requires a working libsndfile installation.
|
13 |
-
"""
|
14 |
-
|
15 |
-
import argparse
|
16 |
-
import os
|
17 |
-
import sys
|
18 |
-
import warnings
|
19 |
-
|
20 |
-
import numpy as np
|
21 |
-
import soundfile as sf
|
22 |
-
from tqdm import tqdm
|
23 |
-
from tqdm.std import TqdmWarning
|
24 |
-
|
25 |
-
from pedalboard import Reverb
|
26 |
-
|
27 |
-
BUFFER_SIZE_SAMPLES = 1024 * 16
|
28 |
-
NOISE_FLOOR = 1e-4
|
29 |
-
|
30 |
-
|
31 |
-
def get_num_frames(f: sf.SoundFile) -> int:
|
32 |
-
# On some platforms and formats, f.frames == -1L.
|
33 |
-
# Check for this bug and work around it:
|
34 |
-
if f.frames > 2 ** 32:
|
35 |
-
f.seek(0)
|
36 |
-
last_position = f.tell()
|
37 |
-
while True:
|
38 |
-
# Seek through the file in chunks, returning
|
39 |
-
# if the file pointer stops advancing.
|
40 |
-
f.seek(1024 * 1024 * 1024, sf.SEEK_CUR)
|
41 |
-
new_position = f.tell()
|
42 |
-
if new_position == last_position:
|
43 |
-
f.seek(0)
|
44 |
-
return new_position
|
45 |
-
else:
|
46 |
-
last_position = new_position
|
47 |
-
else:
|
48 |
-
return f.frames
|
49 |
-
|
50 |
-
|
51 |
-
def main():
|
52 |
-
warnings.filterwarnings("ignore", category=TqdmWarning)
|
53 |
-
|
54 |
-
parser = argparse.ArgumentParser(description="Add reverb to an audio file.")
|
55 |
-
parser.add_argument("input_file", help="The input file to add reverb to.")
|
56 |
-
parser.add_argument(
|
57 |
-
"--output-file",
|
58 |
-
help=(
|
59 |
-
"The name of the output file to write to. If not provided, {input_file}.reverb.wav will"
|
60 |
-
" be used."
|
61 |
-
),
|
62 |
-
default=None,
|
63 |
-
)
|
64 |
-
|
65 |
-
# Instantiate the Reverb object early so we can read its defaults for the argparser --help:
|
66 |
-
reverb = Reverb()
|
67 |
-
|
68 |
-
parser.add_argument("--room-size", type=float, default=reverb.room_size)
|
69 |
-
parser.add_argument("--damping", type=float, default=reverb.damping)
|
70 |
-
parser.add_argument("--wet-level", type=float, default=reverb.wet_level)
|
71 |
-
parser.add_argument("--dry-level", type=float, default=reverb.dry_level)
|
72 |
-
parser.add_argument("--width", type=float, default=reverb.width)
|
73 |
-
parser.add_argument("--freeze-mode", type=float, default=reverb.freeze_mode)
|
74 |
-
|
75 |
-
parser.add_argument(
|
76 |
-
"-y",
|
77 |
-
"--overwrite",
|
78 |
-
action="store_true",
|
79 |
-
help="If passed, overwrite the output file if it already exists.",
|
80 |
-
)
|
81 |
-
|
82 |
-
parser.add_argument(
|
83 |
-
"--cut-reverb-tail",
|
84 |
-
action="store_true",
|
85 |
-
help=(
|
86 |
-
"If passed, remove the reverb tail to the end of the file. "
|
87 |
-
"The output file will be identical in length to the input file."
|
88 |
-
),
|
89 |
-
)
|
90 |
-
args = parser.parse_args()
|
91 |
-
|
92 |
-
for arg in ('room_size', 'damping', 'wet_level', 'dry_level', 'width', 'freeze_mode'):
|
93 |
-
setattr(reverb, arg, getattr(args, arg))
|
94 |
-
|
95 |
-
if not args.output_file:
|
96 |
-
args.output_file = args.input_file + ".reverb.wav"
|
97 |
-
|
98 |
-
sys.stderr.write(f"Opening {args.input_file}...\n")
|
99 |
-
|
100 |
-
with sf.SoundFile(args.input_file) as input_file:
|
101 |
-
sys.stderr.write(f"Writing to {args.output_file}...\n")
|
102 |
-
if os.path.isfile(args.output_file) and not args.overwrite:
|
103 |
-
raise ValueError(
|
104 |
-
f"Output file {args.output_file} already exists! (Pass -y to overwrite.)"
|
105 |
-
)
|
106 |
-
with sf.SoundFile(
|
107 |
-
args.output_file,
|
108 |
-
'w',
|
109 |
-
samplerate=input_file.samplerate,
|
110 |
-
channels=input_file.channels,
|
111 |
-
) as output_file:
|
112 |
-
length = get_num_frames(input_file)
|
113 |
-
length_seconds = length / input_file.samplerate
|
114 |
-
sys.stderr.write(f"Adding reverb to {length_seconds:.2f} seconds of audio...\n")
|
115 |
-
with tqdm(
|
116 |
-
total=length_seconds,
|
117 |
-
desc="Adding reverb...",
|
118 |
-
bar_format=(
|
119 |
-
"{percentage:.0f}%|{bar}| {n:.2f}/{total:.2f} seconds processed"
|
120 |
-
" [{elapsed}<{remaining}, {rate:.2f}x]"
|
121 |
-
),
|
122 |
-
# Avoid a formatting error that occurs if
|
123 |
-
# TQDM tries to print before we've processed a block
|
124 |
-
delay=1000,
|
125 |
-
) as t:
|
126 |
-
for dry_chunk in input_file.blocks(BUFFER_SIZE_SAMPLES, frames=length):
|
127 |
-
# Actually call Pedalboard here:
|
128 |
-
# (reset=False is necessary to allow the reverb tail to
|
129 |
-
# continue from one chunk to the next.)
|
130 |
-
effected_chunk = reverb.process(
|
131 |
-
dry_chunk, sample_rate=input_file.samplerate, reset=False
|
132 |
-
)
|
133 |
-
# print(effected_chunk.shape, np.amax(np.abs(effected_chunk)))
|
134 |
-
output_file.write(effected_chunk)
|
135 |
-
t.update(len(dry_chunk) / input_file.samplerate)
|
136 |
-
t.refresh()
|
137 |
-
if not args.cut_reverb_tail:
|
138 |
-
while True:
|
139 |
-
# Pull audio from the effect until there's nothing left:
|
140 |
-
effected_chunk = reverb.process(
|
141 |
-
np.zeros((BUFFER_SIZE_SAMPLES, input_file.channels), np.float32),
|
142 |
-
sample_rate=input_file.samplerate,
|
143 |
-
reset=False,
|
144 |
-
)
|
145 |
-
if np.amax(np.abs(effected_chunk)) < NOISE_FLOOR:
|
146 |
-
break
|
147 |
-
output_file.write(effected_chunk)
|
148 |
-
sys.stderr.write("Done!\n")
|
149 |
-
|
150 |
-
|
151 |
-
if __name__ == "__main__":
|
152 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|