package edu.cmu.sphinx.frontend.util;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import javax.sound.sampled.*;
import edu.cmu.sphinx.util.ReferenceSource;
/**
* Concatenates a list of audio files as one continuous audio stream.
* <p>
* A {@link edu.cmu.sphinx.frontend.DataStartSignal DataStartSignal} will be
* placed before the start of the first file, and a
* {@link edu.cmu.sphinx.frontend.DataEndSignal DataEndSignal} after the last
* file. No DataStartSignal or DataEndSignal will be placed between them.
*
* @author Holger Brandl
*/
public class ConcatAudioFileDataSource extends AudioFileDataSource implements
ReferenceSource {
private URL nextFile;
private List<String> referenceList;
private boolean isInitialized;
List<URL> batchFiles;
public ConcatAudioFileDataSource(int bytesPerRead,
List<AudioFileProcessListener> listeners) {
super(bytesPerRead, listeners);
}
public ConcatAudioFileDataSource() {
}
/** Initializes a ConcatFileDataSource. */
@Override
public void initialize() {
super.initialize();
if (batchFiles == null)
return;
try {
referenceList = new ArrayList<String>();
dataStream = new SequenceInputStream(new InputStreamEnumeration(batchFiles));
} catch (IOException e) {
e.printStackTrace();
}
}
public void setBatchFile(File file) {
setBatchUrls(readDriver(file.getAbsolutePath()));
}
public void setBatchFiles(List<File> files) {
List<URL> urls = new ArrayList<URL>();
try {
for (File file : files)
urls.add(file.toURI().toURL());
} catch (MalformedURLException e) {
e.printStackTrace();
}
setBatchUrls(urls);
}
public void setBatchUrls(List<URL> urls) {
batchFiles = new ArrayList<URL>(urls);
initialize();
}
/**
* Reads and verifies a driver file.
*
* @param fileName
*/
private static List<URL> readDriver(String fileName) {
File inputFile = new File(fileName);
List<URL> driverFiles = null;
try {
BufferedReader bf = new BufferedReader(new FileReader(inputFile));
driverFiles = new ArrayList<URL>();
String line;
while ((line = bf.readLine()) != null && line.trim().length() != 0) {
File file = new File(line);
driverFiles.add(file.toURI().toURL());
}
bf.close();
} catch (IOException e) {
e.printStackTrace();
}
assert driverFiles != null;
return driverFiles;
}
@Override
public void setAudioFile(URL audioFileURL, String streamName) {
throw new UnsupportedOperationException();
}
/**
* Returns a list of all reference text. Implements the getReferences()
* method of ReferenceSource.
*
* @return a list of all reference text
*/
public List<String> getReferences() {
return referenceList;
}
/**
* The work of the concatenating of the audio files are done here. The idea
* here is to turn the list of audio files into an Enumeration, and then
* fed it to a SequenceInputStream, giving the illusion that the audio
* files are concatenated, but only logically.
*/
class InputStreamEnumeration implements Enumeration<AudioInputStream> {
private URL lastFile;
final Iterator<URL> fileIt;
InputStreamEnumeration(List<URL> files) throws IOException {
fileIt = new ArrayList<URL>(files).iterator();
}
/**
* Tests if this enumeration contains more elements.
*
* @return true if and only if this enumeration object contains at
* least one more element to provide; false otherwise.
*/
public boolean hasMoreElements() {
if (nextFile == null) {
nextFile = readNext();
}
return (nextFile != null);
}
/**
* Returns the next element of this enumeration if this enumeration
* object has at least one more element to provide.
*
* @return the next element of this enumeration.
*/
public AudioInputStream nextElement() {
AudioInputStream stream = null;
if (lastFile == null) {
nextFile = readNext();
}
if (nextFile != null) {
try {
AudioInputStream ais = AudioSystem
.getAudioInputStream(nextFile);
// test whether all files in the stream have the same
// format
AudioFormat format = ais.getFormat();
if (!isInitialized) {
isInitialized = true;
bigEndian = format.isBigEndian();
sampleRate = (int) format.getSampleRate();
signedData = format.getEncoding()
.equals(AudioFormat.Encoding.PCM_SIGNED);
bytesPerValue = format.getSampleSizeInBits() / 8;
}
if (format.getSampleRate() != sampleRate
|| format.getChannels() != 1
|| format.isBigEndian() != bigEndian) {
throw new RuntimeException("format mismatch for subsequent files");
}
stream = ais;
logger.finer("Strating processing of '"
+ lastFile.getFile() + '\'');
for (AudioFileProcessListener fl : fileListeners)
fl.audioFileProcStarted(new File(nextFile.getFile()));
lastFile = nextFile;
nextFile = null;
} catch (IOException ioe) {
ioe.printStackTrace();
throw new Error("Cannot convert " + nextFile
+ " to a FileInputStream");
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
}
}
return stream;
}
/**
* Returns the name of next audio file
*
* @return the name of the appropriate audio file
*/
public URL readNext() {
if (lastFile != null) {
logger.finest("Finished processing of '" + lastFile.getFile()
+ '\'');
for (AudioFileProcessListener fl : fileListeners)
fl.audioFileProcFinished(new File(lastFile.getFile()));
lastFile = null;
}
if (fileIt.hasNext())
lastFile = fileIt.next();
return lastFile;
}
}
}