Chris@138: Chris@138: // Take two lab files of the form Chris@138: // Chris@138: // onset offset frequency Chris@138: // Chris@138: // and report their onset-only note-level accuracy F-measure. Chris@138: Chris@138: program evaluate_lab; Chris@138: Chris@138: usage () = Chris@138: (eprintln "\nUsage: evaluate_lab reqd reference.lab transcribed.lab\n"; Chris@138: eprintln "where reqd is the number of milliseconds allowed for timing error (+/-)\n"); Chris@138: Chris@138: toMIDIPitch f = Chris@138: round (12 * (Math#log(f / 220) / Math#log(2)) + 57); Chris@138: Chris@138: suck f = Chris@138: (str = openInFile f "UTF-8"; // better to use readFile here, oh well Chris@138: d = map do line: Chris@138: case list (strSplit "\t" line) of Chris@138: onset::offset::frequency::_: Chris@138: { onset = number onset, midi = toMIDIPitch (number frequency) }; Chris@138: _: Chris@138: failWith "badly formed line: \(line)"; Chris@138: esac; Chris@138: done (str.lines ()); Chris@138: str.close (); Chris@138: d); Chris@138: Chris@138: select f = fold do r x: if f x then x::r else r fi done []; Chris@138: Chris@138: evaluate permitted ref trans = Chris@138: (reference = suck ref; Chris@138: transcribed = suck trans; Chris@138: accurate = Chris@138: select do here : Chris@138: any do other: Chris@138: here.midi == other.midi and Chris@138: abs (here.onset - other.onset) < permitted Chris@138: done reference Chris@138: done transcribed; Chris@138: { Chris@138: ntot = length transcribed, Chris@138: nref = length reference, Chris@138: ncorr = length accurate, Chris@138: }); Chris@138: Chris@138: pc n = Chris@138: int (n * 1000) / 10; Chris@138: Chris@138: report { ntot, nref, ncorr } = Chris@138: (nfp = ntot - ncorr; Chris@138: nfn = nref - ncorr; Chris@222: if nref == 0 then Chris@222: println "ERROR: no events in reference!" Chris@222: elif ntot == 0 then Chris@222: println "WARNING: no events transcribed!" Chris@222: else Chris@222: rec = ncorr / nref; Chris@222: pre = ncorr / ntot; Chris@222: f = if pre + rec == 0 then 0 else 2 * ((pre * rec) / (pre + rec)) fi; Chris@222: acc = ncorr / (ncorr + nfp + nfn); Chris@222: println "precision \(pc pre), recall \(pc rec), accuracy \(pc acc), F \(pc f)"; Chris@222: fi); Chris@138: Chris@138: case (list _argv) of Chris@138: reqd::ref::trans::[]: report (evaluate (number reqd / 1000) ref trans); Chris@138: _: usage (); Chris@138: esac; Chris@138: