fix wav format
This commit is contained in:
parent
b27a1b2f30
commit
c65b1277a0
4 changed files with 97 additions and 151 deletions
|
|
@ -347,14 +347,26 @@ public class RecordingActivity extends AppCompatActivity {
|
|||
AudioRecord recorder = null;
|
||||
try {
|
||||
File tmp = storage.getTempRecording();
|
||||
final long s = getSamples(tmp);
|
||||
handle.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
samples = 0;
|
||||
addSamples(s);
|
||||
|
||||
{
|
||||
long ss = tmp.length();
|
||||
if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
|
||||
ss = ss / 2;
|
||||
}
|
||||
});
|
||||
if (channelConfig == AudioFormat.CHANNEL_IN_STEREO) {
|
||||
ss = ss / 2;
|
||||
}
|
||||
|
||||
final long s = ss;
|
||||
handle.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
samples = 0;
|
||||
addSamples(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
os = new DataOutputStream(storage.open(tmp));
|
||||
|
||||
int min = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||
|
|
@ -524,18 +536,6 @@ public class RecordingActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
long getSamples(File in) {
|
||||
long samples = in.length();
|
||||
if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
|
||||
samples = samples / 2;
|
||||
}
|
||||
if (channelConfig == AudioFormat.CHANNEL_IN_STEREO) {
|
||||
samples = samples / 2;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
EncoderInfo getInfo() {
|
||||
final int channels = channelConfig == AudioFormat.CHANNEL_IN_STEREO ? 2 : 1;
|
||||
final int bps = audioFormat == AudioFormat.ENCODING_PCM_16BIT ? 16 : 8;
|
||||
|
|
|
|||
|
|
@ -3,5 +3,7 @@ package com.github.axet.audiorecorder.encoders;
|
|||
public interface Encoder {
|
||||
public EncoderInfo getInfo();
|
||||
|
||||
public void encode(byte[] buf, int offset, int len);
|
||||
public void encode(short[] buf);
|
||||
|
||||
public void close();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,9 @@ import android.widget.Toast;
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* Created by axet on 11/03/16.
|
||||
*/
|
||||
public class FileEncoder {
|
||||
Context context;
|
||||
Handler handler;
|
||||
|
|
@ -40,8 +39,7 @@ public class FileEncoder {
|
|||
thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
samples = in.length();
|
||||
samples = getSamples(samples);
|
||||
samples = getSamples(in.length());
|
||||
|
||||
cur = 0;
|
||||
|
||||
|
|
@ -50,7 +48,7 @@ public class FileEncoder {
|
|||
is = new FileInputStream(in);
|
||||
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
byte[] buf = new byte[info.channels * (info.bps / 8) * 100];
|
||||
byte[] buf = new byte[info.channels * info.bps / 8 * 100];
|
||||
|
||||
int len = is.read(buf);
|
||||
if (len <= 0) {
|
||||
|
|
@ -58,7 +56,9 @@ public class FileEncoder {
|
|||
return;
|
||||
}
|
||||
if (len > 0) {
|
||||
encoder.encode(buf, 0, len);
|
||||
short[] shorts = new short[len / 2];
|
||||
ByteBuffer.wrap(buf, 0, len).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get(shorts);
|
||||
encoder.encode(shorts);
|
||||
handler.post(progress);
|
||||
synchronized (thread) {
|
||||
cur += getSamples(len);
|
||||
|
|
@ -69,6 +69,7 @@ public class FileEncoder {
|
|||
t = e;
|
||||
handler.post(error);
|
||||
} finally {
|
||||
encoder.close();
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
|
|
|
|||
|
|
@ -1,101 +1,21 @@
|
|||
package com.github.axet.audiorecorder.encoders;
|
||||
|
||||
// Based on "wav IO based on code by Evan Merz"
|
||||
// based on http://soundfile.sapp.org/doc/WaveFormat/
|
||||
|
||||
// Wav file format parsed by Evan X. Merz
|
||||
// www.thisisnotalabel.com
|
||||
|
||||
// Example Wav file input and output
|
||||
// this was written for educational purposes, but feel free to use it for anything you like
|
||||
// as long as you credit me appropriately ("wav IO based on code by Evan Merz")
|
||||
|
||||
// if you catch any bugs in this, or improve upon it significantly, send me the changes
|
||||
// at evan at thisisnotalabel dot com, so we can share your changes with the world
|
||||
|
||||
// http://computermusicblog.com/blog/2008/08/29/reading-and-writing-wav-files-in-java/
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class Wav implements Encoder {
|
||||
/*
|
||||
WAV File Specification
|
||||
FROM http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
|
||||
The canonical WAVE format starts with the RIFF header:
|
||||
0 4 ChunkID Contains the letters "RIFF" in ASCII form
|
||||
(0x52494646 big-endian form).
|
||||
4 4 ChunkSize 36 + SubChunk2Size, or more precisely:
|
||||
4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
|
||||
This is the size of the rest of the chunk
|
||||
following this number. This is the size of the
|
||||
entire file in bytes minus 8 bytes for the
|
||||
two fields not included in this count:
|
||||
ChunkID and ChunkSize.
|
||||
8 4 Format Contains the letters "WAVE"
|
||||
(0x57415645 big-endian form).
|
||||
|
||||
The "WAVE" format consists of two subchunks: "fmt " and "data":
|
||||
The "fmt " subchunk describes the sound data's format:
|
||||
12 4 Subchunk1ID Contains the letters "fmt "
|
||||
(0x666d7420 big-endian form).
|
||||
16 4 Subchunk1Size 16 for PCM. This is the size of the
|
||||
rest of the Subchunk which follows this number.
|
||||
20 2 AudioFormat PCM = 1 (i.e. Linear quantization)
|
||||
Values other than 1 indicate some
|
||||
form of compression.
|
||||
22 2 NumChannels Mono = 1, Stereo = 2, etc.
|
||||
24 4 SampleRate 8000, 44100, etc.
|
||||
28 4 ByteRate == SampleRate * NumChannels * BitsPerSample/8
|
||||
32 2 BlockAlign == NumChannels * BitsPerSample/8
|
||||
The number of bytes for one sample including
|
||||
all channels. I wonder what happens when
|
||||
this number isn't an integer?
|
||||
34 2 BitsPerSample 8 bits = 8, 16 bits = 16, etc.
|
||||
|
||||
The "data" subchunk contains the size of the data and the actual sound:
|
||||
36 4 Subchunk2ID Contains the letters "data"
|
||||
(0x64617461 big-endian form).
|
||||
40 4 Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8
|
||||
This is the number of bytes in the data.
|
||||
You can also think of this as the size
|
||||
of the read of the subchunk following this
|
||||
number.
|
||||
44 * Data The actual sound data.
|
||||
|
||||
|
||||
NOTE TO READERS:
|
||||
|
||||
The thing that makes reading wav files tricky is that java has no unsigned types. This means that the
|
||||
binary data can't just be read and cast appropriately. Also, we have to use larger types
|
||||
than are normally necessary.
|
||||
|
||||
In many languages including java, an integer is represented by 4 bytes. The issue here is
|
||||
that in most languages, integers can be signed or unsigned, and in wav files the integers
|
||||
are unsigned. So, to make sure that we can store the proper values, we have to use longs
|
||||
to hold integers, and integers to hold shorts.
|
||||
|
||||
Then, we have to convert back when we want to save our wav data.
|
||||
|
||||
It's complicated, but ultimately, it just results in a few extra functions at the bottom of
|
||||
this file. Once you understand the issue, there is no reason to pay any more attention
|
||||
to it.
|
||||
|
||||
|
||||
ALSO:
|
||||
|
||||
This code won't read ALL wav files. This does not use to full specification. It just uses
|
||||
a trimmed down version that most wav files adhere to.
|
||||
*/
|
||||
|
||||
long NumSamples;
|
||||
int NumSamples;
|
||||
EncoderInfo info;
|
||||
int BytesPerSample;
|
||||
RandomAccessFile outFile;
|
||||
|
||||
// I made this public so that you can toss whatever you want in here
|
||||
// maybe a recorded buffer, maybe just whatever you want
|
||||
public byte[] myData;
|
||||
ByteOrder order = ByteOrder.LITTLE_ENDIAN;
|
||||
|
||||
// empty constructor
|
||||
public Wav() {
|
||||
}
|
||||
|
||||
|
|
@ -115,42 +35,80 @@ a trimmed down version that most wav files adhere to.
|
|||
}
|
||||
|
||||
public void save() {
|
||||
long SubChunk2Size = NumSamples * info.channels * BytesPerSample;
|
||||
int SubChunk1Size = 16;
|
||||
int SubChunk2Size = NumSamples * info.channels * BytesPerSample;
|
||||
int ChunkSize = 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size);
|
||||
|
||||
long SubChunk1Size = 16;
|
||||
|
||||
int BlockAlign = info.bps * info.channels;
|
||||
|
||||
short Format = 1;
|
||||
write("RIFF", ByteOrder.BIG_ENDIAN);
|
||||
write(ChunkSize, order);
|
||||
write("WAVE", ByteOrder.BIG_ENDIAN);
|
||||
|
||||
int ByteRate = info.sampleRate * info.channels * BytesPerSample;
|
||||
short AudioFormat = 1; // PCM = 1 (i.e. Linear quantization)
|
||||
int BlockAlign = BytesPerSample * info.channels;
|
||||
|
||||
long ChunkSize = 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size);
|
||||
write("fmt ", ByteOrder.BIG_ENDIAN);
|
||||
write(SubChunk1Size, order);
|
||||
write((short)AudioFormat, order); //short
|
||||
write((short) info.channels, order); // short
|
||||
write(info.sampleRate, order);
|
||||
write(ByteRate, order);
|
||||
write((short)BlockAlign, order); // short
|
||||
write((short)info.bps, order); // short
|
||||
|
||||
write("data", ByteOrder.BIG_ENDIAN);
|
||||
write(SubChunk2Size, order);
|
||||
}
|
||||
|
||||
void write(String str, ByteOrder order) {
|
||||
try {
|
||||
// write the wav file per the wav file format
|
||||
outFile.writeBytes("RIFF"); // 00 - RIFF
|
||||
outFile.write(intToByteArray((int) ChunkSize), 0, 4); // 04 - how big is the rest of this file?
|
||||
outFile.writeBytes("WAVE"); // 08 - WAVE
|
||||
outFile.writeBytes("fmt "); // 12 - fmt
|
||||
outFile.write(intToByteArray((int) SubChunk1Size), 0, 4); // 16 - size of this chunk
|
||||
outFile.write(shortToByteArray((short) Format), 0, 2); // 20 - what is the audio format? 1 for PCM = Pulse Code Modulation
|
||||
outFile.write(shortToByteArray((short) info.channels), 0, 2); // 22 - mono or stereo? 1 or 2? (or 5 or ???)
|
||||
outFile.write(intToByteArray((int) info.sampleRate), 0, 4); // 24 - samples per second (numbers per second)
|
||||
outFile.write(intToByteArray((int) ByteRate), 0, 4); // 28 - bytes per second
|
||||
outFile.write(shortToByteArray((short) BlockAlign), 0, 2); // 32 - # of bytes in one sample, for all channels
|
||||
outFile.write(shortToByteArray((short) info.bps), 0, 2); // 34 - how many bits in a sample(number)? usually 16 or 24
|
||||
outFile.writeBytes("data"); // 36 - data
|
||||
outFile.write(intToByteArray((int) SubChunk2Size), 0, 4); // 40 - how big is this data chunk
|
||||
byte[] cc = str.getBytes("UTF-8");
|
||||
ByteBuffer bb = ByteBuffer.allocate(cc.length);
|
||||
bb.order(order);
|
||||
bb.put(cc);
|
||||
bb.flip();
|
||||
|
||||
outFile.write(bb.array());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void encode(byte[] buf, int o, int l) {
|
||||
NumSamples += buf.length / info.channels / BytesPerSample;
|
||||
void write(int i, ByteOrder order) {
|
||||
ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE);
|
||||
bb.order(order);
|
||||
bb.putInt(i);
|
||||
bb.flip();
|
||||
|
||||
try {
|
||||
outFile.write(buf, o, l); // 44 - the actual data itself - just a long string of numbers
|
||||
outFile.write(bb.array());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
void write(short i, ByteOrder order) {
|
||||
ByteBuffer bb = ByteBuffer.allocate(Short.SIZE / Byte.SIZE);
|
||||
bb.order(order);
|
||||
bb.putShort(i);
|
||||
bb.flip();
|
||||
|
||||
try {
|
||||
outFile.write(bb.array());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void encode(short[] buf) {
|
||||
NumSamples += buf.length / info.channels;
|
||||
try {
|
||||
ByteBuffer bb = ByteBuffer.allocate(buf.length * (Short.SIZE / Byte.SIZE));
|
||||
bb.order(order);
|
||||
for (int i = 0; i < buf.length; i++)
|
||||
bb.putShort(buf[i]);
|
||||
bb.flip();
|
||||
outFile.write(bb.array());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
@ -166,21 +124,6 @@ a trimmed down version that most wav files adhere to.
|
|||
}
|
||||
}
|
||||
|
||||
// returns a byte array of length 4
|
||||
private static byte[] intToByteArray(int i) {
|
||||
byte[] b = new byte[4];
|
||||
b[0] = (byte) (i & 0x00FF);
|
||||
b[1] = (byte) ((i >> 8) & 0x000000FF);
|
||||
b[2] = (byte) ((i >> 16) & 0x000000FF);
|
||||
b[3] = (byte) ((i >> 24) & 0x000000FF);
|
||||
return b;
|
||||
}
|
||||
|
||||
// convert a short to a byte array
|
||||
public static byte[] shortToByteArray(short data) {
|
||||
return new byte[]{(byte) (data & 0xff), (byte) ((data >>> 8) & 0xff)};
|
||||
}
|
||||
|
||||
public EncoderInfo getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue