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@7
|
21 #include "Induction.h"
|
Chris@6
|
22
|
Chris@6
|
23 using std::vector;
|
Chris@6
|
24
|
Chris@6
|
25 class BeatTracker
|
Chris@6
|
26 {
|
Chris@6
|
27 protected:
|
Chris@6
|
28 /** beat data encoded as a list of Events */
|
Chris@6
|
29 EventList beats;
|
Chris@6
|
30
|
Chris@6
|
31 /** a list of onset events for passing to the tempo induction and beat tracking methods */
|
Chris@6
|
32 EventList onsetList;
|
Chris@6
|
33
|
Chris@6
|
34 /** the times of onsets (in seconds) */
|
Chris@6
|
35 vector<double> onsets;
|
Chris@6
|
36
|
Chris@6
|
37 /** the times corresponding to each point in the <code>magnitudes</code> array */
|
Chris@6
|
38 vector<double> env;
|
Chris@6
|
39
|
Chris@6
|
40 /** smoothed amplitude envelope of audio signal */
|
Chris@6
|
41 vector<int> magnitudes;
|
Chris@6
|
42
|
Chris@6
|
43 public:
|
Chris@6
|
44 /** Constructor:
|
Chris@6
|
45 * @param b The list of beats
|
Chris@6
|
46 */
|
Chris@6
|
47 BeatTracker(EventList b) {
|
Chris@6
|
48 beats = b;
|
Chris@6
|
49 } // BeatTracker constructor
|
Chris@6
|
50
|
Chris@6
|
51 /** Creates a new Event object representing a beat.
|
Chris@6
|
52 * @param time The time of the beat in seconds
|
Chris@6
|
53 * @param beatNum The index of the beat
|
Chris@6
|
54 * @return The Event object representing the beat
|
Chris@6
|
55 */
|
Chris@6
|
56 static Event newBeat(double time, int beatNum) {
|
Chris@6
|
57 return Event(time, beatNum, 0);
|
Chris@6
|
58 } // newBeat()
|
Chris@6
|
59
|
Chris@6
|
60 /** Perform beat tracking.
|
Chris@6
|
61 * @param events The onsets or peaks in a feature list
|
Chris@6
|
62 * @return The list of beats, or an empty list if beat tracking fails
|
Chris@6
|
63 */
|
Chris@6
|
64 static EventList beatTrack(EventList events) {
|
Chris@6
|
65 return beatTrack(events, EventList());
|
Chris@6
|
66 }
|
Chris@6
|
67
|
Chris@6
|
68 /** Perform beat tracking.
|
Chris@6
|
69 * @param events The onsets or peaks in a feature list
|
Chris@6
|
70 * @param beats The initial beats which are given, if any
|
Chris@6
|
71 * @return The list of beats, or an empty list if beat tracking fails
|
Chris@6
|
72 */
|
Chris@6
|
73 static EventList beatTrack(EventList events, EventList beats) {
|
Chris@7
|
74 AgentList agents;
|
Chris@6
|
75 int count = 0;
|
Chris@6
|
76 double beatTime = -1;
|
Chris@6
|
77 if (!beats.empty()) {
|
Chris@6
|
78 count = beats.size() - 1;
|
Chris@7
|
79 EventList::iterator itr = beats.end();
|
Chris@7
|
80 --itr;
|
Chris@7
|
81 beatTime = itr->time;
|
Chris@6
|
82 }
|
Chris@6
|
83 if (count > 0) { // tempo given by mean of initial beats
|
Chris@6
|
84 double ioi = (beatTime - beats.l.getFirst().keyDown) / count;
|
Chris@6
|
85 agents = new AgentList(new Agent(ioi), null);
|
Chris@6
|
86 } else // tempo not given; use tempo induction
|
Chris@6
|
87 agents = Induction.beatInduction(events);
|
Chris@6
|
88 if (beats != null)
|
Chris@6
|
89 for (AgentList ptr = agents; ptr.ag != null; ptr = ptr.next) {
|
Chris@6
|
90 ptr.ag.beatTime = beatTime;
|
Chris@6
|
91 ptr.ag.beatCount = count;
|
Chris@6
|
92 ptr.ag.events = new EventList(beats);
|
Chris@6
|
93 }
|
Chris@6
|
94 agents.beatTrack(events, -1);
|
Chris@6
|
95 Agent best = agents.bestAgent();
|
Chris@6
|
96 if (best != null) {
|
Chris@6
|
97 best.fillBeats(beatTime);
|
Chris@6
|
98 return best.events;
|
Chris@6
|
99 }
|
Chris@6
|
100 return new EventList();
|
Chris@6
|
101 } // beatTrack()/1
|
Chris@6
|
102
|
Chris@6
|
103 /** Finds the mean tempo (as inter-beat interval) from an array of beat times
|
Chris@6
|
104 * @param d An array of beat times
|
Chris@6
|
105 * @return The average inter-beat interval
|
Chris@6
|
106 */
|
Chris@6
|
107 static double getAverageIBI(vector<double> d) {
|
Chris@6
|
108 if ((d == null) || (d.length < 2))
|
Chris@6
|
109 return -1.0;
|
Chris@6
|
110 return (d[d.length - 1] - d[0]) / (d.length - 1);
|
Chris@6
|
111 } // getAverageIBI()
|
Chris@6
|
112
|
Chris@6
|
113 /** Finds the median tempo (as inter-beat interval) from an array of beat times
|
Chris@6
|
114 * @param d An array of beat times
|
Chris@6
|
115 * @return The median inter-beat interval
|
Chris@6
|
116 */
|
Chris@6
|
117 static double getMedianIBI(vector<double> d) {
|
Chris@6
|
118 if ((d == null) || (d.length < 2))
|
Chris@6
|
119 return -1.0;
|
Chris@6
|
120 vector<double> ibi = new double[d.length-1];
|
Chris@6
|
121 for (int i = 1; i < d.length; i++)
|
Chris@6
|
122 ibi[i-1] = d[i] - d[i-1];
|
Chris@6
|
123 Arrays.sort(ibi);
|
Chris@6
|
124 if (ibi.length % 2 == 0)
|
Chris@6
|
125 return (ibi[ibi.length / 2] + ibi[ibi.length / 2 - 1]) / 2;
|
Chris@6
|
126 else
|
Chris@6
|
127 return ibi[ibi.length / 2];
|
Chris@6
|
128 } // getAverageIBI()
|
Chris@6
|
129
|
Chris@6
|
130
|
Chris@6
|
131 // Various get and set methods
|
Chris@6
|
132
|
Chris@6
|
133 /** @return the list of beats */
|
Chris@6
|
134 EventList getBeats() {
|
Chris@6
|
135 return beats;
|
Chris@6
|
136 } // getBeats()
|
Chris@6
|
137
|
Chris@6
|
138 /** @return the array of onset times */
|
Chris@6
|
139 vector<double> getOnsets() {
|
Chris@6
|
140 return onsets;
|
Chris@6
|
141 } // getOnsets()
|
Chris@6
|
142
|
Chris@6
|
143 /** @return the array of offset times */
|
Chris@6
|
144 vector<double> getOffsets() {
|
Chris@6
|
145 return offsets;
|
Chris@6
|
146 } // getOffsets()
|
Chris@6
|
147
|
Chris@6
|
148 /** @return the array of MIDI pitches */
|
Chris@6
|
149 vector<int> getPitches() {
|
Chris@6
|
150 return pitches;
|
Chris@6
|
151 } // getPitches()
|
Chris@6
|
152
|
Chris@6
|
153 /** Sets the onset times as a list of Events, for use by the beat tracking methods.
|
Chris@6
|
154 * @param on The times of onsets in seconds
|
Chris@6
|
155 */
|
Chris@6
|
156 void setOnsetList(EventList on) {
|
Chris@6
|
157 onsetList = on;
|
Chris@6
|
158 } // setOnsetList()
|
Chris@6
|
159
|
Chris@6
|
160 /** Sets the array of onset times, for displaying MIDI or audio input data.
|
Chris@6
|
161 * @param on The times of onsets in seconds
|
Chris@6
|
162 */
|
Chris@6
|
163 void setOnsets(vector<double> on) {
|
Chris@6
|
164 onsets = on;
|
Chris@6
|
165 } // setOnsets()
|
Chris@6
|
166
|
Chris@6
|
167 /** Sets the array of offset times, for displaying MIDI input data.
|
Chris@6
|
168 * @param off The array of MIDI offset times
|
Chris@6
|
169 */
|
Chris@6
|
170 void setOffsets(vector<double> off) {
|
Chris@6
|
171 offsets = off;
|
Chris@6
|
172 // setMode(SHOW_MIDI, SHOW_AUDIO | SHOW_SPECTRO);
|
Chris@6
|
173 } // setOffsets()
|
Chris@6
|
174
|
Chris@6
|
175 /** Sets the array of times of amplitude envelope points, for displaying.
|
Chris@6
|
176 * @param envTimes The array of times in seconds corresponding to the values in <code>magnitudes</code>
|
Chris@6
|
177 */
|
Chris@6
|
178 void setEnvTimes(vector<double> envTimes) {
|
Chris@6
|
179 env = envTimes;
|
Chris@6
|
180 setMode(SHOW_AUDIO, SHOW_MIDI);
|
Chris@6
|
181 } // setEnvTimes()
|
Chris@6
|
182
|
Chris@6
|
183 /** Sets the array of magnitude values, for displaying.
|
Chris@6
|
184 * @param mag The array of amplitude envelope values
|
Chris@6
|
185 */
|
Chris@6
|
186 void setMagnitudes(vector<int> mag) {
|
Chris@6
|
187 magnitudes = mag;
|
Chris@6
|
188 } // setMagnitudes()
|
Chris@6
|
189
|
Chris@6
|
190 /** Sets the array of pitch values, for displaying MIDI input data.
|
Chris@6
|
191 * @param p The array of MIDI pitch values
|
Chris@6
|
192 */
|
Chris@6
|
193 void setPitches(vector<int> p) {
|
Chris@6
|
194 pitches = p;
|
Chris@6
|
195 } // setPitches()
|
Chris@6
|
196
|
Chris@6
|
197 /** Sets the list of beats.
|
Chris@6
|
198 * @param b The list of beats
|
Chris@6
|
199 */
|
Chris@6
|
200 void setBeats(EventList b) {
|
Chris@6
|
201 beats = b;
|
Chris@6
|
202 selectedBeat = null;
|
Chris@6
|
203 beatPtr = beats.listIterator();
|
Chris@6
|
204 } // setBeats()
|
Chris@6
|
205
|
Chris@6
|
206 }; // class BeatTrackDisplay
|
Chris@6
|
207
|
Chris@6
|
208
|
Chris@6
|
209 #endif
|
Chris@6
|
210
|