/*
* Copyright 2006-2007 Columbia University.
*
* This file is part of MEAPsoft.
*
* MEAPsoft is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* MEAPsoft 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MEAPsoft; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* See the file "COPYING" for the text of the license.
*/
package com.meapsoft;
import gnu.getopt.Getopt;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.UnsupportedAudioFileException;
//import featextractors.*;
/**
* Program to segment input files and output text feature files
* compatible with other MEAPsoft tools.
*
* @author Ron Weiss (ronw@ee.columbia.edu)
*/
public class Segmenter extends MEAPUtil
{
// Files to use
String[] audioFiles;
String outFileName="out.segments";
FeatFile outFile;
// Extractor parts
AudioInputStream ais;
STFT stft;
boolean useBeatOD = true;
OnsetDetector detector;
SegmentExtractor extractor;
boolean onsetAtFirstFrame = false;
// sensitivity of DpweOnsetDetector. higher is more sensitive
double threshMult = 1.0;
// length of smoothing window in seconds.
double smtime = 0.1;
double tempoMult = 1;
public Segmenter(String[] inFiles, String outputFile)
{
audioFiles = inFiles;
outFileName = outputFile;
outFile = new FeatFile(outFileName);
}
public Segmenter(String inFile, String outputFile)
{
audioFiles = new String[1];
audioFiles[0] = inFile;
outFileName = outputFile;
outFile = new FeatFile(outFileName);
}
public Segmenter(String[] inFiles, String outputFile, double thr, boolean beatOD, boolean oaff)
{
this(inFiles, outputFile);
threshMult = thr;
useBeatOD = beatOD;
onsetAtFirstFrame = oaff;
}
public Segmenter(String[] inFiles, String outputFile, double thr, double smt, boolean beatOD, boolean oaff)
{
this(inFiles, outputFile);
threshMult = thr;
useBeatOD = beatOD;
onsetAtFirstFrame = oaff;
smtime = smt;
}
public Segmenter(String inFile, String outputFile, double thr, boolean beatOD, boolean oaff)
{
this(inFile, outputFile);
threshMult = thr;
useBeatOD = beatOD;
onsetAtFirstFrame = oaff;
}
public Segmenter(String inFile, String outputFile, double thr, double smt, boolean beatOD, boolean oaff)
{
this(inFile, outputFile);
threshMult = thr;
useBeatOD = beatOD;
onsetAtFirstFrame = oaff;
smtime = smt;
}
public void printUsageAndExit()
{
System.out.println("Usage: Segmenter [-options] source1.wav source2.mp3 ... \n\n" +
" where options include:\n" +
" -o output_file the file to write the output to (defaults to ./out.segments)\n" +
" -m N [0] use mixer N for input\n" +
" -t D.D [1.0] onset detector threshold (event detector), tempo multiplier (beat detector)\n" +
" -s D.D [0.1] length of smoothing window in seconds for event detector\n" +
" -d use the old style onset detector (defaults to BeatOnsetDetector)\n" +
" -0 add an onset at the very beginning of the file\n" +
"");
System.exit(0);
}
/**
* Segmenter constructor. Parses command line arguments
*/
public Segmenter(String[] args)
{
if(args.length == 0)
printUsageAndExit();
// Parse arguments
Getopt opt = new Getopt("Segmenter", args, "m:t:do:s:0");
opt.setOpterr(false);
int c = -1;
while ((c = opt.getopt()) != -1)
{
switch(c)
{
case 'm':
mixerToUse = Integer.parseInt(opt.getOptarg());
System.out.println("Using mixer " + mixerToUse);
break;
case 't':
threshMult = Double.parseDouble(opt.getOptarg());
tempoMult = threshMult;
break;
case 's':
smtime = Double.parseDouble(opt.getOptarg());
break;
case 'd':
useBeatOD = false;
break;
case 'o':
outFileName = opt.getOptarg();
break;
case '0':
onsetAtFirstFrame = true;
break;
case '?':
printUsageAndExit();
break;
default:
System.out.print("getopt() returned " + c + "\n");
}
}
// parse arguments
int ind = opt.getOptind();
if(ind > args.length)
printUsageAndExit();
audioFiles = new String[args.length - ind];
for(int i=ind; i<args.length; i++)
audioFiles[i-ind] = args[i];
outFile = new FeatFile(outFileName);
}
public FeatFile processAudioFile() throws IOException, UnsupportedAudioFileException
{
return processAudioFile(outFile.filename);
}
public FeatFile processAudioFile(String fn) throws IOException, UnsupportedAudioFileException
{
// Create Extractor
//ais = openInputStream(fn);
// downsample to 8kHz
ais = openInputStream(fn, new AudioFormat(DpweOnsetDetector.sr,
bitsPerSamp, 1, signed,
bigEndian));
long frameLength = ais.getFrameLength();
// the mp3/flac decoders don't like to tell us the frame
// length, so we have to make a guess. You might get an
// error and progress bars will be off.
if(frameLength < 0)
{
frameLength = 50000*DpweOnsetDetector.swin/2;
}
int numFrames = (int) frameLength/(DpweOnsetDetector.swin/2);
// keep track of our progress in segmenting this file:
progress.setMinimum(0);
progress.setMaximum(numFrames);
progress.setValue(0);
// use 30ms window
stft = new STFT(ais, DpweOnsetDetector.swin, DpweOnsetDetector.swin/2, frameLatency);
if(useBeatOD)
//detector = new BeatOnsetDetector(stft, bandThresh, bandFrac);
detector = new DpweBeatOnsetDetector(stft, numFrames, tempoMult);
else
//detector = new OnsetDetector(stft, bandThresh, bandFrac);
detector = new DpweOnsetDetector(stft, numFrames, threshMult, smtime);
extractor = new SegmentExtractor(stft, fn, outFile, progress);
detector.addOnsetListener(extractor);
// Hook things together
stft.addFrameListener(extractor);
// insert an onset at frame 0?
if(onsetAtFirstFrame)
extractor.newOnset(0, 0);
// Extract the entire source file
extractor.run();
// outFile now contains some chunks.
outFile.haveReadFile = true;
return outFile;
}
/**
* Set everything up, process input, and write output.
*/
public void run()
{
// process supplied files
for(int i=0; i<audioFiles.length; i++)
{
if(verbose)
System.out.println("Writing segments from " + audioFiles[i] + " to "
+ outFileName + ".");
long startTime = System.currentTimeMillis();
try
{
processAudioFile(audioFiles[i]);
if(writeMEAPFile)
outFile.writeFile();
}
catch(Exception e)
{
exceptionHandler.handleException(e);
}
if(verbose)
System.out.println("Done. Took " +
((System.currentTimeMillis() - startTime)/1000.0)
+ "s");
}
}
// Set the tempo multiplier for the beat onset detector.
public void setTempoMultiplier(double mult)
{
tempoMult = mult;
}
public FeatFile getSegFile()
{
return outFile;
}
public static void main(String[] args)
{
Segmenter o2or = new Segmenter(args);
o2or.verbose = true;
o2or.run();
System.exit(0);
}
}