Upgrade OpenAL bits to LWJGL 3.
I didn't find a good way to determine whether OpenAL had already been initialized. So if we need this whole "sharing AL with something else in the same JVM" business, we'll need to find another way to do it.
This commit is contained in:
+3
-9
@@ -32,15 +32,9 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lwjgl.lwjgl</groupId>
|
||||
<artifactId>lwjgl</artifactId>
|
||||
<version>2.9.3</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lwjgl.lwjgl</groupId>
|
||||
<artifactId>lwjgl_util</artifactId>
|
||||
<version>2.9.3</version>
|
||||
<groupId>org.lwjgl</groupId>
|
||||
<artifactId>lwjgl-openal</artifactId>
|
||||
<version>3.3.6</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.threerings.openal;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.lwjgl.util.WaveData;
|
||||
import com.threerings.openal.util.WaveData;
|
||||
|
||||
/**
|
||||
* Contains data for a single sampled sound.
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
|
||||
package com.threerings.openal;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
/**
|
||||
@@ -89,9 +86,13 @@ public class Listener
|
||||
public void setOrientation (float ax, float ay, float az, float ux, float uy, float uz)
|
||||
{
|
||||
if (_ax != ax || _ay != ay || _az != az || _ux != ux || _uy != uy || _uz != uz) {
|
||||
_vbuf.put(_ax = ax).put(_ay = ay).put(_az = az);
|
||||
_vbuf.put(_ux = ux).put(_uy = uy).put(_uz = uz).rewind();
|
||||
AL10.alListener(AL10.AL_ORIENTATION, _vbuf);
|
||||
_vbuf[0] = (_ax = ax);
|
||||
_vbuf[1] = (_ay = ay);
|
||||
_vbuf[2] = (_az = az);
|
||||
_vbuf[3] = (_ux = ux);
|
||||
_vbuf[4] = (_uy = uy);
|
||||
_vbuf[5] = (_uz = uz);
|
||||
AL10.alListenerfv(AL10.AL_ORIENTATION, _vbuf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,5 +116,5 @@ public class Listener
|
||||
protected float _ax, _ay, _az = -1f, _ux, _uy = 1f, _uz;
|
||||
|
||||
/** A buffer for floating point values. */
|
||||
protected FloatBuffer _vbuf = BufferUtils.createFloatBuffer(6);
|
||||
protected float[] _vbuf = new float[6];
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.lwjgl.util.WaveData;
|
||||
import com.threerings.openal.util.WaveData;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@@ -23,11 +23,15 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.openal.AL;
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.lwjgl.openal.AL;
|
||||
import org.lwjgl.openal.ALC10;
|
||||
import org.lwjgl.openal.ALC;
|
||||
import org.lwjgl.openal.ALCCapabilities;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -77,8 +81,13 @@ public class SoundManager
|
||||
*/
|
||||
public void shutdown ()
|
||||
{
|
||||
if (isInitialized() && !_sharingAL) {
|
||||
AL.destroy();
|
||||
if (_alcContext != 0L) {
|
||||
ALC10.alcDestroyContext(_alcContext);
|
||||
_alcContext = 0L;
|
||||
}
|
||||
if (_alcDevice != 0L) {
|
||||
ALC10.alcCloseDevice(_alcDevice);
|
||||
_alcDevice = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,11 +210,20 @@ public class SoundManager
|
||||
_rqueue = rqueue;
|
||||
|
||||
// initialize the OpenAL sound system
|
||||
if (AL.isCreated()) {
|
||||
_sharingAL = true;
|
||||
} else {
|
||||
try {
|
||||
AL.create("", 44100, 15, false);
|
||||
_alcDevice = ALC10.alcOpenDevice((ByteBuffer)null);
|
||||
if (_alcDevice == 0) {
|
||||
log.warning("Failed to create AL device.");
|
||||
// don't start the background loading thread
|
||||
return;
|
||||
}
|
||||
ALCCapabilities deviceCaps = ALC.createCapabilities(_alcDevice);
|
||||
|
||||
_alcContext = ALC10.alcCreateContext(_alcDevice, (IntBuffer)null);
|
||||
ALC10.alcMakeContextCurrent(_alcContext);
|
||||
AL.createCapabilities(deviceCaps);
|
||||
|
||||
// AL.create("", 44100, 15, false);
|
||||
} catch (Exception e) {
|
||||
log.warning("Failed to initialize sound system.", e);
|
||||
// don't start the background loading thread
|
||||
@@ -218,7 +236,6 @@ public class SoundManager
|
||||
// don't start the background loading thread
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// configure our LRU map with a removal observer
|
||||
_clips.setRemovalObserver(new LRUHashMap.RemovalObserver<String, ClipBuffer>() {
|
||||
@@ -415,8 +432,8 @@ public class SoundManager
|
||||
}
|
||||
};
|
||||
|
||||
/** Whether or not we're sharing an AL context that someone else created. */
|
||||
protected boolean _sharingAL;
|
||||
protected long _alcDevice;
|
||||
protected long _alcContext;
|
||||
|
||||
/** Used to get back from the background thread to our "main" thread. */
|
||||
protected RunQueue _rqueue;
|
||||
|
||||
@@ -21,7 +21,7 @@ package com.threerings.openal;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.lwjgl.util.WaveData;
|
||||
import com.threerings.openal.util.WaveData;
|
||||
|
||||
/**
|
||||
* Loads clips via the class loader using LWJGL's {@link WaveData} utility
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.threerings.openal.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.DataInputStream;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.sound.sampled.AudioFileFormat;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
import javax.sound.sampled.spi.AudioFileReader;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Abstract File Reader class.
|
||||
*
|
||||
* @author Jan Borgersen
|
||||
*/
|
||||
abstract class SunFileReader extends AudioFileReader {
|
||||
|
||||
// buffer size for temporary input streams
|
||||
protected static final int bisBufferSize = 4096;
|
||||
|
||||
/**
|
||||
* Constructs a new SunFileReader object.
|
||||
*/
|
||||
SunFileReader() {
|
||||
}
|
||||
|
||||
|
||||
// METHODS TO IMPLEMENT AudioFileReader
|
||||
|
||||
/**
|
||||
* Obtains the audio file format of the input stream provided. The stream must
|
||||
* point to valid audio file data. In general, audio file providers may
|
||||
* need to read some data from the stream before determining whether they
|
||||
* support it. These parsers must
|
||||
* be able to mark the stream, read enough data to determine whether they
|
||||
* support the stream, and, if not, reset the stream's read pointer to its original
|
||||
* position. If the input stream does not support this, this method may fail
|
||||
* with an IOException.
|
||||
* @param stream the input stream from which file format information should be
|
||||
* extracted
|
||||
* @return an <code>AudioFileFormat</code> object describing the audio file format
|
||||
* @throws UnsupportedAudioFileException if the stream does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
* @see InputStream#markSupported
|
||||
* @see InputStream#mark
|
||||
*/
|
||||
abstract public AudioFileFormat getAudioFileFormat(InputStream stream) throws UnsupportedAudioFileException, IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the audio file format of the URL provided. The URL must
|
||||
* point to valid audio file data.
|
||||
* @param url the URL from which file format information should be
|
||||
* extracted
|
||||
* @return an <code>AudioFileFormat</code> object describing the audio file format
|
||||
* @throws UnsupportedAudioFileException if the URL does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
*/
|
||||
abstract public AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the audio file format of the File provided. The File must
|
||||
* point to valid audio file data.
|
||||
* @param file the File from which file format information should be
|
||||
* extracted
|
||||
* @return an <code>AudioFileFormat</code> object describing the audio file format
|
||||
* @throws UnsupportedAudioFileException if the File does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
*/
|
||||
abstract public AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Obtains an audio stream from the input stream provided. The stream must
|
||||
* point to valid audio file data. In general, audio file providers may
|
||||
* need to read some data from the stream before determining whether they
|
||||
* support it. These parsers must
|
||||
* be able to mark the stream, read enough data to determine whether they
|
||||
* support the stream, and, if not, reset the stream's read pointer to its original
|
||||
* position. If the input stream does not support this, this method may fail
|
||||
* with an IOException.
|
||||
* @param stream the input stream from which the <code>AudioInputStream</code> should be
|
||||
* constructed
|
||||
* @return an <code>AudioInputStream</code> object based on the audio file data contained
|
||||
* in the input stream.
|
||||
* @throws UnsupportedAudioFileException if the stream does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
* @see InputStream#markSupported
|
||||
* @see InputStream#mark
|
||||
*/
|
||||
abstract public AudioInputStream getAudioInputStream(InputStream stream) throws UnsupportedAudioFileException, IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Obtains an audio stream from the URL provided. The URL must
|
||||
* point to valid audio file data.
|
||||
* @param url the URL for which the <code>AudioInputStream</code> should be
|
||||
* constructed
|
||||
* @return an <code>AudioInputStream</code> object based on the audio file data pointed
|
||||
* to by the URL
|
||||
* @throws UnsupportedAudioFileException if the URL does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
*/
|
||||
abstract public AudioInputStream getAudioInputStream(URL url) throws UnsupportedAudioFileException, IOException;
|
||||
|
||||
|
||||
/**
|
||||
* Obtains an audio stream from the File provided. The File must
|
||||
* point to valid audio file data.
|
||||
* @param file the File for which the <code>AudioInputStream</code> should be
|
||||
* constructed
|
||||
* @return an <code>AudioInputStream</code> object based on the audio file data pointed
|
||||
* to by the File
|
||||
* @throws UnsupportedAudioFileException if the File does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
*/
|
||||
abstract public AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException;
|
||||
|
||||
|
||||
// HELPER METHODS
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* rllong
|
||||
* Protected helper method to read 64 bits and changing the order of
|
||||
* each bytes.
|
||||
* @param DataInputStream
|
||||
* @return 32 bits swapped value.
|
||||
* @exception IOException
|
||||
*/
|
||||
final int rllong(DataInputStream dis) throws IOException {
|
||||
|
||||
int b1, b2, b3, b4 ;
|
||||
int i = 0;
|
||||
|
||||
i = dis.readInt();
|
||||
|
||||
b1 = ( i & 0xFF ) << 24 ;
|
||||
b2 = ( i & 0xFF00 ) << 8;
|
||||
b3 = ( i & 0xFF0000 ) >> 8;
|
||||
b4 = ( i & 0xFF000000 ) >>> 24;
|
||||
|
||||
i = ( b1 | b2 | b3 | b4 );
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* big2little
|
||||
* Protected helper method to swap the order of bytes in a 32 bit int
|
||||
* @param int
|
||||
* @return 32 bits swapped value
|
||||
*/
|
||||
final int big2little(int i) {
|
||||
|
||||
int b1, b2, b3, b4 ;
|
||||
|
||||
b1 = ( i & 0xFF ) << 24 ;
|
||||
b2 = ( i & 0xFF00 ) << 8;
|
||||
b3 = ( i & 0xFF0000 ) >> 8;
|
||||
b4 = ( i & 0xFF000000 ) >>> 24;
|
||||
|
||||
i = ( b1 | b2 | b3 | b4 );
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* rlshort
|
||||
* Protected helper method to read 16 bits value. Swap high with low byte.
|
||||
* @param DataInputStream
|
||||
* @return the swapped value.
|
||||
* @exception IOException
|
||||
*/
|
||||
final short rlshort(DataInputStream dis) throws IOException {
|
||||
|
||||
short s=0;
|
||||
short high, low;
|
||||
|
||||
s = dis.readShort();
|
||||
|
||||
high = (short)(( s & 0xFF ) << 8) ;
|
||||
low = (short)(( s & 0xFF00 ) >>> 8);
|
||||
|
||||
s = (short)( high | low );
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* big2little
|
||||
* Protected helper method to swap the order of bytes in a 16 bit short
|
||||
* @param int
|
||||
* @return 16 bits swapped value
|
||||
*/
|
||||
final short big2littleShort(short i) {
|
||||
|
||||
short high, low;
|
||||
|
||||
high = (short)(( i & 0xFF ) << 8) ;
|
||||
low = (short)(( i & 0xFF00 ) >>> 8);
|
||||
|
||||
i = (short)( high | low );
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/** Calculates the frame size for PCM frames.
|
||||
* Note that this method is appropriate for non-packed samples.
|
||||
* For instance, 12 bit, 2 channels will return 4 bytes, not 3.
|
||||
* @param sampleSizeInBits the size of a single sample in bits
|
||||
* @param channels the number of channels
|
||||
* @return the size of a PCM frame in bytes.
|
||||
*/
|
||||
static final int calculatePCMFrameSize(int sampleSizeInBits, int channels) {
|
||||
return ((sampleSizeInBits + 7) / 8) * channels;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2008 LWJGL Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'LWJGL' nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.threerings.openal.util;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
import static com.threerings.openal.Log.log;
|
||||
|
||||
/**
|
||||
*
|
||||
* Utitlity class for loading wavefiles.
|
||||
*
|
||||
* @author Brian Matzon <brian@matzon.dk>
|
||||
* @version $Revision$
|
||||
* $Id$
|
||||
*/
|
||||
public class WaveData {
|
||||
/** actual wave data */
|
||||
public final ByteBuffer data;
|
||||
|
||||
/** format type of data */
|
||||
public final int format;
|
||||
|
||||
/** sample rate of data */
|
||||
public final int samplerate;
|
||||
|
||||
/**
|
||||
* Creates a new WaveData
|
||||
*
|
||||
* @param data actual wavedata
|
||||
* @param format format of wave data
|
||||
* @param samplerate sample rate of data
|
||||
*/
|
||||
private WaveData(ByteBuffer data, int format, int samplerate) {
|
||||
this.data = data;
|
||||
this.format = format;
|
||||
this.samplerate = samplerate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the wavedata
|
||||
*/
|
||||
public void dispose() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified url
|
||||
*
|
||||
* @param path URL to file
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(URL path) {
|
||||
try {
|
||||
// due to an issue with AudioSystem.getAudioInputStream
|
||||
// and mixing unsigned and signed code
|
||||
// we will use the reader directly
|
||||
WaveFileReader wfr = new WaveFileReader();
|
||||
return create(wfr.getAudioInputStream(new BufferedInputStream(path.openStream())));
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to create from: " + path + ", " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified in the classpath
|
||||
*
|
||||
* @param path path to file (relative, and in classpath)
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(String path) {
|
||||
return create(Thread.currentThread().getContextClassLoader().getResource(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified inputstream
|
||||
*
|
||||
* @param is InputStream to read from
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(InputStream is) {
|
||||
try {
|
||||
return create(
|
||||
AudioSystem.getAudioInputStream(is));
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to create from inputstream, " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified bytes
|
||||
*
|
||||
* @param buffer array of bytes containing the complete wave file
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(byte[] buffer) {
|
||||
try {
|
||||
return create(
|
||||
AudioSystem.getAudioInputStream(
|
||||
new BufferedInputStream(new ByteArrayInputStream(buffer))));
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to create from byte array, " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified ByetBuffer.
|
||||
* If the buffer is backed by an array, it will be used directly,
|
||||
* else the contents of the buffer will be copied using get(byte[]).
|
||||
*
|
||||
* @param buffer ByteBuffer containing sound file
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(ByteBuffer buffer) {
|
||||
try {
|
||||
byte[] bytes = null;
|
||||
|
||||
if(buffer.hasArray()) {
|
||||
bytes = buffer.array();
|
||||
} else {
|
||||
bytes = new byte[buffer.capacity()];
|
||||
buffer.get(bytes);
|
||||
}
|
||||
return create(bytes);
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to create from ByteBuffer, " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WaveData container from the specified stream
|
||||
*
|
||||
* @param ais AudioInputStream to read from
|
||||
* @return WaveData containing data, or null if a failure occured
|
||||
*/
|
||||
public static WaveData create(AudioInputStream ais) {
|
||||
//get format of data
|
||||
AudioFormat audioformat = ais.getFormat();
|
||||
|
||||
// get channels
|
||||
int channels = 0;
|
||||
if (audioformat.getChannels() == 1) {
|
||||
if (audioformat.getSampleSizeInBits() == 8) {
|
||||
channels = AL10.AL_FORMAT_MONO8;
|
||||
} else if (audioformat.getSampleSizeInBits() == 16) {
|
||||
channels = AL10.AL_FORMAT_MONO16;
|
||||
} else {
|
||||
assert false : "Illegal sample size";
|
||||
}
|
||||
} else if (audioformat.getChannels() == 2) {
|
||||
if (audioformat.getSampleSizeInBits() == 8) {
|
||||
channels = AL10.AL_FORMAT_STEREO8;
|
||||
} else if (audioformat.getSampleSizeInBits() == 16) {
|
||||
channels = AL10.AL_FORMAT_STEREO16;
|
||||
} else {
|
||||
assert false : "Illegal sample size";
|
||||
}
|
||||
} else {
|
||||
assert false : "Only mono or stereo is supported";
|
||||
}
|
||||
|
||||
//read data into buffer
|
||||
ByteBuffer buffer = null;
|
||||
try {
|
||||
int available = ais.available();
|
||||
if(available <= 0) {
|
||||
available = ais.getFormat().getChannels() * (int) ais.getFrameLength() * ais.getFormat().getSampleSizeInBits() / 8;
|
||||
}
|
||||
byte[] buf = new byte[ais.available()];
|
||||
int read = 0, total = 0;
|
||||
while ((read = ais.read(buf, total, buf.length - total)) != -1
|
||||
&& total < buf.length) {
|
||||
total += read;
|
||||
}
|
||||
buffer = convertAudioBytes(buf, audioformat.getSampleSizeInBits() == 16, audioformat.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
|
||||
} catch (IOException ioe) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
//create our result
|
||||
WaveData wavedata =
|
||||
new WaveData(buffer, channels, (int) audioformat.getSampleRate());
|
||||
|
||||
//close stream
|
||||
try {
|
||||
ais.close();
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
|
||||
return wavedata;
|
||||
}
|
||||
|
||||
private static ByteBuffer convertAudioBytes(byte[] audio_bytes, boolean two_bytes_data, ByteOrder order) {
|
||||
ByteBuffer dest = ByteBuffer.allocateDirect(audio_bytes.length);
|
||||
dest.order(ByteOrder.nativeOrder());
|
||||
ByteBuffer src = ByteBuffer.wrap(audio_bytes);
|
||||
src.order(order);
|
||||
if (two_bytes_data) {
|
||||
ShortBuffer dest_short = dest.asShortBuffer();
|
||||
ShortBuffer src_short = src.asShortBuffer();
|
||||
while (src_short.hasRemaining())
|
||||
dest_short.put(src_short.get());
|
||||
} else {
|
||||
while (src.hasRemaining())
|
||||
dest.put(src.get());
|
||||
}
|
||||
dest.rewind();
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.threerings.openal.util;
|
||||
|
||||
import javax.sound.sampled.AudioFileFormat;
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
|
||||
|
||||
/**
|
||||
* WAVE file format class.
|
||||
*
|
||||
* @author Jan Borgersen
|
||||
*/
|
||||
|
||||
final class WaveFileFormat extends AudioFileFormat {
|
||||
|
||||
/**
|
||||
* Wave format type.
|
||||
*/
|
||||
private final int waveType;
|
||||
|
||||
//$$fb 2001-07-13: added management of header size in this class
|
||||
//$$fb 2002-04-16: Fix for 4636355: RIFF audio headers could be _more_ spec compliant
|
||||
private static final int STANDARD_HEADER_SIZE = 28;
|
||||
|
||||
//$$fb 2002-04-16: Fix for 4636355: RIFF audio headers could be _more_ spec compliant
|
||||
/**
|
||||
* fmt_ chunk size in bytes
|
||||
*/
|
||||
private static final int STANDARD_FMT_CHUNK_SIZE = 16;
|
||||
|
||||
// magic numbers
|
||||
static final int RIFF_MAGIC = 1380533830;
|
||||
static final int WAVE_MAGIC = 1463899717;
|
||||
static final int FMT_MAGIC = 0x666d7420; // "fmt "
|
||||
static final int DATA_MAGIC = 0x64617461; // "data"
|
||||
|
||||
// encodings
|
||||
static final int WAVE_FORMAT_UNKNOWN = 0x0000;
|
||||
static final int WAVE_FORMAT_PCM = 0x0001;
|
||||
static final int WAVE_FORMAT_ADPCM = 0x0002;
|
||||
static final int WAVE_FORMAT_ALAW = 0x0006;
|
||||
static final int WAVE_FORMAT_MULAW = 0x0007;
|
||||
static final int WAVE_FORMAT_OKI_ADPCM = 0x0010;
|
||||
static final int WAVE_FORMAT_DIGISTD = 0x0015;
|
||||
static final int WAVE_FORMAT_DIGIFIX = 0x0016;
|
||||
static final int WAVE_IBM_FORMAT_MULAW = 0x0101;
|
||||
static final int WAVE_IBM_FORMAT_ALAW = 0x0102;
|
||||
static final int WAVE_IBM_FORMAT_ADPCM = 0x0103;
|
||||
static final int WAVE_FORMAT_DVI_ADPCM = 0x0011;
|
||||
static final int WAVE_FORMAT_SX7383 = 0x1C07;
|
||||
|
||||
|
||||
WaveFileFormat( AudioFileFormat aff ) {
|
||||
|
||||
this( aff.getType(), aff.getByteLength(), aff.getFormat(), aff.getFrameLength() );
|
||||
}
|
||||
|
||||
WaveFileFormat(AudioFileFormat.Type type, int lengthInBytes, AudioFormat format, int lengthInFrames) {
|
||||
|
||||
super(type,lengthInBytes,format,lengthInFrames);
|
||||
|
||||
AudioFormat.Encoding encoding = format.getEncoding();
|
||||
|
||||
if( encoding.equals(AudioFormat.Encoding.ALAW) ) {
|
||||
waveType = WAVE_FORMAT_ALAW;
|
||||
} else if( encoding.equals(AudioFormat.Encoding.ULAW) ) {
|
||||
waveType = WAVE_FORMAT_MULAW;
|
||||
} else if( encoding.equals(AudioFormat.Encoding.PCM_SIGNED) ||
|
||||
encoding.equals(AudioFormat.Encoding.PCM_UNSIGNED) ) {
|
||||
waveType = WAVE_FORMAT_PCM;
|
||||
} else {
|
||||
waveType = WAVE_FORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
int getWaveType() {
|
||||
|
||||
return waveType;
|
||||
}
|
||||
|
||||
int getHeaderSize() {
|
||||
return getHeaderSize(getWaveType());
|
||||
}
|
||||
|
||||
static int getHeaderSize(int waveType) {
|
||||
//$$fb 2002-04-16: Fix for 4636355: RIFF audio headers could be _more_ spec compliant
|
||||
// use dynamic format chunk size
|
||||
return STANDARD_HEADER_SIZE + getFmtChunkSize(waveType);
|
||||
}
|
||||
|
||||
static int getFmtChunkSize(int waveType) {
|
||||
//$$fb 2002-04-16: Fix for 4636355: RIFF audio headers could be _more_ spec compliant
|
||||
// add 2 bytes for "codec specific data length" for non-PCM codecs
|
||||
int result = STANDARD_FMT_CHUNK_SIZE;
|
||||
if (waveType != WAVE_FORMAT_PCM) {
|
||||
result += 2; // WORD for "codec specific data length"
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.threerings.openal.util;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.sound.sampled.AudioFileFormat;
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* WAVE file reader.
|
||||
*
|
||||
* @author Kara Kytle
|
||||
* @author Jan Borgersen
|
||||
* @author Florian Bomers
|
||||
*/
|
||||
public final class WaveFileReader extends SunFileReader {
|
||||
|
||||
private static final int MAX_READ_LENGTH = 12;
|
||||
|
||||
/**
|
||||
* Obtains the audio file format of the input stream provided. The stream must
|
||||
* point to valid audio file data. In general, audio file providers may
|
||||
* need to read some data from the stream before determining whether they
|
||||
* support it. These parsers must
|
||||
* be able to mark the stream, read enough data to determine whether they
|
||||
* support the stream, and, if not, reset the stream's read pointer to its original
|
||||
* position. If the input stream does not support this, this method may fail
|
||||
* with an IOException.
|
||||
* @param stream the input stream from which file format information should be
|
||||
* extracted
|
||||
* @return an <code>AudioFileFormat</code> object describing the audio file format
|
||||
* @throws UnsupportedAudioFileException if the stream does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
* @see InputStream#markSupported
|
||||
* @see InputStream#mark
|
||||
*/
|
||||
public AudioFileFormat getAudioFileFormat(InputStream stream) throws UnsupportedAudioFileException, IOException {
|
||||
// fix for 4489272: AudioSystem.getAudioFileFormat() fails for InputStream, but works for URL
|
||||
AudioFileFormat aff = getFMT(stream, true);
|
||||
// the following is not strictly necessary - but was implemented like that in 1.3.0 - 1.4.1
|
||||
// so I leave it as it was. May remove this for 1.5.0
|
||||
stream.reset();
|
||||
return aff;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the audio file format of the URL provided. The URL must
|
||||
* point to valid audio file data.
|
||||
* @param url the URL from which file format information should be
|
||||
* extracted
|
||||
* @return an <code>AudioFileFormat</code> object describing the audio file format
|
||||
* @throws UnsupportedAudioFileException if the URL does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
*/
|
||||
public AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException {
|
||||
InputStream urlStream = url.openStream(); // throws IOException
|
||||
AudioFileFormat fileFormat = null;
|
||||
try {
|
||||
fileFormat = getFMT(urlStream, false);
|
||||
} finally {
|
||||
urlStream.close();
|
||||
}
|
||||
return fileFormat;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the audio file format of the File provided. The File must
|
||||
* point to valid audio file data.
|
||||
* @param file the File from which file format information should be
|
||||
* extracted
|
||||
* @return an <code>AudioFileFormat</code> object describing the audio file format
|
||||
* @throws UnsupportedAudioFileException if the File does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
*/
|
||||
public AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException {
|
||||
AudioFileFormat fileFormat = null;
|
||||
FileInputStream fis = new FileInputStream(file); // throws IOException
|
||||
// part of fix for 4325421
|
||||
try {
|
||||
fileFormat = getFMT(fis, false);
|
||||
} finally {
|
||||
fis.close();
|
||||
}
|
||||
|
||||
return fileFormat;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains an audio stream from the input stream provided. The stream must
|
||||
* point to valid audio file data. In general, audio file providers may
|
||||
* need to read some data from the stream before determining whether they
|
||||
* support it. These parsers must
|
||||
* be able to mark the stream, read enough data to determine whether they
|
||||
* support the stream, and, if not, reset the stream's read pointer to its original
|
||||
* position. If the input stream does not support this, this method may fail
|
||||
* with an IOException.
|
||||
* @param stream the input stream from which the <code>AudioInputStream</code> should be
|
||||
* constructed
|
||||
* @return an <code>AudioInputStream</code> object based on the audio file data contained
|
||||
* in the input stream.
|
||||
* @throws UnsupportedAudioFileException if the stream does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
* @see InputStream#markSupported
|
||||
* @see InputStream#mark
|
||||
*/
|
||||
public AudioInputStream getAudioInputStream(InputStream stream) throws UnsupportedAudioFileException, IOException {
|
||||
// getFMT leaves the input stream at the beginning of the audio data
|
||||
AudioFileFormat fileFormat = getFMT(stream, true); // throws UnsupportedAudioFileException, IOException
|
||||
|
||||
// we've got everything, and the stream is at the
|
||||
// beginning of the audio data, so return an AudioInputStream.
|
||||
return new AudioInputStream(stream, fileFormat.getFormat(), fileFormat.getFrameLength());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains an audio stream from the URL provided. The URL must
|
||||
* point to valid audio file data.
|
||||
* @param url the URL for which the <code>AudioInputStream</code> should be
|
||||
* constructed
|
||||
* @return an <code>AudioInputStream</code> object based on the audio file data pointed
|
||||
* to by the URL
|
||||
* @throws UnsupportedAudioFileException if the URL does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
*/
|
||||
public AudioInputStream getAudioInputStream(URL url) throws UnsupportedAudioFileException, IOException {
|
||||
InputStream urlStream = url.openStream(); // throws IOException
|
||||
AudioFileFormat fileFormat = null;
|
||||
try {
|
||||
fileFormat = getFMT(urlStream, false);
|
||||
} finally {
|
||||
if (fileFormat == null) {
|
||||
urlStream.close();
|
||||
}
|
||||
}
|
||||
return new AudioInputStream(urlStream, fileFormat.getFormat(), fileFormat.getFrameLength());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains an audio stream from the File provided. The File must
|
||||
* point to valid audio file data.
|
||||
* @param file the File for which the <code>AudioInputStream</code> should be
|
||||
* constructed
|
||||
* @return an <code>AudioInputStream</code> object based on the audio file data pointed
|
||||
* to by the File
|
||||
* @throws UnsupportedAudioFileException if the File does not point to valid audio
|
||||
* file data recognized by the system
|
||||
* @throws IOException if an I/O exception occurs
|
||||
*/
|
||||
public AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException {
|
||||
FileInputStream fis = new FileInputStream(file); // throws IOException
|
||||
AudioFileFormat fileFormat = null;
|
||||
// part of fix for 4325421
|
||||
try {
|
||||
fileFormat = getFMT(fis, false);
|
||||
} finally {
|
||||
if (fileFormat == null) {
|
||||
fis.close();
|
||||
}
|
||||
}
|
||||
return new AudioInputStream(fis, fileFormat.getFormat(), fileFormat.getFrameLength());
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
|
||||
private AudioFileFormat getFMT(InputStream stream, boolean doReset) throws UnsupportedAudioFileException, IOException {
|
||||
|
||||
// assumes sream is rewound
|
||||
|
||||
int bytesRead;
|
||||
int nread = 0;
|
||||
int fmt;
|
||||
int length = 0;
|
||||
int wav_type = 0;
|
||||
short channels;
|
||||
long sampleRate;
|
||||
long avgBytesPerSec;
|
||||
short blockAlign;
|
||||
int sampleSizeInBits;
|
||||
AudioFormat.Encoding encoding = null;
|
||||
|
||||
DataInputStream dis = new DataInputStream( stream );
|
||||
|
||||
if (doReset) {
|
||||
dis.mark(MAX_READ_LENGTH);
|
||||
}
|
||||
|
||||
int magic = dis.readInt();
|
||||
int fileLength = rllong(dis);
|
||||
int waveMagic = dis.readInt();
|
||||
int totallength;
|
||||
if (fileLength <= 0) {
|
||||
fileLength = AudioSystem.NOT_SPECIFIED;
|
||||
totallength = AudioSystem.NOT_SPECIFIED;
|
||||
} else {
|
||||
totallength = fileLength + 8;
|
||||
}
|
||||
|
||||
if ((magic != WaveFileFormat.RIFF_MAGIC) || (waveMagic != WaveFileFormat.WAVE_MAGIC)) {
|
||||
// not WAVE, throw UnsupportedAudioFileException
|
||||
if (doReset) {
|
||||
dis.reset();
|
||||
}
|
||||
throw new UnsupportedAudioFileException("not a WAVE file");
|
||||
}
|
||||
|
||||
// find and read the "fmt" chunk
|
||||
// we break out of this loop either by hitting EOF or finding "fmt "
|
||||
while(true) {
|
||||
|
||||
try {
|
||||
fmt = dis.readInt();
|
||||
nread += 4;
|
||||
if( fmt==WaveFileFormat.FMT_MAGIC ) {
|
||||
// we've found the 'fmt' chunk
|
||||
break;
|
||||
} else {
|
||||
// else not 'fmt', skip this chunk
|
||||
length = rllong(dis);
|
||||
nread += 4;
|
||||
if (length % 2 > 0) length++;
|
||||
nread += dis.skipBytes(length);
|
||||
}
|
||||
} catch (EOFException eof) {
|
||||
// we've reached the end of the file without finding the 'fmt' chunk
|
||||
throw new UnsupportedAudioFileException("Not a valid WAV file");
|
||||
}
|
||||
}
|
||||
|
||||
// Read the format chunk size.
|
||||
length = rllong(dis);
|
||||
nread += 4;
|
||||
|
||||
// This is the nread position at the end of the format chunk
|
||||
int endLength = nread + length;
|
||||
|
||||
// Read the wave format data out of the format chunk.
|
||||
|
||||
// encoding.
|
||||
wav_type = rlshort(dis); nread += 2;
|
||||
|
||||
if (wav_type == WaveFileFormat.WAVE_FORMAT_PCM)
|
||||
encoding = AudioFormat.Encoding.PCM_SIGNED; // if 8-bit, we need PCM_UNSIGNED, below...
|
||||
else if ( wav_type == WaveFileFormat.WAVE_FORMAT_ALAW )
|
||||
encoding = AudioFormat.Encoding.ALAW;
|
||||
else if ( wav_type == WaveFileFormat.WAVE_FORMAT_MULAW )
|
||||
encoding = AudioFormat.Encoding.ULAW;
|
||||
else {
|
||||
// we don't support any other WAVE formats....
|
||||
throw new UnsupportedAudioFileException("Not a supported WAV file");
|
||||
}
|
||||
// channels
|
||||
channels = rlshort(dis); nread += 2;
|
||||
if (channels <= 0) {
|
||||
throw new UnsupportedAudioFileException("Invalid number of channels");
|
||||
}
|
||||
|
||||
// sample rate.
|
||||
sampleRate = rllong(dis); nread += 4;
|
||||
|
||||
// this is the avgBytesPerSec
|
||||
avgBytesPerSec = rllong(dis); nread += 4;
|
||||
|
||||
// this is blockAlign value
|
||||
blockAlign = rlshort(dis); nread += 2;
|
||||
|
||||
// this is the PCM-specific value bitsPerSample
|
||||
sampleSizeInBits = (int)rlshort(dis); nread += 2;
|
||||
if (sampleSizeInBits <= 0) {
|
||||
throw new UnsupportedAudioFileException("Invalid bitsPerSample");
|
||||
}
|
||||
|
||||
// if sampleSizeInBits==8, we need to use PCM_UNSIGNED
|
||||
if ((sampleSizeInBits==8) && encoding.equals(AudioFormat.Encoding.PCM_SIGNED))
|
||||
encoding = AudioFormat.Encoding.PCM_UNSIGNED;
|
||||
|
||||
// skip any difference between the length of the format chunk
|
||||
// and what we read
|
||||
|
||||
// if the length of the chunk is odd, there's an extra pad byte
|
||||
// at the end. i've never seen this in the fmt chunk, but we
|
||||
// should check to make sure.
|
||||
|
||||
if (length % 2 != 0) length += 1;
|
||||
|
||||
// $$jb: 07.28.99: endLength>nread, not length>nread.
|
||||
// This fixes #4257986
|
||||
if (endLength > nread)
|
||||
nread += dis.skipBytes(endLength - nread);
|
||||
|
||||
// we have a format now, so find the "data" chunk
|
||||
// we break out of this loop either by hitting EOF or finding "data"
|
||||
// $$kk: if "data" chunk precedes "fmt" chunk we are hosed -- can this legally happen?
|
||||
nread = 0;
|
||||
while(true) {
|
||||
try{
|
||||
int datahdr = dis.readInt();
|
||||
nread+=4;
|
||||
if (datahdr == WaveFileFormat.DATA_MAGIC) {
|
||||
// we've found the 'data' chunk
|
||||
break;
|
||||
} else {
|
||||
// else not 'data', skip this chunk
|
||||
int thisLength = rllong(dis); nread += 4;
|
||||
if (thisLength % 2 > 0) thisLength++;
|
||||
nread += dis.skipBytes(thisLength);
|
||||
}
|
||||
} catch (EOFException eof) {
|
||||
// we've reached the end of the file without finding the 'data' chunk
|
||||
throw new UnsupportedAudioFileException("Not a valid WAV file");
|
||||
}
|
||||
}
|
||||
// this is the length of the data chunk
|
||||
int dataLength = rllong(dis); nread += 4;
|
||||
|
||||
// now build the new AudioFileFormat and return
|
||||
|
||||
AudioFormat format = new AudioFormat(encoding,
|
||||
(float)sampleRate,
|
||||
sampleSizeInBits, channels,
|
||||
calculatePCMFrameSize(sampleSizeInBits, channels),
|
||||
(float)sampleRate, false);
|
||||
|
||||
return new WaveFileFormat(AudioFileFormat.Type.WAVE,
|
||||
totallength,
|
||||
format,
|
||||
dataLength / format.getFrameSize());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user