Calibration System - Quick Start Guide¶
Installation¶
Using CMake¶
cd 3 - COMPONENTS/05_MODULES/05_12_CALIBRATION_SYSTEM
mkdir build && cd build
cmake .. -DBUILD_CALIBRATION_EXAMPLES=ON
cmake --build .
Using in Your Project¶
find_package(AudioLabCalibration REQUIRED)
target_link_libraries(your_target
PRIVATE
AudioLab::calibration_framework
AudioLab::frequency_calibration
)
Basic Usage¶
Step 1: Make Your Component Calibratable¶
Implement the ICalibratableComponent interface:
#include <CalibrationTarget.hpp>
class MyFilter : public AudioLab::Calibration::ICalibratableComponent {
public:
std::string getComponentId() const override {
return "MyFilter";
}
void process(const float* input, float* output, size_t numSamples) override {
// Your DSP processing here
}
void setParameter(const std::string& name, float value) override {
if (name == "cutoff") {
cutoff_ = value;
updateCoefficients();
}
}
float getParameter(const std::string& name) const override {
if (name == "cutoff") return cutoff_;
return 0.0f;
}
std::vector<std::string> getParameterNames() const override {
return {"cutoff", "resonance"};
}
void getParameterRange(const std::string& name,
float& minVal, float& maxVal) const override {
if (name == "cutoff") {
minVal = 20.0f;
maxVal = 20000.0f;
} else if (name == "resonance") {
minVal = 0.1f;
maxVal = 10.0f;
}
}
void reset() override {
// Reset internal state
}
double getSampleRate() const override { return sampleRate_; }
void setSampleRate(double sr) override { sampleRate_ = sr; }
private:
double sampleRate_ = 48000.0;
float cutoff_ = 1000.0f;
float resonance_ = 0.707f;
};
Step 2: Create Calibration Target¶
using namespace AudioLab::Calibration;
// Create your component
auto filter = std::make_shared<MyFilter>();
// Create calibration target
CalibrationTarget target("MyFilter", filter);
Step 3: Define Specification¶
// Define what you want to calibrate
CalibrationSpecification spec(TargetType::FREQUENCY_RESPONSE);
// Measurement config
spec.measurement.sampleRate = 48000.0;
spec.measurement.blockSize = 512;
spec.measurement.signalLevel = -18.0f; // dBFS
// Target behavior
spec.idealResponse = {
// Your target frequency response
// e.g., flat to 1kHz, then rolloff
};
spec.tolerances = {
// Acceptable error per frequency
// e.g., ±0.5dB
};
// Optimization settings
spec.errorThreshold = 0.1f; // Stop when error < 0.1dB
spec.maxIterations = 100;
// Add to target
target.addSpecification(spec);
Step 4: Run Calibration¶
#include <FilterCalibrator.hpp>
// Create calibrator
FilterCalibrator calibrator;
// Optional: Set progress callback
calibrator.setProgressCallback(
[](CalibrationStage stage, float progress, const std::string& message) {
std::cout << "[" << (int)(progress * 100) << "%] " << message << "\n";
}
);
// Run calibration
CalibrationResult result = calibrator.calibrate(target);
// Check results
if (result.success) {
std::cout << "Calibration successful!\n";
std::cout << "Final error: " << result.finalError << " dB\n";
std::cout << "Iterations: " << result.iterationsUsed << "\n";
// Parameters are already applied to component
// Optionally save them:
for (const auto& [name, value] : result.parameters) {
std::cout << name << ": " << value << "\n";
}
} else {
std::cerr << "Calibration failed: " << result.notes << "\n";
}
Common Use Cases¶
Use Case 1: Calibrate Filter Cutoff¶
Goal: Set filter cutoff to exactly 1kHz (-3dB point)
auto filter = std::make_shared<MyFilter>();
CalibrationTarget target("Filter_1kHz", filter);
CalibrationSpecification spec(TargetType::FREQUENCY_RESPONSE);
spec.measurement.sampleRate = 48000.0;
// Target: -3dB at 1kHz (one-pole lowpass characteristic)
std::vector<float> testFreqs = {500, 750, 1000, 1500, 2000};
spec.idealResponse = {-0.5, -1.5, -3.0, -6.0, -9.0}; // Expected magnitudes
spec.tolerances = {0.5, 0.5, 0.3, 0.5, 0.5}; // Tighter at cutoff
target.addSpecification(spec);
FilterCalibrator calibrator;
auto result = calibrator.calibrate(target);
if (result.success) {
std::cout << "Calibrated cutoff: "
<< result.parameters["cutoff"] << " Hz\n";
}
Use Case 2: Match Hardware EQ Curve¶
Goal: Match the frequency response of a hardware EQ
// 1. Measure reference hardware
auto referenceResponse = measureHardwareEQ();
// 2. Create software component
auto softwareEQ = std::make_shared<MySoftwareEQ>();
CalibrationTarget target("EQ_Clone", softwareEQ);
// 3. Set reference as target
CalibrationSpecification spec(TargetType::FREQUENCY_RESPONSE);
spec.idealResponse = referenceResponse.magnitudes;
spec.tolerances.resize(spec.idealResponse.size(), 0.5f); // ±0.5dB
target.addSpecification(spec);
// 4. Calibrate
FilterCalibrator calibrator;
auto result = calibrator.calibrate(target);
// 5. Verify with null test
auto nullError = performNullTest(softwareEQ, referenceResponse);
std::cout << "Null test error: " << nullError << " dB\n";
Use Case 3: Multi-Objective Calibration¶
Goal: Optimize both frequency response AND phase response
CalibrationTarget target("LinearPhaseEQ", eq);
// Objective 1: Frequency response
CalibrationSpecification freqSpec(TargetType::FREQUENCY_RESPONSE);
freqSpec.idealResponse = flatResponse;
freqSpec.importance = 1.0f; // High priority
target.addSpecification(freqSpec);
// Objective 2: Phase response
CalibrationSpecification phaseSpec(TargetType::PHASE_RESPONSE);
phaseSpec.idealResponse = linearPhase;
phaseSpec.importance = 0.7f; // Medium priority
target.addSpecification(phaseSpec);
// Use multi-objective optimizer
MultiObjectiveOptimizer optimizer;
auto result = optimizer.optimize(target, evaluationFunction);
// Result is Pareto-optimal compromise
Use Case 4: Batch Calibration¶
Calibrate multiple components:
std::vector<CalibrationResult> results;
for (auto& component : components) {
CalibrationTarget target(component->getId(), component);
// Add specifications
target.addSpecification(spec);
// Calibrate
FilterCalibrator calibrator;
auto result = calibrator.calibrate(target);
results.push_back(result);
// Report progress
std::cout << component->getId() << ": "
<< (result.success ? "SUCCESS" : "FAILED")
<< " (error: " << result.finalError << " dB)\n";
}
// Summary
int successCount = std::count_if(results.begin(), results.end(),
[](const auto& r) { return r.success; });
std::cout << "\nCalibrated " << successCount << " / " << results.size()
<< " components\n";
Configuration¶
Calibrator Configuration¶
FilterCalibrator::Config config;
// Frequency range
config.startFreq = 20.0f;
config.endFreq = 20000.0f;
config.numFrequencies = 100;
// Measurement method
config.useLogSweep = true; // vs discrete tones
config.sweepDuration = 1.0f; // seconds
config.averagingCount = 5; // measurements to average
// Optimization
config.learningRate = 0.01f;
config.momentum = 0.9f;
config.useAdaptiveLearning = true;
calibrator.setConfig(config);
Measurement Configuration¶
MeasurementConfig measConfig;
measConfig.sampleRate = 48000.0;
measConfig.blockSize = 512;
measConfig.signalLevel = -18.0f; // dBFS
measConfig.averagingCount = 10;
measConfig.useOversampling = false;
Troubleshooting¶
Calibration Not Converging¶
Problem: result.success = false, max iterations reached
Solutions:
1. Reduce errorThreshold (less strict)
2. Increase maxIterations
3. Adjust learning rate: try 0.001f to 0.1f
4. Check parameter ranges are reasonable
5. Verify component works correctly
// Debug: Monitor error over iterations
calibrator.setProgressCallback(
[](CalibrationStage stage, float progress, const std::string& message) {
if (stage == CalibrationStage::OPTIMIZATION) {
// Log error progression
logFile << progress << "," << message << "\n";
}
}
);
High Error Despite Convergence¶
Problem: result.success = true but error still high
Solutions:
1. Component may not be capable of achieving target
2. Target specification may be unrealistic
3. Measurement noise too high - increase averagingCount
4. Check if component is stable/reproducible
// Test reproducibility
auto r1 = measure(target);
auto r2 = measure(target);
float diff = calculateDifference(r1, r2);
if (diff > 0.5f) {
std::cout << "Warning: Component is not reproducible\n";
}
Calibration Too Slow¶
Problem: Takes too long
Solutions:
1. Reduce numFrequencies
2. Reduce averagingCount
3. Use log sweep instead of discrete tones
4. Reduce maxIterations
5. Use faster optimizer (gradient descent vs GA)
// Fast configuration
FilterCalibrator::Config fastConfig;
fastConfig.numFrequencies = 50; // vs 100
fastConfig.averagingCount = 3; // vs 10
fastConfig.useLogSweep = true; // Faster measurement
Examples¶
See examples/ directory for complete examples:
simple_filter_calibration.cpp- Basic filter calibrationeq_curve_matching.cpp- Match hardware EQ (TODO)multi_objective_example.cpp- Multi-objective optimization (TODO)batch_calibration.cpp- Calibrate multiple components (TODO)
Next Steps¶
- Read ARCHITECTURE.md for detailed design
- Explore calibration types in subsystem directories
- Implement custom calibrators for your components
- Contribute calibration profiles to the community
Support¶
For issues, questions, or contributions:
- See main project README
- Check documentation in 05_12_documentation/
- Review tests in 05_12_test_integration/
Happy Calibrating! 🎯