How to reverse byte[]<=>double[] in java - java

In java.
I had some file (*.wav) from where I extracted byte[]. After that I converted this to double[] by this code:
for (int i = 0; i < bytesIn.length && idx < buffer.length; i += 2)
{
byte lowByte = bytesIn[i];
byte highByte = bytesIn[i+1];
//Little endian
buffer[idx++] = (lowByte & 0xFF | highByte << 8);
}
Where bytesIn = byte[] of file
and buffer = double[]
After this I did some operation on buffer used fast Fourier transform and inverse. Now after inverse operation from fft I have double[] but I dont know how to get back to byte[].
I found this:
byte[] bytes = new byte[8];
ByteBuffer.wrap(bytes).putDouble(value);
but bytes store different value than I expected. Changed buffer after IFFT is same as original one. Can anyone write how to reverse code from first code block?

Related

Audio samples mixing or changing volume causes saturation and white noise

I have a multichannel input (i'm using Soundflower 64ch on mac), and I'm trying to mixdown 4 channels of the 64 channels to an stereo output.
What i am doing is, reading chunks of 1024 frames, with 64 channels every frame, then converting the bytebuffer to Short array (values between -32,768 <-> 32,767, because samples are 16 bits).
This way I add for example channel1[sample] + channel2[sample] and I get the mix of both channels.
But here is a problem, the sum can overflow the Short (16 bit) range, introducing saturation in the sound. So what I'm doing is (channel1[sample] + channel2[sample]) / 2 but when I divide by 2, I hear a lot of white sound.
Also if I try to reduce the volumen of a channel by doing channel1[sample] * 0.5 there is a lot of saturation.
Why does it happen?
Here is my full code, note that I'm converting bytes to short to handle better, and then I'm converting back to bytes for write the mix to the stereo output:
public static void main(String[] args) throws LineUnavailableException {
int inputChannels = 64;
AudioFormat inputFormat = new AudioFormat(48000, 16, inputChannels, true, false);
AudioFormat outputFormat = new AudioFormat(48000, 16, 2, true, false);
TargetDataLine mic = AudioSystem.getTargetDataLine(inputFormat);
SourceDataLine speaker = AudioSystem.getSourceDataLine(outputFormat);
mic.open(inputFormat);
speaker.open(outputFormat);
mic.start();
speaker.start();
AudioInputStream audioInputStream = new AudioInputStream(mic);
int bytesPerFrame = audioInputStream.getFormat().getFrameSize();
// Set an arbitrary buffer size of 1024 frames.
int CHUNK = 1024 ;
int numBytes = CHUNK * bytesPerFrame;
byte[] audioBytes = new byte[numBytes];
try {
byte[][] frames = new byte[CHUNK][bytesPerFrame];
int i = 0, j = 0
;
while (true) {
// read to audioBytes.
audioInputStream.read(audioBytes);
// split audioBytes in _CHUNK_ frames (1024 frames)
for(j=0; j<CHUNK; j++) {
frames[j] = Arrays.copyOfRange(audioBytes, j * bytesPerFrame, j * bytesPerFrame + bytesPerFrame);
}
// convert bytearray to shortarray
short[][] shortFrames = new short[CHUNK][inputChannels];
for(i=0; i < frames.length; i++) {
ByteBuffer.wrap(frames[i]).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get(shortFrames[i]);
}
short[] leftOutput = new short[CHUNK*2];
short[] rightOutput = new short[CHUNK*2];
for (i=0; i<CHUNK; i++) {
short channel1 = shortFrames[i][0];
short channel2 = shortFrames[i][1];
short channel3 = shortFrames[i][2];
short channel4 = shortFrames[i][3];
leftOutput[i] = (short)(channel4);
rightOutput[i] = (short)(channel4);;
}
//convert shortarray in byte buffer
ByteBuffer byteBuf = ByteBuffer.allocate(CHUNK * 2 * 2); // 2 bytes * 2 output channels
for (i=0; i<CHUNK; i++) {
byteBuf.putShort(leftOutput[i]);
byteBuf.putShort(rightOutput[i]);
}
speaker.write(byteBuf.array(),0,byteBuf.array().length);
}
} catch (Exception ex) {
// Handle the error...
System.out.println("exception");
System.out.println(ex.toString());
}
}
IDK if the issue is how the bytes are being converted to shorts and back, but since you asked about this in the comment, I will post it. Assume buffer has contiguous little-endian bytes at 16-bit encoding. Just reverse the byte indexes for big-endian.
pcmShort = ( buffer[i] & 0xff ) | ( buffer[i+1] << 8 );
The conversion of pcm to byte that I use follows (for little-endian, reverse the indexes for big-endian):
outBuffer[i] = (byte)pcmShort[0];
outBuffer[i+1] = (byte)((int)pcmShort[0] >> 8);
Maybe you can use the two methods (your attempt with ByteBuffer and getShort, and the above) side-by-side on the same data and check if the resulting arrays hold the same values?
Another thing I'd try to do is to just get a single track working. If that sounds okay, then check on the mixing. It's kind of unlikely that the signals are so hot that they are overrunning. So something else is probably going on.
I should try this out myself, I'm not sure when I'll get to it. It could potentially be an improvement over what I've been doing.

How can I mix two PCM audio files

I did test mix two PCM audio file.
but don't get true audio file.
I used this example
So, my code:
private void mixSound() throws IOException {
byte[] music1 = null;
music1 = new byte[in1.available()];
music1 = convertStreamToByteArray(in1);
in1.close();
byte[] music2 = null;
music2 = new byte[in2.available()];
music2 = convertStreamToByteArray(in2);
in2.close();
byte[] output = new byte[music1.length];
for (int i = 0; i < output.length; i++) {
samplef1 = music1[i] / 128.0f;
samplef2 = music2[i] / 128.0f;
float mixed = samplef1 + samplef2;
// reduce the volume a bit:
mixed *= 0.8;
// hard clipping
if (mixed > 1.0f) mixed = 1.0f;
if (mixed < -1.0f) mixed = -1.0f;
byte outputSample = (byte) (mixed * 128.0f);
output[i] = outputSample;
} //for loop
save = openFileOutput(filename, Context.MODE_PRIVATE);
save.write(output);
save.flush();
save.close();
}
public byte[] convertStreamToByteArray(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[8000];
int i;
while ((i = is.read(buff, 0, buff.length)) > 0) {
baos.write(buff, 0, i);
}
return baos.toByteArray(); // be sure to close InputStream in calling function
}
2 audio files with bit rate 64000 & sampling rate 16000 GH & sterio
in1 = getResources().openRawResource(R.raw.a_2);
in2 = getResources().openRawResource(R.raw.a_diz_2);
Also try to convert
bytes array to short array -> then calculate-> then convert short to byte using converts methods
like bytes2Shorts(byte[] buf) and shorts2Bytes(short[] s).
But steel have a fail result.
Someone can say me Where is my wrong?
There are a number of issues here and I'll try to address some of them
First, using byte[] suggests that your PCM wave data format is AudioFormat.ENCODING_PCM_8BIT (or it should be this format if it already isn't). This format uses 8-bit (1 byte) unsigned, which means that the sound
samples are stored in the [0, 255] range (not in the [-127, +128] or [-128,+127] range).
This means that the negative values are in the [0, 127] range and the positive samples are in the [128,255] range.
When mixing values, it's best to prevent clipping right from the start so I'd use
byte mixed = (music1[i] + music2[i])/2; //this ensures that mixed remains within the `correct range` for your PCM format
You can also divide your samples by 128 (if you want to convert them to floating point values)
float samplef1 = (((float)music1[i]-127)/128 ; //converting samples to [-1, +1] range -- -1 corresponds a sample value of 0 and +1 to 255
float samplef2 = (((float)music2[i]-127)/128;
float mixed = (samplef1+samplef2)/2;
Note that you now have 2 options to play data(samples) generated in this way. Either, convert floats back to bytes or use the AudioFormat.ENCODING_PCM_FLOAT format.
audio files with bit rate 64000 & sampling rate 16000 GH & sterio
This can't be correct. Typical sampling rates are 4000Hz, 8000Hz, 11000Hz, 16000Hz, 22050Hz or 44100Hz. For bit depths, audio usually uses 8 bits, 16 bits or 32 bits.
For instance, CD quality audio uses 44100Hz, 16bit, stereo format.

Why method for ByteBuffer putShort(value) does not working correctly in my case?

My task is convert short[] array to byte[] array, because need send bytes via socket. This is bytes for AudioTrack (Android)
For converting use this post, specifically this and this
This method gives only white noise, when try to convert short to byte array:
val sampleBuffer = decoder.decodeFrame(frameHeader, bitstream) as SampleBuffer
val pcm = sampleBuffer.buffer //pcm is short[] array
byteBuf = ByteBuffer.allocate(pcm.size * 2) // because 1 short = 2 bytes
while (pcm.size > i) {
byteBuf.putShort(pcm[i])
i++
}
auddioTrack.write(byteBuf.array(), 0, byteBuf.limit());
But this convert works fine:
var i = 0
val byteBuf = ByteBuffer.allocate(pcm.size * 2)
val buff = ByteBuffer.allocate(2)
//pcm size equals 2304
while (pcm.size > i) {
// byteBuf.putShort(pcm[i])
byteBuf.put(byteArrayOf((pcm[i].toInt() and 0x00FF).toByte(), ((pcm[i].toInt() and 0xFF00) shr (8)).toByte()))
i++
}
auddioTrack.write(byteBuf.array(), 0, byteBuf.limit());
Why has it happened?
byteBuf.array().size will return the size of the buffer (pcm.size * 2) regardless of whether that many bytes were written into the buffer. You probably want byteBuf.limit() instead.

Java Reading large files into byte array chunk by chunk

So I've been trying to make a small program that inputs a file into a byte array, then it will turn that byte array into hex, then binary. It will then play with the binary values (I haven't thought of what to do when I get to this stage) and then save it as a custom file.
I studied a lot of internet code and I can turn a file into a byte array and into hex, but the problem is I can't turn huge files into byte arrays (out of memory).
This is the code that is not a complete failure
public void rundis(Path pp) {
byte bb[] = null;
try {
bb = Files.readAllBytes(pp); //Files.toByteArray(pathhold);
System.out.println("byte array made");
} catch (Exception e) {
e.printStackTrace();
}
if (bb.length != 0 || bb != null) {
System.out.println("byte array filled");
//send to method to turn into hex
} else {
System.out.println("byte array NOT filled");
}
}
I know how the process should go, but I don't know how to code that properly.
The process if you are interested:
Input file using File
Read the chunk by chunk of the file into a byte array. Ex. each byte array record hold 600 bytes
Send that chunk to be turned into a Hex value --> Integer.tohexstring
Send that hex value chunk to be made into a binary value --> Integer.toBinarystring
Mess around with the Binary value
Save to custom file line by line
Problem:: I don't know how to turn a huge file into a byte array chunk by chunk to be processed.
Any and all help will be appreciated, thank you for reading :)
To chunk your input use a FileInputStream:
Path pp = FileSystems.getDefault().getPath("logs", "access.log");
final int BUFFER_SIZE = 1024*1024; //this is actually bytes
FileInputStream fis = new FileInputStream(pp.toFile());
byte[] buffer = new byte[BUFFER_SIZE];
int read = 0;
while( ( read = fis.read( buffer ) ) > 0 ){
// call your other methodes here...
}
fis.close();
To stream a file, you need to step away from Files.readAllBytes(). It's a nice utility for small files, but as you noticed not so much for large files.
In pseudocode it would look something like this:
while there are more bytes available
read some bytes
process those bytes
(write the result back to a file, if needed)
In Java, you can use a FileInputStream to read a file byte by byte or chunk by chunk. Lets say we want to write back our processed bytes. First we open the files:
FileInputStream is = new FileInputStream(new File("input.txt"));
FileOutputStream os = new FileOutputStream(new File("output.txt"));
We need the FileOutputStream to write back our results - we don't want to just drop our precious processed data, right? Next we need a buffer which holds a chunk of bytes:
byte[] buf = new byte[4096];
How many bytes is up to you, I kinda like chunks of 4096 bytes. Then we need to actually read some bytes
int read = is.read(buf);
this will read up to buf.length bytes and store them in buf. It will return the total bytes read. Then we process the bytes:
//Assuming the processing function looks like this:
//byte[] process(byte[] data, int bytes);
byte[] ret = process(buf, read);
process() in above example is your processing method. It takes in a byte-array, the number of bytes it should process and returns the result as byte-array.
Last, we write the result back to a file:
os.write(ret);
We have to execute this in a loop until there are no bytes left in the file, so lets write a loop for it:
int read = 0;
while((read = is.read(buf)) > 0) {
byte[] ret = process(buf, read);
os.write(ret);
}
and finally close the streams
is.close();
os.close();
And thats it. We processed the file in 4096-byte chunks and wrote the result back to a file. It's up to you what to do with the result, you could also send it over TCP or even drop it if it's not needed, or even read from TCP instead of a file, the basic logic is the same.
This still needs some proper error-handling to work around missing files or wrong permissions but that's up to you to implement that.
A example implementation for the process method:
//returns the hex-representation of the bytes
public static byte[] process(byte[] bytes, int length) {
final char[] hexchars = "0123456789ABCDEF".toCharArray();
char[] ret = new char[length * 2];
for ( int i = 0; i < length; ++i) {
int b = bytes[i] & 0xFF;
ret[i * 2] = hexchars[b >>> 4];
ret[i * 2 + 1] = hexchars[b & 0x0F];
}
return ret;
}

Java - is there something like binary stream?

I had one question.
Is there library or etc to compose int & strings to byte array ?
Like :
byte temparray[] = new byte[10];
int a = 10;
int b = 10;
temparray << new String("12") << a << b;
Thanks.
edit
byte[] buffer = new byte[649];
byte[] charname = this.getName().getBytes();
System.arraycopy(charname, 0 , buffer, 0, charname.length);
for(int i=0;i<16;i++) //mystery crs 16 zeros
{
buffer[i+17] = (byte)0x30;
}
buffer[34] = this.faction;
if(this.characterClass == 2)
{
buffer[40] = 2;
} else
{
buffer[40] = 1;
}
System.arraycopy(BitTools.shortToByteArray(face), 0, buffer, 42, 2);
buffer[44] = 1;
buffer[48] = (byte)this.characterClass; //class byte
buffer[52] = 2; explanation yet
buffer[54] = (byte)this.getLevel();
This is an example of my packet generator and i wanted to simplify it, but in packet i use only shorts, ints and strings.
java.io.ByteArrayOutputStream is a stream implementation that collects content on an internal byte array, and you can wrap it in a java.io.OutputStreamWriter to write character content to it.
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
Writer out = new OutputStreamWriter(byteOut, "UTF-8"); // Uses UTF-8 encoding
out.write("12");
out.write(10);
out.write(10);
out.close();
byte[] bytes = byteOut.toByteArray();
After this, bytes.length is just long enough for the bytes written to byteOut.
Yes. See java.io.ByteArrayOutputStream. Note that you can wrap this stream to support writing of other types like String: PrintWriter pw = new PrintWriter(yourByteArrayOutputStream); pw.print("Hello");
And afterwards use yourByteArrayOutputStream.toByteArray(); to get the byte array.
http://docs.oracle.com/javase/7/docs/api/
Integer.byteValue();
Double.byteValue();
String.getBytes();
// etc.
Take a look at String#getBytes and ByteBuffer. Charsets and byte order might be important depending on your use case.

Categories

Resources