MP3 文件大体分为三部分:TAG_V2(ID3V2),Frame, TAG_V1(ID3V1)
ID3V2 | 包含了作者,作曲,专辑等信息,长度不固定,扩展了ID3V1的信息量 |
---|---|
Frame | 一系列的帧,个数有文件大小和帧长决定;每个FRAM的长度可能不固定,也可能固定,由位率bitrate决定;每个FRAME由分为帧头和数据实体两部分;帧头记录了mp3的位率,采样率,版本等信息,每个帧之间相互独立 |
ID3V1 | 包含了作者,作曲,专辑等信息,长度为128BYTE |
在读取此部分时,是按照字节位数查找对应,只能以字节方式读取,所以无法呈现出字符。
00
MPEG 2.501
保留10
MPEG 211
MPEG 100
立体声 01
联合立体声10
双声道11
单声道在读取此部分时,由于相关格式信息以二进制形式存储,需要将读到的字节转化为二进制数据,再逐位进行判断。
用UE打开.mps文件,如下图所示一首歌曲
在读取帧头部,可使用在线进制转换将十六进制的字节转化为二进制
如此首歌曲中:32 00 00 00 转为二进制:0011 0010 0000 0000 0000 0000 0000 0000
在转化过程中,存在高位补0,当转化后的二进制数不满足8、16、32等位数时,需要在前面补足相应位数的0,java实现语言如下:
String s1 = String.format("%8s", Integer.toBinaryString(buf1[0] & 0xFF)).replace(' ', '0');
%8s:
输出的字符串宽度至少为8个字节,如果小于8个,则在该字符串前面加空格
&0xFF:
与1111 1111
按位与运算,实现高位清零
该首歌曲对应信息如下:
位置 | 编码 | 长度 | 描述 |
---|---|---|---|
12-13 | 00 | 2 | 版本MPEG2.5 |
21-22 | 00 | 2 | 采样率11025 |
25-26 | 00 | 2 | 双声道 |
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.*;
public class ReadMp3Inf {
public static void main(String args[]) {
try {
RandomAccessFile raf = new RandomAccessFile("薛之谦 - 丑八怪.mp3", "rw");
byte buf[] = new byte[128];
byte buf1[] = new byte[4];
raf.seek(raf.length() - 128);
raf.read(buf);
raf.seek(45);// 指针往前走45个字节,从第46个字节开始
raf.read(buf1);
if (buf.length != 128) {
System.err.println("MP3标签信息数据长度不合法!");
}
if (!"TAG".equalsIgnoreCase(new String(buf, 0, 3))) {
System.err.println("MP3标签信息数据格式不正确!");
}
String SongName = new String(buf, 3, 30,"gbk");
System.out.println("歌名:" + SongName);
String Singer = new String(buf, 33, 30,"gbk");
System.out.println("作者:" + Singer);
String Album = new String(buf, 63, 30,"gbk");
System.out.println("专辑:" + Album);
System.out.println("文件长度:" + raf.length());
raf.close();
// 将字节转化为二进制字符串
String s1 = String.format("%8s", Integer.toBinaryString(buf1[0] & 0xFF)).replace(' ', '0');
String s2 = String.format("%8s", Integer.toBinaryString(buf1[1] & 0xFF)).replace(' ', '0');
String s3 = String.format("%8s", Integer.toBinaryString(buf1[2] & 0xFF)).replace(' ', '0');
String s4 = String.format("%8s", Integer.toBinaryString(buf1[3] & 0xFF)).replace(' ', '0');
String c = s1 + s2 + s3 + s4;
char sc[] = c.toCharArray();
System.out.println("音频版本:" + AudioVersion(sc[11], sc[12]));
System.out.println("采样频率:" + SampleFrequency(sc[11], sc[12], sc[20], sc[21]));
System.out.println("声道模式:" + ChannelMode(sc[24], sc[25]));
} catch (IOException e) {
System.err.println("发生异常:" + e);
e.printStackTrace();
}
}
//判断音频版本
public static String AudioVersion(char a, char b) {
String result = "";
if ('0' == a && '0' == b)
result = "MPEG 2.5";
else if ('0' == a && '1' == b)
result = "保留";
else if ('1' == a && '0' == b)
result = "MPEG 2";
else if ('1' == a && '1' == b)
result = "MPEG 1";
return result;
}
//判断采样频率
public static int SampleFrequency(char a1, char b1, char a, char b) {
int f = 0;
if (AudioVersion(a1, b1) == "MPEG 1" && '0' == a && '0' == b)
f = 44100;
if (AudioVersion(a1, b1) == "MPEG 2" && '0' == a && '0' == b)
f = 22050;
if (AudioVersion(a1, b1) == "MPEG 2.5" && '0' == a && '0' == b)
f = 11025;
if (AudioVersion(a1, b1) == "MPEG 1" && '0' == a && '1' == b)
f = 48000;
if (AudioVersion(a1, b1) == "MPEG 2" && '0' == a && '1' == b)
f = 24000;
if (AudioVersion(a1, b1) == "MPEG 2.5" && '0' == a && '1' == b)
f = 12000;
if (AudioVersion(a1, b1) == "MPEG 1" && '1' == a && '0' == b)
f = 32000;
if (AudioVersion(a1, b1) == "MPEG 2" && '1' == a && '0' == b)
f = 16000;
if (AudioVersion(a1, b1) == "MPEG 2.5" && '1' == a && '0' == b)
f = 8000;
return f;
}
//判断声道模式
public static String ChannelMode(char a, char b) {
String result = "";
if ('0' == a && '0' == b)
result = "立体声";
else if ('0' == a && '1' == b)
result = "联合立体声";
else if ('1' == a && '0' == b)
result = "双声道";
else if ('1' == a && '1' == b)
result = "单声道";
return result;
}
}
注意:
如果直接用UTF-8
格式读取字节流中的文字,解码方式不与编码gbk
不一致,会导致出现乱码,所以在读取字节时要加上gbk
格式String SongName = new String(buf, 3, 30,"gbk");