« get me outta code hell

mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--backend.js8
-rw-r--r--players.js32
-rw-r--r--todo.txt3
-rw-r--r--ui.js28
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) {