cannam@2
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@2
|
2
|
cannam@2
|
3 /*
|
cannam@2
|
4 Vamp Plugin Tester
|
cannam@2
|
5 Chris Cannam, cannam@all-day-breakfast.com
|
cannam@2
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@42
|
7 Copyright 2009-2014 QMUL.
|
cannam@2
|
8
|
cannam@2
|
9 This program loads a Vamp plugin and tests its susceptibility to a
|
cannam@2
|
10 number of common pitfalls, including handling of extremes of input
|
cannam@2
|
11 data. If you can think of any additional useful tests that are
|
cannam@2
|
12 easily added, please send them to me.
|
cannam@2
|
13
|
cannam@2
|
14 Permission is hereby granted, free of charge, to any person
|
cannam@2
|
15 obtaining a copy of this software and associated documentation
|
cannam@2
|
16 files (the "Software"), to deal in the Software without
|
cannam@2
|
17 restriction, including without limitation the rights to use, copy,
|
cannam@2
|
18 modify, merge, publish, distribute, sublicense, and/or sell copies
|
cannam@2
|
19 of the Software, and to permit persons to whom the Software is
|
cannam@2
|
20 furnished to do so, subject to the following conditions:
|
cannam@2
|
21
|
cannam@2
|
22 The above copyright notice and this permission notice shall be
|
cannam@2
|
23 included in all copies or substantial portions of the Software.
|
cannam@2
|
24
|
cannam@2
|
25 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
cannam@2
|
26 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
cannam@2
|
27 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
cannam@2
|
28 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
cannam@2
|
29 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
cannam@2
|
30 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
cannam@2
|
31 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
cannam@2
|
32
|
cannam@2
|
33 Except as contained in this notice, the names of the Centre for
|
cannam@2
|
34 Digital Music; Queen Mary, University of London; and Chris Cannam
|
cannam@2
|
35 shall not be used in advertising or otherwise to promote the sale,
|
cannam@2
|
36 use or other dealings in this Software without prior written
|
cannam@2
|
37 authorization.
|
cannam@2
|
38 */
|
cannam@2
|
39
|
cannam@2
|
40 #include "TestMultipleRuns.h"
|
cannam@2
|
41
|
cannam@2
|
42 #include <vamp-hostsdk/Plugin.h>
|
cannam@2
|
43 using namespace Vamp;
|
cannam@2
|
44
|
cannam@2
|
45 #include <memory>
|
cannam@2
|
46 using namespace std;
|
cannam@2
|
47
|
cannam@2
|
48 #include <cmath>
|
cannam@2
|
49
|
cannam@28
|
50 #ifndef __GNUC__
|
cannam@28
|
51 #include <alloca.h>
|
cannam@28
|
52 #endif
|
cannam@28
|
53
|
cannam@2
|
54 Tester::TestRegistrar<TestDistinctRuns>
|
Chris@39
|
55 TestDistinctRuns::m_registrar("D1", "Consecutive runs with separate instances");
|
cannam@2
|
56
|
cannam@2
|
57 Tester::TestRegistrar<TestReset>
|
Chris@39
|
58 TestReset::m_registrar("D2", "Consecutive runs with a single instance using reset");
|
cannam@2
|
59
|
cannam@2
|
60 Tester::TestRegistrar<TestInterleavedRuns>
|
Chris@39
|
61 TestInterleavedRuns::m_registrar("D3", "Simultaneous interleaved runs in a single thread");
|
cannam@2
|
62
|
cannam@5
|
63 Tester::TestRegistrar<TestDifferentStartTimes>
|
Chris@39
|
64 TestDifferentStartTimes::m_registrar("D4", "Consecutive runs with different start times");
|
cannam@5
|
65
|
cannam@3
|
66 static const size_t _step = 1000;
|
cannam@3
|
67
|
cannam@2
|
68 Test::Results
|
cannam@8
|
69 TestDistinctRuns::test(string key, Options options)
|
cannam@2
|
70 {
|
cannam@2
|
71 Plugin::FeatureSet f[2];
|
cannam@2
|
72 int rate = 44100;
|
cannam@2
|
73 Results r;
|
cannam@3
|
74 float **data = 0;
|
cannam@3
|
75 size_t channels = 0;
|
cannam@3
|
76 size_t count = 100;
|
cannam@2
|
77
|
cannam@2
|
78 for (int run = 0; run < 2; ++run) {
|
cannam@2
|
79 auto_ptr<Plugin> p(load(key, rate));
|
cannam@3
|
80 if (!initAdapted(p.get(), channels, _step, _step, r)) return r;
|
cannam@3
|
81 if (!data) data = createTestAudio(channels, _step, count);
|
cannam@3
|
82 for (size_t i = 0; i < count; ++i) {
|
cannam@28
|
83 #ifdef __GNUC__
|
cannam@3
|
84 float *ptr[channels];
|
cannam@28
|
85 #else
|
cannam@28
|
86 float **ptr = (float **)alloca(channels * sizeof(float));
|
cannam@28
|
87 #endif
|
cannam@3
|
88 size_t idx = i * _step;
|
cannam@3
|
89 for (size_t c = 0; c < channels; ++c) ptr[c] = data[c] + idx;
|
cannam@2
|
90 RealTime timestamp = RealTime::frame2RealTime(idx, rate);
|
cannam@3
|
91 Plugin::FeatureSet fs = p->process(ptr, timestamp);
|
cannam@2
|
92 appendFeatures(f[run], fs);
|
cannam@2
|
93 }
|
cannam@2
|
94 Plugin::FeatureSet fs = p->getRemainingFeatures();
|
cannam@2
|
95 appendFeatures(f[run], fs);
|
cannam@2
|
96 }
|
cannam@3
|
97 if (data) destroyTestAudio(data, channels);
|
cannam@2
|
98
|
cannam@2
|
99 if (!(f[0] == f[1])) {
|
cannam@8
|
100 Result res;
|
cannam@8
|
101 string message = "Consecutive runs with separate instances produce different results";
|
cannam@8
|
102 if (options & NonDeterministic) res = note(message);
|
cannam@8
|
103 else res = error(message);
|
Chris@52
|
104 if (options & Verbose) dumpDiff(res, f[0], f[1]);
|
cannam@7
|
105 r.push_back(res);
|
cannam@2
|
106 } else {
|
cannam@2
|
107 r.push_back(success());
|
cannam@2
|
108 }
|
cannam@2
|
109
|
cannam@2
|
110 return r;
|
cannam@2
|
111 }
|
cannam@2
|
112
|
cannam@2
|
113 Test::Results
|
cannam@8
|
114 TestReset::test(string key, Options options)
|
cannam@2
|
115 {
|
cannam@2
|
116 Plugin::FeatureSet f[2];
|
cannam@2
|
117 int rate = 44100;
|
cannam@2
|
118 Results r;
|
cannam@3
|
119 float **data = 0;
|
cannam@3
|
120 size_t channels = 0;
|
cannam@3
|
121 size_t count = 100;
|
cannam@2
|
122
|
cannam@2
|
123 auto_ptr<Plugin> p(load(key, rate));
|
cannam@3
|
124
|
cannam@2
|
125 for (int run = 0; run < 2; ++run) {
|
cannam@2
|
126 if (run == 1) p->reset();
|
cannam@11
|
127 else if (!initAdapted(p.get(), channels, _step, _step, r)) return r;
|
cannam@3
|
128 if (!data) data = createTestAudio(channels, _step, count);
|
cannam@3
|
129 for (size_t i = 0; i < count; ++i) {
|
cannam@28
|
130 #ifdef __GNUC__
|
cannam@3
|
131 float *ptr[channels];
|
cannam@28
|
132 #else
|
cannam@28
|
133 float **ptr = (float **)alloca(channels * sizeof(float));
|
cannam@28
|
134 #endif
|
cannam@3
|
135 size_t idx = i * _step;
|
cannam@3
|
136 for (size_t c = 0; c < channels; ++c) ptr[c] = data[c] + idx;
|
cannam@2
|
137 RealTime timestamp = RealTime::frame2RealTime(idx, rate);
|
cannam@3
|
138 Plugin::FeatureSet fs = p->process(ptr, timestamp);
|
cannam@2
|
139 appendFeatures(f[run], fs);
|
cannam@2
|
140 }
|
cannam@2
|
141 Plugin::FeatureSet fs = p->getRemainingFeatures();
|
cannam@2
|
142 appendFeatures(f[run], fs);
|
cannam@2
|
143 }
|
cannam@3
|
144 if (data) destroyTestAudio(data, channels);
|
cannam@2
|
145
|
cannam@2
|
146 if (!(f[0] == f[1])) {
|
cannam@8
|
147 string message = "Consecutive runs with the same instance (using reset) produce different results";
|
cannam@8
|
148 Result res;
|
cannam@8
|
149 if (options & NonDeterministic) res = note(message);
|
cannam@8
|
150 else res = error(message);
|
Chris@52
|
151 if (options & Verbose) dumpDiff(res, f[0], f[1]);
|
cannam@3
|
152 r.push_back(res);
|
cannam@2
|
153 } else {
|
cannam@2
|
154 r.push_back(success());
|
cannam@2
|
155 }
|
cannam@2
|
156
|
cannam@2
|
157 return r;
|
cannam@2
|
158 }
|
cannam@2
|
159
|
cannam@2
|
160 Test::Results
|
cannam@8
|
161 TestInterleavedRuns::test(string key, Options options)
|
cannam@2
|
162 {
|
cannam@2
|
163 Plugin::FeatureSet f[2];
|
cannam@2
|
164 int rate = 44100;
|
cannam@2
|
165 Results r;
|
cannam@3
|
166 float **data = 0;
|
cannam@3
|
167 size_t channels = 0;
|
cannam@3
|
168 size_t count = 100;
|
cannam@3
|
169
|
cannam@2
|
170 Plugin *p[2];
|
cannam@2
|
171 for (int run = 0; run < 2; ++run) {
|
cannam@2
|
172 p[run] = load(key, rate);
|
cannam@3
|
173 if (!initAdapted(p[run], channels, _step, _step, r)) {
|
cannam@2
|
174 delete p[run];
|
cannam@2
|
175 if (run > 0) delete p[0];
|
cannam@2
|
176 return r;
|
cannam@2
|
177 }
|
cannam@3
|
178 if (!data) data = createTestAudio(channels, _step, count);
|
cannam@2
|
179 }
|
cannam@3
|
180 for (size_t i = 0; i < count; ++i) {
|
cannam@28
|
181 #ifdef __GNUC__
|
cannam@3
|
182 float *ptr[channels];
|
cannam@28
|
183 #else
|
cannam@28
|
184 float **ptr = (float **)alloca(channels * sizeof(float));
|
cannam@28
|
185 #endif
|
cannam@3
|
186 size_t idx = i * _step;
|
cannam@3
|
187 for (size_t c = 0; c < channels; ++c) ptr[c] = data[c] + idx;
|
cannam@2
|
188 RealTime timestamp = RealTime::frame2RealTime(idx, rate);
|
cannam@2
|
189 for (int run = 0; run < 2; ++run) {
|
cannam@3
|
190 Plugin::FeatureSet fs = p[run]->process(ptr, timestamp);
|
cannam@2
|
191 appendFeatures(f[run], fs);
|
cannam@2
|
192 }
|
cannam@2
|
193 }
|
cannam@2
|
194 for (int run = 0; run < 2; ++run) {
|
cannam@2
|
195 Plugin::FeatureSet fs = p[run]->getRemainingFeatures();
|
cannam@2
|
196 appendFeatures(f[run], fs);
|
cannam@2
|
197 delete p[run];
|
cannam@2
|
198 }
|
cannam@2
|
199
|
cannam@3
|
200 if (data) destroyTestAudio(data, channels);
|
cannam@2
|
201
|
cannam@2
|
202 if (!(f[0] == f[1])) {
|
cannam@8
|
203 string message = "Simultaneous runs with separate instances produce different results";
|
cannam@8
|
204 Result res;
|
cannam@8
|
205 if (options & NonDeterministic) res = note(message);
|
cannam@8
|
206 else res = error(message);
|
Chris@52
|
207 if (options & Verbose) dumpDiff(res, f[0], f[1]);
|
cannam@7
|
208 r.push_back(res);
|
cannam@2
|
209 } else {
|
cannam@2
|
210 r.push_back(success());
|
cannam@2
|
211 }
|
cannam@2
|
212
|
cannam@2
|
213 return r;
|
cannam@2
|
214 }
|
cannam@5
|
215
|
cannam@5
|
216 Test::Results
|
cannam@8
|
217 TestDifferentStartTimes::test(string key, Options options)
|
cannam@5
|
218 {
|
cannam@5
|
219 Plugin::FeatureSet f[2];
|
cannam@5
|
220 int rate = 44100;
|
cannam@5
|
221 Results r;
|
cannam@5
|
222 float **data = 0;
|
cannam@5
|
223 size_t channels = 0;
|
cannam@5
|
224 size_t count = 100;
|
cannam@5
|
225
|
cannam@5
|
226 for (int run = 0; run < 2; ++run) {
|
cannam@5
|
227 auto_ptr<Plugin> p(load(key, rate));
|
cannam@5
|
228 if (!initAdapted(p.get(), channels, _step, _step, r)) return r;
|
cannam@5
|
229 if (!data) data = createTestAudio(channels, _step, count);
|
cannam@5
|
230 for (size_t i = 0; i < count; ++i) {
|
cannam@28
|
231 #ifdef __GNUC__
|
cannam@5
|
232 float *ptr[channels];
|
cannam@28
|
233 #else
|
cannam@28
|
234 float **ptr = (float **)alloca(channels * sizeof(float));
|
cannam@28
|
235 #endif
|
cannam@5
|
236 size_t idx = i * _step;
|
cannam@5
|
237 for (size_t c = 0; c < channels; ++c) ptr[c] = data[c] + idx;
|
cannam@5
|
238 RealTime timestamp = RealTime::frame2RealTime(idx, rate);
|
cannam@5
|
239 if (run == 1) timestamp = timestamp + RealTime::fromSeconds(10);
|
cannam@5
|
240 Plugin::FeatureSet fs = p->process(ptr, timestamp);
|
cannam@5
|
241 appendFeatures(f[run], fs);
|
cannam@5
|
242 }
|
cannam@5
|
243 Plugin::FeatureSet fs = p->getRemainingFeatures();
|
cannam@5
|
244 appendFeatures(f[run], fs);
|
cannam@5
|
245 }
|
cannam@5
|
246 if (data) destroyTestAudio(data, channels);
|
cannam@5
|
247
|
cannam@5
|
248 if (f[0] == f[1]) {
|
cannam@8
|
249 string message = "Consecutive runs with different starting timestamps produce the same result";
|
cannam@8
|
250 Result res;
|
cannam@8
|
251 if (options & NonDeterministic) res = note(message);
|
cannam@13
|
252 else res = warning(message);
|
Chris@52
|
253 if (options & Verbose) {
|
Chris@52
|
254 cout << message << endl;
|
Chris@52
|
255 dump(f[0], false);
|
Chris@52
|
256 }
|
cannam@7
|
257 r.push_back(res);
|
cannam@5
|
258 } else {
|
cannam@5
|
259 r.push_back(success());
|
cannam@5
|
260 }
|
cannam@5
|
261
|
cannam@5
|
262 return r;
|
cannam@5
|
263 }
|