cannam@0
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@0
|
2
|
cannam@0
|
3 /*
|
cannam@0
|
4 Vamp Plugin Tester
|
cannam@0
|
5 Chris Cannam, cannam@all-day-breakfast.com
|
cannam@0
|
6 Centre for Digital Music, Queen Mary, University of London.
|
cannam@0
|
7 Copyright 2009 QMUL.
|
cannam@0
|
8
|
cannam@0
|
9 This program loads a Vamp plugin and tests its susceptibility to a
|
cannam@0
|
10 number of common pitfalls, including handling of extremes of input
|
cannam@0
|
11 data. If you can think of any additional useful tests that are
|
cannam@0
|
12 easily added, please send them to me.
|
cannam@0
|
13
|
cannam@0
|
14 Permission is hereby granted, free of charge, to any person
|
cannam@0
|
15 obtaining a copy of this software and associated documentation
|
cannam@0
|
16 files (the "Software"), to deal in the Software without
|
cannam@0
|
17 restriction, including without limitation the rights to use, copy,
|
cannam@0
|
18 modify, merge, publish, distribute, sublicense, and/or sell copies
|
cannam@0
|
19 of the Software, and to permit persons to whom the Software is
|
cannam@0
|
20 furnished to do so, subject to the following conditions:
|
cannam@0
|
21
|
cannam@0
|
22 The above copyright notice and this permission notice shall be
|
cannam@0
|
23 included in all copies or substantial portions of the Software.
|
cannam@0
|
24
|
cannam@0
|
25 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
cannam@0
|
26 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
cannam@0
|
27 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
cannam@0
|
28 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
cannam@0
|
29 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
cannam@0
|
30 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
cannam@0
|
31 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
cannam@0
|
32
|
cannam@0
|
33 Except as contained in this notice, the names of the Centre for
|
cannam@0
|
34 Digital Music; Queen Mary, University of London; and Chris Cannam
|
cannam@0
|
35 shall not be used in advertising or otherwise to promote the sale,
|
cannam@0
|
36 use or other dealings in this Software without prior written
|
cannam@0
|
37 authorization.
|
cannam@0
|
38 */
|
cannam@0
|
39
|
cannam@0
|
40 #include "TestStaticData.h"
|
cannam@0
|
41
|
cannam@0
|
42 #include <vamp-hostsdk/Plugin.h>
|
cannam@0
|
43 using namespace Vamp;
|
cannam@0
|
44
|
cannam@0
|
45 #include <memory>
|
cannam@0
|
46 using namespace std;
|
cannam@0
|
47
|
cannam@0
|
48 #include <cmath>
|
cannam@0
|
49
|
cannam@0
|
50 Tester::TestRegistrar<TestIdentifiers>
|
cannam@4
|
51 TestIdentifiers::m_registrar("A1 Invalid identifiers");
|
cannam@0
|
52
|
cannam@0
|
53 Tester::TestRegistrar<TestEmptyFields>
|
cannam@4
|
54 TestEmptyFields::m_registrar("A2 Empty metadata fields");
|
cannam@0
|
55
|
cannam@0
|
56 Tester::TestRegistrar<TestValueRanges>
|
cannam@4
|
57 TestValueRanges::m_registrar("A3 Inappropriate value extents");
|
cannam@0
|
58
|
cannam@0
|
59 Test::Results
|
cannam@14
|
60 TestIdentifiers::test(string key, Options)
|
cannam@0
|
61 {
|
cannam@0
|
62 auto_ptr<Plugin> p(load(key));
|
cannam@0
|
63
|
cannam@0
|
64 Results r;
|
cannam@17
|
65 r.push_back(testIdentifier(p->getIdentifier(), "Plugin identifier"));
|
cannam@0
|
66
|
cannam@0
|
67 Plugin::ParameterList params = p->getParameterDescriptors();
|
cannam@0
|
68 for (int i = 0; i < (int)params.size(); ++i) {
|
cannam@17
|
69 r.push_back(testIdentifier(params[i].identifier, "Parameter identifier"));
|
cannam@0
|
70 }
|
cannam@0
|
71
|
cannam@0
|
72 Plugin::OutputList outputs = p->getOutputDescriptors();
|
cannam@0
|
73 for (int i = 0; i < (int)outputs.size(); ++i) {
|
cannam@17
|
74 r.push_back(testIdentifier(outputs[i].identifier, "Output identifier"));
|
cannam@0
|
75 }
|
cannam@0
|
76
|
cannam@0
|
77 return r;
|
cannam@0
|
78 }
|
cannam@0
|
79
|
cannam@0
|
80 Test::Result
|
cannam@0
|
81 TestIdentifiers::testIdentifier(string identifier, string desc)
|
cannam@0
|
82 {
|
cannam@0
|
83 for (int i = 0; i < (int)identifier.length(); ++i) {
|
cannam@0
|
84 char c = identifier[i];
|
cannam@0
|
85 if (c >= 'a' && c <= 'z') continue;
|
cannam@0
|
86 if (c >= 'A' && c <= 'Z') continue;
|
cannam@0
|
87 if (c >= '0' && c <= '9') continue;
|
cannam@0
|
88 if (c == '_' || c == '-') continue;
|
cannam@0
|
89 return error
|
cannam@0
|
90 (desc + " \"" + identifier +
|
cannam@0
|
91 "\" contains invalid character(s); permitted are: [a-zA-Z0-9_-]");
|
cannam@0
|
92 }
|
cannam@0
|
93 return success();
|
cannam@0
|
94 }
|
cannam@0
|
95
|
cannam@0
|
96 Test::Results
|
cannam@14
|
97 TestEmptyFields::test(string key, Options)
|
cannam@0
|
98 {
|
cannam@0
|
99 auto_ptr<Plugin> p(load(key));
|
cannam@0
|
100
|
cannam@0
|
101 Results r;
|
cannam@0
|
102
|
cannam@17
|
103 r.push_back(testMandatory(p->getName(), "Plugin name"));
|
cannam@17
|
104 r.push_back(testRecommended(p->getDescription(), "Plugin description"));
|
cannam@17
|
105 r.push_back(testRecommended(p->getMaker(), "Plugin maker"));
|
cannam@17
|
106 r.push_back(testRecommended(p->getCopyright(), "Plugin copyright"));
|
cannam@0
|
107
|
cannam@0
|
108 Plugin::ParameterList params = p->getParameterDescriptors();
|
cannam@0
|
109 for (int i = 0; i < (int)params.size(); ++i) {
|
cannam@0
|
110 r.push_back(testMandatory
|
cannam@0
|
111 (params[i].name,
|
cannam@17
|
112 "Plugin parameter \"" + params[i].identifier + "\" name"));
|
cannam@0
|
113 r.push_back(testRecommended
|
cannam@0
|
114 (params[i].description,
|
cannam@17
|
115 "Plugin parameter \"" + params[i].identifier + "\" description"));
|
cannam@0
|
116 }
|
cannam@0
|
117
|
cannam@0
|
118 Plugin::OutputList outputs = p->getOutputDescriptors();
|
cannam@0
|
119 for (int i = 0; i < (int)outputs.size(); ++i) {
|
cannam@0
|
120 r.push_back(testMandatory
|
cannam@0
|
121 (outputs[i].name,
|
cannam@17
|
122 "Plugin output \"" + outputs[i].identifier + "\" name"));
|
cannam@0
|
123 r.push_back(testRecommended
|
cannam@0
|
124 (outputs[i].description,
|
cannam@17
|
125 "Plugin output \"" + outputs[i].identifier + "\" description"));
|
cannam@0
|
126 }
|
cannam@0
|
127
|
cannam@0
|
128 return r;
|
cannam@0
|
129 }
|
cannam@0
|
130
|
cannam@0
|
131 Test::Result
|
cannam@0
|
132 TestEmptyFields::testMandatory(string text, string desc)
|
cannam@0
|
133 {
|
cannam@0
|
134 if (text == "") {
|
cannam@0
|
135 return error(desc + " is empty");
|
cannam@0
|
136 }
|
cannam@0
|
137 return success();
|
cannam@0
|
138 }
|
cannam@0
|
139
|
cannam@0
|
140 Test::Result
|
cannam@0
|
141 TestEmptyFields::testRecommended(string text, string desc)
|
cannam@0
|
142 {
|
cannam@0
|
143 if (text == "") {
|
cannam@0
|
144 return warning(desc + " is empty");
|
cannam@0
|
145 }
|
cannam@0
|
146 return success();
|
cannam@0
|
147 }
|
cannam@0
|
148
|
cannam@0
|
149 Test::Results
|
cannam@14
|
150 TestValueRanges::test(string key, Options)
|
cannam@0
|
151 {
|
cannam@0
|
152 auto_ptr<Plugin> p(load(key));
|
cannam@0
|
153
|
cannam@0
|
154 Results r;
|
cannam@0
|
155
|
cannam@0
|
156 Plugin::ParameterList params = p->getParameterDescriptors();
|
cannam@0
|
157 for (int i = 0; i < (int)params.size(); ++i) {
|
cannam@0
|
158 Plugin::ParameterDescriptor &pd(params[i]);
|
cannam@17
|
159 string pfx("Plugin parameter \"" + pd.identifier + "\"");
|
cannam@0
|
160 float min = pd.minValue;
|
cannam@0
|
161 float max = pd.maxValue;
|
cannam@0
|
162 float deft = pd.defaultValue;
|
cannam@0
|
163 if (max <= min) {
|
cannam@0
|
164 r.push_back(error(pfx + " maxValue <= minValue"));
|
cannam@0
|
165 }
|
cannam@0
|
166 if (deft < min || deft > max) {
|
cannam@0
|
167 r.push_back(error(pfx + " defaultValue out of range"));
|
cannam@0
|
168 }
|
cannam@0
|
169 if (pd.isQuantized) {
|
cannam@0
|
170 if (pd.quantizeStep == 0.f) {
|
cannam@0
|
171 r.push_back(error(pfx + " is quantized, but quantize step is zero"));
|
cannam@0
|
172 } else {
|
cannam@0
|
173
|
cannam@0
|
174 float epsilon = 0.00001f;
|
cannam@0
|
175 int qty = int((max - min) / pd.quantizeStep + 0.5);
|
cannam@0
|
176 float target = min + pd.quantizeStep * qty;
|
cannam@0
|
177 if (fabsf(max - target) > epsilon) {
|
cannam@0
|
178 r.push_back(warning(pfx + " value range is not a multiple of quantize step"));
|
cannam@0
|
179 }
|
cannam@0
|
180
|
cannam@0
|
181 if (!pd.valueNames.empty()) {
|
cannam@0
|
182 if ((int)pd.valueNames.size() < qty+1) {
|
cannam@0
|
183 r.push_back(warning(pfx + " has fewer value names than quantize steps"));
|
cannam@0
|
184 } else if ((int)pd.valueNames.size() > qty+1) {
|
cannam@0
|
185 r.push_back(warning(pfx + " has more value names than quantize steps"));
|
cannam@0
|
186 }
|
cannam@0
|
187 }
|
cannam@0
|
188
|
cannam@0
|
189 qty = int((deft - min) / pd.quantizeStep + 0.5);
|
cannam@0
|
190 target = min + pd.quantizeStep * qty;
|
cannam@0
|
191 if (fabsf(deft - target) > epsilon) {
|
cannam@0
|
192 r.push_back(warning(pfx + " default value is not a multiple of quantize step beyond minimum"));
|
cannam@0
|
193 }
|
cannam@0
|
194 }
|
cannam@0
|
195 }
|
cannam@0
|
196 }
|
cannam@0
|
197
|
cannam@0
|
198 return r;
|
cannam@0
|
199 }
|
cannam@0
|
200
|
cannam@0
|
201
|