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 + } + } +}