com.jme3.audio.plugins.WAVLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jme3-core Show documentation
Show all versions of jme3-core Show documentation
jMonkeyEngine is a 3-D game engine for adventurous Java developers
/*
* Copyright (c) 2009-2021 jMonkeyEngine
* 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 'jMonkeyEngine' 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.jme3.audio.plugins;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetLoader;
import com.jme3.audio.AudioBuffer;
import com.jme3.audio.AudioData;
import com.jme3.audio.AudioKey;
import com.jme3.audio.AudioStream;
import com.jme3.audio.SeekableStream;
import com.jme3.util.BufferUtils;
import com.jme3.util.LittleEndien;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
public class WAVLoader implements AssetLoader {
private static final Logger logger = Logger.getLogger(WAVLoader.class.getName());
// all these are in big endian
private static final int i_RIFF = 0x46464952;
private static final int i_WAVE = 0x45564157;
private static final int i_fmt = 0x20746D66;
private static final int i_data = 0x61746164;
private boolean readStream = false;
private AudioBuffer audioBuffer;
private AudioStream audioStream;
private AudioData audioData;
private int bytesPerSec;
private float duration;
private ResettableInputStream in;
private int inOffset = 0;
private static class ResettableInputStream extends LittleEndien implements SeekableStream {
final private AssetInfo info;
private int resetOffset = 0;
public ResettableInputStream(AssetInfo info, InputStream in) {
super(in);
this.info = info;
}
public void setResetOffset(int resetOffset) {
this.resetOffset = resetOffset;
}
@Override
public void setTime(float time) {
if (time != 0f) {
throw new UnsupportedOperationException("Seeking WAV files not supported");
}
InputStream newStream = info.openStream();
try {
newStream.skip(resetOffset);
this.in = new BufferedInputStream(newStream);
} catch (IOException ex) {
// Resource could have gotten lost, etc.
try {
newStream.close();
} catch (IOException ex2) {
}
throw new RuntimeException(ex);
}
}
}
private void readFormatChunk(int size) throws IOException{
// if other compressions are supported, size doesn't have to be 16
// if (size != 16)
// logger.warning("Expected size of format chunk to be 16");
int compression = in.readShort();
if (compression != 1){
throw new IOException("WAV Loader only supports PCM wave files");
}
int channels = in.readShort();
int sampleRate = in.readInt();
bytesPerSec = in.readInt(); // used to calculate duration
int bytesPerSample = in.readShort();
int bitsPerSample = in.readShort();
int expectedBytesPerSec = (bitsPerSample * channels * sampleRate) / 8;
if (expectedBytesPerSec != bytesPerSec){
logger.log(Level.WARNING, "Expected {0} bytes per second, got {1}",
new Object[]{expectedBytesPerSec, bytesPerSec});
}
if (bitsPerSample != 8 && bitsPerSample != 16)
throw new IOException("Only 8 and 16 bits per sample are supported!");
if ( (bitsPerSample / 8) * channels != bytesPerSample)
throw new IOException("Invalid bytes per sample value");
if (bytesPerSample * sampleRate != bytesPerSec)
throw new IOException("Invalid bytes per second value");
audioData.setupFormat(channels, bitsPerSample, sampleRate);
int remaining = size - 16;
if (remaining > 0){
in.skipBytes(remaining);
}
}
private void readDataChunkForBuffer(int len) throws IOException {
ByteBuffer data = BufferUtils.createByteBuffer(len);
byte[] buf = new byte[512];
int read = 0;
while ( (read = in.read(buf)) > 0){
data.put(buf, 0, Math.min(read, data.remaining()) );
}
data.flip();
audioBuffer.updateData(data);
in.close();
}
private void readDataChunkForStream(int offset, int len) throws IOException {
in.setResetOffset(offset);
audioStream.updateData(in, duration);
}
private AudioData load(AssetInfo info, InputStream inputStream, boolean stream) throws IOException{
this.in = new ResettableInputStream(info, inputStream);
inOffset = 0;
int sig = in.readInt();
if (sig != i_RIFF)
throw new IOException("File is not a WAVE file");
// skip size
in.readInt();
if (in.readInt() != i_WAVE)
throw new IOException("WAVE File does not contain audio");
inOffset += 4 * 3;
readStream = stream;
if (readStream){
audioStream = new AudioStream();
audioData = audioStream;
}else{
audioBuffer = new AudioBuffer();
audioData = audioBuffer;
}
while (true) {
int type = in.readInt();
int len = in.readInt();
inOffset += 4 * 2;
switch (type) {
case i_fmt:
readFormatChunk(len);
inOffset += len;
break;
case i_data:
// Compute duration based on data chunk size
duration = len / bytesPerSec;
if (readStream) {
readDataChunkForStream(inOffset, len);
} else {
readDataChunkForBuffer(len);
}
return audioData;
default:
int skipped = in.skipBytes(len);
if (skipped <= 0) {
return null;
}
inOffset += skipped;
break;
}
}
}
@Override
public Object load(AssetInfo info) throws IOException {
AudioData data;
InputStream inputStream = null;
try {
inputStream = info.openStream();
data = load(info, inputStream, ((AudioKey)info.getKey()).isStream());
if (data instanceof AudioStream){
inputStream = null;
}
return data;
} finally {
if (inputStream != null){
inputStream.close();
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy