feat: exponential volume
This commit is contained in:
parent
43be45b76f
commit
af1c0fc1ee
4 changed files with 78 additions and 16 deletions
40
index.html
40
index.html
|
|
@ -2646,21 +2646,21 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="lastfm-credential-auth" style="display: none; margin-top: 12px">
|
<div id="lastfm-credential-auth-secondary" style="display: none; margin-top: 12px">
|
||||||
<div style="display: flex; flex-direction: column; gap: 8px">
|
<div style="display: flex; flex-direction: column; gap: 8px">
|
||||||
<div id="lastfm-credential-toggle-container">
|
<div id="lastfm-credential-toggle-container-secondary">
|
||||||
<button
|
<button
|
||||||
id="lastfm-show-credential-auth"
|
id="lastfm-show-credential-auth-secondary"
|
||||||
class="btn-secondary"
|
class="btn-secondary"
|
||||||
style="padding: 6px 12px; font-size: 0.85rem; width: 100%"
|
style="padding: 6px 12px; font-size: 0.85rem; width: 100%"
|
||||||
>
|
>
|
||||||
Login with Username/Password
|
Login with Username/Password
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="lastfm-credential-form" style="display: none">
|
<div id="lastfm-credential-form-secondary" style="display: none">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="lastfm-username"
|
id="lastfm-username-secondary"
|
||||||
placeholder="Last.fm Username"
|
placeholder="Last.fm Username"
|
||||||
style="
|
style="
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
@ -2674,7 +2674,7 @@
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="lastfm-password"
|
id="lastfm-password-secondary"
|
||||||
placeholder="Last.fm Password"
|
placeholder="Last.fm Password"
|
||||||
style="
|
style="
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
@ -2688,14 +2688,14 @@
|
||||||
/>
|
/>
|
||||||
<div style="display: flex; gap: 8px">
|
<div style="display: flex; gap: 8px">
|
||||||
<button
|
<button
|
||||||
id="lastfm-login-credentials"
|
id="lastfm-login-credentials-secondary"
|
||||||
class="btn-secondary"
|
class="btn-secondary"
|
||||||
style="padding: 6px 12px; font-size: 0.85rem; flex: 1"
|
style="padding: 6px 12px; font-size: 0.85rem; flex: 1"
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
id="lastfm-use-oauth"
|
id="lastfm-use-oauth-secondary"
|
||||||
class="btn-secondary"
|
class="btn-secondary"
|
||||||
style="padding: 6px 12px; font-size: 0.85rem; flex: 1"
|
style="padding: 6px 12px; font-size: 0.85rem; flex: 1"
|
||||||
>
|
>
|
||||||
|
|
@ -2705,11 +2705,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="lastfm-credential-auth" style="margin-top: 12px; display: none">
|
<div id="lastfm-credential-auth-alt" style="margin-top: 12px; display: none">
|
||||||
<div style="display: flex; flex-direction: column; gap: 8px">
|
<div style="display: flex; flex-direction: column; gap: 8px">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="lastfm-username"
|
id="lastfm-username-alt"
|
||||||
placeholder="Last.fm Username"
|
placeholder="Last.fm Username"
|
||||||
style="
|
style="
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
@ -2721,7 +2721,7 @@
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="lastfm-password"
|
id="lastfm-password-alt"
|
||||||
placeholder="Last.fm Password"
|
placeholder="Last.fm Password"
|
||||||
style="
|
style="
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
@ -2733,14 +2733,14 @@
|
||||||
/>
|
/>
|
||||||
<div style="display: flex; gap: 8px; margin-top: 4px">
|
<div style="display: flex; gap: 8px; margin-top: 4px">
|
||||||
<button
|
<button
|
||||||
id="lastfm-login-credentials"
|
id="lastfm-login-credentials-alt"
|
||||||
class="btn-secondary"
|
class="btn-secondary"
|
||||||
style="padding: 6px 12px; font-size: 0.85rem"
|
style="padding: 6px 12px; font-size: 0.85rem"
|
||||||
>
|
>
|
||||||
Login with Credentials
|
Login with Credentials
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
id="lastfm-use-oauth"
|
id="lastfm-use-oauth-alt"
|
||||||
class="btn-secondary"
|
class="btn-secondary"
|
||||||
style="padding: 6px 12px; font-size: 0.85rem"
|
style="padding: 6px 12px; font-size: 0.85rem"
|
||||||
>
|
>
|
||||||
|
|
@ -2749,7 +2749,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; gap: 8px; margin-top: 4px">
|
<div style="display: flex; gap: 8px; margin-top: 4px">
|
||||||
<button
|
<button
|
||||||
id="lastfm-show-credential-auth"
|
id="lastfm-show-credential-auth-alt"
|
||||||
class="btn-secondary"
|
class="btn-secondary"
|
||||||
style="padding: 6px 12px; font-size: 0.85rem"
|
style="padding: 6px 12px; font-size: 0.85rem"
|
||||||
>
|
>
|
||||||
|
|
@ -3069,6 +3069,18 @@
|
||||||
<span class="slider"></span>
|
<span class="slider"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Exponential Volume</span>
|
||||||
|
<span class="description"
|
||||||
|
>Use logarithmic volume curve for finer low-volume control</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="exponential-volume-toggle" />
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 16-Band Equalizer -->
|
<!-- 16-Band Equalizer -->
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
getTrackYearDisplay,
|
getTrackYearDisplay,
|
||||||
createQualityBadgeHTML,
|
createQualityBadgeHTML,
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { queueManager, replayGainSettings, trackDateSettings } from './storage.js';
|
import { queueManager, replayGainSettings, trackDateSettings, exponentialVolumeSettings } from './storage.js';
|
||||||
import { audioContextManager } from './audio-context.js';
|
import { audioContextManager } from './audio-context.js';
|
||||||
|
|
||||||
export class Player {
|
export class Player {
|
||||||
|
|
@ -99,8 +99,11 @@ export class Player {
|
||||||
scale = 1.0 / peak;
|
scale = 1.0 / peak;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply exponential volume curve if enabled
|
||||||
|
const curvedVolume = exponentialVolumeSettings.applyCurve(this.userVolume);
|
||||||
|
|
||||||
// Calculate effective volume
|
// Calculate effective volume
|
||||||
const effectiveVolume = this.userVolume * scale;
|
const effectiveVolume = curvedVolume * scale;
|
||||||
|
|
||||||
// Apply to audio element
|
// Apply to audio element
|
||||||
this.audio.volume = Math.max(0, Math.min(1, effectiveVolume));
|
this.audio.volume = Math.max(0, Math.min(1, effectiveVolume));
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import {
|
||||||
sidebarSectionSettings,
|
sidebarSectionSettings,
|
||||||
fontSettings,
|
fontSettings,
|
||||||
monoAudioSettings,
|
monoAudioSettings,
|
||||||
|
exponentialVolumeSettings,
|
||||||
} from './storage.js';
|
} from './storage.js';
|
||||||
import { audioContextManager, EQ_PRESETS } from './audio-context.js';
|
import { audioContextManager, EQ_PRESETS } from './audio-context.js';
|
||||||
import { getButterchurnPresets } from './visualizers/butterchurn.js';
|
import { getButterchurnPresets } from './visualizers/butterchurn.js';
|
||||||
|
|
@ -774,6 +775,17 @@ export function initializeSettings(scrobbler, player, api, ui) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exponential Volume Toggle
|
||||||
|
const exponentialVolumeToggle = document.getElementById('exponential-volume-toggle');
|
||||||
|
if (exponentialVolumeToggle) {
|
||||||
|
exponentialVolumeToggle.checked = exponentialVolumeSettings.isEnabled();
|
||||||
|
exponentialVolumeToggle.addEventListener('change', (e) => {
|
||||||
|
exponentialVolumeSettings.setEnabled(e.target.checked);
|
||||||
|
// Re-apply current volume to use new curve
|
||||||
|
player.applyReplayGain();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// 16-Band Equalizer Settings
|
// 16-Band Equalizer Settings
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
|
||||||
|
|
@ -860,6 +860,41 @@ export const monoAudioSettings = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const exponentialVolumeSettings = {
|
||||||
|
STORAGE_KEY: 'exponential-volume-enabled',
|
||||||
|
|
||||||
|
isEnabled() {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(this.STORAGE_KEY) === 'true';
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setEnabled(enabled) {
|
||||||
|
localStorage.setItem(this.STORAGE_KEY, enabled ? 'true' : 'false');
|
||||||
|
},
|
||||||
|
|
||||||
|
// Apply exponential curve to linear volume (0-1)
|
||||||
|
// Uses a power curve: output = input^3 for more natural volume control
|
||||||
|
applyCurve(linearVolume) {
|
||||||
|
if (!this.isEnabled()) {
|
||||||
|
return linearVolume;
|
||||||
|
}
|
||||||
|
// Exponential curve: cubed for much finer low-volume control
|
||||||
|
// This creates a more dramatic difference that you'll actually notice
|
||||||
|
return Math.pow(linearVolume, 3);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert from perceived volume back to linear for UI
|
||||||
|
inverseCurve(perceivedVolume) {
|
||||||
|
if (!this.isEnabled()) {
|
||||||
|
return perceivedVolume;
|
||||||
|
}
|
||||||
|
return Math.cbrt(perceivedVolume);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const sidebarSettings = {
|
export const sidebarSettings = {
|
||||||
STORAGE_KEY: 'monochrome-sidebar-collapsed',
|
STORAGE_KEY: 'monochrome-sidebar-collapsed',
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue