plectrum

Plectrum: instrument tuner for Android
Log | Files | Refs | README | LICENSE

EventList.java (12854B)


      1 /*
      2 *      _______                       _____   _____ _____  
      3 *     |__   __|                     |  __ \ / ____|  __ \ 
      4 *        | | __ _ _ __ ___  ___  ___| |  | | (___ | |__) |
      5 *        | |/ _` | '__/ __|/ _ \/ __| |  | |\___ \|  ___/ 
      6 *        | | (_| | |  \__ \ (_) \__ \ |__| |____) | |     
      7 *        |_|\__,_|_|  |___/\___/|___/_____/|_____/|_|     
      8 *                                                         
      9 * -------------------------------------------------------------
     10 *
     11 * TarsosDSP is developed by Joren Six at IPEM, University Ghent
     12 *  
     13 * -------------------------------------------------------------
     14 *
     15 *  Info: http://0110.be/tag/TarsosDSP
     16 *  Github: https://github.com/JorenSix/TarsosDSP
     17 *  Releases: http://0110.be/releases/TarsosDSP/
     18 *  
     19 *  TarsosDSP includes modified source code by various authors,
     20 *  for credits and info, see README.
     21 * 
     22 */
     23 
     24 /*
     25 	Copyright (C) 2001, 2006 by Simon Dixon
     26 
     27 	This program is free software; you can redistribute it and/or modify
     28 	it under the terms of the GNU General Public License as published by
     29 	the Free Software Foundation; either version 2 of the License, or
     30 	(at your option) any later version.
     31 
     32 	This program is distributed in the hope that it will be useful,
     33 	but WITHOUT ANY WARRANTY; without even the implied warranty of
     34 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     35 	GNU General Public License for more details.
     36 
     37 	You should have received a copy of the GNU General Public License along
     38 	with this program (the file gpl.txt); if not, download it from
     39 	http://www.gnu.org/licenses/gpl.txt or write to the
     40 	Free Software Foundation, Inc.,
     41 	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     42 */
     43 
     44 package be.tarsos.dsp.beatroot;
     45 
     46 import java.io.FileInputStream;
     47 import java.io.FileOutputStream;
     48 import java.io.IOException;
     49 import java.io.ObjectInputStream;
     50 import java.io.ObjectOutputStream;
     51 import java.util.Iterator;
     52 import java.util.LinkedList;
     53 import java.util.ListIterator;
     54 
     55 
     56 
     57 // Adapted from eventList::readMatchFile in beatroot/src/eventMidi.cpp
     58 
     59 // Reads in a Prolog score+performance (.match) file; returns it as an eventList
     60 // Lines in the match file can be of the form:
     61 //		hammer_bounce-PlayedNote.
     62 //		info(Attribute, Value).
     63 //		insertion-PlayedNote.
     64 //		ornament(Anchor)-PlayedNote.
     65 //		ScoreNote-deletion.
     66 //		ScoreNote-PlayedNote.
     67 //		ScoreNote-trailing_score_note.
     68 //		trailing_played_note-PlayedNote.
     69 //		trill(Anchor)-PlayedNote.
     70 // where ScoreNote is of the form
     71 //		snote(Anchor,[NoteName,Modifier],Octave,Bar:Beat,Offset,Duration,
     72 //				BeatNumber,DurationInBeats,ScoreAttributesList)
     73 //		e.g. snote(n1,[b,b],5,1:1,0,3/16,0,0.75,[s])
     74 // and PlayedNote is of the form
     75 //		note(Number,[NoteName,Modifier],Octave,Onset,Offset,AdjOffset,Velocity)
     76 //		e.g. note(1,[a,#],5,5054,6362,6768,53)
     77 
     78 class WormFileParseException extends RuntimeException {
     79 
     80 	static final long serialVersionUID = 0;
     81 	public WormFileParseException(String s) {
     82 		super(s);
     83 	} // constructor
     84 
     85 } // class WormFileParseException
     86 
     87 class MatchFileParseException extends RuntimeException {
     88 
     89 	static final long serialVersionUID = 0;
     90 	public MatchFileParseException(String s) {
     91 		super(s);
     92 	} // constructor
     93 
     94 } // class MatchFileParseException
     95 
     96 class BTFileParseException extends RuntimeException {
     97 
     98 	static final long serialVersionUID = 0;
     99 	public BTFileParseException(String s) {
    100 		super(s);
    101 	} // constructor
    102 
    103 } // class BTFileParseException
    104 
    105 
    106 // Process the strings which label extra features of notes in match files.
    107 // We assume no more than 32 distinct labels in a file.
    108 class Flags {
    109 
    110 	String[] labels = new String[32];
    111 	int size = 0;
    112 	
    113 	int getFlag(String s) {
    114 		if ((s == null) || s.equals(""))
    115 			return 0;
    116 		//int val = 1;
    117 		for (int i = 0; i < size; i++)
    118 			if (s.equals(labels[i]))
    119 				return 1 << i;
    120 		if (size == 32)	{
    121 			System.err.println("Overflow: Too many flags: " + s);
    122 			size--;
    123 		}
    124 		labels[size] = s;
    125 		return 1 << size++;
    126 	} // getFlag()
    127 
    128 	String getLabel(int i) {
    129 		if (i >= size)
    130 			return "ERROR: Unknown flag";
    131 		return labels[i];
    132 	} // getLabel()
    133 
    134 } // class Flags
    135 
    136 
    137 // A score/match/midi file is represented as an EventList object,
    138 //  which contains pointers to the head and tail links, and some
    139 //  class-wide parameters. Parameters are class-wide, as it is
    140 //  assumed that the Worm has only one input file at a time.
    141 public class EventList {
    142 
    143 	public LinkedList<Event> l;
    144 
    145 	protected static boolean timingCorrection = false;
    146 	protected static double timingDisplacement = 0;
    147 	protected static int clockUnits = 480;
    148 	protected static int clockRate = 500000;
    149 	protected static double metricalLevel = 0;
    150 	public static final double UNKNOWN = Double.NaN;
    151 	protected static boolean noMelody = false;
    152 	protected static boolean onlyMelody = false;
    153 	protected static Flags flags = new Flags();
    154 
    155 	public EventList() {
    156 		l = new LinkedList<Event>();
    157 	} // constructor
    158 
    159 	public EventList(EventList e) {
    160 		this();
    161 		ListIterator<Event> it = e.listIterator();
    162 		while (it.hasNext())
    163 			add(it.next());
    164 	} // constructor
    165 
    166 	public EventList(Event[] e) {
    167 		this();
    168 		for (int i=0; i < e.length; i++)
    169 			add(e[i]);
    170 	} // constructor
    171 
    172 	public void add(Event e) {
    173 		l.add(e);
    174 	} // add()
    175 
    176 	public void add(EventList ev) {
    177 		l.addAll(ev.l);
    178 	} // add()
    179 
    180 	public void insert(Event newEvent, boolean uniqueTimes) {
    181 		ListIterator<Event> li = l.listIterator();
    182 		while (li.hasNext()) {
    183 			int sgn = newEvent.compareTo(li.next());
    184 			if (sgn < 0) {
    185 				li.previous();
    186 				break;
    187 			} else if (uniqueTimes && (sgn == 0)) {
    188 				li.remove();
    189 				break;
    190 			}
    191 		}
    192 		li.add(newEvent);
    193 	} // insert()
    194 
    195 	public ListIterator<Event> listIterator() {
    196 		return l.listIterator();
    197 	} // listIterator()
    198 
    199 	public Iterator<Event> iterator() {
    200 		return l.iterator();
    201 	} // iterator()
    202 
    203 	public int size() {
    204 		return l.size();
    205 	} // size()
    206 
    207 	public Event[] toArray() {
    208 		return toArray(0);
    209 	} // toArray()
    210 
    211 	public double[] toOnsetArray() {
    212 		double[] d = new double[l.size()];
    213 		int i = 0;
    214 		for (Iterator<Event> it = l.iterator(); it.hasNext(); i++)
    215 			d[i] = it.next().keyDown;
    216 		return d;
    217 	} // toOnsetArray()
    218 
    219 	public Event[] toArray(int match) {
    220 		int count = 0;
    221 		for (Event e : l)
    222 			if ((match == 0) || (e.midiCommand == match))
    223 				count++;
    224 		Event[] a = new Event[count];
    225 		int i = 0;
    226 		for (Event e : l)
    227 			if ((match == 0) || (e.midiCommand == match))
    228 				a[i++] = e;
    229 		return a;
    230 	} // toArray()
    231 
    232 	public void writeBinary(String fileName) {
    233 		try {
    234 			ObjectOutputStream oos = new ObjectOutputStream(
    235 										new FileOutputStream(fileName));
    236 			oos.writeObject(this);
    237 			oos.close();
    238 		} catch (IOException e) {
    239 			System.err.println(e);
    240 		}
    241 	} // writeBinary()
    242 
    243 	public static EventList readBinary(String fileName) {
    244 		try {
    245 			ObjectInputStream ois = new ObjectInputStream(
    246 										new FileInputStream(fileName));
    247 			EventList e = (EventList) ois.readObject();
    248 			ois.close();
    249 			return e;
    250 		} catch (IOException e) {
    251 			System.err.println(e);
    252 			return null;
    253 		} catch (ClassNotFoundException e) {
    254 			System.err.println(e);
    255 			return null;
    256 		}
    257 	} // readBinary()
    258 
    259 	/*
    260 	public void writeMIDI(String fileName) {
    261 		writeMIDI(fileName, null);
    262 	} // writeMIDI()
    263 
    264 	public void writeMIDI(String fileName, EventList pedal) {
    265 		try {
    266 			MidiSystem.write(toMIDI(pedal), 1, new File(fileName));
    267 		} catch (Exception e) {
    268 			System.err.println("Error: Unable to write MIDI file " + fileName);
    269 			e.printStackTrace();
    270 		}
    271 	} // writeMIDI()
    272 
    273 	public Sequence toMIDI(EventList pedal) throws InvalidMidiDataException {
    274 		final int midiTempo = 1000000;
    275 		Sequence s = new Sequence(Sequence.PPQ, 1000);
    276 		Track[] tr = new Track[16];
    277 		tr[0] = s.createTrack();
    278 		MetaMessage mm = new MetaMessage();
    279 		byte[] b = new byte[3];
    280 		b[0] = (byte)((midiTempo >> 16) & 0xFF);
    281 		b[1] = (byte)((midiTempo >> 8) & 0xFF);
    282 		b[2] = (byte)(midiTempo & 0xFF);
    283 		mm.setMessage(0x51, b, 3);
    284 		tr[0].add(new MidiEvent(mm, 0L));
    285 		for (Event e : l) {		// from match or beatTrack file
    286 			if (e.midiCommand == 0)	// skip beatTrack file
    287 				break;
    288 			if (tr[e.midiTrack] == null)
    289 				tr[e.midiTrack] = s.createTrack();
    290 			//switch (e.midiCommand) 
    291 			//case ShortMessage.NOTE_ON:
    292 			//case ShortMessage.POLY_PRESSURE:
    293 			//case ShortMessage.CONTROL_CHANGE:
    294 			//case ShortMessage.PROGRAM_CHANGE:
    295 			//case ShortMessage.CHANNEL_PRESSURE:
    296 			//case ShortMessage.PITCH_BEND:
    297 			ShortMessage sm = new ShortMessage();
    298 			sm.setMessage(e.midiCommand, e.midiChannel,
    299 							e.midiPitch, e.midiVelocity);
    300 			tr[e.midiTrack].add(new MidiEvent(sm,
    301 						(long)Math.round(1000 * e.keyDown)));
    302 			if (e.midiCommand == ShortMessage.NOTE_ON) {
    303 				sm = new ShortMessage();
    304 				sm.setMessage(ShortMessage.NOTE_OFF, e.midiChannel, e.midiPitch, 0);
    305 				tr[e.midiTrack].add(new MidiEvent(sm, (long)Math.round(1000 * e.keyUp)));
    306 			}
    307 		}
    308 		if (pedal != null) {	// from MIDI file
    309 	//		if (t.size() > 0)	// otherwise beatTrack files leave an empty trk
    310 	//			t = s.createTrack();
    311 			for (Event e : pedal.l) {
    312 				if (tr[e.midiTrack] == null)
    313 					tr[e.midiTrack] = s.createTrack();
    314 				ShortMessage sm = new ShortMessage();
    315 				sm.setMessage(e.midiCommand, e.midiChannel, 
    316 								e.midiPitch, e.midiVelocity);
    317 				tr[e.midiTrack].add(new MidiEvent(sm,
    318 						(long)Math.round(1000 * e.keyDown)));
    319 				if (e.midiCommand == ShortMessage.NOTE_ON) {
    320 					sm = new ShortMessage();
    321 					sm.setMessage(ShortMessage.NOTE_OFF, e.midiChannel,
    322 									e.midiPitch,e.midiVelocity);
    323 					tr[e.midiTrack].add(new MidiEvent(sm,
    324 							(long)Math.round(1000 * e.keyUp)));
    325 				}
    326 				//catch (InvalidMidiDataException exception) {}
    327 			}
    328 		}
    329 		return s;
    330 	} // toMIDI()
    331 
    332 	public static EventList readMidiFile(String fileName) {
    333 		return readMidiFile(fileName, 0);
    334 	} // readMidiFile()
    335 
    336 	public static EventList readMidiFile(String fileName, int skipTrackFlag) {
    337 		EventList list = new EventList();
    338 		Sequence s;
    339 		try {
    340 			s = MidiSystem.getSequence(new File(fileName));
    341 		} catch (Exception e) {
    342 			e.printStackTrace();
    343 			return list;
    344 		}
    345 		double midiTempo = 500000;
    346 		double tempoFactor = midiTempo / s.getResolution() / 1000000.0;
    347 		// System.err.println(tempoFactor);
    348 		Event[][] noteOns = new Event[128][16];
    349 		Track[] tracks = s.getTracks();
    350 		for (int t = 0; t < tracks.length; t++, skipTrackFlag >>= 1) {
    351 			if ((skipTrackFlag & 1) == 1)
    352 				continue;
    353 			for (int e = 0; e < tracks[t].size(); e++) {
    354 				MidiEvent me = tracks[t].get(e);
    355 				MidiMessage mm = me.getMessage();
    356 				double time = me.getTick() * tempoFactor;
    357 				byte[] mesg = mm.getMessage();
    358 				int channel = mesg[0] & 0x0F;
    359 				int command = mesg[0] & 0xF0;
    360 				if (command == ShortMessage.NOTE_ON) {
    361 					int pitch = mesg[1] & 0x7F;
    362 					int velocity = mesg[2] & 0x7F;
    363 					if (noteOns[pitch][channel] != null) {
    364 						if (velocity == 0) {	// NOTE_OFF in disguise :(
    365 							noteOns[pitch][channel].keyUp = time;
    366 							noteOns[pitch][channel].pedalUp = time;
    367 							noteOns[pitch][channel] = null;
    368 						} else
    369  							System.err.println("Double note on: n=" + pitch +
    370 									" c=" + channel +
    371 									" t1=" + noteOns[pitch][channel] +
    372 									" t2=" + time);
    373 					} else {
    374 						Event n = new Event(time, 0, 0, pitch, velocity, -1, -1,
    375 										0, ShortMessage.NOTE_ON, channel, t);
    376 						noteOns[pitch][channel] = n;
    377 						list.add(n);
    378 					}
    379 				} else if (command == ShortMessage.NOTE_OFF) {
    380 					int pitch = mesg[1] & 0x7F;
    381 					noteOns[pitch][channel].keyUp = time;
    382 					noteOns[pitch][channel].pedalUp = time;
    383 					noteOns[pitch][channel] = null;
    384 				} else if (command == 0xF0) {
    385 					if ((channel == 0x0F) && (mesg[1] == 0x51)) {
    386 						midiTempo = (mesg[5] & 0xFF) |
    387 									((mesg[4] & 0xFF) << 8) |
    388 									((mesg[3] & 0xFF) << 16);
    389 						tempoFactor = midiTempo / s.getResolution() / 1000000.0;
    390 					//	System.err.println("Info: Tempo change: " + midiTempo +
    391 					//						"  tf=" + tempoFactor);
    392 					}
    393 				} else if (mesg.length > 3) {
    394 					System.err.println("midi message too long: " + mesg.length);
    395 					System.err.println("\tFirst byte: " + mesg[0]);
    396 				} else {
    397 					int b0 = mesg[0] & 0xFF;
    398 					int b1 = -1;
    399 					int b2 = -1;
    400 					if (mesg.length > 1)
    401 						b1 = mesg[1] & 0xFF;
    402 					if (mesg.length > 2)
    403 						b2 = mesg[2] & 0xFF;
    404 					list.add(new Event(time, time, -1, b1, b2, -1, -1, 0,
    405 										b0 & 0xF0, b0 & 0x0F, t));
    406 				}
    407 			}
    408 		}
    409 		for (int pitch = 0; pitch < 128; pitch++)
    410 			for (int channel = 0; channel < 16; channel++)
    411 				if (noteOns[pitch][channel] != null)
    412 					System.err.println("Missing note off: n=" + 
    413 							noteOns[pitch][channel].midiPitch + " t=" +
    414 							noteOns[pitch][channel].keyDown);
    415 		return list;
    416 	} // readMidiFile()
    417 */
    418 	public void print() {
    419 		for (Iterator<Event> i = l.iterator(); i.hasNext(); )
    420 			i.next().print(flags);
    421 	} // print()
    422 
    423 	public static void setTimingCorrection(double corr) {
    424 		timingCorrection = corr >= 0;
    425 		timingDisplacement = corr;
    426 	} // setTimingCorrection()
    427 
    428 	
    429 	
    430 	
    431 	
    432 } // class EventList