feat: exponential volume

This commit is contained in:
Eduard Prigoana 2026-02-09 13:36:33 +00:00
parent 43be45b76f
commit af1c0fc1ee
4 changed files with 78 additions and 16 deletions

View file

@ -2646,21 +2646,21 @@
</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 id="lastfm-credential-toggle-container">
<div id="lastfm-credential-toggle-container-secondary">
<button
id="lastfm-show-credential-auth"
id="lastfm-show-credential-auth-secondary"
class="btn-secondary"
style="padding: 6px 12px; font-size: 0.85rem; width: 100%"
>
Login with Username/Password
</button>
</div>
<div id="lastfm-credential-form" style="display: none">
<div id="lastfm-credential-form-secondary" style="display: none">
<input
type="text"
id="lastfm-username"
id="lastfm-username-secondary"
placeholder="Last.fm Username"
style="
padding: 8px;
@ -2674,7 +2674,7 @@
/>
<input
type="password"
id="lastfm-password"
id="lastfm-password-secondary"
placeholder="Last.fm Password"
style="
padding: 8px;
@ -2688,14 +2688,14 @@
/>
<div style="display: flex; gap: 8px">
<button
id="lastfm-login-credentials"
id="lastfm-login-credentials-secondary"
class="btn-secondary"
style="padding: 6px 12px; font-size: 0.85rem; flex: 1"
>
Login
</button>
<button
id="lastfm-use-oauth"
id="lastfm-use-oauth-secondary"
class="btn-secondary"
style="padding: 6px 12px; font-size: 0.85rem; flex: 1"
>
@ -2705,11 +2705,11 @@
</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">
<input
type="text"
id="lastfm-username"
id="lastfm-username-alt"
placeholder="Last.fm Username"
style="
padding: 8px;
@ -2721,7 +2721,7 @@
/>
<input
type="password"
id="lastfm-password"
id="lastfm-password-alt"
placeholder="Last.fm Password"
style="
padding: 8px;
@ -2733,14 +2733,14 @@
/>
<div style="display: flex; gap: 8px; margin-top: 4px">
<button
id="lastfm-login-credentials"
id="lastfm-login-credentials-alt"
class="btn-secondary"
style="padding: 6px 12px; font-size: 0.85rem"
>
Login with Credentials
</button>
<button
id="lastfm-use-oauth"
id="lastfm-use-oauth-alt"
class="btn-secondary"
style="padding: 6px 12px; font-size: 0.85rem"
>
@ -2749,7 +2749,7 @@
</div>
<div style="display: flex; gap: 8px; margin-top: 4px">
<button
id="lastfm-show-credential-auth"
id="lastfm-show-credential-auth-alt"
class="btn-secondary"
style="padding: 6px 12px; font-size: 0.85rem"
>
@ -3069,6 +3069,18 @@
<span class="slider"></span>
</label>
</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 -->
<div class="setting-item">

View file

@ -9,7 +9,7 @@ import {
getTrackYearDisplay,
createQualityBadgeHTML,
} from './utils.js';
import { queueManager, replayGainSettings, trackDateSettings } from './storage.js';
import { queueManager, replayGainSettings, trackDateSettings, exponentialVolumeSettings } from './storage.js';
import { audioContextManager } from './audio-context.js';
export class Player {
@ -99,8 +99,11 @@ export class Player {
scale = 1.0 / peak;
}
// Apply exponential volume curve if enabled
const curvedVolume = exponentialVolumeSettings.applyCurve(this.userVolume);
// Calculate effective volume
const effectiveVolume = this.userVolume * scale;
const effectiveVolume = curvedVolume * scale;
// Apply to audio element
this.audio.volume = Math.max(0, Math.min(1, effectiveVolume));

View file

@ -25,6 +25,7 @@ import {
sidebarSectionSettings,
fontSettings,
monoAudioSettings,
exponentialVolumeSettings,
} from './storage.js';
import { audioContextManager, EQ_PRESETS } from './audio-context.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
// ========================================

View file

@ -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 = {
STORAGE_KEY: 'monochrome-sidebar-collapsed',