changeset 223:51c18a17404a

Move some scripts about, make piano test script produce norm'd ref output as well as non-norm'd test output
author Chris Cannam
date Wed, 16 Jul 2014 13:29:38 +0100
parents 69ac2d0770b8
children 63bc32d6873b
files testdata/TRIOS-groundtruth/convert_midifileout.yeti testdata/TRIOS-groundtruth/convert_svout.yeti testdata/evaluation/batchProcessingEvaluate.m testdata/evaluation/computeNoteLevelAccuracy.m testdata/evaluation/convertMIDIToPianoRoll.m testdata/evaluation/run-piano.sh testdata/scripts/convert_midifileout.yeti testdata/scripts/convert_svout.yeti testdata/scripts/matlab/batchProcessingEvaluate.m testdata/scripts/matlab/computeNoteLevelAccuracy.m testdata/scripts/matlab/convertMIDIToPianoRoll.m
diffstat 11 files changed, 263 insertions(+), 256 deletions(-) [+]
line wrap: on
line diff
--- a/testdata/TRIOS-groundtruth/convert_midifileout.yeti	Wed Jul 16 12:46:18 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-
-// Convert the output of the midifile program
-// (https://code.soundsoftware.ac.uk/projects/midifile)
-// into lab file like those output from the test scripts.
-//
-// The midifile format contains lines looking like:
-//
-// <t>: Note: channel <c> duration <d> pitch <p> velocity <v>
-//
-// where t and d are defined in terms of the timebase and
-// tempo, which are (usually) given in earlier lines:
-//
-// Timing division: <n> ppq
-// <t>: Tempo: <n>
-//
-// Note that we assume 4 quarter-notes per bar, we don't parse time
-// signatures, and we only handle the first tempo event.
-// 
-// The output file format looks like:
-//
-// onset offset frequency
-//
-// with times in seconds.
-
-program convert_midifileout;
-
-usage () =
-    eprintln "\nUsage: convert_midifileout file.txt\n";
-
-toFrequency m = 
-    440 * Math#pow(2.0, (m - 69) / 12.0);
-
-toTime timebase tempo t =
-    (t / timebase) * (60 / tempo);
-
-convert f =
-   (str = openInFile f "UTF-8";
-    var timebase = 480;
-    var tempo = 120;
-    var tempoNowFixed = false;
-    for (str.lines ()) do line:
-        bits = strSplit ": " line;
-        if length bits > 1 then
-            if bits[0] == "Timing division" then
-                timebase := number (strReplace " ppq" "" bits[1]);
-                eprintln "Set timing division to \(timebase)";
-            elif bits[1] == "Tempo" then
-                if tempoNowFixed then
-                    failWith "Can't handle variable-tempo file";
-                fi;
-                if length bits < 3 then 
-                    failWith "Too few bits in tempo line: \(line)";
-                fi;
-                tempo := number (bits[2]);
-                tempoNowFixed := true;
-                eprintln "Set tempo to \(tempo)";
-            elif bits[1] == "Note" then
-                tempoNowFixed := true;
-                if length bits < 3 then
-                    failWith "Too few bits in note line: \(line)";
-                fi;
-                noteparts = strSplit " " bits[2];
-                if length noteparts < 8 then
-                    failWith "Too few note parameters in line: \(line)";
-                fi;
-                onset = toTime timebase tempo (number bits[0]);
-                duration = toTime timebase tempo (number noteparts[3]);
-                frequency = toFrequency (number noteparts[5]);
-                println "\(onset)\t\(onset + duration)\t\(frequency)";
-            fi;
-        fi;
-    done;
-    str.close ());
-
-case (list _argv) of
-file::[]: convert file;
-_: usage ();
-esac;
-
--- a/testdata/TRIOS-groundtruth/convert_svout.yeti	Wed Jul 16 12:46:18 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-
-// Convert CSV file exported by Sonic Visualiser:
-// 
-// onset,midinote,duration,level,label
-//
-// into lab file like those output from the test scripts:
-//
-// onset offset frequency
-
-program convert_svout;
-
-usage () =
-    eprintln "\nUsage: convert_svout file.csv\n";
-
-toFrequency m = 
-    440 * Math#pow(2.0, (m - 69) / 12.0);
-
-convert f =
-   (str = openInFile f "UTF-8";
-    for (str.lines ()) do line:
-        case list (strSplit "," line) of
-        onset::midinote::duration::_: 
-            println "\(onset)\t\((number onset) + (number duration))\t\(toFrequency (number midinote))";
-        _:
-            failWith "badly formed line: \(line)";
-        esac;
-    done;
-    str.close ());
-
-case (list _argv) of
-file::[]: convert file;
-_: usage ();
-esac;
-
--- a/testdata/evaluation/batchProcessingEvaluate.m	Wed Jul 16 12:46:18 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-function  [Pre,Rec,F]  = batchProcessingEvaluate(folder)
-
-% Evaluate transcription output, using onset-only note-based metrics
-% e.g. [Pre,Rec,F]  = batchProcessingEvaluate('TRIOS-mirex2012-matlab');
-
-
-fileList = dir(folder);
-fileCount = 0;
-
-for i=3:length(fileList) 
-    
-    if(isdir([folder '/' fileList(i).name])) 
-        
-        fileCount = fileCount + 1;
-
-        
-        % Load ground truth
-        [pianoRollGT,nmatGT] = convertMIDIToPianoRoll([fileList(i).name '.mid'],10,1.0);
-        
-        
-        % Load transcripton nmat
-        nmat = load([folder '/' fileList(i).name '/' 'mix.lab']);
-        
-        
-        % Convert 3rd nmat column to MIDI scale        
-        nmat(:,3) = round(12.*log2(nmat(:,3)./27.5) + 1);
-        
-        
-        % Compute onset-based note-level accuracy
-        [Pre(fileCount),Rec(fileCount),F(fileCount)] = computeNoteLevelAccuracy(nmat,nmatGT);
-        
-    end; 
-end;
\ No newline at end of file
--- a/testdata/evaluation/computeNoteLevelAccuracy.m	Wed Jul 16 12:46:18 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-function [Pre,Rec,F,Acc,PreOff,RecOff,FOff,AccOff] = computeNoteLevelAccuracy(nmat1,nmat2)
-
-% Compute note-level onset-only and onset-offset accuracy (Bay09)
-
-
-% Initialize
-if (isempty(nmat1)) Pre=0; Rec=0; F=0; Acc=0; return; end;
-
-% Total number of transcribed notes
-Ntot = size(nmat1,1);
-
-% Number of reference notes
-Nref = size(nmat2,1);
-
-% Number of correctly transcribed notes, onset within a +/-50 ms range
-Ncorr = 0;
-NcorrOff = 0;
-for j=1:size(nmat2,1)
-    for i=1:size(nmat1,1)
-        if( (nmat1(i,3) == nmat2(j,3)) && (abs(nmat2(j,1)-nmat1(i,1))<=0.05) )
-            Ncorr = Ncorr+1;
-            
-            % If offset within a +/-50 ms range or within 20% of ground-truth note's duration
-            if abs(nmat2(j,2) - nmat1(i,2)) <= max(0.05, 0.2 * (nmat2(j,2) - nmat2(j,1)))
-               NcorrOff = NcorrOff +1;
-            end;
-            
-            break; % In order to consider duplicates as false alarms
-            
-        end;
-    end;
-end;
-
-% Number of onset-only P-R-F-Acc
-Nfp = Ntot-Ncorr;
-Nfn = Nref-Ncorr;
-Rec = Ncorr/Nref;
-Pre = Ncorr/Ntot;
-F = 2*((Pre*Rec)/(Pre+Rec));
-Acc= Ncorr/(Ncorr+Nfp+Nfn);
-
-% Number of onset-offset P-R-F-Acc
-NfpOff = Ntot-NcorrOff;
-NfnOff = Nref-NcorrOff;
-RecOff = NcorrOff/Nref;
-PreOff = NcorrOff/Ntot;
-FOff = 2*((PreOff*RecOff)/(PreOff+RecOff));
-AccOff= NcorrOff/(NcorrOff+NfpOff+NfnOff);
\ No newline at end of file
--- a/testdata/evaluation/convertMIDIToPianoRoll.m	Wed Jul 16 12:46:18 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-function [pianoRoll,newnmat] = convertMIDIToPianoRoll(filename,timeResolution,dur)
-
-% Time resolution is in msec
-% eg. pianoRoll = convertMIDIToPianoRoll('bach_847MINp_align.mid',10);
-
-% Read MIDI file
-nmat = readmidi(filename);
-
-
-% Gather MIDI information
-[n1 n2] = size(nmat);
-lenthInSec = nmat(n1,6) + nmat(n1,7);
-pianoRoll = zeros(88,round(lenthInSec*(1000/timeResolution)));
-
-
-% Fill piano roll
-for i=1:n1
-    pianoRoll(round(nmat(i,4)-20),round(nmat(i,6)*(1000/timeResolution))+1:round(nmat(i,6)*(1000/timeResolution)+dur*nmat(i,7)*(1000/timeResolution))+1) = 1;
-end;
-
-pianoRoll = pianoRoll';
-
-% Plot piano roll
-%figure; imagesc(imrotate(pianoRoll,0)); axis xy
-%colormap('gray'); xlabel('time frame'); ylabel('Pitch');
-
-
-% Convert to non-MIDI nmat
-newnmat(:,1) = nmat(:,6);
-newnmat(:,2) = nmat(:,6)+nmat(:,7);
-newnmat(:,3) = nmat(:,4)-20;
\ No newline at end of file
--- a/testdata/evaluation/run-piano.sh	Wed Jul 16 12:46:18 2014 +0100
+++ b/testdata/evaluation/run-piano.sh	Wed Jul 16 13:29:38 2014 +0100
@@ -64,42 +64,49 @@
 
     for instrument in $intended_instrument 0; do
 
-	echo
-	echo "For file $filename, instrument $instrument..."
+	for norm in no yes; do
 
-	# Don't normalise -- part of the point here is to make it work
-	# for various different levels
-	cp "$infile" "$tmpwav"
+	    echo
+	    echo "For file $filename, instrument $instrument, norm $norm..."
 
-	# generate the transform by interpolating the instrument parameter
-	cat transform.ttl | sed "s/INSTRUMENT_PARAMETER/$instrument/" > "$transfile"
+	    if [ "$norm" = "no" ]; then
+		# Don't normalise -- part of the point here is to make
+		# it work for various different levels
+		cp "$infile" "$tmpwav"
+	    else
+		# Normalise as reference
+		sox "$infile" "$tmpwav" gain -n -6.020599913279624
+	    fi
 
-	sonic-annotator \
-	    --writer csv \
-	    --csv-one-file "$outfile" \
-	    --csv-force \
-	    --transform "$transfile" \
-	    "$tmpwav"
+	    # generate the transform by interpolating the instrument parameter
+	    cat transform.ttl | sed "s/INSTRUMENT_PARAMETER/$instrument/" > "$transfile"
 
-	cat "$outfile" | \
-	    sed 's/^[^,]*,//' | \
-	    while IFS=, read start duration frequency level label; do
-	    end=`echo "$start $duration + p" | dc`
-	    echo -e "$start\t$end\t$frequency"
-	done > "$outfile.lab"
+	    sonic-annotator \
+		--writer csv \
+		--csv-one-file "$outfile" \
+		--csv-force \
+		--transform "$transfile" \
+		"$tmpwav"
 
-	for ms in 50; do
-	    mark=""
-	    if [ "$instrument" = "0" ]; then
-		mark="  <-- generic for $filename"; 
-	    else
-		mark="  <-- piano preset for $filename";
-	    fi;
+	    cat "$outfile" | \
+		sed 's/^[^,]*,//' | \
+		while IFS=, read start duration frequency level label; do
+		end=`echo "$start $duration + p" | dc`
+		echo -e "$start\t$end\t$frequency"
+	    done > "$outfile.lab"
+
+	    for ms in 50; do
+		mark=""
+		if [ "$instrument" = "0" ]; then
+		    mark="  <-- generic for $filename (norm = $norm)"; 
+		else
+		    mark="  <-- piano preset for $filename (norm = $norm)";
+		fi;
+		echo
+		echo "Validating against ground truth at $ms ms:"
+		"$yc" ./evaluate_lab.yeti "$ms" "../piano-groundtruth/$filename.lab" "$outfile.lab" | sed 's,$,'"$mark"','
+	    done;
 	    echo
-	    echo "Validating against ground truth at $ms ms:"
-	    "$yc" ./evaluate_lab.yeti "$ms" "../piano-groundtruth/$filename.lab" "$outfile.lab" | sed 's,$,'"$mark"','
-	done;
-
-	echo
+	done
     done
 done
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testdata/scripts/convert_midifileout.yeti	Wed Jul 16 13:29:38 2014 +0100
@@ -0,0 +1,79 @@
+
+// Convert the output of the midifile program
+// (https://code.soundsoftware.ac.uk/projects/midifile)
+// into lab file like those output from the test scripts.
+//
+// The midifile format contains lines looking like:
+//
+// <t>: Note: channel <c> duration <d> pitch <p> velocity <v>
+//
+// where t and d are defined in terms of the timebase and
+// tempo, which are (usually) given in earlier lines:
+//
+// Timing division: <n> ppq
+// <t>: Tempo: <n>
+//
+// Note that we assume 4 quarter-notes per bar, we don't parse time
+// signatures, and we only handle the first tempo event.
+// 
+// The output file format looks like:
+//
+// onset offset frequency
+//
+// with times in seconds.
+
+program convert_midifileout;
+
+usage () =
+    eprintln "\nUsage: convert_midifileout file.txt\n";
+
+toFrequency m = 
+    440 * Math#pow(2.0, (m - 69) / 12.0);
+
+toTime timebase tempo t =
+    (t / timebase) * (60 / tempo);
+
+convert f =
+   (str = openInFile f "UTF-8";
+    var timebase = 480;
+    var tempo = 120;
+    var tempoNowFixed = false;
+    for (str.lines ()) do line:
+        bits = strSplit ": " line;
+        if length bits > 1 then
+            if bits[0] == "Timing division" then
+                timebase := number (strReplace " ppq" "" bits[1]);
+                eprintln "Set timing division to \(timebase)";
+            elif bits[1] == "Tempo" then
+                if tempoNowFixed then
+                    failWith "Can't handle variable-tempo file";
+                fi;
+                if length bits < 3 then 
+                    failWith "Too few bits in tempo line: \(line)";
+                fi;
+                tempo := number (bits[2]);
+                tempoNowFixed := true;
+                eprintln "Set tempo to \(tempo)";
+            elif bits[1] == "Note" then
+                tempoNowFixed := true;
+                if length bits < 3 then
+                    failWith "Too few bits in note line: \(line)";
+                fi;
+                noteparts = strSplit " " bits[2];
+                if length noteparts < 8 then
+                    failWith "Too few note parameters in line: \(line)";
+                fi;
+                onset = toTime timebase tempo (number bits[0]);
+                duration = toTime timebase tempo (number noteparts[3]);
+                frequency = toFrequency (number noteparts[5]);
+                println "\(onset)\t\(onset + duration)\t\(frequency)";
+            fi;
+        fi;
+    done;
+    str.close ());
+
+case (list _argv) of
+file::[]: convert file;
+_: usage ();
+esac;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testdata/scripts/convert_svout.yeti	Wed Jul 16 13:29:38 2014 +0100
@@ -0,0 +1,34 @@
+
+// Convert CSV file exported by Sonic Visualiser:
+// 
+// onset,midinote,duration,level,label
+//
+// into lab file like those output from the test scripts:
+//
+// onset offset frequency
+
+program convert_svout;
+
+usage () =
+    eprintln "\nUsage: convert_svout file.csv\n";
+
+toFrequency m = 
+    440 * Math#pow(2.0, (m - 69) / 12.0);
+
+convert f =
+   (str = openInFile f "UTF-8";
+    for (str.lines ()) do line:
+        case list (strSplit "," line) of
+        onset::midinote::duration::_: 
+            println "\(onset)\t\((number onset) + (number duration))\t\(toFrequency (number midinote))";
+        _:
+            failWith "badly formed line: \(line)";
+        esac;
+    done;
+    str.close ());
+
+case (list _argv) of
+file::[]: convert file;
+_: usage ();
+esac;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testdata/scripts/matlab/batchProcessingEvaluate.m	Wed Jul 16 13:29:38 2014 +0100
@@ -0,0 +1,33 @@
+function  [Pre,Rec,F]  = batchProcessingEvaluate(folder)
+
+% Evaluate transcription output, using onset-only note-based metrics
+% e.g. [Pre,Rec,F]  = batchProcessingEvaluate('TRIOS-mirex2012-matlab');
+
+
+fileList = dir(folder);
+fileCount = 0;
+
+for i=3:length(fileList) 
+    
+    if(isdir([folder '/' fileList(i).name])) 
+        
+        fileCount = fileCount + 1;
+
+        
+        % Load ground truth
+        [pianoRollGT,nmatGT] = convertMIDIToPianoRoll([fileList(i).name '.mid'],10,1.0);
+        
+        
+        % Load transcripton nmat
+        nmat = load([folder '/' fileList(i).name '/' 'mix.lab']);
+        
+        
+        % Convert 3rd nmat column to MIDI scale        
+        nmat(:,3) = round(12.*log2(nmat(:,3)./27.5) + 1);
+        
+        
+        % Compute onset-based note-level accuracy
+        [Pre(fileCount),Rec(fileCount),F(fileCount)] = computeNoteLevelAccuracy(nmat,nmatGT);
+        
+    end; 
+end;
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testdata/scripts/matlab/computeNoteLevelAccuracy.m	Wed Jul 16 13:29:38 2014 +0100
@@ -0,0 +1,48 @@
+function [Pre,Rec,F,Acc,PreOff,RecOff,FOff,AccOff] = computeNoteLevelAccuracy(nmat1,nmat2)
+
+% Compute note-level onset-only and onset-offset accuracy (Bay09)
+
+
+% Initialize
+if (isempty(nmat1)) Pre=0; Rec=0; F=0; Acc=0; return; end;
+
+% Total number of transcribed notes
+Ntot = size(nmat1,1);
+
+% Number of reference notes
+Nref = size(nmat2,1);
+
+% Number of correctly transcribed notes, onset within a +/-50 ms range
+Ncorr = 0;
+NcorrOff = 0;
+for j=1:size(nmat2,1)
+    for i=1:size(nmat1,1)
+        if( (nmat1(i,3) == nmat2(j,3)) && (abs(nmat2(j,1)-nmat1(i,1))<=0.05) )
+            Ncorr = Ncorr+1;
+            
+            % If offset within a +/-50 ms range or within 20% of ground-truth note's duration
+            if abs(nmat2(j,2) - nmat1(i,2)) <= max(0.05, 0.2 * (nmat2(j,2) - nmat2(j,1)))
+               NcorrOff = NcorrOff +1;
+            end;
+            
+            break; % In order to consider duplicates as false alarms
+            
+        end;
+    end;
+end;
+
+% Number of onset-only P-R-F-Acc
+Nfp = Ntot-Ncorr;
+Nfn = Nref-Ncorr;
+Rec = Ncorr/Nref;
+Pre = Ncorr/Ntot;
+F = 2*((Pre*Rec)/(Pre+Rec));
+Acc= Ncorr/(Ncorr+Nfp+Nfn);
+
+% Number of onset-offset P-R-F-Acc
+NfpOff = Ntot-NcorrOff;
+NfnOff = Nref-NcorrOff;
+RecOff = NcorrOff/Nref;
+PreOff = NcorrOff/Ntot;
+FOff = 2*((PreOff*RecOff)/(PreOff+RecOff));
+AccOff= NcorrOff/(NcorrOff+NfpOff+NfnOff);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testdata/scripts/matlab/convertMIDIToPianoRoll.m	Wed Jul 16 13:29:38 2014 +0100
@@ -0,0 +1,31 @@
+function [pianoRoll,newnmat] = convertMIDIToPianoRoll(filename,timeResolution,dur)
+
+% Time resolution is in msec
+% eg. pianoRoll = convertMIDIToPianoRoll('bach_847MINp_align.mid',10);
+
+% Read MIDI file
+nmat = readmidi(filename);
+
+
+% Gather MIDI information
+[n1 n2] = size(nmat);
+lenthInSec = nmat(n1,6) + nmat(n1,7);
+pianoRoll = zeros(88,round(lenthInSec*(1000/timeResolution)));
+
+
+% Fill piano roll
+for i=1:n1
+    pianoRoll(round(nmat(i,4)-20),round(nmat(i,6)*(1000/timeResolution))+1:round(nmat(i,6)*(1000/timeResolution)+dur*nmat(i,7)*(1000/timeResolution))+1) = 1;
+end;
+
+pianoRoll = pianoRoll';
+
+% Plot piano roll
+%figure; imagesc(imrotate(pianoRoll,0)); axis xy
+%colormap('gray'); xlabel('time frame'); ylabel('Pitch');
+
+
+% Convert to non-MIDI nmat
+newnmat(:,1) = nmat(:,6);
+newnmat(:,2) = nmat(:,6)+nmat(:,7);
+newnmat(:,3) = nmat(:,4)-20;
\ No newline at end of file