mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-02 11:05:38 +02:00
feat(replaygain): add Opus R128 gain tags
Write R128_TRACK_GAIN/R128_ALBUM_GAIN (Q7.8, ref -23 LUFS) alongside the existing REPLAYGAIN_* tags for .opus files so spec-compliant Opus players (RFC 7845) apply gain. Covers per-track and album embed during download (incl. SAF) and standalone re-scan.
This commit is contained in:
@@ -4860,6 +4860,10 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
scannedReplayGain = rgResult;
|
||||
metadata['REPLAYGAIN_TRACK_GAIN'] = rgResult.trackGain;
|
||||
metadata['REPLAYGAIN_TRACK_PEAK'] = rgResult.trackPeak;
|
||||
if (format == 'opus') {
|
||||
final r128 = FFmpegService.replayGainDbToR128(rgResult.trackGain);
|
||||
if (r128 != null) metadata['R128_TRACK_GAIN'] = r128;
|
||||
}
|
||||
_log.d(
|
||||
'ReplayGain for $format: gain=${rgResult.trackGain}, peak=${rgResult.trackPeak}',
|
||||
);
|
||||
|
||||
@@ -1240,8 +1240,22 @@ class FFmpegService {
|
||||
);
|
||||
}
|
||||
|
||||
/// Write album ReplayGain tags to a non-FLAC file (MP3/Opus) using FFmpeg.
|
||||
/// Preserves all existing metadata and adds/overwrites album gain fields.
|
||||
/// Convert a ReplayGain gain value (dB, referenced to -18 LUFS) into an Opus
|
||||
/// R128 gain tag value (Q7.8 fixed point integer, referenced to -23 LUFS).
|
||||
///
|
||||
/// Opus players read `R128_TRACK_GAIN` / `R128_ALBUM_GAIN` per RFC 7845, not
|
||||
/// the `REPLAYGAIN_*` dB strings. The reference levels differ by exactly 5 dB
|
||||
/// (-18 vs -23 LUFS), so the R128 gain equals the ReplayGain value minus 5 dB,
|
||||
/// stored as `round(dB * 256)`.
|
||||
static String? replayGainDbToR128(String replayGainDb) {
|
||||
final match = RegExp(r'-?\d+\.?\d*').firstMatch(replayGainDb);
|
||||
if (match == null) return null;
|
||||
final rgDb = double.tryParse(match.group(0) ?? '');
|
||||
if (rgDb == null) return null;
|
||||
final r128Db = rgDb - 5.0;
|
||||
return (r128Db * 256).round().toString();
|
||||
}
|
||||
|
||||
/// Write album ReplayGain tags to a file via FFmpeg.
|
||||
///
|
||||
/// For local files, replaces the file in-place and returns `true`.
|
||||
@@ -1277,10 +1291,21 @@ class FFmpegService {
|
||||
'REPLAYGAIN_ALBUM_GAIN=$albumGain',
|
||||
'-metadata',
|
||||
'REPLAYGAIN_ALBUM_PEAK=$albumPeak',
|
||||
tempOutput,
|
||||
'-y',
|
||||
];
|
||||
|
||||
if (ext.toLowerCase() == '.opus') {
|
||||
final r128 = replayGainDbToR128(albumGain);
|
||||
if (r128 != null) {
|
||||
arguments
|
||||
..add('-metadata')
|
||||
..add('R128_ALBUM_GAIN=$r128');
|
||||
}
|
||||
}
|
||||
|
||||
arguments
|
||||
..add(tempOutput)
|
||||
..add('-y');
|
||||
|
||||
_log.d('Writing album ReplayGain tags via FFmpeg');
|
||||
final result = await _executeWithArguments(arguments);
|
||||
|
||||
@@ -1347,10 +1372,21 @@ class FFmpegService {
|
||||
'REPLAYGAIN_TRACK_GAIN=$trackGain',
|
||||
'-metadata',
|
||||
'REPLAYGAIN_TRACK_PEAK=$trackPeak',
|
||||
tempOutput,
|
||||
'-y',
|
||||
];
|
||||
|
||||
if (ext.toLowerCase() == '.opus') {
|
||||
final r128 = replayGainDbToR128(trackGain);
|
||||
if (r128 != null) {
|
||||
arguments
|
||||
..add('-metadata')
|
||||
..add('R128_TRACK_GAIN=$r128');
|
||||
}
|
||||
}
|
||||
|
||||
arguments
|
||||
..add(tempOutput)
|
||||
..add('-y');
|
||||
|
||||
_log.d('Writing track ReplayGain tags via FFmpeg');
|
||||
final result = await _executeWithArguments(arguments);
|
||||
|
||||
@@ -2725,6 +2761,12 @@ class FFmpegService {
|
||||
case 'REPLAYGAINALBUMPEAK':
|
||||
vorbis['REPLAYGAIN_ALBUM_PEAK'] = value;
|
||||
break;
|
||||
case 'R128TRACKGAIN':
|
||||
vorbis['R128_TRACK_GAIN'] = value;
|
||||
break;
|
||||
case 'R128ALBUMGAIN':
|
||||
vorbis['R128_ALBUM_GAIN'] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user