/** * * @author greg (at) myrobotlab.org * * This file is part of MyRobotLab (http://myrobotlab.org). * * MyRobotLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version (subject to the "Classpath" exception * as provided in the LICENSE.txt file that accompanied this code). * * MyRobotLab is distributed in the hope that it will be useful or fun, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * All libraries in thirdParty bundle are subject to their own license * requirements - please refer to http://myrobotlab.org/libraries for * details. * * Enjoy ! * * The working part of this was eventually traced back to: * http://www.developer.com/java/other/article.php/1565671/Java-Sound-An-Introduction.htm * And I would like to give all well deserved credit to * Richard G. Baldwin's excellent and comprehensive tutorial regarding the many * details of sound and Java * * */ package org.myrobotlab.service; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.TargetDataLine; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.ServiceType; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.myrobotlab.logging.LoggingFactory; import org.slf4j.Logger; /** * AudioCapture - a service that can record and playback from a microphone. * */ public class AudioCapture extends Service { class CaptureThread extends Thread { // An arbitrary-size temporary holding // buffer byte tempBuffer[] = new byte[10000]; @Override public void run() { byteArrayOutputStream = new ByteArrayOutputStream(); stopCapture = false; try {// Loop until stopCapture is set // by another thread that // services the Stop button. while (!stopCapture) { // Read data from the internal // buffer of the data line. int cnt = targetDataLine.read(tempBuffer, 0, tempBuffer.length); if (cnt > 0) { // Save data in output stream // object. byteArrayOutputStream.write(tempBuffer, 0, cnt); } // end if } // end while targetDataLine.close(); byteArrayOutputStream.close(); } catch (Exception e) { error(e); } // end catch }// end run }// end inner class CaptureThread public class PlayThread extends Thread { byte tempBuffer[] = new byte[10000]; @Override public void run() { try { int cnt; // Keep looping until the input // read method returns -1 for // empty stream. while ((cnt = audioInputStream.read(tempBuffer, 0, tempBuffer.length)) != -1) { if (cnt > 0) { // Write data to the internal // buffer of the data line // where it will be delivered // to the speaker. sourceDataLine.write(tempBuffer, 0, cnt); } // end if } // end while // Block and wait for internal // buffer of the data line to // empty. sourceDataLine.drain(); sourceDataLine.close(); } catch (Exception e) { error(e); } // end catch }// end run }// end inner class PlayThread public final static Logger log = LoggerFactory.getLogger(AudioCapture.class.getCanonicalName()); private static final long serialVersionUID = 1L; boolean stopCapture = false; ByteArrayOutputStream byteArrayOutputStream; AudioFormat audioFormat; TargetDataLine targetDataLine; AudioInputStream audioInputStream; SourceDataLine sourceDataLine; // ===================================// public static void main(String[] args) throws InterruptedException { LoggingFactory.init(Level.DEBUG); try { AudioCapture audioIn = (AudioCapture) Runtime.start("audioIn", "AudioCapture"); Runtime.start("gui", "GUIService"); audioIn.captureAudio(); Thread.sleep(3000); audioIn.stopAudioCapture(); audioIn.playAudio(); audioIn.save("me5.wav"); audioIn.captureAudio(); Thread.sleep(3000); audioIn.stopAudioCapture(); audioIn.playAudio(); audioIn.save("me1.wav"); } catch (Exception e) { Logging.logError(e); } /* * AudioInputStream stream = AudioSystem.getAudioInputStream(new File( * "bump.wav")); // stream = AudioSystem.getAudioInputStream(new URL( // * "http://hostname/audiofile")); * * AudioFormat format = stream.getFormat(); if (format.getEncoding() != * AudioFormat.Encoding.PCM_SIGNED) { format = new * AudioFormat(AudioFormat.Encoding.PCM_SIGNED, format .getSampleRate(), * format.getSampleSizeInBits() * 2, format .getChannels(), * format.getFrameSize() * 2, format.getFrameRate(), true); // big endian * stream = AudioSystem.getAudioInputStream(format, stream); } * * SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class, stream * .getFormat(), ((int) stream.getFrameLength() * format.getFrameSize())); * SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info); * line.open(stream.getFormat()); line.start(); * * int numRead = 0; byte[] buf = new byte[line.getBufferSize()]; while * ((numRead = stream.read(buf, 0, buf.length)) >= 0) { int offset = 0; * while (offset < numRead) { offset += line.write(buf, offset, numRead - * offset); } } line.drain(); line.stop(); } */ } public AudioCapture(String n) { super(n); } public void captureAudio() { try { // Get everything set up for // capture audioFormat = getAudioFormat(); DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat); targetDataLine = (TargetDataLine) AudioSystem.getLine(dataLineInfo); targetDataLine.open(audioFormat); targetDataLine.start(); // Create a thread to capture the // microphone data and start it // running. It will run until // the Stop button is clicked. Thread captureThread = new Thread(new CaptureThread()); captureThread.start(); } catch (Exception e) { Logging.logError(e); } // end catch }// end captureAudio method // This method creates and returns an // AudioFormat object for a given set // of format parameters. If these // parameters don't work well for // you, try some of the other // allowable parameter values, which // are shown in comments following // the declarations. private AudioFormat getAudioFormat() { float sampleRate = 16000.0F; // 8000,11025,16000,22050,44100 int sampleSizeInBits = 16; // 8,16 int channels = 1; // 1,2 boolean signed = true; // true,false boolean bigEndian = false; // true,false return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian); }// end getAudioFormat // This method plays back the audio // data that has been saved in the // ByteArrayOutputStream public void playAudio() { try { // Get everything set up for // playback. // Get the previously-saved data // into a byte array object. byte audioData[] = byteArrayOutputStream.toByteArray(); // Get an input stream on the // byte array containing the data InputStream byteArrayInputStream = new ByteArrayInputStream(audioData); AudioFormat audioFormat = getAudioFormat(); audioInputStream = new AudioInputStream(byteArrayInputStream, audioFormat, audioData.length / audioFormat.getFrameSize()); DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat); sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo); sourceDataLine.open(audioFormat); sourceDataLine.start(); // Create a thread to play back // the data and start it // running. It will run until // all the data has been played // back. Thread playThread = new Thread(new PlayThread()); playThread.start(); } catch (Exception e) { error(e); } // end catch }// end playAudio public ByteArrayOutputStream publishCapture() { return byteArrayOutputStream; } public void save(String filename) throws IOException { File file = new File(filename); AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, file); } public void stopAudioCapture() { stopCapture = true; } /** * This static method returns all the details of the class without it having * to be constructed. It has description, categories, dependencies, and peer * definitions. * * @return ServiceType - returns all the data * */ static public ServiceType getMetaData() { ServiceType meta = new ServiceType(AudioCapture.class.getCanonicalName()); meta.addDescription("captures and stores audio from microphone"); meta.addCategory("sound"); return meta; } }