我有几个 mp3 文件作为具有相同通道数和相同采样率的二进制字符串。我需要在不使用命令行工具的情况下将它们连接到内存中。
目前我只是进行字符串连接,如下所示:
out = ''
mp3s.each { |mp3| out << mp3 }
音频播放器可以播放结果,但有一些警告,因为据我所知,mp3 标头未正确处理。
有没有办法以更正确的方式进行连接?
在阅读了这篇关于俄语 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)