【问题标题】:Python library to split and join mp3 files用于拆分和连接 mp3 文件的 Python 库
【发布时间】:2011-02-26 11:53:04
【问题描述】:

有很多库可以处理 mp3 标签,但我只需要 2 个函数 - 将 mp3 文件分成 2 个部分,第二个用于合并 5 个 mp3。

你有什么建议吗? 谢谢!

【问题讨论】:

  • 当你加入 mp3 文件时,你会发现它们之间存在间隙,因为 mp3 是基于块的,文件的最后一个块将被静音填充。

标签: python mp3


【解决方案1】:

我为这个确切的用例编写了一个库 (pydub):

from pydub import AudioSegment

sound = AudioSegment.from_mp3("/path/to/file.mp3")

# len() and slicing are in milliseconds
halfway_point = len(sound) / 2
second_half = sound[halfway_point:]

# Concatenation is just adding
second_half_3_times = second_half + second_half + second_half

# writing mp3 files is a one liner
second_half_3_times.export("/path/to/new/file.mp3", format="mp3")

添加无声间隙

如果您想在声音的各个部分之间添加静音:

two_sec_silence = AudioSegment.silent(duration=2000)
sound_with_gap = sound[:1000] + two_sec_silence + sound[1000:]

【讨论】:

  • @발렌텐 是的,只需创建一个静音音频片段:two_sec_pause = AudioSegment.silent(duration=2000),然后您可以像平常一样连接sound1 + two_sec_pause + sound2
  • 这个库是一个宝石,感谢您的分享,这很有帮助,我正在为语言学习者开发一个软件,让他们有机会生成由口语短 mp3 文件制成的训练声音。
  • 这个库是否避免重新编码?
  • @andresp 不,它在您打开时解码并在您导出时重新编码。如果不重新编码,大多数类型的更改都是不可能的(尤其是当您使用多个音频输入时,可能没有匹配的格式/通道/比特率/编码器设置/等)
  • @Jun 如果要进行格式转换,则需要 ffmpeg 或 avlib。纯 python 支持 WAVE 格式。要打开或保存其他格式,您需要安装其中一个包
【解决方案2】:

查看维基百科上的MP3 file structure。在 python 中使用二进制读取模式来编辑 MP3 文件。 s = open(file_name, 'rb').read() 会将整个文件放入一个字符串对象中,表示文件中的原始字节(例如\xeb\xfe\x80)。然后,您可以搜索和编辑字符串,使用方括号使用 indeces 寻址字节偏移:s[n]。最后,只需对新文件中所需的 MP3 帧进行二进制写入,将 ID3 标头附加到要构成每个文件的帧集。

【讨论】:

    【解决方案3】:

    这是我在不重新编码的情况下使用 python 拆分 MP3 的尝试。并非所有种类的 MP3 文件都受支持,我很乐意接受建议或改进。该脚本被硬编码为在 55 秒处拆分,但代码演示了一般原则。

    from __future__ import print_function
    import struct
    import sys
    
    #MP3 frames are not independent because of the byte reservoir. This script does not account for
    #that in determining where to do the split.
    
    def SplitMp3(fi, splitSec, out):
    
        #Constants for MP3
        bitrates = {0x0: "free", 0x1: 32, 0x2: 40, 0x3: 48, 0x4: 56, 0x5: 64, 0x6: 80, 0x7: 96, 0x8: 112,
            0x9: 128, 0xa: 160, 0xb: 192, 0xc: 224, 0xd: 256, 0xe: 320, 0xf: "bad"}
        freqrates = {0x0: 44100, 0x1: 48000, 0x2: 32000, 0x3: "reserved"}
        countMpegFrames = 0
        frameDuration = 0.026
        unrecognizedBytes = 0
        splitFrame = int(round(splitSec / frameDuration))
    
        while True:
    
            startPos = fi.tell()
    
            #Check for 3 byte headers
            id3Start = fi.read(3)
            if len(id3Start) == 3:
    
                if id3Start == b'TAG':
                    print ("Found ID3 v1/1.1 header")
                    fi.seek(startPos + 256)
                    continue
    
                if id3Start == b'ID3':
                    #Possibly a ID3v2 header
                    majorVer, minorVer, flags, encSize = struct.unpack(">BBBI", fi.read(7))
                    if majorVer != 0xFF and minorVer != 0xFF:
                        encSize1 = (encSize & 0x7f000000) >> 24
                        encSize2 = (encSize & 0x7f0000) >> 16
                        encSize3 = (encSize & 0x7f00) >> 8
                        encSize4 = (encSize & 0x7f)
                        if encSize1 < 0x80 and encSize2 < 0x80 and encSize3 < 0x80 and encSize4 < 0x80:
                            size = ((encSize & 0x7f000000) >> 3) + ((encSize & 0x7f0000) >> 2) + ((encSize & 0x7f00) >> 1) + (encSize & 0x7f)
                            unsync = (flags >> 7) & 0x1
                            extendedHeader = (flags >> 6) & 0x1
                            experimental = (flags >> 5) & 0x1
                            print ("Found ID3v2 header")
                            print ("version", majorVer, minorVer, unsync, extendedHeader, experimental)
                            print ("size", size)
                            #TODO extendedHeader not supported yet
    
                            fi.seek(startPos + 10 + size)
                            continue
    
            #Check for 4 byte headers
            fi.seek(startPos)
            headerRaw = fi.read(4)
            if len(headerRaw) == 4:
                headerWord = struct.unpack(">I", headerRaw)[0]
    
                #Check for MPEG-1 audio frame
                if headerWord & 0xfff00000 == 0xfff00000:
                    print ("Possible MPEG-1 audio header", hex(headerWord))
                    countMpegFrames += 1
                    ver = (headerWord & 0xf0000) >> 16
                    bitrateEnc = (headerWord & 0xf000) >> 12
                    freqEnc = (headerWord & 0xf00) >> 8
                    mode = (headerWord & 0xf0) >> 4
                    cpy = (headerWord & 0xf)
                    if ver & 0xe == 0xa and freqEnc != 0xf:
                        print ("Probably an MP3 frame")
                        bitrate = bitrates[bitrateEnc]
                        freq = freqrates[freqEnc >> 2]
                        padding = ((freqEnc >> 1) & 0x1) == 1
                        print ("bitrate", bitrate, "kbps")
                        print ("freq", freq, "Hz")
                        print ("padding", padding)
                        frameLen = int((144 * bitrate * 1000 / freq ) + padding)
    
                        #Copy frame to output
                        fi.seek(startPos)
                        frameData = fi.read(frameLen)
                        if countMpegFrames >= splitFrame:
                            out.write(frameData)
    
                        fi.seek(startPos + frameLen)
                        continue
                    else:
                        raise RuntimeError("Unsupported format:", hex(ver), "header:", hex(headerWord))
    
            #If no header can be detected, move on to the next byte
            fi.seek(startPos)
            nextByteRaw = fi.read(1)
            if len(nextByteRaw) == 0:
                break #End of file
            unrecognizedBytes += 1
    
        print ("unrecognizedBytes", unrecognizedBytes)
        print ("countMpegFrames", countMpegFrames)
        print ("duration", countMpegFrames * frameDuration, "sec")
    
    if __name__=="__main__":
        fi = open(sys.argv[1], "rb")
        out = open("test.mp3", "wb")
        SplitMp3(fi, 55.0, out)
        out.close()
    

    合并类似于从两个单独的 MP3 输入文件中提取和附加帧。

    【讨论】:

    • 如果你想修剪音频,修改代码,添加另一个参数来表示结束时间(比如secondSplitSec)。使用与splitFrame 类似的公式计算secondSplitFrame。将if countMpegFrames &gt;= splitFrame: 更改为if secondSplitFrame &gt;= countMpegFrames &gt;= splitFrame:
    • 我想修剪音频,并按照 aronquemarr(upper comment) 的说明编辑了代码。我已成功拆分长 mp3 文件,但它太松散了。例如,我将 0 到 41 秒的音频拆分,它拆分为 0 到 37 秒,并且松散度随着数量的增加而增加。我研究了一下,但我找不到原因
    【解决方案4】:

    查看 GStreamer 及其 Python 包装器 Gst-Python

    【讨论】:

      【解决方案5】:

      使用 unix split 命令:

      split -b 200k file.mp3 output_
      

      这将输出 output_a, output_b, output_c, ..

      然后你可以通过重命名来获取 mp3 文件

      for file in ./output_*; do mv "$file" "$(basename $file).mp3"; done
      

      这将输出 output_a.mp3, output_b.mp3, output_c.mp3 ...所有的(除了最后一个,可能是)大小都是200kb,并且output_x的总大小与文件相同。 mp3

      您可以使用du(磁盘使用)命令获取文件的字节数,然后决定要剪切多少字节..

      du -sh file.mp3
      

      然后加入使用 cat 命令:

      cat output_2.mp3 output_3.mp3 output_4.mp3 > output.mp3
      

      当然,您可以将所有这些都放在一个 shell 脚本中,然后从 python 中调用它。

      【讨论】:

      • 您将无法播放此类片段。
      • 注释很有用,但是分割音频与分割文件的二进制结构完全不同。
      猜你喜欢
      • 1970-01-01
      • 2023-03-11
      • 1970-01-01
      • 2012-07-20
      • 2018-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-20
      相关资源
      最近更新 更多