HART  0.2.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_zcr.hpp
Go to the documentation of this file.
1#pragma once
2
5#include "metrics/hart_metric_query.hpp"
6#include "metrics/hart_metrics_common.hpp" // ChannelSubsets
7#include "hart_slice.hpp"
8#include "hart_units.hpp" // Unit
9#include "hart_utils.hpp" // floatsEqual(), nan()
10
11namespace hart
12{
13
14/// @brief Calculates zero-crossing rate (ZCR) of a signal
15/// @details Useful to estimate frequency of stationary monophonic signals.
16/// Supports `Unit::native` and `Unit::Hz` units, which both result in the same value.
17/// @tparam SampleType type of audio buffer data, typically float or double
18/// @param buffer Audio buffer to calculate ZCR at
19/// @return Chainable `MetricQuery` object, which calculates RMS as linear ratio
20/// or decibels
21/// @ingroup Metrics
22template <typename SampleType>
23MetricQuery<double> zcr (const AudioBuffer<SampleType>& buffer)
24{
25 if (! buffer.hasSampleRate())
26 HART_THROW_OR_RETURN (hart::SampleRateError, "Audio buffer must have sample rate metadata", {});
27
28 if (floatsEqual (buffer.getSampleRateHz(), 0.0))
29 HART_THROW_OR_RETURN (hart::SampleRateError, "Audio buffer's sample rate should not be zero", {});
30
31 typename MetricQuery<double>::SingleChannelMetricEvaluator evaluator =
32 [&buffer]
33 (size_t channel, Slice slice, Unit requestedUnit)
34 -> double
35 {
36 hassert (channel < buffer.getNumChannels());
37
38 if (requestedUnit != Unit::native && requestedUnit != Unit::Hz)
39 HART_THROW_OR_RETURN (hart::UnitError, "Unsupported unit", hart::nan<double>());
40
41 if (slice.isEmpty())
42 return hart::nan<double>();
43
44 const auto sliceFrameIndices = buffer.getFrameIndices (slice);
45 const size_t sliceStart = sliceFrameIndices.first;
46 const size_t sliceStop = sliceFrameIndices.second;
47 const size_t numFrames = sliceStop - sliceStart;
48 hassert (numFrames != 0);
49 hassert (sliceStart < sliceStop);
50 hassert (sliceStop <= buffer.getNumFrames());
51
52 if (numFrames <= 2)
53 return hart::nan<double>();
54
55 const SampleType* channelData = buffer[channel];
56 size_t zeroCrossings = 0;
57 double previous = channelData[sliceStart];
58
59 for (size_t frame = sliceStart + 1; frame < sliceStop; ++frame)
60 {
61 const double current = channelData[frame];
62 zeroCrossings += (previous >= 0.0) ^ (current >= 0.0);
63 previous = current;
64 }
65
66 const double sliceDurationSeconds = static_cast<double> (numFrames) / buffer.getSampleRateHz();
67 return static_cast<double> (zeroCrossings) / sliceDurationSeconds;
68 };
69
70 const size_t numChannels = buffer.getNumChannels();
71 return MetricQuery<double> (
72 std::move (evaluator),
73 numChannels,
75 );
76}
77
78} // namespace hart
Manages the metrics calculations.
MetricQuery(SingleChannelMetricEvaluator evaluator, size_t totalNumChannels, std::vector< size_t > &&defaultChannelsToProcess)
Create a metric query object for a metric that operates on one channel at a time.
Thrown when sample rate is mismatched or invalid.
Thrown when some metric is requested to return a value in an unsupported unit.
#define hassert(condition)
Triggers a HartAssertException if the condition is false
#define HART_THROW_OR_RETURN(ExceptionType, message, returnValue)
Throws an exception if HART_DO_NOT_THROW_EXCEPTIONS is set, prints a message and returns a specified ...
MetricQuery< double > zcr(const AudioBuffer< SampleType > &buffer)
Calculates zero-crossing rate (ZCR) of a signal.
Definition hart_zcr.hpp:23
FloatType nan()
Returns a quiet NaN value for the given floating-point type.
Unit
Represents a physical unit.
@ native
Default (native) unit of whatever returns some value.
@ Hz
Hertz.
Helpers to generate common default channel subsets.
static std::vector< size_t > allChannels(size_t numChannels)
Represents a slice of analysis data.
bool isEmpty() const