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 #ifndef _BEAT_TRACKER_H_
|
Chris@6
|
17 #define _BEAT_TRACKER_H_
|
Chris@6
|
18
|
Chris@6
|
19 #include "Event.h"
|
Chris@6
|
20 #include "Agent.h"
|
Chris@8
|
21 #include "AgentList.h"
|
Chris@7
|
22 #include "Induction.h"
|
Chris@6
|
23
|
Chris@6
|
24 using std::vector;
|
Chris@6
|
25
|
Chris@6
|
26 class BeatTracker
|
Chris@6
|
27 {
|
Chris@6
|
28 protected:
|
Chris@6
|
29 /** beat data encoded as a list of Events */
|
Chris@6
|
30 EventList beats;
|
Chris@6
|
31
|
Chris@6
|
32 /** a list of onset events for passing to the tempo induction and beat tracking methods */
|
Chris@6
|
33 EventList onsetList;
|
Chris@6
|
34
|
Chris@6
|
35 /** the times of onsets (in seconds) */
|
Chris@6
|
36 vector<double> onsets;
|
Chris@6
|
37
|
Chris@6
|
38 public:
|
Chris@6
|
39 /** Constructor:
|
Chris@6
|
40 * @param b The list of beats
|
Chris@6
|
41 */
|
Chris@6
|
42 BeatTracker(EventList b) {
|
Chris@6
|
43 beats = b;
|
Chris@6
|
44 } // BeatTracker constructor
|
Chris@6
|
45
|
Chris@6
|
46 /** Creates a new Event object representing a beat.
|
Chris@6
|
47 * @param time The time of the beat in seconds
|
Chris@6
|
48 * @param beatNum The index of the beat
|
Chris@6
|
49 * @return The Event object representing the beat
|
Chris@6
|
50 */
|
Chris@6
|
51 static Event newBeat(double time, int beatNum) {
|
Chris@6
|
52 return Event(time, beatNum, 0);
|
Chris@6
|
53 } // newBeat()
|
Chris@6
|
54
|
Chris@6
|
55 /** Perform beat tracking.
|
Chris@6
|
56 * @param events The onsets or peaks in a feature list
|
Chris@6
|
57 * @return The list of beats, or an empty list if beat tracking fails
|
Chris@6
|
58 */
|
Chris@6
|
59 static EventList beatTrack(EventList events) {
|
Chris@6
|
60 return beatTrack(events, EventList());
|
Chris@6
|
61 }
|
Chris@6
|
62
|
Chris@6
|
63 /** Perform beat tracking.
|
Chris@6
|
64 * @param events The onsets or peaks in a feature list
|
Chris@6
|
65 * @param beats The initial beats which are given, if any
|
Chris@6
|
66 * @return The list of beats, or an empty list if beat tracking fails
|
Chris@6
|
67 */
|
Chris@6
|
68 static EventList beatTrack(EventList events, EventList beats) {
|
Chris@7
|
69 AgentList agents;
|
Chris@6
|
70 int count = 0;
|
Chris@6
|
71 double beatTime = -1;
|
Chris@6
|
72 if (!beats.empty()) {
|
Chris@6
|
73 count = beats.size() - 1;
|
Chris@7
|
74 EventList::iterator itr = beats.end();
|
Chris@7
|
75 --itr;
|
Chris@7
|
76 beatTime = itr->time;
|
Chris@6
|
77 }
|
Chris@6
|
78 if (count > 0) { // tempo given by mean of initial beats
|
Chris@8
|
79 double ioi = (beatTime - beats.begin()->time) / count;
|
Chris@8
|
80 agents.push_back(Agent(ioi));
|
Chris@8
|
81 } else // tempo not given; use tempo induction
|
Chris@8
|
82 agents = Induction::beatInduction(events);
|
Chris@8
|
83 if (!beats.empty())
|
Chris@8
|
84 for (AgentList::iterator itr = agents.begin(); itr != agents.end();
|
Chris@8
|
85 ++itr) {
|
Chris@8
|
86 itr->beatTime = beatTime;
|
Chris@8
|
87 itr->beatCount = count;
|
Chris@8
|
88 itr->events = beats;
|
Chris@6
|
89 }
|
Chris@6
|
90 agents.beatTrack(events, -1);
|
Chris@8
|
91 Agent *best = agents.bestAgent();
|
Chris@8
|
92 if (best) {
|
Chris@8
|
93 best->fillBeats(beatTime);
|
Chris@8
|
94 return best->events;
|
Chris@6
|
95 }
|
Chris@8
|
96 return EventList();
|
Chris@6
|
97 } // beatTrack()/1
|
Chris@6
|
98
|
Chris@6
|
99 /** Finds the mean tempo (as inter-beat interval) from an array of beat times
|
Chris@6
|
100 * @param d An array of beat times
|
Chris@6
|
101 * @return The average inter-beat interval
|
Chris@6
|
102 */
|
Chris@6
|
103 static double getAverageIBI(vector<double> d) {
|
Chris@8
|
104 if (d.size() < 2)
|
Chris@6
|
105 return -1.0;
|
Chris@8
|
106 return (d[d.size() - 1] - d[0]) / (d.size() - 1);
|
Chris@6
|
107 } // getAverageIBI()
|
Chris@6
|
108
|
Chris@6
|
109 /** Finds the median tempo (as inter-beat interval) from an array of beat times
|
Chris@6
|
110 * @param d An array of beat times
|
Chris@6
|
111 * @return The median inter-beat interval
|
Chris@6
|
112 */
|
Chris@6
|
113 static double getMedianIBI(vector<double> d) {
|
Chris@8
|
114 if (d.size() < 2)
|
Chris@6
|
115 return -1.0;
|
Chris@8
|
116 vector<double> ibi;
|
Chris@8
|
117 ibi.resize(d.size()-1);
|
Chris@8
|
118 for (int i = 1; i < d.size(); i++)
|
Chris@6
|
119 ibi[i-1] = d[i] - d[i-1];
|
Chris@8
|
120 std::sort(ibi.begin(), ibi.end());
|
Chris@8
|
121 if (ibi.size() % 2 == 0)
|
Chris@8
|
122 return (ibi[ibi.size() / 2] + ibi[ibi.size() / 2 - 1]) / 2;
|
Chris@6
|
123 else
|
Chris@8
|
124 return ibi[ibi.size() / 2];
|
Chris@6
|
125 } // getAverageIBI()
|
Chris@6
|
126
|
Chris@6
|
127
|
Chris@6
|
128 // Various get and set methods
|
Chris@6
|
129
|
Chris@6
|
130 /** @return the list of beats */
|
Chris@6
|
131 EventList getBeats() {
|
Chris@6
|
132 return beats;
|
Chris@6
|
133 } // getBeats()
|
Chris@6
|
134
|
Chris@6
|
135 /** @return the array of onset times */
|
Chris@6
|
136 vector<double> getOnsets() {
|
Chris@6
|
137 return onsets;
|
Chris@6
|
138 } // getOnsets()
|
Chris@6
|
139
|
Chris@6
|
140 /** Sets the onset times as a list of Events, for use by the beat tracking methods.
|
Chris@6
|
141 * @param on The times of onsets in seconds
|
Chris@6
|
142 */
|
Chris@6
|
143 void setOnsetList(EventList on) {
|
Chris@6
|
144 onsetList = on;
|
Chris@6
|
145 } // setOnsetList()
|
Chris@6
|
146
|
Chris@6
|
147 /** Sets the array of onset times, for displaying MIDI or audio input data.
|
Chris@6
|
148 * @param on The times of onsets in seconds
|
Chris@6
|
149 */
|
Chris@6
|
150 void setOnsets(vector<double> on) {
|
Chris@6
|
151 onsets = on;
|
Chris@6
|
152 } // setOnsets()
|
Chris@6
|
153
|
Chris@6
|
154 /** Sets the list of beats.
|
Chris@6
|
155 * @param b The list of beats
|
Chris@6
|
156 */
|
Chris@6
|
157 void setBeats(EventList b) {
|
Chris@6
|
158 beats = b;
|
Chris@6
|
159 } // setBeats()
|
Chris@6
|
160
|
Chris@6
|
161 }; // class BeatTrackDisplay
|
Chris@6
|
162
|
Chris@6
|
163
|
Chris@6
|
164 #endif
|
Chris@6
|
165
|