/* * Copyright (C) 2004 Keith Stribley <tech@thanlwinsoft.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package org.thanlwinsoft.languagetest.sound; import java.util.Vector; import java.util.Iterator; import java.io.File; import java.io.OutputStream; import java.io.InputStream; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.FileNotFoundException; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.TargetDataLine; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; //import org.tritonus.sampled.convert.javalayer.MpegFormatConversionProvider; import org.tritonus.share.sampled.AudioFileTypes; /** * * @author keith */ public class Recorder implements Runnable { private final int READ_BUFFER_SIZE = 176400; // one second at 44100Hz private final int INPUT_BUFFER_SIZE = 1764000; // 10 seconds private File outputFile = null; private OutputStream outputStream = null; private TargetDataLine targetLine = null; private AudioInputStream inputStream = null; private AudioFormat rawFormat = null; private File targetFile = null; private AudioFormat.Encoding targetEncoding = null; private AudioFileFormat.Type targetFileFormat = null; private boolean hasFinished = false; private boolean stop = false; private int dumpLevel = 0; private long lengthInFrames = -1; private long lengthInMs = -1; private boolean initialised = false; private boolean recordError = false; private StringBuffer errorDescription = null; private LineController lineController = null; private Vector<AudioPlayListener> listeners = null; private AudioFileFormat.Type vorbisFileFormat = null; /** Creates a new instance of Recorder */ public Recorder(LineController lineController) { this.lineController = lineController; errorDescription = new StringBuffer(); rawFormat = lineController.getRecLineFormat(); listeners = new Vector<AudioPlayListener>(); } public synchronized boolean isFinished() { return hasFinished; } public synchronized boolean isInitialised() { return initialised; } public synchronized boolean isRecordError() { return recordError; } public synchronized long getRecordingMsLength() { return lengthInMs; } public synchronized String getErrorDescription() { return errorDescription.toString(); } public AudioFileFormat.Type getVorbisFileFormat() { return vorbisFileFormat; } public boolean initialise(File targetFile) { boolean formatOk = false; synchronized (this) { // return false if already initialised if (initialised) { recordException("Recorder not finished previous session"); return false; } hasFinished = false; stop = false; } // intialise raw format if it isn't already if (rawFormat == null) rawFormat = lineController.getRecLineFormat(); // check format is still not null otherwise we can't proceed if (rawFormat == null) { errorDescription.delete(0, errorDescription.length()); errorDescription.append(lineController.getError()); return false; } targetEncoding = encodingFromExtension(targetFile); targetFileFormat = fileFormatFromExtension(targetFile); if (targetEncoding != null && targetFileFormat != null) { formatOk = true; } // else if (targetFile.getName().toLowerCase().endsWith(".ogg")) // { // targetEncoding = org.tritonus.share.sampled.Encodings.getEncoding("VORBIS") // targetFileFormat = AudioFileFormat.getType("VORBIS","ogg"); // formatOk = true; // } if (formatOk) { if (!AudioSystem.isConversionSupported(targetEncoding, rawFormat)) { formatOk = false; } else if (!AudioSystem.isFileTypeSupported(targetFileFormat)) { formatOk = false; } } if (formatOk) { this.targetFile = targetFile; new Thread(this).start(); // reset error index recordError = false; errorDescription.delete(0, errorDescription.length()); } return formatOk; } public static AudioFormat.Encoding encodingFromExtension(File file) { AudioFormat.Encoding encoding = null; if (file.getName().toLowerCase().endsWith(".mp3")) { // encoding = MpegFormatConversionProvider.MPEG1L3; } else if (file.getName().toLowerCase().endsWith(".wav")) { encoding = AudioFormat.Encoding.PCM_SIGNED; } else if (file.getName().toLowerCase().endsWith(".au")) { encoding = AudioFormat.Encoding.PCM_SIGNED; } return encoding; } public static AudioFileFormat.Type fileFormatFromExtension(File file) { AudioFileFormat.Type fileFormat = null; if (file.getName().toLowerCase().endsWith(".mp3")) { fileFormat = AudioFileTypes.getType("MP3", "mp3"); } else if (file.getName().toLowerCase().endsWith(".wav")) { fileFormat = AudioFileFormat.Type.WAVE; } else if (file.getName().toLowerCase().endsWith(".au")) { fileFormat = AudioFileFormat.Type.AU; } return fileFormat; } protected synchronized void recordException(String message) { recordError = true; errorDescription.append(message); } protected void reinit(File outFile) throws LineUnavailableException, IllegalArgumentException, SecurityException, FileNotFoundException { this.outputFile = outFile; if (targetLine == null) { if (!lineController.linesAreAvailable()) lineController.openLines(); if (lineController.linesAreAvailable()) { rawFormat = lineController.getRecLineFormat(); targetLine = lineController.getTargetDataLine(); } else { errorDescription.append("Unable to get line for recording"); } } if (targetLine != null) { targetLine.open(rawFormat, INPUT_BUFFER_SIZE); outputStream = new BufferedOutputStream(new FileOutputStream(outFile)); inputStream = new AudioInputStream(targetLine); synchronized (this) { initialised = true; } } } public void addPlayListener(AudioPlayListener listener) { listeners.add(listener); } public void run() { try { File tempAudioFile = File.createTempFile("LangTestRec",".raw"); tempAudioFile.deleteOnExit(); reinit(tempAudioFile); int totalWritten = 0; int read = 0; // check that initalisation was successful if (!initialised) { read = -1; System.out.println("Recorder initialisation failed"); } else System.out.println("Line open"); while (read > -1) { byte [] readBuffer = new byte[READ_BUFFER_SIZE]; int targetRead = inputStream.available(); if (targetRead > readBuffer.length) { targetRead = readBuffer.length; } if (targetRead > 0) { read = inputStream.read(readBuffer, 0, targetRead); } else { read = 0; } if (read > 0) { outputStream.write(readBuffer, 0, read); totalWritten += read; playPosition(totalWritten); } else { try { Thread.sleep(10); } catch (InterruptedException e) { } } synchronized (this) { if (stop) break; } // wrote = AudioSystem.write(inputStream, fileFormat, outputStream); // System.out.println("Wrote " + wrote + " bytes " // + inputStream.getFrameLength() + " frames"); } lengthInFrames = -1; if (inputStream != null && inputStream.getFormat() != null) { lengthInFrames = totalWritten / inputStream.getFormat().getFrameSize(); lengthInMs = (long)(((float)lengthInFrames * 1000) / inputStream.getFormat().getFrameRate()); } System.out.println("Recorded " + lengthInFrames + " frames"); if (targetLine != null) { targetLine.stop(); targetLine.flush(); targetLine.close(); } if (inputStream != null) inputStream.close(); if (outputStream != null) outputStream.close(); convert(targetFile, targetEncoding, targetFileFormat); inputStream = null; outputStream = null; targetFileFormat = null; } catch (LineUnavailableException e) { System.out.println(e.getMessage()); recordException(e.getLocalizedMessage()); } catch (SecurityException e) { System.out.println(e.getMessage()); recordException(e.getLocalizedMessage()); } catch (FileNotFoundException e) { System.out.println(e.getMessage()); recordException(e.getLocalizedMessage()); } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); recordException(e.getLocalizedMessage()); } catch (IOException e) { System.out.println(e.getMessage()); recordException(e.getLocalizedMessage()); } synchronized (this) { hasFinished = true; initialised = false; } } public synchronized void stop() { if (targetLine != null) { targetLine.stop(); System.out.println("stop"); } } public synchronized void start() { if (targetLine != null) { targetLine.start(); System.out.println("start"); } } public synchronized void finish() { stop = true; } public void close() { if (targetLine != null) { synchronized (this) { if (!hasFinished) { // if line still running close it, but wait for it to // finish stop = true; targetLine.close(); } else { targetLine.close(); targetLine = null; } } } } protected boolean convert(File newFile, AudioFormat.Encoding encoding, AudioFileFormat.Type newFileFormat) { boolean success = false; try { InputStream iStream = new BufferedInputStream(new FileInputStream(outputFile)); AudioInputStream ieStream = null; AudioInputStream ilStream = new AudioInputStream(iStream, rawFormat, lengthInFrames); // AudioFormat.Encoding [] encodings = // AudioSystem.getTargetEncodings(ilStream.getFormat()); // for (int i = 0; i<encodings.length; i++) // System.out.println(encodings[i].toString()); if (ilStream.getFormat().getEncoding() != encoding) { ieStream = AudioSystem.getAudioInputStream(encoding, ilStream); } else { ieStream = ilStream; } // // check supported formats // AudioFileFormat.Type [] supportedFormats = // AudioSystem.getAudioFileTypes(ieStream); // for (int i = 0; i<supportedFormats.length; i++) // { // System.out.println(supportedFormats[i].toString()); // } OutputStream oStream = new NullStripOutputStream(new FileOutputStream(newFile)); int wrote = AudioSystem.write(ieStream, newFileFormat, oStream); iStream.close(); ilStream.close(); ieStream.close(); oStream.close(); success = true; System.out.println("Converted " + wrote + " bytes " + ieStream.getFrameLength() + " frames"); outputFile.delete(); outputFile = null; } // catch (UnsupportedAudioFileException e) // { // System.out.println(e.getMessage()); // } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); recordException(e.getLocalizedMessage()); dumpAudioFileFormats(); System.out.println(encoding); System.out.println(rawFormat); System.out.println(newFileFormat); } catch (IOException e) { System.out.println(e.getMessage()); recordException(e.getLocalizedMessage()); } return success; } public boolean convert(File wavFile, File newFile, AudioFormat.Encoding encoding, AudioFileFormat.Type newFileFormat) { boolean success = false; try { AudioInputStream ieStream = null; AudioInputStream ilStream = AudioSystem.getAudioInputStream(wavFile); AudioFormat.Encoding [] encodings = AudioSystem.getTargetEncodings(ilStream.getFormat()); for (int i = 0; i<encodings.length; i++) System.out.println(encodings[i].toString()); dumpAudioFileFormats(ilStream); if (!ilStream.getFormat().getEncoding().equals(encoding)) { ieStream = AudioSystem.getAudioInputStream(encoding, ilStream); // check supported formats dumpAudioFileFormats(ieStream); } else { ieStream = ilStream; } OutputStream oStream = new NullStripOutputStream(new FileOutputStream(newFile)); int wrote = AudioSystem.write(ieStream, newFileFormat, oStream); ilStream.close(); ieStream.close(); oStream.close(); success = true; System.out.println("Converted " + wrote + " bytes " + ieStream.getFrameLength() + " frames"); } catch (UnsupportedAudioFileException e) { System.out.println(e.getMessage()); recordException(e.getLocalizedMessage()); } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); recordException(e.getLocalizedMessage()); } catch (IOException e) { System.out.println(e.getMessage()); recordException(e.getLocalizedMessage()); } return success; } public void dumpAudioFileFormats() { AudioFileFormat.Type [] supportedFormats = AudioSystem.getAudioFileTypes(); for (int i = 0; i<supportedFormats.length; i++) { if (supportedFormats[i].toString().equals("Vorbis")) { vorbisFileFormat = supportedFormats[i]; } System.out.println("FileFormat:" + supportedFormats[i].toString()); } } public void dumpAudioFileFormats(AudioInputStream stream) { AudioFileFormat.Type [] supportedFormats = AudioSystem.getAudioFileTypes(stream); for (int i = 0; i<supportedFormats.length; i++) { System.out.println("FileFormat:" + supportedFormats[i].toString()); } } public void dumpAudioEncodings(AudioFormat sourceFormat) { AudioFormat.Encoding [] supportedFormats = AudioSystem.getTargetEncodings(sourceFormat); for (int i = 0; i<supportedFormats.length; i++) { System.out.println("TargetEncoding:" + supportedFormats[i].toString() + " for " + sourceFormat.toString()); dumpAudioFormats(supportedFormats[i], sourceFormat); } } public void dumpAudioFormats(AudioFormat.Encoding encoding, AudioFormat sourceFormat) { String spaces = " "; dumpLevel++; AudioFormat [] supportedFormats = AudioSystem.getTargetFormats(encoding, sourceFormat); for (int i = 0; i<supportedFormats.length; i++) { System.out.println(spaces.substring(0,dumpLevel) + " TargetFormat:" + supportedFormats[i].toString()); if (dumpLevel < 2) { dumpAudioEncodings(supportedFormats[i]); } } dumpLevel--; } /** * @param args the command line arguments */ public static void main(String args[]) { LineController lineController = new LineController(LineController.REC_MODE); lineController.run(); if (!lineController.openLines()) { System.out.println("Failed to open lines"); System.exit(2); } Recorder recorder = new Recorder(lineController); recorder.dumpAudioFileFormats(); while (!recorder.initialise(new File("sample.wav"))) { try { Thread.sleep(100); } catch (InterruptedException e) { } } while (!recorder.isInitialised() && !recorder.isRecordError()) { try { Thread.sleep(100); } catch (InterruptedException e) { } } if (recorder.isRecordError()) System.exit(1); recorder.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { } recorder.stop(); recorder.finish(); while (!recorder.isFinished()) { try { Thread.sleep(100); } catch (InterruptedException e) { } } //recorder.convert(new File("sample.wav"), new File("sample.mp3"), // MpegFormatConversionProvider.MPEG1L3, // AudioFileTypes.getType("MP3", "mp3")); // recorder.convert(new File("sample.wav"), new File("sample.ogg"), // AudioFormat.Encoding.PCM_SIGNED, // recorder.getVorbisFileFormat()); // recorder.convertA2B(new File("sample.mp3"), // MpegFormatConversionProvider.MPEG1L3, // AudioFileTypes.getType("MP3", "mp3")); // recorder.convert(new File("sample.ogg"), // org.tritonus.share.sampled.Encodings.getEncoding("VORBIS"), // AudioSystem.getAudioFileTypes()[4]); recorder = null; lineController.closeLines(); System.out.println("Recorder finished"); System.exit(0); } /** Method calls the playPosition method on each registered * AudioPlayListener. */ public synchronized void playPosition(int bytes) { long position = (long)(1000.0 * (double)bytes / (double)(rawFormat.getFrameSize() * rawFormat.getFrameRate())); Iterator<AudioPlayListener> l = listeners.iterator(); while (l.hasNext()) { ((AudioPlayListener)l.next()) .playPosition(position, -1); } } }