Reputation: 676
I have recorded a sound using AudioRecord class . But its not playable via music player. So I have added some header information to the audio file. Though its now playable via music player but its lagging and noisy , its not playing exact sound . Any help would be appreciated . My code is below
private void rawToWave(final File rawFile, final File waveFile) throws IOException {
byte[] rawData = new byte[(int) rawFile.length()];
DataInputStream input = null;
try {
input = new DataInputStream(new FileInputStream(rawFile));
input.read(rawData);
} finally {
if (input != null) {
input.close();
}
}
DataOutputStream output = null;
try {
int myBitsPerSample= 2;
int myFormat = 1;
long myChannels = 1;
long mySampleRate = 8000 ;
long myByteRate = mySampleRate * myChannels * myBitsPerSample/8;
int myBlockAlign = (int) (myChannels * myBitsPerSample/8);
output = new DataOutputStream(new FileOutputStream(waveFile));
// WAVE header
// see http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
writeString(output, "RIFF"); // chunk id
writeInt(output, 36 + rawData.length); // chunk size
writeString(output, "WAVE"); // format
writeString(output, "fmt "); // subchunk 1 id
writeInt(output, 16); // subchunk 1 size
writeShort(output, (short) 1); // audio format (1 = PCM)
writeShort(output, (short) 1); // number of channels
writeInt(output, (int)mySampleRate ); // sample ratemySampleRate
writeInt(output, (int) (myByteRate)); // byte rate
writeShort(output, (short) myBlockAlign); // block align
writeShort(output, (short) 16); // bits per sample
writeString(output, "data"); // subchunk 2 id
writeInt(output, rawData.length); // subchunk 2 size
// Audio data (conversion big endian -> little endian)
short[] shorts = new short[rawData.length /2];
ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);
ByteBuffer bytes = ByteBuffer.allocate(shorts.length * 2);
for (short s : shorts) {
bytes.putShort(s);
}
output.write(bytes.array());
} finally {
if (output != null) {
output.close();
}
}
}
public void writeShortLE(DataOutputStream out, short value) throws IOException {
out.writeByte(value & 0xFF);
out.writeByte((value >> 8) & 0xFF);
}
private void writeInt(final DataOutputStream output, final int value) throws IOException {
output.write(value >> 0);
output.write(value >> 8);
output.write(value >> 16);
output.write(value >> 24);
}
private void writeShort(final DataOutputStream output, final short value) throws IOException {
output.write(value >> 0);
output.write(value >> 8);
}
private void writeString(final DataOutputStream output, final String value) throws IOException {
for (int i = 0; i < value.length(); i++) {
output.write(value.charAt(i));
}
}
Upvotes: 0
Views: 3387
Reputation: 559
This is a modification to Tobiq's answer in Kotlin allowing you to upsample from 8000Hz to 48000Hz.
Usage
PCMToWAV(File(audioFile), File(upsampledAudioFile), 1, 8000, 48000, 16)
PCMToWAV
/**
* @param input raw PCM data
* limit of file size for wave file: < 2^(2*4) - 36 bytes (~4GB)
* @param output file to encode to in wav format
* @param channelCount number of channels: 1 for mono, 2 for stereo, etc.
* @param sampleRate sample rate of PCM audio
* @param bitsPerSample bits per sample, i.e. 16 for PCM16
* @throws IOException in event of an error between input/output files
* @see [soundfile.sapp.org/doc/WaveFormat](http://soundfile.sapp.org/doc/WaveFormat/)
*/
@Throws(IOException::class)
fun PCMToWAV(input: File, output: File?, channelCount: Int, originSampleRate: Int, sampleRate: Int, bitsPerSample: Int) {
val inputSize = input.length().toInt()
val sampleFactor : Float = sampleRate.toFloat() / originSampleRate.toFloat()
FileOutputStream(output).use { encoded ->
// WAVE RIFF header
writeToOutput(encoded, "RIFF") // chunk id
writeToOutput(encoded, 36 + (inputSize.toFloat() * sampleFactor).toInt()) // chunk size
writeToOutput(encoded, "WAVE") // format
// SUB CHUNK 1 (FORMAT)
writeToOutput(encoded, "fmt ") // subchunk 1 id
writeToOutput(encoded, 16) // subchunk 1 size
writeToOutput(encoded, 1.toShort()) // audio format (1 = PCM)
writeToOutput(encoded, channelCount.toShort()) // number of channelCount
writeToOutput(encoded, sampleRate) // sample rate
writeToOutput(encoded, sampleRate * channelCount * bitsPerSample / 8) // byte rate
writeToOutput(encoded, (channelCount * bitsPerSample / 8).toShort()) // block align
writeToOutput(encoded, bitsPerSample.toShort()) // bits per sample
// SUB CHUNK 2 (AUDIO DATA)
if (originSampleRate == sampleRate) {
writeToOutput(encoded, "data") // subchunk 2 id
writeToOutput(encoded, inputSize) // subchunk 2 size
copy(FileInputStream(input), encoded)
} else {
// SUB CHUNK 2 (AUDIO DATA) - UPSAMPLE
writeToOutput(encoded, "data") // subchunk 2 id
writeToOutput(encoded, (inputSize.toFloat() * sampleFactor).toInt()) // subchunk 2 size
val inputStream = FileInputStream(input)
val buffer = ByteArray(2)
var len: Int = inputStream.read(buffer)
var overFlow = 0
while (len != -1) {
for (i in 0 until (sampleFactor * 1000F).toInt()) {
overFlow += 1
if (overFlow >= 1000) {
encoded.write(buffer, 0, len)
overFlow = 0
}
}
len = inputStream.read(buffer)
}
inputStream.close()
}
}
}
/**
* Size of buffer used for transfer, by default
*/
private const val TRANSFER_BUFFER_SIZE = 10 * 1024
/**
* Writes string in big endian form to an output stream
*
* @param output stream
* @param data string
* @throws IOException
*/
@Throws(IOException::class)
fun writeToOutput(output: OutputStream, data: String) {
for (i in 0 until data.length) {
output.write(data[i].toInt())
}
}
@Throws(IOException::class)
fun writeToOutput(output: OutputStream, data: Int) {
output.write(data shr 0)
output.write(data shr 8)
output.write(data shr 16)
output.write(data shr 24)
}
@Throws(IOException::class)
fun writeToOutput(output: OutputStream, data: Short) {
output.write(data.toInt() shr 0)
output.write(data.toInt() shr 8)
}
@Throws(IOException::class)
fun copy(source: InputStream, output: OutputStream): Long {
return copy(source, output, TRANSFER_BUFFER_SIZE)
}
@Throws(IOException::class)
fun copy(source: InputStream, output: OutputStream, bufferSize: Int): Long {
var read = 0L
val buffer = ByteArray(bufferSize)
var n: Int
while (source.read(buffer).also { n = it } != -1) {
output.write(buffer, 0, n)
read += n.toLong()
}
return read
}
Upvotes: 1
Reputation: 2657
Here's a complete working example:
/**
* @param input raw PCM data
* limit of file size for wave file: < 2^(2*4) - 36 bytes (~4GB)
* @param output file to encode to in wav format
* @param channelCount number of channels: 1 for mono, 2 for stereo, etc.
* @param sampleRate sample rate of PCM audio
* @param bitsPerSample bits per sample, i.e. 16 for PCM16
* @throws IOException in event of an error between input/output files
* @see <a href="http://soundfile.sapp.org/doc/WaveFormat/">soundfile.sapp.org/doc/WaveFormat</a>
*/
static public void PCMToWAV(File input, File output, int channelCount, int sampleRate, int bitsPerSample) throws IOException {
final int inputSize = (int) input.length();
try (OutputStream encoded = new FileOutputStream(output)) {
// WAVE RIFF header
writeToOutput(encoded, "RIFF"); // chunk id
writeToOutput(encoded, 36 + inputSize); // chunk size
writeToOutput(encoded, "WAVE"); // format
// SUB CHUNK 1 (FORMAT)
writeToOutput(encoded, "fmt "); // subchunk 1 id
writeToOutput(encoded, 16); // subchunk 1 size
writeToOutput(encoded, (short) 1); // audio format (1 = PCM)
writeToOutput(encoded, (short) channelCount); // number of channelCount
writeToOutput(encoded, sampleRate); // sample rate
writeToOutput(encoded, sampleRate * channelCount * bitsPerSample / 8); // byte rate
writeToOutput(encoded, (short) (channelCount * bitsPerSample / 8)); // block align
writeToOutput(encoded, (short) bitsPerSample); // bits per sample
// SUB CHUNK 2 (AUDIO DATA)
writeToOutput(encoded, "data"); // subchunk 2 id
writeToOutput(encoded, inputSize); // subchunk 2 size
copy(new FileInputStream(input), encoded);
}
}
/**
* Size of buffer used for transfer, by default
*/
private static final int TRANSFER_BUFFER_SIZE = 10 * 1024;
/**
* Writes string in big endian form to an output stream
*
* @param output stream
* @param data string
* @throws IOException
*/
public static void writeToOutput(OutputStream output, String data) throws IOException {
for (int i = 0; i < data.length(); i++)
output.write(data.charAt(i));
}
public static void writeToOutput(OutputStream output, int data) throws IOException {
output.write(data >> 0);
output.write(data >> 8);
output.write(data >> 16);
output.write(data >> 24);
}
public static void writeToOutput(OutputStream output, short data) throws IOException {
output.write(data >> 0);
output.write(data >> 8);
}
public static long copy(InputStream source, OutputStream output)
throws IOException {
return copy(source, output, TRANSFER_BUFFER_SIZE);
}
public static long copy(InputStream source, OutputStream output, int bufferSize) throws IOException {
long read = 0L;
byte[] buffer = new byte[bufferSize];
for (int n; (n = source.read(buffer)) != -1; read += n) {
output.write(buffer, 0, n);
}
return read;
}
Upvotes: 6