mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-01 19:47:37 +03:00
fix(ui): show effective dB of track when playing (#3293)
* show effective db of track when playing * tests
This commit is contained in:
parent
11d96f1da4
commit
196557a41a
5 changed files with 110 additions and 53 deletions
|
@ -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 (
|
||||
|
|
|
@ -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(
|
||||
() => ({
|
||||
|
|
|
@ -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}`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
|
|
31
ui/src/utils/calculateReplayGain.js
Normal file
31
ui/src/utils/calculateReplayGain.js
Normal 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§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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue