package com.tlabs.speechalyzer;
import java.io.File;
import java.io.PrintStream;
import java.io.StringReader;
import java.net.URI;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import org.apache.log4j.Logger;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import com.felix.util.FileUtil;
import com.felix.util.KeyValues;
import com.felix.util.StringUtil;
import com.felix.util.Util;
import com.felix.util.logging.Log4JLogger;
import com.felix.util.logging.LoggerInterface;
import com.tlabs.speechalyzer.classifier.ClassificationResult;
import com.tlabs.speechalyzer.emotions.Emotion;
import com.tlabs.speechalyzer.emotions.EmotionMLManager;
import com.tlabs.speechalyzer.emotions.VocabManager;
public class AudioFileManager {
private Vector<RecFile> _audioFiles;
private KeyValues _config;
private LoggerInterface _logger;
private String _fileList;
private String _audioExtension = "";
private int _sampleRate = 0;
private RecFile _lastRec = null;
private File _recordingDir;
private VocabManager _vocabManager;
private EmotionMLManager _emoMlManager;
public AudioFileManager(String fileList, KeyValues config, File recordingDir) {
_config = config;
_fileList = fileList;
_recordingDir = recordingDir;
_logger = new Log4JLogger(
Logger.getLogger("com.tlabs.speechalyzer.AudioFileManager"));
_audioExtension = _config.getString("audioFormat");
_sampleRate = _config.getInt("sampleRate");
_emoMlManager = new EmotionMLManager(this, _config, _logger);
reload();
}
public AudioFileManager(String fileList, KeyValues config) {
_config = config;
_fileList = fileList;
_recordingDir = _config.getFileHandler("recordingDir");
_logger = new Log4JLogger(
Logger.getLogger("com.tlabs.speechalyzer.AudioFileManager"));
_audioExtension = _config.getString("audioFormat");
_sampleRate = _config.getInt("sampleRate");
_emoMlManager = new EmotionMLManager(this, _config, _logger);
reload();
}
public String get_audioExtension() {
return _audioExtension;
}
public void setAudioExtension(String audioExtension) {
_audioExtension = audioExtension;
}
public void setSampleRate(int sampleRate) {
_sampleRate = sampleRate;
}
public KeyValues getConfig() {
return _config;
}
public void reload(String file) {
_recordingDir = null;
_fileList = file;
reload();
}
public LoggerInterface getLogger() {
return _logger;
}
public void reloadDir(String file) {
_recordingDir = new File(file);
_fileList = null;
reload();
}
public void initAudioFiles() {
_audioFiles = new Vector<RecFile>();
}
public void addAudioFile(RecFile newFile) {
_audioFiles.add(newFile);
}
public void printEmotionML(PrintStream out) {
try {
Document doc = new Document();
Element root = Emotion.getRoot();
root.setAttribute(new Attribute("version", "1.0"));
root.setAttribute(Emotion.EMOTIONML_ATT_CATEGORYSET,
_config.getString("emotionml-category-set-default"));
root.setAttribute(Emotion.EMOTIONML_ATT_DIMENSIONSET,
_config.getString("emotionml-dimension-set-default"));
root.setAttribute(Emotion.EMOTIONML_ATT_APPRAISALSET,
_config.getString("emotionml-appraisal-set-default"));
root.setAttribute(Emotion.EMOTIONML_ATT_ACTIONTENDENCYSET,
_config.getString("emotionml-action-tendency-set-default"));
for (RecFile rec : _audioFiles) {
if (rec.hasLabel()) {
root.addContent(rec.getEmotionMLElement());
}
}
doc.addContent(root);
XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
outputter.output(doc, out);
} catch (Exception e) {
e.printStackTrace();
}
}
public void importTranscriptions(String file) {
_recordingDir = null;
_fileList = file;
_audioFiles = new Vector<RecFile>();
if (_fileList.length() > 0) {
_logger.info("loading audio files with transcriptions from "
+ _fileList + "...");
try {
int index = 0;
Vector<String> tmp = FileUtil.getFileLines(_fileList,
_config.getString("charEnc"));
for (Iterator<String> iterator = tmp.iterator(); iterator
.hasNext();) {
String line = iterator.next();
_logger.debug("loading line: " + line);
if (!FileUtil.isCommentOrEmpty(line)) {
StringTokenizer st = new StringTokenizer(line);
String path = st.nextToken();
if (path.endsWith("." + _audioExtension)) {
if (!isAudioFileContained(path)) {
_logger.warn("AFM: file, line " + line
+ ", doesnt exist, creating it.");
String transcription = StringUtil
.getRestOfLine(st);
RecFile recFile = new RecFile(path, _config);
recFile.storeTranscript(transcription);
_logger.debug("adding file " + path
+ " with transcript: " + transcription);
_audioFiles.add(recFile);
}
}
// indicate progress while loading
if (index % 10 == 0) {
System.err.print(".");
}
index++;
}
}
} catch (Exception e) {
e.printStackTrace();
_logger.error("invalid filelist: " + e.getMessage());
System.exit(1);
}
}
}
public void parseEmotionMLFromFile(String filePath) {
try {
_recordingDir = null;
_fileList = FileUtil.getFileText(filePath);
_logger.info("loading audio files with transcriptions from EmotionML document "
+ _fileList + "...");
_audioFiles = new Vector<RecFile>();
SAXBuilder parser = new SAXBuilder();
Document doc = parser.build(filePath);
Element root = doc.getRootElement();
_emoMlManager.loadEmotionMLElement(root);
} catch (Exception e) {
e.printStackTrace();
}
}
public void parseEmotionML(String contents) {
try {
_recordingDir = null;
_fileList = contents;
_logger.info("loading audio files with transcriptions from EmotionML document "
+ _fileList + "...");
_audioFiles = new Vector<RecFile>();
SAXBuilder parser = new SAXBuilder();
Document doc = parser.build(new StringReader(contents));
Element root = doc.getRootElement();
_emoMlManager.loadEmotionMLElement(root);
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean fileListIsEmotionML() {
if (_fileList != null) {
if (_fileList.startsWith("<"))
return true;
}
return false;
}
public void reload() {
if (fileListIsEmotionML()) {
parseEmotionML(_fileList);
return;
}
try {
_audioFiles = new Vector<RecFile>();
if (_fileList != null && _fileList.length() > 0) {
_logger.info("loading audio files from " + _fileList + "...");
try {
int index = 0;
Vector<String> tmp = FileUtil.getFileLines(_fileList,
_config.getString("charEnc"));
for (String line : tmp) {
_logger.debug("loading line: " + line);
if (!FileUtil.isCommentOrEmpty(line)) {
StringTokenizer st = new StringTokenizer(line);
String path = st.nextToken();
if (path.endsWith("." + _audioExtension)) {
if (!isAudioFileContained(path)) {
if (!FileUtil.existFile(path)) {
_logger.warn("AFM: file, line "
+ line
+ ", doesnt exist, creating it.");
String transcription = StringUtil
.getRestOfLine(st);
RecFile recFile = new RecFile(path,
_config);
if (transcription.length() > 0) {
recFile.storeTranscript(transcription);
_logger.debug("adding file " + path
+ " with transcript: "
+ transcription);
} else {
_logger.debug("adding file " + path
+ " without transcript.");
}
_audioFiles.add(recFile);
} else {
RecFile recFile = new RecFile(path,
_config);
String txtS = path.substring(0,
path.length() - 4)
+ ".txt";
if (FileUtil.existFile(txtS)) {
_logger.debug("adding already existing file (ignoring transcript) "
+ path);
} else {
String transcription = StringUtil
.getRestOfLine(st);
if (transcription.length() > 0) {
recFile.storeTranscript(transcription);
_logger.debug("adding file "
+ path
+ " with transcript: "
+ transcription);
} else {
_logger.debug("adding file "
+ path
+ " without transcript.");
}
}
_audioFiles.add(recFile);
}
}
}
// indicate progress while loading
if (index % 10 == 0) {
System.err.print(".");
}
index++;
}
}
} catch (Exception e) {
e.printStackTrace();
_logger.error("invalid filelist: " + e.getMessage());
System.exit(1);
}
}
if (_recordingDir != null
&& FileUtil.existPath(_recordingDir.getPath())) {
_logger.info("loading audio files from " + _recordingDir
+ "...");
File files[] = _recordingDir.listFiles();
if (files != null) {
for (int j = 0; j < files.length; j++) {
File file = files[j];
if (file.getName().endsWith("." + _audioExtension)) {
String filepath = file.getPath().replace("\\", "/");
_audioFiles.add(new RecFile(filepath, _config));
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
_logger.info("AFM: added " + getStats());
}
public String getStats() {
int fileNum = _audioFiles.size();
long dataSize = 0;
int dataSec = 0;
double dataMin = 0;
double dataHour = 0;
double meanDataSec = 0;
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
dataSize += recFile._size;
if (_audioExtension.compareTo("wav") == 0) {
dataSize -= 44;
}
}
dataSec = (int) (dataSize / 2) / _sampleRate;
dataMin = Util.cutDouble(dataSec / 60.0);
dataHour = Util.cutDouble(dataSec / 3600.0);
meanDataSec = Util.cutDouble((double) dataSec / fileNum);
return "#files: " + fileNum + ", " + dataSize + " bytes (" + dataSize
/ 1000 + " kb, " + dataSize / 1000000 + " mb), " + "secs: "
+ dataSec + ", mins: " + dataMin + ", hours: " + dataHour
+ ", avr sec: " + meanDataSec + "\n";
}
public void addSympaResults(String fileList) {
int index = 0;
try {
Vector<String> tmp = FileUtil.getFileLines(fileList);
for (Iterator<String> iterator = tmp.iterator(); iterator.hasNext();) {
String line = iterator.next();
StringTokenizer st = new StringTokenizer(line);
String path = st.nextToken();
st.nextToken();
double na = Double.parseDouble(st.nextToken());
double ha = Double.parseDouble(st.nextToken());
RecFile recFile = findAudioFile(path);
ClassificationResult cr = new ClassificationResult();
cr.addResult("N", na);
cr.addResult("A", ha);
if (recFile != null) {
index++;
recFile.storePred(cr);
} else {
_logger.error("recfile not found: " + path);
}
}
} catch (Exception e) {
e.printStackTrace();
}
_logger.info("AFM: added sympalog Results to " + index + " audio files");
}
/**
* Add/Append labels to audiofiles.
*
* @param fileList
*/
public void addLabels(String fileList) {
int index = 0;
try {
Vector<String> tmp = FileUtil.getFileLines(fileList);
for (Iterator<String> iterator = tmp.iterator(); iterator.hasNext();) {
String line = iterator.next();
if (!FileUtil.isCommentOrEmpty(line)) {
StringTokenizer st = new StringTokenizer(line);
String path = st.nextToken();
String labString = "";
while (st.hasMoreTokens()) {
labString += st.nextToken() + " ";
}
labString = labString.trim();
if (labString.length() > 0) {
RecFile recFile = findAudioFile(path);
if (recFile != null) {
index++;
recFile.addLabel(labString);
} else {
_logger.error("recfile not found: " + path);
}
}
if (index % 10 == 0) {
System.err.print(".");
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
_logger.info("AFM: added " + index + " labels to audio files");
}
public RecFile addAudioFile(File file) {
RecFile newRec = new RecFile(file.getPath(), _config);
try {
_audioFiles.add(newRec);
_lastRec = newRec;
} catch (Exception e) {
e.printStackTrace();
}
return newRec;
}
public boolean removeLastRecording() {
try {
_lastRec.removeAnnotationFile();
_lastRec.removeAudioFile();
_audioFiles.remove(_lastRec);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* Replace/add labels to audiofiles
*
* @param fileList
*/
public void replaceLabels(String fileList) {
int index = 0;
try {
Vector<String> tmp = FileUtil.getFileLines(fileList);
for (Iterator<String> iterator = tmp.iterator(); iterator.hasNext();) {
String line = iterator.next();
StringTokenizer st = new StringTokenizer(line);
String path = st.nextToken();
String labString = "";
while (st.hasMoreTokens()) {
labString += st.nextToken() + " ";
}
labString = labString.trim();
if (labString.length() > 0) {
RecFile recFile = findAudioFile(path);
if (recFile != null) {
index++;
recFile.replaceLabel(labString);
} else {
_logger.error("recfile not found: " + path);
}
}
// indicate progress while loading
if (index % 10 == 0) {
System.err.print(".");
}
}
} catch (Exception e) {
e.printStackTrace();
}
_logger.info("AFM: replaced/added labels to " + index + " audio files");
}
public void addTranscripts(String fileList) {
int index = 0;
try {
Vector<String> tmp = FileUtil.getFileLines(fileList,
_config.getString("charEnc"));
for (Iterator<String> iterator = tmp.iterator(); iterator.hasNext();) {
String line = iterator.next();
if (!FileUtil.isCommentOrEmpty(line)) {
StringTokenizer st = new StringTokenizer(line);
String path = st.nextToken();
String trans = "";
while (st.hasMoreTokens()) {
trans += st.nextToken() + " ";
}
trans = trans.trim();
if (trans.length() > 0) {
RecFile recFile = findAudioFile(path);
if (recFile != null) {
index++;
recFile.storeTranscript(trans);
} else {
_logger.error("recfile not found: " + path);
}
}
// indicate progress while loading
if (index % 10 == 0) {
System.err.print(".");
}
}
}
_logger.info("AFM: added " + index
+ " transcriptions to audio files");
} catch (Exception e) {
e.printStackTrace();
}
}
public void addRecognition(String fileList) {
int index = 0;
try {
Vector<String> tmp = FileUtil.getFileLines(fileList,
_config.getString("charEnc"));
for (Iterator<String> iterator = tmp.iterator(); iterator.hasNext();) {
String line = iterator.next();
if (!FileUtil.isCommentOrEmpty(line)) {
StringTokenizer st = new StringTokenizer(line);
String path = st.nextToken();
String trans = "";
while (st.hasMoreTokens()) {
trans += st.nextToken() + " ";
}
trans = trans.trim();
if (trans.length() > 0) {
RecFile recFile = findAudioFile(path);
if (recFile != null) {
index++;
recFile.storeRecongnition(trans);
} else {
_logger.error("recfile not found: " + path);
}
}
// indicate progress while loading
if (index % 10 == 0) {
System.err.print(".");
}
}
}
_logger.info("AFM: added " + index
+ " transcriptions to audio files");
} catch (Exception e) {
e.printStackTrace();
}
}
public void printHypothesisFile() {
int index = 0;
String hypFileText = "";
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
hypFileText += recFile.getRecognition() + " ("
+ recFile._dialog + "_" + recFile._name + ")\n";
}
FileUtil.writeFileContent(_config.getFileHandler("hypothesisFile"),
hypFileText);
} catch (Exception e) {
e.printStackTrace();
}
}
public void printReferenceFile() {
int index = 0;
String hypFileText = "";
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
hypFileText += recFile.getTranscript() + " (" + recFile._dialog
+ "_" + recFile._name + ")\n";
}
FileUtil.writeFileContent(_config.getFileHandler("referenceFile"),
hypFileText);
} catch (Exception e) {
e.printStackTrace();
}
}
public void addTranscriptsAndSynthesize(String fileList) {
int index = 0;
try {
Vector<String> tmp = FileUtil.getFileLines(fileList);
for (Iterator<String> iterator = tmp.iterator(); iterator.hasNext();) {
String line = iterator.next();
if (!FileUtil.isCommentOrEmpty(line)) {
StringTokenizer st = new StringTokenizer(line);
String path = st.nextToken();
String trans = "";
while (st.hasMoreTokens()) {
trans += st.nextToken() + " ";
}
trans = trans.trim();
if (trans.length() > 0) {
RecFile recFile = findAudioFile(path);
if (recFile != null) {
index++;
recFile.storeTranscript(trans);
recFile.generateAudioFile(
_config.getBool("ttsSexFemale"),
_config.getString("ttsLang"));
} else {
_logger.error("recfile not found: " + path);
}
}
// indicate progress while loading
if (index % 10 == 0) {
System.err.print(".");
}
}
}
_logger.info("AFM: synthesized " + index + " audio files");
} catch (Exception e) {
e.printStackTrace();
}
}
public void synthesizeAll(boolean female, String lang) {
int index = 0;
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile != null) {
index++;
recFile.generateAudioFile(female, lang);
// indicate progress while loading
if (index % 10 == 0) {
System.err.print(".");
}
}
_logger.info("AFM: synthesized " + index + " audio files");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void normalizeAll() {
int index = 0;
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile != null) {
index++;
recFile.normalizeTranscription();
// indicate progress while loading
if (index % 10 == 0) {
System.err.print(".");
}
}
_logger.info("AFM: normalized " + index + " audio files");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void removeLabels() {
int index = 0;
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile != null) {
if (FileUtil.existFile(recFile._path)) {
index++;
recFile.removeLabels();
} else {
_logger.error("AFM: ERROR: _file " + recFile._path
+ " doesn't exists");
}
}
// indicate progress while loading
if (index % 10 == 0) {
System.err.print(".");
}
}
} catch (Exception e) {
e.printStackTrace();
}
System.err
.println("AFM: removed labels from " + index + " audio files");
}
/**
* Remove Predicions for all loaded audio files.
*/
public void removePredictions() {
int index = 0;
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile != null) {
if (FileUtil.existFile(recFile._path)) {
index++;
recFile.removePrediction();
} else {
_logger.error("AFM: ERROR: _file " + recFile._path
+ " doesn't exists");
}
}
// indicate progress while loading
if (index % 10 == 0) {
System.err.print(".");
}
}
} catch (Exception e) {
e.printStackTrace();
}
System.err.println("AFM: removed predictions from " + index
+ " audio files");
}
public RecFile findAudioFile(String path) {
try {
for (RecFile recFile : _audioFiles) {
if (recFile._path.compareTo(path) == 0) {
return recFile;
}
}
} catch (Exception e) {
e.printStackTrace();
}
_logger.error("AFM: did not find audio: " + path);
return null;
}
public File findAudioFileAndRemove(String path) {
try {
for (RecFile recFile : _audioFiles) {
if (recFile._path.compareTo(path) == 0) {
recFile.removeAnnotationFile();
_audioFiles.remove(recFile);
String fn = recFile._file.getAbsolutePath();
return new File(fn);
}
}
} catch (Exception e) {
e.printStackTrace();
}
_logger.error("AFM: didn't find audio; " + path);
return null;
}
public void renameRecording(String path, String newName) {
try {
for (RecFile recFile : _audioFiles) {
if (recFile._path.compareTo(path) == 0) {
_audioFiles.remove(recFile);
recFile.rename(newName);
_audioFiles.add(recFile);
}
}
} catch (Exception e) {
e.printStackTrace();
}
_logger.error("AFM: didn't find audio; " + path);
}
public Vector<RecFile> getAudioFiles() {
return _audioFiles;
}
public void recognizeAll() {
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
recFile.recognize();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public Vector<RecFile> getAudioFilesWithLabels() {
Vector<RecFile> returnVec = new Vector<RecFile>();
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile.hasLabel()) {
returnVec.add(recFile);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return returnVec;
}
public Vector<RecFile> getAudioFilesWithPredictions() {
Vector<RecFile> returnVec = new Vector<RecFile>();
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile.hasPrediction()) {
returnVec.add(recFile);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return returnVec;
}
public Vector<RecFile> getAudioFilesWithoutPredictions() {
Vector<RecFile> returnVec = new Vector<RecFile>();
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (!recFile.hasPrediction()) {
returnVec.add(recFile);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return returnVec;
}
public void removeUntagged() {
try {
_logger.debug("deleting untagged...");
int length = _audioFiles.size();
for (int i = 0; i < length - 1; i++) {
RecFile recFile = _audioFiles.elementAt(i);
if (!recFile.hasLabel()) {
recFile.removeFiles();
_audioFiles.remove(recFile);
i--;
}
}
_logger.debug("done deleting untagged.");
} catch (Exception e) {
e.printStackTrace();
}
}
public void set_vocabManager(VocabManager _vocabManager) {
this._vocabManager = _vocabManager;
}
public Vector<RecFile> getAudioFilesWithTranscripts() {
Vector<RecFile> returnVec = new Vector<RecFile>();
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile.hasTranscript()) {
returnVec.add(recFile);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return returnVec;
}
public boolean isAudioFileContained(String path) {
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile._path.compareTo(path) == 0) {
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public void printFilesWithoutTranscription() {
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile.getTranscript() == null) {
System.out.println(recFile._path);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean removeLastLabel(String path) {
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile._path.compareTo(path) == 0) {
recFile.removeLastLabel();
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
_logger.error("AFM: didn't find audio; " + path);
return false;
}
public boolean removeLastPred(String path) {
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile._path.compareTo(path) == 0) {
recFile.removePrediction();
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
_logger.error("AFM: didn't find audio; " + path);
return false;
}
public boolean updateAudioFile(String path) {
try {
for (Iterator<RecFile> iterator = _audioFiles.iterator(); iterator
.hasNext();) {
RecFile recFile = iterator.next();
if (recFile._path.compareTo(path) == 0) {
recFile.loadAnnotations();
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
_logger.error("AFM: didn't find audio; " + path);
return false;
}
}