Chris@6
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@6
|
2
|
Chris@6
|
3 /*
|
Chris@6
|
4 Vamp feature extraction plugin for the BeatRoot beat tracker.
|
Chris@6
|
5
|
Chris@6
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@6
|
7 This file copyright 2011 Simon Dixon, Chris Cannam and QMUL.
|
Chris@6
|
8
|
Chris@6
|
9 This program is free software; you can redistribute it and/or
|
Chris@6
|
10 modify it under the terms of the GNU General Public License as
|
Chris@6
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@6
|
12 License, or (at your option) any later version. See the file
|
Chris@6
|
13 COPYING included with this distribution for more information.
|
Chris@6
|
14 */
|
Chris@6
|
15
|
Chris@6
|
16 #include "Agent.h"
|
Chris@6
|
17 #include "BeatTracker.h"
|
Chris@6
|
18
|
Chris@23
|
19 const double AgentParameters::DEFAULT_POST_MARGIN_FACTOR = 0.3;
|
Chris@23
|
20 const double AgentParameters::DEFAULT_PRE_MARGIN_FACTOR = 0.15;
|
Chris@23
|
21 const double AgentParameters::DEFAULT_MAX_CHANGE = 0.2;
|
Chris@23
|
22 const double AgentParameters::DEFAULT_EXPIRY_TIME = 10.0;
|
Chris@23
|
23
|
Chris@6
|
24 const double Agent::INNER_MARGIN = 0.040;
|
Chris@23
|
25 const double Agent::CONF_FACTOR = 0.5;
|
Chris@6
|
26 const double Agent::DEFAULT_CORRECTION_FACTOR = 50.0;
|
Chris@6
|
27
|
Chris@6
|
28 int Agent::idCounter = 0;
|
Chris@6
|
29
|
Chris@15
|
30 void Agent::accept(Event e, double err, int beats) {
|
Chris@15
|
31 beatTime = e.time;
|
Chris@15
|
32 events.push_back(e);
|
Chris@15
|
33 if (fabs(initialBeatInterval - beatInterval -
|
Chris@23
|
34 err / correctionFactor) < maxChange * initialBeatInterval)
|
Chris@15
|
35 beatInterval += err / correctionFactor;// Adjust tempo
|
Chris@15
|
36 beatCount += beats;
|
Chris@15
|
37 double conFactor = 1.0 - CONF_FACTOR * err /
|
Chris@15
|
38 (err>0? postMargin: -preMargin);
|
Chris@15
|
39 if (decayFactor > 0) {
|
Chris@15
|
40 double memFactor = 1. - 1. / threshold((double)beatCount,1,decayFactor);
|
Chris@15
|
41 phaseScore = memFactor * phaseScore +
|
Chris@15
|
42 (1.0 - memFactor) * conFactor * e.salience;
|
Chris@15
|
43 } else
|
Chris@15
|
44 phaseScore += conFactor * e.salience;
|
Chris@15
|
45
|
Chris@15
|
46 #ifdef DEBUG_BEATROOT
|
Chris@15
|
47 std::cerr << "Ag#" << idNumber << ": " << beatInterval << std::endl;
|
Chris@15
|
48 std::cerr << " Beat" << beatCount << " Time=" << beatTime
|
Chris@15
|
49 << " Score=" << tempoScore << ":P" << phaseScore << ":"
|
Chris@15
|
50 << topScoreTime << std::endl;
|
Chris@15
|
51 #endif
|
Chris@15
|
52 } // accept()
|
Chris@15
|
53
|
Chris@8
|
54 bool Agent::considerAsBeat(Event e, AgentList &a) {
|
Chris@6
|
55 double err;
|
Chris@6
|
56 if (beatTime < 0) { // first event
|
Chris@12
|
57 #ifdef DEBUG_BEATROOT
|
Chris@12
|
58 std::cerr << "Ag#" << idNumber << ": accepting first event trivially at " << e.time << std::endl;
|
Chris@12
|
59 #endif
|
Chris@6
|
60 accept(e, 0, 1);
|
Chris@6
|
61 return true;
|
Chris@6
|
62 } else { // subsequent events
|
Chris@9
|
63 EventList::iterator last = events.end();
|
Chris@9
|
64 --last;
|
Chris@9
|
65 if (e.time - last->time > expiryTime) {
|
Chris@12
|
66 #ifdef DEBUG_BEATROOT
|
Chris@12
|
67 std::cerr << "Ag#" << idNumber << ": time " << e.time
|
Chris@12
|
68 << " too late relative to " << last->time << " (expiry "
|
Chris@12
|
69 << expiryTime << "), giving up" << std::endl;
|
Chris@12
|
70 #endif
|
Chris@6
|
71 phaseScore = -1.0; // flag agent to be deleted
|
Chris@6
|
72 return false;
|
Chris@6
|
73 }
|
Chris@8
|
74 double beats = nearbyint((e.time - beatTime) / beatInterval);
|
Chris@8
|
75 err = e.time - beatTime - beats * beatInterval;
|
Chris@12
|
76 #ifdef DEBUG_BEATROOT
|
Chris@12
|
77 std::cerr << "Ag#" << idNumber << ": time " << e.time << ", err " << err << " for beats " << beats << std::endl;
|
Chris@12
|
78 #endif
|
Chris@6
|
79 if ((beats > 0) && (-preMargin <= err) && (err <= postMargin)) {
|
Chris@16
|
80 if (fabs(err) > innerMargin) {
|
Chris@12
|
81 #ifdef DEBUG_BEATROOT
|
Chris@12
|
82 std::cerr << "Ag#" << idNumber << ": creating another new agent" << std::endl;
|
Chris@12
|
83 #endif
|
Chris@16
|
84 // Create new agent that skips this event (avoids
|
Chris@16
|
85 // large phase jump)
|
Chris@16
|
86 a.add(clone());
|
Chris@12
|
87 }
|
Chris@6
|
88 accept(e, err, (int)beats);
|
Chris@6
|
89 return true;
|
Chris@6
|
90 }
|
Chris@6
|
91 }
|
Chris@6
|
92 return false;
|
Chris@6
|
93 } // considerAsBeat()
|
Chris@6
|
94
|
Chris@6
|
95
|
Chris@9
|
96 void Agent::fillBeats(double start) {
|
Chris@6
|
97 double prevBeat = 0, nextBeat, currentInterval, beats;
|
Chris@9
|
98 EventList::iterator ei = events.begin();
|
Chris@9
|
99 if (ei != events.end()) {
|
Chris@13
|
100 EventList::iterator ni = ei;
|
Chris@18
|
101 if (++ni != events.end()) {
|
Chris@18
|
102 prevBeat = ni->time;
|
Chris@18
|
103 }
|
Chris@6
|
104 }
|
Chris@16
|
105 while (ei != events.end()) {
|
Chris@13
|
106 EventList::iterator ni = ei;
|
Chris@18
|
107 if (ni == events.end() ||
|
Chris@18
|
108 ++ni == events.end()) {
|
Chris@18
|
109 break;
|
Chris@18
|
110 }
|
Chris@16
|
111 nextBeat = ni->time;
|
Chris@6
|
112 beats = nearbyint((nextBeat - prevBeat) / beatInterval - 0.01); //prefer slow
|
Chris@6
|
113 currentInterval = (nextBeat - prevBeat) / beats;
|
Chris@6
|
114 for ( ; (nextBeat > start) && (beats > 1.5); beats--) {
|
Chris@6
|
115 prevBeat += currentInterval;
|
Chris@13
|
116 events.insert(ni, BeatTracker::newBeat(prevBeat, 0));
|
Chris@6
|
117 }
|
Chris@6
|
118 prevBeat = nextBeat;
|
Chris@13
|
119 ei = ni;
|
Chris@6
|
120 }
|
Chris@6
|
121 } // fillBeats()
|