Mercurial > hg > beatroot
view at/ofai/music/worm/WormFile.java @ 2:4c3f5bc01c97
* Import BeatRoot v0.5.7
author | Chris Cannam |
---|---|
date | Fri, 08 Oct 2010 16:11:06 +0100 |
parents | |
children |
line wrap: on
line source
/* Performance Worm: Visualisation of Expressive Musical Performance Copyright (C) 2001, 2006 by Simon Dixon This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 this program (the file gpl.txt); if not, download it from http://www.gnu.org/licenses/gpl.txt or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package at.ofai.music.worm; import java.io.File; import java.io.FileOutputStream; import java.io.PrintStream; import java.io.FileReader; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.util.StringTokenizer; import java.util.Iterator; import java.awt.Frame; import at.ofai.music.util.Event; import at.ofai.music.util.EventList; import at.ofai.music.util.Format; import at.ofai.music.util.MatchTempoMap; // Read/write performance worm data public class WormFile { Worm worm; double outFramePeriod, inFramePeriod; int length; double[] time; double[] inTempo, outTempo; double[] inIntensity, outIntensity; int[] inFlags, outFlags; String[] label; public static final int TRACK=1, BEAT=2, BAR=4, SEG1=8, SEG2=16, SEG3=32, SEG4=64; public static final double defaultFramePeriod = 0.1; // 10 FPS WormParameters info; private WormFile(Frame f) { info = new WormParameters(f); inFramePeriod = defaultFramePeriod; outFramePeriod = defaultFramePeriod; } // shared constructor public WormFile(int size) { this(null); length = size; init(); } // constructor public WormFile(int size, double step) { this(size); inFramePeriod = step; } // constructor public WormFile(EventList el, double step) { this(null); inFramePeriod = step; convertList(el); } // constructor public WormFile(Worm w, EventList el) { this(w == null? null: w.theFrame); worm = w; convertList(el); } // constructor public WormFile(Worm w, String fileName) { this(w.theFrame); worm = w; read(fileName); } // constructor public void init() { inTempo = new double[length]; inIntensity = new double[length]; inFlags = new int[length]; time = new double[length]; } // init() public void smooth(int mode, double left, double right, int smoothLevel) { if (worm != null) worm.setSmoothMode(Worm.NONE); info.smoothing = "None"; if ((outFramePeriod == 0) || ((inFramePeriod == 0) && (time == null))) { System.err.println("Error: smooth() frameLength unspecified"); return; } if (inFramePeriod != 0) { for (int i = 0; i < length; i++) time[i] = inFramePeriod * i; } int outLength = 1+(int) Math.ceil(time[time.length-1] / outFramePeriod); if ((outTempo == null) || (outTempo.length != outLength)) { outTempo = new double[outLength]; outIntensity = new double[outLength]; outFlags = new int[outLength]; label = new String[outLength]; } if (mode == Worm.NONE) { int i = 0, o = 0; while (o * outFramePeriod < time[0]) { outTempo[o] = inTempo[0]; outIntensity[o] = inIntensity[0]; o++; } for ( ; i < time.length - 1; i++) { while (o * outFramePeriod < time[i+1]) { outTempo[o] = inTempo[i]; outIntensity[o] = inIntensity[i]; o++; } } while (o < outLength) { outTempo[o] = inTempo[i]; outIntensity[o] = inIntensity[i]; o++; } } else { info.smoothing = "Gaussian" + "\t" + Format.d(left, 4) + "\t" + Format.d(right, 4); if (smoothLevel != 0) { int count = 0; double first = 0, last = 0; for (int i = 0; i < time.length; i++) if ((inFlags[i] & smoothLevel) != 0) { if (count == 0) first = time[i]; else last = time[i]; count++; } if (count < 2) System.err.println("Warning: Beat data not available"); else { double IBI = (last - first) / (count - 1); left *= IBI; right *= IBI; info.smoothing += "\t" +Format.d(IBI,4) + "\t" +smoothLevel; System.out.println("Smoothing parameters (seconds): pre=" + Format.d(left,3) + " post=" + Format.d(right,3)); } } int start = 0; for (int o = 0; o < outLength; o++) { double sum = 0, val = 0, tempo = 0, intensity = 0; for (int i = start; i < time.length; i++) { double d = o * outFramePeriod - time[i]; if (d > 4 * left) { // average over 4 stddevs start++; continue; } if (d < -4 * right) break; if (d < 0) val = Math.exp(-d*d/(left*left*2)); else val = Math.exp(-d*d/(right*right*2)); sum += val; tempo += val * inTempo[i]; intensity += val * inIntensity[i]; } if (sum == 0) { // assume this only occurs at beginning outTempo[o] = inTempo[0]; outIntensity[o] = inIntensity[0]; } else { outTempo[o] = tempo / sum; outIntensity[o] = intensity / sum; } } } for (int i = 0; i < outFlags.length; i++) outFlags[i] = 0; for (int i = 0; i < inFlags.length; i++) outFlags[(int)Math.round(time[i] / outFramePeriod)] |= inFlags[i]; int bar = 0; int beat = 0; int track = 0; for (int i = 0; i < outFlags.length; i++) { if ((outFlags[i] & BAR) != 0) bar++; if ((outFlags[i] & BEAT) != 0) beat++; if ((outFlags[i] & TRACK) != 0) track++; label[i] = bar + ":" + beat + ":" + track + ":" + Format.d(i * outFramePeriod, 1); } } // smooth() public void editParameters() { info.editParameters(); update(); } // editParameters() public void update() { length = info.length; inFramePeriod = info.framePeriod; worm.setTitle(info.composer + ", " + info.piece + ", played by " + info.performer); // not used (?) : beatLevel trackLevel upbeat beatsPerBar if ((inTempo == null) || (inTempo.length != length)) init(); worm.setInputFile(info.audioPath, info.audioFile); worm.setSmoothMode(Worm.NONE); if (info.axis.length() > 0) worm.setAxis(info.axis); worm.setFramePeriod(outFramePeriod); worm.setLoudnessUnits(info.loudnessUnits); } // update() public void convertList(EventList el) { double tMax = 0; int count = 0; for (Iterator<Event> i = el.iterator(); i.hasNext(); ) { double pedalUpTime = i.next().pedalUp; if (pedalUpTime > tMax) tMax = pedalUpTime; count++; } length = (int)Math.ceil(tMax / inFramePeriod); init(); // double[] decayFactor = new double[128]; // for (int i = 0; i < 128; i++) // decayFactor[i] = Math.max(5.0, (i - 6.0) / 3.0) * inFramePeriod; // // was Math.pow(0.1, inFramePeriod); // modify for pitch? for (Iterator<Event> i = el.l.iterator(); i.hasNext(); ) { Event e = i.next(); double loudness = 30.29 * Math.pow(e.midiVelocity, 0.2609); loudness += (e.midiPitch - 66.0) / 12.0; // +1dB / oct int start = (int)Math.floor(e.keyDown / inFramePeriod); if (start < 0) start = 0; int stop = (int)Math.ceil((e.pedalUp + 0.5) / inFramePeriod); if (stop > inIntensity.length) stop = inIntensity.length; for (int t = start; t < stop; t++) { if (loudness > inIntensity[t]) inIntensity[t] = loudness; loudness -= Math.max(5.0, (e.midiPitch - 6.0) / 3.0) * inFramePeriod; // was: mult by decay factor. But since vals are dB, we subtract } } MatchTempoMap tMap = new MatchTempoMap(count); for (Iterator<Event> i = el.l.iterator(); i.hasNext(); ) { Event e = i.next(); tMap.add(e.keyDown, e.scoreBeat); } // el.print(); // tMap.print(); // for debugging tMap.dump(inTempo, inFramePeriod); } // convertList() public void write(String fileName) { PrintStream out; try { out = new PrintStream(new FileOutputStream(fileName)); } catch (FileNotFoundException e) { System.err.println("Unable to open output file " + fileName); return; } info.write(out, outTempo.length, outFramePeriod); for (int i = 0; i < outTempo.length; i++) { if (outFramePeriod == 0) out.print(Format.d(time[i],3) + " "); out.println(Format.d(outTempo[i],4) +" "+ Format.d(outIntensity[i],4) +" "+outFlags[i]); } out.close(); } // write() public void read(String fileName) { try { File f = new File(fileName); if (!f.isFile()) // a local hack for UNC file names under Windows f = new File("//fichte" + fileName); if (!f.isFile()) throw(new FileNotFoundException("Could not open " + fileName)); BufferedReader in = new BufferedReader(new FileReader(f)); String input = info.read(in); update(); int index = 0; int bar = 0; while ((input != null) && (index < length)) { StringTokenizer tk = new StringTokenizer(input); if (inFramePeriod != 0) time[index] = Double.parseDouble(tk.nextToken()); inTempo[index] = Double.parseDouble(tk.nextToken()); inIntensity[index] = Double.parseDouble(tk.nextToken()); if (tk.hasMoreTokens()) inFlags[index] = Integer.parseInt(tk.nextToken()); else inFlags[index] = 0; input = in.readLine(); index++; } in.close(); smooth(Worm.NONE, 0, 0, 0); } catch (FileNotFoundException e) { System.err.println(e); } catch (IOException e) { System.err.println("IOException reading " + fileName); } catch (Exception e) { System.err.println("Error parsing file " + fileName + ": " + e); e.printStackTrace(); } } // read() } // class WormFile