All Downloads are FREE. Search and download functionalities are using the official Maven repository.

be.tarsos.dsp.beatroot.EventList Maven / Gradle / Ivy

There is a newer version: 2.4-1
Show newest version
/*
*      _______                       _____   _____ _____  
*     |__   __|                     |  __ \ / ____|  __ \ 
*        | | __ _ _ __ ___  ___  ___| |  | | (___ | |__) |
*        | |/ _` | '__/ __|/ _ \/ __| |  | |\___ \|  ___/ 
*        | | (_| | |  \__ \ (_) \__ \ |__| |____) | |     
*        |_|\__,_|_|  |___/\___/|___/_____/|_____/|_|     
*                                                         
* -------------------------------------------------------------
*
* TarsosDSP is developed by Joren Six at IPEM, University Ghent
*  
* -------------------------------------------------------------
*
*  Info: http://0110.be/tag/TarsosDSP
*  Github: https://github.com/JorenSix/TarsosDSP
*  Releases: http://0110.be/releases/TarsosDSP/
*  
*  TarsosDSP includes modified source code by various authors,
*  for credits and info, see README.
* 
*/

/*
	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 be.tarsos.dsp.beatroot;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;



// Adapted from eventList::readMatchFile in beatroot/src/eventMidi.cpp

// Reads in a Prolog score+performance (.match) file; returns it as an eventList
// Lines in the match file can be of the form:
//		hammer_bounce-PlayedNote.
//		info(Attribute, Value).
//		insertion-PlayedNote.
//		ornament(Anchor)-PlayedNote.
//		ScoreNote-deletion.
//		ScoreNote-PlayedNote.
//		ScoreNote-trailing_score_note.
//		trailing_played_note-PlayedNote.
//		trill(Anchor)-PlayedNote.
// where ScoreNote is of the form
//		snote(Anchor,[NoteName,Modifier],Octave,Bar:Beat,Offset,Duration,
//				BeatNumber,DurationInBeats,ScoreAttributesList)
//		e.g. snote(n1,[b,b],5,1:1,0,3/16,0,0.75,[s])
// and PlayedNote is of the form
//		note(Number,[NoteName,Modifier],Octave,Onset,Offset,AdjOffset,Velocity)
//		e.g. note(1,[a,#],5,5054,6362,6768,53)

class WormFileParseException extends RuntimeException {

	static final long serialVersionUID = 0;
	public WormFileParseException(String s) {
		super(s);
	} // constructor

} // class WormFileParseException

class MatchFileParseException extends RuntimeException {

	static final long serialVersionUID = 0;
	public MatchFileParseException(String s) {
		super(s);
	} // constructor

} // class MatchFileParseException

class BTFileParseException extends RuntimeException {

	static final long serialVersionUID = 0;
	public BTFileParseException(String s) {
		super(s);
	} // constructor

} // class BTFileParseException


// Process the strings which label extra features of notes in match files.
// We assume no more than 32 distinct labels in a file.
class Flags {

	String[] labels = new String[32];
	int size = 0;
	
	int getFlag(String s) {
		if ((s == null) || s.equals(""))
			return 0;
		//int val = 1;
		for (int i = 0; i < size; i++)
			if (s.equals(labels[i]))
				return 1 << i;
		if (size == 32)	{
			System.err.println("Overflow: Too many flags: " + s);
			size--;
		}
		labels[size] = s;
		return 1 << size++;
	} // getFlag()

	String getLabel(int i) {
		if (i >= size)
			return "ERROR: Unknown flag";
		return labels[i];
	} // getLabel()

} // class Flags


// A score/match/midi file is represented as an EventList object,
//  which contains pointers to the head and tail links, and some
//  class-wide parameters. Parameters are class-wide, as it is
//  assumed that the Worm has only one input file at a time.
public class EventList {

	public LinkedList l;

	protected static boolean timingCorrection = false;
	protected static double timingDisplacement = 0;
	protected static int clockUnits = 480;
	protected static int clockRate = 500000;
	protected static double metricalLevel = 0;
	public static final double UNKNOWN = Double.NaN;
	protected static boolean noMelody = false;
	protected static boolean onlyMelody = false;
	protected static Flags flags = new Flags();

	public EventList() {
		l = new LinkedList();
	} // constructor

	public EventList(EventList e) {
		this();
		ListIterator it = e.listIterator();
		while (it.hasNext())
			add(it.next());
	} // constructor

	public EventList(Event[] e) {
		this();
		for (int i=0; i < e.length; i++)
			add(e[i]);
	} // constructor

	public void add(Event e) {
		l.add(e);
	} // add()

	public void add(EventList ev) {
		l.addAll(ev.l);
	} // add()

	public void insert(Event newEvent, boolean uniqueTimes) {
		ListIterator li = l.listIterator();
		while (li.hasNext()) {
			int sgn = newEvent.compareTo(li.next());
			if (sgn < 0) {
				li.previous();
				break;
			} else if (uniqueTimes && (sgn == 0)) {
				li.remove();
				break;
			}
		}
		li.add(newEvent);
	} // insert()

	public ListIterator listIterator() {
		return l.listIterator();
	} // listIterator()

	public Iterator iterator() {
		return l.iterator();
	} // iterator()

	public int size() {
		return l.size();
	} // size()

	public Event[] toArray() {
		return toArray(0);
	} // toArray()

	public double[] toOnsetArray() {
		double[] d = new double[l.size()];
		int i = 0;
		for (Iterator it = l.iterator(); it.hasNext(); i++)
			d[i] = it.next().keyDown;
		return d;
	} // toOnsetArray()

	public Event[] toArray(int match) {
		int count = 0;
		for (Event e : l)
			if ((match == 0) || (e.midiCommand == match))
				count++;
		Event[] a = new Event[count];
		int i = 0;
		for (Event e : l)
			if ((match == 0) || (e.midiCommand == match))
				a[i++] = e;
		return a;
	} // toArray()

	public void writeBinary(String fileName) {
		try {
			ObjectOutputStream oos = new ObjectOutputStream(
										new FileOutputStream(fileName));
			oos.writeObject(this);
			oos.close();
		} catch (IOException e) {
			System.err.println(e);
		}
	} // writeBinary()

	public static EventList readBinary(String fileName) {
		try {
			ObjectInputStream ois = new ObjectInputStream(
										new FileInputStream(fileName));
			EventList e = (EventList) ois.readObject();
			ois.close();
			return e;
		} catch (IOException e) {
			System.err.println(e);
			return null;
		} catch (ClassNotFoundException e) {
			System.err.println(e);
			return null;
		}
	} // readBinary()

	/*
	public void writeMIDI(String fileName) {
		writeMIDI(fileName, null);
	} // writeMIDI()

	public void writeMIDI(String fileName, EventList pedal) {
		try {
			MidiSystem.write(toMIDI(pedal), 1, new File(fileName));
		} catch (Exception e) {
			System.err.println("Error: Unable to write MIDI file " + fileName);
			e.printStackTrace();
		}
	} // writeMIDI()

	public Sequence toMIDI(EventList pedal) throws InvalidMidiDataException {
		final int midiTempo = 1000000;
		Sequence s = new Sequence(Sequence.PPQ, 1000);
		Track[] tr = new Track[16];
		tr[0] = s.createTrack();
		MetaMessage mm = new MetaMessage();
		byte[] b = new byte[3];
		b[0] = (byte)((midiTempo >> 16) & 0xFF);
		b[1] = (byte)((midiTempo >> 8) & 0xFF);
		b[2] = (byte)(midiTempo & 0xFF);
		mm.setMessage(0x51, b, 3);
		tr[0].add(new MidiEvent(mm, 0L));
		for (Event e : l) {		// from match or beatTrack file
			if (e.midiCommand == 0)	// skip beatTrack file
				break;
			if (tr[e.midiTrack] == null)
				tr[e.midiTrack] = s.createTrack();
			//switch (e.midiCommand) 
			//case ShortMessage.NOTE_ON:
			//case ShortMessage.POLY_PRESSURE:
			//case ShortMessage.CONTROL_CHANGE:
			//case ShortMessage.PROGRAM_CHANGE:
			//case ShortMessage.CHANNEL_PRESSURE:
			//case ShortMessage.PITCH_BEND:
			ShortMessage sm = new ShortMessage();
			sm.setMessage(e.midiCommand, e.midiChannel,
							e.midiPitch, e.midiVelocity);
			tr[e.midiTrack].add(new MidiEvent(sm,
						(long)Math.round(1000 * e.keyDown)));
			if (e.midiCommand == ShortMessage.NOTE_ON) {
				sm = new ShortMessage();
				sm.setMessage(ShortMessage.NOTE_OFF, e.midiChannel, e.midiPitch, 0);
				tr[e.midiTrack].add(new MidiEvent(sm, (long)Math.round(1000 * e.keyUp)));
			}
		}
		if (pedal != null) {	// from MIDI file
	//		if (t.size() > 0)	// otherwise beatTrack files leave an empty trk
	//			t = s.createTrack();
			for (Event e : pedal.l) {
				if (tr[e.midiTrack] == null)
					tr[e.midiTrack] = s.createTrack();
				ShortMessage sm = new ShortMessage();
				sm.setMessage(e.midiCommand, e.midiChannel, 
								e.midiPitch, e.midiVelocity);
				tr[e.midiTrack].add(new MidiEvent(sm,
						(long)Math.round(1000 * e.keyDown)));
				if (e.midiCommand == ShortMessage.NOTE_ON) {
					sm = new ShortMessage();
					sm.setMessage(ShortMessage.NOTE_OFF, e.midiChannel,
									e.midiPitch,e.midiVelocity);
					tr[e.midiTrack].add(new MidiEvent(sm,
							(long)Math.round(1000 * e.keyUp)));
				}
				//catch (InvalidMidiDataException exception) {}
			}
		}
		return s;
	} // toMIDI()

	public static EventList readMidiFile(String fileName) {
		return readMidiFile(fileName, 0);
	} // readMidiFile()

	public static EventList readMidiFile(String fileName, int skipTrackFlag) {
		EventList list = new EventList();
		Sequence s;
		try {
			s = MidiSystem.getSequence(new File(fileName));
		} catch (Exception e) {
			e.printStackTrace();
			return list;
		}
		double midiTempo = 500000;
		double tempoFactor = midiTempo / s.getResolution() / 1000000.0;
		// System.err.println(tempoFactor);
		Event[][] noteOns = new Event[128][16];
		Track[] tracks = s.getTracks();
		for (int t = 0; t < tracks.length; t++, skipTrackFlag >>= 1) {
			if ((skipTrackFlag & 1) == 1)
				continue;
			for (int e = 0; e < tracks[t].size(); e++) {
				MidiEvent me = tracks[t].get(e);
				MidiMessage mm = me.getMessage();
				double time = me.getTick() * tempoFactor;
				byte[] mesg = mm.getMessage();
				int channel = mesg[0] & 0x0F;
				int command = mesg[0] & 0xF0;
				if (command == ShortMessage.NOTE_ON) {
					int pitch = mesg[1] & 0x7F;
					int velocity = mesg[2] & 0x7F;
					if (noteOns[pitch][channel] != null) {
						if (velocity == 0) {	// NOTE_OFF in disguise :(
							noteOns[pitch][channel].keyUp = time;
							noteOns[pitch][channel].pedalUp = time;
							noteOns[pitch][channel] = null;
						} else
 							System.err.println("Double note on: n=" + pitch +
									" c=" + channel +
									" t1=" + noteOns[pitch][channel] +
									" t2=" + time);
					} else {
						Event n = new Event(time, 0, 0, pitch, velocity, -1, -1,
										0, ShortMessage.NOTE_ON, channel, t);
						noteOns[pitch][channel] = n;
						list.add(n);
					}
				} else if (command == ShortMessage.NOTE_OFF) {
					int pitch = mesg[1] & 0x7F;
					noteOns[pitch][channel].keyUp = time;
					noteOns[pitch][channel].pedalUp = time;
					noteOns[pitch][channel] = null;
				} else if (command == 0xF0) {
					if ((channel == 0x0F) && (mesg[1] == 0x51)) {
						midiTempo = (mesg[5] & 0xFF) |
									((mesg[4] & 0xFF) << 8) |
									((mesg[3] & 0xFF) << 16);
						tempoFactor = midiTempo / s.getResolution() / 1000000.0;
					//	System.err.println("Info: Tempo change: " + midiTempo +
					//						"  tf=" + tempoFactor);
					}
				} else if (mesg.length > 3) {
					System.err.println("midi message too long: " + mesg.length);
					System.err.println("\tFirst byte: " + mesg[0]);
				} else {
					int b0 = mesg[0] & 0xFF;
					int b1 = -1;
					int b2 = -1;
					if (mesg.length > 1)
						b1 = mesg[1] & 0xFF;
					if (mesg.length > 2)
						b2 = mesg[2] & 0xFF;
					list.add(new Event(time, time, -1, b1, b2, -1, -1, 0,
										b0 & 0xF0, b0 & 0x0F, t));
				}
			}
		}
		for (int pitch = 0; pitch < 128; pitch++)
			for (int channel = 0; channel < 16; channel++)
				if (noteOns[pitch][channel] != null)
					System.err.println("Missing note off: n=" + 
							noteOns[pitch][channel].midiPitch + " t=" +
							noteOns[pitch][channel].keyDown);
		return list;
	} // readMidiFile()
*/
	public void print() {
		for (Iterator i = l.iterator(); i.hasNext(); )
			i.next().print(flags);
	} // print()

	public static void setTimingCorrection(double corr) {
		timingCorrection = corr >= 0;
		timingDisplacement = corr;
	} // setTimingCorrection()

	
	
	
	
} // class EventList




© 2015 - 2024 Weber Informatics LLC | Privacy Policy