├── LICENSE.md ├── README.md ├── index.js └── package.json /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## The MIT License (MIT) ## 2 | 3 | Copyright (c) 2013 Hugh Kennedy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # beats [![experimental](http://hughsk.github.io/stability-badges/dist/experimental.svg)](http://github.com/hughsk/stability-badges) # 2 | 3 | A naive but generic beat-detection module. 4 | 5 | ## Usage ## 6 | 7 | [![beats](https://nodei.co/npm/beats.png?mini=true)](https://nodei.co/npm/beats) 8 | 9 | ### `detect = require('beats')(bins[, minSeparation])` ### 10 | 11 | Takes an array of `bins`, and an optional `minSeparation` value which 12 | determines the minimum amount of time between beats for each bin (defaults to 13 | 0). 14 | 15 | Each bin takes the following options: 16 | 17 | ``` javascript 18 | { 19 | // the minimum index to sample in 20 | // the frequencies array. 21 | lo: 0 22 | // The maximum index to sample in 23 | // the frequencies array. 24 | , hi: 512 25 | // The minimum volume at which to 26 | // trigger a beat for this bin. 27 | , threshold: 0 28 | // the amount by which to decay 29 | // the threshold for this bin for 30 | // each sampled frame. 31 | , decay: 0.005 32 | } 33 | ``` 34 | 35 | ### `result = detect(frequencies[, dt])` ### 36 | 37 | Returns a `Float32Array` containing the beats for the current frame: one 38 | element per bin. If an element's value is 0, then there is no beat this frame. 39 | Otherwise, the value will match the average volume of the bin's frequencies. 40 | 41 | `frequencies` is an array of audio frequencies for the current frame. `dt` is 42 | the amount of time between this frame and the last one - it defaults to 1. 43 | 44 | ## License ## 45 | 46 | MIT. See [LICENSE.md](http://github.com/hughsk/beats/blob/master/LICENSE.md) for details. 47 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = beats 2 | 3 | function beats(bins, hold) { 4 | bins = Array.isArray(bins) ? bins : [bins] 5 | 6 | var minthresholds = bins.map(pick('threshold', 0)) 7 | var thresholds = bins.map(pick('threshold', 0)) 8 | var decays = bins.map(pick('decay', 0.005)) 9 | var his = bins.map(roundFn(pick('hi', 512))) 10 | var los = bins.map(roundFn(pick('lo', 0))) 11 | var sizes = diff(his, los) 12 | var binCount = bins.length 13 | var times = new Float64Array(binCount) 14 | var beats = new Uint8Array(binCount) 15 | 16 | hold = hold || 0 17 | 18 | allNumbers(his, 'All "hi" keys must be numbers') 19 | allNumbers(los, 'All "lo" keys must be numbers') 20 | allNumbers(thresholds, 'All "threshold" keys must be numbers') 21 | allNumbers(decays, 'All "decay" keys must be numbers') 22 | 23 | for (var i = 0; i < decays.length; i += 1) { 24 | decays[i] = 1 - decays[i] 25 | } 26 | 27 | return function(data, dt) { 28 | dt = dt || 1 29 | 30 | for (var i = 0; i < binCount; i += 1) { 31 | var scale = 1 / sizes[i] 32 | var hi = his[i] 33 | var lo = los[i] 34 | var volume = 0 35 | 36 | for (var j = lo; j < hi; j += 1) { 37 | volume += scale * data[j] 38 | } 39 | 40 | times[i] += dt 41 | 42 | if (times[i] > hold && volume > thresholds[i]) { 43 | beats[i] = volume 44 | times[i] = 0 45 | thresholds[i] = volume > minthresholds[i] 46 | ? volume 47 | : thresholds[i] 48 | } else { 49 | beats[i] = 0 50 | } 51 | 52 | thresholds[i] *= decays[i] 53 | } 54 | 55 | return beats 56 | } 57 | } 58 | 59 | 60 | function pick(key, def) { 61 | return function(object) { 62 | return key in object ? object[key] : def 63 | } 64 | } 65 | 66 | function diff(a, b) { 67 | var arr = [] 68 | for (var i = 0; i < a.length; i += 1) { 69 | arr[i] = a[i] - b[i] 70 | } 71 | return arr 72 | } 73 | 74 | function roundFn(fn) { 75 | return function(value) { 76 | return Math.round(fn(value)) 77 | } 78 | } 79 | 80 | function allNumbers(arr, msg) { 81 | for (var i = 0; i < arr.length; i += 1) { 82 | if (typeof arr[i] !== 'number') throw new Error(msg) 83 | } 84 | return arr 85 | } 86 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beats", 3 | "description": "A naive but generic beat-detection module.", 4 | "version": "0.0.0", 5 | "main": "index.js", 6 | "browser": "index.js", 7 | "dependencies": {}, 8 | "devDependencies": {}, 9 | "scripts": {}, 10 | "author": "Hugh Kennedy (http://github.com/hughsk)", 11 | "license": "MIT", 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/hughsk/beats" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/hughsk/beats/issues" 18 | } 19 | } 20 | --------------------------------------------------------------------------------