com.jflyfox.util.extend.mp3.MusicInfo Maven / Gradle / Ivy
/**
* Copyright 2015-2025 FLY的狐狸(email:[email protected] qq:369191470).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.jflyfox.util.extend.mp3;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 每个ID3V2.3的标签都一个标签头和若干个标签帧或一个扩展标签头组成。关于曲目的信息如标题、作者等都存放在不同的标签帧中,扩展标签头和标签帧并不是必要的,但每个标签至少要有一个标签帧。标签头和标签帧一起顺序存放在MP3文件的首部。
* @author moon.lee
*
*/
public class MusicInfo {
private String path="";
private boolean isAnalysis=false;
/**
* 必须为"ID3"否则认为标签不存在
* 3个字节
*/
private final int HEADER_SIZE=3;
private byte[] header;
private String HEAHER_START="ID3";
/**
* 版本号;ID3V2.3就记录03,ID3V2.4就记录04
* 一个字节
*/
private byte version;
/**
* 副版本号;此版本记录为00
* 一个字节
*/
private byte reVersion;
/**
* 标志字节一般为0,定义如下:
* 一个字节
* abc00000
* a -- 表示是否使用不同步(一般不设置)
* b -- 表示是否有扩展头部,一般没有(至少Winamp没有记录),所以一般也不设置
* c -- 表示是否为测试标签(99.99%的标签都不是测试用的啦,所以一般也不设置)
*/
private byte flag;
/**
* 标签大小,包括标签帧和扩展标签头。(不包括标签头的10个字节)
* 一共四个字节,但每个字节只用7位,最高位不使用恒为0。所以格式如下
* 0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx
* 计算大小时要将0去掉,得到一个28位的二进制数,就是标签大小(不懂为什么要这样做),计算公式如下:
* int total_size;
* total_size = Size[0]*0x200000
* +Size[1]*0x4000
* +Size[2]*0x80
* +Size[3]
*/
private int SIZE_SIZE=4;
private byte[] size;
private Map frameInfos;
private int LABEL_SIZE=10;
public MusicInfo() {
super();
frameInfos=new HashMap();
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public boolean isAnalysis() {
return isAnalysis;
}
public void setAnalysis(boolean isAnalysis) {
this.isAnalysis = isAnalysis;
}
public byte[] getHeader() {
return header;
}
public void setHeader(byte[] header) {
this.header = header;
}
public byte getVersion() {
return version;
}
public void setVersion(byte version) {
this.version = version;
}
public byte getReVersion() {
return reVersion;
}
public void setReVersion(byte reVersion) {
this.reVersion = reVersion;
}
public byte getFlag() {
return flag;
}
public void setFlag(byte flag) {
this.flag = flag;
}
public byte[] getSize() {
return size;
}
public void setSize(byte[] size) {
this.size = size;
}
public Map getFrameInfos() {
return frameInfos;
}
/**
* 解析信息
* @return 0表示成功 1表示不是mp3文件 2表示文件不存在
*/
public int parseMusic(){
return parseMusic("UTF-16");
}
/**
* 解析信息
* @param charset 编码方式
* @return 0表示成功 1表示不是mp3文件 2表示文件不存在 3表示解析时异常
*/
public int parseMusic(String charset) {
File file = new File(path);
if (!file.exists()) {
return 2;
}
if (!file.getName().endsWith(".mp3")) {
return 1;
}
try {
RandomAccessFile raf = new RandomAccessFile(file, "r");
/**
* 头部信息
*/
header = new byte[HEADER_SIZE];
raf.read(header, 0, HEADER_SIZE);
// System.out.println("header:"+new String(header));
if (new String(header).equals(HEAHER_START)) {
/**
* 版本
*/
version = raf.readByte();
System.out.println("version:" + version);
/**
* 副版本
*/
reVersion = raf.readByte();
System.out.println("reVersion:" + reVersion);
/**
* 标志
*/
flag = raf.readByte();
System.out.println("flag:" + flag);
/**
* 标签大小
*/
size = new byte[SIZE_SIZE];
raf.read(size);
for (int i = 0; i < size.length; i++) {
System.out.println("size[" + i + "]:0x" + parseDecimalToBinary(size[i]));
}
// int total_size=size[0]*0x200000
// +size[1]*0x4000
// +size[2]*0x80
// +size[3];
/**
* 标签信息
*/
byte[] label = new byte[LABEL_SIZE];
raf.read(label);
/**
* 遍历标签信息
*/
FrameInfo frameInfo = null;
while ((frameInfo = decodeFrame(label)) != null) {
/**
* 根据标签内容大小获取标签内容
*/
int frameContentSize = frameInfo.getFrameContentSize();
byte[] content = new byte[frameContentSize];
/**
* 跳过一个字节 '\0'
*/
raf.skipBytes(1);
/**
* 读取帧内容
*/
raf.read(content);
frameInfo.setContent(content);
/**
* 将帧内容加入到歌曲信息
*/
frameInfos.put(frameInfo.getFrameId(), frameInfo);
raf.read(label);
}
/**
* 信息解析完成关闭管道
*/
raf.close();
}
return 0;
} catch (Exception e) {
e.printStackTrace();
return 3;
}
}
/**
* 返回图片数据信息
*
* @return 图片map 键mime 图片类型 键data 图片数据
* 返回空值表示没有解析到图片
*
*/
public Map getImage(){
if(frameInfos==null)return null;
FrameInfo apicInfo=frameInfos.get("APIC");
if(apicInfo==null)return null;
/**
* 图片数据
*/
byte[]apic=apicInfo.getContent();
boolean isMIMEComplte=false;
int i=0;
/**
* 查找图片数据起始位置
*/
Mapmap=new HashMap();
for(;i> j) & 1;
tempStr = x + tempStr;
}
return tempStr;
}
/**
* 解析帧标签信息
*
* @param frameHead帧标签
* 10字节
* @return
*/
private FrameInfo decodeFrame(byte[] frameHead) {
if (frameHead.length != LABEL_SIZE) {
return null;
}
try {
/**
* 将读取到的开头四个字节匹配字符串[A-Z]{3}[A-Z0-9]{1} 匹配不成功就返回空标签
*/
String frameId = new String(frameHead, 0, 4);
Pattern pattern = Pattern.compile("[A-Z]{3}[A-Z0-9]{1}");
Matcher matcher = pattern.matcher(frameId);
if (!matcher.matches()) {
return null;
}
/**
* 匹配成功就解析帧标签
*/
System.out.println("frameID:" + frameId);
/**
* 标签内容大小 减去 '\0'之后的标签内容大小
*/
int qw = frameHead[4];
int bw = frameHead[5];
int sw = frameHead[6];
int gw = frameHead[7];
if (qw < 0) {
qw = Math.abs(qw) + 128;
}
if (bw < 0) {
bw = Math.abs(bw) + 128;
}
if (sw < 0) {
sw = Math.abs(sw) + 128;
}
if (gw < 0) {
gw = Math.abs(gw) + 128;
}
// int frameContentSize=new Integer(new String(frameHead,4,4));
int frameContentSize = qw * 0x1000000 + bw * 0x10000 + sw * 0x100 + gw - 1;
/**
* 解析标志信息
*/
byte[] flag = new byte[2];
flag[0] = frameHead[8];
flag[1] = frameHead[9];
FrameInfo frameInfo = new FrameInfo();
frameInfo.setFrameId(frameId);
frameInfo.setFrameContentSize(frameContentSize);
frameInfo.setFlag(flag);
return frameInfo;
} catch (NumberFormatException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return toString("UTF-16");
}
public String toString(String charset) {
return "title:" + getTitle(charset) + "\nperformer:" + getPerformer(charset) + "\nalbum:" + getAlbum(charset);
}
}