如何用ruby连接mp3文件

时间:2013-10-15 13:42:09

标签: ruby mp3

我有几个mp3文件作为二进制字符串,具有相同数量的通道和相同的采样率。我需要在不使用命令行工具的情况下将它们连接到内存中。

目前我只是进行字符串连接,如下所示:

out = ''
mp3s.each { |mp3| out << mp3 }

音频播放器可以播放结果,但有一些警告,因为据我所知,mp3标头处理不正确。

有没有办法以更正确的方式进行连接?

1 个答案:

答案 0 :(得分:6)

阅读此article about MP3 in russian后,我提出了解决方案。 您必须能够在http://id3.org/获得完整的ID3规范,但目前似乎已经失效。

通常Mp3文件具有下一种格式:

[ID3 head(10 bytes) | ID3 tags | MP3 frames ]

ID3不是MP3格式的一部分,但它是用于放置艺术家,专辑等信息的容器......

音频数据本身存储在MP3帧中。每帧以4字节标题开头,提供元信息(编解码器,比特率等)。

每个帧都有固定的大小。因此,如果在最后一帧结束时没有足够的样本,编码器会添加静音以使帧具有必要的大小。我还发现了像 LAME3.97这样的块(编码器的名称和版本)。

因此,我们需要做的就是摆脱ID3容器。以下解决方案对我来说是完美的,不再有任何警告,而且文件变小了:

# Length of header that describes ID3 container
ID3_HEADER_SIZE = 10

# Get size of ID3 container.
# Length is stored in 4 bytes, and the 7th bit of every byte is ignored.
#
# Example:
#         Hex: 00       00       07       76
#         Bin: 00000000 00000000 00000111 01110110
#    Real bin:                        111  1110110
#    Real dec: 1014
#
def get_id3_size(header)
  result = 0
  str = header[6..9]

  # Read 4 size bytes from left to right applying bit mask to exclude 7th bit
  # in every byte.
  4.times do |i|
    result += (str[i].ord & 0x7F) * (2 ** (7 * (3-i)))
  end

  result
end

def strip_mp3!(raw_mp3)
  # 10 bytes that describe ID3 container.
  id3_header = raw_mp3[0...ID3_HEADER_SIZE]
  id3_size = get_id3_size(id3_header)

  # Offset from which mp3 frames start
  offset = id3_size + ID3_HEADER_SIZE

  # Get rid of ID3 container
  raw_mp3.slice!(0...offset)
  raw_mp3
end

# Read raw mp3s
hi  = File.binread('hi.mp3')
bye = File.binread('bye.mp3')

# Get rid of ID3 tags
strip_mp3!(hi)
strip_mp3!(bye)

# Concatenate mp3 frames
hi << bye

# Save result to disk
File.binwrite('out.mp3', hi)