| Crates.io | autoeq-cea2034 |
| lib.rs | autoeq-cea2034 |
| version | 0.3.0 |
| created_at | 2025-10-05 10:40:52.108953+00 |
| updated_at | 2026-01-02 09:32:56.282604+00 |
| description | Compute the CEA2034 properties of a loudspeaker also called a Spinorama |
| homepage | |
| repository | https://github.com/pierreaubert/autoeq |
| max_upload_size | |
| id | 1868882 |
| size | 49,869 |
This crate implements CEA2034-based preference scoring algorithms for loudspeaker measurements, based on research by Harman, Olive, and others.
The CEA2034 standard defines a set of measurements for evaluating loudspeaker performance:

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(())
}
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);
This crate is part of the AutoEQ ecosystem:
autoeq for optimization target scoringautoeq-de optimizationBased on published research:
GPL-3.0-or-later