⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mp3fix.rb

📁 Amarok是一款在LINUX或其他类UNIX操作系统中运行的音频播放器软件。 经过两年开发后
💻 RB
字号:
#!/usr/bin/env ruby## Script for fixing mp3 files which show a bogus track length. Calculates the real# track length and adds the XING header to the file, and repairs broken MPEG headers.## (c) 2005 Mark Kretschmann <markey@web.de># License: GNU General Public License V2class NumArray < Array    #    # Return the nearest matching number from the array    #    def nearest_match( num )        smallest     = 0        smallestDiff = nil        each() do |x|            next if x == nil            diff = ( x - num ).abs()            if smallestDiff == nil or diff < smallestDiff                smallestDiff = diff                smallest = x            end        end        return smallest    endend## Returns the size of the entire ID3-V2 tag. In other words, the offset from# where the real mp3 data starts.# @see http://id3lib.sourceforge.net/id3/id3v2com-00.html#sec3.1#def calcId3v2Size( data )    size = data[6]*2**21 + data[7]*2**14 + data[8]*2**7 + data[9]    size = size + 10 # Header    return sizeend############################################################################# MAIN############################################################################path = ""destination = ""repairLog = []if $*.empty?() or $*[0] == "--help"    puts( "Usage: mp3fix.rb source [destination]" )    puts()    puts( "Mp3fix is a tool for fixing VBR encoded mp3 that show a bogus track length in your" )    puts( "audio player. Mp3fix calculates the real track length and adds the missing XING" )    puts( "header to the file. " )    puts()    puts( "Additionally, Mp3Fix can repair files with broken MPEG frame headers. This is useful " )    puts( "for tracks which show an invalid bitrate and samplerate, which also results in bogus " )    puts( "track length." )    exit( 1 )endpath = $*[0]destination = pathif $*.length() == 2    destination = $*[1]endunless FileTest::readable_real?( path )    puts( "Error: File not found or not readable." )    exit( 1 )endunless File.extname( path ) == ".mp3" or File.extname( path ) == ".MP3"    puts( "Error: File is not mp3." )    exit( 1 )endif destination == path and not FileTest.writable_real?( destination )    puts( "Error: Destination file not writable." )    exit( 1 )endfile = File.new( path, "r" )data = file.read()id3length = 0offset = 0if data[0,3] == "ID3"    id3length = calcId3v2Size( data )    puts( "ID3-V2 detected. Tag size: #{id3length}" )else    puts( "ID3-V1 detected." )endoffset = id3lengthSamplesPerFrame = 1152  # Constant for MPEG1 layer 3BitrateTable = NumArray.new()BitrateTable << nil << 32000 << 40000 << 48000 << 56000 << 64000 << 80000 << 96000BitrateTable << 112000 << 128000 << 160000 << 192000 << 224000 << 256000 << 320000SamplerateTable = NumArray.new()SamplerateTable << 44100 << 48000 << 32100ChannelMode = data[offset + 3] & 0xc0 >> 6XingOffset = ChannelMode == 0x03 ? 17 : 32puts()puts( "Analyzing mpeg frames.." )puts()frameCount       = 0bitrateCount     = 0samplerateCount  = 0firstFrameBroken = falsefirstFrameOffset = niloffset           = 0header           = 0# Iterate over all frames:#loop do    loop do        # Find frame sync        offset = data.index( 0xff, offset )        break if offset == nil or offset > data.length() - 4        header = data[offset+0]*2**24 + data[offset+1]*2**16 + data[offset+2]*2**8 + data[offset+3]        if header & 0xfff00000 == 0xfff00000            firstFrameOffset = offset if firstFrameOffset == nil            break        end        offset += 1    end    break if offset == nil    validHeader = true    bitrate = BitrateTable[( header & 0x0000f000 ) >> 12]    if bitrate == nil        validHeader = false        puts( "Bitrate invalid." )    end    samplerate = SamplerateTable[( header & 0x00000c00 ) >> 10]    if samplerate == nil        validHeader = false        puts( "Samplerate invalid." )    end    padding = ( header & 0x00000200 ) >> 9    if validHeader        frameSize = ( SamplesPerFrame / 8 * bitrate ) / samplerate + padding#         puts( "bitrate     : #{bitrate.to_s()}" )#         puts( "samplerate  : #{samplerate.to_s()}" )#         puts( "padding     : #{padding.to_s()}" )#         puts( "framesize   : #{frameSize}" )#         puts()        frameCount      += 1        bitrateCount    += bitrate        samplerateCount += samplerate        offset          += frameSize    else        firstFrameBroken = true if frameCount == 0        offset += 1    endendAverageBitrate = bitrateCount / frameCountLength = data.length() / AverageBitrate * 8puts( "Number of frames : #{frameCount}" )puts( "Average bitrate  : #{AverageBitrate}" )puts( "Length (seconds) : #{Length}" )puts()# Repair first frame header, if it is broken:#if firstFrameBroken    puts()    puts( "Repairing broken header in first frame." )    repairLog << "* Fixed broken MPEG header\n"    firstHeader = data[firstFrameOffset+0]*2**24 + data[firstFrameOffset+1]*2**16 + data[firstFrameOffset+2]*2**8 + data[firstFrameOffset+3]    firstHeader |= 0xfff00000  # Frame sync    # MPEG ID, Layer, Protection    firstHeader &= 0xfff0ffff    firstHeader |= 0x000b0000    br = BitrateTable.nearest_match( ( bitrateCount / frameCount ).round() )    sr = SamplerateTable.nearest_match( ( samplerateCount / frameCount ).round() )    puts( "Setting bitrate    : #{br}" )    puts( "Setting samplerate : #{sr}" )    firstHeader &= 0xffff00ff    firstHeader |= BitrateTable.index( br )    << 12    firstHeader |= SamplerateTable.index( sr ) << 10    # Write header back    data[firstFrameOffset+0] = ( firstHeader >> 24 ) & 0xff    data[firstFrameOffset+1] = ( firstHeader >> 16 ) & 0xff    data[firstFrameOffset+2] = ( firstHeader >> 8  ) & 0xff    data[firstFrameOffset+3] = ( firstHeader >> 0  ) & 0xffendunless data[firstFrameOffset + 4 + XingOffset, 4] == "Xing"    puts()    puts( "Adding XING header." )    repairLog << "* Added XING header\n"    xing = String.new()    xing << "Xing"    Flags = 0x0001 | 0x0002 | 0x0004  # Frames and Bytes fields valid    xing << 0 << 0 << 0 << Flags    xing << ( ( frameCount & 0xff000000 ) >> 24 )    xing << ( ( frameCount & 0x00ff0000 ) >> 16 )    xing << ( ( frameCount & 0x0000ff00 ) >> 8 )    xing << ( ( frameCount & 0x000000ff ) >> 0 )    xing << ( ( data.length() & 0xff000000 ) >> 24 )    xing << ( ( data.length() & 0x00ff0000 ) >> 16 )    xing << ( ( data.length() & 0x0000ff00 ) >> 8 )    xing << ( ( data.length() & 0x000000ff ) >> 0 )    # Insert XING header into string, after the first MPEG header    data[firstFrameOffset + 4 + XingOffset, 0] = xingendunless repairLog.empty?()    puts()    print( "Writing file..  " )    destfile = File::open( destination, File::CREAT|File::TRUNC|File::WRONLY )    if destfile == nil        puts( "Error: Destination file is not writable." )        exit( 1 )    end    destfile << data    puts( "done." )endputs()puts()puts( "MP3FIX REPAIR SUMMARY:" )puts( "======================" )unless repairLog.empty?()    puts( repairLog )else    puts( "Mp3Fix did not find any defects.")endputs()

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -