diff --git a/src/components/QualityDetailsBtn.vue b/src/components/QualityDetailsBtn.vue
index e0cff21f..a777e9bd 100644
--- a/src/components/QualityDetailsBtn.vue
+++ b/src/components/QualityDetailsBtn.vue
@@ -59,7 +59,7 @@
- {{ streamDetails.target_loudness }} dB
+ {{ loudness }}
@@ -85,12 +85,44 @@ import { computed } from 'vue';
import ProviderIcon from '@/components/ProviderIcon.vue';
import api from '@/plugins/api';
import { store } from '@/plugins/store';
-import { ContentType } from '@/plugins/api/interfaces';
+import { ContentType, VolumeNormalizationMode } from '@/plugins/api/interfaces';
+import { $t } from '@/plugins/i18n';
// computed properties
const streamDetails = computed(() => {
return store.activePlayerQueue?.current_item?.streamdetails;
});
+const loudness = computed(() => {
+ let sd = streamDetails.value;
+ if (!sd) return null;
+
+ if (
+ (sd.volume_normalization_mode == VolumeNormalizationMode.MEASUREMENT_ONLY ||
+ sd.volume_normalization_mode ==
+ VolumeNormalizationMode.FALLBACK_FIXED_GAIN ||
+ sd.volume_normalization_mode ==
+ VolumeNormalizationMode.FALLBACK_DYNAMIC) &&
+ sd.loudness !== null
+ ) {
+ if (sd.prefer_album_loudness && sd.loudness_album !== null) {
+ return $t('loudness_measurement_album', [sd.loudness_album?.toFixed(2)]);
+ } else {
+ return $t('loudness_measurement', [sd.loudness?.toFixed(2)]);
+ }
+ } else if (
+ sd.volume_normalization_mode == VolumeNormalizationMode.DYNAMIC ||
+ sd.volume_normalization_mode == VolumeNormalizationMode.FALLBACK_DYNAMIC
+ ) {
+ return $t('loudness_dynamic');
+ } else if (
+ sd.volume_normalization_mode == VolumeNormalizationMode.FIXED_GAIN ||
+ sd.volume_normalization_mode == VolumeNormalizationMode.FALLBACK_FIXED_GAIN
+ ) {
+ return $t('loudness_fixed');
+ } else {
+ return null;
+ }
+});
const getContentTypeIcon = function (contentType: ContentType) {
if (contentType == ContentType.AAC) return iconAac;
if (contentType == ContentType.FLAC) return iconFlac;
diff --git a/src/plugins/api/interfaces.ts b/src/plugins/api/interfaces.ts
index f79e5e99..955be6fd 100644
--- a/src/plugins/api/interfaces.ts
+++ b/src/plugins/api/interfaces.ts
@@ -190,6 +190,15 @@ export enum ConfigEntryType {
ALERT = 'alert',
}
+export enum VolumeNormalizationMode {
+ DISABLED = 'disabled',
+ DYNAMIC = 'dynamic', // Force dynamic
+ MEASUREMENT_ONLY = 'measurement_only', // Measurement only (no fallback)
+ FALLBACK_FIXED_GAIN = 'fallback_fixed_gain', // Fallback to gain correction
+ FIXED_GAIN = 'fixed_gain', // Fixed gain correction
+ FALLBACK_DYNAMIC = 'fallback_dynamic', // Fallback to dynamic
+}
+
//// api
export interface CommandMessage {
@@ -479,14 +488,6 @@ export interface AudioFormat {
bit_rate: number;
}
-export interface LoudnessMeasurement {
- integrated: number;
- true_peak: number;
- lra: number;
- threshold: number;
- target_offset: number;
-}
-
export interface StreamDetails {
provider: string;
item_id: string;
@@ -494,10 +495,13 @@ export interface StreamDetails {
media_type: MediaType;
stream_title?: string;
duration?: number;
+ loudness?: number;
+ loudness_album?: number;
+ prefer_album_loudness?: boolean;
+ volume_normalization_mode?: VolumeNormalizationMode;
+ target_loudness?: number;
queue_id?: string;
- loudness?: LoudnessMeasurement;
- target_loudness?: number;
}
// queue_item
diff --git a/src/translations/en.json b/src/translations/en.json
index b694e08a..d667f31e 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -525,5 +525,9 @@
"image_make_primary": "Make primary",
"update_metadata": "Update metadata",
"cancel": "Cancel",
- "transfer_queue": "Transfer queue to another player"
+ "transfer_queue": "Transfer queue to another player",
+ "loudness_measurement": "{0} LUFS",
+ "loudness_measurement_album": "{0} LUFS (album)",
+ "loudness_dynamic": "Dynamic volume normalization",
+ "loudness_fixed": "Fixed gain correction"
}