1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge pull request #4318 from TelepathicWalrus/audio-normalization

This commit is contained in:
Bill Thornton 2023-05-25 08:30:19 -04:00 committed by GitHub
commit 2a016a6d5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 74 additions and 2 deletions

View file

@ -59,6 +59,7 @@
- [Vankerkom](https://github.com/vankerkom)
- [edvwib](https://github.com/edvwib)
- [Rob Farraher](https://github.com/farraherbg)
- [TelepathicWalrus](https://github.com/TelepathicWalrus)
- [Pier-Luc Ducharme](https://github.com/pl-ducharme)
- [Anantharaju S](https://github.com/Anantharajus)
- [Merlin Sievers](https://github.com/dann-merlin)

View file

@ -416,6 +416,8 @@ export function setContentType(parent, contentType) {
}
}
parent.querySelector('.chkEnableLUFSScan').classList.toggle('hide', contentType !== 'music');
if (contentType === 'tvshows') {
parent.querySelector('.chkEnableEmbeddedEpisodeInfosContainer').classList.remove('hide');
} else {
@ -512,6 +514,7 @@ export function getLibraryOptions(parent) {
EnableArchiveMediaFiles: false,
EnablePhotos: parent.querySelector('.chkEnablePhotos').checked,
EnableRealtimeMonitor: parent.querySelector('.chkEnableRealtimeMonitor').checked,
EnableLUFSScan: parent.querySelector('.chkEnableLUFSScan').checked,
ExtractChapterImagesDuringLibraryScan: parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked,
EnableChapterImageExtraction: parent.querySelector('.chkExtractChapterImages').checked,
EnableInternetProviders: true,
@ -573,6 +576,7 @@ export function setLibraryOptions(parent, options) {
parent.querySelector('#txtSeasonZeroName').value = options.SeasonZeroDisplayName || 'Specials';
parent.querySelector('.chkEnablePhotos').checked = options.EnablePhotos;
parent.querySelector('.chkEnableRealtimeMonitor').checked = options.EnableRealtimeMonitor;
parent.querySelector('.chkEnableLUFSScan').checked = options.EnableLUFSScan;
parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked = options.ExtractChapterImagesDuringLibraryScan;
parent.querySelector('.chkExtractChapterImages').checked = options.EnableChapterImageExtraction;
parent.querySelector('#chkSaveLocal').checked = options.SaveLocalMetadata;

View file

@ -55,6 +55,14 @@
<div class="fieldDescription checkboxFieldDescription">${LabelEnableRealtimeMonitorHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription advanced">
<label>
<input type="checkbox" is="emby-checkbox" class="chkEnableLUFSScan" checked />
<span>${LabelEnableLUFSScan}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${LabelEnableLUFSScanHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription chkAutomaticallyAddToCollectionContainer hide advanced">
<label>
<input is="emby-checkbox" type="checkbox" id="chkAutomaticallyAddToCollection" />

View file

@ -173,6 +173,7 @@ function loadForm(context, user, userSettings, apiClient) {
context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false;
context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
context.querySelector('.chkEnableAudioNormalization').checked = userSettings.enableAudioNormalization();
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
context.querySelector('.chkRememberAudioSelections').checked = user.Configuration.RememberAudioSelections || false;
context.querySelector('.chkRememberSubtitleSelections').checked = user.Configuration.RememberSubtitleSelections || false;
@ -217,7 +218,7 @@ function saveUser(context, user, userSettingsInstance, apiClient) {
user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked;
userSettingsInstance.preferFmp4HlsContainer(context.querySelector('.chkPreferFmp4HlsContainer').checked);
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
userSettingsInstance.enableAudioNormalization(context.querySelector('.chkEnableAudioNormalization').checked);
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
user.Configuration.RememberAudioSelections = context.querySelector('.chkRememberAudioSelections').checked;
user.Configuration.RememberSubtitleSelections = context.querySelector('.chkRememberSubtitleSelections').checked;

View file

@ -72,6 +72,14 @@
${TabAdvanced}
</h2>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" class="chkEnableAudioNormalization" />
<span>${EnableAudioNormalization}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${EnableAudioNormalizationHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label>
<input type="checkbox" is="emby-checkbox" class="chkPreferFmp4HlsContainer" />

View file

@ -101,6 +101,7 @@ class HtmlAudioPlayer {
self._currentTime = null;
const elem = createMediaElement();
return setCurrentSrc(elem, options);
};
@ -110,6 +111,17 @@ class HtmlAudioPlayer {
let val = options.url;
console.debug('playing url: ' + val);
import('../../scripts/settings/userSettings').then((userSettings) => {
if (userSettings.enableAudioNormalization() && options.item.LUFS != null) {
const dbGain = -18 - options.item.LUFS;
self.gainNode.gain.value = Math.pow(10, (dbGain / 20));
} else {
self.gainNode.gain.value = 1;
}
console.debug('gain:' + self.gainNode.gain.value);
}).catch((err) => {
console.error('Failed to add/change gainNode', err);
});
// Convert to seconds
const seconds = (options.playerStartPositionTicks || 0) / 10000000;
@ -245,9 +257,29 @@ class HtmlAudioPlayer {
self._mediaElement = elem;
addGainElement(elem);
return elem;
}
function addGainElement(elem) {
try {
const AudioContext = window.AudioContext || window.webkitAudioContext; /* eslint-disable-line compat/compat */
const audioCtx = new AudioContext();
const source = audioCtx.createMediaElementSource(elem);
const gainNode = audioCtx.createGain();
source.connect(gainNode);
gainNode.connect(audioCtx.destination);
self.gainNode = gainNode;
} catch (e) {
console.error('Web Audio API is not supported in this browser', e);
}
}
function onEnded() {
htmlMediaHelper.onEndedInternal(self, this, onError);
}

View file

@ -156,6 +156,19 @@ export class UserSettings {
return toBoolean(this.get('enableCinemaMode', false), true);
}
/**
* Get or set 'Enable Audio Normalization' state.
* @param {boolean|undefined} val - Flag to enable 'Enable Audio Normalization' or undefined.
* @return {boolean} 'Enable Audio Normalization' state.
*/
enableAudioNormalization(val) {
if (val !== undefined) {
return this.set('enableAudioNormalization', val.toString(), false);
}
return toBoolean(this.get('enableAudioNormalization', false), true);
}
/**
* Get or set 'Next Video Info Overlay' state.
* @param {boolean|undefined} val - Flag to enable 'Next Video Info Overlay' or undefined.
@ -592,6 +605,7 @@ export const serverConfig = currentSettings.serverConfig.bind(currentSettings);
export const allowedAudioChannels = currentSettings.allowedAudioChannels.bind(currentSettings);
export const preferFmp4HlsContainer = currentSettings.preferFmp4HlsContainer.bind(currentSettings);
export const enableCinemaMode = currentSettings.enableCinemaMode.bind(currentSettings);
export const enableAudioNormalization = currentSettings.enableAudioNormalization.bind(currentSettings);
export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
export const enableVideoRemainingTime = currentSettings.enableVideoRemainingTime.bind(currentSettings);
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);

View file

@ -133,6 +133,7 @@
"ChannelNumber": "Channel number",
"Channels": "Channels",
"CinemaModeConfigurationHelp": "Cinema mode brings the theater experience straight to your living room with the ability to play trailers and custom intros before the main feature.",
"EnableAudioNormalizationHelp": "Audio normalization will add a constant gain to keep the average at a desired level (-18dB).",
"ClearQueue": "Clear queue",
"ClientSettings": "Client Settings",
"Collections": "Collections",
@ -221,6 +222,7 @@
"EnableBlurHash": "Enable blurred placeholders for images",
"EnableBlurHashHelp": "Images that are still being loaded will be displayed with a unique placeholder.",
"EnableCinemaMode": "Cinema mode",
"EnableAudioNormalization": "Audio Normalization",
"EnableColorCodedBackgrounds": "Color coded backgrounds",
"EnableDecodingColorDepth10Hevc": "Enable 10-bit hardware decoding for HEVC",
"EnableDecodingColorDepth10Vp9": "Enable 10-bit hardware decoding for VP9",
@ -659,6 +661,8 @@
"LabelEnableIP4Help": "Enable IPv4 functionality.",
"LabelEnableIP6": "Enable IPv6",
"LabelEnableIP6Help": "Enable IPv6 functionality.",
"LabelEnableLUFSScan": "Enable LUFS scan",
"LabelEnableLUFSScanHelp": "Enable LUFS scan for music (This will take longer and more resources).",
"LabelEnableRealtimeMonitor": "Enable real time monitoring",
"LabelEnableRealtimeMonitorHelp": "Changes to files will be processed immediately on supported file systems.",
"LabelEnableSingleImageInDidlLimit": "Limit to single embedded image",