📄 mp3file.java
字号:
/**
* @author : Paul Taylor
* @author : Eric Farng
*
* Version @version:$Id: MP3File.java,v 1.31 2007/11/26 14:20:28 paultaylor Exp $
*
* MusicTag Copyright (C)2003,2004
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library; if not,
* you can get a copy from http://www.opensource.org/licenses/lgpl-license.php or write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package com.hadeslee.audiotag.audio.mp3;
import com.hadeslee.audiotag.audio.exceptions.InvalidAudioFrameException;
import com.hadeslee.audiotag.audio.exceptions.ReadOnlyFileException;
import com.hadeslee.audiotag.audio.exceptions.CannotWriteException;
import com.hadeslee.audiotag.audio.AudioFile;
import com.hadeslee.audiotag.logging.AbstractTagDisplayFormatter;
import com.hadeslee.audiotag.logging.PlainTextTagDisplayFormatter;
import com.hadeslee.audiotag.logging.XMLTagDisplayFormatter;
import com.hadeslee.audiotag.tag.Tag;
import com.hadeslee.audiotag.tag.TagException;
import com.hadeslee.audiotag.tag.TagNotFoundException;
import com.hadeslee.audiotag.tag.TagOptionSingleton;
import com.hadeslee.audiotag.tag.ape.APEv2Tag;
import com.hadeslee.audiotag.tag.id3.AbstractID3v2Tag;
import com.hadeslee.audiotag.tag.id3.AbstractTag;
import com.hadeslee.audiotag.tag.id3.ID3v11Tag;
import com.hadeslee.audiotag.tag.id3.ID3v1Tag;
import com.hadeslee.audiotag.tag.id3.ID3v22Tag;
import com.hadeslee.audiotag.tag.id3.ID3v23Tag;
import com.hadeslee.audiotag.tag.id3.ID3v24Tag;
import com.hadeslee.audiotag.tag.lyrics3.AbstractLyrics3;
import com.hadeslee.yoyoplayer.util.Util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.logging.Level;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
* This class represents a physical MP3 File
*/
public class MP3File extends AudioFile {
protected static AbstractTagDisplayFormatter tagFormatter;
/**
* the ID3v2 tag that this file contains.
*/
private AbstractID3v2Tag id3v2tag = null;
/**
* Representation of the idv2 tag as a idv24 tag
*/
private ID3v24Tag id3v2Asv24tag = null;
/**
* The Lyrics3 tag that this file contains.
*/
private AbstractLyrics3 lyrics3tag = null;
/**
* The ID3v1 tag that this file contains.
*/
private ID3v1Tag id3v1tag = null;
/**
* Creates a new empty MP3File datatype that is not associated with a
* specific file.
*/
private APEv2Tag apev2Tag;
public MP3File() {
}
/**
* Creates a new MP3File datatype and parse the tag from the given filename.
*
* @param filename MP3 file
* @throws IOException on any I/O error
* @throws TagException on any exception generated by this library.
*/
public MP3File(String filename) throws IOException, TagException, ReadOnlyFileException, InvalidAudioFrameException {
this(new File(filename));
}
/* Load ID3V1tag if exists */
public static final int LOAD_IDV1TAG = 2;
/* Load ID3V2tag if exists */
public static final int LOAD_IDV2TAG = 4;
/**
* This option is currently ignored
*/
public static final int LOAD_LYRICS3 = 8;
public static final int LOAD_APEV2TAG = 16;
public static final int LOAD_ALL = LOAD_IDV1TAG | LOAD_IDV2TAG | LOAD_LYRICS3 | LOAD_APEV2TAG;
/**
* Creates a new MP3File datatype and parse the tag from the given file
* Object, files must be writable to use this constructor.
*
* @param file MP3 file
* @param loadOptions decide what tags to load
* @throws IOException on any I/O error
* @throws TagException on any exception generated by this library.
*/
public MP3File(File file, int loadOptions) throws IOException, TagException, ReadOnlyFileException, InvalidAudioFrameException {
this(file, loadOptions, false);
}
/**
* Read v1 tag
*
* @param file
* @param newFile
* @param loadOptions
* @throws IOException
*/
private void readV1Tag(File file, RandomAccessFile newFile, int loadOptions) throws IOException {
if ((loadOptions & LOAD_IDV1TAG) != 0) {
log.finer("Attempting to read id3v1tags");
try {
id3v1tag = new ID3v11Tag(newFile, file.getName());
} catch (TagNotFoundException ex) {
log.info("No ids3v11 tag found");
}
try {
if (id3v1tag == null) {
id3v1tag = new ID3v1Tag(newFile, file.getName());
}
} catch (TagNotFoundException ex) {
log.info("No id3v1 tag found");
}
}
}
/**
* Read V2tag if exists
* <p/>
* TODO:shouldnt we be handing TagExceptions:when will they be thrown
*
* @param file
* @param loadOptions
* @throws IOException
* @throws TagException
*/
private void readV2Tag(File file, int loadOptions) throws IOException, TagException {
//We know where the Actual Audio starts so load all the file from start to that point into
//a buffer then we can read the IDv2 information without needing any more file I/O
int startByte = (int) ((MP3AudioHeader) audioHeader).getMp3StartByte();
if (startByte >= AbstractID3v2Tag.TAG_HEADER_LENGTH) {
log.finer("Attempting to read id3v2tags");
FileInputStream fis = null;
FileChannel fc = null;
ByteBuffer bb = null;
try {
fis = new FileInputStream(file);
fc = fis.getChannel();
//Read into Byte Buffer
bb = ByteBuffer.allocate(startByte);
fc.read(bb);
} finally {
if (fc != null) {
fc.close();
}
if (fis != null) {
fis.close();
}
}
bb.rewind();
if ((loadOptions & LOAD_IDV2TAG) != 0) {
log.info("Attempting to read id3v2tags");
try {
this.setID3v2Tag(new ID3v24Tag(bb, file.getName()));
} catch (TagNotFoundException ex) {
log.info("No id3v24 tag found");
System.out.println("*************************************");
System.out.println("*************************************");
System.out.println("*************************************");
System.out.println("*************************************");
System.out.println("*************************************");
System.out.println("*************************************");
System.out.println("*************************************");
}
try {
if (id3v2tag == null) {
this.setID3v2Tag(new ID3v23Tag(bb, file.getName()));
}
} catch (TagNotFoundException ex) {
log.info("No id3v23 tag found");
}
try {
if (id3v2tag == null) {
this.setID3v2Tag(new ID3v22Tag(bb, file.getName()));
}
} catch (TagNotFoundException ex) {
log.info("No id3v22 tag found");
}
}
} else {
log.info("Not enough room for valid id3v2 tag:" + startByte);
}
}
/**
* Read lyrics3 Tag
* <p/>
* TODO:not working
*
* @param file
* @param newFile
* @param loadOptions
* @throws IOException
*/
private void readLyrics3Tag(File file, RandomAccessFile newFile, int loadOptions) throws IOException {
/*if ((loadOptions & LOAD_LYRICS3) != 0)
{
try
{
lyrics3tag = new Lyrics3v2(newFile);
}
catch (TagNotFoundException ex)
{
}
try
{
if (lyrics3tag == null)
{
lyrics3tag = new Lyrics3v1(newFile);
}
}
catch (TagNotFoundException ex)
{
}
}
*/
}
/**
* Regets the auddio header starting from start of file, and write appropriate logging to indicate
* potential problem to user.
*
* @param startByte
* @param currentHeader
* @return
* @throws IOException
* @throws InvalidAudioFrameException
*/
private MP3AudioHeader checkAudioStart(long startByte, MP3AudioHeader currentHeader) throws IOException, InvalidAudioFrameException {
MP3AudioHeader newAudioHeader;
log.warning(file.getPath() + "ID3Tag ends at:" + startByte + ":but mp3audio doesnt start until:" + currentHeader.getMp3StartByte());
//because we cant agree on start location we reread the audioheader from the start of the file, at least
//this way we cant overwrite the audio although we might overwrtite part of the tag if we write this file
//back later
newAudioHeader = new MP3AudioHeader(file, 0);
if (currentHeader.getMp3StartByte() == newAudioHeader.getMp3StartByte()) {
//Although the tag size appears to be incorrect at least we have found the same location for the start
//of audio whether we start searching from start of file or at the end of the alleged of file
log.warning(file.getPath() + "Using mp3audio start:" + newAudioHeader.getMp3StartByte());
} else {
//We get a different value if read from start, can't gurantee 100% correct
log.warning(file.getPath() + "Recalculated using mp3audio start:" + newAudioHeader.getMp3StartByte());
}
return newAudioHeader;
}
/**
* Creates a new MP3File datatype and parse the tag from the given file
* Object, files can be onpened read only if required.
*
* @param file MP3 file
* @param loadOptions decide what tags to load
* @param readOnly causes the files to be opened readonly
* @throws IOException on any I/O error
* @throws TagException on any exception generated by this library.
*/
public MP3File(File file, int loadOptions, boolean readOnly) throws IOException, TagException, ReadOnlyFileException, InvalidAudioFrameException {
RandomAccessFile newFile = null;
try {
this.file = file;
//Check File accessibility
newFile = checkFilePermissions(file, readOnly);
//Read ID3v2 tag size (if tag exists) to allow audioheader parsing to skip over tag
long startByte = AbstractID3v2Tag.getV2TagSizeIfExists(file);
//If exception reading Mpeg then we should give up no point continuing
audioHeader = new MP3AudioHeader(file, startByte);
if (startByte != ((MP3AudioHeader) audioHeader).getMp3StartByte()) {
audioHeader = checkAudioStart(startByte, (MP3AudioHeader) audioHeader);
}
//Read v1 tags (if any)
readV1Tag(file, newFile, loadOptions);
//Read v2 tags (if any)
readV2Tag(file, loadOptions);
readAPEv2Tag(file, loadOptions);
//If we have a v2 tag use that, if we dont but have v1 tag use that
//otherwise use nothing
//TODO:if have both should we merge
//rather than just returning specific ID3v22 tag, would it be better to return v24 version ?
if (this.getID3v2Tag() != null) {
tag = this.getID3v2Tag();
} else if (id3v1tag != null) {
tag = id3v1tag;
}
//Read Lyrics 3
//readLyrics3Tag(File file,RandomAccessFile newFile,int loadOptions)
} finally {
if (newFile != null) {
newFile.close();
}
}
}
private void readAPEv2Tag(File file, int loadOptions) throws IOException {
if ((loadOptions & LOAD_APEV2TAG) != 0) {
try {
log.log(Level.FINE, "尝试读取APEv2Tag");
apev2Tag = new APEv2Tag(file);
} catch (UnsupportedAudioFileException ex) {
log.log(Level.INFO, "没有读到APEV2标签");
}
}
}
/**
* Used by tags when writing to calculate the location of the music file
*
* @return the location within the file that the audio starts
*/
public long getMP3StartByte(File file) throws InvalidAudioFrameException, IOException {
try {
//Read ID3v2 tag size (if tag exists) to allow audioheader parsing to skip over tag
long startByte = AbstractID3v2Tag.getV2TagSizeIfExists(file);
MP3AudioHeader audioHeader = new MP3AudioHeader(file, startByte);
if (startByte != audioHeader.getMp3StartByte()) {
audioHeader = checkAudioStart(startByte, audioHeader);
}
return audioHeader.getMp3StartByte();
} catch (InvalidAudioFrameException iafe) {
throw iafe;
} catch (IOException ioe) {
throw ioe;
}
}
/**
* Extracts the raw ID3v2 tag data into a file.
* <p/>
* This provides access to the raw data before manipulation, the data is written from the start of the file
* to the start of the Audio Data. This is primarily useful for manipulating corrupted tags that are not
* (fully) loaded using the standard methods.
*
* @param outputFile to write the data to
* @return
* @throws TagNotFoundException
* @throws IOException
*/
public File extractID3v2TagDataIntoFile(File outputFile) throws TagNotFoundException, IOException {
int startByte = (int) ((MP3AudioHeader) audioHeader).getMp3StartByte();
if (startByte >= 0) {
//Read byte into buffer
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteBuffer bb = ByteBuffer.allocate(startByte);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -