aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/com/jsyn/util/SampleLoader.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/jsyn/util/SampleLoader.java')
-rw-r--r--src/main/java/com/jsyn/util/SampleLoader.java230
1 files changed, 230 insertions, 0 deletions
diff --git a/src/main/java/com/jsyn/util/SampleLoader.java b/src/main/java/com/jsyn/util/SampleLoader.java
new file mode 100644
index 0000000..170b4cb
--- /dev/null
+++ b/src/main/java/com/jsyn/util/SampleLoader.java
@@ -0,0 +1,230 @@
+/*
+ * 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.
+ *
+ * <pre>
+ * <code>
+ * File sampleFile = new File("guitar.wav");
+ * FloatSample sample = SampleLoader.loadFloatSample( sampleFile );
+ * </code>
+ * </pre>
+ *
+ * @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;
+ }
+ }
+
+}