diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 708558e..0c636f9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,7 +15,8 @@ "music-metadata": "^7.13.0", "realtime-bpm-analyzer": "^3.2.1", "vue": "^3.2.13", - "vue-router": "^4.0.3" + "vue-router": "^4.0.3", + "web-audio-beat-detector": "^8.1.55" }, "devDependencies": { "@vue/cli-plugin-babel": "~5.0.0", @@ -1640,12 +1641,11 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", - "dev": true, + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -3877,6 +3877,18 @@ "node": ">= 4.0.0" } }, + "node_modules/automation-events": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/automation-events/-/automation-events-6.0.11.tgz", + "integrity": "sha512-tUqFMJalQ3OAcbQOXzzNDpxzkuygMQ3eM92lJWJRn6YWJnH4oYIU3pzKipANOul/6L2vDFrOzgXVE21Dv4z/zw==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, "node_modules/autoprefixer": { "version": "10.4.14", "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.14.tgz", @@ -6998,6 +7010,18 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "node_modules/fast-unique-numbers": { + "version": "8.0.10", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.10.tgz", + "integrity": "sha512-NUIqIhFYSwexP73lH76CLFkqNk9wHP00mUuqo4CMdhW+ETEGaIb6meriqdXRClwOQcTx2Qj4Ctw9Axo8cSwSqw==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.15.0.tgz", @@ -11171,10 +11195,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "node_modules/regenerator-transform": { "version": "0.15.1", @@ -12057,6 +12080,16 @@ "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", "dev": true }, + "node_modules/standardized-audio-context": { + "version": "25.3.58", + "resolved": "https://registry.npmjs.org/standardized-audio-context/-/standardized-audio-context-25.3.58.tgz", + "integrity": "sha512-1p5wVGiy4MfmhEt9MRY8yjZdkX+fi5jMxG1lqnAS1YmLUpE9VrOowxSINQ9Gjs89dFZMaENVwcMSPeQCjlz90Q==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "automation-events": "^6.0.11", + "tslib": "^2.6.2" + } + }, "node_modules/stat-mode": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/stat-mode/-/stat-mode-1.0.0.tgz", @@ -12636,10 +12669,9 @@ } }, "node_modules/tslib": { - "version": "2.5.2", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.5.2.tgz", - "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==", - "dev": true + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tunnel": { "version": "0.0.6", @@ -13821,6 +13853,38 @@ "defaults": "^1.0.3" } }, + "node_modules/web-audio-beat-detector": { + "version": "8.1.55", + "resolved": "https://registry.npmjs.org/web-audio-beat-detector/-/web-audio-beat-detector-8.1.55.tgz", + "integrity": "sha512-K/RdkLNl+oBSM9lXb7t2scdqXhk4nkG8+6abknRD3YM+VP4nSYIugN7p8Bm/4EYZua1GWx7JeJXDDdrfFhabYg==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "tslib": "^2.6.2", + "web-audio-beat-detector-broker": "^4.0.92", + "web-audio-beat-detector-worker": "^5.2.43" + } + }, + "node_modules/web-audio-beat-detector-broker": { + "version": "4.0.92", + "resolved": "https://registry.npmjs.org/web-audio-beat-detector-broker/-/web-audio-beat-detector-broker-4.0.92.tgz", + "integrity": "sha512-uxCHh9iVlS4fqc1Tn2A3jIybeoyvmtm1pqvC0/k2IjmcW44pIvaZMMkHCLf7SPJmPZMlbotBR4LDatnRacE5zA==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "fast-unique-numbers": "^8.0.10", + "standardized-audio-context": "^25.3.58", + "tslib": "^2.6.2", + "web-audio-beat-detector-worker": "^5.2.43" + } + }, + "node_modules/web-audio-beat-detector-worker": { + "version": "5.2.43", + "resolved": "https://registry.npmjs.org/web-audio-beat-detector-worker/-/web-audio-beat-detector-worker-5.2.43.tgz", + "integrity": "sha512-/7U/eHDixwM5NBGZ6CEF9K5D5XrhJbRawY+52vrpIkhHpoyaM0EF02LDwW8WZ7lLjTDo0ab/WzjUIO+oeUpEDg==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "tslib": "^2.6.2" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -15586,12 +15650,11 @@ "dev": true }, "@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", - "dev": true, + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "@babel/template": { @@ -17471,6 +17534,15 @@ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true }, + "automation-events": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/automation-events/-/automation-events-6.0.11.tgz", + "integrity": "sha512-tUqFMJalQ3OAcbQOXzzNDpxzkuygMQ3eM92lJWJRn6YWJnH4oYIU3pzKipANOul/6L2vDFrOzgXVE21Dv4z/zw==", + "requires": { + "@babel/runtime": "^7.23.2", + "tslib": "^2.6.2" + } + }, "autoprefixer": { "version": "10.4.14", "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.14.tgz", @@ -20006,6 +20078,15 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "fast-unique-numbers": { + "version": "8.0.10", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.10.tgz", + "integrity": "sha512-NUIqIhFYSwexP73lH76CLFkqNk9wHP00mUuqo4CMdhW+ETEGaIb6meriqdXRClwOQcTx2Qj4Ctw9Axo8cSwSqw==", + "requires": { + "@babel/runtime": "^7.23.2", + "tslib": "^2.6.2" + } + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.15.0.tgz", @@ -23251,10 +23332,9 @@ } }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "regenerator-transform": { "version": "0.15.1", @@ -24006,6 +24086,16 @@ "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", "dev": true }, + "standardized-audio-context": { + "version": "25.3.58", + "resolved": "https://registry.npmjs.org/standardized-audio-context/-/standardized-audio-context-25.3.58.tgz", + "integrity": "sha512-1p5wVGiy4MfmhEt9MRY8yjZdkX+fi5jMxG1lqnAS1YmLUpE9VrOowxSINQ9Gjs89dFZMaENVwcMSPeQCjlz90Q==", + "requires": { + "@babel/runtime": "^7.23.2", + "automation-events": "^6.0.11", + "tslib": "^2.6.2" + } + }, "stat-mode": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/stat-mode/-/stat-mode-1.0.0.tgz", @@ -24460,10 +24550,9 @@ } }, "tslib": { - "version": "2.5.2", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.5.2.tgz", - "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==", - "dev": true + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "tunnel": { "version": "0.0.6", @@ -25426,6 +25515,38 @@ "defaults": "^1.0.3" } }, + "web-audio-beat-detector": { + "version": "8.1.55", + "resolved": "https://registry.npmjs.org/web-audio-beat-detector/-/web-audio-beat-detector-8.1.55.tgz", + "integrity": "sha512-K/RdkLNl+oBSM9lXb7t2scdqXhk4nkG8+6abknRD3YM+VP4nSYIugN7p8Bm/4EYZua1GWx7JeJXDDdrfFhabYg==", + "requires": { + "@babel/runtime": "^7.23.2", + "tslib": "^2.6.2", + "web-audio-beat-detector-broker": "^4.0.92", + "web-audio-beat-detector-worker": "^5.2.43" + } + }, + "web-audio-beat-detector-broker": { + "version": "4.0.92", + "resolved": "https://registry.npmjs.org/web-audio-beat-detector-broker/-/web-audio-beat-detector-broker-4.0.92.tgz", + "integrity": "sha512-uxCHh9iVlS4fqc1Tn2A3jIybeoyvmtm1pqvC0/k2IjmcW44pIvaZMMkHCLf7SPJmPZMlbotBR4LDatnRacE5zA==", + "requires": { + "@babel/runtime": "^7.23.2", + "fast-unique-numbers": "^8.0.10", + "standardized-audio-context": "^25.3.58", + "tslib": "^2.6.2", + "web-audio-beat-detector-worker": "^5.2.43" + } + }, + "web-audio-beat-detector-worker": { + "version": "5.2.43", + "resolved": "https://registry.npmjs.org/web-audio-beat-detector-worker/-/web-audio-beat-detector-worker-5.2.43.tgz", + "integrity": "sha512-/7U/eHDixwM5NBGZ6CEF9K5D5XrhJbRawY+52vrpIkhHpoyaM0EF02LDwW8WZ7lLjTDo0ab/WzjUIO+oeUpEDg==", + "requires": { + "@babel/runtime": "^7.23.2", + "tslib": "^2.6.2" + } + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 07484ad..cb5af51 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,7 +18,8 @@ "music-metadata": "^7.13.0", "realtime-bpm-analyzer": "^3.2.1", "vue": "^3.2.13", - "vue-router": "^4.0.3" + "vue-router": "^4.0.3", + "web-audio-beat-detector": "^8.1.55" }, "devDependencies": { "@vue/cli-plugin-babel": "~5.0.0", diff --git a/frontend/src/client/audioProcessing.js b/frontend/src/client/audioProcessing.js deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/client/backgroundAnim.css b/frontend/src/client/backgroundAnim.css index e7e5827..80dd2f8 100644 --- a/frontend/src/client/backgroundAnim.css +++ b/frontend/src/client/backgroundAnim.css @@ -9,16 +9,25 @@ background: conic-gradient( blue, green, red, blue ); animation: gradientAnim 10s infinite linear; background-position: center; - transition: all 0.1s; } .beat { height: 100%; width: 100%; background-color: rgba( 0, 0, 0, 0.2 ); + animation: beatAnim 0.6s infinite linear; display: none; } +@keyframes beatAnim { + 0% { + background-color: rgba( 0, 0, 0, 0.2 ); + } + 50% { + background-color: rgba( 0, 0, 0, 0 ); + } +} + @keyframes gradientAnim { from { transform: rotate( 0deg ); diff --git a/frontend/src/client/showcase.css b/frontend/src/client/showcase.css index 26796ed..68baef3 100644 --- a/frontend/src/client/showcase.css +++ b/frontend/src/client/showcase.css @@ -1,52 +1,3 @@ -:root, :root.light { - --primary-color: #2c3e50; - --accent-background: rgb(30, 30, 82); - --secondary-color: white; - --background-color: white; - --popup-color: rgb(224, 224, 224); - --accent-color: #42b983; - --hover-color: rgb(165, 165, 165); - --accent-background-hover: rgb(124, 140, 236); - --overlay-color: rgba(0, 0, 0, 0.7); - --border-color: rgb(100, 100, 100); - --highlight-backdrop: rgb(143, 134, 192); - --hint-color: rgb(174, 210, 221); - --PI: 3.14159265358979; -} - -:root.dark { - --primary-color: white; - --accent-background: rgb(56, 56, 112); - --secondary-color: white; - --background-color: rgb(32, 32, 32); - --popup-color: rgb(58, 58, 58); - --accent-color: #42b983; - --hover-color: rgb(83, 83, 83); - --accent-background-hover: #4380a8; - --overlay-color: rgba(104, 104, 104, 0.575); - --border-color: rgb(190, 190, 190); - --highlight-backdrop: rgb(85, 63, 207); - --hint-color: rgb(88, 91, 110); -} - -@media ( prefers-color-scheme: dark ) { - :root { - --primary-color: white; - --accent-background: rgb(56, 56, 112); - --secondary-color: white; - --background-color: rgb(32, 32, 32); - --popup-color: rgb(58, 58, 58); - --accent-color: #42b983; - --hover-color: rgb(83, 83, 83); - --accent-background-hover: #4380a8; - --overlay-color: rgba(104, 104, 104, 0.575); - --border-color: rgb(190, 190, 190); - --highlight-backdrop: rgb(85, 63, 207); - --hint-color: rgb(88, 91, 110); - } -} - - .material-symbols-outlined { font-variation-settings: 'FILL' 0, @@ -60,6 +11,7 @@ body, html { height: 100%; margin: 0; padding: 0; + color: white; } body { @@ -145,7 +97,8 @@ body { width: 80%; margin: 2px; padding: 1vh; - border: 1px var( --border-color ) solid; + border: 1px white solid; + background-color: rgba( 0, 0, 0, 0.4 ); } .song-details-wrapper { @@ -172,13 +125,26 @@ body { user-select: none; } -.current-song { +.current-song-wrapper { display: flex; justify-content: center; align-items: center; flex-direction: column; height: 55vh; width: 100%; + margin-bottom: 0.5%; + margin-top: 0.5%; +} + +.current-song { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + margin-top: 2vh; + padding: 1vh; + text-align: center; + background-color: rgba( 0, 0, 0, 0.4 ); } .fancy-view-song-art { @@ -196,4 +162,11 @@ body { #app { background-color: rgba( 0, 0, 0, 0 ); +} + +#progress { + background-color: rgba(45, 28, 145); + width: 30vw; + border: none; + border-radius: 50px; } \ No newline at end of file diff --git a/frontend/src/client/showcase.html b/frontend/src/client/showcase.html index b7627cb..1577519 100644 --- a/frontend/src/client/showcase.html +++ b/frontend/src/client/showcase.html @@ -14,12 +14,14 @@
-
+
music_note - -

{{ playingSong.title }}

-

{{ playingSong.artist }}

+
+ +

{{ playingSong.title }}

+

{{ playingSong.artist }}

+
@@ -37,6 +39,9 @@

{{ song.title }}

{{ song.artist }}

+
+ {{ getTimeUntil( song ) }} +
diff --git a/frontend/src/client/showcase.js b/frontend/src/client/showcase.js index 9e3860c..3f1938b 100644 --- a/frontend/src/client/showcase.js +++ b/frontend/src/client/showcase.js @@ -27,6 +27,22 @@ createApp( { } } return ret; + }, + getTimeUntil( ) { + return ( song ) => { + let timeRemaining = 0; + for ( let i = this.queuePos; i < this.songs.length; i++ ) { + if ( this.songs[ i ] == song ) { + break; + } + timeRemaining += parseInt( this.songs[ i ].duration ); + } + if ( timeRemaining === 0 ) { + return 'Currently playing'; + } else { + return 'Playing in less than ' + Math.ceil( timeRemaining / 60 - this.pos / 60 ) + 'min'; + } + } } }, methods: { @@ -34,12 +50,12 @@ createApp( { this.startTime = new Date().getTime(); this.timeTracker = setInterval( () => { this.pos += 0.075; - this.progressBar = this.pos / this.playingSong.duration * 1000; + this.progressBar = ( this.pos / this.playingSong.duration ) * 1000; }, 75 ); this.timeCorrector = setInterval( () => { this.pos = this.oldPos + ( new Date().getTime() - this.startTime ) / 1000; - this.progressBar = this.pos / this.playingSong.duration * 1000; + this.progressBar = ( this.pos / this.playingSong.duration ) * 1000; }, 5000 ); }, stopTimeTracker () { @@ -47,6 +63,20 @@ createApp( { clearInterval( this.timeCorrector ); this.oldPos = this.pos; }, + // getTimeUntil( song ) { + // let timeRemaining = 0; + // for ( let i = this.queuePos; i < this.songs.length; i++ ) { + // if ( this.songs[ i ] == song ) { + // break; + // } + // timeRemaining += parseInt( this.songs[ i ].duration ); + // } + // if ( timeRemaining === 0 ) { + // return 'Currently playing'; + // } else { + // return 'Playing in about ' + Math.ceil( timeRemaining / 60 ) + 'min'; + // } + // }, connect() { let source = new EventSource( '/clientDisplayNotifier', { withCredentials: true } ); source.onmessage = ( e ) => { @@ -62,9 +92,9 @@ createApp( { this.songs = data.data.songQueue ?? []; this.pos = data.data.pos ?? 0; this.oldPos = data.data.pos ?? 0; + this.startTime = new Date().getTime(); this.progressBar = this.pos / this.playingSong.duration * 1000; this.queuePos = data.data.queuePos ?? 0; - if ( this.isPlaying ) this.startTimeTracker(); getColourPalette( '/getSongCover?filename=' + data.data.playingSong.filename ).then( palette => { this.colourPalette = palette; this.handleBackground(); @@ -109,7 +139,8 @@ createApp( { }, false ); }, handleBackground() { - // TODO: Consider using mic and realtime-bpm-analyzer + // TODO: Add hotkeys + // TODO: Check that colours are not too similar let colours = {}; if ( this.colourPalette[ 0 ] ) { for ( let i = 0; i < 3; i++ ) { @@ -117,23 +148,17 @@ createApp( { } } $( '#background' ).css( 'background', `conic-gradient( ${ colours[ 0 ] }, ${ colours[ 1 ] }, ${ colours[ 2 ] }, ${ colours[ 0 ] } )` ); - // if ( this.playingSong.bpm && this.isPlaying ) { - // $( '.beat' ).show(); - // $( '.beat' ).css( 'animation-duration', 60 / this.playingSong.bpm ); - // $( '.beat' ).css( 'animation-delay', this.pos % ( 60 / this.playingSong.bpm * this.pos ) ); - // } else { - // $( '.beat' ).hide(); - // } + if ( this.playingSong.bpm && this.isPlaying ) { + $( '.beat' ).show(); + $( '.beat' ).css( 'animation-duration', 60 / this.playingSong.bpm ); + $( '.beat' ).css( 'animation-delay', this.pos % ( 60 / this.playingSong.bpm * this.pos ) + this.playingSong.bpmOffset - ( 60 / this.playingSong.bpm * this.pos / 2 ) ); + } else { + $( '.beat' ).hide(); + } } }, mounted() { this.connect(); - // Initialize Web Audio API components - const audioContext = new ( window.AudioContext || window.webkitAudioContext )(); - // Start audio analysis - navigator.mediaDevices.getUserMedia( { audio: true } ).then( ( stream ) => { - - } ); }, watch: { isPlaying( value ) { diff --git a/frontend/src/components/player.vue b/frontend/src/components/player.vue index d9993c3..e46749c 100644 --- a/frontend/src/components/player.vue +++ b/frontend/src/components/player.vue @@ -124,7 +124,7 @@ import FancyView from './fancyView.vue'; import Notifications from './notifications.vue'; import SliderView from './sliderView.vue'; -import * as realtimeBPM from 'realtime-bpm-analyzer'; +import { guess } from 'web-audio-beat-detector'; export default { data() { @@ -140,6 +140,7 @@ export default { durationBeautified: '--:--', hasLoadedSongs: false, isShowingFancyView: false, + notifier: null, } }, components: { @@ -193,15 +194,14 @@ export default { const audioContext = new AudioContext(); fetch( 'http://localhost:8081/getSongFile?filename=' + filename ).then( res => { res.arrayBuffer().then( buf => { - // The file is uploaded, now we decode it audioContext.decodeAudioData( buf, audioBuffer => { - // The result is passed to the analyzer - realtimeBPM.analyzeFullBuffer( audioBuffer ).then( topCandidates => { - // Do something with the BPM - this.playingSong.bpm = topCandidates[ 0 ].tempo; + guess( audioBuffer ).then( ( data ) => { + this.playingSong.bpm = data.bpm; + this.playingSong.accurateTempo = data.tempo; + this.playingSong.bpmOffset = data.offset; this.sendUpdate( 'playingSong' ); } ); - }); + } ); } ); } ); } @@ -250,12 +250,16 @@ export default { this.playbackPosBeautified += secondCount; } }, 0.02 ); + this.progressTracker = setInterval( () => { + this.sendUpdate( 'pos' ); + }, 5000 ); this.sendUpdate( 'isPlaying' ); } else if ( action === 'pause' ) { this.$emit( 'update', { 'type': 'playback', 'status': false } ); musicPlayer.pause(); try { clearInterval( this.progressTracker ); + clearInterval( this.notifier ); } catch ( err ) {}; this.isPlaying = false; this.sendUpdate( 'isPlaying' );