Skip to content

Commit

Permalink
fixed bytes to float conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
dennisppaul committed Jun 19, 2024
1 parent 70c70d3 commit 7abfde3
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 99 deletions.
Binary file modified lib/wellen.jar
Binary file not shown.
20 changes: 17 additions & 3 deletions src/wellen/Tone.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static <T extends Instrument> T create_instrument(Class<T> instrument_cla
// c = instrument_class.getDeclaredConstructor(Minim.class, int.class);
// mInstrument = c.newInstance((Minim) ((ToneEngineMinim) instance()).minim(), ID);
// } else {
c = instrument_class.getDeclaredConstructor(int.class);
c = instrument_class.getDeclaredConstructor(int.class);
mInstrument = c.newInstance(ID);
// }
} catch (Exception ex) {
Expand Down Expand Up @@ -85,6 +85,20 @@ public static float[] get_buffer() {
return instance().get_buffer();
}

/**
* @return reference to left output buffer
*/
public static float[] get_output_buffer_left() {
return get_buffer_left();
}

/**
* @return reference to right output buffer
*/
public static float[] get_output_buffer_right() {
return get_buffer_right();
}

public static float[] get_buffer_left() {
return instance().get_buffer_left();
}
Expand Down Expand Up @@ -251,7 +265,7 @@ public static void stop() {

private static void printAlreadyStartedWarning() {
System.err.println("+++ WARNING @" + Tone.class.getSimpleName() + ".start" + " / tone engine already " +
"initialized. make sure that `start` is the first call to `Ton`. " + "use " +
"`set_engine(ToneEngine)` to switch tone engines.");
"initialized. make sure that `start` is the first call to `Ton`. " + "use " +
"`set_engine(ToneEngine)` to switch tone engines.");
}
}
99 changes: 58 additions & 41 deletions src/wellen/WAVConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,37 @@ public class WAVConverter {
// @TODO(write header could also support `WAVE_FORMAT_PCM_32BIT_FLOAT`)
// @TODO(currently fixed to little endianness)

public static boolean VERBOSE = false;
private static final String WAV_CHUNK_DATA = "data";
private static final String WAV_CHUNK_FMT_ = "fmt ";
private static final String WAV_CHUNK_RIFF = "RIFF";
private static final String WAV_CHUNK_WAVE = "WAVE";
private final int mBitsPerSample;
private final int mChannels;
private final int mCompressionFormat;
private final ArrayList<Byte> mData;
private final ArrayList<Byte> mHeader;
private final int mSampleRate;
public static boolean VERBOSE = false;
private static final String WAV_CHUNK_DATA = "data";
private static final String WAV_CHUNK_FMT_ = "fmt ";
private static final String WAV_CHUNK_RIFF = "RIFF";
private static final String WAV_CHUNK_WAVE = "WAVE";
private final int mBitsPerSample;
private final int mChannels;
private final int mCompressionFormat;
private final ArrayList<Byte> mData;
private final ArrayList<Byte> mHeader;
private final int mSampleRate;

public WAVConverter(Info pInfo) {
this(pInfo.channels, pInfo.bits_per_sample, pInfo.sample_rate, pInfo.format);
}

public WAVConverter(int pChannels, int pBitsPerSample, int pSampleRate, int pCompressionFormat) {
mChannels = pChannels;
mBitsPerSample = pBitsPerSample;
mSampleRate = pSampleRate;
mChannels = pChannels;
mBitsPerSample = pBitsPerSample;
mSampleRate = pSampleRate;
mCompressionFormat = pCompressionFormat;
mData = new ArrayList<>();
mHeader = new ArrayList<>();
mData = new ArrayList<>();
mHeader = new ArrayList<>();
}

public static Info convert_bytes_to_samples(byte[] pHeader) {
final Info mWAVStruct = new Info();
// see http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
// see https://sites.google.com/site/musicgapi/technical-documents/wav-file-format
/* RIFF Chunk */
int mOffset = 0x00;
int mOffset = 0x00;
final String mRIFFChunkName = WAVConverter.read_string(pHeader, mOffset + 0x00);
if (!mRIFFChunkName.equalsIgnoreCase(WAV_CHUNK_RIFF)) {
System.err.println("+++ WARNING @" + WAVConverter.class.getSimpleName() + " / expected `" + WAV_CHUNK_RIFF + "`" + " in header.");
Expand All @@ -65,14 +65,14 @@ public static Info convert_bytes_to_samples(byte[] pHeader) {
System.err.println("+++ WARNING @" + WAVConverter.class.getSimpleName() + " / expected `" + WAV_CHUNK_FMT_ + "` " + "in header.");
}
final int mFormatChunkSize = WAVConverter.read__int32(pHeader, mOffset + 0x04);
mWAVStruct.format = WAVConverter.read__int16(pHeader, mOffset + 0x08);
mWAVStruct.channels = WAVConverter.read__int16(pHeader, mOffset + 0x0A);
mWAVStruct.sample_rate = WAVConverter.read__int32(pHeader, mOffset + 0x0C);
mWAVStruct.format = WAVConverter.read__int16(pHeader, mOffset + 0x08);
mWAVStruct.channels = WAVConverter.read__int16(pHeader, mOffset + 0x0A);
mWAVStruct.sample_rate = WAVConverter.read__int32(pHeader, mOffset + 0x0C);
mWAVStruct.bits_per_sample = WAVConverter.read__int16(pHeader, mOffset + 0x16);
if (VERBOSE) {
System.out.println("+++ CHUNK: " + mFormatChunkName);
System.out.println(" chunk size : " + mFormatChunkSize);
System.out.println(" format code: " + mWAVStruct.format);
System.out.println(" format code: " + mWAVStruct.format + " (" + getFormatString(mWAVStruct.format) + ")");
System.out.println(" channels : " + mWAVStruct.channels);
System.out.println(" sample rate: " + mWAVStruct.sample_rate);
System.out.println(" byte/sec : " + WAVConverter.read__int32(pHeader, mOffset + 0x10));
Expand All @@ -81,15 +81,15 @@ public static Info convert_bytes_to_samples(byte[] pHeader) {
}
if (mWAVStruct.format != Wellen.WAV_FORMAT_PCM && mWAVStruct.format != Wellen.WAV_FORMAT_IEEE_FLOAT_32BIT) {
System.err.println("+++ WARNING @" + WAVConverter.class.getSimpleName() + " / format not " + "supported. "
+ "currently only `WAV_FORMAT_PCM` + `WAV_FORMAT_IEEE_FLOAT_32BIT` " + "works" + "." + " " + "(" + mWAVStruct.format + ")");
+ "currently only `WAV_FORMAT_PCM` + `WAV_FORMAT_IEEE_FLOAT_32BIT` " + "works" + "." + " " + "(" + mWAVStruct.format + ")");
}

/* data chunk */
mOffset = 0x0C + 0x18;
if (WAVConverter.read_string(pHeader, mOffset + 0x00).equalsIgnoreCase("fact")) {
// @TODO(hack! skipping `fact` chunk … handle this a bit more elegantly)
final int mFactChunkSize = WAVConverter.read__int32(pHeader, mOffset + 0x04);
final int mPeakOffset = WAVConverter.read__int32(pHeader, mOffset + 0x10);
final int mPeakOffset = WAVConverter.read__int32(pHeader, mOffset + 0x10);
if (VERBOSE) {
System.out.println("+++ skipping `fact` chunk");
System.out.println("+++ CHUNK: " + WAVConverter.read_string(pHeader, mOffset + 0x00));
Expand All @@ -105,9 +105,9 @@ public static Info convert_bytes_to_samples(byte[] pHeader) {
if (!mDataChunkName.equalsIgnoreCase(WAV_CHUNK_DATA)) {
System.err.println("+++ WARNING @" + WAVConverter.class.getSimpleName() + " / expected `" + WAV_CHUNK_DATA + "`" + " in header.");
}
final int mDataChunkSize = WAVConverter.read__int32(pHeader, mOffset + 0x04);
byte[] mInterlacedByteBuffer = WAVConverter.read__bytes(pHeader, mOffset + 0x08, mDataChunkSize);
int mDataSize = mInterlacedByteBuffer.length / mWAVStruct.channels / (mWAVStruct.bits_per_sample / 8);
final int mDataChunkSize = WAVConverter.read__int32(pHeader, mOffset + 0x04);
byte[] mInterlacedByteBuffer = WAVConverter.read__bytes(pHeader, mOffset + 0x08, mDataChunkSize);
int mDataSize = mInterlacedByteBuffer.length / mWAVStruct.channels / (mWAVStruct.bits_per_sample / 8);
if (VERBOSE) {
System.out.println("+++ CHUNK: " + mDataChunkName);
System.out.println(" chunk size : " + mDataChunkSize);
Expand All @@ -116,10 +116,10 @@ public static Info convert_bytes_to_samples(byte[] pHeader) {

mWAVStruct.samples = new float[mWAVStruct.channels][mDataSize];
final int mBytesPerSample = mWAVStruct.bits_per_sample / 8;
final int mStride = mWAVStruct.channels * mBytesPerSample;
final int mStride = mWAVStruct.channels * mBytesPerSample;
for (int j = 0; j < mWAVStruct.channels; j++) {
byte[] mByteSamples = new byte[mBytesPerSample * mDataSize];
int c = 0;
int c = 0;
for (int i = 0; i < mInterlacedByteBuffer.length; i += mStride) {
for (int l = 0; l < mBytesPerSample; l++) {
byte b = mInterlacedByteBuffer[i + j * mBytesPerSample + l];
Expand All @@ -137,6 +137,21 @@ public static Info convert_bytes_to_samples(byte[] pHeader) {
return mWAVStruct;
}

private static String getFormatString(int pFormat) {
String mFormatString;
switch (pFormat) {
case Wellen.WAV_FORMAT_PCM:
mFormatString = "PCM_16BIT";
break;
case Wellen.WAV_FORMAT_IEEE_FLOAT_32BIT:
mFormatString = "IEEE_FLOAT_32BIT";
break;
default:
mFormatString = "UNKNOWN/UNSUPPORTED";
}
return mFormatString;
}

public static byte[] convert_samples_to_bytes(Info pInfo) {
WAVConverter mWAVConverter = new WAVConverter(pInfo);
mWAVConverter.appendData(pInfo.samples);
Expand All @@ -150,11 +165,11 @@ public static byte[] convert_samples_to_bytes(float[][] pBuffer,
int pSampleRate,
int pCompressionCode) {
Info mInfo = new Info();
mInfo.samples = pBuffer;
mInfo.channels = pChannels;
mInfo.samples = pBuffer;
mInfo.channels = pChannels;
mInfo.bits_per_sample = pBitsPerSample;
mInfo.sample_rate = pSampleRate;
mInfo.format = pCompressionCode;
mInfo.sample_rate = pSampleRate;
mInfo.format = pCompressionCode;
return convert_samples_to_bytes(mInfo);
}

Expand Down Expand Up @@ -203,8 +218,8 @@ private static int read__int32(byte[] pBuffer, int pStart) {
}

private static String read_string(byte[] pBuffer, int pStart) {
final int mStringLength = 4;
StringBuilder sb = new StringBuilder();
final int mStringLength = 4;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mStringLength; i++) {
sb.append((char) pBuffer[pStart + i]);
}
Expand Down Expand Up @@ -255,7 +270,7 @@ private static void write_string(ArrayList<Byte> pBuffer, String s) {
}

public void appendData(float[][] pFloatBuffer) {
int mNumberOfFrames = findSingleBufferLength(pFloatBuffer);
int mNumberOfFrames = findSingleBufferLength(pFloatBuffer);
float[] mInterleavedFloatBuffer = new float[mNumberOfFrames * mChannels];
for (int i = 0; i < mNumberOfFrames; i++) {
for (int mChannel = 0; mChannel < mChannels; mChannel++) {
Expand All @@ -272,7 +287,9 @@ public void appendData(float[][] pFloatBuffer) {
System.err.println("+++ ERROR @" + WAVConverter.class.getSimpleName() + " / data format not supported.");
mByteBuffer = null;
}
write__bytes(mData, mByteBuffer);
if (mByteBuffer != null) {
write__bytes(mData, mByteBuffer);
}
}

public void writeHeader() {
Expand Down Expand Up @@ -307,11 +324,11 @@ public byte[] getByteData() {
}

public static class Info {
public int bits_per_sample;
public int channels;
public byte[] data;
public int format;
public int sample_rate;
public int bits_per_sample;
public int channels;
public byte[] data;
public int format;
public int sample_rate;
public float[][] samples;
}
}
28 changes: 21 additions & 7 deletions src/wellen/Wellen.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
Expand Down Expand Up @@ -257,16 +258,28 @@ public static float[] bytes_to_floatIEEEs(byte[] pBytes) {
return mSignal;
}

/**
* convert byte array to float array.
*
* @param pBytes source unsigned byte array
* @param pFloats destination float array
* @param pBitsPerFloat number of bits per float ( usually 8, 16, 24, or 32-bits )
*/
public static void bytes_to_floats(byte[] pBytes, float[] pFloats, int pBitsPerFloat) {
final int mBytesPerFloat = pBitsPerFloat / 8;
final int mBytesPerFloat = pBitsPerFloat / 8;
final double mScale = 1.0 / ((1 << (pBitsPerFloat - 1)) - 1);
for (int i = 0; i < pFloats.length; i++) {
final double mScale = 1.0 / ((1 << (pBitsPerFloat - 1)) - 1);
long f = 0;
long f = 0;

for (int j = 0; j < mBytesPerFloat; j++) {
final long mBitShift = j * 8;
long b = pBytes[i * mBytesPerFloat + j];
f += b << mBitShift;
int b = pBytes[i * mBytesPerFloat + j] & 0xFF;
f |= (long) b << (j * 8);
}

if (f >= (1L << (pBitsPerFloat - 1))) {
f -= 1L << pBitsPerFloat;
}

pFloats[i] = (float) (f * mScale);
}
}
Expand Down Expand Up @@ -606,7 +619,8 @@ public static float[] get_extremum(float[] pSignal) {
}

public static String get_resource_path() {
return Wellen.class.getResource("").getPath();
URL mURLPath = Wellen.class.getResource("");
return mURLPath == null ? "" : mURLPath.getPath();
}

public static float[][] importWAV(PApplet p, String pFilepath) {
Expand Down
Loading

0 comments on commit 7abfde3

Please sign in to comment.