Chris@6: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@6: Chris@6: /* Chris@6: Vamp feature extraction plugin for the BeatRoot beat tracker. Chris@6: Chris@6: Centre for Digital Music, Queen Mary, University of London. Chris@6: This file copyright 2011 Simon Dixon, Chris Cannam and QMUL. Chris@6: Chris@6: This program is free software; you can redistribute it and/or Chris@6: modify it under the terms of the GNU General Public License as Chris@6: published by the Free Software Foundation; either version 2 of the Chris@6: License, or (at your option) any later version. See the file Chris@6: COPYING included with this distribution for more information. Chris@6: */ Chris@6: Chris@6: #include "Agent.h" Chris@6: #include "BeatTracker.h" Chris@6: Chris@23: const double AgentParameters::DEFAULT_POST_MARGIN_FACTOR = 0.3; Chris@23: const double AgentParameters::DEFAULT_PRE_MARGIN_FACTOR = 0.15; Chris@23: const double AgentParameters::DEFAULT_MAX_CHANGE = 0.2; Chris@23: const double AgentParameters::DEFAULT_EXPIRY_TIME = 10.0; Chris@23: Chris@6: const double Agent::INNER_MARGIN = 0.040; Chris@23: const double Agent::CONF_FACTOR = 0.5; Chris@6: const double Agent::DEFAULT_CORRECTION_FACTOR = 50.0; Chris@6: Chris@6: int Agent::idCounter = 0; Chris@6: Chris@15: void Agent::accept(Event e, double err, int beats) { Chris@15: beatTime = e.time; Chris@15: events.push_back(e); Chris@15: if (fabs(initialBeatInterval - beatInterval - Chris@23: err / correctionFactor) < maxChange * initialBeatInterval) Chris@15: beatInterval += err / correctionFactor;// Adjust tempo Chris@15: beatCount += beats; Chris@15: double conFactor = 1.0 - CONF_FACTOR * err / Chris@15: (err>0? postMargin: -preMargin); Chris@15: if (decayFactor > 0) { Chris@15: double memFactor = 1. - 1. / threshold((double)beatCount,1,decayFactor); Chris@15: phaseScore = memFactor * phaseScore + Chris@15: (1.0 - memFactor) * conFactor * e.salience; Chris@15: } else Chris@15: phaseScore += conFactor * e.salience; Chris@15: Chris@15: #ifdef DEBUG_BEATROOT Chris@15: std::cerr << "Ag#" << idNumber << ": " << beatInterval << std::endl; Chris@15: std::cerr << " Beat" << beatCount << " Time=" << beatTime Chris@15: << " Score=" << tempoScore << ":P" << phaseScore << ":" Chris@15: << topScoreTime << std::endl; Chris@15: #endif Chris@15: } // accept() Chris@15: Chris@8: bool Agent::considerAsBeat(Event e, AgentList &a) { Chris@6: double err; Chris@6: if (beatTime < 0) { // first event Chris@12: #ifdef DEBUG_BEATROOT Chris@12: std::cerr << "Ag#" << idNumber << ": accepting first event trivially at " << e.time << std::endl; Chris@12: #endif Chris@6: accept(e, 0, 1); Chris@6: return true; Chris@6: } else { // subsequent events Chris@9: EventList::iterator last = events.end(); Chris@9: --last; Chris@9: if (e.time - last->time > expiryTime) { Chris@12: #ifdef DEBUG_BEATROOT Chris@12: std::cerr << "Ag#" << idNumber << ": time " << e.time Chris@12: << " too late relative to " << last->time << " (expiry " Chris@12: << expiryTime << "), giving up" << std::endl; Chris@12: #endif Chris@6: phaseScore = -1.0; // flag agent to be deleted Chris@6: return false; Chris@6: } Chris@8: double beats = nearbyint((e.time - beatTime) / beatInterval); Chris@8: err = e.time - beatTime - beats * beatInterval; Chris@12: #ifdef DEBUG_BEATROOT Chris@12: std::cerr << "Ag#" << idNumber << ": time " << e.time << ", err " << err << " for beats " << beats << std::endl; Chris@12: #endif Chris@6: if ((beats > 0) && (-preMargin <= err) && (err <= postMargin)) { Chris@16: if (fabs(err) > innerMargin) { Chris@12: #ifdef DEBUG_BEATROOT Chris@12: std::cerr << "Ag#" << idNumber << ": creating another new agent" << std::endl; Chris@12: #endif Chris@16: // Create new agent that skips this event (avoids Chris@16: // large phase jump) Chris@16: a.add(clone()); Chris@12: } Chris@6: accept(e, err, (int)beats); Chris@6: return true; Chris@6: } Chris@6: } Chris@6: return false; Chris@6: } // considerAsBeat() Chris@6: Chris@6: Chris@9: void Agent::fillBeats(double start) { Chris@6: double prevBeat = 0, nextBeat, currentInterval, beats; Chris@9: EventList::iterator ei = events.begin(); Chris@9: if (ei != events.end()) { Chris@13: EventList::iterator ni = ei; Chris@18: if (++ni != events.end()) { Chris@18: prevBeat = ni->time; Chris@18: } Chris@6: } Chris@16: while (ei != events.end()) { Chris@13: EventList::iterator ni = ei; Chris@18: if (ni == events.end() || Chris@18: ++ni == events.end()) { Chris@18: break; Chris@18: } Chris@16: nextBeat = ni->time; Chris@6: beats = nearbyint((nextBeat - prevBeat) / beatInterval - 0.01); //prefer slow Chris@6: currentInterval = (nextBeat - prevBeat) / beats; Chris@6: for ( ; (nextBeat > start) && (beats > 1.5); beats--) { Chris@6: prevBeat += currentInterval; Chris@13: events.insert(ni, BeatTracker::newBeat(prevBeat, 0)); Chris@6: } Chris@6: prevBeat = nextBeat; Chris@13: ei = ni; Chris@6: } Chris@6: } // fillBeats()