From 196557a41abe7e63b84c6c9931b23e70bfd3abde Mon Sep 17 00:00:00 2001
From: Kendall Garner <17521368+kgarner7@users.noreply.github.com>
Date: Sat, 21 Sep 2024 20:46:14 +0000
Subject: [PATCH] fix(ui): show effective dB of track when playing (#3293)
* show effective db of track when playing
* tests
---
ui/src/audioplayer/AudioTitle.js | 6 ++--
ui/src/audioplayer/Player.js | 45 ++---------------------
ui/src/common/QualityInfo.js | 26 +++++++++-----
ui/src/common/QualityInfo.test.js | 55 ++++++++++++++++++++++++++++-
ui/src/utils/calculateReplayGain.js | 31 ++++++++++++++++
5 files changed, 110 insertions(+), 53 deletions(-)
create mode 100644 ui/src/utils/calculateReplayGain.js
diff --git a/ui/src/audioplayer/AudioTitle.js b/ui/src/audioplayer/AudioTitle.js
index 89d046db6..7f25e8f49 100644
--- a/ui/src/audioplayer/AudioTitle.js
+++ b/ui/src/audioplayer/AudioTitle.js
@@ -18,8 +18,10 @@ const AudioTitle = React.memo(({ audioInfo, gainInfo, isMobile }) => {
const qi = {
suffix: song.suffix,
bitRate: song.bitRate,
- albumGain: song.rgAlbumGain,
- trackGain: song.rgTrackGain,
+ rgAlbumGain: song.rgAlbumGain,
+ rgAlbumPeak: song.rgAlbumPeak,
+ rgTrackGain: song.rgTrackGain,
+ rgTrackPeak: song.rgTrackPeak,
}
return (
diff --git a/ui/src/audioplayer/Player.js b/ui/src/audioplayer/Player.js
index 174940aa2..6af8dd11f 100644
--- a/ui/src/audioplayer/Player.js
+++ b/ui/src/audioplayer/Player.js
@@ -23,16 +23,7 @@ import subsonic from '../subsonic'
import locale from './locale'
import { keyMap } from '../hotkeys'
import keyHandlers from './keyHandlers'
-
-function calculateReplayGain(preAmp, gain, peak) {
- if (gain === undefined || peak === undefined) {
- return 1
- }
-
- // https://wiki.hydrogenaud.io/index.php?title=ReplayGain_1.0_specification§ion=19
- // Normalized to max gain
- return Math.min(10 ** ((gain + preAmp) / 20), 1 / peak)
-}
+import { calculateGain } from '../utils/calculateReplayGain'
const Player = () => {
const theme = useCurrentTheme()
@@ -93,40 +84,10 @@ const Player = () => {
const current = playerState.current || {}
const song = current.song || {}
- let numericGain
-
- switch (gainInfo.gainMode) {
- case 'album': {
- numericGain = calculateReplayGain(
- gainInfo.preAmp,
- song.rgAlbumGain,
- song.rgAlbumPeak,
- )
- break
- }
- case 'track': {
- numericGain = calculateReplayGain(
- gainInfo.preAmp,
- song.rgTrackGain,
- song.rgTrackPeak,
- )
- break
- }
- default: {
- numericGain = 1
- }
- }
-
+ const numericGain = calculateGain(gainInfo, song)
gainNode.gain.setValueAtTime(numericGain, context.currentTime)
}
- }, [
- audioInstance,
- context,
- gainNode,
- gainInfo.gainMode,
- gainInfo.preAmp,
- playerState,
- ])
+ }, [audioInstance, context, gainNode, playerState, gainInfo])
const defaultOptions = useMemo(
() => ({
diff --git a/ui/src/common/QualityInfo.js b/ui/src/common/QualityInfo.js
index e663d3092..171f5e0f0 100644
--- a/ui/src/common/QualityInfo.js
+++ b/ui/src/common/QualityInfo.js
@@ -1,9 +1,10 @@
-import React from 'react'
+import React, { useMemo } from 'react'
import PropTypes from 'prop-types'
import Chip from '@material-ui/core/Chip'
import config from '../config'
import { makeStyles } from '@material-ui/core'
import clsx from 'clsx'
+import { calculateGain } from '../utils/calculateReplayGain'
const llFormats = new Set(config.losslessFormats.split(','))
const placeholder = 'N/A'
@@ -21,7 +22,8 @@ const useStyle = makeStyles(
export const QualityInfo = ({ record, size, gainMode, preAmp, className }) => {
const classes = useStyle()
- let { suffix, bitRate } = record
+ let { suffix, bitRate, rgAlbumGain, rgAlbumPeak, rgTrackGain, rgTrackPeak } =
+ record
let info = placeholder
if (suffix) {
@@ -32,18 +34,26 @@ export const QualityInfo = ({ record, size, gainMode, preAmp, className }) => {
}
}
- if (gainMode !== 'none') {
- info += ` (${
- (gainMode === 'album' ? record.albumGain : record.trackGain) + preAmp
- } dB)`
- }
+ const extra = useMemo(() => {
+ if (gainMode !== 'none') {
+ const gainValue = calculateGain(
+ { gainMode, preAmp },
+ { rgAlbumGain, rgAlbumPeak, rgTrackGain, rgTrackPeak },
+ )
+ // convert normalized gain (after peak) back to dB
+ const toDb = (Math.log10(gainValue) * 20).toFixed(2)
+ return ` (${toDb} dB)`
+ }
+
+ return ''
+ }, [gainMode, preAmp, rgAlbumGain, rgAlbumPeak, rgTrackGain, rgTrackPeak])
return (
)
}
diff --git a/ui/src/common/QualityInfo.test.js b/ui/src/common/QualityInfo.test.js
index 735fe70b9..ae1874715 100644
--- a/ui/src/common/QualityInfo.test.js
+++ b/ui/src/common/QualityInfo.test.js
@@ -11,7 +11,14 @@ describe('', () => {
expect(screen.getByText('FLAC')).toBeInTheDocument()
})
it('only render suffix and bitrate for lossy formats', () => {
- const info = { suffix: 'MP3', bitRate: 320 }
+ const info = {
+ suffix: 'MP3',
+ bitRate: 320,
+ rgAlbumGain: -5,
+ rgAlbumPeak: 1,
+ rgTrackGain: 2.3,
+ rgTrackPeak: 0.5,
+ }
render()
expect(screen.getByText('MP3 320')).toBeInTheDocument()
})
@@ -24,4 +31,50 @@ describe('', () => {
render()
expect(screen.getByText('N/A')).toBeInTheDocument()
})
+ it('renders album gain info, no peak limit', () => {
+ render(
+ ,
+ )
+ expect(screen.getByText('N/A (-5.00 dB)')).toBeInTheDocument()
+ })
+ it('renders track gain info, no peak limit capping, preAmp', () => {
+ render(
+ ,
+ )
+ expect(screen.getByText('N/A (1.30 dB)')).toBeInTheDocument()
+ })
+ it('renders gain info limited by peak', () => {
+ render(
+ ,
+ )
+ expect(screen.getByText('FLAC (0.00 dB)')).toBeInTheDocument()
+ })
})
diff --git a/ui/src/utils/calculateReplayGain.js b/ui/src/utils/calculateReplayGain.js
new file mode 100644
index 000000000..0ce69fa4b
--- /dev/null
+++ b/ui/src/utils/calculateReplayGain.js
@@ -0,0 +1,31 @@
+const calculateReplayGain = (preAmp, gain, peak) => {
+ if (gain === undefined || peak === undefined) {
+ return 1
+ }
+
+ // https://wiki.hydrogenaud.io/index.php?title=ReplayGain_1.0_specification§ion=19
+ // Normalized to max gain
+ return Math.min(10 ** ((gain + preAmp) / 20), 1 / peak)
+}
+
+export const calculateGain = (gainInfo, song) => {
+ switch (gainInfo.gainMode) {
+ case 'album': {
+ return calculateReplayGain(
+ gainInfo.preAmp,
+ song.rgAlbumGain,
+ song.rgAlbumPeak,
+ )
+ }
+ case 'track': {
+ return calculateReplayGain(
+ gainInfo.preAmp,
+ song.rgTrackGain,
+ song.rgTrackPeak,
+ )
+ }
+ default: {
+ return 1
+ }
+ }
+}