Add 10-band equalizer support and AutoEQ integration

This commit is contained in:
Gianluca Boiano
2026-02-06 02:50:39 +01:00
parent 2e33b81914
commit 0097b0a052
6 changed files with 1075 additions and 0 deletions
+150
View File
@@ -0,0 +1,150 @@
package backend
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"sort"
)
const eqPresetsDir = "eq_presets"
// EQPreset represents an equalizer preset that can be saved/loaded
type EQPreset struct {
Name string `json:"name"`
Type string `json:"type"` // "ISO10Band" or "ISO15Band"
Preamp float64 `json:"preamp"`
Bands []float64 `json:"bands"`
IsBuiltin bool `json:"-"` // not saved to file, determined at load time
}
// EQPresetManager handles loading and saving EQ presets
type EQPresetManager struct {
presetsDir string
builtinPresets []EQPreset
}
// NewEQPresetManager creates a new preset manager
func NewEQPresetManager(configDir string) *EQPresetManager {
return &EQPresetManager{
presetsDir: filepath.Join(configDir, eqPresetsDir),
builtinPresets: getBuiltinPresets(),
}
}
// getBuiltinPresets returns the built-in equalizer presets
func getBuiltinPresets() []EQPreset {
return []EQPreset{
{Name: "Flat", Type: "ISO15Band", Preamp: 0, Bands: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, IsBuiltin: true},
{Name: "Rock", Type: "ISO15Band", Preamp: 0, Bands: []float64{5, 4, 3, 1, -1, -1, 0, 2, 3, 4, 4, 4, 3, 2, 2}, IsBuiltin: true},
{Name: "Pop", Type: "ISO15Band", Preamp: 0, Bands: []float64{-1, -1, 0, 2, 4, 4, 2, 0, -1, -1, 0, 1, 2, 3, 3}, IsBuiltin: true},
{Name: "Jazz", Type: "ISO15Band", Preamp: 0, Bands: []float64{4, 3, 1, 2, -2, -2, 0, 2, 3, 3, 3, 4, 4, 4, 4}, IsBuiltin: true},
{Name: "Classical", Type: "ISO15Band", Preamp: 0, Bands: []float64{5, 4, 3, 2, -1, -1, 0, 2, 3, 3, 3, 2, 2, 2, -1}, IsBuiltin: true},
{Name: "Bass Boost", Type: "ISO15Band", Preamp: 0, Bands: []float64{6, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, IsBuiltin: true},
{Name: "Treble Boost", Type: "ISO15Band", Preamp: 0, Bands: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 4, 5, 6, 6}, IsBuiltin: true},
{Name: "Vocal", Type: "ISO15Band", Preamp: 0, Bands: []float64{-2, -3, -3, 1, 4, 4, 4, 3, 2, 1, 0, -1, -2, -2, -3}, IsBuiltin: true},
{Name: "Electronic", Type: "ISO15Band", Preamp: 0, Bands: []float64{5, 4, 2, 0, -2, -2, 0, 2, 3, 4, 4, 3, 4, 4, 3}, IsBuiltin: true},
{Name: "Acoustic", Type: "ISO15Band", Preamp: 0, Bands: []float64{5, 4, 3, 1, 2, 1, 1, 2, 2, 2, 1, 2, 2, 3, 2}, IsBuiltin: true},
{Name: "R&B", Type: "ISO15Band", Preamp: 0, Bands: []float64{3, 6, 5, 2, -2, -2, 2, 3, 2, 2, 3, 3, 3, 3, 4}, IsBuiltin: true},
{Name: "Loudness", Type: "ISO15Band", Preamp: 0, Bands: []float64{6, 5, 3, 0, -1, -1, -1, -1, 0, 1, 2, 4, 5, 5, 3}, IsBuiltin: true},
}
}
// LoadPresets loads all presets (builtin + user-defined)
func (m *EQPresetManager) LoadPresets() ([]EQPreset, error) {
// Start with builtin presets
presets := make([]EQPreset, len(m.builtinPresets))
copy(presets, m.builtinPresets)
// Create presets directory if it doesn't exist
if err := os.MkdirAll(m.presetsDir, 0o755); err != nil {
return presets, err
}
// Load user presets
entries, err := os.ReadDir(m.presetsDir)
if err != nil {
return presets, nil // Return builtins if can't read dir
}
for _, entry := range entries {
if entry.IsDir() || filepath.Ext(entry.Name()) != ".json" {
continue
}
presetPath := filepath.Join(m.presetsDir, entry.Name())
data, err := os.ReadFile(presetPath)
if err != nil {
continue // Skip invalid files
}
var preset EQPreset
if err := json.Unmarshal(data, &preset); err != nil {
continue // Skip invalid JSON
}
preset.IsBuiltin = false
presets = append(presets, preset)
}
// Sort: builtins first, then user presets alphabetically
sort.Slice(presets, func(i, j int) bool {
if presets[i].IsBuiltin != presets[j].IsBuiltin {
return presets[i].IsBuiltin
}
return presets[i].Name < presets[j].Name
})
return presets, nil
}
// SavePreset saves a preset to disk
func (m *EQPresetManager) SavePreset(preset EQPreset) error {
if preset.IsBuiltin {
return fmt.Errorf("cannot overwrite builtin preset")
}
// Create presets directory if it doesn't exist
if err := os.MkdirAll(m.presetsDir, 0o755); err != nil {
return err
}
// Sanitize filename
filename := sanitizeFilename(preset.Name) + ".json"
presetPath := filepath.Join(m.presetsDir, filename)
data, err := json.MarshalIndent(preset, "", " ")
if err != nil {
return err
}
return os.WriteFile(presetPath, data, 0o644)
}
// DeletePreset deletes a user preset
func (m *EQPresetManager) DeletePreset(name string) error {
// Check if it's a builtin preset
for _, bp := range m.builtinPresets {
if bp.Name == name {
return fmt.Errorf("cannot delete builtin preset")
}
}
filename := sanitizeFilename(name) + ".json"
presetPath := filepath.Join(m.presetsDir, filename)
return os.Remove(presetPath)
}
// sanitizeFilename removes characters that aren't safe for filenames
func sanitizeFilename(name string) string {
// Simple sanitization - could be enhanced
var result []rune
for _, r := range name {
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '-' || r == '_' || r == ' ' {
result = append(result, r)
}
}
return string(result)
}