/* * ModeShape (http://www.modeshape.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.modeshape.sequencer.audio; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.jaudiotagger.audio.AudioFile; import org.jaudiotagger.audio.AudioHeader; import org.jaudiotagger.audio.asf.AsfFileReader; import org.jaudiotagger.audio.flac.FlacFileReader; import org.jaudiotagger.audio.mp3.MP3AudioHeader; import org.jaudiotagger.audio.mp3.MP3FileReader; import org.jaudiotagger.audio.mp4.Mp4AudioHeader; import org.jaudiotagger.audio.mp4.Mp4FileReader; import org.jaudiotagger.audio.ogg.OggFileReader; import org.jaudiotagger.audio.wav.WavFileReader; import org.jaudiotagger.tag.FieldKey; import org.jaudiotagger.tag.Tag; import org.jaudiotagger.tag.datatype.Artwork; import org.modeshape.common.util.IoUtil; /** * Utility for extracting metadata from audio files. * * @since 5.1 */ public class AudioMetadata { /** * Return value of {@link #getFormat()} for MP3 streams. AudioMetadata can extract metadata and audio information from * MP3 streams. */ public static final int FORMAT_MP3 = 0; /** * Return value of {@link #getFormat()} for MP4 streams. AudioMetadata can extract metadata and audio information from * MP4 streams. */ public static final int FORMAT_MP4 = 1; /** * Return value of {@link #getFormat()} for Vorbis streams. AudioMetadata can extract metadata and audio information from * Vorbis stream and Ogg container. */ public static final int FORMAT_VORBIS = 2; /** * Return value of {@link #getFormat()} for FLAC streams. AudioMetadata can extract metadata and audio information from * FLAC stream and Ogg container. */ public static final int FORMAT_FLAC = 3; /** * Return value of {@link #getFormat()} for WMA streams. AudioMetadata can extract metadata and audio information from * WMA stream and ASF container. */ public static final int FORMAT_WMA = 4; /** * Return value of {@link #getFormat()} for WAVE streams. AudioMetadata can extract metadata and audio information from * WAVE stream and ASF container. */ public static final int FORMAT_WAV = 5; public static final int FORMAT_UNSUPPORTED = -1; /** * The names of the MIME types for all supported file formats. */ static final String[] MIME_TYPE_STRINGS = {"audio/mpeg", "audio/mp4", "video/mp4", "video/quicktime", "audio/vorbis", "audio/x-vorbis", "audio/ogg", "audio/flac", "audio/x-flac", "audio/vnd.ms-asf", "audio/x-ms-wma", "audio/x-ms-asf", "audio/x-wav", "audio/wav", "audio/wave"}; /** * The extensions of all supported file formats. The FORMAT_xyz int constants can be used as index values for * this array. */ static final String[] FORMAT_NAMES = {"mp3", "mp4", "ogg", "flac", "wma", "wav", "real"}; private int format; private InputStream in; private AudioFile audioFile; private Long bitrate; private Integer sampleRate; private Double duration; private String channels; private String title; private String artist; private String album; private String year; private String comment; private String track; private String genre; private List<AudioMetadataArtwork> artwork; private String mimeType; public AudioMetadata( InputStream inputStream, final String mimeType ) { this.in = inputStream; this.mimeType = mimeType; } /* * Check that given file is supported by this sequencer and find out the format. */ public boolean check() throws Exception { format = FORMAT_UNSUPPORTED; // create a temporary copy from input File fileCopy = File.createTempFile("modeshape-sequencer-audio", ".tmp"); IoUtil.write(in, new BufferedOutputStream(new FileOutputStream(fileCopy))); if (mimeType.startsWith("audio/mpeg")) { format = FORMAT_MP3; audioFile = new MP3FileReader().read(fileCopy); } else if (mimeType.startsWith("audio/vorbis") || mimeType.startsWith("audio/x-vorbis") || mimeType.startsWith("audio/ogg")) { format = FORMAT_VORBIS; audioFile = new OggFileReader().read(fileCopy); } else if (mimeType.startsWith("audio/flac") || mimeType.startsWith("audio/x-flac")) { format = FORMAT_FLAC; audioFile = new FlacFileReader().read(fileCopy); } else if (mimeType.equals("audio/mp4") || mimeType.equals("video/mp4") || mimeType.equals("video/quicktime")) { format = FORMAT_MP4; audioFile = new Mp4FileReader().read(fileCopy); } else if (mimeType.equals("audio/x-ms-wma")) { format = FORMAT_WMA; audioFile = new AsfFileReader().read(fileCopy); } else if (mimeType.startsWith("audio/x-wav") || mimeType.startsWith("audio/wav")) { format = FORMAT_WAV; audioFile = new WavFileReader().read(fileCopy); } // try to delete the file immediately or on JVM exit boolean deleted = false; try { deleted = fileCopy.delete(); } catch (SecurityException e) { // ignore } if (!deleted) { fileCopy.deleteOnExit(); } return checkSupportedAudio(); } /** * Parse tags common for all audio files. */ private boolean checkSupportedAudio() { AudioHeader header = audioFile.getAudioHeader(); bitrate = header.getBitRateAsNumber(); sampleRate = header.getSampleRateAsNumber(); channels = header.getChannels(); if (header.getChannels().toLowerCase().contains("stereo")) { channels = "2"; } if (header instanceof MP3AudioHeader) { duration = ((MP3AudioHeader) header).getPreciseTrackLength(); } else if (header instanceof Mp4AudioHeader) { duration = (double) ((Mp4AudioHeader) header).getPreciseLength(); } else { duration = (double) header.getTrackLength(); } // generic frames Tag tag = audioFile.getTag(); artist = tag.getFirst(FieldKey.ARTIST); album = tag.getFirst(FieldKey.ALBUM); title = tag.getFirst(FieldKey.TITLE); comment = tag.getFirst(FieldKey.COMMENT); year = tag.getFirst(FieldKey.YEAR); track = tag.getFirst(FieldKey.TRACK); genre = tag.getFirst(FieldKey.GENRE); artwork = new ArrayList<>(); for (Artwork a : tag.getArtworkList()) { AudioMetadataArtwork ama = new AudioMetadataArtwork(); ama.setMimeType(a.getMimeType()); if (a.getPictureType() >= 0) { ama.setType(a.getPictureType()); } ama.setData(a.getBinaryData()); artwork.add(ama); } return true; } /** * If {@link #check()} was successful, returns the audio format as one of the FORMAT_xyz constants from this class. Use * {@link #getFormatName()} to get a textual description of the file format. * * @return file format as a FORMAT_xyz constant */ public int getFormat() { return format; } /** * If {@link #check()} was successful, returns the audio format's name. Use {@link #getFormat()} to get a unique number. * * @return file format name */ public String getFormatName() { if (format >= 0 && format < FORMAT_NAMES.length) { return FORMAT_NAMES[format]; } return "?"; } public Long getBitrate() { return bitrate; } public Integer getSampleRate() { return sampleRate; } public String getChannels() { return channels; } public Double getDuration() { return duration; } public String getTitle() { return title; } public String getArtist() { return artist; } public String getAlbum() { return album; } public String getYear() { return year; } public String getComment() { return comment; } public String getTrack() { return track; } public String getGenre() { return genre; } public List<AudioMetadataArtwork> getArtwork() { return artwork; } }