Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用静音检测分割音频文件_Python_Batch File_Audio_Split_Ffmpeg - Fatal编程技术网

Python 使用静音检测分割音频文件

Python 使用静音检测分割音频文件,python,batch-file,audio,split,ffmpeg,Python,Batch File,Audio,Split,Ffmpeg,我有200多个MP3文件,我需要使用静音检测来分割每个文件。我试过Audacity和WavePad,但它们没有批处理过程,而且一个接一个地制作它们非常慢 情况如下: 分割音轨,静音2秒或更长时间 然后在这些曲目的开头和结尾添加0.5秒,并将它们另存为.mp3 比特率192立体声 规范化卷以确保所有文件的卷和质量相同 我尝试了FFmpeg,但没有成功。我发现这是最简单的工具,可以用简单的方法和紧凑的代码来进行这种音频处理 您可以使用安装 如果需要,您可能需要安装ffmpeg/avlib。有关更

我有200多个MP3文件,我需要使用静音检测来分割每个文件。我试过Audacity和WavePad,但它们没有批处理过程,而且一个接一个地制作它们非常慢

情况如下:

  • 分割音轨,静音2秒或更长时间
  • 然后在这些曲目的开头和结尾添加0.5秒,并将它们另存为.mp3
  • 比特率192立体声
  • 规范化卷以确保所有文件的卷和质量相同
我尝试了FFmpeg,但没有成功。

我发现这是最简单的工具,可以用简单的方法和紧凑的代码来进行这种音频处理

您可以使用安装

如果需要,您可能需要安装ffmpeg/avlib。有关更多详细信息,请参阅

下面是一个代码片段,可以满足您的要求。一些参数,如
silence\u threshold
target\u dBFS
可能需要进行一些调整以符合您的要求。 总的来说,我能够分割
mp3
文件,尽管我必须尝试不同的
silence\u阈值值

片段

# 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"
    )
如果您的原始音频是立体声(2声道),那么您的块也将是立体声的。 您可以这样检查原始音频:

>>> song.channels
2

您可以尝试使用此选项在静默状态下分割音频,而无需探索静默阈值的可能性

def split(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
    )
请注意,使用此选项后,无需调整静音_阈值

此外,如果您想通过设置音频块的最小长度来分割音频,可以在上述代码之后添加此项

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)
target_length=25*1000//将每个块的最小长度设置为25秒
输出_chunks=[chunks[0]]
对于块中的块[1:]:
如果len(输出块[-1])

现在我们使用输出块进行进一步处理

测试了所有这些解决方案,但没有一个对我有效。我找到了一个对我有效且速度相对较快的解决方案

先决条件:

  • 它与
    ffmpeg
  • 它基于Vincent Berthiaume在这篇文章中的代码()
  • 它需要
    numpy
    (虽然它不需要从numpy获得太多,但是没有
    numpy
    的解决方案可能会相对容易编写并进一步提高速度)
  • 运作模式、理由:

  • 这里提供的解决方案是基于人工智能的,或者速度非常慢,或者将整个音频加载到内存中,这对我来说是不可行的(我想将巴赫所有勃兰登堡协奏曲的录音分割成特定的歌曲,2个LPs长2小时,@44 kHz 16位立体声,内存为1.4 GB,速度非常慢)。从一开始,当我偶然发现这篇文章时,我告诉自己一定有一个简单的方法,因为这仅仅是一个阈值过滤操作,不需要太多开销,并且可以一次在小块音频上完成。几个月后,我偶然发现了一个相对高效地完成音频分割的方法
  • 命令行参数给出源mp3(或ffmpeg可以读取的任何内容)、静默持续时间和噪声阈值。对于我的巴赫LP录音,0.01全振幅的1秒音程就成功了
  • 它允许
    ffmpeg
    将输入转换为无损16位22kHz PCM,并通过
    subprocess.Popen
    将其传回,其优点是
    ffmpeg
    的速度非常快,并且只占用很少的内存块
  • 回到python中,最后一个缓冲区和前一个缓冲区的2个临时
    numpy
    数组被连接起来,并检查它们是否超过给定的阈值。如果他们不这样做,那就意味着有一片沉默,而且(我天真地承认)只要数一数“沉默”的时间就可以了。如果时间至少与给定的最小静默持续时间一样长,(同样天真地),则将当前间隔的中间部分作为分割时刻
  • 该程序实际上不处理源文件,而是创建一个可以运行的批处理文件,该批处理文件告诉
    ffmpeg
    获取由这些“静默”限定的片段,并将其保存到单独的文件中
  • 然后,用户可以运行输出批处理文件,如果歌曲之间有长时间的停顿,可能会过滤一些重复的微间隔和小片段的静默
  • 此解决方案既有效又快速(此线程中的其他解决方案都不适用于我)
  • 小代码:

    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()
    
    将子流程作为sp导入
    导入系统
    进口numpy
    FFMPEG_BIN=“FFMPEG.exe”
    打印“ASplit.py”
    src=sys.argv[1]
    dur=float(sys.argv[2])
    thr=int(浮点(sys.argv[3])*65535)
    f=打开('%s-out.bat'%src',wb')
    tmprate=22050
    len2=dur*tmprate
    buflen=int(len2*2)
    #t*速率*16位
    oarr=numpy.arange(1,dtype='int16')
    #只是第一个块的虚拟数组
    命令=[FFMPEG_BIN,
    “-i”,src,
    “-f”、“s16le”,
    “-acodec”,“pcm_s16le”,
    '-ar',str(tmprate),#输出采样率
    单声道的“-ac”、“1”、“1”
    '-']#-输出到标准输出
    pipe=sp.Popen(命令,stdout=sp.pipe,bufsize=10**8)
    tf=真
    pos=0
    opos=0
    部分=0
    而tf:
    原始=管道.标准输出.读取(buflen)
    如果原始=“”:
    tf=假
    打破
    arr=numpy.fromstring(原始,dtype=“int16”)
    rng=numpy.concatenate([oarr,arr])
    mx=numpy.amax(rng)
    
    如果mx有一个看。我已经使用了合理的成功。话虽如此,StackOverflow是一个用于编程的Q/a站点。它不是一个请求推荐软件或其他非现场资源的网站。请注意,
    split\u on\u silene()
    具有
    keep\u silene=100
    ,其中已包含200毫秒的内容
    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()