changeset 1:4de8d7f01bd4

* Import BeatRoot v0.5.6
author Chris Cannam
date Fri, 08 Oct 2010 16:09:10 +0100
parents c7e0e3007677
children 4c3f5bc01c97
files at/ofai/music/beatroot/BeatRoot.java at/ofai/music/beatroot/BeatTrackDisplay.java at/ofai/music/beatroot/GUI.java
diffstat 3 files changed, 69 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/at/ofai/music/beatroot/BeatRoot.java	Fri Oct 08 16:08:48 2010 +0100
+++ b/at/ofai/music/beatroot/BeatRoot.java	Fri Oct 08 16:09:10 2010 +0100
@@ -134,6 +134,8 @@
 	 * <LI><I><B>-l</B> lowThreshold</I> Spectrogram energy threshold corresponding
 	 *  to minimum value in colour map</LI>
 	 * <LI><I><B>-o</B> outputFile</I> Save output to this file (implies <I><B>-b</B></I>)</LI>
+	 * <LI><I><B>-O</B> Output the times of onsets, not beats, and exit (use -o
+	 * flag to specify the output file; implies batch mode)</LI>
 	 * <LI><I><B>-p</B></I> Play audio with beats as soon as processing is completed</LI>
 	 * <LI><I><B>-q</B></I> Suppress output of warnings (TODO) </LI>
 	 * <LI><I><B>-s</B> audioScaleFactor</I> Constant for scaling amplitude envelope display</LI>
@@ -142,6 +144,7 @@
 	 * <LI><I><B>-w</B></I> live input (not used)</LI>
 	 * <LI><I><B>-c</B></I> cursor is always at centre; data scrolls past it</LI>
 	 * <LI><I><B>-e</B> allowedError</I> allowed error in beat position for evaluation</LI>
+	 * <LI><I><B>-E</B> allowedRelativeError</I> allowed relative error (0-1) in beat position for evaluation</LI>
 	 * </BL>
 	 */
 	public void processArgs(String[] args) {
@@ -169,6 +172,14 @@
 					break;
 				case 'e':
 					BeatTrackDisplay.allowedError = Double.parseDouble(args[++i]);
+					BeatTrackDisplay.useRelativeError = false;
+					break;
+				case 'E':
+					BeatTrackDisplay.allowedError = Double.parseDouble(args[++i]);
+					BeatTrackDisplay.useRelativeError = true;
+					break;
+				case 'P':
+					BeatTrackDisplay.usePScore = true;
 					break;
 				case 'h':
 					GUI.DEFAULT_HIGH_THRESHOLD = Double.parseDouble(args[++i]);
@@ -265,8 +276,17 @@
 					audioProcessor.setInputFile(audioIn);
 					audioProcessor.processFile();
 					if (onsetOnly && (textOutputFile != null)) {
-						audioProcessor.onsetList.writeBinary(textOutputFile);
-						continue;
+						if (textOutputFile.endsWith(".obj"))
+							audioProcessor.onsetList.writeBinary(textOutputFile);
+						else try {
+							audioProcessor.onsetList.writeBeatsAsText(textOutputFile);
+						} catch (Exception e) {
+							System.err.println("Can't write onset file\n" + e);
+						}
+						if (argsFile != null)
+							continue;
+						else
+							break;
 					}
 				} else if (beatsIn == null) {
 					System.exit(0);
--- a/at/ofai/music/beatroot/BeatTrackDisplay.java	Fri Oct 08 16:08:48 2010 +0100
+++ b/at/ofai/music/beatroot/BeatTrackDisplay.java	Fri Oct 08 16:09:10 2010 +0100
@@ -30,6 +30,7 @@
 import java.awt.event.MouseMotionListener;
 import java.awt.image.BufferedImage;
 
+import java.util.Arrays;
 import java.util.ListIterator;
 
 import javax.swing.JPanel;
@@ -281,6 +282,14 @@
 	 */
 	public static double allowedError = 0.1;
 
+	/** For evaluation of beat tracking results, indicates whether <code>allowedError</code>
+	 *  is interpreted as absolute (in seconds) or relative (0-1).
+	 */
+	public static boolean useRelativeError = false;
+	
+	/** For evaluation, select whether to use the P-score or T-score */
+	public static boolean usePScore = false;
+
 	/** mode for scrolling the display:
 	 * 	if true the cursor remains centred on the screen and the data scrolls;
 	 *  if false the cursor moves and the data is changed just before
@@ -475,7 +484,7 @@
 
 	/** Sets the length of audio that is visible at any one time (i.e. zoom factor).
 	 *  @param msec Length in milliseconds
-	 */ 
+	 */
 	synchronized public void setVisibleAmount(int msec) {
 		visibleTimeLength = (double)msec / 1000.0;
 		if (visibleTimeStart + visibleTimeLength > maximumTime)
@@ -1144,7 +1153,7 @@
 	/** Constant representing an unknown relationship between metrical levels */
 	protected static final double UNKNOWN = -1.0;
 	
-	/** Finds the mean tempo from an array of beat times
+	/** Finds the mean tempo (as inter-beat interval) from an array of beat times
 	 *  @param d An array of beat times
 	 *  @return The average inter-beat interval
 	 */
@@ -1154,6 +1163,23 @@
 		return (d[d.length - 1] - d[0]) / (d.length - 1);
 	} // getAverageIBI()
 	
+	/** Finds the median tempo (as inter-beat interval) from an array of beat times
+	 *  @param d An array of beat times
+	 *  @return The median inter-beat interval
+	 */
+	public static double getMedianIBI(double[] d) {
+		if ((d == null) || (d.length < 2))
+			return -1.0;
+		double[] ibi = new double[d.length-1];
+		for (int i = 1; i < d.length; i++)
+			ibi[i-1] = d[i] - d[i-1];
+		Arrays.sort(ibi);
+		if (ibi.length % 2 == 0)
+			return (ibi[ibi.length / 2] + ibi[ibi.length / 2 - 1]) / 2;
+		else
+			return ibi[ibi.length / 2];
+	} // getAverageIBI()
+	
 	/** Estimates the metrical relationship between two beat sequences.
 	 *  @param beats The system's beat times
 	 *  @param correct The annotated beat times
@@ -1234,13 +1260,16 @@
 	    int ok = 0;
 	    int extra = 0;
 	    double metricalLevel = getRhythmicLevel(beatsArr, correct);
+	    double errorWindow = allowedError;
+	    if (useRelativeError)
+	    	errorWindow *= getMedianIBI(correct);
 	    // double[] revBeats = makeRealBeatList(correctBeats, metricalLevel, true);
 	    int b = 0;	// index of beats to be evaluated
 	    int c = 0;	// index of correct (annotated) beats
 	    for ( ; b < beatsArr.length && (beatsArr[b] < correct[c]); b++)
 	        extra++;    // skip any "beats" before music starts
 	    while ((c < correct.length) && (b < beatsArr.length)) {
-	        if (Math.abs(beatsArr[b] - correct[c]) < allowedError) {
+	        if (Math.abs(beatsArr[b] - correct[c]) < errorWindow) {
 	            ok++;
 	            b++;
 	            c++;
@@ -1260,9 +1289,15 @@
 	            fp++;
 	        b++;
 	    }
-	    System.out.printf("%4.2f %5.3f %5.3f", metricalLevel, getAverageIBI(correct), getAverageIBI(beatsArr));
-	    System.out.printf("  ok: " + ok + "  f+: " + fp + "  f-: " + fn +
+	    if (usePScore) {
+	    	System.out.printf("%4.2f %5.3f %5.3f", metricalLevel, getMedianIBI(correct), getMedianIBI(beatsArr));
+	    	System.out.printf("  ok: " + ok + "  f+: " + fp + "  f-: " + fn +
+	       				  "  Score: %5.1f\n", (double)(ok*100) / (double)(Math.max(fp, fn) + ok));
+	    } else {
+	    	System.out.printf("%4.2f %5.3f %5.3f", metricalLevel, getAverageIBI(correct), getAverageIBI(beatsArr));
+	    	System.out.printf("  ok: " + ok + "  f+: " + fp + "  f-: " + fn +
 	       				  "  Score: %5.1f\n", (double)(ok*100) / (double)(fp+fn+ok));
+	    }
 	    if (ok + fp + extra != beatsArr.length)
 	    	System.err.println("Beat count wrong " + extra + " " + beatsArr.length + " " + correct.length);
 	} // evaluate()
--- a/at/ofai/music/beatroot/GUI.java	Fri Oct 08 16:08:48 2010 +0100
+++ b/at/ofai/music/beatroot/GUI.java	Fri Oct 08 16:09:10 2010 +0100
@@ -95,7 +95,7 @@
 	
 	/** Version number of program - displayed as part of window title.
 	 *  DO NOT EDIT: This line is also used in creating the file name of the jar file. */
-	public static final String version = "0.5.5";
+	public static final String version = "0.5.6";
 	
 	/** Strings displayed on menus and buttons */
 	public static final String LOAD_AUDIO = "Load Audio Data";
@@ -339,7 +339,12 @@
 	public void loadBeatData(String fileName) {
 		if (fileName != null) {
 			try {
-				beats = EventList.readBeatTrackFile(fileName);
+				if (fileName.endsWith(".tmf"))
+					beats = EventList.readBeatTrackFile(fileName);
+				else if (fileName.endsWith(".lbl"))
+					beats = EventList.readLabelFile(fileName);
+				else // if (fileName.endsWith(".txt"))
+					beats = EventList.readBeatsAsText(fileName);
 				setBeatData(beats);
 			} catch (Exception e) {
 				System.err.println("Error loading beat data: " + e);