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