Expand description
§AutoEQ CEA2034 Scoring
This crate implements CEA2034-based preference scoring algorithms for loudspeaker measurements, based on research by Harman, Olive, and others.
§Features
- CEA2034 Metrics Computation: Calculate standard metrics from spinorama measurements
- Preference Score Calculation: Harman/Olive preference rating algorithm
- Curve Analysis: Slope, smoothness, and spectral analysis tools
- PIR Computation: Predicted In-Room response calculation from CEA2034 data
- Octave Band Processing: Frequency-weighted analysis and filtering
§CEA2034 Standard
The CEA2034 standard defines a set of measurements for evaluating loudspeaker performance:
- On-axis (0°): Direct response
- Listening Window: Average of on-axis and early reflections (±10°, ±15°, ±20°, ±25°, ±30°)
- Early Reflections: Floor, ceiling, front/rear wall, and side wall reflections
- Sound Power: Total acoustic power output
- Directivity Index (DI): Ratio of on-axis to sound power
§Two examples of CEA2034 / Spinorama

§Preference Score Algorithm
The preference score is based on research showing correlation between measured responses and listener preference:
use autoeq_cea2034::{compute_cea2034_metrics, Curve};
use ndarray::Array1;
use std::collections::HashMap;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let frequencies = Array1::from(vec![20.0, 25.0, 31.5, /* ... */ 20000.0]);
// Example spinorama data - would typically come from measurements
let mut spinorama_data = HashMap::new();
spinorama_data.insert("On Axis".to_string(), Curve {
freq: frequencies.clone(),
spl: Array1::from(vec![-2.1, -1.8, -1.2, /* ... */ -10.5]),
phase: None,
});
spinorama_data.insert("Listening Window".to_string(), Curve {
freq: frequencies.clone(),
spl: Array1::from(vec![-2.0, -1.7, -1.1, /* ... */ -10.3]),
phase: None,
});
spinorama_data.insert("Sound Power".to_string(), Curve {
freq: frequencies.clone(),
spl: Array1::from(vec![-2.5, -2.0, -1.5, /* ... */ -11.0]),
phase: None,
});
spinorama_data.insert("Estimated In-Room Response".to_string(), Curve {
freq: frequencies.clone(),
spl: Array1::from(vec![-2.2, -1.9, -1.3, /* ... */ -10.7]),
phase: None,
});
let equalized_response = Array1::from(vec![0.5, 0.3, 0.1, /* ... */ -1.0]);
let metrics = compute_cea2034_metrics(
&frequencies,
&spinorama_data,
Some(&equalized_response)
).await?;
println!("Preference Score: {:.2}", metrics.pref_score);
Ok(())
}§Scoring Components
§Listening Window (LW) Score
- Flatness and smoothness of the listening window response
- Deviation from target curve
- Frequency-weighted penalties
§Predicted In-Room (PIR) Score
- Computed from listening window, early reflections, and sound power
- Represents typical in-room response
- Critical for overall preference rating
§Bass Extension
- Low-frequency response evaluation
- Extension and smoothness below 100 Hz
§Directivity Analysis
- Consistency of off-axis response
- Smoothness of directivity index
- Early reflection characteristics
§Usage
use autoeq_cea2034::{score, octave_intervals, compute_pir_from_lw_er_sp};
use ndarray::Array1;
// Example frequency and response data
let frequencies = Array1::from(vec![20.0, 25.0, 31.5, /* ... */ 20000.0]);
let on_axis = Array1::from(vec![-2.1, -1.8, -1.2, /* ... */ -10.5]);
let listening_window = Array1::from(vec![-2.0, -1.7, -1.1, /* ... */ -10.3]);
let sound_power = Array1::from(vec![-2.5, -2.0, -1.5, /* ... */ -11.0]);
let pir_response = Array1::from(vec![-2.2, -1.9, -1.3, /* ... */ -10.7]);
// Compute octave band intervals for analysis
let intervals = octave_intervals(2, &frequencies);
// Compute preference score for the frequency response
let preference_metrics = score(
&frequencies,
&intervals,
&on_axis,
&listening_window,
&sound_power,
&pir_response
);
println!("Preference Score: {:.2}", preference_metrics.pref_score);
// Compute PIR from CEA2034 measurements
let lw_curve = Array1::from(vec![-2.0, -1.7, -1.1, /* ... */ -10.3]);
let er_curve = Array1::from(vec![-2.3, -2.1, -1.6, /* ... */ -10.8]);
let sp_curve = Array1::from(vec![-2.5, -2.0, -1.5, /* ... */ -11.0]);
let computed_pir = compute_pir_from_lw_er_sp(&lw_curve, &er_curve, &sp_curve);§Integration
This crate is part of the AutoEQ ecosystem:
- Used by
autoeqfor optimization target scoring - Provides objective functions for
autoeq-deoptimization - Integrates with measurement data from Spinorama.org API
§Research Background
Based on published research:
- Olive, S. E., & Toole, F. E. (1989). “The detection of reflections in typical rooms”
- Olive, S. E. (2004). “A method for training listeners and selecting program material”
- Harman International patent applications on preference scoring algorithms
§License
GPL-3.0-or-later
Structs§
- Curve
- A struct to hold frequency and SPL data. Re-exported from the main autoeq crate for compatibility
- Directivity
Curve - A single directivity measurement at a specific angle
- Directivity
Data - Complete directivity data for horizontal and vertical planes
- Score
Metrics - Metrics computed for the CEA2034 preference score
Functions§
- cea2034
- Compute the CEA2034 spinorama from SPL data
- compute_
cea2034_ metrics - Compute CEA2034 metrics for speaker performance evaluation
- compute_
pir_ from_ lw_ er_ sp - Compute Predicted In-Room (PIR) response from LW, ER, and SP measurements
- lfx
- Compute the Low Frequency Extension (LFX) metric
- nbd
- Compute the Narrow Band Deviation (NBD) metric
- octave
- Generate octave band frequencies
- octave_
intervals - Compute octave band intervals for a given frequency array
- score
- Compute all CEA2034 metrics and preference score
- score_
peq - Compute CEA2034 metrics and preference score for a PEQ filter
- score_
peq_ approx - Compute approximate CEA2034 metrics and preference score for a PEQ filter
- sm
- Compute the Smoothness Metric (SM)