Vibhav Chaddha Vibhav Chaddha - 10 days ago 6
Java Question

How to find length of a 2D byte array and to how use that 2D byte array in ByteArrayInputStream

Firstly, in the following code what I am trying to do is find the length of a 2D byte array by using 'byteBuffer[0].length', but it is actually not working. When I am printing 'byteBuffer[0].length' it is giving the output as 4 instead of 882000, which (latter) should be the correct output according to the parameters I had passed. So how do I iterate it in my loop?

Secondly, I want to pass 'byteBuffer' in 'ByteArrayInputStream', but in 'ByteArrayInputStream' we cannot pass a 2D array. So is there a way of appending the values and use it there? And I also need to pass the values of 'Frequency1' and 'Frequency2' alternatively and save them in .wav format, so that I can play them accordingly in my media player. For example: an Ambulance's siren.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;

public class AudioPlay {

public static void main(String[] args) throws IOException {

Scanner in = new Scanner(System.in);
final double SAMPLING_RATE = 44100; // Audio sampling rate
int time = in.nextInt(); //Time specified by user in seconds
int frequency1 = in.nextInt(); //Frequency specified by the user in hz
int frequency2 = in.nextInt();

//Size of buffer, in case time is 10 seconds it will be [2][441000]
float buffer[][] = new float[2][(int) (time * SAMPLING_RATE)];

for (int sample = 0; sample < buffer[0].length; sample++) {
double cycle = sample / SAMPLING_RATE; //Fraction of cycle between samples
buffer[0][sample] = (float) (Math.sin(2 * Math.PI * frequency1 * cycle)); //Storing value at every index of 1st row
buffer[1][sample] = (float) (Math.sin(2 * Math.PI * frequency2 * cycle)); //Storing value at every index of 2nd row
}

//Size of byteBuffer, in case time is 10sec it will be [2][882000]
byte byteBuffer[][] = new byte[2][(int)(buffer.length * 2)];
System.out.println(byteBuffer[0].length); // Giving wrong output

int count = 0;

for (int j = 0; j < byteBuffer.length; j++) {
for (int i = 0; i < byteBuffer[0].length; i++) {
final int x = (int) ((buffer[j][count++]) * Short.MAX_VALUE);
byteBuffer[j][i++] = (byte) x;
byteBuffer[j][i] = (byte) (x / 256); //Total Value of Byte
}
}
File out = new File("E:/RecordAudio7.wav"); //The path where user want the file data to be written

//Construct an audio format, using 44100hz sampling rate, 16 bit samples, mono, and big
// endian byte ordering
AudioFormat format = new AudioFormat((float) SAMPLING_RATE, 16, 1, true, false);

// It uses bytebuffer as its buffer array that contains bytes that may be read from the stream.
ByteArrayInputStream bais = new ByteArrayInputStream(byteBuffer[0]);

//Constructs an audio input stream that has the requested format and length in sample frames, using audio data
//from the specified input stream.
AudioInputStream audioInputStream = new AudioInputStream(bais, format, buffer.length);

//Writes a stream of bytes representing an audio file of the specified file type to the external file provided.
AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, out);

audioInputStream.close(); //Closes this audio input stream
}


}

Answer

declaring byteBuffer is not correct you are using buffer.length which is 2 that's why output is 4

use buffer[0].length * 2 instead of buffer.length * 2 as below:

byte byteBuffer[][] = new byte[2][(int)(buffer[0].length * 2)];

for the second part, (passing 2D array into ByteArrayInputStream) you can put the 2D elements into a 1D longer array where it's length will equals byteBuffer[0].length*byteBuffer.length

you can use System.arraycopy() something like this:

int newArrayLength = byteBuffer.length*byteBuffer[0].length;
byte oneArray[] = new byte[newArrayLength];

//arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
for(int b=0;b<byteBuffer.length;b++){
    System.arraycopy(byteBuffer[b], 0, oneArray, (b*byteBuffer[b].length), byteBuffer[b].length)
}

what this code do is converting this

byteBuffer (2D)

0-[ ][ ][ ][ ][ ][ ][ ]...

1-[ ][ ][ ][ ][ ][ ][ ]...

2-[ ][ ][ ][ ][ ][ ][ ]...

into this:

oneArray (1D)

[][][][][][][]...[][][][][][][]...[][][][][][][]...

And use oneArray for your ByteArrayInputStream

P.S: if your app will continue running after this step, it's good to release memory allocated for byteBuffer as it's not needed anymore, because you will work with oneArray, you can release memory by un-referencing it.

byteBuffer=null;