UNPKG

@sebastbake/music-tempo

Version:

Finding out tempo of the music

250 lines (230 loc) 11 kB
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <base data-ice="baseUrl" href="../../"> <title data-ice="title">src/Agent.js | API Document</title> <link type="text/css" rel="stylesheet" href="css/style.css"> <link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css"> <script src="script/prettify/prettify.js"></script> <script src="script/manual.js"></script> </head> <body class="layout-container" data-ice="rootContainer"> <header> <a href="./">Home</a> <a href="identifiers.html">Reference</a> <a href="source.html">Source</a> <a data-ice="repoURL" href="https://github.com/killercrush/music-tempo" class="repo-url-github">Repository</a> <div class="search-box"> <span> <img src="./image/search.png"> <span class="search-input-edge"></span><input class="search-input"><span class="search-input-edge"></span> </span> <ul class="search-result"></ul> </div> </header> <nav class="navigation" data-ice="nav"><div> <ul> <li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/Agent.js~Agent.html">Agent</a></span></span></li> <li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/BeatTracking.js~BeatTracking.html">BeatTracking</a></span></span></li> <li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/FFT.js~FFT.html">FFT</a></span></span></li> <li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/MusicTempo.js~MusicTempo.html">MusicTempo</a></span></span></li> <li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/OnsetDetection.js~OnsetDetection.html">OnsetDetection</a></span></span></li> <li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/TempoInduction.js~TempoInduction.html">TempoInduction</a></span></span></li> </ul> </div> </nav> <div class="content" data-ice="content"><h1 data-ice="title">src/Agent.js</h1> <pre class="source-code line-number raw-source-code"><code class="prettyprint linenums" data-ice="content">/** * Agent is the central class for beat tracking * @class */ export default class Agent { /** * Constructor * @param {Number} tempo - tempo hypothesis of the Agent * @param {Number} firstBeatTime - the time of the first beat accepted by this Agent * @param {Number} firsteventScore - salience value of the first beat accepted by this Agent * @param {Array} agentList - reference to the agent list * @param {Object} [params={}] - parameters * @param {Number} [params.expiryTime=10] - the time after which an Agent that has not accepted any beat will be destroyed * @param {Number} [params.toleranceWndInner=0.04] - the maximum time that a beat can deviate from the predicted beat time without a fork occurring * @param {Number} [params.toleranceWndPre=0.15] - the maximum amount by which a beat can be earlier than the predicted beat time, expressed as a fraction of the beat period * @param {Number} [params.toleranceWndPost=0.3] - the maximum amount by which a beat can be later than the predicted beat time, expressed as a fraction of the beat period * @param {Number} [params.correctionFactor=50] - correction factor for updating beat period * @param {Number} [params.maxChange=0.2] - the maximum allowed deviation from the initial tempo, expressed as a fraction of the initial beat period * @param {Number} [params.penaltyFactor=0.5] - factor for correcting score, if onset do not coincide precisely with predicted beat time */ constructor(tempo, firstBeatTime, firsteventScore, agentList, params = {}) { /** * the time after which an Agent that has not accepted any beat will be destroyed * @type {Number} */ this.expiryTime = params.expiryTime || 10; /** * the maximum time that a beat can deviate from the predicted beat time without a fork occurring * @type {Number} */ this.toleranceWndInner = params.toleranceWndInner || 0.04; /** * the maximum amount by which a beat can be earlier than the predicted beat time, expressed as a fraction of the beat period * @type {Number} */ this.toleranceWndPre = params.toleranceWndPre || 0.15; /** * the maximum amount by which a beat can be later than the predicted beat time, expressed as a fraction of the beat period * @type {Number} */ this.toleranceWndPost = params.toleranceWndPost || 0.3; this.toleranceWndPre *= tempo; this.toleranceWndPost *= tempo; /** * correction factor for updating beat period * @type {Number} */ this.correctionFactor = params.correctionFactor || 50; /** * the maximum allowed deviation from the initial tempo, expressed as a fraction of the initial beat period * @type {Number} */ this.maxChange = params.maxChange || 0.2; /** * factor for correcting score, if onset do not coincide precisely with predicted beat time * @type {Number} */ this.penaltyFactor = params.penaltyFactor || 0.5; /** * the current tempo hypothesis of the Agent, expressed as the beat period * @type {Number} */ this.beatInterval = tempo; /** * the initial tempo hypothesis of the Agent, expressed as the beat period * @type {Number} */ this.initialBeatInterval = tempo; /** * the time of the most recent beat accepted by this Agent * @type {Number} */ this.beatTime = firstBeatTime; /** * the number of beats found by this Agent, including interpolated beats * @type {Number} */ this.totalBeatCount = 1; /** * the array of onsets accepted by this Agent as beats, plus interpolated beats * @type {Array} */ this.events = [firstBeatTime]; /** * sum of salience values of the onsets which have been interpreted as beats by this Agent * @type {Number} */ this.score = firsteventScore; /** * reference to the agent list * @type {Array} */ this.agentListRef = agentList; } /** * The event time is tested if it is a beat time * @param {Number} eventTime - the event time to be tested * @param {Number} eventScore - salience values of the event time * @return {Boolean} indicate whether the given event time was accepted as a beat time */ considerEvent(eventTime, eventScore) { if (eventTime - this.events[this.events.length - 1] &gt; this.expiryTime) { this.score = -1; return false; } let beatCount = Math.round( (eventTime - this.beatTime) / this.beatInterval ); let err = eventTime - this.beatTime - beatCount * this.beatInterval; if (beatCount &gt; 0 &amp;&amp; err &gt;= -this.toleranceWndPre &amp;&amp; err &lt;= this.toleranceWndPost) { if (Math.abs(err) &gt; this.toleranceWndInner) { this.agentListRef.push(this.clone()); } this.acceptEvent(eventTime, eventScore, err, beatCount); return true; } return false; } /** * Accept the event time as a beat time, and update the state of the Agent accordingly * @param {Number} eventTime - the event time to be accepted * @param {Number} eventScore - salience values of the event time * @param {Number} err - the difference between the predicted and actual beat times * @param {Number} beatCount - the number of beats since the last beat */ acceptEvent(eventTime, eventScore, err, beatCount) { this.beatTime = eventTime; this.events.push(eventTime); let corrErr = err / this.correctionFactor; if (Math.abs(this.initialBeatInterval - this.beatInterval - corrErr) &lt; this.maxChange * this.initialBeatInterval) { this.beatInterval += corrErr; } this.totalBeatCount += beatCount; let errFactor = err &gt; 0 ? err / this.toleranceWndPost : err / -this.toleranceWndPre; let scoreFactor = 1 - this.penaltyFactor * errFactor; this.score += eventScore * scoreFactor; } /** * Interpolates missing beats in the Agent&apos;s beat track */ fillBeats() { let prevBeat, nextBeat, currentInterval, beats; prevBeat = 0; if (this.events.length &gt; 2) { prevBeat = this.events[0]; } for (let i = 0; i &lt; this.events.length; i++) { nextBeat = this.events[i]; beats = Math.round((nextBeat - prevBeat) / this.beatInterval - 0.01); currentInterval = (nextBeat - prevBeat) / beats; let k = 0; for ( ; beats &gt; 1; beats--) { prevBeat += currentInterval; this.events.splice(i + k, 0, prevBeat); k++; } prevBeat = nextBeat; } } /** * Makes a clone of the Agent * @return {Agent} agent&apos;s clone */ clone() { let newAgent = new Agent(); newAgent.beatInterval = this.beatInterval; newAgent.initialBeatInterval = this.initialBeatInterval; newAgent.beatTime = this.beatTime; newAgent.totalBeatCount = this.totalBeatCount; newAgent.events = this.events.slice(); newAgent.expiryTime = this.expiryTime; newAgent.toleranceWndInner = this.toleranceWndInner; newAgent.toleranceWndPre = this.toleranceWndPre; newAgent.toleranceWndPost = this.toleranceWndPost; newAgent.correctionFactor = this.correctionFactor; newAgent.maxChange = this.maxChange; newAgent.penaltyFactor = this.penaltyFactor; newAgent.score = this.score; newAgent.agentListRef = this.agentListRef; return newAgent; } }</code></pre> </div> <footer class="footer"> Generated by <a href="https://esdoc.org">ESDoc<span data-ice="esdocVersion">(0.5.2)</span><img src="./image/esdoc-logo-mini-black.png"></a> </footer> <script src="script/search_index.js"></script> <script src="script/search.js"></script> <script src="script/pretty-print.js"></script> <script src="script/inherited-summary.js"></script> <script src="script/test-summary.js"></script> <script src="script/inner-link.js"></script> <script src="script/patch-for-local.js"></script> </body> </html>