I have the following java class that generates WAV audio file from PCM data. I want to implement the same functionality in JavaScript or NodeJS equivalent. Is there any existing library you can recommend?
There's an existing NPM library out there called random-access-file but it is limited.
SampleAudioExport.java
package com.example.sampleapp;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.concurrent.LinkedBlockingQueue;
public class SampleAudioExport implements Runnable {
public final LinkedBlockingQueue<short[]> samples = new
LinkedBlockingQueue<>();
public String audioDir;
private Thread thread;
public boolean isRunning = false;
public boolean isDone = false;
public File wav;
private final RandomAccessFile randomAccessWriter;
private int payloadSize;
public SampleAudioExport(String dir) throws IOException {
payloadSize = 0;
audioDir = dir;
String fileName = "recording.wav";
wav = new File(audioDir + "/" + fileName);
int sRate = 44100;
short nChannels = 2;
int mBitsPerSample = 16;
randomAccessWriter = new RandomAccessFile(wav.getAbsoluteFile(), "rw");
randomAccessWriter.setLength(0); // Set file length to 0, to prevent unexpected behavior in case the file already existed
randomAccessWriter.writeBytes("RIFF");
randomAccessWriter.writeInt(0); // Final file size not known yet, write 0
randomAccessWriter.writeBytes("WAVE");
randomAccessWriter.writeBytes("fmt ");
randomAccessWriter.writeInt(Integer.reverseBytes(mBitsPerSample)); // Sub-chunk size, 16 for PCM
randomAccessWriter.writeShort(Short.reverseBytes((short) 1)); // AudioFormat, 1 for PCM
randomAccessWriter.writeShort(Short.reverseBytes((short) 2));// Number of channels, 1 for mono, 2 for stereo
randomAccessWriter.writeInt(Integer.reverseBytes(sRate)); // Sample rate
randomAccessWriter.writeInt(Integer.reverseBytes(sRate*nChannels*mBitsPerSample/8)); // Byte rate, SampleRate*NumberOfChannels*mBitsPersample/8
randomAccessWriter.writeShort(Short.reverseBytes((short)(nChannels*mBitsPerSample/8))); // Block align, NumberOfChannels*mBitsPersample/8
randomAccessWriter.writeShort(Short.reverseBytes((short) mBitsPerSample)); // Bits per sample
randomAccessWriter.writeBytes("data");
randomAccessWriter.writeInt(0); // Data chunk size not known yet, write 0
}
public void start() {
this.isRunning = true;
this.thread = new Thread(this);
this.thread.start();
}
public void stop() {
this.isRunning = false;
}
public void pushAudio(short[] sample) {
this.samples.add(sample);
}
@Override
public void run() {
try {
Log.d("AudioExport", "RUNNING AUDIO EXPORT");
Log.d("AudioExport", "Samples=" + this.samples.size());
while (Thread.currentThread() == this.thread && this.samples.size() > 0) {
short[] sample = this.samples.take();
byte[] buffer = this.toByteArrayForAudioData(sample);
randomAccessWriter.write(buffer);
payloadSize += buffer.length;
}
randomAccessWriter.seek(4); // Write size to RIFF header
randomAccessWriter.writeInt(Integer.reverseBytes(36 + payloadSize));
randomAccessWriter.seek(40); // Write size to Subchunk2Size field
randomAccessWriter.writeInt(Integer.reverseBytes(payloadSize));
randomAccessWriter.close();
isRunning = false;
isDone = true;
Log.d("Audio", "AUDIO EXPORT DONE");
} catch (Exception e) {
e.printStackTrace();
}
}
public byte[] toByteArrayForAudioData(short[] shorts) {
byte[] output = new byte[shorts.length * 2];
for (int i = 0; i < shorts.length; i++) {
output[i * 2] = (byte) (shorts[i] & 0xff);
output[(i * 2) + 1] = (byte) ((shorts[i] >> 8) & 0xff);
}
return output;
}
}