Merge pull request #4318 from TelepathicWalrus/audio-normalization
This commit is contained in:
commit
2a016a6d5f
8 changed files with 74 additions and 2 deletions
|
@ -59,6 +59,7 @@
|
||||||
- [Vankerkom](https://github.com/vankerkom)
|
- [Vankerkom](https://github.com/vankerkom)
|
||||||
- [edvwib](https://github.com/edvwib)
|
- [edvwib](https://github.com/edvwib)
|
||||||
- [Rob Farraher](https://github.com/farraherbg)
|
- [Rob Farraher](https://github.com/farraherbg)
|
||||||
|
- [TelepathicWalrus](https://github.com/TelepathicWalrus)
|
||||||
- [Pier-Luc Ducharme](https://github.com/pl-ducharme)
|
- [Pier-Luc Ducharme](https://github.com/pl-ducharme)
|
||||||
- [Anantharaju S](https://github.com/Anantharajus)
|
- [Anantharaju S](https://github.com/Anantharajus)
|
||||||
- [Merlin Sievers](https://github.com/dann-merlin)
|
- [Merlin Sievers](https://github.com/dann-merlin)
|
||||||
|
|
|
@ -416,6 +416,8 @@ export function setContentType(parent, contentType) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent.querySelector('.chkEnableLUFSScan').classList.toggle('hide', contentType !== 'music');
|
||||||
|
|
||||||
if (contentType === 'tvshows') {
|
if (contentType === 'tvshows') {
|
||||||
parent.querySelector('.chkEnableEmbeddedEpisodeInfosContainer').classList.remove('hide');
|
parent.querySelector('.chkEnableEmbeddedEpisodeInfosContainer').classList.remove('hide');
|
||||||
} else {
|
} else {
|
||||||
|
@ -512,6 +514,7 @@ export function getLibraryOptions(parent) {
|
||||||
EnableArchiveMediaFiles: false,
|
EnableArchiveMediaFiles: false,
|
||||||
EnablePhotos: parent.querySelector('.chkEnablePhotos').checked,
|
EnablePhotos: parent.querySelector('.chkEnablePhotos').checked,
|
||||||
EnableRealtimeMonitor: parent.querySelector('.chkEnableRealtimeMonitor').checked,
|
EnableRealtimeMonitor: parent.querySelector('.chkEnableRealtimeMonitor').checked,
|
||||||
|
EnableLUFSScan: parent.querySelector('.chkEnableLUFSScan').checked,
|
||||||
ExtractChapterImagesDuringLibraryScan: parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked,
|
ExtractChapterImagesDuringLibraryScan: parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked,
|
||||||
EnableChapterImageExtraction: parent.querySelector('.chkExtractChapterImages').checked,
|
EnableChapterImageExtraction: parent.querySelector('.chkExtractChapterImages').checked,
|
||||||
EnableInternetProviders: true,
|
EnableInternetProviders: true,
|
||||||
|
@ -573,6 +576,7 @@ export function setLibraryOptions(parent, options) {
|
||||||
parent.querySelector('#txtSeasonZeroName').value = options.SeasonZeroDisplayName || 'Specials';
|
parent.querySelector('#txtSeasonZeroName').value = options.SeasonZeroDisplayName || 'Specials';
|
||||||
parent.querySelector('.chkEnablePhotos').checked = options.EnablePhotos;
|
parent.querySelector('.chkEnablePhotos').checked = options.EnablePhotos;
|
||||||
parent.querySelector('.chkEnableRealtimeMonitor').checked = options.EnableRealtimeMonitor;
|
parent.querySelector('.chkEnableRealtimeMonitor').checked = options.EnableRealtimeMonitor;
|
||||||
|
parent.querySelector('.chkEnableLUFSScan').checked = options.EnableLUFSScan;
|
||||||
parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked = options.ExtractChapterImagesDuringLibraryScan;
|
parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked = options.ExtractChapterImagesDuringLibraryScan;
|
||||||
parent.querySelector('.chkExtractChapterImages').checked = options.EnableChapterImageExtraction;
|
parent.querySelector('.chkExtractChapterImages').checked = options.EnableChapterImageExtraction;
|
||||||
parent.querySelector('#chkSaveLocal').checked = options.SaveLocalMetadata;
|
parent.querySelector('#chkSaveLocal').checked = options.SaveLocalMetadata;
|
||||||
|
|
|
@ -55,6 +55,14 @@
|
||||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableRealtimeMonitorHelp}</div>
|
<div class="fieldDescription checkboxFieldDescription">${LabelEnableRealtimeMonitorHelp}</div>
|
||||||
</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">
|
<div class="checkboxContainer checkboxContainer-withDescription chkAutomaticallyAddToCollectionContainer hide advanced">
|
||||||
<label>
|
<label>
|
||||||
<input is="emby-checkbox" type="checkbox" id="chkAutomaticallyAddToCollection" />
|
<input is="emby-checkbox" type="checkbox" id="chkAutomaticallyAddToCollection" />
|
||||||
|
|
|
@ -173,6 +173,7 @@ function loadForm(context, user, userSettings, apiClient) {
|
||||||
context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false;
|
context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false;
|
||||||
context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
|
context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
|
||||||
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
|
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
|
||||||
|
context.querySelector('.chkEnableAudioNormalization').checked = userSettings.enableAudioNormalization();
|
||||||
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
|
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
|
||||||
context.querySelector('.chkRememberAudioSelections').checked = user.Configuration.RememberAudioSelections || false;
|
context.querySelector('.chkRememberAudioSelections').checked = user.Configuration.RememberAudioSelections || false;
|
||||||
context.querySelector('.chkRememberSubtitleSelections').checked = user.Configuration.RememberSubtitleSelections || 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;
|
user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked;
|
||||||
userSettingsInstance.preferFmp4HlsContainer(context.querySelector('.chkPreferFmp4HlsContainer').checked);
|
userSettingsInstance.preferFmp4HlsContainer(context.querySelector('.chkPreferFmp4HlsContainer').checked);
|
||||||
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
|
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
|
||||||
|
userSettingsInstance.enableAudioNormalization(context.querySelector('.chkEnableAudioNormalization').checked);
|
||||||
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
|
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
|
||||||
user.Configuration.RememberAudioSelections = context.querySelector('.chkRememberAudioSelections').checked;
|
user.Configuration.RememberAudioSelections = context.querySelector('.chkRememberAudioSelections').checked;
|
||||||
user.Configuration.RememberSubtitleSelections = context.querySelector('.chkRememberSubtitleSelections').checked;
|
user.Configuration.RememberSubtitleSelections = context.querySelector('.chkRememberSubtitleSelections').checked;
|
||||||
|
|
|
@ -72,6 +72,14 @@
|
||||||
${TabAdvanced}
|
${TabAdvanced}
|
||||||
</h2>
|
</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">
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" is="emby-checkbox" class="chkPreferFmp4HlsContainer" />
|
<input type="checkbox" is="emby-checkbox" class="chkPreferFmp4HlsContainer" />
|
||||||
|
@ -102,7 +110,7 @@
|
||||||
</label>
|
</label>
|
||||||
<div class="fieldDescription checkboxFieldDescription">${RememberAudioSelectionsHelp}</div>
|
<div class="fieldDescription checkboxFieldDescription">${RememberAudioSelectionsHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" is="emby-checkbox" class="chkRememberSubtitleSelections" />
|
<input type="checkbox" is="emby-checkbox" class="chkRememberSubtitleSelections" />
|
||||||
|
|
|
@ -101,6 +101,7 @@ class HtmlAudioPlayer {
|
||||||
self._currentTime = null;
|
self._currentTime = null;
|
||||||
|
|
||||||
const elem = createMediaElement();
|
const elem = createMediaElement();
|
||||||
|
|
||||||
return setCurrentSrc(elem, options);
|
return setCurrentSrc(elem, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,6 +111,17 @@ class HtmlAudioPlayer {
|
||||||
|
|
||||||
let val = options.url;
|
let val = options.url;
|
||||||
console.debug('playing url: ' + val);
|
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
|
// Convert to seconds
|
||||||
const seconds = (options.playerStartPositionTicks || 0) / 10000000;
|
const seconds = (options.playerStartPositionTicks || 0) / 10000000;
|
||||||
|
@ -245,9 +257,29 @@ class HtmlAudioPlayer {
|
||||||
|
|
||||||
self._mediaElement = elem;
|
self._mediaElement = elem;
|
||||||
|
|
||||||
|
addGainElement(elem);
|
||||||
|
|
||||||
return 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() {
|
function onEnded() {
|
||||||
htmlMediaHelper.onEndedInternal(self, this, onError);
|
htmlMediaHelper.onEndedInternal(self, this, onError);
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,6 +156,19 @@ export class UserSettings {
|
||||||
return toBoolean(this.get('enableCinemaMode', false), true);
|
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.
|
* Get or set 'Next Video Info Overlay' state.
|
||||||
* @param {boolean|undefined} val - Flag to enable 'Next Video Info Overlay' or undefined.
|
* @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 allowedAudioChannels = currentSettings.allowedAudioChannels.bind(currentSettings);
|
||||||
export const preferFmp4HlsContainer = currentSettings.preferFmp4HlsContainer.bind(currentSettings);
|
export const preferFmp4HlsContainer = currentSettings.preferFmp4HlsContainer.bind(currentSettings);
|
||||||
export const enableCinemaMode = currentSettings.enableCinemaMode.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 enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
|
||||||
export const enableVideoRemainingTime = currentSettings.enableVideoRemainingTime.bind(currentSettings);
|
export const enableVideoRemainingTime = currentSettings.enableVideoRemainingTime.bind(currentSettings);
|
||||||
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
|
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
|
||||||
|
|
|
@ -133,6 +133,7 @@
|
||||||
"ChannelNumber": "Channel number",
|
"ChannelNumber": "Channel number",
|
||||||
"Channels": "Channels",
|
"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.",
|
"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",
|
"ClearQueue": "Clear queue",
|
||||||
"ClientSettings": "Client Settings",
|
"ClientSettings": "Client Settings",
|
||||||
"Collections": "Collections",
|
"Collections": "Collections",
|
||||||
|
@ -221,6 +222,7 @@
|
||||||
"EnableBlurHash": "Enable blurred placeholders for images",
|
"EnableBlurHash": "Enable blurred placeholders for images",
|
||||||
"EnableBlurHashHelp": "Images that are still being loaded will be displayed with a unique placeholder.",
|
"EnableBlurHashHelp": "Images that are still being loaded will be displayed with a unique placeholder.",
|
||||||
"EnableCinemaMode": "Cinema mode",
|
"EnableCinemaMode": "Cinema mode",
|
||||||
|
"EnableAudioNormalization": "Audio Normalization",
|
||||||
"EnableColorCodedBackgrounds": "Color coded backgrounds",
|
"EnableColorCodedBackgrounds": "Color coded backgrounds",
|
||||||
"EnableDecodingColorDepth10Hevc": "Enable 10-bit hardware decoding for HEVC",
|
"EnableDecodingColorDepth10Hevc": "Enable 10-bit hardware decoding for HEVC",
|
||||||
"EnableDecodingColorDepth10Vp9": "Enable 10-bit hardware decoding for VP9",
|
"EnableDecodingColorDepth10Vp9": "Enable 10-bit hardware decoding for VP9",
|
||||||
|
@ -659,6 +661,8 @@
|
||||||
"LabelEnableIP4Help": "Enable IPv4 functionality.",
|
"LabelEnableIP4Help": "Enable IPv4 functionality.",
|
||||||
"LabelEnableIP6": "Enable IPv6",
|
"LabelEnableIP6": "Enable IPv6",
|
||||||
"LabelEnableIP6Help": "Enable IPv6 functionality.",
|
"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",
|
"LabelEnableRealtimeMonitor": "Enable real time monitoring",
|
||||||
"LabelEnableRealtimeMonitorHelp": "Changes to files will be processed immediately on supported file systems.",
|
"LabelEnableRealtimeMonitorHelp": "Changes to files will be processed immediately on supported file systems.",
|
||||||
"LabelEnableSingleImageInDidlLimit": "Limit to single embedded image",
|
"LabelEnableSingleImageInDidlLimit": "Limit to single embedded image",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue