Audacity 3.2.0
StaffPadTimeAndPitchTest.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 StaffPadTimeAndPitchTest.cpp
7
8 Matthieu Hodgkinson
9
10**********************************************************************/
12#include "AudioContainer.h"
13#include "MockedPrefs.h"
16#include "WavFileIO.h"
17
18#include <catch2/catch.hpp>
19
20using namespace std::literals::string_literals;
21using namespace std::literals::chrono_literals;
22
23TEST_CASE("StaffPadTimeAndPitch")
24{
25 MockedPrefs mockedPrefs;
26 SECTION("Smoke test")
27 {
28 using TestParameter =
29 std::pair<std::string, std::optional<std::chrono::seconds>>;
30 const auto [filenameStem, upTo] = GENERATE(
31 TestParameter { "AudacitySpectral", std::nullopt },
32 TestParameter { "FifeAndDrumsStereo", 3s });
33 const auto inputPath = std::string(CMAKE_SOURCE_DIR) + "/tests/samples/" +
34 filenameStem + ".wav";
35
36 std::optional<std::string> outputDir;
37 // Uncomment this if you want to look at the output locally.
38 // outputDir = "C:/Users/saint/Downloads/StaffPadTimeAndPitchTestOut";
39
40 std::vector<std::vector<float>> input;
41 AudioFileInfo info;
42 REQUIRE(WavFileIO::Read(inputPath, input, info, upTo));
43 for (const auto pitchRatio : std::vector<std::pair<int, int>> {
44 { 4, 5 }, // major 3rd down
45 { 1, 1 }, // no shift
46 { 5, 4 }, // major 3rd up
47 })
48 {
49 const auto pr =
50 static_cast<double>(pitchRatio.first) / pitchRatio.second;
51 for (const auto timeRatio : std::vector<std::pair<int, int>> {
52 { 1, 2 },
53 { 1, 1 },
54 { 2, 1 },
55 })
56 {
57 const auto tr =
58 static_cast<double>(timeRatio.first) / timeRatio.second;
59 const auto numOutputFrames =
60 static_cast<size_t>(info.numFrames * tr);
61 AudioContainer container(numOutputFrames, info.numChannels);
63 params.timeRatio = tr;
64 params.pitchRatio = pr;
65 TimeAndPitchRealSource src(input);
67 info.sampleRate, info.numChannels, src, std::move(params));
68 constexpr size_t blockSize = 1234u;
69 auto offset = 0u;
70 while (offset < numOutputFrames)
71 {
72 std::vector<float*> offsetBuffers(info.numChannels);
73 for (auto i = 0u; i < info.numChannels; ++i)
74 offsetBuffers[i] = container.channelPointers[i] + offset;
75 const auto numToRead =
76 std::min(numOutputFrames - offset, blockSize);
77 sut.GetSamples(offsetBuffers.data(), numToRead);
78 offset += numToRead;
79 }
80
81 if (outputDir)
82 {
83 const auto filename = filenameStem + "_t"s +
84 std::to_string(timeRatio.first) + "-" +
85 std::to_string(timeRatio.second) + "_p" +
86 std::to_string(pitchRatio.first) + "-" +
87 std::to_string(pitchRatio.second) + ".wav";
88 const auto outputPath = *outputDir + "/" + filename;
89 REQUIRE(WavFileIO::Write(
90 outputPath, container.channelVectors, info.sampleRate));
91 }
92 }
93 }
94 }
95
96 SECTION("Not specifying time or pitch or ratio yields bit-exact results")
97 {
98 // Although this is no hard-proof, a bit-exact output is a strong evidence
99 // that no processing happens if no time or pitch ratio is specified,
100 // which is what we want.
101
102 const auto inputPath =
103 std::string(CMAKE_SOURCE_DIR) + "/tests/samples/AudacitySpectral.wav";
104 std::vector<std::vector<float>> input;
105 AudioFileInfo info;
106 REQUIRE(WavFileIO::Read(inputPath, input, info));
107 AudioContainer container(info.numFrames, info.numChannels);
109 TimeAndPitchRealSource src(input);
111 info.sampleRate, info.numChannels, src, std::move(params));
112 sut.GetSamples(container.Get(), info.numFrames);
113 // Calling REQUIRE(container.channelVectors == input) directly for large
114 // vectors might be rough on stdout in case of an error printout ...
115 const auto outputEqualsInput = container.channelVectors == input;
116 REQUIRE(outputEqualsInput);
117 }
118
119 SECTION("Extreme stretch ratios")
120 {
121 constexpr auto originalDuration = 60.; // 1 minute
122 constexpr auto targetDuration = 1. / 44100; // 1 sample
123 constexpr auto superSmallRatio = targetDuration / originalDuration;
124 constexpr auto requestedNumSamples =
125 1; // ... a single sample, but which is the condensed form of one
126 // minute of audio :D
127 constexpr auto numChannels = 1;
129 AudioContainer container(requestedNumSamples, numChannels);
131 params.timeRatio = superSmallRatio;
132 StaffPadTimeAndPitch sut(44100, numChannels, src, std::move(params));
133 sut.GetSamples(
134 container.Get(),
135 requestedNumSamples); // This is just not supposed to hang.
136 }
137}
int min(int a, int b)
EffectDistortionSettings params
for(int ii=0, nn=names.size();ii< nn;++ii)
TEST_CASE("StaffPadTimeAndPitch")
void GetSamples(float *const *, size_t) override
std::vector< float * > channelPointers
std::vector< std::vector< float > > channelVectors
float *const * Get() const