Add ReplayGain support.

This commit is contained in:
David Beswick 2020-10-31 09:34:54 +11:00
parent fc3257a7fb
commit aab63a7da2
6 changed files with 142 additions and 10 deletions

View File

@ -25,6 +25,7 @@ const mapFunDefault = function(left, right) {
'album-art': left.aaFile,
filepath: left.filepath,
rating: right.rating,
"replaygain-track-db": left.replaygainTrackDb,
vpath: left.vpath
};
};
@ -137,7 +138,8 @@ exports.setup = function (mstream, program) {
"title": result[0].title ? result[0].title : null,
"year": result[0].year ? result[0].year : null,
"album-art": result[0].aaFile ? result[0].aaFile : null,
"rating": result[0].rating ? result[0].rating : null
"rating": result[0].rating ? result[0].rating : null,
"replaygain-track-db": result[0]['replaygain-track-db'] ? result[0]['replaygain-track-db'] : null
}
});
});
@ -279,7 +281,8 @@ exports.setup = function (mstream, program) {
"title": result[0].title ? result[0].title : null,
"year": result[0].year ? result[0].year : null,
"album-art": result[0].aaFile ? result[0].aaFile : null,
"rating": result[0].rating ? result[0].rating : null
"rating": result[0].rating ? result[0].rating : null,
"replaygain-track-db": result[0]['replaygain-track-db'] ? result[0]['replaygain-track-db'] : null
};
}
}
@ -448,7 +451,8 @@ exports.setup = function (mstream, program) {
"year": row.year ? row.year : null,
"album-art": row.aaFile ? row.aaFile : null,
"filename": fe.basename(row.filepath),
"rating": row.rating ? row.rating : null
"rating": row.rating ? row.rating : null,
"replaygain-track-db": row['replaygain-track-db'] ? row['replaygain-track-db'] : null
}
});
}
@ -567,7 +571,8 @@ exports.setup = function (mstream, program) {
"title": randomSong.title ? randomSong.title : null,
"year": randomSong.year ? randomSong.year : null,
"album-art": randomSong.aaFile ? randomSong.aaFile : null,
"rating": randomSong.rating ? randomSong.rating : null
"rating": randomSong.rating ? randomSong.rating : null,
"replaygain-track-db": randomSong['replaygain-track-db'] ? randomSong['replaygain-track-db'] : null
}
});
@ -669,6 +674,7 @@ exports.setup = function (mstream, program) {
'album-art': right.aaFile,
filepath: right.filepath,
rating: left.rating,
"replaygain-track-db": right.replaygainTrackDb,
vpath: right.vpath
};
};
@ -700,7 +706,8 @@ exports.setup = function (mstream, program) {
"year": row.year ? row.year : null,
"album-art": row.aaFile ? row.aaFile : null,
"filename": fe.basename(row.filepath),
"rating": row.rating ? row.rating : null
"rating": row.rating ? row.rating : null,
"replaygain-track-db": row['replaygain-track-db'] ? row['replaygain-track-db'] : null
}
});
}
@ -752,7 +759,8 @@ exports.setup = function (mstream, program) {
"year": row.year ? row.year : null,
"album-art": row.aaFile ? row.aaFile : null,
"filename": fe.basename(row.filepath),
"rating": row.rating ? row.rating : null
"rating": row.rating ? row.rating : null,
"replaygain-track": row.replaygainTrack ? row.replaygainTrack : null
}
});
}

View File

@ -75,7 +75,8 @@ exports.insertEntries = function (arrayOfSongs, vpath) {
"hash": song.hash,
"aaFile": song.aaFile ? song.aaFile : null,
"vpath": vpath,
"ts": Math.floor(Date.now() / 1000)
"ts": Math.floor(Date.now() / 1000),
"replaygainTrackDb": song.replaygain_track_gain ? song.replaygain_track_gain.dB : null
});
saveCounter++;

View File

@ -464,3 +464,19 @@ input[type=range]:focus::-ms-fill-lower {
input[type=range]:focus::-ms-fill-upper {
background: #5c5c5c;
}
#rg-pregain-info {
color: rgb(102, 132, 178);
font-weight: 800;
font-size: 13px;
font-family: 'Jura', sans-serif;
width: 34px;
padding: 13px 0;
opacity: 0;
transition: opacity 0.25s;
text-align: right;
}
#rg-status {
transition: opacity 0.25s;
}

View File

@ -5,6 +5,8 @@ var MSTREAMPLAYER = (function () {
mstreamModule.positionCache = { val: -1 };
mstreamModule.playlist = [];
var cacheTimeout = 30000;
var currentReplayGainAmp = 1.0;
mstreamModule.editSongMetadata = function (key, value, songIndex) {
for (var i = 0, len = mstreamModule.playlist.length; i < len; i++) {
@ -22,13 +24,14 @@ var MSTREAMPLAYER = (function () {
var localPlayerObject = getCurrentPlayer();
var otherPlayerObject = getOtherPlayer();
const rgainAdjustedVolume = newVolume / 100 * currentReplayGainAmp
if (localPlayerObject && localPlayerObject.playerObject) {
localPlayerObject.playerObject.volume(newVolume / 100);
localPlayerObject.playerObject.volume(rgainAdjustedVolume);
}
if (otherPlayerObject && otherPlayerObject.playerObject) {
otherPlayerObject.playerObject.volume(newVolume / 100);
otherPlayerObject.playerObject.volume(rgainAdjustedVolume);
}
}
@ -495,6 +498,8 @@ var MSTREAMPLAYER = (function () {
}
// Should be called whenever the "metadata" field of the current song is changed, or
// the current song is changed.
mstreamModule.resetCurrentMetadata = function () {
var lPlayer = getCurrentPlayer();
var curSong = lPlayer.songObject;
@ -505,9 +510,40 @@ var MSTREAMPLAYER = (function () {
mstreamModule.playerStats.metadata.title = curSong.metadata.title;
mstreamModule.playerStats.metadata.year = curSong.metadata.year;
mstreamModule.playerStats.metadata['album-art'] = curSong.metadata['album-art'];
mstreamModule.playerStats.metadata['replaygain-track-db'] = curSong.metadata['replaygain-track-db'];
}
mstreamModule.updateReplayGainFromSong(curSong);
}
// Update ReplayGain state from given song, if required.
mstreamModule.updateReplayGainFromSong = function (song) {
console.assert(song);
var newRgAmpValue = undefined;
if (mstreamModule.playerStats.replayGain) {
if (song.metadata) {
const rgainDb = song.metadata['replaygain-track-db'];
if (rgainDb) {
// Note: the music-metadata package has a similar calculation in its Utils class, and that's used to
// calculate a returned 'ratio' value. However, the calculation used there is actually calculating the power
// ratio and not the amplitude ratio as required. As power is amplitude squared, that results in a volume
// reduction that's too small (i.e. 0.25**2 = 0.00625).
newRgAmpValue = Math.pow(10, (rgainDb + mstreamModule.playerStats.replayGainPreGainDb) / 20)
}
}
if (newRgAmpValue === undefined) {
currentReplayGainAmp = 0.316; // -10 db for songs without ReplayGain info.
} else {
currentReplayGainAmp = newRgAmpValue;
}
} else {
currentReplayGainAmp = 1.0;
}
mstreamModule.changeVolume(mstreamModule.playerStats.volume);
}
mstreamModule.resetPositionCache = function () {
var len;
@ -610,7 +646,9 @@ var MSTREAMPLAYER = (function () {
"year": false,
"album-art": false,
"filepath": false,
}
},
replayGain: false,
replayGainPreGainDb: 0
}
var playerA = {
@ -889,6 +927,21 @@ var MSTREAMPLAYER = (function () {
return mstreamModule.playerStats.autoDJ;
}
// ReplayGain
mstreamModule.setReplayGainActive = function (isActive) {
mstreamModule.playerStats.replayGain = isActive;
if (getCurrentPlayer() && getCurrentPlayer().songObject) {
mstreamModule.updateReplayGainFromSong(getCurrentPlayer().songObject);
}
}
mstreamModule.setReplayGainPreGainDb = function (db) {
mstreamModule.playerStats.replayGainPreGainDb = db;
if (getCurrentPlayer() && getCurrentPlayer().songObject) {
mstreamModule.updateReplayGainFromSong(getCurrentPlayer().songObject);
}
}
// Return an object that is assigned to Module
return mstreamModule;
}());

View File

@ -2,6 +2,14 @@ var VUEPLAYER = (function () {
const mstreamModule = {};
mstreamModule.playlists = [];
const replayGainPreGainSettings = [
-15.0,
-10.0,
-6.0,
0.0
];
var replayGainInfoTimeout;
var currentPopperSongIndex2;
var currentPopperSongIndex;
var currentPopperSong;
@ -218,6 +226,10 @@ var VUEPLAYER = (function () {
created: function () {
if (typeof(Storage) !== "undefined") {
this.curVol = localStorage.getItem("volume");
MSTREAMPLAYER.setReplayGainActive(localStorage.getItem("replayGain") == "true");
const rgPregain = Number(localStorage.getItem("replayGainPreGainDb"));
MSTREAMPLAYER.setReplayGainPreGainDb(rgPregain === NaN ? 0 : rgPregain);
}
MSTREAMPLAYER.changeVolume(parseInt(this.curVol));
},
@ -296,6 +308,43 @@ var VUEPLAYER = (function () {
toggleAutoDJ: function () {
MSTREAMPLAYER.toggleAutoDJ();
},
toggleReplayGain: function () {
// With a series of clicks, allow the user to first activate ReplayGain, then progress through a list of
// settings for the desired level of pre-gain, and then finally disable ReplayGain again.
if (replayGainInfoTimeout) { clearTimeout(replayGainInfoTimeout); }
var pregainInfoElement = document.getElementById('rg-pregain-info')
var rgStatusElement = document.getElementById('rg-status')
if (!this.playerStats.replayGain) {
MSTREAMPLAYER.setReplayGainPreGainDb(replayGainPreGainSettings[0]);
MSTREAMPLAYER.setReplayGainActive(true);
} else {
const settingsIdx = replayGainPreGainSettings.indexOf(this.playerStats.replayGainPreGainDb);
if (settingsIdx == -1 || settingsIdx >= replayGainPreGainSettings.length - 1) {
MSTREAMPLAYER.setReplayGainActive(false);
pregainInfoElement.style.opacity = "0.0";
rgStatusElement.style.opacity = "1.0";
} else {
MSTREAMPLAYER.setReplayGainPreGainDb(replayGainPreGainSettings[settingsIdx + 1]);
}
}
if (this.playerStats.replayGain) {
pregainInfoElement.style.opacity = "1.0";
rgStatusElement.style.opacity = "0.0";
replayGainInfoTimeout = setTimeout(function () {
pregainInfoElement.style.opacity = "0.0";
rgStatusElement.style.opacity = "1.0";
}, 1000);
}
if (typeof(Storage) !== "undefined") {
localStorage.setItem("replayGain", this.playerStats.replayGain);
localStorage.setItem("replayGainPreGainDb", this.playerStats.replayGainPreGainDb);
}
},
fadeOverlay: function () {
if ($('#main-overlay').is(':visible')) {
$('#main-overlay').fadeOut("slow");

View File

@ -425,6 +425,7 @@
<p v-cloak class="metadata-panel-text">Artist: {{ (meta.artist) ? meta.artist : '' }}</p>
<p v-cloak class="metadata-panel-text">Album: {{ (meta.album) ? meta.album : '' }}</p>
<p v-cloak class="metadata-panel-text">Year: {{ (meta.year) ? meta.year : '' }}</p>
<p v-cloak class="metadata-panel-text">Gain: {{ (meta['replaygain-track-db']) ? String(meta['replaygain-track-db']) + " db" : 'n/a' }}</p>
</div>
</div>
@ -474,6 +475,10 @@
</span>
</div>
<div v-on:click="toggleReplayGain" class="next-button" title="ReplayGain">
<div id="rg-pregain-info">{{playerStats.replayGainPreGainDb}}db</div>
<svg id="rg-status" v-bind:class="{ 'aux-button-active': playerStats.replayGain }" class="center" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid" style="width:35px;webkit-logical-width:35px;webkit-logical-height:25px;user-select:none;transform-origin:12.5px 12.5px;r:0;perspective-origin:12.5px 12.5px;overflow-y:hidden;overflow-x:hidden;inline-size:35px;height:25px;d:none;block-size:25px;background:0% 0%/auto padding-box border-box" overflow="hidden" display="block" fill="#fff"><g transform="translate(-.938 19.85)" style="y:0;user-select:none;transform:matrix(1,0,0,1,-.9375,19.85);perspective-origin:0 0"><g class="ld" style="y:0;x:0;user-select:none;transform-origin:7.9375px 0;transform:none;r:0;perspective-origin:0 0;line-height:31.4286px;font:400 22px/31.4286px 'Varela Round','century gothic',verdana;d:none"><text style="y:0;user-select:none;r:0;perspective-origin:7.9375px 0;line-height:31.4286px;font:400 22px/31.4286px Arial" white-space="nowrap" display="block">R</text></g></g><g transform="translate(14.938 19.85)" style="y:0;user-select:none;transform:matrix(1,0,0,1,14.9375,19.85);perspective-origin:0 0"><g class="ld" style="y:0;x:0;user-select:none;transform-origin:5.5px 0;transform:none;r:0;perspective-origin:0 0;line-height:31.4286px;font:400 22px/31.4286px 'Varela Round','century gothic',verdana;d:none"><text style="y:0;user-select:none;r:0;perspective-origin:5.5px 0;line-height:31.4286px;font:400 22px/31.4286px Arial" white-space="nowrap" display="block">G</text></g></g></svg>
</div>
<div v-on:click="toggleVolume" class="player-button">
<img class="noselect fill-white center" :src="volumeSrc" title="Mute/Unmute">
</div>