MeasurementCalculator.java
package org.hammer.audio.analysis;
import org.hammer.audio.core.AudioBlock;
/** Computes robust aggregate UI measurements for the latest block/spectrum. */
public final class MeasurementCalculator {
private static final double CLIPPING_THRESHOLD = 0.999;
private static final double EPSILON = 1e-12;
/**
* Calculate current measurements.
*
* @param block latest audio block (may be {@code null})
* @param spectrum latest spectrum (may be {@code null})
* @return immutable measurement snapshot
*/
public MeasurementSnapshot calculate(AudioBlock block, SpectrumSnapshot spectrum) {
double dominantFrequency = dominantFrequencyHz(spectrum);
if (block == null || block.channels() <= 0 || block.frames() <= 0) {
return new MeasurementSnapshot(0.0, 0.0, dominantFrequency, Double.NaN, false, false);
}
double sumSquares = 0.0;
double peak = 0.0;
long sampleCount = 0L;
boolean clipping = false;
for (int channel = 0; channel < block.channels(); channel++) {
float[] samples = block.channelView(channel);
for (int i = 0; i < block.frames(); i++) {
double sample = samples[i];
double absSample = Math.abs(sample);
sumSquares += sample * sample;
peak = Math.max(peak, absSample);
clipping |= absSample >= CLIPPING_THRESHOLD;
sampleCount++;
}
}
double rms = sampleCount == 0 ? 0.0 : Math.sqrt(sumSquares / sampleCount);
double stereoCorrelation = stereoCorrelation(block);
boolean stereoCorrelationAvailable = !Double.isNaN(stereoCorrelation);
return new MeasurementSnapshot(
rms, peak, dominantFrequency, stereoCorrelation, stereoCorrelationAvailable, clipping);
}
private static double dominantFrequencyHz(SpectrumSnapshot spectrum) {
if (spectrum == null || spectrum.binCount() <= 1) {
return Double.NaN;
}
int peakBin = -1;
float peakMagnitude = 0f;
for (int bin = 1; bin < spectrum.binCount(); bin++) {
float magnitude = spectrum.magnitude(bin);
if (magnitude > peakMagnitude) {
peakMagnitude = magnitude;
peakBin = bin;
}
}
return peakBin < 0 ? Double.NaN : spectrum.frequencyOfBin(peakBin);
}
private static double stereoCorrelation(AudioBlock block) {
if (block.channels() < 2 || block.frames() <= 0) {
return Double.NaN;
}
float[] left = block.channelView(0);
float[] right = block.channelView(1);
int frames = block.frames();
double meanLeft = 0.0;
double meanRight = 0.0;
for (int i = 0; i < frames; i++) {
meanLeft += left[i];
meanRight += right[i];
}
meanLeft /= frames;
meanRight /= frames;
double covariance = 0.0;
double varianceLeft = 0.0;
double varianceRight = 0.0;
for (int i = 0; i < frames; i++) {
double leftCentered = left[i] - meanLeft;
double rightCentered = right[i] - meanRight;
covariance += leftCentered * rightCentered;
varianceLeft += leftCentered * leftCentered;
varianceRight += rightCentered * rightCentered;
}
double denominator = Math.sqrt(varianceLeft * varianceRight);
if (denominator <= EPSILON) {
return Double.NaN;
}
double correlation = covariance / denominator;
if (correlation > 1.0) {
return 1.0;
}
if (correlation < -1.0) {
return -1.0;
}
return correlation;
}
}