aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/jsyn/util/soundfile
diff options
context:
space:
mode:
authorRubbaBoy <[email protected]>2020-07-06 02:33:28 -0400
committerPhil Burk <[email protected]>2020-10-30 11:19:34 -0700
commit46888fae6eb7b1dd386f7af7d101ead99ae61981 (patch)
tree8969bbfd68d2fb5c0d8b86da49ec2eca230a72ab /src/com/jsyn/util/soundfile
parentc51e92e813dd481603de078f0778e1f75db2ab05 (diff)
Restructured project, added gradle, JUnit, logger, and more
Added Gradle (and removed ant), modernized testing via the JUnit framework, moved standalone examples from the tests directory to a separate module, removed sparsely used Java logger and replaced it with SLF4J. More work could be done, however this is a great start to greatly improving the health of the codebase.
Diffstat (limited to 'src/com/jsyn/util/soundfile')
-rw-r--r--src/com/jsyn/util/soundfile/AIFFFileParser.java227
-rw-r--r--src/com/jsyn/util/soundfile/AudioFileParser.java129
-rw-r--r--src/com/jsyn/util/soundfile/ChunkHandler.java49
-rw-r--r--src/com/jsyn/util/soundfile/CustomSampleLoader.java60
-rw-r--r--src/com/jsyn/util/soundfile/IFFParser.java307
-rw-r--r--src/com/jsyn/util/soundfile/WAVEFileParser.java334
6 files changed, 0 insertions, 1106 deletions
diff --git a/src/com/jsyn/util/soundfile/AIFFFileParser.java b/src/com/jsyn/util/soundfile/AIFFFileParser.java
deleted file mode 100644
index 2b09d78..0000000
--- a/src/com/jsyn/util/soundfile/AIFFFileParser.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright 2009 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.soundfile;
-
-import java.io.EOFException;
-import java.io.IOException;
-
-import com.jsyn.data.FloatSample;
-import com.jsyn.data.SampleMarker;
-import com.jsyn.util.SampleLoader;
-
-public class AIFFFileParser extends AudioFileParser {
- private static final String SUPPORTED_FORMATS = "Only 16 and 24 bit PCM or 32-bit float AIF files supported.";
- static final int AIFF_ID = ('A' << 24) | ('I' << 16) | ('F' << 8) | 'F';
- static final int AIFC_ID = ('A' << 24) | ('I' << 16) | ('F' << 8) | 'C';
- static final int COMM_ID = ('C' << 24) | ('O' << 16) | ('M' << 8) | 'M';
- static final int SSND_ID = ('S' << 24) | ('S' << 16) | ('N' << 8) | 'D';
- static final int MARK_ID = ('M' << 24) | ('A' << 16) | ('R' << 8) | 'K';
- static final int INST_ID = ('I' << 24) | ('N' << 16) | ('S' << 8) | 'T';
- static final int NONE_ID = ('N' << 24) | ('O' << 16) | ('N' << 8) | 'E';
- static final int FL32_ID = ('F' << 24) | ('L' << 16) | ('3' << 8) | '2';
- static final int FL32_ID_LC = ('f' << 24) | ('l' << 16) | ('3' << 8) | '2';
-
- int sustainBeginID = -1;
- int sustainEndID = -1;
- int releaseBeginID = -1;
- int releaseEndID = -1;
- boolean typeFloat = false;
-
- @Override
- FloatSample finish() throws IOException {
- setLoops();
-
- if ((byteData == null)) {
- throw new IOException("No data found in audio sample.");
- }
- float[] floatData = new float[numFrames * samplesPerFrame];
- if (bitsPerSample == 16) {
- SampleLoader.decodeBigI16ToF32(byteData, 0, byteData.length, floatData, 0);
- } else if (bitsPerSample == 24) {
- SampleLoader.decodeBigI24ToF32(byteData, 0, byteData.length, floatData, 0);
- } else if (bitsPerSample == 32) {
- if (typeFloat) {
- SampleLoader.decodeBigF32ToF32(byteData, 0, byteData.length, floatData, 0);
- } else {
- SampleLoader.decodeBigI32ToF32(byteData, 0, byteData.length, floatData, 0);
- }
- } else {
- throw new IOException(SUPPORTED_FORMATS + " size = " + bitsPerSample);
- }
-
- return makeSample(floatData);
- }
-
- double read80BitFloat() throws IOException {
- /*
- * This is not a full decoding of the 80 bit number but it should suffice for the range we
- * expect.
- */
- byte[] bytes = new byte[10];
- parser.read(bytes);
- int exp = ((bytes[0] & 0x3F) << 8) | (bytes[1] & 0xFF);
- int mant = ((bytes[2] & 0xFF) << 16) | ((bytes[3] & 0xFF) << 8) | (bytes[4] & 0xFF);
- // System.out.println( "exp = " + exp + ", mant = " + mant );
- return mant / (double) (1 << (22 - exp));
- }
-
- void parseCOMMChunk(IFFParser parser, int ckSize) throws IOException {
- samplesPerFrame = parser.readShortBig();
- numFrames = parser.readIntBig();
- bitsPerSample = parser.readShortBig();
- frameRate = read80BitFloat();
- if (ckSize > 18) {
- int format = parser.readIntBig();
- // Validate data format.
- if ((format == FL32_ID) || (format == FL32_ID_LC)) {
- typeFloat = true;
- } else if (format == NONE_ID) {
- typeFloat = false;
- } else {
- throw new IOException(SUPPORTED_FORMATS + " format " + IFFParser.IDToString(format));
- }
- }
-
- bytesPerSample = (bitsPerSample + 7) / 8;
- bytesPerFrame = bytesPerSample * samplesPerFrame;
- }
-
- /* parse tuning and multi-sample info */
- @SuppressWarnings("unused")
- void parseINSTChunk(IFFParser parser, int ckSize) throws IOException {
- int baseNote = parser.readByte();
- int detune = parser.readByte();
- originalPitch = baseNote + (0.01 * detune);
-
- int lowNote = parser.readByte();
- int highNote = parser.readByte();
-
- parser.skip(2); /* lo,hi velocity */
- int gain = parser.readShortBig();
-
- int playMode = parser.readShortBig(); /* sustain */
- sustainBeginID = parser.readShortBig();
- sustainEndID = parser.readShortBig();
-
- playMode = parser.readShortBig(); /* release */
- releaseBeginID = parser.readShortBig();
- releaseEndID = parser.readShortBig();
- }
-
- private void setLoops() {
- SampleMarker cuePoint = cueMap.get(sustainBeginID);
- if (cuePoint != null) {
- sustainBegin = cuePoint.position;
- }
- cuePoint = cueMap.get(sustainEndID);
- if (cuePoint != null) {
- sustainEnd = cuePoint.position;
- }
- }
-
- void parseSSNDChunk(IFFParser parser, int ckSize) throws IOException {
- long numRead;
- // System.out.println("parseSSNDChunk()");
- int offset = parser.readIntBig();
- parser.readIntBig(); /* blocksize */
- parser.skip(offset);
- dataPosition = parser.getOffset();
- int numBytes = ckSize - 8 - offset;
- if (ifLoadData) {
- byteData = new byte[numBytes];
- numRead = parser.read(byteData);
- } else {
- numRead = parser.skip(numBytes);
- }
- if (numRead != numBytes)
- throw new EOFException("AIFF data chunk too short!");
- }
-
- void parseMARKChunk(IFFParser parser, int ckSize) throws IOException {
- long startOffset = parser.getOffset();
- int numCuePoints = parser.readShortBig();
- // System.out.println( "parseCueChunk: numCuePoints = " + numCuePoints
- // );
- for (int i = 0; i < numCuePoints; i++) {
- // Some AIF files have a bogus numCuePoints so check to see if we
- // are at end.
- long numInMark = parser.getOffset() - startOffset;
- if (numInMark >= ckSize) {
- System.out.println("Reached end of MARK chunk with bogus numCuePoints = "
- + numCuePoints);
- break;
- }
-
- int uniqueID = parser.readShortBig();
- int position = parser.readIntBig();
- int len = parser.read();
- String markerName = parseString(parser, len);
- if ((len & 1) == 0) {
- parser.skip(1); /* skip pad byte */
- }
-
- SampleMarker cuePoint = findOrCreateCuePoint(uniqueID);
- cuePoint.position = position;
- cuePoint.name = markerName;
-
- if (IFFParser.debug) {
- System.out.println("AIFF Marker at " + position + ", " + markerName);
- }
- }
- }
-
- /**
- * Called by parse() method to handle FORM chunks in an AIFF specific manner.
- *
- * @param ckID four byte chunk ID such as 'data'
- * @param ckSize size of chunk in bytes
- * @exception IOException If parsing fails, or IO error occurs.
- */
- @Override
- public void handleForm(IFFParser parser, int ckID, int ckSize, int type) throws IOException {
- if ((ckID == IFFParser.FORM_ID) && (type != AIFF_ID) && (type != AIFC_ID))
- throw new IOException("Bad AIFF form type = " + IFFParser.IDToString(type));
- }
-
- /**
- * Called by parse() method to handle chunks in an AIFF specific manner.
- *
- * @param ckID four byte chunk ID such as 'data'
- * @param ckSize size of chunk in bytes
- * @exception IOException If parsing fails, or IO error occurs.
- */
- @Override
- public void handleChunk(IFFParser parser, int ckID, int ckSize) throws IOException {
- switch (ckID) {
- case COMM_ID:
- parseCOMMChunk(parser, ckSize);
- break;
- case SSND_ID:
- parseSSNDChunk(parser, ckSize);
- break;
- case MARK_ID:
- parseMARKChunk(parser, ckSize);
- break;
- case INST_ID:
- parseINSTChunk(parser, ckSize);
- break;
- default:
- break;
- }
- }
-
-}
diff --git a/src/com/jsyn/util/soundfile/AudioFileParser.java b/src/com/jsyn/util/soundfile/AudioFileParser.java
deleted file mode 100644
index e7bb066..0000000
--- a/src/com/jsyn/util/soundfile/AudioFileParser.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2001 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.soundfile;
-
-import java.io.IOException;
-import java.util.HashMap;
-
-import com.jsyn.data.FloatSample;
-import com.jsyn.data.SampleMarker;
-
-/**
- * Base class for various types of audio specific file parsers.
- *
- * @author (C) 2001 Phil Burk, SoftSynth.com
- */
-
-abstract class AudioFileParser implements ChunkHandler {
- IFFParser parser;
- protected byte[] byteData;
- boolean ifLoadData = true; /* If true, load sound data into memory. */
- long dataPosition; /*
- * Number of bytes from beginning of file where sound data resides.
- */
- protected int bitsPerSample;
- protected int bytesPerFrame; // in the file
- protected int bytesPerSample; // in the file
- protected HashMap<Integer, SampleMarker> cueMap = new HashMap<Integer, SampleMarker>();
- protected short samplesPerFrame;
- protected double frameRate;
- protected int numFrames;
- protected double originalPitch = 60.0;
- protected int sustainBegin = -1;
- protected int sustainEnd = -1;
-
- public AudioFileParser() {
- }
-
- /**
- * @return Number of bytes from beginning of stream where sound data resides.
- */
- public long getDataPosition() {
- return dataPosition;
- }
-
- /**
- * This can be read by another thread when load()ing a sample to determine how many bytes have
- * been read so far.
- */
- public synchronized long getNumBytesRead() {
- IFFParser p = parser; // prevent race
- if (p != null)
- return p.getOffset();
- else
- return 0;
- }
-
- /**
- * This can be read by another thread when load()ing a sample to determine how many bytes need
- * to be read.
- */
- public synchronized long getFileSize() {
- IFFParser p = parser; // prevent race
- if (p != null)
- return p.getFileSize();
- else
- return 0;
- }
-
- protected SampleMarker findOrCreateCuePoint(int uniqueID) {
- SampleMarker cuePoint = cueMap.get(uniqueID);
- if (cuePoint == null) {
- cuePoint = new SampleMarker();
- cueMap.put(uniqueID, cuePoint);
- }
- return cuePoint;
- }
-
- public FloatSample load(IFFParser parser) throws IOException {
- this.parser = parser;
- parser.parseAfterHead(this);
- return finish();
- }
-
- abstract FloatSample finish() throws IOException;
-
- FloatSample makeSample(float[] floatData) throws IOException {
- FloatSample floatSample = new FloatSample(floatData, samplesPerFrame);
-
- floatSample.setChannelsPerFrame(samplesPerFrame);
- floatSample.setFrameRate(frameRate);
- floatSample.setPitch(originalPitch);
-
- if (sustainBegin >= 0) {
- floatSample.setSustainBegin(sustainBegin);
- floatSample.setSustainEnd(sustainEnd);
- }
-
- for (SampleMarker marker : cueMap.values()) {
- floatSample.addMarker(marker);
- }
-
- /* Set Sustain Loop by assuming first two markers are loop points. */
- if (floatSample.getMarkerCount() >= 2) {
- floatSample.setSustainBegin(floatSample.getMarker(0).position);
- floatSample.setSustainEnd(floatSample.getMarker(1).position);
- }
- return floatSample;
- }
-
- protected String parseString(IFFParser parser, int textLength) throws IOException {
- byte[] bar = new byte[textLength];
- parser.read(bar);
- return new String(bar);
- }
-}
diff --git a/src/com/jsyn/util/soundfile/ChunkHandler.java b/src/com/jsyn/util/soundfile/ChunkHandler.java
deleted file mode 100644
index 6dfe26d..0000000
--- a/src/com/jsyn/util/soundfile/ChunkHandler.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 1997 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.soundfile;
-
-import java.io.IOException;
-
-/**
- * Handle IFF Chunks as they are parsed from an IFF or RIFF file.
- *
- * @see IFFParser
- * @see AudioSampleAIFF
- * @author (C) 1997 Phil Burk, SoftSynth.com
- */
-interface ChunkHandler {
- /**
- * The parser will call this when it encounters a FORM or LIST chunk that contains other chunks.
- * This handler can either read the form's chunks, or let the parser find them and call
- * handleChunk().
- *
- * @param ID a 4 byte identifier such as FORM_ID that identifies the IFF chunk type.
- * @param numBytes number of bytes contained in the FORM, not counting the FORM type.
- * @param type a 4 byte identifier such as AIFF_ID that identifies the FORM type.
- */
- public void handleForm(IFFParser parser, int ID, int numBytes, int type) throws IOException;
-
- /**
- * The parser will call this when it encounters a chunk that is not a FORM or LIST. This handler
- * can either read the chunk's, or ignore it. The parser will skip over any unread data. Do NOT
- * read past the end of the chunk!
- *
- * @param ID a 4 byte identifier such as SSND_ID that identifies the IFF chunk type.
- * @param numBytes number of bytes contained in the chunk, not counting the ID and size field.
- */
- public void handleChunk(IFFParser parser, int ID, int numBytes) throws IOException;
-}
diff --git a/src/com/jsyn/util/soundfile/CustomSampleLoader.java b/src/com/jsyn/util/soundfile/CustomSampleLoader.java
deleted file mode 100644
index 14efde9..0000000
--- a/src/com/jsyn/util/soundfile/CustomSampleLoader.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2009 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.soundfile;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-
-import com.jsyn.data.FloatSample;
-import com.jsyn.util.AudioSampleLoader;
-
-public class CustomSampleLoader implements AudioSampleLoader {
-
- @Override
- public FloatSample loadFloatSample(File fileIn) throws IOException {
- FileInputStream fileStream = new FileInputStream(fileIn);
- BufferedInputStream inputStream = new BufferedInputStream(fileStream);
- return loadFloatSample(inputStream);
- }
-
- @Override
- public FloatSample loadFloatSample(URL url) throws IOException {
- InputStream rawStream = url.openStream();
- BufferedInputStream inputStream = new BufferedInputStream(rawStream);
- return loadFloatSample(inputStream);
- }
-
- @Override
- public FloatSample loadFloatSample(InputStream inputStream) throws IOException {
- AudioFileParser fileParser;
- IFFParser parser = new IFFParser(inputStream);
- parser.readHead();
- if (parser.isRIFF()) {
- fileParser = new WAVEFileParser();
- } else if (parser.isIFF()) {
- fileParser = new AIFFFileParser();
- } else {
- throw new IOException("Unsupported audio file type.");
- }
- return fileParser.load(parser);
- }
-
-}
diff --git a/src/com/jsyn/util/soundfile/IFFParser.java b/src/com/jsyn/util/soundfile/IFFParser.java
deleted file mode 100644
index f429657..0000000
--- a/src/com/jsyn/util/soundfile/IFFParser.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright 1997 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.soundfile;
-
-import java.io.EOFException;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Parse Electronic Arts style IFF File. IFF is a file format that allows "chunks" of data to be
- * placed in a hierarchical file. It was designed by Jerry Morrison at Electronic Arts for the Amiga
- * computer and is now used extensively by Apple Computer and other companies. IFF is an open
- * standard.
- *
- * @see RIFFParser
- * @see AudioSampleAIFF
- * @author (C) 1997 Phil Burk, SoftSynth.com
- */
-
-class IFFParser extends FilterInputStream {
- private long numBytesRead = 0;
- private long totalSize = 0;
- private int fileId;
- static boolean debug = false;
-
- public static final int RIFF_ID = ('R' << 24) | ('I' << 16) | ('F' << 8) | 'F';
- public static final int LIST_ID = ('L' << 24) | ('I' << 16) | ('S' << 8) | 'T';
- public static final int FORM_ID = ('F' << 24) | ('O' << 16) | ('R' << 8) | 'M';
-
- IFFParser(InputStream stream) {
- super(stream);
- numBytesRead = 0;
- }
-
- /**
- * Size of file based on outermost chunk size plus 8. Can be used to report progress when
- * loading samples.
- *
- * @return Number of bytes in outer chunk plus header.
- */
- public long getFileSize() {
- return totalSize;
- }
-
- /**
- * Since IFF files use chunks with explicit size, it is important to keep track of how many
- * bytes have been read from the file. Can be used to report progress when loading samples.
- *
- * @return Number of bytes read from stream, or skipped.
- */
- public long getOffset() {
- return numBytesRead;
- }
-
- /** @return Next byte from stream. Increment offset by 1. */
- @Override
- public int read() throws IOException {
- numBytesRead++;
- return super.read();
- }
-
- /** @return Next byte array from stream. Increment offset by len. */
- @Override
- public int read(byte[] bar) throws IOException {
- return read(bar, 0, bar.length);
- }
-
- /** @return Next byte array from stream. Increment offset by len. */
- @Override
- public int read(byte[] bar, int off, int len) throws IOException {
- // Reading from a URL can return before all the bytes are available.
- // So we keep reading until we get the whole thing.
- int cursor = off;
- int numLeft = len;
- // keep reading data until we get it all
- while (numLeft > 0) {
- int numRead = super.read(bar, cursor, numLeft);
- if (numRead < 0)
- return numRead;
- cursor += numRead;
- numBytesRead += numRead;
- numLeft -= numRead;
- // System.out.println("read " + numRead + ", cursor = " + cursor +
- // ", len = " + len);
- }
- return cursor - off;
- }
-
- /** @return Skip forward in stream and add numBytes to offset. */
- @Override
- public long skip(long numBytes) throws IOException {
- numBytesRead += numBytes;
- return super.skip(numBytes);
- }
-
- /** Read 32 bit signed integer assuming Big Endian byte order. */
- public int readIntBig() throws IOException {
- int result = read() & 0xFF;
- result = (result << 8) | (read() & 0xFF);
- result = (result << 8) | (read() & 0xFF);
- int data = read();
- if (data == -1)
- throw new EOFException("readIntBig() - EOF in middle of word at offset " + numBytesRead);
- result = (result << 8) | (data & 0xFF);
- return result;
- }
-
- /** Read 32 bit signed integer assuming Little Endian byte order. */
- public int readIntLittle() throws IOException {
- int result = read() & 0xFF; // LSB
- result |= ((read() & 0xFF) << 8);
- result |= ((read() & 0xFF) << 16);
- int data = read();
- if (data == -1)
- throw new EOFException("readIntLittle() - EOF in middle of word at offset "
- + numBytesRead);
- result |= (data << 24);
- return result;
- }
-
- /** Read 16 bit signed short assuming Big Endian byte order. */
- public short readShortBig() throws IOException {
- short result = (short) ((read() << 8)); // MSB
- int data = read();
- if (data == -1)
- throw new EOFException("readShortBig() - EOF in middle of word at offset "
- + numBytesRead);
- result |= data & 0xFF;
- return result;
- }
-
- /** Read 16 bit signed short assuming Little Endian byte order. */
- public short readShortLittle() throws IOException {
- short result = (short) (read() & 0xFF); // LSB
- int data = read(); // MSB
- if (data == -1)
- throw new EOFException("readShortLittle() - EOF in middle of word at offset "
- + numBytesRead);
- result |= data << 8;
- return result;
- }
-
- public int readUShortLittle() throws IOException {
- return (readShortLittle()) & 0x0000FFFF;
- }
-
- /** Read 8 bit signed byte. */
- public byte readByte() throws IOException {
- return (byte) read();
- }
-
- /** Read 32 bit signed int assuming IFF order. */
- public int readChunkSize() throws IOException {
- if (isRIFF()) {
- return readIntLittle();
- }
- {
- return readIntBig();
- }
- }
-
- /** Convert a 4 character IFF ID to a String */
- public static String IDToString(int ID) {
- byte bar[] = new byte[4];
- bar[0] = (byte) (ID >> 24);
- bar[1] = (byte) (ID >> 16);
- bar[2] = (byte) (ID >> 8);
- bar[3] = (byte) ID;
- return new String(bar);
- }
-
- /**
- * Parse the stream after reading the first ID and pass the forms and chunks to the ChunkHandler
- */
- public void parseAfterHead(ChunkHandler handler) throws IOException {
- int numBytes = readChunkSize();
- totalSize = numBytes + 8;
- parseChunk(handler, fileId, numBytes);
- if (debug)
- System.out.println("parse() ------- end");
- }
-
- /**
- * Parse the FORM and pass the chunks to the ChunkHandler The cursor should be positioned right
- * after the type field.
- */
- void parseForm(ChunkHandler handler, int ID, int numBytes, int type) throws IOException {
- if (debug) {
- System.out.println("IFF: parseForm >>>>>>>>>>>>>>>>>> BEGIN");
- }
- while (numBytes > 8) {
- int ckid = readIntBig();
- int size = readChunkSize();
- numBytes -= 8;
- if (debug) {
- System.out.println("chunk( " + IDToString(ckid) + ", " + size + " )");
- }
- if (size < 0) {
- throw new IOException("Bad IFF chunk Size: " + IDToString(ckid) + " = 0x"
- + Integer.toHexString(ckid) + ", Size = " + size);
- }
- parseChunk(handler, ckid, size);
- if ((size & 1) == 1)
- size++; // even-up
- numBytes -= size;
- if (debug) {
- System.out.println("parseForm: numBytes left in form = " + numBytes);
- }
- }
- if (debug) {
- System.out.println("IFF: parseForm <<<<<<<<<<<<<<<<<<<< END");
- }
-
- if (numBytes > 0) {
- System.out.println("IFF Parser detected " + numBytes
- + " bytes of garbage at end of FORM.");
- skip(numBytes);
- }
- }
-
- /*
- * Parse one chunk from IFF file. After calling handler, make sure stream is positioned at end
- * of chunk.
- */
- void parseChunk(ChunkHandler handler, int ckid, int numBytes) throws IOException {
- long startOffset, endOffset;
- int numRead;
- startOffset = getOffset();
- if (isForm(ckid)) {
- int type = readIntBig();
- if (debug)
- System.out.println("parseChunk: form = " + IDToString(ckid) + ", " + numBytes
- + ", " + IDToString(type));
- handler.handleForm(this, ckid, numBytes - 4, type);
- endOffset = getOffset();
- numRead = (int) (endOffset - startOffset);
- if (numRead < numBytes)
- parseForm(handler, ckid, (numBytes - numRead), type);
- } else {
- handler.handleChunk(this, ckid, numBytes);
- }
- endOffset = getOffset();
- numRead = (int) (endOffset - startOffset);
- if (debug) {
- System.out.println("parseChunk: endOffset = " + endOffset);
- System.out.println("parseChunk: numRead = " + numRead);
- }
- if ((numBytes & 1) == 1)
- numBytes++; // even-up
- if (numRead < numBytes)
- skip(numBytes - numRead);
- }
-
- public void readHead() throws IOException {
- if (debug)
- System.out.println("parse() ------- begin");
- numBytesRead = 0;
- fileId = readIntBig();
- }
-
- public boolean isRIFF() {
- return (fileId == RIFF_ID);
- }
-
- public boolean isIFF() {
- return (fileId == FORM_ID);
- }
-
- /**
- * Does the following chunk ID correspond to a container type like FORM?
- */
- public boolean isForm(int ckid) {
- if (isRIFF()) {
- switch (ckid) {
- case LIST_ID:
- case RIFF_ID:
- return true;
- default:
- return false;
- }
- } else {
- switch (ckid) {
- case LIST_ID:
- case FORM_ID:
- return true;
- default:
- return false;
- }
- }
- }
-
-}
diff --git a/src/com/jsyn/util/soundfile/WAVEFileParser.java b/src/com/jsyn/util/soundfile/WAVEFileParser.java
deleted file mode 100644
index ec9350c..0000000
--- a/src/com/jsyn/util/soundfile/WAVEFileParser.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright 2009 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.soundfile;
-
-import java.io.EOFException;
-import java.io.IOException;
-
-import com.jsyn.data.FloatSample;
-import com.jsyn.data.SampleMarker;
-import com.jsyn.util.SampleLoader;
-
-class WAVEFileParser extends AudioFileParser implements ChunkHandler {
- static final short WAVE_FORMAT_PCM = 1;
- static final short WAVE_FORMAT_IEEE_FLOAT = 3;
- static final short WAVE_FORMAT_EXTENSIBLE = (short) 0xFFFE;
-
- static final byte[] KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {
- 3, 0, 0, 0, 0, 0, 16, 0, -128, 0, 0, -86, 0, 56, -101, 113
- };
- static final byte[] KSDATAFORMAT_SUBTYPE_PCM = {
- 1, 0, 0, 0, 0, 0, 16, 0, -128, 0, 0, -86, 0, 56, -101, 113
- };
-
- static final int WAVE_ID = ('W' << 24) | ('A' << 16) | ('V' << 8) | 'E';
- static final int FMT_ID = ('f' << 24) | ('m' << 16) | ('t' << 8) | ' ';
- static final int DATA_ID = ('d' << 24) | ('a' << 16) | ('t' << 8) | 'a';
- static final int CUE_ID = ('c' << 24) | ('u' << 16) | ('e' << 8) | ' ';
- static final int FACT_ID = ('f' << 24) | ('a' << 16) | ('c' << 8) | 't';
- static final int SMPL_ID = ('s' << 24) | ('m' << 16) | ('p' << 8) | 'l';
- static final int LTXT_ID = ('l' << 24) | ('t' << 16) | ('x' << 8) | 't';
- static final int LABL_ID = ('l' << 24) | ('a' << 16) | ('b' << 8) | 'l';
-
- int samplesPerBlock = 0;
- int blockAlign = 0;
- private int numFactSamples = 0;
- private short format;
-
- WAVEFileParser() {
- }
-
- @Override
- FloatSample finish() throws IOException {
- if ((byteData == null)) {
- throw new IOException("No data found in audio sample.");
- }
- float[] floatData = new float[numFrames * samplesPerFrame];
- if (bitsPerSample == 16) {
- SampleLoader.decodeLittleI16ToF32(byteData, 0, byteData.length, floatData, 0);
- } else if (bitsPerSample == 24) {
- SampleLoader.decodeLittleI24ToF32(byteData, 0, byteData.length, floatData, 0);
- } else if (bitsPerSample == 32) {
- if (format == WAVE_FORMAT_IEEE_FLOAT) {
- SampleLoader.decodeLittleF32ToF32(byteData, 0, byteData.length, floatData, 0);
- } else if (format == WAVE_FORMAT_PCM) {
- SampleLoader.decodeLittleI32ToF32(byteData, 0, byteData.length, floatData, 0);
- } else {
- throw new IOException("WAV: Unsupported format = " + format);
- }
- } else {
- throw new IOException("WAV: Unsupported bitsPerSample = " + bitsPerSample);
- }
-
- return makeSample(floatData);
- }
-
- // typedef struct {
- // long dwIdentifier;
- // long dwPosition;
- // ID fccChunk;
- // long dwChunkStart;
- // long dwBlockStart;
- // long dwSampleOffset;
- // } CuePoint;
-
- /* Parse various chunks encountered in WAV file. */
- void parseCueChunk(IFFParser parser, int ckSize) throws IOException {
- int numCuePoints = parser.readIntLittle();
- if (IFFParser.debug) {
- System.out.println("WAV: numCuePoints = " + numCuePoints);
- }
- if ((ckSize - 4) != (6 * 4 * numCuePoints))
- throw new EOFException("Cue chunk too short!");
- for (int i = 0; i < numCuePoints; i++) {
- int dwName = parser.readIntLittle(); /* dwName */
- int position = parser.readIntLittle(); // dwPosition
- parser.skip(3 * 4); // fccChunk, dwChunkStart, dwBlockStart
- int sampleOffset = parser.readIntLittle(); // dwPosition
-
- if (IFFParser.debug) {
- System.out.println("WAV: parseCueChunk: #" + i + ", dwPosition = " + position
- + ", dwName = " + dwName + ", dwSampleOffset = " + sampleOffset);
- }
- SampleMarker cuePoint = findOrCreateCuePoint(dwName);
- cuePoint.position = position;
- }
- }
-
- void parseLablChunk(IFFParser parser, int ckSize) throws IOException {
- int dwName = parser.readIntLittle();
- int textLength = (ckSize - 4) - 1; // don't read NUL terminator
- String text = parseString(parser, textLength);
- if (IFFParser.debug) {
- System.out.println("WAV: label id = " + dwName + ", text = " + text);
- }
- SampleMarker cuePoint = findOrCreateCuePoint(dwName);
- cuePoint.name = text;
- }
-
- void parseLtxtChunk(IFFParser parser, int ckSize) throws IOException {
- int dwName = parser.readIntLittle();
- int dwSampleLength = parser.readIntLittle();
- parser.skip(4 + (4 * 2)); // purpose through codepage
- int textLength = (ckSize - ((4 * 4) + (4 * 2))) - 1; // don't read NUL
- // terminator
- if (textLength > 0) {
- String text = parseString(parser, textLength);
- if (IFFParser.debug) {
- System.out.println("WAV: ltxt id = " + dwName + ", dwSampleLength = "
- + dwSampleLength + ", text = " + text);
- }
- SampleMarker cuePoint = findOrCreateCuePoint(dwName);
- cuePoint.comment = text;
- }
- }
-
- void parseFmtChunk(IFFParser parser, int ckSize) throws IOException {
- format = parser.readShortLittle();
- samplesPerFrame = parser.readShortLittle();
- frameRate = parser.readIntLittle();
- parser.readIntLittle(); /* skip dwAvgBytesPerSec */
- blockAlign = parser.readShortLittle();
- bitsPerSample = parser.readShortLittle();
-
- if (IFFParser.debug) {
- System.out.println("WAV: format = 0x" + Integer.toHexString(format));
- System.out.println("WAV: bitsPerSample = " + bitsPerSample);
- System.out.println("WAV: samplesPerFrame = " + samplesPerFrame);
- }
- bytesPerFrame = blockAlign;
- bytesPerSample = bytesPerFrame / samplesPerFrame;
- samplesPerBlock = (8 * blockAlign) / bitsPerSample;
-
- if (format == WAVE_FORMAT_EXTENSIBLE) {
- int extraSize = parser.readShortLittle();
- short validBitsPerSample = parser.readShortLittle();
- int channelMask = parser.readIntLittle();
- byte[] guid = new byte[16];
- parser.read(guid);
- if (IFFParser.debug) {
- System.out.println("WAV: extraSize = " + extraSize);
- System.out.println("WAV: validBitsPerSample = " + validBitsPerSample);
- System.out.println("WAV: channelMask = " + channelMask);
- System.out.print("guid = {");
- for (int i = 0; i < guid.length; i++) {
- System.out.print(guid[i] + ", ");
- }
- System.out.println("}");
- }
- if (matchBytes(guid, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
- format = WAVE_FORMAT_IEEE_FLOAT;
- } else if (matchBytes(guid, KSDATAFORMAT_SUBTYPE_PCM)) {
- format = WAVE_FORMAT_PCM;
- }
- }
- if ((format != WAVE_FORMAT_PCM) && (format != WAVE_FORMAT_IEEE_FLOAT)) {
- throw new IOException(
- "Only WAVE_FORMAT_PCM and WAVE_FORMAT_IEEE_FLOAT supported. format = " + format);
- }
- if ((bitsPerSample != 16) && (bitsPerSample != 24) && (bitsPerSample != 32)) {
- throw new IOException(
- "Only 16 and 24 bit PCM or 32-bit float WAV files supported. width = "
- + bitsPerSample);
- }
- }
-
- private boolean matchBytes(byte[] bar1, byte[] bar2) {
- if (bar1.length != bar2.length)
- return false;
- for (int i = 0; i < bar1.length; i++) {
- if (bar1[i] != bar2[i])
- return false;
- }
- return true;
- }
-
- private int convertByteToFrame(int byteOffset) throws IOException {
- if (blockAlign == 0) {
- throw new IOException("WAV file has bytesPerBlock = zero");
- }
- if (samplesPerFrame == 0) {
- throw new IOException("WAV file has samplesPerFrame = zero");
- }
- int nFrames = (samplesPerBlock * byteOffset) / (samplesPerFrame * blockAlign);
- return nFrames;
- }
-
- private int calculateNumFrames(int numBytes) throws IOException {
- int nFrames;
- if (numFactSamples > 0) {
- // nFrames = numFactSamples / samplesPerFrame;
- nFrames = numFactSamples; // FIXME which is right
- } else {
- nFrames = convertByteToFrame(numBytes);
- }
- return nFrames;
- }
-
- // Read fraction in range of 0 to 0xFFFFFFFF and
- // convert to 0.0 to 1.0 range.
- private double readFraction(IFFParser parser) throws IOException {
- // Put L at end or we get -1.
- long maxFraction = 0x0FFFFFFFFL;
- // Get unsigned fraction. Have to fit in long.
- long fraction = (parser.readIntLittle()) & maxFraction;
- return (double) fraction / (double) maxFraction;
- }
-
- void parseSmplChunk(IFFParser parser, int ckSize) throws IOException {
- parser.readIntLittle(); // Manufacturer
- parser.readIntLittle(); // Product
- parser.readIntLittle(); // Sample Period
- int unityNote = parser.readIntLittle();
- double pitchFraction = readFraction(parser);
- originalPitch = unityNote + pitchFraction;
-
- parser.readIntLittle(); // SMPTE Format
- parser.readIntLittle(); // SMPTE Offset
- int numLoops = parser.readIntLittle();
- parser.readIntLittle(); // Sampler Data
-
- int lastCueID = Integer.MAX_VALUE;
- for (int i = 0; i < numLoops; i++) {
- int cueID = parser.readIntLittle();
- parser.readIntLittle(); // type
- int loopStartPosition = parser.readIntLittle();
- // Point to sample one after.
- int loopEndPosition = parser.readIntLittle() + 1;
- // TODO handle fractional loop sizes?
- double endFraction = readFraction(parser);
- parser.readIntLittle(); // playCount
-
- // Use lowest numbered cue.
- if (cueID < lastCueID) {
- sustainBegin = loopStartPosition;
- sustainEnd = loopEndPosition;
- }
- }
- }
-
- void parseFactChunk(IFFParser parser, int ckSize) throws IOException {
- numFactSamples = parser.readIntLittle();
- }
-
- void parseDataChunk(IFFParser parser, int ckSize) throws IOException {
- long numRead;
- dataPosition = parser.getOffset();
- if (ifLoadData) {
- byteData = new byte[ckSize];
- numRead = parser.read(byteData);
- } else {
- numRead = parser.skip(ckSize);
- }
- if (numRead != ckSize) {
- throw new EOFException("WAV data chunk too short! Read " + numRead + " instead of "
- + ckSize);
- }
- numFrames = calculateNumFrames(ckSize);
- }
-
- @Override
- public void handleForm(IFFParser parser, int ckID, int ckSize, int type) throws IOException {
- if ((ckID == IFFParser.RIFF_ID) && (type != WAVE_ID))
- throw new IOException("Bad WAV form type = " + IFFParser.IDToString(type));
- }
-
- /**
- * Called by parse() method to handle chunks in a WAV specific manner.
- *
- * @param ckID four byte chunk ID such as 'data'
- * @param ckSize size of chunk in bytes
- * @return number of bytes left in chunk
- */
- @Override
- public void handleChunk(IFFParser parser, int ckID, int ckSize) throws IOException {
- switch (ckID) {
- case FMT_ID:
- parseFmtChunk(parser, ckSize);
- break;
- case DATA_ID:
- parseDataChunk(parser, ckSize);
- break;
- case CUE_ID:
- parseCueChunk(parser, ckSize);
- break;
- case FACT_ID:
- parseFactChunk(parser, ckSize);
- break;
- case SMPL_ID:
- parseSmplChunk(parser, ckSize);
- break;
- case LABL_ID:
- parseLablChunk(parser, ckSize);
- break;
- case LTXT_ID:
- parseLtxtChunk(parser, ckSize);
- break;
- default:
- break;
- }
- }
-
- /*
- * (non-Javadoc)
- * @see com.softsynth.javasonics.util.AudioSampleLoader#isLittleEndian()
- */
- boolean isLittleEndian() {
- return true;
- }
-
-}