Chris@8: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@8: Chris@8: /* Chris@8: Vamp feature extraction plugin for the BeatRoot beat tracker. Chris@8: Chris@8: Centre for Digital Music, Queen Mary, University of London. Chris@8: This file copyright 2011 Simon Dixon, Chris Cannam and QMUL. Chris@8: Chris@8: This program is free software; you can redistribute it and/or Chris@8: modify it under the terms of the GNU General Public License as Chris@8: published by the Free Software Foundation; either version 2 of the Chris@8: License, or (at your option) any later version. See the file Chris@8: COPYING included with this distribution for more information. Chris@8: */ Chris@8: Chris@8: #include "AgentList.h" Chris@8: Chris@8: bool AgentList::useAverageSalience = false; Chris@8: const double AgentList::DEFAULT_BI = 0.02; Chris@8: const double AgentList::DEFAULT_BT = 0.04; Chris@8: Chris@15: void AgentList::removeDuplicates() Chris@15: { Chris@15: sort(); Chris@15: for (iterator itr = begin(); itr != end(); ++itr) { Chris@22: #ifdef DEBUG_BEATROOT Chris@22: std::cerr << "removeDuplicates: considering agent " << (*itr)->idNumber << std::endl; Chris@22: #endif Chris@16: if ((*itr)->phaseScore < 0.0) // already flagged for deletion Chris@15: continue; Chris@15: iterator itr2 = itr; Chris@15: for (++itr2; itr2 != end(); ++itr2) { Chris@16: if ((*itr2)->beatInterval - (*itr)->beatInterval > DEFAULT_BI) Chris@15: break; Chris@16: if (fabs((*itr)->beatTime - (*itr2)->beatTime) > DEFAULT_BT) Chris@15: continue; Chris@16: if ((*itr)->phaseScore < (*itr2)->phaseScore) { Chris@22: #ifdef DEBUG_BEATROOT Chris@22: std::cerr << "agent " << (*itr)->idNumber << " is similar to but lower-scoring than agent " << (*itr2)->idNumber << ", marking for deletion" << std::endl; Chris@22: #endif Chris@16: (*itr)->phaseScore = -1.0; // flag for deletion Chris@16: if ((*itr2)->topScoreTime < (*itr)->topScoreTime) Chris@16: (*itr2)->topScoreTime = (*itr)->topScoreTime; Chris@15: break; Chris@15: } else { Chris@22: #ifdef DEBUG_BEATROOT Chris@22: std::cerr << "agent " << (*itr2)->idNumber << " is similar to but lower-scoring than agent " << (*itr)->idNumber << ", marking for deletion" << std::endl; Chris@22: #endif Chris@16: (*itr2)->phaseScore = -1.0; // flag for deletion Chris@16: if ((*itr)->topScoreTime < (*itr2)->topScoreTime) Chris@16: (*itr)->topScoreTime = (*itr2)->topScoreTime; Chris@15: } Chris@15: } Chris@15: } Chris@15: int removed = 0; Chris@15: for (iterator itr = begin(); itr != end(); ) { Chris@16: if ((*itr)->phaseScore < 0.0) { Chris@15: ++removed; Chris@16: delete *itr; Chris@15: list.erase(itr); Chris@15: } else { Chris@15: ++itr; Chris@15: } Chris@15: } Chris@15: #ifdef DEBUG_BEATROOT Chris@15: if (removed > 0) { Chris@15: std::cerr << "removeDuplicates: removed " << removed << ", have " Chris@15: << list.size() << " agent(s) remaining" << std::endl; Chris@15: } Chris@15: int n = 0; Chris@15: for (Container::iterator i = list.begin(); i != list.end(); ++i) { Chris@16: std::cerr << "agent " << n++ << ": time " << (*i)->beatTime << std::endl; Chris@15: } Chris@15: #endif Chris@15: } // removeDuplicates() Chris@15: Chris@15: Chris@23: void AgentList::beatTrack(EventList el, AgentParameters params, double stop) Chris@15: { Chris@15: EventList::iterator ei = el.begin(); Chris@16: bool phaseGiven = !empty() && ((*begin())->beatTime >= 0); // if given for one, assume given for others Chris@15: while (ei != el.end()) { Chris@15: Event ev = *ei; Chris@15: ++ei; Chris@15: if ((stop > 0) && (ev.time > stop)) Chris@15: break; Chris@15: bool created = phaseGiven; Chris@15: double prevBeatInterval = -1.0; Chris@15: // cc: Duplicate our list of agents, and scan through the Chris@15: // copy. This means we can safely add agents to our own Chris@15: // list while scanning without disrupting our scan. Each Chris@15: // agent needs to be re-added to our own list explicitly Chris@15: // (since it is modified by e.g. considerAsBeat) Chris@15: Container currentAgents = list; Chris@15: list.clear(); Chris@15: for (Container::iterator ai = currentAgents.begin(); Chris@15: ai != currentAgents.end(); ++ai) { Chris@16: Agent *currentAgent = *ai; Chris@16: if (currentAgent->beatInterval != prevBeatInterval) { Chris@15: if ((prevBeatInterval>=0) && !created && (ev.time<5.0)) { Chris@15: #ifdef DEBUG_BEATROOT Chris@15: std::cerr << "Creating a new agent" << std::endl; Chris@15: #endif Chris@15: // Create new agent with different phase Chris@23: Agent *newAgent = new Agent(params, prevBeatInterval); Chris@15: // This may add another agent to our list as well Chris@16: newAgent->considerAsBeat(ev, *this); Chris@15: add(newAgent); Chris@15: } Chris@16: prevBeatInterval = currentAgent->beatInterval; Chris@15: created = phaseGiven; Chris@15: } Chris@16: if (currentAgent->considerAsBeat(ev, *this)) Chris@15: created = true; Chris@15: add(currentAgent); Chris@15: } // loop for each agent Chris@15: removeDuplicates(); Chris@15: } // loop for each event Chris@15: } // beatTrack() Chris@15: Chris@15: Agent *AgentList::bestAgent() Chris@15: { Chris@15: double best = -1.0; Chris@15: Agent *bestAg = 0; Chris@15: for (iterator itr = begin(); itr != end(); ++itr) { Chris@16: if ((*itr)->events.empty()) continue; Chris@16: double conf = ((*itr)->phaseScore + (*itr)->tempoScore) / Chris@16: (useAverageSalience? (double)(*itr)->beatCount: 1.0); Chris@15: if (conf > best) { Chris@16: bestAg = *itr; Chris@15: best = conf; Chris@15: } Chris@15: } Chris@15: #ifdef DEBUG_BEATROOT Chris@15: if (bestAg) { Chris@15: std::cerr << "Best agent: Ag#" << bestAg->idNumber << std::endl; Chris@15: std::cerr << " Av-salience = " << best << std::endl; Chris@15: } else { Chris@15: std::cerr << "No surviving agent - beat tracking failed" << std::endl; Chris@15: } Chris@15: #endif Chris@15: return bestAg; Chris@15: } // bestAgent() Chris@15: