be.tarsos.dsp.beatroot.AgentList Maven / Gradle / Ivy
/*
* _______ _____ _____ _____
* |__ __| | __ \ / ____| __ \
* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
*
* -------------------------------------------------------------
*
* 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.
*
*/
/* BeatRoot: An interactive beat tracking system
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.util.ListIterator;
/** Class for maintaining the set of all Agents involved in beat tracking a piece of music.
* Implements a simple linked list terminated by an AgentList with a null Agent (ag).
*/
public class AgentList {
/** Flag for choice between sum and average beat salience values for Agent scores.
* The use of summed saliences favours faster tempi or lower metrical levels. */
public static boolean useAverageSalience = false;
/** Flag for printing debugging output. */
public static boolean debug = false;
/** For the purpose of removing duplicate agents, the default JND of IBI */
public static final double DEFAULT_BI = 0.02;
/** For the purpose of removing duplicate agents, the default JND of phase */
public static final double DEFAULT_BT = 0.04;
/** A beat tracking Agent */
public Agent ag;
/** The remainder of the linked list */
public AgentList next;
/** The length of the list (number of beat tracking Agents) */
public static int count = 0;
/** For the purpose of removing duplicate agents, the JND of IBI.
* Not changed in the current version. */
public static double thresholdBI = DEFAULT_BI;
/** For the purpose of removing duplicate agents, the JND of phase.
* Not changed in the current version. */
public static double thresholdBT = DEFAULT_BT;
/** Default constructor */
public AgentList() {
this(null, null);
}
/** Constructor for an AgentList: the Agent a is prepended to the list al.
* @param a The Agent at the head of the list
* @param al The tail of the list
*/
public AgentList(Agent a, AgentList al) {
ag = a;
next = al;
if (next == null) {
if (ag != null)
next = new AgentList(); // insert null-terminator if it was forgotten
else {
count = 0;
thresholdBI = DEFAULT_BI;
thresholdBT = DEFAULT_BT;
}
}
} // constructor
/** Deep print of AgentList for debugging */
public void print() {
System.out.println("agentList.print: (size=" + count + ")");
for (AgentList ptr = this; ptr.ag != null; ptr = ptr.next)
ptr.ag.print(2);
System.out.println("End of agentList.print()");
} // print()
/**
* Inserts newAgent into the list in ascending order of beatInterval
* @param a
*/
public void add(Agent a) {
add(a, true);
} // add()/1
/** Appends newAgent to list (sort==false), or inserts newAgent into the list
* in ascending order of beatInterval
* @param newAgent The agent to be added to the list
* @param sort Flag indicating whether the list is sorted or not
*/
public void add(Agent newAgent, boolean sort){
if (newAgent == null)
return;
AgentList ptr;
count++;
for (ptr = this; ptr.ag != null; ptr = ptr.next)
if (sort && (newAgent.beatInterval <= ptr.ag.beatInterval)) {
ptr.next = new AgentList(ptr.ag, ptr.next);
ptr.ag = newAgent;
return;
}
ptr.next = new AgentList();
ptr.ag = newAgent;
} // add()/2
/** Sorts the AgentList by increasing beatInterval, using a bubble sort
* since it is assumed that the list is almost sorted. */
public void sort() {
boolean sorted = false;
while (!sorted) {
sorted = true;
for (AgentList ptr = this; ptr.ag != null; ptr = ptr.next) {
if ((ptr.next.ag != null) &&
(ptr.ag.beatInterval > ptr.next.ag.beatInterval)) {
Agent temp = ptr.ag;
ptr.ag = ptr.next.ag;
ptr.next.ag = temp;
sorted = false;
}
} // for
} // while
} // sort()
/** Removes the current item from the list.
* The current item does not need to be the head of the whole list.
* @param ptr Points to the Agent which is removed from the list
*/
public void remove(AgentList ptr) {
count--;
ptr.ag = ptr.next.ag; // null-terminated list always has next
ptr.next = ptr.next.next;
} // remove()
/** Removes Agents from the list which are duplicates of other Agents.
* A duplicate is defined by the tempo and phase thresholds
* thresholdBI and thresholdBT respectively.
*/
protected void removeDuplicates() {
sort();
for (AgentList ptr = this; ptr.ag != null; ptr = ptr.next) {
if (ptr.ag.phaseScore < 0.0) // already flagged for deletion
continue;
for (AgentList ptr2 = ptr.next; ptr2.ag != null; ptr2 = ptr2.next) {
if (ptr2.ag.beatInterval - ptr.ag.beatInterval > thresholdBI)
break;
if (Math.abs(ptr.ag.beatTime - ptr2.ag.beatTime) > thresholdBT)
continue;
if (ptr.ag.phaseScore < ptr2.ag.phaseScore) {
ptr.ag.phaseScore = -1.0; // flag for deletion
if (ptr2.ag.topScoreTime < ptr.ag.topScoreTime)
ptr2.ag.topScoreTime = ptr.ag.topScoreTime;
break;
} else {
ptr2.ag.phaseScore = -1.0; // flag for deletion
if (ptr.ag.topScoreTime < ptr2.ag.topScoreTime)
ptr.ag.topScoreTime = ptr2.ag.topScoreTime;
}
}
}
for (AgentList ptr = this; ptr.ag != null; ) {
if (ptr.ag.phaseScore < 0.0) {
remove(ptr);
} else
ptr = ptr.next;
}
} // removeDuplicates()
/** Perform beat tracking on a list of events (onsets).
* @param el The list of onsets (or events or peaks) to beat track
*/
public void beatTrack(EventList el) {
beatTrack(el, -1.0);
} // beatTrack()/1
/** Perform beat tracking on a list of events (onsets).
* @param el The list of onsets (or events or peaks) to beat track.
* @param stop Do not find beats after stop
seconds.
*/
public void beatTrack(EventList el, double stop) {
ListIterator ptr = el.listIterator();
boolean phaseGiven = (ag != null) &&
(ag.beatTime >= 0); // if given for one, assume given for others
while (ptr.hasNext()) {
Event ev = ptr.next();
if ((stop > 0) && (ev.keyDown > stop))
break;
boolean created = phaseGiven;
double prevBeatInterval = -1.0;
for (AgentList ap = this; ap.ag != null; ap = ap.next) {
Agent currentAgent = ap.ag;
if (currentAgent.beatInterval != prevBeatInterval) {
if ((prevBeatInterval>=0) && !created && (ev.keyDown<5.0)) {
// Create new agent with different phase
Agent newAgent = new Agent(prevBeatInterval);
newAgent.considerAsBeat(ev, this);
add(newAgent);
}
prevBeatInterval = currentAgent.beatInterval;
created = phaseGiven;
}
if (currentAgent.considerAsBeat(ev, this))
created = true;
if (currentAgent != ap.ag) // new one been inserted, skip it
ap = ap.next;
} // loop for each agent
removeDuplicates();
} // loop for each event
} // beatTrack()
/** Finds the Agent with the highest score in the list.
* @return The Agent with the highest score
*/
public Agent bestAgent() {
double best = -1.0;
Agent bestAg = null;
for (AgentList ap = this; ap.ag != null; ap = ap.next) {
double startTime = ap.ag.events.l.getFirst().keyDown;
double conf = (ap.ag.phaseScore + ap.ag.tempoScore) /
(useAverageSalience? (double)ap.ag.beatCount: 1.0);
if (conf > best) {
bestAg = ap.ag;
best = conf;
}
if (debug) {
ap.ag.print(0);
System.out.printf(" +%5.3f Av-salience = %3.1f\n",
startTime, conf);
}
}
if (debug) {
if (bestAg != null) {
System.out.print("Best ");
bestAg.print(0);
System.out.printf(" Av-salience = %5.1f\n", best);
// bestAg.events.print();
} else
System.out.println("No surviving agent - beat tracking failed");
}
return bestAg;
} // bestAgent()
} // class AgentList
© 2015 - 2024 Weber Informatics LLC | Privacy Policy