jaudiotagger update to maven

This commit is contained in:
openaudible 2018-06-30 12:12:46 -07:00
parent b9089f8653
commit 319f49b709
514 changed files with 18 additions and 76428 deletions

14
pom.xml
View file

@ -142,6 +142,14 @@
<id>maven-eclipse-repo</id>
<url>http://maven-eclipse.github.io/maven</url>
</repository>
<repository>
<id>jaudiotagger-repository</id>
<url>https://dl.bintray.com/ijabz/maven</url>
</repository>
</repositories>
@ -216,6 +224,12 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.jthink</groupId>
<artifactId>jaudiotagger</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>

View file

@ -62,9 +62,9 @@
</ul>
<H3>Help</H3>
<p>This may stop working at any time, due to changes in Audible systems or web site. If this happens, check for an update.</p>
<p>No technical support is provided. Use at your own risk. Open Source additions are welcome. </p>
<p>Suggestions welcome by sending issue or pull request.</p>
<H3>Additional Help</H3>
<p>This may stop working at any time, due to changes in Audible systems or web site. If this happens, check for an update or open issues.</p>
<p>No technical support is provided. Open Source additions are welcome. </p>
<p>Suggestions welcome by creating an issue or submitting a pull request.</p>
</body>
</html>

View file

@ -1,60 +0,0 @@
/**
* @author : Paul Taylor
* <p/>
* Version @version:$Id: FileConstants.java 520 2008-01-01 15:16:38Z paultaylor $
* <p/>
* Jaudiotagger Copyright (C)2004,2005
* <p/>
* 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.
* <p/>
* 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.
* <p/>
* 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
* <p/>
* Description:
*/
package org.jaudiotagger;
/**
* Definitions of the bit used when reading file format from file
*/
public interface FileConstants {
/**
* defined for convenience
*/
int BIT7 = 0x80;
/**
* defined for convenience
*/
int BIT6 = 0x40;
/**
* defined for convenience
*/
int BIT5 = 0x20;
/**
* defined for convenience
*/
int BIT4 = 0x10;
/**
* defined for convenience
*/
int BIT3 = 0x08;
/**
* defined for convenience
*/
int BIT2 = 0x04;
/**
* defined for convenience
*/
int BIT1 = 0x02;
/**
* defined for convenience
*/
int BIT0 = 0x01;
}

View file

@ -1,303 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio;
import org.jaudiotagger.audio.aiff.AiffTag;
import org.jaudiotagger.audio.exceptions.CannotWriteException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.audio.flac.metadatablock.MetadataBlockDataPicture;
import org.jaudiotagger.audio.real.RealTag;
import org.jaudiotagger.audio.wav.WavTag;
import org.jaudiotagger.logging.ErrorMessage;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.asf.AsfTag;
import org.jaudiotagger.tag.flac.FlacTag;
import org.jaudiotagger.tag.mp4.Mp4Tag;
import org.jaudiotagger.tag.vorbiscomment.VorbisCommentTag;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.logging.Logger;
/**
* <p>This is the main object manipulated by the user representing an audiofile, its properties and its tag.</p>
* <p>The prefered way to obtain an <code>AudioFile</code> is to use the <code>AudioFileIO.read(File)</code> method.</p>
* <p>The <code>AudioFile</code> contains every properties associated with the file itself (no meta-data), like the bitrate, the sampling rate, the encoding audioHeaders, etc.</p>
* <p>To get the meta-data contained in this file you have to get the <code>Tag</code> of this <code>AudioFile</code></p>
*
* @author Raphael Slinckx
* @version $Id: AudioFile.java 1067 2012-10-17 18:01:32Z garymcgath $
* @see AudioFileIO
* @see Tag
* @since v0.01
*/
public class AudioFile {
//Logger
public static Logger logger = Logger.getLogger("org.jaudiotagger.audio");
/**
* The physical file that this instance represents.
*/
protected File file;
/**
* The Audio header info
*/
protected AudioHeader audioHeader;
/**
* The tag
*/
protected Tag tag;
public AudioFile() {
}
/**
* <p>These constructors are used by the different readers, users should not use them, but use the <code>AudioFileIO.read(File)</code> method instead !.</p>
* <p>Create the AudioFile representing file f, the encoding audio headers and containing the tag</p>
*
* @param f The file of the audio file
* @param audioHeader the encoding audioHeaders over this file
* @param tag the tag contained in this file or null if no tag exists
*/
public AudioFile(File f, AudioHeader audioHeader, Tag tag) {
this.file = f;
this.audioHeader = audioHeader;
this.tag = tag;
}
/**
* <p>These constructors are used by the different readers, users should not use them, but use the <code>AudioFileIO.read(File)</code> method instead !.</p>
* <p>Create the AudioFile representing file denoted by pathnames, the encoding audio Headers and containing the tag</p>
*
* @param s The pathname of the audio file
* @param audioHeader the encoding audioHeaders over this file
* @param tag the tag contained in this file
*/
public AudioFile(String s, AudioHeader audioHeader, Tag tag) {
this.file = new File(s);
this.audioHeader = audioHeader;
this.tag = tag;
}
/**
* @param file
* @return filename with audioFormat separator stripped off.
*/
public static String getBaseFilename(File file) {
int index = file.getName().toLowerCase().lastIndexOf(".");
if (index > 0) {
return file.getName().substring(0, index);
}
return file.getName();
}
/**
* <p>Write the tag contained in this AudioFile in the actual file on the disk, this is the same as calling the <code>AudioFileIO.write(this)</code> method.</p>
*
* @throws CannotWriteException If the file could not be written/accessed, the extension wasn't recognized, or other IO error occured.
* @see AudioFileIO
*/
public void commit() throws CannotWriteException {
AudioFileIO.write(this);
}
/**
* Retrieve the physical file
*
* @return
*/
public File getFile() {
return file;
}
/**
* Set the file to store the info in
*
* @param file
*/
public void setFile(File file) {
this.file = file;
}
/**
* Return audio header information
*
* @return
*/
public AudioHeader getAudioHeader() {
return audioHeader;
}
/**
* <p>Returns the tag contained in this AudioFile, the <code>Tag</code> contains any useful meta-data, like
* artist, album, title, etc. If the file does not contain any tag the null is returned. Some audio formats do
* not allow there to be no tag so in this case the reader would return an empty tag whereas for others such
* as mp3 it is purely optional.
*
* @return Returns the tag contained in this AudioFile, or null if no tag exists.
*/
public Tag getTag() {
return tag;
}
/**
* Assign a tag to this audio file
*
* @param tag Tag to be assigned
*/
public void setTag(Tag tag) {
this.tag = tag;
}
/**
* <p>Returns a multi-line string with the file path, the encoding audioHeader, and the tag contents.</p>
*
* @return A multi-line string with the file path, the encoding audioHeader, and the tag contents.
* TODO Maybe this can be changed ?
*/
public String toString() {
return "AudioFile " + getFile().getAbsolutePath()
+ " --------\n" + audioHeader.toString() + "\n" + ((tag == null) ? "" : tag.toString()) + "\n-------------------";
}
/**
* Check does file exist
*
* @param file
* @throws FileNotFoundException if file not found
*/
public void checkFileExists(File file) throws FileNotFoundException {
logger.config("Reading file:" + "path" + file.getPath() + ":abs:" + file.getAbsolutePath());
if (!file.exists()) {
logger.severe("Unable to find:" + file.getPath());
throw new FileNotFoundException(ErrorMessage.UNABLE_TO_FIND_FILE.getMsg(file.getPath()));
}
}
/**
* Checks the file is accessible with the correct permissions, otherwise exception occurs
*
* @param file
* @param readOnly
* @return
* @throws ReadOnlyFileException
* @throws FileNotFoundException
*/
protected RandomAccessFile checkFilePermissions(File file, boolean readOnly) throws ReadOnlyFileException, FileNotFoundException {
RandomAccessFile newFile;
checkFileExists(file);
// Unless opened as readonly the file must be writable
if (readOnly) {
newFile = new RandomAccessFile(file, "r");
} else {
if (!file.canWrite()) {
logger.severe("Unable to write:" + file.getPath());
throw new ReadOnlyFileException(ErrorMessage.NO_PERMISSIONS_TO_WRITE_TO_FILE.getMsg(file.getPath()));
}
newFile = new RandomAccessFile(file, "rws");
}
return newFile;
}
/**
* Optional debugging method. Must override to do anything interesting.
*
* @return Empty string.
*/
public String displayStructureAsXML() {
return "";
}
/**
* Optional debugging method. Must override to do anything interesting.
*
* @return
*/
public String displayStructureAsPlainText() {
return "";
}
/**
* Create Default Tag
*
* @return
*/
public Tag createDefaultTag() {
if (SupportedFileFormat.FLAC.getFilesuffix().equals(file.getName().substring(file.getName().lastIndexOf('.')))) {
return new FlacTag(VorbisCommentTag.createNewTag(), new ArrayList<MetadataBlockDataPicture>());
} else if (SupportedFileFormat.OGG.getFilesuffix().equals(file.getName().substring(file.getName().lastIndexOf('.')))) {
return VorbisCommentTag.createNewTag();
} else if (SupportedFileFormat.MP4.getFilesuffix().equals(file.getName().substring(file.getName().lastIndexOf('.')))) {
return new Mp4Tag();
} else if (SupportedFileFormat.M4A.getFilesuffix().equals(file.getName().substring(file.getName().lastIndexOf('.')))) {
return new Mp4Tag();
} else if (SupportedFileFormat.M4P.getFilesuffix().equals(file.getName().substring(file.getName().lastIndexOf('.')))) {
return new Mp4Tag();
} else if (SupportedFileFormat.WMA.getFilesuffix().equals(file.getName().substring(file.getName().lastIndexOf('.')))) {
return new AsfTag();
} else if (SupportedFileFormat.WAV.getFilesuffix().equals(file.getName().substring(file.getName().lastIndexOf('.')))) {
return new WavTag();
} else if (SupportedFileFormat.RA.getFilesuffix().equals(file.getName().substring(file.getName().lastIndexOf('.')))) {
return new RealTag();
} else if (SupportedFileFormat.RM.getFilesuffix().equals(file.getName().substring(file.getName().lastIndexOf('.')))) {
return new RealTag();
} else if (SupportedFileFormat.AIF.getFilesuffix().equals(file.getName().substring(file.getName().lastIndexOf('.')))) {
return new AiffTag();
} else {
throw new RuntimeException("Unable to create default tag for this file format");
}
}
/**
* Get the tag or if the file doesn't have one at all, create a default tag and return
*
* @return
*/
public Tag getTagOrCreateDefault() {
Tag tag = getTag();
if (tag == null) {
return createDefaultTag();
}
return tag;
}
/**
* Get the tag or if the file doesn't have one at all, create a default tag and set it
*
* @return
*/
public Tag getTagOrCreateAndSetDefault() {
Tag tag = getTagOrCreateDefault();
setTag(tag);
return tag;
}
public Tag getTagAndConvertOrCreateAndSetDefault() {
return getTagOrCreateAndSetDefault();
}
}

View file

@ -1,77 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio;
import org.jaudiotagger.audio.generic.Utils;
import java.io.File;
import java.io.FileFilter;
/**
* <p>This is a simple FileFilter that will only allow the file supported by this library.</p>
* <p>It will also accept directories. An additional condition is that file must be readable (read permission) and
* are not hidden (dot files, or hidden files)</p>
*
* @author Raphael Slinckx
* @version $Id: AudioFileFilter.java 836 2009-11-12 15:44:07Z paultaylor $
* @since v0.01
*/
public class AudioFileFilter implements FileFilter {
/**
* allows Directories
*/
private final boolean allowDirectories;
public AudioFileFilter(boolean allowDirectories) {
this.allowDirectories = allowDirectories;
}
public AudioFileFilter() {
this(true);
}
/**
* <p>Check whether the given file meet the required conditions (supported by the library OR directory).
* The File must also be readable and not hidden.</p>
*
* @param f The file to test
* @return a boolean indicating if the file is accepted or not
*/
public boolean accept(File f) {
if (f.isHidden() || !f.canRead()) {
return false;
}
if (f.isDirectory()) {
return allowDirectories;
}
String ext = Utils.getExtension(f);
try {
if (SupportedFileFormat.valueOf(ext.toUpperCase()) != null) {
return true;
}
} catch (IllegalArgumentException iae) {
//Not known enum value
return false;
}
return false;
}
}

View file

@ -1,317 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio;
import org.jaudiotagger.audio.aiff.AiffFileReader;
import org.jaudiotagger.audio.asf.AsfFileReader;
import org.jaudiotagger.audio.asf.AsfFileWriter;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.CannotWriteException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.audio.flac.FlacFileReader;
import org.jaudiotagger.audio.flac.FlacFileWriter;
import org.jaudiotagger.audio.generic.*;
import org.jaudiotagger.audio.mp3.MP3FileReader;
import org.jaudiotagger.audio.mp3.MP3FileWriter;
import org.jaudiotagger.audio.mp4.Mp4FileReader;
import org.jaudiotagger.audio.mp4.Mp4FileWriter;
import org.jaudiotagger.audio.ogg.OggFileReader;
import org.jaudiotagger.audio.ogg.OggFileWriter;
import org.jaudiotagger.audio.real.RealFileReader;
import org.jaudiotagger.audio.wav.WavFileReader;
import org.jaudiotagger.audio.wav.WavFileWriter;
import org.jaudiotagger.logging.ErrorMessage;
import org.jaudiotagger.tag.TagException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Logger;
/**
* <p/>
* The main entry point for the Tag Reading/Writing operations, this class will
* select the appropriate reader/writer for the given file.
* </p>
* <p/>
* It selects the appropriate reader/writer based on the file extension (case
* ignored).
* </p>
* <p/>
* Here is an simple example of use:
* </p>
* <p/>
* <code>
* AudioFile audioFile = AudioFileIO.read(new File("audiofile.mp3")); //Reads the given file.<br/>
* int bitrate = audioFile.getBitrate(); //Retreives the bitrate of the file.<br/>
* String artist = audioFile.getTag().getFirst(TagFieldKey.ARTIST); //Retreive the artist name.<br/>
* audioFile.getTag().setGenre("Progressive Rock"); //Sets the genre to Prog. Rock, note the file on disk is still unmodified.<br/>
* AudioFileIO.write(audioFile); //Write the modifications in the file on disk.
* </code>
* </p>
* <p/>
* You can also use the <code>commit()</code> method defined for
* <code>AudioFile</code>s to achieve the same goal as
* <code>AudioFileIO.write(File)</code>, like this:
* </p>
* <p/>
* <code>
* AudioFile audioFile = AudioFileIO.read(new File("audiofile.mp3"));<br/>
* audioFile.getTag().setGenre("Progressive Rock");<br/>
* audioFile.commit(); //Write the modifications in the file on disk.<br/>
* </code>
* </p>
*
* @author Raphael Slinckx
* @version $Id: AudioFileIO.java 1067 2012-10-17 18:01:32Z garymcgath $
* @see AudioFile
* @see org.jaudiotagger.tag.Tag
* @since v0.01
*/
public class AudioFileIO {
//Logger
public static Logger logger = Logger.getLogger("org.jaudiotagger.audio");
// !! Do not forget to also add new supported extensions to AudioFileFilter
// !!
/**
* This field contains the default instance for static use.
*/
private static AudioFileIO defaultInstance;
/**
* This member is used to broadcast modification events to registered
*/
private final ModificationHandler modificationHandler;
// These tables contains all the readers/writers associated with extension
// as a key
private Map<String, AudioFileReader> readers = new HashMap<String, AudioFileReader>();
private Map<String, AudioFileWriter> writers = new HashMap<String, AudioFileWriter>();
/**
* Creates an instance.
*/
public AudioFileIO() {
this.modificationHandler = new ModificationHandler();
prepareReadersAndWriters();
}
/**
* <p/>
* Delete the tag, if any, contained in the given file.
* </p>
*
* @param f The file where the tag will be deleted
* @throws org.jaudiotagger.audio.exceptions.CannotWriteException If the file could not be written/accessed, the extension
* wasn't recognized, or other IO error occurred.
* @throws org.jaudiotagger.audio.exceptions.CannotReadException
*/
public static void delete(AudioFile f) throws CannotReadException, CannotWriteException {
getDefaultAudioFileIO().deleteTag(f);
}
/**
* This method returns the default instance for static use.<br>
*
* @return The default instance.
*/
public static AudioFileIO getDefaultAudioFileIO() {
if (defaultInstance == null) {
defaultInstance = new AudioFileIO();
}
return defaultInstance;
}
/**
* <p/>
* Read the tag contained in the given file.
* </p>
*
* @param f The file to read.
* @return The AudioFile with the file tag and the file encoding info.
* @throws org.jaudiotagger.audio.exceptions.CannotReadException If the file could not be read, the extension wasn't
* recognized, or an IO error occurred during the read.
* @throws org.jaudiotagger.tag.TagException
* @throws org.jaudiotagger.audio.exceptions.ReadOnlyFileException
* @throws java.io.IOException
* @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
*/
public static AudioFile read(File f)
throws CannotReadException, IOException, TagException, ReadOnlyFileException, InvalidAudioFrameException {
return getDefaultAudioFileIO().readFile(f);
}
/**
* <p/>
* Write the tag contained in the audioFile in the actual file on the disk.
* </p>
*
* @param f The AudioFile to be written
* @throws CannotWriteException If the file could not be written/accessed, the extension
* wasn't recognized, or other IO error occurred.
*/
public static void write(AudioFile f) throws CannotWriteException {
getDefaultAudioFileIO().writeFile(f);
}
/**
* Adds an listener for all file formats.
*
* @param listener listener
*/
public void addAudioFileModificationListener(
AudioFileModificationListener listener) {
this.modificationHandler.addAudioFileModificationListener(listener);
}
/**
* <p/>
* Delete the tag, if any, contained in the given file.
* </p>
*
* @param f The file where the tag will be deleted
* @throws org.jaudiotagger.audio.exceptions.CannotWriteException If the file could not be written/accessed, the extension
* wasn't recognized, or other IO error occurred.
* @throws org.jaudiotagger.audio.exceptions.CannotReadException
*/
public void deleteTag(AudioFile f) throws CannotReadException, CannotWriteException {
String ext = Utils.getExtension(f.getFile());
Object afw = writers.get(ext);
if (afw == null) {
throw new CannotWriteException(ErrorMessage.NO_DELETER_FOR_THIS_FORMAT.getMsg(ext));
}
((AudioFileWriter) afw).delete(f);
}
/**
* Creates the readers and writers.
*/
private void prepareReadersAndWriters() {
// Tag Readers
readers.put(SupportedFileFormat.OGG.getFilesuffix(), new OggFileReader());
readers.put(SupportedFileFormat.FLAC.getFilesuffix(), new FlacFileReader());
readers.put(SupportedFileFormat.MP3.getFilesuffix(), new MP3FileReader());
readers.put(SupportedFileFormat.MP4.getFilesuffix(), new Mp4FileReader());
readers.put(SupportedFileFormat.M4A.getFilesuffix(), new Mp4FileReader());
readers.put(SupportedFileFormat.M4P.getFilesuffix(), new Mp4FileReader());
readers.put(SupportedFileFormat.M4B.getFilesuffix(), new Mp4FileReader());
readers.put(SupportedFileFormat.WAV.getFilesuffix(), new WavFileReader());
readers.put(SupportedFileFormat.WMA.getFilesuffix(), new AsfFileReader());
readers.put(SupportedFileFormat.AIF.getFilesuffix(), new AiffFileReader());
final RealFileReader realReader = new RealFileReader();
readers.put(SupportedFileFormat.RA.getFilesuffix(), realReader);
readers.put(SupportedFileFormat.RM.getFilesuffix(), realReader);
// Tag Writers
writers.put(SupportedFileFormat.OGG.getFilesuffix(), new OggFileWriter());
writers.put(SupportedFileFormat.FLAC.getFilesuffix(), new FlacFileWriter());
writers.put(SupportedFileFormat.MP3.getFilesuffix(), new MP3FileWriter());
writers.put(SupportedFileFormat.MP4.getFilesuffix(), new Mp4FileWriter());
writers.put(SupportedFileFormat.M4A.getFilesuffix(), new Mp4FileWriter());
writers.put(SupportedFileFormat.M4P.getFilesuffix(), new Mp4FileWriter());
writers.put(SupportedFileFormat.M4B.getFilesuffix(), new Mp4FileWriter());
writers.put(SupportedFileFormat.WAV.getFilesuffix(), new WavFileWriter());
writers.put(SupportedFileFormat.WMA.getFilesuffix(), new AsfFileWriter());
// Register modificationHandler
Iterator<AudioFileWriter> it = writers.values().iterator();
for (AudioFileWriter curr : writers.values()) {
curr.setAudioFileModificationListener(this.modificationHandler);
}
}
/**
* <p/>
* Read the tag contained in the given file.
* </p>
*
* @param f The file to read.
* @return The AudioFile with the file tag and the file encoding info.
* @throws org.jaudiotagger.audio.exceptions.CannotReadException If the file could not be read, the extension wasn't
* recognized, or an IO error occurred during the read.
* @throws org.jaudiotagger.tag.TagException
* @throws org.jaudiotagger.audio.exceptions.ReadOnlyFileException
* @throws java.io.IOException
* @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
*/
public AudioFile readFile(File f)
throws CannotReadException, IOException, TagException, ReadOnlyFileException, InvalidAudioFrameException {
checkFileExists(f);
String ext = Utils.getExtension(f);
AudioFileReader afr = readers.get(ext);
if (afr == null) {
throw new CannotReadException(ErrorMessage.NO_READER_FOR_THIS_FORMAT.getMsg(ext));
}
return afr.read(f);
}
/**
* Check does file exist
*
* @param file
* @throws java.io.FileNotFoundException
*/
public void checkFileExists(File file) throws FileNotFoundException {
logger.config("Reading file:" + "path" + file.getPath() + ":abs:" + file.getAbsolutePath());
if (!file.exists()) {
logger.severe("Unable to find:" + file.getPath());
throw new FileNotFoundException(ErrorMessage.UNABLE_TO_FIND_FILE.getMsg(file.getPath()));
}
}
/**
* Removes a listener for all file formats.
*
* @param listener listener
*/
public void removeAudioFileModificationListener(
AudioFileModificationListener listener) {
this.modificationHandler.removeAudioFileModificationListener(listener);
}
/**
* <p/>
* Write the tag contained in the audioFile in the actual file on the disk.
* </p>
*
* @param f The AudioFile to be written
* @throws CannotWriteException If the file could not be written/accessed, the extension
* wasn't recognized, or other IO error occurred.
*/
public void writeFile(AudioFile f) throws CannotWriteException {
String ext = Utils.getExtension(f.getFile());
AudioFileWriter afw = writers.get(ext);
if (afw == null) {
throw new CannotWriteException(ErrorMessage.NO_WRITER_FOR_THIS_FORMAT.getMsg(ext));
}
afw.write(f);
}
}

View file

@ -1,64 +0,0 @@
package org.jaudiotagger.audio;
/**
* Representation of AudioHeader
* <p/>
* <p>Contains info about the Audio Header
*/
public interface AudioHeader {
/**
* @return the audio file type
*/
String getEncodingType();
/**
* @return the BitRate of the Audio
*/
String getBitRate();
/**
* @return birate as a number
*/
long getBitRateAsNumber();
/**
* @return the Sampling rate
*/
String getSampleRate();
/**
* @return
*/
int getSampleRateAsNumber();
/**
* @return the format
*/
String getFormat();
/**
* @return the Channel Mode such as Stereo or Mono
*/
String getChannels();
/**
* @return if the bitRate is variable
*/
boolean isVariableBitRate();
/**
* @return track length
*/
int getTrackLength();
/**
* @return the number of bits per sample
*/
int getBitsPerSample();
/**
* @return
*/
boolean isLossless();
}

View file

@ -1,36 +0,0 @@
package org.jaudiotagger.audio;
/**
* Files formats currently supported by Library.
* Each enum value is associated with a file suffix (extension).
*/
public enum SupportedFileFormat {
OGG("ogg"),
MP3("mp3"),
FLAC("flac"),
MP4("mp4"),
M4A("m4a"),
M4P("m4p"),
WMA("wma"),
WAV("wav"),
RA("ra"),
RM("rm"),
M4B("m4b"),
AIF("aif");
private String filesuffix;
/**
* Constructor for internal use by this enum.
*/
SupportedFileFormat(String filesuffix) {
this.filesuffix = filesuffix;
}
/**
* Returns the file suffix (lower case without initial .) associated with the format.
*/
public String getFilesuffix() {
return filesuffix;
}
}

View file

@ -1,179 +0,0 @@
package org.jaudiotagger.audio.aiff;
import org.jaudiotagger.audio.generic.GenericAudioHeader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Non-"tag" metadata from the AIFF file. In general, read-only.
*/
public class AiffAudioHeader extends GenericAudioHeader {
private FileType fileType;
private Date timestamp;
private Endian endian;
private String audioEncoding;
private String name;
private String author;
private String copyright;
private List<String> applicationIdentifiers;
private List<String> comments;
public AiffAudioHeader() {
applicationIdentifiers = new ArrayList<String>();
comments = new ArrayList<String>();
endian = Endian.BIG_ENDIAN;
}
/**
* Return the timestamp of the file.
*/
public Date getTimestamp() {
return timestamp;
}
/**
* Set the timestamp.
*/
public void setTimestamp(Date d) {
timestamp = d;
}
/**
* Return the file type (AIFF or AIFC)
*/
public FileType getFileType() {
return fileType;
}
/**
* Set the file type (AIFF or AIFC)
*/
public void setFileType(FileType typ) {
fileType = typ;
}
/**
* Return the author
*/
public String getAuthor() {
return author;
}
/**
* Set the author
*/
public void setAuthor(String a) {
author = a;
}
/**
* Return the name. May be null.
*/
public String getName() {
return name;
}
/**
* Set the name
*/
public void setName(String n) {
name = n;
}
/**
* Return the copyright. May be null.
*/
public String getCopyright() {
return copyright;
}
/**
* Set the copyright
*/
public void setCopyright(String c) {
copyright = c;
}
/**
* Return endian status (big or little)
*/
public Endian getEndian() {
return endian;
}
/**
* Set endian status (big or little)
*/
public void setEndian(Endian e) {
endian = e;
}
/**
* Return list of all application identifiers
*/
public List<String> getApplicationIdentifiers() {
return applicationIdentifiers;
}
/**
* Add an application identifier. There can be any number of these.
*/
public void addApplicationIdentifier(String id) {
applicationIdentifiers.add(id);
}
/**
* Return list of all annotations
*/
public List<String> getAnnotations() {
return applicationIdentifiers;
}
/**
* Add an annotation. There can be any number of these.
*/
public void addAnnotation(String a) {
applicationIdentifiers.add(a);
}
/**
* Return list of all comments
*/
public List<String> getComments() {
return comments;
}
/**
* Add a comment. There can be any number of these.
*/
public void addComment(String c) {
comments.add(c);
}
/**
* Return the audio encoding as a descriptive string
*/
public String getAudioEncoding() {
return audioEncoding;
}
/**
* Set the audio encoding as a descriptive string
*/
public void setAudioEncoding(String s) {
audioEncoding = s;
}
public enum FileType {
AIFFTYPE,
AIFCTYPE
}
public enum Endian {
BIG_ENDIAN,
LITTLE_ENDIAN
}
}

View file

@ -1,83 +0,0 @@
package org.jaudiotagger.audio.aiff;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.tag.TagException;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
import java.util.logging.Level;
public class AiffFile extends AudioFile {
/**
* A static DateFormat object for generating ISO date strings
*/
public final static SimpleDateFormat ISO_DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
/**
* Creates a new empty AiffFile that is not associated with a
* specific file.
*/
public AiffFile() {
}
/**
* Creates a new MP3File datatype and parse the tag from the given filename.
*
* @param filename AIFF file
* @throws IOException on any I/O error
* @throws TagException on any exception generated by this library.
* @throws org.jaudiotagger.audio.exceptions.ReadOnlyFileException
* @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
*/
public AiffFile(String filename) throws
IOException, TagException, ReadOnlyFileException, InvalidAudioFrameException {
this(new File(filename));
}
/**
* Creates a new MP3File datatype and parse the tag from the given file
* Object.
*
* @param file MP3 file
* @throws IOException on any I/O error
* @throws TagException on any exception generated by this library.
* @throws org.jaudiotagger.audio.exceptions.ReadOnlyFileException
* @throws org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
*/
public AiffFile(File file)
throws IOException, TagException, ReadOnlyFileException, InvalidAudioFrameException {
this(file, true);
}
public AiffFile(File file, boolean readOnly)
throws IOException, TagException, ReadOnlyFileException, InvalidAudioFrameException {
RandomAccessFile newFile = null;
try {
logger.setLevel(Level.FINEST);
logger.fine("Called AiffFile constructor on " + file.getAbsolutePath());
this.file = file;
//Check File accessibility
newFile = checkFilePermissions(file, readOnly);
audioHeader = new AiffAudioHeader();
//readTag();
} finally {
if (newFile != null) {
newFile.close();
}
}
}
public AiffAudioHeader getAiffAudioHeader() {
return (AiffAudioHeader) audioHeader;
}
}

View file

@ -1,150 +0,0 @@
package org.jaudiotagger.audio.aiff;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.generic.AudioFileReader;
import org.jaudiotagger.audio.generic.GenericAudioHeader;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.aiff.AiffTag;
import java.io.IOException;
import java.io.RandomAccessFile;
public class AiffFileReader extends AudioFileReader {
/* Fixed value for first 4 bytes */
private static final int[] sigByte =
{0X46, 0X4F, 0X52, 0X4D};
/* AIFF-specific information which isn't "tag" information */
private AiffAudioHeader aiffHeader;
/* "Tag" information */
private AiffTag aiffTag;
/* InputStream that reads the file sequentially */
// private DataInputStream inStream;
public AiffFileReader() {
aiffHeader = new AiffAudioHeader();
aiffTag = new AiffTag();
}
public AiffFileReader(RandomAccessFile raf) {
aiffHeader = new AiffAudioHeader();
aiffTag = new AiffTag();
}
/**
* Reads the file and fills in the audio header and tag information.
* Holds the tag information for later and returns the audio header.
*/
@Override
protected GenericAudioHeader getEncodingInfo(RandomAccessFile raf)
throws CannotReadException, IOException {
logger.finest("Reading AIFF file ");
byte sigBuf[] = new byte[4];
raf.read(sigBuf);
for (int i = 0; i < 4; i++) {
if (sigBuf[i] != sigByte[i]) {
logger.finest("AIFF file has incorrect signature");
throw new CannotReadException("Not an AIFF file: incorrect signature");
}
}
long bytesRemaining = AiffUtil.readUINT32(raf);
// Read the file type.
if (!readFileType(raf)) {
throw new CannotReadException("Invalid AIFF file: Incorrect file type info");
}
bytesRemaining -= 4;
while (bytesRemaining > 0) {
if (!readChunk(raf, bytesRemaining)) {
break;
}
}
return aiffHeader;
}
@Override
protected Tag getTag(RandomAccessFile raf) throws CannotReadException,
IOException {
logger.info("getTag called");
// TODO fill out stub code
return aiffTag;
}
/* Reads the file type.
* Broken out from parse().
* If it is not a valid file type, returns false.
*/
private boolean readFileType(RandomAccessFile raf) throws IOException {
String typ = AiffUtil.read4Chars(raf);
if ("AIFF".equals(typ)) {
aiffHeader.setFileType(AiffAudioHeader.FileType.AIFFTYPE);
return true;
} else if ("AIFC".equals(typ)) {
aiffHeader.setFileType(AiffAudioHeader.FileType.AIFCTYPE);
return true;
} else {
return false;
}
}
/**
* Reads an AIFF Chunk.
*/
protected boolean readChunk
(RandomAccessFile raf, long bytesRemaining)
throws IOException {
Chunk chunk = null;
ChunkHeader chunkh = new ChunkHeader();
if (!chunkh.readHeader(raf)) {
return false;
}
int chunkSize = (int) chunkh.getSize();
bytesRemaining -= chunkSize + 8;
String id = chunkh.getID();
if ("FVER".equals(id)) {
chunk = new FormatVersionChunk(chunkh, raf, aiffHeader);
} else if ("APPL".equals(id)) {
chunk = new ApplicationChunk(chunkh, raf, aiffHeader);
// Any number of application chunks is ok
} else if ("COMM".equals(id)) {
// There should be no more than one of these
chunk = new CommonChunk(chunkh, raf, aiffHeader);
} else if ("COMT".equals(id)) {
chunk = new CommentsChunk(chunkh, raf, aiffHeader);
} else if ("NAME".equals(id)) {
chunk = new NameChunk(chunkh, raf, aiffHeader);
} else if ("AUTH".equals(id)) {
chunk = new AuthorChunk(chunkh, raf, aiffHeader);
} else if ("(c) ".equals(id)) {
chunk = new CopyrightChunk(chunkh, raf, aiffHeader);
} else if ("ANNO".equals(id)) {
chunk = new AnnotationChunk(chunkh, raf, aiffHeader);
} else if ("ID3 ".equals(id)) {
chunk = new ID3Chunk(chunkh, raf, aiffTag);
}
if (chunk != null) {
if (!chunk.readChunk()) {
return false;
}
} else {
// Other chunk types are legal, just skip over them
raf.skipBytes(chunkSize);
}
if ((chunkSize & 1) != 0) {
// Must come out to an even byte boundary
raf.skipBytes(1);
--bytesRemaining;
}
return true;
}
}

View file

@ -1,22 +0,0 @@
package org.jaudiotagger.audio.aiff;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* Functions for reading an AIFF file
*/
public class AiffInfoReader {
public AiffAudioHeader read(RandomAccessFile raf) throws CannotReadException, IOException {
if (raf.length() < 4) {
//Empty File
throw new CannotReadException("Not an AIFF file; too short");
}
return null; // TODO stub
}
}

View file

@ -1,75 +0,0 @@
package org.jaudiotagger.audio.aiff;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
/**
* This class implements an InputStream over a RandomAccessFile for the
* sake of efficiency. The AIFF reader uses a RandomAccessFile for
* consistency with the other modules, but really just reads the file
* sequentially. This class permits reasonable buffering.
*/
public class AiffInputStream extends InputStream {
private final static int BUFSIZE = 2048;
/**
* The underlying file
*/
private RandomAccessFile raf;
/**
* The input buffer
*/
private byte[] fileBuf;
/**
* Number of valid bytes in the input buffer
*/
private int fileBufSize;
/**
* Current position in the input buffer
*/
private int fileBufOffset;
/**
* End of file flag
*/
private boolean eof;
public AiffInputStream(RandomAccessFile raf) {
this.raf = raf;
eof = false;
fileBuf = new byte[BUFSIZE];
fileBufSize = 0;
fileBufOffset = 0;
}
@Override
public int read() throws IOException {
for (; ; ) {
if (eof) {
return -1;
}
if (fileBufOffset < fileBufSize) {
return ((int) fileBuf[fileBufOffset++]) & 0XFF;
} else {
fillBuf();
}
}
}
/* Refill the buffer */
private void fillBuf() throws IOException {
int bytesRead = raf.read(fileBuf, 0, BUFSIZE);
fileBufOffset = 0;
fileBufSize = bytesRead;
if (fileBufSize == 0) {
eof = true;
}
}
}

View file

@ -1,166 +0,0 @@
package org.jaudiotagger.audio.aiff;
import org.jaudiotagger.audio.generic.GenericTag;
import org.jaudiotagger.audio.generic.Utils;
import org.jaudiotagger.tag.FieldDataInvalidException;
import org.jaudiotagger.tag.KeyNotFoundException;
import org.jaudiotagger.tag.TagField;
import org.jaudiotagger.tag.TagTextField;
public class AiffTag extends GenericTag {
public boolean hasField(AiffTagFieldKey fieldKey) {
return hasField(fieldKey.name());
}
/**
* Create new AIFF-specific field and set it in the tag
*
* @param genericKey
* @param value
* @throws KeyNotFoundException
* @throws FieldDataInvalidException
*/
public void setField(AiffTagFieldKey genericKey, String value) throws KeyNotFoundException, FieldDataInvalidException {
TagField tagfield = createField(genericKey, value);
setField(tagfield);
}
public TagField createField(AiffTagFieldKey genericKey, String value) throws KeyNotFoundException, FieldDataInvalidException {
return new AiffTagTextField(genericKey.name(), value);
}
private class AiffTagTextField implements TagTextField {
/**
* Stores the identifier.
*/
private final String id;
/**
* Stores the string.
*/
private String content;
/**
* Creates an instance.
*
* @param fieldId The identifier.
* @param initialContent The string.
*/
public AiffTagTextField(String fieldId, String initialContent) {
this.id = fieldId;
this.content = initialContent;
}
/**
* (overridden)
*
* @see org.jaudiotagger.tag.TagField#copyContent(org.jaudiotagger.tag.TagField)
*/
public void copyContent(TagField field) {
if (field instanceof TagTextField) {
this.content = ((TagTextField) field).getContent();
}
}
/**
* (overridden)
*
* @see org.jaudiotagger.tag.TagTextField#getContent()
*/
public String getContent() {
return this.content;
}
/**
* (overridden)
*
* @see org.jaudiotagger.tag.TagTextField#setContent(java.lang.String)
*/
public void setContent(String s) {
this.content = s;
}
/**
* (overridden)
*
* @see org.jaudiotagger.tag.TagTextField#getEncoding()
*/
public String getEncoding() {
return "ISO-8859-1";
}
/**
* (overridden)
*
* @see org.jaudiotagger.tag.TagTextField#setEncoding(java.lang.String)
*/
public void setEncoding(String s) {
/* Not allowed */
}
/**
* (overridden)
*
* @see org.jaudiotagger.tag.TagField#getId()
*/
public String getId() {
return id;
}
/**
* (overridden)
*
* @see org.jaudiotagger.tag.TagField#getRawContent()
*/
public byte[] getRawContent() {
return this.content == null ? new byte[]{} : Utils.getDefaultBytes(this.content, getEncoding());
}
/**
* (overridden)
*
* @see org.jaudiotagger.tag.TagField#isBinary()
*/
public boolean isBinary() {
return false;
}
/**
* (overridden)
*
* @see org.jaudiotagger.tag.TagField#isBinary(boolean)
*/
public void isBinary(boolean b) {
/* not supported */
}
/**
* (overridden)
*
* @see org.jaudiotagger.tag.TagField#isCommon()
*/
public boolean isCommon() {
return true;
}
/**
* (overridden)
*
* @see org.jaudiotagger.tag.TagField#isEmpty()
*/
public boolean isEmpty() {
return this.content.equals("");
}
/**
* (overridden)
*
* @see java.lang.Object#toString()
*/
public String toString() {
return getContent();
}
} // End of AiffTagTextField
}

View file

@ -1,18 +0,0 @@
package org.jaudiotagger.audio.aiff;
/**
* Enum for AIFF fields that don't have obvious matches in FieldKey
*/
public enum AiffTagFieldKey {
TIMESTAMP("TIMESTAMP");
private String fieldName;
AiffTagFieldKey(String fieldName) {
this.fieldName = fieldName;
}
public String getFieldName() {
return fieldName;
}
}

View file

@ -1,111 +0,0 @@
package org.jaudiotagger.audio.aiff;
//import java.io.EOFException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
//import java.io.InputStream;
public class AiffUtil {
private final static SimpleDateFormat dateFmt =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
private final static Charset LATIN1 = Charset.availableCharsets().get("ISO-8859-1");
/**
* Reads 4 bytes from file and interprets them as UINT32.<br>
*
* @param raf file to read from.
* @return UINT32 value
* @throws IOException on I/O Errors.
*/
public static long readUINT32(RandomAccessFile raf) throws IOException {
long result = 0;
for (int i = 0; i < 4; i++) {
// Warning, always cast to long here. Otherwise it will be
// shifted as int, which may produce a negative value, which will
// then be extended to long and assign the long variable a negative
// value.
result <<= 8;
result |= (long) raf.read();
}
return result;
}
/**
* Reads 4 bytes and concatenates them into a String.
* This pattern is used for ID's of various kinds.
*/
public static String read4Chars(RandomAccessFile raf) throws IOException {
StringBuffer sbuf = new StringBuffer(4);
for (int i = 0; i < 4; i++) {
char ch = (char) raf.read();
sbuf.append(ch);
}
return sbuf.toString();
}
public static double read80BitDouble(RandomAccessFile raf)
throws IOException {
byte[] buf = new byte[10];
raf.readFully(buf);
ExtDouble xd = new ExtDouble(buf);
return xd.toDouble();
}
/**
* Converts a Macintosh-style timestamp (seconds since
* January 1, 1904) into a Java date. The timestamp is
* treated as a time in the default localization.
* Depending on that localization,
* there may be some variation in the exact hour of the date
* returned, e.g., due to daylight savings time.
*/
public static Date timestampToDate(long timestamp) {
Calendar cal = Calendar.getInstance();
cal.set(1904, 0, 1, 0, 0, 0);
// If we add the seconds directly, we'll truncate the long
// value when converting to int. So convert to hours plus
// residual seconds.
int hours = (int) (timestamp / 3600);
int seconds = (int) (timestamp - (long) hours * 3600L);
cal.add(Calendar.HOUR_OF_DAY, hours);
cal.add(Calendar.SECOND, seconds);
Date dat = cal.getTime();
return dat;
}
/**
* Format a date as text
*/
public static String formatDate(Date dat) {
return dateFmt.format(dat);
}
/**
* Convert a byte array to a Pascal string. The first byte is the byte count,
* followed by that many active characters.
*/
public static String bytesToPascalString(byte[] data) {
int len = (int) data[0];
return new String(data, 0, len, LATIN1);
}
/**
* Read a Pascal string from the file.
*/
public static String readPascalString(RandomAccessFile raf) throws IOException {
int len = raf.read();
byte[] buf = new byte[len + 1];
raf.read(buf, 1, len);
buf[0] = (byte) len;
return bytesToPascalString(buf);
}
}

View file

@ -1,34 +0,0 @@
package org.jaudiotagger.audio.aiff;
import java.io.IOException;
import java.io.RandomAccessFile;
public class AnnotationChunk extends TextChunk {
private AiffAudioHeader aiffHeader;
/**
* Constructor.
*
* @param hdr The header for this chunk
* @param raf The file from which the AIFF data are being read
* @param aHdr The AiffAudioHeader into which information is stored
*/
public AnnotationChunk(
ChunkHeader hdr,
RandomAccessFile raf,
AiffAudioHeader aHdr) {
super(hdr, raf);
aiffHeader = aHdr;
}
@Override
public boolean readChunk() throws IOException {
if (!super.readChunk()) {
return false;
}
aiffHeader.addAnnotation(chunkText);
return true;
}
}

View file

@ -1,54 +0,0 @@
package org.jaudiotagger.audio.aiff;
import org.jaudiotagger.audio.generic.Utils;
import java.io.IOException;
import java.io.RandomAccessFile;
public class ApplicationChunk extends Chunk {
// private AiffTag aiffTag;
private AiffAudioHeader aiffHeader;
/**
* Constructor.
*
* @param hdr The header for this chunk
* @param raf The file from which the AIFF data are being read
* @param tag The AiffTag into which information is stored
*/
public ApplicationChunk(
ChunkHeader hdr,
RandomAccessFile raf,
AiffAudioHeader aHdr) {
super(raf, hdr);
aiffHeader = aHdr;
}
/**
* Reads a chunk and puts an Application property into
* the RepInfo object.
*
* @return <code>false</code> if the chunk is structurally
* invalid, otherwise <code>true</code>
*/
public boolean readChunk() throws IOException {
String applicationSignature = Utils.readString(raf, 4);
String applicationName = null;
byte[] data = new byte[(int) (bytesLeft - 4)];
raf.readFully(data);
// If the application signature is 'pdos' or 'stoc',
// then the beginning of the data area is a Pascal
// string naming the application. Otherwise, we
// ignore the data. ('pdos' is for Apple II
// applications, 'stoc' for the entire non-Apple world.)
if ("stoc".equals(applicationSignature) ||
"pdos".equals(applicationSignature)) {
applicationName = AiffUtil.bytesToPascalString(data);
}
aiffHeader.addApplicationIdentifier
(applicationSignature + ": " + applicationName);
return true;
}
}

View file

@ -1,33 +0,0 @@
package org.jaudiotagger.audio.aiff;
import java.io.IOException;
import java.io.RandomAccessFile;
public class AuthorChunk extends TextChunk {
private AiffAudioHeader aiffHeader;
/**
* Constructor.
*
* @param hdr The header for this chunk
* @param raf The file from which the AIFF data are being read
* @param aHdr The AiffAudioHeader into which information is stored
*/
public AuthorChunk(
ChunkHeader hdr,
RandomAccessFile raf,
AiffAudioHeader aHdr) {
super(hdr, raf);
aiffHeader = aHdr;
}
@Override
public boolean readChunk() throws IOException {
if (!super.readChunk()) {
return false;
}
aiffHeader.setAuthor(chunkText);
return true;
}
}

View file

@ -1,61 +0,0 @@
package org.jaudiotagger.audio.aiff;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* Abstract superclass for IFF/AIFF chunks.
*
* @author Gary McGath
*/
public abstract class Chunk {
protected long bytesLeft;
protected RandomAccessFile raf;
/**
* Constructor.
*
* @param module The Module under which this was called
* @param hdr The header for this chunk
* @param dstrm The stream from which the data are being read
*/
public Chunk(RandomAccessFile raf, ChunkHeader hdr) {
this.raf = raf;
bytesLeft = hdr.getSize();
}
/**
* Reads a chunk and puts appropriate information into
* the RepInfo object.
*
* @param info RepInfo object to receive information
* @return <code>false</code> if the chunk is structurally
* invalid, otherwise <code>true</code>
*/
public abstract boolean readChunk() throws IOException;
/**
* Convert a byte buffer cleanly to an ASCII string.
* This is used for fixed-allocation strings in Broadcast
* WAVE chunks, and might have uses elsewhere.
* If a string is shorter than its fixed allocation, we're
* guaranteed only that there is a null terminating the string,
* and noise could follow it. So we can't use the byte buffer
* constructor for a string.
*/
protected String byteBufString(byte[] b) {
StringBuffer sb = new StringBuffer(b.length);
for (int i = 0; i < b.length; i++) {
byte c = b[i];
if (c == 0) {
// Terminate when we see a null
break;
}
sb.append((char) c);
}
return sb.toString();
}
}

View file

@ -1,63 +0,0 @@
package org.jaudiotagger.audio.aiff;
import java.io.IOException;
import java.io.RandomAccessFile;
public class ChunkHeader {
private long _size; // This does not include the 8 bytes of header
private String _chunkID; // 4-character ID of the chunk
/**
* Constructor.
*
* @param module The module under which the chunk is being read
* @param info The RepInfo object being used by the module
*/
public ChunkHeader() {
}
/**
* Reads the header of a chunk. If _chunkID is non-null,
* it's assumed to have already been read.
*/
public boolean readHeader(RandomAccessFile raf) throws IOException {
StringBuffer id = new StringBuffer(4);
for (int i = 0; i < 4; i++) {
int ch = raf.read();
if (ch < 32) {
String hx = Integer.toHexString(ch);
if (hx.length() < 2) {
hx = "0" + hx;
}
return false;
}
id.append((char) ch);
}
_chunkID = id.toString();
_size = AiffUtil.readUINT32(raf);
return true;
}
/**
* Returns the chunk type, which is a 4-character code
*/
public String getID() {
return _chunkID;
}
/**
* Sets the chunk type, which is a 4-character code, directly.
*/
public void setID(String id) {
_chunkID = id;
}
/**
* Returns the chunk size (excluding the first 8 bytes)
*/
public long getSize() {
return _size;
}
}

View file

@ -1,55 +0,0 @@
package org.jaudiotagger.audio.aiff;
import org.jaudiotagger.audio.generic.Utils;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Date;
public class CommentsChunk extends Chunk {
private AiffAudioHeader aiffHeader;
/**
* Constructor.
*
* @param hdr The header for this chunk
* @param raf The file from which the AIFF data are being read
* @param tag The AiffTag into which information is stored
*/
public CommentsChunk(
ChunkHeader hdr,
RandomAccessFile raf,
AiffAudioHeader aHdr) {
super(raf, hdr);
aiffHeader = aHdr;
}
/**
* Reads a chunk and extracts information.
*
* @return <code>false</code> if the chunk is structurally
* invalid, otherwise <code>true</code>
*/
public boolean readChunk() throws IOException {
int numComments = Utils.readUint16(raf);
// Create a List of comments
for (int i = 0; i < numComments; i++) {
long timestamp = Utils.readUint32(raf);
Date jTimestamp = AiffUtil.timestampToDate(timestamp);
int marker = Utils.readInt16(raf);
int count = Utils.readUint16(raf);
bytesLeft -= 8;
byte[] buf = new byte[count];
raf.read(buf);
bytesLeft -= count;
String cmt = new String(buf);
// Append a timestamp to the comment
cmt += " " + AiffUtil.formatDate(jTimestamp);
aiffHeader.addComment(cmt);
}
return true;
}
}

View file

@ -1,101 +0,0 @@
package org.jaudiotagger.audio.aiff;
import org.jaudiotagger.audio.generic.Utils;
import java.io.IOException;
import java.io.RandomAccessFile;
public class CommonChunk extends Chunk {
private AiffAudioHeader aiffHeader;
/**
* Constructor.
*
* @param hdr The header for this chunk
* @param raf The file from which the AIFF data are being read
* @param tag The AiffTag into which information is stored
*/
public CommonChunk(
ChunkHeader hdr,
RandomAccessFile raf,
AiffAudioHeader aHdr) {
super(raf, hdr);
aiffHeader = aHdr;
}
@Override
public boolean readChunk() throws IOException {
int numChannels = Utils.readUint16(raf);
long numSampleFrames = Utils.readUint32(raf);
int sampleSize = Utils.readUint16(raf);
bytesLeft -= 8;
String compressionType = null;
String compressionName = null;
double sampleRate = AiffUtil.read80BitDouble(raf);
bytesLeft -= 10;
if (aiffHeader.getFileType() == AiffAudioHeader.FileType.AIFCTYPE) {
if (bytesLeft == 0) {
// This is a rather special case, but testing did turn up
// a file that misbehaved in this way.
return false;
}
compressionType = AiffUtil.read4Chars(raf);
// According to David Ackerman, the compression type can
// change the endianness of the document.
if (compressionType.equals("sowt")) {
aiffHeader.setEndian(AiffAudioHeader.Endian.LITTLE_ENDIAN);
}
bytesLeft -= 4;
compressionName = AiffUtil.readPascalString(raf);
bytesLeft -= compressionName.length() + 1;
}
aiffHeader.setBitsPerSample(sampleSize);
aiffHeader.setSamplingRate((int) sampleRate);
aiffHeader.setChannelNumber(numChannels);
aiffHeader.setLength((int) (numSampleFrames / sampleRate));
aiffHeader.setPreciseLength((float) (numSampleFrames / sampleRate));
aiffHeader.setLossless(true); // for all known compression types
// Proper handling of compression type should depend
// on whether raw output is set
if (compressionType != null) {
if (compressionType.equals("NONE")) {
} else if (compressionType.equals("raw ")) {
compressionName = "PCM 8-bit offset-binary";
} else if (compressionType.equals("twos")) {
compressionName = "PCM 16-bit twos-complement big-endian";
} else if (compressionType.equals("sowt")) {
compressionName = "PCM 16-bit twos-complement little-endian";
} else if (compressionType.equals("fl32")) {
compressionName = "PCM 32-bit integer";
} else if (compressionType.equals("fl64")) {
compressionName = "PCM 64-bit floating point";
} else if (compressionType.equals("in24")) {
compressionName = "PCM 24-bit integer";
} else if (compressionType.equals("in32")) {
compressionName = "PCM 32-bit integer";
} else {
aiffHeader.setLossless(false); // We don't know, so we have to assume lossy
}
aiffHeader.setAudioEncoding(compressionName);
// The size of the data after compression isn't available
// from the Common chunk, so we mark it as "unknown."
// With a bit more sophistication, we could combine the
// information from here and the Sound Data chunk to get
// the effective byte rate, but we're about to release.
String name = compressionName;
if (name == null || name.length() == 0) {
name = compressionType;
}
}
return true;
}
}

View file

@ -1,34 +0,0 @@
package org.jaudiotagger.audio.aiff;
import java.io.IOException;
import java.io.RandomAccessFile;
public class CopyrightChunk extends TextChunk {
private AiffAudioHeader aiffHeader;
/**
* Constructor.
*
* @param hdr The header for this chunk
* @param raf The file from which the AIFF data are being read
* @param aHdr The AiffAudioHeader into which information is stored
*/
public CopyrightChunk(
ChunkHeader hdr,
RandomAccessFile raf,
AiffAudioHeader aHdr) {
super(hdr, raf);
aiffHeader = aHdr;
}
@Override
public boolean readChunk() throws IOException {
if (!super.readChunk()) {
return false;
}
aiffHeader.setCopyright(chunkText);
return true;
}
}

View file

@ -1,78 +0,0 @@
package org.jaudiotagger.audio.aiff;
/**
* Code to deal with the 80-bit floating point (extended double)
* numbers which occur in AIFF files. Should also be applicable
* in general.
* <p>
* Java has no built-in support for IEEE 754 extended double numbers.
* Thus, we have to unpack the number and convert it to a double by
* hand. There is, of course, loss of precision.
* <p>
* This isn't designed for high-precision work; as the standard
* disclaimer says, don't use it for life support systems or nuclear
* power plants.
* <p>
* Lifted bodily from JHOVE.
*
* @author Gary McGath
*/
public class ExtDouble {
byte[] _rawData;
/**
* Constructor.
*
* @param rawData A 10-byte array representing the number
* in the sequence in which it was stored.
*/
public ExtDouble(byte[] rawData) {
_rawData = rawData;
}
/**
* Convert the value to a Java double. This results in
* loss of precision. If the number is out of range,
* results aren't guaranteed.
*/
public double toDouble() {
int sign;
int exponent;
long mantissa = 0;
// Extract the sign bit.
sign = _rawData[0] >> 7;
// Extract the exponent. It's stored with a
// bias of 16383, so subtract that off.
// Also, the mantissa is between 1 and 2 (i.e.,
// all but 1 digits are to the right of the binary point, so
// we take 62 (not 63: see below) off the exponent for that.
exponent = (_rawData[0] << 8) | _rawData[1];
exponent &= 0X7FFF; // strip off sign bit
exponent -= (16383 + 62); // 1 is added to the "real" exponent
// Extract the mantissa. It's 64 bits of unsigned
// data, but a long is a signed number, so we have to
// discard the LSB. We'll lose more than that converting
// to double anyway. This division by 2 is the reason for
// adding an extra 1 to the exponent above.
int shifter = 55;
for (int i = 2; i < 9; i++) {
mantissa |= ((long) _rawData[i] & 0XFFL) << shifter;
shifter -= 8;
}
mantissa |= _rawData[9] >>> 1;
// Now put it together in a floating point number.
double val = Math.pow(2, exponent);
val *= mantissa;
if (sign != 0) {
val = -val;
}
return val;
}
}

View file

@ -1,43 +0,0 @@
package org.jaudiotagger.audio.aiff;
import org.jaudiotagger.audio.generic.Utils;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Date;
public class FormatVersionChunk extends Chunk {
private AiffAudioHeader aiffHeader;
/**
* Constructor.
*
* @param hdr The header for this chunk
* @param raf The file from which the AIFF data are being read
* @param tag The AiffTag into which information is stored
*/
public FormatVersionChunk(
ChunkHeader hdr,
RandomAccessFile raf,
AiffAudioHeader aHdr) {
super(raf, hdr);
aiffHeader = aHdr;
}
/**
* Reads a chunk and extracts information.
*
* @return <code>false</code> if the chunk is structurally
* invalid, otherwise <code>true</code>
*/
public boolean readChunk() throws IOException {
long rawTimestamp = Utils.readUint32(raf);
// The timestamp is in seconds since January 1, 1904.
// We must convert to Java time.
Date timestamp = AiffUtil.timestampToDate(rawTimestamp);
aiffHeader.setTimestamp(timestamp);
return true;
}
}

View file

@ -1,98 +0,0 @@
package org.jaudiotagger.audio.aiff;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.tag.TagException;
import org.jaudiotagger.tag.aiff.AiffTag;
import org.jaudiotagger.tag.id3.AbstractID3v2Tag;
import org.jaudiotagger.tag.id3.ID3v22Tag;
import org.jaudiotagger.tag.id3.ID3v23Tag;
import org.jaudiotagger.tag.id3.ID3v24Tag;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
public class ID3Chunk extends Chunk {
private AiffTag aiffTag;
/**
* Constructor.
*
* @param hdr The header for this chunk
* @param raf The file from which the AIFF data are being read
* @param tag The AiffTag into which information is stored
*/
public ID3Chunk(
ChunkHeader hdr,
RandomAccessFile raf,
AiffTag tag) {
super(raf, hdr);
aiffTag = tag;
}
@Override
public boolean readChunk() throws IOException {
// TODO Auto-generated method stub
if (!isId3v2Tag()) {
return false; // Bad ID3V2 tag
}
int version = raf.read();
AbstractID3v2Tag id3Tag;
switch (version) {
case 2:
id3Tag = new ID3v22Tag();
AudioFile.logger.finest("Reading ID3V2.2 tag");
break;
case 3:
id3Tag = new ID3v23Tag();
AudioFile.logger.finest("Reading ID3V2.3 tag");
break;
case 4:
id3Tag = new ID3v24Tag();
AudioFile.logger.finest("Reading ID3V2.4 tag");
break;
default:
return false; // bad or unknown version
}
aiffTag.setID3Tag(id3Tag);
raf.seek(raf.getFilePointer() - 4); // back up to start of tag
byte[] buf = new byte[(int) bytesLeft];
raf.read(buf);
ByteBuffer bb = ByteBuffer.allocate((int) bytesLeft);
bb.put(buf);
try {
id3Tag.read(bb);
} catch (TagException e) {
AudioFile.logger.info("Exception reading ID3 tag: " + e.getClass().getName()
+ ": " + e.getMessage());
return false;
}
return true;
}
/**
* @param rawdata
* @param isFramingBit
* @return logical representation of VorbisCommentTag
* @throws IOException
* @throws CannotReadException
*/
public void parse(byte[] rawdata, AiffTag aiffTag) throws IOException, CannotReadException {
}
/**
* Reads 3 bytes to determine if the tag really looks like ID3 data.
*/
private boolean isId3v2Tag() throws IOException {
byte buf[] = new byte[3];
raf.read(buf);
String id = new String(buf, "ASCII");
return "ID3".equals(id);
}
}

View file

@ -1,33 +0,0 @@
package org.jaudiotagger.audio.aiff;
import java.io.IOException;
import java.io.RandomAccessFile;
public class NameChunk extends TextChunk {
private AiffAudioHeader aiffHeader;
/**
* Constructor.
*
* @param hdr The header for this chunk
* @param raf The file from which the AIFF data are being read
* @param aHdr The AiffAudioHeader into which information is stored
*/
public NameChunk(
ChunkHeader hdr,
RandomAccessFile raf,
AiffAudioHeader aHdr) {
super(hdr, raf);
aiffHeader = aHdr;
}
@Override
public boolean readChunk() throws IOException {
if (!super.readChunk()) {
return false;
}
aiffHeader.setName(chunkText);
return true;
}
}

View file

@ -1,40 +0,0 @@
package org.jaudiotagger.audio.aiff;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* This class provides common functionality for NameChunk, AuthorChunk,
* and CopyrightChunk
*/
public abstract class TextChunk extends Chunk {
protected String chunkText;
private AiffAudioHeader aiffHeader;
/**
* Constructor.
*
* @param hdr The header for this chunk
* @param raf The file from which the AIFF data are being read
*/
public TextChunk(
ChunkHeader hdr,
RandomAccessFile raf) {
super(raf, hdr);
}
/**
* Read the chunk. The subclasses need to take the value of
* chunkText and use it appropriately.
*/
@Override
public boolean readChunk() throws IOException {
byte[] buf = new byte[(int) bytesLeft];
raf.read(buf);
chunkText = new String(buf, "ISO-8859-1");
return true;
}
}

View file

@ -1,20 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html long="en">
<head>
</head>
<body bgcolor="white">
<b>Very preliminary</b> code for AIFF files.
<b>Not even remotely working yet.</b> Uploaded for review.
<br>
<!-- package.html by Gary McGath -->
<!-- Put @see and @since tags down here. -->
</body>
</html>

View file

@ -1,267 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.asf.data.AsfHeader;
import org.jaudiotagger.audio.asf.data.AudioStreamChunk;
import org.jaudiotagger.audio.asf.data.MetadataContainer;
import org.jaudiotagger.audio.asf.data.MetadataDescriptor;
import org.jaudiotagger.audio.asf.io.*;
import org.jaudiotagger.audio.asf.util.TagConverter;
import org.jaudiotagger.audio.asf.util.Utils;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.audio.generic.AudioFileReader;
import org.jaudiotagger.audio.generic.GenericAudioHeader;
import org.jaudiotagger.logging.ErrorMessage;
import org.jaudiotagger.tag.TagException;
import org.jaudiotagger.tag.asf.AsfTag;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* This reader can read ASF files containing any content (stream type). <br>
*
* @author Christian Laireiter
*/
public class AsfFileReader extends AudioFileReader {
/**
* Logger instance
*/
private final static Logger LOGGER = Logger
.getLogger("org.jaudiotagger.audio.asf");
/**
* This reader will be configured to read tag and audio header information.<br>
*/
private final static AsfHeaderReader HEADER_READER;
static {
final List<Class<? extends ChunkReader>> readers = new ArrayList<Class<? extends ChunkReader>>();
readers.add(ContentDescriptionReader.class);
readers.add(ContentBrandingReader.class);
readers.add(MetadataReader.class);
readers.add(LanguageListReader.class);
// Create the header extension object reader with just content
// description reader as well
// as extended content description reader.
final AsfExtHeaderReader extReader = new AsfExtHeaderReader(readers,
true);
readers.add(FileHeaderReader.class);
readers.add(StreamChunkReader.class);
HEADER_READER = new AsfHeaderReader(readers, true);
HEADER_READER.setExtendedHeaderReader(extReader);
}
/**
* Determines if the &quot;isVbr&quot; field is set in the extended content
* description.<br>
*
* @param header the header to look up.
* @return <code>true</code> if &quot;isVbr&quot; is present with a
* <code>true</code> value.
*/
private boolean determineVariableBitrate(final AsfHeader header) {
assert header != null;
boolean result = false;
final MetadataContainer extDesc = header
.findExtendedContentDescription();
if (extDesc != null) {
final List<MetadataDescriptor> descriptors = extDesc
.getDescriptorsByName("IsVBR");
if (descriptors != null && !descriptors.isEmpty()) {
result = Boolean.TRUE.toString().equals(
descriptors.get(0).getString());
}
}
return result;
}
/**
* Creates a generic audio header instance with provided data from header.
*
* @param header ASF header which contains the information.
* @return generic audio header representation.
* @throws CannotReadException If header does not contain mandatory information. (Audio
* stream chunk and file header chunk)
*/
private GenericAudioHeader getAudioHeader(final AsfHeader header)
throws CannotReadException {
final GenericAudioHeader info = new GenericAudioHeader();
if (header.getFileHeader() == null) {
throw new CannotReadException(
"Invalid ASF/WMA file. File header object not available.");
}
if (header.getAudioStreamChunk() == null) {
throw new CannotReadException(
"Invalid ASF/WMA file. No audio stream contained.");
}
info.setBitrate(header.getAudioStreamChunk().getKbps());
info.setChannelNumber((int) header.getAudioStreamChunk()
.getChannelCount());
info.setEncodingType("ASF (audio): "
+ header.getAudioStreamChunk().getCodecDescription());
info
.setLossless(header.getAudioStreamChunk()
.getCompressionFormat() == AudioStreamChunk.WMA_LOSSLESS);
info.setPreciseLength(header.getFileHeader().getPreciseDuration());
info.setSamplingRate((int) header.getAudioStreamChunk()
.getSamplingRate());
info.setVariableBitRate(determineVariableBitrate(header));
info.setBitsPerSample(header.getAudioStreamChunk().getBitsPerSample());
return info;
}
/**
* (overridden)
*
* @see org.jaudiotagger.audio.generic.AudioFileReader#getEncodingInfo(java.io.RandomAccessFile)
*/
@Override
protected GenericAudioHeader getEncodingInfo(final RandomAccessFile raf)
throws CannotReadException, IOException {
raf.seek(0);
GenericAudioHeader info;
try {
final AsfHeader header = AsfHeaderReader.readInfoHeader(raf);
if (header == null) {
throw new CannotReadException(
"Some values must have been "
+ "incorrect for interpretation as asf with wma content.");
}
info = getAudioHeader(header);
} catch (final Exception e) {
if (e instanceof IOException) {
throw (IOException) e;
} else if (e instanceof CannotReadException) {
throw (CannotReadException) e;
} else {
throw new CannotReadException("Failed to read. Cause: "
+ e.getMessage(), e);
}
}
return info;
}
/**
* Creates a tag instance with provided data from header.
*
* @param header ASF header which contains the information.
* @return generic audio header representation.
*/
private AsfTag getTag(final AsfHeader header) {
return TagConverter.createTagOf(header);
}
/**
* (overridden)
*
* @see org.jaudiotagger.audio.generic.AudioFileReader#getTag(java.io.RandomAccessFile)
*/
@Override
protected AsfTag getTag(final RandomAccessFile raf)
throws CannotReadException, IOException {
raf.seek(0);
AsfTag tag;
try {
final AsfHeader header = AsfHeaderReader.readTagHeader(raf);
if (header == null) {
throw new CannotReadException(
"Some values must have been "
+ "incorrect for interpretation as asf with wma content.");
}
tag = TagConverter.createTagOf(header);
} catch (final Exception e) {
logger.severe(e.getMessage());
if (e instanceof IOException) {
throw (IOException) e;
} else if (e instanceof CannotReadException) {
throw (CannotReadException) e;
} else {
throw new CannotReadException("Failed to read. Cause: "
+ e.getMessage());
}
}
return tag;
}
/**
* {@inheritDoc}
*/
@Override
public AudioFile read(final File f) throws CannotReadException,
IOException, TagException, ReadOnlyFileException,
InvalidAudioFrameException {
if (!f.canRead()) {
throw new CannotReadException(
ErrorMessage.GENERAL_READ_FAILED_DO_NOT_HAVE_PERMISSION_TO_READ_FILE
.getMsg(f.getAbsolutePath()));
}
InputStream stream = null;
try {
stream = new FullRequestInputStream(new BufferedInputStream(
new FileInputStream(f)));
final AsfHeader header = HEADER_READER.read(Utils.readGUID(stream),
stream, 0);
if (header == null) {
throw new CannotReadException(ErrorMessage.ASF_HEADER_MISSING
.getMsg(f.getAbsolutePath()));
}
if (header.getFileHeader() == null) {
throw new CannotReadException(
ErrorMessage.ASF_FILE_HEADER_MISSING.getMsg(f
.getAbsolutePath()));
}
// Just log a warning because file seems to play okay
if (header.getFileHeader().getFileSize().longValue() != f.length()) {
logger
.warning(ErrorMessage.ASF_FILE_HEADER_SIZE_DOES_NOT_MATCH_FILE_SIZE
.getMsg(f.getAbsolutePath(), header
.getFileHeader().getFileSize()
.longValue(), f.length()));
}
return new AudioFile(f, getAudioHeader(header), getTag(header));
} catch (final CannotReadException e) {
throw e;
} catch (final Exception e) {
throw new CannotReadException("\"" + f + "\" :" + e, e);
} finally {
try {
if (stream != null) {
stream.close();
}
} catch (final Exception ex) {
LOGGER.severe("\"" + f + "\" :" + ex);
}
}
}
}

View file

@ -1,141 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf;
import org.jaudiotagger.audio.asf.data.AsfHeader;
import org.jaudiotagger.audio.asf.data.ChunkContainer;
import org.jaudiotagger.audio.asf.data.MetadataContainer;
import org.jaudiotagger.audio.asf.io.*;
import org.jaudiotagger.audio.asf.util.TagConverter;
import org.jaudiotagger.audio.exceptions.CannotWriteException;
import org.jaudiotagger.audio.generic.AudioFileWriter;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.asf.AsfTag;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
/**
* This class writes given tags to ASF files containing WMA content. <br>
* <br>
*
* @author Christian Laireiter
*/
public class AsfFileWriter extends AudioFileWriter {
/**
* {@inheritDoc}
*/
@Override
protected void deleteTag(final RandomAccessFile raf,
final RandomAccessFile tempRaf) throws CannotWriteException,
IOException {
writeTag(new AsfTag(true), raf, tempRaf);
}
private boolean[] searchExistence(final ChunkContainer container,
final MetadataContainer[] metaContainers) {
assert container != null;
assert metaContainers != null;
final boolean[] result = new boolean[metaContainers.length];
for (int i = 0; i < result.length; i++) {
result[i] = container.hasChunkByGUID(metaContainers[i]
.getContainerType().getContainerGUID());
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
protected void writeTag(final Tag tag, final RandomAccessFile raf,
final RandomAccessFile rafTemp) throws CannotWriteException,
IOException {
/*
* Since this implementation should not change the structure of the ASF
* file (locations of content description chunks), we need to read the
* content description chunk and the extended content description chunk
* from the source file. In the second step we need to determine which
* modifier (asf header or asf extended header) gets the appropriate
* modifiers. The following policies are applied: if the source does not
* contain any descriptor, the necessary descriptors are appended to the
* header object.
*
* if the source contains only one descriptor in the header extension
* object, and the other type is needed as well, the other one will be
* put into the header extension object.
*
* for each descriptor type, if an object is found, an updater will be
* configured.
*/
final AsfHeader sourceHeader = AsfHeaderReader.readTagHeader(raf);
raf.seek(0); // Reset for the streamer
/*
* Now createField modifiers for metadata descriptor and extended content
* descriptor as implied by the given Tag.
*/
// TODO not convinced that we need to copy fields here
final AsfTag copy = new AsfTag(tag, true);
final MetadataContainer[] distribution = TagConverter
.distributeMetadata(copy);
final boolean[] existHeader = searchExistence(sourceHeader,
distribution);
final boolean[] existExtHeader = searchExistence(sourceHeader
.getExtendedHeader(), distribution);
// Modifiers for the asf header object
final List<ChunkModifier> headerModifier = new ArrayList<ChunkModifier>();
// Modifiers for the asf header extension object
final List<ChunkModifier> extHeaderModifier = new ArrayList<ChunkModifier>();
for (int i = 0; i < distribution.length; i++) {
final WriteableChunkModifer modifier = new WriteableChunkModifer(
distribution[i]);
if (existHeader[i]) {
// Will remove or modify chunks in ASF header
headerModifier.add(modifier);
} else if (existExtHeader[i]) {
// Will remove or modify chunks in extended header
extHeaderModifier.add(modifier);
} else {
// Objects (chunks) will be added here.
if (i == 0 || i == 2 || i == 1) {
// Add content description and extended content description
// at header for maximum compatibility
headerModifier.add(modifier);
} else {
// For now, the rest should be created at extended header
// since other positions aren't known.
extHeaderModifier.add(modifier);
}
}
}
// only addField an AsfExtHeaderModifier, if there is actually something to
// change (performance)
if (!extHeaderModifier.isEmpty()) {
headerModifier.add(new AsfExtHeaderModifier(extHeaderModifier));
}
new AsfStreamer()
.createModifiedCopy(new RandomAccessFileInputstream(raf),
new RandomAccessFileOutputStream(rafTemp),
headerModifier);
}
}

View file

@ -1,69 +0,0 @@
package org.jaudiotagger.audio.asf.data;
import java.math.BigInteger;
/**
* This class represents the ASF extended header object (chunk).<br>
* Like {@link AsfHeader} it contains multiple other ASF objects (chunks).<br>
*
* @author Christian Laireiter
*/
public final class AsfExtendedHeader extends ChunkContainer {
/**
* Creates an instance.<br>
*
* @param pos Position within the stream.<br>
* @param length the length of the extended header object.
*/
public AsfExtendedHeader(final long pos, final BigInteger length) {
super(GUID.GUID_HEADER_EXTENSION, pos, length);
}
/**
* @return Returns the contentDescription.
*/
public ContentDescription getContentDescription() {
return (ContentDescription) getFirst(GUID.GUID_CONTENTDESCRIPTION,
ContentDescription.class);
}
/**
* @return Returns the tagHeader.
*/
public MetadataContainer getExtendedContentDescription() {
return (MetadataContainer) getFirst(
GUID.GUID_EXTENDED_CONTENT_DESCRIPTION, MetadataContainer.class);
}
/**
* Returns a language list object if present.
*
* @return a language list object.
*/
public LanguageList getLanguageList() {
return (LanguageList) getFirst(GUID.GUID_LANGUAGE_LIST,
LanguageList.class);
}
/**
* Returns a metadata library object if present.
*
* @return metadata library objet
*/
public MetadataContainer getMetadataLibraryObject() {
return (MetadataContainer) getFirst(GUID.GUID_METADATA_LIBRARY,
MetadataContainer.class);
}
/**
* Returns a metadata object if present.
*
* @return metadata object
*/
public MetadataContainer getMetadataObject() {
return (MetadataContainer) getFirst(GUID.GUID_METADATA,
MetadataContainer.class);
}
}

View file

@ -1,213 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Each ASF file starts with a so called header. <br>
* This header contains other chunks. Each chunk starts with a 16 byte GUID
* followed by the length (in bytes) of the chunk (including GUID). The length
* number takes 8 bytes and is unsigned. Finally the chunk's data appears. <br>
*
* @author Christian Laireiter
*/
public final class AsfHeader extends ChunkContainer {
/**
* The charset &quot;UTF-16LE&quot; is mandatory for ASF handling.
*/
public final static Charset ASF_CHARSET = Charset.forName("UTF-16LE"); //$NON-NLS-1$
/**
* Byte sequence representing the zero term character.
*/
public final static byte[] ZERO_TERM = {0, 0};
static {
Set<GUID> MULTI_CHUNKS = new HashSet<GUID>();
MULTI_CHUNKS.add(GUID.GUID_STREAM);
}
/**
* An ASF header contains multiple chunks. <br>
* The count of those is stored here.
*/
private final long chunkCount;
/**
* Creates an instance.
*
* @param pos see {@link Chunk#position}
* @param chunkLen see {@link Chunk#chunkLength}
* @param chunkCnt
*/
public AsfHeader(final long pos, final BigInteger chunkLen,
final long chunkCnt) {
super(GUID.GUID_HEADER, pos, chunkLen);
this.chunkCount = chunkCnt;
}
/**
* This method looks for an content description object in this header
* instance, if not found there, it tries to get one from a contained ASF
* header extension object.
*
* @return content description if found, <code>null</code> otherwise.
*/
public ContentDescription findContentDescription() {
ContentDescription result = getContentDescription();
if (result == null && getExtendedHeader() != null) {
result = getExtendedHeader().getContentDescription();
}
return result;
}
/**
* This method looks for an extended content description object in this
* header instance, if not found there, it tries to get one from a contained
* ASF header extension object.
*
* @return extended content description if found, <code>null</code>
* otherwise.
*/
public MetadataContainer findExtendedContentDescription() {
MetadataContainer result = getExtendedContentDescription();
if (result == null && getExtendedHeader() != null) {
result = getExtendedHeader().getExtendedContentDescription();
}
return result;
}
/**
* This method searches for a metadata container of the given type.<br>
*
* @param type the type of the container to look up.
* @return a container of specified type, of <code>null</code> if not
* contained.
*/
public MetadataContainer findMetadataContainer(final ContainerType type) {
MetadataContainer result = (MetadataContainer) getFirst(type
.getContainerGUID(), MetadataContainer.class);
if (result == null) {
result = (MetadataContainer) getExtendedHeader().getFirst(
type.getContainerGUID(), MetadataContainer.class);
}
return result;
}
/**
* This method returns the first audio stream chunk found in the asf file or
* stream.
*
* @return Returns the audioStreamChunk.
*/
public AudioStreamChunk getAudioStreamChunk() {
AudioStreamChunk result = null;
final List<Chunk> streamChunks = assertChunkList(GUID.GUID_STREAM);
for (int i = 0; i < streamChunks.size() && result == null; i++) {
if (streamChunks.get(i) instanceof AudioStreamChunk) {
result = (AudioStreamChunk) streamChunks.get(i);
}
}
return result;
}
/**
* Returns the amount of chunks, when this instance was created.<br>
* If chunks have been added, this won't be reflected with this call.<br>
* For that use {@link #getChunks()}.
*
* @return Chunkcount at instance creation.
*/
public long getChunkCount() {
return this.chunkCount;
}
/**
* @return Returns the contentDescription.
*/
public ContentDescription getContentDescription() {
return (ContentDescription) getFirst(GUID.GUID_CONTENTDESCRIPTION,
ContentDescription.class);
}
/**
* @return Returns the encodingChunk.
*/
public EncodingChunk getEncodingChunk() {
return (EncodingChunk) getFirst(GUID.GUID_ENCODING, EncodingChunk.class);
}
/**
* @return Returns the encodingChunk.
*/
public EncryptionChunk getEncryptionChunk() {
return (EncryptionChunk) getFirst(GUID.GUID_CONTENT_ENCRYPTION,
EncryptionChunk.class);
}
/**
* @return Returns the tagHeader.
*/
public MetadataContainer getExtendedContentDescription() {
return (MetadataContainer) getFirst(
GUID.GUID_EXTENDED_CONTENT_DESCRIPTION, MetadataContainer.class);
}
/**
* @return Returns the extended header.
*/
public AsfExtendedHeader getExtendedHeader() {
return (AsfExtendedHeader) getFirst(GUID.GUID_HEADER_EXTENSION,
AsfExtendedHeader.class);
}
/**
* @return Returns the fileHeader.
*/
public FileHeader getFileHeader() {
return (FileHeader) getFirst(GUID.GUID_FILE, FileHeader.class);
}
/**
* @return Returns the streamBitratePropertiesChunk.
*/
public StreamBitratePropertiesChunk getStreamBitratePropertiesChunk() {
return (StreamBitratePropertiesChunk) getFirst(
GUID.GUID_STREAM_BITRATE_PROPERTIES,
StreamBitratePropertiesChunk.class);
}
/**
* {@inheritDoc}
*/
@Override
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder(super.prettyPrint(prefix,
prefix + " | : Contains: \"" + getChunkCount() + "\" chunks"
+ Utils.LINE_SEPARATOR));
return result.toString();
}
}

View file

@ -1,302 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.math.BigInteger;
/**
* This class represents the stream chunk describing an audio stream. <br>
*
* @author Christian Laireiter
*/
public final class AudioStreamChunk extends StreamChunk {
/**
* Stores the hex values of codec identifiers to their descriptions. <br>
*/
public final static String[][] CODEC_DESCRIPTIONS = {
{"161", " (Windows Media Audio (ver 7,8,9))"},
{"162", " (Windows Media Audio 9 series (Professional))"},
{"163", "(Windows Media Audio 9 series (Lossless))"},
{"7A21", " (GSM-AMR (CBR))"}, {"7A22", " (GSM-AMR (VBR))"}};
/**
* Stores the audio codec number for WMA
*/
public final static long WMA = 0x161;
/**
* Stores the audio codec number for WMA (CBR)
*/
public final static long WMA_CBR = 0x7A21;
/**
* Stores the audio codec number for WMA_LOSSLESS
*/
public final static long WMA_LOSSLESS = 0x163;
/**
* Stores the audio codec number for WMA_PRO
*/
public final static long WMA_PRO = 0x162;
/**
* Stores the audio codec number for WMA (VBR)
*/
public final static long WMA_VBR = 0x7A22;
/**
* Stores the average amount of bytes used by audio stream. <br>
* This value is a field within type specific data of audio stream. Maybe it
* could be used to calculate the KBPs.
*/
private long averageBytesPerSec;
/**
* Amount of bits used per sample. <br>
*/
private int bitsPerSample;
/**
* The block alignment of the audio data.
*/
private long blockAlignment;
/**
* Number of channels.
*/
private long channelCount;
/**
* Some data which needs to be interpreted if the codec is handled.
*/
private byte[] codecData = new byte[0];
/**
* The audio compression format code.
*/
private long compressionFormat;
/**
* this field stores the error concealment type.
*/
private GUID errorConcealment;
/**
* Sampling rate of audio stream.
*/
private long samplingRate;
/**
* Creates an instance.
*
* @param chunkLen Length of the entire chunk (including guid and size)
*/
public AudioStreamChunk(final BigInteger chunkLen) {
super(GUID.GUID_AUDIOSTREAM, chunkLen);
}
/**
* @return Returns the averageBytesPerSec.
*/
public long getAverageBytesPerSec() {
return this.averageBytesPerSec;
}
/**
* @param avgeBytesPerSec The averageBytesPerSec to set.
*/
public void setAverageBytesPerSec(final long avgeBytesPerSec) {
this.averageBytesPerSec = avgeBytesPerSec;
}
/**
* @return Returns the bitsPerSample.
*/
public int getBitsPerSample() {
return this.bitsPerSample;
}
/**
* Sets the bitsPerSample
*
* @param bps
*/
public void setBitsPerSample(final int bps) {
this.bitsPerSample = bps;
}
/**
* @return Returns the blockAlignment.
*/
public long getBlockAlignment() {
return this.blockAlignment;
}
/**
* Sets the blockAlignment.
*
* @param align
*/
public void setBlockAlignment(final long align) {
this.blockAlignment = align;
}
/**
* @return Returns the channelCount.
*/
public long getChannelCount() {
return this.channelCount;
}
/**
* @param channels The channelCount to set.
*/
public void setChannelCount(final long channels) {
this.channelCount = channels;
}
/**
* @return Returns the codecData.
*/
public byte[] getCodecData() {
return this.codecData.clone();
}
/**
* Sets the codecData
*
* @param codecSpecificData
*/
public void setCodecData(final byte[] codecSpecificData) {
if (codecSpecificData == null) {
throw new IllegalArgumentException();
}
this.codecData = codecSpecificData.clone();
}
/**
* This method will take a look at {@link #compressionFormat}and returns a
* String with its hex value and if known a textual note on what coded it
* represents. <br>
*
* @return A description for the used codec.
*/
public String getCodecDescription() {
final StringBuilder result = new StringBuilder(Long
.toHexString(getCompressionFormat()));
String furtherDesc = " (Unknown)";
for (final String[] aCODEC_DESCRIPTIONS : CODEC_DESCRIPTIONS) {
if (aCODEC_DESCRIPTIONS[0].equalsIgnoreCase(result.toString())) {
furtherDesc = aCODEC_DESCRIPTIONS[1];
break;
}
}
if (result.length() % 2 == 0) {
result.insert(0, "0x");
} else {
result.insert(0, "0x0");
}
result.append(furtherDesc);
return result.toString();
}
/**
* @return Returns the compressionFormat.
*/
public long getCompressionFormat() {
return this.compressionFormat;
}
/**
* @param cFormatCode The compressionFormat to set.
*/
public void setCompressionFormat(final long cFormatCode) {
this.compressionFormat = cFormatCode;
}
/**
* @return Returns the errorConcealment.
*/
public GUID getErrorConcealment() {
return this.errorConcealment;
}
/**
* This method sets the error concealment type which is given by two GUIDs. <br>
*
* @param errConc the type of error concealment the audio stream is stored as.
*/
public void setErrorConcealment(final GUID errConc) {
this.errorConcealment = errConc;
}
/**
* This method takes the value of {@link #getAverageBytesPerSec()}and
* calculates the kbps out of it, by simply multiplying by 8 and dividing by
* 1000. <br>
*
* @return amount of bits per second in kilo bits.
*/
public int getKbps() {
return (int) getAverageBytesPerSec() * 8 / 1000;
}
/**
* @return Returns the samplingRate.
*/
public long getSamplingRate() {
return this.samplingRate;
}
/**
* @param sampRate The samplingRate to set.
*/
public void setSamplingRate(final long sampRate) {
this.samplingRate = sampRate;
}
/**
* This mehtod returns whether the audio stream data is error concealed. <br>
* For now only interleaved concealment is known. <br>
*
* @return <code>true</code> if error concealment is used.
*/
public boolean isErrorConcealed() {
return getErrorConcealment().equals(
GUID.GUID_AUDIO_ERROR_CONCEALEMENT_INTERLEAVED);
}
/**
* {@inheritDoc}
*/
@Override
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder(super.prettyPrint(prefix));
result.append(prefix).append(" |-> Audio info:").append(
Utils.LINE_SEPARATOR);
result.append(prefix).append(" | : Bitrate : ").append(getKbps())
.append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" | : Channels : ").append(
getChannelCount()).append(" at ").append(getSamplingRate())
.append(" Hz").append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" | : Bits per Sample: ").append(
getBitsPerSample()).append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" | : Formatcode: ").append(
getCodecDescription()).append(Utils.LINE_SEPARATOR);
return result.toString();
}
}

View file

@ -1,175 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.math.BigInteger;
/**
* This class represents a chunk within ASF streams. <br>
* Each chunk starts with a 16byte {@linkplain GUID GUID} identifying the type.
* After that a number (represented by 8 bytes) follows which shows the size in
* bytes of the chunk. Finally there is the data of the chunk.
*
* @author Christian Laireiter
*/
public class Chunk {
/**
* The length of current chunk. <br>
*/
protected final BigInteger chunkLength;
/**
* The GUID of represented chunk header.
*/
protected final GUID guid;
/**
* The position of current header object within file or stream.
*/
protected long position;
/**
* Creates an instance
*
* @param headerGuid The GUID of header object.
* @param chunkLen Length of current chunk.
*/
public Chunk(final GUID headerGuid, final BigInteger chunkLen) {
if (headerGuid == null) {
throw new IllegalArgumentException("GUID must not be null.");
}
if (chunkLen == null || chunkLen.compareTo(BigInteger.ZERO) < 0) {
throw new IllegalArgumentException(
"chunkLen must not be null nor negative.");
}
this.guid = headerGuid;
this.chunkLength = chunkLen;
}
/**
* Creates an instance
*
* @param headerGuid The GUID of header object.
* @param pos Position of header object within stream or file.
* @param chunkLen Length of current chunk.
*/
public Chunk(final GUID headerGuid, final long pos,
final BigInteger chunkLen) {
if (headerGuid == null) {
throw new IllegalArgumentException("GUID must not be null");
}
if (pos < 0) {
throw new IllegalArgumentException(
"Position of header can't be negative.");
}
if (chunkLen == null || chunkLen.compareTo(BigInteger.ZERO) < 0) {
throw new IllegalArgumentException(
"chunkLen must not be null nor negative.");
}
this.guid = headerGuid;
this.position = pos;
this.chunkLength = chunkLen;
}
/**
* This method returns the End of the current chunk introduced by current
* header object.
*
* @return Position after current chunk.
* @deprecated typo, use {@link #getChunkEnd()} instead.
*/
@Deprecated
public long getChunckEnd() {
return this.position + this.chunkLength.longValue();
}
/**
* This method returns the End of the current chunk introduced by current
* header object.
*
* @return Position after current chunk.
*/
public long getChunkEnd() {
return this.position + this.chunkLength.longValue();
}
/**
* @return Returns the chunkLength.
*/
public BigInteger getChunkLength() {
return this.chunkLength;
}
/**
* @return Returns the guid.
*/
public GUID getGuid() {
return this.guid;
}
/**
* @return Returns the position.
*/
public long getPosition() {
return this.position;
}
/**
* Sets the position.
*
* @param pos position to set.
*/
public void setPosition(final long pos) {
this.position = pos;
}
/**
* This method creates a String containing useful information prepared to be
* printed on STD-OUT. <br>
* This method is intended to be overwritten by inheriting classes.
*
* @param prefix each line gets this string prepended.
* @return Information of current Chunk Object.
*/
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder();
result.append(prefix).append("-> GUID: ").append(
GUID.getGuidDescription(this.guid))
.append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" | : Starts at position: ").append(
getPosition()).append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" | : Last byte at: ").append(
getChunkEnd() - 1).append(Utils.LINE_SEPARATOR);
return result.toString();
}
/**
* (overridden)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return prettyPrint("");
}
}

View file

@ -1,176 +0,0 @@
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.ChunkPositionComparator;
import org.jaudiotagger.audio.asf.util.Utils;
import java.math.BigInteger;
import java.util.*;
/**
* Stores multiple ASF objects (chunks) in form of {@link Chunk} objects, and is
* itself an ASF object (chunk).<br>
* <br>
* Because current implementation is solely used for ASF metadata, all chunks
* (except for {@link StreamChunk}) may only be {@linkplain #addChunk(Chunk)
* inserted} once.
*
* @author Christian Laireiter
*/
public class ChunkContainer extends Chunk {
/**
* Stores the {@link GUID} instances, which are allowed multiple times
* within an ASF header.
*/
private final static Set<GUID> MULTI_CHUNKS;
static {
MULTI_CHUNKS = new HashSet<GUID>();
MULTI_CHUNKS.add(GUID.GUID_STREAM);
}
/**
* Stores the {@link Chunk} objects to their {@link GUID}.
*/
private final Map<GUID, List<Chunk>> chunkTable;
/**
* Creates an instance.
*
* @param chunkGUID the GUID which identifies the chunk.
* @param pos the position of the chunk within the stream.
* @param length the length of the chunk.
*/
public ChunkContainer(final GUID chunkGUID, final long pos,
final BigInteger length) {
super(chunkGUID, pos, length);
this.chunkTable = new Hashtable<GUID, List<Chunk>>();
}
/**
* Tests whether all stored chunks have a unique starting position among
* their brothers.
*
* @param container the container to test.
* @return <code>true</code> if all chunks are located at an unique
* position. However, no intersection is tested.
*/
protected static boolean chunkstartsUnique(final ChunkContainer container) {
boolean result = true;
final Set<Long> chunkStarts = new HashSet<Long>();
final Collection<Chunk> chunks = container.getChunks();
for (final Chunk curr : chunks) {
result &= chunkStarts.add(curr.getPosition());
}
return result;
}
/**
* Adds a chunk to the container.<br>
*
* @param toAdd The chunk which is to be added.
* @throws IllegalArgumentException If a chunk of same type is already added, except for
* {@link StreamChunk}.
*/
public void addChunk(final Chunk toAdd) {
final List<Chunk> list = assertChunkList(toAdd.getGuid());
if (!list.isEmpty() && !MULTI_CHUNKS.contains(toAdd.getGuid())) {
throw new IllegalArgumentException(
"The GUID of the given chunk indicates, that there is no more instance allowed."); //$NON-NLS-1$
}
list.add(toAdd);
assert chunkstartsUnique(this) : "Chunk has equal start position like an already inserted one."; //$NON-NLS-1$
}
/**
* This method asserts that a {@link List} exists for the given {@link GUID}
* , in {@link #chunkTable}.<br>
*
* @param lookFor The GUID to get list for.
* @return an already existing, or newly created list.
*/
protected List<Chunk> assertChunkList(final GUID lookFor) {
List<Chunk> result = this.chunkTable.get(lookFor);
if (result == null) {
result = new ArrayList<Chunk>();
this.chunkTable.put(lookFor, result);
}
return result;
}
/**
* Returns a collection of all contained chunks.<br>
*
* @return all contained chunks
*/
public Collection<Chunk> getChunks() {
final List<Chunk> result = new ArrayList<Chunk>();
for (final List<Chunk> curr : this.chunkTable.values()) {
result.addAll(curr);
}
return result;
}
/**
* Looks for the first stored chunk which has the given GUID.
*
* @param lookFor GUID to look up.
* @param instanceOf The class which must additionally be matched.
* @return <code>null</code> if no chunk was found, or the stored instance
* doesn't match.
*/
protected Chunk getFirst(final GUID lookFor,
final Class<? extends Chunk> instanceOf) {
Chunk result = null;
final List<Chunk> list = this.chunkTable.get(lookFor);
if (list != null && !list.isEmpty()) {
final Chunk chunk = list.get(0);
if (instanceOf.isAssignableFrom(chunk.getClass())) {
result = chunk;
}
}
return result;
}
/**
* This method checks if a chunk has been {@linkplain #addChunk(Chunk)
* added} with specified {@linkplain Chunk#getGuid() GUID}.<br>
*
* @param lookFor GUID to look up.
* @return <code>true</code> if chunk with specified GUID has been added.
*/
public boolean hasChunkByGUID(final GUID lookFor) {
return this.chunkTable.containsKey(lookFor);
}
/**
* {@inheritDoc}
*/
@Override
public String prettyPrint(final String prefix) {
return prettyPrint(prefix, "");
}
/**
* Nearly the same as {@link #prettyPrint(String)} however, additional
* information can be injected below the {@link Chunk#prettyPrint(String)}
* output and the listing of the contained chunks.<br>
*
* @param prefix The prefix to prepend.
* @param containerInfo Information to inject.
* @return Information of current Chunk Object.
*/
public String prettyPrint(final String prefix, final String containerInfo) {
final StringBuilder result = new StringBuilder(super.prettyPrint(prefix));
result.append(containerInfo);
result.append(prefix).append(" |").append(Utils.LINE_SEPARATOR);
final ArrayList<Chunk> list = new ArrayList<Chunk>(getChunks());
Collections.sort(list, new ChunkPositionComparator());
for (Chunk curr : list) {
result.append(curr.prettyPrint(prefix + " |"));
result.append(prefix).append(" |").append(Utils.LINE_SEPARATOR);
}
return result.toString();
}
}

View file

@ -1,283 +0,0 @@
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import org.jaudiotagger.logging.ErrorMessage;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
/**
* Enumerates capabilities, respectively uses, of metadata descriptors.<br>
* <br>
* The {@link #METADATA_LIBRARY_OBJECT} allows the most variations of data, as
* well as no size limitation (if it can be stored within a DWORD amount of
* bytes).<br>
*
* @author Christian Laireiter
*/
public enum ContainerType {
/**
* The descriptor is used in the content branding object (chunk)
*/
CONTENT_BRANDING(GUID.GUID_CONTENT_BRANDING, 32, false, false, false, false),
/**
* The descriptor is used in the content description object (chunk), so
* {@linkplain MetadataDescriptor#DWORD_MAXVALUE maximum data length}
* applies, no language index and stream number are allowed, as well as no
* multiple values.
*/
CONTENT_DESCRIPTION(GUID.GUID_CONTENTDESCRIPTION, 16, false, false, false,
false),
/**
* The descriptor is used in an extended content description object, so the
* {@linkplain MetadataDescriptor#DWORD_MAXVALUE maximum data size} applies,
* and no language index and stream number other than &quot;0&quot; is
* allowed. Additionally no multiple values are permitted.
*/
EXTENDED_CONTENT(GUID.GUID_EXTENDED_CONTENT_DESCRIPTION, 16, false, false,
false, false),
/**
* The descriptor is used in a metadata library object. No real size limit
* (except DWORD range) applies. Stream numbers and language indexes can be
* specified.
*/
METADATA_LIBRARY_OBJECT(GUID.GUID_METADATA_LIBRARY, 32, true, true, true,
true),
/**
* The descriptor is used in a metadata object. The
* {@linkplain MetadataDescriptor#DWORD_MAXVALUE maximum data size} applies.
* Stream numbers can be specified. But no language index (always
* &quot;0&quot;).
*/
METADATA_OBJECT(GUID.GUID_METADATA, 16, false, true, false, true);
/**
* Stores the guid that identifies ASF chunks which store metadata of the
* current type.
*/
private final GUID containerGUID;
/**
* <code>true</code> if the descriptor field can store {@link GUID} values.
*/
private final boolean guidEnabled;
/**
* <code>true</code> if descriptor field can refer to a language.
*/
private final boolean languageEnabled;
/**
* The maximum amount of bytes the descriptor data may consume.<br>
*/
private final BigInteger maximumDataLength;
/**
* <code>true</code> if the container may store multiple values of the same
* metadata descriptor specification (equality on name, language, and
* stream).<br>
* WindowsMedia players advanced tag editor for example stores the
* WM/Picture attribute once in the extended content description, and all
* others in the metadata library object.
*/
private final boolean multiValued;
/**
* if <code>-1</code> a size value has to be compared against
* {@link #maximumDataLength} because {@link Long#MAX_VALUE} is exceeded.<br>
* Otherwise this is the {@link BigInteger#longValue()} representation.
*/
private final long perfMaxDataLen;
/**
* <code>true</code> if descriptor field can refer to specific streams.
*/
private final boolean streamEnabled;
/**
* Creates an instance
*
* @param guid see {@link #containerGUID}
* @param maxDataLenBits The amount of bits that is used to represent an unsigned value
* for the containers size descriptors. Will create a maximum
* value for {@link #maximumDataLength}. (2 ^ maxDataLenBits -1)
* @param guidAllowed see {@link #guidEnabled}
* @param stream see {@link #streamEnabled}
* @param language see {@link #languageEnabled}
* @param multiValue see {@link #multiValued}
*/
ContainerType(final GUID guid, final int maxDataLenBits,
final boolean guidAllowed, final boolean stream,
final boolean language, final boolean multiValue) {
this.containerGUID = guid;
this.maximumDataLength = BigInteger.valueOf(2).pow(maxDataLenBits)
.subtract(BigInteger.ONE);
if (this.maximumDataLength
.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) <= 0) {
this.perfMaxDataLen = this.maximumDataLength.longValue();
} else {
this.perfMaxDataLen = -1;
}
this.guidEnabled = guidAllowed;
this.streamEnabled = stream;
this.languageEnabled = language;
this.multiValued = multiValue;
}
/**
* Determines if low has <= index as high, in respect to
* {@link #getOrdered()}
*
* @param low
* @param high
* @return <code>true</code> if in correct order.
*/
public static boolean areInCorrectOrder(final ContainerType low,
final ContainerType high) {
final List<ContainerType> asList = Arrays.asList(getOrdered());
return asList.indexOf(low) <= asList.indexOf(high);
}
/**
* Returns the elements in an order, that indicates more capabilities
* (ascending).<br>
*
* @return capability ordered types
*/
public static ContainerType[] getOrdered() {
return new ContainerType[]{CONTENT_DESCRIPTION, CONTENT_BRANDING, EXTENDED_CONTENT, METADATA_OBJECT, METADATA_LIBRARY_OBJECT};
}
/**
* Calls {@link #checkConstraints(String, byte[], int, int, int)} and
* actually throws the exception if there is one.
*
* @param name name of the descriptor
* @param data content
* @param type data type
* @param stream stream number
* @param language language index
*/
public void assertConstraints(final String name, final byte[] data,
final int type, final int stream, final int language) {
final RuntimeException result = checkConstraints(name, data, type,
stream, language);
if (result != null) {
throw result;
}
}
/**
* Checks if the values for a {@linkplain MetadataDescriptor content
* descriptor} match the contraints of the container type, and returns a
* {@link RuntimeException} if the requirements aren't met.
*
* @param name name of the descriptor
* @param data content
* @param type data type
* @param stream stream number
* @param language language index
* @return <code>null</code> if everything is fine.
*/
public RuntimeException checkConstraints(final String name,
final byte[] data, final int type, final int stream,
final int language) {
RuntimeException result = null;
// TODO generate tests
if (name == null || data == null) {
result = new IllegalArgumentException("Arguments must not be null.");
} else {
if (!Utils.isStringLengthValidNullSafe(name)) {
result = new IllegalArgumentException(
ErrorMessage.WMA_LENGTH_OF_STRING_IS_TOO_LARGE
.getMsg(name.length()));
}
}
if (result == null && !isWithinValueRange(data.length)) {
result = new IllegalArgumentException(
ErrorMessage.WMA_LENGTH_OF_DATA_IS_TOO_LARGE.getMsg(
data.length, getMaximumDataLength(),
getContainerGUID().getDescription()));
}
if (result == null
&& (stream < 0 || stream > MetadataDescriptor.MAX_STREAM_NUMBER || (!isStreamNumberEnabled() && stream != 0))) {
final String streamAllowed = isStreamNumberEnabled() ? "0 to 127"
: "0";
result = new IllegalArgumentException(
ErrorMessage.WMA_INVALID_STREAM_REFERNCE.getMsg(stream,
streamAllowed, getContainerGUID().getDescription()));
}
if (result == null && type == MetadataDescriptor.TYPE_GUID
&& !isGuidEnabled()) {
result = new IllegalArgumentException(
ErrorMessage.WMA_INVALID_GUID_USE.getMsg(getContainerGUID()
.getDescription()));
}
if (result == null
&& ((language != 0 && !isLanguageEnabled()) || (language < 0 || language >= MetadataDescriptor.MAX_LANG_INDEX))) {
final String langAllowed = isStreamNumberEnabled() ? "0 to 126"
: "0";
result = new IllegalArgumentException(
ErrorMessage.WMA_INVALID_LANGUAGE_USE.getMsg(language,
getContainerGUID().getDescription(), langAllowed));
}
if (result == null && this == CONTENT_DESCRIPTION
&& type != MetadataDescriptor.TYPE_STRING) {
result = new IllegalArgumentException(
ErrorMessage.WMA_ONLY_STRING_IN_CD.getMsg());
}
return result;
}
/**
* @return the containerGUID
*/
public GUID getContainerGUID() {
return this.containerGUID;
}
/**
* @return the maximumDataLength
*/
public BigInteger getMaximumDataLength() {
return this.maximumDataLength;
}
/**
* @return the guidEnabled
*/
public boolean isGuidEnabled() {
return this.guidEnabled;
}
/**
* @return the languageEnabled
*/
public boolean isLanguageEnabled() {
return this.languageEnabled;
}
/**
* Tests if the given value is less than or equal to
* {@link #getMaximumDataLength()}, and greater or equal to zero.<br>
*
* @param value The value to test
* @return <code>true</code> if size restrictions for binary data are met
* with this container type.
*/
public boolean isWithinValueRange(final long value) {
return (this.perfMaxDataLen == -1 || this.perfMaxDataLen >= value)
&& value >= 0;
}
/**
* @return the multiValued
*/
public boolean isMultiValued() {
return this.multiValued;
}
/**
* @return the streamEnabled
*/
public boolean isStreamNumberEnabled() {
return this.streamEnabled;
}
}

View file

@ -1,209 +0,0 @@
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;
/**
* This structure represents the value of the content branding object, which
* stores the banner image, the banner image URL and the copyright URL.<br>
*
* @author Christian Laireiter
*/
public final class ContentBranding extends MetadataContainer {
/**
* Stores the allowed {@linkplain MetadataDescriptor#getName() descriptor
* keys}.
*/
public final static Set<String> ALLOWED;
/**
* Descriptor key representing the banner image.
*/
public final static String KEY_BANNER_IMAGE = "BANNER_IMAGE";
/**
* Descriptor key representing the banner image type.<br>
* <br>
* <b>Known/valid values are:</b>
* <ol>
* <li>0: there is no image present</li>
* <li>1: there is a BMP image</li>
* <li>2: there is a JPEG image</li>
* <li>3: there is a GIF image</li>
* </ol>
*/
public final static String KEY_BANNER_TYPE = "BANNER_IMAGE_TYPE";
/**
* Descriptor key representing the banner image URL.
*/
public final static String KEY_BANNER_URL = "BANNER_IMAGE_URL";
/**
* Descriptor key representing the copyright URL.
*/
public final static String KEY_COPYRIGHT_URL = "COPYRIGHT_URL";
static {
ALLOWED = new HashSet<String>();
ALLOWED.add(KEY_BANNER_IMAGE);
ALLOWED.add(KEY_BANNER_TYPE);
ALLOWED.add(KEY_BANNER_URL);
ALLOWED.add(KEY_COPYRIGHT_URL);
}
/**
* Creates an instance.
*/
public ContentBranding() {
this(0, BigInteger.ZERO);
}
/**
* Creates an instance.
*
* @param pos Position of content description within file or stream
* @param size Length of content description.
*/
public ContentBranding(final long pos, final BigInteger size) {
super(ContainerType.CONTENT_BRANDING, pos, size);
}
/**
* Returns the banner image URL.
*
* @return the banner image URL.
*/
public String getBannerImageURL() {
return getValueFor(KEY_BANNER_URL);
}
/**
* This method sets the banner image URL, if <code>imageURL</code> is not
* blank.<br>
*
* @param imageURL image URL to set.
*/
public void setBannerImageURL(final String imageURL) {
if (Utils.isBlank(imageURL)) {
removeDescriptorsByName(KEY_BANNER_URL);
} else {
assertDescriptor(KEY_BANNER_URL).setStringValue(imageURL);
}
}
/**
* Returns the copyright URL.
*
* @return the banner image URL.
*/
public String getCopyRightURL() {
return getValueFor(KEY_COPYRIGHT_URL);
}
/**
* This method sets the copyright URL, if <code>copyRight</code> is not
* blank.<br>
*
* @param copyRight copyright URL to set.
*/
public void setCopyRightURL(final String copyRight) {
if (Utils.isBlank(copyRight)) {
removeDescriptorsByName(KEY_COPYRIGHT_URL);
} else {
assertDescriptor(KEY_COPYRIGHT_URL).setStringValue(copyRight);
}
}
/**
* {@inheritDoc}
*/
@Override
public long getCurrentAsfChunkSize() {
// GUID, size, image type, image data size, image url data size,
// copyright data size
long result = 40;
result += assertDescriptor(KEY_BANNER_IMAGE,
MetadataDescriptor.TYPE_BINARY).getRawDataSize();
result += getBannerImageURL().length();
result += getCopyRightURL().length();
return result;
}
/**
* Returns the binary image data.
*
* @return binary image data.
*/
public byte[] getImageData() {
return assertDescriptor(KEY_BANNER_IMAGE,
MetadataDescriptor.TYPE_BINARY).getRawData();
}
/**
* Returns the image type.<br>
*
* @return image type
* @see #KEY_BANNER_TYPE for known/valid values.
*/
public long getImageType() {
if (!hasDescriptor(KEY_BANNER_TYPE)) {
final MetadataDescriptor descriptor = new MetadataDescriptor(
ContainerType.CONTENT_BRANDING, KEY_BANNER_TYPE,
MetadataDescriptor.TYPE_DWORD);
descriptor.setDWordValue(0);
addDescriptor(descriptor);
}
return assertDescriptor(KEY_BANNER_TYPE).getNumber();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isAddSupported(final MetadataDescriptor descriptor) {
return ALLOWED.contains(descriptor.getName())
&& super.isAddSupported(descriptor);
}
/**
* @param imageType
* @param imageData
*/
public void setImage(final long imageType, final byte[] imageData) {
assert imageType >= 0 && imageType <= 3;
assert imageType > 0 || imageData.length == 0;
assertDescriptor(KEY_BANNER_TYPE, MetadataDescriptor.TYPE_DWORD)
.setDWordValue(imageType);
assertDescriptor(KEY_BANNER_IMAGE, MetadataDescriptor.TYPE_BINARY)
.setBinaryValue(imageData);
}
/**
* {@inheritDoc}
*/
@Override
public long writeInto(final OutputStream out) throws IOException {
final long chunkSize = getCurrentAsfChunkSize();
out.write(getGuid().getBytes());
Utils.writeUINT64(chunkSize, out);
Utils.writeUINT32(getImageType(), out);
assert getImageType() >= 0 && getImageType() <= 3;
final byte[] imageData = getImageData();
assert getImageType() > 0 || imageData.length == 0;
Utils.writeUINT32(imageData.length, out);
out.write(imageData);
Utils.writeUINT32(getBannerImageURL().length(), out);
out.write(getBannerImageURL().getBytes("ASCII"));
Utils.writeUINT32(getCopyRightURL().length(), out);
out.write(getCopyRightURL().getBytes("ASCII"));
return chunkSize;
}
}

View file

@ -1,243 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* This class represents the data of a chunk which contains title, author,
* copyright, description and the rating of the file. <br>
* It is optional within ASF files. But if, exists only once.
*
* @author Christian Laireiter
*/
public final class ContentDescription extends MetadataContainer {
/**
* Stores the only allowed keys of this metadata container.
*/
public final static Set<String> ALLOWED;
/**
* Field key for author.
*/
public final static String KEY_AUTHOR = "AUTHOR";
/**
* Field key for copyright.
*/
public final static String KEY_COPYRIGHT = "COPYRIGHT";
/**
* Field key for description.
*/
public final static String KEY_DESCRIPTION = "DESCRIPTION";
/**
* Field key for rating.
*/
public final static String KEY_RATING = "RATING";
/**
* Field key for title.
*/
public final static String KEY_TITLE = "TITLE";
static {
ALLOWED = new HashSet<String>(Arrays.asList(KEY_AUTHOR,
KEY_COPYRIGHT, KEY_DESCRIPTION, KEY_RATING, KEY_TITLE));
}
/**
* Creates an instance. <br>
*/
public ContentDescription() {
this(0, BigInteger.ZERO);
}
/**
* Creates an instance.
*
* @param pos Position of content description within file or stream
* @param chunkLen Length of content description.
*/
public ContentDescription(final long pos, final BigInteger chunkLen) {
super(ContainerType.CONTENT_DESCRIPTION, pos, chunkLen);
}
/**
* @return Returns the author.
*/
public String getAuthor() {
return getValueFor(KEY_AUTHOR);
}
/**
* @param fileAuthor The author to set.
* @throws IllegalArgumentException If "UTF-16LE"-byte-representation would take more than 65535
* bytes.
*/
public void setAuthor(final String fileAuthor) throws IllegalArgumentException {
setStringValue(KEY_AUTHOR, fileAuthor);
}
/**
* @return Returns the comment.
*/
public String getComment() {
return getValueFor(KEY_DESCRIPTION);
}
/**
* @param tagComment The comment to set.
* @throws IllegalArgumentException If "UTF-16LE"-byte-representation would take more than 65535
* bytes.
*/
public void setComment(final String tagComment) throws IllegalArgumentException {
setStringValue(KEY_DESCRIPTION, tagComment);
}
/**
* @return Returns the copyRight.
*/
public String getCopyRight() {
return getValueFor(KEY_COPYRIGHT);
}
/**
* {@inheritDoc}
*/
@Override
public long getCurrentAsfChunkSize() {
long result = 44; // GUID + UINT64 for size + 5 times string length
// (each
// 2 bytes) + 5 times zero term char (2 bytes each).
result += getAuthor().length() * 2; // UTF-16LE
result += getComment().length() * 2;
result += getRating().length() * 2;
result += getTitle().length() * 2;
result += getCopyRight().length() * 2;
return result;
}
/**
* @return returns the rating.
*/
public String getRating() {
return getValueFor(KEY_RATING);
}
/**
* @param ratingText The rating to be set.
* @throws IllegalArgumentException If "UTF-16LE"-byte-representation would take more than 65535
* bytes.
*/
public void setRating(final String ratingText) throws IllegalArgumentException {
setStringValue(KEY_RATING, ratingText);
}
/**
* @return Returns the title.
*/
public String getTitle() {
return getValueFor(KEY_TITLE);
}
/**
* @param songTitle The title to set.
* @throws IllegalArgumentException If "UTF-16LE"-byte-representation would take more than 65535
* bytes.
*/
public void setTitle(final String songTitle) throws IllegalArgumentException {
setStringValue(KEY_TITLE, songTitle);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isAddSupported(final MetadataDescriptor descriptor) {
return ALLOWED.contains(descriptor.getName())
&& super.isAddSupported(descriptor);
}
/**
* {@inheritDoc}
*/
@Override
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder(super.prettyPrint(prefix));
result.append(prefix).append(" |->Title : ").append(getTitle())
.append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |->Author : ").append(getAuthor())
.append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |->Copyright : ").append(
getCopyRight()).append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |->Description: ").append(getComment())
.append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |->Rating :").append(getRating())
.append(Utils.LINE_SEPARATOR);
return result.toString();
}
/**
* @param cpright The copyRight to set.
* @throws IllegalArgumentException If "UTF-16LE"-byte-representation would take more than 65535
* bytes.
*/
public void setCopyright(final String cpright) throws IllegalArgumentException {
setStringValue(KEY_COPYRIGHT, cpright);
}
/**
* {@inheritDoc}
*/
@Override
public long writeInto(final OutputStream out) throws IOException {
final long chunkSize = getCurrentAsfChunkSize();
out.write(this.getGuid().getBytes());
Utils.writeUINT64(getCurrentAsfChunkSize(), out);
// write the sizes of the string representations plus 2 bytes zero term
// character
Utils.writeUINT16(getTitle().length() * 2 + 2, out);
Utils.writeUINT16(getAuthor().length() * 2 + 2, out);
Utils.writeUINT16(getCopyRight().length() * 2 + 2, out);
Utils.writeUINT16(getComment().length() * 2 + 2, out);
Utils.writeUINT16(getRating().length() * 2 + 2, out);
// write the Strings
out.write(Utils.getBytes(getTitle(), AsfHeader.ASF_CHARSET));
out.write(AsfHeader.ZERO_TERM);
out.write(Utils.getBytes(getAuthor(), AsfHeader.ASF_CHARSET));
out.write(AsfHeader.ZERO_TERM);
out.write(Utils.getBytes(getCopyRight(), AsfHeader.ASF_CHARSET));
out.write(AsfHeader.ZERO_TERM);
out.write(Utils.getBytes(getComment(), AsfHeader.ASF_CHARSET));
out.write(AsfHeader.ZERO_TERM);
out.write(Utils.getBytes(getRating(), AsfHeader.ASF_CHARSET));
out.write(AsfHeader.ZERO_TERM);
return chunkSize;
}
}

View file

@ -1,87 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* This class was intended to store the data of a chunk which contained the
* encoding parameters in textual form. <br>
* Since the needed parameters were found in other chunks the implementation of
* this class was paused. <br>
* TODO complete analysis.
*
* @author Christian Laireiter
*/
public class EncodingChunk extends Chunk {
/**
* The read strings.
*/
private final List<String> strings;
/**
* Creates an instance.
*
* @param chunkLen Length of current chunk.
*/
public EncodingChunk(final BigInteger chunkLen) {
super(GUID.GUID_ENCODING, chunkLen);
this.strings = new ArrayList<String>();
}
/**
* This method appends a String.
*
* @param toAdd String to add.
*/
public void addString(final String toAdd) {
this.strings.add(toAdd);
}
/**
* This method returns a collection of all {@linkplain String Strings} which
* were added due {@link #addString(String)}.
*
* @return Inserted Strings.
*/
public Collection<String> getStrings() {
return new ArrayList<String>(this.strings);
}
/**
* {@inheritDoc}
*/
@Override
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder(super
.prettyPrint(prefix));
this.strings.iterator();
for (final String string : this.strings) {
result.append(prefix).append(" | : ").append(string).append(
Utils.LINE_SEPARATOR);
}
return result.toString();
}
}

View file

@ -1,149 +0,0 @@
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author eric
*/
public class EncryptionChunk extends Chunk {
/**
* The read strings.
*/
private final ArrayList<String> strings;
private String keyID;
private String licenseURL;
private String protectionType;
private String secretData;
/**
* Creates an instance.
*
* @param chunkLen Length of current chunk.
*/
public EncryptionChunk(final BigInteger chunkLen) {
super(GUID.GUID_CONTENT_ENCRYPTION, chunkLen);
this.strings = new ArrayList<String>();
this.secretData = "";
this.protectionType = "";
this.keyID = "";
this.licenseURL = "";
}
/**
* This method appends a String.
*
* @param toAdd String to add.
*/
public void addString(final String toAdd) {
this.strings.add(toAdd);
}
/**
* This method gets the keyID.
*
* @return
*/
public String getKeyID() {
return this.keyID;
}
/**
* This method appends a String.
*
* @param toAdd String to add.
*/
public void setKeyID(final String toAdd) {
this.keyID = toAdd;
}
/**
* This method gets the license URL.
*
* @return
*/
public String getLicenseURL() {
return this.licenseURL;
}
/**
* This method appends a String.
*
* @param toAdd String to add.
*/
public void setLicenseURL(final String toAdd) {
this.licenseURL = toAdd;
}
/**
* This method gets the secret data.
*
* @return
*/
public String getProtectionType() {
return this.protectionType;
}
/**
* This method appends a String.
*
* @param toAdd String to add.
*/
public void setProtectionType(final String toAdd) {
this.protectionType = toAdd;
}
/**
* This method gets the secret data.
*
* @return
*/
public String getSecretData() {
return this.secretData;
}
/**
* This method adds the secret data.
*
* @param toAdd String to add.
*/
public void setSecretData(final String toAdd) {
this.secretData = toAdd;
}
/**
* This method returns a collection of all {@link String}s which were addid
* due {@link #addString(String)}.
*
* @return Inserted Strings.
*/
public Collection<String> getStrings() {
return new ArrayList<String>(this.strings);
}
/**
* {@inheritDoc}
*/
@Override
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder(super.prettyPrint(prefix));
result.insert(0, Utils.LINE_SEPARATOR + prefix + " Encryption:"
+ Utils.LINE_SEPARATOR);
result.append(prefix).append(" |->keyID ").append(this.keyID).append(
Utils.LINE_SEPARATOR);
result.append(prefix).append(" |->secretData ").append(this.secretData)
.append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |->protectionType ").append(
this.protectionType).append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |->licenseURL ").append(this.licenseURL)
.append(Utils.LINE_SEPARATOR);
this.strings.iterator();
for (final String string : this.strings) {
result.append(prefix).append(" |->").append(string).append(Utils.LINE_SEPARATOR);
}
return result.toString();
}
}

View file

@ -1,233 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.math.BigInteger;
import java.util.Date;
/**
* This class stores the information about the file, which is contained within a
* special chunk of ASF files.<br>
*
* @author Christian Laireiter
*/
public class FileHeader extends Chunk {
/**
* Duration of the media content in 100ns steps.
*/
private final BigInteger duration;
/**
* The time the file was created.
*/
private final Date fileCreationTime;
/**
* Size of the file or stream.
*/
private final BigInteger fileSize;
/**
* Usually contains value of 2.
*/
private final long flags;
/**
* Maximum size of stream packages. <br>
* <b>Warning: </b> must be same size as {@link #minPackageSize}. Its not
* known how to handle deviating values.
*/
private final long maxPackageSize;
/**
* Minimun size of stream packages. <br>
* <b>Warning: </b> must be same size as {@link #maxPackageSize}. Its not
* known how to handle deviating values.
*/
private final long minPackageSize;
/**
* Number of stream packages within the File.
*/
private final BigInteger packageCount;
/**
* No Idea of the Meaning, but stored anyway. <br>
* Source documentation says it is: "Timestamp of end position"
*/
private final BigInteger timeEndPos;
/**
* Like {@link #timeEndPos}no Idea.
*/
private final BigInteger timeStartPos;
/**
* Size of an uncompressed video frame.
*/
private final long uncompressedFrameSize;
/**
* Creates an instance.
*
* @param chunckLen Length of the file header (chunk)
* @param size Size of file or stream
* @param fileTime Time file or stream was created. Time is calculated since 1st
* january of 1601 in 100ns steps.
* @param pkgCount Number of stream packages.
* @param dur Duration of media clip in 100ns steps
* @param timestampStart Timestamp of start {@link #timeStartPos}
* @param timestampEnd Timestamp of end {@link #timeEndPos}
* @param headerFlags some stream related flags.
* @param minPkgSize minimum size of packages
* @param maxPkgSize maximum size of packages
* @param uncmpVideoFrameSize Size of an uncompressed Video Frame.
*/
public FileHeader(final BigInteger chunckLen, final BigInteger size,
final BigInteger fileTime, final BigInteger pkgCount,
final BigInteger dur, final BigInteger timestampStart,
final BigInteger timestampEnd, final long headerFlags,
final long minPkgSize, final long maxPkgSize,
final long uncmpVideoFrameSize) {
super(GUID.GUID_FILE, chunckLen);
this.fileSize = size;
this.packageCount = pkgCount;
this.duration = dur;
this.timeStartPos = timestampStart;
this.timeEndPos = timestampEnd;
this.flags = headerFlags;
this.minPackageSize = minPkgSize;
this.maxPackageSize = maxPkgSize;
this.uncompressedFrameSize = uncmpVideoFrameSize;
this.fileCreationTime = Utils.getDateOf(fileTime).getTime();
}
/**
* @return Returns the duration.
*/
public BigInteger getDuration() {
return this.duration;
}
/**
* This method converts {@link #getDuration()}from 100ns steps to normal
* seconds.
*
* @return Duration of the media in seconds.
*/
public int getDurationInSeconds() {
return this.duration.divide(new BigInteger("10000000")).intValue();
}
/**
* @return Returns the fileCreationTime.
*/
public Date getFileCreationTime() {
return new Date(this.fileCreationTime.getTime());
}
/**
* @return Returns the fileSize.
*/
public BigInteger getFileSize() {
return this.fileSize;
}
/**
* @return Returns the flags.
*/
public long getFlags() {
return this.flags;
}
/**
* @return Returns the maxPackageSize.
*/
public long getMaxPackageSize() {
return this.maxPackageSize;
}
/**
* @return Returns the minPackageSize.
*/
public long getMinPackageSize() {
return this.minPackageSize;
}
/**
* @return Returns the packageCount.
*/
public BigInteger getPackageCount() {
return this.packageCount;
}
/**
* This method converts {@link #getDuration()} from 100ns steps to normal
* seconds with a fractional part taking milliseconds.<br>
*
* @return The duration of the media in seconds (with a precision of
* milliseconds)
*/
public float getPreciseDuration() {
return (float) (getDuration().doubleValue() / 10000000d);
}
/**
* @return Returns the timeEndPos.
*/
public BigInteger getTimeEndPos() {
return this.timeEndPos;
}
/**
* @return Returns the timeStartPos.
*/
public BigInteger getTimeStartPos() {
return this.timeStartPos;
}
/**
* @return Returns the uncompressedFrameSize.
*/
public long getUncompressedFrameSize() {
return this.uncompressedFrameSize;
}
/**
* (overridden)
*
* @see org.jaudiotagger.audio.asf.data.Chunk#prettyPrint(String)
*/
@Override
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder(super.prettyPrint(prefix));
result.append(prefix).append(" |-> Filesize = ").append(
getFileSize().toString()).append(" Bytes").append(
Utils.LINE_SEPARATOR);
result.append(prefix).append(" |-> Media duration= ").append(
getDuration().divide(new BigInteger("10000")).toString())
.append(" ms").append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |-> Created at = ").append(
getFileCreationTime()).append(Utils.LINE_SEPARATOR);
return result.toString();
}
}

View file

@ -1,527 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* This class is used for representation of GUIDs and as a reference list of all
* Known GUIDs. <br>
*
* @author Christian Laireiter
*/
public final class GUID {
/**
* This constant defines the GUID for stream chunks describing audio
* streams, indicating the the audio stream has no error concealment. <br>
*/
public final static GUID GUID_AUDIO_ERROR_CONCEALEMENT_ABSENT = new GUID(
new int[]{0x40, 0xA4, 0xF1, 0x49, 0xCE, 0x4E, 0xD0, 0x11, 0xA3,
0xAC, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6},
"Audio error concealment absent.");
/**
* This constant defines the GUID for stream chunks describing audio
* streams, indicating the the audio stream has interleaved error
* concealment. <br>
*/
public final static GUID GUID_AUDIO_ERROR_CONCEALEMENT_INTERLEAVED = new GUID(
new int[]{0x40, 0xA4, 0xF1, 0x49, 0xCE, 0x4E, 0xD0, 0x11, 0xA3,
0xAC, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6},
"Interleaved audio error concealment.");
/**
* This constant stores the GUID indicating that stream type is audio.
*/
public final static GUID GUID_AUDIOSTREAM = new GUID(new int[]{0x40,
0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80,
0x5F, 0x5C, 0x44, 0x2B}, " Audio stream");
/**
* This constant stores the GUID indicating a content branding object.
*/
public final static GUID GUID_CONTENT_BRANDING = new GUID(new int[]{0xFA,
0xB3, 0x11, 0x22, 0x23, 0xBD, 0xD2, 0x11, 0xB4, 0xB7, 0x00, 0xA0,
0xC9, 0x55, 0xFC, 0x6E}, "Content Branding");
/**
* This is for the Content Encryption Object
* 2211B3FB-BD23-11D2-B4B7-00A0C955FC6E, needs to be little-endian.
*/
public final static GUID GUID_CONTENT_ENCRYPTION = new GUID(new int[]{
0xfb, 0xb3, 0x11, 0x22, 0x23, 0xbd, 0xd2, 0x11, 0xb4, 0xb7, 0x00,
0xa0, 0xc9, 0x55, 0xfc, 0x6e}, "Content Encryption Object");
/**
* This constant represents the guidData for a chunk which contains Title,
* author, copyright, description and rating.
*/
public final static GUID GUID_CONTENTDESCRIPTION = new GUID(new int[]{
0x33, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00,
0xAA, 0x00, 0x62, 0xCE, 0x6C}, "Content Description");
/**
* This constant stores the GUID for Encoding-Info chunks.
*/
public final static GUID GUID_ENCODING = new GUID(new int[]{0x40, 0x52,
0xD1, 0x86, 0x1D, 0x31, 0xD0, 0x11, 0xA3, 0xA4, 0x00, 0xA0, 0xC9,
0x03, 0x48, 0xF6}, "Encoding description");
/**
* This constant defines the GUID for a WMA "Extended Content Description"
* chunk. <br>
*/
public final static GUID GUID_EXTENDED_CONTENT_DESCRIPTION = new GUID(
new int[]{0x40, 0xA4, 0xD0, 0xD2, 0x07, 0xE3, 0xD2, 0x11, 0x97,
0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50},
"Extended Content Description");
/**
* GUID of ASF file header.
*/
public final static GUID GUID_FILE = new GUID(new int[]{0xA1, 0xDC, 0xAB,
0x8C, 0x47, 0xA9, 0xCF, 0x11, 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20,
0x53, 0x65}, "File header");
/**
* This constant defines the GUID of a asf header chunk.
*/
public final static GUID GUID_HEADER = new GUID(new int[]{0x30, 0x26,
0xb2, 0x75, 0x8e, 0x66, 0xcf, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00,
0x62, 0xce, 0x6c}, "Asf header");
/**
* This constant stores a GUID whose functionality is unknown.
*/
public final static GUID GUID_HEADER_EXTENSION = new GUID(new int[]{0xB5,
0x03, 0xBF, 0x5F, 0x2E, 0xA9, 0xCF, 0x11, 0x8E, 0xE3, 0x00, 0xC0,
0x0C, 0x20, 0x53, 0x65}, "Header Extension");
/**
* This constant stores the GUID indicating the asf language list object.<br>
*/
public final static GUID GUID_LANGUAGE_LIST = new GUID(new int[]{0xa9,
0x46, 0x43, 0x7c, 0xe0, 0xef, 0xfc, 0x4b, 0xb2, 0x29, 0x39, 0x3e,
0xde, 0x41, 0x5c, 0x85}, "Language List");
/**
* This constant stores the length of GUIDs used with ASF streams. <br>
*/
public final static int GUID_LENGTH = 16;
/**
* This constant stores the GUID indicating the asf metadata object.<br>
*/
public final static GUID GUID_METADATA = new GUID(new int[]{0xea, 0xcb,
0xf8, 0xc5, 0xaf, 0x5b, 0x77, 0x48, 0x84, 0x67, 0xaa, 0x8c, 0x44,
0xfa, 0x4c, 0xca}, "Metadata");
/**
* This constant stores the GUID indicating the asf metadata library object.<br>
*/
public final static GUID GUID_METADATA_LIBRARY = new GUID(new int[]{0x94,
0x1c, 0x23, 0x44, 0x98, 0x94, 0xd1, 0x49, 0xa1, 0x41, 0x1d, 0x13,
0x4e, 0x45, 0x70, 0x54}, "Metadata Library");
/**
* This constant stores the GUID indicating a stream object.
*/
public final static GUID GUID_STREAM = new GUID(new int[]{0x91, 0x07,
0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C,
0x20, 0x53, 0x65}, "Stream");
/**
* This constant stores a GUID indicating a "stream bitrate properties"
* chunk.
*/
public final static GUID GUID_STREAM_BITRATE_PROPERTIES = new GUID(
new int[]{0xCE, 0x75, 0xF8, 0x7B, 0x8D, 0x46, 0xD1, 0x11, 0x8D,
0x82, 0x00, 0x60, 0x97, 0xC9, 0xA2, 0xB2},
"Stream bitrate properties");
/**
* This constant represents a GUID implementation which can be used for
* generic implementations, which have to provide a GUID, but do not really
* require a specific GUID to work.
*/
public final static GUID GUID_UNSPECIFIED = new GUID(new int[]{0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}, "Unspecified");
/**
* This constant stores the GUID indicating that stream type is video.
*/
public final static GUID GUID_VIDEOSTREAM = new GUID(new int[]{0xC0,
0xEF, 0x19, 0xBC, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80,
0x5F, 0x5C, 0x44, 0x2B}, "Video stream");
/**
* This field stores all known GUIDs.
*/
public final static GUID[] KNOWN_GUIDS;
/**
* This constant stores the GUID for a &quot;script command object&quot;.<br>
*/
public final static GUID SCRIPT_COMMAND_OBJECT = new GUID(new int[]{0x30,
0x1a, 0xfb, 0x1e, 0x62, 0x0b, 0xd0, 0x11, 0xa3, 0x9b, 0x00, 0xa0,
0xc9, 0x03, 0x48, 0xf6}, "Script Command Object");
/**
* The GUID String values format.<br>
*/
private final static Pattern GUID_PATTERN = Pattern
.compile(
"[a-f0-9]{8}\\-[a-f0-9]{4}\\-[a-f0-9]{4}\\-[a-f0-9]{4}\\-[a-f0-9]{12}",
Pattern.CASE_INSENSITIVE);
/**
* This map is used, to get the description of a GUID instance, which has
* been created by reading.<br>
* The map comparison is done against the {@link GUID#guidData} field. But
* only the {@link #KNOWN_GUIDS} have a description set.
*/
private final static Map<GUID, GUID> GUID_TO_CONFIGURED;
static {
KNOWN_GUIDS = new GUID[]{GUID_AUDIO_ERROR_CONCEALEMENT_ABSENT,
GUID_CONTENTDESCRIPTION, GUID_AUDIOSTREAM, GUID_ENCODING,
GUID_FILE, GUID_HEADER, GUID_STREAM,
GUID_EXTENDED_CONTENT_DESCRIPTION, GUID_VIDEOSTREAM,
GUID_HEADER_EXTENSION, GUID_STREAM_BITRATE_PROPERTIES,
SCRIPT_COMMAND_OBJECT, GUID_CONTENT_ENCRYPTION,
GUID_CONTENT_BRANDING, GUID_UNSPECIFIED, GUID_METADATA_LIBRARY,
GUID_METADATA, GUID_LANGUAGE_LIST};
GUID_TO_CONFIGURED = new HashMap<GUID, GUID>(KNOWN_GUIDS.length);
for (final GUID curr : KNOWN_GUIDS) {
assert !GUID_TO_CONFIGURED.containsKey(curr) : "Double definition: \""
+ GUID_TO_CONFIGURED.get(curr).getDescription()
+ "\" <-> \"" + curr.getDescription() + "\"";
GUID_TO_CONFIGURED.put(curr, curr);
}
}
/**
* Stores an optionally description of the GUID.
*/
private String description = "";
/**
* An instance of this class stores the value of the wrapped GUID in this
* field. <br>
*/
private int[] guidData = null;
/**
* Stores the hash code of the object.<br>
* <code>&quot;-1&quot;</code> if not determined yet.
*/
private int hash;
/**
* Creates an instance and assigns given <code>value</code>.<br>
*
* @param value GUID, which should be assigned. (will be converted to int[])
*/
public GUID(final byte[] value) {
assert value != null;
final int[] tmp = new int[value.length];
for (int i = 0; i < value.length; i++) {
tmp[i] = (0xFF & value[i]);
}
setGUID(tmp);
}
/**
* Creates an instance and assigns given <code>value</code>.<br>
*
* @param value GUID, which should be assigned.
*/
public GUID(final int[] value) {
setGUID(value);
}
/**
* Creates an instance like {@link #GUID(int[])}and sets the optional
* description. <br>
*
* @param value GUID, which should be assigned.
* @param desc Description for the GUID.
*/
public GUID(final int[] value, final String desc) {
this(value);
if (desc == null) {
throw new IllegalArgumentException("Argument must not be null.");
}
this.description = desc;
}
/**
* Creates an instance like {@link #GUID(int[])} and sets the optional
* description. (the int[] is obtained by {@link GUID#parseGUID(String)}) <br>
*
* @param guidString GUID, which should be assigned.
* @param desc Description for the GUID.
*/
public GUID(final String guidString, final String desc) {
this(parseGUID(guidString).getGUID());
if (desc == null) {
throw new IllegalArgumentException("Argument must not be null.");
}
this.description = desc;
}
/**
* This method checks if the given <code>value</code> is matching the GUID
* specification of ASF streams. <br>
*
* @param value possible GUID.
* @return <code>true</code> if <code>value</code> matches the specification
* of a GUID.
*/
public static boolean assertGUID(final int[] value) {
return value != null && value.length == GUID.GUID_LENGTH;
}
/**
* This method looks up a GUID instance from {@link #KNOWN_GUIDS} which
* matches the value of the given GUID.
*
* @param orig GUID to look up.
* @return a GUID instance from {@link #KNOWN_GUIDS} if available.
* <code>null</code> else.
*/
public static GUID getConfigured(final GUID orig) {
// safe against null
return GUID_TO_CONFIGURED.get(orig);
}
/**
* This method searches a GUID in {@link #KNOWN_GUIDS}which is equal to the
* given <code>guidData</code> and returns its description. <br>
* This method is useful if a GUID was read out of a file and no
* identification has been done yet.
*
* @param guid GUID, which description is needed.
* @return description of the GUID if found. Else <code>null</code>
*/
public static String getGuidDescription(final GUID guid) {
String result = null;
if (guid == null) {
throw new IllegalArgumentException("Argument must not be null.");
}
if (getConfigured(guid) != null) {
result = getConfigured(guid).getDescription();
}
return result;
}
/**
* This method parses a String as GUID.<br>
* The format is like the one in the ASF specification.<br>
* An Example: <code>C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA</code><br>
*
* @param guid the string to parse.
* @return the GUID.
* @throws GUIDFormatException If the GUID has an invalid format.
*/
public static GUID parseGUID(final String guid) throws GUIDFormatException {
if (guid == null) {
throw new GUIDFormatException("null");
}
if (!GUID_PATTERN.matcher(guid).matches()) {
throw new GUIDFormatException("Invalid guidData format.");
}
final int[] bytes = new int[GUID_LENGTH];
/*
* Don't laugh, but did not really come up with a nicer solution today
*/
final int[] arrayIndices = {3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12,
13, 14, 15};
int arrayPointer = 0;
for (int i = 0; i < guid.length(); i++) {
if (guid.charAt(i) == '-') {
continue;
}
bytes[arrayIndices[arrayPointer++]] = Integer.parseInt(guid
.substring(i, i + 2), 16);
i++;
}
return new GUID(bytes);
}
/**
* This method compares two objects. If the given Object is a {@link GUID},
* the stored GUID values are compared. <br>
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
boolean result = false;
if (obj instanceof GUID) {
final GUID other = (GUID) obj;
result = Arrays.equals(this.getGUID(), other.getGUID());
}
return result;
}
/**
* This method returns the GUID as an array of bytes. <br>
*
* @return The GUID as a byte array.
* @see #getGUID()
*/
public byte[] getBytes() {
final byte[] result = new byte[this.guidData.length];
for (int i = 0; i < result.length; i++) {
result[i] = (byte) (this.guidData[i] & 0xFF);
}
return result;
}
/**
* @return Returns the description.
*/
public String getDescription() {
return this.description;
}
/**
* This method returns the GUID of this object. <br>
*
* @return stored GUID.
*/
public int[] getGUID() {
final int[] copy = new int[this.guidData.length];
System.arraycopy(this.guidData, 0, copy, 0, this.guidData.length);
return copy;
}
/**
* This method saves a copy of the given <code>value</code> as the
* represented value of this object. <br>
* The given value is checked with {@link #assertGUID(int[])}.<br>
*
* @param value GUID to assign.
*/
private void setGUID(final int[] value) {
if (assertGUID(value)) {
this.guidData = new int[GUID_LENGTH];
System.arraycopy(value, 0, this.guidData, 0, GUID_LENGTH);
} else {
throw new IllegalArgumentException(
"The given guidData doesn't match the GUID specification.");
}
}
/**
* Convenience method to get 2digit hex values of each byte.
*
* @param bytes bytes to convert.
* @return each byte as 2 digit hex.
*/
private String[] getHex(final byte[] bytes) {
final String[] result = new String[bytes.length];
final StringBuilder tmp = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
tmp.delete(0, tmp.length());
tmp.append(Integer.toHexString(0xFF & bytes[i]));
if (tmp.length() == 1) {
tmp.insert(0, "0");
}
result[i] = tmp.toString();
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
if (this.hash == -1) {
int tmp = 0;
for (final int curr : getGUID()) {
tmp = tmp * 31 + curr;
}
this.hash = tmp;
}
return this.hash;
}
/**
* This method checks if the currently stored GUID ({@link #guidData}) is
* correctly filled. <br>
*
* @return <code>true</code> if it is.
*/
public boolean isValid() {
return assertGUID(getGUID());
}
/**
* This method gives a hex formatted representation of {@link #getGUID()}
*
* @return hex formatted representation.
*/
public String prettyPrint() {
final StringBuilder result = new StringBuilder();
String descr = getDescription();
if (Utils.isBlank(descr)) {
descr = getGuidDescription(this);
}
if (!Utils.isBlank(descr)) {
result.append("Description: ").append(descr).append(
Utils.LINE_SEPARATOR).append(" ");
}
result.append(this.toString());
return result.toString();
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
// C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA
// 0xea, 0xcb,0xf8, 0xc5, 0xaf, 0x5b, 0x77, 0x48, 0x84, 0x67, 0xaa,
// 0x8c, 0x44,0xfa, 0x4c, 0xca
final StringBuilder result = new StringBuilder();
final String[] bytes = getHex(getBytes());
result.append(bytes[3]);
result.append(bytes[2]);
result.append(bytes[1]);
result.append(bytes[0]);
result.append('-');
result.append(bytes[5]);
result.append(bytes[4]);
result.append('-');
result.append(bytes[7]);
result.append(bytes[6]);
result.append('-');
result.append(bytes[8]);
result.append(bytes[9]);
result.append('-');
result.append(bytes[10]);
result.append(bytes[11]);
result.append(bytes[12]);
result.append(bytes[13]);
result.append(bytes[14]);
result.append(bytes[15]);
return result.toString();
}
}

View file

@ -1,24 +0,0 @@
package org.jaudiotagger.audio.asf.data;
/**
* This exception is used when a string was about to be interpreted as a GUID,
* but did not match the format.<br>
*
* @author Christian Laireiter
*/
public class GUIDFormatException extends IllegalArgumentException {
/**
*
*/
private static final long serialVersionUID = 6035645678612384953L;
/**
* Creates an instance.
*
* @param detail detail message.
*/
public GUIDFormatException(final String detail) {
super(detail);
}
}

View file

@ -1,113 +0,0 @@
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import org.jaudiotagger.logging.ErrorMessage;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
/**
* This structure represents the data of the ASF language object.<br>
* The language list is simply a listing of language codes which should comply
* to RFC-1766.<br>
* <b>Consider:</b> the index of a language is used by other entries in the ASF
* metadata.
*
* @author Christian Laireiter
*/
public class LanguageList extends Chunk {
/**
* List of language codes, complying RFC-1766
*/
private final List<String> languages = new ArrayList<String>();
/**
* Creates a new instance.<br>
*/
public LanguageList() {
super(GUID.GUID_LANGUAGE_LIST, 0, BigInteger.ZERO);
}
/**
* Creates an instance.
*
* @param pos position within the ASF file.
* @param size size of the chunk
*/
public LanguageList(final long pos, final BigInteger size) {
super(GUID.GUID_LANGUAGE_LIST, pos, size);
}
/**
* This method adds a language.<br>
*
* @param language language code
*/
public void addLanguage(final String language) {
if (language.length() < MetadataDescriptor.MAX_LANG_INDEX) {
if (!this.languages.contains(language)) {
this.languages.add(language);
}
} else {
throw new IllegalArgumentException(
ErrorMessage.WMA_LENGTH_OF_LANGUAGE_IS_TOO_LARGE
.getMsg(language.length() * 2 + 2));
}
}
/**
* Returns the language code at the specified index.
*
* @param index the index of the language code to get.
* @return the language code at given index.
*/
public String getLanguage(final int index) {
return this.languages.get(index);
}
/**
* Returns the amount of stored language codes.
*
* @return number of stored language codes.
*/
public int getLanguageCount() {
return this.languages.size();
}
/**
* Returns all language codes in list.
*
* @return list of language codes.
*/
public List<String> getLanguages() {
return new ArrayList<String>(this.languages);
}
/**
* {@inheritDoc}
*/
@Override
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder(super.prettyPrint(prefix));
for (int i = 0; i < getLanguageCount(); i++) {
result.append(prefix);
result.append(" |-> ");
result.append(i);
result.append(" : ");
result.append(getLanguage(i));
result.append(Utils.LINE_SEPARATOR);
}
return result.toString();
}
/**
* Removes the language entry at specified index.
*
* @param index index of language to remove.
*/
public void removeLanguage(final int index) {
this.languages.remove(index);
}
}

View file

@ -1,443 +0,0 @@
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.io.WriteableChunk;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.*;
/**
* This structure represents the &quot;Metadata Object&quot;,&quot;Metadata
* Library Object&quot; and &quot;Extended Content Description&quot;.<br>
*
* @author Christian Laireiter
*/
public class MetadataContainer extends Chunk implements WriteableChunk {
/**
* stores the represented container type.<br>
*/
private final ContainerType containerType;
/**
* Stores the descriptors.
*/
private final Map<DescriptorPointer, List<MetadataDescriptor>> descriptors = new Hashtable<DescriptorPointer, List<MetadataDescriptor>>();
/**
* for performance reasons this instance is used to look up existing
* descriptors in {@link #descriptors}.<br>
*/
private final DescriptorPointer perfPoint = new DescriptorPointer(
new MetadataDescriptor(""));
/**
* Creates an instance.
*
* @param type determines the type of the container
*/
public MetadataContainer(final ContainerType type) {
this(type, 0, BigInteger.ZERO);
}
/**
* Creates an instance.
*
* @param type determines the type of the container
* @param pos location in the ASF file
* @param size size of the chunk.
*/
public MetadataContainer(final ContainerType type, final long pos,
final BigInteger size) {
super(type.getContainerGUID(), pos, size);
this.containerType = type;
}
/**
* Creates an instance.
*
* @param containerGUID the containers GUID
* @param pos location in the ASF file
* @param size size of the chunk.
*/
public MetadataContainer(final GUID containerGUID, final long pos,
final BigInteger size) {
this(determineType(containerGUID), pos, size);
}
/**
* Looks up all {@linkplain ContainerType#getContainerGUID() guids} and
* returns the matching type.
*
* @param guid GUID to look up
* @return matching container type.
* @throws IllegalArgumentException if no container type matches
*/
private static ContainerType determineType(final GUID guid)
throws IllegalArgumentException {
assert guid != null;
ContainerType result = null;
for (final ContainerType curr : ContainerType.values()) {
if (curr.getContainerGUID().equals(guid)) {
result = curr;
break;
}
}
if (result == null) {
throw new IllegalArgumentException(
"Unknown metadata container specified by GUID ("
+ guid.toString() + ")");
}
return result;
}
/**
* Adds a metadata descriptor.
*
* @param toAdd the descriptor to add.
* @throws IllegalArgumentException if descriptor does not meet container requirements, or
* already exist.
*/
public final void addDescriptor(final MetadataDescriptor toAdd)
throws IllegalArgumentException {
// check with throwing exceptions
this.containerType.assertConstraints(toAdd.getName(), toAdd
.getRawData(), toAdd.getType(), toAdd.getStreamNumber(), toAdd
.getLanguageIndex());
// validate containers capabilities
if (!isAddSupported(toAdd)) {
throw new IllegalArgumentException(
"Descriptor cannot be added, see isAddSupported(...)");
}
/*
* Check for containers types capabilities.
*/
// Search for descriptor list by name, language and stream.
List<MetadataDescriptor> list;
synchronized (this.perfPoint) {
list = this.descriptors.get(this.perfPoint.setDescriptor(toAdd));
}
if (list == null) {
list = new ArrayList<MetadataDescriptor>();
this.descriptors.put(new DescriptorPointer(toAdd), list);
} else {
if (!list.isEmpty() && !this.containerType.isMultiValued()) {
throw new IllegalArgumentException(
"Container does not allow multiple values of descriptors with same name, language index and stream number");
}
}
list.add(toAdd);
}
/**
* This method asserts that this container has a descriptor with the
* specified key, means returns an existing or creates a new descriptor.
*
* @param key the descriptor name to look up (or create)
* @return the/a descriptor with the specified name (and initial type of
* {@link MetadataDescriptor#TYPE_STRING}.
*/
protected final MetadataDescriptor assertDescriptor(final String key) {
return assertDescriptor(key, MetadataDescriptor.TYPE_STRING);
}
/**
* This method asserts that this container has a descriptor with the
* specified key, means returns an existing or creates a new descriptor.
*
* @param key the descriptor name to look up (or create)
* @param type if the descriptor is created, this data type is applied.
* @return the/a descriptor with the specified name.
*/
protected final MetadataDescriptor assertDescriptor(final String key,
final int type) {
MetadataDescriptor desc;
final List<MetadataDescriptor> descriptorsByName = getDescriptorsByName(key);
if (descriptorsByName == null || descriptorsByName.isEmpty()) {
desc = new MetadataDescriptor(getContainerType(), key, type);
addDescriptor(desc);
} else {
desc = descriptorsByName.get(0);
}
return desc;
}
/**
* Checks whether a descriptor already exists.<br>
* Name, stream number and language index are compared. Data and data type
* are ignored.
*
* @param lookup descriptor to look up.
* @return <code>true</code> if such a descriptor already exists.
*/
public final boolean containsDescriptor(final MetadataDescriptor lookup) {
assert lookup != null;
return this.descriptors.containsKey(this.perfPoint
.setDescriptor(lookup));
}
/**
* Returns the type of container this instance represents.<br>
*
* @return represented container type.
*/
public final ContainerType getContainerType() {
return this.containerType;
}
/**
* {@inheritDoc}
*/
public long getCurrentAsfChunkSize() {
/*
* 16 bytes GUID, 8 bytes chunk size, 2 bytes descriptor count
*/
long result = 26;
for (final MetadataDescriptor curr : getDescriptors()) {
result += curr.getCurrentAsfSize(this.containerType);
}
return result;
}
/**
* Returns the number of contained descriptors.
*
* @return number of descriptors.
*/
public final int getDescriptorCount() {
return this.getDescriptors().size();
}
/**
* Returns all stored descriptors.
*
* @return stored descriptors.
*/
public final List<MetadataDescriptor> getDescriptors() {
final List<MetadataDescriptor> result = new ArrayList<MetadataDescriptor>();
for (final List<MetadataDescriptor> curr : this.descriptors.values()) {
result.addAll(curr);
}
return result;
}
/**
* Returns a list of descriptors with the given
* {@linkplain MetadataDescriptor#getName() name}.<br>
*
* @param name name of the descriptors to return
* @return list of descriptors with given name.
*/
public final List<MetadataDescriptor> getDescriptorsByName(final String name) {
assert name != null;
final List<MetadataDescriptor> result = new ArrayList<MetadataDescriptor>();
final Collection<List<MetadataDescriptor>> values = this.descriptors
.values();
for (final List<MetadataDescriptor> currList : values) {
if (!currList.isEmpty() && currList.get(0).getName().equals(name)) {
result.addAll(currList);
}
}
return result;
}
/**
* This method looks up a descriptor with given name and returns its value
* as string.<br>
*
* @param name the name of the descriptor to look up.
* @return the string representation of a found descriptors value. Even an
* empty string if no descriptor has been found.
*/
protected final String getValueFor(final String name) {
String result = "";
final List<MetadataDescriptor> descs = getDescriptorsByName(name);
if (descs != null) {
assert descs.size() <= 1;
if (!descs.isEmpty()) {
result = descs.get(0).getString();
}
}
return result;
}
/**
* Determines if this container contains a descriptor with given
* {@linkplain MetadataDescriptor#getName() name}.<br>
*
* @param name Name of the descriptor to look for.
* @return <code>true</code> if descriptor has been found.
*/
public final boolean hasDescriptor(final String name) {
return !getDescriptorsByName(name).isEmpty();
}
/**
* Determines/checks if the given descriptor may be added to the container.<br>
* This implies a check for the capabilities of the container specified by
* its {@linkplain #getContainerType() container type}.<br>
*
* @param descriptor the descriptor to test.
* @return <code>true</code> if {@link #addDescriptor(MetadataDescriptor)}
* can be called with given descriptor.
*/
public boolean isAddSupported(final MetadataDescriptor descriptor) {
boolean result = getContainerType().checkConstraints(
descriptor.getName(), descriptor.getRawData(),
descriptor.getType(), descriptor.getStreamNumber(),
descriptor.getLanguageIndex()) == null;
// Now check if there is already a value contained.
if (result && !getContainerType().isMultiValued()) {
synchronized (this.perfPoint) {
final List<MetadataDescriptor> list = this.descriptors
.get(this.perfPoint.setDescriptor(descriptor));
if (list != null) {
result = list.isEmpty();
}
}
}
return result;
}
/**
* {@inheritDoc}
*/
public final boolean isEmpty() {
boolean result = true;
if (getDescriptorCount() != 0) {
final Iterator<MetadataDescriptor> iterator = getDescriptors()
.iterator();
while (result && iterator.hasNext()) {
result &= iterator.next().isEmpty();
}
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder(super.prettyPrint(prefix));
for (final MetadataDescriptor curr : getDescriptors()) {
result.append(prefix).append(" |-> ");
result.append(curr);
result.append(Utils.LINE_SEPARATOR);
}
return result.toString();
}
/**
* Removes all stored descriptors with the given
* {@linkplain MetadataDescriptor#getName() name}.<br>
*
* @param name the name to remove.
*/
public final void removeDescriptorsByName(final String name) {
assert name != null;
final Iterator<List<MetadataDescriptor>> iterator = this.descriptors
.values().iterator();
while (iterator.hasNext()) {
final List<MetadataDescriptor> curr = iterator.next();
if (!curr.isEmpty() && curr.get(0).getName().equals(name)) {
iterator.remove();
}
}
}
/**
* {@linkplain #assertDescriptor(String) asserts} the existence of a
* descriptor with given <code>name</code> and
* {@linkplain MetadataDescriptor#setStringValue(String) assings} the string
* value.
*
* @param name the name of the descriptor to set the value for.
* @param value the string value.
*/
protected final void setStringValue(final String name, final String value) {
assertDescriptor(name).setStringValue(value);
}
/**
* {@inheritDoc}
*/
public long writeInto(final OutputStream out) throws IOException {
final long chunkSize = getCurrentAsfChunkSize();
final List<MetadataDescriptor> descriptorList = getDescriptors();
out.write(getGuid().getBytes());
Utils.writeUINT64(chunkSize, out);
Utils.writeUINT16(descriptorList.size(), out);
for (final MetadataDescriptor curr : descriptorList) {
curr.writeInto(out, this.containerType);
}
return chunkSize;
}
/**
* This class is used to uniquely identify an enclosed descriptor by its
* name, language index and stream number.<br>
* The type of the descriptor is ignored, since it just specifies the data
* content.
*
* @author Christian Laireiter
*/
private final static class DescriptorPointer {
/**
* The represented descriptor.
*/
private MetadataDescriptor desc;
/**
* Creates an instance.
*
* @param descriptor the metadata descriptor to identify.
*/
public DescriptorPointer(final MetadataDescriptor descriptor) {
setDescriptor(descriptor);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(final Object obj) {
boolean result = obj == this;
if (obj instanceof DescriptorPointer && !result) {
final MetadataDescriptor other = ((DescriptorPointer) obj).desc;
result = this.desc.getName().equals(other.getName());
result &= this.desc.getLanguageIndex() == other
.getLanguageIndex();
result &= this.desc.getStreamNumber() == other
.getStreamNumber();
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
int hashCode;
hashCode = this.desc.getName().hashCode();
hashCode = hashCode * 31 + this.desc.getLanguageIndex();
hashCode = hashCode * 31 + this.desc.getStreamNumber();
return hashCode;
}
/**
* Sets the descriptor to identify.
*
* @param descriptor the descriptor to identify.
* @return this instance.
*/
protected DescriptorPointer setDescriptor(
final MetadataDescriptor descriptor) {
assert descriptor != null;
this.desc = descriptor;
return this;
}
}
}

View file

@ -1,86 +0,0 @@
package org.jaudiotagger.audio.asf.data;
import java.math.BigInteger;
/**
* A factory for creating appropriate {@link MetadataContainer} objects upon
* specified {@linkplain ContainerType container types}.<br>
*
* @author Christian Laireiter
*/
public final class MetadataContainerFactory {
/**
* Factory instance.
*/
private final static MetadataContainerFactory INSTANCE = new MetadataContainerFactory();
/**
* Hidden utility class constructor.
*/
private MetadataContainerFactory() {
// Hidden
}
/**
* Returns an instance.
*
* @return an instance.
*/
public static MetadataContainerFactory getInstance() {
return INSTANCE;
}
/**
* Creates an appropriate {@linkplain MetadataContainer container
* implementation} for the given container type.
*
* @param type the type of container to get a container instance for.
* @return appropriate container implementation.
*/
public MetadataContainer createContainer(final ContainerType type) {
return createContainer(type, 0, BigInteger.ZERO);
}
/**
* Convenience Method for I/O. Same as
* {@link #createContainer(ContainerType)}, but additionally assigns
* position and size. (since a {@link MetadataContainer} is actually a
* {@link Chunk}).
*
* @param type The containers type.
* @param pos the position within the stream.
* @param chunkSize the size of the container.
* @return an appropriate container implementation with assigned size and
* position.
*/
public MetadataContainer createContainer(final ContainerType type,
final long pos, final BigInteger chunkSize) {
MetadataContainer result;
if (type == ContainerType.CONTENT_DESCRIPTION) {
result = new ContentDescription(pos, chunkSize);
} else if (type == ContainerType.CONTENT_BRANDING) {
result = new ContentBranding(pos, chunkSize);
} else {
result = new MetadataContainer(type, pos, chunkSize);
}
return result;
}
/**
* Convenience method which calls {@link #createContainer(ContainerType)}
* for each given container type.
*
* @param types types of the container which are to be created.
* @return appropriate container implementations.
*/
public MetadataContainer[] createContainers(final ContainerType[] types) {
assert types != null;
final MetadataContainer[] result = new MetadataContainer[types.length];
for (int i = 0; i < result.length; i++) {
result[i] = createContainer(types[i]);
}
return result;
}
}

View file

@ -1,877 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import org.jaudiotagger.logging.ErrorMessage;
import org.jaudiotagger.tag.TagOptionSingleton;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.logging.Logger;
/**
* This structure represents metadata objects in ASF {@link MetadataContainer}.<br>
* The values are
* {@linkplain ContainerType#assertConstraints(String, byte[], int, int, int)
* checked} against the capability introduced by the given
* {@link ContainerType} at construction.<br>
* <br>
* <b>Limitation</b>: Even though some container types do not restrict the data
* size to {@link Integer#MAX_VALUE}, this implementation does it (due to java
* nature).<br>
* 2 GiB of data should suffice, and even be to large for normal java heap.
*
* @author Christian Laireiter
*/
public class MetadataDescriptor implements Comparable<MetadataDescriptor>,
Cloneable {
/**
* Maximum value for WORD.
*/
public static final long DWORD_MAXVALUE = new BigInteger("FFFFFFFF", 16)
.longValue();
/**
* The maximum language index allowed. (exclusive)
*/
public static final int MAX_LANG_INDEX = 127;
/**
* Maximum stream number. (inclusive)
*/
public static final int MAX_STREAM_NUMBER = 127;
/**
* Maximum value for a QWORD value (64 bit unsigned).<br>
*/
public static final BigInteger QWORD_MAXVALUE = new BigInteger(
"FFFFFFFFFFFFFFFF", 16);
/**
* Constant for the metadata descriptor-type for binary data.
*/
public final static int TYPE_BINARY = 1;
/**
* Constant for the metadata descriptor-type for booleans.
*/
public final static int TYPE_BOOLEAN = 2;
/**
* Constant for the metadata descriptor-type for DWORD (32-bit unsigned). <br>
*/
public final static int TYPE_DWORD = 3;
/**
* Constant for the metadata descriptor-type for GUIDs (128-bit).<br>
*/
public final static int TYPE_GUID = 6;
/**
* Constant for the metadata descriptor-type for QWORD (64-bit unsinged). <br>
*/
public final static int TYPE_QWORD = 4;
/**
* Constant for the metadata descriptor-type for Strings.
*/
public final static int TYPE_STRING = 0;
/**
* Constant for the metadata descriptor-type for WORD (16-bit unsigned). <br>
*/
public final static int TYPE_WORD = 5;
/**
* Maximum value for WORD.
*/
public static final int WORD_MAXVALUE = 65535;
/**
* Logger instance.
*/
private static final Logger LOGGER = Logger
.getLogger("org.jaudiotagger.audio.asf.data");
/**
* Stores the containerType of the descriptor.
*/
private final ContainerType containerType;
/**
* The name of the metadata descriptor.
*/
private final String name;
/**
* The binary representation of the value.
*/
/*
* Note: The maximum data length could be up to a 64-Bit number (unsigned),
* but java for now handles just int sized byte[]. Since this class stores
* all data in primitive byte[] this size restriction is cascaded to all
* dependent implementations.
*/
private byte[] content = new byte[0];
/**
* This field shows the type of the metadata descriptor. <br>
*
* @see #TYPE_BINARY
* @see #TYPE_BOOLEAN
* @see #TYPE_DWORD
* @see #TYPE_GUID
* @see #TYPE_QWORD
* @see #TYPE_STRING
* @see #TYPE_WORD
*/
private int descriptorType;
/**
* the index of the language in the {@linkplain LanguageList language list}
* this descriptor applies to.<br>
*/
private int languageIndex = 0;
/**
* The number of the stream, this descriptor applies to.<br>
*/
private int streamNumber = 0;
/**
* Creates an Instance.<br>
*
* @param type the container type, this descriptor is resctricted to.
* @param propName Name of the MetadataDescriptor.
* @param propType Type of the metadata descriptor. See {@link #descriptorType}
*/
public MetadataDescriptor(final ContainerType type, final String propName,
final int propType) {
this(type, propName, propType, 0, 0);
}
/**
* Creates an Instance.
*
* @param type The container type the values (the whole descriptor) is
* restricted to.
* @param propName Name of the MetadataDescriptor.
* @param propType Type of the metadata descriptor. See {@link #descriptorType}
* @param stream the number of the stream the descriptor refers to.
* @param language the index of the language entry in a {@link LanguageList} this
* descriptor refers to.<br>
* <b>Consider</b>: No checks performed if language entry exists.
*/
public MetadataDescriptor(final ContainerType type, final String propName,
final int propType, final int stream, final int language) {
assert type != null;
type.assertConstraints(propName, new byte[0], propType, stream,
language);
this.containerType = type;
this.name = propName;
this.descriptorType = propType;
this.streamNumber = stream;
this.languageIndex = language;
}
/**
* Creates an instance.<br>
* Capabilities are set to {@link ContainerType#METADATA_LIBRARY_OBJECT}.<br>
*
* @param propName name of the metadata descriptor.
*/
public MetadataDescriptor(final String propName) {
this(propName, TYPE_STRING);
}
/**
* Creates an Instance.<br>
* Capabilities are set to {@link ContainerType#METADATA_LIBRARY_OBJECT}.<br>
*
* @param propName Name of the MetadataDescriptor.
* @param propType Type of the metadata descriptor. See {@link #descriptorType}
*/
public MetadataDescriptor(final String propName, final int propType) {
this(ContainerType.METADATA_LIBRARY_OBJECT, propName, propType, 0, 0);
}
/**
* Converts the descriptors value into a number if possible.<br>
* A boolean will be converted to &quot;1&quot; if <code>true</code>,
* otherwise &quot;0&quot;.<br>
* String will be interpreted as number with radix &quot;10&quot;.<br>
* Binary data will be interpreted as the default WORD,DWORD or QWORD binary
* representation, but only if the data does not exceed 8 bytes. This
* precaution is done to prevent creating a number of a multi kilobyte
* image.<br>
* A GUID cannot be converted in any case.
*
* @return number representation.
* @throws NumberFormatException If no conversion is supported.
*/
public BigInteger asNumber() {
BigInteger result = null;
switch (this.descriptorType) {
case TYPE_BOOLEAN:
case TYPE_WORD:
case TYPE_DWORD:
case TYPE_QWORD:
case TYPE_BINARY:
if (this.content.length > 8) {
throw new NumberFormatException(
"Binary data would exceed QWORD");
}
break;
case TYPE_GUID:
throw new NumberFormatException(
"GUID cannot be converted to a number.");
case TYPE_STRING:
result = new BigInteger(getString(), 10);
break;
default:
throw new IllegalStateException();
}
if (result == null) {
final byte[] copy = new byte[this.content.length];
for (int i = 0; i < copy.length; i++) {
copy[i] = this.content[this.content.length - (i + 1)];
}
result = new BigInteger(1, copy);
}
return result;
}
/**
* (overridden)
*
* @see java.lang.Object#clone()
*/
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* {@inheritDoc}
*/
public int compareTo(final MetadataDescriptor other) {
return getName().compareTo(other.getName());
}
/**
* This method creates a copy of the current object. <br>
* All data will be copied, too. <br>
*
* @return A new metadata descriptor containing the same values as the
* current one.
*/
public MetadataDescriptor createCopy() {
final MetadataDescriptor result = new MetadataDescriptor(
this.containerType, this.name, this.descriptorType,
this.streamNumber, this.languageIndex);
result.content = getRawData();
return result;
}
/**
* (overridden)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
boolean result = false;
if (obj instanceof MetadataDescriptor) {
if (obj == this) {
result = true;
} else {
final MetadataDescriptor other = (MetadataDescriptor) obj;
result = other.getName().equals(getName())
&& other.descriptorType == this.descriptorType
&& other.languageIndex == this.languageIndex
&& other.streamNumber == this.streamNumber
&& Arrays.equals(this.content, other.content);
}
}
return result;
}
/**
* Returns the value of the MetadataDescriptor as a Boolean. <br>
* If no Conversion is Possible false is returned. <br>
* <code>true</code> if first byte of {@link #content}is not zero.
*
* @return boolean representation of the current value.
*/
public boolean getBoolean() {
return this.content.length > 0 && this.content[0] != 0;
}
/**
* This method will return a byte array, which can directly be written into
* an "Extended Content Description"-chunk. <br>
*
* @return byte[] with the data, that occurs in ASF files.
* @deprecated {@link #writeInto(OutputStream, ContainerType)} is used
*/
@Deprecated
public byte[] getBytes() {
final ByteArrayOutputStream result = new ByteArrayOutputStream();
try {
writeInto(result, this.containerType);
} catch (final IOException e) {
LOGGER.warning(e.getMessage());
}
return result.toByteArray();
}
/**
* Returns the container type this descriptor ist restricted to.
*
* @return the container type
*/
public ContainerType getContainerType() {
return this.containerType;
}
/**
* Returns the size (in bytes) this descriptor will take when written to an
* ASF file.<br>
*
* @param type the container type for which the size is calculated.
* @return size of the descriptor in an ASF file.
*/
public int getCurrentAsfSize(final ContainerType type) {
/*
* 2 bytes name length, 2 bytes name zero term, 2 bytes type, 2 bytes
* content length
*/
int result = 8;
if (type != ContainerType.EXTENDED_CONTENT) {
// Stream number and language index (respectively reserved field).
// And +2 bytes, because data type is 32 bit, not 16
result += 6;
}
result += getName().length() * 2;
if (this.getType() == TYPE_BOOLEAN) {
result += 2;
if (type == ContainerType.EXTENDED_CONTENT) {
// Extended content description boolean values are stored with
// 32-bit
result += 2;
}
} else {
result += this.content.length;
if (TYPE_STRING == this.getType()) {
result += 2; // zero term of content string.
}
}
return result;
}
/**
* Returns the GUID value, if content could represent one.
*
* @return GUID value
*/
public GUID getGuid() {
GUID result = null;
if (getType() == TYPE_GUID && this.content.length == GUID.GUID_LENGTH) {
result = new GUID(this.content);
}
return result;
}
/**
* Returns the index of the language that is referred (see
* {@link LanguageList}):
*
* @return the language index
*/
public int getLanguageIndex() {
return this.languageIndex;
}
/**
* Sets the index of the referred language (see {@link LanguageList}).<br>
* <b>Consider</b>: The {@linkplain #containerType requirements} must be
* held.
*
* @param language the language index to set
*/
public void setLanguageIndex(final int language) {
this.containerType.assertConstraints(this.name, this.content,
this.descriptorType, this.streamNumber, language);
this.languageIndex = language;
}
/**
* This method returns the name of the metadata descriptor.
*
* @return Name.
*/
public String getName() {
return this.name;
}
/**
* This method returns the value of the metadata descriptor as a long. <br>
* Converts the needed amount of byte out of {@link #content}to a number. <br>
* Only possible if {@link #getType()}equals on of the following: <br>
* <li>
*
* @return integer value.
* @see #TYPE_BOOLEAN </li> <li>
* @see #TYPE_DWORD </li> <li>
* @see #TYPE_QWORD </li> <li>
* @see #TYPE_WORD </li>
*/
public long getNumber() {
int bytesNeeded;
switch (getType()) {
case TYPE_BOOLEAN:
bytesNeeded = 1;
break;
case TYPE_DWORD:
bytesNeeded = 4;
break;
case TYPE_QWORD:
bytesNeeded = 8;
break;
case TYPE_WORD:
bytesNeeded = 2;
break;
default:
throw new UnsupportedOperationException(
"The current type doesn't allow an interpretation as a number. ("
+ getType() + ")");
}
if (bytesNeeded > this.content.length) {
throw new IllegalStateException(
"The stored data cannot represent the type of current object.");
}
long result = 0;
for (int i = 0; i < bytesNeeded; i++) {
result |= (((long) this.content[i] & 0xFF) << (i * 8));
}
return result;
}
/**
* This method returns a copy of the content of the descriptor. <br>
*
* @return The content in binary representation, as it would be written to
* asf file. <br>
*/
public byte[] getRawData() {
final byte[] copy = new byte[this.content.length];
System.arraycopy(this.content, 0, copy, 0, this.content.length);
return copy;
}
/**
* Returns the size (in bytes) the binary representation of the content
* uses. (length of {@link #getRawData()})<br>
*
* @return size of binary representation of the content.
*/
public int getRawDataSize() {
return this.content.length;
}
/**
* Returns the stream number this descriptor applies to.<br>
*
* @return the stream number.
*/
public int getStreamNumber() {
return this.streamNumber;
}
/**
* Sets the stream number the descriptor applies to.<br>
* <b>Consider</b>: The {@linkplain #containerType requirements} must be
* held.
*
* @param stream the stream number to set
*/
public void setStreamNumber(final int stream) {
this.containerType.assertConstraints(this.name, this.content,
this.descriptorType, stream, this.languageIndex);
this.streamNumber = stream;
}
/**
* Returns the value of the MetadataDescriptor as a String. <br>
*
* @return String - Representation Value
*/
public String getString() {
String result = null;
switch (getType()) {
case TYPE_BINARY:
result = "binary data";
break;
case TYPE_BOOLEAN:
result = String.valueOf(getBoolean());
break;
case TYPE_GUID:
result = getGuid() == null ? "Invalid GUID" : getGuid().toString();
break;
case TYPE_QWORD:
case TYPE_DWORD:
case TYPE_WORD:
result = String.valueOf(getNumber());
break;
case TYPE_STRING:
try {
result = new String(this.content, "UTF-16LE");
} catch (final UnsupportedEncodingException e) {
LOGGER.warning(e.getMessage());
}
break;
default:
throw new IllegalStateException("Current type is not known.");
}
return result;
}
/**
* This method converts the given string value into the current
* {@linkplain #getType() data type}.
*
* @param value value to set.
* @throws IllegalArgumentException If conversion was impossible.
*/
public void setString(final String value)
throws IllegalArgumentException {
try {
switch (getType()) {
case TYPE_BINARY:
throw new IllegalArgumentException(
"Cannot interpret binary as string.");
case TYPE_BOOLEAN:
setBooleanValue(Boolean.parseBoolean(value));
break;
case TYPE_DWORD:
setDWordValue(Long.parseLong(value));
break;
case TYPE_QWORD:
setQWordValue(new BigInteger(value, 10));
break;
case TYPE_WORD:
setWordValue(Integer.parseInt(value));
break;
case TYPE_GUID:
setGUIDValue(GUID.parseGUID(value));
break;
case TYPE_STRING:
setStringValue(value);
break;
default:
// new Type added but not handled.
throw new IllegalStateException();
}
} catch (final NumberFormatException nfe) {
throw new IllegalArgumentException(
"Value cannot be parsed as Number or is out of range (\""
+ value + "\")", nfe);
}
}
/**
* Returns the type of the metadata descriptor. <br>
*
* @return the value of {@link #descriptorType}
* @see #TYPE_BINARY
* @see #TYPE_BOOLEAN
* @see #TYPE_DWORD
* @see #TYPE_GUID
* @see #TYPE_QWORD
* @see #TYPE_STRING
* @see #TYPE_WORD
*/
public int getType() {
return this.descriptorType;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return this.name.hashCode();
}
/**
* This method checks if the binary data is empty. <br>
* Disregarding the type of the descriptor its content is stored as a byte
* array.
*
* @return <code>true</code> if no value is set.
*/
public boolean isEmpty() {
return this.content.length == 0;
}
/**
* Sets the Value of the current metadata descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_BINARY}.<br>
*
* @param data Value to set.
* @throws IllegalArgumentException if data is invalid for {@linkplain #getContainerType()
* container}.
*/
public void setBinaryValue(final byte[] data)
throws IllegalArgumentException {
this.containerType.assertConstraints(this.name, data,
this.descriptorType, this.streamNumber, this.languageIndex);
this.content = data.clone();
this.descriptorType = TYPE_BINARY;
}
/**
* Sets the Value of the current metadata descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_BOOLEAN}.<br>
*
* @param value Value to set.
*/
public void setBooleanValue(final boolean value) {
this.content = new byte[]{value ? (byte) 1 : 0};
this.descriptorType = TYPE_BOOLEAN;
}
/**
* Sets the Value of the current metadata descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_DWORD}.
*
* @param value Value to set.
*/
public void setDWordValue(final long value) {
if (value < 0 || value > DWORD_MAXVALUE) {
throw new IllegalArgumentException("value out of range (0-"
+ DWORD_MAXVALUE + ")");
}
this.content = Utils.getBytes(value, 4);
this.descriptorType = TYPE_DWORD;
}
/**
* Sets the value of the metadata descriptor.<br>
* Using this method will change {@link #descriptorType} to
* {@link #TYPE_GUID}
*
* @param value value to set.
*/
public void setGUIDValue(final GUID value) {
this.containerType.assertConstraints(this.name, value.getBytes(),
TYPE_GUID, this.streamNumber, this.languageIndex);
this.content = value.getBytes();
this.descriptorType = TYPE_GUID;
}
/**
* Sets the Value of the current metadata descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_QWORD}
*
* @param value Value to set.
* @throws NumberFormatException on <code>null</code> values.
* @throws IllegalArgumentException on illegal values or values exceeding range.
*/
public void setQWordValue(final BigInteger value)
throws IllegalArgumentException {
if (value == null) {
throw new NumberFormatException("null");
}
if (BigInteger.ZERO.compareTo(value) > 0) {
throw new IllegalArgumentException(
"Only unsigned values allowed (no negative)");
}
if (MetadataDescriptor.QWORD_MAXVALUE.compareTo(value) < 0) {
throw new IllegalArgumentException(
"Value exceeds QWORD (64 bit unsigned)");
}
this.content = new byte[8];
final byte[] valuesBytes = value.toByteArray();
if (valuesBytes.length <= 8) {
for (int i = valuesBytes.length - 1; i >= 0; i--) {
this.content[valuesBytes.length - (i + 1)] = valuesBytes[i];
}
} else {
/*
* In case of 64-Bit set
*/
Arrays.fill(this.content, (byte) 0xFF);
}
this.descriptorType = TYPE_QWORD;
}
/**
* Sets the Value of the current metadata descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_QWORD}
*
* @param value Value to set.
*/
public void setQWordValue(final long value) {
if (value < 0) {
throw new IllegalArgumentException("value out of range (0-"
+ MetadataDescriptor.QWORD_MAXVALUE.toString() + ")");
}
this.content = Utils.getBytes(value, 8);
this.descriptorType = TYPE_QWORD;
}
/**
* Sets the Value of the current metadata descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_STRING}.
*
* @param value Value to set.
* @throws IllegalArgumentException If byte representation would take more than 65535 Bytes.
*/
// TODO Test
public void setStringValue(final String value)
throws IllegalArgumentException {
if (value == null) {
this.content = new byte[0];
} else {
final byte[] tmp = Utils.getBytes(value, AsfHeader.ASF_CHARSET);
if (getContainerType().isWithinValueRange(tmp.length)) {
// Everything is fine here, data can be stored.
this.content = tmp;
} else {
// Normally a size violation, check if JAudiotagger my truncate
// the string
if (TagOptionSingleton.getInstance()
.isTruncateTextWithoutErrors()) {
// truncate the string
final int copyBytes = (int) getContainerType()
.getMaximumDataLength().longValue();
this.content = new byte[copyBytes % 2 == 0 ? copyBytes
: copyBytes - 1];
System.arraycopy(tmp, 0, this.content, 0,
this.content.length);
} else {
// We may not truncate, so its an error
throw new IllegalArgumentException(
ErrorMessage.WMA_LENGTH_OF_DATA_IS_TOO_LARGE
.getMsg(tmp.length, getContainerType()
.getMaximumDataLength(),
getContainerType()
.getContainerGUID()
.getDescription()));
}
}
}
this.descriptorType = TYPE_STRING;
}
/**
* Sets the Value of the current metadata descriptor. <br>
* Using this method will change {@link #descriptorType}to
* {@link #TYPE_WORD}
*
* @param value Value to set.
* @throws IllegalArgumentException on negative values. ASF just supports unsigned values.
*/
public void setWordValue(final int value)
throws IllegalArgumentException {
if (value < 0 || value > WORD_MAXVALUE) {
throw new IllegalArgumentException("value out of range (0-"
+ WORD_MAXVALUE + ")");
}
this.content = Utils.getBytes(value, 2);
this.descriptorType = TYPE_WORD;
}
/**
* (overridden)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return getName()
+ " : "
+ new String[]{"String: ", "Binary: ", "Boolean: ",
"DWORD: ", "QWORD:", "WORD:", "GUID:"}[this.descriptorType]
+ getString() + " (language: " + this.languageIndex
+ " / stream: " + this.streamNumber + ")";
}
/**
* Writes this descriptor into the specified output stream.<br>
*
* @param out stream to write into.
* @param contType the container type this descriptor is written to.
* @return amount of bytes written.
* @throws IOException on I/O Errors
*/
public int writeInto(final OutputStream out,
final ContainerType contType) throws IOException {
final int size = getCurrentAsfSize(contType);
/*
* Booleans are stored as one byte, if a boolean is written, the data
* must be converted according to the container type.
*/
byte[] binaryData;
if (this.descriptorType == TYPE_BOOLEAN) {
binaryData = new byte[contType == ContainerType.EXTENDED_CONTENT ? 4
: 2];
binaryData[0] = (byte) (getBoolean() ? 1 : 0);
} else {
binaryData = this.content;
}
// for Metadata objects the stream number and language index
if (contType != ContainerType.EXTENDED_CONTENT) {
Utils.writeUINT16(getLanguageIndex(), out);
Utils.writeUINT16(getStreamNumber(), out);
}
Utils.writeUINT16(getName().length() * 2 + 2, out);
// The name for the metadata objects come later
if (contType == ContainerType.EXTENDED_CONTENT) {
out.write(Utils.getBytes(getName(), AsfHeader.ASF_CHARSET));
out.write(AsfHeader.ZERO_TERM);
}
// type and content len follow up are identical
final int type = getType();
Utils.writeUINT16(type, out);
int contentLen = binaryData.length;
if (TYPE_STRING == type) {
contentLen += 2; // Zero Term
}
if (contType == ContainerType.EXTENDED_CONTENT) {
Utils.writeUINT16(contentLen, out);
} else {
Utils.writeUINT32(contentLen, out);
}
// Metadata objects now write their descriptor name
if (contType != ContainerType.EXTENDED_CONTENT) {
out.write(Utils.getBytes(getName(), AsfHeader.ASF_CHARSET));
out.write(AsfHeader.ZERO_TERM);
}
// The content.
out.write(binaryData);
if (TYPE_STRING == type) {
out.write(AsfHeader.ZERO_TERM);
}
return size;
}
}

View file

@ -1,107 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
/**
* This class represents the "Stream Bitrate Properties" chunk of an ASF media
* file. <br>
* It is optional, but contains useful information about the streams bitrate.<br>
*
* @author Christian Laireiter
*/
public class StreamBitratePropertiesChunk extends Chunk {
/**
* For each call of {@link #addBitrateRecord(int, long)} an {@link Long}
* object is appended, which represents the average bitrate.
*/
private final List<Long> bitRates;
/**
* For each call of {@link #addBitrateRecord(int, long)} an {@link Integer}
* object is appended, which represents the stream-number.
*/
private final List<Integer> streamNumbers;
/**
* Creates an instance.
*
* @param chunkLen Length of current chunk.
*/
public StreamBitratePropertiesChunk(final BigInteger chunkLen) {
super(GUID.GUID_STREAM_BITRATE_PROPERTIES, chunkLen);
this.bitRates = new ArrayList<Long>();
this.streamNumbers = new ArrayList<Integer>();
}
/**
* Adds the public values of a stream-record.
*
* @param streamNum The number of the referred stream.
* @param averageBitrate Its average bitrate.
*/
public void addBitrateRecord(final int streamNum, final long averageBitrate) {
this.streamNumbers.add(streamNum);
this.bitRates.add(averageBitrate);
}
/**
* Returns the average bitrate of the given stream.<br>
*
* @param streamNumber Number of the stream whose bitrate to determine.
* @return The average bitrate of the numbered stream. <code>-1</code> if no
* information was given.
*/
public long getAvgBitrate(final int streamNumber) {
final Integer seach = streamNumber;
final int index = this.streamNumbers.indexOf(seach);
long result;
if (index == -1) {
result = -1;
} else {
result = this.bitRates.get(index);
}
return result;
}
/**
* (overridden)
*
* @see org.jaudiotagger.audio.asf.data.Chunk#prettyPrint(String)
*/
@Override
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder(super.prettyPrint(prefix));
for (int i = 0; i < this.bitRates.size(); i++) {
result.append(prefix).append(" |-> Stream no. \"").append(
this.streamNumbers.get(i)).append(
"\" has an average bitrate of \"").append(
this.bitRates.get(i)).append('"').append(
Utils.LINE_SEPARATOR);
}
return result.toString();
}
}

View file

@ -1,180 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.math.BigInteger;
/**
* This class is the base for all handled stream contents. <br>
* A Stream chunk delivers information about a audio or video stream. Because of
* this the stream chunk identifies in one field what type of stream it is
* describing and so other data is provided. However some information is common
* to all stream chunks which are stored in this hierarchy of the class tree.
*
* @author Christian Laireiter
*/
public abstract class StreamChunk extends Chunk {
/**
* Stores the stream type.<br>
*
* @see GUID#GUID_AUDIOSTREAM
* @see GUID#GUID_VIDEOSTREAM
*/
private final GUID type;
/**
* If <code>true</code>, the stream data is encrypted.
*/
private boolean contentEncrypted;
/**
* This field stores the number of the current stream. <br>
*/
private int streamNumber;
/**
* @see #typeSpecificDataSize
*/
private long streamSpecificDataSize;
/**
* Something technical. <br>
* Format time in 100-ns steps.
*/
private long timeOffset;
/**
* Stores the size of type specific data structure within chunk.
*/
private long typeSpecificDataSize;
/**
* Creates an instance
*
* @param streamType The GUID which tells the stream type represented (
* {@link GUID#GUID_AUDIOSTREAM} or {@link GUID#GUID_VIDEOSTREAM}
* ):
* @param chunkLen length of chunk
*/
public StreamChunk(final GUID streamType, final BigInteger chunkLen) {
super(GUID.GUID_STREAM, chunkLen);
assert GUID.GUID_AUDIOSTREAM.equals(streamType)
|| GUID.GUID_VIDEOSTREAM.equals(streamType);
this.type = streamType;
}
/**
* @return Returns the streamNumber.
*/
public int getStreamNumber() {
return this.streamNumber;
}
/**
* @param streamNum The streamNumber to set.
*/
public void setStreamNumber(final int streamNum) {
this.streamNumber = streamNum;
}
/**
* @return Returns the streamSpecificDataSize.
*/
public long getStreamSpecificDataSize() {
return this.streamSpecificDataSize;
}
/**
* @param strSpecDataSize The streamSpecificDataSize to set.
*/
public void setStreamSpecificDataSize(final long strSpecDataSize) {
this.streamSpecificDataSize = strSpecDataSize;
}
/**
* Returns the stream type of the stream chunk.<br>
*
* @return {@link GUID#GUID_AUDIOSTREAM} or {@link GUID#GUID_VIDEOSTREAM}.
*/
public GUID getStreamType() {
return this.type;
}
/**
* @return Returns the timeOffset.
*/
public long getTimeOffset() {
return this.timeOffset;
}
/**
* @param timeOffs sets the time offset
*/
public void setTimeOffset(final long timeOffs) {
this.timeOffset = timeOffs;
}
/**
* @return Returns the typeSpecificDataSize.
*/
public long getTypeSpecificDataSize() {
return this.typeSpecificDataSize;
}
/**
* @param typeSpecDataSize The typeSpecificDataSize to set.
*/
public void setTypeSpecificDataSize(final long typeSpecDataSize) {
this.typeSpecificDataSize = typeSpecDataSize;
}
/**
* @return Returns the contentEncrypted.
*/
public boolean isContentEncrypted() {
return this.contentEncrypted;
}
/**
* @param cntEnc The contentEncrypted to set.
*/
public void setContentEncrypted(final boolean cntEnc) {
this.contentEncrypted = cntEnc;
}
/**
* (overridden)
*
* @see org.jaudiotagger.audio.asf.data.Chunk#prettyPrint(String)
*/
@Override
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder(super.prettyPrint(prefix));
result.append(prefix).append(" |-> Stream number: ").append(
getStreamNumber()).append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |-> Type specific data size : ")
.append(getTypeSpecificDataSize()).append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |-> Stream specific data size: ")
.append(getStreamSpecificDataSize()).append(
Utils.LINE_SEPARATOR);
result.append(prefix).append(" |-> Time Offset : ")
.append(getTimeOffset()).append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |-> Content Encryption : ")
.append(isContentEncrypted()).append(Utils.LINE_SEPARATOR);
return result.toString();
}
}

View file

@ -1,131 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.data;
import org.jaudiotagger.audio.asf.util.Utils;
import java.math.BigInteger;
/**
* @author Christian Laireiter
*/
public class VideoStreamChunk extends StreamChunk {
/**
* Stores the codecs id. Normally the Four-CC (4-Bytes).
*/
private byte[] codecId = new byte[0];
/**
* This field stores the height of the video stream.
*/
private long pictureHeight;
/**
* This field stores the width of the video stream.
*/
private long pictureWidth;
/**
* Creates an instance.
*
* @param chunkLen Length of the entire chunk (including guid and size)
*/
public VideoStreamChunk(final BigInteger chunkLen) {
super(GUID.GUID_VIDEOSTREAM, chunkLen);
}
/**
* @return Returns the codecId.
*/
public byte[] getCodecId() {
return this.codecId.clone();
}
/**
* @param codecIdentifier The codecId to set.
*/
public void setCodecId(final byte[] codecIdentifier) {
this.codecId = codecIdentifier.clone();
}
/**
* Returns the {@link #getCodecId()}, as a String, where each byte has been
* converted to a <code>char</code>.
*
* @return Codec Id as String.
*/
public String getCodecIdAsString() {
String result;
if (this.codecId == null) {
result = "Unknown";
} else {
result = new String(getCodecId());
}
return result;
}
/**
* @return Returns the pictureHeight.
*/
public long getPictureHeight() {
return this.pictureHeight;
}
/**
* @param picHeight
*/
public void setPictureHeight(final long picHeight) {
this.pictureHeight = picHeight;
}
/**
* @return Returns the pictureWidth.
*/
public long getPictureWidth() {
return this.pictureWidth;
}
/**
* @param picWidth
*/
public void setPictureWidth(final long picWidth) {
this.pictureWidth = picWidth;
}
/**
* (overridden)
*
* @see org.jaudiotagger.audio.asf.data.StreamChunk#prettyPrint(String)
*/
@Override
public String prettyPrint(final String prefix) {
final StringBuilder result = new StringBuilder(super.prettyPrint(prefix));
result.insert(0, Utils.LINE_SEPARATOR + prefix + "|->VideoStream");
result.append(prefix).append("Video info:")
.append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |->Width : ").append(
getPictureWidth()).append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |->Heigth : ").append(
getPictureHeight()).append(Utils.LINE_SEPARATOR);
result.append(prefix).append(" |->Codec : ").append(
getCodecIdAsString()).append(Utils.LINE_SEPARATOR);
return result.toString();
}
}

View file

@ -1,19 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html long="en">
<head>
</head>
<body bgcolor="white">
Classes for data components of the Microsoft Advanced Systems Format header.
<br>
<!-- package.html by Gary McGath -->
<!-- Put @see and @since tags down here. -->
</body>
</html>

View file

@ -1,135 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* This modifier manipulates an ASF header extension object.
*
* @author Christian Laireiter
*/
public class AsfExtHeaderModifier implements ChunkModifier {
/**
* List of modifiers which are to be applied to contained chunks.
*/
private final List<ChunkModifier> modifierList;
/**
* Creates an instance.<br>
*
* @param modifiers modifiers to apply.
*/
public AsfExtHeaderModifier(final List<ChunkModifier> modifiers) {
assert modifiers != null;
this.modifierList = new ArrayList<ChunkModifier>(modifiers);
}
/**
* Simply copies a chunk from <code>source</code> to
* <code>destination</code>.<br>
* The method assumes, that the GUID has already been read and will write
* the provided one to the destination.<br>
* The chunk length however will be read and used to determine the amount of
* bytes to copy.
*
* @param guid GUID of the current CHUNK.
* @param source source of an ASF chunk, which is to be located at the chunk
* length field.
* @param destination the destination to copy the chunk to.
* @throws IOException on I/O errors.
*/
private void copyChunk(final GUID guid, final InputStream source,
final OutputStream destination) throws IOException {
final long chunkSize = Utils.readUINT64(source);
destination.write(guid.getBytes());
Utils.writeUINT64(chunkSize, destination);
Utils.copy(source, destination, chunkSize - 24);
}
/**
* {@inheritDoc}
*/
public boolean isApplicable(final GUID guid) {
return GUID.GUID_HEADER_EXTENSION.equals(guid);
}
/**
* {@inheritDoc}
*/
public ModificationResult modify(final GUID guid, final InputStream source,
final OutputStream destination) throws IOException {
assert GUID.GUID_HEADER_EXTENSION.equals(guid);
long difference = 0;
final List<ChunkModifier> modders = new ArrayList<ChunkModifier>(
this.modifierList);
final Set<GUID> occuredGuids = new HashSet<GUID>();
occuredGuids.add(guid);
final BigInteger chunkLen = Utils.readBig64(source);
final GUID reserved1 = Utils.readGUID(source);
final int reserved2 = Utils.readUINT16(source);
final long dataSize = Utils.readUINT32(source);
assert dataSize == 0 || dataSize >= 24;
assert chunkLen.subtract(BigInteger.valueOf(46)).longValue() == dataSize;
/*
* Stream buffer for the chunk list
*/
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
/*
* Stream which counts read bytes. Dirty but quick way of implementing
* this.
*/
final CountingInputStream cis = new CountingInputStream(source);
while (cis.getReadCount() < dataSize) {
// read GUID
final GUID curr = Utils.readGUID(cis);
boolean handled = false;
for (int i = 0; i < modders.size() && !handled; i++) {
if (modders.get(i).isApplicable(curr)) {
final ModificationResult modRes = modders.get(i).modify(
curr, cis, bos);
difference += modRes.getByteDifference();
occuredGuids.addAll(modRes.getOccuredGUIDs());
modders.remove(i);
handled = true;
}
}
if (!handled) {
occuredGuids.add(curr);
copyChunk(curr, cis, bos);
}
}
// Now apply the left modifiers.
for (final ChunkModifier curr : modders) {
// chunks, which were not in the source file, will be added to the
// destination
final ModificationResult result = curr.modify(null, null, bos);
difference += result.getByteDifference();
occuredGuids.addAll(result.getOccuredGUIDs());
}
destination.write(GUID.GUID_HEADER_EXTENSION.getBytes());
Utils.writeUINT64(chunkLen.add(BigInteger.valueOf(difference))
.longValue(), destination);
destination.write(reserved1.getBytes());
Utils.writeUINT16(reserved2, destination);
Utils.writeUINT32(dataSize + difference, destination);
destination.write(bos.toByteArray());
return new ModificationResult(0, difference, occuredGuids);
}
}

View file

@ -1,71 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.AsfExtendedHeader;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.List;
/**
* This reader reads an ASF header extension object from an {@link InputStream}
* and creates an {@link AsfExtendedHeader} object.<br>
*
* @author Christian Laireiter
*/
public class AsfExtHeaderReader extends ChunkContainerReader<AsfExtendedHeader> {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {GUID.GUID_HEADER_EXTENSION};
/**
* Creates a reader instance, which only utilizes the given list of chunk
* readers.<br>
*
* @param toRegister List of {@link ChunkReader} class instances, which are to be
* utilized by the instance.
* @param readChunkOnce if <code>true</code>, each chunk type (identified by chunk
* GUID) will handled only once, if a reader is available, other
* chunks will be discarded.
*/
public AsfExtHeaderReader(
final List<Class<? extends ChunkReader>> toRegister,
final boolean readChunkOnce) {
super(toRegister, readChunkOnce);
}
/**
* {@inheritDoc}
*/
public boolean canFail() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
protected AsfExtendedHeader createContainer(final long streamPosition,
final BigInteger chunkLength, final InputStream stream)
throws IOException {
Utils.readGUID(stream); // First reserved field (should be a specific
// GUID.
Utils.readUINT16(stream); // Second reserved field (should always be 6)
final long extensionSize = Utils.readUINT32(stream);
assert extensionSize == 0 || extensionSize >= 24;
assert chunkLength.subtract(BigInteger.valueOf(46)).longValue() == extensionSize;
return new AsfExtendedHeader(streamPosition, chunkLength);
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
}

View file

@ -1,231 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.AsfHeader;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.*;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
/**
* This <i>class </i> reads an ASF header out of an input stream an creates an
* {@link org.jaudiotagger.audio.asf.data.AsfHeader} object if successful. <br>
* For now only ASF ver 1.0 is supported, because ver 2.0 seems not to be used
* anywhere. <br>
* ASF headers contains other chunks. As of this other readers of current
* <b>package </b> are called from within.
*
* @author Christian Laireiter
*/
public class AsfHeaderReader extends ChunkContainerReader<AsfHeader> {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {GUID.GUID_HEADER};
/**
* ASF reader configured to extract all information.
*/
private final static AsfHeaderReader FULL_READER;
/**
* ASF reader configured to just extract information about audio streams.<br>
* If the ASF file only contains one audio stream it works fine.<br>
*/
private final static AsfHeaderReader INFO_READER;
/**
* ASF reader configured to just extract metadata information.<br>
*/
private final static AsfHeaderReader TAG_READER;
static {
final List<Class<? extends ChunkReader>> readers = new ArrayList<Class<? extends ChunkReader>>();
readers.add(FileHeaderReader.class);
readers.add(StreamChunkReader.class);
INFO_READER = new AsfHeaderReader(readers, true);
readers.clear();
readers.add(ContentDescriptionReader.class);
readers.add(ContentBrandingReader.class);
readers.add(LanguageListReader.class);
readers.add(MetadataReader.class);
/*
* Create the header extension object readers with just content
* description reader, extended content description reader, language
* list reader and both metadata object readers.
*/
final AsfExtHeaderReader extReader = new AsfExtHeaderReader(readers,
true);
final AsfExtHeaderReader extReader2 = new AsfExtHeaderReader(readers,
true);
TAG_READER = new AsfHeaderReader(readers, true);
TAG_READER.setExtendedHeaderReader(extReader);
readers.add(FileHeaderReader.class);
readers.add(StreamChunkReader.class);
readers.add(EncodingChunkReader.class);
readers.add(EncryptionChunkReader.class);
readers.add(StreamBitratePropertiesReader.class);
FULL_READER = new AsfHeaderReader(readers, false);
FULL_READER.setExtendedHeaderReader(extReader2);
}
/**
* Creates an instance of this reader.
*
* @param toRegister The chunk readers to utilize.
* @param readChunkOnce if <code>true</code>, each chunk type (identified by chunk
* GUID) will handled only once, if a reader is available, other
* chunks will be discarded.
*/
public AsfHeaderReader(final List<Class<? extends ChunkReader>> toRegister,
final boolean readChunkOnce) {
super(toRegister, readChunkOnce);
}
/**
* Creates a Stream that will read from the specified
* {@link RandomAccessFile};<br>
*
* @param raf data source to read from.
* @return a stream which accesses the source.
*/
private static InputStream createStream(final RandomAccessFile raf) {
return new FullRequestInputStream(new BufferedInputStream(
new RandomAccessFileInputstream(raf)));
}
/**
* This method extracts the full ASF-Header from the given file.<br>
* If no header could be extracted <code>null</code> is returned. <br>
*
* @param file the ASF file to read.<br>
* @return AsfHeader-Wrapper, or <code>null</code> if no supported ASF
* header was found.
* @throws IOException on I/O Errors.
*/
public static AsfHeader readHeader(final File file) throws IOException {
final InputStream stream = new FileInputStream(file);
final AsfHeader result = FULL_READER.read(Utils.readGUID(stream),
stream, 0);
stream.close();
return result;
}
/**
* This method tries to extract a full ASF-header out of the given stream. <br>
* If no header could be extracted <code>null</code> is returned. <br>
*
* @param file File which contains the ASF header.
* @return AsfHeader-Wrapper, or <code>null</code> if no supported ASF
* header was found.
* @throws IOException Read errors
*/
public static AsfHeader readHeader(final RandomAccessFile file)
throws IOException {
final InputStream stream = createStream(file);
return FULL_READER.read(Utils.readGUID(stream), stream, 0);
}
/**
* This method tries to extract an ASF-header out of the given stream, which
* only contains information about the audio stream.<br>
* If no header could be extracted <code>null</code> is returned. <br>
*
* @param file File which contains the ASF header.
* @return AsfHeader-Wrapper, or <code>null</code> if no supported ASF
* header was found.
* @throws IOException Read errors
*/
public static AsfHeader readInfoHeader(final RandomAccessFile file)
throws IOException {
final InputStream stream = createStream(file);
return INFO_READER.read(Utils.readGUID(stream), stream, 0);
}
/**
* This method tries to extract an ASF-header out of the given stream, which
* only contains metadata.<br>
* If no header could be extracted <code>null</code> is returned. <br>
*
* @param file File which contains the ASF header.
* @return AsfHeader-Wrapper, or <code>null</code> if no supported ASF
* header was found.
* @throws IOException Read errors
*/
public static AsfHeader readTagHeader(final RandomAccessFile file)
throws IOException {
final InputStream stream = createStream(file);
return TAG_READER.read(Utils.readGUID(stream), stream, 0);
}
/**
* {@inheritDoc}
*/
public boolean canFail() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
protected AsfHeader createContainer(final long streamPosition,
final BigInteger chunkLength, final InputStream stream)
throws IOException {
final long chunkCount = Utils.readUINT32(stream);
/*
* 2 reserved bytes. first should be equal to 0x01 and second 0x02. ASF
* specification suggests to not read the content if second byte is not
* 0x02.
*/
if (stream.read() != 1) {
throw new IOException("No ASF"); //$NON-NLS-1$
}
if (stream.read() != 2) {
throw new IOException("No ASF"); //$NON-NLS-1$
}
/*
* Creating the resulting object
*/
return new AsfHeader(streamPosition, chunkLength, chunkCount);
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
/**
* Sets the {@link AsfExtHeaderReader}, which is to be used, when an header
* extension object is found.
*
* @param extReader header extension object reader.
*/
public void setExtendedHeaderReader(final AsfExtHeaderReader extReader) {
for (final GUID curr : extReader.getApplyingIds()) {
this.readerMap.put(curr, extReader);
}
}
}

View file

@ -1,176 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* This class creates a modified copy of an ASF file.<br>
*
* @author Christian Laireiter
*/
public class AsfStreamer {
/**
* Simply copies a chunk from <code>source</code> to
* <code>destination</code>.<br>
* The method assumes, that the GUID has already been read and will write
* the provided one to the destination.<br>
* The chunk length however will be read and used to determine the amount of
* bytes to copy.
*
* @param guid GUID of the current chunk.
* @param source source of an ASF chunk, which is to be located at the chunk
* length field.
* @param destination the destination to copy the chunk to.
* @throws IOException on I/O errors.
*/
private void copyChunk(final GUID guid, final InputStream source,
final OutputStream destination) throws IOException {
final long chunkSize = Utils.readUINT64(source);
destination.write(guid.getBytes());
Utils.writeUINT64(chunkSize, destination);
Utils.copy(source, destination, chunkSize - 24);
}
/**
* Reads the <code>source</code> and applies the modifications provided by
* the given <code>modifiers</code>, and puts it to <code>dest</code>.<br>
* Each {@linkplain ChunkModifier modifier} is used only once, so if one
* should be used multiple times, it should be added multiple times into the
* list.<br>
*
* @param source the source ASF file
* @param dest the destination to write the modified version to.
* @param modifiers list of chunk modifiers to apply.
* @throws IOException on I/O errors.
*/
public void createModifiedCopy(final InputStream source,
final OutputStream dest, final List<ChunkModifier> modifiers)
throws IOException {
final List<ChunkModifier> modders = new ArrayList<ChunkModifier>();
if (modifiers != null) {
modders.addAll(modifiers);
}
// Read and check ASF GUID
final GUID readGUID = Utils.readGUID(source);
if (GUID.GUID_HEADER.equals(readGUID)) {
// used to calculate differences
long totalDiff = 0;
long chunkDiff = 0;
// read header information
final long headerSize = Utils.readUINT64(source);
final long chunkCount = Utils.readUINT32(source);
final byte[] reserved = new byte[2];
reserved[0] = (byte) (source.read() & 0xFF);
reserved[1] = (byte) (source.read() & 0xFF);
/*
* bos will get all unmodified and modified header chunks. This is
* necessary, because the header chunk (and file properties chunk)
* need to be adjusted but are written in front of the others.
*/
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
// fileHeader will get the binary representation of the file
// properties chunk, without GUID
byte[] fileHeader = null;
// Iterate through all chunks
for (long i = 0; i < chunkCount; i++) {
// Read GUID
final GUID curr = Utils.readGUID(source);
// special case for file properties chunk
if (GUID.GUID_FILE.equals(curr)) {
final ByteArrayOutputStream tmp = new ByteArrayOutputStream();
final long size = Utils.readUINT64(source);
Utils.writeUINT64(size, tmp);
Utils.copy(source, tmp, size - 24);
fileHeader = tmp.toByteArray();
} else {
/*
* Now look for ChunkModifier objects which modify the
* current chunk
*/
boolean handled = false;
for (int j = 0; j < modders.size() && !handled; j++) {
if (modders.get(j).isApplicable(curr)) {
// alter current chunk
final ModificationResult result = modders.get(j)
.modify(curr, source, bos);
// remember size differences.
chunkDiff += result.getChunkCountDifference();
totalDiff += result.getByteDifference();
// remove current modifier from index.
modders.remove(j);
handled = true;
}
}
if (!handled) {
// copy chunks which are not modified.
copyChunk(curr, source, bos);
}
}
}
// Now apply the left modifiers.
for (final ChunkModifier curr : modders) {
// chunks, which were not in the source file, will be added to
// the destination
final ModificationResult result = curr.modify(null, null, bos);
chunkDiff += result.getChunkCountDifference();
totalDiff += result.getByteDifference();
}
/*
* Now all header objects have been read or manipulated and stored
* in the internal buffer (bos).
*/
// write ASF GUID
dest.write(readGUID.getBytes());
// write altered header object size
Utils.writeUINT64(headerSize + totalDiff, dest);
// write altered number of chunks
Utils.writeUINT32(chunkCount + chunkDiff, dest);
// write the reserved 2 bytes (0x01,0x02).
dest.write(reserved);
// write the new file header
modifyFileHeader(new ByteArrayInputStream(fileHeader), dest,
totalDiff);
// write the header objects (chunks)
dest.write(bos.toByteArray());
// copy the rest of the file (data and index)
Utils.flush(source, dest);
} else {
throw new IllegalArgumentException("No ASF header object.");
}
}
/**
* This is a slight variation of
* {@link #copyChunk(GUID, InputStream, OutputStream)}, it only handles file
* property chunks correctly.<br>
* The copied chunk will have the file size field modified by the given
* <code>fileSizeDiff</code> value.
*
* @param source source of file properties chunk, located at its chunk length
* field.
* @param destination the destination to copy the chunk to.
* @param fileSizeDiff the difference which should be applied. (negative values would
* subtract the stored file size)
* @throws IOException on I/O errors.
*/
private void modifyFileHeader(final InputStream source,
final OutputStream destination, final long fileSizeDiff)
throws IOException {
destination.write(GUID.GUID_FILE.getBytes());
final long chunkSize = Utils.readUINT64(source);
Utils.writeUINT64(chunkSize, destination);
destination.write(Utils.readGUID(source).getBytes());
final long fileSize = Utils.readUINT64(source);
Utils.writeUINT64(fileSize + fileSizeDiff, destination);
Utils.copy(source, destination, chunkSize - 48);
}
}

View file

@ -1,215 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.Chunk;
import org.jaudiotagger.audio.asf.data.ChunkContainer;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.*;
import java.util.logging.Logger;
/**
* This class represents a reader implementation, which is able to read ASF
* objects (chunks) which store other objects (chunks) within them.<br>
*
* @param <ChunkType> The {@link ChunkContainer} instance, the implementation will
* create.
* @author Christian Laireiter
*/
abstract class ChunkContainerReader<ChunkType extends ChunkContainer>
implements ChunkReader {
/**
* Within this range, a {@link ChunkReader} should be aware if it fails.
*/
public final static int READ_LIMIT = 8192;
/**
* Logger
*/
protected static final Logger LOGGER = Logger
.getLogger("org.jaudiotabgger.audio"); //$NON-NLS-1$
/**
* If <code>true</code> each chunk type will only be read once.<br>
*/
protected final boolean eachChunkOnce;
/**
* Registers GUIDs to their reader classes.<br>
*/
protected final Map<GUID, ChunkReader> readerMap = new HashMap<GUID, ChunkReader>();
/**
* If <code>true</code> due to a {@linkplain #register(Class) registered}
* chunk reader, all {@link InputStream} objects passed to
* {@link #read(GUID, InputStream, long)} must support mark/reset.
*/
protected boolean hasFailingReaders = false;
/**
* Creates a reader instance, which only utilizes the given list of chunk
* readers.<br>
*
* @param toRegister List of {@link ChunkReader} class instances, which are to be
* utilized by the instance.
* @param readChunkOnce if <code>true</code>, each chunk type (identified by chunk
* GUID) will handled only once, if a reader is available, other
* chunks will be discarded.
*/
protected ChunkContainerReader(
final List<Class<? extends ChunkReader>> toRegister,
final boolean readChunkOnce) {
this.eachChunkOnce = readChunkOnce;
for (final Class<? extends ChunkReader> curr : toRegister) {
register(curr);
}
}
/**
* Checks for the constraints of this class.
*
* @param stream stream to test.
* @throws IllegalArgumentException If stream does not meet the requirements.
*/
protected void checkStream(final InputStream stream)
throws IllegalArgumentException {
if (this.hasFailingReaders && !stream.markSupported()) {
throw new IllegalArgumentException(
"Stream has to support mark/reset.");
}
}
/**
* This method is called by {@link #read(GUID, InputStream, long)} in order
* to create the resulting object. Implementations of this class should now
* return a new instance of their implementation specific result <b>AND</b>
* all data should be read, until the list of chunks starts. (The
* {@link ChunkContainer#getChunkEnd()} must return a sane result, too)<br>
*
* @param streamPosition position of the stream, the chunk starts.
* @param chunkLength the length of the chunk (from chunk header)
* @param stream to read the implementation specific information.
* @return instance of the implementations result.
* @throws IOException On I/O Errors and Invalid data.
*/
abstract protected ChunkType createContainer(long streamPosition,
BigInteger chunkLength, InputStream stream) throws IOException;
/**
* Gets a configured {@linkplain ChunkReader reader} instance for ASF
* objects (chunks) with the specified <code>guid</code>.
*
* @param guid GUID which identifies the chunk to be read.
* @return an appropriate reader implementation, <code>null</code> if not
* {@linkplain #register(Class) registered}.
*/
protected ChunkReader getReader(final GUID guid) {
return this.readerMap.get(guid);
}
/**
* Tests whether {@link #getReader(GUID)} won't return <code>null</code>.<br>
*
* @param guid GUID which identifies the chunk to be read.
* @return <code>true</code> if a reader is available.
*/
protected boolean isReaderAvailable(final GUID guid) {
return this.readerMap.containsKey(guid);
}
/**
* This Method implements the reading of a chunk container.<br>
*
* @param guid GUID of the currently read container.
* @param stream Stream which contains the chunk container.
* @param chunkStart The start of the chunk container from stream start.<br>
* For direct file streams one can assume <code>0</code> here.
* @return <code>null</code> if no valid data found, else a Wrapper
* containing all supported data.
* @throws IOException Read errors.
* @throws IllegalArgumentException If one used {@link ChunkReader} could
* {@linkplain ChunkReader#canFail() fail} and the stream source
* doesn't support mark/reset.
*/
public ChunkType read(final GUID guid, final InputStream stream,
final long chunkStart) throws IOException, IllegalArgumentException {
checkStream(stream);
final CountingInputStream cis = new CountingInputStream(stream);
if (!Arrays.asList(getApplyingIds()).contains(guid)) {
throw new IllegalArgumentException(
"provided GUID is not supported by this reader.");
}
// For Know the file pointer pointed to an ASF header chunk.
final BigInteger chunkLen = Utils.readBig64(cis);
/*
* now read implementation specific information until the chunk
* collection starts and create the resulting object.
*/
final ChunkType result = createContainer(chunkStart, chunkLen, cis);
// 16 bytes have already been for providing the GUID
long currentPosition = chunkStart + cis.getReadCount() + 16;
final HashSet<GUID> alreadyRead = new HashSet<GUID>();
/*
* Now reading header of chuncks.
*/
while (currentPosition < result.getChunkEnd()) {
final GUID currentGUID = Utils.readGUID(cis);
final boolean skip = this.eachChunkOnce
&& (!isReaderAvailable(currentGUID) || !alreadyRead
.add(currentGUID));
Chunk chunk;
/*
* If one reader tells it could fail (new method), then check the
* input stream for mark/reset. And use it if failed.
*/
if (!skip && isReaderAvailable(currentGUID)) {
final ChunkReader reader = getReader(currentGUID);
if (reader.canFail()) {
cis.mark(READ_LIMIT);
}
chunk = getReader(currentGUID).read(currentGUID, cis,
currentPosition);
} else {
chunk = ChunkHeaderReader.getInstance().read(currentGUID, cis,
currentPosition);
}
if (chunk == null) {
/*
* Reader failed
*/
cis.reset();
} else {
if (!skip) {
result.addChunk(chunk);
}
currentPosition = chunk.getChunkEnd();
// Always take into account, that 16 bytes have been read prior
// to calling this method
assert cis.getReadCount() + chunkStart + 16 == currentPosition;
}
}
return result;
}
/**
* Registers the given reader.<br>
*
* @param <T> The actual reader implementation.
* @param toRegister chunk reader which is to be registered.
*/
private <T extends ChunkReader> void register(final Class<T> toRegister) {
try {
final T reader = toRegister.newInstance();
for (final GUID curr : reader.getApplyingIds()) {
this.readerMap.put(curr, reader);
}
} catch (InstantiationException e) {
LOGGER.severe(e.getMessage());
} catch (IllegalAccessException e) {
LOGGER.severe(e.getMessage());
}
}
}

View file

@ -1,88 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.Chunk;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
/**
* Default reader, Reads GUID and size out of an input stream and creates a
* {@link org.jaudiotagger.audio.asf.data.Chunk}object, finally skips the
* remaining chunk bytes.
*
* @author Christian Laireiter
*/
final class ChunkHeaderReader implements ChunkReader {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {GUID.GUID_UNSPECIFIED};
/**
* Default instance.
*/
private static final ChunkHeaderReader INSTANCE = new ChunkHeaderReader();
/**
* Hidden Utility class constructor.
*/
private ChunkHeaderReader() {
// Hidden
}
/**
* Returns an instance of the reader.
*
* @return instance.
*/
public static ChunkHeaderReader getInstance() {
return INSTANCE;
}
/**
* {@inheritDoc}
*/
public boolean canFail() {
return false;
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
/**
* {@inheritDoc}
*/
public Chunk read(final GUID guid, final InputStream stream,
final long chunkStart) throws IOException {
final BigInteger chunkLen = Utils.readBig64(stream);
stream.skip(chunkLen.longValue() - 24);
return new Chunk(guid, chunkStart, chunkLen);
}
}

View file

@ -1,39 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.GUID;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Reads an ASF chunk and writes a modified copy.<br>
*
* @author Christian Laireiter
*/
public interface ChunkModifier {
/**
* Determines, whether the modifier handles chunks identified by given
* <code>guid</code>.
*
* @param guid GUID to test.
* @return <code>true</code>, if this modifier can be used to modify the
* chunk.
*/
boolean isApplicable(GUID guid);
/**
* Writes a modified copy of the chunk into the <code>destination.</code>.<br>
*
* @param guid GUID of the chunk to modify.
* @param source a stream providing the chunk, starting at the chunks length
* field.
* @param destination destination for the modified chunk.
* @return the differences between source and destination.
* @throws IOException on I/O errors.
*/
ModificationResult modify(GUID guid, InputStream source,
OutputStream destination) throws IOException;
}

View file

@ -1,50 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.Chunk;
import org.jaudiotagger.audio.asf.data.GUID;
import java.io.IOException;
import java.io.InputStream;
/**
* A ChunkReader provides methods for reading an ASF chunk.<br>
*
* @author Christian Laireiter
*/
public interface ChunkReader {
/**
* Tells whether the reader can fail to return a valid chunk.<br>
* The current Use would be a modified version of {@link StreamChunkReader},
* which is configured to only manage audio streams. However, the primary
* GUID for audio and video streams is the same. So if a stream shows itself
* to be a video stream, the reader would return <code>null</code>.<br>
*
* @return <code>true</code>, if further analysis of the chunk can show,
* that the reader is not applicable, despite the header GUID
* {@linkplain #getApplyingIds() identification} told it can handle
* the chunk.
*/
boolean canFail();
/**
* Returns the GUIDs identifying the types of chunk, this reader will parse.<br>
*
* @return the GUIDs identifying the types of chunk, this reader will parse.<br>
*/
GUID[] getApplyingIds();
/**
* Parses the chunk.
*
* @param guid the GUID of the chunks header, which is about to be read.
* @param stream source to read chunk from.<br>
* No {@link GUID} is expected at the currents stream position.
* The length of the chunk is about to follow.
* @param streamPosition the position in stream, the chunk starts.<br>
* @return the read chunk. (Mostly a subclass of {@link Chunk}).<br>
* @throws IOException On I/O Errors.
*/
Chunk read(GUID guid, InputStream stream, long streamPosition)
throws IOException;
}

View file

@ -1,65 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
/**
* This {@link ChunkModifier} implementation is meant to remove selected chunks.<br>
*
* @author Christian Laireiter
*/
@SuppressWarnings({"ManualArrayToCollectionCopy"})
public class ChunkRemover implements ChunkModifier {
/**
* Stores the GUIDs, which are about to be removed by this modifier.<br>
*/
private final Set<GUID> toRemove;
/**
* Creates an instance, for removing selected chunks.<br>
*
* @param guids the GUIDs which are about to be removed by this modifier.
*/
public ChunkRemover(final GUID... guids) {
this.toRemove = new HashSet<GUID>();
for (final GUID current : guids) {
this.toRemove.add(current);
}
}
/**
* {@inheritDoc}
*/
public boolean isApplicable(final GUID guid) {
return this.toRemove.contains(guid);
}
/**
* {@inheritDoc}
*/
public ModificationResult modify(final GUID guid, final InputStream source,
final OutputStream destination) throws IOException {
ModificationResult result;
if (guid == null) {
// Now a chunk should be added, however, this implementation is for
// removal.
result = new ModificationResult(0, 0);
} else {
assert isApplicable(guid);
// skip the chunk length minus 24 bytes for the already read length
// and the guid.
final long chunkLen = Utils.readUINT64(source);
source.skip(chunkLen - 24);
result = new ModificationResult(-1, -1 * chunkLen, guid);
}
return result;
}
}

View file

@ -1,73 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.Chunk;
import org.jaudiotagger.audio.asf.data.ContentBranding;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
/**
* This reader is used to read the content branding object of ASF streams.<br>
*
* @author Christian Laireiter
* @see org.jaudiotagger.audio.asf.data.ContainerType#CONTENT_BRANDING
* @see ContentBranding
*/
public class ContentBrandingReader implements ChunkReader {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {GUID.GUID_CONTENT_BRANDING};
/**
* Should not be used for now.
*/
protected ContentBrandingReader() {
// NOTHING toDo
}
/**
* {@inheritDoc}
*/
public boolean canFail() {
return false;
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
/**
* {@inheritDoc}
*/
public Chunk read(final GUID guid, final InputStream stream,
final long streamPosition) throws IOException {
assert GUID.GUID_CONTENT_BRANDING.equals(guid);
final BigInteger chunkSize = Utils.readBig64(stream);
final long imageType = Utils.readUINT32(stream);
assert imageType >= 0 && imageType <= 3 : imageType;
final long imageDataSize = Utils.readUINT32(stream);
assert imageType > 0 || imageDataSize == 0 : imageDataSize;
assert imageDataSize < Integer.MAX_VALUE;
final byte[] imageData = Utils.readBinary(stream, imageDataSize);
final long copyRightUrlLen = Utils.readUINT32(stream);
final String copyRight = new String(Utils.readBinary(stream,
copyRightUrlLen));
final long imageUrlLen = Utils.readUINT32(stream);
final String imageUrl = new String(Utils
.readBinary(stream, imageUrlLen));
final ContentBranding result = new ContentBranding(streamPosition,
chunkSize);
result.setImage(imageType, imageData);
result.setCopyRightURL(copyRight);
result.setBannerImageURL(imageUrl);
return result;
}
}

View file

@ -1,123 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.Chunk;
import org.jaudiotagger.audio.asf.data.ContentDescription;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
/**
* Reads and interprets the data of a ASF chunk containing title, author... <br>
*
* @author Christian Laireiter
* @see org.jaudiotagger.audio.asf.data.ContentDescription
*/
public class ContentDescriptionReader implements ChunkReader {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {GUID.GUID_CONTENTDESCRIPTION};
/**
* Should not be used for now.
*/
protected ContentDescriptionReader() {
// NOTHING toDo
}
/**
* {@inheritDoc}
*/
public boolean canFail() {
return false;
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
/**
* Returns the next 5 UINT16 values as an array.<br>
*
* @param stream stream to read from
* @return 5 int values read from stream.
* @throws IOException on I/O Errors.
*/
private int[] getStringSizes(final InputStream stream) throws IOException {
final int[] result = new int[5];
for (int i = 0; i < result.length; i++) {
result[i] = Utils.readUINT16(stream);
}
return result;
}
/**
* {@inheritDoc}
*/
public Chunk read(final GUID guid, final InputStream stream,
final long chunkStart) throws IOException {
final BigInteger chunkSize = Utils.readBig64(stream);
/*
* Now comes 16-Bit values representing the length of the Strings which
* follows.
*/
final int[] stringSizes = getStringSizes(stream);
/*
* Now we know the String length of each occuring String.
*/
final String[] strings = new String[stringSizes.length];
for (int i = 0; i < strings.length; i++) {
if (stringSizes[i] > 0) {
strings[i] = Utils
.readFixedSizeUTF16Str(stream, stringSizes[i]);
}
}
/*
* Now create the result
*/
final ContentDescription result = new ContentDescription(chunkStart,
chunkSize);
if (stringSizes[0] > 0) {
result.setTitle(strings[0]);
}
if (stringSizes[1] > 0) {
result.setAuthor(strings[1]);
}
if (stringSizes[2] > 0) {
result.setCopyright(strings[2]);
}
if (stringSizes[3] > 0) {
result.setComment(strings[3]);
}
if (stringSizes[4] > 0) {
result.setRating(strings[4]);
}
return result;
}
}

View file

@ -1,108 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* This implementation of {@link FilterInputStream} counts each read byte.<br>
* So at each time, with {@link #getReadCount()} one can determine how many
* bytes have been read, by this classes read and skip methods (mark and reset
* are also taken into account).<br>
*
* @author Christian Laireiter
*/
class CountingInputStream extends FilterInputStream {
/**
* If {@link #mark(int)} has been called, the current value of
* {@link #readCount} is stored, in order to reset it upon {@link #reset()}.
*/
private long markPos;
/**
* The amount of read or skipped bytes.
*/
private long readCount;
/**
* Creates an instance, which delegates the commands to the given stream.
*
* @param stream stream to actually work with.
*/
public CountingInputStream(final InputStream stream) {
super(stream);
this.markPos = 0;
this.readCount = 0;
}
/**
* Counts the given amount of bytes.
*
* @param amountRead number of bytes to increase.
*/
private synchronized void bytesRead(final long amountRead) {
if (amountRead >= 0) {
this.readCount += amountRead;
}
}
/**
* @return the readCount
*/
public synchronized long getReadCount() {
return this.readCount;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void mark(final int readlimit) {
super.mark(readlimit);
this.markPos = this.readCount;
}
/**
* {@inheritDoc}
*/
@Override
public int read() throws IOException {
final int result = super.read();
bytesRead(1);
return result;
}
/**
* {@inheritDoc}
*/
@Override
public int read(final byte[] destination, final int off, final int len)
throws IOException {
final int result = super.read(destination, off, len);
bytesRead(result);
return result;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void reset() throws IOException {
super.reset();
synchronized (this) {
this.readCount = this.markPos;
}
}
/**
* {@inheritDoc}
*/
@Override
public long skip(final long amount) throws IOException {
final long skipped = super.skip(amount);
bytesRead(skipped);
return skipped;
}
}

View file

@ -1,88 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import java.io.IOException;
import java.io.OutputStream;
/**
* This output stream wraps around another {@link OutputStream} and delegates
* the write calls.<br>
* Additionally all written bytes are counted and available by
* {@link #getCount()}.
*
* @author Christian Laireiter
*/
public class CountingOutputstream extends OutputStream {
/**
* The stream to forward the write calls.
*/
private final OutputStream wrapped;
/**
* Stores the amount of bytes written.
*/
private long count = 0;
/**
* Creates an instance which will delegate the write calls to the given
* output stream.
*
* @param outputStream stream to wrap.
*/
public CountingOutputstream(final OutputStream outputStream) {
super();
assert outputStream != null;
this.wrapped = outputStream;
}
/**
* {@inheritDoc}
*/
@Override
public void close() throws IOException {
this.wrapped.close();
}
/**
* {@inheritDoc}
*/
@Override
public void flush() throws IOException {
this.wrapped.flush();
}
/**
* @return the count
*/
public long getCount() {
return this.count;
}
/**
* {@inheritDoc}
*/
@Override
public void write(final byte[] bytes) throws IOException {
this.wrapped.write(bytes);
this.count += bytes.length;
}
/**
* {@inheritDoc}
*/
@Override
public void write(final byte[] bytes, final int off, final int len)
throws IOException {
this.wrapped.write(bytes, off, len);
this.count += len;
}
/**
* {@inheritDoc}
*/
@Override
public void write(final int toWrite) throws IOException {
this.wrapped.write(toWrite);
this.count++;
}
}

View file

@ -1,100 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.Chunk;
import org.jaudiotagger.audio.asf.data.EncodingChunk;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
/**
* This class reads the chunk containing encoding data <br>
* <b>Warning:<b><br>
* Implementation is not completed. More analysis of this chunk is needed.
*
* @author Christian Laireiter
*/
class EncodingChunkReader implements ChunkReader {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {GUID.GUID_ENCODING};
/**
* Should not be used for now.
*/
protected EncodingChunkReader() {
// NOTHING toDo
}
/**
* {@inheritDoc}
*/
public boolean canFail() {
return false;
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
/**
* {@inheritDoc}
*/
public Chunk read(final GUID guid, final InputStream stream,
final long chunkStart) throws IOException {
final BigInteger chunkLen = Utils.readBig64(stream);
final EncodingChunk result = new EncodingChunk(chunkLen);
int readBytes = 24;
// Can't be interpreted
/*
* What do I think of this data, well it seems to be another GUID. Then
* followed by a UINT16 indicating a length of data following (by half).
* My test files just had the length of one and a two bytes zero.
*/
stream.skip(20);
readBytes += 20;
/*
* Read the number of strings which will follow
*/
final int stringCount = Utils.readUINT16(stream);
readBytes += 2;
/*
* Now reading the specified amount of strings.
*/
for (int i = 0; i < stringCount; i++) {
final String curr = Utils.readCharacterSizedString(stream);
result.addString(curr);
readBytes += 4 + 2 * curr.length();
}
stream.skip(chunkLen.longValue() - readBytes);
result.setPosition(chunkStart);
return result;
}
}

View file

@ -1,128 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.Chunk;
import org.jaudiotagger.audio.asf.data.EncryptionChunk;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
/**
* This class reads the chunk containing encoding data <br>
* <b>Warning:<b><br>
* Implementation is not completed. More analysis of this chunk is needed.
*
* @author Christian Laireiter
*/
class EncryptionChunkReader implements ChunkReader {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {GUID.GUID_CONTENT_ENCRYPTION};
/**
* Should not be used for now.
*/
protected EncryptionChunkReader() {
// NOTHING toDo
}
/**
* {@inheritDoc}
*/
public boolean canFail() {
return false;
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
/**
* {@inheritDoc}
*/
public Chunk read(final GUID guid, final InputStream stream,
final long chunkStart) throws IOException {
EncryptionChunk result;
final BigInteger chunkLen = Utils.readBig64(stream);
result = new EncryptionChunk(chunkLen);
// Can't be interpreted
/*
* Object ID GUID 128 Object Size QWORD 64 Secret Data Length DWORD 32
* Secret Data INTEGER varies Protection Type Length DWORD 32 Protection
* Type char varies Key ID Length DWORD 32 Key ID char varies License
* URL Length DWORD 32 License URL char varies * Read the
*/
byte[] secretData;
byte[] protectionType;
byte[] keyID;
byte[] licenseURL;
// Secret Data length
int fieldLength;
fieldLength = (int) Utils.readUINT32(stream);
// Secret Data
secretData = new byte[fieldLength + 1];
stream.read(secretData, 0, fieldLength);
secretData[fieldLength] = 0;
// Protection type Length
fieldLength = 0;
fieldLength = (int) Utils.readUINT32(stream);
// Protection Data Length
protectionType = new byte[fieldLength + 1];
stream.read(protectionType, 0, fieldLength);
protectionType[fieldLength] = 0;
// Key ID length
fieldLength = 0;
fieldLength = (int) Utils.readUINT32(stream);
// Key ID
keyID = new byte[fieldLength + 1];
stream.read(keyID, 0, fieldLength);
keyID[fieldLength] = 0;
// License URL length
fieldLength = 0;
fieldLength = (int) Utils.readUINT32(stream);
// License URL
licenseURL = new byte[fieldLength + 1];
stream.read(licenseURL, 0, fieldLength);
licenseURL[fieldLength] = 0;
result.setSecretData(new String(secretData));
result.setProtectionType(new String(protectionType));
result.setKeyID(new String(keyID));
result.setLicenseURL(new String(licenseURL));
result.setPosition(chunkStart);
return result;
}
}

View file

@ -1,94 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.Chunk;
import org.jaudiotagger.audio.asf.data.FileHeader;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
/**
* Reads and interprets the data of the file header. <br>
*
* @author Christian Laireiter
*/
public class FileHeaderReader implements ChunkReader {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {GUID.GUID_FILE};
/**
* Should not be used for now.
*/
protected FileHeaderReader() {
// NOTHING toDo
}
/**
* {@inheritDoc}
*/
public boolean canFail() {
return false;
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
/**
* {@inheritDoc}
*/
public Chunk read(final GUID guid, final InputStream stream,
final long chunkStart) throws IOException {
final BigInteger chunkLen = Utils.readBig64(stream);
// Skip client GUID.
stream.skip(16);
final BigInteger fileSize = Utils.readBig64(stream);
// fileTime in 100 ns since midnight of 1st january 1601 GMT
final BigInteger fileTime = Utils.readBig64(stream);
final BigInteger packageCount = Utils.readBig64(stream);
final BigInteger timeEndPos = Utils.readBig64(stream);
final BigInteger duration = Utils.readBig64(stream);
final BigInteger timeStartPos = Utils.readBig64(stream);
final long flags = Utils.readUINT32(stream);
final long minPkgSize = Utils.readUINT32(stream);
final long maxPkgSize = Utils.readUINT32(stream);
final long uncompressedFrameSize = Utils.readUINT32(stream);
final FileHeader result = new FileHeader(chunkLen, fileSize, fileTime,
packageCount, duration, timeStartPos, timeEndPos, flags,
minPkgSize, maxPkgSize, uncompressedFrameSize);
result.setPosition(chunkStart);
return result;
}
}

View file

@ -1,77 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* This implementation repeatedly reads from the wrapped input stream until the
* requested amount of bytes are read.<br>
*
* @author Christian Laireiter
*/
public class FullRequestInputStream extends FilterInputStream {
/**
* Creates an instance.
*
* @param source stream to read from.
*/
public FullRequestInputStream(final InputStream source) {
super(source);
}
/**
* {@inheritDoc}
*/
@Override
public int read(final byte[] buffer) throws IOException {
return read(buffer, 0, buffer.length);
}
/**
* {@inheritDoc}
*/
@Override
public int read(final byte[] buffer, final int off, final int len)
throws IOException {
int totalRead = 0;
int read;
while (totalRead < len) {
read = super.read(buffer, off + totalRead, len - totalRead);
if (read >= 0) {
totalRead += read;
}
if (read == -1) {
throw new IOException((len - totalRead)
+ " more bytes expected.");
}
}
return totalRead;
}
/**
* {@inheritDoc}
*/
@Override
public long skip(final long amount) throws IOException {
long skipped = 0;
int zeroSkipCnt = 0;
long currSkipped;
while (skipped < amount) {
currSkipped = super.skip(amount - skipped);
if (currSkipped == 0) {
zeroSkipCnt++;
if (zeroSkipCnt == 2) {
// If the skip value exceeds streams size, this and the
// number is extremely large, this can lead to a very long
// running loop.
break;
}
}
skipped += currSkipped;
}
return skipped;
}
}

View file

@ -1,63 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.Chunk;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.data.LanguageList;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
/**
* Reads and interprets the &quot;Language List Object&quot; of ASF files.<br>
*
* @author Christian Laireiter
*/
public class LanguageListReader implements ChunkReader {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {GUID.GUID_LANGUAGE_LIST};
/**
* {@inheritDoc}
*/
public boolean canFail() {
return false;
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
/**
* {@inheritDoc}
*/
public Chunk read(final GUID guid, final InputStream stream,
final long streamPosition) throws IOException {
assert GUID.GUID_LANGUAGE_LIST.equals(guid);
final BigInteger chunkLen = Utils.readBig64(stream);
final int readUINT16 = Utils.readUINT16(stream);
final LanguageList result = new LanguageList(streamPosition, chunkLen);
for (int i = 0; i < readUINT16; i++) {
final int langIdLen = (stream.read() & 0xFF);
final String langId = Utils
.readFixedSizeUTF16Str(stream, langIdLen);
// langIdLen = 2 bytes for each char and optionally one zero
// termination character
assert langId.length() == langIdLen / 2 - 1
|| langId.length() == langIdLen / 2;
result.addLanguage(langId);
}
return result;
}
}

View file

@ -1,154 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.*;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
/**
* Reads an interprets &quot;Metadata Object&quot;, &quot;Metadata Library
* Object&quot; and &quot;Extended Content Description&quot; of ASF files.<br>
*
* @author Christian Laireiter
*/
public class MetadataReader implements ChunkReader {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {
ContainerType.EXTENDED_CONTENT.getContainerGUID(),
ContainerType.METADATA_OBJECT.getContainerGUID(),
ContainerType.METADATA_LIBRARY_OBJECT.getContainerGUID()};
/**
* {@inheritDoc}
*/
public boolean canFail() {
return false;
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
/**
* {@inheritDoc}
*/
public Chunk read(final GUID guid, final InputStream stream,
final long streamPosition) throws IOException {
final BigInteger chunkLen = Utils.readBig64(stream);
final MetadataContainer result = new MetadataContainer(guid,
streamPosition, chunkLen);
// isExtDesc will be set to true, if a extended content description
// chunk is read
// otherwise it is a metadata object, there are only slight differences
final boolean isExtDesc = result.getContainerType() == ContainerType.EXTENDED_CONTENT;
final int recordCount = Utils.readUINT16(stream);
for (int i = 0; i < recordCount; i++) {
int languageIndex = 0;
int streamNumber = 0;
if (!isExtDesc) {
/*
* Metadata objects have a language index and a stream number
*/
languageIndex = Utils.readUINT16(stream);
assert languageIndex >= 0
&& languageIndex < MetadataDescriptor.MAX_LANG_INDEX;
assert result.getContainerType() == ContainerType.METADATA_LIBRARY_OBJECT
|| languageIndex == 0;
streamNumber = Utils.readUINT16(stream);
assert streamNumber >= 0
&& streamNumber <= MetadataDescriptor.MAX_STREAM_NUMBER;
}
final int nameLen = Utils.readUINT16(stream);
String recordName = null;
if (isExtDesc) {
recordName = Utils.readFixedSizeUTF16Str(stream, nameLen);
}
final int dataType = Utils.readUINT16(stream);
assert dataType >= 0 && dataType <= 6;
final long dataLen = isExtDesc ? Utils.readUINT16(stream) : Utils
.readUINT32(stream);
assert dataLen >= 0;
assert result.getContainerType() == ContainerType.METADATA_LIBRARY_OBJECT
|| dataLen <= MetadataDescriptor.DWORD_MAXVALUE;
if (!isExtDesc) {
recordName = Utils.readFixedSizeUTF16Str(stream, nameLen);
}
final MetadataDescriptor descriptor = new MetadataDescriptor(result
.getContainerType(), recordName, dataType, streamNumber,
languageIndex);
switch (dataType) {
case MetadataDescriptor.TYPE_STRING:
descriptor.setStringValue(Utils.readFixedSizeUTF16Str(stream,
(int) dataLen));
break;
case MetadataDescriptor.TYPE_BINARY:
descriptor.setBinaryValue(Utils.readBinary(stream, dataLen));
break;
case MetadataDescriptor.TYPE_BOOLEAN:
assert isExtDesc && dataLen == 4 || !isExtDesc && dataLen == 2;
descriptor.setBooleanValue(readBoolean(stream, (int) dataLen));
break;
case MetadataDescriptor.TYPE_DWORD:
assert dataLen == 4;
descriptor.setDWordValue(Utils.readUINT32(stream));
break;
case MetadataDescriptor.TYPE_WORD:
assert dataLen == 2;
descriptor.setWordValue(Utils.readUINT16(stream));
break;
case MetadataDescriptor.TYPE_QWORD:
assert dataLen == 8;
descriptor.setQWordValue(Utils.readUINT64(stream));
break;
case MetadataDescriptor.TYPE_GUID:
assert dataLen == GUID.GUID_LENGTH;
descriptor.setGUIDValue(Utils.readGUID(stream));
break;
default:
// Unknown, hopefully the convention for the size of the
// value
// is given, so we could read it binary
descriptor.setStringValue("Invalid datatype: "
+ new String(Utils.readBinary(stream, dataLen)));
}
result.addDescriptor(descriptor);
}
return result;
}
/**
* Reads the given amount of bytes and checks the last byte, if its equal to
* one or zero (true / false).<br>
* All other bytes must be zero. (if assertions enabled).
*
* @param stream stream to read from.
* @param bytes amount of bytes
* @return <code>true</code> or <code>false</code>.
* @throws IOException on I/O Errors
*/
private boolean readBoolean(final InputStream stream, final int bytes)
throws IOException {
final byte[] tmp = new byte[bytes];
stream.read(tmp);
boolean result = false;
for (int i = 0; i < bytes; i++) {
if (i == bytes - 1) {
result = tmp[i] == 1;
assert tmp[i] == 0 || tmp[i] == 1;
} else {
assert tmp[i] == 0;
}
}
return result;
}
}

View file

@ -1,90 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.GUID;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* Structure to tell the differences occurred by altering a chunk.
*
* @author Christian Laireiter
*/
final class ModificationResult {
/**
* Stores the difference of bytes.<br>
*/
private final long byteDifference;
/**
* Stores the difference of the amount of chunks.<br>
* &quot;-1&quot; if the chunk disappeared upon modification.<br>
* &quot;0&quot; if the chunk was just modified.<br>
* &quot;1&quot; if a chunk has been created.<br>
*/
private final int chunkDifference;
/**
* Stores all GUIDs, which have been read.<br>
*/
private final Set<GUID> occuredGUIDs = new HashSet<GUID>();
/**
* Creates an instance.<br>
*
* @param chunkCountDiff amount of chunks appeared, disappeared
* @param bytesDiffer amount of bytes added or removed.
* @param occurred all GUIDs which have been occurred, during processing
*/
public ModificationResult(final int chunkCountDiff, final long bytesDiffer,
final GUID... occurred) {
assert occurred != null && occurred.length > 0;
this.chunkDifference = chunkCountDiff;
this.byteDifference = bytesDiffer;
this.occuredGUIDs.addAll(Arrays.asList(occurred));
}
/**
* Creates an instance.<br>
*
* @param chunkCountDiff amount of chunks appeared, disappeared
* @param bytesDiffer amount of bytes added or removed.
* @param occurred all GUIDs which have been occurred, during processing
*/
public ModificationResult(final int chunkCountDiff, final long bytesDiffer,
final Set<GUID> occurred) {
this.chunkDifference = chunkCountDiff;
this.byteDifference = bytesDiffer;
this.occuredGUIDs.addAll(occurred);
}
/**
* Returns the difference of bytes.
*
* @return the byte difference
*/
public long getByteDifference() {
return this.byteDifference;
}
/**
* Returns the difference of the amount of chunks.
*
* @return the chunk count difference
*/
public int getChunkCountDifference() {
return this.chunkDifference;
}
/**
* Returns all GUIDs which have been occurred during processing.
*
* @return see description.s
*/
public Set<GUID> getOccuredGUIDs() {
return new HashSet<GUID>(this.occuredGUIDs);
}
}

View file

@ -1,66 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
/**
* Wraps a {@link RandomAccessFile} into an {@link InputStream}.<br>
*
* @author Christian Laireiter
*/
public final class RandomAccessFileInputstream extends InputStream {
/**
* The file access to read from.<br>
*/
private final RandomAccessFile source;
/**
* Creates an instance that will provide {@link InputStream} functionality
* on the given {@link RandomAccessFile} by delegating calls.<br>
*
* @param file The file to read.
*/
public RandomAccessFileInputstream(final RandomAccessFile file) {
super();
if (file == null) {
throw new IllegalArgumentException("null");
}
this.source = file;
}
/**
* {@inheritDoc}
*/
@Override
public int read() throws IOException {
return this.source.read();
}
/**
* {@inheritDoc}
*/
@Override
public int read(final byte[] buffer, final int off, final int len)
throws IOException {
return this.source.read(buffer, off, len);
}
/**
* {@inheritDoc}
*/
@Override
public long skip(final long amount) throws IOException {
if (amount < 0) {
throw new IllegalArgumentException("invalid negative value");
}
long left = amount;
while (left > Integer.MAX_VALUE) {
this.source.skipBytes(Integer.MAX_VALUE);
left -= Integer.MAX_VALUE;
}
return this.source.skipBytes((int) left);
}
}

View file

@ -1,46 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
/**
* Wraps a {@link RandomAccessFile} into an {@link OutputStream}.<br>
*
* @author Christian Laireiter
*/
public final class RandomAccessFileOutputStream extends OutputStream {
/**
* the file to write to.
*/
private final RandomAccessFile targetFile;
/**
* Creates an instance.<br>
*
* @param target file to write to.
*/
public RandomAccessFileOutputStream(final RandomAccessFile target) {
super();
this.targetFile = target;
}
/**
* {@inheritDoc}
*/
@Override
public void write(final byte[] bytes, final int off, final int len)
throws IOException {
this.targetFile.write(bytes, off, len);
}
/**
* {@inheritDoc}
*/
@Override
public void write(final int toWrite) throws IOException {
this.targetFile.write(toWrite);
}
}

View file

@ -1,87 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.Chunk;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.data.StreamBitratePropertiesChunk;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
/**
* This class reads the chunk containing the stream bitrate properties.<br>
*
* @author Christian Laireiter
*/
public class StreamBitratePropertiesReader implements ChunkReader {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {GUID.GUID_STREAM_BITRATE_PROPERTIES};
/**
* Should not be used for now.
*/
protected StreamBitratePropertiesReader() {
// NOTHING toDo
}
/**
* {@inheritDoc}
*/
public boolean canFail() {
return false;
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
/**
* {@inheritDoc}
*/
public Chunk read(final GUID guid, final InputStream stream,
final long chunkStart) throws IOException {
final BigInteger chunkLen = Utils.readBig64(stream);
final StreamBitratePropertiesChunk result = new StreamBitratePropertiesChunk(
chunkLen);
/*
* Read the amount of bitrate records
*/
final long recordCount = Utils.readUINT16(stream);
for (int i = 0; i < recordCount; i++) {
final int flags = Utils.readUINT16(stream);
final long avgBitrate = Utils.readUINT32(stream);
result.addBitrateRecord(flags & 0x00FF, avgBitrate);
}
result.setPosition(chunkStart);
return result;
}
}

View file

@ -1,188 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.*;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
/**
* Reads and interprets the data of the audio or video stream information chunk. <br>
*
* @author Christian Laireiter
*/
public class StreamChunkReader implements ChunkReader {
/**
* The GUID this reader {@linkplain #getApplyingIds() applies to}
*/
private final static GUID[] APPLYING = {GUID.GUID_STREAM};
/**
* Shouldn't be used for now.
*/
protected StreamChunkReader() {
// Nothing to do
}
/**
* {@inheritDoc}
*/
public boolean canFail() {
return true;
}
/**
* {@inheritDoc}
*/
public GUID[] getApplyingIds() {
return APPLYING.clone();
}
/**
* {@inheritDoc}
*/
public Chunk read(final GUID guid, final InputStream stream,
final long chunkStart) throws IOException {
StreamChunk result = null;
final BigInteger chunkLength = Utils.readBig64(stream);
// Now comes GUID indicating whether stream content type is audio or
// video
final GUID streamTypeGUID = Utils.readGUID(stream);
if (GUID.GUID_AUDIOSTREAM.equals(streamTypeGUID)
|| GUID.GUID_VIDEOSTREAM.equals(streamTypeGUID)) {
// A GUID is indicating whether the stream is error
// concealed
final GUID errorConcealment = Utils.readGUID(stream);
/*
* Read the Time Offset
*/
final long timeOffset = Utils.readUINT64(stream);
final long typeSpecificDataSize = Utils.readUINT32(stream);
final long streamSpecificDataSize = Utils.readUINT32(stream);
/*
* Read a bit field. (Contains stream number, and whether the stream
* content is encrypted.)
*/
final int mask = Utils.readUINT16(stream);
final int streamNumber = mask & 127;
final boolean contentEncrypted = (mask & 0x8000) != 0;
/*
* Skip a reserved field
*/
stream.skip(4);
/*
* very important to set for every stream type. The size of bytes
* read by the specific stream type, in order to skip the remaining
* unread bytes of the stream chunk.
*/
long streamSpecificBytes;
if (GUID.GUID_AUDIOSTREAM.equals(streamTypeGUID)) {
/*
* Reading audio specific information
*/
final AudioStreamChunk audioStreamChunk = new AudioStreamChunk(
chunkLength);
result = audioStreamChunk;
/*
* read WAVEFORMATEX and format extension.
*/
final long compressionFormat = Utils.readUINT16(stream);
final long channelCount = Utils.readUINT16(stream);
final long samplingRate = Utils.readUINT32(stream);
final long avgBytesPerSec = Utils.readUINT32(stream);
final long blockAlignment = Utils.readUINT16(stream);
final int bitsPerSample = Utils.readUINT16(stream);
final int codecSpecificDataSize = Utils.readUINT16(stream);
final byte[] codecSpecificData = new byte[codecSpecificDataSize];
stream.read(codecSpecificData);
audioStreamChunk.setCompressionFormat(compressionFormat);
audioStreamChunk.setChannelCount(channelCount);
audioStreamChunk.setSamplingRate(samplingRate);
audioStreamChunk.setAverageBytesPerSec(avgBytesPerSec);
audioStreamChunk.setErrorConcealment(errorConcealment);
audioStreamChunk.setBlockAlignment(blockAlignment);
audioStreamChunk.setBitsPerSample(bitsPerSample);
audioStreamChunk.setCodecData(codecSpecificData);
streamSpecificBytes = 18 + codecSpecificData.length;
} else {
/*
* Reading video specific information
*/
final VideoStreamChunk videoStreamChunk = new VideoStreamChunk(
chunkLength);
result = videoStreamChunk;
final long pictureWidth = Utils.readUINT32(stream);
final long pictureHeight = Utils.readUINT32(stream);
// Skip unknown field
stream.skip(1);
/*
* Now read the format specific data
*/
// Size of the data section (formatDataSize)
stream.skip(2);
stream.skip(16);
final byte[] fourCC = new byte[4];
stream.read(fourCC);
videoStreamChunk.setPictureWidth(pictureWidth);
videoStreamChunk.setPictureHeight(pictureHeight);
videoStreamChunk.setCodecId(fourCC);
streamSpecificBytes = 31;
}
/*
* Setting common values for audio and video
*/
result.setStreamNumber(streamNumber);
result.setStreamSpecificDataSize(streamSpecificDataSize);
result.setTypeSpecificDataSize(typeSpecificDataSize);
result.setTimeOffset(timeOffset);
result.setContentEncrypted(contentEncrypted);
result.setPosition(chunkStart);
/*
* Now skip remainder of chunks bytes. chunk-length - 24 (size of
* GUID and chunklen) - streamSpecificBytes(stream type specific
* data) - 54 (common data)
*/
stream
.skip(chunkLength.longValue() - 24 - streamSpecificBytes
- 54);
}
return result;
}
}

View file

@ -1,48 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.GUID;
import java.io.IOException;
import java.io.OutputStream;
/**
* Implementors can write themselves directly to an output stream, and have the
* ability to tell the size they would need, as well as determine if they are
* empty.<br>
*
* @author Christian Laireiter
*/
public interface WriteableChunk {
/**
* This method calculates the total amount of bytes, the chunk would consume
* in an ASF file.<br>
*
* @return amount of bytes the chunk would currently need in an ASF file.
*/
long getCurrentAsfChunkSize();
/**
* Returns the GUID of the chunk.
*
* @return GUID of the chunk.
*/
GUID getGuid();
/**
* <code>true</code> if it is not necessary to write the chunk into an ASF
* file, since it contains no information.
*
* @return <code>true</code> if no useful data will be preserved.
*/
boolean isEmpty();
/**
* Writes the chunk into the specified output stream, as ASF stream chunk.<br>
*
* @param out stream to write into.
* @return amount of bytes written.
* @throws IOException on I/O errors
*/
long writeInto(OutputStream out) throws IOException;
}

View file

@ -1,77 +0,0 @@
package org.jaudiotagger.audio.asf.io;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.audio.asf.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* A chunk modifier which works with information provided by
* {@link WriteableChunk} objects.<br>
*
* @author Christian Laireiter
*/
public class WriteableChunkModifer implements ChunkModifier {
/**
* The chunk to write.
*/
private final WriteableChunk writableChunk;
/**
* Creates an instance.<br>
*
* @param chunk chunk to write
*/
public WriteableChunkModifer(final WriteableChunk chunk) {
this.writableChunk = chunk;
}
/**
* {@inheritDoc}
*/
public boolean isApplicable(final GUID guid) {
return guid.equals(this.writableChunk.getGuid());
}
/**
* {@inheritDoc}
*/
public ModificationResult modify(final GUID guid, final InputStream chunk,
OutputStream destination) throws IOException { // NOPMD by Christian Laireiter on 5/9/09 5:03 PM
int chunkDiff = 0;
long newSize = 0;
long oldSize = 0;
/*
* Replace the outputstream with the counting one, only if assert's are
* evaluated.
*/
assert (destination = new CountingOutputstream(destination)) != null;
if (!this.writableChunk.isEmpty()) {
newSize = this.writableChunk.writeInto(destination);
assert newSize == this.writableChunk.getCurrentAsfChunkSize();
/*
* If assert's are evaluated, we have replaced destination by a
* CountingOutpustream and can now verify if
* getCurrentAsfChunkSize() really works correctly.
*/
assert ((CountingOutputstream) destination).getCount() == newSize;
if (guid == null) {
chunkDiff++;
}
}
if (guid != null) {
assert isApplicable(guid);
if (this.writableChunk.isEmpty()) {
chunkDiff--;
}
oldSize = Utils.readUINT64(chunk);
chunk.skip(oldSize - 24);
}
return new ModificationResult(chunkDiff, (newSize - oldSize), guid);
}
}

View file

@ -1,19 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html long="en">
<head>
</head>
<body bgcolor="white">
Classes for reading and writing Microsoft Advanced Systems Format files.
<br>
<!-- package.html by Gary McGath -->
<!-- Put @see and @since tags down here. -->
</body>
</html>

View file

@ -1,19 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html long="en">
<head>
</head>
<body bgcolor="white">
Classes for Microsoft Advanced Systems Format files.
<br>
<!-- package.html by Gary McGath -->
<!-- Put @see and @since tags down here. -->
</body>
</html>

View file

@ -1,47 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.util;
import org.jaudiotagger.audio.asf.data.Chunk;
import java.io.Serializable;
import java.util.Comparator;
/**
* This class is needed for ordering all types of
* {@link org.jaudiotagger.audio.asf.data.Chunk}s ascending by their Position. <br>
*
* @author Christian Laireiter
*/
public final class ChunkPositionComparator implements Comparator<Chunk>,
Serializable {
/**
*
*/
private static final long serialVersionUID = -6337108235272376289L;
/**
* {@inheritDoc}
*/
public int compare(final Chunk first, final Chunk second) {
return Long.valueOf(first.getPosition())
.compareTo(second.getPosition());
}
}

View file

@ -1,195 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.util;
import org.jaudiotagger.audio.asf.data.*;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.asf.*;
import org.jaudiotagger.tag.reference.GenreTypes;
import java.util.Iterator;
import java.util.List;
/**
* This class provides functionality to convert
* {@link org.jaudiotagger.audio.asf.data.AsfHeader}objects into
* {@link org.jaudiotagger.tag.Tag}objects.<br>
*
* @author Christian Laireiter (liree)
*/
public final class TagConverter {
/**
* Hidden utility class constructor.
*/
private TagConverter() {
// Nothing to do.
}
/**
* This method assigns those tags of <code>tag</code> which are defined to
* be common by jaudiotagger. <br>
*
* @param tag The tag from which the values are gathered. <br>
* Assigned values are: <br>
* @param description The extended content description which should receive the
* values. <br>
* <b>Warning: </b> the common values will be replaced.
*/
public static void assignCommonTagValues(Tag tag,
MetadataContainer description) {
assert description.getContainerType() == ContainerType.EXTENDED_CONTENT;
MetadataDescriptor tmp;
if (!Utils.isBlank(tag.getFirst(FieldKey.ALBUM))) {
tmp = new MetadataDescriptor(description.getContainerType(),
AsfFieldKey.ALBUM.getFieldName(),
MetadataDescriptor.TYPE_STRING);
tmp.setStringValue(tag.getFirst(FieldKey.ALBUM));
description.removeDescriptorsByName(tmp.getName());
description.addDescriptor(tmp);
} else {
description.removeDescriptorsByName(AsfFieldKey.ALBUM
.getFieldName());
}
if (!Utils.isBlank(tag.getFirst(FieldKey.TRACK))) {
tmp = new MetadataDescriptor(description.getContainerType(),
AsfFieldKey.TRACK.getFieldName(),
MetadataDescriptor.TYPE_STRING);
tmp.setStringValue(tag.getFirst(FieldKey.TRACK));
description.removeDescriptorsByName(tmp.getName());
description.addDescriptor(tmp);
} else {
description.removeDescriptorsByName(AsfFieldKey.TRACK
.getFieldName());
}
if (!Utils.isBlank(tag.getFirst(FieldKey.YEAR))) {
tmp = new MetadataDescriptor(description.getContainerType(),
AsfFieldKey.YEAR.getFieldName(),
MetadataDescriptor.TYPE_STRING);
tmp.setStringValue(tag.getFirst(FieldKey.YEAR));
description.removeDescriptorsByName(tmp.getName());
description.addDescriptor(tmp);
} else {
description
.removeDescriptorsByName(AsfFieldKey.YEAR.getFieldName());
}
if (!Utils.isBlank(tag.getFirst(FieldKey.GENRE))) {
// Write Genre String value
tmp = new MetadataDescriptor(description.getContainerType(),
AsfFieldKey.GENRE.getFieldName(),
MetadataDescriptor.TYPE_STRING);
tmp.setStringValue(tag.getFirst(FieldKey.GENRE));
description.removeDescriptorsByName(tmp.getName());
description.addDescriptor(tmp);
Integer genreNum = GenreTypes.getInstanceOf().getIdForName(
tag.getFirst(FieldKey.GENRE));
// ..and if it is one of the standard genre types used the id as
// well
if (genreNum != null) {
tmp = new MetadataDescriptor(description.getContainerType(),
AsfFieldKey.GENRE_ID.getFieldName(),
MetadataDescriptor.TYPE_STRING);
tmp.setStringValue("(" + genreNum + ")");
description.removeDescriptorsByName(tmp.getName());
description.addDescriptor(tmp);
} else {
description.removeDescriptorsByName(AsfFieldKey.GENRE_ID
.getFieldName());
}
} else {
description.removeDescriptorsByName(AsfFieldKey.GENRE
.getFieldName());
description.removeDescriptorsByName(AsfFieldKey.GENRE_ID
.getFieldName());
}
}
/**
* This method creates a {@link Tag}and fills it with the contents of the
* given {@link AsfHeader}.<br>
*
* @param source The ASF header which contains the information. <br>
* @return A Tag with all its values.
*/
public static AsfTag createTagOf(AsfHeader source) {
// TODO do we need to copy here.
AsfTag result = new AsfTag(true);
for (int i = 0; i < ContainerType.values().length; i++) {
MetadataContainer current = source
.findMetadataContainer(ContainerType.values()[i]);
if (current != null) {
List<MetadataDescriptor> descriptors = current.getDescriptors();
for (MetadataDescriptor descriptor : descriptors) {
AsfTagField toAdd;
if (descriptor.getType() == MetadataDescriptor.TYPE_BINARY) {
if (descriptor.getName().equals(
AsfFieldKey.COVER_ART.getFieldName())) {
toAdd = new AsfTagCoverField(descriptor);
} else if (descriptor.getName().equals(
AsfFieldKey.BANNER_IMAGE.getFieldName())) {
toAdd = new AsfTagBannerField(descriptor);
} else {
toAdd = new AsfTagField(descriptor);
}
} else {
toAdd = new AsfTagTextField(descriptor);
}
result.addField(toAdd);
}
}
}
return result;
}
/**
* This method distributes the tags fields among the
* {@linkplain ContainerType#getOrdered()} {@linkplain MetadataContainer
* containers}.
*
* @param tag the tag with the fields to distribute.
* @return distribution
*/
public static MetadataContainer[] distributeMetadata(final AsfTag tag) {
final Iterator<AsfTagField> asfFields = tag.getAsfFields();
final MetadataContainer[] createContainers = MetadataContainerFactory
.getInstance().createContainers(ContainerType.getOrdered());
boolean assigned;
AsfTagField current;
while (asfFields.hasNext()) {
current = asfFields.next();
assigned = false;
for (int i = 0; !assigned && i < createContainers.length; i++) {
if (ContainerType.areInCorrectOrder(createContainers[i]
.getContainerType(), AsfFieldKey.getAsfFieldKey(
current.getId()).getHighestContainer())) {
if (createContainers[i].isAddSupported(current
.getDescriptor())) {
createContainers[i].addDescriptor(current
.getDescriptor());
assigned = true;
}
}
}
assert assigned;
}
return createContainers;
}
}

View file

@ -1,497 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.asf.util;
import org.jaudiotagger.audio.asf.data.AsfHeader;
import org.jaudiotagger.audio.asf.data.GUID;
import org.jaudiotagger.logging.ErrorMessage;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.GregorianCalendar;
/**
* Some static Methods which are used in several Classes. <br>
*
* @author Christian Laireiter
*/
public class Utils {
public static final long DIFF_BETWEEN_ASF_DATE_AND_JAVA_DATE = 11644470000000l;
/**
* Stores the default line separator of the current underlying system.
*/
public final static String LINE_SEPARATOR = System
.getProperty("line.separator"); //$NON-NLS-1$
/**
*
*/
private static final int MAXIMUM_STRING_LENGTH_ALLOWED = 32766;
/**
* This method checks given string will not exceed limit in bytes[] when
* converted UTF-16LE encoding (2 bytes per character) and checks whether
* the length doesn't exceed 65535 bytes. <br>
*
* @param value The string to check.
* @throws IllegalArgumentException If byte representation takes more than 65535 bytes.
*/
public static void checkStringLengthNullSafe(String value)
throws IllegalArgumentException {
if (value != null) {
if (value.length() > MAXIMUM_STRING_LENGTH_ALLOWED) {
throw new IllegalArgumentException(
ErrorMessage.WMA_LENGTH_OF_STRING_IS_TOO_LARGE
.getMsg((value.length() * 2)));
}
}
}
/**
* @param value String to check for null
* @return true unless string is too long
*/
public static boolean isStringLengthValidNullSafe(String value) {
if (value != null) {
if (value.length() > MAXIMUM_STRING_LENGTH_ALLOWED) {
return false;
}
}
return true;
}
/**
* effectively copies a specified amount of bytes from one stream to
* another.
*
* @param source stream to read from
* @param dest stream to write to
* @param amount amount of bytes to copy
* @throws IOException on I/O errors, and if the source stream depletes before all
* bytes have been copied.
*/
public static void copy(InputStream source, OutputStream dest, long amount)
throws IOException {
byte[] buf = new byte[8192];
long copied = 0;
while (copied < amount) {
int toRead = 8192;
if ((amount - copied) < 8192) {
toRead = (int) (amount - copied);
}
int read = source.read(buf, 0, toRead);
if (read == -1) {
throw new IOException(
"Inputstream has to continue for another "
+ (amount - copied) + " bytes.");
}
dest.write(buf, 0, read);
copied += read;
}
}
/**
* Copies all of the source to the destination.<br>
*
* @param source source to read from
* @param dest stream to write to
* @throws IOException on I/O errors.
*/
public static void flush(final InputStream source, final OutputStream dest)
throws IOException {
final byte[] buf = new byte[8192];
int read;
while ((read = source.read(buf)) != -1) {
dest.write(buf, 0, read);
}
}
/**
* This method will create a byte[] at the size of <code>byteCount</code>
* and insert the bytes of <code>value</code> (starting from lowset byte)
* into it. <br>
* You can easily create a Word (16-bit), DWORD (32-bit), QWORD (64 bit) out
* of the value, ignoring the original type of value, since java
* automatically performs transformations. <br>
* <b>Warning: </b> This method works with unsigned numbers only.
*
* @param value The value to be written into the result.
* @param byteCount The number of bytes the array has got.
* @return A byte[] with the size of <code>byteCount</code> containing the
* lower byte values of <code>value</code>.
*/
public static byte[] getBytes(final long value, final int byteCount) {
byte[] result = new byte[byteCount];
for (int i = 0; i < result.length; i++) {
result[i] = (byte) ((value >>> (i * 8)) & 0xFF);
}
return result;
}
/**
* Convenience method to convert the given string into a byte sequence which
* has the format of the charset given.
*
* @param source string to convert.
* @param charset charset to apply
* @return the source's binary representation according to the charset.
*/
public static byte[] getBytes(final String source, final Charset charset) {
assert charset != null;
assert source != null;
final ByteBuffer encoded = charset.encode(source);
final byte[] result = new byte[encoded.limit()];
encoded.rewind();
encoded.get(result);
return result;
}
/**
* Since date values in ASF files are given in 100 ns steps since first
* january of 1601 a little conversion must be done. <br>
* This method converts a date given in described manner to a calendar.
*
* @param fileTime
* Time in 100ns since 1 jan 1601
* @return Calendar holding the date representation.
*/
/* Old method that ran very slowely and doesnt logical correct, how does dividing something
at 10-4 by 10,000 convert it to 10 -3
public static GregorianCalendar getDateOf(final BigInteger fileTime) {
final GregorianCalendar result = new GregorianCalendar(1601, 0, 1);
// lose anything beyond milliseconds, because calendar can't handle
// less value
BigInteger time = fileTime.divide(new BigInteger("10000")); //$NON-NLS-1$
final BigInteger maxInt = new BigInteger(String
.valueOf(Integer.MAX_VALUE));
while (time.compareTo(maxInt) > 0) {
result.add(Calendar.MILLISECOND, Integer.MAX_VALUE);
time = time.subtract(maxInt);
}
result.add(Calendar.MILLISECOND, time.intValue());
return result;
}
*/
/**
* Date values in ASF files are given in 100 ns (10 exp -4) steps since first
*
* @param fileTime Time in 100ns since 1 jan 1601
* @return Calendar holding the date representation.
*/
public static GregorianCalendar getDateOf(final BigInteger fileTime) {
final GregorianCalendar result = new GregorianCalendar();
// Divide by 10 to convert from -4 to -3 (millisecs)
BigInteger time = fileTime.divide(new BigInteger("10"));
// Construct Date taking into the diff between 1601 and 1970
Date date = new Date(time.longValue() - DIFF_BETWEEN_ASF_DATE_AND_JAVA_DATE);
result.setTime(date);
return result;
}
/**
* Tests if the given string is <code>null</code> or just contains
* whitespace characters.
*
* @param toTest String to test.
* @return see description.
*/
public static boolean isBlank(String toTest) {
if (toTest == null) {
return true;
}
for (int i = 0; i < toTest.length(); i++) {
if (!Character.isWhitespace(toTest.charAt(i))) {
return false;
}
}
return true;
}
/**
* Reads 8 bytes from stream and interprets them as a UINT64 which is
* returned as {@link BigInteger}.<br>
*
* @param stream stream to readm from.
* @return a BigInteger which represents the read 8 bytes value.
* @throws IOException if problem reading bytes
*/
public static BigInteger readBig64(InputStream stream) throws IOException {
byte[] bytes = new byte[8];
byte[] oa = new byte[8];
int read = stream.read(bytes);
if (read != 8) {
// 8 bytes mandatory.
throw new EOFException();
}
for (int i = 0; i < bytes.length; i++) {
oa[7 - i] = bytes[i];
}
return new BigInteger(oa);
}
/**
* Reads <code>size</code> bytes from the stream.<br>
*
* @param stream stream to read from.
* @param size amount of bytes to read.
* @return the read bytes.
* @throws IOException on I/O errors.
*/
public static byte[] readBinary(InputStream stream, long size)
throws IOException {
byte[] result = new byte[(int) size];
stream.read(result);
return result;
}
/**
* This method reads a UTF-16 String, which length is given on the number of
* characters it consists of. <br>
* The stream must be at the number of characters. This number contains the
* terminating zero character (UINT16).
*
* @param stream Input source
* @return String
* @throws IOException read errors
*/
public static String readCharacterSizedString(InputStream stream)
throws IOException {
StringBuilder result = new StringBuilder();
int strLen = readUINT16(stream);
int character = stream.read();
character |= stream.read() << 8;
do {
if (character != 0) {
result.append((char) character);
character = stream.read();
character |= stream.read() << 8;
}
} while (character != 0 || (result.length() + 1) > strLen);
if (strLen != (result.length() + 1)) {
throw new IllegalStateException(
"Invalid Data for current interpretation"); //$NON-NLS-1$
}
return result.toString();
}
/**
* This method reads a UTF-16 encoded String. <br>
* For the use this method the number of bytes used by current string must
* be known. <br>
* The ASF specification recommends that those strings end with a
* terminating zero. However it also says that it is not always the case.
*
* @param stream Input source
* @param strLen Number of bytes the String may take.
* @return read String.
* @throws IOException read errors.
*/
public static String readFixedSizeUTF16Str(InputStream stream, int strLen)
throws IOException {
byte[] strBytes = new byte[strLen];
int read = stream.read(strBytes);
if (read == strBytes.length) {
if (strBytes.length >= 2) {
/*
* Zero termination is recommended but optional. So check and
* if, remove.
*/
if (strBytes[strBytes.length - 1] == 0
&& strBytes[strBytes.length - 2] == 0) {
byte[] copy = new byte[strBytes.length - 2];
System.arraycopy(strBytes, 0, copy, 0, strBytes.length - 2);
strBytes = copy;
}
}
return new String(strBytes, "UTF-16LE");
}
throw new IllegalStateException(
"Couldn't read the necessary amount of bytes.");
}
/**
* This Method reads a GUID (which is a 16 byte long sequence) from the
* given <code>raf</code> and creates a wrapper. <br>
* <b>Warning </b>: <br>
* There is no way of telling if a byte sequence is a guid or not. The next
* 16 bytes will be interpreted as a guid, whether it is or not.
*
* @param stream Input source.
* @return A class wrapping the guid.
* @throws IOException happens when the file ends before guid could be extracted.
*/
public static GUID readGUID(InputStream stream) throws IOException {
if (stream == null) {
throw new IllegalArgumentException("Argument must not be null"); //$NON-NLS-1$
}
int[] binaryGuid = new int[GUID.GUID_LENGTH];
for (int i = 0; i < binaryGuid.length; i++) {
binaryGuid[i] = stream.read();
}
return new GUID(binaryGuid);
}
/**
* Reads 2 bytes from stream and interprets them as UINT16.<br>
*
* @param stream stream to read from.
* @return UINT16 value
* @throws IOException on I/O Errors.
*/
public static int readUINT16(InputStream stream) throws IOException {
int result = stream.read();
result |= stream.read() << 8;
return result;
}
/**
* Reads 4 bytes from stream and interprets them as UINT32.<br>
*
* @param stream stream to read from.
* @return UINT32 value
* @throws IOException on I/O Errors.
*/
public static long readUINT32(InputStream stream) throws IOException {
long result = 0;
for (int i = 0; i <= 24; i += 8) {
// Warning, always cast to long here. Otherwise it will be
// shifted as int, which may produce a negative value, which will
// then be extended to long and assign the long variable a negative
// value.
result |= (long) stream.read() << i;
}
return result;
}
/**
* Reads long as little endian.
*
* @param stream Data source
* @return long value
* @throws IOException read error, or eof is reached before long is completed
*/
public static long readUINT64(InputStream stream) throws IOException {
long result = 0;
for (int i = 0; i <= 56; i += 8) {
// Warning, always cast to long here. Otherwise it will be
// shifted as int, which may produce a negative value, which will
// then be extended to long and assign the long variable a negative
// value.
result |= (long) stream.read() << i;
}
return result;
}
/**
* This method reads a UTF-16 encoded String, beginning with a 16-bit value
* representing the number of bytes needed. The String is terminated with as
* 16-bit ZERO. <br>
*
* @param stream Input source
* @return read String.
* @throws IOException read errors.
*/
public static String readUTF16LEStr(InputStream stream) throws IOException {
int strLen = readUINT16(stream);
byte[] buf = new byte[strLen];
int read = stream.read(buf);
if (read == strLen || (strLen == 0 && read == -1)) {
/*
* Check on zero termination
*/
if (buf.length >= 2) {
if (buf[buf.length - 1] == 0 && buf[buf.length - 2] == 0) {
byte[] copy = new byte[buf.length - 2];
System.arraycopy(buf, 0, copy, 0, buf.length - 2);
buf = copy;
}
}
return new String(buf, AsfHeader.ASF_CHARSET.name());
}
throw new IllegalStateException(
"Invalid Data for current interpretation"); //$NON-NLS-1$
}
/**
* Writes the given value as UINT16 into the stream.
*
* @param number value to write.
* @param out stream to write into.
* @throws IOException On I/O errors
*/
public static void writeUINT16(int number, OutputStream out)
throws IOException {
if (number < 0) {
throw new IllegalArgumentException("positive value expected."); //$NON-NLS-1$
}
byte[] toWrite = new byte[2];
for (int i = 0; i <= 8; i += 8) {
toWrite[i / 8] = (byte) ((number >> i) & 0xFF);
}
out.write(toWrite);
}
/**
* Writes the given value as UINT32 into the stream.
*
* @param number value to write.
* @param out stream to write into.
* @throws IOException On I/O errors
*/
public static void writeUINT32(long number, OutputStream out)
throws IOException {
if (number < 0) {
throw new IllegalArgumentException("positive value expected."); //$NON-NLS-1$
}
byte[] toWrite = new byte[4];
for (int i = 0; i <= 24; i += 8) {
toWrite[i / 8] = (byte) ((number >> i) & 0xFF);
}
out.write(toWrite);
}
/**
* Writes the given value as UINT64 into the stream.
*
* @param number value to write.
* @param out stream to write into.
* @throws IOException On I/O errors
*/
public static void writeUINT64(long number, OutputStream out)
throws IOException {
if (number < 0) {
throw new IllegalArgumentException("positive value expected."); //$NON-NLS-1$
}
byte[] toWrite = new byte[8];
for (int i = 0; i <= 56; i += 8) {
toWrite[i / 8] = (byte) ((number >> i) & 0xFF);
}
out.write(toWrite);
}
}

View file

@ -1,19 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html long="en">
<head>
</head>
<body bgcolor="white">
Utility classes for data components of the Microsoft Advanced Systems Format header.
<br>
<!-- package.html by Gary McGath -->
<!-- Put @see and @since tags down here. -->
</body>
</html>

View file

@ -1,57 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.exceptions;
/**
* This exception is thrown if an audio file cannot be read.<br>
* Causes may be invalid data or IO errors.
*
* @author Raphaël Slinckx
*/
public class CannotReadException extends Exception {
/**
* Creates an instance.
*/
public CannotReadException() {
super();
}
public CannotReadException(Throwable ex) {
super(ex);
}
/**
* Creates an instance.
*
* @param message The message.
*/
public CannotReadException(String message) {
super(message);
}
/**
* Creates an instance.
*
* @param message The error message.
* @param cause The throwable causing this exception.
*/
public CannotReadException(String message, Throwable cause) {
super(message, cause);
}
}

View file

@ -1,37 +0,0 @@
package org.jaudiotagger.audio.exceptions;
/**
* This exception should be thrown idf it appears the file is a video file, jaudiotagger only supports audio
* files.
*/
public class CannotReadVideoException extends CannotReadException {
/**
* Creates an instance.
*/
public CannotReadVideoException() {
super();
}
public CannotReadVideoException(Throwable ex) {
super(ex);
}
/**
* Creates an instance.
*
* @param message The message.
*/
public CannotReadVideoException(String message) {
super(message);
}
/**
* Creates an instance.
*
* @param message The error message.
* @param cause The throwable causing this exception.
*/
public CannotReadVideoException(String message, Throwable cause) {
super(message, cause);
}
}

View file

@ -1,68 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.exceptions;
/**
* This exception is thrown if the writing process of an audio file failed.
*
* @author Rapha<EFBFBD>l Slinckx
*/
public class CannotWriteException extends Exception {
/**
* (overridden)
*
* @see Exception#Exception()
*/
public CannotWriteException() {
super();
}
/**
* (overridden)
*
* @param message
* @see Exception#Exception(java.lang.String)
*/
public CannotWriteException(String message) {
super(message);
}
/**
* (overridden)
*
* @param message
* @param cause
* @see Exception#Exception(java.lang.String, java.lang.Throwable)
*/
public CannotWriteException(String message, Throwable cause) {
super(message, cause);
}
/**
* (overridden)
*
* @param cause
* @see Exception#Exception(java.lang.Throwable)
*/
public CannotWriteException(Throwable cause) {
super(cause);
}
}

View file

@ -1,18 +0,0 @@
/**
* @author : Paul Taylor
* <p/>
* Version @version:$Id: InvalidAudioFrameException.java 345 2007-08-07 16:14:03Z paultaylor $
* Date :${DATE}
* <p/>
* Jaikoz Copyright Copyright (C) 2003 -2005 JThink Ltd
*/
package org.jaudiotagger.audio.exceptions;
/**
* Thrown if portion of file thought to be an AudioFrame is found to not be.
*/
public class InvalidAudioFrameException extends Exception {
public InvalidAudioFrameException(String message) {
super(message);
}
}

View file

@ -1,10 +0,0 @@
package org.jaudiotagger.audio.exceptions;
/**
* Thrown if when trying to read box id the length doesn't make any sense
*/
public class InvalidBoxHeaderException extends RuntimeException {
public InvalidBoxHeaderException(String message) {
super(message);
}
}

View file

@ -1,72 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2003-2005 Christian Laireiter <liree@web.de>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.exceptions;
/**
* This exception is thrown if a
* {@link org.jaudiotagger.audio.generic.AudioFileModificationListener} wants to
* prevent the &quote;entagged audio library&quote; from actually finishing its
* operation.<br>
* This exception can be used in all methods but
* {@link org.jaudiotagger.audio.generic.AudioFileModificationListener#fileOperationFinished(java.io.File)}.
*
* @author Christian Laireiter
*/
public class ModifyVetoException extends Exception {
/**
* (overridden)
*/
public ModifyVetoException() {
super();
}
/**
* (overridden)
*
* @param message
* @see Exception#Exception(java.lang.String)
*/
public ModifyVetoException(String message) {
super(message);
}
/**
* (overridden)
*
* @param message
* @param cause
* @see Exception#Exception(java.lang.String, java.lang.Throwable)
*/
public ModifyVetoException(String message, Throwable cause) {
super(message, cause);
}
/**
* (overridden)
*
* @param cause
* @see Exception#Exception(java.lang.Throwable)
*/
public ModifyVetoException(Throwable cause) {
super(cause);
}
}

View file

@ -1,11 +0,0 @@
package org.jaudiotagger.audio.exceptions;
/**
* Thrown if when trying to read box id just finds nulls
* Normally an error, but if occurs at end of file we allow it
*/
public class NullBoxIdException extends RuntimeException {
public NullBoxIdException(String message) {
super(message);
}
}

View file

@ -1,49 +0,0 @@
/**
* @author : Paul Taylor
* @author : Eric Farng
* <p>
* Version @version:$Id: ReadOnlyFileException.java 345 2007-08-07 16:14:03Z paultaylor $
* <p>
* MusicTag Copyright (C)2003,2004
* <p>
* 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.
* <p>
* 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.
* <p>
* 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 org.jaudiotagger.audio.exceptions;
/**
* This is the exception when try and access a read only file
*/
public class ReadOnlyFileException extends Exception {
/**
* Creates a new ReadOnlyException datatype.
*/
public ReadOnlyFileException() {
}
public ReadOnlyFileException(Throwable ex) {
super(ex);
}
/**
* Creates a new ReadOnlyException datatype.
*
* @param msg the detail message.
*/
public ReadOnlyFileException(String msg) {
super(msg);
}
public ReadOnlyFileException(String msg, Throwable ex) {
super(msg, ex);
}
}

View file

@ -1,13 +0,0 @@
package org.jaudiotagger.audio.exceptions;
import java.io.IOException;
/**
* Should be thrown when unable to create a file when it is expected it should be creatable. For example because
* you dont have permission to write to the folder that it is in.
*/
public class UnableToCreateFileException extends IOException {
public UnableToCreateFileException(String message) {
super(message);
}
}

View file

@ -1,13 +0,0 @@
package org.jaudiotagger.audio.exceptions;
import java.io.IOException;
/**
* Should be thrown when unable to modify a file when it is expected it should bemodifiable. For example because
* you dont have permission to modify files in the folder that it is in.
*/
public class UnableToModifyFileException extends IOException {
public UnableToModifyFileException(String message) {
super(message);
}
}

View file

@ -1,13 +0,0 @@
package org.jaudiotagger.audio.exceptions;
import java.io.IOException;
/**
* Should be thrown when unable to rename a file when it is expected it should rename. For example could occur on Vista
* because you do not have Special Permission 'Delete' set to Denied.
*/
public class UnableToRenameFileException extends IOException {
public UnableToRenameFileException(String message) {
super(message);
}
}

View file

@ -1,19 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html long="en">
<head>
</head>
<body bgcolor="white">
Exceptions defined for Jaudiotagger.
<br>
<!-- package.html by Gary McGath -->
<!-- Put @see and @since tags down here. -->
</body>
</html>

View file

@ -1,15 +0,0 @@
package org.jaudiotagger.audio.flac;
import org.jaudiotagger.audio.generic.GenericAudioHeader;
public class FlacAudioHeader extends GenericAudioHeader {
private String md5;
public String getMd5() {
return md5;
}
public void setMd5(String md5) {
this.md5 = md5;
}
}

View file

@ -1,44 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.flac;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.generic.AudioFileReader;
import org.jaudiotagger.audio.generic.GenericAudioHeader;
import org.jaudiotagger.tag.Tag;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* Read encoding and tag info for Flac file (open source lossless encoding)
*/
public class FlacFileReader extends AudioFileReader {
private FlacInfoReader ir = new FlacInfoReader();
private FlacTagReader tr = new FlacTagReader();
protected GenericAudioHeader getEncodingInfo(RandomAccessFile raf) throws CannotReadException, IOException {
return ir.read(raf);
}
protected Tag getTag(RandomAccessFile raf) throws CannotReadException, IOException {
return tr.read(raf);
}
}

View file

@ -1,44 +0,0 @@
/*
* Entagged Audio Tag library
* Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jaudiotagger.audio.flac;
import org.jaudiotagger.audio.exceptions.CannotWriteException;
import org.jaudiotagger.audio.generic.AudioFileWriter;
import org.jaudiotagger.tag.Tag;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* Write/delete tag info for Flac file (opensource lossless encoding)
*/
public class FlacFileWriter extends AudioFileWriter {
private FlacTagWriter tw = new FlacTagWriter();
protected void writeTag(Tag tag, RandomAccessFile raf, RandomAccessFile rafTemp) throws CannotWriteException, IOException {
tw.write(tag, raf, rafTemp);
}
protected void deleteTag(RandomAccessFile raf, RandomAccessFile tempRaf) throws CannotWriteException, IOException {
tw.delete(raf, tempRaf);
}
}

Some files were not shown because too many files have changed in this diff Show more