/* AAudio.java
Purpose:
Description:
History:
Wed Nov 16 15:15:40 2005, Created by tomyeh
Copyright (C) 2005 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under LGPL Version 2.1 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.sound;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.Reader;
import java.io.IOException;
import java.net.URL;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import org.zkoss.lang.SystemException;
import org.zkoss.util.media.ContentTypes;
import org.zkoss.io.NullInputStream;
/**
* Represents an audio.
* Unlike javax.sound.AudioClip, this class is used only to hold the raw
* data as opaque rather than manipulate the sound.
*
* <p>In other words, it is used to retrieve and store the opaque data
* as polymorphic thru the {@link org.zkoss.util.media.Media} interface.
*
* <p>AAudio is serializable, but, if you are using InputStream,
* you have to extend this class, and provide the implementation to
* serialize and deserialize {@link #_isdata}. (Since 5.0.11)
*
* @author tomyeh
*/
public class AAudio implements Audio, java.io.Serializable {
/** Used if you want to implement a media whose input stream is created
* dynamically each time {@link #getStreamData} is called.
* @see #AAudio(String,,InputStream)
*/
protected static final InputStream DYNAMIC_STREAM = new NullInputStream();
/** The raw data in byte array.
* Exactly one of {@link #_data} and {@link #_isdata} is not null.
*/
private final byte[] _data;
/** The raw data in stream (or {@link #DYNAMIC_STREAM}).
* Exactly one of {@link #_data} and {@link #_isdata} is not null.
*/
protected final transient InputStream _isdata;
/** The URL of the data.
*/
private final URL _url;
/** The file of the data.
*/
private final File _file;
/** The format name, e.g., "jpeg", "gif" and "png". */
private String _format;
/** The content type. */
private String _ctype;
/** The name (usually filename). */
private final String _name;
public AAudio(String name, byte[] data) throws IOException {
if (data == null)
throw new IllegalArgumentException("null data");
_name = name;
_data = data;
_isdata = null;
_url = null;
_file = null;
}
/**
* Creates an instance of an audio with an input stream.
* If the stream shall be created each time {@link #getStreamData} is called,
* you can pass {@link #DYNAMIC_STREAM} to the data argument, and then
* override {@link #getStreamData}.
*
* <p>Note: the caller of {@link #getStreamData} has to close
* the returned input stream.
*/
public AAudio(String name, InputStream isdata) throws IOException {
if (isdata == null)
throw new IllegalArgumentException("null stream");
_name = name;
_isdata = isdata;
_data = null;
_url = null;
_file = null;
}
/** Constructs an audio with an URL.
*/
public AAudio(URL url) {
if (url == null)
throw new IllegalArgumentException("null url");
_name = getName(url);
_url = url;
_isdata = DYNAMIC_STREAM;
_data = null;
_file = null;
}
/** Constructs an audio with a file.
*/
public AAudio(File file) {
if (file == null)
throw new IllegalArgumentException("null url");
_name = file.getName();
_file = file;
_isdata = DYNAMIC_STREAM;
_data = null;
_url = null;
}
/** Constructs an audio with a file name.
*/
public AAudio(String filename) throws IOException {
this(new File(filename));
}
/**
* Creates an instance of an audio with an input stream.
* If the stream shall be created each time {@link #getStreamData} is called,
* you can pass {@link #DYNAMIC_STREAM} to the data argument, and then
* override {@link #getStreamData}.
*
* <p>Note: the caller of {@link #getStreamData} has to close
* the returned input stream.
*/
public AAudio(InputStream is) throws IOException {
this(null, is);
}
private static String getName(URL url) {
String name = url.getPath();
if (name != null) {
{
final int j = name.lastIndexOf(File.pathSeparatorChar);
if (j >= 0) name = name.substring(j + 1);
}
if (File.pathSeparatorChar != '/') {
final int j = name.lastIndexOf('/');
if (j >= 0) name = name.substring(j + 1);
}
}
return name;
}
private static String getContentType(String format) {
final String ctype = ContentTypes.getContentType(format);
return ctype != null ? ctype: "audio/" + format;
}
private static String getFormatByName(String name) {
if (name != null) {
final int j = name.lastIndexOf('.') + 1,
k = name.lastIndexOf('/') + 1;
if (j > k && j < name.length())
return name.substring(j);
}
return null;
}
//-- Media --//
public final boolean isBinary() {
return true;
}
public final boolean inMemory() {
return _data != null;
}
public byte[] getByteData() {
if (_data == null)
throw new IllegalStateException("Use getStreamData() instead");
return _data;
}
/** Always throws IllegalStateException.
*/
public final String getStringData() {
throw newIllegalStateException();
}
/** Returns the data in the input stream.
*
* <p>Note: the caller has to invoke {@link InputStream#close}
* after using the input stream returned by {@link #getStreamData}.
*/
public InputStream getStreamData() {
try {
if (_url != null) {
InputStream is = _url.openStream();
return is != null ? new BufferedInputStream(is): null;
}
if (_file != null)
return new BufferedInputStream(new FileInputStream(_file));
} catch (java.io.IOException ex) {
throw new SystemException("Unable to read "
+(_url != null ? _url.toString(): _file.toString()), ex);
}
if (_isdata != null) return _isdata;
return new ByteArrayInputStream(_data);
}
/** Not supported. It always throws IllegalStateException.
*/
public final Reader getReaderData() {
throw newIllegalStateException();
}
private final IllegalStateException newIllegalStateException() {
return new IllegalStateException(
_isdata != null ? "Use getStreamData() instead":
"Use getByteData() instead");
}
public final String getName() {
return _name;
}
public String getFormat() {
if (_format == null) {
try {
_format = getFormat0();
} catch (IOException ex) {
throw new SystemException("Unable to read", ex);
}
}
return _format;
}
private String getFormat0() throws IOException {
String format = null;
if (_data != null) {
try {
format =
AudioSystem.getAudioFileFormat(new ByteArrayInputStream(_data))
.getType().getExtension();
} catch (UnsupportedAudioFileException ex) {
format = getFormatByName(_name);
if (format == null)
throw (IOException)new IOException().initCause(ex);
}
} else if (_file != null) {
try {
format = AudioSystem.getAudioFileFormat(_file)
.getType().getExtension();
} catch (UnsupportedAudioFileException ex) {
format = getFormatByName(_name);
if (format == null)
throw (IOException)new IOException().initCause(ex);
}
} else if (_url != null) {
try {
format = AudioSystem.getAudioFileFormat(_url)
.getType().getExtension();
} catch (UnsupportedAudioFileException ex) {
format = getFormatByName(_name);
if (format == null)
throw (IOException)new IOException().initCause(ex);
}
} else {
try {
format = AudioSystem.getAudioFileFormat(
_isdata == DYNAMIC_STREAM ? getStreamData(): _isdata)
.getType().getExtension();
} catch (UnsupportedAudioFileException ex) {
format = getFormatByName(_name);
if (format == null)
throw (IOException)new IOException().initCause(ex);
}
}
return format;
}
public String getContentType() {
if (_ctype == null) {
_ctype = getContentType(getFormat());
}
return _ctype;
}
public boolean isContentDisposition() {
return true;
}
}