Skip to content

Commit

Permalink
Merge pull request #65 from dlepaux/feature/web-test-runner
Browse files Browse the repository at this point in the history
Feature/web test runner
  • Loading branch information
dlepaux committed Jan 28, 2024
2 parents c71fb0c + d487267 commit cddd0ce
Show file tree
Hide file tree
Showing 13 changed files with 2,471 additions and 1,949 deletions.
3,924 changes: 2,247 additions & 1,677 deletions package-lock.json

Large diffs are not rendered by default.

15 changes: 5 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
"scripts": {
"build": "bash bin/build/library.sh",
"build:docs": "bash bin/build/library.sh",
"coverage": "codecov -f coverage/lcov.info",
"lint": "xo",
"lint:fix": "xo --fix",
"prepare": "husky install",
Expand All @@ -35,7 +34,8 @@
"release:major": "bash bin/release/version major",
"testing:prepare": "ts-node testing/tools/generate-manifest.ts",
"testing": "NODE_ENV=test karma start karma.testing.js",
"test": "NODE_ENV=test karma start karma.test.js"
"test:karma": "NODE_ENV=test karma start karma.test.js",
"test": "web-test-runner --node-resolve"
},
"repository": {
"type": "git",
Expand All @@ -46,17 +46,12 @@
"@commitlint/config-conventional": "^18.5.0",
"@types/chai": "^4.3.11",
"@types/mocha": "^10.0.6",
"chai": "^5.0.0",
"codecov": "^3.8.3",
"@web/test-runner": "^0.18.0",
"@web/test-runner-puppeteer": "^0.15.0",
"chai": "^5.0.3",
"esbuild": "^0.19.12",
"gh-pages": "^6.1.1",
"husky": "^8.0.3",
"karma": "^6.4.2",
"karma-chrome-launcher": "^3.2.0",
"karma-coverage-istanbul-reporter": "^3.0.3",
"karma-esbuild": "^2.3.0",
"karma-mocha": "^2.0.1",
"karma-typescript": "^5.5.4",
"mocha": "^10.2.0",
"music-metadata": "^7.14.0",
"standard-version": "^9.5.0",
Expand Down
2 changes: 1 addition & 1 deletion src/generated-processor.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default `"use strict";(()=>{var x="realtime-bpm-processor";async function p(n,o=.2,e=.95,s=.05){let t=e;do if(t-=s,await n(t))break;while(t>o)}function y(n=.2,o=.95,e=.05){let s={},t=o;do t-=e,s[t.toString()]=[];while(t>n);return s}function A(n=.2,o=.95,e=.05){let s={},t=o;do t-=e,s[t.toString()]=0;while(t>n);return s}function h(){let o=0,e=new Float32Array(0);function s(){o=0,e=new Float32Array(0)}function t(){return o===4096}function a(){s()}return function(r){t()&&a();let l=new Float32Array(e.length+r.length);return l.set(e,0),l.set(r,e.length),e=l,o+=r.length,{isBufferFull:t(),buffer:e,bufferSize:4096}}}function v(n,o,e=0,s=1e4){let t=[],{length:a}=n;for(let r=e;r<a;r+=1)n[r]>o&&(t.push(r),r+=s);return{peaks:t,threshold:o}}async function b(n,o){let e=15,s=!1,t=.2;if(await p(async a=>s?!0:(n[a].length>e&&(s=!0,t=a),!1)),s&&t){let a=w(n[t]),r=C(o,a);return{bpm:V(r),threshold:t}}return{bpm:[],threshold:t}}function V(n,o=5){return n.sort((e,s)=>s.count-e.count).splice(0,o)}function w(n){let o=[];for(let e=0;e<n.length;e++)for(let s=0;s<10;s++){let t=n[e],a=e+s,r=n[a]-t;if(!o.some(i=>i.interval===r?(i.count+=1,i.count):!1)){let i={interval:r,count:1};o.push(i)}}return o}function C(n,o){let e=[];for(let s of o){if(s.interval===0)continue;s.interval=Math.abs(s.interval);let t=60/(s.interval/n);for(;t<90;)t*=2;for(;t>180;)t/=2;if(t=Math.round(t),!e.some(r=>r.tempo===t?(r.count+=s.count,r.count):!1)){let r={tempo:t,count:s.count,confidence:0};e.push(r)}}return e}var d={minValidThreshold:()=>.2,validPeaks:()=>y(),nextIndexPeaks:()=>A(),skipIndexes:()=>1,effectiveBufferTime:()=>0},m=class{constructor(){this.options={continuousAnalysis:!1,stabilizationTime:2e4,muteTimeInIndexes:1e4,debug:!1};this.minValidThreshold=d.minValidThreshold();this.validPeaks=d.validPeaks();this.nextIndexPeaks=d.nextIndexPeaks();this.skipIndexes=d.skipIndexes();this.effectiveBufferTime=d.effectiveBufferTime();this.computedStabilizationTimeInSeconds=0;this.updateComputedValues()}setAsyncConfiguration(o){Object.assign(this.options,o),this.updateComputedValues()}updateComputedValues(){this.computedStabilizationTimeInSeconds=this.options.stabilizationTime/1e3}reset(){this.minValidThreshold=d.minValidThreshold(),this.validPeaks=d.validPeaks(),this.nextIndexPeaks=d.nextIndexPeaks(),this.skipIndexes=d.skipIndexes(),this.effectiveBufferTime=d.effectiveBufferTime()}async clearValidPeaks(o){this.minValidThreshold=Number.parseFloat(o.toFixed(2)),await p(async e=>(e<o&&typeof this.validPeaks[e]<"u"&&(delete this.validPeaks[e],delete this.nextIndexPeaks[e]),!1))}async analyzeChunck(o,e,s,t){this.options.debug&&t({message:"ANALYZE_CHUNK",data:o}),this.effectiveBufferTime+=s;let a=s*this.skipIndexes,r=a-s;await this.findPeaks(o,s,r,a,t),this.skipIndexes++;let l=await b(this.validPeaks,e),{threshold:i}=l;t({message:"BPM",result:l}),this.minValidThreshold<i&&(t({message:"BPM_STABLE",result:l}),await this.clearValidPeaks(i)),this.options.continuousAnalysis&&this.effectiveBufferTime/e>this.computedStabilizationTimeInSeconds&&(this.reset(),t({message:"ANALYZER_RESETED"}))}async findPeaks(o,e,s,t,a){await p(async r=>{if(this.nextIndexPeaks[r]>=t)return!1;let l=this.nextIndexPeaks[r]%e,{peaks:i,threshold:c}=v(o,r,l);if(i.length===0)return!1;for(let I of i){let f=s+I;this.nextIndexPeaks[c]=f+this.options.muteTimeInIndexes,this.validPeaks[c].push(f),this.options.debug&&a({message:"VALID_PEAK",data:{threshold:c,index:f}})}return!1},this.minValidThreshold)}};var T=class extends AudioWorkletProcessor{constructor(){super();this.realTimeBpmAnalyzer=new m;this.stopped=!1;this.aggregate=h(),this.port.addEventListener("message",this.onMessage.bind(this)),this.port.start()}onMessage(e){e.data.message==="ASYNC_CONFIGURATION"&&(console.log("[processor.onMessage] ASYNC_CONFIGURATION"),this.realTimeBpmAnalyzer.setAsyncConfiguration(e.data.parameters)),e.data.message==="RESET"&&(console.log("[processor.onMessage] RESET"),this.aggregate=h(),this.stopped=!1,this.realTimeBpmAnalyzer.reset()),e.data.message==="STOP"&&(console.log("[processor.onMessage] STOP"),this.aggregate=h(),this.stopped=!0,this.realTimeBpmAnalyzer.reset())}process(e,s,t){let a=e[0][0];if(this.stopped||!a)return!0;let{isBufferFull:r,buffer:l,bufferSize:i}=this.aggregate(a);return r&&this.realTimeBpmAnalyzer.analyzeChunck(l,sampleRate,i,c=>{this.port.postMessage(c)}).catch(c=>{console.error(c)}),!0}};registerProcessor(x,T);var Q={};})();
export default `"use strict";(()=>{var c=(a,o,e)=>new Promise((s,t)=>{var n=i=>{try{l(e.next(i))}catch(d){t(d)}},r=i=>{try{l(e.throw(i))}catch(d){t(d)}},l=i=>i.done?s(i.value):Promise.resolve(i.value).then(n,r);l((e=e.apply(a,o)).next())});var y="realtime-bpm-processor";function h(t){return c(this,arguments,function*(a,o=.2,e=.95,s=.05){let n=e;do if(n-=s,yield a(n))break;while(n>o)})}function A(a=.2,o=.95,e=.05){let s={},t=o;do t-=e,s[t.toString()]=[];while(t>a);return s}function v(a=.2,o=.95,e=.05){let s={},t=o;do t-=e,s[t.toString()]=0;while(t>a);return s}function m(){let o=0,e=new Float32Array(0);function s(){o=0,e=new Float32Array(0)}function t(){return o===4096}function n(){s()}return function(r){t()&&n();let l=new Float32Array(e.length+r.length);return l.set(e,0),l.set(r,e.length),e=l,o+=r.length,{isBufferFull:t(),buffer:e,bufferSize:4096}}}function b(a,o,e=0,s=1e4){let t=[],{length:n}=a;for(let r=e;r<n;r+=1)a[r]>o&&(t.push(r),r+=s);return{peaks:t,threshold:o}}function I(a,o){return c(this,null,function*(){let e=15,s=!1,t=.2;if(yield h(n=>c(this,null,function*(){return s?!0:(a[n].length>e&&(s=!0,t=n),!1)})),s&&t){let n=C(a[t]),r=S(o,n);return{bpm:w(r),threshold:t}}return{bpm:[],threshold:t}})}function w(a,o=5){return a.sort((e,s)=>s.count-e.count).splice(0,o)}function C(a){let o=[];for(let e=0;e<a.length;e++)for(let s=0;s<10;s++){let t=a[e],n=e+s,r=a[n]-t;if(!o.some(i=>i.interval===r?(i.count+=1,i.count):!1)){let i={interval:r,count:1};o.push(i)}}return o}function S(a,o){let e=[];for(let s of o){if(s.interval===0)continue;s.interval=Math.abs(s.interval);let t=60/(s.interval/a);for(;t<90;)t*=2;for(;t>180;)t/=2;if(t=Math.round(t),!e.some(r=>r.tempo===t?(r.count+=s.count,r.count):!1)){let r={tempo:t,count:s.count,confidence:0};e.push(r)}}return e}var u={minValidThreshold:()=>.2,validPeaks:()=>A(),nextIndexPeaks:()=>v(),skipIndexes:()=>1,effectiveBufferTime:()=>0},f=class{constructor(){this.options={continuousAnalysis:!1,stabilizationTime:2e4,muteTimeInIndexes:1e4,debug:!1};this.minValidThreshold=u.minValidThreshold();this.validPeaks=u.validPeaks();this.nextIndexPeaks=u.nextIndexPeaks();this.skipIndexes=u.skipIndexes();this.effectiveBufferTime=u.effectiveBufferTime();this.computedStabilizationTimeInSeconds=0;this.updateComputedValues()}setAsyncConfiguration(o){Object.assign(this.options,o),this.updateComputedValues()}updateComputedValues(){this.computedStabilizationTimeInSeconds=this.options.stabilizationTime/1e3}reset(){this.minValidThreshold=u.minValidThreshold(),this.validPeaks=u.validPeaks(),this.nextIndexPeaks=u.nextIndexPeaks(),this.skipIndexes=u.skipIndexes(),this.effectiveBufferTime=u.effectiveBufferTime()}clearValidPeaks(o){return c(this,null,function*(){this.minValidThreshold=Number.parseFloat(o.toFixed(2)),yield h(e=>c(this,null,function*(){return e<o&&typeof this.validPeaks[e]!="undefined"&&(delete this.validPeaks[e],delete this.nextIndexPeaks[e]),!1}))})}analyzeChunck(o,e,s,t){return c(this,null,function*(){this.options.debug&&t({message:"ANALYZE_CHUNK",data:o}),this.effectiveBufferTime+=s;let n=s*this.skipIndexes,r=n-s;yield this.findPeaks(o,s,r,n,t),this.skipIndexes++;let l=yield I(this.validPeaks,e),{threshold:i}=l;t({message:"BPM",result:l}),this.minValidThreshold<i&&(t({message:"BPM_STABLE",result:l}),yield this.clearValidPeaks(i)),this.options.continuousAnalysis&&this.effectiveBufferTime/e>this.computedStabilizationTimeInSeconds&&(this.reset(),t({message:"ANALYZER_RESETED"}))})}findPeaks(o,e,s,t,n){return c(this,null,function*(){yield h(r=>c(this,null,function*(){if(this.nextIndexPeaks[r]>=t)return!1;let l=this.nextIndexPeaks[r]%e,{peaks:i,threshold:d}=b(o,r,l);if(i.length===0)return!1;for(let F of i){let g=s+F;this.nextIndexPeaks[d]=g+this.options.muteTimeInIndexes,this.validPeaks[d].push(g),this.options.debug&&n({message:"VALID_PEAK",data:{threshold:d,index:g}})}return!1}),this.minValidThreshold)})}};var x=class extends AudioWorkletProcessor{constructor(){super();this.realTimeBpmAnalyzer=new f;this.stopped=!1;this.aggregate=m(),this.port.addEventListener("message",this.onMessage.bind(this)),this.port.start()}onMessage(e){e.data.message==="ASYNC_CONFIGURATION"&&(console.log("[processor.onMessage] ASYNC_CONFIGURATION"),this.realTimeBpmAnalyzer.setAsyncConfiguration(e.data.parameters)),e.data.message==="RESET"&&(console.log("[processor.onMessage] RESET"),this.aggregate=m(),this.stopped=!1,this.realTimeBpmAnalyzer.reset()),e.data.message==="STOP"&&(console.log("[processor.onMessage] STOP"),this.aggregate=m(),this.stopped=!0,this.realTimeBpmAnalyzer.reset())}process(e,s,t){let n=e[0][0];if(this.stopped||!n)return!0;let{isBufferFull:r,buffer:l,bufferSize:i}=this.aggregate(n);return r&&this.realTimeBpmAnalyzer.analyzeChunck(l,sampleRate,i,d=>{this.port.postMessage(d)}).catch(d=>{console.error(d)}),!0}};registerProcessor(y,x);var K={};})();
//# sourceMappingURL=realtime-bpm-processor.js.map
`;
2 changes: 0 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {realtimeBpmProcessorName} from './consts';

import realtimeBpmProcessorContent from './generated-processor';

export * from './realtime-bpm-analyzer';
Expand All @@ -8,7 +7,6 @@ export * from './types';

/**
* Create the RealTimeBpmProcessor needed to run the realtime strategy
* ENsure that the biquad lowpass filter is done before using this library
* @param {AudioContext} audioContext AudioContext instance
* @returns {Promise<AudioWorkletNode>}
* @public
Expand Down
25 changes: 0 additions & 25 deletions testing/index.html

This file was deleted.

8 changes: 3 additions & 5 deletions testing/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import {analyzeFullBuffer} from '../src/analyzer';

async function runTests() {
const audioContext = new AudioContext();
const response = await fetch('http://localhost:9876/base/tests/datasets/manifest.json');
const audioContext = new OfflineAudioContext(2, 44100 * 40, 44100);
const response = await fetch('/tests/datasets/manifest.json');
const json: Record<string, number> = await response.json(); // eslint-disable-line @typescript-eslint/no-unsafe-assignment
for (const fileName of Object.keys(json)) {
const bpm = json[fileName];
const response = await fetch(`http://localhost:9876/base/tests/datasets/${fileName}`);
const response = await fetch(`/tests/datasets/${fileName}`);
const buffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(buffer);
const tempo = await analyzeFullBuffer(audioBuffer);
console.assert(tempo[0].tempo === bpm, 'The computed tempo is not matching');
}

await audioContext.close();
}

runTests(); // eslint-disable-line @typescript-eslint/no-floating-promises
25 changes: 0 additions & 25 deletions tests/index.html

This file was deleted.

12 changes: 0 additions & 12 deletions tests/index.ts

This file was deleted.

Loading

0 comments on commit cddd0ce

Please sign in to comment.