/*
* Copyright 1999-2004 Carnegie Mellon University.
* Portions Copyright 2002-2004 Sun Microsystems, Inc.
* Portions Copyright 2002-2004 Mitsubishi Electric Research Laboratories.
* All Rights Reserved. Use is subject to license terms.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
*
*/
package edu.cmu.sphinx.tools.audio;
import javax.sound.sampled.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/** Grabs audio from the microphone and returns an array of samples. */
public class RawRecorder {
final Object lock = new Object();
RecordThread recorder;
AudioFormat inFormat;
final AudioFormat outFormat;
TargetDataLine microphone;
boolean downsample;
/**
* Create a new RawRecorder.
*
* @param audioFormat the desired output
* @throws LineUnavailableException if the audioFormat is not supported
*/
public RawRecorder(AudioFormat audioFormat)
throws LineUnavailableException {
inFormat = audioFormat;
outFormat = audioFormat;
/* Some machines, such as my Mac OS X PowerBook, don't support
* a wide range of input formats. So...we may need to read
* data in using a different format and then resample to the
* desired format. Here, I'm just going to go for 44.1kHz
* 16-bit signed little endian data if the given audio format
* is not supported.
*/
DataLine.Info info = new DataLine.Info(TargetDataLine.class,
inFormat);
if (!AudioSystem.isLineSupported(info)) {
downsample = true;
inFormat = new AudioFormat(44100.0f, // sample rate
16, // sample size
1, // channels (1 == mono)
true, // signed
false); // little endian
info = new DataLine.Info(TargetDataLine.class,
inFormat);
if (!AudioSystem.isLineSupported(info)) {
throw new LineUnavailableException(
"Unsupported format: " + audioFormat);
}
}
microphone = (TargetDataLine) AudioSystem.getLine(info);
microphone.open(audioFormat, microphone.getBufferSize());
}
/**
* Start recording. The stop method will give us the clip.
*
* @see #stop
*/
public void start() {
synchronized (lock) {
if (recorder != null) {
recorder.stopRecording();
}
recorder = new RecordThread();
recorder.start();
}
}
/**
* Stop recording and give us the clip.
*
* @return the clip that was recorded since the last time start was called
* @see #start
*/
public short[] stop() {
synchronized (lock) {
if (recorder == null) {
return new short[0];
}
ByteArrayOutputStream out = recorder.stopRecording();
microphone.close();
recorder = null;
byte audioBytes[] = out.toByteArray();
ByteArrayInputStream in = new ByteArrayInputStream(audioBytes);
try {
short[] samples = RawReader.readAudioData(in, inFormat);
if (downsample) {
samples = Downsampler.downsample(
samples,
(int) (inFormat.getSampleRate() / 1000.0f),
(int) (outFormat.getSampleRate() / 1000.0f));
}
return samples;
} catch (IOException e) {
e.printStackTrace();
return new short[0];
}
}
}
class RecordThread extends Thread {
boolean done;
final Object lock = new Object();
ByteArrayOutputStream out;
public ByteArrayOutputStream stopRecording() {
try {
synchronized (lock) {
done = true;
lock.wait();
}
} catch (InterruptedException e) {
}
return out;
}
@Override
public void run() {
byte[] data = new byte[microphone.getBufferSize()];
out = new ByteArrayOutputStream();
try {
microphone.flush();
microphone.start();
while (!done) {
int numBytesRead = microphone.read(data, 0, data.length);
if (numBytesRead != -1) {
out.write(data, 0, numBytesRead);
} else {
break;
}
}
microphone.stop();
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
synchronized (lock) {
lock.notify();
}
}
}
}