J'ai plus de 200 fichiers MP3 et je dois diviser chacun d'eux en utilisant la détection de silence. J'ai essayé Audacity et WavePad mais ils n'ont pas de processus batch et c'est très lent de les faire un par un.
Le scénario est le suivant:
J'ai essayé FFmpeg mais sans succès.
J'ai trouvé pydub pour être l'outil le plus simple pour faire ce genre de manipulation audio de manière simple et avec du code compact.
Vous pouvez installer pydub avec
pip install pydub
Vous devrez peut-être installer ffmpeg/avlib si nécessaire. Voir ce lien pour plus de détails.
Voici un extrait qui fait ce que vous avez demandé. Certains des paramètres tels que silence_threshold
et target_dBFS
peut nécessiter quelques ajustements pour répondre à vos besoins. Dans l'ensemble, j'ai pu diviser mp3
fichiers, même si j'ai dû essayer différentes valeurs pour silence_threshold
.
Extrait
# Import the AudioSegment class for processing audio and the
# split_on_silence function for separating out silent chunks.
from pydub import AudioSegment
from pydub.silence import split_on_silence
# Define a function to normalize a chunk to a target amplitude.
def match_target_amplitude(aChunk, target_dBFS):
''' Normalize given audio chunk '''
change_in_dBFS = target_dBFS - aChunk.dBFS
return aChunk.apply_gain(change_in_dBFS)
# Load your audio.
song = AudioSegment.from_mp3("your_audio.mp3")
# Split track where the silence is 2 seconds or more and get chunks using
# the imported function.
chunks = split_on_silence (
# Use the loaded audio.
song,
# Specify that a silent chunk must be at least 2 seconds or 2000 ms long.
min_silence_len = 2000,
# Consider a chunk silent if it's quieter than -16 dBFS.
# (You may want to adjust this parameter.)
silence_thresh = -16
)
# Process each chunk with your parameters
for i, chunk in enumerate(chunks):
# Create a silence chunk that's 0.5 seconds (or 500 ms) long for padding.
silence_chunk = AudioSegment.silent(duration=500)
# Add the padding chunk to beginning and end of the entire chunk.
audio_chunk = silence_chunk + chunk + silence_chunk
# Normalize the entire chunk.
normalized_chunk = match_target_amplitude(audio_chunk, -20.0)
# Export the audio chunk with new bitrate.
print("Exporting chunk{0}.mp3.".format(i))
normalized_chunk.export(
".//chunk{0}.mp3".format(i),
bitrate = "192k",
format = "mp3"
)
Si votre audio d'origine est stéréo (2 canaux), vos morceaux seront également stéréo. Vous pouvez vérifier l'audio d'origine comme ceci:
>>> song.channels
2
Après avoir testé toutes ces solutions et aucune d'entre elles n'ayant fonctionné pour moi, j'ai trouvé une solution qui a fonctionné pour moi et qui est relativement rapide.
Conditions préalables:
ffmpeg
numpy
(bien qu'il n'ait pas besoin de beaucoup de numpy et une solution sans numpy
serait probablement relativement facile à écrire et augmenterait encore la vitesse)Mode de fonctionnement, justification:
ffmpeg
de convertir l'entrée en un PCM 16 bits 22 kHz sans perte et de la renvoyer via subprocess.Popen
, avec l'avantage que ffmpeg
le fait très rapidement et en petits morceaux qui n'occupent pas beaucoup de mémoire.numpy
temporaires du dernier et avant-dernier tampon sont concaténés et vérifient s'ils dépassent le seuil donné. S'ils ne le font pas, cela signifie qu'il y a un bloc de silence, et (j'admets naïvement) il suffit de compter le temps où il y a du "silence". Si le temps est au moins aussi long que le min donné. la durée du silence, (encore naïvement) le milieu de cet intervalle actuel est pris comme moment de division.ffmpeg
de prendre les segments délimités par ces "silences" et de les enregistrer dans des fichiers séparés.Le petit code:
import subprocess as sp
import sys
import numpy
FFMPEG_BIN = "ffmpeg.exe"
print 'ASplit.py <src.mp3> <silence duration in seconds> <threshold amplitude 0.0 .. 1.0>'
src = sys.argv[1]
dur = float(sys.argv[2])
thr = int(float(sys.argv[3]) * 65535)
f = open('%s-out.bat' % src, 'wb')
tmprate = 22050
len2 = dur * tmprate
buflen = int(len2 * 2)
# t * rate * 16 bits
oarr = numpy.arange(1, dtype='int16')
# just a dummy array for the first chunk
command = [ FFMPEG_BIN,
'-i', src,
'-f', 's16le',
'-acodec', 'pcm_s16le',
'-ar', str(tmprate), # ouput sampling rate
'-ac', '1', # '1' for mono
'-'] # - output to stdout
pipe = sp.Popen(command, stdout=sp.PIPE, bufsize=10**8)
tf = True
pos = 0
opos = 0
part = 0
while tf :
raw = pipe.stdout.read(buflen)
if raw == '' :
tf = False
break
arr = numpy.fromstring(raw, dtype = "int16")
rng = numpy.concatenate([oarr, arr])
mx = numpy.amax(rng)
if mx <= thr :
# the peak in this range is less than the threshold value
trng = (rng <= thr) * 1
# effectively a pass filter with all samples <= thr set to 0 and > thr set to 1
sm = numpy.sum(trng)
# i.e. simply (naively) check how many 1's there were
if sm >= len2 :
part += 1
apos = pos + dur * 0.5
print mx, sm, len2, apos
f.write('ffmpeg -i "%s" -ss %f -to %f -c copy -y "%s-p%04d.mp3"\r\n' % (src, opos, apos, src, part))
opos = apos
pos += dur
oarr = arr
part += 1
f.write('ffmpeg -i "%s" -ss %f -to %f -c copy -y "%s-p%04d.mp3"\r\n' % (src, opos, pos, src, part))
f.close()
Vous pouvez essayer de l'utiliser pour diviser l'audio sur le silence sans avoir à explorer les possibilités pour le seuil de silence
def split(file, filepath):
sound = AudioSegment.from_wav(filepath)
dBFS = sound.dBFS
chunks = split_on_silence(sound,
min_silence_len = 500,
silence_thresh = dBFS-16,
keep_silence = 250 //optional
)
Notez que la valeur silence_thresh n'a pas besoin d'être ajustée après l'avoir utilisée.
De plus, si vous souhaitez diviser l'audio en définissant la longueur minimale du morceau audio, vous pouvez l'ajouter après le code mentionné ci-dessus.
target_length = 25 * 1000 //setting minimum length of each chunk to 25 seconds
output_chunks = [chunks[0]]
for chunk in chunks[1:]:
if len(output_chunks[-1]) < target_length:
output_chunks[-1] += chunk
else:
# if the last output chunk is longer than the target length,
# we can start a new one
output_chunks.append(chunk)
maintenant nous utilisons output_chunks pour un traitement ultérieur