This commit is contained in:
Drew Weymouth
2026-02-08 17:15:38 -08:00
parent 8cc4476aba
commit 6c84910553
3 changed files with 26 additions and 26 deletions
@@ -8,15 +8,15 @@ var autoEQFreqs = []float64{31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000,
// Supersonic uses 15 bands at these frequencies (in Hz) // Supersonic uses 15 bands at these frequencies (in Hz)
var supersonicFreqs = []float64{25, 40, 63, 100, 160, 250, 400, 630, 1000, 1600, 2500, 4000, 6300, 10000, 16000} var supersonicFreqs = []float64{25, 40, 63, 100, 160, 250, 400, 630, 1000, 1600, 2500, 4000, 6300, 10000, 16000}
// InterpolateAutoEQTo15Band converts a 10-band AutoEQ profile to Supersonic's 15-band ISO equalizer. // InterpolateEQ10To15Band converts a 10-band EQ profile to Supersonic's 15-band ISO equalizer.
// Uses logarithmic frequency positioning with linear dB interpolation. // Uses logarithmic frequency positioning with linear dB interpolation.
// //
// Parameters: // Parameters:
// - autoEQGains: Array of 10 gain values (dB) from AutoEQ at 31, 62, 125, 250, 500, 1k, 2k, 4k, 8k, 16k Hz // - eq10Gains: Array of 10 gain values (dB) from 10-band EQ at 31, 62, 125, 250, 500, 1k, 2k, 4k, 8k, 16k Hz
// //
// Returns: // Returns:
// - Array of 15 gain values (dB) for Supersonic at 25, 40, 63, 100, 160, 250, 400, 630, 1k, 1.6k, 2.5k, 4k, 6.3k, 10k, 16k Hz // - Array of 15 gain values (dB) for Supersonic at 25, 40, 63, 100, 160, 250, 400, 630, 1k, 1.6k, 2.5k, 4k, 6.3k, 10k, 16k Hz
func InterpolateAutoEQTo15Band(autoEQGains [10]float64) [15]float64 { func InterpolateEQ10To15Band(eq10Gains [10]float64) [15]float64 {
var result [15]float64 var result [15]float64
for i, targetFreq := range supersonicFreqs { for i, targetFreq := range supersonicFreqs {
@@ -25,21 +25,21 @@ func InterpolateAutoEQTo15Band(autoEQGains [10]float64) [15]float64 {
if lowerIdx == upperIdx { if lowerIdx == upperIdx {
// Exact match - use the AutoEQ gain directly // Exact match - use the AutoEQ gain directly
result[i] = autoEQGains[lowerIdx] result[i] = eq10Gains[lowerIdx]
} else if lowerIdx == -1 { } else if lowerIdx == -1 {
// Target frequency is below the lowest AutoEQ band (25 Hz < 31.25 Hz) // Target frequency is below the lowest AutoEQ band (25 Hz < 31.25 Hz)
// Extrapolate using the first two AutoEQ bands // Extrapolate using the first two AutoEQ bands
result[i] = extrapolateBelow(targetFreq, autoEQGains) result[i] = extrapolateBelow(targetFreq, eq10Gains)
} else if upperIdx == -1 { } else if upperIdx == -1 {
// Target frequency is above the highest AutoEQ band (should not happen with our ranges) // Target frequency is above the highest AutoEQ band (should not happen with our ranges)
// Use the highest AutoEQ gain // Use the highest AutoEQ gain
result[i] = autoEQGains[len(autoEQGains)-1] result[i] = eq10Gains[len(eq10Gains)-1]
} else { } else {
// Interpolate between two AutoEQ bands // Interpolate between two AutoEQ bands
fLow := autoEQFreqs[lowerIdx] fLow := autoEQFreqs[lowerIdx]
fHigh := autoEQFreqs[upperIdx] fHigh := autoEQFreqs[upperIdx]
gLow := autoEQGains[lowerIdx] gLow := eq10Gains[lowerIdx]
gHigh := autoEQGains[upperIdx] gHigh := eq10Gains[upperIdx]
// Calculate logarithmic position between the two bands // Calculate logarithmic position between the two bands
// t = log(targetFreq/fLow) / log(fHigh/fLow) // t = log(targetFreq/fLow) / log(fHigh/fLow)
@@ -107,9 +107,9 @@ func extrapolateBelow(targetFreq float64, autoEQGains [10]float64) float64 {
return g1 + slope*math.Log(targetFreq/f1) return g1 + slope*math.Log(targetFreq/f1)
} }
// Interpolate15BandTo10Band converts a 15-band ISO equalizer to 10-band format. // InterpolateEQ15BandTo10Band converts a 15-band ISO equalizer to 10-band format.
// Uses logarithmic frequency positioning with linear dB interpolation. // Uses logarithmic frequency positioning with linear dB interpolation.
func Interpolate15BandTo10Band(gains15Band [15]float64) [10]float64 { func InterpolateEQ15BandTo10Band(gains15Band [15]float64) [10]float64 {
var result [10]float64 var result [10]float64
for i, targetFreq := range autoEQFreqs { for i, targetFreq := range autoEQFreqs {
@@ -8,7 +8,7 @@ import (
func TestInterpolateAutoEQTo15Band_FlatProfile(t *testing.T) { func TestInterpolateAutoEQTo15Band_FlatProfile(t *testing.T) {
// Test with a flat profile (all zeros) // Test with a flat profile (all zeros)
flatProfile := [10]float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} flatProfile := [10]float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
result := InterpolateAutoEQTo15Band(flatProfile) result := InterpolateEQ10To15Band(flatProfile)
for i, gain := range result { for i, gain := range result {
if math.Abs(gain) > 0.001 { if math.Abs(gain) > 0.001 {
@@ -24,7 +24,7 @@ func TestInterpolateAutoEQTo15Band_ExactMatches(t *testing.T) {
// Exact matches: 250 (idx 5→3), 1k (idx 5→8), 4k (idx 7→11), 16k (idx 9→14) // Exact matches: 250 (idx 5→3), 1k (idx 5→8), 4k (idx 7→11), 16k (idx 9→14)
testProfile := [10]float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} testProfile := [10]float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := InterpolateAutoEQTo15Band(testProfile) result := InterpolateEQ10To15Band(testProfile)
tests := []struct { tests := []struct {
supersonicIdx int supersonicIdx int
@@ -51,7 +51,7 @@ func TestInterpolateAutoEQTo15Band_Interpolation(t *testing.T) {
testProfile := [10]float64{0, 0, 0, 0, 0, 10, 0, 0, 0, 0} testProfile := [10]float64{0, 0, 0, 0, 0, 10, 0, 0, 0, 0}
// Only 1000 Hz (index 5) has gain of 10 dB // Only 1000 Hz (index 5) has gain of 10 dB
result := InterpolateAutoEQTo15Band(testProfile) result := InterpolateEQ10To15Band(testProfile)
// Check that 1000 Hz (index 8) has the exact value // Check that 1000 Hz (index 8) has the exact value
if math.Abs(result[8]-10) > 0.001 { if math.Abs(result[8]-10) > 0.001 {
@@ -75,7 +75,7 @@ func TestInterpolateAutoEQTo15Band_Extrapolation(t *testing.T) {
testProfile := [10]float64{5, 10, 0, 0, 0, 0, 0, 0, 0, 0} testProfile := [10]float64{5, 10, 0, 0, 0, 0, 0, 0, 0, 0}
// 31.25 Hz = 5 dB, 62.5 Hz = 10 dB // 31.25 Hz = 5 dB, 62.5 Hz = 10 dB
result := InterpolateAutoEQTo15Band(testProfile) result := InterpolateEQ10To15Band(testProfile)
// 25 Hz should be extrapolated below 31.25 Hz // 25 Hz should be extrapolated below 31.25 Hz
// Since the slope is positive (5 to 10), 25 Hz should be < 5 dB // Since the slope is positive (5 to 10), 25 Hz should be < 5 dB
@@ -94,7 +94,7 @@ func TestInterpolateAutoEQTo15Band_RealProfile(t *testing.T) {
// Approximating a V-shaped response // Approximating a V-shaped response
testProfile := [10]float64{6, 5, 3, 1, 0, 0, 1, 3, 5, 6} testProfile := [10]float64{6, 5, 3, 1, 0, 0, 1, 3, 5, 6}
result := InterpolateAutoEQTo15Band(testProfile) result := InterpolateEQ10To15Band(testProfile)
// Verify output is reasonable // Verify output is reasonable
if len(result) != 15 { if len(result) != 15 {
@@ -115,7 +115,7 @@ func TestInterpolateAutoEQTo15Band_MonotonicPreservation(t *testing.T) {
// If AutoEQ gains are monotonically increasing, interpolated values should also increase // If AutoEQ gains are monotonically increasing, interpolated values should also increase
monotonicProfile := [10]float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} monotonicProfile := [10]float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
result := InterpolateAutoEQTo15Band(monotonicProfile) result := InterpolateEQ10To15Band(monotonicProfile)
// Check general increasing trend (allowing for some extrapolation variance at edges) // Check general increasing trend (allowing for some extrapolation variance at edges)
for i := 2; i < len(result)-1; i++ { for i := 2; i < len(result)-1; i++ {
+10 -10
View File
@@ -42,14 +42,14 @@ type SettingsDialog struct {
OnPageNeedsRefresh func() OnPageNeedsRefresh func()
OnClearCaches func() OnClearCaches func()
config *backend.Config config *backend.Config
audioDevices []mpv.AudioDevice audioDevices []mpv.AudioDevice
themeFiles map[string]string // filename -> displayName themeFiles map[string]string // filename -> displayName
promptText *widget.RichText promptText *widget.RichText
eqPresetManager *backend.EQPresetManager eqPresetManager *backend.EQPresetManager
autoEQManager *backend.AutoEQManager autoEQManager *backend.AutoEQManager
imageManager util.ImageFetcher imageManager util.ImageFetcher
window fyne.Window window fyne.Window
clientDecidesScrobble bool clientDecidesScrobble bool
@@ -534,7 +534,7 @@ func (s *SettingsDialog) createEqualizerTab(eqBands []string) *container.TabItem
// Use interpolation to downsample // Use interpolation to downsample
var bands15 [15]float64 var bands15 [15]float64
copy(bands15[:], currentBands) copy(bands15[:], currentBands)
bands10 := backend.Interpolate15BandTo10Band(bands15) bands10 := backend.InterpolateEQ15BandTo10Band(bands15)
newBands = bands10[:] newBands = bands10[:]
} else { } else {
// Already 10-band or invalid size, just copy what we can // Already 10-band or invalid size, just copy what we can
@@ -548,7 +548,7 @@ func (s *SettingsDialog) createEqualizerTab(eqBands []string) *container.TabItem
// Use interpolation to upsample // Use interpolation to upsample
var bands10 [10]float64 var bands10 [10]float64
copy(bands10[:], currentBands) copy(bands10[:], currentBands)
bands15 := backend.InterpolateAutoEQTo15Band(bands10) bands15 := backend.InterpolateEQ10To15Band(bands10)
newBands = bands15[:] newBands = bands15[:]
} else { } else {
// Already 15-band or invalid size, just copy what we can // Already 15-band or invalid size, just copy what we can