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
21template <typename SampleType>
22MetricQuery<double> zcr (const AudioBuffer<SampleType>& buffer)
23{
24 if (! buffer.hasSampleRate())
25 HART_THROW_OR_RETURN (hart::SampleRateError, "Audio buffer must have sample rate metadata", {});
26
27 if (floatsEqual (buffer.getSampleRateHz(), 0.0))
28 HART_THROW_OR_RETURN (hart::SampleRateError, "Audio buffer's sample rate should not be zero", {});
29
30 typename MetricQuery<double>::SingleChannelMetricEvaluator evaluator =
31 [&buffer]
32 (size_t channel, Slice slice, Unit requestedUnit)
33 -> double
34 {
35 hassert (channel < buffer.getNumChannels());
36
37 if (requestedUnit != Unit::native && requestedUnit != Unit::Hz)
38 HART_THROW_OR_RETURN (hart::UnitError, "Unsupported unit", hart::nan<double>());
39
40 if (slice.isEmpty())
41 return hart::nan<double>();
42
43 const auto sliceFrameIndices = buffer.getFrameIndices (slice);
44 const size_t sliceStart = sliceFrameIndices.first;
45 const size_t sliceStop = sliceFrameIndices.second;
46 const size_t numFrames = sliceStop - sliceStart;
47 hassert (numFrames != 0);
48 hassert (sliceStart < sliceStop);
49 hassert (sliceStop <= buffer.getNumFrames());
50
51 if (numFrames <= 2)
52 return hart::nan<double>();
53
54 const SampleType* channelData = buffer[channel];
55 size_t zeroCrossings = 0;
56 double previous = channelData[sliceStart];
57
58 for (size_t frame = sliceStart + 1; frame < sliceStop; ++frame)
59 {
60 const double current = channelData[frame];
61 zeroCrossings += (previous >= 0.0) ^ (current >= 0.0);
62 previous = current;
63 }
64
65 const double sliceDurationSeconds = static_cast<double> (numFrames) / buffer.getSampleRateHz();
66 return static_cast<double> (zeroCrossings) / sliceDurationSeconds;
67 };
68
69 const size_t numChannels = buffer.getNumChannels();
70 return MetricQuery<double> (
71 std::move (evaluator),
72 numChannels,
74 );
75}
76
77} // namespace hart
Container for audio data.
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.
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 ...
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.
MetricQuery< double > zcr(const AudioBuffer< SampleType > &buffer)
Calculates zero-crossing rate (ZCR) of a signal.
Definition hart_zcr.hpp:22
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