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: #ifndef _BEAT_TRACKER_H_ Chris@6: #define _BEAT_TRACKER_H_ Chris@6: Chris@6: #include "Event.h" Chris@6: #include "Agent.h" Chris@7: #include "Induction.h" Chris@6: Chris@6: using std::vector; Chris@6: Chris@6: class BeatTracker Chris@6: { Chris@6: protected: Chris@6: /** beat data encoded as a list of Events */ Chris@6: EventList beats; Chris@6: Chris@6: /** a list of onset events for passing to the tempo induction and beat tracking methods */ Chris@6: EventList onsetList; Chris@6: Chris@6: /** the times of onsets (in seconds) */ Chris@6: vector onsets; Chris@6: Chris@6: /** the times corresponding to each point in the magnitudes array */ Chris@6: vector env; Chris@6: Chris@6: /** smoothed amplitude envelope of audio signal */ Chris@6: vector magnitudes; Chris@6: Chris@6: public: Chris@6: /** Constructor: Chris@6: * @param b The list of beats Chris@6: */ Chris@6: BeatTracker(EventList b) { Chris@6: beats = b; Chris@6: } // BeatTracker constructor Chris@6: Chris@6: /** Creates a new Event object representing a beat. Chris@6: * @param time The time of the beat in seconds Chris@6: * @param beatNum The index of the beat Chris@6: * @return The Event object representing the beat Chris@6: */ Chris@6: static Event newBeat(double time, int beatNum) { Chris@6: return Event(time, beatNum, 0); Chris@6: } // newBeat() Chris@6: Chris@6: /** Perform beat tracking. Chris@6: * @param events The onsets or peaks in a feature list Chris@6: * @return The list of beats, or an empty list if beat tracking fails Chris@6: */ Chris@6: static EventList beatTrack(EventList events) { Chris@6: return beatTrack(events, EventList()); Chris@6: } Chris@6: Chris@6: /** Perform beat tracking. Chris@6: * @param events The onsets or peaks in a feature list Chris@6: * @param beats The initial beats which are given, if any Chris@6: * @return The list of beats, or an empty list if beat tracking fails Chris@6: */ Chris@6: static EventList beatTrack(EventList events, EventList beats) { Chris@7: AgentList agents; Chris@6: int count = 0; Chris@6: double beatTime = -1; Chris@6: if (!beats.empty()) { Chris@6: count = beats.size() - 1; Chris@7: EventList::iterator itr = beats.end(); Chris@7: --itr; Chris@7: beatTime = itr->time; Chris@6: } Chris@6: if (count > 0) { // tempo given by mean of initial beats Chris@6: double ioi = (beatTime - beats.l.getFirst().keyDown) / count; Chris@6: agents = new AgentList(new Agent(ioi), null); Chris@6: } else // tempo not given; use tempo induction Chris@6: agents = Induction.beatInduction(events); Chris@6: if (beats != null) Chris@6: for (AgentList ptr = agents; ptr.ag != null; ptr = ptr.next) { Chris@6: ptr.ag.beatTime = beatTime; Chris@6: ptr.ag.beatCount = count; Chris@6: ptr.ag.events = new EventList(beats); Chris@6: } Chris@6: agents.beatTrack(events, -1); Chris@6: Agent best = agents.bestAgent(); Chris@6: if (best != null) { Chris@6: best.fillBeats(beatTime); Chris@6: return best.events; Chris@6: } Chris@6: return new EventList(); Chris@6: } // beatTrack()/1 Chris@6: Chris@6: /** Finds the mean tempo (as inter-beat interval) from an array of beat times Chris@6: * @param d An array of beat times Chris@6: * @return The average inter-beat interval Chris@6: */ Chris@6: static double getAverageIBI(vector d) { Chris@6: if ((d == null) || (d.length < 2)) Chris@6: return -1.0; Chris@6: return (d[d.length - 1] - d[0]) / (d.length - 1); Chris@6: } // getAverageIBI() Chris@6: Chris@6: /** Finds the median tempo (as inter-beat interval) from an array of beat times Chris@6: * @param d An array of beat times Chris@6: * @return The median inter-beat interval Chris@6: */ Chris@6: static double getMedianIBI(vector d) { Chris@6: if ((d == null) || (d.length < 2)) Chris@6: return -1.0; Chris@6: vector ibi = new double[d.length-1]; Chris@6: for (int i = 1; i < d.length; i++) Chris@6: ibi[i-1] = d[i] - d[i-1]; Chris@6: Arrays.sort(ibi); Chris@6: if (ibi.length % 2 == 0) Chris@6: return (ibi[ibi.length / 2] + ibi[ibi.length / 2 - 1]) / 2; Chris@6: else Chris@6: return ibi[ibi.length / 2]; Chris@6: } // getAverageIBI() Chris@6: Chris@6: Chris@6: // Various get and set methods Chris@6: Chris@6: /** @return the list of beats */ Chris@6: EventList getBeats() { Chris@6: return beats; Chris@6: } // getBeats() Chris@6: Chris@6: /** @return the array of onset times */ Chris@6: vector getOnsets() { Chris@6: return onsets; Chris@6: } // getOnsets() Chris@6: Chris@6: /** @return the array of offset times */ Chris@6: vector getOffsets() { Chris@6: return offsets; Chris@6: } // getOffsets() Chris@6: Chris@6: /** @return the array of MIDI pitches */ Chris@6: vector getPitches() { Chris@6: return pitches; Chris@6: } // getPitches() Chris@6: Chris@6: /** Sets the onset times as a list of Events, for use by the beat tracking methods. Chris@6: * @param on The times of onsets in seconds Chris@6: */ Chris@6: void setOnsetList(EventList on) { Chris@6: onsetList = on; Chris@6: } // setOnsetList() Chris@6: Chris@6: /** Sets the array of onset times, for displaying MIDI or audio input data. Chris@6: * @param on The times of onsets in seconds Chris@6: */ Chris@6: void setOnsets(vector on) { Chris@6: onsets = on; Chris@6: } // setOnsets() Chris@6: Chris@6: /** Sets the array of offset times, for displaying MIDI input data. Chris@6: * @param off The array of MIDI offset times Chris@6: */ Chris@6: void setOffsets(vector off) { Chris@6: offsets = off; Chris@6: // setMode(SHOW_MIDI, SHOW_AUDIO | SHOW_SPECTRO); Chris@6: } // setOffsets() Chris@6: Chris@6: /** Sets the array of times of amplitude envelope points, for displaying. Chris@6: * @param envTimes The array of times in seconds corresponding to the values in magnitudes Chris@6: */ Chris@6: void setEnvTimes(vector envTimes) { Chris@6: env = envTimes; Chris@6: setMode(SHOW_AUDIO, SHOW_MIDI); Chris@6: } // setEnvTimes() Chris@6: Chris@6: /** Sets the array of magnitude values, for displaying. Chris@6: * @param mag The array of amplitude envelope values Chris@6: */ Chris@6: void setMagnitudes(vector mag) { Chris@6: magnitudes = mag; Chris@6: } // setMagnitudes() Chris@6: Chris@6: /** Sets the array of pitch values, for displaying MIDI input data. Chris@6: * @param p The array of MIDI pitch values Chris@6: */ Chris@6: void setPitches(vector p) { Chris@6: pitches = p; Chris@6: } // setPitches() Chris@6: Chris@6: /** Sets the list of beats. Chris@6: * @param b The list of beats Chris@6: */ Chris@6: void setBeats(EventList b) { Chris@6: beats = b; Chris@6: selectedBeat = null; Chris@6: beatPtr = beats.listIterator(); Chris@6: } // setBeats() Chris@6: Chris@6: }; // class BeatTrackDisplay Chris@6: Chris@6: Chris@6: #endif Chris@6: