├── BeatDetectionTeensy.ino ├── README.md └── Beat.h /BeatDetectionTeensy.ino: -------------------------------------------------------------------------------- 1 | // based off of PassThroughStereo example 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Beat.h" 8 | 9 | // GUItool: begin automatically generated code 10 | AudioInputI2S i2s1; //xy=200,69 11 | AudioOutputI2S i2s2; //xy=365,94 12 | AudioMixer4 mixer; 13 | AudioConnection inputToMixer1(i2s1, 0, mixer, 0); 14 | AudioConnection inputToMixer2(i2s1, 1, mixer, 1); 15 | AudioConnection patchCord1(mixer, 0, i2s2, 0); 16 | AudioConnection patchCord2(mixer, 0, i2s2, 1); 17 | AudioAnalyzePeak analyzer; 18 | // route mixer output to analyzer 19 | AudioConnection mixerToAnalyzer(mixer, analyzer); 20 | AudioControlSGTL5000 sgtl5000_1; //xy=302,184 21 | // GUItool: end automatically generated code 22 | BeatAnalyzer beatAnalyzer(&analyzer); 23 | 24 | 25 | const int myInput = AUDIO_INPUT_LINEIN; 26 | //const int myInput = AUDIO_INPUT_MIC; 27 | 28 | 29 | void setup() { 30 | // Audio connections require memory to work. For more 31 | // detailed information, see the MemoryAndCpuUsage example 32 | AudioMemory(12); 33 | 34 | // Enable the audio shield, select input, and enable output 35 | sgtl5000_1.enable(); 36 | sgtl5000_1.inputSelect(myInput); 37 | sgtl5000_1.volume(0.5); 38 | 39 | Serial.begin(9600); 40 | // turn on onboard led to signal successful completion of setup // 41 | digitalWrite(13, HIGH); 42 | } 43 | 44 | elapsedMillis volmsec=0; 45 | 46 | void loop() { 47 | beatAnalyzer.update(); 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ####Teensy Beat Detection 2 | 3 | ## Important Note 4 | This was a quick experiment and isn't necessarily pristine, guaranteed, or fully vetted. I hope to get around to polishing it up a bit in the future, but it is currently on hold. 5 | 6 | There are a number of improvements (API improvements, FFT support, etc) in the works but not present on this repo. 7 | 8 | Please note that the 'lib' branch contains the most up-to-date code here. 9 | 10 | ## Description 11 | This example uses the Teensy's built in audio libraries to do a simple form of beat detection. 12 | 13 | The 'BeatAnalyzer' class (found in Beat.h) takes a reference to an AudioAnalyzePeak object in its constructor. It then calculates a running average of the song's peak intensity, comparing that against the current peak intensity. If it finds a beat, it simply logs out to the Serial console. A beat is only considered valid if it meets the following criteria: 14 | 15 | 1. Its peak intensity is greater than the running average 16 | 2. Its peak intensity is greater than a minimum threshold (BEAT_MIN) 17 | 3. Its peak intensity is greater than or equal to that of the last detected beat 18 | 3. It occurs more than n-number of milliseconds after the last valid beat (where n is defined by BEAT_WAIT_INTERVAL) 19 | 20 | Criteria #3 self-adjusts over time -- with each beat, the cutoff value for the next one will be set at slightly over the peak intensity of the current beat. Over time, this cutoff value will decay until the next beat, by a factor defined by BEAT_DECAY_RATE. 21 | 22 | These criteria, when combined, do a fairly good job of detecting beats with a minimum of false positives. Adjusting the defines at the top of Beat.h may net you different results. 23 | 24 | Future iterations may include an FFT pre-pass to isolate valid beats to a specific frequency range, or only test for beats within the frequency range that has the highest peak intensity average. 25 | 26 | 27 | ####Notes 28 | http://www.airtightinteractive.com/2013/10/making-audio-reactive-visuals/ 29 | http://www.gamedev.net/page/resources/_/technical/math-and-physics/beat-detection-algorithms-r1952 30 | https://github.com/ddf/Minim/blob/master/src/ddf/minim/analysis/BeatDetect.java 31 | -------------------------------------------------------------------------------- /Beat.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define AVERAGE_FRAMES 200 // ticks to average audio energy for comparison against instance energy // 5 | #define BEAT_WAIT_INTERVAL 400 // time to wait for detection after a beat 6 | #define BEAT_HOLD_TIME 80 // number of frames to hold a beat 7 | #define BEAT_DECAY_RATE 0.99 // how much to decay the beat threshold on a non-beat 8 | #define BEAT_MIN 0.25 // anything with a peak level lower than this will not count as a beat 9 | 10 | //Track a threshold volume level. 11 | //If the current volume exceeds the threshold then you have a beat. Set the new threshold to the current volume. 12 | //Reduce the threshold over time, using the Decay Rate. 13 | //Wait for the Hold Time before detecting for the next beat. This can help reduce false positives. 14 | 15 | class BeatAnalyzer { 16 | public: 17 | BeatAnalyzer(AudioAnalyzePeak* _peakAnalyzer) { 18 | analyzer = _peakAnalyzer; 19 | numBeats = 0; 20 | } 21 | 22 | ~BeatAnalyzer() { 23 | delete analyzer; 24 | } 25 | 26 | void update() { 27 | if(analyzer->available()) { 28 | float peak = analyzer->read(); 29 | 30 | // calculate an average of the last few frames of 31 | // audio data -- anything greater than this AND the running beat threshold 32 | // will count as a beat 33 | if(numFramesCounted >= AVERAGE_FRAMES) { 34 | avgPeak = peakRunningTotal / AVERAGE_FRAMES; 35 | peakRunningTotal = 0; 36 | numFramesCounted = 0; 37 | 38 | // if(avgPeak > 0.1) { 39 | // Serial.print("Average: "); 40 | // Serial.println(avgPeak); 41 | // } 42 | } 43 | 44 | peakRunningTotal += peak; 45 | numFramesCounted++; 46 | 47 | boolean doDetectBeat = (millis() - timer > BEAT_WAIT_INTERVAL); 48 | if(doDetectBeat && peak > beatCutoff && peak > BEAT_MIN && peak > avgPeak) { 49 | // got beat // 50 | Serial.print("BEAT: "); 51 | Serial.println(peak); 52 | numBeats++; 53 | 54 | // slightly increase beat threshold // 55 | beatCutoff = peak * 1.1; 56 | 57 | // reset timer // 58 | timer = millis(); 59 | beatTime = 0; 60 | } else { 61 | if(beatTime <= BEAT_HOLD_TIME) { 62 | beatTime++; 63 | } else { 64 | // slowly decrease beat cutoff with time // 65 | beatCutoff *= BEAT_DECAY_RATE; 66 | beatCutoff = max(beatCutoff, BEAT_MIN); 67 | } 68 | } 69 | } 70 | } 71 | 72 | uint16_t getNumBeats(boolean _clear = false) { 73 | uint16_t n = numBeats; 74 | if(_clear) numBeats = 0; 75 | return n; 76 | } 77 | 78 | private: 79 | AudioAnalyzePeak* analyzer; 80 | float avgPeak; 81 | float peakRunningTotal = 0.0; 82 | int numFramesCounted = 0; 83 | long timer = 0; 84 | int numBeats; 85 | 86 | float beatCutoff = 0.5; 87 | long beatTime = 0; 88 | }; 89 | 90 | 91 | --------------------------------------------------------------------------------