Reputation: 653
I'am trying to concatenate byte array audio.(audio is .wav, and is converted to byte array). My current code seems to concatenate. But when i play the result it only play the first sample of bytes in the final array. Do you know why the next bytes are not play?
int length = 0;
for (byte[] array : ListSamplesByte) {
length += array.length;
}
byte[] result = new byte[length];
int pos = 0;
for (byte[] array : ListSamplesByte) {
System.arraycopy(array, 0, result, pos, array.length);
pos += array.length;
}
byte[] playByte = result;
MediaPlayer.playAudio(playByte);
EDIT 1: All the files are 44100Hz, 16bit PCM. Even if all the byte array are the same, it only play the first one. I think the problem is linked to the header or byte in the chunk which are not added or removed.
Upvotes: 1
Views: 811
Reputation: 653
I found the solution. Here is the Procedure to concatenate, take in note all my wav files have same sample rate, channels.
First i needed some knowledge about the structure of a WAV files. A wav file is composed of a header (first 44 bytes) following by the raw data. http://soundfile.sapp.org/doc/WaveFormat/
1) Convert your wav Files to byte array
2) Look at the function below HeaderInformation() which takes in parameter your byte array, it create a InputDataStream and read the bytes array in the aim to get the header informations, most important in is to get ChunkSize length (byte 4-8) and the raw data length (byte 44).
public class AudioConcatenate {
long myChunkSize;
long mySubChunk1Size;
int myFormat;
long myChannels;
long mySampleRate;
long myByteRate;
int myBlockAlign;
int myBitsPerSample;
long myDataSize;
long totalChunkSize;
byte[] myData;
int byteRate=176400;
long longSampleRate=44100;
protected ArrayList<Long> ListDataSize= new ArrayList<Long>();
protected ArrayList<byte[]> ListDataByte = new ArrayList<byte[]>();
protected ArrayList<Long> ListChunkSize = new ArrayList<>();
public AudioConcatenate(){
}
protected void HeaderInformation(byte[] SoundSample) {
myData = null;
DataInputStream inFile = newDataInputStream(newByteArrayInputStream(sample));
byte[] tmpLong = new byte[4];
byte[] tmpInt = new byte[2];
try {
while (inFile.available() > 0) {
String chunkID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
inFile.read(tmpLong); // read the ChunkSize
myChunkSize = byteArrayToLong(tmpLong)+silenceArray.length;
String format = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
String subChunk1ID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
inFile.read(tmpLong); // read the SubChunk1Size
mySubChunk1Size = byteArrayToLong(tmpLong);
inFile.read(tmpInt); // read the audio format. This should be 1 for PCM
myFormat = byteArrayToInt(tmpInt);
inFile.read(tmpInt); // read the # of channels (1 or 2)
myChannels = byteArrayToInt(tmpInt);
inFile.read(tmpLong); // read the samplerate
mySampleRate = byteArrayToLong(tmpLong);
inFile.read(tmpLong); // read the byterate
myByteRate = byteArrayToLong(tmpLong);
inFile.read(tmpInt); // read the blockalign
myBlockAlign = byteArrayToInt(tmpInt);
inFile.read(tmpInt); // read the bitspersample
myBitsPerSample = byteArrayToInt(tmpInt);
String dataChunkID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
inFile.read(tmpLong); // read the size of the data
myDataSize = byteArrayToLong(tmpLong)+silenceArray.length;
// read the data chunk
myData = new byte[(int) myDataSize];
inFile.read(myData);
ListDataSize.add(myDataSize);
ListDataByte.add(FinalDataArray);
ListChunkSize.add(myChunkSize);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Three arraylist of byte[] at the end of the function are used to store the chunk Size, store the byte array of raw data, and size of byte array of data. This way each time you call the function for a sound it'll store the essential data.
4) Once you have call this function for multiples sounds. We need to recreate the wav header which will take in count the length of the multiple sounds. We have to calculate the total size of ChunkSize, and the total size of raw data.
public long TotalDataSize(){
int i;
long sum = 0;
for(i = 0; i < ListDataSize.size(); i++) {
sum += ListDataSize.get(i);
}
return sum;
}
public long TotalChunkSize(){
int i;
long sum = 0;
for(i = 0; i < ListChunkSize.size(); i++) {
sum += ListChunkSize.get(i);
}
return sum;
}
We create the header and we include the new size, total chunkSize at byte [4-8], total data byte[40-43]
public byte[] WriteHeader(){
byte[] header = new byte[44];
long totalDataSize= TotalDataSize();
long totalChunkSize=TotalChunkSize();
header[0] = 'R'; // RIFF/WAVE header
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
header[4] = (byte) (totalChunkSize & 0xff);
header[5] = (byte) ((totalChunkSize >> 8) & 0xff);
header[6] = (byte) ((totalChunkSize >> 16) & 0xff);
header[7] = (byte) ((totalChunkSize >> 24) & 0xff);
header[8] = 'W';
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
header[12] = 'f'; // 'fmt ' chunk
header[13] = 'm';
header[14] = 't';
header[15] = ' ';
header[16] = 16; // 4 bytes: size of 'fmt ' chunk
header[17] = 0;
header[18] = 0;
header[19] = 0;
header[20] = 1; // format = 1
header[21] = 0;
header[22] = (byte) 2;
header[23] = 0;
header[24] = (byte) (longSampleRate & 0xff);
header[25] = (byte) ((longSampleRate >> 8) & 0xff);
header[26] = (byte) ((longSampleRate >> 16) & 0xff);
header[27] = (byte) ((longSampleRate >> 24) & 0xff);
header[28] = (byte) (byteRate & 0xff);
header[29] = (byte) ((byteRate >> 8) & 0xff);
header[30] = (byte) ((byteRate >> 16) & 0xff);
header[31] = (byte) ((byteRate >> 24) & 0xff);
header[32] = (byte) (2 * 16 / 8); // block align
header[33] = 0;
header[34] = 16; // bits per sample
header[35] = 0;
header[36] = 'd';
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
header[40] = (byte) (totalDataSize & 0xff);
header[41] = (byte) ((totalDataSize >> 8) & 0xff);
header[42] = (byte) ((totalDataSize >> 16) & 0xff);
header[43] = (byte) ((totalDataSize >> 24) & 0xff);
return header;
}
6) Function to Concatenate: First we create a new ArrayList "ListFinalSound" to structure the final sound. we add first the new header and after the byte[] raw data.
public byte[] ConcatenateSound(){
ListFinalSound.add(WriteHeader());
for (byte[] arrayRawData : ListDataByte) {
ListFinalSound.add(arrayRawData);
}
/* sum the length of each byte array to get the total and create later a new byte array of the total length.**/
int length = 0;
for (byte[] array : ListFinalSound) {
length += array.length;
}
/* we concatenate with the use of System.arraycopy.
For each loop it'll increment the position and concatenate the byte array**/
byte[] ConcatenateSounds = new byte[length];
int pos = 0;
for (byte[] array : ListFinalSound) {
System.arraycopy(array, 0, ConcatenateSounds, pos, array.length);
pos += array.length;
}
return ConcatenateSounds;
}
7) The ConcatenateSound() function return the concatenation.
Upvotes: 2