Rocé Tarentula
Rocé Tarentula

Reputation: 653

byte array audio concatenate Android

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

Answers (1)

Rocé Tarentula
Rocé Tarentula

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

Related Questions