package org.mobicents.media.format; import org.mobicents.media.Format; import org.mobicents.media.FormatUtils; /** * Standard JMF class -- see <a href="http://java.sun.com/products/java-media/jmf/2.1.1/apidocs/javax/media/format/AudioFormat.html" * target="_blank">this class in the JMF Javadoc</a>. Coding complete. * * @author Ken Larson * */ public class AudioFormat extends Format { public static final int BIG_ENDIAN = 1; public static final int LITTLE_ENDIAN = 0; public static final int SIGNED = 1; public static final int UNSIGNED = 0; protected double sampleRate = NOT_SPECIFIED; protected int sampleSizeInBits = NOT_SPECIFIED; protected int channels = NOT_SPECIFIED; protected int endian = NOT_SPECIFIED; protected int signed = NOT_SPECIFIED; protected double frameRate = NOT_SPECIFIED; protected int frameSizeInBits = NOT_SPECIFIED; public static final String LINEAR = "LINEAR"; public static final String ULAW = "ULAW"; public static final String ULAW_RTP = "ULAW/rtp"; public static final String ALAW = "ALAW"; // strange that this is lower // case and ULAW is not... public static final String SPEEX = "SPEEX"; public static final String IMA4 = "ima4"; public static final String IMA4_MS = "ima4/ms"; public static final String MSADPCM = "msadpcm"; public static final String DVI = "dvi"; public static final String G723 = "g723"; public static final String G728 = "g728"; public static final String G729 = "G729"; public static final String G729A = "g729a"; public static final String GSM = "GSM"; public static final String GSM_MS = "gsm/ms"; public static final String MAC3 = "MAC3"; public static final String MAC6 = "MAC6"; public static final String TRUESPEECH = "truespeech"; public static final String MSNAUDIO = "msnaudio"; public static final String MPEGLAYER3 = "mpeglayer3"; public static final String VOXWAREAC8 = "voxwareac8"; public static final String VOXWAREAC10 = "voxwareac10"; public static final String VOXWAREAC16 = "voxwareac16"; public static final String VOXWAREAC20 = "voxwareac20"; public static final String VOXWAREMETAVOICE = "voxwaremetavoice"; public static final String VOXWAREMETASOUND = "voxwaremetasound"; public static final String VOXWARERT29H = "voxwarert29h"; public static final String VOXWAREVR12 = "voxwarevr12"; public static final String VOXWAREVR18 = "voxwarevr18"; public static final String VOXWARETQ40 = "voxwaretq40"; public static final String VOXWARETQ60 = "voxwaretq60"; public static final String MSRT24 = "msrt24"; public static final String MPEG = "mpegaudio"; public static final String DOLBYAC3 = "dolbyac3"; // TODO: these are not // used in this impl, // only added for // serialization // compatibility. // TODO: probably used for computeDuration, to make it faster. double multiplier = -1.0; int margin = 0; boolean init = false; private int hash; public AudioFormat(String encoding) { super(encoding); super.dataType = super.byteArray; hash = encoding.hashCode(); } public AudioFormat(String encoding, double sampleRate, int sampleSizeInBits, int channels) { super(encoding); this.dataType = byteArray; this.sampleRate = sampleRate; this.sampleSizeInBits = sampleSizeInBits; this.channels = channels; hash = encoding.hashCode() + (int) sampleRate + sampleSizeInBits + channels; } public AudioFormat(String encoding, double sampleRate, int sampleSizeInBits, int channels, int endian, int signed) { super(encoding); this.dataType = byteArray; this.sampleRate = sampleRate; this.sampleSizeInBits = sampleSizeInBits; this.channels = channels; this.endian = endian; this.signed = signed; hash = encoding.hashCode() + (int) sampleRate + sampleSizeInBits + channels + endian + signed; } public AudioFormat(String encoding, double sampleRate, int sampleSizeInBits, int channels, int endian, int signed, int frameSizeInBits, double frameRate, Class dataType) { super(encoding, dataType); this.sampleRate = sampleRate; this.sampleSizeInBits = sampleSizeInBits; this.channels = channels; this.endian = endian; this.signed = signed; this.frameSizeInBits = frameSizeInBits; this.frameRate = frameRate; hash = encoding.hashCode() + (int) sampleRate + sampleSizeInBits + channels + endian + signed; } public double getSampleRate() { return sampleRate; } public int getSampleSizeInBits() { return sampleSizeInBits; } public int getChannels() { return channels; } public int getEndian() { return endian; } public int getSigned() { return signed; } public int getFrameSizeInBits() { return frameSizeInBits; } public double getFrameRate() { return frameRate; } public long computeDuration(long length) { // TODO: this calculation does not always give the exact same result as // the reference implementation. // see JavaSound API for information on frames and samples. //http://java.sun.com/j2se/1.4.2/docs/api/javax/sound/sampled/AudioFormat // .html final double frameRate; final int frameSizeInBits; if (FormatUtils.specified(this.frameRate) && FormatUtils.specified(this.frameSizeInBits)) { frameRate = this.frameRate; frameSizeInBits = this.frameSizeInBits; } else if (FormatUtils.specified(this.sampleRate) && FormatUtils.specified(this.sampleSizeInBits) && FormatUtils.specified(this.channels)) { // this calculation is correct for uncompressed data. TODO: are // there any formats where this is not correct? frameRate = this.sampleRate; frameSizeInBits = this.sampleSizeInBits * this.channels; } else { return -1L; } return 1000L * (long) (length * 8 * 1000000.0 / ((double) frameRate * (double) frameSizeInBits)); } /** * Overrides default toString. Flag indicates if output should be equal to * parsed string: dolbyac3, 2, 1, 2; * * @param asInput * <ul> * <li> <b>true</b> - will return in the exact same way it has been fed: dolbyac3, 2, 1, 2 - this format is defined in rtp media format * <li> <b>false</b> - will return in format like: dolbyac3, 2.0 Hz, 1-bit, Stereo, Unsigned, 6.0 frame rate * </ul> * @return */ public String toString(boolean asInput) { // examples: // dolbyac3, Unknown Sample Rate // dolbyac3, 2.0 Hz, 1-bit, Stereo, Unsigned, 6.0 frame rate, // FrameSize=5 bits // dolbyac3, 2.0 Hz, 1-bit, 0-channel, Unsigned, 6.0 frame rate, // FrameSize=5 bits // dolbyac3, 2.0 Hz, 1-bit, 3-channel, Unsigned, 6.0 frame rate, // FrameSize=5 bits // TODO: use "KHz" when appropriate. final StringBuffer b = new StringBuffer(); b.append(encoding); // b.append(", "); if (FormatUtils.specified(sampleRate)) { // FIXME: This removes .0 b.append(", " + (int) sampleRate); if (!asInput) { b.append(" Hz"); } } else { if (!asInput) { b.append(", Unknown Sample Rate"); } } if (FormatUtils.specified(sampleSizeInBits)) { b.append(", "); b.append("" + sampleSizeInBits); if (!asInput) { b.append("-bit"); } } if (FormatUtils.specified(channels)) { b.append(", "); if (!asInput) { if (channels == 1) { b.append("Mono"); } else if (channels == 2) { b.append("Stereo"); } else { b.append("" + channels + "-channel"); } } else { b.append("" + channels); } } if (FormatUtils.specified(endian) && FormatUtils.specified(sampleSizeInBits) && sampleSizeInBits > 8) { b.append(", "); if (!asInput) { if (endian == BIG_ENDIAN) { b.append("BigEndian"); } else if (endian == LITTLE_ENDIAN) { b.append("LittleEndian"); } else { // unknown, don't append anything } } else { b.append("" + endian); } } if (FormatUtils.specified(signed)) { b.append(", "); if (!asInput) { if (signed != SIGNED) { b.append("Unsigned"); } else { b.append("Signed"); } } else { b.append("" + signed); } } if (FormatUtils.specified(frameRate)) { b.append(", "); if (!asInput) { b.append("" + frameRate + " frame rate"); } else { b.append("" + frameRate); } } if (FormatUtils.specified(frameSizeInBits)) { if (!asInput) { b.append(", FrameSize=" + frameSizeInBits + " bits"); } else { b.append(", " + frameSizeInBits); } } return b.toString(); } @Override public String toString() { return toString(true); } @Override public boolean equals(Object format) { /* if (!super.equals(format)) { return false; } if (!(format instanceof AudioFormat)) { return false; } final AudioFormat oCast = (AudioFormat) format; return this.sampleRate == oCast.sampleRate && this.sampleSizeInBits == oCast.sampleSizeInBits && this.channels == oCast.channels && this.endian == oCast.endian && this.signed == oCast.signed && this.frameSizeInBits == oCast.frameSizeInBits && this.frameRate == oCast.frameRate; * */ return format.hashCode() == hash; } @Override public int hashCode() { return hash; } @Override public boolean matches(Format format) { if (!super.matches(format)) { // if (getClass() == FormatUtils.audioFormatClass) { // FormatTraceUtils.traceMatches(this, format, false); // otherwise // let subclass trace // } return false; } if (!(format instanceof AudioFormat)) { final boolean result = true; // if (getClass() == FormatUtils.audioFormatClass){ // FormatTraceUtils.traceMatches(this, format, result); // } return result; } final AudioFormat oCast = (AudioFormat) format; final boolean result = FormatUtils.matches(this.sampleRate, oCast.sampleRate) && FormatUtils.matches(this.sampleSizeInBits, oCast.sampleSizeInBits) && FormatUtils.matches(this.channels, oCast.channels) && FormatUtils.matches(this.endian, oCast.endian) && FormatUtils.matches(this.signed, oCast.signed) && FormatUtils.matches(this.frameSizeInBits, oCast.frameSizeInBits) && FormatUtils.matches(this.frameRate, oCast.frameRate); // if (getClass() == FormatUtils.audioFormatClass){ // FormatTraceUtils.traceMatches(this, format, result); // otherwise let // subclass trace // } return result; } @Override public Format intersects(Format other) { final Format result = super.intersects(other); if (other instanceof AudioFormat) { final AudioFormat resultCast = (AudioFormat) result; final AudioFormat oCast = (AudioFormat) other; if (getClass().isAssignableFrom(other.getClass())) { // "other" was cloned. if (FormatUtils.specified(this.sampleRate)) { resultCast.sampleRate = this.sampleRate; } if (FormatUtils.specified(this.sampleSizeInBits)) { resultCast.sampleSizeInBits = this.sampleSizeInBits; } if (FormatUtils.specified(this.channels)) { resultCast.channels = this.channels; } if (FormatUtils.specified(this.endian)) { resultCast.endian = this.endian; } if (FormatUtils.specified(this.signed)) { resultCast.signed = this.signed; } if (FormatUtils.specified(this.frameSizeInBits)) { resultCast.frameSizeInBits = this.frameSizeInBits; } if (FormatUtils.specified(this.frameRate)) { resultCast.frameRate = this.frameRate; } } else if (other.getClass().isAssignableFrom(getClass())) { // this // was // cloned if (!FormatUtils.specified(resultCast.sampleRate)) { resultCast.sampleRate = oCast.sampleRate; } if (!FormatUtils.specified(resultCast.sampleSizeInBits)) { resultCast.sampleSizeInBits = oCast.sampleSizeInBits; } if (!FormatUtils.specified(resultCast.channels)) { resultCast.channels = oCast.channels; } if (!FormatUtils.specified(resultCast.endian)) { resultCast.endian = oCast.endian; } if (!FormatUtils.specified(resultCast.signed)) { resultCast.signed = oCast.signed; } if (!FormatUtils.specified(resultCast.frameSizeInBits)) { resultCast.frameSizeInBits = oCast.frameSizeInBits; } if (!FormatUtils.specified(resultCast.frameRate)) { resultCast.frameRate = oCast.frameRate; } } } // if (getClass() == FormatUtils.audioFormatClass) { // FormatTraceUtils.traceIntersects(this, other, result); // } return result; } @Override public Object clone() { return new AudioFormat(encoding, sampleRate, sampleSizeInBits, channels, endian, signed, frameSizeInBits, frameRate, dataType); } @Override protected void copy(Format f) { super.copy(f); final AudioFormat oCast = (AudioFormat) f; // it has to be a // AudioFormat, or // ClassCastException will // be thrown. this.sampleRate = oCast.sampleRate; this.sampleSizeInBits = oCast.sampleSizeInBits; this.channels = oCast.channels; this.endian = oCast.endian; this.signed = oCast.signed; this.frameSizeInBits = oCast.frameSizeInBits; this.frameRate = oCast.frameRate; } }