tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_TestUtilities.h
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11#pragma once
12
13namespace tracktion { inline namespace engine
14{
15
16#ifndef DOXYGEN
17namespace test_utilities
18{
19 //==============================================================================
20 inline std::optional<juce::AudioBuffer<float>> loadFileInToBuffer (juce::AudioFormatManager& formatManager, const juce::File& f)
21 {
22 if (auto reader = std::unique_ptr<juce::AudioFormatReader> (formatManager.createReaderFor (f)))
23 {
24 juce::AudioBuffer<float> destBuffer (static_cast<int> (reader->numChannels), static_cast<int> (reader->lengthInSamples));
25
26 if (! reader->read (&destBuffer,
27 0, destBuffer.getNumSamples(), 0,
28 true, true))
29 return {};
30
31 return destBuffer;
32 }
33
34 return {};
35 }
36
37 inline std::optional<juce::AudioBuffer<float>> loadFileInToBuffer (Engine& e, const juce::File& f)
38 {
39 return loadFileInToBuffer (e.getAudioFileFormatManager().readFormatManager, f);
40 }
41
42 //==============================================================================
43 void expectInt (juce::UnitTest& ut, std::unsigned_integral auto int1, std::unsigned_integral auto int2)
44 {
45 ut.expectEquals (static_cast<std::uint64_t> (int1), static_cast<std::uint64_t> (int2));
46 }
47
48 /* Only valid for values less than std::int64_t::max. */
49 void expectInt (juce::UnitTest& ut, std::integral auto int1, std::integral auto int2)
50 {
53 ut.expectEquals (static_cast<std::int64_t> (int1), static_cast<std::int64_t> (int2));
54 }
55
56 //==============================================================================
57 inline Renderer::Statistics logStats (juce::UnitTest& ut, Renderer::Statistics stats)
58 {
59 ut.logMessage ("Stats: peak " + juce::String (stats.peak) + ", avg " + juce::String (stats.average)
60 + ", duration " + juce::String (stats.audioDuration));
61 return stats;
62 }
63
64 inline void expectPeak (juce::UnitTest& ut, Edit& edit, TimeRange tr, juce::Array<Track*> tracks, float expectedPeak)
65 {
66 auto blockSize = edit.engine.getDeviceManager().getBlockSize();
67 auto stats = logStats (ut, Renderer::measureStatistics ("Tests", edit, tr, toBitSet (tracks), blockSize));
68 ut.expect (juce::isWithin (stats.peak, expectedPeak, 0.001f), juce::String ("Expected peak: ") + juce::String (expectedPeak, 4));
69 }
70
71 //==============================================================================
72 inline std::unique_ptr<juce::TemporaryFile> renderToTempFileAndLogPath (Edit& edit)
73 {
75 Renderer::renderToFile (edit, tf->getFile(), false);
76 DBG(tf->getFile().getFullPathName());
77 return tf;
78 }
79
80 struct BufferAndSampleRate
81 {
83 double sampleRate = 0;
85 };
86
87 inline BufferAndSampleRate renderToAudioBuffer (Edit& edit)
88 {
90 Renderer::renderToFile (edit, tf->getFile(), false);
91
93 manager.registerFormat (new juce::WavAudioFormat(), true);
94
95 if (auto reader = std::unique_ptr<juce::AudioFormatReader> (manager.createReaderFor (tf->getFile())))
96 {
97 juce::AudioBuffer<float> buffer (static_cast<int> (reader->numChannels),
98 static_cast<int> (reader->lengthInSamples));
99
100 if (reader->read (&buffer, 0, buffer.getNumSamples(),
101 0, true, buffer.getNumChannels() > 1))
102 {
103 return { buffer, reader->sampleRate, std::move (tf) };
104 }
105 }
106
107 return {};
108 }
109
110 inline void expectPeak (juce::UnitTest& ut, const BufferAndSampleRate& data, TimeRange tr, float expectedPeak)
111 {
112 const auto sampleRange = toSamples (tr, data.sampleRate);
113 const auto peak = data.buffer.getMagnitude (static_cast<int> (sampleRange.getStart()),
114 static_cast<int> (sampleRange.getLength()));
115 ut.expect (juce::isWithin (peak, expectedPeak, 0.001f),
116 juce::String ("Expected peak: ") + juce::String (expectedPeak, 4) + ", actual peak: " + juce::String (peak, 4));
117 }
118
119 inline void expectRMS (juce::UnitTest& ut, const BufferAndSampleRate& data, TimeRange tr, int channel, float expectedRMS)
120 {
121 const auto sampleRange = toSamples (tr, data.sampleRate);
122 ut.expectWithinAbsoluteError (data.buffer.getRMSLevel (channel,
123 static_cast<int> (sampleRange.getStart()),
124 static_cast<int> (sampleRange.getLength())),
125 expectedRMS, 0.01f);
126 }
127
128 //==============================================================================
129 inline std::unique_ptr<Edit> createTestEdit (Engine& engine, int numAudioTracks = 1, Edit::EditRole role = Edit::EditRole::forRendering)
130 {
131 // Make tempo 60bpm and 0dB master vol for easy calculations
132
133 auto edit = Edit::createSingleTrackEdit (engine, role);
134
135 edit->ensureNumberOfAudioTracks (numAudioTracks);
136 edit->tempoSequence.getTempo (0)->setBpm (60.0);
137 edit->getMasterVolumePlugin()->setVolumeDb (0.0);
138
139 return edit;
140 }
141}
142#endif
143
144}} // namespace tracktion { inline namespace engine
assert
AudioFormatReader * createReaderFor(const File &audioFile)
void registerFormat(AudioFormat *newFormat, bool makeThisTheDefaultFormat)
void logMessage(const String &message)
void expectEquals(ValueType actual, ValueType expected, String failureMessage=String())
void expect(bool testResult, const String &failureMessage=String())
void expectWithinAbsoluteError(ValueType actual, ValueType expected, ValueType maxAbsoluteError, String failureMessage=String())
@ forRendering
Creates an Edit for rendering, not output device playback.
T data(T... args)
T is_pointer_v
#define DBG(textToWrite)
bool isWithin(Type a, Type b, Type tolerance) noexcept
juce::BigInteger toBitSet(const juce::Array< Track * > &tracks)
Returns the set of tracks as a BigInteger with each bit corresponding to the array of all tracks in a...
constexpr int64_t toSamples(TimePosition, double sampleRate)
Converts a TimePosition to a number of samples.