Java读取MP3 曲名、歌手、专辑、文件长度、版本号、采样率、声道模式

文章目录

  • 一、文件结构
  • 二、字节位置
  • 三、实例分析
  • 四、代码实现
  • 五、执行结果

一、文件结构

MP3 文件大体分为三部分:TAG_V2(ID3V2),Frame, TAG_V1(ID3V1)

ID3V2 包含了作者,作曲,专辑等信息,长度不固定,扩展了ID3V1的信息量
Frame 一系列的帧,个数有文件大小和帧长决定;每个FRAM的长度可能不固定,也可能固定,由位率bitrate决定;每个FRAME由分为帧头和数据实体两部分;帧头记录了mp3的位率,采样率,版本等信息,每个帧之间相互独立
ID3V1 包含了作者,作曲,专辑等信息,长度为128BYTE

二、字节位置

  1. ID3V1部分
    1-3 判断是否是MP3文件
    4-33 曲名
    34-63 作者
    64-93 专辑
    在读取此部分时,是按照字节位数查找对应,只能以字节方式读取,所以无法呈现出字符。
  2. Frame部分
    12-13 音频版本:00MPEG 2.501保留10MPEG 211MPEG 1
    25-26 声道模式:00立体声 01联合立体声10双声道11单声道
    21-22 采样频率:取决于音频版本和相应位置的二进制数,如下图所示
    Java读取MP3 曲名、歌手、专辑、文件长度、版本号、采样率、声道模式_第1张图片
    在读取此部分时,由于相关格式信息以二进制形式存储,需要将读到的字节转化为二进制数据,再逐位进行判断。

三、实例分析

用UE打开.mps文件,如下图所示一首歌曲
Java读取MP3 曲名、歌手、专辑、文件长度、版本号、采样率、声道模式_第2张图片
在读取帧头部,可使用在线进制转换将十六进制的字节转化为二进制
如此首歌曲中: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");
Java读取MP3 曲名、歌手、专辑、文件长度、版本号、采样率、声道模式_第3张图片

你可能感兴趣的:(Java)