cannam@150: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
cannam@150: /*
cannam@150:   Piper C++
cannam@150: 
cannam@150:   An API for audio analysis and feature extraction plugins.
cannam@150: 
cannam@150:   Centre for Digital Music, Queen Mary, University of London.
cannam@150:   Copyright 2006-2016 Chris Cannam and QMUL.
cannam@150:   
cannam@150:   Permission is hereby granted, free of charge, to any person
cannam@150:   obtaining a copy of this software and associated documentation
cannam@150:   files (the "Software"), to deal in the Software without
cannam@150:   restriction, including without limitation the rights to use, copy,
cannam@150:   modify, merge, publish, distribute, sublicense, and/or sell copies
cannam@150:   of the Software, and to permit persons to whom the Software is
cannam@150:   furnished to do so, subject to the following conditions:
cannam@150: 
cannam@150:   The above copyright notice and this permission notice shall be
cannam@150:   included in all copies or substantial portions of the Software.
cannam@150: 
cannam@150:   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
cannam@150:   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
cannam@150:   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
cannam@150:   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
cannam@150:   ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
cannam@150:   CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
cannam@150:   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cannam@150: 
cannam@150:   Except as contained in this notice, the names of the Centre for
cannam@150:   Digital Music; Queen Mary, University of London; and Chris Cannam
cannam@150:   shall not be used in advertising or otherwise to promote the sale,
cannam@150:   use or other dealings in this Software without prior written
cannam@150:   authorization.
cannam@150: */
cannam@150: 
cannam@150: #include "ProcessQtTransport.h"
cannam@150: #include "CapnpRRClient.h"
cannam@208: #include "PiperAutoPlugin.h"
cannam@150: 
cannam@270: #include <vamp-hostsdk/PluginInputDomainAdapter.h>
cannam@270: 
cannam@150: #include <stdexcept>
cannam@150: 
cannam@150: using std::cerr;
cannam@150: using std::endl;
cannam@150: using std::exception;
cannam@150: using std::vector;
cannam@270: using std::string;
cannam@150: 
cannam@150: int main(int argc, char **argv)
cannam@150: {
cannam@150:     if (argc != 2) {
cannam@150:         cerr << "Usage: " << argv[0] << " <server-executable-path>" << endl;
cannam@150:         return 2;
cannam@150:     }
cannam@150: 
cannam@270:     string server = argv[1];
cannam@270:     string format = "capnp";
cannam@270:     string zeroCrossing = "vamp-example-plugins:zerocrossing";
cannam@270:     string powerSpectrum = "vamp-example-plugins:powerspectrum";
cannam@270:     piper_vamp::client::LogCallback *logger = nullptr;
cannam@270:     
cannam@150:     try {
cannam@270:         cerr << endl << "*** Test: starting transport" << endl;
cannam@270:         piper_vamp::client::ProcessQtTransport transport(server, format, logger);
cannam@150:         if (!transport.isOK()) {
cannam@274:             cerr << "--- ERROR: Transport failed to start" << endl;
cannam@150:             return 1;
cannam@150:         }
cannam@274:         cerr << "+++ OK" << endl;
cannam@150: 
cannam@270:         cerr << endl << "*** Test: constructing client" << endl;
cannam@270:         piper_vamp::client::CapnpRRClient client(&transport, logger);
cannam@274:         cerr << "+++ OK" << endl;
cannam@270: 
cannam@270:         cerr << endl << "*** Test: listing plugins" << endl;
cannam@207:         piper_vamp::ListResponse lr = client.list({});
cannam@274:         cerr << "+++ OK; plugins are:" << endl;
cannam@150:         int i = 1;
cannam@150:         for (const auto &p: lr.available) {
cannam@150:             cerr << i++ << ". [" << p.pluginKey << "] " << p.basic.name << endl;
cannam@150:         }
cannam@270: 
cannam@270:         enum {
cannam@270:             explicitServer = 1,
cannam@270:             autoServer = 2
cannam@270:         };
cannam@270:         enum {
cannam@270:             timeDomain = 1,
cannam@270:             frequencyDomainServerSide = 2,
cannam@270:             frequencyDomainClientSide = 3
cannam@270:         };
cannam@270: 
cannam@270:         for (int domain = timeDomain;
cannam@270:              domain <= frequencyDomainClientSide;
cannam@270:              ++domain) {
cannam@270: 
cannam@270:             for (int serverSort = explicitServer;
cannam@270:                  serverSort <= autoServer;
cannam@270:                  ++serverSort) {
cannam@270: 
cannam@270:                 string id = zeroCrossing;
cannam@270:                 if (domain == frequencyDomainServerSide ||
cannam@270:                     domain == frequencyDomainClientSide) {
cannam@270:                     id = powerSpectrum;
cannam@270:                 }
cannam@270:             
cannam@270:                 Vamp::Plugin *plugin = nullptr;
cannam@270: 
cannam@270:                 int adapterFlags = 0;
cannam@270:                 if (domain == frequencyDomainServerSide) {
cannam@270:                     adapterFlags = (int)
cannam@270:                         Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN;
cannam@270:                 }
cannam@270: 
cannam@270:                 if (serverSort == explicitServer) {
cannam@270:             
cannam@270:                     cerr << endl << "*** Test: loading \"" << id
cannam@270:                          << "\" with explicit server" << endl;
cannam@270:                     
cannam@270:                     piper_vamp::LoadRequest req;
cannam@270:                     req.pluginKey = id;
cannam@270:                     req.inputSampleRate = 16;
cannam@270:                     req.adapterFlags = adapterFlags;
cannam@270:                     piper_vamp::LoadResponse resp = client.load(req);
cannam@270:                     plugin = resp.plugin;
cannam@270:                     if (!plugin) {
cannam@274:                         cerr << "--- ERROR: plugin is null" << endl;
cannam@270:                         return 1;
cannam@270:                     }
cannam@274:                     cerr << "+++ OK" << endl;
cannam@270: 
cannam@270:                 } else {
cannam@270:                 
cannam@270:                     cerr << endl << "*** Test: loading \"" << id
cannam@270:                          << "\" with auto-plugin" << endl;
cannam@270:                     
cannam@270:                     piper_vamp::client::PiperAutoPlugin *ap =
cannam@270:                         new piper_vamp::client::PiperAutoPlugin
cannam@270:                         (server, id, 16, adapterFlags, logger);
cannam@270:                     if (!ap->isOK()) {
cannam@274:                         cerr << "--- ERROR: PiperAutoPlugin creation failed" << endl;
cannam@270:                         return 1;
cannam@270:                     }
cannam@274:                     cerr << "+++ OK" << endl;
cannam@270: 
cannam@270:                     plugin = ap;
cannam@270:                 }
cannam@270: 
cannam@270:                 if (domain == frequencyDomainClientSide) {
cannam@270:                     cerr << "*** Test: creating input-domain adapter" << endl;
cannam@270:                     plugin = new Vamp::HostExt::PluginInputDomainAdapter(plugin);
cannam@274:                     cerr << "+++ OK" << endl;
cannam@270:                 }
cannam@270: 
cannam@270:                 cerr << endl << "*** Test: initialising plugin" << endl;
cannam@270:                 if (!plugin->initialise(1, 4, 4)) {
cannam@274:                     cerr << "--- ERROR: initialisation failed" << endl;
cannam@270:                     return 1;
cannam@270:                 }
cannam@274:                 cerr << "+++ OK" << endl;
cannam@270: 
cannam@270:                 for (int round = 1; round <= 2; ++round) {
cannam@270:                     cerr << endl << "*** Test: processing"
cannam@270:                          << " (domain " << domain
cannam@270:                          << ", sort " << serverSort
cannam@270:                          << ", round " << round << ")" << endl;
cannam@270: 
cannam@270:                     vector<float> buf = { 1.0, -1.0, 1.0, -1.0 };
cannam@270:                     float *bd = buf.data();
cannam@270:                     Vamp::Plugin::FeatureSet features = plugin->process
cannam@270:                         (&bd, Vamp::RealTime::zeroTime);
cannam@274:                     cerr << "+++ OK, process succeeded" << endl;
cannam@270:                     if (features[0].size() != 1) {
cannam@274:                         cerr << "--- ERROR: wrong number of features on output 0"
cannam@270:                              << " (expected 1, obtained " << features[0].size() << ")"
cannam@270:                              << endl;
cannam@270:                         return 1;
cannam@270:                     }
cannam@270:                     vector<float> expected;
cannam@270:                     if (domain == timeDomain) {
cannam@270:                         expected.push_back(4);
cannam@270:                     } else {
cannam@270:                         // these would be 0, 0, 16 if we were
cannam@270:                         // calculating the power spectrum without
cannam@270:                         // windowing - but the input domain adapter
cannam@270:                         // does window, and an asymmetrical Hann
cannam@270:                         // window gives these results
cannam@270:                         expected.push_back(0);
cannam@270:                         expected.push_back(1);
cannam@270:                         expected.push_back(4);
cannam@270:                     }
cannam@270:                     
cannam@270:                     if (features[0][0].values.size() != expected.size()) {
cannam@274:                         cerr << "--- ERROR: wrong size for feature on output 0"
cannam@270:                              << " (expected " << expected.size()
cannam@270:                              << ", obtained "
cannam@270:                              << features[0][0].values.size() << ")" << endl;
cannam@270:                         return 1;
cannam@270:                     }
cannam@270:                     for (size_t i = 0; i < expected.size(); ++i) {
cannam@270:                         if (features[0][0].values[i] != expected[i]) {
cannam@274:                             cerr << "--- ERROR: wrong value for index " << i
cannam@270:                                  << " of feature on output 0"
cannam@270:                                  << " (expected " << expected[i]
cannam@270:                                  << ", obtained "
cannam@270:                                  << features[0][0].values[i] << ")" << endl;
cannam@270:                             cerr << "(All obtained values are:";
cannam@270:                             for (size_t j = 0; j < expected.size(); ++j) {
cannam@270:                                 cerr << " " << features[0][0].values[j];
cannam@270:                             }
cannam@270:                             cerr << ")" << endl;
cannam@270:                             return 1;
cannam@270:                         }
cannam@270:                     }
cannam@274:                     cerr << "+++ OK, results are correct" << endl;
cannam@270: 
cannam@270:                     (void)plugin->getRemainingFeatures();
cannam@270: 
cannam@270:                     if (round == 1) {
cannam@270:                         cerr << endl << "*** Test: resetting plugin for round 2" << endl;
cannam@270:                         plugin->reset();
cannam@274:                         cerr << "+++ OK" << endl;
cannam@270:                     }
cannam@270:                 }
cannam@270: 
cannam@270:                 cerr << endl << "*** Test: deleting plugin" << endl;
cannam@270:                 delete plugin;
cannam@274:                 cerr << "+++ OK" << endl;
cannam@150:             }
cannam@150:         }
cannam@150: 
cannam@150:     } catch (const exception &e) {
cannam@274:         cerr << "--- ERROR: Exception caught: " << e.what() << endl;
cannam@150:         return 1;
cannam@150:     }
cannam@150: 
cannam@270:     cerr << endl << "*** All tests succeeded" << endl;
cannam@270:         
cannam@150:     return 0;
cannam@150: }
cannam@150: