Saving short[] in java (android) with low cpu and space - java

while streaming music i'm getting pcm data as type short[] and i want to save it to file in my android device so i can play it again later (using AudioTrack). i wan't the store of the music to be efficent in memory and cpu.
how to save short[] to file cause i dont see any function in.write(short[])?
how can i decrease the space\cpu for saving this file?

Wrap your FileOutputStream with DataOutputStream:
DataOutputStream doStream = new DataOutputStream(new BufferedOutputStream(fileOutputStream));
doStream.writeInt(numberArray.length); //Save size
for (int i=0;i<numberArray.length;i++) {
doStream.writeShort(numberArray[i]); //Save each number
}
Same way for reading it back:
DataInputStream diStream = new DataInputStream(new BufferedInputStream(fileInputStream));
int size = diStream.readInt(); //Read size
short[] data = new short[size]; //Create new array with required length
for (int i=0;i<size;i++) {
data[i] = diStream.readShort(); //Read each number
}

Without any encoding to MP3 or similar you can always do like this.
short[] sound = ...;
ByteBuffer byteMyShorts = ByteBuffer.allocate(sound.length * 2);
ShortBuffer shortBytes = byteMyShorts.asShortBuffer();
shortBytes.put(sound);
byteMyShorts.flip();
// byteMyShorts.array() now contains your short[] array as an
// array of bytes.

Related

Best way to convert RGB byte[][] image to byte[]

Have frame data in the form of a byte[][] object, where each row corresponds to a (R,G,B) channel and is of length (frame width*frame height). I wish to convert it to a byte[] format in a similar vein as follows:
byte[][] original_frame;
byte[] converted_frame = convert(original_frame);
ByteArrayInputStream bis = new ByteArrayInputStream(frame);
BufferedImage bImage2 = ImageIO.read(bis);
From what I can tell, ImageIO assumes a jpeg format. Do I need to convert every frame to a JPEG image, or is there a more natural way to do this?
I believe that you are asking how to convert from a 2D array to 1D array. How to do this if all rows are length 3 is as follows:
ArrayList<byte> x = new ArrayList<byte>();
for(int i=0; i<original_frame.length; i++){
x.add(original_frame[i][0]);
x.add(original_frame[i][1]);
x.add(original_frame[i][2]);
}
byte[] converted_frame= new byte[x.size()];
for(int i=0;i<x.size();i++){
converted_frame[i]=x.get(i);
}
I hope this helps

How to create a TargetDataLine using a binary array WebSocket?

I've created a byte array WebSocket that receives audio chunks in real time from the client's mic (navigator.getUserMedia). I'm already recording this stream to a WAV file in the server, after some time that the WebSocket stops to receive new byte arrays. The following code represents the current situation.
WebSocket
#OnMessage
public void message(byte[] b) throws IOException{
if(byteOutputStream == null) {
byteOutputStream = new ByteArrayOutputStream();
byteOutputStream.write(b);
} else {
byteOutputStream.write(b);
}
}
Thread that stores the WAV file
public void store(){
byte b[] = byteOutputStream.toByteArray();
try {
AudioFormat audioFormat = new AudioFormat(44100, 16, 1, true, true);
ByteArrayInputStream byteStream = new ByteArrayInputStream(b);
AudioInputStream audioStream = new AudioInputStream(byteStream, audioFormat, b.length);
DateTime date = new DateTime();
File file = new File("/tmp/"+date.getMillis()+ ".wav");
AudioSystem.write(audioStream, AudioFileFormat.Type.WAVE, file);
audioStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
But instead of record a WAV file, my goal with this WebSocket is to process audio in real time using YIN pitch detection algorithm implemented on TarsosDSP library. In other words, this is basically execute the PitchDetectorExample, but using the data from the WebSocket instead of the Default Audio Device (OS mic). The following code represents how PitchDetectorExample is currently initializing live audio processing using the mic line provided by the OS.
private void setNewMixer(Mixer mixer) throws LineUnavailableException, UnsupportedAudioFileException {
if(dispatcher!= null){
dispatcher.stop();
}
currentMixer = mixer;
float sampleRate = 44100;
int bufferSize = 1024;
int overlap = 0;
final AudioFormat format = new AudioFormat(sampleRate, 16, 1, true, true);
final DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, format);
TargetDataLine line;
line = (TargetDataLine) mixer.getLine(dataLineInfo);
final int numberOfSamples = bufferSize;
line.open(format, numberOfSamples);
line.start();
final AudioInputStream stream = new AudioInputStream(line);
JVMAudioInputStream audioStream = new JVMAudioInputStream(stream);
// create a new dispatcher
dispatcher = new AudioDispatcher(audioStream, bufferSize, overlap);
// add a processor
dispatcher.addAudioProcessor(new PitchProcessor(algo, sampleRate, bufferSize, this));
new Thread(dispatcher,"Audio dispatching").start();
}
There is a way to deal with WebSocket data as a TargetDataLine, so it will be possible to hook it up with AudioDispatcher and PitchProcessor? Somehow, i need to send the byte arrays received from the WebSocket to the audio processing Thread.
Another ideas on how reach this objective are welcome. Thanks!
I'm not sure you need an audioDispatcher. If you know how the bytes are encoded (PCM, 16bits le mono?) then you can convert them to floating points real-time and feed them to the pitchdetector algorithm, in your websocket you can do something like this (and forget about the inputstreams and audiodispatcher):
int index;
byte[] buffer = new byte[2048];
float[] floatBuffer = new float[1024];
FastYin detector = new FastYin(44100,1024);
public void message(byte[] b){
for(int i = 0 ; i < b.length; i++){
buffer[index] = b[i];
index++
if(index==2048){
AudioFloatConverter converter = AudioFloatConverter.getConverter(new Format(16bits, little endian, mono,...));
//converts the byte buffer to float
converter.toFloatArray(buffer,floatBuffer);
float pitch = detector.getPitch(floatBuffer);
//here you have your pitch info that you can use
index = 0;
}
}
You do need to watch the number of bytes that have passed: since two bytes represent one float (if 16bits pcm encoding is used) you need to start on even bytes. The endianness and samplerate are also important.
Regards
Joren

Optimization of rewriting from AudioInputStream to ByteArrayOutputStream

There were few similar topics on stackoverflow but none of them seemed good enough for me. The problem is i have such a snippet of code:
// AudioInputStream in;
final int BUFFER_SIZE = 8192;
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
byte[] byteBuffer = new byte[BUFFER_SIZE];
int bytesRead = 0;
while ((bytesRead = in.read(byteBuffer, 0, BUFFER_SIZE)) != -1) {
byteStream.write(byteBuffer, 0, bytesRead);
}
byte[] audioData = byteStream.toByteArray();
This code reads data of mp3 file and saves it to byte array, but takes about 10 seconds. Is there any possibility to store whole mp3 data into buffer in shorter time?
I've tried to avoid passing the size of buffer or extend it but that caused additional problems (e.g. file was not read correctly).
Thanks in advance
You are copying data 3 times, you can avoid that by first checking the file size, allocating a byte array big enough, and reading into that directly.
It also helps a little bit to pre-allocate space in the ByteArrayOutputStream so it doesn't have to be increased in size many times.
If you can't get the file size up front, you can allocate a new byte[] for every block, and put them in a list, until you got them all. Then allocate the final array and copy all of them into the last array. IF you need a byte[] in the end. If you don't, there may be more efficient ways to proceed.

Convert ByteArrayOutputStream to int values

I am constantly trying to convert a ByteArrayOutputStream to int values.
I am recording an Audio with microphone and writing it to out = new ByteArrayOutputStream() like so:
out.write(buffer, 0, count);
byte audio[] = out.toByteArray();
When I print this I get these : [B#3456337e
How do I convert these to integer numbers.
Please Help, Thanks
There is no standard way to do it because actually it depends on what kind of bytes you have but, as it is an audio source, I think you can do it like that :
IntBuffer intBuf =
ByteBuffer.wrap(byteArray)
.order(ByteOrder.BIG_ENDIAN) //or try ByteOrder.LITTLE_ENDIAN
.asIntBuffer();
int[] array = new int[intBuf.remaining()];
intBuf.get(array);
//The result you want is "array"
I hope it will help you.
Convert it to an array, wrap the array in a ByteArrayInputStream, wrap that in a DataInputStream, and use readInt().
Try the following -
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataInputStream dataIs = new DataInputStream
(new ByteArrayInputStream(out.toByteArray());
// available stream to be read
while(dataIs.available()>0)
{
int k = dataIs.readInt();
// print int
System.out.print(k+" ");
}

Java spectrogram images: mp3 and microphone

First of all i'm working on a little project to see the spectrum from some sounds.
I got this working with a microphone:
alt text http://img25.imageshack.us/img25/4271/spectrumanalyzerfourier.png
The image above is just me talking and shouting through a microphone for a few seconds. This looks good to me.
But when I try to read an MP3 file and make a spectogram image of it it looks a bit different. I tried the Aphex Twin - Windowlicker where you should normally see a face in the spectrogram image or at least some more darker colors. But it doesn't look so good:
alt text http://img10.imageshack.us/img10/3475/aphextwinhmm.png
Here is what I did with the microphone:
byte tempBuffer[] = new byte[10000];
ByteArrayOutputStream out = new ByteArrayOutputStream();
counter = 20;
// Microphone
while (counter != 0) {
int count = line.read(tempBuffer, 0, tempBuffer.length);
if (count > 0) {
out.write(tempBuffer, 0, count);
}
counter--;
}
out.close();
// FFT code below ...
byte audio[] = out.toByteArray();
// ...
And this is how I do it with the MP3:
I used the same code to do the transformation and visualization only the audio capturing part is different (I only adjusted the hight in the drawing method to see if there is a difference but there wasn't one):
byte tempBuffer[] = new byte[10000];
ByteArrayOutputStream out = new ByteArrayOutputStream();
FileInputStream input = null;
File mp3 = new File("Aphex Twin - Widowlicker.mp3");
input = new FileInputStream(mp3);
int len;
while((len = input.read(tempBuffer)) > 0) {
out.write(tempBuffer, 0, len);
}
out.close();
input.close();
// FFT code below ...
byte audio[] = out.toByteArray();
// ...
It would be nice if somebody could point me out what I am doing wrong with the MP3 file.
These are my settings:
Sample rate: 44100
Bit per sample: 8
Channels: 1 (mono)
signed: true
big endian: true (i'm using AudioFormat in Java)
tempBuffer to read audio: 10000 ( byte tempBuffer[] = new byte[10000]; )
and for the FFT I split the audio in chuncks of 4096 (must be a power of 2)
By the way: are these settings ok or should I use 16bps or stereo or is 10000 for the buffer too much or 4096 to small/big ?
Thanks in advance
MP3 is a compressed audio format. You should first decompress the data before you can use it as an audio stream comparable to the data from your microphone. The raw MP3 data has maximum entropy and should look much like white noise, which it does in you spectrogram.

Categories