3

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

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

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

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

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

4

1 回答 1

6

在阅读了这篇关于俄语 MP3 的文章后,我想出了解决方案。您必须能够在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)
于 2013-10-15T23:48:09.560 回答