From 0890f592ecdada66e8e264d6201e43ed0c6bf7ac Mon Sep 17 00:00:00 2001 From: Florrie Date: Mon, 6 Apr 2020 22:03:17 -0300 Subject: Auto-DJ --- backend.js | 8 ++++++++ players.js | 32 ++++++++++++++++++++++++++++++-- todo.txt | 3 +++ ui.js | 28 +++++++++++++++++++++++++--- 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/backend.js b/backend.js index d3ad122..ed1bd08 100644 --- a/backend.js +++ b/backend.js @@ -544,6 +544,14 @@ class QueuePlayer extends EventEmitter { this.player.setVolume(value) } + setVolumeMultiplier(value) { + this.player.setVolumeMultiplier(value); + } + + fadeIn() { + return this.player.fadeIn(); + } + setPauseNextTrack(value) { this.pauseNextTrack = !!value } diff --git a/players.js b/players.js index 868129d..5fe4714 100644 --- a/players.js +++ b/players.js @@ -19,6 +19,7 @@ class Player extends EventEmitter { this.isLooping = false this.isPaused = false this.volume = 100 + this.volumeMultiplier = 1.0 } set process(newProcess) { @@ -41,6 +42,8 @@ class Player extends EventEmitter { seekBack(secs) {} volUp(amount) {} volDown(amount) {} + setVolume(value) {} + updateVolume() {} togglePause() {} toggleLoop() {} setPause() {} @@ -61,6 +64,25 @@ class Player extends EventEmitter { this.emit('printStatusLine', data) } } + + setVolumeMultiplier(value) { + this.volumeMultiplier = value + this.updateVolume() + } + + fadeIn() { + const interval = 50 + const duration = 1000 + const delta = 1.0 - this.volumeMultiplier + const id = setInterval(() => { + this.volumeMultiplier += delta * interval / duration + if (this.volumeMultiplier >= 1.0) { + this.volumeMultiplier = 1.0 + clearInterval(id) + } + this.updateVolume() + }, interval) + } } module.exports.MPVPlayer = class extends Player { @@ -72,7 +94,7 @@ module.exports.MPVPlayer = class extends Player { if (this.isPaused) { opts.unshift('--pause') } - opts.unshift('--volume=' + this.volume) + opts.unshift('--volume=' + this.volume * this.volumeMultiplier) return opts } @@ -112,6 +134,8 @@ module.exports.MPVPlayer = class extends Player { this.printStatusLine(getTimeStrings({curHour, curMin, curSec, lenHour, lenMin, lenSec})) } + + this.updateVolume(); }) return new Promise(resolve => { @@ -177,7 +201,11 @@ module.exports.ControllableMPVPlayer = class extends module.exports.MPVPlayer { this.volume = value this.volume = Math.max(0, this.volume) this.volume = Math.min(100, this.volume) - this.sendCommand('set_property', 'volume', this.volume) + this.updateVolume() + } + + updateVolume() { + this.sendCommand('set_property', 'volume', this.volume * this.volumeMultiplier) } togglePause() { diff --git a/todo.txt b/todo.txt index e039cc8..35ac635 100644 --- a/todo.txt +++ b/todo.txt @@ -487,3 +487,6 @@ TODO: Figure out looping not always working consistently. I've tried to deal with this before, but it's been broken since switching to socat. Maybe we aren't receiving time data as consistently, or aren't re-applying loop when we're supposed to? + +TODO: Show how many tracks remain in a queue player's queue, ala "+1" floated + to the right, behind the playback position/duration indicator. diff --git a/ui.js b/ui.js index f9f066e..f432ebe 100644 --- a/ui.js +++ b/ui.js @@ -7,8 +7,10 @@ const processSmartPlaylist = require('./smart-playlist') const UndoManager = require('./undo-manager') const { - shuffleArray, - getTimeStringsFromSec + commandExists, + getTimeStringsFromSec, + promisifyProcess, + shuffleArray } = require('./general-util') const { @@ -54,6 +56,7 @@ const { const TuiTextEditor = require('tui-text-editor') const { promisify } = require('util') +const { spawn } = require('child_process') const fs = require('fs') const open = require('open') const path = require('path') @@ -181,6 +184,7 @@ class AppElement extends FocusElement { this.backend = backend this.telnetServer = null this.isPartyHost = false + this.enableAutoDJ = false this.config = Object.assign({ canControlPlayback: true, @@ -330,6 +334,7 @@ class AppElement extends FocusElement { playingTrack && {element: this.playingControl}, {element: this.loopingControl}, {element: this.pauseNextControl}, + {element: this.autoDJControl}, {element: this.volumeSlider}, {divider: true}, previous && {label: `Previous (${previous.name})`, action: () => this.SQP.playPrevious(playingTrack)}, @@ -387,6 +392,12 @@ class AppElement extends FocusElement { getEnabled: () => this.config.canControlPlayback }) + this.autoDJControl = new ToggleControl('Enable Auto-DJ?', { + setValue: val => (this.enableAutoDJ = val), + getValue: val => this.enableAutoDJ, + getEnabled: () => this.config.canControlPlayback + }) + this.bindListeners() this.initialAttachListeners() @@ -491,7 +502,7 @@ class AppElement extends FocusElement { this.removeQueuePlayerListenersAndUI(queuePlayer) } - handlePlaying(track, oldTrack, queuePlayer) { + async handlePlaying(track, oldTrack, queuePlayer) { const PIE = this.getPlaybackInfoElementForQueuePlayer(queuePlayer) if (PIE) { PIE.updateTrack() @@ -503,6 +514,17 @@ class AppElement extends FocusElement { this.queueListingElement.selectAndShow(track) } } + + if (track && this.enableAutoDJ) { + queuePlayer.setVolumeMultiplier(0.5); + const message = 'now playing: ' + getNameWithoutTrackNumber(track); + if (await commandExists('espeak')) { + await promisifyProcess(spawn('espeak', [message])); + } else if (await commandExists('say')) { + await promisifyProcess(spawn('espeak', [message])); + } + queuePlayer.fadeIn(); + } } handleReceivedTimeData(data, queuePlayer) { -- cgit 1.3.0-6-gf8a5