HART  0.1.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_equalsto.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath> // abs()
4#include <iomanip>
5#include <sstream>
6
7#include "matchers/hart_matcher.hpp"
9#include "signals/hart_signal.hpp"
10#include "hart_utils.hpp"
11
12namespace hart
13{
14
15/// @brief Checks whether the audio is identical to some signal
16/// @details Holds an internal Signal instance, and gets it to generate reference audio as
17/// the matcher receives new audio blocks through match(). Reports mismatch if even
18/// one of the frames in the audio does not match the reference signal within tolerance
19/// specified in @c epsilon during its instantiation.
20/// @ingroup Matchers
21template<typename SampleType>
23 public Matcher<SampleType>
24{
25public:
26 /// @brief Creates a matcher for a specific signal
27 /// details The reference signal can be something simple like a @ref SineWave, or more
28 /// complex signal with DSP effects chain and automation envelopes.
29 /// @note Tip: To compare audio to a pre-recorded wav file, you can use @ref WavFile.
30 /// @param referenceSignal Signal to compare the incoming audio against
31 /// @param toleranceLinear Absolute tolerance for comparing frames, in linear domain (not decibels)
32 template <typename SignalType>
33 EqualsTo (const SignalType& referenceSignal, double toleranceLinear = (SampleType) 1e-5):
34 m_referenceSignal (referenceSignal.copy()),
35 m_toleranceLinear ((SampleType) toleranceLinear)
36 {
37 using DecayedType = typename std::decay<SignalType>::type;
38 static_assert (
39 std::is_base_of<Signal<SampleType>, DecayedType>::value,
40 "SignalType must be a hart::Signal subclass"
41 );
42 }
43
44 EqualsTo (EqualsTo&& other) noexcept:
45 m_referenceSignal (std::move (other.m_referenceSignal)),
46 m_toleranceLinear (other.m_toleranceLinear)
47 {
48 }
49
50 EqualsTo (const EqualsTo& other):
51 m_referenceSignal (other.m_referenceSignal != nullptr ? other.m_referenceSignal->copy() : nullptr),
52 m_toleranceLinear (other.m_toleranceLinear)
53 {
54 }
55
56 void prepare (double sampleRateHz, size_t numChannels, size_t maxBlockSizeFrames) override
57 {
58 m_referenceSignal->prepareWithDSPChain (sampleRateHz, numChannels, maxBlockSizeFrames);
59 }
60
61 bool match (const AudioBuffer<SampleType>& observedAudio) override
62 {
63 auto referenceAudio = AudioBuffer<SampleType>::emptyLike (observedAudio);
64 m_referenceSignal->renderNextBlockWithDSPChain (referenceAudio);
65
66 for (size_t channel = 0; channel < referenceAudio.getNumChannels(); ++channel)
67 {
68 for (size_t frame = 0; frame < referenceAudio.getNumFrames(); ++frame)
69 {
70 if (notEqual (observedAudio[channel][frame], referenceAudio[channel][frame]))
71 {
72 m_failedFrame = frame;
73 m_failedChannel = (int) channel;
74 m_failedObservedValue = observedAudio[channel][frame];
75 m_failedExpectedValue = referenceAudio[channel][frame];
76 return false;
77 }
78 }
79 }
80
81 return true;
82 }
83
84 bool canOperatePerBlock() override
85 {
86 return true;
87 }
88
89 void reset() override
90 {
91 m_referenceSignal->resetWithDSPChain();
92 }
93
95 {
96 const SampleType m_differenceLinear = std::abs (m_failedExpectedValue - m_failedObservedValue);
97 std::stringstream stream;
98 stream << linPrecision << "Expected sample value: " << m_failedExpectedValue
99 << dbPrecision << " (" << ratioToDecibels (m_failedExpectedValue) << " dB)"
100 << linPrecision << ", difference: " << m_differenceLinear
101 << dbPrecision << " (" << ratioToDecibels (m_differenceLinear) << " dB)";
102
103 MatcherFailureDetails details;
104 details.frame = m_failedFrame;
105 details.channel = m_failedChannel;
106 details.description = std::move (stream.str());
107 return details;
108 }
109
110 void represent (std::ostream& stream) const override
111 {
112 stream << "EqualsTo (" << *m_referenceSignal
113 << linPrecision << ", " << m_toleranceLinear << ')';
114 }
115
117
118private:
119 std::unique_ptr<Signal<SampleType>> m_referenceSignal;
120 const SampleType m_toleranceLinear;
121
122 size_t m_failedFrame = 0;
123 size_t m_failedChannel = 0;
124 SampleType m_failedObservedValue = (SampleType) 0;
125 SampleType m_failedExpectedValue = (SampleType) 0;
126
127 inline bool notEqual (SampleType x, SampleType y)
128 {
129 return std::abs (x - y) > m_toleranceLinear;
130 }
131};
132
134
135} // namespace hart
Checks whether the audio is identical to some signal.
bool match(const AudioBuffer< SampleType > &observedAudio) override
Tells the host if the piece of audio satisfies Matcher's condition or not.
void represent(std::ostream &stream) const override
Makes a text representation of this Macther for test failure outputs.
EqualsTo(EqualsTo &&other) noexcept
bool canOperatePerBlock() override
Tells the host is if can operate on a block-by-block basis.
virtual MatcherFailureDetails getFailureDetails() const override
Returns a description of why the match has failed.
EqualsTo(const EqualsTo &other)
void reset() override
Resets the matcher to its initial state.
EqualsTo(const SignalType &referenceSignal, double toleranceLinear=(SampleType) 1e-5)
Creates a matcher for a specific signal details The reference signal can be something simple like a S...
void prepare(double sampleRateHz, size_t numChannels, size_t maxBlockSizeFrames) override
Prepare for processing It is guaranteed that all subsequent process() calls will be in line with the ...
Base for audio matchers.
Base class for signals.
#define HART_MATCHER_DEFINE_COPY_AND_MOVE(ClassName)
Defines hart::Matcher::copy() and hart::Matcher::move() methods.
std::ostream & linPrecision(std::ostream &stream)
Sets number of decimal places for linear (sample) values.
std::ostream & dbPrecision(std::ostream &stream)
Sets number of decimal places for values in decibels.
#define HART_MATCHER_DECLARE_ALIASES_FOR(ClassName)
size_t channel
Index of channel at which the failure was detected.
std::string description
Readable description of why the match has failed.
size_t frame
Index of frame at which the match has failed.