/*
* 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.composers;
import gnu.getopt.Getopt;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;
import java.util.Vector;
import com.meapsoft.ChunkDist;
import com.meapsoft.EDLChunk;
import com.meapsoft.EDLFile;
import com.meapsoft.EuclideanDist;
import com.meapsoft.FeatChunk;
import com.meapsoft.FeatFile;
import com.meapsoft.ParserException;
/**
* Select top three chroma, write out wonky EDL file specifying how to
* use square/sine waves to create new .wav. Also write out RTcmix file
* that will generate those sinewaves. This is a weird one.
*
* @author Douglas Repetto (douglas@music.columbia.edu)
*/
public class ChromaTopComposer extends Composer
{
public static String oldDesc = "ChromaTopComposer selects the top three chroma bins in each segment and " +
"composes an EDL featuring just those bins for each segment. Also outputs an RTcmix .sco" +
"file that can be used to synthesize the output. REQUIRES AvgChroma!";
String outFileName = "chromatop.edl";
String cmixFileName = "chromatop.sco";
FeatFile featFile;
int[] featdim = null;
ChunkDist dist;
boolean debug = false;
boolean normalizeFeatures = true;
//@mike: empty constructor just to sniff this guy
//please don't call this constructor, it will not work
public ChromaTopComposer()
{
initNameAndDescription("ChromaTop", new String(oldDesc));
}
public ChromaTopComposer(String featFN, String outFN)
{
this(featFN, outFN, new EuclideanDist());
}
public ChromaTopComposer(FeatFile featFN, EDLFile outFN)
{
this(featFN, outFN, new EuclideanDist());
}
public ChromaTopComposer(String featFN, String outFN, ChunkDist cd)
{
this(new FeatFile(featFN), new EDLFile(outFN), cd);
}
public ChromaTopComposer(FeatFile featFN, EDLFile outFN, ChunkDist cd)
{
featFile = featFN;
outFile = outFN;
dist = cd;
featdim = cd.featdim;
if (outFile == null)
outFile = new EDLFile("");
try
{
setup();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
System.exit(-1);
}
Vector chunks = (Vector)featFile.chunks;
FeatChunk fC = (FeatChunk)chunks.elementAt(0);
if (!fC.containsFeature("AvgChroma"))
{
System.out.println("ChromaTopComposer requires the AvgChroma feature!");
return;
}
}
public void printUsageAndExit()
{
System.out
.println("Usage: ChromaTopComposer [-options] features.feat (must contain AvgChroma features) \n\n"
+ " where options include:\n"
+ " -o output_file the file to write the output to (defaults to chromatop.edl)\n"
+ " -x output RTCMIX score file name (defaults to chromatop.sco)\n"
+ " -g debug mode (prints out chunk features on each line of output file)\n"
);
System.out.println();
System.exit(0);
}
/**
* ChromaTopComposer constructor. Parses command line arguments
*/
public ChromaTopComposer(String[] args)
{
if (args.length == 0)
printUsageAndExit();
// Vector features = new Vector();
// Parse arguments
String argString = "o:x:g";
parseCommands(args, argString);
Getopt opt = new Getopt("ChromaTopComposer", args, argString);
opt.setOpterr(false);
int c = -1;
while ((c = opt.getopt()) != -1)
{
switch (c)
{
case 'o':
outFileName = opt.getOptarg();
break;
case 'g':
debug = true;
break;
case 'x':
cmixFileName = opt.getOptarg();
break;
case '?':
printUsageAndExit();
break;
default:
System.out.print("getopt() returned " + c + "\n");
}
}
// parse arguments
int ind = opt.getOptind();
if (ind > args.length)
printUsageAndExit();
featFile = new FeatFile(args[args.length - 1]);
outFile = new EDLFile(outFileName);
try
{
setup();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
System.exit(-1);
}
//System.out.println("chunks.size(): " + featFile.chunks.size());
//Vector chunks = (Vector)featFile.chunks;
FeatChunk fC = (FeatChunk) featFile.chunks.get(0);
if (!fC.containsFeature("AvgChroma"))
{
System.out.println("ChromaTopComposer requires the AvgChroma feature!");
return;
}
System.out.println("Composing " + outFileName + " and " + cmixFileName + " from "
+ args[args.length - 1] + ".");
}
public void setNormalizeFeatures(boolean b)
{
normalizeFeatures = b;
}
public void setup() throws IOException, ParserException
{
super.setup();
if (!featFile.haveReadFile)
featFile.readFile();
if (featFile.chunks.size() == 0)
throw new ParserException(featFile.filename, "No chunks found");
if (normalizeFeatures)
{
featFile = (FeatFile) featFile.clone();
featFile.normalizeFeatures();
featFile.applyFeatureWeights();
}
progress.setMaximum(featFile.chunks.size());
}
public EDLFile compose()
{
Vector chunks = (Vector)featFile.chunks;
int numChunks = chunks.size();
System.out.println("numChunks: " + numChunks);
FileWriter fstream = null;
BufferedWriter out = null;
try
{
fstream = new FileWriter(cmixFileName);
out = new BufferedWriter(fstream);
out.write("print_off()\n");
out.write("set_option(\"audio = off\", \"play = off\", \"clobber = on\")\n");
out.write("rtsetparams(44100, 2)\n");
out.write("load(\"WAVETABLE\")\n");
out.write("rtoutput(\"" + cmixFileName + ".wav\")\n");
out.write("waveform = maketable(\"wave\", 1000, \"tri\")\n");
out.write("ampenv = maketable(\"line\", 1000, 0,0, 1,1, 9,1, 10,0)\n");
}
catch (Exception e)
{//Catch exception if any
System.err.println("Error: " + e.getMessage());
return null;
}
for (int chunkNum = 0; chunkNum < numChunks; chunkNum++)
{
FeatChunk fC = (FeatChunk)chunks.elementAt(chunkNum);
double chromas[] = fC.getFeatureByName("AvgChroma");
double chromasSort[] = fC.getFeatureByName("AvgChroma");
Arrays.sort(chromasSort);
int firstChroma = -1;
int secondChroma = -1;
int thirdChroma = -1;
//System.out.println("chromasSort[0]: " + chromasSort[0] + " chromasSort[11]: " + chromasSort[11]);
for (int i = 0; i < 12; i++)
{
if (chromasSort[11] == chromas[i])
{
firstChroma = i;
//System.out.println("fC: " + i + " value: " + chromas[i]);
}
if (chromasSort[10] == chromas[i])
{
secondChroma = i;
//System.out.println("sC: " + i + " value: " + chromas[i]);
}
if (chromasSort[9] == chromas[i])
{
thirdChroma = i;
//System.out.println("tC: " + i + " value: " + chromas[i]);
}
}
try
{
out.write("WAVETABLE(" + fC.startTime + ", " + fC.length +
", " + chromasSort[11] * 20000 + " * ampenv, " +
(6.0 + (firstChroma * 0.01)) + ", 0.5, waveform)\n");
out.write("WAVETABLE(" + fC.startTime + ", " + fC.length +
", " + chromasSort[10] * 15000 + " * ampenv, " +
(6.0 + (secondChroma * 0.01)) + ", 0.25, waveform)\n");
out.write("WAVETABLE(" + fC.startTime + ", " + fC.length +
", " + chromasSort[9] * 15000 + " * ampenv, " +
(6.0 + (thirdChroma * 0.01)) + ", 0.75, waveform)\n");
out.write("WAVETABLE(" + fC.startTime + ", " + fC.length +
", " + chromasSort[11] * 20000 + " * ampenv, " +
(8.0 + (firstChroma * 0.01)) + ", 0.5, waveform)\n");
out.write("WAVETABLE(" + fC.startTime + ", " + fC.length +
", " + chromasSort[10] * 15000 + " * ampenv, " +
(8.0 + (secondChroma * 0.01)) + ", 0.75, waveform)\n");
out.write("WAVETABLE(" + fC.startTime + ", " + fC.length +
", " + chromasSort[9] * 15000 + " * ampenv, " +
(8.0 + (thirdChroma * 0.01)) + ", 0.25, waveform)\n");
}
catch (Exception e)
{
}
for (int i = 0; i < 12; i++)
{
if (firstChroma != i &&
secondChroma != i &&
thirdChroma != i)
{
chromas[i] = 0.0;
}
}
fC.clearFeatures();
fC.addFeature(chromas);
outFile.chunks.add(fC);
}
try
{
out.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
// outFile now contains some chunks.
outFile.haveReadFile = true;
return outFile;
}
public static void main(String[] args)
{
ChromaTopComposer m = new ChromaTopComposer(args);
long startTime = System.currentTimeMillis();
m.run();
System.out.println("Done. Took "
+ ((System.currentTimeMillis() - startTime) / 1000.0) + "s");
System.exit(0);
}
}