AgentSkillsCN

eqapo-format-expert

EqualizerAPO配置文件格式、解析和生成专家。适用于实现EAPO配置导入/导出、添加新滤波器类型或调试配置文件问题时。

SKILL.md
--- frontmatter
name: eqapo-format-expert
description: Expert in EqualizerAPO configuration file format, parsing, and generation. Use when implementing EAPO config import/export, adding support for new filter types, or debugging config file issues.

EqualizerAPO Format Expert

Specialized agent for working with EqualizerAPO configuration files, ensuring correct parsing and generation of .txt config files.

EqualizerAPO Overview

EqualizerAPO is a parametric equalizer for Windows that processes system audio via APO (Audio Processing Objects) framework.

  • Config Location: C:\Program Files\EqualizerAPO\config\config.txt
  • Format: Plain text with line-based commands
  • Real-time: Changes apply immediately when file is saved
  • Channels: Supports per-channel processing (L, R, C, SUB, etc.)

Config File Format

Basic Structure

code
# Comment lines start with #

Preamp: -5.0 dB
Filter: ON PK Fc 1000 Hz Gain 3.0 dB Q 1.41
Filter: ON LS Fc 80 Hz Gain -2.5 dB Q 0.71
Filter: ON HS Fc 10000 Hz Gain 4.0 dB Q 0.71

Filter Types Supported by EQAPO GUI

TypeEAPO CodeDescriptionParameters
PeakingPKBell curve EQFc, Gain, Q
Low ShelfLSBass adjustmentFc, Gain, Q
High ShelfHSTreble adjustmentFc, Gain, Q

Additional Filter Types (Not Yet Implemented)

TypeEAPO CodeDescription
Low PassLPCuts high frequencies
High PassHPCuts low frequencies
Band PassBPPasses only a frequency range
NotchNOCuts a specific frequency
All PassAPAffects phase, not magnitude

Preamp Syntax

code
Preamp: <value> dB

Examples:

code
Preamp: -5.0 dB
Preamp: 3 dB
Preamp: 0 dB

Rules:

  • Must be on its own line
  • Value can be integer or float
  • Unit "dB" required
  • Negative values reduce volume (prevent clipping)
  • Typical range: -20 to +20 dB

Filter Syntax

code
Filter: <ON|OFF> <TYPE> Fc <frequency> Hz Gain <gain> dB Q <q_factor>

Examples:

code
Filter: ON PK Fc 1000 Hz Gain 3.0 dB Q 1.41
Filter: ON LS Fc 80 Hz Gain -2.5 dB Q 0.71
Filter: OFF HS Fc 10000 Hz Gain 4.0 dB Q 0.71

Rules:

  • ON or OFF state (case-insensitive)
  • Filter type: PK, LS, HS, etc.
  • Frequency: Fc <value> Hz (20-20000 typical)
  • Gain: Gain <value> dB (can be negative)
  • Q factor: Q <value> (0.1-30 typical)

Channel-Specific Filters

code
Channel: L
Filter: ON PK Fc 1000 Hz Gain 3.0 dB Q 1.41

Channel: R
Filter: ON PK Fc 1000 Hz Gain 2.0 dB Q 1.41

Channel: ALL

Not yet supported in EQAPO GUI - applies filters to all channels globally.

Parsing EqualizerAPO Files

Parser Implementation (TypeScript)

typescript
export interface ParsedEapoConfig {
  preamp: number;
  bands: ParametricBand[];
}

export function parseEapoConfig(content: string): ParsedEapoConfig {
  const lines = content.split('\n').map((line) => line.trim());

  let preamp = 0;
  const bands: ParametricBand[] = [];

  for (const line of lines) {
    // Skip comments and empty lines
    if (line.startsWith('#') || line === '') continue;

    // Parse preamp
    if (line.startsWith('Preamp:')) {
      const match = line.match(/Preamp:\s*([-+]?\d+\.?\d*)\s*dB/i);
      if (match) {
        preamp = parseFloat(match[1]);
      }
      continue;
    }

    // Parse filter
    if (line.startsWith('Filter:')) {
      const band = parseFilterLine(line);
      if (band) {
        bands.push(band);
      }
    }
  }

  return { preamp, bands };
}

function parseFilterLine(line: string): ParametricBand | null {
  // Extract state (ON/OFF)
  const stateMatch = line.match(/Filter:\s*(ON|OFF)/i);
  if (!stateMatch || stateMatch[1].toUpperCase() === 'OFF') {
    return null; // Skip disabled filters
  }

  // Extract filter type
  const typeMatch = line.match(/(PK|LS|HS|LP|HP|BP|NO|AP)/i);
  if (!typeMatch) return null;

  const filterType = normalizeFilterType(typeMatch[1].toUpperCase());

  // Extract frequency
  const freqMatch = line.match(/Fc\s+([\d.]+)\s*Hz/i);
  if (!freqMatch) return null;
  const frequency = parseFloat(freqMatch[1]);

  // Extract gain
  const gainMatch = line.match(/Gain\s+([-+]?[\d.]+)\s*dB/i);
  if (!gainMatch) return null;
  const gain = parseFloat(gainMatch[1]);

  // Extract Q factor
  const qMatch = line.match(/Q\s+([\d.]+)/i);
  if (!qMatch) return null;
  const qFactor = parseFloat(qMatch[1]);

  // Validate
  if (!isValidBand(frequency, gain, qFactor)) {
    return null;
  }

  return { filterType, frequency, gain, qFactor };
}

function normalizeFilterType(type: string): FilterType {
  switch (type) {
    case 'PK':
    case 'PEAKING':
      return 'Peaking';
    case 'LS':
    case 'LOWSHELF':
      return 'LowShelf';
    case 'HS':
    case 'HIGHSHELF':
      return 'HighShelf';
    default:
      throw new Error(`Unsupported filter type: ${type}`);
  }
}

function isValidBand(frequency: number, gain: number, qFactor: number): boolean {
  return (
    frequency >= 20 &&
    frequency <= 20000 &&
    gain >= -30 &&
    gain <= 30 &&
    qFactor >= 0.01 &&
    qFactor <= 100
  );
}

Edge Cases in Parsing

  1. Case Insensitivity

    code
    filter: on pk fc 1000 hz gain 3.0 db q 1.41  ✓
    FILTER: ON PK FC 1000 HZ GAIN 3.0 DB Q 1.41  ✓
    Filter: ON PK Fc 1000 Hz Gain 3.0 dB Q 1.41  ✓
    
  2. Extra Whitespace

    code
    Filter:  ON   PK  Fc  1000  Hz  Gain  3.0  dB  Q  1.41  ✓
    
  3. Integer vs Float

    code
    Filter: ON PK Fc 1000 Hz Gain 3 dB Q 1.41     ✓
    Filter: ON PK Fc 1000.0 Hz Gain 3.0 dB Q 1.41 ✓
    
  4. Negative Gains

    code
    Filter: ON PK Fc 1000 Hz Gain -3.0 dB Q 1.41  ✓
    Filter: ON LS Fc 80 Hz Gain -6 dB Q 0.71      ✓
    
  5. Comments Mid-Line (Not standard, but seen in wild)

    code
    Filter: ON PK Fc 1000 Hz Gain 3.0 dB Q 1.41 # Boost presence
    

Generating EqualizerAPO Files

Writer Implementation (Rust)

rust
pub fn write_eapo_config(
    path: &Path,
    bands: &[ParametricBand],
    preamp: f32,
) -> Result<(), std::io::Error> {
    let mut content = String::new();

    // Header comment
    content.push_str("# Generated by EQAPO GUI\n");
    content.push_str(&format!("# Generated: {}\n\n", chrono::Local::now().format("%Y-%m-%d %H:%M:%S")));

    // Preamp
    content.push_str(&format!("Preamp: {:.1} dB\n", preamp));

    // Filters
    for band in bands {
        let filter_line = format!(
            "Filter: ON {} Fc {} Hz Gain {:.1} dB Q {:.2}\n",
            filter_type_to_code(&band.filter_type),
            band.frequency as u32,
            band.gain,
            band.q_factor
        );
        content.push_str(&filter_line);
    }

    // Write to file
    std::fs::write(path, content)?;

    Ok(())
}

fn filter_type_to_code(filter_type: &FilterType) -> &'static str {
    match filter_type {
        FilterType::Peaking => "PK",
        FilterType::LowShelf => "LS",
        FilterType::HighShelf => "HS",
    }
}

Output Format Example

code
# Generated by EQAPO GUI
# Generated: 2026-01-04 15:30:00

Preamp: -3.5 dB
Filter: ON PK Fc 1000 Hz Gain 3.0 dB Q 1.41
Filter: ON LS Fc 80 Hz Gain -2.5 dB Q 0.71
Filter: ON HS Fc 10000 Hz Gain 4.0 dB Q 0.71

Formatting Rules:

  • Frequency: Integer (no decimal places)
  • Gain: 1 decimal place
  • Q factor: 2 decimal places
  • Consistent spacing
  • One filter per line

Advanced EqualizerAPO Features

Include Directive

code
Include: subwoofer_eq.txt
Include: headphone_compensation.txt

Loads another config file at that position.

Copy Filter

code
Copy: L=L+0.5*R R=R+0.5*L

Mixes channels (crossfeed for headphones).

Delay

code
Delay: L=0 R=2.5

Delays right channel by 2.5ms (speaker positioning).

Convolution (Impulse Response)

code
Convolution: room_correction.wav

Applies FIR filter from WAV file (room correction).

None of these are supported in EQAPO GUI yet - future features.

File Permissions

Windows UAC Challenges

EqualizerAPO's default config location requires admin rights:

code
C:\Program Files\EqualizerAPO\config\config.txt

Solutions:

  1. Live Config (Recommended)

    code
    C:\Users\<Username>\Documents\EQAPO GUI\live_config.txt
    

    Then use EqualizerAPO's Include: directive in main config:

    code
    Include: C:\Users\<Username>\Documents\EQAPO GUI\live_config.txt
    
  2. Run as Admin (Not recommended)

    • Tauri app requests elevation
    • Security risk
    • UAC prompt every time

Permission Check (Rust)

rust
pub fn can_write_to_eapo_config(path: &Path) -> bool {
    // Try to open file for writing
    std::fs::OpenOptions::new()
        .write(true)
        .create(false)
        .open(path)
        .is_ok()
}

pub fn get_recommended_config_path() -> PathBuf {
    if can_write_to_eapo_config(&get_default_config_path()) {
        get_default_config_path()
    } else {
        get_live_config_path()
    }
}

Validation

Pre-Write Validation

typescript
function validateEapoConfig(bands: ParametricBand[], preamp: number): string[] {
  const errors: string[] = [];

  // Check preamp
  if (preamp < -20 || preamp > 20) {
    errors.push(`Preamp ${preamp} dB exceeds typical range (-20 to +20 dB)`);
  }

  // Check band count
  if (bands.length > 32) {
    errors.push(`Too many bands (${bands.length}). EqualizerAPO supports max 32.`);
  }

  // Check each band
  bands.forEach((band, i) => {
    if (band.frequency < 20 || band.frequency > 20000) {
      errors.push(`Band ${i + 1}: Frequency ${band.frequency} Hz out of range (20-20000 Hz)`);
    }

    if (band.gain < -30 || band.gain > 30) {
      errors.push(`Band ${i + 1}: Gain ${band.gain} dB exceeds typical range (-30 to +30 dB)`);
    }

    if (band.qFactor < 0.1 || band.qFactor > 30) {
      errors.push(`Band ${i + 1}: Q factor ${band.qFactor} out of range (0.1-30)`);
    }
  });

  return errors;
}

Testing

Parser Test Cases

typescript
describe('EAPO Parser', () => {
  it('should parse basic config', () => {
    const config = `
      Preamp: -5.0 dB
      Filter: ON PK Fc 1000 Hz Gain 3.0 dB Q 1.41
    `;

    const result = parseEapoConfig(config);

    expect(result.preamp).toBe(-5.0);
    expect(result.bands).toHaveLength(1);
    expect(result.bands[0].frequency).toBe(1000);
  });

  it('should skip disabled filters', () => {
    const config = `
      Filter: ON PK Fc 1000 Hz Gain 3.0 dB Q 1.41
      Filter: OFF PK Fc 2000 Hz Gain 6.0 dB Q 1.41
    `;

    const result = parseEapoConfig(config);
    expect(result.bands).toHaveLength(1);
  });

  it('should handle case insensitivity', () => {
    const config = `FILTER: on pk fc 1000 hz gain 3.0 db q 1.41`;
    const result = parseEapoConfig(config);

    expect(result.bands[0].filterType).toBe('Peaking');
  });
});

Common Issues

  1. ❌ Missing "dB" Unit

    code
    Preamp: -5     # WRONG: Missing unit
    Preamp: -5 dB  # CORRECT
    
  2. ❌ Wrong Filter Code

    code
    Filter: ON Peaking Fc 1000 Hz ...  # WRONG: Use "PK"
    Filter: ON PK Fc 1000 Hz ...       # CORRECT
    
  3. ❌ Missing Fc Keyword

    code
    Filter: ON PK 1000 Hz ...      # WRONG
    Filter: ON PK Fc 1000 Hz ...   # CORRECT
    
  4. ❌ Comma as Decimal Separator

    code
    Filter: ON PK Fc 1000 Hz Gain 3,0 dB Q 1,41  # WRONG (European notation)
    Filter: ON PK Fc 1000 Hz Gain 3.0 dB Q 1.41  # CORRECT
    

Reference Materials

  • references/eapo_spec.md - Full EqualizerAPO specification
  • references/filter_types.md - All supported filter types
  • references/examples.md - Real-world config file examples