I have an ByteArrayOutputStream connected with a Dataline from an AudioSource. I need to convert the Stream in some significative values that probally are the sound values taken from source or not ?
Well then how can I conver the byteArray (from ByteArrayOutStream.getByteArray()) in a intArray?. I googled it but with no luck .
p.s. the audioFormat that I used is : PCM_SIGNED 192.0Hz 16Bit big endian
Use a ByteBuffer. You can convert not only to different array types this way, but also deal with endian issues.
You can try the following:
ByteBuffer.wrap(byteArray).asIntBuffer().array()
When you do ByteArrayOutStream.toByteArray(), you get: byte[]. So now, I assume you need to convert byte[] to int.
You can do this:
/**
* Convert the byte array to an int.
*
* #param b The byte array
* #return The integer
*/
public static int byteArrayToInt(byte[] b) {
return byteArrayToInt(b, 0);
}
/**
* Convert the byte array to an int starting from the given offset.
*
* #param b The byte array
* #param offset The array offset
* #return The integer
*/
public static int byteArrayToInt(byte[] b, int offset) {
int value = 0;
for (int i = 0; i < 4; i++) {
int shift = (4 - 1 - i) * 8;
value += (b[i + offset] & 0x000000FF) << shift;
}
return value;
}
Related
I am trying to developing a java card application. There will a dynamic input string which will contain a phone number in a byte array like:
byte[] number =new byte[] {1,2,3,4,5,6,7,8,9,5};
I want this array to be changed into following array:
byte[] changed num = {(byte)0x0C, (byte)0x91, (byte)0x19, (byte)0x21, (byte)0x43, (byte)0x65, (byte)0x87, (byte)0x59}
Where first three bytes will be same always and remaining 5 will be update from the incoming array.
I have tried the following:
public static void main(String args[]){
byte[] number =new byte[] {1,2,3,4,5,6,7,8,9,5};
byte[] changednum = new byte[8];
changednum[0] = (byte)0x0C;
changednum[1] = (byte)0x91;
changednum[2] = (byte)0x19;
changednum[3] = (byte)0x(number[0] + number[1]*10);
changednum[4] = (byte)0x(number[2] + number[3]*10);
changednum[5] = (byte)0x(number[4] + number[5]*10);
changednum[6] = (byte)0x(number[6] + number[7]*10);
changednum[7] = (byte)0x(number[8] + number[9]*10);
System.out.println(Arrays.toString(changednum));
}
}
But the last 5 values are not being converted to byte value
s.
This line
changednum[3] = (byte)0x(number[0] + number[1]*10);
could be done with a complicate set of String manipulation how simple maths will do what you want.
changednum[3] = (byte)(number[0] + number[1]*16);
The *16 is needed because you appear to be assuming the number is in hexadecimal.
You could use a loop
for (int i = 0; i < 4; i++)
changednum[i+3] = (byte)(number[i*2] + number[i*2+1]*16);
or using += to avoid the cast
for (int i = 0; i < 4; i++)
changednum[i+3] += number[i*2] + number[i*2+1] * 16;
or you can write
for (int i = 0; i < 4; i++)
changednum[i+3] += number[i*2] + (number[i*2+1] << 4);
Although it is not very important in the standard edition of Java in this case, performance is often crucial in Java Card. This solution is slightly faster than the accepted answer thanks to bitwise operators and it is a valid Java Card code without 32-bit integers:
for (short i = 3, j = 0; i < 7; i++, j += 2) {
changednum[i] = (byte) ((number[j+1] << 4) | (number[j] & 0x0F));
}
You can use the method Integer.toHexString, that will do the convertion,
you need to carefully note that casting explicity integer to byte will truncate its value in a range between -128 to 127
final int f = -2211;
System.out.println(f);
System.out.println((byte) f);
System.out.println(Integer.toHexString((byte) f));
The other answers lack readability, in my opinion.
This solution uses separate methods and a ByteBuffer to make the methods work without passing an offset and other plumbing. It has weird things like constants and well thought out identifiers, exceptions, JavaDoc and other scary concepts from the book of maintainability.
package nl.owlstead.stackoverflow;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
/**
* Helper class to create telephone numbers with headers for Java card using
* reverse packed BCD format.
*
* #param buffer
* a buffer with enough space for the 3 byte header
*/
public final class OverlyDesignedTelephoneNumberHelper {
private static final int TELEPHONE_NUMBER_SIZE = 10;
private static final int NIBBLE_SIZE = 4;
private static final byte[] HEADER = { (byte) 0x0C, (byte) 0x91,
(byte) 0x19 };
/**
* Adds the header for the telephone number to the given buffer.
*
* #param buffer
* a buffer with enough space for the 3 byte header
* #throws NullPointerException
* if the buffer is null
* #throws BufferOverflowException
* if the buffer doesn't have enough space for the header
*/
private static void addTelephoneNumberHeader(final ByteBuffer buffer) {
buffer.put(HEADER);
}
/**
* Adds the telephone number to the given buffer.
*
* #param buffer
* a buffer with enough space for the 3 byte header
* #param number
* the number in BCD format, should be 10 bytes in size
* #throws IllegalArgumentException
* if the number is null or doesn't contain 10 BCD digits
* #throws NullPointerException
* if the buffer is null
* #throws BufferOverflowException
* if the buffer doesn't have enough space for the telephone
* number
*/
private static void addTelephoneNumber(final ByteBuffer buffer,
final byte[] number) {
if (number == null || number.length != TELEPHONE_NUMBER_SIZE) {
throw new IllegalArgumentException("Expecting 10 digit number");
}
for (int i = 0; i < number.length; i += 2) {
final byte lowDigit = number[i];
validateUnpackedBCDDigit(lowDigit);
final byte highDigit = number[i + 1];
validateUnpackedBCDDigit(highDigit);
buffer.put((byte) ((highDigit << NIBBLE_SIZE) | lowDigit));
}
}
/**
* Tests if the given unpacked BCD digit is within range.
*
* #param b
* the byte to test
* #throws IllegalArgumentException
* if it isn't
*/
private static void validateUnpackedBCDDigit(final byte b) {
if (b < 0 || b > 9) {
throw new IllegalArgumentException(
"Telefonenumber isn't all bytes representing digits in BCD");
}
}
public static void main(final String... args) {
final byte[] number = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 5 };
final ByteBuffer buf = ByteBuffer.allocate(HEADER.length
+ TELEPHONE_NUMBER_SIZE);
addTelephoneNumberHeader(buf);
addTelephoneNumber(buf, number);
buf.flip();
while (buf.hasRemaining()) {
System.out.printf("%02X", buf.get());
}
System.out.println();
}
private OverlyDesignedTelephoneNumberHelper() {
// avoid instantiation
}
}
I have the following multi-byte represented in hex format:
0xdc, 0xd3
I can process the bytes in little endian () format to get the decimal value 54236:
List<Integer> packet = new LinkedList<Integer>(Arrays.asList(0xdc, 0xd3 ));
int idx=0;
int rpm = (int)readBytes(packet, idx, 2);
private long readBytes(List<Integer> packet, int idx, int size){
long val=0;
int element;
for(int i=0;i<size;i++, idx++){
element = packet.get(idx);
val |= element << (8 * i);
}
return val;
}
The above method produces the value as expected. However, now I want to get the value in the reverse order (big endian format), but it gives me some crazy value of 14471936:
private long bigEndianReadBytes(List<Integer> packet, int idx, int size){
long val=0;
int element;
for(int i=size;i>0;i--, idx++){
element = packet.get(idx);
val |= element << (8 * i);
}
return val;
}
What might be wrong with this method?
With #TimBiegeleisen answer (+1), you have good "manual" code to for converting bytes to values.
Have you considered using a ByteBuffer? It does all this for you and works with both Big Endian and Little Endian.
public static void main(String[] args) throws Exception {
ByteBuffer bb = ByteBuffer.allocate(2);
bb.put(new byte[] {(byte)0xdc, (byte)0xd3});
System.out.println(bb.order(ByteOrder.BIG_ENDIAN).getShort(0) & 0xFFFF);
System.out.println(bb.order(ByteOrder.LITTLE_ENDIAN).getShort(0) & 0xFFFF);
}
ANDing the result against 0xFFFF so the short result is promoted to an int, otherwise the result is a negative number because java works with signed data types.
Results:
56531
54236
The for loop in your bigEndianReadBytes() method should start by shifting size - 1 bytes, and end by shifting zero bytes (in the last iteration). Currently, you are shifting size bytes in the first iteration, and one byte in the last iteration. Try this code instead:
for (int i=size-1; i >= 0; i--, idx++) {
element = packet.get(idx);
val |= element << (8 * i);
}
I'm trying to generate sine wave and add it to byte array. I searched and found. However, I always get distorted waveform like an attachment.
Please give me your opinion why it happens. Thanks.
My code is here
private byte[] getData(int freq) { // taking pitch data
double pha = Math.PI/2; // defining phase
final int LENGTH = 44100 * 10; // defining length of sine wave, byte array
final byte[] arr = new byte[LENGTH];
for(int i = 0; i < arr.length; i++) {
double angle = (2.0 * Math.PI * i*freq+pha) / (44100);
arr[i] = (byte) (Math.cos(angle) *127* 0.3); // 0.3 is amplitude scale
}
return arr;
}
Distort waveform example pic
The code looks fine. I suspect it's the visualiser interpreting the two's complement signed values as unsigned (-1 becoming 255, -2 becoming 254 and so on).
I write to a wav file and plot it with SonicVisualiser
According to WAVE PCM soundfile format:
8-bit samples are stored as unsigned bytes, ranging from 0 to 255. 16-bit samples are stored as 2's-complement signed integers, ranging from -32768 to 32767.
It looks like you either need to shift your sine wave up by 128 (so that it fits fully within the 0-255 range), or move to using 16-bit samples.
You can use this code to convince yourself that what you generate is appropriate at the level of Java semantics:
public static void main(String[] args) {
for (byte b : getData(300)) System.out.println(sample(b));
}
static String sample(byte val) {
final int len = (val-Byte.MIN_VALUE)/2;
final StringBuilder b = new StringBuilder();
for (int i = 0; i < len; i++) b.append(i < len-1? ' ' : '#');
return b.toString();
}
It will print a nice vertical sine. Fix your code by producing unsigned bytes with this method:
static byte[] getData(int freq) {
double pha = Math.PI/2;
final int LENGTH = 44100 * 10;
final byte[] arr = new byte[LENGTH];
for(int i = 0; i < arr.length; i++) {
double angle = (2.0 * Math.PI * i*freq+pha) / (44100);
int unsignedSample = (int) (Math.cos(angle)*Byte.MAX_VALUE*0.3 - Byte.MIN_VALUE);
arr[i] = (byte) (unsignedSample & 0xFF);
}
return arr;
}
If you print this, you'll see the same waveform which you saw in SonicVisualizer, but in that tool it will look the way you intended to.
decrypt with 3des. I can get Base64 output correctly but i want to get output binary. How can i do?
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedText = cipher.doFinal(unencryptedString);
byte[] sdd = Base64.encode(encryptedText, Base64.DEFAULT);
A simple method that turns a byte array in to a String containing the binary value.
String bytesToBinaryString(byte[] bytes){
StringBuilder binaryString = new StringBuilder();
/* Iterate each byte in the byte array */
for(byte b : bytes){
/* Initialize mask to 1000000 */
int mask = 0x80;
/* Iterate over current byte, bit by bit.*/
for(int i = 0; i < 8; i++){
/* Apply mask to current byte with AND,
* if result is 0 then current bit is 0. Otherwise 1.*/
binaryString.append((mask & b) == 0 ? "0" : "1");
/* bit-wise right shift the mask 1 position. */
mask >>>= 1;
}
}
/* Return the resulting 'binary' String.*/
return binaryString.toString();
}
i have a java application that records audio from a mixer and store it on a byte array, or save it to a file.
What I need is to get audio from two mixers simultaneously, and save it to an audio file (i am trying with .wav).
The thing is that I can get the two byte arrays, but don't know how to merge them (by "merge" i don't mean concatenate).
To be specific, it is an application that handles conversations over an USB modem and I need to record them (the streams are the voices for each talking person, already maged to record them separately).
Any clue on how to do it?
Here is my code:
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.Path;
public class FileMixer {
Path path1 = Paths.get("/file1.wav");
Path path2 = Paths.get("/file2.wav");
byte[] byte1 = Files.readAllBytes(path1);
byte[] byte2 = Files.readAllBytes(path2);
byte[] out = new byte[byte1.length];
public FileMixer() {
byte[] byte1 = Files.readAllBytes(path1);
byte[] byte2 = Files.readAllBytes(path2);
for (int i=0; i<byte1.Length; i++)
out[i] = (byte1[i] + byte2[i]) >> 1;
}
}
Thanks in advance
To mix sound waves digitally, you add each corresponding data point from the two files together.
for (int i=0; i<source1.length; i++)
result[i] = (source1[i] + source2[i]) >> 1;
In other words, you take item 0 from byte array 1, and item 0 from byte array two, add them together, and put the resulting number in item 0 of your result array. Repeat for the remaining values. To prevent overload, you may need to divide each resulting value by two.
Make sure to merge amplitude data and not just byte data. If your SampleRate is 8: one byte equals one amplitude data. But if it is 16 you need to add two bytes to one short and merge them.
Currently your loading your file like this
byte[] byte1 = Files.readAllBytes(path1);
This will also load your .wav file header into the byte array but you only want to merge actual audio data. Load it like this:
public static ByteBuffer loadFile(File file) throws IOException {
DataInputStream in = new DataInputStream(new FileInputStream(file));
byte[] sound = new byte[in.available() - 44];
in.skipNBytes(44); // skip the header
in.read(sound);
return ByteBuffer.wrap(sound);
}
You can then merge every byte of these Buffers or every two bytes depending on your sample size. I will use 16 as its more common.
public static ByteBuffer mergeAudio(ByteBuffer smaller, ByteBuffer larger) {
// When we merge we will get problems with LittleEndian/BigEndian
// Actually the amplitude data is stored reverse in the .wav fille
// When we extract the amplitude value we need to reverse it to get the actuall
// value
// We can then add up all the amplitude data and divide it by their amount to
// get the mean
// When we save the value we need to reverse it again
// The result will have the size of the larger audio file. In my case its file2
ByteBuffer result = ByteBuffer.allocate(larger.capacity());
while (larger.hasRemaining()) {
// getShort() for SampleSize 16bit get() for 8 bit.
// Reverse the short because of LittleEndian/BigEndian
short sum = Short.reverseBytes(larger.getShort());
int matches = 1;
// check if the smaller file still has content so it needs to merge
if (smaller.hasRemaining()) {
// getShort() for SampleSize 16bit get() for 8 bit
// Reverse the short because of LittleEndian/BigEndian
sum += Short.reverseBytes(smaller.getShort());
matches++;
}
// append the mean of all merged values
// reverse again
result.putShort(Short.reverseBytes((short) (sum / (float) matches)));
}
return result;
}
We now need to create our own .wav file header and append our merged data. Finally we can write the changes to the disk.
public static void saveToFile(File file, byte[] audioData) throws IOException {
int audioSize = audioData.length;
int fileSize = audioSize + 44;
// The stream that writes the audio file to the disk
DataOutputStream out = new DataOutputStream(new FileOutputStream(file));
// Write Header
out.writeBytes("RIFF");// 0-4 ChunkId always RIFF
out.writeInt(Integer.reverseBytes(fileSize));// 5-8 ChunkSize always audio-length +header-length(44)
out.writeBytes("WAVE");// 9-12 Format always WAVE
out.writeBytes("fmt ");// 13-16 Subchunk1 ID always "fmt " with trailing whitespace
out.writeInt(Integer.reverseBytes(16)); // 17-20 Subchunk1 Size always 16
out.writeShort(Short.reverseBytes(audioFormat));// 21-22 Audio-Format 1 for PCM PulseAudio
out.writeShort(Short.reverseBytes(channels));// 23-24 Num-Channels 1 for mono, 2 for stereo
out.writeInt(Integer.reverseBytes(sampleRate));// 25-28 Sample-Rate
out.writeInt(Integer.reverseBytes(byteRate));// 29-32 Byte Rate
out.writeShort(Short.reverseBytes(blockAlign));// 33-34 Block Align
out.writeShort(Short.reverseBytes(sampleSize));// 35-36 Bits-Per-Sample
out.writeBytes("data");// 37-40 Subchunk2 ID always data
out.writeInt(Integer.reverseBytes(audioSize));// 41-44 Subchunk 2 Size audio-length
out.write(audioData);// append the merged data
out.close();// close the stream properly
}
Its important that the two files you want to merge have the same
Channels, SampleSize, SampleRate, AudioFormat
This is how you calculate the header data:
private static short audioFormat = 1;
private static int sampleRate = 44100;
private static short sampleSize = 16;
private static short channels = 2;
private static short blockAlign = (short) (sampleSize * channels / 8);
private static int byteRate = sampleRate * sampleSize * channels / 8;
Here is your working example where I put everything together:
import static java.lang.Math.ceil;
import static java.lang.Math.round;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
public class AudioMerger {
private short audioFormat = 1;
private int sampleRate = 44100;
private short sampleSize = 16;
private short channels = 2;
private short blockAlign = (short) (sampleSize * channels / 8);
private int byteRate = sampleRate * sampleSize * channels / 8;
private ByteBuffer audioBuffer;
private ArrayList<MergeSound> sounds = new ArrayList<MergeSound>();
private ArrayList<Integer> offsets = new ArrayList<Integer>();
public void addSound(double offsetInSeconds, MergeSound sound) {
if (sound.getAudioFormat() != audioFormat)
new RuntimeException("Incompatible AudioFormat");
if (sound.getSampleRate() != sampleRate)
new RuntimeException("Incompatible SampleRate");
if (sound.getSampleSize() != sampleSize)
new RuntimeException("Incompatible SampleSize");
if (sound.getChannels() != channels)
new RuntimeException("Incompatible amount of Channels");
int offset = secondsToByte(offsetInSeconds);
offset = offset % 2 == 0 ? offset : offset + 1;// ensure we start at short when merging
sounds.add(sound);
offsets.add(secondsToByte(offsetInSeconds));
}
public void merge(double durationInSeconds) {
audioBuffer = ByteBuffer.allocate(secondsToByte(durationInSeconds));
for (int i = 0; i < sounds.size(); i++) {
ByteBuffer buffer = sounds.get(i).getBuffer();
int offset1 = offsets.get(i);
// iterate over all sound data to append it
while (buffer.hasRemaining()) {
int position = offset1 + buffer.position();// the global position in audioBuffer
// add the audio data to the vars
short sum = Short.reverseBytes(buffer.getShort());
int matches = 1;
// make sure later entries dont override the previsously merged
// continue only if theres empty audio data
if (audioBuffer.getShort(position) == 0) {
// iterate over the other sounds and check if the need to be merged
for (int j = i + 1; j < sounds.size(); j++) {// set j to i+1 to avoid all previous
ByteBuffer mergeBuffer = sounds.get(j).getBuffer();
int mergeOffset = offsets.get(j);
// check if this soundfile contains data that has to be merged
if (position >= mergeOffset && position < mergeOffset + mergeBuffer.capacity()) {
sum += Short.reverseBytes(mergeBuffer.getShort(position - mergeOffset));
matches++;
}
}
// make sure to cast to float 3/1=1 BUT round(3/1f)=2 for example
audioBuffer.putShort(position, Short.reverseBytes((short) round(sum / (float) matches)));
}
}
buffer.rewind();// So the sound can be added again
}
}
private int secondsToByte(double seconds) {
return (int) ceil(seconds * byteRate);
}
public void saveToFile(File file) throws IOException {
byte[] audioData = audioBuffer.array();
int audioSize = audioData.length;
int fileSize = audioSize + 44;
// The stream that writes the audio file to the disk
DataOutputStream out = new DataOutputStream(new FileOutputStream(file));
// Write Header
out.writeBytes("RIFF");// 0-4 ChunkId always RIFF
out.writeInt(Integer.reverseBytes(fileSize));// 5-8 ChunkSize always audio-length +header-length(44)
out.writeBytes("WAVE");// 9-12 Format always WAVE
out.writeBytes("fmt ");// 13-16 Subchunk1 ID always "fmt " with trailing whitespace
out.writeInt(Integer.reverseBytes(16)); // 17-20 Subchunk1 Size always 16
out.writeShort(Short.reverseBytes(audioFormat));// 21-22 Audio-Format 1 for PCM PulseAudio
out.writeShort(Short.reverseBytes(channels));// 23-24 Num-Channels 1 for mono, 2 for stereo
out.writeInt(Integer.reverseBytes(sampleRate));// 25-28 Sample-Rate
out.writeInt(Integer.reverseBytes(byteRate));// 29-32 Byte Rate
out.writeShort(Short.reverseBytes(blockAlign));// 33-34 Block Align
out.writeShort(Short.reverseBytes(sampleSize));// 35-36 Bits-Per-Sample
out.writeBytes("data");// 37-40 Subchunk2 ID always data
out.writeInt(Integer.reverseBytes(audioSize));// 41-44 Subchunk 2 Size audio-length
out.write(audioData);// append the merged data
out.close();// close the stream properly
}
}