blob: 5f6c767a011b357553ed8c879f1f3d705ef1989f [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_MEDIA_AUDIO_LIB_ANALYSIS_GLITCH_H_
#define SRC_MEDIA_AUDIO_LIB_ANALYSIS_GLITCH_H_
#include <zircon/types.h>
#include <cmath>
#include <limits>
#include "src/media/audio/lib/format/audio_buffer.h"
namespace media::audio {
// A utility class that can be used to detect audio discontinuities ("glitches").
//
// SlopeChecker verifies a one-channel stream containing a full-scale, sine wave signal at known
// frequency. If the slope exceeds the expected maximum for that frequency, it returns false.
class SlopeChecker {
public:
SlopeChecker(int32_t samples_per_second, int32_t expected_frequency,
double expected_max_amplitude = 1.0, const std::string& tag = "")
: expected_max_amplitude_(expected_max_amplitude), tag_(tag.empty() ? tag + ": " : tag) {
double samples_per_period =
static_cast<double>(samples_per_second) / static_cast<double>(expected_frequency);
// Max delta for a sine of this freq is the diff between vals at -1/2 smpls and +1/2 smpls.
// Val at +1/2 smpls is sin(2 * pi * 1/2 / samples_per_period) * max_ampl, which equals:
// sin(pi/samples_per_period) * max_ampl.
// This is the change in Y, across X-axis span [0, 1/2]. Sinusoids are symmetric across the
// origin, so we multiply by 2.0 to get the change in Y-axis, across X-axis span [-1/2, +1/2].
max_expected_slope_ = std::sin(M_PI / samples_per_period) * expected_max_amplitude_ * 2.0;
}
bool Check(float sample, bool print = false) {
bool ret = true;
auto diff = sample - *prev_sample_;
if (std::abs(sample) > expected_max_amplitude_ ||
(prev_sample_ &&
std::abs(diff) > max_expected_slope_ + std::numeric_limits<float>::epsilon())) {
if (print) {
FX_LOGS(ERROR) << tag_ << "********** slope discontinuity detected. diff " << diff
<< "; max_expected " << max_expected_slope_ << " (prev " << *prev_sample_
<< ", new " << sample << ")";
}
ret = false;
}
prev_sample_ = sample;
return ret;
}
void Reset() { prev_sample_ = std::nullopt; }
private:
const double expected_max_amplitude_;
const std::string tag_;
double max_expected_slope_;
std::optional<float> prev_sample_;
};
} // namespace media::audio
#endif // SRC_MEDIA_AUDIO_LIB_ANALYSIS_GLITCH_H_