Adds screen audio/narration voices to the screens (Can be turned off in settings)
Some checks are pending
build-docker / Build Image (push) Waiting to run
Some checks are pending
build-docker / Build Image (push) Waiting to run
This commit is contained in:
parent
e4a14fdedd
commit
0afd3f14a0
10 changed files with 131 additions and 0 deletions
BIN
server/alert/current-conditions.mp3
Normal file
BIN
server/alert/current-conditions.mp3
Normal file
Binary file not shown.
BIN
server/alert/hourly-forecast.mp3
Normal file
BIN
server/alert/hourly-forecast.mp3
Normal file
Binary file not shown.
BIN
server/alert/hourly-graph.mp3
Normal file
BIN
server/alert/hourly-graph.mp3
Normal file
Binary file not shown.
BIN
server/alert/local-radar.mp3
Normal file
BIN
server/alert/local-radar.mp3
Normal file
Binary file not shown.
BIN
server/alert/regional-observations.mp3
Normal file
BIN
server/alert/regional-observations.mp3
Normal file
Binary file not shown.
BIN
server/alert/travel-forecast.mp3
Normal file
BIN
server/alert/travel-forecast.mp3
Normal file
Binary file not shown.
|
|
@ -59,6 +59,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
if (mediaVolume.value !== clampMediaVolume(mediaVolume.value)) {
|
if (mediaVolume.value !== clampMediaVolume(mediaVolume.value)) {
|
||||||
mediaVolume.value = clampMediaVolume(mediaVolume.value);
|
mediaVolume.value = clampMediaVolume(mediaVolume.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Screen audio setting is managed via localStorage and checked directly in playScreenAudio()
|
||||||
});
|
});
|
||||||
|
|
||||||
const unlockAudio = () => {
|
const unlockAudio = () => {
|
||||||
|
|
@ -342,6 +344,7 @@ const startAlertTone = async () => {
|
||||||
alertTonePending = true;
|
alertTonePending = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
stopScreenAudio(); // Stop screen audio when alert plays
|
||||||
initializeAlertTonePlayer();
|
initializeAlertTonePlayer();
|
||||||
try {
|
try {
|
||||||
alertTonePending = true;
|
alertTonePending = true;
|
||||||
|
|
@ -370,6 +373,82 @@ const stopAlertTone = () => {
|
||||||
|
|
||||||
const playAlertTone = () => startAlertTone();
|
const playAlertTone = () => startAlertTone();
|
||||||
|
|
||||||
|
// Screen audio constants and variables
|
||||||
|
const SCREEN_AUDIO_DUCK_VOLUME = 0.10; // 10% ducking (vs 5% for alerts)
|
||||||
|
const screenAudioMap = {
|
||||||
|
'radar': 'local-radar.mp3',
|
||||||
|
'regional-forecast': 'regional-observations.mp3',
|
||||||
|
'travel': 'travel-forecast.mp3',
|
||||||
|
'hourly-graph': 'hourly-graph.mp3',
|
||||||
|
'hourly': 'hourly-forecast.mp3',
|
||||||
|
'current-weather': 'current-conditions.mp3',
|
||||||
|
};
|
||||||
|
|
||||||
|
let screenAudioPlayer = null;
|
||||||
|
|
||||||
|
// Helper function to check if screen audio is enabled (always reads from localStorage)
|
||||||
|
const isScreenAudioEnabled = () => {
|
||||||
|
const saved = window.localStorage.getItem('screenAudioEnabled');
|
||||||
|
return saved !== null ? saved === 'true' : true; // Default: enabled
|
||||||
|
};
|
||||||
|
|
||||||
|
// Play screen audio
|
||||||
|
const playScreenAudio = async (screenId) => {
|
||||||
|
// Always check localStorage to ensure setting is current
|
||||||
|
if (!isScreenAudioEnabled()) return;
|
||||||
|
|
||||||
|
const fileName = screenAudioMap[screenId];
|
||||||
|
if (!fileName) return;
|
||||||
|
|
||||||
|
// Stop any existing screen audio first
|
||||||
|
stopScreenAudio();
|
||||||
|
|
||||||
|
// Don't play if alert tone is active
|
||||||
|
if (alertToneActive || alertTonePending) return;
|
||||||
|
|
||||||
|
// Duck background music to 10%
|
||||||
|
if (player && !player.paused) {
|
||||||
|
player.volume = SCREEN_AUDIO_DUCK_VOLUME;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and play audio
|
||||||
|
screenAudioPlayer = new Audio(withBasePath(`alert/${fileName}`));
|
||||||
|
screenAudioPlayer.type = 'audio/mpeg';
|
||||||
|
|
||||||
|
screenAudioPlayer.addEventListener('ended', () => {
|
||||||
|
screenAudioPlayer = null;
|
||||||
|
// Only restore if alert isn't playing
|
||||||
|
if (!alertToneActive && !alertTonePending) {
|
||||||
|
restoreMediaAfterAlert();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
screenAudioPlayer.addEventListener('error', () => {
|
||||||
|
screenAudioPlayer = null;
|
||||||
|
if (!alertToneActive && !alertTonePending) {
|
||||||
|
restoreMediaAfterAlert();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await screenAudioPlayer.play();
|
||||||
|
} catch (e) {
|
||||||
|
screenAudioPlayer = null;
|
||||||
|
if (!alertToneActive && !alertTonePending) {
|
||||||
|
restoreMediaAfterAlert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stop screen audio immediately
|
||||||
|
const stopScreenAudio = () => {
|
||||||
|
if (screenAudioPlayer) {
|
||||||
|
screenAudioPlayer.pause();
|
||||||
|
screenAudioPlayer.currentTime = 0;
|
||||||
|
screenAudioPlayer = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const playerCanPlay = async () => {
|
const playerCanPlay = async () => {
|
||||||
// check to make sure they user still wants music (protect against slow loading music)
|
// check to make sure they user still wants music (protect against slow loading music)
|
||||||
if (!mediaPlaying.value) return;
|
if (!mediaPlaying.value) return;
|
||||||
|
|
@ -402,4 +481,6 @@ export {
|
||||||
handleClick,
|
handleClick,
|
||||||
playAlertTone,
|
playAlertTone,
|
||||||
stopAlertTone,
|
stopAlertTone,
|
||||||
|
playScreenAudio,
|
||||||
|
stopScreenAudio,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -385,10 +385,18 @@ const handleNavButton = (button) => {
|
||||||
break;
|
break;
|
||||||
case 'next':
|
case 'next':
|
||||||
setPlaying(false);
|
setPlaying(false);
|
||||||
|
// Stop screen audio immediately when navigating
|
||||||
|
import('./media.mjs').then((media) => {
|
||||||
|
media.stopScreenAudio();
|
||||||
|
});
|
||||||
navTo(msg.command.nextFrame);
|
navTo(msg.command.nextFrame);
|
||||||
break;
|
break;
|
||||||
case 'previous':
|
case 'previous':
|
||||||
setPlaying(false);
|
setPlaying(false);
|
||||||
|
// Stop screen audio immediately when navigating
|
||||||
|
import('./media.mjs').then((media) => {
|
||||||
|
media.stopScreenAudio();
|
||||||
|
});
|
||||||
navTo(msg.command.previousFrame);
|
navTo(msg.command.previousFrame);
|
||||||
break;
|
break;
|
||||||
case 'menu':
|
case 'menu':
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,32 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
settingsSection.innerHTML = '';
|
settingsSection.innerHTML = '';
|
||||||
settingsSection.append(...settingHtml);
|
settingsSection.append(...settingHtml);
|
||||||
|
|
||||||
|
// Add screen audio toggle checkbox
|
||||||
|
const screenAudioContainer = document.createElement('div');
|
||||||
|
screenAudioContainer.className = 'info';
|
||||||
|
const screenAudioLabel = document.createElement('label');
|
||||||
|
screenAudioLabel.htmlFor = 'screen-audio-checkbox';
|
||||||
|
const screenAudioCheckbox = document.createElement('input');
|
||||||
|
screenAudioCheckbox.type = 'checkbox';
|
||||||
|
screenAudioCheckbox.id = 'screen-audio-checkbox';
|
||||||
|
// Load saved preference or default to enabled
|
||||||
|
const savedScreenAudio = window.localStorage.getItem('screenAudioEnabled');
|
||||||
|
screenAudioCheckbox.checked = savedScreenAudio !== null ? savedScreenAudio === 'true' : true;
|
||||||
|
screenAudioCheckbox.addEventListener('change', () => {
|
||||||
|
window.localStorage.setItem('screenAudioEnabled', screenAudioCheckbox.checked);
|
||||||
|
// Import media module and update setting
|
||||||
|
import('./media.mjs').then((media) => {
|
||||||
|
// Media module will read the localStorage value
|
||||||
|
if (!screenAudioCheckbox.checked) {
|
||||||
|
media.stopScreenAudio();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
screenAudioLabel.appendChild(screenAudioCheckbox);
|
||||||
|
screenAudioLabel.appendChild(document.createTextNode(' Play screen audio'));
|
||||||
|
screenAudioContainer.appendChild(screenAudioLabel);
|
||||||
|
settingsSection.appendChild(screenAudioContainer);
|
||||||
|
|
||||||
// update visibility on some settings
|
// update visibility on some settings
|
||||||
const modeSelect = document.getElementById('settings-scanLineMode-label');
|
const modeSelect = document.getElementById('settings-scanLineMode-label');
|
||||||
const { value } = settings.scanLines;
|
const { value } = settings.scanLines;
|
||||||
|
|
|
||||||
|
|
@ -219,8 +219,19 @@ class WeatherDisplay {
|
||||||
|
|
||||||
this.startNavCount();
|
this.startNavCount();
|
||||||
|
|
||||||
|
// Check if display was already active before showing
|
||||||
|
const wasActive = this.active;
|
||||||
|
|
||||||
this.elem.classList.add('show');
|
this.elem.classList.add('show');
|
||||||
document.querySelector('#divTwc').classList.add(this.elemId);
|
document.querySelector('#divTwc').classList.add(this.elemId);
|
||||||
|
|
||||||
|
// Play screen-specific audio only if display was not already active
|
||||||
|
// This prevents audio restart on frame changes (e.g., Local Radar animation)
|
||||||
|
if (!wasActive) {
|
||||||
|
import('./media.mjs').then((media) => {
|
||||||
|
media.playScreenAudio(this.elemId);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hideCanvas() {
|
hideCanvas() {
|
||||||
|
|
@ -228,6 +239,11 @@ class WeatherDisplay {
|
||||||
this.elem.classList.remove('show');
|
this.elem.classList.remove('show');
|
||||||
// used to change backgrounds for widescreen
|
// used to change backgrounds for widescreen
|
||||||
document.querySelector('#divTwc').classList.remove(this.elemId);
|
document.querySelector('#divTwc').classList.remove(this.elemId);
|
||||||
|
|
||||||
|
// Stop screen audio when leaving
|
||||||
|
import('./media.mjs').then((media) => {
|
||||||
|
media.stopScreenAudio();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get active() {
|
get active() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue