fix(ui): show effective dB of track when playing (#3293)

* show effective db of track when playing

* tests
This commit is contained in:
Kendall Garner 2024-09-21 20:46:14 +00:00 committed by GitHub
parent 11d96f1da4
commit 196557a41a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 110 additions and 53 deletions

View file

@ -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 (

View file

@ -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&section=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(
() => ({

View file

@ -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 (
<Chip
className={clsx(classes.chip, className)}
variant="outlined"
size={size}
label={info}
label={`${info}${extra}`}
/>
)
}

View file

@ -11,7 +11,14 @@ describe('<QualityInfo />', () => {
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(<QualityInfo record={info} />)
expect(screen.getByText('MP3 320')).toBeInTheDocument()
})
@ -24,4 +31,50 @@ describe('<QualityInfo />', () => {
render(<QualityInfo />)
expect(screen.getByText('N/A')).toBeInTheDocument()
})
it('renders album gain info, no peak limit', () => {
render(
<QualityInfo
gainMode="album"
preAmp={0}
record={{
rgAlbumGain: -5,
rgAlbumPeak: 1,
rgTrackGain: -2,
rgTrackPeak: 0.2,
}}
/>,
)
expect(screen.getByText('N/A (-5.00 dB)')).toBeInTheDocument()
})
it('renders track gain info, no peak limit capping, preAmp', () => {
render(
<QualityInfo
gainMode="track"
preAmp={-1}
record={{
rgAlbumGain: -5,
rgAlbumPeak: 1,
rgTrackGain: 2.3,
rgTrackPeak: 0.5,
}}
/>,
)
expect(screen.getByText('N/A (1.30 dB)')).toBeInTheDocument()
})
it('renders gain info limited by peak', () => {
render(
<QualityInfo
gainMode="track"
preAmp={-1}
record={{
suffix: 'FLAC',
rgAlbumGain: -5,
rgAlbumPeak: 1,
rgTrackGain: 2.3,
rgTrackPeak: 1,
}}
/>,
)
expect(screen.getByText('FLAC (0.00 dB)')).toBeInTheDocument()
})
})

View file

@ -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&section=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
}
}
}