From 9fee0ec4ca0c5b7a334cc29dbb58e76c7a4c736e Mon Sep 17 00:00:00 2001 From: Michiel Van Der Kolk Date: Mon, 11 Jul 2005 15:42:37 +0000 Subject: Songdb java version, source. only 1.5 compatible git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7101 a1c6a512-1295-4272-9138-f99709370657 --- .../TAsynchronousFilteredAudioInputStream.java | 256 ++++++++++++++ .../share/sampled/convert/TAudioInputStream.java | 120 +++++++ .../convert/TEncodingFormatConversionProvider.java | 129 ++++++++ .../sampled/convert/TFormatConversionProvider.java | 170 ++++++++++ .../convert/TMatrixFormatConversionProvider.java | 182 ++++++++++ .../convert/TSimpleFormatConversionProvider.java | 367 +++++++++++++++++++++ .../TSynchronousFilteredAudioInputStream.java | 271 +++++++++++++++ .../tritonus/share/sampled/convert/package.html | 17 + 8 files changed, 1512 insertions(+) create mode 100644 songdbj/org/tritonus/share/sampled/convert/TAsynchronousFilteredAudioInputStream.java create mode 100644 songdbj/org/tritonus/share/sampled/convert/TAudioInputStream.java create mode 100644 songdbj/org/tritonus/share/sampled/convert/TEncodingFormatConversionProvider.java create mode 100644 songdbj/org/tritonus/share/sampled/convert/TFormatConversionProvider.java create mode 100644 songdbj/org/tritonus/share/sampled/convert/TMatrixFormatConversionProvider.java create mode 100644 songdbj/org/tritonus/share/sampled/convert/TSimpleFormatConversionProvider.java create mode 100644 songdbj/org/tritonus/share/sampled/convert/TSynchronousFilteredAudioInputStream.java create mode 100644 songdbj/org/tritonus/share/sampled/convert/package.html (limited to 'songdbj/org/tritonus/share/sampled/convert') diff --git a/songdbj/org/tritonus/share/sampled/convert/TAsynchronousFilteredAudioInputStream.java b/songdbj/org/tritonus/share/sampled/convert/TAsynchronousFilteredAudioInputStream.java new file mode 100644 index 0000000000..83349439eb --- /dev/null +++ b/songdbj/org/tritonus/share/sampled/convert/TAsynchronousFilteredAudioInputStream.java @@ -0,0 +1,256 @@ +/* + * TAsynchronousFilteredAudioInputStream.java + * + * This file is part of Tritonus: http://www.tritonus.org/ + */ + +/* + * Copyright (c) 1999, 2000 by Matthias Pfisterer + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* +|<--- this code is formatted to fit into 80 columns --->| +*/ + +package org.tritonus.share.sampled.convert; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; + +import org.tritonus.share.TDebug; +import org.tritonus.share.TCircularBuffer; + + + +/** Base class for asynchronus converters. + This class serves as base class for + converters that do not have a fixed + ratio between the size of a block of input + data and the size of a block of output data. + These types of converters therefore need an + internal buffer, which is realized in this + class. + + @author Matthias Pfisterer +*/ +public abstract class TAsynchronousFilteredAudioInputStream +extends TAudioInputStream +implements TCircularBuffer.Trigger +{ + private static final int DEFAULT_BUFFER_SIZE = 327670; + private static final int DEFAULT_MIN_AVAILABLE = 4096; + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + + private TCircularBuffer m_circularBuffer; + private int m_nMinAvailable; + private byte[] m_abSingleByte; + + + + /** Constructor. + This constructor uses the default buffer size and the default + min available amount. + + @param lLength length of this stream in frames. May be + AudioSystem.NOT_SPECIFIED. + */ + public TAsynchronousFilteredAudioInputStream(AudioFormat outputFormat, long lLength) + { + this(outputFormat, lLength, + DEFAULT_BUFFER_SIZE, + DEFAULT_MIN_AVAILABLE); + } + + + + /** Constructor. + With this constructor, the buffer size and the minimum + available amount can be specified as parameters. + + @param lLength length of this stream in frames. May be + AudioSystem.NOT_SPECIFIED. + + @param nBufferSize size of the circular buffer in bytes. + */ + public TAsynchronousFilteredAudioInputStream( + AudioFormat outputFormat, long lLength, + int nBufferSize, + int nMinAvailable) + { + /* The usage of a ByteArrayInputStream is a hack. + * (the infamous "JavaOne hack", because I did it on June + * 6th 2000 in San Francisco, only hours before a + * JavaOne session where I wanted to show mp3 playback + * with Java Sound.) It is necessary because in the FCS + * version of the Sun jdk1.3, the constructor of + * AudioInputStream throws an exception if its first + * argument is null. So we have to pass a dummy non-null + * value. + */ + super(new ByteArrayInputStream(EMPTY_BYTE_ARRAY), + outputFormat, + lLength); + if (TDebug.TraceAudioConverter) { TDebug.out("TAsynchronousFilteredAudioInputStream.(): begin"); } + m_circularBuffer = new TCircularBuffer( + nBufferSize, + false, // blocking read + true, // blocking write + this); // trigger + m_nMinAvailable = nMinAvailable; + if (TDebug.TraceAudioConverter) { TDebug.out("TAsynchronousFilteredAudioInputStream.(): end"); } + } + + + /** Returns the circular buffer. + */ + protected TCircularBuffer getCircularBuffer() + { + return m_circularBuffer; + } + + + + /** Check if writing more data to the circular buffer is recommanded. + This checks the available write space in the circular buffer + against the minimum available property. If the available write + space is greater than th minimum available property, more + writing is encouraged, so this method returns true. + Note that this is only a hint to subclasses. However, + it is an important hint. + + @return true if more writing to the circular buffer is + recommanden. Otherwise, false is returned. + */ + protected boolean writeMore() + { + return getCircularBuffer().availableWrite() > m_nMinAvailable; + } + + + + public int read() + throws IOException + { + // if (TDebug.TraceAudioConverter) { TDebug.out("TAsynchronousFilteredAudioInputStream.read(): begin"); } + int nByte = -1; + if (m_abSingleByte == null) + { + m_abSingleByte = new byte[1]; + } + int nReturn = read(m_abSingleByte); + if (nReturn == -1) + { + nByte = -1; + } + else + { + //$$fb 2001-04-14 nobody really knows that... + nByte = m_abSingleByte[0] & 0xFF; + } + // if (TDebug.TraceAudioConverter) { TDebug.out("TAsynchronousFilteredAudioInputStream.read(): end"); } + return nByte; + } + + + + public int read(byte[] abData) + throws IOException + { + if (TDebug.TraceAudioConverter) { TDebug.out("TAsynchronousFilteredAudioInputStream.read(byte[]): begin"); } + int nRead = read(abData, 0, abData.length); + if (TDebug.TraceAudioConverter) { TDebug.out("TAsynchronousFilteredAudioInputStream.read(byte[]): end"); } + return nRead; + } + + + + public int read(byte[] abData, int nOffset, int nLength) + throws IOException + { + if (TDebug.TraceAudioConverter) { TDebug.out("TAsynchronousFilteredAudioInputStream.read(byte[], int, int): begin"); } + //$$fb 2001-04-22: this returns at maximum circular buffer + // length. This is not very efficient... + //$$fb 2001-04-25: we should check that we do not exceed getFrameLength() ! + int nRead = m_circularBuffer.read(abData, nOffset, nLength); + if (TDebug.TraceAudioConverter) { TDebug.out("TAsynchronousFilteredAudioInputStream.read(byte[], int, int): end"); } + return nRead; + } + + + + public long skip(long lSkip) + throws IOException + { + // TODO: this is quite inefficient + for (long lSkipped = 0; lSkipped < lSkip; lSkipped++) + { + int nReturn = read(); + if (nReturn == -1) + { + return lSkipped; + } + } + return lSkip; + } + + + + public int available() + throws IOException + { + return m_circularBuffer.availableRead(); + } + + + + public void close() + throws IOException + { + m_circularBuffer.close(); + } + + + + public boolean markSupported() + { + return false; + } + + + + public void mark(int nReadLimit) + { + } + + + + public void reset() + throws IOException + { + throw new IOException("mark not supported"); + } +} + + + +/*** TAsynchronousFilteredAudioInputStream.java ***/ diff --git a/songdbj/org/tritonus/share/sampled/convert/TAudioInputStream.java b/songdbj/org/tritonus/share/sampled/convert/TAudioInputStream.java new file mode 100644 index 0000000000..d84530e115 --- /dev/null +++ b/songdbj/org/tritonus/share/sampled/convert/TAudioInputStream.java @@ -0,0 +1,120 @@ +/* + * TAudioInputStream.java + * + * This file is part of Tritonus: http://www.tritonus.org/ + */ + +/* + * Copyright (c) 2003 by Matthias Pfisterer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* +|<--- this code is formatted to fit into 80 columns --->| +*/ + +package org.tritonus.share.sampled.convert; + +import java.io.InputStream; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; + + +/** AudioInputStream base class. This class implements "dynamic" + properties. "Dynamic" properties are properties that may change + during the life time of the objects. This is typically used to + pass information like the current frame number, volume of subbands + and similar values. "Dynamic" properties are different from + properties in AudioFormat and AudioFileFormat, which are + considered "static", as they aren't allowed to change after + creating of the object, thereby maintaining the immutable + character of these classes. +*/ + +public class TAudioInputStream +extends AudioInputStream +{ + private Map m_properties; + private Map m_unmodifiableProperties; + + + /** Constructor without properties. + Creates an empty properties map. + */ + public TAudioInputStream(InputStream inputStream, + AudioFormat audioFormat, + long lLengthInFrames) + { + super(inputStream, audioFormat, lLengthInFrames); + initMaps(new HashMap()); + } + + + /** Constructor with properties. + The passed properties map is not copied. This allows subclasses + to change values in the map after creation, and the changes are + reflected in the map the application program can obtain. + */ + public TAudioInputStream(InputStream inputStream, + AudioFormat audioFormat, + long lLengthInFrames, + Map properties) + { + super(inputStream, audioFormat, lLengthInFrames); + initMaps(properties); + } + + + private void initMaps(Map properties) + { + /* Here, we make a shallow copy of the map. It's unclear if this + is sufficient (of if a deep copy should be made). + */ + m_properties = properties; + m_unmodifiableProperties = Collections.unmodifiableMap(m_properties); + } + + + /** Obtain a Map containing the properties. This method returns a + Map that cannot be modified by the application program, but + reflects changes to the map made by the implementation. + + @return a map containing the properties. + */ + public Map properties() + { + return m_unmodifiableProperties; + } + + + /** Set a property. Unlike in AudioFormat and AudioFileFormat, + this method may be used anywhere by subclasses - it is not + restricted to be used in the constructor. + */ + protected void setProperty(String key, Object value) + { + m_properties.put(key, value); + } +} + + + +/*** TAudioInputStream.java ***/ diff --git a/songdbj/org/tritonus/share/sampled/convert/TEncodingFormatConversionProvider.java b/songdbj/org/tritonus/share/sampled/convert/TEncodingFormatConversionProvider.java new file mode 100644 index 0000000000..6b83403c43 --- /dev/null +++ b/songdbj/org/tritonus/share/sampled/convert/TEncodingFormatConversionProvider.java @@ -0,0 +1,129 @@ +/* + * TEncodingFormatConversionProvider.java + * + * This file is part of Tritonus: http://www.tritonus.org/ + */ + +/* + * Copyright (c) 2000 by Florian Bomers + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* +|<--- this code is formatted to fit into 80 columns --->| +*/ + +package org.tritonus.share.sampled.convert; + +import java.util.Collection; +import java.util.Iterator; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; + +import org.tritonus.share.TDebug; +import org.tritonus.share.ArraySet; + + +// this class depends on handling of AudioSystem.NOT_SPECIFIED in AudioFormat.matches() + +/** + * This is a base class for FormatConversionProviders that only + * change the encoding, i.e. they never + *
    + *
  • change the sample size in bits without changing the encoding + *
  • change the sample rate + *
  • change the number of channels + *
+ *

It is assumed that each source format can be encoded to all + * target formats. + *

In the sourceFormats and targetFormats collections that are passed to + * the constructor of this class, fields may be set to AudioSystem.NOT_SPECIFIED. + * This means that it handles all values of that field, but cannot change it. + *

This class prevents that a conversion is done (e.g. for sample rates), + * because the overriding class specified AudioSystem.NOT_SPECIFIED as sample rate, + * meaning it handles all sample rates. + *

Overriding classes must implement at least + * AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream) + * and provide a constructor that calls the protected constructor of this class. + * + * @author Florian Bomers + */ +public abstract class TEncodingFormatConversionProvider +extends TSimpleFormatConversionProvider +{ + protected TEncodingFormatConversionProvider( + Collection sourceFormats, + Collection targetFormats) + { + super(sourceFormats, targetFormats); + } + + + + /** + * This implementation assumes that the converter can convert + * from each of its source formats to each of its target + * formats. If this is not the case, the converter has to + * override this method. + *

When conversion is supported, for every target encoding, + * the fields sample size in bits, channels and sample rate are checked: + *

    + *
  • When a field in both the source and target format is AudioSystem.NOT_SPECIFIED, + * one instance of that targetFormat is returned with this field set to AudioSystem.NOT_SPECIFIED. + *
  • When a field in sourceFormat is set and it is AudioSystem.NOT_SPECIFIED in the target format, + * the value of the field of source format is set in the returned format. + *
  • The same applies for the other way round. + *
+ * For this, replaceNotSpecified(sourceFormat, targetFormat) in the base + * class TSimpleFormatConversionProvider is used - and accordingly, the frameSize + * is recalculated with getFrameSize(...) if a field with AudioSystem.NOT_SPECIFIED + * is replaced. Inheriting classes may wish to override this method if the + * default mode of calculating the frame size is not appropriate. + */ + public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) { + if (TDebug.TraceAudioConverter) { + TDebug.out(">TEncodingFormatConversionProvider.getTargetFormats(AudioFormat.Encoding, AudioFormat):"); + TDebug.out("checking if conversion possible"); + TDebug.out("from: " + sourceFormat); + TDebug.out("to: " + targetEncoding); + } + if (isConversionSupported(targetEncoding, sourceFormat)) { + // TODO: check that no duplicates may occur... + ArraySet result=new ArraySet(); + Iterator iterator = getCollectionTargetFormats().iterator(); + while (iterator.hasNext()) { + AudioFormat targetFormat = iterator.next(); + targetFormat=replaceNotSpecified(sourceFormat, targetFormat); + result.add(targetFormat); + } + if (TDebug.TraceAudioConverter) { + TDebug.out("< returning "+result.size()+" elements."); + } + return result.toArray(EMPTY_FORMAT_ARRAY); + } else { + if (TDebug.TraceAudioConverter) { + TDebug.out("< returning empty array."); + } + return EMPTY_FORMAT_ARRAY; + } + } + +} + +/*** TEncodingFormatConversionProvider.java ***/ diff --git a/songdbj/org/tritonus/share/sampled/convert/TFormatConversionProvider.java b/songdbj/org/tritonus/share/sampled/convert/TFormatConversionProvider.java new file mode 100644 index 0000000000..eaec65bb06 --- /dev/null +++ b/songdbj/org/tritonus/share/sampled/convert/TFormatConversionProvider.java @@ -0,0 +1,170 @@ +/* + * TFormatConversionProvider.java + * + * This file is part of Tritonus: http://www.tritonus.org/ + */ + +/* + * Copyright (c) 1999, 2000 by Matthias Pfisterer + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* +|<--- this code is formatted to fit into 80 columns --->| +*/ + +package org.tritonus.share.sampled.convert; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.spi.FormatConversionProvider; + +import org.tritonus.share.TDebug; +import org.tritonus.share.sampled.AudioFormats; + + + +/** Base class for all conversion providers of Tritonus. + * + * @author Matthias Pfisterer + */ +public abstract class TFormatConversionProvider +extends FormatConversionProvider +{ + protected static final AudioFormat.Encoding[] EMPTY_ENCODING_ARRAY = new AudioFormat.Encoding[0]; + protected static final AudioFormat[] EMPTY_FORMAT_ARRAY = new AudioFormat[0]; + + + + // $$fb2000-10-04: use AudioSystem.NOT_SPECIFIED for all fields. + public AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding, AudioInputStream audioInputStream) + { + AudioFormat sourceFormat = audioInputStream.getFormat(); + AudioFormat targetFormat = new AudioFormat( + targetEncoding, + AudioSystem.NOT_SPECIFIED, // sample rate + AudioSystem.NOT_SPECIFIED, // sample size in bits + AudioSystem.NOT_SPECIFIED, // channels + AudioSystem.NOT_SPECIFIED, // frame size + AudioSystem.NOT_SPECIFIED, // frame rate + sourceFormat.isBigEndian()); // big endian + if (TDebug.TraceAudioConverter) + { + TDebug.out("TFormatConversionProvider.getAudioInputStream(AudioFormat.Encoding, AudioInputStream):"); + TDebug.out("trying to convert to " + targetFormat); + } + return getAudioInputStream(targetFormat, audioInputStream); + } + + + + /** + * WARNING: this method uses getTargetFormats(AudioFormat.Encoding, AudioFormat) + * which may create infinite loops if the latter is overwritten. + *

+ * This method is overwritten here to make use of org.tritonus.share.sampled.AudioFormats.matches + * and is considered temporary until AudioFormat.matches is corrected in the JavaSound API. + */ + /* $$mp: if we decide to use getMatchingFormat(), this method should be + implemented by simply calling getMatchingFormat() and comparing the + result against null. + */ + public boolean isConversionSupported( + AudioFormat targetFormat, + AudioFormat sourceFormat) + { + if (TDebug.TraceAudioConverter) + { + TDebug.out(">TFormatConversionProvider.isConversionSupported(AudioFormat, AudioFormat):"); + TDebug.out("class: "+getClass().getName()); + TDebug.out("checking if conversion possible"); + TDebug.out("from: " + sourceFormat); + TDebug.out("to: " + targetFormat); + } + AudioFormat[] aTargetFormats = getTargetFormats(targetFormat.getEncoding(), sourceFormat); + for (int i = 0; i < aTargetFormats.length; i++) + { + if (TDebug.TraceAudioConverter) + { + TDebug.out("checking against possible target format: " + aTargetFormats[i]); + } + if (aTargetFormats[i] != null + && AudioFormats.matches(aTargetFormats[i], targetFormat)) + { + if (TDebug.TraceAudioConverter) + { + TDebug.out("getTargetFormats(AudioFormat.Encoding, AudioFormat) + * which may create infinite loops if the latter is overwritten. + *

+ * This method is overwritten here to make use of org.tritonus.share.sampled.AudioFormats.matches + * and is considered temporary until AudioFormat.matches is corrected in the JavaSound API. + */ + public AudioFormat getMatchingFormat( + AudioFormat targetFormat, + AudioFormat sourceFormat) + { + if (TDebug.TraceAudioConverter) + { + TDebug.out(">TFormatConversionProvider.isConversionSupported(AudioFormat, AudioFormat):"); + TDebug.out("class: "+getClass().getName()); + TDebug.out("checking if conversion possible"); + TDebug.out("from: " + sourceFormat); + TDebug.out("to: " + targetFormat); + } + AudioFormat[] aTargetFormats = getTargetFormats(targetFormat.getEncoding(), sourceFormat); + for (int i = 0; i < aTargetFormats.length; i++) + { + if (TDebug.TraceAudioConverter) + { + TDebug.out("checking against possible target format: " + aTargetFormats[i]); + } + if (aTargetFormats[i] != null + && AudioFormats.matches(aTargetFormats[i], targetFormat)) + { + if (TDebug.TraceAudioConverter) + { + TDebug.out(" + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +package org.tritonus.share.sampled.convert; + + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Iterator; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.spi.FormatConversionProvider; + +import org.tritonus.share.sampled.AudioFormats; +import org.tritonus.share.ArraySet; + +/** + * Base class for arbitrary formatConversionProviders. + * + * @author Matthias Pfisterer + */ + + +public abstract class TMatrixFormatConversionProvider + extends TSimpleFormatConversionProvider +{ + /* + * keys: source AudioFormat + * values: collection of possible target encodings + * + * Note that accessing values with get() is not appropriate, + * since the equals() method in AudioFormat is not overloaded. + * The hashtable is just used as a convenient storage + * organization. + */ + private Map m_targetEncodingsFromSourceFormat; + + + /* + * keys: source AudioFormat + * values: a Map that contains a mapping from target encodings + * (keys) to a collection of target formats (values). + * + * Note that accessing values with get() is not appropriate, + * since the equals() method in AudioFormat is not overloaded. + * The hashtable is just used as a convenient storage + * organization. + */ + private Map m_targetFormatsFromSourceFormat; + + + + protected TMatrixFormatConversionProvider( + List sourceFormats, + List targetFormats, + boolean[][] abConversionPossible) + { + super(sourceFormats, + targetFormats); + m_targetEncodingsFromSourceFormat = new HashMap(); + m_targetFormatsFromSourceFormat = new HashMap(); + + for (int nSourceFormat = 0; + nSourceFormat < sourceFormats.size(); + nSourceFormat++) + { + AudioFormat sourceFormat = (AudioFormat) sourceFormats.get(nSourceFormat); + List supportedTargetEncodings = new ArraySet(); + m_targetEncodingsFromSourceFormat.put(sourceFormat, supportedTargetEncodings); + Map targetFormatsFromTargetEncodings = new HashMap(); + m_targetFormatsFromSourceFormat.put(sourceFormat, targetFormatsFromTargetEncodings); + for (int nTargetFormat = 0; + nTargetFormat < targetFormats.size(); + nTargetFormat++) + { + AudioFormat targetFormat = (AudioFormat) targetFormats.get(nTargetFormat); + if (abConversionPossible[nSourceFormat][nTargetFormat]) + { + AudioFormat.Encoding targetEncoding = targetFormat.getEncoding(); + supportedTargetEncodings.add(targetEncoding); + Collection supportedTargetFormats = (Collection) targetFormatsFromTargetEncodings.get(targetEncoding); + if (supportedTargetFormats == null) + { + supportedTargetFormats = new ArraySet(); + targetFormatsFromTargetEncodings.put(targetEncoding, supportedTargetFormats); + } + supportedTargetFormats.add(targetFormat); + } + } + } + } + + + + public AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat) + { + Iterator iterator = m_targetEncodingsFromSourceFormat.entrySet().iterator(); + while (iterator.hasNext()) + { + Map.Entry entry = (Map.Entry) iterator.next(); + AudioFormat format = (AudioFormat) entry.getKey(); + if (AudioFormats.matches(format, sourceFormat)) + { + Collection targetEncodings = (Collection) entry.getValue(); + return (AudioFormat.Encoding[]) targetEncodings.toArray(EMPTY_ENCODING_ARRAY); + } + + } + return EMPTY_ENCODING_ARRAY; + } + + + + // TODO: this should work on the array returned by getTargetEncodings(AudioFormat) +/* + public boolean isConversionSupported(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) + { + return isAllowedSourceFormat(sourceFormat) && + isTargetEncodingSupported(targetEncoding); + } +*/ + + + + public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) + { + Iterator iterator = m_targetFormatsFromSourceFormat.entrySet().iterator(); + while (iterator.hasNext()) + { + Map.Entry entry = (Map.Entry) iterator.next(); + AudioFormat format = (AudioFormat) entry.getKey(); + if (AudioFormats.matches(format, sourceFormat)) + { + Map targetEncodings = (Map) entry.getValue(); + Collection targetFormats = (Collection) targetEncodings.get(targetEncoding); + if (targetFormats != null) + { + return (AudioFormat[]) targetFormats.toArray(EMPTY_FORMAT_ARRAY); + } + else + { + return EMPTY_FORMAT_ARRAY; + } + } + + } + return EMPTY_FORMAT_ARRAY; + } + + +} + + + +/*** TMatrixFormatConversionProvider.java ***/ diff --git a/songdbj/org/tritonus/share/sampled/convert/TSimpleFormatConversionProvider.java b/songdbj/org/tritonus/share/sampled/convert/TSimpleFormatConversionProvider.java new file mode 100644 index 0000000000..71b055ff79 --- /dev/null +++ b/songdbj/org/tritonus/share/sampled/convert/TSimpleFormatConversionProvider.java @@ -0,0 +1,367 @@ +/* + * TSimpleFormatConversionProvider.java + * + * This file is part of Tritonus: http://www.tritonus.org/ + */ + +/* + * Copyright (c) 1999 - 2004 by Matthias Pfisterer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* +|<--- this code is formatted to fit into 80 columns --->| +*/ + +package org.tritonus.share.sampled.convert; + +import java.util.Collection; +import java.util.Iterator; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; + +import org.tritonus.share.sampled.AudioFormats; +import org.tritonus.share.ArraySet; +import org.tritonus.share.TDebug; + + +/** + * This is a base class for FormatConversionProviders that can convert + * from each source encoding/format to each target encoding/format. + * If this is not the case, use TEncodingFormatConversionProvider. + * + *

Overriding classes must + * provide a constructor that calls the protected constructor of this class and override + * AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream). + * The latter method should be able to handle the case that all fields are NOT_SPECIFIED + * and provide appropriate default values. + * + * @author Matthias Pfisterer + */ + +// todo: +// - declare a constant ALL_BUT_SAME_VALUE (==-2) or so that can be used in format lists +// - consistent implementation of replacing NOT_SPECIFIED when not given in conversion + +public abstract class TSimpleFormatConversionProvider +extends TFormatConversionProvider +{ + private Collection m_sourceEncodings; + private Collection m_targetEncodings; + private Collection m_sourceFormats; + private Collection m_targetFormats; + + + + protected TSimpleFormatConversionProvider( + Collection sourceFormats, + Collection targetFormats) + { + m_sourceEncodings = new ArraySet(); + m_targetEncodings = new ArraySet(); + m_sourceFormats = sourceFormats; + m_targetFormats = targetFormats; + collectEncodings(m_sourceFormats, m_sourceEncodings); + collectEncodings(m_targetFormats, m_targetEncodings); + } + + + + /** Disables this FormatConversionProvider. + This may be useful when e.g. native libraries are not present. + TODO: enable method, better implementation + */ + protected void disable() + { + if (TDebug.TraceAudioConverter) { TDebug.out("TSimpleFormatConversionProvider.disable(): disabling " + getClass().getName()); } + m_sourceEncodings = new ArraySet(); + m_targetEncodings = new ArraySet(); + m_sourceFormats = new ArraySet(); + m_targetFormats = new ArraySet(); + } + + + + private static void collectEncodings(Collection formats, + Collection encodings) + { + Iterator iterator = formats.iterator(); + while (iterator.hasNext()) + { + AudioFormat format = iterator.next(); + encodings.add(format.getEncoding()); + } + } + + + + public AudioFormat.Encoding[] getSourceEncodings() + { + return m_sourceEncodings.toArray(EMPTY_ENCODING_ARRAY); + } + + + + public AudioFormat.Encoding[] getTargetEncodings() + { + return m_targetEncodings.toArray(EMPTY_ENCODING_ARRAY); + } + + + + // overwritten of FormatConversionProvider + public boolean isSourceEncodingSupported(AudioFormat.Encoding sourceEncoding) + { + return m_sourceEncodings.contains(sourceEncoding); + } + + + + // overwritten of FormatConversionProvider + public boolean isTargetEncodingSupported(AudioFormat.Encoding targetEncoding) + { + return m_targetEncodings.contains(targetEncoding); + } + + + + /** + * This implementation assumes that the converter can convert + * from each of its source encodings to each of its target + * encodings. If this is not the case, the converter has to + * override this method. + */ + public AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat) + { + if (isAllowedSourceFormat(sourceFormat)) + { + return getTargetEncodings(); + } + else + { + return EMPTY_ENCODING_ARRAY; + } + } + + + + /** + * This implementation assumes that the converter can convert + * from each of its source formats to each of its target + * formats. If this is not the case, the converter has to + * override this method. + */ + public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) + { + if (isConversionSupported(targetEncoding, sourceFormat)) + { + return m_targetFormats.toArray(EMPTY_FORMAT_ARRAY); + } + else + { + return EMPTY_FORMAT_ARRAY; + } + } + + + // TODO: check if necessary + protected boolean isAllowedSourceEncoding(AudioFormat.Encoding sourceEncoding) + { + return m_sourceEncodings.contains(sourceEncoding); + } + + + + protected boolean isAllowedTargetEncoding(AudioFormat.Encoding targetEncoding) + { + return m_targetEncodings.contains(targetEncoding); + } + + + + protected boolean isAllowedSourceFormat(AudioFormat sourceFormat) + { + Iterator iterator = m_sourceFormats.iterator(); + while (iterator.hasNext()) + { + AudioFormat format = iterator.next(); + if (AudioFormats.matches(format, sourceFormat)) + { + return true; + } + } + return false; + } + + + + protected boolean isAllowedTargetFormat(AudioFormat targetFormat) + { + Iterator iterator = m_targetFormats.iterator(); + while (iterator.hasNext()) + { + AudioFormat format = iterator.next(); + if (AudioFormats.matches(format, targetFormat)) + { + return true; + } + } + return false; + } + + // $$fb 2000-04-02 added some convenience methods for overriding classes + protected Collection getCollectionSourceEncodings() + { + return m_sourceEncodings; + } + + protected Collection getCollectionTargetEncodings() + { + return m_targetEncodings; + } + + protected Collection getCollectionSourceFormats() { + return m_sourceFormats; + } + + protected Collection getCollectionTargetFormats() { + return m_targetFormats; + } + + /** + * Utility method to check whether these values match, + * taking into account AudioSystem.NOT_SPECIFIED. + * @return true if any of the values is AudioSystem.NOT_SPECIFIED + * or both values have the same value. + */ + //$$fb 2000-08-16: moved from TEncodingFormatConversionProvider + protected static boolean doMatch(int i1, int i2) { + return i1==AudioSystem.NOT_SPECIFIED + || i2==AudioSystem.NOT_SPECIFIED + || i1==i2; + } + + /** + * @see #doMatch(int,int) + */ + //$$fb 2000-08-16: moved from TEncodingFormatConversionProvider + protected static boolean doMatch(float f1, float f2) { + return f1==AudioSystem.NOT_SPECIFIED + || f2==AudioSystem.NOT_SPECIFIED + || Math.abs(f1 - f2) < 1.0e-9; + } + + /** + * Utility method, replaces all occurences of AudioSystem.NOT_SPECIFIED + * in targetFormat with the corresponding value in sourceFormat. + * If targetFormat does not contain any fields with AudioSystem.NOT_SPECIFIED, + * it is returned unmodified. The endian-ness and encoding remain the same in all cases. + *

+ * If any of the fields is AudioSystem.NOT_SPECIFIED in both sourceFormat and + * targetFormat, it will remain not specified. + *

+ * This method uses getFrameSize(...) (see below) to set the new frameSize, + * if a new AudioFormat instance is created. + *

+ * This method isn't used in TSimpleFormatConversionProvider - it is solely there + * for inheriting classes. + */ + //$$fb 2000-08-16: moved from TEncodingFormatConversionProvider + protected AudioFormat replaceNotSpecified(AudioFormat sourceFormat, AudioFormat targetFormat) { + boolean bSetSampleSize=false; + boolean bSetChannels=false; + boolean bSetSampleRate=false; + boolean bSetFrameRate=false; + if (targetFormat.getSampleSizeInBits()==AudioSystem.NOT_SPECIFIED + && sourceFormat.getSampleSizeInBits()!=AudioSystem.NOT_SPECIFIED) { + bSetSampleSize=true; + } + if (targetFormat.getChannels()==AudioSystem.NOT_SPECIFIED + && sourceFormat.getChannels()!=AudioSystem.NOT_SPECIFIED) { + bSetChannels=true; + } + if (targetFormat.getSampleRate()==AudioSystem.NOT_SPECIFIED + && sourceFormat.getSampleRate()!=AudioSystem.NOT_SPECIFIED) { + bSetSampleRate=true; + } + if (targetFormat.getFrameRate()==AudioSystem.NOT_SPECIFIED + && sourceFormat.getFrameRate()!=AudioSystem.NOT_SPECIFIED) { + bSetFrameRate=true; + } + if (bSetSampleSize || bSetChannels || bSetSampleRate || bSetFrameRate + || (targetFormat.getFrameSize()==AudioSystem.NOT_SPECIFIED + && sourceFormat.getFrameSize()!=AudioSystem.NOT_SPECIFIED)) { + // create new format in place of the original target format + float sampleRate=bSetSampleRate? + sourceFormat.getSampleRate():targetFormat.getSampleRate(); + float frameRate=bSetFrameRate? + sourceFormat.getFrameRate():targetFormat.getFrameRate(); + int sampleSize=bSetSampleSize? + sourceFormat.getSampleSizeInBits():targetFormat.getSampleSizeInBits(); + int channels=bSetChannels? + sourceFormat.getChannels():targetFormat.getChannels(); + int frameSize=getFrameSize( + targetFormat.getEncoding(), + sampleRate, + sampleSize, + channels, + frameRate, + targetFormat.isBigEndian(), + targetFormat.getFrameSize()); + targetFormat= new AudioFormat( + targetFormat.getEncoding(), + sampleRate, + sampleSize, + channels, + frameSize, + frameRate, + targetFormat.isBigEndian()); + } + return targetFormat; + } + + /** + * Calculates the frame size for the given format description. + * The default implementation returns AudioSystem.NOT_SPECIFIED + * if either sampleSize or channels is AudioSystem.NOT_SPECIFIED, + * otherwise sampleSize*channels/8 is returned. + *

+ * If this does not reflect the way to calculate the right frame size, + * inheriting classes should overwrite this method if they use + * replaceNotSpecified(...). It is not used elsewhere in this class. + */ + //$$fb 2000-08-16: added + protected int getFrameSize( + AudioFormat.Encoding encoding, + float sampleRate, + int sampleSize, + int channels, + float frameRate, + boolean bigEndian, + int oldFrameSize) { + if (sampleSize==AudioSystem.NOT_SPECIFIED || channels==AudioSystem.NOT_SPECIFIED) { + return AudioSystem.NOT_SPECIFIED; + } + return sampleSize*channels/8; + } + + + +} + +/*** TSimpleFormatConversionProvider.java ***/ diff --git a/songdbj/org/tritonus/share/sampled/convert/TSynchronousFilteredAudioInputStream.java b/songdbj/org/tritonus/share/sampled/convert/TSynchronousFilteredAudioInputStream.java new file mode 100644 index 0000000000..8a588e5c3e --- /dev/null +++ b/songdbj/org/tritonus/share/sampled/convert/TSynchronousFilteredAudioInputStream.java @@ -0,0 +1,271 @@ +/* + * TSynchronousFilteredAudioInputStream.java + * + * This file is part of Tritonus: http://www.tritonus.org/ + */ + +/* + * Copyright (c) 1999,2000 by Florian Bomers + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* +|<--- this code is formatted to fit into 80 columns --->| +*/ + +package org.tritonus.share.sampled.convert; + +import java.io.IOException; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.spi.FormatConversionProvider; + +import org.tritonus.share.TDebug; +import org.tritonus.share.sampled.AudioUtils; + + + +/** + * Base class for types of audio filter/converter that translate one frame to another frame.
+ * It provides all the transformation of frame sizes.
+ * It does NOT handle different sample rates of original stream and this stream ! + * + * @author Florian Bomers + */ +public abstract class TSynchronousFilteredAudioInputStream +extends TAudioInputStream { + + private AudioInputStream originalStream; + private AudioFormat originalFormat; + /** 1 if original format's frame size is NOT_SPECIFIED */ + private int originalFrameSize; + /** 1 if original format's frame size is NOT_SPECIFIED */ + private int newFrameSize; + + /** + * The intermediate buffer used during convert actions + * (if not convertInPlace is used). + * It remains until this audioStream is closed or destroyed + * and grows with the time - it always has the size of the + * largest intermediate buffer ever needed. + */ + protected byte[] buffer=null; + + /** + * For use of the more efficient method convertInPlace. + * it will be set to true when (frameSizeFactor==1) + */ + private boolean m_bConvertInPlace = false; + + public TSynchronousFilteredAudioInputStream(AudioInputStream audioInputStream, AudioFormat newFormat) { + // the super class will do nothing... we override everything + super(audioInputStream, newFormat, audioInputStream.getFrameLength()); + originalStream=audioInputStream; + originalFormat=audioInputStream.getFormat(); + originalFrameSize=(originalFormat.getFrameSize()<=0) ? + 1 : originalFormat.getFrameSize(); + newFrameSize=(getFormat().getFrameSize()<=0) ? + 1 : getFormat().getFrameSize(); + if (TDebug.TraceAudioConverter) { + TDebug.out("TSynchronousFilteredAudioInputStream: original format =" + +AudioUtils.format2ShortStr(originalFormat)); + TDebug.out("TSynchronousFilteredAudioInputStream: converted format=" + +AudioUtils.format2ShortStr(getFormat())); + } + //$$fb 2000-07-17: convert in place has to be enabled explicitly with "enableConvertInPlace" + //if (getFormat().getFrameSize() == originalFormat.getFrameSize()) { + // m_bConvertInPlace = true; + //} + m_bConvertInPlace = false; + } + + protected boolean enableConvertInPlace() { + if (newFrameSize >= originalFrameSize) { + m_bConvertInPlace = true; + } + return m_bConvertInPlace; + } + + + /** + * Override this method to do the actual conversion. + * inBuffer starts always at index 0 (it is an internal buffer) + * You should always override this. + * inFrameCount is the number of frames in inBuffer. These + * frames are of the format originalFormat. + * @return the resulting number of frames converted and put into + * outBuffer. The return value is in the format of this stream. + */ + protected abstract int convert(byte[] inBuffer, byte[] outBuffer, int outByteOffset, int inFrameCount); + + + + /** + * Override this method to provide in-place conversion of samples. + * To use it, call "enableConvertInPlace()". It will only be used when + * input bytes per frame >= output bytes per frame. + * This method must always convert frameCount frames, so no return value is necessary. + */ + protected void convertInPlace(byte[] buffer, int byteOffset, int frameCount) { + throw new RuntimeException("Illegal call to convertInPlace"); + } + + public int read() + throws IOException { + if (newFrameSize != 1) { + throw new IOException("frame size must be 1 to read a single byte"); + } + // very ugly, but efficient. Who uses this method anyway ? + // TODO: use an instance variable + byte[] temp = new byte[1]; + int result = read(temp); + if (result == -1) { + return -1; + } + if (result == 0) { + // what in this case ??? Let's hope it never occurs. + return -1; + } + return temp[0] & 0xFF; + } + + + + private void clearBuffer() { + buffer = null; + } + + public AudioInputStream getOriginalStream() { + return originalStream; + } + + public AudioFormat getOriginalFormat() { + return originalFormat; + } + + /** + * Read nLength bytes that will be the converted samples + * of the original InputStream. + * When nLength is not an integral number of frames, + * this method may read less than nLength bytes. + */ + public int read(byte[] abData, int nOffset, int nLength) + throws IOException { + // number of frames that we have to read from the underlying stream. + int nFrameLength = nLength/newFrameSize; + + // number of bytes that we need to read from underlying stream. + int originalBytes = nFrameLength * originalFrameSize; + + if (TDebug.TraceAudioConverter) { + TDebug.out("> TSynchronousFilteredAIS.read(buffer["+abData.length+"], " + +nOffset+" ,"+nLength+" bytes ^="+nFrameLength+" frames)"); + } + int nFramesConverted = 0; + + // set up buffer to read + byte readBuffer[]; + int readOffset; + if (m_bConvertInPlace) { + readBuffer=abData; + readOffset=nOffset; + } else { + // assert that the buffer fits + if (buffer == null || buffer.length < originalBytes) { + buffer = new byte[originalBytes]; + } + readBuffer=buffer; + readOffset=0; + } + int nBytesRead = originalStream.read(readBuffer, readOffset, originalBytes); + if (nBytesRead == -1) { + // end of stream + clearBuffer(); + return -1; + } + int nFramesRead = nBytesRead / originalFrameSize; + if (TDebug.TraceAudioConverter) { + TDebug.out("original.read returned " + +nBytesRead+" bytes ^="+nFramesRead+" frames"); + } + if (m_bConvertInPlace) { + convertInPlace(abData, nOffset, nFramesRead); + nFramesConverted=nFramesRead; + } else { + nFramesConverted = convert(buffer, abData, nOffset, nFramesRead); + } + if (TDebug.TraceAudioConverter) { + TDebug.out("< converted "+nFramesConverted+" frames"); + } + return nFramesConverted*newFrameSize; + } + + + public long skip(long nSkip) + throws IOException { + // only returns integral frames + long skipFrames = nSkip / newFrameSize; + long originalSkippedBytes = originalStream.skip(skipFrames*originalFrameSize); + long skippedFrames = originalSkippedBytes/originalFrameSize; + return skippedFrames * newFrameSize; + } + + + public int available() + throws IOException { + int origAvailFrames = originalStream.available()/originalFrameSize; + return origAvailFrames*newFrameSize; + } + + + public void close() + throws IOException { + originalStream.close(); + clearBuffer(); + } + + + + public void mark(int readlimit) { + int readLimitFrames=readlimit/newFrameSize; + originalStream.mark(readLimitFrames*originalFrameSize); + } + + + + public void reset() + throws IOException { + originalStream.reset(); + } + + + public boolean markSupported() { + return originalStream.markSupported(); + } + + + private int getFrameSize() { + return getFormat().getFrameSize(); + } + +} + + +/*** TSynchronousFilteredAudioInputStream.java ***/ diff --git a/songdbj/org/tritonus/share/sampled/convert/package.html b/songdbj/org/tritonus/share/sampled/convert/package.html new file mode 100644 index 0000000000..d0cc35c408 --- /dev/null +++ b/songdbj/org/tritonus/share/sampled/convert/package.html @@ -0,0 +1,17 @@ + + + + + + +

Base classes for the implementation of FormatConversionProviders. + The classes provided here .

+ + @see javax.sound.sampled.spi.FormatConversionProvider + @see org.tritonus.sampled.convert + @see org.tritonus.sampled.convert.gsm + @see org.tritonus.sampled.convert.jorbis + @see org.tritonus.sampled.convert.lame + @see org.tritonus.sampled.convert.vorbis + + -- cgit v1.2.3