How to capture sound in Java? - java

I'm trying to capture the sound of the PC. I have managed to capture the sound that enters the microphone through TargetDataLine, but I cannot find the way to capture the sound that comes out of the speakers.
I've been watching the mixer but I have not managed to capture the sound. I would like to know if someone has done it and if you can give me some clue as to where to start.

Although, your question is not really according to the "rules", here is a code snippet:
private byte[] record() throws LineUnavailableException {
AudioFormat format = AudioUtil.getAudioFormat(audioConf);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
// Checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
LOGGER.error("Line not supported");
System.exit(0);
}
microphone = (TargetDataLine) AudioSystem.getLine(info);
microphone.open(format);
microphone.start();
LOGGER.info("Listening, tap enter to stop ...");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int numBytesRead;
byte[] data = new byte[microphone.getBufferSize() / 5];
// Begin audio capture.
microphone.start();
// Here, stopped is a global boolean set by another thread.
while (!stopped) {
// Read the next chunk of data from the TargetDataLine.
numBytesRead = microphone.read(data, 0, data.length);
// Save this chunk of data.
byteArrayOutputStream.write(data, 0, numBytesRead);
}
return byteArrayOutputStream.toByteArray();
}
Get more info from here:
https://www.programcreek.com/java-api-examples/?class=javax.sound.sampled.TargetDataLine&method=read

Related

Echo when recording and playing sound

I have a simple application that starts recording from the microphone and then immediately plays the sounds back to my headphones, however the problem here is: the sound quality is not good and there is a lasting echo after speaking something in the microphone which can last up to 5 seconds after speaking. I'm not sure whether the problem lies in the Java Sound API and I need to switch to another library.
I also want to mention that I use a headphone that is plugged into my computer. When I use low volume then the sound plays normal however when I turn it up to 90% of my maximum value the sound quality really becomes bad.
Here is my code:
AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, false);
DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, format);
TargetDataLine mic = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
mic.open();
mic.start();
DataLine.Info dataLineInfo2 = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine speakers = (SourceDataLine) AudioSystem.getLine(dataLineInfo2);
speakers.open();
speakers.start();
byte[] buffer = new byte[1024];
while(true) {
mic.read(buffer, 0, buffer.length);
speakers.write(buffer, 0, buffer.length);
}

Detect Specific Frequency From Microphone Java

I'm trying to capture audio that is coming from microphone and i wanted to check the frequency of sound. If I get a frequency greater then let's say : 1316.8 then I will start recording for 1 minute.
I am struggling with converting byte Data to Frequency.
I have used Javax.sound to capture audio that is coming from microphone and I have done the recording part as well.
AudioFormat format = new AudioFormat(44100, 16, 2, true, true);
DataLine.Info targetInfo = new DataLine.Info(TargetDataLine.class, format);
DataLine.Info sourceInfo = new DataLine.Info(SourceDataLine.class, format);
try {
TargetDataLine targetLine = (TargetDataLine) AudioSystem.getLine(targetInfo);
targetLine.open(format);
targetLine.start();
SourceDataLine sourceLine = (SourceDataLine) AudioSystem.getLine(sourceInfo);
sourceLine.open(format);
sourceLine.start();
int numBytesRead;
byte[] targetData = new byte[targetLine.getBufferSize() / 5];
I expect the output to be like Frequency of every sound that is coming from microphone.

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

How to get Audio for encoding using Xuggler

I'm writing an application that records the screen and audio. While the screen recording works perfectly, I'm having difficulty in getting the raw audio using the JDK libraries. Here's the code:
try {
// Now, we're going to loop
long startTime = System.nanoTime();
System.out.println("Encoding Image.....");
while (!Thread.currentThread().isInterrupted()) {
// take the screen shot
BufferedImage screen = robot.createScreenCapture(screenBounds);
// convert to the right image type
BufferedImage bgrScreen = convertToType(screen,
BufferedImage.TYPE_3BYTE_BGR);
// encode the image
writer.encodeVideo(0, bgrScreen, System.nanoTime()
- startTime, TimeUnit.NANOSECONDS);
/* Need to get audio here and then encode using xuggler. Something like
WaveData wd = new WaveData();
TargetDataLine line;
AudioInputStream aus = new AudioInputStream(line);
short[] samples = getSourceSamples();
writer.encodeAudio(0, samples); */
if (timeCreation < 10) {
timeCreation = getGMTTime();
}
// sleep for framerate milliseconds
try {
Thread.sleep((long) (1000 / FRAME_RATE.getDouble()));
} catch (Exception ex) {
System.err.println("stopping....");
break;
}
}
// Finally we tell the writer to close and write the trailer if
// needed
} finally {
writer.close();
}
This page has some pseudo code like
while(haveMoreAudio())
{
short[] samples = getSourceSamples();
writer.encodeAudio(0, samples);
}
but what exactly should I do for getSourceSamples()?
Also, a bonus question - is it possible to choose from multiple microphones in this approach?
See also:
Xuggler encoding and muxing
Try this:
// Pick a format. Need 16 bits, the rest can be set to anything
// It is better to enumerate the formats that the system supports, because getLine() can error out with any particular format
AudioFormat audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);
// Get default TargetDataLine with that format
DataLine.Info dataLineInfo = new DataLine.Info( TargetDataLine.class, audioFormat );
TargetDataLine line = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
// Open and start capturing audio
line.open(audioFormat, line.getBufferSize());
line.start();
while (true) {
// read as raw bytes
byte[] audioBytes = new byte[ line.getBufferSize() / 2 ]; // best size?
int numBytesRead = 0;
numBytesRead = line.read(audioBytes, 0, audioBytes.length);
// convert to signed shorts representing samples
int numSamplesRead = numBytesRead / 2;
short[] audioSamples = new short[ numSamplesRead ];
if (format.isBigEndian()) {
for (int i = 0; i < numSamplesRead; i++) {
audioSamples[i] = (short)((audioBytes[2*i] << 8) | audioBytes[2*i + 1]);
}
}
else {
for (int i = 0; i < numSamplesRead; i++) {
audioSamples[i] = (short)((audioBytes[2*i + 1] << 8) | audioBytes[2*i]);
}
}
// use audioSamples in Xuggler etc
}
To pick a microphone, you'd probably have to do this:
Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo();
// Look through and select a mixer here, different mixers should be different inputs
int selectedMixerIndex = 0;
Mixer mixer = AudioSystem.getMixer(mixerInfo[ selectedMixerIndex ]);
TargetDataLine line = (TargetDataLine) mixer.getLine(dataLineInfo);
I think it's possible that multiple microphones will show up in one mixer as different source data lines. In that case you'd have to open them and call dataLine.getControl(FloatControl.Type.MASTER_GAIN).setValue( volume ); to turn them on and off.
See:
WaveData.java
Sound wave from TargetDataLine
How to set volume of a SourceDataLine in Java

Process microphone data in real time in java

I'm using this instructions for get a signal audio of a microphone:
while(!stopCapture){
int cnt = targetDataLine.read(tempBuffer, 0, tempBuffer.length);
if(cnt > 0){
byteArrayOutputStream.write(tempBuffer, 0, cnt);
}
}
byteArrayOutputStream.close();
byte audio[] = byteArrayOutputStream.toByteArray();
InputStream input = new ByteArrayInputStream(audio);
AudioInputStream ais = new AudioInputStream(input, audioFormat, audio.length / audioFormat.getFrameSize());
But these instructions wait the entire signal before elaborate it.
I need elaborate it in real time...is possible?
How can i do it?
thanks

Categories