/* * Copyright 2011 Phil Burk, Mobileer Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jsyn.util; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import com.jsyn.data.FloatSample; import com.jsyn.util.soundfile.CustomSampleLoader; /** * Load a FloatSample from various sources. The default loader uses custom code to load WAV or AIF * files. Supported data formats are 16, 24 and 32 bit PCM, and 32-bit float. Compressed formats * such as unsigned 8-bit, uLaw, A-Law and MP3 are not support. If you need to load one of those * files try setJavaSoundPreferred(true). Or convert it to a supported format using Audacity or Sox * or some other sample file tool. Here is an example of loading a sample from a file. * *
*
* File sampleFile = new File("guitar.wav");
* FloatSample sample = SampleLoader.loadFloatSample( sampleFile );
*
*
*
* @author Phil Burk (C) 2011 Mobileer Inc
*/
public class SampleLoader {
private static boolean javaSoundPreferred = false;
private static final String JS_LOADER_NAME = "com.jsyn.util.JavaSoundSampleLoader";
/**
* Try to create an implementation of AudioSampleLoader.
*
* @return A device supported on this platform.
*/
private static AudioSampleLoader createLoader() {
AudioSampleLoader loader = null;
try {
if (javaSoundPreferred) {
loader = (AudioSampleLoader) JavaTools.loadClass(JS_LOADER_NAME).newInstance();
} else {
loader = new CustomSampleLoader();
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return loader;
}
/**
* Load a FloatSample from a File object.
*/
public static FloatSample loadFloatSample(File fileIn) throws IOException {
AudioSampleLoader loader = SampleLoader.createLoader();
return loader.loadFloatSample(fileIn);
}
/**
* Load a FloatSample from an InputStream. This is handy when loading Resources from a JAR file.
*/
public static FloatSample loadFloatSample(InputStream inputStream) throws IOException {
AudioSampleLoader loader = SampleLoader.createLoader();
return loader.loadFloatSample(inputStream);
}
/**
* Load a FloatSample from a URL.. This is handy when loading Resources from a website.
*/
public static FloatSample loadFloatSample(URL url) throws IOException {
AudioSampleLoader loader = SampleLoader.createLoader();
return loader.loadFloatSample(url);
}
public static boolean isJavaSoundPreferred() {
return javaSoundPreferred;
}
/**
* If set true then the audio file parser from JavaSound will be used. Note that JavaSound
* cannot load audio files containing floating point data. But it may be able to load some
* compressed data formats such as uLaw.
*
* Note: JavaSound is not supported on Android.
*
* @param javaSoundPreferred
*/
public static void setJavaSoundPreferred(boolean javaSoundPreferred) {
SampleLoader.javaSoundPreferred = javaSoundPreferred;
}
/**
* Decode 24 bit samples from a BigEndian byte array into a float array. The samples will be
* normalized into the range -1.0 to +1.0.
*
* @param audioBytes raw data from an audio file
* @param offset first element of byte array
* @param numBytes number of bytes to process
* @param data array to be filled with floats
* @param outputOffset first element of float array to be filled
*/
public static void decodeBigI24ToF32(byte[] audioBytes, int offset, int numBytes, float[] data,
int outputOffset) {
int lastByte = offset + numBytes;
int byteCursor = offset;
int floatCursor = outputOffset;
while (byteCursor < lastByte) {
int hi = ((audioBytes[byteCursor++]) & 0x00FF);
int mid = ((audioBytes[byteCursor++]) & 0x00FF);
int lo = ((audioBytes[byteCursor++]) & 0x00FF);
int value = (hi << 24) | (mid << 16) | (lo << 8);
data[floatCursor++] = value * (1.0f / Integer.MAX_VALUE);
}
}
public static void decodeBigI16ToF32(byte[] audioBytes, int offset, int numBytes, float[] data,
int outputOffset) {
int lastByte = offset + numBytes;
int byteCursor = offset;
int floatCursor = outputOffset;
while (byteCursor < lastByte) {
int hi = ((audioBytes[byteCursor++]) & 0x00FF);
int lo = ((audioBytes[byteCursor++]) & 0x00FF);
short value = (short) ((hi << 8) | lo);
data[floatCursor++] = value * (1.0f / 32768);
}
}
public static void decodeBigF32ToF32(byte[] audioBytes, int offset, int numBytes, float[] data,
int outputOffset) {
int lastByte = offset + numBytes;
int byteCursor = offset;
int floatCursor = outputOffset;
while (byteCursor < lastByte) {
int bits = audioBytes[byteCursor++];
bits = (bits << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
bits = (bits << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
bits = (bits << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
data[floatCursor++] = Float.intBitsToFloat(bits);
}
}
public static void decodeBigI32ToF32(byte[] audioBytes, int offset, int numBytes, float[] data,
int outputOffset) {
int lastByte = offset + numBytes;
int byteCursor = offset;
int floatCursor = outputOffset;
while (byteCursor < lastByte) {
int value = audioBytes[byteCursor++]; // MSB
value = (value << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
value = (value << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
value = (value << 8) | ((audioBytes[byteCursor++]) & 0x00FF);
data[floatCursor++] = value * (1.0f / Integer.MAX_VALUE);
}
}
public static void decodeLittleF32ToF32(byte[] audioBytes, int offset, int numBytes,
float[] data, int outputOffset) {
int lastByte = offset + numBytes;
int byteCursor = offset;
int floatCursor = outputOffset;
while (byteCursor < lastByte) {
int bits = ((audioBytes[byteCursor++]) & 0x00FF); // LSB
bits += ((audioBytes[byteCursor++]) & 0x00FF) << 8;
bits += ((audioBytes[byteCursor++]) & 0x00FF) << 16;
bits += (audioBytes[byteCursor++]) << 24;
data[floatCursor++] = Float.intBitsToFloat(bits);
}
}
public static void decodeLittleI32ToF32(byte[] audioBytes, int offset, int numBytes,
float[] data, int outputOffset) {
int lastByte = offset + numBytes;
int byteCursor = offset;
int floatCursor = outputOffset;
while (byteCursor < lastByte) {
int value = ((audioBytes[byteCursor++]) & 0x00FF);
value += ((audioBytes[byteCursor++]) & 0x00FF) << 8;
value += ((audioBytes[byteCursor++]) & 0x00FF) << 16;
value += (audioBytes[byteCursor++]) << 24;
data[floatCursor++] = value * (1.0f / Integer.MAX_VALUE);
}
}
public static void decodeLittleI24ToF32(byte[] audioBytes, int offset, int numBytes,
float[] data, int outputOffset) {
int lastByte = offset + numBytes;
int byteCursor = offset;
int floatCursor = outputOffset;
while (byteCursor < lastByte) {
int lo = ((audioBytes[byteCursor++]) & 0x00FF);
int mid = ((audioBytes[byteCursor++]) & 0x00FF);
int hi = ((audioBytes[byteCursor++]) & 0x00FF);
int value = (hi << 24) | (mid << 16) | (lo << 8);
data[floatCursor++] = value * (1.0f / Integer.MAX_VALUE);
}
}
public static void decodeLittleI16ToF32(byte[] audioBytes, int offset, int numBytes,
float[] data, int outputOffset) {
int lastByte = offset + numBytes;
int byteCursor = offset;
int floatCursor = outputOffset;
while (byteCursor < lastByte) {
int lo = ((audioBytes[byteCursor++]) & 0x00FF);
int hi = ((audioBytes[byteCursor++]) & 0x00FF);
short value = (short) ((hi << 8) | lo);
float sample = value * (1.0f / 32768);
data[floatCursor++] = sample;
}
}
}