JavaSound: Extract one channel of stereo audio - java

I have a stereo wave file that I need to read and play back only the selected channel. What is the best way to accomplish this?

When you bring the wav file in via an AudioInputStream, use the AudioFileFormat info to convert the bytes to PCM. The data for the right and left alternates. So, if the line is 16-bit, you will have 4 bytes per frame. The first two will be assembled into the left channel and the second two will be assembled into the right channel. (Or vice versa--I have trouble keeping straight in my mind which channel is left or right.)
Here's a good tutorial with example on how to read a line:
http://docs.oracle.com/javase/tutorial/sound/converters.html
Some of the earlier tutorials in the trail might be needed to help clarify. Also, if you have questions about converting bytes to PCM and back, there are several explanations already on StackOverflow to reference. Should not be too hard to find them.

Here is a simple method to extract single channels from a multi channel direct audio line (JavaSound). In tried it with my Line6(r) Helix(r) guitar sound effect board (8 channels) and it works pretty fine. I guess it works with any kind of DataTargetLine. In this case we process data based on an AudioFormat bearing 16 bits samples. Hope it helps.
public ArrayList<byte[]> extract16BitsSingleChannels(byte[] audioBuffer, int channels) {
/* Parameters :
*
* audioBuffer : the buffer that has just been produced by
* your targetDataLine.read();
* channels : the number of channels defined in the AudioFormat you
* use with the line
*
* the AudioFormat which I tested :
* float sampleRate = 44100;
* int sampleSizeInBits = 16;
* int channels = 8;
* boolean signed = true;
* boolean bigEndian = true;
*/
/* let's create a container which will receive our "per channel" buffers */
ArrayList<byte[]> channelsData = new ArrayList<byte[]>();
/* take care of adjusting the size of the audioBuffer so that
* audioBuffer % channels == 0 is true ... because :
*/
final int channelLength=audioBuffer.length/channels;
/* let's create one buffer per channel and place them in the
* container
*/
for (int c=0 ; c < channels ; c++)
{
byte[] channel=new byte[channelLength];
channelsData.add(channel);
}
/* then process bytes from audioBuffer and copy each channels byte
* in its dedicated buffer
*/
int byteIndex=0;
for(int i = 0; i < channelLength; i+=2) //i+=2 for 16 bits=2 Bytes samples
{
for (int c=0 ; c < channels ; c++) {
channelsData.get(c)[i]=audioBuffer[byteIndex]; // 1st Byte
byteIndex++;
channelsData.get(c)[i+1]=audioBuffer[byteIndex]; // 2nd Byte
byteIndex++;
}
}
/* Returns each set of bytes from each channel in its buffer you can use to
write on whatever Byte streamer you like. */
return channelsData;
}

Related

Strcture data communication between Java and C socket programming

I have a Java socket channel and I'm sending a object data and receiving it in C socket ..
Java Code::
//structure
class data
{
public String jobtype;
public String budget;
public String time ;
}
//creating a Socket Channel and sending data through it in java
Selector incomingMessageSelector = Selector.open();
SocketChannel sChannel = SocketChannel.open();
sChannel.configureBlocking(false);
sChannel.connect(new InetSocketAddress("localhost", 5000));
sChannel.register(incomingMessageSelector, SelectionKey.OP_CONNECT);
if(sChannel.finishConnect()==true)
{
sChannel.register(incomingMessageSelector, SelectionKey.OP_WRITE);
}
int len = 256;
ByteBuffer buf = ByteBuffer.allocate(len);
buf.putInt(len);
// Writing object of data in socket
buf.put(obj.jobtype.getBytes("US-ASCII"));
buf.put(obj.budget.getBytes("US-ASCII"));
buf.put(obj.time.getBytes("US-ASCII"));
buf.put((byte) 0);
buf.flip();
sChannel.write(buf);
C Code ::
struct data
{
char time[50];
char jobtype[50];
char budget[50];
};
n = read(newsockfd, &size, sizeof(size));
struct data *result = malloc(size);
n = read(newsockfd, result, size);
printf("\njobtype :: %s\nbudget :: %s\ntime :: %s\n",result->jobtype,result->budget,result->time);
After giving input in Java as:
jobtype = h1
budget = 20
time = 12
I'm getting these output in C:
jobtype ::
budget ::
time :: h1
The buffer which you are sending from Java to C needs to have exactly the same definition (from a byte point of view) in both languages. In your code that is not the case. The buffer you construct in Java does not have the same format as the struct you are using in C to interpret that buffer. Both the length of the strings and order of the strings do not match between sender (Java) and receiver (C). In addition, the size of the buffer sent does not match the size of the buffer expected based on the length information sent (i.e. you are not sending the correct length of your buffer).
In C you have defined a structure that is 150 bytes long containing 3 char arrays (strings), each 50 bytes long. With the order: time, jobtype, budget
In Java you have created a buffer of variable length with strings of variable length in the order: jobtype, budget, time. Fundamentally, the Java code is creating a variable length buffer where the C code is expecting to map this to a fixed length structure.
While it is not what you desire, your C program is obtaining the jobtype string which you placed first in the buffer and assigns it to time. This is how it is currently written.
Assuming that you leave the C program the same, the portion of your Java code which creates and fills the buffer could look something like:
public ByteBuffer createFixedLengthCString(String src, int len) {
//If the string is longer than len-1 it is truncated.
ByteBuffer cString = ByteBuffer.allocate(len);
if(src.length() > len - 1) {
//Using len-1 prevents the last 0 in the ByteBuffer from being
// overwritten. A final 0 is needed:C uses null (0) terminated strings.
cString.put(src.getBytes("US-ASCII"), 0, len-1);
} else {
//The string is not longer than the maximum length.
cString.put(src.getBytes("US-ASCII"));
}
//Already have null termination. Do not want to flip (would change length).
//Reset the position to 0.
cString.position(0);
return cString;
}
int maxBufLen = 256;
int payloadLen = 150
int cStringLen = 50;
ByteBuffer buf = ByteBuffer.allocate(maxBufLen);
//Tell C that the payload is 150 bytes long.
buf.putInt(payloadLen);
// Writing object data in the buffer
buf.put(createFixedLengthCString(obj.time, cStringLen));
buf.put(createFixedLengthCString(obj.jobtype, cStringLen));
buf.put(createFixedLengthCString(obj.budget, cStringLen));
//Use flip() here as it changes the length of bytes sent to the correct
// number (an int plus 150) and sets the position to 0, ready for reading.
buf.flip();
while(buf.hasRemaining()) {
//There is the possibility that a single call to write() will not
// write the entire buffer. Thus, loop until all data is written.
//There should be other conditions which cause us to break out of
// this loop (e.g. a maximum number of write attempts). Without such,
// if the channel is hung this is code will hang in this loop; effectively
// a blocking (for this code) write loop.
sChannel.write(buf);
}
This answer is only intended to address the specific malfunction you have identified in the question. However, the code as presented is really only appropriate as an example/test of transmitting limited data from one process to another on the same machine. Even for that there should be exception and error handling which is not included here.
As EJP implied in his comment, it is often better/easier to use already existing protocols when communicating over a bit pipe. These protocols are designed to address many different issues which can become relevant, even in simple inter-process communications.

How to determine length of OGG file

I'm making a rhythm game and I need a quick way to get the length of an ogg file. The only way I could think would be to stream the file really fast without playing it but if I have hundreds of songs this would obviously not be practical. Another way would be to store the length of the file in some sort of properties file but I would like to avoid this. I know there must be some way to do this as most music players can tell you the length of a song.
The quickest way to do it is to seek to the end of the file, then back up to the last Ogg page header you find and read its granulePosition (which is the total number of samples per channel in the file). That's not foolproof (you might be looking at a chained file, in which case you're only getting the last stream's length), but should work for the vast majority of Ogg files out there.
If you need help with reading the Ogg page header, you can read the Jorbis source code... The short version is to look for "OggS", read a byte (should be 0), read a byte (only bit 3 should be set), then read a 64-bit little endian value.
I implemented the solution described by ioctlLR and it seems to work:
double calculateDuration(final File oggFile) throws IOException {
int rate = -1;
int length = -1;
int size = (int) oggFile.length();
byte[] t = new byte[size];
FileInputStream stream = new FileInputStream(oggFile);
stream.read(t);
for (int i = size-1-8-2-4; i>=0 && length<0; i--) { //4 bytes for "OggS", 2 unused bytes, 8 bytes for length
// Looking for length (value after last "OggS")
if (
t[i]==(byte)'O'
&& t[i+1]==(byte)'g'
&& t[i+2]==(byte)'g'
&& t[i+3]==(byte)'S'
) {
byte[] byteArray = new byte[]{t[i+6],t[i+7],t[i+8],t[i+9],t[i+10],t[i+11],t[i+12],t[i+13]};
ByteBuffer bb = ByteBuffer.wrap(byteArray);
bb.order(ByteOrder.LITTLE_ENDIAN);
length = bb.getInt(0);
}
}
for (int i = 0; i<size-8-2-4 && rate<0; i++) {
// Looking for rate (first value after "vorbis")
if (
t[i]==(byte)'v'
&& t[i+1]==(byte)'o'
&& t[i+2]==(byte)'r'
&& t[i+3]==(byte)'b'
&& t[i+4]==(byte)'i'
&& t[i+5]==(byte)'s'
) {
byte[] byteArray = new byte[]{t[i+11],t[i+12],t[i+13],t[i+14]};
ByteBuffer bb = ByteBuffer.wrap(byteArray);
bb.order(ByteOrder.LITTLE_ENDIAN);
rate = bb.getInt(0);
}
}
stream.close();
double duration = (double) (length*1000) / (double) rate;
return duration;
}
Beware, finding the rate this way will work only for vorbis OGG!
Feel free to edit my answer, it may not be perfect.

What is the format of the data you can write to a SourceDataLine?

In java you can create a SourceDataLine like so:
AudioFormat af = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100.0, 16, 1, 2, 44100.0, false);
SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
After which you can open and then write data to it:
byte[] data = new byte[1024];
fillwithsounds(data);
sdl.open()
sdl.start()
sdl.write(data, 0, 1024);
This all works fine for mono data.
What I'd like to do is to be able to write stereo data, and I can't find any documentation online on how I need to change my byte array to be able to write stereo data.
It seems like I need to increase the amount of channels when I create the AudioFormat - to make it stereo - and then I need to half the framerate (otherwise Java throws an IllegalArgumentException)
I don't understand why this is though, or what the new format should be for the data that I feed to the DataSourceLine.
Perhaps somebody with a little more experience in audio formats than I could shed some light on this problem. Thanks in advance!
The format I use for stereo is as follows:
audioFmt = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
44100, 16, 2, 4, 44100, false);
You probably want to double the bytes per frame instead of halving your bits-encoding. I'm not sure what 8-bit encoding sounds like, but it is definitely going to be noisier than 16-bit encoding!
The resulting file is twice as long. You can then take the two-byte pairs that make the 16-bit sample and copy them into the next two positions, for "mono" playback (both stereo channels identical).
Given:
frame = F
little end byte = A
big end byte = B
AB = 16-bit little-endian encoding
left channel = L
right channel = R
Your original mono:
F1A, F1B, F2A, F2B, F3A, F3B ...
Stereo using the above format:
F1AL, F1BL, F1AR, F1BR, F2AL, F2BL, F2AR, F2BR, F3AL, F3BL, F3AR, F3BR ...
I could very well have the order of left and right mixed up. But I hope you get the idea!
I found out the solution just now, and found Andrew Thompson's comment to explain exactly what I needed.
I figured that I'd have to write each frame twice, what caught me up was the fact that Java wouldn't let me just use the frame size I had for my mono channel. (It threw an IllegalArgumentException)
So I halved the framerate to satisfy Java, but I didn't remember to modify the byte array.
I've implemented code that takes the "2 bytes per frame, 1 channel" byte[] and converts it into a "1 byte per frame, 2 channel" byte[].
private static final float LEFT = -1;
private static final float CENTER = 0;
private static final float RIGHT = 1;
private byte[] monoToStereo(byte[] data, float pan){
byte[] output = new byte[data.length];
for (int i = 0; i < (data.length - 2); i+=2){
int currentvalue = (data[i+1]*256 + data[i])/(256*2);
if (pan == LEFT || pan == CENTER){
output[i] = (byte) currentvalue;
} else {
output[i] = 0;
}
if (pan == RIGHT || pan == CENTER){
output[i+1] = (byte) currentvalue;
} else {
output[i+1] = 0;
}
}
return output;
}
Using this, I can get stereo audio to playback (although there is soft static, I can clearly hear the original track)

Mixing and Adding Silence to Audio Android/Java

I have 2 files. Once is an mp3 being decoded to pcm into a stream and I have a wav being read into pcm also. The samples are being held in a short data type.
Audio stats: 44,100 samples * 16 bits per sample * 2 channels = 1,411,200 bits/sec
I have X seconds of silence that I need to apply to the beginning of the mp3 pcm data and I am doing it like this:
private short[] mp3Buffer = null;
private short[] wavBuffer = null;
private short[] mixedBuffer = null;
double silenceSamples = (audioInfo.rate * padding) * 2;
for (int i = 0; i < minBufferSize; i++){
if (silenceSamples > 0 ){
mp3Buffer[i] = 0; //Add 0 to the buffer as silence
mixedBuffer[i] = (short)((mp3Buffer[i] + stereoWavBuffer[i])/2);
silenceSamples = silenceSamples - 0.5;
}
else
mixedBuffer[i] = (short)((mp3Buffer[i] + stereoWavBuffer[i])/2);
}
The audio is always off. Sometimes its a second or two too fast, sometimes its a second or two too slow too slow. I dont think its a problem with the timing as I start the audiorecord(wav) first and then set a start timer->start mediaplayer(already prepared)->end timer and setting the difference to the "padding" variable. I am also skipping the 44kb when from the wav header.
Any help would be much appreciated.
I'm assuming you are wanting to align two sources of audio in some way by inserting padding at the start of one of the streams? There are a few things wrong here.
mp3Buffer[i] = 0; //Add 0 to the buffer as silence
This is not adding silence to the beginning, is is just setting the entry at offest [i] in the array to 0. The next line:
mixedBuffer[i] = (short)((mp3Buffer[i] + stereoWavBuffer[i])/2);
Then just overwrites this value.
If you are wanting to align the streams in some way, the best way to go about it is not to insert silence at the beginning of either stream, but to just begin mixing in one of the streams at an offset from the other. Also it would be better to mix them into a 32 bit float and then normalise. Something like:
int silenceSamples = (audioInfo.rate * padding) * 2;
float[] mixedBuffer = new float[minBufferSize + silenceSamples]
for (int i = 0; i < minBufferSize + silenceSamples; i++){
if (i < silenceSamples )
{
mixedBuffer[i] = (float) stereoWavBuffer[i];
}
else if(i < minBufferSize)
{
mixedBuffer[i] = (float) (stereoWavBuffer[i] + mp3Buffer[i-silenceSamples]);
}
else
{
mixedBuffer[i] = (float) (mp3Buffer[i-silenceSamples]);
}
To normalise the data you need to run through the mixedBuffer and find the absolute largest value Math.abs(...), and then multiple all the values in the array by 32,767/largestValue - this will give you a buffer where the largest value fits back into a short without clipping. Then iterate through your float array moving each value back into a short array.
I'm not sure what your minBufferSize is - this will need to be large enough to get all your data mixed.

How do I peek at the first two bytes in an InputStream?

Should be pretty simple: I have an InputStream where I want to peek at (not read) the first two bytes, i.e. I want the "current position" of the InputStream to stil be at 0 after my peeking. What is the best and safest way to do this?
Answer - As I had suspected, the solution was to wrap it in a BufferedInputStream which offers markability. Thanks Rasmus.
For a general InputStream, I would wrap it in a BufferedInputStream and do something like this:
BufferedInputStream bis = new BufferedInputStream(inputStream);
bis.mark(2);
int byte1 = bis.read();
int byte2 = bis.read();
bis.reset();
// note: you must continue using the BufferedInputStream instead of the inputStream
You might find PushbackInputStream to be useful:
http://docs.oracle.com/javase/6/docs/api/java/io/PushbackInputStream.html
When using a BufferedInputStream make sure that the inputStream is not already buffered, double buffering will cause some seriously hard to find bugs.
Also you need to handle Readers differently, converting to a StreamReader and Buffering will cause bytes to be lost if the Reader is Buffered.
Also if you are using a Reader remember that you are not reading bytes but characters in the default encoding (unless an explicit encoding was set).
An example of a buffered input stream, that you may not know is URL url; url.openStream();
I do not have any references for this information, it comes from debugging code.
The main case where the issue occurred for me was in code that read from a file into a compressed stream.
If I remember correctly once you start debugging through the code there are comments in the Java source that certain things do not work correctly always.
I do not remember where the information from using BufferedReader and BufferedInputStream
comes from but I think that fails straight away on even the simplest test.
Remember to test this you need to be marking more than the buffer size (which is different for BufferedReader versus BufferedInputStream), the problems occur when the bytes being read reach the end of the buffer.
Note there is a source code buffer size which can be different to the buffer size you set in the constructor.
It is a while since I did this so my recollections of details may be a little off.
Testing was done using a FilterReader/FilterInputStream, add one to the direct stream and one to the buffered stream to see the difference.
I found an implementation of a PeekableInputStream here:
http://www.heatonresearch.com/articles/147/page2.html
The idea of the implementation shown in the article is that it keeps an array of "peeked" values internally. When you call read, the values are returned first from the peeked array, then from the input stream. When you call peek, the values are read and stored in the "peeked" array.
As the license of the sample code is LGPL, It can be attached to this post:
package com.heatonresearch.httprecipes.html;
import java.io.*;
/**
* The Heaton Research Spider Copyright 2007 by Heaton
* Research, Inc.
*
* HTTP Programming Recipes for Java ISBN: 0-9773206-6-9
* http://www.heatonresearch.com/articles/series/16/
*
* PeekableInputStream: This is a special input stream that
* allows the program to peek one or more characters ahead
* in the file.
*
* This class is released under the:
* GNU Lesser General Public License (LGPL)
* http://www.gnu.org/copyleft/lesser.html
*
* #author Jeff Heaton
* #version 1.1
*/
public class PeekableInputStream extends InputStream
{
/**
* The underlying stream.
*/
private InputStream stream;
/**
* Bytes that have been peeked at.
*/
private byte peekBytes[];
/**
* How many bytes have been peeked at.
*/
private int peekLength;
/**
* The constructor accepts an InputStream to setup the
* object.
*
* #param is
* The InputStream to parse.
*/
public PeekableInputStream(InputStream is)
{
this.stream = is;
this.peekBytes = new byte[10];
this.peekLength = 0;
}
/**
* Peek at the next character from the stream.
*
* #return The next character.
* #throws IOException
* If an I/O exception occurs.
*/
public int peek() throws IOException
{
return peek(0);
}
/**
* Peek at a specified depth.
*
* #param depth
* The depth to check.
* #return The character peeked at.
* #throws IOException
* If an I/O exception occurs.
*/
public int peek(int depth) throws IOException
{
// does the size of the peek buffer need to be extended?
if (this.peekBytes.length <= depth)
{
byte temp[] = new byte[depth + 10];
for (int i = 0; i < this.peekBytes.length; i++)
{
temp[i] = this.peekBytes[i];
}
this.peekBytes = temp;
}
// does more data need to be read?
if (depth >= this.peekLength)
{
int offset = this.peekLength;
int length = (depth - this.peekLength) + 1;
int lengthRead = this.stream.read(this.peekBytes, offset, length);
if (lengthRead == -1)
{
return -1;
}
this.peekLength = depth + 1;
}
return this.peekBytes[depth];
}
/*
* Read a single byte from the stream. #throws IOException
* If an I/O exception occurs. #return The character that
* was read from the stream.
*/
#Override
public int read() throws IOException
{
if (this.peekLength == 0)
{
return this.stream.read();
}
int result = this.peekBytes[0];
this.peekLength--;
for (int i = 0; i < this.peekLength; i++)
{
this.peekBytes[i] = this.peekBytes[i + 1];
}
return result;
}
}

Categories