FIX: try to fix application volume control on linux
This commit is contained in:
parent
bb93edefde
commit
02cf092904
4 changed files with 46 additions and 19 deletions
|
|
@ -81,12 +81,14 @@ class AudioContextManager {
|
|||
this.analyser = null;
|
||||
this.filters = [];
|
||||
this.outputNode = null;
|
||||
this.volumeNode = null;
|
||||
this.isInitialized = false;
|
||||
this.isEQEnabled = false;
|
||||
this.isMonoAudioEnabled = false;
|
||||
this.monoMergerNode = null;
|
||||
this.currentGains = new Array(16).fill(0);
|
||||
this.audio = null;
|
||||
this.currentVolume = 1.0;
|
||||
|
||||
// Callbacks for audio graph changes (for visualizers like Butterchurn)
|
||||
this._graphChangeCallbacks = [];
|
||||
|
|
@ -170,6 +172,10 @@ class AudioContextManager {
|
|||
this.outputNode = this.audioContext.createGain();
|
||||
this.outputNode.gain.value = 1;
|
||||
|
||||
// Create volume node
|
||||
this.volumeNode = this.audioContext.createGain();
|
||||
this.volumeNode.gain.value = this.currentVolume;
|
||||
|
||||
// Create mono audio merger node
|
||||
this.monoMergerNode = this.audioContext.createChannelMerger(2);
|
||||
|
||||
|
|
@ -199,6 +205,11 @@ class AudioContextManager {
|
|||
// Disconnect everything first
|
||||
this.source.disconnect();
|
||||
this.outputNode.disconnect();
|
||||
if (this.volumeNode) {
|
||||
this.volumeNode.disconnect();
|
||||
}
|
||||
this.analyser.disconnect();
|
||||
|
||||
if (this.monoMergerNode) {
|
||||
try {
|
||||
this.monoMergerNode.disconnect();
|
||||
|
|
@ -207,13 +218,6 @@ class AudioContextManager {
|
|||
}
|
||||
}
|
||||
|
||||
// Only disconnect destination from analyser to preserve other taps (like Butterchurn)
|
||||
try {
|
||||
this.analyser.disconnect(this.audioContext.destination);
|
||||
} catch {
|
||||
// Ignore if not connected
|
||||
}
|
||||
|
||||
let lastNode = this.source;
|
||||
|
||||
// Apply mono audio if enabled
|
||||
|
|
@ -234,15 +238,17 @@ class AudioContextManager {
|
|||
}
|
||||
|
||||
if (this.isEQEnabled && this.filters.length > 0) {
|
||||
// EQ enabled: lastNode -> EQ filters -> output -> analyser -> destination
|
||||
// EQ enabled: lastNode -> EQ filters -> output -> analyser -> volume -> destination
|
||||
lastNode.connect(this.filters[0]);
|
||||
this.outputNode.connect(this.analyser);
|
||||
this.analyser.connect(this.audioContext.destination);
|
||||
this.analyser.connect(this.volumeNode);
|
||||
this.volumeNode.connect(this.audioContext.destination);
|
||||
console.log('[AudioContext] EQ connected');
|
||||
} else {
|
||||
// EQ disabled: lastNode -> analyser -> destination
|
||||
// EQ disabled: lastNode -> analyser -> volume -> destination
|
||||
lastNode.connect(this.analyser);
|
||||
this.analyser.connect(this.audioContext.destination);
|
||||
this.analyser.connect(this.volumeNode);
|
||||
this.volumeNode.connect(this.audioContext.destination);
|
||||
console.log('[AudioContext] EQ bypassed');
|
||||
}
|
||||
|
||||
|
|
@ -313,6 +319,18 @@ class AudioContextManager {
|
|||
return this.isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the volume level (0.0 to 1.0)
|
||||
* @param {number} value - Volume level
|
||||
*/
|
||||
setVolume(value) {
|
||||
this.currentVolume = Math.max(0, Math.min(1, value));
|
||||
if (this.volumeNode && this.audioContext) {
|
||||
const now = this.audioContext.currentTime;
|
||||
this.volumeNode.gain.setTargetAtTime(this.currentVolume, now, 0.01);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle EQ on/off
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -66,11 +66,6 @@ export function initializePlayerEvents(player, audioPlayer, scrobbler, ui) {
|
|||
scrobbler.updateNowPlaying(player.currentTrack);
|
||||
}
|
||||
|
||||
// Resume AudioContext for waveform on mobile (iOS)
|
||||
if (waveformGenerator.audioContext.state === 'suspended') {
|
||||
waveformGenerator.audioContext.resume();
|
||||
}
|
||||
|
||||
updateWaveform();
|
||||
}
|
||||
|
||||
|
|
|
|||
16
js/player.js
16
js/player.js
|
|
@ -111,8 +111,17 @@ export class Player {
|
|||
// Calculate effective volume
|
||||
const effectiveVolume = curvedVolume * scale;
|
||||
|
||||
// Apply to audio element
|
||||
this.audio.volume = Math.max(0, Math.min(1, effectiveVolume));
|
||||
// Apply to audio element and/or Web Audio graph
|
||||
if (audioContextManager.isReady()) {
|
||||
// If Web Audio is active, we apply volume there for better compatibility
|
||||
// Especially on Linux where audio.volume might not affect the Web Audio graph
|
||||
// We set audio.volume to 1.0 to avoid double-reduction, or keep it synced?
|
||||
// Some browsers require audio.volume to be set for system media controls to show volume
|
||||
this.audio.volume = 1.0;
|
||||
audioContextManager.setVolume(effectiveVolume);
|
||||
} else {
|
||||
this.audio.volume = Math.max(0, Math.min(1, effectiveVolume));
|
||||
}
|
||||
}
|
||||
|
||||
applyAudioEffects() {
|
||||
|
|
@ -213,6 +222,7 @@ export class Player {
|
|||
// Must happen before audio.play() or audio won't route through Web Audio
|
||||
if (!audioContextManager.isReady()) {
|
||||
audioContextManager.init(this.audio);
|
||||
this.applyReplayGain();
|
||||
}
|
||||
await audioContextManager.resume();
|
||||
|
||||
|
|
@ -233,6 +243,7 @@ export class Player {
|
|||
// Ensure audio context is active for iOS lock screen controls
|
||||
if (!audioContextManager.isReady()) {
|
||||
audioContextManager.init(this.audio);
|
||||
this.applyReplayGain();
|
||||
}
|
||||
await audioContextManager.resume();
|
||||
this.playPrev();
|
||||
|
|
@ -242,6 +253,7 @@ export class Player {
|
|||
// Ensure audio context is active for iOS lock screen controls
|
||||
if (!audioContextManager.isReady()) {
|
||||
audioContextManager.init(this.audio);
|
||||
this.applyReplayGain();
|
||||
}
|
||||
await audioContextManager.resume();
|
||||
this.playNext();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
export class WaveformGenerator {
|
||||
constructor() {
|
||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
// Use OfflineAudioContext to prevent creating unnecessary OS audio streams
|
||||
// decodeAudioData doesn't require a real-time AudioContext
|
||||
this.audioContext = new (window.OfflineAudioContext || window.webkitOfflineAudioContext)(1, 1, 44100);
|
||||
this.cache = new Map();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue