Mtp1 » History » Version 11

Chris Cannam, 2012-03-01 07:06 PM

1 10 Chris Cannam
2 1 Chris Cannam
h1. From Method to Plugin: Building a new plugin on OS/X with make
3 1 Chris Cannam
4 11 Chris Cannam
{{>toc}}
5 11 Chris Cannam
6 11 Chris Cannam
**Note:** This tutorial is specific to **OS/X**.
7 11 Chris Cannam
Go [[mtp2|here]] for a version that uses Windows and Visual C++.
8 1 Chris Cannam
9 1 Chris Cannam
We're going to walk through the process of making, and compiling, a new Vamp plugin based on the skeleton files included with the Vamp plugin SDK.
10 1 Chris Cannam
11 1 Chris Cannam
We will start by setting up a project in which we just get the skeleton plugin to compile without it doing any actual work, and then we'll add some substance to it afterwards.
12 1 Chris Cannam
13 4 Chris Cannam
All work will be done using the terminal window and @make@ (rather than using, say, the Xcode IDE), so a passing familiarity with the command line will really help.  Some familiarity with C++ will be necessary too in the later steps.
14 1 Chris Cannam
15 8 Chris Cannam
The focus here is on the practical details of what you need to put in a plugin and how to get it to build and run -- not on the real mathematical or signal-processing aspect.  We will pick a very simple method (time-domain signal power, block by block) for this example.  Please refer to the "Vamp plugin API programmer's guide":http://vamp-plugins.org/guide.pdf for further reading, with information about returning more sophisticated features.
16 1 Chris Cannam
17 1 Chris Cannam
**Before you begin:** Make sure you have the Xcode tools (the OS/X developer SDK) installed!  You can't compile anything without it.
18 1 Chris Cannam
19 1 Chris Cannam
**Note on build architectures:** Before OS/X 10.6, the default for the Xcode tools was to build 32-bit Intel binaries (known as "i386") when running on an Intel Mac, and 32-bit PowerPC ("ppc") when running on a PowerPC.  This was changed in 10.6 so as to build 64-bit Intel binaries ("x86_64") by default.  Unfortunately, plugins that are 64-bit only cannot be loaded into 32-bit hosts, such as the commonly distributed versions of all current Vamp hosts.  OS/X does support building for more than one architecture at once (storing the results in a fat file or "universal binary"), and that is the approach we take in this tutorial.  If we were to build for only a single architecture, i386 would currently be the more useful choice.
20 1 Chris Cannam
21 3 Chris Cannam
h2. 1. Download and build the SDK
22 3 Chris Cannam
23 5 Chris Cannam
Download the Vamp plugin SDK version 2.2 from the "development headers and source code" link on the developer page at http://vamp-plugins.org/develop.html -- the file you want is @vamp-plugin-sdk-2.2.tar.gz@.  Save it into your home directory, open a terminal window, and unpack it.  We'll also rename its directory from @vamp-plugin-sdk-2.2@ to @vamp-plugin-sdk@ for easier reference later on.
24 2 Chris Cannam
<pre>
25 1 Chris Cannam
mac:~ chris$ ls vamp*
26 1 Chris Cannam
vamp-plugin-sdk-2.2.tar.gz
27 1 Chris Cannam
mac:~ chris$ tar xvzf vamp-plugin-sdk-2.2.tar.gz
28 1 Chris Cannam
 ... lots of output ...
29 1 Chris Cannam
mac:~ chris$ mv vamp-plugin-sdk-2.2 vamp-plugin-sdk
30 1 Chris Cannam
mac:~ chris$
31 2 Chris Cannam
</pre>
32 1 Chris Cannam
33 5 Chris Cannam
At this point you really ought to read the @README@ file in the SDK directory, and the @README.osx@ file in the SDK's @build@ subdirectory.  But for the tutorial we'll skip that and plunge in and build the SDK directly.
34 1 Chris Cannam
35 1 Chris Cannam
We'll only build the SDK libraries and example plugins.  We won't build the test host, because it requires an additional library (libsndfile).  We'll download a pre-compiled binary of the test host later instead.  (There are pre-compiled libraries too, but since we still need the SDK for the header files, we might as well compile it in place.)
36 1 Chris Cannam
37 2 Chris Cannam
<pre>
38 1 Chris Cannam
mac:~ chris$ cd vamp-plugin-sdk
39 1 Chris Cannam
mac:~/vamp-plugin-sdk chris$ make -f build/Makefile.osx sdk
40 1 Chris Cannam
 ... lots of output ...
41 1 Chris Cannam
mac:~/vamp-plugin-sdk chris$ make -f build/Makefile.osx plugins
42 1 Chris Cannam
 ... lots of output ...
43 1 Chris Cannam
mac:~/vamp-plugin-sdk chris$
44 1 Chris Cannam
</pre>
45 1 Chris Cannam
46 3 Chris Cannam
h2. 2. Copy the skeleton files to our new project home
47 1 Chris Cannam
48 5 Chris Cannam
We're going to build our plugin in a new directory called @tutorial@ in our home directory.
49 1 Chris Cannam
50 2 Chris Cannam
<pre>
51 1 Chris Cannam
mac:~/vamp-plugin-sdk chris$ cd 
52 1 Chris Cannam
mac:~ chris$ mkdir tutorial
53 1 Chris Cannam
mac:~ chris$ cd tutorial
54 1 Chris Cannam
mac:~/tutorial chris$
55 2 Chris Cannam
</pre>
56 1 Chris Cannam
57 1 Chris Cannam
The starting point will be the set of skeleton source files provided with the SDK.  These compile into a valid, "working" Vamp plugin that happens to do nothing at all.
58 1 Chris Cannam
59 2 Chris Cannam
<pre>
60 1 Chris Cannam
mac:~/tutorial chris$ cp ../vamp-plugin-sdk/skeleton/* .
61 1 Chris Cannam
mac:~/tutorial chris$ ls
62 1 Chris Cannam
Makefile.skeleton       MyPlugin.h              vamp-plugin.list
63 1 Chris Cannam
MyPlugin.cpp            plugins.cpp             vamp-plugin.map
64 1 Chris Cannam
mac:~/tutorial chris$
65 2 Chris Cannam
</pre>
66 1 Chris Cannam
67 5 Chris Cannam
The bulk of the skeleton plugin code is contained in the files @MyPlugin.cpp@ and @MyPlugin.h@.  These two files implement a single C++ class, called @MyPlugin@.  For the sake of brevity in the tutorial we'll leave these names unchanged, but you might prefer to change them!  To do so, rename the two files as you wish, and replace every occurrence of the text @MyPlugin@ in both of them, and in @plugins.cpp@, with your preferred plugin class name.
68 1 Chris Cannam
69 5 Chris Cannam
The file @plugins.cpp@ contains the entry point for the plugin library.  A library can hold more than one plugin, and the job of @plugins.cpp@ is to provide a single known public function (@vampGetPluginDescriptor@) which the host can use to find out what plugins are available in the library.  The skeleton version of @plugins.cpp@ just returns the single MyPlugin plugin class.
70 1 Chris Cannam
71 1 Chris Cannam
Note that it makes absolutely no difference to the operation of the plugin what its class is called, or what any of these files is called; MyPlugin is (in purely technical terms) as good a name as any.  It also shouldn't matter if two different libraries happen to use the same class name.  But if you have more than one plugin in the same library, they'll need to have different class names then!
72 1 Chris Cannam
73 3 Chris Cannam
h2. 3. Get the skeleton build working
74 3 Chris Cannam
75 1 Chris Cannam
The first thing we'll do with this skeleton project is build it into a "working" (although pointless) plugin.
76 1 Chris Cannam
77 5 Chris Cannam
To build it we're going to use the tool @make@, which takes a set of production rules described in a @Makefile@ and uses them to turn source files into targets, in this case with the help of the C++ compiler.
78 1 Chris Cannam
79 1 Chris Cannam
The skeleton project contains a file Makefile.skeleton which will be the basis of our Makefile.
80 2 Chris Cannam
<pre>
81 1 Chris Cannam
mac:~/tutorial chris$ cp Makefile.skeleton Makefile
82 2 Chris Cannam
</pre>
83 1 Chris Cannam
Now, open the Makefile in the text editor; we need to edit it to suit our new project.  We haven't changed the names of any of the skeleton source files, so we don't need to edit those, but we do need to uncomment the lines that are specific to compiling on OS/X.  We want to make a universal binary (32- and 64-bit) rather than a native build, so we look for:
84 2 Chris Cannam
<pre>
85 1 Chris Cannam
##  Uncomment these for an OS/X universal binary (PPC, 32- and 64-bit Intel) using command-line tools:
86 1 Chris Cannam
87 1 Chris Cannam
# CXXFLAGS = -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch x86_64 -arch ppc -I$(VAMP_SDK_DIR) -Wall -fPIC
88 1 Chris Cannam
# PLUGIN_EXT = .dylib
89 1 Chris Cannam
# PLUGIN = $(PLUGIN_LIBRARY_NAME)$(PLUGIN_EXT)
90 1 Chris Cannam
# LDFLAGS = -dynamiclib -install_name $(PLUGIN) $(VAMP_SDK_DIR)/libvamp-sdk.a -exported_symbols_list vamp-plugin.list
91 2 Chris Cannam
</pre>
92 6 Chris Cannam
Remove the @#@ characters from the starts of the four lines in that block:
93 2 Chris Cannam
<pre>
94 1 Chris Cannam
##  Uncomment these for an OS/X universal binary (PPC, 32- and 64-bit Intel) using command-line tools:
95 1 Chris Cannam
96 1 Chris Cannam
CXXFLAGS = -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch x86_64 -arch ppc -I$(VAMP_SDK_DIR) -Wall -fPIC
97 1 Chris Cannam
PLUGIN_EXT = .dylib
98 1 Chris Cannam
PLUGIN = $(PLUGIN_LIBRARY_NAME)$(PLUGIN_EXT)
99 1 Chris Cannam
LDFLAGS = -dynamiclib -install_name $(PLUGIN) $(VAMP_SDK_DIR)/libvamp-sdk.a -exported_symbols_list vamp-plugin.list
100 2 Chris Cannam
</pre>
101 5 Chris Cannam
Then, without changing anything else, save the file and run @make@.
102 1 Chris Cannam
103 2 Chris Cannam
<pre>
104 1 Chris Cannam
mac:~/tutorial chris$ make
105 1 Chris Cannam
g++ -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch x86_64 -arch ppc -I../vamp-plugin-sdk -Wall -fPIC -c -o MyPlugin.o MyPlugin.cpp
106 1 Chris Cannam
g++ -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch x86_64 -arch ppc -I../vamp-plugin-sdk -Wall -fPIC -c -o plugins.o plugins.cpp
107 1 Chris Cannam
g++ -o myplugins.dylib MyPlugin.o plugins.o -dynamiclib -install_name myplugins.dylib ../vamp-plugin-sdk/libvamp-sdk.a -exported_symbols_list vamp-plugin.list
108 1 Chris Cannam
mac:~/tutorial chris$ 
109 2 Chris Cannam
</pre>
110 1 Chris Cannam
111 5 Chris Cannam
You should now have a plugin library file called @myplugins.dylib@, as well as some @.o@ files created during the build process.
112 2 Chris Cannam
<pre>
113 1 Chris Cannam
mac:~/tutorial chris$ ls
114 1 Chris Cannam
Makefile                MyPlugin.o              vamp-plugin.list
115 1 Chris Cannam
Makefile.skeleton       myplugins.dylib         vamp-plugin.map
116 1 Chris Cannam
MyPlugin.cpp            plugins.cpp
117 1 Chris Cannam
MyPlugin.h              plugins.o
118 2 Chris Cannam
mac:~/tutorial chris$
119 1 Chris Cannam
</pre>
120 1 Chris Cannam
121 5 Chris Cannam
This @myplugins.dylib@ file is a valid and complete Vamp plugin library.  It doesn't do anything worthwhile, but it can be loaded and "used" in any host.  It defines a single Vamp plugin, whose identifier is "myplugin" (this is coded into the MyPlugin.cpp file, we'll be changing it later).
122 1 Chris Cannam
123 3 Chris Cannam
h2. 4. Check that the plugin works with some test programs
124 1 Chris Cannam
125 1 Chris Cannam
The next thing to do is gather some programs we can use to test our plugin, so that we can check it built correctly, and so that we'll be well placed to test it properly when it actually does something.
126 1 Chris Cannam
127 3 Chris Cannam
h3. vamp-simple-host
128 1 Chris Cannam
129 9 Luis Figueira
The first one is the @vamp-simple-host@ that is part of the Vamp SDK.  This is the part of the SDK that we didn't build in step 1 (because of its dependency on libsndfile).  Download it from the "pre-compiled library and host binaries" link at http://vamp-plugins.org/develop.html -- the file you're downloading will be @vamp-plugin-sdk-2.1-binaries-osx-universal.tar.gz@.  For the time being, just open this archive in the OS/X Finder and unpack the single file @vamp-simple-host@ into our project directory (the @tutorial@ one).
130 1 Chris Cannam
131 6 Chris Cannam
Like all Vamp hosts, @vamp-simple-host@ understands the @VAMP_PATH@ environment variable to tell it where to look for plugins.  We can set this variable for a single run of the program by prefixing the program name with the variable assignment on the command line.  And, with the @-l@ option, we can ask it to list the plugins it finds there.
132 1 Chris Cannam
133 2 Chris Cannam
<pre>
134 1 Chris Cannam
mac:~/tutorial chris$ ls vamp-simple-host
135 1 Chris Cannam
vamp-simple-host
136 1 Chris Cannam
mac:~/tutorial chris$ VAMP_PATH=. ./vamp-simple-host -l
137 1 Chris Cannam
Vamp plugin search path: [.]
138 1 Chris Cannam
139 1 Chris Cannam
Vamp plugin libraries found in search path:
140 1 Chris Cannam
141 1 Chris Cannam
  ./myplugins.dylib:
142 1 Chris Cannam
    [A] [v2] My Plugin, "myplugin" []
143 1 Chris Cannam
144 1 Chris Cannam
mac:~/tutorial chris$
145 2 Chris Cannam
</pre>
146 1 Chris Cannam
147 1 Chris Cannam
Huzzah.  We can use this host to run the plugin on some test audio files, not just list it, but there isn't much point yet.
148 1 Chris Cannam
149 3 Chris Cannam
h3. vamp-plugin-tester
150 1 Chris Cannam
151 6 Chris Cannam
The other test host worth setting up at the start is @vamp-plugin-tester@, a program that tests your plugin for a number of possible problems and pitfalls.  You can download this from http://vamp-plugins.org/develop.html as well; copy it into the same directory as well, for the time being.
152 1 Chris Cannam
153 2 Chris Cannam
<pre>
154 1 Chris Cannam
mac:~/tutorial chris$ ls vamp-plugin-tester
155 1 Chris Cannam
vamp-plugin-tester
156 1 Chris Cannam
mac:~/tutorial chris$ VAMP_PATH=. ./vamp-plugin-tester -a
157 1 Chris Cannam
vamp-plugin-tester: Running...
158 1 Chris Cannam
Testing plugin: myplugins:myplugin
159 1 Chris Cannam
 -- Performing test: A1 Invalid identifiers
160 1 Chris Cannam
 -- Performing test: A2 Empty metadata fields
161 1 Chris Cannam
 ** WARNING: Plugin description is empty
162 1 Chris Cannam
 ** WARNING: Plugin maker is empty
163 1 Chris Cannam
 ** WARNING: Plugin copyright is empty
164 1 Chris Cannam
 ** WARNING: Plugin parameter "parameter" description is empty
165 1 Chris Cannam
 ** WARNING: Plugin output "output" description is empty
166 1 Chris Cannam
 -- Performing test: A3 Inappropriate value extents
167 1 Chris Cannam
 -- Performing test: B1 Output number mismatching
168 1 Chris Cannam
 ** NOTE: No results returned for output "output"
169 1 Chris Cannam
 -- Performing test: B2 Invalid or dubious timestamp usage
170 1 Chris Cannam
 -- Performing test: C1 Normal input
171 1 Chris Cannam
 -- Performing test: C2 Empty input
172 1 Chris Cannam
 -- Performing test: C3 Short input
173 1 Chris Cannam
 -- Performing test: C4 Absolutely silent input
174 1 Chris Cannam
 -- Performing test: C5 Input beyond traditional +/-1 range
175 1 Chris Cannam
 -- Performing test: C6 Random input
176 1 Chris Cannam
 -- Performing test: D1 Consecutive runs with separate instances
177 1 Chris Cannam
 -- Performing test: D2 Consecutive runs with a single instance using reset
178 1 Chris Cannam
 -- Performing test: D3 Simultaneous interleaved runs in a single thread
179 1 Chris Cannam
 -- Performing test: D4 Consecutive runs with different start times
180 1 Chris Cannam
 ** WARNING: Consecutive runs with different starting timestamps produce the same result
181 1 Chris Cannam
 -- Performing test: E1 Inconsistent default program
182 1 Chris Cannam
 -- Performing test: E2 Inconsistent default parameters
183 1 Chris Cannam
 -- Performing test: F1 Different sample rates
184 1 Chris Cannam
 -- Performing test: F2 Lengthy constructor
185 1 Chris Cannam
vamp-plugin-tester: All tests succeeded for this plugin
186 1 Chris Cannam
187 1 Chris Cannam
vamp-plugin-tester: All tests succeeded, with 6 warning(s) and 1 other note(s)
188 1 Chris Cannam
mac:~/tutorial chris$
189 2 Chris Cannam
</pre>
190 1 Chris Cannam
191 6 Chris Cannam
As you see, @vamp-plugin-tester@ runs quite a number of tests -- see its @README@ file for more details about the error and warning messages it might give.  It's a good idea to use the tester right from the start of plugin development.
192 1 Chris Cannam
193 3 Chris Cannam
h2. 5. Now, the code! 
194 1 Chris Cannam
195 6 Chris Cannam
Right, let's make the plugin do something.  We're going to calculate the mean power for each processing block.  The work we do in this section will involve editing the @MyPlugin.cpp@ file (and at one point also @MyPlugins.h@) in the text editor.
196 1 Chris Cannam
197 6 Chris Cannam
The calculation we want is @sum(x[i]^2) / N@, where @x[i]@ is audio sample number @i@, for @i@ in the range 0 to @N-1@, with @N@ the number of samples in the processing block.
198 1 Chris Cannam
199 3 Chris Cannam
h3. Describing the input and output formats
200 1 Chris Cannam
201 1 Chris Cannam
Our calculation is a time-domain one (working directly from the PCM audio data), which means we don't need to change this function (found at line 63):
202 1 Chris Cannam
203 2 Chris Cannam
<pre>
204 1 Chris Cannam
MyPlugin::InputDomain
205 1 Chris Cannam
MyPlugin::getInputDomain() const
206 1 Chris Cannam
{
207 1 Chris Cannam
    return TimeDomain;
208 1 Chris Cannam
}
209 2 Chris Cannam
</pre>
210 1 Chris Cannam
211 1 Chris Cannam
We are going to write code to handle a single audio channel only, and leave it to the host to decide what to do if more than one channel is provided (most hosts will mix-down the input for us).  So that means we don't need to change these functions either:
212 1 Chris Cannam
213 2 Chris Cannam
<pre>
214 1 Chris Cannam
size_t
215 1 Chris Cannam
MyPlugin::getMinChannelCount() const
216 1 Chris Cannam
{
217 1 Chris Cannam
    return 1;
218 1 Chris Cannam
}
219 1 Chris Cannam
220 1 Chris Cannam
size_t
221 1 Chris Cannam
MyPlugin::getMaxChannelCount() const
222 1 Chris Cannam
{
223 1 Chris Cannam
    return 1;
224 1 Chris Cannam
}
225 2 Chris Cannam
</pre>
226 1 Chris Cannam
227 1 Chris Cannam
Nothing about our calculation requires us to constrain the processing block size -- we can handle any block size.  So we can leave this function unchanged as well:
228 1 Chris Cannam
229 2 Chris Cannam
<pre>
230 1 Chris Cannam
size_t
231 1 Chris Cannam
MyPlugin::getPreferredBlockSize() const
232 1 Chris Cannam
{
233 1 Chris Cannam
    return 0; // 0 means "I can handle any block size"
234 1 Chris Cannam
}
235 2 Chris Cannam
</pre>
236 1 Chris Cannam
237 7 Chris Cannam
The function @getOutputDescriptors@ describes what sort of features we intend to return.  As it happens, the skeleton already contains pretty much the description we are going to need: a single feature, with a single value, returned for each processing block.  We should probably change the name of the output, at least:
238 1 Chris Cannam
239 2 Chris Cannam
<pre>
240 1 Chris Cannam
MyPlugin::OutputList
241 1 Chris Cannam
MyPlugin::getOutputDescriptors() const
242 1 Chris Cannam
{
243 1 Chris Cannam
    OutputList list;
244 1 Chris Cannam
245 1 Chris Cannam
    OutputDescriptor d;
246 1 Chris Cannam
    d.identifier = "power";
247 1 Chris Cannam
    d.name = "Power";
248 1 Chris Cannam
    d.description = "";
249 1 Chris Cannam
    d.unit = "";
250 1 Chris Cannam
    d.hasFixedBinCount = true;
251 1 Chris Cannam
    d.binCount = 1;
252 1 Chris Cannam
    d.hasKnownExtents = false;
253 1 Chris Cannam
    d.isQuantized = false;
254 1 Chris Cannam
    d.sampleType = OutputDescriptor::OneSamplePerStep;
255 1 Chris Cannam
    d.hasDuration = false;
256 1 Chris Cannam
    list.push_back(d);
257 1 Chris Cannam
258 1 Chris Cannam
    return list;
259 1 Chris Cannam
}
260 2 Chris Cannam
</pre>
261 1 Chris Cannam
262 3 Chris Cannam
h3. Initialisation
263 1 Chris Cannam
264 1 Chris Cannam
We said that we can **accept** any block size -- but we do need to know what the block size is.
265 1 Chris Cannam
266 7 Chris Cannam
This is told to us in the @initialise@ function.  Looking at that function, we can see the argument is @size_t blockSize@.  It's our job to remember the value of this.
267 1 Chris Cannam
268 7 Chris Cannam
We need to add a class data member for this.  In @MyPlugin.h@, look for this line at line 54 (near the bottom of the file):
269 1 Chris Cannam
270 2 Chris Cannam
<pre>
271 1 Chris Cannam
    // plugin-specific data and methods go here
272 2 Chris Cannam
</pre>
273 1 Chris Cannam
274 1 Chris Cannam
and add a line after it:
275 1 Chris Cannam
276 2 Chris Cannam
<pre>
277 1 Chris Cannam
    // plugin-specific data and methods go here
278 1 Chris Cannam
    size_t m_blockSize;
279 2 Chris Cannam
</pre>
280 1 Chris Cannam
281 7 Chris Cannam
Then, back in @MyPlugin.cpp@, find this line at line 187 in the @initialise@ function:
282 1 Chris Cannam
283 2 Chris Cannam
<pre>
284 1 Chris Cannam
    // Real initialisation work goes here!
285 2 Chris Cannam
</pre>
286 1 Chris Cannam
287 1 Chris Cannam
and add a line to set the data member:
288 1 Chris Cannam
289 2 Chris Cannam
<pre>
290 1 Chris Cannam
    // Real initialisation work goes here!
291 1 Chris Cannam
    m_blockSize = blockSize;
292 2 Chris Cannam
</pre>
293 1 Chris Cannam
294 7 Chris Cannam
Also it's very good practice to make sure the data member is initialised to zero in the class constructor.  That, at line 10 of @MyPlugin.cpp@, initially reads:
295 1 Chris Cannam
296 2 Chris Cannam
<pre>
297 1 Chris Cannam
MyPlugin::MyPlugin(float inputSampleRate) :
298 1 Chris Cannam
    Plugin(inputSampleRate)
299 1 Chris Cannam
{
300 1 Chris Cannam
}
301 2 Chris Cannam
</pre>
302 1 Chris Cannam
303 1 Chris Cannam
and we want it to read:
304 2 Chris Cannam
305 1 Chris Cannam
<pre>
306 1 Chris Cannam
MyPlugin::MyPlugin(float inputSampleRate) :
307 1 Chris Cannam
    Plugin(inputSampleRate),
308 1 Chris Cannam
    m_blockSize(0)
309 1 Chris Cannam
{
310 1 Chris Cannam
}
311 2 Chris Cannam
</pre>
312 1 Chris Cannam
313 7 Chris Cannam
At this point, after saving both the header and @.cpp@ file, it's probably worth going back to the terminal window and making sure it still compiles.  (Just run @make@ again.)
314 1 Chris Cannam
315 3 Chris Cannam
h3. Processing
316 1 Chris Cannam
317 7 Chris Cannam
The core of our calculation happens in the @process@ method:
318 1 Chris Cannam
319 2 Chris Cannam
<pre>
320 1 Chris Cannam
MyPlugin::FeatureSet
321 1 Chris Cannam
MyPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
322 1 Chris Cannam
{
323 1 Chris Cannam
    // Do actual work!
324 1 Chris Cannam
    return FeatureSet();
325 1 Chris Cannam
}
326 2 Chris Cannam
</pre>
327 1 Chris Cannam
328 7 Chris Cannam
Here @inputBuffers@ is effectively an array of arrays -- to retrieve a single audio sample, we index it first by audio channel number (we know that we only have one channel, so the only valid index is 0) and then by audio sample number (from 0 to the processing block size less 1).
329 1 Chris Cannam
330 1 Chris Cannam
What we want to do is add up the squares of the audio sample values, and divide by the number of samples.
331 1 Chris Cannam
332 2 Chris Cannam
<pre>
333 1 Chris Cannam
MyPlugin::FeatureSet
334 1 Chris Cannam
MyPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
335 1 Chris Cannam
{
336 1 Chris Cannam
    float sumOfSquares = 0.0f;
337 1 Chris Cannam
338 1 Chris Cannam
    size_t i = 0; // note: same type as m_blockSize
339 1 Chris Cannam
340 1 Chris Cannam
    while (i < m_blockSize) {
341 1 Chris Cannam
        float sample = inputBuffers[0][i];
342 1 Chris Cannam
        sumOfSquares += sample * sample;
343 1 Chris Cannam
        ++i;
344 1 Chris Cannam
    }
345 1 Chris Cannam
346 1 Chris Cannam
    float meanPower = sumOfSquares / m_blockSize;
347 1 Chris Cannam
348 1 Chris Cannam
    // now what?
349 1 Chris Cannam
350 1 Chris Cannam
    return FeatureSet();
351 1 Chris Cannam
}
352 2 Chris Cannam
</pre>
353 1 Chris Cannam
354 1 Chris Cannam
So we've calculated the mean power value -- now how to return it?
355 1 Chris Cannam
356 7 Chris Cannam
In Vamp plugin terms, what we have is a plugin that has a single output, on which is returned a single audio feature for each process block, with one value.  We need to construct a @Feature@ object, give it a single value, and then push it as the only feature in output 0 (the first) of a new @FeatureSet@ object.  See the "Vamp plugin API programmer's guide":http://vamp-plugins.org/guide.pdf for more information about feature representation.
357 1 Chris Cannam
358 1 Chris Cannam
Here's the code:
359 1 Chris Cannam
360 2 Chris Cannam
<pre>
361 1 Chris Cannam
MyPlugin::FeatureSet
362 1 Chris Cannam
MyPlugin::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
363 1 Chris Cannam
{
364 1 Chris Cannam
    float sumOfSquares = 0.0f;
365 1 Chris Cannam
366 1 Chris Cannam
    size_t i = 0;
367 1 Chris Cannam
368 1 Chris Cannam
    while (i < m_blockSize) {
369 1 Chris Cannam
        float sample = inputBuffers[0][i];
370 1 Chris Cannam
        sumOfSquares += sample * sample;
371 1 Chris Cannam
        ++i;
372 1 Chris Cannam
    }
373 1 Chris Cannam
374 1 Chris Cannam
    float meanPower = sumOfSquares / m_blockSize;
375 1 Chris Cannam
376 1 Chris Cannam
    Feature f;
377 1 Chris Cannam
    f.hasTimestamp = false;
378 1 Chris Cannam
    f.values.push_back(meanPower);
379 1 Chris Cannam
380 1 Chris Cannam
    FeatureSet fs;
381 1 Chris Cannam
    fs[0].push_back(f);
382 1 Chris Cannam
    return fs;
383 1 Chris Cannam
}
384 2 Chris Cannam
</pre>
385 1 Chris Cannam
386 7 Chris Cannam
After making this change and returning to the terminal window to run @make@ again, we now have a plugin that actually does something.
387 1 Chris Cannam
388 7 Chris Cannam
With a suitable input file (@.wav@ or @.aiff@ or a similar uncompressed format that @vamp-simple-host@ understands), we can now run it:
389 1 Chris Cannam
390 2 Chris Cannam
<pre>
391 1 Chris Cannam
mac:~/tutorial chris$ VAMP_PATH=. ./vamp-simple-host myplugins:myplugin ~/my-song.wav
392 1 Chris Cannam
vamp-simple-host: Running...
393 1 Chris Cannam
Reading file: "/Users/chris/my-song.wav", writing to standard output
394 1 Chris Cannam
Running plugin: "myplugin"...                                        
395 1 Chris Cannam
Using block size = 1024, step size = 1024                            
396 1 Chris Cannam
Plugin accepts 1 -> 1 channel(s)                                     
397 1 Chris Cannam
Sound file has 2 (will mix/augment if necessary)                     
398 1 Chris Cannam
Output is: "output"                                                  
399 1 Chris Cannam
 0.000000000: 0
400 1 Chris Cannam
 0.023219954: 0
401 1 Chris Cannam
 0.046439909: 0
402 1 Chris Cannam
 0.069659863: 0
403 1 Chris Cannam
 0.092879818: 0
404 1 Chris Cannam
 0.116099773: 0
405 1 Chris Cannam
 0.139319727: 0
406 1 Chris Cannam
 0.162539682: 1.56888e-11
407 1 Chris Cannam
 0.185759637: 4.90218e-09
408 1 Chris Cannam
 0.208979591: 2.135e-07
409 1 Chris Cannam
 0.232199546: 0.00666197
410 1 Chris Cannam
 ... and lots and lots and lots and lots more output ...
411 1 Chris Cannam
mac:~/tutorial chris$
412 2 Chris Cannam
</pre>
413 1 Chris Cannam
414 7 Chris Cannam
Try using the @vamp-plugin-tester@ again as well.
415 1 Chris Cannam
416 1 Chris Cannam
417 3 Chris Cannam
h2. 6. Fill in descriptions and other metadata
418 1 Chris Cannam
419 7 Chris Cannam
Now we have a working plugin, but it still has the rather awkward name of @MyPlugin@.  There are several functions at the top of @MyPlugin.cpp@ which we can use to give it a more sensible name and description.
420 1 Chris Cannam
421 1 Chris Cannam
For example:
422 1 Chris Cannam
423 2 Chris Cannam
<pre>
424 1 Chris Cannam
string
425 1 Chris Cannam
MyPlugin::getIdentifier() const
426 1 Chris Cannam
{
427 1 Chris Cannam
    return "myplugin";
428 1 Chris Cannam
}
429 2 Chris Cannam
</pre>
430 1 Chris Cannam
431 7 Chris Cannam
The identifier is a string that is not normally used by people (for example, it never appears when plugins are listed in a menu of a graphical application), but that uniquely identifies the plugin within its library.  Something like @"power"@ is perfectly appropriate here.
432 1 Chris Cannam
433 7 Chris Cannam
You should fill in all of @getIdentifier@, @getName@, @getDescription@, @getMaker@, @getPluginVersion@, and @getCopyright@ for every plugin you write.
434 1 Chris Cannam
435 1 Chris Cannam
In my case, I would need something like:
436 1 Chris Cannam
437 2 Chris Cannam
<pre>
438 1 Chris Cannam
string
439 1 Chris Cannam
MyPlugin::getIdentifier() const
440 1 Chris Cannam
{
441 1 Chris Cannam
    return "power";
442 1 Chris Cannam
}
443 1 Chris Cannam
444 1 Chris Cannam
string
445 1 Chris Cannam
MyPlugin::getName() const
446 1 Chris Cannam
{
447 1 Chris Cannam
    return "Signal power level";
448 1 Chris Cannam
}
449 1 Chris Cannam
450 1 Chris Cannam
string
451 1 Chris Cannam
MyPlugin::getDescription() const
452 1 Chris Cannam
{
453 1 Chris Cannam
    return "Calculate the mean signal power for each processing block";
454 1 Chris Cannam
}
455 1 Chris Cannam
456 1 Chris Cannam
string
457 1 Chris Cannam
MyPlugin::getMaker() const
458 1 Chris Cannam
{
459 1 Chris Cannam
    return "Chris Cannam";
460 1 Chris Cannam
}
461 1 Chris Cannam
462 1 Chris Cannam
int
463 1 Chris Cannam
MyPlugin::getPluginVersion() const
464 1 Chris Cannam
{
465 1 Chris Cannam
    return 1;
466 1 Chris Cannam
}
467 1 Chris Cannam
468 1 Chris Cannam
string
469 1 Chris Cannam
MyPlugin::getCopyright() const
470 1 Chris Cannam
{
471 1 Chris Cannam
    return "Freely redistributable (tutorial example code)";
472 1 Chris Cannam
}
473 2 Chris Cannam
</pre>