65 lines
2.3 KiB
Python
Executable File
65 lines
2.3 KiB
Python
Executable File
from .utils import (
|
|
db_to_float,
|
|
ratio_to_db,
|
|
register_pydub_effect,
|
|
make_chunks,
|
|
)
|
|
from .exceptions import TooManyMissingFrames
|
|
|
|
|
|
@register_pydub_effect
|
|
def normalize(seg, headroom=0.1):
|
|
"""
|
|
headroom is how close to the maximum volume to boost the signal up to (specified in dB)
|
|
"""
|
|
peak_sample_val = seg.max
|
|
target_peak = seg.max_possible_amplitude * db_to_float(-headroom)
|
|
|
|
needed_boost = ratio_to_db(target_peak / peak_sample_val)
|
|
return seg.apply_gain(needed_boost)
|
|
|
|
|
|
@register_pydub_effect
|
|
def speedup(seg, playback_speed=1.5, chunk_size=150, crossfade=25):
|
|
# we will keep audio in 150ms chunks since one waveform at 20Hz is 50ms long
|
|
# (20 Hz is the lowest frequency audible to humans)
|
|
|
|
# portion of AUDIO TO KEEP. if playback speed is 1.25 we keep 80% (0.8) and
|
|
# discard 20% (0.2)
|
|
atk = 1.0 / playback_speed
|
|
|
|
if playback_speed < 2.0:
|
|
# throwing out more than half the audio - keep 50ms chunks
|
|
ms_to_remove_per_chunk = int(chunk_size * (1 - atk) / atk)
|
|
else:
|
|
# throwing out less than half the audio - throw out 50ms chunks
|
|
ms_to_remove_per_chunk = int(chunk_size)
|
|
chunk_size = int(atk * chunk_size / (1 - atk))
|
|
|
|
# the crossfade cannot be longer than the amount of audio we're removing
|
|
crossfade = min(crossfade, ms_to_remove_per_chunk - 1)
|
|
|
|
# DEBUG
|
|
#print("chunk: {0}, rm: {1}".format(chunk_size, ms_to_remove_per_chunk))
|
|
|
|
chunks = make_chunks(seg, chunk_size + ms_to_remove_per_chunk)
|
|
if len(chunks) < 2:
|
|
raise Exception("Could not speed up AudioSegment, it was too short {2:0.2f}s for the current settings:\n{0}ms chunks at {1:0.1f}x speedup".format(
|
|
chunk_size, playback_speed, seg.duration_seconds))
|
|
|
|
# we'll actually truncate a bit less than we calculated to make up for the
|
|
# crossfade between chunks
|
|
ms_to_remove_per_chunk -= crossfade
|
|
|
|
# we don't want to truncate the last chunk since it is not guaranteed to be
|
|
# the full chunk length
|
|
last_chunk = chunks[-1]
|
|
chunks = [chunk[:-ms_to_remove_per_chunk] for chunk in chunks[:-1]]
|
|
|
|
out = chunks[0]
|
|
for chunk in chunks[1:]:
|
|
out = out.append(chunk, crossfade=crossfade)
|
|
|
|
out += last_chunk
|
|
return out
|