New upstream version 1.3.0+ds0
authorIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>
Wed, 20 Jan 2021 08:10:34 +0000 (09:10 +0100)
committerIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>
Wed, 20 Jan 2021 08:10:34 +0000 (09:10 +0100)
71 files changed:
.travis.yml
CHANGESLOG.txt
JMess.cpp [deleted file]
WIN10BUILDINSTRUCTIONS.pdf [deleted file]
faust-src/Makefile
faust-src/Makefile.headers [new file with mode: 0644]
faust-src/README-Limiter.md [new file with mode: 0644]
faust-src/compressor-limiter-test.dsp [new file with mode: 0644]
faust-src/compressordsp.dsp [new file with mode: 0644]
faust-src/faust2header.cpp [new file with mode: 0644]
faust-src/freeverbdsp.dsp [new file with mode: 0644]
faust-src/freeverbmonodsp.dsp [new file with mode: 0644]
faust-src/limiterdsp.dsp [new file with mode: 0644]
faust-src/limitertest.dsp [new file with mode: 0644]
faust-src/tests/compressor-limiter-test.dsp [new file with mode: 0644]
faust-src/tests/compressor2dsp.dsp [new file with mode: 0644]
faust-src/zitarevdsp.dsp [new file with mode: 0644]
faust-src/zitarevmonodsp.dsp [new file with mode: 0644]
meson.build
scripts/hubMode/art.sh [new file with mode: 0755]
scripts/hubMode/startJacktripHubClient.sh [new file with mode: 0755]
scripts/hubMode/startJacktripHubServer.sh [new file with mode: 0755]
scripts/hubMode/test_hub_mode_server_and_client.sh [new file with mode: 0755]
src/AudioInterface.cpp
src/AudioInterface.h
src/AudioTester.cpp [new file with mode: 0644]
src/AudioTester.h [new file with mode: 0644]
src/Compressor.cpp [new file with mode: 0644]
src/Compressor.h [new file with mode: 0644]
src/CompressorPresets.h [new file with mode: 0644]
src/DataProtocol.cpp
src/DataProtocol.h
src/Effects.h [new file with mode: 0644]
src/JMess.cpp
src/JackAudioInterface.cpp
src/JackAudioInterface.h
src/JackTrip.cpp
src/JackTrip.h
src/JackTripThread.cpp
src/JackTripWorker.cpp
src/JackTripWorker.h
src/JitterBuffer.cpp [new file with mode: 0644]
src/JitterBuffer.h [new file with mode: 0644]
src/Limiter.cpp [new file with mode: 0644]
src/Limiter.h [new file with mode: 0644]
src/PacketHeader.cpp
src/PacketHeader.h
src/ProcessPlugin.h
src/Reverb.cpp [new file with mode: 0644]
src/Reverb.h [new file with mode: 0644]
src/RingBuffer.cpp
src/RingBuffer.h
src/Settings.cpp
src/Settings.h
src/UdpDataProtocol.cpp
src/UdpDataProtocol.h
src/UdpHubListener.cpp
src/UdpHubListener.h
src/build
src/compressordsp.h [new file with mode: 0644]
src/freeverbdsp.h [new file with mode: 0644]
src/freeverbmonodsp.h [new file with mode: 0644]
src/jacktrip.pro
src/jacktrip_globals.cpp
src/jacktrip_globals.h
src/jacktrip_main.cpp
src/jacktrip_types_alt.h [new file with mode: 0644]
src/limiterdsp.h [new file with mode: 0644]
src/makeXcodeproj.sh [new file with mode: 0755]
src/zitarevdsp.h [new file with mode: 0644]
src/zitarevmonodsp.h [new file with mode: 0644]

index 2fe76ba9d515b258689e8a85fb332aea4f9c3df4..c6ff4a8f04e5174ae67aa0f4fffa04575aeb6a1c 100644 (file)
@@ -9,8 +9,9 @@ language:
 dist: bionic
 
 branches:
+  - main
   - dev
-
+  
 env:
   global:
   - PKGS_OSX="jack qt rt-audio"
index 00398724b62443c5ebe685df30f77a9942ba8433..139ba355f36e9b62c2364331486ba803706d7042 100644 (file)
@@ -1,3 +1,31 @@
+---
+1.3.0
+- (added) async networking in hub listener
+- (added) limiter, compressor, reverb
+- (added) examine audio delay
+- (added) jitter buffer alternatives
+- (added) broadcast output ports
+- (added) PREFIX variable for installation path
+- (added) disconnect on timeout
+- (added) SIGTERM
+- (added) simulate packet loss, jitter
+- (added) hubpatch 5, no auto patching
+- (added) jack client name length check
+- (added) scripts/hubMode/test_hub_mode_server_and_client.sh
+- (fixed) misc. typos, indentation
+- (fixed) short form IO stat options
+- (fixed) nullptr jack server name when creating jack client
+- (fixed) stop ring buffer blocking when jack has been stopped
+- (fixed) JMess handling of non-western characters
+- (fixed) closing curly brace on mJackTrip client creation
+- (fixed) Warnings
+- (fixed) remove rtaudio device and mpeeraddress msgs.
+- (fixed) signal and slot connections
+- (fixed) incorrect dependency from jacktrip_main
+- (update) RT thread priority for network I/O
+- (update) clipping to saturation
+- (update) build instructions
+
 ---
 1.2.2 (main)
 - (added) bindPort range to reject oddball connections
diff --git a/JMess.cpp b/JMess.cpp
deleted file mode 100644 (file)
index 2708bc4..0000000
--- a/JMess.cpp
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
-  JMess: A simple utility so save your jack-audio mess.
-
-  Copyright (C) 2007-2010 Juan-Pablo Caceres.
-
-  Permission is hereby granted, free of charge, to any person
-  obtaining a copy of this software and associated documentation
-  files (the "Software"), to deal in the Software without
-  restriction, including without limitation the rights to use,
-  copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the
-  Software is furnished to do so, subject to the following
-  conditions:
-
-  The above copyright notice and this permission notice shall be
-  included in all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-  OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-
-/*
- * JMess.cpp
- */
-
-#include "JMess.h"
-#include "JackTrip.h"
-#include "jacktrip_globals.h"
-#include <QDebug>
-
-
-//-------------------------------------------------------------------------------
-/*! \brief Constructs a JMess object that has a jack client.
- *
- */
-//-------------------------------------------------------------------------------
-JMess::JMess()
-{
-    //Open a client connection to the JACK server.  Starting a
-    //new server only to list its ports seems pointless, so we
-    //specify JackNoStartServer.
-    mClient = jack_client_open ("lsp", JackNoStartServer, &mStatus);
-    if (mClient == NULL) {
-        if (mStatus & JackServerFailed) {
-            cerr << "JACK server not running" << endl;
-        } else {
-            cerr << "jack_client_open() failed, "
-                 << "status = 0x%2.0x\n" << mStatus << endl;
-        }
-        exit(1);
-    }
-}
-
-
-//-------------------------------------------------------------------------------
-/*! \brief Distructor closes the jmess jack audio client.
- *
- */
-//-------------------------------------------------------------------------------
-JMess::~JMess()
-{
-    if (jack_client_close(mClient))
-        cerr << "ERROR: Could not close the hidden jmess jack client." << endl;
-}
-
-
-//-------------------------------------------------------------------------------
-/*! \brief Write an XML file with the name specified at xmlOutFile.
- *
- */
-//-------------------------------------------------------------------------------
-void JMess::writeOutput(QString xmlOutFile)
-{
-    //  QDomDocument jmess_xml;   QDomElement root;
-    //  QDomElement connection;   QDomElement output;
-    //  QDomElement input;        QDomText output_name;
-    //  QDomText input_name;
-
-    //  QVector<QString> OutputInput(2);
-
-    //  this->setConnectedPorts();
-
-    //  root = jmess_xml.createElement("jmess");
-    //  for (QVector<QVector<QString> >::iterator it = mConnectedPorts.begin();
-    //       it != mConnectedPorts.end(); ++it) {
-    //    OutputInput = *it;
-    //    //cout << "Output ===> " <<qPrintable(OutputInput[0]) << endl;
-    //    //cout << "Input ===> " <<qPrintable(OutputInput[1]) << endl;
-
-    //    //Initialize XML elements
-    //    connection = jmess_xml.createElement("connection");
-    //    output = jmess_xml.createElement("output");
-    //    input = jmess_xml.createElement("input");
-    //    output_name = jmess_xml.createTextNode(OutputInput[0]);
-    //    input_name = jmess_xml.createTextNode(OutputInput[1]);
-
-    //    jmess_xml.appendChild(root);      root.appendChild(connection);
-    //    connection.appendChild(output);   connection.appendChild(input);
-    //    output.appendChild(output_name);  input.appendChild(input_name);
-    //  }
-
-    //  //Write output file
-    //  QFile file(xmlOutFile);
-    //  string answer = "";
-    //  //Check for existing file first, and confirm before overwriting
-    //  if (file.exists()) {
-    //    while ((answer != "yes") && (answer != "no")) {
-    //      cout << "WARNING: The File " <<qPrintable(xmlOutFile)
-    //    << " exists. Do you want to overwrite it? (yes/no): ";
-    //      cin >> answer;
-    //    }
-    //  }
-    //  else {
-    //    answer = "yes";
-    //  }
-
-    //  if (answer == "yes") {
-    //    if (!file.open(QIODevice::WriteOnly)) {
-    //      cerr << "Cannot open file for writing: "
-    //    << qPrintable(file.errorString()) << endl;
-    //      exit(1);
-    //    }
-
-    //    QTextStream out(&file);
-    //    jmess_xml.save(out, Indent);
-    //    cout << qPrintable(xmlOutFile) << " written." << endl;
-    //  }
-}
-
-
-//-------------------------------------------------------------------------------
-/*! \brief Set list of ouput ports that have connections.
- *
- */
-//-------------------------------------------------------------------------------
-void JMess::setConnectedPorts()
-{
-    mConnectedPorts.clear();
-
-    const char **ports, **connections; //vector of ports and connections
-    QVector<QString> OutputInput(2); //helper variable
-
-    //Get active output ports.
-    ports = jack_get_ports (mClient, NULL, NULL, JackPortIsOutput);
-
-    for (unsigned int out_i = 0; ports[out_i]; ++out_i) {
-        if ((connections = jack_port_get_all_connections
-             (mClient, jack_port_by_name(mClient, ports[out_i]))) != 0) {
-            for (unsigned int in_i = 0; connections[in_i]; ++in_i) {
-                OutputInput[0] = ports[out_i];
-                //    cout << "Output ===> " <<qPrintable(OutputInput[0]) << endl;
-                OutputInput[1] = connections[in_i];
-                //    cout << "Input ===> " << qPrintable(OutputInput[1]) << endl;
-                mConnectedPorts.append(OutputInput);
-            }
-        }
-    }
-
-    free(ports);
-}
-//*******************************************************************************
-void JMess::connectSpawnedPorts(int nChans, int hubPatch)
-// called from UdpHubListener::connectMesh
-{
-    QString IPS[gMAX_WAIRS];
-    int ctr = 0;
-
-    const char **ports, **connections; //vector of ports and connections
-    QVector<QString> OutputInput(2); //helper variable
-
-    //Get active output ports.
-    ports = jack_get_ports (mClient, NULL, NULL, JackPortIsOutput);
-
-    for (unsigned int out_i = 0; ports[out_i]; ++out_i) {
-        //        qDebug() << QString(ports[out_i]);
-        bool systemPort = QString(ports[out_i]).contains(QString("system"));
-
-        QString str = QString(ports[out_i]);
-        //  for example              "171.64.197.121:receive_1"
-        QString s = str.section(':', 0, 0);
-        //        qDebug() << s << systemPort;
-        //  for example              "171.64.197.121"
-
-        bool newOne = !systemPort;
-        for (int i = 0; i<ctr; i++) if (newOne && (IPS[i]==s)) newOne = false;
-        if (newOne)
-        {
-            IPS[ctr] = s;
-            ctr++;
-            //                        qDebug() << ports[out_i] << systemPort << s;
-        }
-    }
-    for (int i = 0; i<ctr; i++) qDebug() << IPS[i];
-    disconnectAll();
-
-    int k = 0;
-    int jLimit = 1;
-
-    // FULLMIX is the union of CLIENTFOFI, CLIENTECHO
-
-    // implements CLIENTFOFI, CLIENTECHO -- also FULLMIX part which is CLIENTECHO
-    for (int i = 0; i<ctr; i++) {
-        if (hubPatch == JackTrip::CLIENTFOFI) jLimit = (ctr-1);
-        for (int j = 0; j<jLimit; j++) {
-            if ((hubPatch == JackTrip::CLIENTECHO)||(hubPatch == JackTrip::FULLMIX)) k = i;
-            else if (hubPatch == JackTrip::CLIENTFOFI) k = (j+(i+1))%ctr;
-            for (int l = 1; l<=nChans; l++) { // chans are 1-based
-                qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l)
-                         <<"with " << IPS[k]+":send_"+QString::number(l);
-
-                QString left = IPS[i] +
-                        ":receive_" + QString::number(l);
-                QString right = IPS[k] +
-                        ":send_" + QString::number(l);
-
-                if (0 !=
-                        jack_connect(mClient, left.toStdString().c_str(), right.toStdString().c_str())) {
-                    qDebug() << "WARNING: port: " << left
-                             << "and port: " << right
-                             << " could not be connected.";
-                }
-            }
-        }
-    }
-
-    // do it again to implement the FULLMIX part which is CLIENTFOFI
-    if (hubPatch == JackTrip::FULLMIX) {
-        jLimit = (ctr-1); // same as CLIENTFOFI
-        /*************/
-        // todo: the next block should be in a method, it's a repeat of the above
-        for (int i = 0; i<ctr; i++) {
-            for (int j = 0; j<jLimit; j++) {
-                k = (j+(i+1))%ctr;
-                for (int l = 1; l<=nChans; l++) { // chans are 1-based
-                    qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l)
-                             <<"with " << IPS[k]+":send_"+QString::number(l);
-
-                    QString left = IPS[i] +
-                            ":receive_" + QString::number(l);
-                    QString right = IPS[k] +
-                            ":send_" + QString::number(l);
-
-                    if (0 !=
-                            jack_connect(mClient, left.toStdString().c_str(), right.toStdString().c_str())) {
-                        qDebug() << "WARNING: port: " << left
-                                 << "and port: " << right
-                                 << " could not be connected.";
-                    }
-                }
-            }
-        }
-    }
-
-    free(ports);
-}
-
-//*******************************************************************************
-// connectTUB is called when in hubpatch mode 4 = RESERVEDMATRIX
-// TU Berlin Raspberry Pi ensemble, Winter 2019
-// this gets run on the ensemble's hub server with
-// ./jacktrip -S -p3
-// it connects a set of client jacktrips with known hardwired IP addresses
-// to a known hardwired audio process with known hardwired audio port names
-// when clients connect / disconnect dynamically this just runs through the
-// audio connection sequence bruteforce at every new connection change
-// those that are preexisting won't change
-// a new one will connect accordingly and
-// those that fail because they don't exist will fail, no worries
-
-// setting the connections tested with jacktrip_globals.h
-// const QString gDOMAIN_TRIPLE = QString("130.149.23"); // for TUB multiclient hub
-// const int gMIN_TUB = 215; // lowest client address
-// const int gMAX_TUB = 215; // highest client address
-
-///////////////////////////////
-// test NUC as server
-//#define HARDWIRED_AUDIO_PROCESS_ON_SERVER "par20straightWire"
-//#define ENUMERATE ""
-//#define HARDWIRED_AUDIO_PROCESS_ON_SERVER_IN ":in_"
-//#define HARDWIRED_AUDIO_PROCESS_ON_SERVER_OUT ":out_"
-
-///////////////////////////////
-// test Riviera as server
-// for deployment change jacktrip_globals.h to
-// const QString gDOMAIN_TRIPLE = QString("192.168.0"); // for TUB multiclient hub
-// const int gMIN_TUB = 11; // lowest client address
-// const int gMAX_TUB = 20; // highest client address
-// and give the proper audio process and connection names
-
-#define HARDWIRED_AUDIO_PROCESS_ON_SERVER "SuperCollider"
-#define HARDWIRED_AUDIO_PROCESS_ON_SERVER_IN ":in_"
-#define HARDWIRED_AUDIO_PROCESS_ON_SERVER_OUT ":out_"
-// On server side it is SC jack-clients with indivisual names:
-// POE_0...POE_16
-// and each has (at this moment) one port in/out:
-// receive_1
-// send_1
-// I think it should be extended to 4 in/out ports per client.
-
-// this is brute force, does not look at individual clients, just patches the whole ensemble
-// each time
-void JMess::connectTUB(int /*nChans*/)
-// called from UdpHubListener::connectPatch
-{
-    for (int i = 0; i<=gMAX_TUB-gMIN_TUB; i++) // last IP decimal octet
-        for (int l = 1; l<=1; l++) // mono for now // chans are 1-based, 1...2
-        {
-            // jacktrip to SC
-            QString client = gDOMAIN_TRIPLE + QString(".") + QString::number(gMIN_TUB+i);
-            QString serverAudio = QString(HARDWIRED_AUDIO_PROCESS_ON_SERVER);
-            int tmp = i + l; // only works for mono... completely wrong for 2 or more chans
-            qDebug() << "connect " << client << ":receive_ " << l
-                     <<"with " << serverAudio << HARDWIRED_AUDIO_PROCESS_ON_SERVER_IN << tmp;
-
-            QString left = QString(client + ":receive_" + QString::number(l));
-            QString right = QString(serverAudio + HARDWIRED_AUDIO_PROCESS_ON_SERVER_IN +
-                                    QString::number(tmp));
-
-            if (0 !=
-                    jack_connect(mClient, left.toStdString().c_str(),
-                                 right.toStdString().c_str())) {
-                qDebug() << "WARNING: port: " << left
-                         << "and port: " << right
-                         << " could not be connected.";
-            }
-
-            // SC to jacktrip
-            tmp += 4; // increase tmp for port offest
-            qDebug() << "connect " << serverAudio << HARDWIRED_AUDIO_PROCESS_ON_SERVER_OUT
-                     << tmp <<"with " << client << ":send_" << l;
-
-            left = QString(serverAudio + HARDWIRED_AUDIO_PROCESS_ON_SERVER_OUT +
-                           QString::number(tmp));
-            right = QString(client + ":send_" + QString::number(l));
-
-            if (0 !=
-                    jack_connect(mClient, left.toStdString().c_str(),
-                                 right.toStdString().c_str())) {
-                qDebug() << "WARNING: port: " << left
-                         << "and port: " << right
-                         << " could not be connected.";
-            }
-
-        }
-}
-
-//-------------------------------------------------------------------------------
-/*! \brief Disconnect all the clients.
- *
- */
-//-------------------------------------------------------------------------------
-void JMess::disconnectAll()
-{
-    QVector<QString> OutputInput(2);
-
-    this->setConnectedPorts();
-
-    for (QVector<QVector<QString> >::iterator it = mConnectedPorts.begin();
-         it != mConnectedPorts.end(); ++it) {
-        OutputInput = *it;
-
-        if (jack_disconnect(mClient, OutputInput[0].toLatin1(), OutputInput[1].toLatin1())) {
-            cerr << "WARNING: port: " << qPrintable(OutputInput[0])
-                    << "and port: " << qPrintable(OutputInput[1])
-                    << " could not be disconnected.\n";
-        }
-    }
-
-}
-
-
-//-------------------------------------------------------------------------------
-/*! \brief Parse the XML input file.
- *
- * Returns 0 on success, or 1 if the file has an incorrect format or cannot
- * read the file.
- */
-//-------------------------------------------------------------------------------
-int JMess::parseXML(QString xmlInFile)
-{
-    //  mPortsToConnect.clear();
-    //  QString errorStr;
-    //  int errorLine;
-    //  int errorColumn;
-
-    //  QFile file(xmlInFile);
-    //  if (!file.open(QIODevice::ReadOnly)) {
-    //    cerr << "Cannot open file for reading: "
-    //  << qPrintable(file.errorString()) << endl;
-    //    return 1;
-    //  }
-
-    //  QDomDocument doc;
-    //  if (!doc.setContent(&file, true, &errorStr, &errorLine,
-    //               &errorColumn)) {
-    //    cerr << "===================================================\n"
-    //  << "Error parsing XML input file:\n"
-    //  << "Parse error at line " << errorLine
-    //  << ", column " << errorColumn << "\n"
-    //  << qPrintable(errorStr) << "\n"
-    //  << "===================================================\n";
-    //    return 1;
-    //  }
-
-    //  QDomElement jmess = doc.documentElement();
-    //  if (jmess.tagName() != "jmess") {
-    //    cerr << "Error: Root tag should be <jmess>: "
-    //  << qPrintable(jmess.tagName()) << endl;
-    //    return 1;
-    //  }
-
-
-    //  QVector<QString> OutputInput(2);
-    //  //First check for <connection> tag
-    //  for(QDomNode n_cntn = jmess.firstChild();
-    //      !n_cntn.isNull(); n_cntn = n_cntn.nextSibling()) {
-    //    QDomElement cntn = n_cntn.toElement();
-    //    if (cntn.tagName() == "connection") {
-    //      //Now check for ouput & input tag
-    //      for(QDomNode n_sck = cntn.firstChild();
-    //   !n_sck.isNull(); n_sck = n_sck.nextSibling()) {
-    // QDomElement sck = n_sck.toElement();
-    // //cout << qPrintable(sck.tagName()) << endl;
-    // //cout << qPrintable(sck.text()) << endl;
-    // if (sck.tagName() == "output") {
-    //   OutputInput[0] = sck.text();
-    // }
-    // else if (sck.tagName() == "input") {
-    //   OutputInput[1] = sck.text();
-    // }
-    //      }
-    //      mPortsToConnect.append(OutputInput);
-    //    }
-    //  }
-
-    return 0;
-
-}
-
-
-//-------------------------------------------------------------------------------
-/*! \brief Connect ports specified in input XML file xmlInFile
- *
- */
-//-------------------------------------------------------------------------------
-void JMess::connectPorts(QString xmlInFile)
-{
-    QVector<QString> OutputInput(2);
-
-    //  if ( !(this->parseXML(xmlInFile)) ) {
-    //    for (QVector<QVector<QString> >::iterator it = mPortsToConnect.begin();
-    //  it != mPortsToConnect.end(); ++it) {
-    //      OutputInput = *it;
-
-    //      if (jack_connect(mClient, OutputInput[0].toLatin1(), OutputInput[1].toLatin1())) {
-    // //Display a warining only if the error is not because the ports are already
-    // //connected, in case the program doesn't display anyting.
-    // if (EEXIST !=
-    //        jack_connect(mClient, OutputInput[0].toLatin1(), OutputInput[1].toLatin1())) {
-    //   cerr << "WARNING: port: " << qPrintable(OutputInput[0])
-    //        << "and port: " << qPrintable(OutputInput[1])
-    //        << " could not be connected.\n";
-    // }
-    //      }
-    //    }
-    //  }
-
-}
diff --git a/WIN10BUILDINSTRUCTIONS.pdf b/WIN10BUILDINSTRUCTIONS.pdf
deleted file mode 100644 (file)
index 28901e0..0000000
Binary files a/WIN10BUILDINSTRUCTIONS.pdf and /dev/null differ
index 14ffe57b33bf4fda2286414e4f937cda1f9f3265..2eebaf7926810b96f4d1d6f4047a8014cbfd0f95 100644 (file)
@@ -1,17 +1,26 @@
+h headers :
+       install -d headersdir
+       $(MAKE) DEST='headersdir/' ARCH='faust2header.cpp' -f Makefile.headers
+
+ih hi installheaders : headers
+       install -m 0444 headersdir/*.h ../src/
+
+# The JackTrip project does not need the remaining make targets, except "clean" is nice to have:
+
 all : jackgtk
 
 test: jackgtk alsagtk jackqt alsaqt ladspa ossgtk bench plot sndfile jackconsole
 
 svg:
        $(MAKE) -f Makefile.svg
-       
+
 puredata :
        install -d puredatadir
        $(MAKE) DEST='puredatadir/' ARCH='puredata.cpp' LIB='' -f Makefile.pdcompile
 
 alsagtk :
        install -d alsagtkdir
-       $(MAKE) DEST='alsagtkdir/' ARCH='alsa-gtk.cpp' LIB='-lpthread -lasound  `pkg-config --cflags --libs gtk+-2.0`' -f Makefile.compile
+       $(MAKE) DEST='alsagtkdir/' ARCH='alsa-gtk.cpp' LIB='-lpthread -lasound  `pkg-config --cflags --libs gtk+-2.0`' -f Makefile.compile
 
 jackgtk :
        install -d jackgtkdir
@@ -28,7 +37,7 @@ alsaqt :
 ladspa :
        install -d ladspadir
        $(MAKE) DEST='ladspadir/' ARCH='ladspa.cpp' LIB='-fPIC -shared' EXT='.so' -f Makefile.ladspacompile
-       
+
 jackwx :
        install -d jackwxdir
        $(MAKE) DEST='jackwxdir/' ARCH='jack-wx.cpp' LIB='`pkg-config jack  --cflags --libs` `wx-config --cflags --libs`' -f Makefile.compile
@@ -47,7 +56,7 @@ pagtk :
 
 pawx :
        install -d pawxdir
-       $(MAKE) DEST='pawxdir/' ARCH='pa-wx.cpp' LIB='-lpthread  -lportaudio `wx-config --cflags --libs`' -f Makefile.compile
+       $(MAKE) DEST='pawxdir/' ARCH='pa-wx.cpp' LIB='-lpthread  -lportaudio `wx-config --cflags --libs`' -f Makefile.compile
 
 module :
        install -d moduledir
@@ -94,6 +103,4 @@ jackconsole :
        $(MAKE) DEST='jackconsoledir/' ARCH='jack-console.cpp' LIB='`pkg-config --cflags --libs jack `' -f Makefile.compile
 
 clean :
-       rm -rf alsagtkdir jackgtkdir alsaqtdir jackqtdir vecalsagtkdir vecjackgtkdir ladspadir jackwxdir ossgtkdir osswxdir pagtkdir pawxdir moduledir bundledir mspdir vstdir benchdir sndfiledir plotdir benchdir supercolliderdir puredatadir qdir plotdir jackconsoledir matlabplotdir *-ps *-svg
-
-
+       -rm -rf alsagtkdir jackgtkdir alsaqtdir jackqtdir vecalsagtkdir vecjackgtkdir ladspadir jackwxdir ossgtkdir osswxdir pagtkdir pawxdir moduledir bundledir mspdir vstdir benchdir sndfiledir plotdir benchdir supercolliderdir puredatadir qdir plotdir jackconsoledir matlabplotdir *-ps *-svg headersdir
diff --git a/faust-src/Makefile.headers b/faust-src/Makefile.headers
new file mode 100644 (file)
index 0000000..e2304de
--- /dev/null
@@ -0,0 +1,27 @@
+dspsrc  := $(wildcard *.dsp)
+headers  := $(addprefix $(DEST), $(dspsrc:.dsp=.h))
+
+all :  $(headers)
+
+FAUST = faust --inline-architecture-files --in-place
+# --in-place (-inpl) means the input signal array can be used
+#           as the output signal array in the compute() function.
+# --inline-architecture-files (-i) means that all Faust headers
+#           and include files get included in the output C++ file
+#           so that it can be compiled without a Faust installation
+#           on the compiling machine.
+
+$(DEST)%.h : %.dsp
+       $(FAUST) $(VEC) -cn $(<:.dsp=) -a $(ARCH) $< -o $(DEST)$(<:.dsp=).h
+       #(cat $(@:.h=).cpp | sed -e s/mydsp/$(<:.dsp=)/g) > $@
+       #/bin/rm $(@:.h=).cpp
+
+# Probably needed for headers after the first: (cat $(@:.h=).cpp | sed -e /template/d | sed -e s/mydsp/$(<:.dsp=)/g) > $@
+
+clean :
+       rm -f $(DEST)
+
+# Working example:
+# lh limiterdsp.h: limiterdsp.dsp
+#      faust -inpl -cn limiterdsp -a faust2header.cpp limiterdsp.dsp -o limiterdsp.cpp
+#      (cat limiterdsp.cpp | sed -e /template/d | sed -e s/mydsp/limiterdsp/g) > limiterdsp.h
diff --git a/faust-src/README-Limiter.md b/faust-src/README-Limiter.md
new file mode 100644 (file)
index 0000000..9caf104
--- /dev/null
@@ -0,0 +1,23 @@
+The audio limiter in use by jacktrip is limiterdsp.dsp
+
+To regenerate ../src/limiterdsp.h, you can say (if you have Faust installed)
+
+  make headers
+
+in this directory to create ./headersdir/limiterdsp.h
+and then copy that to ../src/ to reinstall it.
+
+To test the limiter separately, you can load it into
+fausteditor.grame.fr to compile and download an executable.  With
+Faust installed, you can do it at the command line:  To make a
+standalone JACK app for Mac, say
+
+  faust2jaqt limiterdsp.dsp
+
+For Linux, you want either that or
+
+  faust2jack limiterdsp.dsp
+
+(to use GTK in place of Qt for the GUI).
+
+Then you just run it and patch it in (using qjackctl) between your audio capture and jacktrip input.
diff --git a/faust-src/compressor-limiter-test.dsp b/faust-src/compressor-limiter-test.dsp
new file mode 100644 (file)
index 0000000..7384714
--- /dev/null
@@ -0,0 +1,2 @@
+process = _ : component("compressordsp.dsp") : component("limiterdsp.dsp") <: _,_;
+
diff --git a/faust-src/compressordsp.dsp b/faust-src/compressordsp.dsp
new file mode 100644 (file)
index 0000000..646ddc9
--- /dev/null
@@ -0,0 +1,73 @@
+declare name "compressor";
+declare version "0.0";
+declare author "Julius Smith";
+declare license "MIT Style STK-4.2";
+declare description "Compressor demo application, adapted from the Faust Library's dm.compressor_demo in demos.lib";
+declare documentation "https://faustlibraries.grame.fr/libs/compressors/#cocompressor_mono";
+
+import("stdfaust.lib");
+
+//----------------------------`(dm.)compressor_mono_demo`-------------------------
+// Mono Compressor
+//
+// #### Usage
+//
+// ```
+// _ : compressor_mono_demo : _;
+// ```
+//------------------------------------------------------------
+compressor_demo = ba.bypass1(cbp,compressor_mono_demo)
+with {
+        comp_group(x) = vgroup("COMPRESSOR [tooltip: References:
+                https://faustlibraries.grame.fr/libs/compressors/
+                http://en.wikipedia.org/wiki/Dynamic_range_compression]", x);
+
+        meter_group(x)  = comp_group(hgroup("[0]", x));
+        knob_group(x)  = comp_group(hgroup("[1]", x));
+
+        cbp = meter_group(checkbox("[0] Bypass  [tooltip: When this is checked, the compressor
+                has no effect]"));
+        gainview = co.compression_gain_mono(ratio,threshold,attack,release) : ba.linear2db :
+        meter_group(hbargraph("[1] Compressor Gain [unit:dB] [tooltip: Compressor gain in dB]",-50,+10));
+
+        displaygain = _ <: _,abs : _,gainview : attach;
+
+        compressor_stereo_demo =
+        displaygain(co.compressor_stereo(ratio,threshold,attack,release)) :
+        *(makeupgain), *(makeupgain);
+
+        compressor_mono_demo =
+        displaygain(co.compressor_mono(ratio,threshold,attack,release)) :
+        *(makeupgain);
+
+        ctl_group(x)  = knob_group(hgroup("[3] Compression Control", x));
+
+        ratio = ctl_group(hslider("[0] Ratio [style:knob]
+        [tooltip: A compression Ratio of N means that for each N dB increase in input
+        signal level above Threshold, the output level goes up 1 dB]",
+        2, 1, 20, 0.1));
+
+        threshold = ctl_group(hslider("[1] Threshold [unit:dB] [style:knob]
+        [tooltip: When the signal level exceeds the Threshold (in dB), its level
+        is compressed according to the Ratio]",
+        -24, -100, 10, 0.1));
+
+        env_group(x)  = knob_group(hgroup("[4] Compression Response", x));
+
+        attack = env_group(hslider("[1] Attack [unit:ms] [style:knob] [scale:log]
+        [tooltip: Time constant in ms (1/e smoothing time) for the compression gain
+        to approach (exponentially) a new lower target level (the compression
+        `kicking in')]", 15, 1, 1000, 0.1)) : *(0.001) : max(1/ma.SR);
+
+        release = env_group(hslider("[2] Release [unit:ms] [style: knob] [scale:log]
+        [tooltip: Time constant in ms (1/e smoothing time) for the compression gain
+        to approach (exponentially) a new higher target level (the compression
+        'releasing')]", 40, 1, 1000, 0.1)) : *(0.001) : max(1/ma.SR);
+
+        makeupgain = comp_group(hslider("[5] MakeUpGain [unit:dB]
+        [tooltip: The compressed-signal output level is increased by this amount
+        (in dB) to make up for the level lost due to compression]",
+        2, -96, 96, 0.1)) : ba.db2linear;
+};
+
+process = _ : compressor_demo : _;
diff --git a/faust-src/faust2header.cpp b/faust-src/faust2header.cpp
new file mode 100644 (file)
index 0000000..3404470
--- /dev/null
@@ -0,0 +1,21 @@
+// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME
+//
+// faust2header.cpp - FAUST Architecture File
+// This is a simple variation of matlabplot.cpp in the Faust distribution
+// aimed at creating a simple C++ header file (.h) containing a Faust DSP.
+// See the Makefile for how to use it.
+
+#include <faust/dsp/dsp.h>
+
+#include <faust/gui/APIUI.h>
+
+// NOTE: "faust -scn name" changes the last line above to
+// #include <faust/name/name.h>
+
+//----------------------------------------------------------------------------
+//  FAUST Generated Code
+//----------------------------------------------------------------------------
+
+<<includeIntrinsic>>
+
+<<includeclass>>
diff --git a/faust-src/freeverbdsp.dsp b/faust-src/freeverbdsp.dsp
new file mode 100644 (file)
index 0000000..354a885
--- /dev/null
@@ -0,0 +1,45 @@
+declare name "freeverb";
+declare version "0.0";
+declare author "Romain Michon";
+declare license "LGPL";
+declare description "Freeverb implementation in Faust, from the Faust Library's dm.freeverb_demo in demos.lib";
+
+import("stdfaust.lib");
+
+//----------------------------`(dm.)freeverb_demo`-------------------------
+// Freeverb demo application.
+//
+// #### Usage
+//
+// ```
+// _,_ : freeverb_demo : _,_;
+// ```
+//------------------------------------------------------------
+// Author: Romain Michon
+// License: LGPL
+freeverb_demo = _,_ <: (*(g)*fixedgain,*(g)*fixedgain :
+       re.stereo_freeverb(combfeed, allpassfeed, damping, spatSpread)),
+       *(1-g), *(1-g) :> _,_
+with{
+       scaleroom   = 0.28;
+       offsetroom  = 0.7;
+       allpassfeed = 0.5;
+       scaledamp   = 0.4;
+       fixedgain   = 0.1;
+       origSR = 44100;
+
+       parameters(x) = hgroup("Freeverb",x);
+       knobGroup(x) = parameters(vgroup("[0]",x));
+       damping = knobGroup(vslider("[0] Damp [style: knob] [tooltip: Somehow control the
+               density of the reverb.]",0.5, 0, 1, 0.025)*scaledamp*origSR/ma.SR);
+       combfeed = knobGroup(vslider("[1] RoomSize [style: knob] [tooltip: The room size
+               between 0 and 1 with 1 for the largest room.]", 0.1, 0, 1, 0.025)*scaleroom*
+               origSR/ma.SR + offsetroom);
+       spatSpread = knobGroup(vslider("[2] Stereo Spread [style: knob] [tooltip: Spatial
+               spread between 0 and 1 with 1 for maximum spread.]",0.5,0,1,0.01)*46*ma.SR/origSR
+               : int);
+       g = parameters(vslider("[1] Wet [tooltip: The amount of reverb applied to the signal
+               between 0 and 1 with 1 for the maximum amount of reverb.]", 0.1, 0, 1, 0.025));
+};
+
+process = freeverb_demo;
diff --git a/faust-src/freeverbmonodsp.dsp b/faust-src/freeverbmonodsp.dsp
new file mode 100644 (file)
index 0000000..8008c48
--- /dev/null
@@ -0,0 +1,2 @@
+process = _ <: _,_ : component("freeverbdsp.dsp") : _,_ :> _; // there is no mono-to-stereo support in jacktrip
+
diff --git a/faust-src/limiterdsp.dsp b/faust-src/limiterdsp.dsp
new file mode 100644 (file)
index 0000000..e177d64
--- /dev/null
@@ -0,0 +1,11 @@
+// Version added to JackTrip (standalone program test):
+
+import("stdfaust.lib");
+N = hslider("[0] NumClientsAssumed",2,1,64,1);
+softClipLevel = 0.5; // start compressing at this amplitude - KEEP IN SYNC with setWarningAmplitude() in ../src/Effects.h
+gain = 1.0 / sqrt(float(N)); // assume power-based client sum - KEEP IN SYNC with limiterAmp in ../src/Limiter.h
+// lookahead(s), threshold, attack(s), hold(s), release(s)
+limiter = co.limiter_lad_mono(0.0001, softClipLevel, 0.00001, 0.1, 0.25); // GPLv3 license
+// If you need a less restricted license, try co.limiter_1176_R4_mono (MIT style license)
+
+process = *(gain) : limiter;
diff --git a/faust-src/limitertest.dsp b/faust-src/limitertest.dsp
new file mode 100644 (file)
index 0000000..8d236b5
--- /dev/null
@@ -0,0 +1,7 @@
+// Test signal used by ../src/Limiter.cpp
+
+import("stdfaust.lib");
+freq = hslider("[0] Freq",110.0,20.0,10000.0,1);
+amp = hslider("[0] Amp",0.2,0.0,1.0,0.0001);
+//process = amp * os.oscrs(freq);
+process = amp * os.sawtooth(freq);
diff --git a/faust-src/tests/compressor-limiter-test.dsp b/faust-src/tests/compressor-limiter-test.dsp
new file mode 100644 (file)
index 0000000..64577e4
--- /dev/null
@@ -0,0 +1,21 @@
+// Doc: https://faustlibraries.grame.fr/libs/compressors/
+
+cs = hslider("Compressor [style:radio{'1':0; '2': 1 }]", 0,0,1,1);
+
+c1 = component("compressordsp.dsp");  // ./compressordsp.dsp
+c2 = component("compressor2dsp.dsp"); // ./compressor2dsp.dsp
+compressor(cs) = _ <: select2(cs,c1,c2);
+
+limiter_group(x) = vgroup("LIMITER [tooltip: https://faustlibraries.grame.fr/libs/compressors/#functions-reference]",x);
+
+process = _ : compressor(cs) : limiter_group(component("limiterdsp.dsp")) <: _,_;
+
+/*
+ * My present conclusion is to continue with c1, because it uses the
+ * more standard 'ratio' parameter, while c2 uses 'strength', which becomes
+ * hard-clipping at strength=1.  Also, I hear no compelling difference sonically
+ * (after laboriously finding a strength value that is roughly comparable to ratio).
+ *
+ * Also, c2 is GPL license, which cannot go into closed-source products,
+ * while c1 can be freely used as desired (STK-4.2 license).
+ */
diff --git a/faust-src/tests/compressor2dsp.dsp b/faust-src/tests/compressor2dsp.dsp
new file mode 100644 (file)
index 0000000..831bfd0
--- /dev/null
@@ -0,0 +1,82 @@
+declare name "compressor2"; // more modern feedback-compressor with release-to-threshold
+declare version "0.0";
+declare author "Julius Smith";
+declare license "MIT Style STK-4.2"; // but using GPLv3
+declare description "adapted from ./compressordsp.dsp adding use of co.FBFFcompressor_N_chan";
+declare documentation "https://faustlibraries.grame.fr/libs/compressors/#cofffbcompressor_n_chan";
+
+import("stdfaust.lib");
+
+// #### Usage
+//
+// ```
+// _ : compressor2_mono_demo : _;
+// ```
+//------------------------------------------------------------
+compressor2_demo = ba.bypass1(cbp,compressor2_mono_demo)
+with {
+       comp_group(x) = vgroup("COMPRESSOR2 [tooltip: Reference:
+               http://en.wikipedia.org/wiki/Dynamic_range_compression]", x);
+
+       meter_group(x)  = comp_group(hgroup("[0]", x));
+       knob_group(x)  = comp_group(hgroup("[1]", x));
+
+       cbp = meter_group(checkbox("[0] Bypass  [tooltip: When this is checked, the compressor2
+               has no effect]"));
+
+       // API: co.FBFFcompressor_N_chan(strength,thresh,att,rel,knee,prePost,link,FBFF,meter,N)
+       // strength = min(ratio-1.0,5)/5.0; // crude hack - will be wrong
+       knee = 5; // dB window about threshold for knee
+       prePost = 1; // level detector location: 0 for input, 1 for output (for feedback compressor)
+       link = 0; // linkage between channels (irrelevant for mono)
+       FBFF = 1; // cross-fade between feedforward (0) and feedback (1) compression
+       maxGR = -50; // dB - Max Gain Reduction (only affects display)
+       meter = _<:(_, (ba.linear2db:max(maxGR):meter_group((hbargraph("[1] Compressor Gain [unit:dB][tooltip: Compressor gain in dB]", maxGR, 10))))):attach;
+       //meter = _; // use gainview below instead to look more like compressordsp.dsp
+       NChans = 1;
+
+       // compressordsp.dsp: gainview = co.compression_gain_mono(strength,threshold,attack,release) 
+       // threshold gets doubled for the feedback case, but not for feedforward (see compressors.lib):
+       gainview = co.peak_compression_gain_N_chan(strength,2*threshold,attack,release,knee,prePost,link,NChans)
+       : ba.linear2db : max(maxGR) :
+       meter_group(hbargraph("[1] Compressor2 Gain [unit:dB] [tooltip: Current gain of
+       the compressor2 in dB]",maxGR,+10));
+
+       // use built-in gain display:
+       displaygain = _;
+       // not the same: displaygain = _ <: _,abs : _,gainview : attach;
+
+       compressor2_mono_demo =
+       displaygain(co.FBFFcompressor_N_chan(strength,threshold,attack,release,knee,prePost,link,FBFF,meter,NChans)) :
+       *(makeupgain);
+
+       ctl_group(x)  = knob_group(hgroup("[3] Compression Control", x));
+
+       strength = ctl_group(hslider("[0] Strength [style:knob]
+       [tooltip: A compression Strength of 0 means no compression, while 1 yields infinit compression (hard limiting)]",
+       0.1, 0, 1, 0.01)); // 0.1 seems to be pretty close to ratio == 2, based on watching the gain displays
+
+       threshold = ctl_group(hslider("[1] Threshold [unit:dB] [style:knob]
+       [tooltip: When the signal level exceeds the Threshold (in dB), its level
+       is compressed according to the Strength]",
+       -24, -100, 10, 0.1));
+
+       env_group(x)  = knob_group(hgroup("[4] Compression Response", x));
+
+       attack = env_group(hslider("[1] Attack [unit:ms] [style:knob] [scale:log]
+       [tooltip: Time constant in ms (1/e smoothing time) for the compression gain
+       to approach (exponentially) a new lower target level (the compression
+       `kicking in')]", 15, 1, 1000, 0.1)) : *(0.001) : max(1/ma.SR);
+
+       release = env_group(hslider("[2] Release [unit:ms] [style: knob] [scale:log]
+       [tooltip: Time constant in ms (1/e smoothing time) for the compression gain
+       to approach (exponentially) a new higher target level (the compression
+       'releasing')]", 40, 1, 1000, 0.1)) : *(0.001) : max(1/ma.SR);
+
+       makeupgain = comp_group(hslider("[5] MakeUpGain [unit:dB]
+       [tooltip: The compressed-signal output level is increased by this amount
+       (in dB) to make up for the level lost due to compression]",
+       2, -96, 96, 0.1)) : ba.db2linear;
+};
+
+process = _ : compressor2_demo : _;
diff --git a/faust-src/zitarevdsp.dsp b/faust-src/zitarevdsp.dsp
new file mode 100644 (file)
index 0000000..b98a83c
--- /dev/null
@@ -0,0 +1,103 @@
+import("stdfaust.lib");
+
+// Modified version from Faust Libraries demos.lib
+
+process = zita_rev1; // same as dm.zita_rev1 but for wetness control and some defaults
+
+//----------------------------------`(dm.)zita_rev1`------------------------------
+// Example GUI for `zita_rev1_stereo` (mostly following the Linux `zita-rev1` GUI).
+//
+// Only the dry/wet and output level parameters are "dezippered" here. If
+// parameters are to be varied in real time, use `smooth(0.999)` or the like
+// in the same way.
+//
+// #### Usage
+//
+// ```
+// _,_ : zita_rev1 : _,_
+// ```
+//
+// #### Reference
+//
+// <http://www.kokkinizita.net/linuxaudio/zita-rev1-doc/quickguide.html>
+//------------------------------------------------------------
+zita_rev1 = _,_ <: re.zita_rev1_stereo(rdel,f1,f2,t60dc,t60m,fsmax),_,_ : out_eq,_,_ :
+       wet_dry_2(wet) : out_level
+with{
+       fsmax = 48000.0;  // highest sampling rate that will be used
+
+       fdn_group(x) = hgroup(
+       "[0] Zita_Rev1 [tooltip: ~ ZITA REV1 FEEDBACK DELAY NETWORK (FDN) & SCHROEDER
+       ALLPASS-COMB REVERBERATOR (8x8). See Faust's reverbs.lib for documentation and
+       references]", x);
+
+       in_group(x) = fdn_group(hgroup("[1] Input", x));
+
+       rdel = in_group(vslider("[1] In Delay [unit:ms] [style:knob] [tooltip: Delay in ms
+               before reverberation begins]",60,20,100,1));
+
+       freq_group(x) = fdn_group(hgroup("[2] Decay Times in Bands (see tooltips)", x));
+
+       f1 = freq_group(vslider("[1] LF X [unit:Hz] [style:knob] [scale:log] [tooltip:
+               Crossover frequency (Hz) separating low and middle frequencies]", 200, 50, 1000, 1));
+
+       t60dc = freq_group(vslider("[2] Low RT60 [unit:s] [style:knob] [scale:log]
+       [style:knob] [tooltip: T60 = time (in seconds) to decay 60dB in low-frequency band]",
+       3, 1, 8, 0.1));
+
+       t60m = freq_group(vslider("[3] Mid RT60 [unit:s] [style:knob] [scale:log] [tooltip:
+               T60 = time (in seconds) to decay 60dB in middle band]",2, 1, 8, 0.1));
+
+       f2 = freq_group(vslider("[4] HF Damping [unit:Hz] [style:knob] [scale:log]
+       [tooltip: Frequency (Hz) at which the high-frequency T60 is half the middle-band's T60]",
+       6000, 1500, 0.49*fsmax, 1));
+
+       out_eq = pareq_stereo(eq1f,eq1l,eq1q) : pareq_stereo(eq2f,eq2l,eq2q);
+       // Zolzer style peaking eq (not used in zita-rev1) (filters.lib):
+       // pareq_stereo(eqf,eql,Q) = peak_eq(eql,eqf,eqf/Q), peak_eq(eql,eqf,eqf/Q);
+       // Regalia-Mitra peaking eq with "Q" hard-wired near sqrt(g)/2 (filters.lib):
+       pareq_stereo(eqf,eql,Q) = fi.peak_eq_rm(eql,eqf,tpbt), fi.peak_eq_rm(eql,eqf,tpbt)
+       with {
+               tpbt = wcT/sqrt(max(0,g)); // tan(PI*B/SR), B bw in Hz (Q^2 ~ g/4)
+               wcT = 2*ma.PI*eqf/ma.SR;  // peak frequency in rad/sample
+               g = ba.db2linear(eql); // peak gain
+       };
+
+       eq1_group(x) = fdn_group(hgroup("[3] RM Peaking Equalizer 1", x));
+
+       eq1f = eq1_group(vslider("[1] Eq1 Freq [unit:Hz] [style:knob] [scale:log] [tooltip:
+               Center-frequency of second-order Regalia-Mitra peaking equalizer section 1]",
+       315, 40, 2500, 1));
+
+       eq1l = eq1_group(vslider("[2] Eq1 Level [unit:dB] [style:knob] [tooltip: Peak level
+               in dB of second-order Regalia-Mitra peaking equalizer section 1]", 0, -15, 15, 0.1));
+
+       eq1q = eq1_group(vslider("[3] Eq1 Q [style:knob] [tooltip: Q = centerFrequency/bandwidth
+               of second-order peaking equalizer section 1]", 3, 0.1, 10, 0.1));
+
+       eq2_group(x) = fdn_group(hgroup("[4] RM Peaking Equalizer 2", x));
+
+       eq2f = eq2_group(vslider("[1] Eq2 Freq [unit:Hz] [style:knob] [scale:log] [tooltip:
+               Center-frequency of second-order Regalia-Mitra peaking equalizer section 2]",
+       1500, 160, 10000, 1));
+
+       eq2l = eq2_group(vslider("[2] Eq2 Level [unit:dB] [style:knob] [tooltip: Peak level
+               in dB of second-order Regalia-Mitra peaking equalizer section 2]", 0, -15, 15, 0.1));
+
+       eq2q = eq2_group(vslider("[3] Eq2 Q [style:knob] [tooltip: Q = centerFrequency/bandwidth
+               of second-order peaking equalizer section 2]", 3, 0.1, 10, 0.1));
+
+       out_group(x)  = fdn_group(hgroup("[5] Output", x));
+
+       wet_dry(wet,y,x) = wet*y + (1-wet)*x;
+
+       wet_dry_2(wet,y1,y2,x1,x2) = wet_dry(wet,y1,x1), wet_dry(wet,y2,x2);
+
+       wet = out_group(vslider("[1] Wet [style:knob] [tooltip: Dry/Wet Mix: 0 = dry, 1 = wet]",
+       0, 0.0, 1.0, 0.01)) : si.smoo;
+
+       out_level = *(gain),*(gain);
+
+       gain = out_group(vslider("[2] Level [unit:dB] [style:knob] [tooltip: Output scale
+               factor]", -3, -70, 20, 0.1)) : ba.db2linear : si.smoo;
+};
diff --git a/faust-src/zitarevmonodsp.dsp b/faust-src/zitarevmonodsp.dsp
new file mode 100644 (file)
index 0000000..2a293d4
--- /dev/null
@@ -0,0 +1,2 @@
+process = _ <: _,_ : component("zitarevdsp.dsp") : _,_ :> _;
+
index 6debd45b8d72f77fe9ea3bd90e1437d8993e3a6d..c7130975a07a4708a6bf1f8e61b884fac315aa11 100644 (file)
@@ -1,14 +1,14 @@
-project('jacktrip', 'cpp', version: '1.2')
+project('jacktrip', 'cpp', version: '1.2',
+               default_options: ['cpp_std=c++11'])
 qt5 = import('qt5')
 qt5_dep = dependency('qt5', modules: ['Core', 'Network'])
 jack_dep = dependency('jack')
-rtaudio_dep = dependency('rtaudio')
 thread_dep = dependency('threads')
 
 defines = []
 if host_machine.system() == 'linux'
        defines += '-D__LINUX__'
-elif host_machine.system() == 'osx'
+elif host_machine.system() == 'darwin'
        defines += '-D__MAC_OSX__'
 elif host_machine.system() == 'windows'
        defines += '-D__WIN_32__'
@@ -28,6 +28,7 @@ moc_files = qt5.preprocess(moc_headers : moc_h)
 src = ['src/DataProtocol.cpp',
        'src/JMess.cpp',
        'src/JackTrip.cpp',
+       'src/AudioTester.cpp',
        'src/jacktrip_globals.cpp',
        'src/jacktrip_main.cpp',
        'src/JackTripThread.cpp',
@@ -36,10 +37,14 @@ src = ['src/DataProtocol.cpp',
        'src/PacketHeader.cpp',
        'src/ProcessPlugin.cpp',
        'src/RingBuffer.cpp',
+       'src/JitterBuffer.cpp',
        'src/Settings.cpp',
        'src/UdpDataProtocol.cpp',
        'src/UdpHubListener.cpp',
        'src/AudioInterface.cpp',
-       'src/JackAudioInterface.cpp']
+       'src/JackAudioInterface.cpp',
+       'src/Compressor.cpp',
+       'src/Limiter.cpp',
+       'src/Reverb.cpp']
 
-executable('jacktrip', src, moc_files, dependencies: [qt5_dep, jack_dep, rtaudio_dep, thread_dep], cpp_args: defines, install: true )
+executable('jacktrip', src, moc_files, dependencies: [qt5_dep, jack_dep, thread_dep], cpp_args: defines, install: true )
diff --git a/scripts/hubMode/art.sh b/scripts/hubMode/art.sh
new file mode 100755 (executable)
index 0000000..77af9a3
--- /dev/null
@@ -0,0 +1,35 @@
+echo calculate audio round trip
+rm /tmp/art.dat
+$( jack_iodelay > /tmp/art.dat 2>&1 & )
+# jack_iodelay  &
+sleep 1
+# jack_connect jack_delay:out jackloop$1.stanford.edu:send_2
+# jack_connect jackloop$1.stanford.edu:receive_2 jack_delay:in
+jack_disconnect __1:receive_1 __1:send_1
+jack_disconnect __1:receive_2 __1:send_2
+# jack_disconnect localhost:receive_1 localhost:send_1
+# jack_disconnect localhost:receive_2 localhost:send_2
+jack_connect jack_delay:out __1:send_1
+jack_connect __1:receive_1 jack_delay:in
+
+sleep 8
+killall jack_iodelay
+sleep 1
+
+killall jacktrip
+killall jackd
+
+DEFAULTOUTPUT=-1
+AWKOUTPUT=$(grep total  /tmp/art.dat | \
+    awk '{ sum += $3; n++ } END { if (n > 0) print sum / n; }')
+printf -v AWKWARDINT %.0f "$AWKOUTPUT"
+if (($AWKWARDINT > 0 ))
+  then
+    echo $AWKOUTPUT
+    exit 0
+  else
+    echo $DEFAULTOUTPUT
+    exit 1
+fi
+
+
diff --git a/scripts/hubMode/startJacktripHubClient.sh b/scripts/hubMode/startJacktripHubClient.sh
new file mode 100755 (executable)
index 0000000..598c8d3
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/bash
+# bash script for jacktrip automation, Chris Chafe
+# startJacktripHubClient.sh <FPP>
+# 
+# /home/cc/Desktop/sh/startJacktripHubClient.sh /home/cc/jacktrip/builddir/jacktrip %p
+
+JACKTRIP=$1
+
+### qjackctl start / stop example
+## qjackctl Setup : Options : Execute scrpt after Startup
+# /home/cc/Desktop/sh/startJacktripHubClient.sh %p
+## qjackctl Setup : Options : Execute scrpt on Shutdown
+# /home/cc/Desktop/sh/stopJacktrip.sh
+## timing of scrpt call means jacktrip is still running and 
+## an alert will appear for each new server it tries to shutdown
+## for both <scripts> do, chmod +x <script>.sh
+
+### manual start example
+# ./jacktrip -C jackloop128.stanford.edu
+
+### automatic client start in qjackctl : Setup : Options Execute script after Startup
+### examples with line used in qjackctl and corresponding script
+## specify full server name
+# /home/cc/startJacktrip.sh jackloop128.stanford.edu
+# SERVER=localhost
+
+## server name from file
+# /home/cc/startJacktrip.sh jackloop1024.stanford.edu
+# SERVER=$(cat ../../server.txt)
+
+## composed server name, %p = fpp
+# /home/cc/startJacktrip.sh %p
+
+
+if [ -z "$2" ]
+  then
+    SERVER=localhost
+  else
+    FPP=$2
+    SERVER=jackloop$FPP.stanford.edu
+fi
+
+
+echo starting hub client of server running on $SERVER
+
+$( $JACKTRIP -C $SERVER > /dev/null 2>&1 & )
+
+
+
+
diff --git a/scripts/hubMode/startJacktripHubServer.sh b/scripts/hubMode/startJacktripHubServer.sh
new file mode 100755 (executable)
index 0000000..3079ea9
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/bash
+# bash script for jacktrip automation, Chris Chafe
+# startJacktripHubServer.sh <FPP>
+
+JACKTRIP=$1
+
+
+echo starting $JACKTRIP server
+
+$( $JACKTRIP -S -p1 > /dev/null 2>&1 & )
+
+
+
+
diff --git a/scripts/hubMode/test_hub_mode_server_and_client.sh b/scripts/hubMode/test_hub_mode_server_and_client.sh
new file mode 100755 (executable)
index 0000000..7e0502f
--- /dev/null
@@ -0,0 +1,70 @@
+#!/bin/bash
+# test_hub_mode_server_and_client.sh
+# bash script for automatic testing of jacktrip in hub mode , Chris Chafe
+# connects a hub client to a hub server (started with with -p1) on the same host
+# prints avg audio RTT after 8 sec, or -1 if fail 
+# ./test_hub_mode_server_and_client.sh <path-to-executable> <FPP>
+# requires 3 helper scripts
+# -- startJacktripHubServer.sh
+# -- startJacktripHubClient.sh
+# -- art.sh
+# requires jackd be available on the host
+# uses the "dummy" interface, so no audio interface needed
+# first does some jackd cleanup 
+# takes approx. 20 sec to complete
+
+# a passing test prints to the console
+# [cc@localhost sh]$ ./test.sh /home/cc/jacktrip/builddir/jacktrip 32
+# starting /home/cc/jacktrip/builddir/jacktrip hub mode test at 32 FPP
+# starting hub client for server localhost
+# calculate audio round trip
+# 4
+
+# a failed test prints to the console
+# [cc@localhost functionTests]$ ./test.sh ~/jacktrip/builddir/jacktrip 32
+# starting /home/cc/jacktrip/builddir/jacktrip hub mode test at 32 FPP
+# starting /home/cc/jacktrip/builddir/jacktrip server
+# starting hub client of server running on localhost
+# calculate audio round trip
+# ERROR ..1:send_1 not a valid port
+# ERROR ..1:send_2 not a valid port
+# ERROR ..1:send_1 not a valid port
+# ERROR ..1:receive_1 not a valid port
+# jacktrip: no process found
+# -1
+
+
+JACKTRIP=$1
+
+if [ -z "$2" ]
+  then
+    FPP=128
+  else
+    FPP=$2
+fi
+
+# killall jackd
+if [ "$(ps -aux | grep -c jackd)" != 1 ]; then killall jackd; fi;
+# if jackd is or has been running with another driver
+# much experimenation shows it literally takes this long
+sleep 17
+# to flush old connections before starting the dummy driver
+
+# start jack with dummy driver, or change to an audio interface by switching these lines
+$( /usr/bin/jackd  -ddummy -r48000 -p$FPP > /dev/null 2>&1 & )
+# $( /usr/bin/jackd  -dalsa -dhw:A96 -r48000 -p$FPP -n2  > /dev/null 2>&1 & )
+# $( /usr/bin/jackd  -dalsa -dhw:PCH -r48000 -p$FPP -n2  > /dev/null 2>&1 & )
+
+sleep 1
+
+echo starting $JACKTRIP hub mode test at $FPP FPP
+
+$PWD/startJacktripHubServer.sh $JACKTRIP
+sleep 1
+$PWD/startJacktripHubClient.sh $JACKTRIP
+
+sleep 1
+
+# start measuring audio RTT
+$PWD/art.sh
+
index 06fe59ef07db1a63d387979f886c068bc4782e92..8caf34827f3c70269f51dc20922a951e41f624c9 100644 (file)
@@ -39,6 +39,7 @@
 #include "JackTrip.h"
 #include <iostream>
 #include <cmath>
+#include <assert.h>
 
 using std::cout; using std::endl;
 
@@ -57,7 +58,7 @@ AudioInterface::AudioInterface(JackTrip* jacktrip,
     mAudioBitResolution(AudioBitResolution*8),
     mBitResolutionMode(AudioBitResolution),
     mSampleRate(gDefaultSampleRate), mBufferSizeInSamples(gDefaultBufferSizeInSamples),
-    mInputPacket(NULL), mOutputPacket(NULL)
+    mInputPacket(NULL), mOutputPacket(NULL), mLoopBack(false), mProcessingAudio(false)
 {
 #ifndef WAIR
     //cc
@@ -85,6 +86,11 @@ AudioInterface::AudioInterface(JackTrip* jacktrip,
         mAPInBuffer[i] = NULL;
     }
 #endif // endwhere
+
+    mInBufCopy.resize(mNumInChans);
+    for (int i=0; i<mNumInChans; i++) {
+      mInBufCopy[i] = new sample_t[MAX_AUDIO_BUFFER_SIZE]; // required for processing audio input
+    }
 }
 
 
@@ -93,7 +99,7 @@ AudioInterface::~AudioInterface()
 {
     delete[] mInputPacket;
     delete[] mOutputPacket;
-#ifndef WAIR // WAIR
+#ifndef WAIR // NOT WAIR:
     for (int i = 0; i < mNumInChans; i++) {
         delete[] mInProcessBuffer[i];
     }
@@ -115,6 +121,16 @@ AudioInterface::~AudioInterface()
         delete[] mAPInBuffer[i];
     }
 #endif // endwhere
+
+    for (int i = 0; i < mProcessPluginsFromNetwork.size(); i++) {
+      delete mProcessPluginsFromNetwork[i];
+    }
+    for (int i = 0; i < mProcessPluginsToNetwork.size(); i++) {
+      delete mProcessPluginsToNetwork[i];
+    }
+    for (int i=0; i<mNumInChans; i++) {
+      delete mInBufCopy[i];
+    }
 }
 
 
@@ -153,7 +169,7 @@ void AudioInterface::setup()
 
     int nframes = getBufferSizeInSamples();
 
-#ifndef WAIR // WAIR
+#ifndef WAIR // NOT WAIR:
     for (int i = 0; i < mNumInChans; i++) {
         mInProcessBuffer[i] = new sample_t[nframes];
         // set memory to 0
@@ -208,8 +224,8 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer,
     // ----------------------------------
 
 #ifdef WAIR // WAIR
-    //    qDebug() << "--" << mProcessPlugins.size();
-    bool client = (mProcessPlugins.size() == 2);
+    //    qDebug() << "--" << mProcessPluginsFromNetwork.size();
+    bool client = (mProcessPluginsFromNetwork.size() == 2);
 #define COMBDSP 1 // client
 #define APDSP 0 // client
 #define DCBDSP 0 // server
@@ -218,7 +234,18 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer,
     }
 #endif // endwhere
 
+    // ==== RECEIVE AUDIO CHANNELS FROM NETWORK ====
     computeProcessFromNetwork(out_buffer, n_frames);
+    // =============================================
+
+    // out_buffer is from the network and goes "out" to local audio
+    // hardware via JACK:
+
+    // mAudioTesterP will be nullptr for hub server's JackTripWorker instances
+    if (mAudioTesterP && mAudioTesterP->getEnabled()) {
+      mAudioTesterP->lookForReturnPulse(out_buffer, n_frames);
+    }
+
 #ifdef WAIR // WAIR
     // nib16 result now in mNetInBuffer
 #endif // endwhere
@@ -229,19 +256,14 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer,
     /// \todo Implement for more than one process plugin, now it just works propertely with one.
     /// do it chaining outputs to inputs in the buffers. May need a tempo buffer
 
-#ifndef WAIR // WAIR
-    for (int i = 0; i < mNumInChans; i++) {
-        std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * n_frames);
-        std::memcpy(mInProcessBuffer[i], out_buffer[i], sizeof(sample_t) * n_frames);
+#ifndef WAIR // NOT WAIR:
+    for (int i = 0; i < mProcessPluginsFromNetwork.size(); i++) {
+      ProcessPlugin* p = mProcessPluginsFromNetwork[i];
+      if (p->getInited()) {
+        p->compute(n_frames, out_buffer.data(), out_buffer.data());
+      }
     }
-    for (int i = 0; i < mNumOutChans; i++) {
-        std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * n_frames);
-    }
-
-    for (int i = 0; i < mProcessPlugins.size(); i++) {
-        mProcessPlugins[i]->compute(n_frames, mInProcessBuffer.data(), mOutProcessBuffer.data());
-    }
-#else // WAIR
+#else // WAIR:
     for (int i = 0; i < ((mNumNetRevChans)?mNumNetRevChans:mNumOutChans); i++) {
         std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * n_frames);
     }
@@ -257,14 +279,44 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer,
     }
     // nib16 to cib16
 
-    if (mNumNetRevChans && client) mProcessPlugins[COMBDSP]->compute(n_frames,
-                                                                     mInProcessBuffer.data(), mOutProcessBuffer.data());
+    if (mNumNetRevChans && client) {
+      mProcessPluginsFromNetwork[COMBDSP]->compute(n_frames, mInProcessBuffer.data(), mOutProcessBuffer.data());
+    }
     // compute cob16
 #endif // endwhere
 
-    // 3) Finally, send packets to peer
-    // --------------------------------
-    computeProcessToNetwork(in_buffer, n_frames);
+    // 3) Send packets to network:
+    // mAudioTesterP will be nullptr for hub server's JackTripWorker instances:
+    bool audioTesting = (mAudioTesterP && mAudioTesterP->getEnabled());
+    int nop = mProcessPluginsToNetwork.size(); // number of OUTGOING processing modules
+    if (nop>0 || audioTesting) { // cannot modify in_buffer, so make a copy
+      // in_buffer is "in" from local audio hardware via JACK
+      if (mInBufCopy.size() < mNumInChans) { // created in constructor above
+        std::cerr << "*** AudioInterface.cpp: Number of Input Channels changed - insufficient room reserved\n";
+        exit(1);
+      }
+      if (MAX_AUDIO_BUFFER_SIZE < n_frames) { // allocated in constructor above
+        std::cerr << "*** AudioInterface.cpp: n_frames = " << n_frames
+                  << " larger than expected max = " << MAX_AUDIO_BUFFER_SIZE << "\n";
+        exit(1);
+      }
+      for (int i=0; i<mNumInChans; i++) {
+        std::memcpy(mInBufCopy[i], in_buffer[i], sizeof(sample_t) * n_frames);
+      }
+      for (int i = 0; i < nop; i++) {
+        // process all outgoing channels with ProcessPlugins:
+        ProcessPlugin* p = mProcessPluginsToNetwork[i];
+        if (p->getInited()) {
+          p->compute(n_frames, mInBufCopy.data(), mInBufCopy.data());
+        }
+      }
+      if (audioTesting) {
+        mAudioTesterP->writeImpulse(mInBufCopy, n_frames); // writes last channel of mInBufCopy with test impulse
+      }
+      computeProcessToNetwork(mInBufCopy, n_frames);
+    } else { // copy saved if no plugins and no audio testing in progress:
+      computeProcessToNetwork(in_buffer, n_frames); // send processed input audio to network - OUTGOING
+    }
 
 #ifdef WAIR // WAIR
     // aib2 + cob16 to nob16
@@ -302,7 +354,7 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer,
         for (int i = 0; i < mNumOutChans; i++) {
             std::memset(out_buffer[i], 0, sizeof(sample_t) * n_frames);
         }
-        mProcessPlugins[APDSP]->compute(n_frames, mAPInBuffer.data(), out_buffer.data());
+        mProcessPluginsFromNetwork[APDSP]->compute(n_frames, mAPInBuffer.data(), out_buffer.data());
         // compute ap2 into aob2
 
         //#define ADD_DIRECT
@@ -319,8 +371,7 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer,
     }
 #endif // endwhere
 
-
-    ///************PROTORYPE FOR CELT**************************
+    ///************PROTOTYPE FOR CELT**************************
     ///********************************************************
     /*
   CELTMode* mode;
@@ -337,6 +388,28 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer,
 
 }
 
+//*******************************************************************************
+void AudioInterface::broadcastCallback(QVarLengthArray<sample_t*>& mon_buffer,
+                                               unsigned int n_frames)
+{
+    /// \todo cast *mInBuffer[i] to the bit resolution
+    // Output Process (from NETWORK to JACK)
+    // ----------------------------------------------------------------
+    // Read Audio buffer from RingBuffer (read from incoming packets)
+    mJackTrip->receiveBroadcastPacket(mOutputPacket);
+        // Extract separate channels to send to Jack
+        for (int i = 0; i < mNumOutChans; i++) {
+            sample_t* tmp_sample = mon_buffer[i]; //sample buffer for channel i
+            for (unsigned int j = 0; j < n_frames; j++) {
+                // Change the bit resolution on each sample
+                fromBitToSampleConversion(
+                            // use interleaved channel layout
+                            //&mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)],
+                            &mOutputPacket[(j*mBitResolutionMode*mNumOutChans) + (i*mBitResolutionMode)],
+                        &tmp_sample[j], mBitResolutionMode );
+            }
+        }
+}
 
 //*******************************************************************************
 // Before sending and reading to Jack, we have to round to the sample resolution
@@ -359,7 +432,9 @@ void AudioInterface::computeProcessFromNetwork(QVarLengthArray<sample_t*>& out_b
             for (unsigned int j = 0; j < n_frames; j++) {
                 // Change the bit resolution on each sample
                 fromBitToSampleConversion(
-                            &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)],
+                            // use interleaved channel layout
+                            //&mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)],
+                            &mOutputPacket[(j*mBitResolutionMode*mNumOutChans) + (i*mBitResolutionMode)],
                         &tmp_sample[j], mBitResolutionMode );
             }
         }
@@ -371,13 +446,15 @@ void AudioInterface::computeProcessFromNetwork(QVarLengthArray<sample_t*>& out_b
             //--------
             // This should be faster for 32 bits
             //std::memcpy(mOutBuffer[i], &mOutputPacket[i*mSizeInBytesPerChannel],
-            //         mSizeInBytesPerChannel);
+            //         mSizeInBytesPerChannel);
             //--------
             sample_t* tmp_sample = out_buffer[i]; //sample buffer for channel i
             for (unsigned int j = 0; j < n_frames; j++) {
                 // Change the bit resolution on each sample
                 fromBitToSampleConversion(
-                            &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)],
+                            // use interleaved channel layout
+                            //&mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)],
+                            &mOutputPacket[(j*mBitResolutionMode*mNumOutChans) + (i*mBitResolutionMode)],
                         &tmp_sample[j], mBitResolutionMode );
             }
         }
@@ -406,7 +483,9 @@ void AudioInterface::computeProcessToNetwork(QVarLengthArray<sample_t*>& in_buff
                 tmp_result = INGAIN*tmp_sample[j] + COMBGAIN*tmp_process_sample[j];
                 fromSampleToBitConversion(
                             &tmp_result,
-                            &mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)],
+                            // use interleaved channel layout
+                            //&mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)],
+                            &mInputPacket[(j*mBitResolutionMode*mNumOutChans) + (i*mBitResolutionMode)],
                         mBitResolutionMode );
             }
         }
@@ -417,7 +496,7 @@ void AudioInterface::computeProcessToNetwork(QVarLengthArray<sample_t*>& in_buff
             //--------
             // This should be faster for 32 bits
             //std::memcpy(&mInputPacket[i*mSizeInBytesPerChannel], mInBuffer[i],
-            //         mSizeInBytesPerChannel);
+            //         mSizeInBytesPerChannel);
             //--------
             sample_t* tmp_sample = in_buffer[i]; //sample buffer for channel i
             sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process
@@ -428,14 +507,15 @@ void AudioInterface::computeProcessToNetwork(QVarLengthArray<sample_t*>& in_buff
                 tmp_result = tmp_sample[j] + tmp_process_sample[j];
                 fromSampleToBitConversion(
                             &tmp_result,
-                            &mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)],
+                            // use interleaved channel layout
+                            //&mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)],
+                            &mInputPacket[(j*mBitResolutionMode*mNumOutChans) + (i*mBitResolutionMode)],
                         mBitResolutionMode );
             }
         }
     // Send Audio buffer to Network
     mJackTrip->sendNetworkPacket( mInputPacket );
-}
-
+} // /computeProcessToNetwork
 
 //*******************************************************************************
 // This function quantize from 32 bit to a lower bit resolution
@@ -448,22 +528,23 @@ void AudioInterface::fromSampleToBitConversion
     int8_t tmp_8;
     uint8_t tmp_u8; // unsigned to quantize the remainder in 24bits
     int16_t tmp_16;
-    sample_t tmp_sample;
+    double tmp_sample;
     sample_t tmp_sample16;
     sample_t tmp_sample8;
     switch (targetBitResolution)
     {
     case BIT8 :
         // 8bit integer between -128 to 127
-        tmp_sample = floor( (*input) * 128.0 ); // 2^7 = 128.0
+        tmp_sample = std::max(-127.0, std::min(127.0, std::round( (*input) * 127.0 ))); // 2^7 = 128
         tmp_8 = static_cast<int8_t>(tmp_sample);
         std::memcpy(output, &tmp_8, 1); // 8bits = 1 bytes
         break;
     case BIT16 :
         // 16bit integer between -32768 to 32767
-        tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0
+        // original scaling: tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0
+        tmp_sample = std::max(-32767.0, std::min(32767.0, std::round( (*input) * 32767.0 ))); // 2^15 = 32768
         tmp_16 = static_cast<int16_t>(tmp_sample);
-        std::memcpy(output, &tmp_16, 2); // 16bits = 2 bytes
+        std::memcpy(output, &tmp_16, 2); // 2 bytes output in Little Endian order (LSB -> smallest address)
         break;
     case BIT24 :
         // To convert to 24 bits, we first quantize the number to 16bit
@@ -483,7 +564,10 @@ void AudioInterface::fromSampleToBitConversion
         std::memcpy(output+2, &tmp_u8, 1); // 8bits = 1 bytes
         break;
     case BIT32 :
-        std::memcpy(output, input, 4); // 32bit = 4 bytes
+        tmp_sample = *input;
+        // not necessary yet:
+        // tmp_sample = std::max(-1.0, std::min(1.0, tmp_sample));
+        std::memcpy(output, &tmp_sample, 4); // 32bit = 4 bytes
         break;
     }
 }
@@ -532,32 +616,71 @@ void AudioInterface::fromBitToSampleConversion
 
 
 //*******************************************************************************
-void AudioInterface::appendProcessPlugin(ProcessPlugin* plugin)
+void AudioInterface::appendProcessPluginToNetwork(ProcessPlugin* plugin)
+{
+  if (not plugin) { return; }
+  int nTestChans = (mAudioTesterP && mAudioTesterP->getEnabled()) ? 1 : 0;
+  int nPluginChans = mNumInChans - nTestChans;
+  assert(nTestChans==0 || (mAudioTesterP->getSendChannel() == mNumInChans-1));
+  if (plugin->getNumInputs() < nPluginChans) {
+    std::cerr << "*** AudioInterface.cpp: appendProcessPluginToNetwork: ProcessPlugin "
+              << typeid(plugin).name() << " REJECTED due to having "
+              << plugin->getNumInputs() << " inputs, while the audio to JACK needs "
+              << nPluginChans << " inputs\n";
+    return;
+  }
+  mProcessPluginsToNetwork.append(plugin);
+}
+
+void AudioInterface::appendProcessPluginFromNetwork(ProcessPlugin* plugin)
 {
-    /// \todo check that channels in ProcessPlugins are less or same that jack channels
-    if ( plugin->getNumInputs() ) {}
-    mProcessPlugins.append(plugin);
+  if (not plugin) { return; }
+  int nTestChans = (mAudioTesterP && mAudioTesterP->getEnabled()) ? 1 : 0;
+  int nPluginChans = mNumOutChans - nTestChans;
+  assert(nTestChans==0 || (mAudioTesterP->getSendChannel() == mNumOutChans-1));
+  if (plugin->getNumOutputs() > nPluginChans) {
+    std::cerr << "*** AudioInterface.cpp: appendProcessPluginFromNetwork: ProcessPlugin "
+              << typeid(plugin).name() << " REJECTED due to having "
+              << plugin->getNumOutputs() << " inputs, while the JACK audio output requires "
+              << nPluginChans << " outputs\n";
+    return;
+  }
+  mProcessPluginsFromNetwork.append(plugin);
 }
 
+void AudioInterface::initPlugins()
+{
+  int nPlugins = mProcessPluginsFromNetwork.size() + mProcessPluginsToNetwork.size();
+  if (nPlugins > 0) {
+    std::cout << "Initializing Faust plugins (have " << nPlugins
+              << ") at sampling rate " << mSampleRate << "\n";
+    for (ProcessPlugin* plugin : mProcessPluginsFromNetwork) {
+      plugin->init(mSampleRate);
+    }
+    for (ProcessPlugin* plugin : mProcessPluginsToNetwork) {
+      plugin->init(mSampleRate);
+    }
+  }
+}
 
 //*******************************************************************************
 AudioInterface::samplingRateT AudioInterface::getSampleRateType() const
 {
-    uint32_t rate = getSampleRate();
+    int32_t rate = getSampleRate();
 
-    if      ( rate == 22050 ) {
+    if      ( 100 > qAbs(rate - 22050) ) {
         return AudioInterface::SR22; }
-    else if ( rate == 32000 ) {
+    else if ( 100 > qAbs(rate - 32000) ) {
         return AudioInterface::SR32; }
-    else if ( rate == 44100 ) {
+    else if ( 100 > qAbs(rate - 44100) ) {
         return AudioInterface::SR44; }
-    else if ( rate == 48000 ) {
+    else if ( 100 > qAbs(rate - 48000) ) {
         return AudioInterface::SR48; }
-    else if ( rate == 88200 ) {
+    else if ( 100 > qAbs(rate - 88200) ) {
         return AudioInterface::SR88; }
-    else if ( rate == 96000 ) {
+    else if ( 100 > qAbs(rate - 96000) ) {
         return AudioInterface::SR96; }
-    else if ( rate == 19200 ) {
+    else if ( 100 > qAbs(rate - 19200) ) {
         return AudioInterface::SR192; }
 
     return AudioInterface::UNDEF;
index c4b953c7e1141a5dcf4183df6807b7677283317e..f5089ebe593378264e1615e83d456c7d4f1d8b72 100644 (file)
@@ -40,6 +40,7 @@
 
 #include "ProcessPlugin.h"
 #include "jacktrip_types.h"
+#include "AudioTester.h"
 
 #include <QVarLengthArray>
 #include <QVector>
@@ -115,16 +116,33 @@ public:
     * \param in_buffer Array of output audio samplers for each channel. The user
     * is reponsible to check that each channel has n_frames samplers
     */
+    virtual void broadcastCallback(QVarLengthArray<sample_t*>& mon_buffer,
+                          unsigned int n_frames);
     virtual void callback(QVarLengthArray<sample_t*>& in_buffer,
                           QVarLengthArray<sample_t*>& out_buffer,
                           unsigned int n_frames);
-    /** \brief Append a ProcessPlugin. The order of processing is determined by
-   * the order by which appending is done.
-   * \param plugin a ProcesPlugin smart pointer. Create the object instance
+   /** \brief appendProcessPluginToNetwork(): Append a ProcessPlugin for outgoing audio. 
+   * The processing order equals order they were appended.
+   * This processing is in the JackTrip client before sending to the network.
+   * \param plugin a ProcessPlugin smart pointer. Create the object instance
    * using something like:\n
    * <tt>std::tr1::shared_ptr<ProcessPluginName> loopback(new ProcessPluginName);</tt>
    */
-    virtual void appendProcessPlugin(ProcessPlugin* plugin);
+    virtual void appendProcessPluginToNetwork(ProcessPlugin* plugin);
+   /** \brief appendProcessPluginFromNetwork():
+   * Same as appendProcessPluginToNetwork() except that these plugins operate
+   * on the audio received from the network (typically from a JackTrip server).
+   * The complete processing chain then looks like this:
+   * audio -> JACK -> JackTrip client -> processPlugin to network
+   *               -> remote JackTrip server
+   *               -> JackTrip client -> processPlugin from network -> JACK -> audio
+   */
+    virtual void appendProcessPluginFromNetwork(ProcessPlugin* plugin);
+   /** \brief initPlugins():
+   * Initialize all ProcessPlugin modules.
+   * The audio sampling rate (mSampleRate) must be set at this time.
+   */
+    void initPlugins();
     virtual void connectDefaultPorts() = 0;
     /** \brief Convert a 32bit number (sample_t) into one of the bit resolution
    * supported (audioBitResolutionT).
@@ -160,6 +178,9 @@ public:
     { mBufferSizeInSamples = buf_size; }
     /// \brief Set Client Name to something different that the default (JackTrip)
     virtual void setClientName(QString ClientName) = 0;
+    virtual void setLoopBack(bool b) { mLoopBack = b; }
+    virtual void enableBroadcastOutput() {}
+    virtual void setAudioTesterP(AudioTester* atp) { mAudioTesterP = atp; }
     //------------------------------------------------------------------
 
     //--------------GETTERS---------------------------------------------
@@ -209,17 +230,24 @@ private:
     QVarLengthArray<sample_t*> mNetInBuffer; ///< Vector of Input buffers/channel read from net
     QVarLengthArray<sample_t*> mAPInBuffer; ///< Vector of Input buffers/channel for AllPass input
 #endif // endwhere
+    QVarLengthArray<sample_t*> mInBufCopy; ///< needed in callback() to modify JACK audio input
     int mAudioBitResolution; ///< Bit resolution in audio samples
     AudioInterface::audioBitResolutionT mBitResolutionMode; ///< Bit resolution (audioBitResolutionT) mode
     uint32_t mSampleRate; ///< Sampling Rate
     uint32_t mDeviceID; ///< RTAudio DeviceID
     uint32_t mBufferSizeInSamples; ///< Buffer size in samples
     size_t mSizeInBytesPerChannel; ///< Size in bytes per audio channel
-    QVector<ProcessPlugin*> mProcessPlugins; ///< Vector of ProcesPlugin<EM>s</EM>
+    QVector<ProcessPlugin*> mProcessPluginsFromNetwork; ///< Vector of ProcessPlugin<EM>s</EM>
+    QVector<ProcessPlugin*> mProcessPluginsToNetwork; ///< Vector of ProcessPlugin<EM>s</EM>
     QVarLengthArray<sample_t*> mInProcessBuffer;///< Vector of Input buffers/channel for ProcessPlugin
     QVarLengthArray<sample_t*> mOutProcessBuffer;///< Vector of Output buffers/channel for ProcessPlugin
     int8_t* mInputPacket; ///< Packet containing all the channels to read from the RingBuffer
     int8_t* mOutputPacket;  ///< Packet containing all the channels to send to the RingBuffer
+    bool mLoopBack;
+    AudioTester* mAudioTesterP { nullptr };
+protected:
+    bool mProcessingAudio;  ///< Set when processing an audio callback buffer pair
+    const uint32_t MAX_AUDIO_BUFFER_SIZE = 8192;
 };
 
 #endif // __AUDIOINTERFACE_H__
diff --git a/src/AudioTester.cpp b/src/AudioTester.cpp
new file mode 100644 (file)
index 0000000..136c1d4
--- /dev/null
@@ -0,0 +1,195 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file AudioTester.cpp
+ * \author Julius Smith
+ * \license MIT
+ * \date Aug-Oct 2020
+ */
+
+#include "AudioTester.h"
+#include <assert.h>
+
+// Called 1st in Audiointerface.cpp
+void AudioTester::lookForReturnPulse(QVarLengthArray<sample_t*>& out_buffer,
+                                     unsigned int n_frames) {
+  if (not enabled) {
+    std::cerr << "*** AudioTester.h: lookForReturnPulse: NOT ENABLED\n";
+    return;
+  }
+  if (impulsePending) { // look for return impulse in channel sendChannel:
+    assert(sendChannel<out_buffer.size());
+    for (uint n=0; n<n_frames; n++) {
+      float amp = out_buffer[sendChannel][n];
+      if (amp > 0.5 * ampCellHeight) { // got something
+        int cellNum =  getImpulseCellNum(out_buffer[sendChannel][n]);
+        if (cellNum != pendingCell) { // not our impulse!
+          std::cerr <<
+            "*** AudioTester.h: computeProcessFromNetwork: Received pulse amplitude "
+                    << amp << " (cell " << cellNum << ") while looking for cell "
+                    << pendingCell << "\n";
+
+          if (cellNum > pendingCell) { // we missed it
+            std::cerr << " - ABORTING CURRENT PULSE\n";
+            impulsePending = false;
+          } else { // somehow we got the previous pulse again - repeated packet or underrun-caused repetition (old buffer)
+            std::cerr << " - IGNORING FOUND PULSE WAITING FURTHER\n";
+          }
+        } else { // found our impulse:
+          int64_t elapsedSamples = -1;
+          if (n >= n_frames-1) {
+            // Impulse timestamp didn't make it so we skip this one.
+          } else {
+            float sampleCountWhenImpulseSent = - 32768.0f * out_buffer[sendChannel][n+1];
+            elapsedSamples = sampleCountSinceImpulse + n - int64_t(sampleCountWhenImpulseSent);
+            sampleCountSinceImpulse = 1; // reset sample counter between impulses
+            roundTripCount += 1.0;
+          }
+          // int64_t curTimeUS = timeMicroSec(); // time since launch in us
+          // int64_t impulseDelayUS = curTimeUS - ImpulseTimeUS;
+          // float impulseDelaySec = float(impulseDelayUS) * 1.0e-6;
+          // float impulseDelayBuffers = impulseDelaySec / (float(n_frames)/float(sampleRate));
+          // int64_t impulseDelayMS = (int64_t)round(double(impulseDelayUS)/1000.0);
+          if (elapsedSamples > 0) { // found impulse and reset, time to print buffer results:
+            double elapsedSamplesMS = 1000.0 * double(elapsedSamples)/double(sampleRate); // ms
+            extendLatencyHistogram(elapsedSamplesMS);
+            if (roundTripCount > 1.0) {
+              double prevSum = roundTripMean * (roundTripCount-1.0); // undo previous normalization
+              roundTripMean = (prevSum + elapsedSamplesMS) / roundTripCount; // add latest and renormalize
+              double prevSumSq = roundTripMeanSquare * (roundTripCount-1.0); // undo previous normalization
+              roundTripMeanSquare = (prevSumSq + elapsedSamplesMS*elapsedSamplesMS) / roundTripCount;
+            } else { // just getting started:
+              roundTripMean = elapsedSamplesMS;
+              roundTripMeanSquare = elapsedSamplesMS * elapsedSamplesMS;
+            }
+            if (roundTripCount == 1.0) {
+              printf("JackTrip Test Mode (option -x printIntervalInSeconds=%0.3f)\n",printIntervalSec);
+              printf("\tA test impulse-train is output on channel %d (from 0) with repeatedly ramping amplitude\n",
+                     sendChannel);
+              if (printIntervalSec == 0.0) {
+                printf("\tPrinting each audio buffer round-trip latency in ms followed by cumulative (mean and [standard deviation])");
+              } else {
+                printf("\tPrinting cumulative mean and [standard deviation] of audio round-trip latency in ms");
+                printf(" every %0.3f seconds", printIntervalSec);
+              }
+              printf(" after skipping first %d buffers:\n", bufferSkipStart);
+              // not printing this presently: printf("( * means buffer skipped due missing timestamp or lost impulse)\n");
+              lastPrintTimeUS = timeMicroSec();
+            }
+            //printf("%d (%d) ", elapsedSamplesMS, impulseDelayMS); // measured time is "buffer time" not sample time
+            int64_t curTimeUS = timeMicroSec(); // time since launch in us
+            double timeSinceLastPrintUS = double(curTimeUS - lastPrintTimeUS);
+            double stdDev = sqrt(std::max(0.0, (roundTripMeanSquare - (roundTripMean*roundTripMean))));
+            if (timeSinceLastPrintUS >= printIntervalSec * 1.0e6) {
+              if (printIntervalSec == 0.0) { printf("%0.1f (", elapsedSamplesMS); }
+              printf("%0.1f [%0.1f]", roundTripMean, stdDev);
+              if (printIntervalSec == 0.0) { printf(") "); } else { printf(" "); }
+              lastPrintTimeUS = curTimeUS;
+              if (printIntervalSec >= 1.0) { // print histogram
+                std::cout << "\n" << getLatencyHistogramString() << "\n";
+              }
+            }
+            std::cout << std::flush;
+          } else {
+            // not printing this presently: printf("* "); // we got the impulse but lost its timestamp in samples
+          }
+          impulsePending = false;
+        } // found our impulse
+          // remain pending until timeout, hoping to find our return pulse
+      } // got something
+    } // loop over samples
+    sampleCountSinceImpulse += n_frames; // gets reset to 1 when impulse is found, counts freely until then
+  } // ImpulsePending
+}
+
+// Called 2nd in Audiointerface.cpp
+void AudioTester::writeImpulse(QVarLengthArray<sample_t*>& mInBufCopy,
+                               unsigned int n_frames) {
+  if (not enabled) {
+    std::cerr << "*** AudioTester.h: writeImpulse: NOT ENABLED\n";
+    return;
+  }
+  if (bufferSkip <= 0) { // send test signals (-x option)
+    bool sendImpulse;
+    if (impulsePending) {
+      sendImpulse = false; // unless:
+      const uint64_t timeOut = 500e3; // time out after waiting 500 ms
+      if (timeMicroSec() > (impulseTimeUS + timeOut)) {
+        sendImpulse = true;
+        std::cout << "\n*** Audio Latency Test (-x): TIMED OUT waiting for return impulse *** sending a new one\n";
+      }
+    } else { // time for the next repeating impulse:
+      sendImpulse = true;
+    }
+    if (sendImpulse) {
+      assert(sendChannel < mInBufCopy.size());
+      mInBufCopy[sendChannel][0] = getImpulseAmp();
+      for (uint n=1; n<n_frames; n++) {
+        mInBufCopy[sendChannel][n] = 0;
+      }
+      impulsePending = true;
+      impulseTimeUS = timeMicroSec();
+      impulseTimeSamples = sampleCountSinceImpulse; // timer in samples for current impulse loopback test
+      // Also send impulse time:
+      if (n_frames>1) { // always true?
+        mInBufCopy[sendChannel][1] = -float(impulseTimeSamples)/32768.0f; // survives if there is no digital processing at the server
+      } else {
+        std::cerr << "\n*** AudioTester.h: Timestamp cannot fit into a lenth " << n_frames << " buffer ***\n";
+      }
+    } else {
+      mInBufCopy[sendChannel][0] = 0.0f; // send zeros until a new impulse is needed
+      if (n_frames>1) {
+        mInBufCopy[sendChannel][1] = 0.0f;
+      }
+    }
+  } else {
+    bufferSkip--;
+  }
+}
+
+void AudioTester::printHelp(char* command, [[maybe_unused]] char helpCase) {
+  std::cout << "HELP for \"" << command << " printIntervalSec\" // (end-of-line comments start with `//'):\n";
+  std::cout << "\n";
+  std::cout << "Print roundtrip audio delay statistics for the highest-numbered audio channel every printIntervalSec seconds,\n";
+  std::cout << "including an ASCII latency histogram if printIntervalSec is 1.0 or more.\n";
+  std::cout << "\n";
+  std::cout << "A test impulse is sent to the server in the last audio channel,\n";
+  std::cout << "  the number of samples until it returns is measured, and this repeats.\n";
+  std::cout << "The jacktrip server must provide audio loopback (e.g., -p4).\n";
+  std::cout << "The cumulative mean and standard-deviation (\"statistics\") are computed for the measured loopback times,\n";
+  std::cout << "  and printed every printIntervalSec seconds.\n";
+  std::cout << "If printIntervalSec is zero, the roundtrip-time and statistics in milliseconds are printed for each individual impulse.\n";
+  std::cout << "If printIntervalSec is positive, statistics are printed after each print interval, with no individual measurements.\n";
+  std::cout << "If printIntervalSec is 1.0 or larger, a cumulative histogram of all impulse roundtrip-times is printed as well.\n";
+  std::cout << "The first 100 audio buffers are skipped in order to measure only steady-state network-audio-delay performance.\n";
+  std::cout << "Lower audio channels are not affected, enabling latency measurement and display during normal operation.\n";
+}
diff --git a/src/AudioTester.h b/src/AudioTester.h
new file mode 100644 (file)
index 0000000..4a08f78
--- /dev/null
@@ -0,0 +1,220 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file AudioTester.h
+ * \author Julius Smith
+ * \license MIT
+ * \date Aug-Oct 2020
+ */
+
+#pragma once
+
+#include "jacktrip_types.h" // sample_t
+
+#include <iostream>
+//#include <ctime>
+#include <chrono>
+#include <cstdint>
+#include <cmath>
+#include <string>
+#include <map>
+
+#include <QVarLengthArray>
+
+class AudioTester
+{
+  bool enabled { false };
+  float printIntervalSec { 1.0f };
+  int sendChannel { 0 };
+
+  bool impulsePending { false };
+  int64_t lastPrintTimeUS { 0 };
+  int64_t impulseTimeUS { 0 };
+  int64_t impulseTimeSamples { 0 };
+  uint64_t sampleCountSinceImpulse { 1 }; // 0 not used
+  double roundTripMean { 0.0 };
+  double roundTripMeanSquare { 0.0 };
+  double roundTripCount { 0.0 };
+  const int bufferSkipStart { 100 };
+  int bufferSkip { bufferSkipStart };
+  const float impulseAmplitude { 0.1f };
+  const int numAmpCells { 10 };
+  const float ampCellHeight { impulseAmplitude/numAmpCells };
+
+  const double latencyHistogramCellWidth { 5.0 }; // latency range in ms covered one cell
+  const double latencyHistogramCellMin { 0.0 };
+  const double latencyHistogramCellMax { 19.0 };  // in cells, so 5x this is max latency in ms
+  const int latencyHistogramPrintCountMax { 72 }; // normalize when asterisks exceed this number
+
+  int pendingCell { 0 }; // 0 is not used
+  float sampleRate { 48000.0f };
+
+public:
+  AudioTester() {}
+  ~AudioTester() = default;
+
+  void lookForReturnPulse(QVarLengthArray<sample_t*>& out_buffer,
+                                       unsigned int n_frames);
+
+  void writeImpulse(QVarLengthArray<sample_t*>& mInBufCopy,
+                    unsigned int n_frames);
+
+  bool getEnabled() { return enabled; }
+  void setEnabled(bool e) { enabled = e; }
+  void setPrintIntervalSec(float s) { printIntervalSec = s; }
+  void setSendChannel(int c) { sendChannel = c; }
+  int getSendChannel() { return sendChannel; }
+  int getPendingCell() { return pendingCell; }
+  void setPendingCell(int pc) { pendingCell = pc; }
+  void setSampleRate(float fs) { sampleRate = fs; }
+  int getBufferSkip() { return bufferSkip; } // used for debugging breakpoints
+  void printHelp(char* command, char helpCase);
+
+private:
+
+  float getImpulseAmp() {
+    pendingCell += 1; // only called when no impulse is pending
+    if (pendingCell >= numAmpCells) {
+      pendingCell = 1; // wrap-around, not using zero
+    }
+    float imp = float(pendingCell) * (impulseAmplitude/float(numAmpCells));
+    return imp;
+  }
+
+  int getImpulseCellNum(float amp) {
+    float ch = ampCellHeight;
+    float cell = amp / ch;
+    int iCell = int(std::floor(0.5f + cell));
+    if (iCell > numAmpCells - 1) {
+      std::cerr << "*** AudioTester.h: getImpulseCellNum("<<amp<<"): Return pulse amplitude is beyond maximum expected\n";
+      iCell = numAmpCells-1;
+    } else if (iCell < 0) {
+      std::cerr << "*** AudioTester.h: getImpulseCellNum("<<amp<<"): Return pulse amplitude is below minimum expected\n";
+      iCell = 0;
+    }
+    return iCell;
+  }
+
+  uint64_t timeMicroSec() {
+#if 1
+    using namespace std::chrono;
+    // return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+    return duration_cast<microseconds>(high_resolution_clock::now().time_since_epoch()).count();
+#else
+    clock_t tics_since_launch = std::clock();
+    double timeUS = double(tics_since_launch)/double(CLOCKS_PER_SEC);
+    return (uint64_t)timeUS;
+#endif
+  }
+
+  std::map<int, int> latencyHistogram;
+
+  std::map<int, int> getLatencyHistogram() {
+    return latencyHistogram;
+  }
+
+  void extendLatencyHistogram(double latencyMS) {
+    int latencyCell = static_cast<int>(floor(std::max(latencyHistogramCellMin,
+                                                      std::min(latencyHistogramCellMax,
+                                                               std::floor(latencyMS / latencyHistogramCellWidth)))));
+    latencyHistogram[latencyCell] += 1;
+  }
+
+  int latencyHistogramCountMax() {
+    int lhMax = 0;
+    int histStart = latencyHistogramFirstNonzeroCellIndex();
+    int histLast = latencyHistogramLastNonzeroCellIndex();
+    for (int i = histStart; i <= histLast; ++i) {
+      int lhi = latencyHistogram[i];
+      if (lhi > lhMax) {
+        lhMax = lhi;
+      }
+    }
+    return lhMax;
+  }
+
+  int latencyHistogramFirstNonzeroCellIndex() {
+    for (int i=latencyHistogramCellMin; i <= latencyHistogramCellMax; i++) {
+      if (latencyHistogram[i]>0) {
+        return i;
+      }
+    }
+    std::cerr << "*** AudioTester: LATENCY HISTOGRAM IS EMPTY!\n";
+    return -1;
+  }
+
+  int latencyHistogramLastNonzeroCellIndex() {
+    for (int i=latencyHistogramCellMax; i>=latencyHistogramCellMin; i--) {
+      if (latencyHistogram[i]>0) {
+        return i;
+      }
+    }
+    std::cerr << "*** AudioTester: LATENCY HISTOGRAM IS EMPTY!\n";
+    return -1;
+  }
+
+  std::string getLatencyHistogramString() {
+    int histStart = latencyHistogramFirstNonzeroCellIndex();
+    int histLast = latencyHistogramLastNonzeroCellIndex();
+    std::string marker = "*";
+    double histScale = 1.0;
+    int lhcm = latencyHistogramCountMax();
+    int lhpcm = latencyHistogramPrintCountMax;
+    bool normalizing = lhpcm < lhcm;
+    if (normalizing) {
+      marker = "#";
+      histScale = double(lhpcm) / double(lhcm);
+    }
+    std::string rows = "";
+    for (int i = histStart; i <= histLast; ++i) {
+      int hi = latencyHistogram[i];
+      int hin = int(std::round(histScale * double(hi)));
+      std::string istrm1 = std::to_string(int(latencyHistogramCellWidth * double(i)));
+      std::string istr = std::to_string(int(latencyHistogramCellWidth * double(i+1)));
+      // std::string histr = boost::format("%02d",hi);
+      std::string histr = std::to_string(hi);
+      while (histr.length()<3) {
+        histr = " " + histr;
+      }
+      std::string row = "["+istrm1+"-"+istr+"ms]="+histr+":";
+      for (int j=0; j<hin; j++) {
+        row += marker;
+      }
+      rows += row + "\n";
+    }
+    if (histLast == latencyHistogramCellMax) {
+      rows += " and above\n";
+    }
+    return rows;
+  }
+
+};
diff --git a/src/Compressor.cpp b/src/Compressor.cpp
new file mode 100644 (file)
index 0000000..a1ed346
--- /dev/null
@@ -0,0 +1,57 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file Compressor.cpp
+ * \author Julius Smith, based on LoopBack.h
+ * \date July 2008
+ */
+
+
+#include "Compressor.h"
+
+#include <iostream>
+
+//*******************************************************************************
+void Compressor::compute(int nframes, float** inputs, float** outputs)
+{
+  if (not inited) {
+    std::cerr << "*** Compressor " << this << ": init never called! Doing it now.\n";
+    if (fSamplingFreq <= 0) {
+      fSamplingFreq = 48000;
+      std::cout << "Compressor " << this << ": *** HAD TO GUESS the sampling rate (chose 48000 Hz) ***\n";
+    }
+    init(fSamplingFreq);
+  }
+  for ( int i = 0; i < mNumChannels; i++ ) {
+    compressorP[i]->compute(nframes, &inputs[i], &outputs[i]);
+  }
+}
diff --git a/src/Compressor.h b/src/Compressor.h
new file mode 100644 (file)
index 0000000..d5f41ea
--- /dev/null
@@ -0,0 +1,148 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file Compressor.h
+ * \author Julius Smith, starting from Limiter.h
+ * \date August 2020
+ */
+
+
+/** \brief Applies compressor_mono from the faustlibraries distribution, compressors.lib
+ *
+ */
+#ifndef __COMPRESSOR_H__
+#define __COMPRESSOR_H__
+
+#include "ProcessPlugin.h"
+#include "compressordsp.h"
+#include "CompressorPresets.h"
+#include <vector>
+
+/** \brief A Compressor reduces the output dynamic range when the
+ *         signal level exceeds the threshold.
+ */
+class Compressor : public ProcessPlugin
+{
+public:
+  /// \brief The class constructor sets the number of audio channels and default parameters.
+  Compressor(int numchans, // xtor
+             bool verboseIn = false,
+             float ratioIn = 2.0f,
+             float thresholdDBIn = -24.0f,
+             float attackMSIn = 15.0f,
+             float releaseMSIn = 40.0f,
+             float makeUpGainDBIn = 2.0f)
+    : mNumChannels(numchans)
+    , ratio(ratioIn)
+    , thresholdDB(thresholdDBIn)
+    , attackMS(attackMSIn)
+    , releaseMS(releaseMSIn)
+    , makeUpGainDB(makeUpGainDBIn)
+  {
+    setVerbose(verboseIn);
+    // presets.push_back(std::make_unique<CompressorPreset>(ratio,thresholdDB,attackMS,releaseMS,makeUpGainDB));
+    for ( int i = 0; i < mNumChannels; i++ ) {
+      compressorP.push_back(new compressordsp);
+      compressorUIP.push_back(new APIUI); // #included in compressordsp.h
+      compressorP[i]->buildUserInterface(compressorUIP[i]);
+    }
+  }
+
+  Compressor(int numchans, // xtor
+             bool verboseIn = false,
+             CompressorPreset preset = CompressorPresets::voice) :
+    Compressor(numchans,verboseIn,
+               preset.ratio,
+               preset.thresholdDB,
+               preset.attackMS,
+               preset.releaseMS,
+               preset.makeUpGainDB)
+  {}
+  /// \brief The class destructor
+  virtual ~Compressor() {
+    for ( int i = 0; i < mNumChannels; i++ ) {
+      delete compressorP[i];
+      delete compressorUIP[i];
+    }
+    compressorP.clear();
+    compressorUIP.clear();
+  }
+
+  //  void setParamAllChannels(std::string& pName, float p) {
+  void setParamAllChannels(const char pName[], float p) {
+    for ( int i = 0; i < mNumChannels; i++ ) {
+      int ndx = compressorUIP[i]->getParamIndex(pName);
+      if (ndx >= 0) {
+        compressorUIP[i]->setParamValue(ndx, p);
+        if (verbose) {
+          std::cout << "Compressor.h: parameter " << pName << " set to " << p << " on audio channel " << i << "\n";
+        }
+      } else {
+        std::cerr << "*** Compressor.h: Could not find parameter named " << pName << "\n";
+      }
+    }
+  }
+
+  void init(int samplingRate) override {
+    ProcessPlugin::init(samplingRate);
+    if (samplingRate != fSamplingFreq) {
+      std::cerr << "Sampling rate not set by superclass!\n";
+      std::exit(1); }
+    fs = float(fSamplingFreq);
+    for ( int i = 0; i < mNumChannels; i++ ) {
+      compressorP[i]->init(fs); // compression filter parameters depend on sampling rate
+    }
+    setParamAllChannels("Ratio", ratio);
+    setParamAllChannels("Threshold", thresholdDB);
+    setParamAllChannels("Attack", attackMS);
+    setParamAllChannels("Release", releaseMS);
+    setParamAllChannels("MakeUpGain", makeUpGainDB);
+    inited = true;
+  }
+
+  int getNumInputs() override { return(mNumChannels); }
+  int getNumOutputs() override { return(mNumChannels); }
+  void compute(int nframes, float** inputs, float** outputs) override;
+
+private:
+  float fs;
+  int mNumChannels;
+  std::vector<compressordsp*> compressorP;
+  std::vector<APIUI*> compressorUIP;
+  float ratio;
+  float thresholdDB;
+  float attackMS;
+  float releaseMS;
+  float makeUpGainDB;
+};
+
+#endif
diff --git a/src/CompressorPresets.h b/src/CompressorPresets.h
new file mode 100644 (file)
index 0000000..2a12db1
--- /dev/null
@@ -0,0 +1,81 @@
+#pragma once
+
+#include <array>
+
+struct CompressorPreset {
+  float ratio;
+  float thresholdDB;
+  float attackMS;
+  float releaseMS;
+  float makeUpGainDB;
+  CompressorPreset(float r, float t, float a, float rel, float m)
+    : ratio(r)
+    , thresholdDB(t)
+    , attackMS(a)
+    , releaseMS(rel)
+    , makeUpGainDB(m)
+  {}
+  ~CompressorPreset() = default;
+};
+
+namespace CompressorPresets
+{
+  //                     name   ratio  thresh  attack  rel  mugain
+  const CompressorPreset voice { 2.0f, -24.0f, 15.0f, 40.0f, 2.0f };
+  const CompressorPreset horns { 3.0f, -10.0f, 100.0f, 250.0f, 2.0f };
+  const CompressorPreset snare { 5.0f, -4.0f, 5.0f, 150.0f, 3.0f };
+  const uint numPresets { 3 };
+  const std::array<CompressorPreset,numPresets> standardPresets { voice, horns, snare };
+  enum CompressorPresetNames { CPN_VOICE, CPN_BRASS, CPN_SNARE, CPN_NUMPRESETS };
+}
+
+#if 0 // not yet using this
+// Dynamic extension of CompressorPresets:
+struct CompressorPresetList {
+  std::vector<CompressorPreset*> presets;
+  CompressorPresetList() { // define some standard presets
+    presets.push_back( new CompressorPreset(CompressorPresets::voice) );
+    presets.push_back( new CompressorPreset(CompressorPresets::horns) );
+    presets.push_back( new CompressorPreset(CompressorPresets::snare) );
+  }
+  ~CompressorPresetList() = default;
+};
+#endif
+
+/* Settings from http://www.anythingpeaceful.org/sonar/settings/comp.html
+
+   Name     Thresh(dB) Att(ms) Rel(ms) Ratio:1 Gain(dB)    Comments
+   Vocal 1     -20     31      342     2.5     2       Compressor for Solo Vocal
+   Vocal 2     -8      26      331     2.5     1.5     Variation of Solo 1
+   Full Comp 1 -8      60      2500    2.5     0       For Overall Volume Level
+   Full Comp 2 -18     94      447     3.5     2.5     Variation of Total Comp 1: Harder ratio
+   Full Comp 3 -16     11      180     6       6       Nearly a limiter effect
+   Kick Comp   -24     9       58      3       5.5     Compressor for Acoustic Bass Drum
+   Snare Comp  -17     8       12      2.5     3.5     Compressor for Acoustic Snare Drum
+   Guitar      -10     5       238     2.5     1.5     Compressor for Acoustic Guitar
+   Brass Sec   -18     18      226     1.7     4       Brass Sounds for Strong Attacks
+   Bass 1      -12     15      470     2       4.5     Finger Picked Bass Guitar
+   Bass 2      -12     6       133     1.7     4       Slap Electric Bass
+   E Guitar    -8      7       261     3.5     2.5     Electric Guitar Compressor
+   Piano 1     -9      17      238     2.5     1       Brightens Piano
+   Piano 2     -18     7       174     3.5     6       Variation of Piano 1
+   Kick        -14     2       35      2       3.5     For sampled Bass Drum
+   Snare       -18     8       354     4       8       For sampled Snare Drum
+   Strings 1   -11     33      749     2       1.5     For String instruments
+   Strings 2   -12     93      2500    1.5     1.5     For Violas and Cellos
+   Strings 3   -17     76      186     1.5     2.5     Cellos or DoubleBass
+   Syn Bass    -10     9       250     3.5     3       Adjust level of Synth Bass
+   Syn Pad     -13     58      238     2       2       Prevents diffusion of sound in synth pad
+   Limiting    -1      0.1     325     20      0       Slow release limiter
+   Chorusing   -9      39      225     1.7     2.5     For vocal Chorusing
+
+   From https://www.dummies.com/art-center/music/recording-music/dynamic-music-compression-settings-for-horns-piano-and-percussion/
+
+   Horns       –8      100     300     2.5-3   2       Brasses not normally compressed [jos gain estimate based on above table]
+   Piano       -10     100-105 115     1.5-2   2       Normally not compressed ["]
+   Kick        -6      40-50   200-300 4-6     3       Looks more like a limiter to me [jos] ["]
+   Snare       -4      5-10    125-175 4-6     3       Crucial for a tight, punchy sound
+   Bongos      -6      10-25   100-300 3-6     3       "Hand Drums" - protect against excess "slap"
+   Perc.       -10     10-20   50      3-6     3       Transient overdrive protection in mix
+
+*/
index a23120a1b48dba87f72aa97bc80216a92bec5a12..aac86eb03e896656e6b6780ac5639fad64a979b5 100644 (file)
@@ -52,7 +52,7 @@ using std::cout; using std::endl;
 DataProtocol::DataProtocol(JackTrip* jacktrip,
                            const runModeT runmode,
                            int /*bind_port*/, int /*peer_port*/) :
-    mStopped(false), mHasPacketsToReceive(false), mRunMode(runmode), mJackTrip(jacktrip)
+    mStopped(false), mHasPacketsToReceive(false), mRunMode(runmode), mJackTrip(jacktrip), mUseRtPriority(false)
 {}
 
 
index 413263d12e43be4fbbd86929134be48cd4df772a..6be9a5a6ae0aa7ac87a9e1e3986fb476f23962c7 100644 (file)
@@ -180,11 +180,14 @@ public:
     };
     virtual bool getStats(PktStat*) {return false;}
 
+    virtual void setIssueSimulation(double /*loss*/, double /*jitter*/, double /*max_delay*/) {}
+    void setUseRtPriority(bool use) {mUseRtPriority = use;}
+
 signals:
 
     void signalError(const char* error_message);
     void signalReceivedConnectionFromPeer();
-
+    void signalCeaseTransmission(const QString &reason = "");
 
 protected:
 
@@ -222,6 +225,7 @@ private:
 protected:
     //PacketHeader* mHeader; ///< Packet Header
     JackTrip* mJackTrip; ///< JackTrip mediator class
+    bool mUseRtPriority;
 
 };
 
diff --git a/src/Effects.h b/src/Effects.h
new file mode 100644 (file)
index 0000000..086d40e
--- /dev/null
@@ -0,0 +1,571 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2020 Julius Smith.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file Effects.h
+ * \author Julius Smith
+ * \date Aug 2020
+ */
+
+#pragma once
+
+#include "ProcessPlugin.h"
+#include "Limiter.h"
+#include "Compressor.h"
+#include "CompressorPresets.h"
+#include "Reverb.h"
+#include <assert.h>
+#include <vector>
+
+class Effects
+{
+  int mNumIncomingChans;
+  int mNumOutgoingChans;
+  int gVerboseFlag = 0;
+public:
+  enum LIMITER_MODE {
+                     LIMITER_NONE,
+                     LIMITER_INCOMING, // from network
+                     LIMITER_OUTGOING, // to network
+                     LIMITER_BOTH
+  };
+private:
+  LIMITER_MODE mLimit; ///< audio limiter controls
+  unsigned int mNumClientsAssumed; ///< assumed number of clients (audio sources)
+  double limiterWarningAmplitude;
+
+  enum InOrOut { IO_NEITHER, IO_IN, IO_OUT } io;
+  bool inCompressor = false;
+  bool outCompressor = false;
+  bool inZitarev = false;
+  bool outZitarev = false;
+  bool inFreeverb = false;
+  bool outFreeverb = false;
+  bool incomingEffectsAllocated = false;
+  bool outgoingEffectsAllocated = false;
+  Compressor* inCompressorP = nullptr;
+  Compressor* outCompressorP = nullptr;
+  CompressorPreset inCompressorPreset = CompressorPresets::voice; // ./CompressorPresets.h
+  CompressorPreset outCompressorPreset = CompressorPresets::voice;
+  Reverb* inZitarevP = nullptr;
+  Reverb* outZitarevP = nullptr;
+  Reverb* inFreeverbP = nullptr;
+  Reverb* outFreeverbP = nullptr;
+  int parenLevel = 0;
+  char lastEffect = '\0';
+  float zitarevInLevel = 1.0f; // "Level" = wetness from 0 to 1
+  float freeverbInLevel = 1.0f;
+  float zitarevOutLevel = 1.0f;
+  float freeverbOutLevel = 1.0f;
+  float mReverbLevel; // for backward compatibility: 0-1 Freeverb, 1-2 Zitarev
+  Limiter* inLimiterP = nullptr;
+  Limiter* outLimiterP = nullptr;
+
+public:
+
+  Effects(bool outGoingLimiterOn=true) :
+    mNumIncomingChans(2),
+    mNumOutgoingChans(2),
+    mLimit(outGoingLimiterOn ? LIMITER_OUTGOING : LIMITER_NONE),
+    mNumClientsAssumed(2),
+    limiterWarningAmplitude(0.0)
+  {}
+
+  ~Effects() {
+    /*
+      Plugin ownership presently passes to JackTrip,
+      and deletion occurs in AudioInterface.cpp. See
+        delete mProcessPluginsFromNetwork[i];
+        delete mProcessPluginsToNetwork[i];
+      there.  If/when we ever do it here:
+        if (inCompressor) { delete inCompressorP; }
+        if (outCompressor) { delete outCompressorP; }
+        if (inZitarev) { delete inZitarevP; }
+        if (outZitarev) { delete outZitarevP; }
+        if (inFreeverb) { delete inFreeverbP; }
+        if (outFreeverb) { delete outFreeverbP; }
+      but if everyone can compile C++11,
+      let's switch to using std::unique_ptr.
+    */
+  }
+
+  unsigned int getNumClientsAssumed() { return mNumClientsAssumed; }
+
+  LIMITER_MODE getLimit() { return mLimit; }
+  void setNoLimiters() { mLimit = LIMITER_NONE; }
+
+  ProcessPlugin* getInCompressor() { return inCompressorP; }
+  ProcessPlugin* getOutCompressor() { return outCompressorP; }
+  ProcessPlugin* getInZitarev() { return inZitarevP; }
+  ProcessPlugin* getOutZitarev() { return outZitarevP; }
+  ProcessPlugin* getInFreeverb() { return inFreeverbP; }
+  ProcessPlugin* getOutFreeverb() { return outFreeverbP; }
+  ProcessPlugin* getInLimiter() { return inLimiterP; }
+  ProcessPlugin* getOutLimiter() { return outLimiterP; }
+
+  bool getHaveEffect() {
+    return
+      inCompressor || outCompressor ||
+      inZitarev || outZitarev ||
+      inFreeverb || outFreeverb ;
+  }
+
+  bool getHaveLimiter() {
+    return mLimit != LIMITER_NONE;
+  }
+
+  void setVerboseFlag(int v) {
+    gVerboseFlag = v;
+  }
+
+  int getNumIncomingChans() {
+    return mNumIncomingChans;
+  }
+
+  int getOutgoingNumChans() {
+    return mNumOutgoingChans;
+  }
+
+  // call these next two after it is decided what effects we will be using for the duration:
+
+  std::vector<ProcessPlugin*> allocateIncomingEffects(int nIncomingChans) {
+    mNumIncomingChans = nIncomingChans;
+    if (incomingEffectsAllocated) {
+      std::cerr << "*** Effects.h: attempt to allocate incoming effects more than once\n";
+      std::exit(1);
+    }
+    std::vector<ProcessPlugin*> incomingEffects;
+    if (inCompressor) {
+      assert(inCompressorP == nullptr);
+      inCompressorP = new Compressor(mNumIncomingChans, gVerboseFlag, inCompressorPreset);
+      if (gVerboseFlag) { std::cout << "Set up INCOMING COMPRESSOR\n"; }
+      incomingEffects.push_back(inCompressorP);
+    }
+    if (inZitarev) {
+      assert(inZitarevP == nullptr);
+      inZitarevP = new Reverb(mNumIncomingChans,mNumIncomingChans, 1.0 + zitarevInLevel);
+      if (gVerboseFlag) { std::cout << "Set up INCOMING REVERB (Zitarev)\n"; }
+      incomingEffects.push_back(inZitarevP);
+    }
+    if (inFreeverb) {
+      assert(inFreeverbP == nullptr);
+      inFreeverbP = new Reverb(mNumIncomingChans, mNumIncomingChans, freeverbInLevel);
+      if (gVerboseFlag) { std::cout << "Set up INCOMING REVERB (Freeverb)\n"; }
+      incomingEffects.push_back(inFreeverbP);
+    }
+    // LIMITER MUST GO LAST:
+    if ( mLimit == LIMITER_INCOMING || mLimit == LIMITER_BOTH) {
+      if (gVerboseFlag) {
+        std::cout << "Set up INCOMING LIMITER for " << mNumIncomingChans << " input channels\n";
+      }
+      assert(inLimiterP == nullptr);
+      inLimiterP = new Limiter(mNumIncomingChans, 1, gVerboseFlag); // mNumClientsAssumed not needed this direction
+      // Never needed in normal practice for incoming limiter: inLimiterP->setWarningAmplitude(limiterWarningAmplitude);
+      incomingEffects.push_back(inLimiterP);
+    }
+    incomingEffectsAllocated = true;
+    return incomingEffects;
+  }
+
+  std::vector<ProcessPlugin*> allocateOutgoingEffects(int nOutgoingChans) {
+    mNumOutgoingChans = nOutgoingChans;
+    if (outgoingEffectsAllocated) {
+      std::cerr << "*** Effects.h: attempt to allocate outgoing effects more than once\n";
+      std::exit(1);
+    }
+    std::vector<ProcessPlugin*> outgoingEffects;
+    if (outCompressor) {
+      assert(outCompressorP == nullptr);
+      outCompressorP = new Compressor(mNumOutgoingChans, gVerboseFlag, outCompressorPreset);
+      if (gVerboseFlag) { std::cout << "Set up OUTGOING COMPRESSOR\n"; }
+      outgoingEffects.push_back(outCompressorP);
+    }
+    if (outZitarev) {
+      assert(outZitarevP == nullptr);
+      outZitarevP = new Reverb(mNumOutgoingChans, mNumOutgoingChans, 1.0 + zitarevOutLevel);
+      if (gVerboseFlag) { std::cout << "Set up OUTGOING REVERB (Zitarev)\n"; }
+      outgoingEffects.push_back(outZitarevP);
+    }
+    if (outFreeverb) {
+      assert(outFreeverbP == nullptr);
+      outFreeverbP = new Reverb(mNumOutgoingChans, mNumOutgoingChans, freeverbOutLevel);
+      if (gVerboseFlag) { std::cout << "Set up OUTGOING REVERB (Freeverb)\n"; }
+      outgoingEffects.push_back(outFreeverbP);
+    }
+    // LIMITER MUST GO LAST:
+    if ( mLimit != LIMITER_NONE) {
+      if ( mLimit == LIMITER_OUTGOING || mLimit == LIMITER_BOTH) {
+        if (gVerboseFlag) {
+          std::cout << "Set up OUTGOING LIMITER for "
+                    << mNumOutgoingChans << " output channels and "
+                    << mNumClientsAssumed << " assumed client(s) ...\n";
+        }
+        assert(outLimiterP == nullptr);
+        outLimiterP = new Limiter(mNumOutgoingChans,mNumClientsAssumed);
+        outLimiterP->setWarningAmplitude(limiterWarningAmplitude);
+        // do not have mSampleRate yet, so cannot call limiter->init(mSampleRate) here
+        outgoingEffects.push_back(outLimiterP);
+      }
+    }
+    outgoingEffectsAllocated = true;
+    return outgoingEffects;
+  }
+
+  void printHelp(char* command, char helpCase) {
+    std::cout << "HELP for `" << command << "' (end-of-line comments start with `//')\n";
+    std::cout << "\n";
+    std::cout << "Examples:\n";
+    std::cout << "\n";
+    if (helpCase == 0 || helpCase == 'f') { //
+      std::cout << command << " 0.3 // add a default outgoing compressor (for voice) and incoming reverb (freeverb) with wetness 0.3 (wetness from 0 to 1)\n";
+      std::cout << command << " 1.3 // add a default outgoing compressor (for voice) and incoming reverb (zitarev) with wetness 0.3 = 1.3-1 (i.e., 1+ to 2 is for zitarev)\n";
+      std::cout << "\n";
+      std::cout << command << " \"o:c i:f(0.3)\" // outgoing-compressor and incoming-freeverb example above using more general string argument\n";
+      std::cout << command << " \"o:c i:z(0.3)\" // outgoing-compressor and incoming-zitarev example above using more general string argument\n";
+      std::cout << command << " \"o:c(1)\" // outgoing compressor, using preset 1 (designed for voice - see below for details)\n";
+      std::cout << command << " \"o:c(2)\" // outgoing compressor, using preset 2 (for horns)\n";
+      std::cout << command << " \"o:c(3)\" // outgoing compressor, using preset 3 (for snare)\n";
+      std::cout << command << " \"o:c(c:compressionRatio t:thresholdDB a:attackTimeMS r:releaseTimeMS g:makeUpGainDB)\" // general compression parameter specification (all floats)\n";
+      std::cout << command << " \"o:c(c:2 t:-24 a:15 r:40 g:2)\"   // outgoing compressor, preset 1 details\n";
+      std::cout << command << " \"o:c(c:3 t:-10 a:100 r:250 g:2)\" // outgoing compressor, preset 2 details\n";
+      std::cout << command << " \"o:c(c:5 t:-4 a:5 r:150 g:3)\"    // outgoing compressor, preset 3 details\n";
+      std::cout << "  For these and more suggested compression settings, see http://www.anythingpeaceful.org/sonar/settings/comp.html\n";
+      std::cout << "\n";
+    }
+    if (helpCase == 0 || helpCase == 'O') { // limiter (-O option most likely)
+      std::cout << command << " i   // add limiter to INCOMING audio from network (only helpful for floats, i.e., -b32 used by server)\n";
+      std::cout << command << " o   // add limiter to OUTGOING audio to network (prevents your sound from harshly clipping going out)\n";
+      std::cout << command << " ow  // also warn and advise on levels when outgoing limiter compresses audio near clipping\n";
+      std::cout << command << " io  // add limiter to both INCOMING and OUTGOING audio\n";
+      std::cout << command << " iow // limiters both ways and compression warnings on outgoing direction only\n";
+      std::cout << "\n";
+    }
+    if (helpCase == 0 || helpCase == 'a') { // assumedNumClients (-a option)
+      std::cout << command << " 1 // assume 1 client - fine for loopback test, or if only one client plays at a time, or server uses -b32 and -Oi is used\n";
+      std::cout << command << " 2 // assume 2 clients possibly playing at the same time\n";
+      std::cout << command << " N // any integer N>0 can be used - the outgoing limiter will divide final amplitude by 1/sqrt(N) to reduce overages in server\n";
+      std::cout << "\n";
+    }
+  }
+
+  // ----------- Compressor stuff --------------
+
+  int setCompressorPresetIndexFrom1(unsigned long presetIndexFrom1, InOrOut io) {
+    int returnCode = 0;
+    if (presetIndexFrom1 <= 0 || presetIndexFrom1 > CompressorPresets::numPresets) {
+      std::cerr << "*** Effects.h: setCompressorPresetFrom1: Index " << presetIndexFrom1 << " out of range\n";
+      returnCode = 1;
+    } else {
+      CompressorPreset stdPreset = CompressorPresets::standardPresets[presetIndexFrom1-1];
+      if (io == IO_IN) {
+        inCompressorPreset = stdPreset;
+      } else if (io == IO_OUT) {
+        outCompressorPreset = stdPreset;
+      } else if (io != IO_NEITHER) {
+        std::cerr << "*** Effects.h: setCompressorPresetFrom1: Invalid InOrOut value " << io << "\n";
+        returnCode = 1;
+      }
+    }
+    return returnCode;
+  }
+
+  int parseCompresserArgs(char* args, InOrOut inOrOut) {
+    // args can be integerPresetNumberFrom1 or (all optional, any order):
+    // c:compressionRatio, a:attackTimeMS, r:releaseTimeMS, g:makeUpGain
+    int returnCode = 0;
+    if (not isalpha(args[0])) {
+      int presetIndexFrom1 = atoi(args);
+      setCompressorPresetIndexFrom1(presetIndexFrom1,inOrOut);
+    } else {
+      // args can be presetIndexFrom1, handled above, or (all optional, any order):
+      // c(c:compressionRatio, t:thresholdDB, a:attackTimeMS, r:releaseTimeMS, g:makeUpGainDB)
+      // See ./CompressorPresets.h for example settings.
+      if (gVerboseFlag) {
+        std::cout << "parseCompressorArgs = " << args << std::endl;
+      }
+      ulong argLen = strlen(args);
+      char lastParam = '\0';
+
+      CompressorPreset newPreset(CompressorPresets::voice); // Anything unset gets voice value (most gentle)
+
+      int nSkip = 0;
+      for (ulong i=0; i<argLen; i++) {
+        if (nSkip > 0) {
+          nSkip--;
+          continue;
+        }
+        char ch = args[i];
+        switch(ch) {
+        case ' ': break;
+        case '\t': break;
+        case 'c': case 't': case 'a': case 'r': case 'g':
+          lastParam = ch;
+          break;
+        case ':': break;
+        default: // must be a floating-point number at this point:
+          if (ch!='-' && isalpha(ch)) {
+            std::cerr << "*** Effects.h: parseCompressorArgs: " << ch << " not recognized in args = " << args << "\n";
+            returnCode = 2;
+          } else { // must have a digit or '-' or '.'
+            assert(ch=='-'||ch=='.'||isdigit(ch));
+            float paramValue = -1.0e10;
+            for (ulong j=i; j<argLen; j++) { // scan ahead for end of number
+              if (args[j] == ',' || args[j] == ' ' || j==argLen-1) { // comma or space required between parameters
+                char argsj = args[j];
+                if (j<argLen-1) { // there's more
+                  args[j] = '\0';
+                }
+                paramValue = atof(&args[i]);
+                args[j] = argsj;
+                nSkip = j-i;
+                break;
+              }
+            }
+            if (paramValue == -1.0e10) {
+              std::cerr << "*** Effects.h: parseCompressorArgs: Could not find parameter for "
+                        << lastParam << " in args = " << args << "\n";
+              returnCode = 2;
+            } else {
+              switch (lastParam) {
+              case 'c':
+                newPreset.ratio = paramValue;
+                break;
+              case 't':
+                  newPreset.thresholdDB = paramValue;
+                break;
+              case 'a':
+                  newPreset.attackMS = paramValue;
+                break;
+              case 'r':
+                  newPreset.releaseMS = paramValue;
+                break;
+              case 'g':
+                  newPreset.makeUpGainDB = paramValue;
+                break;
+              default: // cannot happen:
+                std::cerr << "*** Effects.h: parseCompressorArgs: lastParam " << lastParam << " invalid\n";
+                returnCode = 3; // "reality failure"
+              } // switch(lastParam)
+            } // have valid parameter from atof
+          } // have valid non-alpha char for parameter
+        } // switch(ch)
+      } // for (ulong i=0; i<argLen; i++) {
+      if (inOrOut == IO_IN) {
+        inCompressorPreset = newPreset;
+      } else if (inOrOut == IO_OUT) {
+        outCompressorPreset = newPreset;
+      } else if (inOrOut != IO_NEITHER) {
+        std::cerr << "*** Effects.h: parseCompressorArgs: invalid InOrOut value " << inOrOut << "\n";
+        returnCode = 2;
+      }
+    } // long-form compressor args
+    return returnCode;
+  } // int parseCompresserArgs(char* args, InOrOut inOrOut)
+
+  // ============== General argument processing for all effects =================
+
+  int parseEffectsOptArg(char* cmd, char* optarg) {
+    int returnCode = 0; // 0 means go, 1 means exit without error, higher => error exit
+
+    char c = optarg[0];
+    if (c == '-' || c==0) {
+      // happens when no -f argument specified
+      returnCode = 2;
+    } else if (not isalpha(c)) { // backward compatibility why not?, e.g., "-f 0.5"
+      // -f reverbLevelFloat
+      mReverbLevel = atof(optarg);
+      outCompressor = true;
+      inZitarev = mReverbLevel > 1.0;
+      inFreeverb = mReverbLevel <= 1.0;
+      if (inZitarev) {
+        zitarevInLevel = mReverbLevel - 1.0; // wetness from 0 to 1
+      }
+      if (inFreeverb) {
+        freeverbInLevel = mReverbLevel; // wetness from 0 to 1
+      }
+    } else { // long-form argument:
+      // -f "i:[c][f|z][(reverbLevel)]], o:[c][f|z][(rl)]"
+      // c can be c(integerPresetNumberFrom1) or (all optional, any order):
+      // c(c:compressionRatio, a:attackTimeMS, r:releaseTimeMS, g:makeUpGain)
+      if (gVerboseFlag) {
+        std::cout << cmd << " argument = " << optarg << std::endl;
+      }
+      ulong argLen = strlen(optarg);
+
+      for (ulong i=0; i<argLen; i++) {
+        if (optarg[i]!=')' && parenLevel>0) { continue; }
+        switch(optarg[i]) {
+        case ' ': break;
+        case ',': break;
+        case ';': break;
+        case '\t': break;
+        case 'h': printHelp(cmd,'f'); returnCode = 1; break;
+        case 'i': io=IO_IN; break;
+        case 'o': io=IO_OUT; break;
+        case ':': break;
+        case 'c': if (io==IO_IN) { inCompressor = true; } else if (io==IO_OUT) { outCompressor = true; }
+          else { std::cerr << "-f arg `" << optarg << "' malformed\n"; exit(1); }
+          lastEffect = 'c';
+          break;
+        case 'f': if (io==IO_IN) { inFreeverb = true; } else if (io==IO_OUT) { outFreeverb = true; }
+          else { std::cerr << "-f arg `" << optarg << "' malformed\n"; exit(1); }
+          lastEffect = 'f';
+          break;
+        case 'z': if (io==IO_IN) { inZitarev = true; } else if (io==IO_OUT) { outZitarev = true; }
+          else { std::cerr << "-f arg `" << optarg << "' malformed\n"; exit(1); }
+          lastEffect = 'z';
+          break;
+        case '(': parenLevel++;
+          for (ulong j=i+1; j<argLen; j++) {
+            if (optarg[j] == ')') {
+              optarg[j] = '\0';
+              switch(lastEffect) {
+              case 'c': {
+                returnCode += parseCompresserArgs(&optarg[i+1],io);
+                break; }
+              case 'z': {
+              float farg = atof(&optarg[i+1]);
+              if (io==IO_IN) {
+                  zitarevInLevel = farg;
+              } else if (io==IO_OUT) {
+                  zitarevOutLevel = farg;
+                } // else ignore the argument
+                break; }
+              case 'f': {
+                float farg = atof(&optarg[i+1]);
+                if (io==IO_IN) {
+                  freeverbInLevel = farg;
+                } else if (io==IO_OUT) {
+                  freeverbOutLevel = farg;
+                } // else ignore the argument
+                break; }
+              default: { // ignore
+                break; }
+              }
+              optarg[j] = ')';
+              break;
+            }
+          }
+          break;
+        case ')': parenLevel--;
+          break;
+        default:
+          break; // ignore
+        } // switch(optarg[i])
+      }
+    }
+    return returnCode;
+  }
+
+  int parseLimiterOptArg(char* cmd, char* optarg) {
+    int returnCode = 0;
+    lastEffect = 'O'; // OverflowLimiter
+    char ch = tolower(optarg[0]);
+    if (ch == '-' || ch == 0) {
+      std::cerr << cmd << " argument i, o, or io is REQUIRED\n";
+      returnCode = 2;
+    } else if (ch == 'h') {
+      printHelp(cmd,'O');
+      returnCode = 1;
+    } else {
+      bool haveIncoming = false;
+      bool haveOutgoing = false;
+      bool haveWarnings = false;
+      for (int i=0; i<strlen(optarg); i++) {
+        ch = tolower(optarg[i]);
+        switch(ch) {
+        case ' ': break;
+        case '\t': break;
+        case 'i':
+          haveIncoming = true;
+          break;
+        case 'o':
+          haveOutgoing = true;
+          break;
+        case 'w':
+          haveWarnings = true;
+          break;
+        case 'n':
+          haveIncoming = false;
+          haveOutgoing = false;
+          break;
+        default:
+          std::cerr << "*** Effects.h: parseLimiterOptArg: Unrecognized option " << ch << "\n";
+          returnCode = 2;
+        } // switch(ch)
+      } // process optarg char ch
+      mLimit = (haveIncoming && haveOutgoing ? LIMITER_BOTH
+                : (haveIncoming ? LIMITER_INCOMING
+                   : (haveOutgoing ? LIMITER_OUTGOING : LIMITER_NONE)));
+      if (haveWarnings) {
+        limiterWarningAmplitude = 0.5; // KEEP IN SYNC WITH LIMITER THRESHOLD/CEILING 'softClipLevel' in ../faust-src/limiterdsp.dsp
+        // the warning amplitude and limiter compression threshold can of course be brought as a parameters, e.g. w(0.5)
+      }
+      if (gVerboseFlag) {
+        if(haveIncoming) {
+          std::cout << "Set up INCOMING Overflow Limiter\n";
+        }
+        if(haveOutgoing) {
+          std::cout << "Set up OUTGOING Overflow Limiter\n";
+        }
+        if(haveWarnings) {
+          std::cout << "Enable DISTORTION WARNINGS in Overflow Limiters\n";
+        }
+        if(not haveIncoming and not haveOutgoing) {
+          std::cout << "Set up NO Overflow Limiters\n";
+        }
+      } // gVerboseFlag
+    } // optarg cases
+    return returnCode;
+  } // parseLimiterOptArg()
+
+  int parseAssumedNumClientsOptArg(char* cmd, char* optarg) {
+    int returnCode = 0;
+    lastEffect = 'a'; // assumedNumClients
+    char ch = optarg[0];
+    if (ch == 'h') {
+      printHelp(cmd,'a');
+      returnCode = 1;
+    } else if (ch == '-' || isalpha(ch) || ch == 0) {
+      std::cerr << cmd << " argument help or integer > 0 is REQUIRED\n";
+      returnCode = 2;
+    } else {
+    mNumClientsAssumed = atoi(optarg);
+    if(mNumClientsAssumed < 1) {
+      std::cerr << "-p ERROR: Must have at least one assumed sound source: "
+                << atoi(optarg) << " is not supported." << std::endl;
+        returnCode = 2;
+      }
+    }
+    return returnCode;
+  }
+
+};
index 8cdab74d50aa31510553b835447729a1a2a45936..f34861702a084bcf4fb240fd55bebc5597d778a9 100644 (file)
@@ -78,7 +78,7 @@ JMess::~JMess()
  *
  */
 //-------------------------------------------------------------------------------
-void JMess::writeOutput(QString xmlOutFile)
+void JMess::writeOutput(__attribute__((unused)) QString xmlOutFile)
 {
     //  QDomDocument jmess_xml;   QDomElement root;
     //  QDomElement connection;   QDomElement output;
@@ -171,13 +171,12 @@ void JMess::setConnectedPorts()
 void JMess::connectSpawnedPorts(int nChans, int hubPatch)
 // called from UdpHubListener::connectMesh
 {
-
     QMutexLocker locker(&sJMessMutex);
-
+    
     QString IPS[gMAX_WAIRS];
     int ctr = 0;
 
-    const char **ports, **connections; //vector of ports and connections
+    const char **ports; //, **connections; //vector of ports and connections
     QVector<QString> OutputInput(2); //helper variable
 
     //Get active output ports.
@@ -202,7 +201,7 @@ void JMess::connectSpawnedPorts(int nChans, int hubPatch)
             //                        qDebug() << ports[out_i] << systemPort << s;
         }
     }
-//    for (int i = 0; i<ctr; i++) qDebug() << IPS[i];
+    //for (int i = 0; i<ctr; i++) qDebug() << IPS[i];
     disconnectAll();
 
     int k = 0;
@@ -217,8 +216,8 @@ void JMess::connectSpawnedPorts(int nChans, int hubPatch)
             if ((hubPatch == JackTrip::CLIENTECHO)||(hubPatch == JackTrip::FULLMIX)) k = i;
             else if (hubPatch == JackTrip::CLIENTFOFI) k = (j+(i+1))%ctr;
             for (int l = 1; l<=nChans; l++) { // chans are 1-based
-//                qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l)
-//                         <<"with " << IPS[k]+":send_"+QString::number(l);
+                //qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l)
+                         //<<"with " << IPS[k]+":send_"+QString::number(l);
 
                 QString left = IPS[i] +
                         ":receive_" + QString::number(l);
@@ -244,8 +243,8 @@ void JMess::connectSpawnedPorts(int nChans, int hubPatch)
             for (int j = 0; j<jLimit; j++) {
                 k = (j+(i+1))%ctr;
                 for (int l = 1; l<=nChans; l++) { // chans are 1-based
-//                    qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l)
-//                             <<"with " << IPS[k]+":send_"+QString::number(l);
+                    //qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l)
+                             //<<"with " << IPS[k]+":send_"+QString::number(l);
 
                     QString left = IPS[i] +
                             ":receive_" + QString::number(l);
@@ -371,7 +370,7 @@ void JMess::disconnectAll()
          it != mConnectedPorts.end(); ++it) {
         OutputInput = *it;
 
-        if (jack_disconnect(mClient, OutputInput[0].toLatin1(), OutputInput[1].toLatin1())) {
+        if (jack_disconnect(mClient, OutputInput[0].toUtf8(), OutputInput[1].toUtf8())) {
             cerr << "WARNING: port: " << qPrintable(OutputInput[0])
                     << "and port: " << qPrintable(OutputInput[1])
                     << " could not be disconnected.\n";
@@ -388,7 +387,7 @@ void JMess::disconnectAll()
  * read the file.
  */
 //-------------------------------------------------------------------------------
-int JMess::parseXML(QString xmlInFile)
+int JMess::parseXML(__attribute__((unused)) QString xmlInFile)
 {
     //  mPortsToConnect.clear();
     //  QString errorStr;
@@ -455,7 +454,7 @@ int JMess::parseXML(QString xmlInFile)
  *
  */
 //-------------------------------------------------------------------------------
-void JMess::connectPorts(QString xmlInFile)
+void JMess::connectPorts(__attribute__((unused)) QString xmlInFile)
 {
     QVector<QString> OutputInput(2);
 
index 70c763237d6877ca331430e1b6900b4f39f342f9..06ff4c815869998e5be69bc0e8eca05ce87fcb9a 100644 (file)
@@ -66,7 +66,7 @@ JackAudioInterface::JackAudioInterface(JackTrip* jacktrip,
                                        int NumNetRevChans,
                                        #endif // endwhere
                                        AudioInterface::audioBitResolutionT AudioBitResolution,
-                                       const char* ClientName) :
+                                       QString ClientName) :
     AudioInterface(jacktrip,
                    NumInChans, NumOutChans,
                    #ifdef WAIR // wair
@@ -81,6 +81,7 @@ JackAudioInterface::JackAudioInterface(JackTrip* jacktrip,
     mBitResolutionMode(AudioBitResolution),
     mClient(NULL),
     mClientName(ClientName),
+    mBroadcast(false),
     mJackTrip(jacktrip)
 {}
 
@@ -171,6 +172,7 @@ void JackAudioInterface::setupClient()
     // Initialize Buffer array to read and write audio
     mInBuffer.resize(mNumInChans);
     mOutBuffer.resize(mNumOutChans);
+    mBroadcastBuffer.resize(mNumOutChans);
 }
 
 
@@ -198,6 +200,18 @@ void JackAudioInterface::createChannels()
                                            JACK_DEFAULT_AUDIO_TYPE,
                                            JackPortIsOutput, 0);
     }
+    //Create Broadcast Ports
+    if (mBroadcast) {
+        mBroadcastPorts.resize(mNumOutChans);
+        for (int i = 0; i < mNumInChans; i++)
+        {
+            QString outName;
+            QTextStream (&outName) << "broadcast_" << i+1;
+            mBroadcastPorts[i] = jack_port_register (mClient, outName.toLatin1(),
+                                               JACK_DEFAULT_AUDIO_TYPE,
+                                               JackPortIsOutput, 0);
+        }
+    }
 }
 
 
@@ -273,7 +287,9 @@ int JackAudioInterface::stopProcess() const
 void JackAudioInterface::jackShutdown (void*)
 {
     //std::cout << "The Jack Server was shut down!" << std::endl;
-    throw std::runtime_error("The Jack Server was shut down!");
+    JackTrip::sJackStopped = true;
+    std::cout << "The Jack Server was shut down!" << std::endl;
+    //throw std::runtime_error("The Jack Server was shut down!");
     //std::cout << "Exiting program..." << std::endl;
     //std::exit(1);
 }
@@ -283,10 +299,15 @@ void JackAudioInterface::jackShutdown (void*)
 //*******************************************************************************
 int JackAudioInterface::processCallback(jack_nframes_t nframes)
 {
+  if(mProcessingAudio) {
+    std::cerr << "*** JackAudioInterface.cpp: DROPPED A BUFFER because AudioInterface::callback() not finished\n";
+    return 1;
+  }
+
     // Get input and output buffers from JACK
     //-------------------------------------------------------------------
     for (int i = 0; i < mNumInChans; i++) {
-        // Input Ports are READ ONLY
+        // Input Ports are READ ONLY and change as needed (no locks) - make a copy for debugging
         mInBuffer[i] = (sample_t*) jack_port_get_buffer(mInPorts[i], nframes);
     }
     for (int i = 0; i < mNumOutChans; i++) {
@@ -302,6 +323,14 @@ int JackAudioInterface::processCallback(jack_nframes_t nframes)
     //-------------------------------------------------------------------
 
     AudioInterface::callback(mInBuffer, mOutBuffer, nframes);
+
+    if (mBroadcast) {
+        for (int i = 0; i < mNumOutChans; i++) {
+            // Broadcast Ports are WRITABLE
+            mBroadcastBuffer[i] = (sample_t*) jack_port_get_buffer(mBroadcastPorts[i], nframes);
+        }
+        AudioInterface::broadcastCallback(mBroadcastBuffer, nframes);
+    }
     return 0;
 }
 
@@ -377,7 +406,7 @@ void JackAudioInterface::connectDefaultPorts()
 
 
 
-// OLD CODE
+// OLD CODE (some moved to parent class AudioInterface.cpp)
 // ==============================================================================
 
 //*******************************************************************************
index 7886d8269ee86b11e5e4a32213c7ba3aba06ddde..5a1b4b0c13c4b2a450a5f0defdc5f95b15eee937 100644 (file)
@@ -78,7 +78,7 @@ public:
                        int NumNetRevChans,
                    #endif // endwhere
                        AudioInterface::audioBitResolutionT AudioBitResolution = AudioInterface::BIT16,
-                       const char* ClientName = "JackTrip");
+                       QString ClientName = "JackTrip");
     /// \brief The class destructor
     virtual ~JackAudioInterface();
 
@@ -104,6 +104,7 @@ public:
     { std::cout << "WARNING: Setting the Sample Rate in Jack mode has no effect." << std::endl; }
     virtual void setBufferSizeInSamples(uint32_t /*buf_size*/)
     { std::cout << "WARNING: Setting the Sample Rate in Jack mode has no effect." << std::endl; }
+    virtual void enableBroadcastOutput() {mBroadcast = true;}
     //------------------------------------------------------------------
 
     //--------------GETTERS---------------------------------------------
@@ -113,7 +114,7 @@ public:
     virtual uint32_t getBufferSizeInSamples() const;
     /// \brief Get the Jack Server Buffer Size, in bytes
     virtual uint32_t getBufferSizeInBytes() const
-    { return (getBufferSizeInSamples() * getAudioBitResolution()/8); }
+    { return (getBufferSizeInSamples() * getAudioBitResolution() / 8); }
     /// \brief Get size of each audio per channel, in bytes
     virtual size_t getSizeInBytesPerChannel() const;
     //------------------------------------------------------------------
@@ -177,8 +178,11 @@ private:
     QString mClientName; ///< Jack Client Name
     QVarLengthArray<jack_port_t*> mInPorts; ///< Vector of Input Ports (Channels)
     QVarLengthArray<jack_port_t*> mOutPorts; ///< Vector of Output Ports (Channels)
+    QVarLengthArray<jack_port_t*> mBroadcastPorts; ///< Vector of Output Ports (Channels)
     QVarLengthArray<sample_t*> mInBuffer; ///< Vector of Input buffers/channel read from JACK
     QVarLengthArray<sample_t*> mOutBuffer; ///< Vector of Output buffer/channel to write to JACK
+    QVarLengthArray<sample_t*> mBroadcastBuffer; ///< Vector of Output buffer/channel to write to JACK
+    bool mBroadcast;
     size_t mSizeInBytesPerChannel; ///< Size in bytes per audio channel
     QVector<ProcessPlugin*> mProcessPlugins; ///< Vector of ProcesPlugin<EM>s</EM>
     JackTrip* mJackTrip; ///< JackTrip mediator class
index 4ba94af530a835422c6fc29803de943d7ab7d5e0..f67298440acc5d861a687cc74d4dee5b1221ba1c 100644 (file)
@@ -38,6 +38,7 @@
 #include "JackTrip.h"
 #include "UdpDataProtocol.h"
 #include "RingBufferWavetable.h"
+#include "JitterBuffer.h"
 #include "jacktrip_globals.h"
 #include "JackAudioInterface.h"
 #ifdef __RT_AUDIO__
@@ -51,7 +52,6 @@
 #include <QHostAddress>
 #include <QHostInfo>
 #include <QThread>
-#include <QTcpSocket>
 #include <QTimer>
 #include <QDateTime>
 
@@ -59,12 +59,15 @@ using std::cout; using std::endl;
 
 //the following function has to remain outside the Jacktrip class definition
 //its purpose is to close the app when control c is hit by the user in rtaudio/asio4all mode
-#if defined __WIN_32__
+/*if defined __WIN_32__
 void sigint_handler(int sig)
 {
     exit(0);
 }
-#endif
+#endif*/
+
+bool JackTrip::sSigInt = false;
+bool JackTrip::sJackStopped = false;
 
 //*******************************************************************************
 JackTrip::JackTrip(jacktripModeT JacktripMode,
@@ -89,15 +92,19 @@ JackTrip::JackTrip(jacktripModeT JacktripMode,
     mNumNetRevChans(NumNetRevChans),
     #endif // endwhere
     mBufferQueueLength(BufferQueueLength),
+    mBufferStrategy(1),
+    mBroadcastQueueLength(0),
     mSampleRate(gDefaultSampleRate),
     mDeviceID(gDefaultDeviceID),
     mAudioBufferSize(gDefaultBufferSizeInSamples),
     mAudioBitResolution(AudioBitResolution),
+    mLoopBack(false),
     mDataProtocolSender(NULL),
     mDataProtocolReceiver(NULL),
     mAudioInterface(NULL),
     mPacketHeader(NULL),
     mUnderRunMode(UnderRunMode),
+    mStopOnTimeout(false),
     mSendRingBuffer(NULL),
     mReceiveRingBuffer(NULL),
     mReceiverBindPort(receiver_bind_port),
@@ -108,20 +115,34 @@ JackTrip::JackTrip(jacktripModeT JacktripMode,
     mRedundancy(redundancy),
     mJackClientName(gJackDefaultClientName),
     mConnectionMode(JackTrip::NORMAL),
+    mTimeoutTimer(this),
+    mSleepTime(100),
+    mElapsedTime(0),
+    mEndTime(0),
+    mTcpClient(this),
+    mUdpSockTemp(this),
     mReceivedConnection(false),
     mTcpConnectionError(false),
     mStopped(false),
+    mHasShutdown(false),
     mConnectDefaultAudioPorts(true),
-    mIOStatLogStream(std::cout.rdbuf())
+    mIOStatTimeout(0),
+    mIOStatLogStream(std::cout.rdbuf()),
+    mSimulatedLossRate(0.0),
+    mSimulatedJitterRate(0.0),
+    mSimulatedDelayRel(0.0),
+    mUseRtUdpPriority(false),
+    mAudioTesterP(nullptr)
 {
     createHeader(mPacketHeaderType);
+    sJackStopped = false;
 }
 
 
 //*******************************************************************************
 JackTrip::~JackTrip()
 {
-    wait();
+    //wait();
     delete mDataProtocolSender;
     delete mDataProtocolReceiver;
     delete mAudioInterface;
@@ -134,14 +155,14 @@ JackTrip::~JackTrip()
 //*******************************************************************************
 void JackTrip::setupAudio(
         #ifdef WAIRTOHUB // WAIR
-        int ID
+        __attribute__((unused)) int ID
         #endif // endwhere
         )
 {
     // Check if mAudioInterface has already been created or not
     if (mAudioInterface != NULL)  { // if it has been created, disconnet it from JACK and delete it
         cout << "WARINING: JackAudio interface was setup already:" << endl;
-        cout << "It will be errased and setup again." << endl;
+        cout << "It will be erased and setup again." << endl;
         cout << gPrintSeparator << endl;
         closeAudio();
     }
@@ -157,23 +178,19 @@ void JackTrip::setupAudio(
                                                  mAudioBitResolution);
 
 #ifdef WAIRTOHUB // WAIR
-//        qDebug() << "mPeerAddress" << mPeerAddress << mPeerAddress.contains(gDOMAIN_TRIPLE);
         QString VARIABLE_AUDIO_NAME = WAIR_AUDIO_NAME; // legacy for WAIR
-        QByteArray tmp = QString(mPeerAddress).replace(":", ".").toLatin1();
         //Set our Jack client name if we're a hub server or a custom name hasn't been set
         if (!mPeerAddress.isEmpty() && (mJackClientName.constData() == gJackDefaultClientName.constData())) {
             mJackClientName = QString(mPeerAddress).replace(":", "_");
         }
-//           if ( mPeerAddress.toStdString() != "" &&
-//               (mJackClientName == gJackDefaultClientName || mJackTripMode == SERVERPINGSERVER)) {
-//            mJackClientName = QString(mPeerAddress).replace(":", ".").toLatin1().constData();
-//        }
+        //std::cout  << "WAIR ID " << ID << " jacktrip client name set to=" <<
+        //              mJackClientName.toStdString() << std::endl;
 
-//        std::cout  << "WAIR ID " << ID << " jacktrip client name set to=" <<
-//                      mJackClientName << std::endl;
 #endif // endwhere
-
         mAudioInterface->setClientName(mJackClientName);
+        if (0 < mBroadcastQueueLength) {
+            mAudioInterface->enableBroadcastOutput();
+        }
 
         if (gVerboseFlag) std::cout << "  JackTrip:setupAudio before mAudioInterface->setup" << std::endl;
         mAudioInterface->setup();
@@ -205,17 +222,27 @@ void JackTrip::setupAudio(
 #endif
     }
 
+    mAudioInterface->setLoopBack(mLoopBack);
+    if (mAudioTesterP) { // if we're a hub server, this will be a nullptr - MAJOR REFACTOR NEEDED, in my opinion
+      mAudioTesterP->setSampleRate(mSampleRate);
+    }
+    mAudioInterface->setAudioTesterP(mAudioTesterP);
+
     std::cout << "The Sampling Rate is: " << mSampleRate << std::endl;
     std::cout << gPrintSeparator << std::endl;
     int AudioBufferSizeInBytes = mAudioBufferSize*sizeof(sample_t);
     std::cout << "The Audio Buffer Size is: " << mAudioBufferSize << " samples" << std::endl;
     std::cout << "                      or: " << AudioBufferSizeInBytes
               << " bytes" << std::endl;
+    if (0 < mBroadcastQueueLength) {
+        std::cout << gPrintSeparator << std::endl;
+        cout << "Broadcast Output is enabled, delay = "
+             << mBroadcastQueueLength * mAudioBufferSize * 1000 / mSampleRate << " ms"
+             << " (" << mBroadcastQueueLength * mAudioBufferSize << " samples)" << endl;
+    }
     std::cout << gPrintSeparator << std::endl;
     cout << "The Number of Channels is: " << mAudioInterface->getNumInputChannels() << endl;
     std::cout << gPrintSeparator << std::endl;
-    cout << "The RTAudio device ID is: " << mAudioInterface->getDeviceID() << endl;
-    std::cout << gPrintSeparator << std::endl;
     QThread::usleep(100);
 }
 
@@ -235,11 +262,11 @@ void JackTrip::closeAudio()
 //*******************************************************************************
 void JackTrip::setupDataProtocol()
 {
+    double simulated_max_delay = mSimulatedDelayRel * getBufferSizeInSamples() / getSampleRate();
     // Create DataProtocol Objects
     switch (mDataProtocol) {
     case UDP:
         std::cout << "Using UDP Protocol" << std::endl;
-        std::cout << gPrintSeparator << std::endl;
         QThread::usleep(100);
         mDataProtocolSender = new UdpDataProtocol(this, DataProtocol::SENDER,
                                                   //mSenderPeerPort, mSenderBindPort,
@@ -248,6 +275,15 @@ void JackTrip::setupDataProtocol()
         mDataProtocolReceiver =  new UdpDataProtocol(this, DataProtocol::RECEIVER,
                                                      mReceiverBindPort, mReceiverPeerPort,
                                                      mRedundancy);
+        if (0.0 < mSimulatedLossRate || 0.0 < mSimulatedJitterRate || 0.0 < simulated_max_delay) {
+            mDataProtocolReceiver->setIssueSimulation(mSimulatedLossRate, mSimulatedJitterRate, simulated_max_delay);
+        }
+        mDataProtocolSender->setUseRtPriority(mUseRtUdpPriority);
+        mDataProtocolReceiver->setUseRtPriority(mUseRtUdpPriority);
+        if (mUseRtUdpPriority) {
+            cout << "Using RT thread priority for UDP data" << endl;
+        }
+        std::cout << gPrintSeparator << std::endl;
         break;
     case TCP:
         throw std::invalid_argument("TCP Protocol is not implemented");
@@ -277,6 +313,12 @@ void JackTrip::setupRingBuffers()
     /// \todo Make all this operations cleaner
     //int total_audio_packet_size = getTotalAudioPacketSizeInBytes();
     int slot_size = getRingBuffersSlotSize();
+    if (0 <=  mBufferStrategy) {
+        mUnderRunMode = ZEROS;
+    }
+    else if (0 > mBufferQueueLength) {
+      throw std::invalid_argument("Auto queue is not supported by RingBuffer");
+    }
 
     switch (mUnderRunMode) {
     case WAVETABLE:
@@ -290,13 +332,23 @@ void JackTrip::setupRingBuffers()
     mReceiveRingBuffer = new RingBufferWavetable(mAudioInterface->getSizeInBytesPerChannel() * mNumChans,
              mBufferQueueLength);
              */
-
         break;
     case ZEROS:
         mSendRingBuffer = new RingBuffer(slot_size,
                                          gDefaultOutputQueueLength);
-        mReceiveRingBuffer = new RingBuffer(slot_size,
-                                            mBufferQueueLength);
+        if (0 > mBufferStrategy) {
+            mReceiveRingBuffer = new RingBuffer(slot_size,
+                                                mBufferQueueLength);
+        }
+        else {
+            cout << "Using JitterBuffer strategy " << mBufferStrategy << endl;
+            if (0 > mBufferQueueLength) {
+                cout << "Using AutoQueue 1/" << -mBufferQueueLength << endl;
+            }
+            mReceiveRingBuffer = new JitterBuffer(mAudioBufferSize, mBufferQueueLength,
+                                        mSampleRate, mBufferStrategy,
+                                        mBroadcastQueueLength, mNumChans, mAudioBitResolution);
+        }
         /*
     mSendRingBuffer = new RingBuffer(mAudioInterface->getSizeInBytesPerChannel() * mNumChans,
              gDefaultOutputQueueLength);
@@ -312,17 +364,28 @@ void JackTrip::setupRingBuffers()
 
 
 //*******************************************************************************
-void JackTrip::setPeerAddress(const char* PeerHostOrIP)
+void JackTrip::setPeerAddress(QString PeerHostOrIP)
 {
     mPeerAddress = PeerHostOrIP;
 }
 
 
 //*******************************************************************************
-void JackTrip::appendProcessPlugin(ProcessPlugin* plugin)
+void JackTrip::appendProcessPluginToNetwork(ProcessPlugin* plugin)
 {
-    mProcessPlugins.append(plugin);
-    //mAudioInterface->appendProcessPlugin(plugin);
+  if (plugin) {
+    mProcessPluginsToNetwork.append(plugin); // ownership transferred
+    //mAudioInterface->appendProcessPluginToNetwork(plugin);
+  }
+}
+
+//*******************************************************************************
+void JackTrip::appendProcessPluginFromNetwork(ProcessPlugin* plugin)
+{
+  if (plugin) {
+    mProcessPluginsFromNetwork.append(plugin); // ownership transferred
+    //mAudioInterface->appendProcessPluginFromNetwork(plugin);
+  }
 }
 
 
@@ -333,20 +396,20 @@ void JackTrip::startProcess(
         #endif // endwhere
         )
 { //signal that catches ctrl c in rtaudio-asio mode
-#if defined (__WIN_32__)
+/*#if defined (__WIN_32__)
     if (signal(SIGINT, sigint_handler) == SIG_ERR) {
         perror("signal");
         exit(1);
     }
-#endif
+#endif*/
     // Check if ports are already binded by another process on this machine
     // ------------------------------------------------------------------
     if (gVerboseFlag) std::cout << "step 1" << std::endl;
 
     if (gVerboseFlag) std::cout << "  JackTrip:startProcess before checkIfPortIsBinded(mReceiverBindPort)" << std::endl;
 #if defined __WIN_32__
-    //cc fixed windows crash with this print statement! hope to delete
-//    qDebug() << "before  mJackTrip->startProcess"  << mReceiverBindPort<< mSenderBindPort;
+    //cc fixed windows crash with this print statement!
+    //qDebug() << "before mJackTrip->startProcess" << mReceiverBindPort<< mSenderBindPort;
 #endif
     checkIfPortIsBinded(mReceiverBindPort);
     if (gVerboseFlag) std::cout << "  JackTrip:startProcess before checkIfPortIsBinded(mSenderBindPort)" << std::endl;
@@ -365,18 +428,24 @@ void JackTrip::startProcess(
     setupRingBuffers();
     // Connect Signals and Slots
     // -------------------------
-    QObject::connect(mPacketHeader, SIGNAL(signalError(const char*)),
-                     this, SLOT(slotStopProcesses()), Qt::QueuedConnection);
+    QObject::connect(mPacketHeader, &PacketHeader::signalError,
+                     this, &JackTrip::slotStopProcessesDueToError, Qt::QueuedConnection);
     QObject::connect(mDataProtocolReceiver, SIGNAL(signalReceivedConnectionFromPeer()),
                      this, SLOT(slotReceivedConnectionFromPeer()),
                      Qt::QueuedConnection);
-    QObject::connect(this, SIGNAL(signalUdpTimeOut()),
-                     this, SLOT(slotStopProcesses()), Qt::QueuedConnection);
+    //QObject::connect(this, SIGNAL(signalUdpTimeOut()),
+    //                 this, SLOT(slotStopProcesses()), Qt::QueuedConnection);
+    QObject::connect((UdpDataProtocol *)mDataProtocolReceiver, &UdpDataProtocol::signalUdpWaitingTooLong, this,
+                     &JackTrip::slotUdpWaitingTooLong, Qt::QueuedConnection);
+    QObject::connect(mDataProtocolSender, &DataProtocol::signalCeaseTransmission,
+                     this, &JackTrip::slotStopProcessesDueToError, Qt::QueuedConnection);
+    QObject::connect(mDataProtocolReceiver, &DataProtocol::signalCeaseTransmission,
+                     this, &JackTrip::slotStopProcessesDueToError, Qt::QueuedConnection);
 
     //QObject::connect(mDataProtocolSender, SIGNAL(signalError(const char*)),
     //                 this, SLOT(slotStopProcesses()), Qt::QueuedConnection);
-    //QObject::connect(mDataProtocolReceiver, SIGNAL(signalError(const char*)),
-    //                 this, SLOT(slotStopProcesses()), Qt::QueuedConnection);
+    QObject::connect(mDataProtocolReceiver, SIGNAL(signalError(const char*)),
+                     this, SLOT(slotStopProcesses()), Qt::QueuedConnection);
 
     // Start the threads for the specific mode
     // ---------------------------------------
@@ -396,8 +465,7 @@ void JackTrip::startProcess(
         if (gVerboseFlag) std::cout << "step 2C client only" << std::endl;
         if (gVerboseFlag) std::cout << "  JackTrip:startProcess case CLIENTTOPINGSERVER before clientPingToServerStart" << std::endl;
         if ( clientPingToServerStart() == -1 ) { // if error on server start (-1) we return inmediatly
-            mTcpConnectionError = true;
-            slotStopProcesses();
+            stop("Peer Address has to be set if you run in CLIENTTOPINGSERVER mode");
             return;
         }
         break;
@@ -405,15 +473,18 @@ void JackTrip::startProcess(
         if (gVerboseFlag) std::cout << "step 2S server only (same as 2s)" << std::endl;
         if (gVerboseFlag) std::cout << "  JackTrip:startProcess case SERVERPINGSERVER before serverStart" << std::endl;
         if ( serverStart(true) == -1 ) { // if error on server start (-1) we return inmediatly
-            slotStopProcesses();
+            stop();
             return;
         }
         break;
     default:
-        throw std::invalid_argument("Jacktrip Mode  undefined");
+        throw std::invalid_argument("Jacktrip Mode undefined");
         break;
     }
+}
 
+void JackTrip::completeConnection()
+{
     // Have the threads share a single socket that operates at full duplex.
 #if defined (__WIN_32__)
     SOCKET sock_fd = INVALID_SOCKET;
@@ -439,21 +510,27 @@ void JackTrip::startProcess(
     QThread::msleep(1);
     if (gVerboseFlag) std::cout << "step 5" << std::endl;
     if (gVerboseFlag) std::cout << "  JackTrip:startProcess before mAudioInterface->startProcess" << std::endl;
-    mAudioInterface->startProcess();
-
-    for (int i = 0; i < mProcessPlugins.size(); ++i) {
-        mAudioInterface->appendProcessPlugin(mProcessPlugins[i]);
+    for (int i = 0; i < mProcessPluginsFromNetwork.size(); ++i) {
+        mAudioInterface->appendProcessPluginFromNetwork(mProcessPluginsFromNetwork[i]);
     }
-    if (mConnectDefaultAudioPorts) {  mAudioInterface->connectDefaultPorts(); }
-}
+    for (int i = 0; i < mProcessPluginsToNetwork.size(); ++i) {
+        mAudioInterface->appendProcessPluginToNetwork(mProcessPluginsToNetwork[i]);
+    }
+    mAudioInterface->initPlugins();  // mSampleRate known now, which plugins require
+    mAudioInterface->startProcess(); // Tell JACK server we are ready for audio flow now
 
-//*******************************************************************************
-void JackTrip::startIOStatTimer(int timeout_sec, const std::ostream& log_stream)
-{
-    mIOStatLogStream.rdbuf(log_stream.rdbuf());
-    QTimer *timer = new QTimer(this);
-    connect(timer, SIGNAL(timeout()), this, SLOT(onStatTimer()));
-    timer->start(timeout_sec*1000);
+    if (mConnectDefaultAudioPorts) {  mAudioInterface->connectDefaultPorts(); }
+    
+    //Start our IO stat timer
+    if (mIOStatTimeout > 0) {
+        cout << "STATS" << mIOStatTimeout << endl;
+        if (!mIOStatStream.isNull()) {
+            mIOStatLogStream.rdbuf(((std::ostream *)mIOStatStream.data())->rdbuf());
+        }
+        QTimer *timer = new QTimer(this);
+        connect(timer, SIGNAL(timeout()), this, SLOT(onStatTimer()));
+        timer->start(mIOStatTimeout*1000);
+    }
 }
 
 //*******************************************************************************
@@ -473,11 +550,12 @@ void JackTrip::onStatTimer()
         return;
     }
     QString now = QDateTime::currentDateTime().toString(Qt::ISODate);
-    int32_t skew = recv_io_stat.underruns - recv_io_stat.overflows
-                - pkt_stat.lost + pkt_stat.revived;
 
     static QMutex mutex;
     QMutexLocker locker(&mutex);
+    if (mAudioTesterP && mAudioTesterP->getEnabled()) {
+      mIOStatLogStream << "\n";
+    }
     mIOStatLogStream << now.toLocal8Bit().constData()
       << " " << getPeerAddress().toLocal8Bit().constData()
       << " send: "
@@ -492,13 +570,197 @@ void JackTrip::onStatTimer()
       << "/" << pkt_stat.revived
       << " tot: "
       << pkt_stat.tot
-      << " skew: " << skew
+      << " sync: "
+      << recv_io_stat.level
+      << "/" << recv_io_stat.buf_inc_underrun
+      << "/" << recv_io_stat.buf_inc_compensate
+      << "/" << recv_io_stat.buf_dec_overflows
+      << "/" << recv_io_stat.buf_dec_pktloss
+      << " skew: " << recv_io_stat.skew
+      << "/" << recv_io_stat.skew_raw
+      << " bcast: " << recv_io_stat.broadcast_skew
+      << "/" << recv_io_stat.broadcast_delta
+      << " autoq: " << 0.1*recv_io_stat.autoq_corr
+      << "/" << 0.1*recv_io_stat.autoq_rate
       << endl;
 }
 
+void JackTrip::receivedConnectionTCP()
+{
+    mTimeoutTimer.stop();
+    if (gVerboseFlag) cout << "TCP Socket Connected to Server!" << endl;
+    emit signalTcpClientConnected();
+
+    // Send Client Port Number to Server
+    // ---------------------------------
+    char port_buf[sizeof(mReceiverBindPort) + gMaxRemoteNameLength];
+    std::memcpy(port_buf, &mReceiverBindPort, sizeof(mReceiverBindPort));
+    std::memset(port_buf + sizeof(mReceiverBindPort), 0, gMaxRemoteNameLength);
+    if (!mRemoteClientName.isEmpty()) {
+        //If our remote client name is set, send it too.
+        QByteArray name = mRemoteClientName.toUtf8();
+        // Find a clean place to truncate if we're over length.
+        // (Make sure we're not in the middle of a multi-byte characetr.)
+        int length = name.length();
+        //Need to take the final null terminator into account here.
+        if (length > gMaxRemoteNameLength - 1) {
+            length = gMaxRemoteNameLength - 1;
+            while ((length > 0) && ((name.at(length) & 0xc0) == 0x80)) {
+                //We're in the middle of a multi-byte character. Work back.
+                length--;
+            }
+        }
+        name.truncate(length);
+        std::memcpy(port_buf + sizeof(mReceiverBindPort), name.data(), length + 1);
+    }
+
+    mTcpClient.write(port_buf, sizeof(port_buf));
+    /*while ( mTcpClient.bytesToWrite() > 0 ) {
+        mTcpClient.waitForBytesWritten(-1);
+    }*/
+    if (gVerboseFlag) cout << "Port " << mReceiverBindPort << " sent to Server" << endl;
+    //Continued in receivedDataTCP slot
+}
+
+void JackTrip::receivedDataTCP()
+{
+    if (mTcpClient.bytesAvailable() < (int)sizeof(uint16_t)) {
+        return;
+    }
+    
+    // Read the size of the package
+    // ----------------------------
+    if (gVerboseFlag) cout << "Reading UDP port from Server..." << endl;
+    if (gVerboseFlag) cout << "Ready To Read From Socket!" << endl;
+
+    // Read UDP Port Number from Server
+    // --------------------------------
+    uint32_t udp_port;
+    int size = sizeof(udp_port);
+    char port_buf[sizeof(mReceiverBindPort)];
+    //char port_buf[size];
+    mTcpClient.read(port_buf, size);
+    std::memcpy(&udp_port, port_buf, size);
+    //cout << "Received UDP Port Number: " << udp_port << endl;
+
+    // Close the TCP Socket
+    // --------------------
+    mTcpClient.close(); // Close the socket
+    //cout << "TCP Socket Closed!" << endl;
+    if (gVerboseFlag) cout << "Connection Succesfull!" << endl;
+
+    // Set with the received UDP port
+    // ------------------------------
+    setPeerPorts(udp_port);
+    mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() );
+    mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() );
+    mDataProtocolSender->setPeerPort(udp_port);
+    mDataProtocolReceiver->setPeerPort(udp_port);
+    cout << "Server Address set to: " << mPeerAddress.toStdString() << " Port: " << udp_port << std::endl;
+    cout << gPrintSeparator << endl;
+    completeConnection();
+}
+
+void JackTrip::receivedDataUDP()
+{
+    //Stop our timer.
+    mTimeoutTimer.stop();
+    
+    QHostAddress peerHostAddress;
+    uint16_t peer_port;
+    
+    // IPv6 addition from fyfe
+    // Get the datagram size to avoid problems with IPv6
+    qint64 datagramSize = mUdpSockTemp.pendingDatagramSize();
+    char buf[datagramSize];
+    // set client address
+    mUdpSockTemp.readDatagram(buf, datagramSize, &peerHostAddress, &peer_port);
+    mUdpSockTemp.close(); // close the socket
+
+    // Check for mapped IPv4->IPv6 addresses that look like ::ffff:x.x.x.x
+    if (peerHostAddress.protocol() == QAbstractSocket::IPv6Protocol) {
+        bool mappedIPv4;
+        uint32_t address = peerHostAddress.toIPv4Address(&mappedIPv4);
+        // If the IPv4 address is mapped to IPv6, convert it to IPv4
+        if (mappedIPv4) {
+            QHostAddress ipv4Address = QHostAddress(address);
+            mPeerAddress = ipv4Address.toString();
+        } else {
+            mPeerAddress = peerHostAddress.toString();
+        }
+    }
+    else {
+        mPeerAddress = peerHostAddress.toString();
+    }
+
+    // Set the peer address to send packets (in the protocol sender)
+    if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolSender->setPeerAddress()" << std::endl;
+    mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().constData() );
+    if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolReceiver->setPeerAddress()" << std::endl;
+    mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().constData() );
+    //     We reply to the same port the peer sent the packets from
+    //     This way we can go through NAT
+    //     Because of the NAT traversal scheme, the portn need to be
+    //     "symetric", e.g.:
+    //     from Client to Server : src = 4474, dest = 4464
+    //     from Server to Client : src = 4464, dest = 4474
+    // no -- all are the same -- 4464
+    if (gVerboseFlag) std::cout << "JackTrip:serverStart before setting all peer_port instances to " << peer_port << std::endl;
+    mDataProtocolSender->setPeerPort(peer_port);
+    mDataProtocolReceiver->setPeerPort(peer_port);
+    setPeerPorts(peer_port);
+    completeConnection();
+}
+
+void JackTrip::udpTimerTick()
+{
+    if (mStopped || sSigInt || sJackStopped) {
+        //Stop everything.
+        mUdpSockTemp.close();
+        mTimeoutTimer.stop();
+        stop();
+    }
+    
+    if (gVerboseFlag) std::cout << mSleepTime << "ms  " << std::flush;
+    mElapsedTime += mSleepTime;
+    if (mEndTime > 0 && mElapsedTime >= mEndTime) {
+        mUdpSockTemp.close();
+        mTimeoutTimer.stop();
+        cout << "JackTrip Server Timed Out!" << endl;
+        stop("JackTrip Server Timed Out");
+    }
+}
+
+void JackTrip::tcpTimerTick()
+{
+    if (mStopped || sSigInt || sJackStopped) {
+        //Stop everything.
+        mTcpClient.close();
+        mTimeoutTimer.stop();
+        stop();
+    }
+    
+    mElapsedTime += mSleepTime;
+    if (mEndTime > 0 && mElapsedTime >= mEndTime) {
+        mTcpClient.close();
+        mTimeoutTimer.stop();
+        cout << "JackTrip Server Timed Out!" << endl;
+        stop("Initial TCP Connection Timed Out");
+    }
+    
+}
+
 //*******************************************************************************
-void JackTrip::stop()
+void JackTrip::stop(QString errorMessage)
 {
+    mStopped = true;
+    //Make sure we're only run once
+    if (mHasShutdown) {
+        return;
+    }
+    mHasShutdown = true;
+    std::cout << "Stopping JackTrip..." << std::endl;
+    
     // Stop The Sender
     mDataProtocolSender->stop();
     mDataProtocolSender->wait();
@@ -510,12 +772,18 @@ void JackTrip::stop()
     // Stop the audio processes
     //mAudioInterface->stopProcess();
     closeAudio();
-
+    
     cout << "JackTrip Processes STOPPED!" << endl;
     cout << gPrintSeparator << endl;
 
     // Emit the jack stopped signal
-    emit signalProcessesStopped();
+    if (sJackStopped) {
+        emit signalError("The Jack Server was shut down!");
+    } else if (errorMessage.isEmpty()) {
+        emit signalProcessesStopped();
+    } else {
+        emit signalError(errorMessage);
+    }
 }
 
 
@@ -539,6 +807,7 @@ void JackTrip::clientStart()
         mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() );
         cout << "Peer Address set to: " << mPeerAddress.toStdString() << std::endl;
         cout << gPrintSeparator << endl;
+        completeConnection();
     }
 }
 
@@ -555,88 +824,34 @@ int JackTrip::serverStart(bool timeout, int udpTimeout) // udpTimeout unused
     }
 
     // Get the client address when it connects
-    QHostAddress peerHostAddress;
-    uint16_t peer_port;
-    if (gVerboseFlag) std::cout << "JackTrip:serverStart before QUdpSocket UdpSockTemp" << std::endl;
-    QUdpSocket UdpSockTemp;// Create socket to wait for client
-
-    if (gVerboseFlag) std::cout << "JackTrip:serverStart before UdpSockTemp.bind(Any)" << std::endl;
+    if (gVerboseFlag) std::cout << "JackTrip:serverStart before mUdpSockTemp.bind(Any)" << std::endl;
     // Bind the socket
-    if ( !UdpSockTemp.bind(QHostAddress::Any, mReceiverBindPort,
+    if ( !mUdpSockTemp.bind(QHostAddress::Any, mReceiverBindPort,
                            QUdpSocket::DefaultForPlatform) )
     {
         std::cerr << "in JackTrip: Could not bind UDP socket. It may be already binded." << endl;
         throw std::runtime_error("Could not bind UDP socket. It may be already binded.");
     }
-    // Listen to client
-    int sleepTime = 100; // ms
-    int elapsedTime = 0;
+    connect(&mUdpSockTemp, &QUdpSocket::readyRead, this, &JackTrip::receivedDataUDP);
+    
+    // Start timer and then wait for a signal to read datagrams.
+    mElapsedTime = 0;
     if (timeout) {
-        while ( (!UdpSockTemp.hasPendingDatagrams()) && (elapsedTime <= udpTimeout) ) {
-            if (mStopped == true) { emit signalUdpTimeOut(); UdpSockTemp.close(); return -1; }
-            QThread::msleep(sleepTime);
-            elapsedTime += sleepTime;
-        }
-        if (!UdpSockTemp.hasPendingDatagrams()) {
-            emit signalUdpTimeOut();
-            cout << "JackTrip Server Timed Out!" << endl;
-            return -1;
-        }
-    } else {
-        if (gVerboseFlag) std::cout << "JackTrip:serverStart before !UdpSockTemp.hasPendingDatagrams()" << std::endl;
-        cout << "Waiting for Connection From a Client..." << endl;
-        while ( !UdpSockTemp.hasPendingDatagrams() ) {
-            if (mStopped == true) { emit signalUdpTimeOut(); return -1; }
-            if (gVerboseFlag) std::cout << sleepTime << "ms  " << std::flush;
-            QThread::msleep(sleepTime);
-        }
+        mEndTime = udpTimeout;
     }
+    mTimeoutTimer.setInterval(mSleepTime);
+    connect(&mTimeoutTimer, &QTimer::timeout, this, &JackTrip::udpTimerTick);
+    mTimeoutTimer.start();
+    
+    if (gVerboseFlag) std::cout << "JackTrip:serverStart before !UdpSockTemp.hasPendingDatagrams()" << std::endl;
+    cout << "Waiting for Connection From a Client..." << endl;
+    return 0;
+    // Continued in the receivedDataUDP slot.
+
     //    char buf[1];
     //    // set client address
     //    UdpSockTemp.readDatagram(buf, 1, &peerHostAddress, &peer_port);
     //    UdpSockTemp.close(); // close the socket
-
-    // IPv6 addition from fyfe
-    // Get the datagram size to avoid problems with IPv6
-    qint64 datagramSize = UdpSockTemp.pendingDatagramSize();
-    char buf[datagramSize];
-    // set client address
-    UdpSockTemp.readDatagram(buf, datagramSize, &peerHostAddress, &peer_port);
-    UdpSockTemp.close(); // close the socket
-
-    // Check for mapped IPv4->IPv6 addresses that look like ::ffff:x.x.x.x
-    if (peerHostAddress.protocol() == QAbstractSocket::IPv6Protocol) {
-        bool mappedIPv4;
-        uint32_t address = peerHostAddress.toIPv4Address(&mappedIPv4);
-        // If the IPv4 address is mapped to IPv6, convert it to IPv4
-        if (mappedIPv4) {
-            QHostAddress ipv4Address = QHostAddress(address);
-            mPeerAddress = ipv4Address.toString();
-        } else {
-            mPeerAddress = peerHostAddress.toString();
-        }
-    }
-    else {
-        mPeerAddress = peerHostAddress.toString();
-    }
-
-    // Set the peer address to send packets (in the protocol sender)
-    if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolSender->setPeerAddress()" << std::endl;
-    mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().constData() );
-    if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolReceiver->setPeerAddress()" << std::endl;
-    mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().constData() );
-    //     We reply to the same port the peer sent the packets from
-    //     This way we can go through NAT
-    //     Because of the NAT traversal scheme, the portn need to be
-    //     "symetric", e.g.:
-    //     from Client to Server : src = 4474, dest = 4464
-    //     from Server to Client : src = 4464, dest = 4474
-    // no -- all are the same -- 4464
-    if (gVerboseFlag) std::cout << "JackTrip:serverStart before setting all peer_port instances to " << peer_port << std::endl;
-    mDataProtocolSender->setPeerPort(peer_port);
-    mDataProtocolReceiver->setPeerPort(peer_port);
-    setPeerPorts(peer_port);
-    return 0;
 }
 
 
@@ -656,7 +871,6 @@ int JackTrip::clientPingToServerStart()
 
     // Create Socket Objects
     // --------------------
-    QTcpSocket tcpClient;
     QHostAddress serverHostAddress;
     if (!serverHostAddress.setAddress(mPeerAddress)) {
         QHostInfo info = QHostInfo::fromName(mPeerAddress);
@@ -668,64 +882,18 @@ int JackTrip::clientPingToServerStart()
 
     // Connect Socket to Server and wait for response
     // ----------------------------------------------
-    tcpClient.connectToHost(serverHostAddress, mTcpServerPort);
+    connect(&mTcpClient, &QTcpSocket::readyRead, this, &JackTrip::receivedDataTCP);
+    connect(&mTcpClient, &QTcpSocket::connected, this, &JackTrip::receivedConnectionTCP);
+    mElapsedTime = 0;
+    mEndTime = 5000; //Timeout after 5 seconds.
+    mTimeoutTimer.setInterval(mSleepTime);
+    connect(&mTimeoutTimer, &QTimer::timeout, this, &JackTrip::tcpTimerTick);
+    mTimeoutTimer.start();
+    mTcpClient.connectToHost(serverHostAddress, mTcpServerPort);
+    
     if (gVerboseFlag) cout << "Connecting to TCP Server at " <<  serverHostAddress.toString().toLatin1().constData() << " port " << mTcpServerPort << "..." << endl;
-    if (!tcpClient.waitForConnected()) {
-        std::cerr << "TCP Socket ERROR at " << mTcpServerPort << ": " << tcpClient.errorString().toStdString() <<  endl;
-        //std::exit(1);
-        return -1;
-    }
-    if (gVerboseFlag) cout << "TCP Socket Connected to Server!" << endl;
-    emit signalTcpClientConnected();
-
-    // Send Client Port Number to Server
-    // ---------------------------------
-    char port_buf[sizeof(mReceiverBindPort)];
-    std::memcpy(port_buf, &mReceiverBindPort, sizeof(mReceiverBindPort));
-
-    tcpClient.write(port_buf, sizeof(mReceiverBindPort));
-    while ( tcpClient.bytesToWrite() > 0 ) {
-        tcpClient.waitForBytesWritten(-1);
-    }
-    if (gVerboseFlag) cout << "Port " << mReceiverBindPort << " sent to Server" << endl;
-
-    // Read the size of the package
-    // ----------------------------
-    if (gVerboseFlag) cout << "Reading UDP port from Server..." << endl;
-    while (tcpClient.bytesAvailable() < (int)sizeof(uint16_t)) {
-        if (!tcpClient.waitForReadyRead()) {
-            std::cerr << "TCP Socket ERROR: " << tcpClient.errorString().toStdString() <<  endl;
-            //std::exit(1);
-            return -1;
-        }
-    }
-    if (gVerboseFlag) cout << "Ready To Read From Socket!" << endl;
-
-    // Read UDP Port Number from Server
-    // --------------------------------
-    uint32_t udp_port;
-    int size = sizeof(udp_port);
-    //char port_buf[size];
-    tcpClient.read(port_buf, size);
-    std::memcpy(&udp_port, port_buf, size);
-    //cout << "Received UDP Port Number: " << udp_port << endl;
-
-    // Close the TCP Socket
-    // --------------------
-    tcpClient.close(); // Close the socket
-    //cout << "TCP Socket Closed!" << endl;
-    if (gVerboseFlag) cout << "Connection Successful!" << endl;
-
-    // Set with the received UDP port
-    // ------------------------------
-    setPeerPorts(udp_port);
-    mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() );
-    mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() );
-    mDataProtocolSender->setPeerPort(udp_port);
-    mDataProtocolReceiver->setPeerPort(udp_port);
-    cout << "Server Address set to: " << mPeerAddress.toStdString() << " Port: " << udp_port << std::endl;
-    cout << gPrintSeparator << endl;
     return 0;
+    // Continued in the receivedConnectionTCP slot.
 
     /*
   else {
@@ -893,7 +1061,6 @@ void JackTrip::parseAudioPacket(int8_t* full_packet, int8_t* audio_packet)
     std::memcpy(audio_packet, audio_part, getTotalAudioPacketSizeInBytes());
 }
 
-
 //*******************************************************************************
 void JackTrip::checkPeerSettings(int8_t* full_packet)
 {
index 9885a213e9c9e531b84b717a1af4b207eb8617b4..50e90ed550a516fb7360e737cf94fb78fd7faa33 100644 (file)
@@ -44,6 +44,9 @@
 #include <QObject>
 #include <QString>
 #include <QUdpSocket>
+#include <QTcpSocket>
+#include <QTimer>
+#include <QSharedPointer>
 
 #include "DataProtocol.h"
 #include "AudioInterface.h"
@@ -54,8 +57,9 @@
 
 #include "PacketHeader.h"
 #include "RingBuffer.h"
+#include "AudioTester.h"
 
-#include <signal.h>
+//#include <signal.h>
 /** \brief Main class to creates a SERVER (to listen) or a CLIENT (to connect
  * to a listening server) to send audio streams in the network.
  *
@@ -64,7 +68,7 @@
  * Classes that uses JackTrip methods need to register with it.
  */
 
-class JackTrip : public QThread
+class JackTrip : public QObject
 {
     Q_OBJECT;
 
@@ -80,8 +84,8 @@ public:
 
     /// \brief Enum for the JackTrip mode
     enum jacktripModeT {
-        SERVER, ///< Run in Server Mode
-        CLIENT,  ///< Run in Client Mode
+        SERVER, ///< Run in P2P Server Mode
+        CLIENT,  ///< Run in P2P Client Mode
         CLIENTTOPINGSERVER, ///< Client of the Ping Server Mode
         SERVERPINGSERVER ///< Server of the MultiThreaded JackTrip
     };
@@ -111,7 +115,8 @@ public:
         CLIENTECHO,  ///< Client Echo (client self-to-self)
         CLIENTFOFI,  ///< Client Fan Out to Clients and Fan In from Clients (but not self-to-self)
         RESERVEDMATRIX,  ///< Reserved for custom patch matrix (for TUB ensemble)
-        FULLMIX  ///< Client Fan Out to Clients and Fan In from Clients (including self-to-self)
+        FULLMIX,  ///< Client Fan Out to Clients and Fan In from Clients (including self-to-self)
+        NOAUTO  ///< No automatic patching
     };
     //---------------------------------------------------------
 
@@ -133,9 +138,9 @@ public:
              int BufferQueueLength = gDefaultQueueLength,
              unsigned int redundancy = gDefaultRedundancy,
              AudioInterface::audioBitResolutionT AudioBitResolution =
-            AudioInterface::BIT16,
+             AudioInterface::BIT16,
              DataProtocol::packetHeaderTypeT PacketHeaderType =
-            DataProtocol::DEFAULT,
+             DataProtocol::DEFAULT,
              underrunModeT UnderRunMode = WAVETABLE,
              int receiver_bind_port = gDefaultPort,
              int sender_bind_port = gDefaultPort,
@@ -145,20 +150,26 @@ public:
 
     /// \brief The class destructor
     virtual ~JackTrip();
+    
+    static void sigIntHandler(__attribute__((unused)) int unused)
+    { std::cout << std::endl << "Shutting Down..." << std::endl; sSigInt = true; }
+    static bool sSigInt;
+    static bool sJackStopped;
 
     /// \brief Starting point for the thread
-    virtual void run() {
+    /*virtual void run() {
         if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->run" << std::endl;
-    }
+    }*/
 
     /// \brief Set the Peer Address for jacktripModeT::CLIENT mode only
-    virtual void setPeerAddress(const char* PeerHostOrIP);
+    virtual void setPeerAddress(QString PeerHostOrIP);
 
     /** \brief Append a process plugin. Processes will be appended in order
    * \param plugin Pointer to ProcessPlugin Class
    */
     //void appendProcessPlugin(const std::tr1::shared_ptr<ProcessPlugin> plugin);
-    virtual void appendProcessPlugin(ProcessPlugin* plugin);
+    virtual void appendProcessPluginToNetwork(ProcessPlugin* plugin);
+    virtual void appendProcessPluginFromNetwork(ProcessPlugin* plugin);
 
     /// \brief Start the processing threads
     virtual void startProcess(
@@ -166,9 +177,10 @@ public:
             int ID
         #endif // endwhere
             );
+    virtual void completeConnection();
 
     /// \brief Stop the processing threads
-    virtual void stop();
+    virtual void stop(QString errorMessage = "");
 
     /// \brief Wait for all the threads to finish. This functions is used when JackTrip is
     /// run as a thread
@@ -199,12 +211,17 @@ public:
     /// \brief Sets (override) Buffer Queue Length Mode after construction
     virtual void setBufferQueueLength(int BufferQueueLength)
     { mBufferQueueLength = BufferQueueLength; }
+    virtual void setBufferStrategy(int BufferStrategy)
+    { mBufferStrategy = BufferStrategy; }
     /// \brief Sets (override) Audio Bit Resolution after construction
     virtual void setAudioBitResolution(AudioInterface::audioBitResolutionT AudioBitResolution)
     { mAudioBitResolution = AudioBitResolution; }
     /// \brief Sets (override) Underrun Mode
     virtual void setUnderRunMode(underrunModeT UnderRunMode)
     { mUnderRunMode = UnderRunMode; }
+    /// \brief Sets whether to quit on timeout.
+    virtual void setStopOnTimeout(bool stopOnTimeout)
+    { mStopOnTimeout = stopOnTimeout; }
     /// \brief Sets port numbers for the local and peer machine.
     /// Receive port is <tt>port</tt>
     virtual void setAllPorts(int port)
@@ -229,9 +246,14 @@ public:
     /// \brief Set Client Name to something different that the default (JackTrip)
     virtual void setClientName(QString clientName)
     { mJackClientName = clientName; }
+    virtual void setRemoteClientName(QString remoteClientName)
+    { mRemoteClientName = remoteClientName; }
     /// \brief Set the number of audio channels
     virtual void setNumChannels(int num_chans)
     { mNumChans = num_chans; }
+    
+    virtual void setIOStatTimeout(int timeout) { mIOStatTimeout = timeout; }
+    virtual void setIOStatStream(QSharedPointer<std::ofstream> statStream) { mIOStatStream = statStream; }
 
     /// Set to connect or not default audio ports (only implemented in Jack)
     virtual void setConnectDefaultAudioPorts(bool connect)
@@ -274,7 +296,9 @@ public:
     { mAudiointerfaceMode = audiointerface_mode; }
     virtual void setAudioInterface(AudioInterface* const AudioInterface)
     { mAudioInterface = AudioInterface; }
-
+    virtual void setLoopBack(bool b)
+    { mLoopBack = b; }
+    virtual void setAudioTesterP(AudioTester* atp) { mAudioTesterP = atp; }
 
     void setSampleRate(uint32_t sample_rate)
     { mSampleRate = sample_rate; }
@@ -305,6 +329,7 @@ public:
 
     bool tcpConnectionError()
     { return mTcpConnectionError; }
+
     //@}
     //------------------------------------------------------------------------------------
 
@@ -318,13 +343,15 @@ public:
     virtual int getPacketSizeInBytes();
     void parseAudioPacket(int8_t* full_packet, int8_t* audio_packet);
     virtual void sendNetworkPacket(const int8_t* ptrToSlot)
-    { mSendRingBuffer->insertSlotNonBlocking(ptrToSlot); }
+    { mSendRingBuffer->insertSlotNonBlocking(ptrToSlot, 0, 0); }
+    virtual void receiveBroadcastPacket(int8_t* ptrToReadSlot)
+    { mReceiveRingBuffer->readBroadcastSlot(ptrToReadSlot); }
     virtual void receiveNetworkPacket(int8_t* ptrToReadSlot)
     { mReceiveRingBuffer->readSlotNonBlocking(ptrToReadSlot); }
     virtual void readAudioBuffer(int8_t* ptrToReadSlot)
     { mSendRingBuffer->readSlotBlocking(ptrToReadSlot); }
-    virtual void writeAudioBuffer(const int8_t* ptrToSlot)
-    { mReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot); }
+    virtual bool writeAudioBuffer(const int8_t* ptrToSlot, int len, int lostLen)
+    { return mReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot, len, lostLen); }
     uint32_t getBufferSizeInSamples() const
     { return mAudioBufferSize; /*return mAudioInterface->getBufferSizeInSamples();*/ }
     uint32_t getDeviceID() const
@@ -393,16 +420,21 @@ public:
     void printTextTest() {std::cout << "=== JackTrip PRINT ===" << std::endl;}
     void printTextTest2() {std::cout << "=== JackTrip PRINT2 ===" << std::endl;}
 
-    void startIOStatTimer(int timeout_sec, const std::ostream& log_stream);
+    void setNetIssuesSimulation(double loss, double jitter, double delay_rel)
+    {
+        mSimulatedLossRate = loss;
+        mSimulatedJitterRate = jitter;
+        mSimulatedDelayRel = delay_rel;
+    }
+    void setBroadcast(int broadcast_queue) {mBroadcastQueueLength = broadcast_queue;}
+    void setUseRtUdpPriority(bool use) {mUseRtUdpPriority = use;}
 
 public slots:
     /// \brief Slot to stop all the processes and threads
     virtual void slotStopProcesses()
-    {
-        std::cout << "Stopping JackTrip..." << std::endl;
-        mStopped = true;
-        this->stop();
-    }
+    { this->stop(); }
+    virtual void slotStopProcessesDueToError(const QString &errorMessage)
+    { this->stop(errorMessage); }
 
     /** \brief This slot emits in turn the signal signalNoUdpPacketsForSeconds
    * when UDP has waited for more than 30 seconds.
@@ -414,25 +446,37 @@ public slots:
         int wait_time = 10000; // msec
         if ( !(wait_msec%wait_time) ) {
             std::cerr << "UDP WAITED MORE THAN 10 seconds." << std::endl;
+            if (mStopOnTimeout) {
+                stop("No network data received for 10 seconds");
+            }
             emit signalNoUdpPacketsForSeconds();
         }
     }
+    void slotUdpWaitingTooLong()
+    { emit signalUdpWaitingTooLong(); }
     void slotPrintTest()
     { std::cout << "=== TESTING ===" << std::endl; }
     void slotReceivedConnectionFromPeer()
-    { mReceivedConnection = true; }
+    { mReceivedConnection = true; emit signalReceivedConnectionFromPeer(); }
     void onStatTimer();
-
+    
+private slots:
+    void receivedConnectionTCP();
+    void receivedDataTCP();
+    void receivedDataUDP();
+    void udpTimerTick();
+    void tcpTimerTick();
 
 signals:
-
-    void signalUdpTimeOut();
+    //void signalUdpTimeOut();
     /// \brief Signal emitted when all the processes and threads are stopped
     void signalProcessesStopped();
     /// \brief Signal emitted when no UDP Packets have been received for a while
     void signalNoUdpPacketsForSeconds();
     void signalTcpClientConnected();
-
+    void signalError(const QString &errorMessage);
+    void signalReceivedConnectionFromPeer();
+    void signalUdpWaitingTooLong();
 
 public:
 
@@ -475,10 +519,13 @@ private:
     int mNumNetRevChans; ///< Number of Network Audio Channels (net comb filters)
 #endif // endwhere
     int mBufferQueueLength; ///< Audio Buffer from network queue length
+    int mBufferStrategy;
+    int mBroadcastQueueLength;
     uint32_t mSampleRate; ///< Sample Rate
     uint32_t mDeviceID; ///< RTAudio DeviceID
     uint32_t mAudioBufferSize; ///< Audio buffer size to process on each callback
     AudioInterface::audioBitResolutionT mAudioBitResolution; ///< Audio Bit Resolutions
+    bool mLoopBack;
     QString mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode
 
     /// Pointer to Abstract Type DataProtocol that sends packets
@@ -488,6 +535,7 @@ private:
     AudioInterface* mAudioInterface; ///< Interface to Jack Client
     PacketHeader* mPacketHeader; ///< Pointer to Packet Header
     underrunModeT mUnderRunMode; ///< underrunModeT Mode
+    bool mStopOnTimeout; ///< Stop on 10 second timeout
 
     /// Pointer for the Send RingBuffer
     RingBuffer* mSendRingBuffer;
@@ -502,18 +550,36 @@ private:
 
     unsigned int mRedundancy; ///< Redundancy factor in network data
     QString mJackClientName; ///< JackAudio Client Name
+    QString mRemoteClientName; ///< Remote JackAudio Client Name for hub client mode
 
     JackTrip::connectionModeT mConnectionMode; ///< Connection Mode
     JackTrip::hubConnectionModeT mHubConnectionModeT; ///< Hub Server Jack Audio Patch Connection Mode
 
-    QVector<ProcessPlugin*> mProcessPlugins; ///< Vector of ProcesPlugin<EM>s</EM>
+    QVector<ProcessPlugin*> mProcessPluginsFromNetwork; ///< Vector of ProcessPlugin<EM>s</EM>
+    QVector<ProcessPlugin*> mProcessPluginsToNetwork; ///< Vector of ProcessPlugin<EM>s</EM>
+    
+    QTimer mTimeoutTimer;
+    int mSleepTime;
+    int mElapsedTime;
+    int mEndTime;
+    QTcpSocket mTcpClient;
+    QUdpSocket mUdpSockTemp;
 
     volatile bool mReceivedConnection; ///< Bool of received connection from peer
     volatile bool mTcpConnectionError;
     volatile bool mStopped;
+    volatile bool mHasShutdown;
 
     bool mConnectDefaultAudioPorts; ///< Connect or not default audio ports
+    QSharedPointer<std::ofstream> mIOStatStream;
+    int mIOStatTimeout;
     std::ostream mIOStatLogStream;
+    double mSimulatedLossRate;
+    double mSimulatedJitterRate;
+    double mSimulatedDelayRel;
+    bool mUseRtUdpPriority;
+
+    AudioTester* mAudioTesterP;
 };
 
 #endif
index abe1e9c89ce213a669e14d5fb8b7461573c47a81..bcaeaf03bbb46dafb9d2850ec8018dc081ff2f5d 100644 (file)
@@ -57,14 +57,14 @@ void JackTripThread::run()
     }
 
     NetKS netks;
-    jacktrip.appendProcessPlugin(&netks);
+    jacktrip.appendProcessPluginFromNetwork(&netks);
     //netks.play();
 
 
     //QThread::sleep(1);
-    jacktrip.start();
+    //jacktrip.start();
     //netks.play();
-    jacktrip.wait();
+    //jacktrip.wait();
 
 
     cout << "******** AFTER JACKTRIPTHREAD START **************" << endl;
index f00626118d6976698432775afd6ff1176c6f9e9b..995c4a262fad4cecd13d0f71c32e0cb2d11e04e3 100644 (file)
@@ -45,7 +45,7 @@
 #include "JackTripWorker.h"
 #include "JackTrip.h"
 #include "UdpHubListener.h"
-#include "NetKS.h"
+//#include "NetKS.h"
 #include "LoopBack.h"
 #include "Settings.h"
 #ifdef WAIR // wair
 using std::cout; using std::endl;
 
 //*******************************************************************************
-JackTripWorker::JackTripWorker(UdpHubListener* udpmasterlistener, int BufferQueueLength, JackTrip::underrunModeT UnderRunMode) :
-    mUdpHubListener(udpmasterlistener),
+JackTripWorker::JackTripWorker(UdpHubListener* udphublistener, int BufferQueueLength, JackTrip::underrunModeT UnderRunMode, QString clientName) :
+    mUdpHubListener(udphublistener),
     m_connectDefaultAudioPorts(false),
     mBufferQueueLength(BufferQueueLength),
     mUnderRunMode(UnderRunMode),
+    mClientName(clientName),
     mSpawning(false),
     mID(0),
-    mNumChans(1)
+    mNumChans(1),
+    mIOStatTimeout(0)
   #ifdef WAIR // wair
   ,mNumNetRevChans(0),
     mWAIR(false)
@@ -74,6 +76,12 @@ JackTripWorker::JackTripWorker(UdpHubListener* udpmasterlistener, int BufferQueu
     setAutoDelete(false); // stick around after calling run()
     //mNetks = new NetKS;
     //mNetks->play();
+    mBufferStrategy = 1;
+    mBroadcastQueue = 0;
+    mSimulatedLossRate = 0.0;
+    mSimulatedJitterRate = 0.0;
+    mSimulatedDelayRel = 0.0;
+    mUseRtUdpPriority = false;
 }
 
 
@@ -128,7 +136,6 @@ void JackTripWorker::run()
         // Create and setup JackTrip Object
         //JackTrip jacktrip(JackTrip::SERVER, JackTrip::UDP, mNumChans, 2);
         if (gVerboseFlag) cout << "---> JackTripWorker: Creating jacktrip objects..." << endl;
-        Settings* settings = mUdpHubListener->getSettings();
 
 #ifdef WAIR // WAIR
         // forces    BufferQueueLength to 2
@@ -162,7 +169,7 @@ void JackTripWorker::run()
             switch ( mNumNetRevChans )
             {
             case 16 : // freeverb
-                mJackTrip->appendProcessPlugin(new dcblock2gain(mNumChans)); // plugin slot 0
+                mJackTrip->appendProcessPluginFromNetwork(new dcblock2gain(mNumChans)); // plugin slot 0
                 ///////////////
                 //            mJackTrip->appendProcessPlugin(new comb16server(mNumNetChans));
                 // -S LAIR no AP  mJackTrip->appendProcessPlugin(new AP8(mNumChans));
@@ -184,6 +191,14 @@ void JackTripWorker::run()
 
         // Set our underrun mode
         jacktrip.setUnderRunMode(mUnderRunMode);
+        if (mIOStatTimeout > 0) {
+            jacktrip.setIOStatTimeout(mIOStatTimeout);
+            jacktrip.setIOStatStream(mIOStatStream);
+        }
+        
+        if (!mClientName.isEmpty()) {
+            jacktrip.setClientName(mClientName);
+        }
 
         // Connect signals and slots
         // -------------------------
@@ -195,6 +210,7 @@ void JackTripWorker::run()
         // Connection to terminate the local eventloop when jacktrip is done
         QObject::connect(&jacktrip, SIGNAL(signalProcessesStopped()),
                          &event_loop, SLOT(quit()), Qt::QueuedConnection);
+        QObject::connect(&jacktrip, &JackTrip::signalError, &event_loop, &QEventLoop::quit, Qt::QueuedConnection);
         QObject::connect(this, SIGNAL(signalRemoveThread()),
                          &jacktrip, SLOT(slotStopProcesses()), Qt::QueuedConnection);
 
@@ -203,9 +219,14 @@ void JackTripWorker::run()
         // I still haven't figure out why
         //ClientAddress.toString().toLatin1().constData();
         //jacktrip.setPeerAddress(ClientAddress.toString().toLatin1().constData());
-        jacktrip.setPeerAddress(mClientAddress.toLatin1().constData());
+        jacktrip.setPeerAddress(mClientAddress);
         jacktrip.setBindPorts(mServerPort);
         //jacktrip.setPeerPorts(mClientPort);
+        jacktrip.setBufferStrategy(mBufferStrategy);
+        jacktrip.setNetIssuesSimulation(mSimulatedLossRate,
+            mSimulatedJitterRate, mSimulatedDelayRel);
+        jacktrip.setBroadcast(mBroadcastQueue);
+        jacktrip.setUseRtUdpPriority(mUseRtUdpPriority);
 
         if (gVerboseFlag) cout << "---> JackTripWorker: setJackTripFromClientHeader..." << endl;
         int PeerConnectionMode = setJackTripFromClientHeader(jacktrip);
@@ -222,9 +243,6 @@ void JackTripWorker::run()
                     mID
             #endif // endwhere
                     );
-        if (0 != settings->getIOStatTimeout()) {
-            jacktrip.startIOStatTimer(settings->getIOStatTimeout(), settings->getIOStatStream());
-        }
         // if (gVerboseFlag) cout << "---> JackTripWorker: start..." << endl;
         // jacktrip.start(); // ########### JamTest Only #################
 
@@ -233,11 +251,11 @@ void JackTripWorker::run()
 
         event_loop.exec(); // Excecution will block here until exit() the QEventLoop
         //--------------------------------------------------------------------------
-
+        
         { QMutexLocker locker(&mMutex); mSpawning = true; }
 
         // wait for jacktrip to be done before exiting the Worker Thread
-        jacktrip.wait();
+        //jacktrip.wait();
 
     }
     catch ( const std::exception & e )
index 62c65dae8b0ad25b8496226f67f58d1ecbab033c..98524cf72bb84768aea5a40a47bfaa40b4c03215 100644 (file)
@@ -70,7 +70,7 @@ class JackTripWorker : public QObject, public QRunnable
 
 public:
     /// \brief The class constructor
-    JackTripWorker(UdpHubListener* udpmasterlistener, int BufferQueueLength = gDefaultQueueLength, JackTrip::underrunModeT UnderRunMode = JackTrip::WAVETABLE);
+    JackTripWorker(UdpHubListener* udphublistener, int BufferQueueLength = gDefaultQueueLength, JackTrip::underrunModeT UnderRunMode = JackTrip::WAVETABLE, QString clientName = "");
     /// \brief The class destructor
     virtual ~JackTripWorker();
 
@@ -97,7 +97,19 @@ public:
         return mID;
     }
 
-
+    void setBufferStrategy(int BufferStrategy) { mBufferStrategy = BufferStrategy; }
+    void setNetIssuesSimulation(double loss, double jitter, double delay_rel)
+    {
+        mSimulatedLossRate = loss;
+        mSimulatedJitterRate = jitter;
+        mSimulatedDelayRel = delay_rel;
+    }
+    void setBroadcast(int broadcast_queue) {mBroadcastQueue = broadcast_queue;}
+    void setUseRtUdpPriority(bool use) {mUseRtUdpPriority = use;}
+    
+    void setIOStatTimeout(int timeout) { mIOStatTimeout = timeout; }
+    void setIOStatStream(QSharedPointer<std::ofstream> statStream) { mIOStatStream = statStream; }
+    
 private slots:
     void slotTest()
     { std::cout << "--- JackTripWorker TEST SLOT ---" << std::endl; }
@@ -106,7 +118,6 @@ private slots:
 signals:
     void signalRemoveThread();
 
-
 private:
     int setJackTripFromClientHeader(JackTrip& jacktrip);
     JackTrip::connectionModeT getConnectionModeFromHeader();
@@ -119,16 +130,28 @@ private:
 
     /// Client Outgoing Port. By convention, the receving port will be <tt>mClientPort -1</tt>
     uint16_t mClientPort;
+    
+    int mBufferQueueLength;
+    JackTrip::underrunModeT mUnderRunMode;
+    QString mClientName;
 
     /// Thread spawning internal lock.
     /// If true, the prototype is working on creating (spawning) a new thread
     volatile bool mSpawning;
     QMutex mMutex; ///< Mutex to protect mSpawning
-    JackTrip::underrunModeT mUnderRunMode;
-    int mBufferQueueLength;
 
     int mID; ///< ID thread number
     int mNumChans; ///< Number of Channels
+
+    int mBufferStrategy;
+    int mBroadcastQueue;
+    double mSimulatedLossRate;
+    double mSimulatedJitterRate;
+    double mSimulatedDelayRel;
+    bool mUseRtUdpPriority;
+    
+    int mIOStatTimeout;
+    QSharedPointer<std::ofstream> mIOStatStream;
 #ifdef WAIR // wair
     int mNumNetRevChans; ///< Number of Net Channels = net combs
     bool mWAIR;
diff --git a/src/JitterBuffer.cpp b/src/JitterBuffer.cpp
new file mode 100644 (file)
index 0000000..9f73d53
--- /dev/null
@@ -0,0 +1,389 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2020 Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file JitterBuffer.cpp
+ * \author Anton Runov
+ * \date June 2020
+ */
+
+
+#include "JitterBuffer.h"
+
+#include <iostream>
+#include <cstring>
+#include <cstdlib>
+#include <stdexcept>
+#include <cmath>
+
+using std::cout; using std::endl;
+
+
+//*******************************************************************************
+JitterBuffer::JitterBuffer(int buf_samples, int qlen, int sample_rate, int strategy,
+                                          int bcast_qlen, int channels, int bit_res) :
+    RingBuffer(0, 0)
+{
+    int total_size = sample_rate * channels * bit_res * 2; // 2 secs of audio
+    int slot_size = buf_samples * channels * bit_res;
+    mSlotSize = slot_size;
+    mInSlotSize = slot_size;
+    if (0 < qlen) {
+        mMaxLatency = qlen * slot_size;
+        mAutoQueue = 0;
+    }
+    else {
+        // AutoQueue
+        mMaxLatency = 3*slot_size;
+        mAutoQueue = 1;
+    }
+    mTotalSize = total_size;
+    mBroadcastLatency = bcast_qlen * mSlotSize;
+    mNumChannels = channels;
+    mAudioBitRes = bit_res;
+    mMinStepSize = channels * bit_res;
+    mFPP = buf_samples;
+    mSampleRate = sample_rate;
+    mActive = false;
+
+    // Defaults for zero strategy
+    mUnderrunIncTolerance = -10 * mSlotSize;
+    mCorrIncTolerance = 100*mMaxLatency;     // should be greater than mUnderrunIncTolerance
+    mOverflowDecTolerance = 100*mMaxLatency;
+    mWritePosition = mMaxLatency;
+    mStatUnit = mSlotSize;
+    mLevelDownRate = std::min(256, mFPP) / (5.0*sample_rate) * mSlotSize;
+    mOverflowDropStep = mMaxLatency / 2;
+    mLevelCur = mMaxLatency;
+    mLevel = mLevelCur;
+    mMinLevelThreshold = 1.9 * mSlotSize;
+    mBroadcastPosition = 0;
+    mBroadcastPositionCorr = 0.0;
+    mLastCorrCounter = 0;
+    mLastCorrDirection = 0;
+
+    switch (strategy) {
+      case 1:
+        mOverflowDropStep = mSlotSize;
+        break;
+      case 2:
+        mUnderrunIncTolerance = 1.1 * mSlotSize;
+        mCorrIncTolerance = 1.9 * mSlotSize;     // should be greater than mUnderrunIncTolerance
+        mOverflowDecTolerance = 0.1*mSlotSize;
+        mOverflowDropStep = mSlotSize;
+        break;
+    }
+
+    mRingBuffer = new int8_t[mTotalSize];
+    std::memset(mRingBuffer, 0, mTotalSize);
+
+    mAutoQueueCorr = 2*mSlotSize;
+    if (0 > qlen) {
+        mAutoQFactor = 1.0/-qlen;
+    }
+    else {
+        mAutoQFactor = 1.0/500;
+    }
+    mAutoQRate = mSlotSize * 0.5;
+    mAutoQRateMin = mSlotSize * 0.0005;
+    mAutoQRateDecay = 1.0 - std::min(mFPP*1.2e-6, 0.0005);
+}
+
+//*******************************************************************************
+bool JitterBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen)
+{
+    if (0 == len) {
+        len = mSlotSize;
+    }
+    QMutexLocker locker(&mMutex);
+    mInSlotSize = len;
+    if (!mActive) {
+        mActive = true;
+    }
+    if (mMaxLatency < len + mSlotSize) {
+        mMaxLatency = len + mSlotSize;
+    }
+    if (0 < lostLen) {
+        processPacketLoss(lostLen);
+    }
+    mSkewRaw += mReadsNew - len;
+    mReadsNew = 0;
+    mUnderruns += mUnderrunsNew;
+    mUnderrunsNew = 0;
+    mLevel = mSlotSize*std::ceil(mLevelCur/mSlotSize);
+
+    // Update positions if necessary
+    int32_t available = mWritePosition - mReadPosition;
+
+    int delta = 0;
+    if (available < -10*mMaxLatency) {
+        delta = available;
+        mBufIncUnderrun += -delta;
+        mLevelCur = len;
+        //cout << "reset" << endl;
+    }
+    else if (available + len > mMaxLatency) {
+        delta = mOverflowDropStep;
+        mOverflows += delta;
+        mBufDecOverflow += delta;
+        mLevelCur = mMaxLatency;
+    }
+    else if (0 > available &&
+          mLevelCur < std::max(mInSlotSize + mMinLevelThreshold,
+              mMaxLatency - mUnderrunIncTolerance - 2*mSlotSize*lastCorrFactor())) {
+        delta = -std::min(-available, mSlotSize);
+        mBufIncUnderrun += -delta;
+    }
+    else if (mLevelCur < mMaxLatency - mCorrIncTolerance - 6*mSlotSize*lastCorrFactor()) {
+        delta = -mSlotSize;
+        mUnderruns += -delta;
+        mBufIncCompensate += -delta;
+    }
+
+    if (0 != delta) {
+      mReadPosition += delta;
+      mLastCorrCounter = 0;
+      mLastCorrDirection = 0 < delta ? 1 : -1;
+    }
+    else {
+      ++mLastCorrCounter;
+    }
+
+    int wpos = mWritePosition % mTotalSize;
+    int n = std::min(mTotalSize - wpos, len);
+    std::memcpy(mRingBuffer+wpos, ptrToSlot, n);
+    if (n < len) {
+        //cout << "split write: " << len << "-" << n << endl;
+        std::memcpy(mRingBuffer, ptrToSlot+n, len-n);
+    }
+    mWritePosition += len;
+
+    return true;
+}
+
+//*******************************************************************************
+void JitterBuffer::readSlotNonBlocking(int8_t* ptrToReadSlot)
+{
+    int len = mSlotSize;
+    QMutexLocker locker(&mMutex);
+    if (!mActive) {
+        std::memset(ptrToReadSlot, 0, len);
+        return;
+    }
+    mReadsNew += len;
+    int32_t available = mWritePosition - mReadPosition;
+    if (available < mLevelCur) {
+        mLevelCur = std::max((double)available, mLevelCur-mLevelDownRate);
+    }
+    else {
+        mLevelCur = available;
+    }
+
+    // auto queue correction
+    if (0 > available + mAutoQueueCorr - mLevelCur) {
+        mAutoQueueCorr += mAutoQRate;
+    }
+    else if (mInSlotSize + mSlotSize < mAutoQueueCorr) {
+        mAutoQueueCorr -= mAutoQRate * mAutoQFactor;
+    }
+    if (mAutoQRate > mAutoQRateMin) {
+        mAutoQRate *= mAutoQRateDecay;
+    }
+    if (0 != mAutoQueue) {
+        int PPS = mSampleRate / mFPP;
+        if (2*PPS == mAutoQueue++ % (4*PPS)) {
+            double k = 1.0 + 1e-5/mAutoQFactor;
+            if (12*PPS > mAutoQueue ||
+                    std::abs(mAutoQueueCorr*k - mMaxLatency + mSlotSize/2) > 0.6*mSlotSize) {
+                mMaxLatency = mSlotSize * std::ceil(mAutoQueueCorr*k/mSlotSize);
+                cout << "AutoQueue: " << mMaxLatency / mSlotSize << endl;
+            }
+        }
+    }
+
+    int read_len = qBound(0, available, len);
+    int rpos = mReadPosition % mTotalSize;
+    int n = std::min(mTotalSize - rpos, read_len);
+    std::memcpy(ptrToReadSlot, mRingBuffer+rpos, n);
+    if (n < read_len) {
+        //cout << "split read: " << read_len << "-" << n << endl;
+        std::memcpy(ptrToReadSlot+n, mRingBuffer, read_len-n);
+    }
+    if (read_len < len) {
+        std::memset(ptrToReadSlot+read_len, 0, len-read_len);
+        mUnderrunsNew += len-read_len;
+    }
+    mReadPosition += len;
+}
+
+//*******************************************************************************
+void JitterBuffer::readBroadcastSlot(int8_t* ptrToReadSlot)
+{
+    int len = mSlotSize;
+    QMutexLocker locker(&mMutex);
+    if (mBroadcastLatency + len > mReadPosition) {
+        std::memset(ptrToReadSlot, 0, len);
+        return;
+    }
+    // latency correction
+    int32_t d = mReadPosition - mBroadcastLatency - mBroadcastPosition - len;
+    if (std::abs(d) > mBroadcastLatency / 2) {
+        mBroadcastPosition = mReadPosition - mBroadcastLatency - len;
+        mBroadcastPositionCorr = 0.0;
+        mBroadcastSkew += d / mMinStepSize;
+    }
+    else {
+        mBroadcastPositionCorr += 0.0003 * d;
+        int delta = mBroadcastPositionCorr / mMinStepSize;
+        if (0 != delta) {
+            mBroadcastPositionCorr -= delta * mMinStepSize;
+            if (2 == mAudioBitRes && (int32_t)(mWritePosition - mBroadcastPosition) > len) {
+                // interpolate
+                len += delta * mMinStepSize;
+            }
+            else {
+                // skip
+                mBroadcastPosition += delta * mMinStepSize;
+            }
+            mBroadcastSkew += delta;
+        }
+    }
+    mBroadcastDelta = d / mMinStepSize;
+    int32_t available = mWritePosition - mBroadcastPosition;
+    int read_len = qBound(0, available, len);
+    if (len == mSlotSize) {
+        int rpos = mBroadcastPosition % mTotalSize;
+        int n = std::min(mTotalSize - rpos, read_len);
+        std::memcpy(ptrToReadSlot, mRingBuffer+rpos, n);
+        if (n < read_len) {
+            //cout << "split read: " << read_len << "-" << n << endl;
+            std::memcpy(ptrToReadSlot+n, mRingBuffer, read_len-n);
+        }
+        if (read_len < len) {
+            std::memset(ptrToReadSlot+read_len, 0, len-read_len);
+        }
+    }
+    else {
+        // interpolation len => mSlotSize
+        double K = 1.0 * len / mSlotSize;
+        for (int c=0; c < mMinStepSize; c+=sizeof(int16_t)) {
+            for (int j=0; j < mSlotSize/mMinStepSize; ++j) {
+                int j1 = std::floor(j*K);
+                double a = j*K - j1;
+                int rpos = (mBroadcastPosition + j1*mMinStepSize + c) % mTotalSize;
+                int16_t v1 = *(int16_t*)(mRingBuffer + rpos);
+                rpos = (rpos + mMinStepSize) % mTotalSize;
+                int16_t v2 = *(int16_t*)(mRingBuffer + rpos);
+                *(int16_t*)(ptrToReadSlot + j*mMinStepSize + c) = std::round((1-a)*v1 + a*v2);
+            }
+        }
+    }
+    mBroadcastPosition += len;
+}
+
+
+//*******************************************************************************
+void JitterBuffer::processPacketLoss(int lostLen)
+{
+    mSkewRaw -= lostLen;
+
+    int32_t available = mWritePosition - mReadPosition;
+    int delta = std::min(available + mInSlotSize + lostLen - mMaxLatency, lostLen);
+    if (0 < delta) {
+        lostLen -= delta;
+        mBufDecPktLoss += delta;
+        mLevelCur = mMaxLatency;
+        mLastCorrCounter = 0;
+        mLastCorrDirection = 1;
+    }
+    else if (mSlotSize < available + lostLen && (
+            mOverflowDecTolerance > mMaxLatency   // for strategies 0,1
+            || (0 < mLastCorrDirection && mLevelCur >
+                    mMaxLatency - mOverflowDecTolerance*(1.1 - lastCorrFactor()))
+            )) {
+        delta = std::min(lostLen, mSlotSize);
+        lostLen -= delta;
+        mBufDecPktLoss += delta;
+        mLevelCur -= delta;
+        mLastCorrCounter = 0;
+        mLastCorrDirection = 1;
+    }
+    if (lostLen >= mTotalSize) {
+        std::memset(mRingBuffer, 0, mTotalSize);
+        mUnderruns += std::max(0, lostLen - std::max(0, -available));
+    }
+    else if (0 < lostLen) {
+        int wpos = mWritePosition % mTotalSize;
+        int n = std::min(mTotalSize - wpos, lostLen);
+        std::memset(mRingBuffer+wpos, 0, n);
+        if (n < lostLen) {
+            //cout << "split write: " << lostLen << "-" << n << endl;
+            std::memset(mRingBuffer, 0, lostLen-n);
+        }
+        mUnderruns += std::max(0, lostLen - std::max(0, -available));
+    }
+    mWritePosition += lostLen;
+}
+
+//*******************************************************************************
+bool JitterBuffer::getStats(RingBuffer::IOStat* stat, bool reset)
+{
+    QMutexLocker locker(&mMutex);
+    if (reset) {
+        mUnderruns = 0;
+        mOverflows = 0;
+        mSkew0 = mLevel;
+        mSkewRaw = 0;
+        mBufDecOverflow = 0;
+        mBufDecPktLoss = 0;
+        mBufIncUnderrun = 0;
+        mBufIncCompensate = 0;
+        mBroadcastSkew = 0;
+    }
+    stat->underruns = mUnderruns / mStatUnit;
+    stat->overflows = mOverflows / mStatUnit;
+    stat->skew = (int32_t)((mSkew0 - mLevel + mBufIncUnderrun + mBufIncCompensate
+                        - mBufDecOverflow - mBufDecPktLoss)) / mStatUnit;
+    stat->skew_raw = mSkewRaw / mStatUnit;
+    stat->level = mLevel / mStatUnit;
+
+    stat->buf_dec_overflows = mBufDecOverflow / mStatUnit;
+    stat->buf_dec_pktloss = mBufDecPktLoss / mStatUnit;
+    stat->buf_inc_underrun = mBufIncUnderrun / mStatUnit;
+    stat->buf_inc_compensate = mBufIncCompensate / mStatUnit;
+    stat->broadcast_skew = mBroadcastSkew;
+    stat->broadcast_delta = mBroadcastDelta;
+
+    stat->autoq_corr = mAutoQueueCorr / mStatUnit * 10;
+    stat->autoq_rate = mAutoQRate / mStatUnit * 1000;
+    return true;
+}
+
diff --git a/src/JitterBuffer.h b/src/JitterBuffer.h
new file mode 100644 (file)
index 0000000..6be4069
--- /dev/null
@@ -0,0 +1,90 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2020 Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file JitterBuffer.h
+ * \author Anton Runov
+ * \date June 2020
+ */
+
+#ifndef __JITTERBUFFER_H__
+#define __JITTERBUFFER_H__
+
+#include "RingBuffer.h"
+
+class JitterBuffer : public RingBuffer
+{
+public:
+    JitterBuffer(int buf_samples, int qlen, int sample_rate, int strategy,
+                                int bcast_qlen, int channels, int bit_res);
+    virtual ~JitterBuffer() {}
+
+    virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen);
+    virtual void readSlotNonBlocking(int8_t* ptrToReadSlot);
+    virtual void readBroadcastSlot(int8_t* ptrToReadSlot);
+
+    virtual bool getStats(IOStat* stat, bool reset);
+
+protected:
+    void processPacketLoss(int lostLen);
+
+protected:
+    int mMaxLatency;
+    int mNumChannels;
+    int mAudioBitRes;
+    int mMinStepSize;
+    int mFPP;
+    int mSampleRate;
+    int mInSlotSize;
+    bool mActive;
+    uint32_t mBroadcastLatency;
+    uint32_t mBroadcastPosition;
+    double  mBroadcastPositionCorr;
+
+    double mUnderrunIncTolerance;
+    double mCorrIncTolerance;
+    double mOverflowDecTolerance;
+    int    mOverflowDropStep;
+    uint32_t mLastCorrCounter;
+    int    mLastCorrDirection;
+    double mMinLevelThreshold;
+    double lastCorrFactor() const {return 500.0 / std::max(500U, mLastCorrCounter);}
+
+    int    mAutoQueue;
+    double mAutoQueueCorr;
+    double mAutoQFactor;
+    double mAutoQRate;
+    double mAutoQRateMin;
+    double mAutoQRateDecay;
+};
+
+
+#endif //__JITTERBUFFER_H__
diff --git a/src/Limiter.cpp b/src/Limiter.cpp
new file mode 100644 (file)
index 0000000..91a918f
--- /dev/null
@@ -0,0 +1,71 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file Limiter.cpp
+ * \author Julius Smith, based on LoopBack.h
+ * \date May-Nov 2020
+ * \license MIT
+ */
+
+#include "Limiter.h"
+#include "jacktrip_types.h"
+
+#include <iostream>
+
+//*******************************************************************************
+void Limiter::compute(int nframes, float** inputs, float** outputs)
+{
+  if (not inited) {
+    std::cerr << "*** Limiter " << this << ": init never called! Doing it now.\n";
+    if (fSamplingFreq <= 0) {
+      fSamplingFreq = 48000;
+      std::cout << "Limiter " << this << ": *** HAD TO GUESS the sampling rate (chose 48000 Hz) ***\n";
+    }
+    init(fSamplingFreq);
+  }
+#ifdef SINE_TEST
+  float sineTestOut[nframes];
+  float* faustSigs[1] { sineTestOut };
+#endif
+  for ( int i = 0; i < mNumChannels; i++ ) {
+    if (warningAmp > 0.0) {
+      checkAmplitudes(nframes, inputs[i]); // we presently do one check across all channels
+    }
+    limiterP[i]->compute(nframes, &inputs[i], &outputs[i]);
+#ifdef SINE_TEST
+    limiterTestP[i]->compute(nframes, faustSigs, faustSigs);
+    for ( int n = 0; n < nframes; n++ ) {
+      outputs[i][n] = outputs[i][n] + sineTestOut[n];
+    }
+#endif
+  }
+}
diff --git a/src/Limiter.h b/src/Limiter.h
new file mode 100644 (file)
index 0000000..6318d22
--- /dev/null
@@ -0,0 +1,179 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file Limiter.h
+ * \author Julius Smith, based on LoopBack.h
+ * \date May-Nov 2020
+ * \license MIT
+ */
+
+/** \brief Applies limiter_lad_mono from the faustlibraries distribution, compressors.lib
+ *
+ */
+#ifndef __LIMITER_H__
+#define __LIMITER_H__
+
+//#define SINE_TEST
+
+#ifdef SINE_TEST
+#include "limitertest.h"
+#endif
+
+#include "ProcessPlugin.h"
+#include "limiterdsp.h"
+#include <vector>
+#include "assert.h"
+
+/** \brief The Limiter class confines the output dynamic range to a
+ *  "dynamic range lane" determined by the assumed number of clients.
+ */
+class Limiter : public ProcessPlugin
+{
+public:
+  /// \brief The class constructor sets the number of channels to limit
+  Limiter(int numchans, int numclients, bool verboseFlag = false) // xtor
+    : mNumChannels(numchans), mNumClients(numclients)
+    , warningAmp(0.0), warnCount(0), peakMagnitude(0.0), nextWarning(1)
+  {
+    setVerbose(verboseFlag);
+    for ( int i = 0; i < mNumChannels; i++ ) {
+      limiterP.push_back(new limiterdsp);
+      limiterUIP.push_back(new APIUI); // #included in limiterdsp.h
+      limiterP[i]->buildUserInterface(limiterUIP[i]);
+#ifdef SINE_TEST
+      limiterTestP.push_back(new limitertest);
+      limiterTestUIP.push_back(new APIUI); // #included in limitertest.h
+      limiterTestP[i]->buildUserInterface(limiterTestUIP[i]);
+#endif
+    }
+    //    std::cout << "Limiter: constructed for "
+    // << mNumChannels << " channels and "
+    // << mNumClients << " assumed clients\n";
+  }
+
+  /// \brief The class destructor
+  virtual ~Limiter() {
+    for ( int i = 0; i < mNumChannels; i++ ) {
+      delete limiterP[i];
+      delete limiterUIP[i];
+    }
+    limiterP.clear();
+    limiterUIP.clear();
+  }
+
+  void init(int samplingRate) override {
+    ProcessPlugin::init(samplingRate);
+    if (samplingRate != fSamplingFreq) {
+      std::cerr << "Sampling rate not set by superclass!\n";
+      std::exit(1); }
+    fs = float(fSamplingFreq);
+    for ( int i = 0; i < mNumChannels; i++ ) {
+      limiterP[i]->init(fs); // compression filter parameters depend on sampling rate
+      int ndx = limiterUIP[i]->getParamIndex("NumClientsAssumed");
+      limiterUIP[i]->setParamValue(ndx, mNumClients);
+#ifdef SINE_TEST
+      limiterTestP[i]->init(fs); // oscillator parameters depend on sampling rate
+      ndx = limiterTestUIP[i]->getParamIndex("Amp");
+      limiterTestUIP[i]->setParamValue(ndx, 0.2);
+      ndx = limiterTestUIP[i]->getParamIndex("Freq");
+      float sineFreq = 110.0 * pow(1.5,double(i)) * (mNumClients>1?1.25:1.0); // Maj 7 chord for stereo in & out
+      limiterTestUIP[i]->setParamValue(ndx, sineFreq);
+#endif
+    }
+    inited = true;
+  }
+  int getNumInputs() override { return(mNumChannels); }
+  int getNumOutputs() override { return(mNumChannels); }
+  void compute(int nframes, float** inputs, float** outputs) override;
+
+  void setWarningAmplitude(double wa) { // setting to 0 turns off warnings
+    warningAmp = std::max(0.0,std::min(1.0,wa));
+  }
+
+ private:
+
+  void checkAmplitudes(int nframes, float* buf) {
+    const int maxWarningInterval { 10000 }; // this could become an option
+    assert(warningAmp > 0.0);
+    assert(mNumClients > 0);
+    for (int i=0; i<nframes; i++) {
+      double tmp_sample = double(buf[i]);
+      double limiterAmp = fabs(tmp_sample)/sqrt(double(mNumClients)); // KEEP IN SYNC with gain in ../faust-src/limiterdsp.dsp
+      if (limiterAmp >= warningAmp) {
+        warnCount++;
+        peakMagnitude = std::max(peakMagnitude,limiterAmp);
+        if (warnCount==nextWarning) {
+          double peakMagnitudeDB = 20.0 * std::log10(peakMagnitude);
+          double warningAmpDB = 20.0 * std::log10(warningAmp);
+          if (warnCount==1) {
+            if (warningAmp == 1.0) {
+              std::cerr << "*** Limiter.cpp: Audio HARD-CLIPPED!\n";
+              fprintf(stderr, "\tReduce your audio input level(s) by %0.1f dB to avoid this.\n", peakMagnitudeDB);
+            } else {
+              fprintf(stderr,
+                      "*** Limiter.cpp: Amplitude levels must stay below %0.1f dBFS to avoid compression.\n",
+                      warningAmpDB);
+              fprintf(stderr, "\tReduce input level(s) by %0.1f dB to achieve this.\n",
+                      peakMagnitudeDB-warningAmpDB);
+            }
+          } else {
+            fprintf(stderr, "\tReduce audio input level(s) by %0.1f dB to avoid limiter compression distortion.\n",
+                    peakMagnitudeDB-warningAmpDB);
+          }
+          peakMagnitude = 0.0; // reset for next group measurement
+          if (nextWarning < maxWarningInterval) { // don't let it stop reporting for too long
+            nextWarning *= 10;
+          } else {
+            warnCount=0;
+          }
+        } // warnCount==nextWarning
+      } // above warningAmp
+    } // loop over frames
+  } // checkAmplitudes()
+
+private:
+  float fs;
+  int mNumChannels;
+  int mNumClients;
+  std::vector<limiterdsp*> limiterP;
+  std::vector<APIUI*> limiterUIP;
+#ifdef SINE_TEST
+  std::vector<limitertest*> limiterTestP;
+  std::vector<APIUI*> limiterTestUIP;
+#endif
+  double warningAmp;
+  uint32_t warnCount;
+  double peakMagnitude;
+  uint32_t nextWarning;
+};
+
+#endif
index 56ac606ccaea0f5d7dcef9aff8e79b7526a8fbd4..5511f940b701a68144c9b25d7b3d9f81ab1e96ba 100644 (file)
@@ -133,11 +133,11 @@ void DefaultHeader::checkPeerSettings(int8_t* full_packet)
     // Check Buffer Size
     if ( peer_header->BufferSize != mHeader.BufferSize )
     {
-        std::cerr << "ERROR: Peer Buffer Size is  : " << peer_header->BufferSize << endl;
-        std::cerr << "       Local Buffer Size is : " << mHeader.BufferSize << endl;
-        std::cerr << "Make sure both machines use same buffer size" << endl;
-        std::cerr << gPrintSeparator << endl;
-        error = true;
+        std::cerr << "WARNING: Peer Buffer Size is  : " << peer_header->BufferSize << endl;
+        std::cerr << "         Local Buffer Size is : " << mHeader.BufferSize << endl;
+        //std::cerr << "Make sure both machines use same buffer size" << endl;
+        //std::cerr << gPrintSeparator << endl;
+        //error = true;
     }
 
     // Check Sampling Rate
index 094d4240157c2aa097a5c82e6cccd57d8384e911..80f38b888ccbe7d67d62ab5b40bf6977c9f2ac64 100644 (file)
@@ -165,7 +165,7 @@ public:
 
 
 signals:
-    void signalError(const char* error_message);
+    void signalError(const QString &error_message);
 
 
 private:
index e0b23f358ad719663aa0b5a4bb928608a40dcb2c..fb48cca54f4640335536370db0b79c83fbdaab88 100644 (file)
@@ -65,17 +65,33 @@ public:
 
     //virtual void buildUserInterface(UI* interface) = 0;
 
+    virtual char* getName() {
+      char* pluginName { const_cast<char*>(typeid(*this).name()) }; // get name of DERIVED class
+      while (isdigit(*pluginName)) { pluginName++; }
+      return pluginName;
+    }
+
     /** \brief Do proper Initialization of members and class instances. By default this
    * initializes the Sampling Frequency. If a class instance depends on the
    * sampling frequency, it should be initialize here.
    */
-    virtual void init(int samplingRate) { fSamplingFreq = samplingRate; };
+    virtual void init(int samplingRate) {
+      fSamplingFreq = samplingRate;
+      if (verbose) {
+        char* derivedClassName = getName();
+        printf("%s: init(%d)\n",derivedClassName,samplingRate);
+      }
+    }
+    virtual bool getInited() { return inited; }
+    virtual void setVerbose(bool v) { verbose = v; }
 
     /// \brief Compute process
     virtual void compute(int nframes, float** inputs, float** outputs) = 0;
 
 protected:
     int fSamplingFreq; ///< Faust Data member, Sampling Rate
+    bool inited = false;
+    bool verbose = false;
 };
 
 #endif
diff --git a/src/Reverb.cpp b/src/Reverb.cpp
new file mode 100644 (file)
index 0000000..abc2c5f
--- /dev/null
@@ -0,0 +1,70 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file Reverb.cpp
+ * \author Julius Smith, based on Limiter.h
+ * \date August 2020
+ */
+
+
+#include "Reverb.h"
+#include "jacktrip_types.h"
+
+#include <iostream>
+
+//*******************************************************************************
+void Reverb::compute(int nframes, float** inputs, float** outputs)
+{
+  if (not inited) {
+    std::cerr << "*** Reverb " << this << ": init never called! Doing it now.\n";
+    if (fSamplingFreq <= 0) {
+      fSamplingFreq = 48000;
+      std::cout << "Reverb " << this << ": *** HAD TO GUESS the sampling rate (chose 48000 Hz) ***\n";
+    }
+    init(fSamplingFreq);
+  }
+  if (mReverbLevel <= 1.0) {
+    if (mNumInChannels == 1) {
+      freeverbMonoP->compute(nframes, inputs, outputs);
+    } else {
+      assert(mNumInChannels == 2);
+      freeverbStereoP->compute(nframes, inputs, outputs);
+    }
+  } else {
+    if (mNumInChannels == 1) {
+      zitarevMonoP->compute(nframes, inputs, outputs);
+    } else {
+      assert(mNumInChannels == 2);
+      zitarevStereoP->compute(nframes, inputs, outputs);
+    }
+  }
+}
diff --git a/src/Reverb.h b/src/Reverb.h
new file mode 100644 (file)
index 0000000..3a7fd84
--- /dev/null
@@ -0,0 +1,158 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe.
+  SoundWIRE group at CCRMA, Stanford University.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the
+  Software is furnished to do so, subject to the following
+  conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+  OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+
+/**
+ * \file Reverb.h
+ * \author Julius Smith, based on Limiter.h
+ * \date August 2020
+ */
+
+
+/** \brief Applies freeverb or zitarev from the faustlibraries distribution: reverbs.lib
+ *
+ */
+#ifndef __REVERB_H__
+#define __REVERB_H__
+
+//#define SINE_TEST
+
+#include "ProcessPlugin.h"
+#include "freeverbdsp.h" // stereo in and out
+#include "freeverbmonodsp.h" // mono in and out (there is no mono to stereo case in jacktrip as yet)
+#include "zitarevdsp.h" // stereo in and out
+#include "zitarevmonodsp.h" // mono in and out
+
+/** \brief A Reverb is an echo-based delay effect,
+ *  providing a virtual acoustic listening space.
+ */
+class Reverb : public ProcessPlugin
+{
+public:
+  /// \brief The class constructor sets the number of channels to limit
+  Reverb(int numInChans, int numOutChans, float reverbLevel = 1.0, bool verboseFlag = false) // xtor
+    : mNumInChannels(numInChans), mNumOutChannels(numOutChans), mReverbLevel(reverbLevel)
+  {
+    setVerbose(verboseFlag);
+    if ( mNumInChannels < 1 ) {
+      std::cerr << "*** Reverb.h: must have at least one input audio channels\n";
+      mNumInChannels = 1;
+    }
+    if ( mNumInChannels > 2 ) {
+      std::cerr << "*** Reverb.h: limiting number of audio output channels to 2\n";
+      mNumInChannels = 2;
+    }
+#if 0
+    std::cout << "Reverb: constructed for "
+              << mNumInChannels << " input channels and "
+              << mNumOutChannels << " output channels with reverb level = "
+              << mReverbLevel << "\n";
+#endif
+
+    if (mReverbLevel <= 1.0) { // freeverb:
+      freeverbStereoP = new freeverbdsp; // stereo input and output
+      freeverbMonoP = new freeverbmonodsp; // mono input, stereo output
+      freeverbStereoUIP = new APIUI; // #included in *dsp.h
+      freeverbMonoUIP = new APIUI;
+      freeverbStereoP->buildUserInterface(freeverbStereoUIP);
+      freeverbMonoP->buildUserInterface(freeverbMonoUIP);
+      // std::cout << "Using freeverb\n";
+    } else {
+      zitarevStereoP = new zitarevdsp; // stereo input and output
+      zitarevMonoP = new zitarevmonodsp; // mono input, stereo output
+      zitarevStereoUIP = new APIUI;
+      zitarevMonoUIP = new APIUI;
+      zitarevStereoP->buildUserInterface(zitarevStereoUIP);
+      zitarevMonoP->buildUserInterface(zitarevMonoUIP);
+      // std::cout << "Using zitarev\n";
+    }
+  }
+
+  /// \brief The class destructor
+  virtual ~Reverb() {
+    if (mReverbLevel <= 1.0) { // freeverb:
+      delete freeverbStereoP;
+      delete freeverbMonoP;
+      delete freeverbStereoUIP;
+      delete freeverbMonoUIP;
+    } else {
+      delete zitarevStereoP;
+      delete zitarevMonoP;
+      delete zitarevStereoUIP;
+      delete zitarevMonoUIP;
+    }
+  }
+
+  void init(int samplingRate) override {
+    ProcessPlugin::init(samplingRate);
+    // std::cout << "Reverb: init(" << samplingRate << ")\n";
+    if (samplingRate != fSamplingFreq) {
+      std::cerr << "Sampling rate not set by superclass!\n";
+      std::exit(1); }
+    fs = float(fSamplingFreq);
+    if (mReverbLevel <= 1.0) { // freeverb:
+      freeverbStereoP->init(fs); // compression filter parameters depend on sampling rate
+      freeverbMonoP->init(fs); // compression filter parameters depend on sampling rate
+      int ndx = freeverbStereoUIP->getParamIndex("Wet");
+      freeverbStereoUIP->setParamValue(ndx, mReverbLevel);
+      freeverbMonoUIP->setParamValue(ndx, mReverbLevel);
+    } else { // zitarev:
+      zitarevStereoP->init(fs); // compression filter parameters depend on sampling rate
+      zitarevMonoP->init(fs); // compression filter parameters depend on sampling rate
+      int ndx = zitarevStereoUIP->getParamIndex("Wet");
+      float zitaLevel = mReverbLevel-1.0f; // range within zitarev is 0 to 1 (our version only)
+      zitarevStereoUIP->setParamValue(ndx, zitaLevel);
+      zitarevMonoUIP->setParamValue(ndx, zitaLevel);
+    }
+    inited = true;
+  }
+  int getNumInputs() override { return(mNumInChannels); }
+  int getNumOutputs() override { return(mNumOutChannels); }
+  void compute(int nframes, float** inputs, float** outputs) override;
+
+private:
+  float fs;
+  int mNumInChannels;
+  int mNumOutChannels;
+
+  float mReverbLevel;
+
+  freeverbdsp* freeverbStereoP;
+  freeverbmonodsp* freeverbMonoP;
+  APIUI* freeverbStereoUIP;
+  APIUI* freeverbMonoUIP;
+
+  zitarevdsp* zitarevStereoP;
+  zitarevmonodsp* zitarevMonoP;
+  APIUI* zitarevStereoUIP;
+  APIUI* zitarevMonoUIP;
+};
+
+#endif
index 124399ed902ec307d52317820fa37746d8ce60a6..e9ff28cb8d57f2e63399194b89764857810cb65c 100644 (file)
@@ -42,6 +42,8 @@
 #include <cstring>
 #include <cstdlib>
 #include <stdexcept>
+#include <cmath>
+#include "JackTrip.h"
 
 using std::cout; using std::endl;
 
@@ -54,40 +56,55 @@ RingBuffer::RingBuffer(int SlotSize, int NumSlots) :
     mReadPosition(0),
     mWritePosition(0),
     mFullSlots(0),
-    mRingBuffer(new int8_t[mTotalSize]),
-    mLastReadSlot(new int8_t[mSlotSize])
+    mRingBuffer(NULL),
+    mLastReadSlot(NULL)
 {
-    //QMutexLocker locker(&mMutex); // lock the mutex
-
-    // Verify if there's enough space to for the buffers
-    if ( (mRingBuffer == NULL) || (mLastReadSlot == NULL) ) {
-        //std::cerr << "ERROR: RingBuffer out of memory!" << endl;
-        //std::cerr << "Exiting program..." << endl;
-        //std::exit(1);
-        throw std::length_error("RingBuffer out of memory!");
+    if (0 < mTotalSize) {
+        mRingBuffer = new int8_t[mTotalSize];
+        mLastReadSlot = new int8_t[mSlotSize];
+        //QMutexLocker locker(&mMutex); // lock the mutex
+
+        // Verify if there's enough space to for the buffers
+        if ( (mRingBuffer == NULL) || (mLastReadSlot == NULL) ) {
+            //std::cerr << "ERROR: RingBuffer out of memory!" << endl;
+            //std::cerr << "Exiting program..." << endl;
+            //std::exit(1);
+            throw std::length_error("RingBuffer out of memory!");
+        }
+
+        // Set the buffers to zeros
+        /*
+      for (int i=0; i<mTotalSize; i++) {
+        mRingBuffer[i] = 0;    // Initialize all elements to zero.
+      }
+      */
+        std::memset(mRingBuffer, 0, mTotalSize); // set buffer to 0
+        /*
+      for (int i=0; i<mSlotSize; i++) {
+        mLastReadSlot[i] = 0;    // Initialize all elements to zero.
+      }
+      */
+        std::memset(mLastReadSlot, 0, mSlotSize); // set buffer to 0
+        mWritePosition = ( (NumSlots/2) * SlotSize ) % mTotalSize;
     }
 
-    // Set the buffers to zeros
-    /*
-  for (int i=0; i<mTotalSize; i++) {
-    mRingBuffer[i] = 0;    // Initialize all elements to zero.
-  }
-  */
-    std::memset(mRingBuffer, 0, mTotalSize); // set buffer to 0
-    /*
-  for (int i=0; i<mSlotSize; i++) {
-    mLastReadSlot[i] = 0;    // Initialize all elements to zero.
-  }
-  */
-    std::memset(mLastReadSlot, 0, mSlotSize); // set buffer to 0
-
-
     // Advance write position to half of the RingBuffer
-    mWritePosition = ( (NumSlots/2) * SlotSize ) % mTotalSize;
     // Udpate Full Slots accordingly
     mFullSlots = (NumSlots/2);
+    mLevelDownRate = 0.01;
+    mStatUnit = 1;
     mUnderruns = 0;
     mOverflows = 0;
+    mSkew0 = 0;
+    mSkewRaw = 0;
+    mLevelCur = mFullSlots;
+    mLevel = mLevelCur;
+    mBufDecOverflow = 0;
+    mBufDecPktLoss = 0;
+    mBufIncUnderrun = 0;
+    mBufIncCompensate = 0;
+    mBroadcastSkew = 0;
+    mBroadcastDelta = 0;
 }
 
 
@@ -105,6 +122,7 @@ RingBuffer::~RingBuffer()
 void RingBuffer::insertSlotBlocking(const int8_t* ptrToSlot)
 {
     QMutexLocker locker(&mMutex); // lock the mutex
+    updateReadStats();
 
     // Check if there is space available to write a slot
     // If the Ringbuffer is full, it waits for the bufferIsNotFull condition
@@ -127,12 +145,16 @@ void RingBuffer::insertSlotBlocking(const int8_t* ptrToSlot)
 void RingBuffer::readSlotBlocking(int8_t* ptrToReadSlot)
 {
     QMutexLocker locker(&mMutex); // lock the mutex
+    ++mReadsNew;
 
     // Check if there are slots available to read
     // If the Ringbuffer is empty, it waits for the bufferIsNotEmpty condition
     while (mFullSlots == 0) {
         //std::cerr << "READ UNDER-RUN BLOCKING before" << endl;
-        mBufferIsNotEmpty.wait(&mMutex);
+        mBufferIsNotEmpty.wait(&mMutex, 200);
+        if (JackTrip::sJackStopped) {
+            return;
+        }
     }
 
     // Copy mSlotSize bytes to ReadSlot
@@ -148,9 +170,20 @@ void RingBuffer::readSlotBlocking(int8_t* ptrToReadSlot)
 
 
 //*******************************************************************************
-void RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot)
+bool RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen)
 {
+    if (len != mSlotSize && 0 != len) {
+        // RingBuffer does not suppport mixed buf sizes
+        return false;
+    }
     QMutexLocker locker(&mMutex); // lock the mutex
+    if (0 < lostLen) {
+        int lostCount = lostLen / mSlotSize;
+        mBufDecPktLoss += lostCount;
+        mSkewRaw -= lostCount;
+        mLevelCur -= lostCount;
+    }
+    updateReadStats();
 
     // Check if there is space available to write a slot
     // If the Ringbuffer is full, it returns without writing anything
@@ -160,7 +193,7 @@ void RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot)
     if (mFullSlots == mNumSlots) {
         //std::cout << "OUPUT OVERFLOW NON BLOCKING = " << mNumSlots << std::endl;
         overflowReset();
-        return;
+        return true;
     }
 
     // Copy mSlotSize bytes to mRingBuffer
@@ -170,6 +203,7 @@ void RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot)
     mFullSlots++; //update full slots
     // Wake threads waitng for bufferIsNotFull condition
     mBufferIsNotEmpty.wakeAll();
+    return true;
 }
 
 
@@ -177,11 +211,17 @@ void RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot)
 void RingBuffer::readSlotNonBlocking(int8_t* ptrToReadSlot)
 {
     QMutexLocker locker(&mMutex); // lock the mutex
-
+    ++mReadsNew;
+    if (mFullSlots < mLevelCur) {
+        mLevelCur = std::max((double)mFullSlots, mLevelCur-mLevelDownRate);
+    }
+    else {
+        mLevelCur = mFullSlots;
+    }
 
     // Check if there are slots available to read
     // If the Ringbuffer is empty, it returns a buffer of zeros and rests the buffer
-    if (mFullSlots == 0) {
+    if (mFullSlots <= 0) {
         // Returns a buffer of zeros if there's nothing to read
         //std::cerr << "READ UNDER-RUN NON BLOCKING = " << mNumSlots << endl;
         //std::memset(ptrToReadSlot, 0, mSlotSize);
@@ -202,6 +242,14 @@ void RingBuffer::readSlotNonBlocking(int8_t* ptrToReadSlot)
 }
 
 
+//*******************************************************************************
+// Not supported in RingBuffer
+void RingBuffer::readBroadcastSlot(int8_t* ptrToReadSlot)
+{
+    std::memset(ptrToReadSlot, 0, mSlotSize);
+}
+
+
 //*******************************************************************************
 void RingBuffer::setUnderrunReadSlot(int8_t* ptrToReadSlot)
 {
@@ -228,7 +276,7 @@ void RingBuffer::underrunReset()
     //mFullSlots += mNumSlots/2;
     // There's nothing new to read, so we clear the whole buffer (Set the entire buffer to 0)
     std::memset(mRingBuffer, 0, mTotalSize);
-    ++mUnderruns;
+    ++mUnderrunsNew;
 }
 
 
@@ -238,9 +286,12 @@ void RingBuffer::overflowReset()
 {
     // Advance the read pointer 1/2 the ring buffer
     //mReadPosition = ( mWritePosition + ( (mNumSlots/2) * mSlotSize ) ) % mTotalSize;
-    mReadPosition = ( mReadPosition + ( (mNumSlots/2) * mSlotSize ) ) % mTotalSize;
-    mFullSlots -= mNumSlots/2;
-    mOverflows += mNumSlots/2 + 1;
+    int d = mNumSlots / 2;
+    mReadPosition = ( mReadPosition + ( d * mSlotSize ) ) % mTotalSize;
+    mFullSlots -= d;
+    mOverflows += d + 1;
+    mBufDecOverflow += d + 1;
+    mLevelCur -= d;
 }
 
 
@@ -256,11 +307,45 @@ void RingBuffer::debugDump() const
 //*******************************************************************************
 bool RingBuffer::getStats(RingBuffer::IOStat* stat, bool reset)
 {
+    QMutexLocker locker(&mMutex);
     if (reset) {
         mUnderruns = 0;
         mOverflows = 0;
+        mSkew0 = mLevel;
+        mSkewRaw = 0;
+        mBufDecOverflow = 0;
+        mBufDecPktLoss = 0;
+        mBufIncUnderrun = 0;
+        mBufIncCompensate = 0;
+        mBroadcastSkew = 0;
     }
-    stat->underruns = mUnderruns;
-    stat->overflows = mOverflows;
+    stat->underruns = mUnderruns / mStatUnit;
+    stat->overflows = mOverflows / mStatUnit;
+    stat->skew = (int32_t)((mSkew0 - mLevel + mBufIncUnderrun + mBufIncCompensate
+                        - mBufDecOverflow - mBufDecPktLoss)) / mStatUnit;
+    stat->skew_raw = mSkewRaw / mStatUnit;
+    stat->level = mLevel / mStatUnit;
+
+    stat->buf_dec_overflows = mBufDecOverflow / mStatUnit;
+    stat->buf_dec_pktloss = mBufDecPktLoss / mStatUnit;
+    stat->buf_inc_underrun = mBufIncUnderrun / mStatUnit;
+    stat->buf_inc_compensate = mBufIncCompensate / mStatUnit;
+    stat->broadcast_skew = mBroadcastSkew;
+    stat->broadcast_delta = mBroadcastDelta;
+
+    stat->autoq_corr = 0;
+    stat->autoq_rate = 0;
     return true;
 }
+
+//*******************************************************************************
+void RingBuffer::updateReadStats()
+{
+    --mSkewRaw;
+    mSkewRaw += mReadsNew;
+    mReadsNew = 0;
+    mUnderruns += mUnderrunsNew;
+    mBufIncUnderrun += mUnderrunsNew;
+    mUnderrunsNew = 0;
+    mLevel = std::ceil(mLevelCur);
+}
index 78e137f79c2d1b0bfb55bfe2caef21835e0b6b83..e68d625d43131d011c8fc2cf6654c9aa94c52def 100644 (file)
@@ -95,16 +95,29 @@ public:
     /** \brief Same as insertSlotBlocking but non-blocking (asynchronous)
    * \param ptrToSlot Pointer to slot to insert into the RingBuffer
    */
-    void insertSlotNonBlocking(const int8_t* ptrToSlot);
+    virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen);
 
     /** \brief Same as readSlotBlocking but non-blocking (asynchronous)
    * \param ptrToReadSlot Pointer to read slot from the RingBuffer
    */
-    void readSlotNonBlocking(int8_t* ptrToReadSlot);
+    virtual void readSlotNonBlocking(int8_t* ptrToReadSlot);
+    virtual void readBroadcastSlot(int8_t* ptrToReadSlot);
 
     struct IOStat {
         uint32_t underruns;
         uint32_t overflows;
+        int32_t skew;
+        int32_t skew_raw;
+        int32_t level;
+        uint32_t buf_dec_overflows;
+        uint32_t buf_dec_pktloss;
+        uint32_t buf_inc_underrun;
+        uint32_t buf_inc_compensate;
+        int32_t broadcast_skew;
+        int32_t broadcast_delta;
+
+        int32_t autoq_corr;
+        int32_t autoq_rate;
     };
     virtual bool getStats(IOStat* stat, bool reset);
 
@@ -124,20 +137,19 @@ protected:
    */
     virtual void setMemoryInReadSlotWithLastReadSlot(int8_t* ptrToReadSlot);
 
-private:
-
     /// \brief Resets the ring buffer for reads under-runs non-blocking
     void underrunReset();
     /// \brief Resets the ring buffer for writes over-flows non-blocking
     void overflowReset();
     /// \brief Helper method to debug, prints member variables to terminal
     void debugDump() const;
+    void updateReadStats();
 
-    const int mSlotSize; ///< The size of one slot in byes
-    const int mNumSlots; ///< Number of Slots
-    const int mTotalSize; ///< Total size of the mRingBuffer = mSlotSize*mNumSlotss
-    int mReadPosition; ///< Read Positions in the RingBuffer (Tail)
-    int mWritePosition; ///< Write Position in the RingBuffer (Head)
+    /*const*/ int mSlotSize; ///< The size of one slot in byes
+    /*const*/ int mNumSlots; ///< Number of Slots
+    /*const*/ int mTotalSize; ///< Total size of the mRingBuffer = mSlotSize*mNumSlotss
+    uint32_t mReadPosition; ///< Read Positions in the RingBuffer (Tail)
+    uint32_t mWritePosition; ///< Write Position in the RingBuffer (Head)
     int mFullSlots; ///< Number of used (full) slots, in slot-size
     int8_t* mRingBuffer; ///< 8-bit array of data (1-byte)
     int8_t* mLastReadSlot; ///< Last slot read
@@ -146,8 +158,29 @@ private:
     QMutex mMutex; ///< Mutex to protect read and write operations
     QWaitCondition mBufferIsNotFull; ///< Buffer not full condition to monitor threads
     QWaitCondition mBufferIsNotEmpty; ///< Buffer not empty condition to monitor threads
-    std::atomic<uint32_t> mUnderruns;
-    std::atomic<uint32_t> mOverflows;
+
+    // IO stat
+    int mStatUnit;
+    uint32_t mUnderruns;
+    uint32_t mOverflows;
+    int32_t  mSkewRaw;
+    double   mLevelCur;
+    double   mLevelDownRate;
+    int32_t  mLevel;
+
+    uint32_t mBufDecOverflow;
+    uint32_t mBufDecPktLoss;
+    uint32_t mBufIncUnderrun;
+    uint32_t mBufIncCompensate;
+
+    // temp counters for reads
+    uint32_t mReadsNew;
+    uint32_t mUnderrunsNew;
+    int32_t  mSkew0;
+
+    // broadcast counters
+    int32_t mBroadcastSkew;
+    int32_t mBroadcastDelta;
 };
 
 #endif
index bbc386b0a8d7677e0a6768a36e9471e921f97dd1..4d0802144f0d1eeac57ec1c66723a878dce9e36d 100644 (file)
 
 #include "Settings.h"
 #include "LoopBack.h"
-#include "NetKS.h"
+//#include "NetKS.h"
+#include "Effects.h"
 
 #ifdef WAIR // wair
 #include "ap8x2.dsp.h"
 #include "Stk16.dsp.h"
 #endif // endwhere
 
-#include "UdpHubListener.h"
-#include "JackTripWorker.h"
+//#include "JackTripWorker.h"
 #include "jacktrip_globals.h"
 
 #include <iostream>
 #include <getopt.h> // for command line parsing
 #include <cstdlib>
+#include <assert.h>
+#include <ctype.h>
 
-#include "ThreadPoolTest.h"
+//#include "ThreadPoolTest.h"
 
 using std::cout; using std::endl;
 
 int gVerboseFlag = 0;
 
+enum JTLongOptIDS {
+  OPT_BUFSTRATEGY = 1001,
+  OPT_SIMLOSS,
+  OPT_SIMJITTER,
+  OPT_BROADCAST,
+  OPT_RTUDPPRIORITY,
+};
 
 //*******************************************************************************
 Settings::Settings() :
-    mJackTrip(NULL),
     mJackTripMode(JackTrip::SERVER),
     mDataProtocol(JackTrip::UDP),
     mNumChans(2),
     mBufferQueueLength(gDefaultQueueLength),
     mAudioBitResolution(AudioInterface::BIT16),
     mBindPortNum(gDefaultPort), mPeerPortNum(gDefaultPort),
-    mServerUdpPortNum(NULL),
-    mClientName(NULL),
-    mUnderrrunZero(false),
+    mServerUdpPortNum(0),
+    mUnderrunMode(JackTrip::WAVETABLE),
+    mStopOnTimeout(false),
+    mBufferStrategy(1),
     mLoopBack(false),
     #ifdef WAIR // WAIR
     mNumNetRevChans(0),
@@ -87,19 +96,23 @@ Settings::Settings() :
     mChanfeDefaultBS(false),
     mHubConnectionMode(JackTrip::SERVERTOCLIENT),
     mConnectDefaultAudioPorts(true),
-    mIOStatTimeout(0)
+    mIOStatTimeout(0),
+    mEffects(false), // outgoing limiter OFF by default
+    mSimulatedLossRate(0.0),
+    mSimulatedJitterRate(0.0),
+    mSimulatedDelayRel(0.0),
+    mBroadcastQueue(0),
+    mUseRtUdpPriority(false)
 {}
 
 //*******************************************************************************
-Settings::~Settings()
-{
-    stopJackTrip();
-    delete mJackTrip;
-}
+Settings::~Settings() = default;
 
 //*******************************************************************************
 void Settings::parseInput(int argc, char** argv)
 {
+    // Always use decimal point for floating point numbers
+    setlocale( LC_NUMERIC, "C" );
     // If no command arguments are given, print instructions
     if(argc == 1) {
         printUsage();
@@ -112,49 +125,60 @@ void Settings::parseInput(int argc, char** argv)
     //----------------------------------------------------------------------------
     static struct option longopts[] = {
         // These options don't set a flag.
-    { "numchannels", required_argument, NULL, 'n' }, // Number of input and output channels
+        { "numchannels", required_argument, NULL, 'n' }, // Number of input and output channels
 #ifdef WAIR // WAIR
-    { "wair", no_argument, NULL, 'w' }, // Run in LAIR mode, sets numnetrevchannels
-    { "addcombfilterlength", required_argument, NULL, 'N' }, // added comb filter length
-    { "combfilterfeedback", required_argument, NULL, 'H' }, // comb filter feedback
+        { "wair", no_argument, NULL, 'w' }, // Run in LAIR mode, sets numnetrevchannels
+        { "addcombfilterlength", required_argument, NULL, 'N' }, // added comb filter length
+        { "combfilterfeedback", required_argument, NULL, 'H' }, // comb filter feedback
 #endif // endwhere
-    { "server", no_argument, NULL, 's' }, // Run in server mode
-    { "client", required_argument, NULL, 'c' }, // Run in client mode, set server IP address
-    { "localaddress", required_argument, NULL, 'L' }, // set local address e.g., 127.0.0.2 for second instance on same host
-    { "jacktripserver", no_argument, NULL, 'S' }, // Run in JamLink mode
-    { "pingtoserver", required_argument, NULL, 'C' }, // Run in ping to server mode, set server IP address
-    { "portoffset", required_argument, NULL, 'o' }, // Port Offset from 4464
-    { "bindport", required_argument, NULL, 'B' }, // Port Offset from 4464
-    { "peerport", required_argument, NULL, 'P' }, // Port Offset from 4464
-    { "udpbaseport", required_argument, NULL, 'U' }, // Server udp base port (defaults to 61002)
-    { "queue", required_argument, NULL, 'q' }, // Queue Length
-    { "redundancy", required_argument, NULL, 'r' }, // Redundancy
-    { "bitres", required_argument, NULL, 'b' }, // Audio Bit Resolution
-    { "zerounderrun", no_argument, NULL, 'z' }, // Use Underrun to Zeros Mode
-    { "loopback", no_argument, NULL, 'l' }, // Run in loopback mode
-    { "jamlink", no_argument, NULL, 'j' }, // Run in JamLink mode
-    { "emptyheader", no_argument, NULL, 'e' }, // Run in JamLink mode
-    { "clientname", required_argument, NULL, 'J' }, // Run in JamLink mode
-    { "rtaudio", no_argument, NULL, 'R' }, // Run in JamLink mode
-    { "srate", required_argument, NULL, 'T' }, // Set Sample Rate
-    { "deviceid", required_argument, NULL, 'd' }, // Set RTAudio device id to use
-    { "bufsize", required_argument, NULL, 'F' }, // Set buffer Size
-    { "nojackportsconnect" , no_argument, NULL,  'D'}, // Don't connect default Audio Ports
-    { "version", no_argument, NULL, 'v' }, // Version Number
-    { "verbose", no_argument, NULL, 'V' }, // Verbose mode
-    { "hubpatch", required_argument, NULL, 'p' }, // Set hubConnectionMode for auto patch in Jack
-    { "iostat", required_argument, NULL, 'I' }, // Set IO stat timeout
-    { "iostatlog", required_argument, NULL, 'G' }, // Set IO stat log file
-    { "help", no_argument, NULL, 'h' }, // Print Help
-    { NULL, 0, NULL, 0 }
-};
+        { "server", no_argument, NULL, 's' }, // Run in P2P server mode
+        { "client", required_argument, NULL, 'c' }, // Run in P2P client mode, set server IP address
+        { "localaddress", required_argument, NULL, 'L' }, // set local address e.g., 127.0.0.2 for second instance on same host
+        { "jacktripserver", no_argument, NULL, 'S' }, // Run in JamLink mode
+        { "pingtoserver", required_argument, NULL, 'C' }, // Run in ping to server mode, set server IP address
+        { "portoffset", required_argument, NULL, 'o' }, // Port Offset from 4464
+        { "bindport", required_argument, NULL, 'B' }, // Port Offset from 4464
+        { "peerport", required_argument, NULL, 'P' }, // Port Offset from 4464
+        { "udpbaseport", required_argument, NULL, 'U' }, // Server udp base port (defaults to 61002)
+        { "queue", required_argument, NULL, 'q' }, // Queue Length
+        { "redundancy", required_argument, NULL, 'r' }, // Redundancy
+        { "bitres", required_argument, NULL, 'b' }, // Audio Bit Resolution
+        { "zerounderrun", no_argument, NULL, 'z' }, // Use Underrun to Zeros Mode
+        { "timeout", no_argument, NULL, 't' }, // Quit after 10 second network timeout
+        { "loopback", no_argument, NULL, 'l' }, // Run in loopback mode
+        { "jamlink", no_argument, NULL, 'j' }, // Run in JamLink mode
+        { "emptyheader", no_argument, NULL, 'e' }, // Run in JamLink mode
+        { "clientname", required_argument, NULL, 'J' }, // Run in JamLink mode
+        { "remotename", required_argument, NULL, 'K' }, // Client name on hub server
+        { "rtaudio", no_argument, NULL, 'R' }, // Run in JamLink mode
+        { "srate", required_argument, NULL, 'T' }, // Set Sample Rate
+        { "deviceid", required_argument, NULL, 'd' }, // Set RTAudio device id to use
+        { "bufsize", required_argument, NULL, 'F' }, // Set buffer Size
+        { "nojackportsconnect" , no_argument, NULL,  'D'}, // Don't connect default Audio Ports
+        { "version", no_argument, NULL, 'v' }, // Version Number
+        { "verbose", no_argument, NULL, 'V' }, // Verbose mode
+        { "hubpatch", required_argument, NULL, 'p' }, // Set hubConnectionMode for auto patch in Jack
+        { "iostat", required_argument, NULL, 'I' }, // Set IO stat timeout
+        { "iostatlog", required_argument, NULL, 'G' }, // Set IO stat log file
+        { "effects", required_argument, NULL, 'f' }, // Turn on outgoing compressor and incoming reverb, reverbLevel arg
+        { "overflowlimiting", required_argument, NULL, 'O' }, // Turn On limiter, cases 'i', 'o', 'io'
+        { "assumednumclients", required_argument, NULL, 'a' }, // assumed number of clients (sound sources) (otherwise 2)
+        { "bufstrategy", required_argument, NULL, OPT_BUFSTRATEGY }, // Set bufstrategy
+        { "simloss", required_argument, NULL, OPT_SIMLOSS },
+        { "simjitter", required_argument, NULL, OPT_SIMJITTER },
+        { "broadcast", required_argument, NULL, OPT_BROADCAST },
+        { "udprt", no_argument, NULL, OPT_RTUDPPRIORITY },
+        { "help", no_argument, NULL, 'h' }, // Print Help
+        { "examine-audio-delay", required_argument, NULL, 'x' }, // test mode - measure audio round-trip latency statistics
+        { NULL, 0, NULL, 0 }
+    };
 
     // Parse Command Line Arguments
     //----------------------------------------------------------------------------
     /// \todo Specify mandatory arguments
     int ch;
-    while ( (ch = getopt_long(argc, argv,
-                              "n:N:H:sc:SC:o:B:P:U:q:r:b:zlwjeJ:RTd:F:p:DvVh", longopts, NULL)) != -1 )
+    while ((ch = getopt_long(argc, argv,
+                             "n:N:H:sc:SC:o:B:P:U:q:r:b:ztlwjeJ:K:RTd:F:p:DvVhI:G:f:O:a:x:", longopts, NULL)) != -1)
         switch (ch) {
 
         case 'n': // Number of input and output channels
@@ -179,15 +203,15 @@ void Settings::parseInput(int argc, char** argv)
             mClientRoomSize = atof(optarg); // cmd line comb feedback adjustment
             break;
 #endif // endwhere
-        case 's': // Run in server mode
+        case 's': // Run in P2P server mode
             //-------------------------------------------------------
             mJackTripMode = JackTrip::SERVER;
             break;
-        case 'S': // Run in jacktripserver mode
+        case 'S': // Run in Hub server mode
             //-------------------------------------------------------
             mJackTripServer = true;
             break;
-        case 'c': // Client mode
+        case 'c': // P2P client mode
             //-------------------------------------------------------
             mJackTripMode = JackTrip::CLIENT;
             mPeerAddress = optarg;
@@ -220,25 +244,32 @@ void Settings::parseInput(int argc, char** argv)
             break;
         case 'b':
             //-------------------------------------------------------
-            if      ( atoi(optarg) == 8 ) {
-                mAudioBitResolution = AudioInterface::BIT8; }
-            else if ( atoi(optarg) == 16 ) {
-                mAudioBitResolution = AudioInterface::BIT16; }
-            else if ( atoi(optarg) == 24 ) {
-                mAudioBitResolution = AudioInterface::BIT24; }
-            else if ( atoi(optarg) == 32 ) {
-                mAudioBitResolution = AudioInterface::BIT32; }
-            else {
-                std::cerr << "--bitres ERROR: Wrong bit resolution: "
-                          << atoi(optarg) << " is not supported." << endl;
+            if (atoi(optarg) == 8) {
+                mAudioBitResolution = AudioInterface::BIT8;
+            } else if (atoi(optarg) == 16) {
+                mAudioBitResolution = AudioInterface::BIT16;
+            } else if (atoi(optarg) == 24) {
+                mAudioBitResolution = AudioInterface::BIT24;
+            } else if (atoi(optarg) == 32) {
+                mAudioBitResolution = AudioInterface::BIT32;
+            } else {
                 printUsage();
-                std::exit(1); }
+                std::cerr << "--bitres ERROR: Bit resolution: "
+                          << atoi(optarg) << " is not supported." << endl;
+                std::exit(1);
+            }
             break;
         case 'q':
             //-------------------------------------------------------
-            if ( atoi(optarg) <= 0 ) {
-                std::cerr << "--queue ERROR: The queue has to be equal or greater than 2" << endl;
+            if (0 == strncmp(optarg, "auto", 4)) {
+              mBufferQueueLength = -atoi(optarg+4);
+              if (0 == mBufferQueueLength) {
+                mBufferQueueLength = -500;
+              }
+            }
+            else if ( atoi(optarg) <= 0 ) {
                 printUsage();
+                std::cerr << "--queue ERROR: The queue has to be equal or greater than 2" << endl;
                 std::exit(1); }
             else {
                 mBufferQueueLength = atoi(optarg);
@@ -247,8 +278,8 @@ void Settings::parseInput(int argc, char** argv)
         case 'r':
             //-------------------------------------------------------
             if ( atoi(optarg) <= 0 ) {
-                std::cerr << "--redundancy ERROR: The reduncancy has to be a positive integer" << endl;
                 printUsage();
+                std::cerr << "--redundancy ERROR: The redundancy has to be a positive integer" << endl;
                 std::exit(1); }
             else {
                 mRedundancy = atoi(optarg);
@@ -256,7 +287,10 @@ void Settings::parseInput(int argc, char** argv)
             break;
         case 'z': // underrun to zero
             //-------------------------------------------------------
-            mUnderrrunZero = true;
+            mUnderrunMode = JackTrip::ZEROS;
+            break;
+        case 't': // quit on timeout
+            mStopOnTimeout = true;
             break;
         case 'l': // loopback
             //-------------------------------------------------------
@@ -274,6 +308,10 @@ void Settings::parseInput(int argc, char** argv)
             //-------------------------------------------------------
             mClientName = optarg;
             break;
+        case 'K': // Set Remote client Name
+            //-------------------------------------------------------
+            mRemoteClientName = optarg;
+            break;
         case 'R': // RtAudio
             //-------------------------------------------------------
             mUseJack = false;
@@ -309,70 +347,204 @@ void Settings::parseInput(int argc, char** argv)
             //-------------------------------------------------------
             gVerboseFlag = true;
             if (gVerboseFlag) std::cout << "Verbose mode" << std::endl;
+            mEffects.setVerboseFlag(gVerboseFlag);
             break;
         case 'p':
             //-------------------------------------------------------
-            if      ( atoi(optarg) == 0 ) {
-                mHubConnectionMode = JackTrip::SERVERTOCLIENT; }
-            else if ( atoi(optarg) == 1 ) {
-                mHubConnectionMode = JackTrip::CLIENTECHO; }
-            else if ( atoi(optarg) == 2 ) {
-                mHubConnectionMode = JackTrip::CLIENTFOFI; }
-            else if ( atoi(optarg) == 3 ) {
-                mHubConnectionMode = JackTrip::RESERVEDMATRIX; }
-            else if ( atoi(optarg) == 4 ) {
-                mHubConnectionMode = JackTrip::FULLMIX; }
-            else {
+            if ( atoi(optarg) == 0 ) {
+                mHubConnectionMode = JackTrip::SERVERTOCLIENT;
+            } else if ( atoi(optarg) == 1 ) {
+                mHubConnectionMode = JackTrip::CLIENTECHO;
+            } else if ( atoi(optarg) == 2 ) {
+                mHubConnectionMode = JackTrip::CLIENTFOFI;
+            } else if ( atoi(optarg) == 3 ) {
+                mHubConnectionMode = JackTrip::RESERVEDMATRIX;
+            } else if ( atoi(optarg) == 4 ) {
+                mHubConnectionMode = JackTrip::FULLMIX;
+            } else if ( atoi(optarg) == 5 ) {
+                mHubConnectionMode = JackTrip::NOAUTO;
+            } else {
+                printUsage();
                 std::cerr << "-p ERROR: Wrong HubConnectionMode: "
                           << atoi(optarg) << " is not supported." << endl;
-                printUsage();
-                std::exit(1); }
+                std::exit(1);
+            }
             break;
         case 'I': // IO Stat timeout
             //-------------------------------------------------------
             mIOStatTimeout = atoi(optarg);
             if (0 > mIOStatTimeout) {
-                std::cerr << "--iostat ERROR: negative timeout." << endl;
                 printUsage();
+                std::cerr << "--iostat ERROR: negative timeout." << endl;
                 std::exit(1);
             }
             break;
         case 'G': // IO Stat log file
             //-------------------------------------------------------
-            mIOStatStream.open(optarg);
-            if (!mIOStatStream.is_open()) {
+            mIOStatStream.reset(new std::ofstream(optarg));
+            if (!mIOStatStream->is_open()) {
+                printUsage();
                 std::cerr << "--iostatlog FAILED to open " << optarg
                           << " for writing." << endl;
+                std::exit(1);
+            }
+            break;
+        case OPT_BUFSTRATEGY: // Buf strategy
+            mBufferStrategy = atoi(optarg);
+            if (-1 > mBufferStrategy || 2 < mBufferStrategy) {
+                std::cerr << "Unsupported buffer strategy " << optarg << endl;
                 printUsage();
                 std::exit(1);
             }
             break;
+        case OPT_SIMLOSS: // Simulate packet loss
+            mSimulatedLossRate = atof(optarg);
+            break;
+        case OPT_SIMJITTER: // Simulate jitter
+            char* endp;
+            mSimulatedJitterRate = strtod(optarg, &endp);
+            if (0 == *endp) {
+                mSimulatedDelayRel = 1.0;
+            }
+            else {
+                mSimulatedDelayRel = atof(endp+1);
+            }
+            break;
+        case OPT_BROADCAST: // Broadcast output
+            mBroadcastQueue = atoi(optarg);
+            break;
+        case OPT_RTUDPPRIORITY: // Use RT priority for UDPDataProtocol thread
+            mUseRtUdpPriority = true;
+            break;
         case 'h':
             //-------------------------------------------------------
             printUsage();
             std::exit(0);
             break;
-        default:
+        case 'O': { // Overflow limiter (i, o, or io)
+          //-------------------------------------------------------
+          char cmd[] { "--overflowlimiting (-O)" };
+          if (gVerboseFlag) {
+            printf("%s argument = %s\n",cmd,optarg);
+          }
+          int returnCode = mEffects.parseLimiterOptArg(cmd,optarg);
+          if (returnCode > 1) {
+            mEffects.printHelp(cmd,ch);
+            std::cerr << cmd << " required argument `" << optarg << "' is malformed\n";
+            std::exit(1);
+          } else if (returnCode == 1) {
+            std::exit(0); // benign but not continuing such as "help"
+          }
+          break; }
+        case 'a': { // assumed number of clients (applies to outgoing limiter)
+          //-------------------------------------------------------
+          char cmd[] { "--assumednumclients (-a)" };
+          if (gVerboseFlag) {
+            printf("%s argument = %s\n",cmd,optarg);
+          }
+          int returnCode = mEffects.parseAssumedNumClientsOptArg(cmd,optarg);
+          if (returnCode > 1) {
+            mEffects.printHelp(cmd,ch);
+            std::cerr << cmd << " required argument `" << optarg << "' is malformed\n";
+            std::exit(1);
+          } else if (returnCode == 1) {
+            std::exit(0); // help printed
+          }
+          break; }
+        case 'f': { // --effects (-f) effectsSpecArg
+          //-------------------------------------------------------
+          char cmd[] { "--effects (-f)" };
+          int returnCode = mEffects.parseEffectsOptArg(cmd,optarg);
+          if (returnCode > 1) {
+            mEffects.printHelp(cmd,ch);
+            std::cerr << cmd << " required argument `" << optarg << "' is malformed\n";
+            std::exit(1);
+          } else if (returnCode == 1) {
+            std::exit(0); // something benign but non-continuing like "help"
+          }
+          break; }
+        case 'x': { // examine connection (test mode)
+          //-------------------------------------------------------
+          char cmd[] { "--examine-audio-delay (-x)" };
+          if (tolower(optarg[0])=='h') {
+            mAudioTester.printHelp(cmd,ch);
+            std::exit(0);
+          }
+          mAudioTester.setEnabled(true);
+          if (optarg == 0 || optarg[0] == '-' || optarg[0] == 0) { // happens when no -f argument specified
+            printUsage();
+            std::cerr << cmd << " ERROR: Print-interval argument REQUIRED (set to 0.0 to see every delay)\n";
+            std::exit(1);
+          }
+          mAudioTester.setPrintIntervalSec(atof(optarg));
+          break; }
+        case ':': {
+          printUsage();
+          printf("*** Missing option argument *** see above for usage\n\n");
+          break; }
+        case '?': {
+          printUsage();
+          printf("*** Unknown, missing, or ambiguous option argument *** see above for usage\n\n");
+          std::exit(1);
+          break; }
+        default: {
             //-------------------------------------------------------
             printUsage();
-            std::exit(0);
-            break;
+            printf("*** Unrecognized option -%c *** see above for usage\n",ch);
+            std::exit(1);
+            break; }
         }
 
     // Warn user if undefined options where entered
     //----------------------------------------------------------------------------
     if (optind < argc) {
+      if (strcmp(argv[optind],"help")!=0) {
         cout << gPrintSeparator << endl;
-        cout << "WARINING: The following entered options have no effect." << endl;
-        cout << "          They will be ignored!" << endl;
-        cout << "          Type 'jacktrip' to see options." << endl;
+        cout << "*** Unexpected command-line argument(s): ";
         for( ; optind < argc; optind++) {
-            cout << "argument: " << argv[optind] << endl;
+          cout << argv[optind] << " ";
         }
-        cout << gPrintSeparator << endl;
+        cout << endl << gPrintSeparator << endl;
+      }
+      printUsage();
+      std::exit(1);
     }
-}
 
+    assert(mNumChans>0);
+    mAudioTester.setSendChannel(mNumChans-1); // use last channel for latency testing
+    // Originally, testing only in the last channel was adopted
+    // because channel 0 ("left") was a clap track on CCRMA loopback
+    // servers.  Now, however, we also do it in order to easily keep
+    // effects in all but the last channel, enabling silent testing
+    // in the last channel in parallel with normal operation of the others.
+
+    // Exit if options are incompatible
+    //----------------------------------------------------------------------------
+    bool haveSomeServerMode = not ((mJackTripMode == JackTrip::CLIENT) || (mJackTripMode == JackTrip::CLIENTTOPINGSERVER));
+    if (mEffects.getHaveEffect() && haveSomeServerMode) {
+      std::cerr << "*** --effects (-f) ERROR: Effects not yet supported server modes (-S and -s).\n\n";
+      std::exit(1);
+    }
+    if (mEffects.getHaveLimiter() && haveSomeServerMode) {
+      if (mEffects.getLimit() != Effects::LIMITER_MODE::LIMITER_OUTGOING) { // default case
+        std::cerr << "*** --overflowlimiting (-O) ERROR: Limiters not yet supported server modes (-S and -s).\n\n";
+      }
+      mEffects.setNoLimiters();
+      // don't exit since an outgoing limiter should be the default (could exit for incoming case):
+      // std::exit(1);
+    }
+    if (mAudioTester.getEnabled() && haveSomeServerMode) {
+      std::cerr << "*** --examine-audio-delay (-x) ERROR: Audio latency measurement not supported in server modes (-S and -s)\n\n";
+      std::exit(1);
+    }
+    if (mAudioTester.getEnabled()
+        && (mAudioBitResolution != AudioInterface::BIT16)
+        && (mAudioBitResolution != AudioInterface::BIT32) ) { // BIT32 not tested but should be ok
+      // BIT24 should work also, but there's a comment saying it's broken right now, so exclude it
+      std::cerr << "*** --examine-audio-delay (-x) ERROR: Only --bitres (-b) 16 and 32 presently supported for audio latency measurement.\n\n";
+      std::exit(1);
+    }
+}
 
 //*******************************************************************************
 void Settings::printUsage()
@@ -384,12 +556,12 @@ void Settings::printUsage()
     cout << "SoundWIRE group at CCRMA, Stanford University" << endl;
     cout << "VERSION: " << gVersion << endl;
     cout << "" << endl;
-    cout << "Usage: jacktrip [-s|-c host] [options]" << endl;
+    cout << "Usage: jacktrip [-s|-c|-S|-C hostIPAddressOrURL] [options]" << endl;
     cout << "" << endl;
     cout << "Options: " << endl;
-    cout << "REQUIRED ARGUMENTS: " << endl;
-    cout << " -s, --server                             Run in Server Mode" << endl;
-    cout << " -c, --client <peer_hostname_or_IP_num>   Run in Client Mode" << endl;
+    cout << "REQUIRED ARGUMENTS: One of:" << endl;
+    cout << " -s, --server                             Run in P2P Server Mode" << endl;
+    cout << " -c, --client <peer_hostname_or_IP_num>   Run in P2P Client Mode" << endl;
     cout << " -S, --jacktripserver                     Run in Hub Server Mode" << endl;
     cout << " -C, --pingtoserver <peer_name_or_IP>     Run in Hub Client Mode" << endl;
     cout << endl;
@@ -400,7 +572,7 @@ void Settings::printUsage()
     cout << " -w, --wair                               Run in WAIR Mode" << endl;
     cout << " -N, --addcombfilterlength #              comb length adjustment for WAIR (default "
          << gDefaultAddCombFilterLength << ")" << endl;
-    cout << " -H, --combfilterfeedback #               comb feedback adjustment for WAIR (default "
+    cout << " -H, --combfilterfeedback # (roomSize)    comb feedback adjustment for WAIR (default "
          << gDefaultCombFilterFeedback << ")" << endl;
 #endif // endwhere
     cout << " -q, --queue       # (2 or more)          Queue Buffer Length, in Packet Size (default: "
@@ -411,24 +583,41 @@ void Settings::printUsage()
     cout << " -B, --bindport        #                  Set only the bind port number (default: " << gDefaultPort << ")" << endl;
     cout << " -P, --peerport        #                  Set only the peer port number (default: " << gDefaultPort << ")" << endl;
     cout << " -U, --udpbaseport                        Set only the server udp base port number (default: 61002)" << endl;
-    cout << " -b, --bitres      # (8, 16, 24, 32)      Audio Bit Rate Resolutions (default: 16)" << endl;
-    cout << " -p, --hubpatch    # (0, 1, 2, 3, 4)      Hub auto audio patch, only has effect if running HUB SERVER mode, 0=server-to-clients, 1=client loopback, 2=clients can hear all clients except themselves, 3=reserved for TUB, 4=full mix (default: 0), i.e. clients auto-connect and hear all clients including themselves" << endl;
+    cout << " -b, --bitres      # (8, 16, 24, 32)      Audio Bit Rate Resolutions (default: 16, 32 uses floating-point)" << endl;
+    cout << " -p, --hubpatch    # (0, 1, 2, 3, 4, 5)   Hub auto audio patch, only has effect if running HUB SERVER mode, 0=server-to-clients, 1=client loopback, 2=client fan out/in but not loopback, 3=reserved for TUB, 4=full mix, 5=no auto patching (default: 0)" << endl;
     cout << " -z, --zerounderrun                       Set buffer to zeros when underrun occurs (default: wavetable)" << endl;
+    cout << " -t, --timeout                            Quit after 10 seconds of no network activity" << endl;
     cout << " -l, --loopback                           Run in Loop-Back Mode" << endl;
     cout << " -j, --jamlink                            Run in JamLink Mode (Connect to a JamLink Box)" << endl;
-    cout << " --clientname                             Change default client name (default: JackTrip)" << endl;
-    cout << " --localaddress                           Change default local host IP address (default: 127.0.0.1)" << endl;
-    cout << " --nojackportsconnect                     Don't connect default audio ports in jack, including not doing hub auto audio patch in HUB SERVER mode." << endl;
+    cout << " -J, --clientname                         Change default client name (default: JackTrip)" << endl;
+    cout << " -K, --remotename                         Change default remote client name when connecting to a hub server (the default is derived from this computer's external facing IP address)" << endl;
+    cout << " -L, --localaddress                       Change default local host IP address (default: 127.0.0.1)" << endl;
+    cout << " -D, --nojackportsconnect                 Don't connect default audio ports in jack" << endl;
+    cout << " --bufstrategy     # (0, 1, 2)            Use alternative jitter buffer" << endl;
+    cout << " --broadcast <broadcast_queue>            Turn on broadcast output ports with extra queue (requires new jitter buffer)" << endl;
+    cout << " --udprt                                  Use RT thread priority for network I/O" << endl;
+    cout << endl;
+    cout << "OPTIONAL SIGNAL PROCESSING: " << endl;
+    cout << " -f, --effects # | paramString | help     Turn on incoming and/or outgoing compressor and/or reverb in Client - see `-f help' for details" << endl;
+    cout << " -O, --overflowlimiting i|o[w]|io[w]|n|help" << endl;
+    cout << "                                          Use audio limiter(s) in Client, i=incoming from network, o=outgoing to network, io=both, n=no limiters, w=warn if limiting (default=n). Say -O help for more." << endl;
+    cout << " -a, --assumednumclients help|# (1,2,...) Assumed number of Clients (sources) mixing at Hub Server (otherwise 2 assumed by -O)" << endl;
     cout << endl;
     cout << "ARGUMENTS TO USE JACKTRIP WITHOUT JACK:" << endl;
-    cout << " --rtaudio                                Use system's default sound system instead of Jack" << endl;
-    cout << "   --srate         #                      Set the sampling rate, works on --rtaudio mode only (default: 48000)" << endl;
-    cout << "   --bufsize       #                      Set the buffer size, works on --rtaudio mode only (default: 128)" << endl;
-    cout << "   --deviceid      #                      The rtaudio device id --rtaudio mode only (default: 0)" << endl;
+    cout << " -R, --rtaudio                            Use system's default sound system instead of Jack" << endl;
+    cout << " -T, --srate         #                    Set the sampling rate, works on --rtaudio mode only (default: 48000)" << endl;
+    cout << " -F, --bufsize       #                    Set the buffer size, works on --rtaudio mode only (default: 128)" << endl;
+    cout << " -d, --deviceid      #                    The rtaudio device id --rtaudio mode only (default: 0)" << endl;
     cout << endl;
     cout << "ARGUMENTS TO DISPLAY IO STATISTICS:" << endl;
     cout << " -I, --iostat <time_in_secs>              Turn on IO stat reporting with specified interval (in seconds)" << endl;
     cout << " -G, --iostatlog <log_file>               Save stat log into a file (default: print in stdout)" << endl;
+    cout << " -x, --examine-audio-delay <print_interval_in_secs> | help\n";
+    cout << "                                          Print round-trip audio delay statistics. See `-x help' for details." << endl;
+    cout << endl;
+    cout << "ARGUMENTS TO SIMULATE NETWORK ISSUES:" << endl;
+    cout << " --simloss <rate>                         Simulate packet loss" << endl;
+    cout << " --simjitter <rate>,<d>                   Simulate jitter, d is max delay in packets" << endl;
     cout << endl;
     cout << "HELP ARGUMENTS: " << endl;
     cout << " -v, --version                            Prints Version Number" << endl;
@@ -439,211 +628,208 @@ void Settings::printUsage()
 
 
 //*******************************************************************************
-void Settings::startJackTrip()
+UdpHubListener *Settings::getConfiguredHubServer()
 {
-
     if ((mBindPortNum < gBindPortLow) || (mBindPortNum > gBindPortHigh))
         std::cout << "BindPort: "<< mBindPortNum << " outside range"  << std::endl;
 
-    /// \todo Change this, just here to test
-    if ( mJackTripServer ) {
-        if (gVerboseFlag) std::cout << "JackTrip HUB SERVER TCP Bind Port: " << mBindPortNum << std::endl;
-        UdpHubListener* udpmaster = new UdpHubListener(mBindPortNum,mServerUdpPortNum);
-        udpmaster->setSettings(this);
+    if (gVerboseFlag) std::cout << "JackTrip HUB SERVER TCP Bind Port: " << mBindPortNum << std::endl;
+    UdpHubListener *udpHub = new UdpHubListener(mBindPortNum, mServerUdpPortNum);
+    //udpHub->setSettings(this);
 #ifdef WAIR // WAIR
-        udpmaster->setWAIR(mWAIR);
+    udpHub->setWAIR(mWAIR);
 #endif // endwhere
-        udpmaster->setHubPatch(mHubConnectionMode);
-        udpmaster->setConnectDefaultAudioPorts(mConnectDefaultAudioPorts);
-        if (gVerboseFlag) std::cout << "Settings:startJackTrip before udpmaster->start" << std::endl;
-        // Set buffers to zero when underrun
-        if ( mUnderrrunZero ) {
-            cout << "Setting buffers to zero when underrun..." << endl;
-            cout << gPrintSeparator << std::endl;
-            udpmaster->setUnderRunMode(JackTrip::ZEROS);
-        }
-        udpmaster->setBufferQueueLength(mBufferQueueLength);
-        udpmaster->start();
-
-        //---Thread Pool Test--------------------------------------------
-        /*
-    cout << "BEFORE START" << endl;
-    ThreadPoolTest* thtest = new ThreadPoolTest();
-    // QThreadPool takes ownership and deletes 'hello' automatically
-    QThreadPool::globalInstance()->start(thtest);
-
-    cout << "AFTER START" << endl;
-    sleep(2);
-    thtest->stop();
-    QThreadPool::globalInstance()->waitForDone();
-    */
-        //---------------------------------------------------------------
+    udpHub->setHubPatch(mHubConnectionMode);
+    if (mHubConnectionMode == JackTrip::NOAUTO) {
+        udpHub->setConnectDefaultAudioPorts(false);
+    } else {
+        udpHub->setConnectDefaultAudioPorts(mConnectDefaultAudioPorts);
     }
+    // Set buffers to zero when underrun
+    if ( mUnderrunMode == JackTrip::ZEROS ) {
+        cout << "Setting buffers to zero when underrun..." << endl;
+        cout << gPrintSeparator << std::endl;
+        udpHub->setUnderRunMode(mUnderrunMode);
+    }
+    udpHub->setBufferQueueLength(mBufferQueueLength);
+
+    udpHub->setBufferStrategy(mBufferStrategy);
+    udpHub->setNetIssuesSimulation(mSimulatedLossRate,
+        mSimulatedJitterRate, mSimulatedDelayRel);
+    udpHub->setBroadcast(mBroadcastQueue);
+    udpHub->setUseRtUdpPriority(mUseRtUdpPriority);
+    
+    if (mIOStatTimeout > 0) {
+        udpHub->setIOStatTimeout(mIOStatTimeout);
+        udpHub->setIOStatStream(mIOStatStream);
+    }
+    return udpHub;
+}
 
-    else {
-
-        //JackTrip jacktrip(mJackTripMode, mDataProtocol, mNumChans,
-        //         mBufferQueueLength, mAudioBitResolution);
+JackTrip *Settings::getConfiguredJackTrip()
+{
 #ifdef WAIR // WAIR
-        if (gVerboseFlag) std::cout << "Settings:startJackTrip mNumNetRevChans = " << mNumNetRevChans << std::endl;
+    if (gVerboseFlag) std::cout << "Settings:startJackTrip mNumNetRevChans = " << mNumNetRevChans << std::endl;
 #endif // endwhere
-        if (gVerboseFlag) std::cout << "Settings:startJackTrip before new JackTrip" << std::endl;
-        mJackTrip = new JackTrip(mJackTripMode, mDataProtocol, mNumChans,
-                         #ifdef WAIR // wair
-                                 mNumNetRevChans,
-                         #endif // endwhere
-                                 mBufferQueueLength, mRedundancy, mAudioBitResolution,
-                                 /*DataProtocol::packetHeaderTypeT PacketHeaderType = */DataProtocol::DEFAULT,
-                                 /*underrunModeT UnderRunMode = */ mUnderRunMode,
-                                 /* int receiver_bind_port = */ gDefaultPort,
-                                 /*int sender_bind_port = */ gDefaultPort,
-                                 /*int receiver_peer_port = */ gDefaultPort,
-                                 /* int sender_peer_port = */ gDefaultPort,
-                                 mPeerPortNum
-                                 );
-
-        // Set connect or not default audio ports. Only work for jack
-        mJackTrip->setConnectDefaultAudioPorts(mConnectDefaultAudioPorts);
-
-        // Connect Signals and Slots
-        QObject::connect(mJackTrip, SIGNAL( signalProcessesStopped() ),
-                         this, SLOT( slotExitProgram() ));
-
-        // Change client name if different from default
-        if (mClientName != NULL) {
-            mJackTrip->setClientName(mClientName);
-        }
+    if (gVerboseFlag) std::cout << "Settings:startJackTrip before new JackTrip" << std::endl;
+    JackTrip *jackTrip = new JackTrip(mJackTripMode, mDataProtocol, mNumChans,
+#ifdef WAIR // wair
+                                      mNumNetRevChans,
+#endif // endwhere
+                                      mBufferQueueLength, mRedundancy, mAudioBitResolution,
+                                      /*DataProtocol::packetHeaderTypeT PacketHeaderType = */DataProtocol::DEFAULT,
+                                      /*underrunModeT UnderRunMode = */ mUnderrunMode,
+                                      /* int receiver_bind_port = */ mBindPortNum,
+                                      /*int sender_bind_port = */ mBindPortNum,
+                                      /*int receiver_peer_port = */ mPeerPortNum,
+                                      /* int sender_peer_port = */ mPeerPortNum,
+                                      mPeerPortNum
+                                      );
+    // Set connect or not default audio ports. Only work for jack
+    jackTrip->setConnectDefaultAudioPorts(mConnectDefaultAudioPorts);
+
+    // Change client name if different from default
+    if (!mClientName.isEmpty()) {
+        jackTrip->setClientName(mClientName);
+    }
 
-        // Set buffers to zero when underrun
-        if ( mUnderrrunZero ) {
-            cout << "Setting buffers to zero when underrun..." << endl;
-            cout << gPrintSeparator << std::endl;
-            mJackTrip->setUnderRunMode(JackTrip::ZEROS);
-        } else {
-            cout << "Setting buffers to wavetable when underrun..." << endl;
-            cout << gPrintSeparator << std::endl;
-            mJackTrip->setUnderRunMode(JackTrip::WAVETABLE);
-        }
+    if (!mRemoteClientName.isEmpty() && (mJackTripMode == JackTrip::CLIENTTOPINGSERVER)) {
+        jackTrip->setRemoteClientName(mRemoteClientName);
+    }
 
-        // Set peer address in server mode
-        if ( mJackTripMode == JackTrip::CLIENT || mJackTripMode == JackTrip::CLIENTTOPINGSERVER ) {
-            mJackTrip->setPeerAddress(mPeerAddress.toLatin1().data()); }
-
-        //        if(mLocalAddress!=QString()) // default
-        //            mJackTrip->setLocalAddress(QHostAddress(mLocalAddress.toLatin1().data()));
-        //        else
-        //            mJackTrip->setLocalAddress(QHostAddress::Any);
-
-        // Set Ports
-        //cout << "SETTING ALL PORTS" << endl;
-        if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->setBindPorts" << std::endl;
-        mJackTrip->setBindPorts(mBindPortNum);
-        if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->setPeerPorts" << std::endl;
-        mJackTrip->setPeerPorts(mPeerPortNum);
-
-        // Set in JamLink Mode
-        if ( mJamLink ) {
-            cout << "Running in JamLink Mode..." << endl;
-            cout << gPrintSeparator << std::endl;
-            mJackTrip->setPacketHeaderType(DataProtocol::JAMLINK);
-        }
+    // Set buffers to zero when underrun (Actual setting is handled in constructor.)
+    if (mUnderrunMode == JackTrip::ZEROS) {
+        cout << "Setting buffers to zero when underrun..." << endl;
+        cout << gPrintSeparator << std::endl;
+    }
 
-        // Set in EmptyHeader Mode
-        if ( mEmptyHeader ) {
-            cout << "Running in EmptyHeader Mode..." << endl;
-            cout << gPrintSeparator << std::endl;
-            mJackTrip->setPacketHeaderType(DataProtocol::EMPTY);
-        }
+    jackTrip->setStopOnTimeout(mStopOnTimeout);
+
+    // Set peer address in server mode
+    if (mJackTripMode == JackTrip::CLIENT || mJackTripMode == JackTrip::CLIENTTOPINGSERVER) {
+        jackTrip->setPeerAddress(mPeerAddress); }
+
+    //        if(mLocalAddress!=QString()) // default
+    //            mJackTrip->setLocalAddress(QHostAddress(mLocalAddress.toLatin1().data()));
+    //        else
+    //            mJackTrip->setLocalAddress(QHostAddress::Any);
+
+    // Set Ports - Done in constructor now.
+    //cout << "SETTING ALL PORTS" << endl;
+    /*if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->setBindPorts" << std::endl;
+    jackTrip->setBindPorts(mBindPortNum);
+    if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->setPeerPorts" << std::endl;
+    jackTrip->setPeerPorts(mPeerPortNum);*/
+
+    // Set in JamLink Mode
+    if ( mJamLink ) {
+        cout << "Running in JamLink Mode..." << endl;
+        cout << gPrintSeparator << std::endl;
+        jackTrip->setPacketHeaderType(DataProtocol::JAMLINK);
+    }
 
-        // Set RtAudio
+    // Set in EmptyHeader Mode
+    if (mEmptyHeader) {
+        cout << "Running in EmptyHeader Mode..." << endl;
+        cout << gPrintSeparator << std::endl;
+        jackTrip->setPacketHeaderType(DataProtocol::EMPTY);
+    }
+
+    // Set RtAudio
 #ifdef __RT_AUDIO__
-        if (!mUseJack) {
-            mJackTrip->setAudiointerfaceMode(JackTrip::RTAUDIO);
-        }
+    if (!mUseJack) {
+        mJackTrip->setAudiointerfaceMode(JackTrip::RTAUDIO);
+    }
 #endif
 
-        // Chanfe default Sampling Rate
-        if (mChanfeDefaultSR) {
-            mJackTrip->setSampleRate(mSampleRate);
-        }
+    // Chanfe default Sampling Rate
+    if (mChanfeDefaultSR) {
+        jackTrip->setSampleRate(mSampleRate);
+    }
 
-        // Chanfe defualt device ID
-        if (mChanfeDefaultID) {
-            mJackTrip->setDeviceID(mDeviceID);
-        }
+    // Chanfe defualt device ID
+    if (mChanfeDefaultID) {
+        jackTrip->setDeviceID(mDeviceID);
+    }
 
-        // Chanfe default Buffer Size
-        if (mChanfeDefaultBS) {
-            mJackTrip->setAudioBufferSizeInSamples(mAudioBufferSize);
-        }
+    // Chanfe default Buffer Size
+    if (mChanfeDefaultBS) {
+        jackTrip->setAudioBufferSizeInSamples(mAudioBufferSize);
+    }
+    jackTrip->setBufferStrategy(mBufferStrategy);
+    jackTrip->setNetIssuesSimulation(mSimulatedLossRate,
+        mSimulatedJitterRate, mSimulatedDelayRel);
+    jackTrip->setBroadcast(mBroadcastQueue);
+    jackTrip->setUseRtUdpPriority(mUseRtUdpPriority);
+
+    // Add Plugins
+    if (mLoopBack) {
+        cout << "Running in Loop-Back Mode..." << endl;
+        cout << gPrintSeparator << std::endl;
+        //std::tr1::shared_ptr<LoopBack> loopback(new LoopBack(mNumChans));
+        //mJackTrip->appendProcessPlugin(loopback.get());
+
+#if 0 // previous technique:
+        LoopBack* loopback = new LoopBack(mNumChans);
+        jackTrip->appendProcessPlugin(loopback);
+#else // simpler method ( see AudioInterface.cpp callback() ):
+        jackTrip->setLoopBack(true);
+#endif
 
-        // Add Plugins
-        if ( mLoopBack ) {
-            cout << "Running in Loop-Back Mode..." << endl;
-            cout << gPrintSeparator << std::endl;
-            //std::tr1::shared_ptr<LoopBack> loopback(new LoopBack(mNumChans));
-            //mJackTrip->appendProcessPlugin(loopback.get());
-
-            LoopBack* loopback = new LoopBack(mNumChans);
-            mJackTrip->appendProcessPlugin(loopback);
-
-            // ----- Test Karplus Strong -----------------------------------
-            //std::tr1::shared_ptr<NetKS> loopback(new NetKS());
-            //mJackTrip->appendProcessPlugin(loopback);
-            //loopback->play();
-            //NetKS* netks = new NetKS;
-            //mJackTrip->appendProcessPlugin(netks);
-            //netks->play();
-            // -------------------------------------------------------------
-        }
+        // ----- Test Karplus Strong -----------------------------------
+        //std::tr1::shared_ptr<NetKS> loopback(new NetKS());
+        //mJackTrip->appendProcessPlugin(loopback);
+        //loopback->play();
+        //NetKS* netks = new NetKS;
+        //mJackTrip->appendProcessPlugin(netks);
+        //netks->play();
+        // -------------------------------------------------------------
+    }
+
+    if (mIOStatTimeout > 0) {
+        jackTrip->setIOStatTimeout(mIOStatTimeout);
+        jackTrip->setIOStatStream(mIOStatStream);
+    }
+
+    jackTrip->setAudioTesterP(&mAudioTester);
+
+    // Allocate audio effects in client, if any:
+    int nReservedChans = mAudioTester.getEnabled() ? 1 : 0; // no fx allowed on tester channel
+    std::vector<ProcessPlugin*> outgoingEffects = mEffects.allocateOutgoingEffects(mNumChans-nReservedChans);
+    for (auto p : outgoingEffects) {
+      jackTrip->appendProcessPluginToNetwork( p );
+    }
+    std::vector<ProcessPlugin*> incomingEffects = mEffects.allocateIncomingEffects(mNumChans-nReservedChans);
+    for (auto p : incomingEffects) {
+      jackTrip->appendProcessPluginFromNetwork( p );
+    }
 
 #ifdef WAIR // WAIR
-        if ( mWAIR ) {
-            cout << "Running in WAIR Mode..." << endl;
-            cout << gPrintSeparator << std::endl;
-            switch ( mNumNetRevChans )
-            {
-            case 16 :
-            {
-                mJackTrip->appendProcessPlugin(new ap8x2(mNumChans)); // plugin slot 0
-                /////////////////////////////////////////////////////////
-                Stk16* plugin = new Stk16(mNumNetRevChans);
-                plugin->Stk16::initCombClient(mClientAddCombLen, mClientRoomSize);
-                mJackTrip->appendProcessPlugin(plugin); // plugin slot 1
-            }
-                break;
-            default:
-                throw std::invalid_argument("Settings: mNumNetRevChans doesn't correspond to Faust plugin");
-                break;
-            }
+    if ( mWAIR ) {
+        cout << "Running in WAIR Mode..." << endl;
+        cout << gPrintSeparator << std::endl;
+        switch ( mNumNetRevChans )
+        {
+        case 16 :
+        {
+            jackTrip->appendProcessPluginFromNetwork(new ap8x2(mNumChans)); // plugin slot 0
+            /////////////////////////////////////////////////////////
+            Stk16* plugin = new Stk16(mNumNetRevChans);
+            plugin->Stk16::initCombClient(mClientAddCombLen, mClientRoomSize);
+            jackTrip->appendProcessPluginFromNetwork(plugin); // plugin slot 1
         }
-#endif // endwhere
-
-        // Start JackTrip
-        if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->startProcess" << std::endl;
-        mJackTrip->startProcess(
-            #ifdef WAIRTOHUB // WAIR
-                    0 // for WAIR compatibility, ID in jack client name
-            #endif // endwhere
-                    );
-        if (0 < getIOStatTimeout()) {
-            mJackTrip->startIOStatTimer(getIOStatTimeout(), getIOStatStream());
+            break;
+        default:
+            throw std::invalid_argument("Settings: mNumNetRevChans doesn't correspond to Faust plugin");
+            break;
+        }
+            break;
+        default:
+            throw std::invalid_argument("Settings: mNumNetRevChans doesn't correspond to Faust plugin");
+            break;
         }
-        //        if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->start" << std::endl;
-        // this is a noop
-        //        mJackTrip->start();
-
-        /*
-       sleep(10);
-       cout << "Stoping JackTrip..." << endl;
-       mJackTrip->stop();
-    */
     }
-}
-
+#endif // endwhere
 
-//*******************************************************************************
-void Settings::stopJackTrip()
-{
-    mJackTrip->stop();
+    return jackTrip;
 }
index 9856bf22d1936402a9f324091cd22bcf96be3d0d..569b451d4a7562de5a81409434f22f41e934d65e 100644 (file)
@@ -41,6 +41,7 @@
 
 #include <cstdlib>
 #include <fstream>
+#include <vector>
 
 #include "DataProtocol.h"
 
 #endif //__NO_JACK__
 
 #include "JackTrip.h"
+#include "UdpHubListener.h"
+
+#include "Effects.h"
+#include "AudioTester.h"
 
 /** \brief Class to set usage options and parse settings from input
  */
-class Settings : public QThread
+class Settings : public QObject
 {
     Q_OBJECT;
 
@@ -63,29 +68,16 @@ public:
     /// \brief Parses command line input
     void parseInput(int argc, char** argv);
 
-    void startJackTrip();
-    void stopJackTrip();
+    UdpHubListener *getConfiguredHubServer();
+    JackTrip *getConfiguredJackTrip();
 
     /// \brief Prints usage help
     void printUsage();
 
     bool getLoopBack() { return mLoopBack; }
-    int getIOStatTimeout() const {return mIOStatTimeout;}
-    const std::ostream& getIOStatStream() const
-    {
-        return mIOStatStream.is_open() ? (std::ostream&)mIOStatStream : std::cout;
-    }
-
-
-public slots:
-    void slotExitProgram()
-    {
-        std::cerr << "Exiting JackTrip..." << std::endl;
-        std::exit(1);
-    }
+    bool isHubServer() { return mJackTripServer; }
 
 private:
-    JackTrip* mJackTrip; ///< JackTrip class
     JackTrip::jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT
     JackTrip::dataProtocolT mDataProtocol; ///< Data Protocol
     int mNumChans; ///< Number of Channels (inputs = outputs)
@@ -95,9 +87,11 @@ private:
     int mBindPortNum; ///< Bind Port Number
     int mPeerPortNum; ///< Peer Port Number
     int mServerUdpPortNum;
-    char* mClientName; ///< JackClient Name
-    bool mUnderrrunZero; ///< Use Underrun to Zero mode
-    JackTrip::underrunModeT mUnderRunMode;
+    QString mClientName; ///< JackClient Name
+    QString mRemoteClientName;
+    JackTrip::underrunModeT mUnderrunMode; ///< Underrun mode
+    bool mStopOnTimeout; /// < Stop jacktrip after 10 second network timeout
+    int mBufferStrategy;
 
 #ifdef WAIR // wair
     int mNumNetRevChans; ///< Number of Network Audio Channels (net comb filters)
@@ -122,7 +116,14 @@ private:
     unsigned int mHubConnectionMode;
     bool mConnectDefaultAudioPorts; ///< Connect or not jack audio ports
     int mIOStatTimeout;
-    std::ofstream mIOStatStream;
+    QSharedPointer<std::ofstream> mIOStatStream;
+    Effects mEffects;
+    double mSimulatedLossRate;
+    double mSimulatedJitterRate;
+    double mSimulatedDelayRel;
+    int mBroadcastQueue;
+    bool mUseRtUdpPriority;
+    AudioTester mAudioTester;
 };
 
 #endif
index 85738a6dc6934325778820b37bff5f04d2f2cd98..3680185f0291c01952daa85eb5da330a0aefc817 100644 (file)
@@ -53,6 +53,8 @@
 #endif
 #if defined (__LINUX__) || (__MAC_OSX__)
 #include <sys/socket.h> // for POSIX Sockets
+#include <unistd.h>
+#include <sys/fcntl.h>
 #endif
 
 using std::cout; using std::endl;
@@ -72,7 +74,9 @@ UdpDataProtocol::UdpDataProtocol(JackTrip* jacktrip, const runModeT runmode,
     mBindPort(bind_port), mPeerPort(peer_port),
     mRunMode(runmode),
     mAudioPacket(NULL), mFullPacket(NULL),
-    mUdpRedundancyFactor(udp_redundancy_factor)
+    mUdpRedundancyFactor(udp_redundancy_factor),
+    mControlPacketSize(63),
+    mStopSignalSent(false)
 {
     mStopped = false;
     mIPv6 = false;
@@ -85,6 +89,9 @@ UdpDataProtocol::UdpDataProtocol(JackTrip* jacktrip, const runModeT runmode,
         QObject::connect(this, SIGNAL(signalWaitingTooLong(int)),
                          jacktrip, SLOT(slotUdpWaitingTooLongClientGoneProbably(int)), Qt::QueuedConnection);
     }
+    mSimulatedLossRate = 0.0;
+    mSimulatedJitterRate = 0.0;
+    mSimulatedJitterMaxDelay = 0.0;
 }
 
 
@@ -93,6 +100,13 @@ UdpDataProtocol::~UdpDataProtocol()
 {
     delete[] mAudioPacket;
     delete[] mFullPacket;
+    if (mRunMode == RECEIVER) {
+#ifdef __WIN_32__
+        closesocket(mSocket);
+#else
+        ::close(mSocket);
+#endif
+    }
     wait();
 }
 
@@ -121,7 +135,7 @@ void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP)
     if ( mPeerAddress.protocol() == QAbstractSocket::IPv6Protocol ) {
         mIPv6 = true;
     } else  if ( mPeerAddress.protocol() != QAbstractSocket::IPv4Protocol ) {
-        QString error_message = "Incorrect presentation format address\n '";
+        QString error_message = "Incorrect presentation format address\n'";
         error_message.append(peerHostOrIP);
         error_message.append("' is not a valid IP address or Host Name");
         //std::cerr << "ERROR: Incorrect presentation format address" << endl;
@@ -163,7 +177,7 @@ void UdpDataProtocol::setSocket(int &socket)
     if (socket == -1) {
 #endif
         try {
-            if (gVerboseFlag) std::cout << "    UdpDataProtocol:run" << mRunMode << " before bindSocket(UdpSocket)" << std::endl;
+            if (gVerboseFlag) std::cout << "    UdpDataProtocol:run" << mRunMode << " before bindSocket" << std::endl;
             socket = bindSocket(); // Bind Socket
         } catch ( const std::exception & e ) {
             emit signalError( e.what() );
@@ -316,11 +330,29 @@ int UdpDataProtocol::bindSocket()
 
 
 //*******************************************************************************
-int UdpDataProtocol::receivePacket(QUdpSocket& UdpSocket, char* buf, const size_t n)
+int UdpDataProtocol::receivePacket(char* buf, const size_t n)
 {
     // Block until There's something to read
-    while ( (UdpSocket.pendingDatagramSize() < n) && !mStopped ) { QThread::usleep(100); }
-    int n_bytes = UdpSocket.readDatagram(buf, n);
+    while ( !datagramAvailable() && !mStopped ) {
+        QThread::usleep(100);
+    }
+    int n_bytes = ::recv(mSocket, buf, n, 0);
+    if (n_bytes == mControlPacketSize) {
+        //Control signal (currently just check for exit packet);
+        bool exit = true;
+        for (int i = 0; i < mControlPacketSize; i++) {
+            if (buf[i] != char(0xff)) {
+                exit = false;
+                i = mControlPacketSize;
+            }
+        }
+        if (exit && !mStopSignalSent) {
+            mStopSignalSent = true;
+            emit signalCeaseTransmission("Peer Stopped");
+            std::cout << "Peer Stopped" <<std::endl;
+        }
+        return 0;
+    }
     return n_bytes;
 }
 
@@ -358,15 +390,24 @@ int UdpDataProtocol::sendPacket(const char* buf, const size_t n)
 
 
 //*******************************************************************************
-void UdpDataProtocol::getPeerAddressFromFirstPacket(QUdpSocket& UdpSocket,
-                                                    QHostAddress& peerHostAddress,
+void UdpDataProtocol::getPeerAddressFromFirstPacket(QHostAddress& peerHostAddress,
                                                     uint16_t& port)
 {
-    while ( !UdpSocket.hasPendingDatagrams() ) {
+    while ( !datagramAvailable() ) {
         msleep(100);
     }
     char buf[1];
-    UdpSocket.readDatagram(buf, 1, &peerHostAddress, &port);
+    
+    struct sockaddr_storage addr;
+    std::memset(&addr, 0, sizeof(addr));
+    socklen_t sa_len = sizeof(addr);
+    ::recvfrom(mSocket, buf, 1, 0, (struct sockaddr*) &addr, &sa_len);
+    peerHostAddress.setAddress((struct sockaddr*) &addr);
+    if (mIPv6) {
+        port = ((struct sockaddr_in6*) &addr)->sin6_port;
+    } else {
+        port = ((struct sockaddr_in*) &addr)->sin_port;
+    }
 }
 
 
@@ -388,19 +429,17 @@ void UdpDataProtocol::run()
     //                 mJackTrip, SLOT(slotStopProcesses()),
     //                 Qt::QueuedConnection);
 
-    //Wrap our socket in a QUdpSocket object if we're the receiver, for convenience.
-    //If we're the sender, we'll just write directly to our socket.
-    QUdpSocket UdpSocket;
     if (mRunMode == RECEIVER) {
-        if (mIPv6) {
-            UdpSocket.setSocketDescriptor(mSocket, QUdpSocket::BoundState,
-                                          QUdpSocket::ReadOnly);
-        } else {
-            UdpSocket.setSocketDescriptor(mSocket, QUdpSocket::ConnectedState,
-                                          QUdpSocket::ReadOnly);
-        }
         cout << "UDP Socket Receiving in Port: " << mBindPort << endl;
         cout << gPrintSeparator << endl;
+        //Make sure our socket is in non-blocking mode.
+#ifdef __WIN_32__
+        u_long nonblock = 1;
+        ioctlsocket(mSocket, FIONBIO, &nonblock);
+#else
+        int flags = ::fcntl(mSocket, F_GETFL, 0);
+        ::fcntl(mSocket, F_SETFL, flags | O_NONBLOCK);
+#endif
     }
 
     if (gVerboseFlag) std::cout << "    UdpDataProtocol:run" << mRunMode << " before Setup Audio Packet buffer, Full Packet buffer, Redundancy Variables" << std::endl;
@@ -409,6 +448,9 @@ void UdpDataProtocol::run()
     //cout << "audio_packet_size: " << audio_packet_size << endl;
     mAudioPacket = new int8_t[audio_packet_size];
     std::memset(mAudioPacket, 0, audio_packet_size); // set buffer to 0
+    mBuffer.resize(audio_packet_size, 0);
+    mChans = mJackTrip->getNumChannels();
+    mSmplSize = mJackTrip->getAudioBitResolution() / 8;
 
     // Setup Full Packet buffer
     int full_packet_size = mJackTrip->getPacketSizeInBytes();
@@ -425,14 +467,15 @@ void UdpDataProtocol::run()
     // (Algorithm explained at the end of this file)
     // ---------------------------------------------
     int full_redundant_packet_size = full_packet_size * mUdpRedundancyFactor;
-    int8_t* full_redundant_packet;
-    full_redundant_packet = new int8_t[full_redundant_packet_size];
-    std::memset(full_redundant_packet, 0, full_redundant_packet_size); // Initialize to 0
+    int8_t* full_redundant_packet = NULL;
 
     // Set realtime priority (function in jacktrip_globals.h)
     if (gVerboseFlag) std::cout << "    UdpDataProtocol:run" << mRunMode << " before setRealtimeProcessPriority()" << std::endl;
     //std::cout << "Experimental version -- not using setRealtimeProcessPriority()" << std::endl;
-    //setRealtimeProcessPriority();
+    // Anton Runov: making setRealtimeProcessPriority optional
+    if (mUseRtPriority) {
+        setRealtimeProcessPriority();
+    }
 
     /////////////////////
     // to see thread priorities
@@ -507,24 +550,31 @@ void UdpDataProtocol::run()
         if (gVerboseFlag) std::cout << "    UdpDataProtocol:run" << mRunMode << " before !UdpSocket.hasPendingDatagrams()" << std::endl;
         std::cout << "Waiting for Peer..." << std::endl;
         // This blocks waiting for the first packet
-        while ( !UdpSocket.hasPendingDatagrams() ) {
+        while ( !datagramAvailable() ) {
             if (mStopped) { return; }
             QThread::msleep(100);
             if (gVerboseFlag) std::cout << "100ms  " << std::flush;
         }
-        int first_packet_size = UdpSocket.pendingDatagramSize();
-        // The following line is the same as
-        int8_t* first_packet = new int8_t[first_packet_size];
-        /// \todo fix this to avoid memory leaks
-        // but avoids memory leaks
-        //std::tr1::shared_ptr<int8_t> first_packet(new int8_t[first_packet_size]);
-        receivePacket( UdpSocket, reinterpret_cast<char*>(first_packet), first_packet_size);
+        full_redundant_packet_size = 0x10000;  // max UDP datagram size
+        full_redundant_packet = new int8_t[full_redundant_packet_size];
+        full_redundant_packet_size = receivePacket(reinterpret_cast<char*>(full_redundant_packet), full_redundant_packet_size);
         // Check that peer has the same audio settings
         if (gVerboseFlag) std::cout << std::endl << "    UdpDataProtocol:run" << mRunMode << " before mJackTrip->checkPeerSettings()" << std::endl;
-        mJackTrip->checkPeerSettings(first_packet);
+        mJackTrip->checkPeerSettings(full_redundant_packet);
+
+        int peer_chans = mJackTrip->getPeerNumChannels(full_redundant_packet);
+        full_packet_size = mJackTrip->getHeaderSizeInBytes()
+                           + mJackTrip->getPeerBufferSize(full_redundant_packet) * peer_chans * mSmplSize;
+        /*
+        cout << "peer sizes: " << mJackTrip->getHeaderSizeInBytes()
+             << " + " << mJackTrip->getPeerBufferSize(full_redundant_packet)
+             << " * " << mJackTrip->getNumChannels() << " * " << (int)mJackTrip->getAudioBitResolution()/8 << endl;
+        cout << "full_packet_size: " << full_packet_size << " / " << mJackTrip->getPacketSizeInBytes() << endl;
+        cout << "full_redundant_packet_size: " << full_redundant_packet_size << endl;
+        // */
+
         if (gVerboseFlag) std::cout << "step 7" << std::endl;
         if (gVerboseFlag) std::cout << "    UdpDataProtocol:run" << mRunMode << " before mJackTrip->parseAudioPacket()" << std::endl;
-        mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket);
         std::cout << "Received Connection from Peer!" << std::endl;
         emit signalReceivedConnectionFromPeer();
 
@@ -538,6 +588,8 @@ void UdpDataProtocol::run()
         mTotCount = 0;
         mLostCount = 0;
         mOutOfOrderCount = 0;
+        mLastOutOfOrderCount = 0;
+        mInitialState = true;
         mRevivedCount = 0;
         mStatCount = 0;
 
@@ -550,7 +602,7 @@ void UdpDataProtocol::run()
             // arrive for a longer time
             //timeout = UdpSocket.waitForReadyRead(30);
             //        timeout = cc unused!
-            waitForReady(UdpSocket, 60000); //60 seconds
+            waitForReady(60000); //60 seconds
 
             // OLD CODE WITHOUT REDUNDANCY----------------------------------------------------
             /*
@@ -565,8 +617,7 @@ void UdpDataProtocol::run()
         mJackTrip->writeAudioBuffer(mAudioPacket);
         */
             //----------------------------------------------------------------------------------
-            receivePacketRedundancy(UdpSocket,
-                                    full_redundant_packet,
+            receivePacketRedundancy(full_redundant_packet,
                                     full_redundant_packet_size,
                                     full_packet_size,
                                     current_seq_num,
@@ -576,7 +627,9 @@ void UdpDataProtocol::run()
         break; }
 
     case SENDER : {
-        while ( !mStopped )
+        full_redundant_packet = new int8_t[full_redundant_packet_size];
+        std::memset(full_redundant_packet, 0, full_redundant_packet_size); // Initialize to 0
+        while ( !mStopped && !JackTrip::sSigInt && !JackTrip::sJackStopped )
         {
             // OLD CODE WITHOUT REDUNDANCY -----------------------------------------------------
             /*
@@ -592,24 +645,34 @@ void UdpDataProtocol::run()
                                  full_redundant_packet_size,
                                  full_packet_size);
         }
+        
+        // Send exit packet (with 1 redundant packet).
+        cout << "sending exit packet" << endl;
+        QByteArray exitPacket = QByteArray(mControlPacketSize, 0xff);
+        sendPacket(exitPacket.constData(), mControlPacketSize);
+        sendPacket(exitPacket.constData(), mControlPacketSize);
+        emit signalCeaseTransmission();
         break; }
     }
+
+    if (NULL != full_redundant_packet) {
+        delete[] full_redundant_packet;
+        full_redundant_packet = NULL;
+    }
 }
 
 
 //*******************************************************************************
 //bool
-void UdpDataProtocol::waitForReady(QUdpSocket& UdpSocket, int timeout_msec)
+void UdpDataProtocol::waitForReady(int timeout_msec)
 {
     int loop_resolution_usec = 100; // usecs to wait on each loop
     int emit_resolution_usec = 10000; // 10 milliseconds
     int timeout_usec = timeout_msec * 1000;
     int elapsed_time_usec = 0; // Ellapsed time in milliseconds
 
-    while ( ( !(
-                  UdpSocket.hasPendingDatagrams() &&
-                  (UdpSocket.pendingDatagramSize() > 0)
-                  ) && (elapsed_time_usec <= timeout_usec) )
+    while ( !datagramAvailable()
+            && (elapsed_time_usec <= timeout_usec)
             && !mStopped ){
         //    if (mStopped) { return false; }
         QThread::usleep(loop_resolution_usec);
@@ -635,13 +698,13 @@ void UdpDataProtocol::printUdpWaitedTooLong(int wait_msec)
     int wait_time = 30; // msec
     if ( !(wait_msec%wait_time) ) {
         std::cerr << "UDP waiting too long (more than " << wait_time << "ms) for " << mPeerAddress.toString().toStdString() << "..." << endl;
+        emit signalUdpWaitingTooLong();
     }
 }
 
 
 //*******************************************************************************
-void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket,
-                                              int8_t* full_redundant_packet,
+void UdpDataProtocol::receivePacketRedundancy(int8_t* full_redundant_packet,
                                               int full_redundant_packet_size,
                                               int full_packet_size,
                                               uint16_t& current_seq_num,
@@ -649,19 +712,41 @@ void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket,
                                               uint16_t& newer_seq_num)
 {
     // This is blocking until we get a packet...
-    receivePacket( UdpSocket, reinterpret_cast<char*>(full_redundant_packet),
-                   full_redundant_packet_size);
+    if (receivePacket( reinterpret_cast<char*>(full_redundant_packet), 
+                       full_redundant_packet_size) == 0) {
+        return;
+    }
+
+    if (0.0 < mSimulatedLossRate || 0.0 < mSimulatedJitterRate) {
+        double x = mUniformDist(mRndEngine);
+        // Drop packets
+        x -= mSimulatedLossRate;
+        if (0 > x) {
+            return;
+        }
+        // Delay packets
+        x -= mSimulatedJitterRate;
+        if (0 > x) {
+            usleep(mUniformDist(mRndEngine) * mSimulatedJitterMaxDelay * 1e6);
+        }
+    }
 
     // Get Packet Sequence Number
     newer_seq_num =
             mJackTrip->getPeerSequenceNumber(full_redundant_packet);
     current_seq_num = newer_seq_num;
 
-    if (0 != last_seq_num) {
-        int16_t lost = newer_seq_num - last_seq_num - 1;
-        if (0 > lost) {
+    int16_t lost = 0;
+    if (!mInitialState) {
+        lost = newer_seq_num - last_seq_num - 1;
+        if (0 > lost || 1000 < lost) {
             // Out of order packet, should be ignored
             ++mOutOfOrderCount;
+            if (5 < ++mLastOutOfOrderCount) {
+                mInitialState = true;
+                mStatCount = 0;
+                mTotCount = 0;
+            }
             return;
         }
         else if (0 != lost) {
@@ -669,6 +754,8 @@ void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket,
         }
         mTotCount += 1 + lost;
     }
+    mLastOutOfOrderCount = 0;
+    mInitialState = false;
 
     //cout << current_seq_num << " ";
     int redun_last_index = 0;
@@ -687,15 +774,38 @@ void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket,
     mRevivedCount += redun_last_index;
     //cout << endl;
 
+    int peer_chans = mJackTrip->getPeerNumChannels(full_redundant_packet);
+    int N = mJackTrip->getPeerBufferSize(full_redundant_packet);
+    int host_buf_size = N * mChans * mSmplSize;
+    int hdr_size = mJackTrip->getHeaderSizeInBytes();
+    int gap_size = mInitialState ? 0 : (lost - redun_last_index) * host_buf_size;
+
     last_seq_num = newer_seq_num; // Save last read packet
 
+    if ((int)mBuffer.size() < host_buf_size) {
+        mBuffer.resize(host_buf_size, 0);
+    }
     // Send to audio all available audio packets, in order
     for (int i = redun_last_index; i>=0; i--) {
-        memcpy(mFullPacket,
-               full_redundant_packet + (i*full_packet_size),
-               full_packet_size);
-        mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket);
-        mJackTrip->writeAudioBuffer(mAudioPacket);
+        int8_t* src = full_redundant_packet + (i*full_packet_size) + hdr_size;
+        if (1 != mChans) {
+            // Convert packet's non-interleaved layout to interleaved one used internally
+            int8_t* dst = mBuffer.data();
+            int C = qMin(mChans, peer_chans);
+            for (int n=0; n<N; ++n) {
+                for (int c=0; c<C; ++c) {
+                    memcpy(dst + (n*mChans + c)*mSmplSize, src + (n + c*N)*mSmplSize, mSmplSize);
+                }
+            }
+            src = dst;
+        }
+        if (!mJackTrip->writeAudioBuffer(src, host_buf_size, gap_size)) {
+            emit signalError("Local and Peer buffer settings are incompatible");
+            cout << "ERROR: Local and Peer buffer settings are incompatible" << endl;
+            mStopped = true;
+            break;
+        }
+        gap_size = 0;
     }
 }
 
@@ -715,13 +825,40 @@ bool UdpDataProtocol::getStats(DataProtocol::PktStat* stat)
     return true;
 }
 
+//*******************************************************************************
+void UdpDataProtocol::setIssueSimulation(double loss, double jitter, double max_delay)
+{
+    mSimulatedLossRate = loss;
+    mSimulatedJitterRate = jitter;
+    mSimulatedJitterMaxDelay = max_delay;
+
+    std::random_device r;
+    mRndEngine = std::default_random_engine(r());
+    mUniformDist = std::uniform_real_distribution<double>(0.0, 1.0);
+
+    cout << "Simulating network issues: "
+      "loss_rate=" << loss << ", jitter_rate=" << jitter << ", jitter_max_delay=" << max_delay << endl;
+}
+
 //*******************************************************************************
 void UdpDataProtocol::sendPacketRedundancy(int8_t* full_redundant_packet,
                                            int full_redundant_packet_size,
                                            int full_packet_size)
 {
     mJackTrip->readAudioBuffer( mAudioPacket );
-    mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket);
+    int8_t* src = mAudioPacket;
+    if (1 != mChans) {
+        // Convert internal interleaved layout to non-interleaved
+        int N = getAudioPacketSizeInBites() / mChans / mSmplSize;
+        int8_t* dst = mBuffer.data();
+        for (int n=0; n<N; ++n) {
+            for (int c=0; c<mChans; ++c) {
+                memcpy(dst + (n + c*N)*mSmplSize, src + (n*mChans + c)*mSmplSize, mSmplSize);
+            }
+        }
+        src = dst;
+    }
+    mJackTrip->putHeaderInPacket(mFullPacket, src);
 
     // Move older packets to end of array of redundant packets
     std::memmove(full_redundant_packet+full_packet_size,
@@ -784,3 +921,32 @@ void UdpDataProtocol::sendPacketRedundancy(int8_t* full_redundant_packet,
   If it has more than one packet that it hasn't yet received, it sends it to the soundcard
   one by one.
 */
+
+bool UdpDataProtocol::datagramAvailable()
+{
+    //Currently using a simplified version of the way QUdpSocket checks for datagrams.
+    //TODO: Consider changing to use poll() or select().
+    char c;
+#if defined (__WIN_32__)
+    //Need to use the winsock version of the function for MSG_PEEK
+    WSABUF buffer;
+    buffer.buf = &c;
+    buffer.len = sizeof(c);
+    DWORD n = 0;
+    DWORD flags = MSG_PEEK;
+    int ret = WSARecv(mSocket, &buffer, 1, &n, &flags, NULL, NULL);
+    if (ret == 0) {
+        //True if no error,
+        return true;
+    } else {
+        //or if our error is that our buffer is too small.
+        int err = WSAGetLastError();
+        return (err == WSAEMSGSIZE);
+    }
+#else
+    ssize_t n;
+    n = ::recv(mSocket, &c, sizeof(c), MSG_PEEK);
+    //We have a datagram if our buffer is too small or if no error.
+    return (n != -1 || errno == EMSGSIZE);
+#endif
+}
index 13d58f9535aa1af48225903bc9b8ec8ad57fb601..f6f570c28c84335ee35ef4e59b6cfb6d20df5de6 100644 (file)
 #include <stdexcept>
 
 #include <QThread>
-#include <QUdpSocket>
 #include <QHostAddress>
 #include <QMutex>
+#include <vector>
+#include <random>
 
 #include "DataProtocol.h"
 #include "jacktrip_types.h"
@@ -104,7 +105,7 @@ public:
    * \return number of bytes read, -1 on error
    */
     //virtual int receivePacket(char* buf, const size_t n);
-    virtual int receivePacket(QUdpSocket& UdpSocket, char* buf, const size_t n);
+    virtual int receivePacket(char* buf, const size_t n);
 
     /** \brief Sends a packet
    *
@@ -121,8 +122,7 @@ public:
    * \param peerHostAddress QHostAddress to store the peer address
    * \param port Receiving port
    */
-    virtual void getPeerAddressFromFirstPacket(QUdpSocket& UdpSocket,
-                                               QHostAddress& peerHostAddress,
+    virtual void getPeerAddressFromFirstPacket(QHostAddress& peerHostAddress,
                                                uint16_t& port);
 
     /** \brief Sets the bind port number
@@ -143,17 +143,18 @@ public:
     virtual void run();
 
     virtual bool getStats(PktStat* stat);
+    virtual void setIssueSimulation(double loss, double jitter, double max_delay);
 
 private slots:
     void printUdpWaitedTooLong(int wait_msec);
-
+    
 
 signals:
 
     /// \brief Signals when waiting every 10 milliseconds, with the total wait on wait_msec
     /// \param wait_msec Total wait in milliseconds
     void signalWaitingTooLong(int wait_msec);
-
+    void signalUdpWaitingTooLong();
 
     //private:
 protected:
@@ -167,7 +168,7 @@ protected:
 #endif
 
     /** \brief This function blocks until data is available for reading in the
-   * QUdpSocket. The function will timeout after timeout_msec microseconds.
+   * socket. The function will timeout after timeout_msec microseconds.
    *
    * This function is intended to replace QAbstractSocket::waitForReadyRead which has
    * some problems with multithreading.
@@ -175,12 +176,11 @@ protected:
    * \return returns true if there is data available for reading;
    * otherwise it returns false (if an error occurred or the operation timed out)
    */
-    void waitForReady(QUdpSocket& UdpSocket, int timeout_msec);
+    void waitForReady(int timeout_msec);
 
     /** \brief Redundancy algorythm at the receiving end
     */
-    virtual void receivePacketRedundancy(QUdpSocket& UdpSocket,
-                                         int8_t* full_redundant_packet,
+    virtual void receivePacketRedundancy(int8_t* full_redundant_packet,
                                          int full_redundant_packet_size,
                                          int full_packet_size,
                                          uint16_t& current_seq_num,
@@ -193,9 +193,9 @@ protected:
                                       int full_redundant_packet_size,
                                       int full_packet_size);
 
-
 private:
-
+    bool datagramAvailable();
+    
     int mBindPort; ///< Local Port number to Bind
     int mPeerPort; ///< Peer Port number
     const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER
@@ -212,6 +212,11 @@ private:
 
     int8_t* mAudioPacket; ///< Buffer to store Audio Packets
     int8_t* mFullPacket; ///< Buffer to store Full Packet (audio+header)
+    std::vector<int8_t> mBuffer;
+    int mChans;
+    int mSmplSize;
+    int mLastOutOfOrderCount;
+    bool mInitialState;
 
     unsigned int mUdpRedundancyFactor; ///< Factor of redundancy
     static QMutex sUdpMutex; ///< Mutex to make thread safe the binding process
@@ -221,6 +226,16 @@ private:
     std::atomic<uint32_t>  mOutOfOrderCount;
     std::atomic<uint32_t>  mRevivedCount;
     uint32_t  mStatCount;
+
+    uint8_t mControlPacketSize;
+    bool mStopSignalSent;
+
+    // packet loss/jitter simulation
+    double mSimulatedLossRate;
+    double mSimulatedJitterRate;
+    double mSimulatedJitterMaxDelay;
+    std::default_random_engine mRndEngine;
+    std::uniform_real_distribution<double> mUniformDist;
 };
 
 #endif // __UDPDATAPROTOCOL_H__
index 4cbd6499ff9f632bc334f7fbbfff0f4e71333dc4..ec1b5085c7d6f07ad830c4bb2dd153f6755d6fed 100644 (file)
 
 using std::cout; using std::endl;
 
+bool UdpHubListener::sSigInt = false;
 
 //*******************************************************************************
 UdpHubListener::UdpHubListener(int server_port, int server_udp_port) :
     //mJTWorker(NULL),
+    mTcpServer(this),
     mServerPort(server_port),
     mServerUdpPort(server_udp_port),//final udp base port number
     mStopped(false),
@@ -62,9 +64,12 @@ UdpHubListener::UdpHubListener(int server_port, int server_udp_port) :
     mWAIR(false),
     #endif // endwhere
     mTotalRunningThreads(0),
-    m_connectDefaultAudioPorts(false)
+    mHubPatchDescriptions({"server-to-clients", "client loopback", "client fan out/in but not loopback",
+                           "reserved for TUB", "full mix", "no auto patching"}),
+    m_connectDefaultAudioPorts(false),
+    mIOStatTimeout(0)
 {
-    // Register JackTripWorker with the master listener
+    // Register JackTripWorker with the hub listener
     //mJTWorker = new JackTripWorker(this);
     mJTWorkers = new QVector<JackTripWorker*>;
     for (int i = 0; i<gMaxThreads; i++) {
@@ -88,7 +93,7 @@ UdpHubListener::UdpHubListener(int server_port, int server_udp_port) :
 
     // SoundWIRE ports open are UDP 61002-62000
     // (server_port - gDefaultPort) apply TCP offset to UDP too
-    if (mServerUdpPort != NULL){
+    if (mServerUdpPort != 0){
       mBasePort = mServerUdpPort;
     } else {
       mBasePort = 61002 + (server_port - gDefaultPort);
@@ -98,6 +103,14 @@ UdpHubListener::UdpHubListener(int server_port, int server_udp_port) :
 
     mUnderRunMode = JackTrip::WAVETABLE;
     mBufferQueueLength = gDefaultQueueLength;
+
+    mBufferStrategy = 1;
+    mBroadcastQueue = 0;
+    mSimulatedLossRate = 0.0;
+    mSimulatedJitterRate = 0.0;
+    mSimulatedDelayRel = 0.0;
+
+    mUseRtUdpPriority = false;
 }
 
 
@@ -119,149 +132,161 @@ UdpHubListener::~UdpHubListener()
 // the client is already on the thread pool, it means that a new connection is
 // requested (the old was desconnected). So we have to remove that thread from
 // the pool and then connect again.
-void UdpHubListener::run()
+void UdpHubListener::start()
 {
     mStopped = false;
 
-    QHostAddress PeerAddress; // Object to store peer address
-    uint16_t peer_udp_port; // Peer listening port
-    int server_udp_port; // Server assigned udp port
-
-    // Create and bind the TCP server
+    // Bind the TCP server
     // ------------------------------
-    QTcpServer TcpServer;
-    if ( !TcpServer.listen(QHostAddress::Any, mServerPort) ) {
-        std::cerr << "TCP Socket Server ERROR on Port " << mServerPort << ": " << TcpServer.errorString().toStdString() <<  endl;
-        std::exit(1);
+    QObject::connect(&mTcpServer, &QTcpServer::newConnection, this, &UdpHubListener::receivedNewConnection);
+    if ( !mTcpServer.listen(QHostAddress::Any, mServerPort) ) {
+        QString error_message = QString("TCP Socket Server on Port %1 ERROR: %2").arg(mServerPort).arg(mTcpServer.errorString());
+        std::cerr << error_message.toStdString() << endl;
+        emit signalError(error_message);
+        return;
     }
+    
+    cout << "JackTrip HUB SERVER: Waiting for client connections..." << endl;
+    cout << "JackTrip HUB SERVER: Hub auto audio patch setting = " << mHubPatch 
+         << " (" << mHubPatchDescriptions.at(mHubPatch).toStdString() << ")" << endl;
+    cout << "=======================================================" << endl;
+    
+    // Start our monitoring timer
+    mStopCheckTimer.setInterval(200);
+    connect(&mStopCheckTimer, &QTimer::timeout, this, &UdpHubListener::stopCheck);
+    mStopCheckTimer.start();
+}
+    
+void UdpHubListener::receivedNewConnection()
+{
+    QTcpSocket *clientSocket = mTcpServer.nextPendingConnection();
+    connect(clientSocket, &QAbstractSocket::readyRead, this, [=]{
+            receivedClientInfo(clientSocket);
+        });
+    cout << "JackTrip HUB SERVER: Client Connection Received!" << endl;
+}
 
-    const int tcpTimeout = 5*1000;
-
-
-    cout << "JackTrip HUB SERVER: TCP Server Listening in Port = " << TcpServer.serverPort() << endl;
-    while ( !mStopped )
-    {
-        cout << "JackTrip HUB SERVER: Waiting for client connections..." << endl;
-        if(m_connectDefaultAudioPorts)
-        {
-          cout << "JackTrip HUB SERVER: Hub auto audio patch setting = " << mHubPatch << endl;
-        } else {
-          cout << "JackTrip HUB SERVER: Hub auto audio patch disabled " << endl;
+void UdpHubListener::receivedClientInfo(QTcpSocket *clientConnection)
+{
+    QHostAddress PeerAddress = clientConnection->peerAddress();
+    cout << "JackTrip HUB SERVER: Client Connect Received from Address : "
+         << PeerAddress.toString().toStdString() << endl;
+         
+    // Get UDP port from client
+    // ------------------------
+    QString clientName = QString();
+    cout << "JackTrip HUB SERVER: Reading UDP port from Client..." << endl;
+    if (clientConnection->bytesAvailable() < (qint64)sizeof(uint16_t)) {
+        // We don't have enough data. Wait for the next readyRead notification.
+        return;
+    }
+    uint16_t peer_udp_port= readClientUdpPort(clientConnection, clientName);
+
+    cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl;
+    if ( peer_udp_port == 0 || peer_udp_port < gBindPortLow || peer_udp_port > gBindPortHigh ) {
+        cout << "JackTrip HUB SERVER: Exiting " << endl;
+        clientConnection->close();
+        clientConnection->deleteLater();
+        return;
+    }
+    
+    // Check is client is new or not
+    // -----------------------------
+    // Check if Address is not already in the thread pool
+    // check by comparing address strings (To handle IPv4 and IPv6.)
+    int id = isNewAddress(PeerAddress.toString(), peer_udp_port);
+    // If the address is not new, we need to remove the client from the pool
+    // before re-starting the connection
+
+    if (id == -1) {
+        int id_remove;
+        id_remove = getPoolID(PeerAddress.toString(), peer_udp_port);
+        // stop the thread
+        mJTWorkers->at(id_remove)->stopThread();
+        // block until the thread has been removed from the pool
+        while ( isNewAddress(PeerAddress.toString(), peer_udp_port) == -1 ) {
+            cout << "JackTrip HUB SERVER: Removing JackTripWorker from pool..." << endl;
+            QThread::msleep(10);
         }
-        cout << "=======================================================" << endl;
-        while ( !TcpServer.hasPendingConnections() && !TcpServer.waitForNewConnection(1000) )
-        { if (mStopped) { return; } } // block until a new connection is received
-        cout << "JackTrip HUB SERVER: Client Connection Received!" << endl;
-
-        // Control loop to be able to exit if UDPs or TCPs error ocurr
-        for (int dum = 0; dum<1; dum++) {
-            QTcpSocket *clientConnection = TcpServer.nextPendingConnection();
-            if ( !clientConnection->waitForConnected(tcpTimeout) ) {
-                std::cerr << clientConnection->errorString().toStdString() << endl;
-                break;
-            }
-            PeerAddress = clientConnection->peerAddress();
-            cout << "JackTrip HUB SERVER: Client Connect Received from Address : "
-                 << PeerAddress.toString().toStdString() << endl;
-
-            // Get UDP port from client
-            // ------------------------
-            peer_udp_port = readClientUdpPort(clientConnection);
-            if ( peer_udp_port == 0 ) {
-                cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl;
-                cout << "JackTrip HUB SERVER: Exiting " << endl;
-                break;
-            }
-            if ( peer_udp_port < gBindPortLow ) {
-                cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl;
-                cout << "JackTrip HUB SERVER: Exiting " << endl;
-                break;
-            }
-            if ( peer_udp_port > gBindPortHigh ) {
-                cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl;
-                cout << "JackTrip HUB SERVER: Exiting " << endl;
-                break;
-            }
-            cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl;
-
-            // Check is client is new or not
-            // -----------------------------
-            // Check if Address is not already in the thread pool
-            // check by comparing address strings (To handle IPv4 and IPv6.)
-            int id = isNewAddress(PeerAddress.toString(), peer_udp_port);
-            // If the address is not new, we need to remove the client from the pool
-            // before re-starting the connection
-
-            if (id == -1) {
-                int id_remove;
-                id_remove = getPoolID(PeerAddress.toString(), peer_udp_port);
-                // stop the thread
-                mJTWorkers->at(id_remove)->stopThread();
-                // block until the thread has been removed from the pool
-                while ( isNewAddress(PeerAddress.toString(), peer_udp_port) == -1 ) {
-                    cout << "JackTrip HUB SERVER: Removing JackTripWorker from pool..." << endl;
-                    QThread::msleep(10);
-                }
-                // Get a new ID for this client
-                //id = isNewAddress(PeerAddress.toIPv4Address(), peer_udp_port);
-                id = getPoolID(PeerAddress.toString(), peer_udp_port);
-            }
-            // Assign server port and send it to Client
-            server_udp_port = mBasePort+id;
-            cout << "JackTrip HUB SERVER: Sending Final UDP Port to Client: " << server_udp_port << endl;
-
-            if ( sendUdpPort(clientConnection, server_udp_port) == 0 ) {
-                clientConnection->close();
-                delete clientConnection;
-                releaseThread(id);
-                break;
-            }
-
-            // Close and Delete the socket
-            // ---------------------------
-            clientConnection->close();
-            delete clientConnection;
-            cout << "JackTrip HUB SERVER: Client TCP Connection Closed!" << endl;
-
-            // Spawn Thread to Pool
-            // --------------------
-            // Register JackTripWorker with the master listener
-            delete mJTWorkers->at(id); // just in case the Worker was previously created
-            mJTWorkers->replace(id, new JackTripWorker(this, mBufferQueueLength, mUnderRunMode));
-            // redirect port and spawn listener
-            cout << "JackTrip HUB SERVER: Spawning JackTripWorker..." << endl;
-            {
-                QMutexLocker lock(&mMutex);
-                mJTWorkers->at(id)->setJackTrip(id,
-                                                mActiveAddress[id].address,
-                                                server_udp_port,
-                                                mActiveAddress[id].port,
-                                                1,
-                                                m_connectDefaultAudioPorts
-                                               ); /// \todo temp default to 1 channel
-
-//                qDebug() << "mPeerAddress" << id <<  mActiveAddress[id].address << mActiveAddress[id].port;
-            }
-            //send one thread to the pool
-            cout << "JackTrip HUB SERVER: Starting JackTripWorker..." << endl;
-            mThreadPool.start(mJTWorkers->at(id), QThread::TimeCriticalPriority);
-            // wait until one is complete before another spawns
-            while (mJTWorkers->at(id)->isSpawning()) { QThread::msleep(10); }
-            //mTotalRunningThreads++;
-            cout << "JackTrip HUB SERVER: Total Running Threads:  " << mTotalRunningThreads << endl;
-            cout << "===============================================================" << endl;
-            QThread::msleep(100);
+        // Get a new ID for this client
+        //id = isNewAddress(PeerAddress.toIPv4Address(), peer_udp_port);
+        id = getPoolID(PeerAddress.toString(), peer_udp_port);
+    }
+    // Assign server port and send it to Client
+    int server_udp_port = mBasePort+id;
+    cout << "JackTrip HUB SERVER: Sending Final UDP Port to Client: " << server_udp_port << endl;
+    
+     if ( sendUdpPort(clientConnection, server_udp_port) == 0 ) {
+        clientConnection->close();
+        clientConnection->deleteLater();
+        releaseThread(id);
+        return;
+    }
+    
+    // Close and mark socket for deletion
+    // ----------------------------------
+    clientConnection->close();
+    clientConnection->deleteLater();
+    cout << "JackTrip HUB SERVER: Client TCP Connection Closed!" << endl;
+
+    // Spawn Thread to Pool
+    // --------------------
+    // Register JackTripWorker with the hub listener
+    delete mJTWorkers->at(id); // just in case the Worker was previously created
+    mJTWorkers->replace(id, new JackTripWorker(this, mBufferQueueLength, mUnderRunMode, clientName));
+    if (mIOStatTimeout > 0) {
+        mJTWorkers->at(id)->setIOStatTimeout(mIOStatTimeout);
+        mJTWorkers->at(id)->setIOStatStream(mIOStatStream);
+    }
+    mJTWorkers->at(id)->setBufferStrategy(mBufferStrategy);
+    mJTWorkers->at(id)->setNetIssuesSimulation(mSimulatedLossRate,
+    mSimulatedJitterRate, mSimulatedDelayRel);
+    mJTWorkers->at(id)->setBroadcast(mBroadcastQueue);
+    mJTWorkers->at(id)->setUseRtUdpPriority(mUseRtUdpPriority);
+    // redirect port and spawn listener
+    cout << "JackTrip HUB SERVER: Spawning JackTripWorker..." << endl;
+    {
+        QMutexLocker lock(&mMutex);
+        mJTWorkers->at(id)->setJackTrip(id,
+                                        mActiveAddress[id].address,
+                                        server_udp_port,
+                                        mActiveAddress[id].port,
+                                        1,
+                                        m_connectDefaultAudioPorts
+                                        ); /// \todo temp default to 1 channel
+
+        //qDebug() << "mPeerAddress" << id <<  mActiveAddress[id].address << mActiveAddress[id].port;
+    }
+    //send one thread to the pool
+    cout << "JackTrip HUB SERVER: Starting JackTripWorker..." << endl;
+    mThreadPool.start(mJTWorkers->at(id), QThread::TimeCriticalPriority);
+    // wait until one is complete before another spawns
+    while (mJTWorkers->at(id)->isSpawning()) { QThread::msleep(10); }
+    //mTotalRunningThreads++;
+    cout << "JackTrip HUB SERVER: Total Running Threads:  " << mTotalRunningThreads << endl;
+    cout << "===============================================================" << endl;
+    QThread::msleep(100);
 #ifdef WAIR // WAIR
-            if (isWAIR()) connectMesh(true); // invoked with -Sw
+    if (isWAIR()) connectMesh(true); // invoked with -Sw
 #endif // endwhere
 
-//            qDebug() << "mPeerAddress" << mActiveAddress[id].address << mActiveAddress[id].port;
+    //qDebug() << "mPeerAddress" << mActiveAddress[id].address << mActiveAddress[id].port;
 
-            connectPatch(true);
-        }
+    connectPatch(true);
+}
+
+void UdpHubListener::stopCheck()
+{
+    if (mStopped || sSigInt) {
+        cout << "JackTrip HUB SERVER: Stopped" << endl;
+        mStopCheckTimer.stop();
+        mTcpServer.close();
+        stopAllThreads();
+        emit signalStopped();
     }
+}
 
-    /*
+    /* From Old Runloop code
   // Create objects on the stack
   QUdpSocket HubUdpSocket;
   QHostAddress PeerAddress;
@@ -310,32 +335,26 @@ void UdpHubListener::run()
     QThread::msleep(100);
   }
   */
-}
-
 
 //*******************************************************************************
 // Returns 0 on error
-uint16_t UdpHubListener::readClientUdpPort(QTcpSocket* clientConnection)
+uint16_t UdpHubListener::readClientUdpPort(QTcpSocket* clientConnection, QString &clientName)
 {
-    // Read the size of the package
-    // ----------------------------
-    //tcpClient.waitForReadyRead();
-    cout << "JackTrip HUB SERVER: Reading UDP port from Client..." << endl;
-    while (clientConnection->bytesAvailable() < (qint64)sizeof(uint16_t)) {
-        if (!clientConnection->waitForReadyRead()) {
-            std::cerr << "TCP Socket ERROR: " << clientConnection->errorString().toStdString() <<  endl;
-            return 0;
-        }
-    }
-
     if (gVerboseFlag) cout << "Ready To Read From Client!" << endl;
     // Read UDP Port Number from Server
     // --------------------------------
-    uint16_t udp_port = 0;
+    uint16_t udp_port;
     qint64 size = sizeof(udp_port);
     char port_buf[size];
     clientConnection->read(port_buf, size);
     std::memcpy(&udp_port, port_buf, size);
+    
+    if (clientConnection->bytesAvailable() == gMaxRemoteNameLength) {
+        char name_buf[gMaxRemoteNameLength];
+        clientConnection->read(name_buf, gMaxRemoteNameLength);
+        clientName = QString::fromUtf8((const char *)name_buf);
+    }
+    
     return udp_port;
 }
 
@@ -357,7 +376,7 @@ int UdpHubListener::sendUdpPort(QTcpSocket* clientConnection, int udp_port)
         }
     }
     return 1;
-    cout << "Port sent to Client" << endl;
+    //cout << "Port sent to Client" << endl;
 }
 
 
@@ -489,11 +508,12 @@ void UdpHubListener::enumerateRunningThreadIDs()
 #include "JMess.h"
 void UdpHubListener::connectPatch(bool spawn)
 {
-    if(m_connectDefaultAudioPorts) {
-      cout << ((spawn)?"spawning":"releasing") << " jacktripWorker so change patch" << endl;
-    } else {
-      cout << ((spawn)?"spawning":"releasing") << " jacktripWorker" << endl;
+    if ((getHubPatch() == JackTrip::NOAUTO) ||
+        (getHubPatch() == JackTrip::SERVERTOCLIENT && !m_connectDefaultAudioPorts)) {
+        cout << ((spawn)?"spawning":"releasing") << " jacktripWorker (auto hub patching disabled)" << endl;
+        return;
     }
+    cout << ((spawn)?"spawning":"releasing") << " jacktripWorker so change patch" << endl;
     JMess tmp;
     // default is patch 0, which connects server audio to all clients
     // these are the other cases:
@@ -506,5 +526,17 @@ void UdpHubListener::connectPatch(bool spawn)
     // FIXME: need change to gDefaultNumInChannels if more than stereo
 }
 
+void UdpHubListener::stopAllThreads()
+{
+    QVectorIterator<JackTripWorker*> iterator(*mJTWorkers);
+    while (iterator.hasNext()) {
+        if (iterator.peekNext() != nullptr) {
+            iterator.next()->stopThread();
+        } else {
+            iterator.next();
+        }
+    }
+    mThreadPool.waitForDone();
+}
 // TODO:
 // USE bool QAbstractSocket::isValid () const to check if socket is connect. if not, exit loop
index 0bc0a8c75777a92d3c025dcf2e88ac8b28008f8e..763cfea2f94518669e3e83413974bcf6e18ecbdc 100644 (file)
@@ -40,6 +40,7 @@
 
 #include <iostream>
 #include <stdexcept>
+#include <fstream>
 
 #include <QThread>
 #include <QThreadPool>
@@ -65,17 +66,16 @@ typedef struct {
  * This creates a server that will listen on the well know port (the server port) and will
  * spawn JackTrip threads into the Thread pool. Clients request a connection.
  */
-class UdpHubListener : public QThread
+class UdpHubListener : public QObject
 {
     Q_OBJECT;
 
 public:
-    UdpHubListener(int server_port = gServerUdpPort, int server_udp_port = NULL);
+    UdpHubListener(int server_port = gServerUdpPort, int server_udp_port = 0);
     virtual ~UdpHubListener();
 
-    /// \brief Implements the Thread Loop. To start the thread, call start()
-    /// ( DO NOT CALL run() )
-    void run();
+    /// \brief Starts the TCP server
+    void start();
 
     /// \brief Stops the execution of the Thread
     void stop() { mStopped = true; }
@@ -83,28 +83,33 @@ public:
     int releaseThread(int id);
 
     void setConnectDefaultAudioPorts(bool connectDefaultAudioPorts) { m_connectDefaultAudioPorts = connectDefaultAudioPorts; }
-
-    void setSettings(Settings* s) {m_settings = s;}
-    Settings* getSettings() const {return m_settings;}
+    
+    static void sigIntHandler(__attribute__((unused)) int unused)
+    { std::cout << std::endl << "Shutting Down..." << std::endl; sSigInt = true; }
 
 private slots:
     void testReceive()
     { std::cout << "========= TEST RECEIVE SLOT ===========" << std::endl; }
+    void receivedNewConnection();
+    void stopCheck();
 
 signals:
     void Listening();
     void ClientAddressSet();
     void signalRemoveThread(int id);
-
+    void signalStopped();
+    void signalError(const QString &errorMessage);
 
 private:
     /** \brief Binds a QUdpSocket. It chooses the available (active) interface.
    * \param udpsocket a QUdpSocket
    * \param port Port number
    */
+    void receivedClientInfo(QTcpSocket *clientConnection);
+
     static void bindUdpSocket(QUdpSocket& udpsocket, int port);
 
-    uint16_t readClientUdpPort(QTcpSocket* clientConnection);
+    uint16_t readClientUdpPort(QTcpSocket* clientConnection, QString &clientName);
     int sendUdpPort(QTcpSocket* clientConnection, int udp_port);
 
 
@@ -124,6 +129,8 @@ private:
     * is not in the pool yet, returns -1.
     */
     int getPoolID(QString address, uint16_t port);
+    
+    void stopAllThreads();
 
     //QUdpSocket mUdpHubSocket; ///< The UDP socket
     //QHostAddress mPeerAddress; ///< The Peer Address
@@ -132,6 +139,7 @@ private:
     QVector<JackTripWorker*>* mJTWorkers; ///< Vector of JackTripWorker s
     QThreadPool mThreadPool; ///< The Thread Pool
 
+    QTcpServer mTcpServer;
     int mServerPort; //< Server known port number
     int mServerUdpPort; //< Server udp base port number
     int mBasePort;
@@ -140,14 +148,26 @@ private:
 
     /// Boolean stop the execution of the thread
     volatile bool mStopped;
+    static bool sSigInt;
+    QTimer mStopCheckTimer;
     int mTotalRunningThreads; ///< Number of Threads running in the pool
     QMutex mMutex;
     JackTrip::underrunModeT mUnderRunMode;
     int mBufferQueueLength;
-
+    
+    QStringList mHubPatchDescriptions;
     bool m_connectDefaultAudioPorts;
-    Settings* m_settings;
 
+    int mIOStatTimeout;
+    QSharedPointer<std::ofstream> mIOStatStream;
+
+    int mBufferStrategy;
+    int mBroadcastQueue;
+    double mSimulatedLossRate;
+    double mSimulatedJitterRate;
+    double mSimulatedDelayRel;
+    bool mUseRtUdpPriority;
+    
 #ifdef WAIR // wair
     bool mWAIR;
     void connectMesh(bool spawn);
@@ -164,6 +184,20 @@ public :
 
     void setUnderRunMode(JackTrip::underrunModeT UnderRunMode) { mUnderRunMode = UnderRunMode; }
     void setBufferQueueLength(int BufferQueueLength) { mBufferQueueLength = BufferQueueLength; }
+    
+    void setIOStatTimeout(int timeout) { mIOStatTimeout = timeout; }
+    void setIOStatStream(QSharedPointer<std::ofstream> statStream) { mIOStatStream = statStream; }
+
+    void setBufferStrategy(int BufferStrategy) { mBufferStrategy = BufferStrategy; }
+    void setNetIssuesSimulation(double loss, double jitter, double delay_rel)
+    {
+        mSimulatedLossRate = loss;
+        mSimulatedJitterRate = jitter;
+        mSimulatedDelayRel = delay_rel;
+    }
+    void setBroadcast(int broadcast_queue) {mBroadcastQueue = broadcast_queue;}
+    void setUseRtUdpPriority(bool use) {mUseRtUdpPriority = use;}
+
 };
 
 
index e3f715094ca4ce1226721d54a525433bfc9a92b2..65ce621754cc119a2448446667377f5d5aaf850a 100755 (executable)
--- a/src/build
+++ b/src/build
@@ -37,8 +37,8 @@ if [[ $1 == 'nojack' ]]; then
     $QCMD -spec $QSPEC -config nojack ../src/jacktrip.pro
     make release
 else
-    $QCMD -spec $QSPEC ../src/jacktrip.pro
+    $QCMD -spec $QSPEC ../src/jacktrip.pro $@
     make clean
-    $QCMD -spec $QSPEC ../src/jacktrip.pro
+    $QCMD -spec $QSPEC ../src/jacktrip.pro $@
     make release
 fi
diff --git a/src/compressordsp.h b/src/compressordsp.h
new file mode 100644 (file)
index 0000000..26b37ac
--- /dev/null
@@ -0,0 +1,1816 @@
+/* ------------------------------------------------------------
+author: "Julius Smith"
+license: "MIT Style STK-4.2"
+name: "compressor"
+version: "0.0"
+Code generated with Faust 2.28.6 (https://faust.grame.fr)
+Compilation options: -lang cpp -inpl -scal -ftz 0
+------------------------------------------------------------ */
+
+#ifndef  __compressordsp_H__
+#define  __compressordsp_H__
+
+// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME
+//
+// faust2header.cpp - FAUST Architecture File
+// This is a simple variation of matlabplot.cpp in the Faust distribution
+// aimed at creating a simple C++ header file (.h) containing a Faust DSP.
+// See the Makefile for how to use it.
+
+/************************** BEGIN dsp.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __dsp__
+#define __dsp__
+
+#include <string>
+#include <vector>
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+struct UI;
+struct Meta;
+
+/**
+ * DSP memory manager.
+ */
+
+struct dsp_memory_manager {
+    
+    virtual ~dsp_memory_manager() {}
+    
+    virtual void* allocate(size_t size) = 0;
+    virtual void destroy(void* ptr) = 0;
+    
+};
+
+/**
+* Signal processor definition.
+*/
+
+class dsp {
+
+    public:
+
+        dsp() {}
+        virtual ~dsp() {}
+
+        /* Return instance number of audio inputs */
+        virtual int getNumInputs() = 0;
+    
+        /* Return instance number of audio outputs */
+        virtual int getNumOutputs() = 0;
+    
+        /**
+         * Trigger the ui_interface parameter with instance specific calls
+         * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI.
+         *
+         * @param ui_interface - the user interface builder
+         */
+        virtual void buildUserInterface(UI* ui_interface) = 0;
+    
+        /* Returns the sample rate currently used by the instance */
+        virtual int getSampleRate() = 0;
+    
+        /**
+         * Global init, calls the following methods:
+         * - static class 'classInit': static tables initialization
+         * - 'instanceInit': constants and instance state initialization
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void init(int sample_rate) = 0;
+
+        /**
+         * Init instance state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceInit(int sample_rate) = 0;
+
+        /**
+         * Init instance constant state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceConstants(int sample_rate) = 0;
+    
+        /* Init default control parameters values */
+        virtual void instanceResetUserInterface() = 0;
+    
+        /* Init instance state (delay lines...) */
+        virtual void instanceClear() = 0;
+        /**
+         * Return a clone of the instance.
+         *
+         * @return a copy of the instance on success, otherwise a null pointer.
+         */
+        virtual dsp* clone() = 0;
+    
+        /**
+         * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata.
+         *
+         * @param m - the Meta* meta user
+         */
+        virtual void metadata(Meta* m) = 0;
+    
+        /**
+         * DSP instance computation, to be called with successive in/out audio buffers.
+         *
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         *
+         */
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0;
+    
+        /**
+         * DSP instance computation: alternative method to be used by subclasses.
+         *
+         * @param date_usec - the timestamp in microsec given by audio driver.
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         *
+         */
+        virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); }
+       
+};
+
+/**
+ * Generic DSP decorator.
+ */
+
+class decorator_dsp : public dsp {
+
+    protected:
+
+        dsp* fDSP;
+
+    public:
+
+        decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {}
+        virtual ~decorator_dsp() { delete fDSP; }
+
+        virtual int getNumInputs() { return fDSP->getNumInputs(); }
+        virtual int getNumOutputs() { return fDSP->getNumOutputs(); }
+        virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); }
+        virtual int getSampleRate() { return fDSP->getSampleRate(); }
+        virtual void init(int sample_rate) { fDSP->init(sample_rate); }
+        virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); }
+        virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); }
+        virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); }
+        virtual void instanceClear() { fDSP->instanceClear(); }
+        virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); }
+        virtual void metadata(Meta* m) { fDSP->metadata(m); }
+        // Beware: subclasses usually have to overload the two 'compute' methods
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); }
+        virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); }
+    
+};
+
+/**
+ * DSP factory class.
+ */
+
+class dsp_factory {
+    
+    protected:
+    
+        // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory);
+        virtual ~dsp_factory() {}
+    
+    public:
+    
+        virtual std::string getName() = 0;
+        virtual std::string getSHAKey() = 0;
+        virtual std::string getDSPCode() = 0;
+        virtual std::string getCompileOptions() = 0;
+        virtual std::vector<std::string> getLibraryList() = 0;
+        virtual std::vector<std::string> getIncludePathnames() = 0;
+    
+        virtual dsp* createDSPInstance() = 0;
+    
+        virtual void setMemoryManager(dsp_memory_manager* manager) = 0;
+        virtual dsp_memory_manager* getMemoryManager() = 0;
+    
+};
+
+/**
+ * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
+ * flags to avoid costly denormals.
+ */
+
+#ifdef __SSE__
+    #include <xmmintrin.h>
+    #ifdef __SSE2__
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
+    #else
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
+    #endif
+#else
+    #define AVOIDDENORMALS
+#endif
+
+#endif
+/**************************  END  dsp.h **************************/
+
+/************************** BEGIN APIUI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef API_UI_H
+#define API_UI_H
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <map>
+
+/************************** BEGIN meta.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __meta__
+#define __meta__
+
+struct Meta
+{
+    virtual ~Meta() {};
+    virtual void declare(const char* key, const char* value) = 0;
+    
+};
+
+#endif
+/**************************  END  meta.h **************************/
+/************************** BEGIN UI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __UI_H__
+#define __UI_H__
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+/*******************************************************************************
+ * UI : Faust DSP User Interface
+ * User Interface as expected by the buildUserInterface() method of a DSP.
+ * This abstract class contains only the method that the Faust compiler can
+ * generate to describe a DSP user interface.
+ ******************************************************************************/
+
+struct Soundfile;
+
+template <typename REAL>
+struct UIReal
+{
+    UIReal() {}
+    virtual ~UIReal() {}
+    
+    // -- widget's layouts
+    
+    virtual void openTabBox(const char* label) = 0;
+    virtual void openHorizontalBox(const char* label) = 0;
+    virtual void openVerticalBox(const char* label) = 0;
+    virtual void closeBox() = 0;
+    
+    // -- active widgets
+    
+    virtual void addButton(const char* label, REAL* zone) = 0;
+    virtual void addCheckButton(const char* label, REAL* zone) = 0;
+    virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    
+    // -- passive widgets
+    
+    virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    
+    // -- soundfiles
+    
+    virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0;
+    
+    // -- metadata declarations
+    
+    virtual void declare(REAL* zone, const char* key, const char* val) {}
+};
+
+struct UI : public UIReal<FAUSTFLOAT>
+{
+    UI() {}
+    virtual ~UI() {}
+};
+
+#endif
+/**************************  END  UI.h **************************/
+/************************** BEGIN PathBuilder.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef FAUST_PATHBUILDER_H
+#define FAUST_PATHBUILDER_H
+
+#include <vector>
+#include <string>
+#include <algorithm>
+
+/*******************************************************************************
+ * PathBuilder : Faust User Interface
+ * Helper class to build complete hierarchical path for UI items.
+ ******************************************************************************/
+
+class PathBuilder
+{
+
+    protected:
+    
+        std::vector<std::string> fControlsLevel;
+       
+    public:
+    
+        PathBuilder() {}
+        virtual ~PathBuilder() {}
+    
+        std::string buildPath(const std::string& label) 
+        {
+            std::string res = "/";
+            for (size_t i = 0; i < fControlsLevel.size(); i++) {
+                res += fControlsLevel[i];
+                res += "/";
+            }
+            res += label;
+            std::replace(res.begin(), res.end(), ' ', '_');
+            return res;
+        }
+    
+        std::string buildLabel(std::string label)
+        {
+            std::replace(label.begin(), label.end(), ' ', '_');
+            return label;
+        }
+    
+        void pushLabel(const std::string& label) { fControlsLevel.push_back(label); }
+        void popLabel() { fControlsLevel.pop_back(); }
+    
+};
+
+#endif  // FAUST_PATHBUILDER_H
+/**************************  END  PathBuilder.h **************************/
+/************************** BEGIN ValueConverter.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __ValueConverter__
+#define __ValueConverter__
+
+/***************************************************************************************
+                                                               ValueConverter.h
+                            (GRAME, Copyright 2015-2019)
+
+Set of conversion objects used to map user interface values (for example a gui slider
+delivering values between 0 and 1) to faust values (for example a vslider between
+20 and 20000) using a log scale.
+
+-- Utilities
+
+Range(lo,hi) : clip a value x between lo and hi
+Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2
+Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2
+
+-- Value Converters
+
+ValueConverter::ui2faust(x)
+ValueConverter::faust2ui(x)
+
+-- ValueConverters used for sliders depending of the scale
+
+LinearValueConverter(umin, umax, fmin, fmax)
+LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments
+LogValueConverter(umin, umax, fmin, fmax)
+ExpValueConverter(umin, umax, fmin, fmax)
+
+-- ValueConverters used for accelerometers based on 3 points
+
+AccUpConverter(amin, amid, amax, fmin, fmid, fmax)             -- curve 0
+AccDownConverter(amin, amid, amax, fmin, fmid, fmax)   -- curve 1
+AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2
+AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3
+
+-- lists of ZoneControl are used to implement accelerometers metadata for each axes
+
+ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter
+
+-- ZoneReader are used to implement screencolor metadata
+
+ZoneReader(zone, valueConverter) : a zone with a data converter
+
+****************************************************************************************/
+
+#include <float.h>
+#include <algorithm>    // std::max
+#include <cmath>
+#include <vector>
+#include <assert.h>
+
+//--------------------------------------------------------------------------------------
+// Interpolator(lo,hi,v1,v2)
+// Maps a value x between lo and hi to a value y between v1 and v2
+// y = v1 + (x-lo)/(hi-lo)*(v2-v1)
+// y = v1 + (x-lo) * coef              with coef = (v2-v1)/(hi-lo)
+// y = v1 + x*coef - lo*coef
+// y = v1 - lo*coef + x*coef
+// y = offset + x*coef                         with offset = v1 - lo*coef
+//--------------------------------------------------------------------------------------
+class Interpolator
+{
+    private:
+
+        //--------------------------------------------------------------------------------------
+        // Range(lo,hi) clip a value between lo and hi
+        //--------------------------------------------------------------------------------------
+        struct Range
+        {
+            double fLo;
+            double fHi;
+
+            Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {}
+            double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; }
+        };
+
+
+        Range fRange;
+        double fCoef;
+        double fOffset;
+
+    public:
+
+        Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi)
+        {
+            if (hi != lo) {
+                // regular case
+                fCoef = (v2-v1)/(hi-lo);
+                fOffset = v1 - lo*fCoef;
+            } else {
+                // degenerate case, avoids division by zero
+                fCoef = 0;
+                fOffset = (v1+v2)/2;
+            }
+        }
+        double operator()(double v)
+        {
+            double x = fRange(v);
+            return  fOffset + x*fCoef;
+        }
+
+        void getLowHigh(double& amin, double& amax)
+        {
+            amin = fRange.fLo;
+            amax = fRange.fHi;
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Interpolator3pt(lo,mi,hi,v1,vm,v2)
+// Map values between lo mid hi to values between v1 vm v2
+//--------------------------------------------------------------------------------------
+class Interpolator3pt
+{
+
+    private:
+
+        Interpolator fSegment1;
+        Interpolator fSegment2;
+        double fMid;
+
+    public:
+
+        Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) :
+            fSegment1(lo, mi, v1, vm),
+            fSegment2(mi, hi, vm, v2),
+            fMid(mi) {}
+        double operator()(double x) { return  (x < fMid) ? fSegment1(x) : fSegment2(x); }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fSegment1.getLowHigh(amin, amid);
+            fSegment2.getLowHigh(amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Abstract ValueConverter class. Converts values between UI and Faust representations
+//--------------------------------------------------------------------------------------
+class ValueConverter
+{
+
+    public:
+
+        virtual ~ValueConverter() {}
+        virtual double ui2faust(double x) = 0;
+        virtual double faust2ui(double x) = 0;
+};
+
+//--------------------------------------------------------------------------------------
+// A converter than can be updated
+//--------------------------------------------------------------------------------------
+
+class UpdatableValueConverter : public ValueConverter {
+    
+    protected:
+        
+        bool fActive;
+        
+    public:
+        
+        UpdatableValueConverter():fActive(true)
+        {}
+        virtual ~UpdatableValueConverter()
+        {}
+        
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0;
+        virtual void getMappingValues(double& amin, double& amid, double& amax) = 0;
+        
+        void setActive(bool on_off) { fActive = on_off; }
+        bool getActive() { return fActive; }
+    
+};
+
+
+//--------------------------------------------------------------------------------------
+// Linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter : public ValueConverter
+{
+    
+    private:
+        
+        Interpolator fUI2F;
+        Interpolator fF2UI;
+        
+    public:
+        
+        LinearValueConverter(double umin, double umax, double fmin, double fmax) :
+            fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax)
+        {}
+        
+        LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.)
+        {}
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Two segments linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter2 : public UpdatableValueConverter
+{
+    
+    private:
+    
+        Interpolator3pt fUI2F;
+        Interpolator3pt fF2UI;
+        
+    public:
+    
+        LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) :
+            fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax)
+        {}
+        
+        LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.)
+        {}
+    
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max)
+        {
+            fUI2F = Interpolator3pt(amin, amid, amax, min, init, max);
+            fF2UI = Interpolator3pt(min, init, max, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fUI2F.getMappingValues(amin, amid, amax);
+        }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Logarithmic conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LogValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        LogValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Exponential conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class ExpValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        ExpValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up curve (curve 0)
+//--------------------------------------------------------------------------------------
+class AccUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt fA2F;
+        Interpolator3pt fF2A;
+
+    public:
+
+        AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmid,fmax),
+            fF2A(fmin,fmid,fmax,amin,amid,amax)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down curve (curve 1)
+//--------------------------------------------------------------------------------------
+class AccDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator3pt        fF2A;
+
+    public:
+
+        AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmid,fmin),
+            fF2A(fmin,fmid,fmax,amax,amid,amin)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+             //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up-Down curve (curve 2)
+//--------------------------------------------------------------------------------------
+class AccUpDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmax,fmin),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down-Up curve (curve 3)
+//--------------------------------------------------------------------------------------
+class AccDownUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmin,fmax),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Base class for ZoneControl
+//--------------------------------------------------------------------------------------
+class ZoneControl
+{
+
+    protected:
+
+        FAUSTFLOAT*    fZone;
+
+    public:
+
+        ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {}
+        virtual ~ZoneControl() {}
+
+        virtual void update(double v) const {}
+
+        virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {}
+        virtual void getMappingValues(double& amin, double& amid, double& amax) {}
+
+        FAUSTFLOAT* getZone() { return fZone; }
+
+        virtual void setActive(bool on_off) {}
+        virtual bool getActive() { return false; }
+
+        virtual int getCurve() { return -1; }
+
+};
+
+//--------------------------------------------------------------------------------------
+//  Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class ConverterZoneControl : public ZoneControl
+{
+
+    protected:
+
+        ValueConverter* fValueConverter;
+
+    public:
+
+        ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {}
+        virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere...
+
+        virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); }
+
+        ValueConverter* getConverter() { return fValueConverter; }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Association of a zone and a four value converter, each one for each possible curve.
+// Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class CurveZoneControl : public ZoneControl
+{
+
+    private:
+
+        std::vector<UpdatableValueConverter*> fValueConverters;
+        int fCurve;
+
+    public:
+
+        CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0)
+        {
+            assert(curve >= 0 && curve <= 3);
+            fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max));
+            fCurve = curve;
+        }
+        virtual ~CurveZoneControl()
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                delete(*it);
+            }
+        }
+        void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); }
+
+        void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max)
+        {
+            fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max);
+            fCurve = curve;
+        }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fValueConverters[fCurve]->getMappingValues(amin, amid, amax);
+        }
+
+        void setActive(bool on_off)
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                (*it)->setActive(on_off);
+            }
+        }
+
+        int getCurve() { return fCurve; }
+};
+
+class ZoneReader
+{
+
+    private:
+
+        FAUSTFLOAT* fZone;
+        Interpolator fInterpolator;
+
+    public:
+
+        ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {}
+
+        virtual ~ZoneReader() {}
+
+        int getValue()
+        {
+            return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127;
+        }
+
+};
+
+#endif
+/**************************  END  ValueConverter.h **************************/
+
+class APIUI : public PathBuilder, public Meta, public UI
+{
+    public:
+    
+        enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph };
+  
+    protected:
+    
+        enum { kLin = 0, kLog = 1, kExp = 2 };
+    
+        int fNumParameters;
+        std::vector<std::string> fPaths;
+        std::vector<std::string> fLabels;
+        std::map<std::string, int> fPathMap;
+        std::map<std::string, int> fLabelMap;
+        std::vector<ValueConverter*> fConversion;
+        std::vector<FAUSTFLOAT*> fZone;
+        std::vector<FAUSTFLOAT> fInit;
+        std::vector<FAUSTFLOAT> fMin;
+        std::vector<FAUSTFLOAT> fMax;
+        std::vector<FAUSTFLOAT> fStep;
+        std::vector<ItemType> fItemType;
+        std::vector<std::map<std::string, std::string> > fMetaData;
+        std::vector<ZoneControl*> fAcc[3];
+        std::vector<ZoneControl*> fGyr[3];
+
+        // Screen color control
+        // "...[screencolor:red]..." etc.
+        bool fHasScreenControl;      // true if control screen color metadata
+        ZoneReader* fRedReader;
+        ZoneReader* fGreenReader;
+        ZoneReader* fBlueReader;
+
+        // Current values controlled by metadata
+        std::string fCurrentUnit;
+        int fCurrentScale;
+        std::string fCurrentAcc;
+        std::string fCurrentGyr;
+        std::string fCurrentColor;
+        std::string fCurrentTooltip;
+        std::map<std::string, std::string> fCurrentMetadata;
+    
+        // Add a generic parameter
+        virtual void addParameter(const char* label,
+                                FAUSTFLOAT* zone,
+                                FAUSTFLOAT init,
+                                FAUSTFLOAT min,
+                                FAUSTFLOAT max,
+                                FAUSTFLOAT step,
+                                ItemType type)
+        {
+            std::string path = buildPath(label);
+            fPathMap[path] = fLabelMap[label] = fNumParameters++;
+            fPaths.push_back(path);
+            fLabels.push_back(label);
+            fZone.push_back(zone);
+            fInit.push_back(init);
+            fMin.push_back(min);
+            fMax.push_back(max);
+            fStep.push_back(step);
+            fItemType.push_back(type);
+            
+            // handle scale metadata
+            switch (fCurrentScale) {
+                case kLin:
+                    fConversion.push_back(new LinearValueConverter(0, 1, min, max));
+                    break;
+                case kLog:
+                    fConversion.push_back(new LogValueConverter(0, 1, min, max));
+                    break;
+                case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max));
+                    break;
+            }
+            fCurrentScale = kLin;
+            
+            if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) {
+                std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n";
+            }
+
+            // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentAcc.size() > 0) {
+                std::istringstream iss(fCurrentAcc);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl;
+                }
+                fCurrentAcc = "";
+            }
+       
+            // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentGyr.size() > 0) {
+                std::istringstream iss(fCurrentGyr);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl;
+                }
+                fCurrentGyr = "";
+            }
+        
+            // handle screencolor metadata "...[screencolor:red|green|blue|white]..."
+            if (fCurrentColor.size() > 0) {
+                if ((fCurrentColor == "red") && (fRedReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "green") && (fGreenReader == 0)) {
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) {
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else {
+                    std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl;
+                }
+            }
+            fCurrentColor = "";
+            
+            fMetaData.push_back(fCurrentMetadata);
+            fCurrentMetadata.clear();
+        }
+
+        int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val)
+        {
+            FAUSTFLOAT* zone = fZone[p];
+            for (size_t i = 0; i < table[val].size(); i++) {
+                if (zone == table[val][i]->getZone()) return int(i);
+            }
+            return -1;
+        }
+    
+        void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            // Deactivates everywhere..
+            if (id1 != -1) table[0][id1]->setActive(false);
+            if (id2 != -1) table[1][id2]->setActive(false);
+            if (id3 != -1) table[2][id3]->setActive(false);
+            
+            if (val == -1) { // Means: no more mapping...
+                // So stay all deactivated...
+            } else {
+                int id4 = getZoneIndex(table, p, val);
+                if (id4 != -1) {
+                    // Reactivate the one we edit...
+                    table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]);
+                    table[val][id4]->setActive(true);
+                } else {
+                    // Allocate a new CurveZoneControl which is 'active' by default
+                    FAUSTFLOAT* zone = fZone[p];
+                    table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]));
+                }
+            }
+        }
+    
+        void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            if (id1 != -1) {
+                val = 0;
+                curve = table[val][id1]->getCurve();
+                table[val][id1]->getMappingValues(amin, amid, amax);
+            } else if (id2 != -1) {
+                val = 1;
+                curve = table[val][id2]->getCurve();
+                table[val][id2]->getMappingValues(amin, amid, amax);
+            } else if (id3 != -1) {
+                val = 2;
+                curve = table[val][id3]->getCurve();
+                table[val][id3]->getMappingValues(amin, amid, amax);
+            } else {
+                val = -1; // No mapping
+                curve = 0;
+                amin = -100.;
+                amid = 0.;
+                amax = 100.;
+            }
+        }
+
+     public:
+    
+        enum Type { kAcc = 0, kGyr = 1, kNoType };
+   
+        APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin)
+        {}
+
+        virtual ~APIUI()
+        {
+            for (auto& it : fConversion) delete it;
+            for (int i = 0; i < 3; i++) {
+                for (auto& it : fAcc[i]) delete it;
+                for (auto& it : fGyr[i]) delete it;
+            }
+            delete fRedReader;
+            delete fGreenReader;
+            delete fBlueReader;
+        }
+    
+        // -- widget's layouts
+
+        virtual void openTabBox(const char* label) { pushLabel(label); }
+        virtual void openHorizontalBox(const char* label) { pushLabel(label); }
+        virtual void openVerticalBox(const char* label) { pushLabel(label); }
+        virtual void closeBox() { popLabel(); }
+
+        // -- active widgets
+
+        virtual void addButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kButton);
+        }
+
+        virtual void addCheckButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kCheckButton);
+        }
+
+        virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kVSlider);
+        }
+
+        virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kHSlider);
+        }
+
+        virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kNumEntry);
+        }
+
+        // -- passive widgets
+
+        virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph);
+        }
+
+        virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph);
+        }
+    
+        // -- soundfiles
+    
+        virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {}
+
+        // -- metadata declarations
+
+        virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val)
+        {
+            // Keep metadata
+            fCurrentMetadata[key] = val;
+            
+            if (strcmp(key, "scale") == 0) {
+                if (strcmp(val, "log") == 0) {
+                    fCurrentScale = kLog;
+                } else if (strcmp(val, "exp") == 0) {
+                    fCurrentScale = kExp;
+                } else {
+                    fCurrentScale = kLin;
+                }
+            } else if (strcmp(key, "unit") == 0) {
+                fCurrentUnit = val;
+            } else if (strcmp(key, "acc") == 0) {
+                fCurrentAcc = val;
+            } else if (strcmp(key, "gyr") == 0) {
+                fCurrentGyr = val;
+            } else if (strcmp(key, "screencolor") == 0) {
+                fCurrentColor = val; // val = "red", "green", "blue" or "white"
+            } else if (strcmp(key, "tooltip") == 0) {
+                fCurrentTooltip = val;
+            }
+        }
+
+        virtual void declare(const char* key, const char* val)
+        {}
+
+               //-------------------------------------------------------------------------------
+               // Simple API part
+               //-------------------------------------------------------------------------------
+               int getParamsCount() { return fNumParameters; }
+        int getParamIndex(const char* path)
+        {
+            if (fPathMap.find(path) != fPathMap.end()) {
+                return fPathMap[path];
+            } else if (fLabelMap.find(path) != fLabelMap.end()) {
+                return fLabelMap[path];
+            } else {
+                return -1;
+            }
+        }
+        const char* getParamAddress(int p) { return fPaths[p].c_str(); }
+        const char* getParamLabel(int p) { return fLabels[p].c_str(); }
+        std::map<const char*, const char*> getMetadata(int p)
+        {
+            std::map<const char*, const char*> res;
+            std::map<std::string, std::string> metadata = fMetaData[p];
+            for (auto it : metadata) {
+                res[it.first.c_str()] = it.second.c_str();
+            }
+            return res;
+        }
+
+        const char* getMetadata(int p, const char* key)
+        {
+            return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : "";
+        }
+        FAUSTFLOAT getParamMin(int p) { return fMin[p]; }
+        FAUSTFLOAT getParamMax(int p) { return fMax[p]; }
+        FAUSTFLOAT getParamStep(int p) { return fStep[p]; }
+        FAUSTFLOAT getParamInit(int p) { return fInit[p]; }
+
+        FAUSTFLOAT* getParamZone(int p) { return fZone[p]; }
+        FAUSTFLOAT getParamValue(int p) { return *fZone[p]; }
+        void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; }
+
+        double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); }
+        void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); }
+
+        double value2ratio(int p, double r)    { return fConversion[p]->faust2ui(r); }
+        double ratio2value(int p, double r)    { return fConversion[p]->ui2faust(r); }
+    
+        /**
+         * Return the control type (kAcc, kGyr, or -1) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the type
+         */
+        Type getParamType(int p)
+        {
+            if (p >= 0) {
+                if (getZoneIndex(fAcc, p, 0) != -1
+                    || getZoneIndex(fAcc, p, 1) != -1
+                    || getZoneIndex(fAcc, p, 2) != -1) {
+                    return kAcc;
+                } else if (getZoneIndex(fGyr, p, 0) != -1
+                           || getZoneIndex(fGyr, p, 1) != -1
+                           || getZoneIndex(fGyr, p, 2) != -1) {
+                    return kGyr;
+                }
+            }
+            return kNoType;
+        }
+    
+        /**
+         * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the Item type
+         */
+        ItemType getParamItemType(int p)
+        {
+            return fItemType[p];
+        }
+   
+        /**
+         * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @param value - the new value
+         *
+         */
+        void propagateAcc(int acc, double value)
+        {
+            for (size_t i = 0; i < fAcc[acc].size(); i++) {
+                fAcc[acc][i]->update(value);
+            }
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax)
+        {
+            setConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax)
+        {
+             setConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - the acc value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+
+        /**
+         * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param gyr - the gyr value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param value - the new value
+         *
+         */
+        void propagateGyr(int gyr, double value)
+        {
+            for (size_t i = 0; i < fGyr[gyr].size(); i++) {
+                fGyr[gyr][i]->update(value);
+            }
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the accelerometer
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @return the number of zones
+         *
+         */
+        int getAccCount(int acc)
+        {
+            return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0;
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the gyroscope
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param the number of zones
+         *
+         */
+        int getGyrCount(int gyr)
+        {
+            return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0;
+        }
+   
+        // getScreenColor() : -1 means no screen color control (no screencolor metadata found)
+        // otherwise return 0x00RRGGBB a ready to use color
+        int getScreenColor()
+        {
+            if (fHasScreenControl) {
+                int r = (fRedReader) ? fRedReader->getValue() : 0;
+                int g = (fGreenReader) ? fGreenReader->getValue() : 0;
+                int b = (fBlueReader) ? fBlueReader->getValue() : 0;
+                return (r<<16) | (g<<8) | b;
+            } else {
+                return -1;
+            }
+        }
+};
+
+#endif
+/**************************  END  APIUI.h **************************/
+
+// NOTE: "faust -scn name" changes the last line above to
+// #include <faust/name/name.h>
+
+//----------------------------------------------------------------------------
+//  FAUST Generated Code
+//----------------------------------------------------------------------------
+
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif 
+
+#include <algorithm>
+#include <cmath>
+#include <math.h>
+
+
+#ifndef FAUSTCLASS 
+#define FAUSTCLASS compressordsp
+#endif
+
+#ifdef __APPLE__ 
+#define exp10f __exp10f
+#define exp10 __exp10
+#endif
+
+class compressordsp : public dsp {
+       
+ private:
+       
+       FAUSTFLOAT fCheckbox0;
+       FAUSTFLOAT fHslider0;
+       int fSampleRate;
+       float fConst0;
+       FAUSTFLOAT fHslider1;
+       FAUSTFLOAT fHslider2;
+       FAUSTFLOAT fHslider3;
+       float fRec5[2];
+       float fRec4[2];
+       FAUSTFLOAT fHslider4;
+       float fRec3[2];
+       float fRec2[2];
+       float fRec1[2];
+       float fRec0[2];
+       FAUSTFLOAT fHbargraph0;
+       
+ public:
+       
+       void metadata(Meta* m) { 
+               m->declare("analyzers.lib/name", "Faust Analyzer Library");
+               m->declare("analyzers.lib/version", "0.1");
+               m->declare("author", "Julius Smith");
+               m->declare("basics.lib/name", "Faust Basic Element Library");
+               m->declare("basics.lib/version", "0.1");
+               m->declare("compressors.lib/compression_gain_mono:author", "Julius O. Smith III");
+               m->declare("compressors.lib/compression_gain_mono:copyright", "Copyright (C) 2014-2020 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("compressors.lib/compression_gain_mono:license", "MIT-style STK-4.3 license");
+               m->declare("compressors.lib/compressor_lad_mono:author", "Julius O. Smith III");
+               m->declare("compressors.lib/compressor_lad_mono:copyright", "Copyright (C) 2014-2020 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("compressors.lib/compressor_lad_mono:license", "MIT-style STK-4.3 license");
+               m->declare("compressors.lib/compressor_mono:author", "Julius O. Smith III");
+               m->declare("compressors.lib/compressor_mono:copyright", "Copyright (C) 2014-2020 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("compressors.lib/compressor_mono:license", "MIT-style STK-4.3 license");
+               m->declare("compressors.lib/name", "Faust Compressor Effect Library");
+               m->declare("compressors.lib/version", "0.0");
+               m->declare("description", "Compressor demo application, adapted from the Faust Library's dm.compressor_demo in demos.lib");
+               m->declare("documentation", "https://faustlibraries.grame.fr/libs/compressors/#cocompressor_mono");
+               m->declare("filename", "compressordsp.dsp");
+               m->declare("license", "MIT Style STK-4.2");
+               m->declare("maths.lib/author", "GRAME");
+               m->declare("maths.lib/copyright", "GRAME");
+               m->declare("maths.lib/license", "LGPL with exception");
+               m->declare("maths.lib/name", "Faust Math Library");
+               m->declare("maths.lib/version", "2.3");
+               m->declare("name", "compressor");
+               m->declare("platform.lib/name", "Generic Platform Library");
+               m->declare("platform.lib/version", "0.1");
+               m->declare("signals.lib/name", "Faust Signal Routing Library");
+               m->declare("signals.lib/version", "0.0");
+               m->declare("version", "0.0");
+       }
+
+       virtual int getNumInputs() {
+               return 1;
+       }
+       virtual int getNumOutputs() {
+               return 1;
+       }
+       virtual int getInputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       virtual int getOutputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       
+       static void classInit(int sample_rate) {
+       }
+       
+       virtual void instanceConstants(int sample_rate) {
+               fSampleRate = sample_rate;
+               fConst0 = (1.0f / std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate))));
+       }
+       
+       virtual void instanceResetUserInterface() {
+               fCheckbox0 = FAUSTFLOAT(0.0f);
+               fHslider0 = FAUSTFLOAT(2.0f);
+               fHslider1 = FAUSTFLOAT(15.0f);
+               fHslider2 = FAUSTFLOAT(2.0f);
+               fHslider3 = FAUSTFLOAT(40.0f);
+               fHslider4 = FAUSTFLOAT(-24.0f);
+       }
+       
+       virtual void instanceClear() {
+               for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
+                       fRec5[l0] = 0.0f;
+               }
+               for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) {
+                       fRec4[l1] = 0.0f;
+               }
+               for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) {
+                       fRec3[l2] = 0.0f;
+               }
+               for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) {
+                       fRec2[l3] = 0.0f;
+               }
+               for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) {
+                       fRec1[l4] = 0.0f;
+               }
+               for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) {
+                       fRec0[l5] = 0.0f;
+               }
+       }
+       
+       virtual void init(int sample_rate) {
+               classInit(sample_rate);
+               instanceInit(sample_rate);
+       }
+       virtual void instanceInit(int sample_rate) {
+               instanceConstants(sample_rate);
+               instanceResetUserInterface();
+               instanceClear();
+       }
+       
+       virtual compressordsp* clone() {
+               return new compressordsp();
+       }
+       
+       virtual int getSampleRate() {
+               return fSampleRate;
+       }
+       
+       virtual void buildUserInterface(UI* ui_interface) {
+               ui_interface->declare(0, "tooltip", "References:                 https://faustlibraries.grame.fr/libs/compressors/                 http://en.wikipedia.org/wiki/Dynamic_range_compression");
+               ui_interface->openVerticalBox("COMPRESSOR");
+               ui_interface->declare(0, "0", "");
+               ui_interface->openHorizontalBox("0x00");
+               ui_interface->declare(&fCheckbox0, "0", "");
+               ui_interface->declare(&fCheckbox0, "tooltip", "When this is checked, the compressor                 has no effect");
+               ui_interface->addCheckButton("Bypass", &fCheckbox0);
+               ui_interface->declare(&fHbargraph0, "1", "");
+               ui_interface->declare(&fHbargraph0, "tooltip", "Compressor gain in dB");
+               ui_interface->declare(&fHbargraph0, "unit", "dB");
+               ui_interface->addHorizontalBargraph("Compressor Gain", &fHbargraph0, -50.0f, 10.0f);
+               ui_interface->closeBox();
+               ui_interface->declare(0, "1", "");
+               ui_interface->openHorizontalBox("0x00");
+               ui_interface->declare(0, "3", "");
+               ui_interface->openHorizontalBox("Compression Control");
+               ui_interface->declare(&fHslider2, "0", "");
+               ui_interface->declare(&fHslider2, "style", "knob");
+               ui_interface->declare(&fHslider2, "tooltip", "A compression Ratio of N means that for each N dB increase in input         signal level above Threshold, the output level goes up 1 dB");
+               ui_interface->addHorizontalSlider("Ratio", &fHslider2, 2.0f, 1.0f, 20.0f, 0.100000001f);
+               ui_interface->declare(&fHslider4, "1", "");
+               ui_interface->declare(&fHslider4, "style", "knob");
+               ui_interface->declare(&fHslider4, "tooltip", "When the signal level exceeds the Threshold (in dB), its level         is compressed according to the Ratio");
+               ui_interface->declare(&fHslider4, "unit", "dB");
+               ui_interface->addHorizontalSlider("Threshold", &fHslider4, -24.0f, -100.0f, 10.0f, 0.100000001f);
+               ui_interface->closeBox();
+               ui_interface->declare(0, "4", "");
+               ui_interface->openHorizontalBox("Compression Response");
+               ui_interface->declare(&fHslider1, "1", "");
+               ui_interface->declare(&fHslider1, "scale", "log");
+               ui_interface->declare(&fHslider1, "style", "knob");
+               ui_interface->declare(&fHslider1, "tooltip", "Time constant in ms (1/e smoothing time) for the compression gain         to approach (exponentially) a new lower target level (the compression         `kicking in')");
+               ui_interface->declare(&fHslider1, "unit", "ms");
+               ui_interface->addHorizontalSlider("Attack", &fHslider1, 15.0f, 1.0f, 1000.0f, 0.100000001f);
+               ui_interface->declare(&fHslider3, "2", "");
+               ui_interface->declare(&fHslider3, "scale", "log");
+               ui_interface->declare(&fHslider3, "style", "knob");
+               ui_interface->declare(&fHslider3, "tooltip", "Time constant in ms (1/e smoothing time) for the compression gain         to approach (exponentially) a new higher target level (the compression         'releasing')");
+               ui_interface->declare(&fHslider3, "unit", "ms");
+               ui_interface->addHorizontalSlider("Release", &fHslider3, 40.0f, 1.0f, 1000.0f, 0.100000001f);
+               ui_interface->closeBox();
+               ui_interface->closeBox();
+               ui_interface->declare(&fHslider0, "5", "");
+               ui_interface->declare(&fHslider0, "tooltip", "The compressed-signal output level is increased by this amount         (in dB) to make up for the level lost due to compression");
+               ui_interface->declare(&fHslider0, "unit", "dB");
+               ui_interface->addHorizontalSlider("MakeUpGain", &fHslider0, 2.0f, -96.0f, 96.0f, 0.100000001f);
+               ui_interface->closeBox();
+       }
+       
+       virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+               FAUSTFLOAT* input0 = inputs[0];
+               FAUSTFLOAT* output0 = outputs[0];
+               int iSlow0 = int(float(fCheckbox0));
+               float fSlow1 = std::pow(10.0f, (0.0500000007f * float(fHslider0)));
+               float fSlow2 = std::max<float>(fConst0, (0.00100000005f * float(fHslider1)));
+               float fSlow3 = (0.5f * fSlow2);
+               int iSlow4 = (std::fabs(fSlow3) < 1.1920929e-07f);
+               float fSlow5 = (iSlow4 ? 0.0f : std::exp((0.0f - (fConst0 / (iSlow4 ? 1.0f : fSlow3)))));
+               float fSlow6 = ((1.0f / std::max<float>(1.00000001e-07f, float(fHslider2))) + -1.0f);
+               int iSlow7 = (std::fabs(fSlow2) < 1.1920929e-07f);
+               float fSlow8 = (iSlow7 ? 0.0f : std::exp((0.0f - (fConst0 / (iSlow7 ? 1.0f : fSlow2)))));
+               float fSlow9 = std::max<float>(fConst0, (0.00100000005f * float(fHslider3)));
+               int iSlow10 = (std::fabs(fSlow9) < 1.1920929e-07f);
+               float fSlow11 = (iSlow10 ? 0.0f : std::exp((0.0f - (fConst0 / (iSlow10 ? 1.0f : fSlow9)))));
+               float fSlow12 = float(fHslider4);
+               float fSlow13 = (1.0f - fSlow5);
+               for (int i = 0; (i < count); i = (i + 1)) {
+                       float fTemp0 = float(input0[i]);
+                       float fTemp1 = (iSlow0 ? 0.0f : fTemp0);
+                       float fTemp2 = std::fabs(fTemp1);
+                       float fTemp3 = ((fRec4[1] > fTemp2) ? fSlow11 : fSlow8);
+                       fRec5[0] = ((fRec5[1] * fTemp3) + (fTemp2 * (1.0f - fTemp3)));
+                       fRec4[0] = fRec5[0];
+                       fRec3[0] = ((fRec3[1] * fSlow5) + (fSlow6 * (std::max<float>(((20.0f * std::log10(fRec4[0])) - fSlow12), 0.0f) * fSlow13)));
+                       float fTemp4 = (fTemp1 * std::pow(10.0f, (0.0500000007f * fRec3[0])));
+                       float fTemp5 = std::fabs(fTemp4);
+                       float fTemp6 = ((fRec1[1] > fTemp5) ? fSlow11 : fSlow8);
+                       fRec2[0] = ((fRec2[1] * fTemp6) + (fTemp5 * (1.0f - fTemp6)));
+                       fRec1[0] = fRec2[0];
+                       fRec0[0] = ((fSlow5 * fRec0[1]) + (fSlow6 * (std::max<float>(((20.0f * std::log10(fRec1[0])) - fSlow12), 0.0f) * fSlow13)));
+                       fHbargraph0 = FAUSTFLOAT((20.0f * std::log10(std::pow(10.0f, (0.0500000007f * fRec0[0])))));
+                       output0[i] = FAUSTFLOAT((iSlow0 ? fTemp0 : (fSlow1 * fTemp4)));
+                       fRec5[1] = fRec5[0];
+                       fRec4[1] = fRec4[0];
+                       fRec3[1] = fRec3[0];
+                       fRec2[1] = fRec2[0];
+                       fRec1[1] = fRec1[0];
+                       fRec0[1] = fRec0[0];
+               }
+       }
+
+};
+
+#endif
diff --git a/src/freeverbdsp.h b/src/freeverbdsp.h
new file mode 100644 (file)
index 0000000..4ccaa5e
--- /dev/null
@@ -0,0 +1,2168 @@
+/* ------------------------------------------------------------
+author: "Romain Michon"
+license: "LGPL"
+name: "freeverb"
+version: "0.0"
+Code generated with Faust 2.28.6 (https://faust.grame.fr)
+Compilation options: -lang cpp -inpl -scal -ftz 0
+------------------------------------------------------------ */
+
+#ifndef  __freeverbdsp_H__
+#define  __freeverbdsp_H__
+
+// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME
+//
+// faust2header.cpp - FAUST Architecture File
+// This is a simple variation of matlabplot.cpp in the Faust distribution
+// aimed at creating a simple C++ header file (.h) containing a Faust DSP.
+// See the Makefile for how to use it.
+
+/************************** BEGIN dsp.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __dsp__
+#define __dsp__
+
+#include <string>
+#include <vector>
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+struct UI;
+struct Meta;
+
+/**
+ * DSP memory manager.
+ */
+
+struct dsp_memory_manager {
+    
+    virtual ~dsp_memory_manager() {}
+    
+    virtual void* allocate(size_t size) = 0;
+    virtual void destroy(void* ptr) = 0;
+    
+};
+
+/**
+* Signal processor definition.
+*/
+
+class dsp {
+
+    public:
+
+        dsp() {}
+        virtual ~dsp() {}
+
+        /* Return instance number of audio inputs */
+        virtual int getNumInputs() = 0;
+    
+        /* Return instance number of audio outputs */
+        virtual int getNumOutputs() = 0;
+    
+        /**
+         * Trigger the ui_interface parameter with instance specific calls
+         * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI.
+         *
+         * @param ui_interface - the user interface builder
+         */
+        virtual void buildUserInterface(UI* ui_interface) = 0;
+    
+        /* Returns the sample rate currently used by the instance */
+        virtual int getSampleRate() = 0;
+    
+        /**
+         * Global init, calls the following methods:
+         * - static class 'classInit': static tables initialization
+         * - 'instanceInit': constants and instance state initialization
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void init(int sample_rate) = 0;
+
+        /**
+         * Init instance state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceInit(int sample_rate) = 0;
+
+        /**
+         * Init instance constant state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceConstants(int sample_rate) = 0;
+    
+        /* Init default control parameters values */
+        virtual void instanceResetUserInterface() = 0;
+    
+        /* Init instance state (delay lines...) */
+        virtual void instanceClear() = 0;
+        /**
+         * Return a clone of the instance.
+         *
+         * @return a copy of the instance on success, otherwise a null pointer.
+         */
+        virtual dsp* clone() = 0;
+    
+        /**
+         * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata.
+         *
+         * @param m - the Meta* meta user
+         */
+        virtual void metadata(Meta* m) = 0;
+    
+        /**
+         * DSP instance computation, to be called with successive in/out audio buffers.
+         *
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         *
+         */
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0;
+    
+        /**
+         * DSP instance computation: alternative method to be used by subclasses.
+         *
+         * @param date_usec - the timestamp in microsec given by audio driver.
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         *
+         */
+        virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); }
+       
+};
+
+/**
+ * Generic DSP decorator.
+ */
+
+class decorator_dsp : public dsp {
+
+    protected:
+
+        dsp* fDSP;
+
+    public:
+
+        decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {}
+        virtual ~decorator_dsp() { delete fDSP; }
+
+        virtual int getNumInputs() { return fDSP->getNumInputs(); }
+        virtual int getNumOutputs() { return fDSP->getNumOutputs(); }
+        virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); }
+        virtual int getSampleRate() { return fDSP->getSampleRate(); }
+        virtual void init(int sample_rate) { fDSP->init(sample_rate); }
+        virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); }
+        virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); }
+        virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); }
+        virtual void instanceClear() { fDSP->instanceClear(); }
+        virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); }
+        virtual void metadata(Meta* m) { fDSP->metadata(m); }
+        // Beware: subclasses usually have to overload the two 'compute' methods
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); }
+        virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); }
+    
+};
+
+/**
+ * DSP factory class.
+ */
+
+class dsp_factory {
+    
+    protected:
+    
+        // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory);
+        virtual ~dsp_factory() {}
+    
+    public:
+    
+        virtual std::string getName() = 0;
+        virtual std::string getSHAKey() = 0;
+        virtual std::string getDSPCode() = 0;
+        virtual std::string getCompileOptions() = 0;
+        virtual std::vector<std::string> getLibraryList() = 0;
+        virtual std::vector<std::string> getIncludePathnames() = 0;
+    
+        virtual dsp* createDSPInstance() = 0;
+    
+        virtual void setMemoryManager(dsp_memory_manager* manager) = 0;
+        virtual dsp_memory_manager* getMemoryManager() = 0;
+    
+};
+
+/**
+ * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
+ * flags to avoid costly denormals.
+ */
+
+#ifdef __SSE__
+    #include <xmmintrin.h>
+    #ifdef __SSE2__
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
+    #else
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
+    #endif
+#else
+    #define AVOIDDENORMALS
+#endif
+
+#endif
+/**************************  END  dsp.h **************************/
+
+/************************** BEGIN APIUI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef API_UI_H
+#define API_UI_H
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <map>
+
+/************************** BEGIN meta.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __meta__
+#define __meta__
+
+struct Meta
+{
+    virtual ~Meta() {};
+    virtual void declare(const char* key, const char* value) = 0;
+    
+};
+
+#endif
+/**************************  END  meta.h **************************/
+/************************** BEGIN UI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __UI_H__
+#define __UI_H__
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+/*******************************************************************************
+ * UI : Faust DSP User Interface
+ * User Interface as expected by the buildUserInterface() method of a DSP.
+ * This abstract class contains only the method that the Faust compiler can
+ * generate to describe a DSP user interface.
+ ******************************************************************************/
+
+struct Soundfile;
+
+template <typename REAL>
+struct UIReal
+{
+    UIReal() {}
+    virtual ~UIReal() {}
+    
+    // -- widget's layouts
+    
+    virtual void openTabBox(const char* label) = 0;
+    virtual void openHorizontalBox(const char* label) = 0;
+    virtual void openVerticalBox(const char* label) = 0;
+    virtual void closeBox() = 0;
+    
+    // -- active widgets
+    
+    virtual void addButton(const char* label, REAL* zone) = 0;
+    virtual void addCheckButton(const char* label, REAL* zone) = 0;
+    virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    
+    // -- passive widgets
+    
+    virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    
+    // -- soundfiles
+    
+    virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0;
+    
+    // -- metadata declarations
+    
+    virtual void declare(REAL* zone, const char* key, const char* val) {}
+};
+
+struct UI : public UIReal<FAUSTFLOAT>
+{
+    UI() {}
+    virtual ~UI() {}
+};
+
+#endif
+/**************************  END  UI.h **************************/
+/************************** BEGIN PathBuilder.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef FAUST_PATHBUILDER_H
+#define FAUST_PATHBUILDER_H
+
+#include <vector>
+#include <string>
+#include <algorithm>
+
+/*******************************************************************************
+ * PathBuilder : Faust User Interface
+ * Helper class to build complete hierarchical path for UI items.
+ ******************************************************************************/
+
+class PathBuilder
+{
+
+    protected:
+    
+        std::vector<std::string> fControlsLevel;
+       
+    public:
+    
+        PathBuilder() {}
+        virtual ~PathBuilder() {}
+    
+        std::string buildPath(const std::string& label) 
+        {
+            std::string res = "/";
+            for (size_t i = 0; i < fControlsLevel.size(); i++) {
+                res += fControlsLevel[i];
+                res += "/";
+            }
+            res += label;
+            std::replace(res.begin(), res.end(), ' ', '_');
+            return res;
+        }
+    
+        std::string buildLabel(std::string label)
+        {
+            std::replace(label.begin(), label.end(), ' ', '_');
+            return label;
+        }
+    
+        void pushLabel(const std::string& label) { fControlsLevel.push_back(label); }
+        void popLabel() { fControlsLevel.pop_back(); }
+    
+};
+
+#endif  // FAUST_PATHBUILDER_H
+/**************************  END  PathBuilder.h **************************/
+/************************** BEGIN ValueConverter.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __ValueConverter__
+#define __ValueConverter__
+
+/***************************************************************************************
+                                                               ValueConverter.h
+                            (GRAME, Copyright 2015-2019)
+
+Set of conversion objects used to map user interface values (for example a gui slider
+delivering values between 0 and 1) to faust values (for example a vslider between
+20 and 20000) using a log scale.
+
+-- Utilities
+
+Range(lo,hi) : clip a value x between lo and hi
+Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2
+Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2
+
+-- Value Converters
+
+ValueConverter::ui2faust(x)
+ValueConverter::faust2ui(x)
+
+-- ValueConverters used for sliders depending of the scale
+
+LinearValueConverter(umin, umax, fmin, fmax)
+LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments
+LogValueConverter(umin, umax, fmin, fmax)
+ExpValueConverter(umin, umax, fmin, fmax)
+
+-- ValueConverters used for accelerometers based on 3 points
+
+AccUpConverter(amin, amid, amax, fmin, fmid, fmax)             -- curve 0
+AccDownConverter(amin, amid, amax, fmin, fmid, fmax)   -- curve 1
+AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2
+AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3
+
+-- lists of ZoneControl are used to implement accelerometers metadata for each axes
+
+ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter
+
+-- ZoneReader are used to implement screencolor metadata
+
+ZoneReader(zone, valueConverter) : a zone with a data converter
+
+****************************************************************************************/
+
+#include <float.h>
+#include <algorithm>    // std::max
+#include <cmath>
+#include <vector>
+#include <assert.h>
+
+//--------------------------------------------------------------------------------------
+// Interpolator(lo,hi,v1,v2)
+// Maps a value x between lo and hi to a value y between v1 and v2
+// y = v1 + (x-lo)/(hi-lo)*(v2-v1)
+// y = v1 + (x-lo) * coef              with coef = (v2-v1)/(hi-lo)
+// y = v1 + x*coef - lo*coef
+// y = v1 - lo*coef + x*coef
+// y = offset + x*coef                         with offset = v1 - lo*coef
+//--------------------------------------------------------------------------------------
+class Interpolator
+{
+    private:
+
+        //--------------------------------------------------------------------------------------
+        // Range(lo,hi) clip a value between lo and hi
+        //--------------------------------------------------------------------------------------
+        struct Range
+        {
+            double fLo;
+            double fHi;
+
+            Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {}
+            double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; }
+        };
+
+
+        Range fRange;
+        double fCoef;
+        double fOffset;
+
+    public:
+
+        Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi)
+        {
+            if (hi != lo) {
+                // regular case
+                fCoef = (v2-v1)/(hi-lo);
+                fOffset = v1 - lo*fCoef;
+            } else {
+                // degenerate case, avoids division by zero
+                fCoef = 0;
+                fOffset = (v1+v2)/2;
+            }
+        }
+        double operator()(double v)
+        {
+            double x = fRange(v);
+            return  fOffset + x*fCoef;
+        }
+
+        void getLowHigh(double& amin, double& amax)
+        {
+            amin = fRange.fLo;
+            amax = fRange.fHi;
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Interpolator3pt(lo,mi,hi,v1,vm,v2)
+// Map values between lo mid hi to values between v1 vm v2
+//--------------------------------------------------------------------------------------
+class Interpolator3pt
+{
+
+    private:
+
+        Interpolator fSegment1;
+        Interpolator fSegment2;
+        double fMid;
+
+    public:
+
+        Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) :
+            fSegment1(lo, mi, v1, vm),
+            fSegment2(mi, hi, vm, v2),
+            fMid(mi) {}
+        double operator()(double x) { return  (x < fMid) ? fSegment1(x) : fSegment2(x); }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fSegment1.getLowHigh(amin, amid);
+            fSegment2.getLowHigh(amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Abstract ValueConverter class. Converts values between UI and Faust representations
+//--------------------------------------------------------------------------------------
+class ValueConverter
+{
+
+    public:
+
+        virtual ~ValueConverter() {}
+        virtual double ui2faust(double x) = 0;
+        virtual double faust2ui(double x) = 0;
+};
+
+//--------------------------------------------------------------------------------------
+// A converter than can be updated
+//--------------------------------------------------------------------------------------
+
+class UpdatableValueConverter : public ValueConverter {
+    
+    protected:
+        
+        bool fActive;
+        
+    public:
+        
+        UpdatableValueConverter():fActive(true)
+        {}
+        virtual ~UpdatableValueConverter()
+        {}
+        
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0;
+        virtual void getMappingValues(double& amin, double& amid, double& amax) = 0;
+        
+        void setActive(bool on_off) { fActive = on_off; }
+        bool getActive() { return fActive; }
+    
+};
+
+
+//--------------------------------------------------------------------------------------
+// Linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter : public ValueConverter
+{
+    
+    private:
+        
+        Interpolator fUI2F;
+        Interpolator fF2UI;
+        
+    public:
+        
+        LinearValueConverter(double umin, double umax, double fmin, double fmax) :
+            fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax)
+        {}
+        
+        LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.)
+        {}
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Two segments linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter2 : public UpdatableValueConverter
+{
+    
+    private:
+    
+        Interpolator3pt fUI2F;
+        Interpolator3pt fF2UI;
+        
+    public:
+    
+        LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) :
+            fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax)
+        {}
+        
+        LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.)
+        {}
+    
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max)
+        {
+            fUI2F = Interpolator3pt(amin, amid, amax, min, init, max);
+            fF2UI = Interpolator3pt(min, init, max, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fUI2F.getMappingValues(amin, amid, amax);
+        }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Logarithmic conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LogValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        LogValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Exponential conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class ExpValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        ExpValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up curve (curve 0)
+//--------------------------------------------------------------------------------------
+class AccUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt fA2F;
+        Interpolator3pt fF2A;
+
+    public:
+
+        AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmid,fmax),
+            fF2A(fmin,fmid,fmax,amin,amid,amax)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down curve (curve 1)
+//--------------------------------------------------------------------------------------
+class AccDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator3pt        fF2A;
+
+    public:
+
+        AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmid,fmin),
+            fF2A(fmin,fmid,fmax,amax,amid,amin)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+             //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up-Down curve (curve 2)
+//--------------------------------------------------------------------------------------
+class AccUpDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmax,fmin),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down-Up curve (curve 3)
+//--------------------------------------------------------------------------------------
+class AccDownUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmin,fmax),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Base class for ZoneControl
+//--------------------------------------------------------------------------------------
+class ZoneControl
+{
+
+    protected:
+
+        FAUSTFLOAT*    fZone;
+
+    public:
+
+        ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {}
+        virtual ~ZoneControl() {}
+
+        virtual void update(double v) const {}
+
+        virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {}
+        virtual void getMappingValues(double& amin, double& amid, double& amax) {}
+
+        FAUSTFLOAT* getZone() { return fZone; }
+
+        virtual void setActive(bool on_off) {}
+        virtual bool getActive() { return false; }
+
+        virtual int getCurve() { return -1; }
+
+};
+
+//--------------------------------------------------------------------------------------
+//  Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class ConverterZoneControl : public ZoneControl
+{
+
+    protected:
+
+        ValueConverter* fValueConverter;
+
+    public:
+
+        ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {}
+        virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere...
+
+        virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); }
+
+        ValueConverter* getConverter() { return fValueConverter; }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Association of a zone and a four value converter, each one for each possible curve.
+// Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class CurveZoneControl : public ZoneControl
+{
+
+    private:
+
+        std::vector<UpdatableValueConverter*> fValueConverters;
+        int fCurve;
+
+    public:
+
+        CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0)
+        {
+            assert(curve >= 0 && curve <= 3);
+            fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max));
+            fCurve = curve;
+        }
+        virtual ~CurveZoneControl()
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                delete(*it);
+            }
+        }
+        void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); }
+
+        void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max)
+        {
+            fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max);
+            fCurve = curve;
+        }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fValueConverters[fCurve]->getMappingValues(amin, amid, amax);
+        }
+
+        void setActive(bool on_off)
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                (*it)->setActive(on_off);
+            }
+        }
+
+        int getCurve() { return fCurve; }
+};
+
+class ZoneReader
+{
+
+    private:
+
+        FAUSTFLOAT* fZone;
+        Interpolator fInterpolator;
+
+    public:
+
+        ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {}
+
+        virtual ~ZoneReader() {}
+
+        int getValue()
+        {
+            return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127;
+        }
+
+};
+
+#endif
+/**************************  END  ValueConverter.h **************************/
+
+class APIUI : public PathBuilder, public Meta, public UI
+{
+    public:
+    
+        enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph };
+  
+    protected:
+    
+        enum { kLin = 0, kLog = 1, kExp = 2 };
+    
+        int fNumParameters;
+        std::vector<std::string> fPaths;
+        std::vector<std::string> fLabels;
+        std::map<std::string, int> fPathMap;
+        std::map<std::string, int> fLabelMap;
+        std::vector<ValueConverter*> fConversion;
+        std::vector<FAUSTFLOAT*> fZone;
+        std::vector<FAUSTFLOAT> fInit;
+        std::vector<FAUSTFLOAT> fMin;
+        std::vector<FAUSTFLOAT> fMax;
+        std::vector<FAUSTFLOAT> fStep;
+        std::vector<ItemType> fItemType;
+        std::vector<std::map<std::string, std::string> > fMetaData;
+        std::vector<ZoneControl*> fAcc[3];
+        std::vector<ZoneControl*> fGyr[3];
+
+        // Screen color control
+        // "...[screencolor:red]..." etc.
+        bool fHasScreenControl;      // true if control screen color metadata
+        ZoneReader* fRedReader;
+        ZoneReader* fGreenReader;
+        ZoneReader* fBlueReader;
+
+        // Current values controlled by metadata
+        std::string fCurrentUnit;
+        int fCurrentScale;
+        std::string fCurrentAcc;
+        std::string fCurrentGyr;
+        std::string fCurrentColor;
+        std::string fCurrentTooltip;
+        std::map<std::string, std::string> fCurrentMetadata;
+    
+        // Add a generic parameter
+        virtual void addParameter(const char* label,
+                                FAUSTFLOAT* zone,
+                                FAUSTFLOAT init,
+                                FAUSTFLOAT min,
+                                FAUSTFLOAT max,
+                                FAUSTFLOAT step,
+                                ItemType type)
+        {
+            std::string path = buildPath(label);
+            fPathMap[path] = fLabelMap[label] = fNumParameters++;
+            fPaths.push_back(path);
+            fLabels.push_back(label);
+            fZone.push_back(zone);
+            fInit.push_back(init);
+            fMin.push_back(min);
+            fMax.push_back(max);
+            fStep.push_back(step);
+            fItemType.push_back(type);
+            
+            // handle scale metadata
+            switch (fCurrentScale) {
+                case kLin:
+                    fConversion.push_back(new LinearValueConverter(0, 1, min, max));
+                    break;
+                case kLog:
+                    fConversion.push_back(new LogValueConverter(0, 1, min, max));
+                    break;
+                case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max));
+                    break;
+            }
+            fCurrentScale = kLin;
+            
+            if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) {
+                std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n";
+            }
+
+            // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentAcc.size() > 0) {
+                std::istringstream iss(fCurrentAcc);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl;
+                }
+                fCurrentAcc = "";
+            }
+       
+            // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentGyr.size() > 0) {
+                std::istringstream iss(fCurrentGyr);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl;
+                }
+                fCurrentGyr = "";
+            }
+        
+            // handle screencolor metadata "...[screencolor:red|green|blue|white]..."
+            if (fCurrentColor.size() > 0) {
+                if ((fCurrentColor == "red") && (fRedReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "green") && (fGreenReader == 0)) {
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) {
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else {
+                    std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl;
+                }
+            }
+            fCurrentColor = "";
+            
+            fMetaData.push_back(fCurrentMetadata);
+            fCurrentMetadata.clear();
+        }
+
+        int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val)
+        {
+            FAUSTFLOAT* zone = fZone[p];
+            for (size_t i = 0; i < table[val].size(); i++) {
+                if (zone == table[val][i]->getZone()) return int(i);
+            }
+            return -1;
+        }
+    
+        void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            // Deactivates everywhere..
+            if (id1 != -1) table[0][id1]->setActive(false);
+            if (id2 != -1) table[1][id2]->setActive(false);
+            if (id3 != -1) table[2][id3]->setActive(false);
+            
+            if (val == -1) { // Means: no more mapping...
+                // So stay all deactivated...
+            } else {
+                int id4 = getZoneIndex(table, p, val);
+                if (id4 != -1) {
+                    // Reactivate the one we edit...
+                    table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]);
+                    table[val][id4]->setActive(true);
+                } else {
+                    // Allocate a new CurveZoneControl which is 'active' by default
+                    FAUSTFLOAT* zone = fZone[p];
+                    table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]));
+                }
+            }
+        }
+    
+        void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            if (id1 != -1) {
+                val = 0;
+                curve = table[val][id1]->getCurve();
+                table[val][id1]->getMappingValues(amin, amid, amax);
+            } else if (id2 != -1) {
+                val = 1;
+                curve = table[val][id2]->getCurve();
+                table[val][id2]->getMappingValues(amin, amid, amax);
+            } else if (id3 != -1) {
+                val = 2;
+                curve = table[val][id3]->getCurve();
+                table[val][id3]->getMappingValues(amin, amid, amax);
+            } else {
+                val = -1; // No mapping
+                curve = 0;
+                amin = -100.;
+                amid = 0.;
+                amax = 100.;
+            }
+        }
+
+     public:
+    
+        enum Type { kAcc = 0, kGyr = 1, kNoType };
+   
+        APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin)
+        {}
+
+        virtual ~APIUI()
+        {
+            for (auto& it : fConversion) delete it;
+            for (int i = 0; i < 3; i++) {
+                for (auto& it : fAcc[i]) delete it;
+                for (auto& it : fGyr[i]) delete it;
+            }
+            delete fRedReader;
+            delete fGreenReader;
+            delete fBlueReader;
+        }
+    
+        // -- widget's layouts
+
+        virtual void openTabBox(const char* label) { pushLabel(label); }
+        virtual void openHorizontalBox(const char* label) { pushLabel(label); }
+        virtual void openVerticalBox(const char* label) { pushLabel(label); }
+        virtual void closeBox() { popLabel(); }
+
+        // -- active widgets
+
+        virtual void addButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kButton);
+        }
+
+        virtual void addCheckButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kCheckButton);
+        }
+
+        virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kVSlider);
+        }
+
+        virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kHSlider);
+        }
+
+        virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kNumEntry);
+        }
+
+        // -- passive widgets
+
+        virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph);
+        }
+
+        virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph);
+        }
+    
+        // -- soundfiles
+    
+        virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {}
+
+        // -- metadata declarations
+
+        virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val)
+        {
+            // Keep metadata
+            fCurrentMetadata[key] = val;
+            
+            if (strcmp(key, "scale") == 0) {
+                if (strcmp(val, "log") == 0) {
+                    fCurrentScale = kLog;
+                } else if (strcmp(val, "exp") == 0) {
+                    fCurrentScale = kExp;
+                } else {
+                    fCurrentScale = kLin;
+                }
+            } else if (strcmp(key, "unit") == 0) {
+                fCurrentUnit = val;
+            } else if (strcmp(key, "acc") == 0) {
+                fCurrentAcc = val;
+            } else if (strcmp(key, "gyr") == 0) {
+                fCurrentGyr = val;
+            } else if (strcmp(key, "screencolor") == 0) {
+                fCurrentColor = val; // val = "red", "green", "blue" or "white"
+            } else if (strcmp(key, "tooltip") == 0) {
+                fCurrentTooltip = val;
+            }
+        }
+
+        virtual void declare(const char* key, const char* val)
+        {}
+
+               //-------------------------------------------------------------------------------
+               // Simple API part
+               //-------------------------------------------------------------------------------
+               int getParamsCount() { return fNumParameters; }
+        int getParamIndex(const char* path)
+        {
+            if (fPathMap.find(path) != fPathMap.end()) {
+                return fPathMap[path];
+            } else if (fLabelMap.find(path) != fLabelMap.end()) {
+                return fLabelMap[path];
+            } else {
+                return -1;
+            }
+        }
+        const char* getParamAddress(int p) { return fPaths[p].c_str(); }
+        const char* getParamLabel(int p) { return fLabels[p].c_str(); }
+        std::map<const char*, const char*> getMetadata(int p)
+        {
+            std::map<const char*, const char*> res;
+            std::map<std::string, std::string> metadata = fMetaData[p];
+            for (auto it : metadata) {
+                res[it.first.c_str()] = it.second.c_str();
+            }
+            return res;
+        }
+
+        const char* getMetadata(int p, const char* key)
+        {
+            return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : "";
+        }
+        FAUSTFLOAT getParamMin(int p) { return fMin[p]; }
+        FAUSTFLOAT getParamMax(int p) { return fMax[p]; }
+        FAUSTFLOAT getParamStep(int p) { return fStep[p]; }
+        FAUSTFLOAT getParamInit(int p) { return fInit[p]; }
+
+        FAUSTFLOAT* getParamZone(int p) { return fZone[p]; }
+        FAUSTFLOAT getParamValue(int p) { return *fZone[p]; }
+        void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; }
+
+        double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); }
+        void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); }
+
+        double value2ratio(int p, double r)    { return fConversion[p]->faust2ui(r); }
+        double ratio2value(int p, double r)    { return fConversion[p]->ui2faust(r); }
+    
+        /**
+         * Return the control type (kAcc, kGyr, or -1) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the type
+         */
+        Type getParamType(int p)
+        {
+            if (p >= 0) {
+                if (getZoneIndex(fAcc, p, 0) != -1
+                    || getZoneIndex(fAcc, p, 1) != -1
+                    || getZoneIndex(fAcc, p, 2) != -1) {
+                    return kAcc;
+                } else if (getZoneIndex(fGyr, p, 0) != -1
+                           || getZoneIndex(fGyr, p, 1) != -1
+                           || getZoneIndex(fGyr, p, 2) != -1) {
+                    return kGyr;
+                }
+            }
+            return kNoType;
+        }
+    
+        /**
+         * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the Item type
+         */
+        ItemType getParamItemType(int p)
+        {
+            return fItemType[p];
+        }
+   
+        /**
+         * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @param value - the new value
+         *
+         */
+        void propagateAcc(int acc, double value)
+        {
+            for (size_t i = 0; i < fAcc[acc].size(); i++) {
+                fAcc[acc][i]->update(value);
+            }
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax)
+        {
+            setConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax)
+        {
+             setConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - the acc value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+
+        /**
+         * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param gyr - the gyr value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param value - the new value
+         *
+         */
+        void propagateGyr(int gyr, double value)
+        {
+            for (size_t i = 0; i < fGyr[gyr].size(); i++) {
+                fGyr[gyr][i]->update(value);
+            }
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the accelerometer
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @return the number of zones
+         *
+         */
+        int getAccCount(int acc)
+        {
+            return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0;
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the gyroscope
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param the number of zones
+         *
+         */
+        int getGyrCount(int gyr)
+        {
+            return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0;
+        }
+   
+        // getScreenColor() : -1 means no screen color control (no screencolor metadata found)
+        // otherwise return 0x00RRGGBB a ready to use color
+        int getScreenColor()
+        {
+            if (fHasScreenControl) {
+                int r = (fRedReader) ? fRedReader->getValue() : 0;
+                int g = (fGreenReader) ? fGreenReader->getValue() : 0;
+                int b = (fBlueReader) ? fBlueReader->getValue() : 0;
+                return (r<<16) | (g<<8) | b;
+            } else {
+                return -1;
+            }
+        }
+};
+
+#endif
+/**************************  END  APIUI.h **************************/
+
+// NOTE: "faust -scn name" changes the last line above to
+// #include <faust/name/name.h>
+
+//----------------------------------------------------------------------------
+//  FAUST Generated Code
+//----------------------------------------------------------------------------
+
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif 
+
+#include <algorithm>
+#include <cmath>
+#include <math.h>
+
+
+#ifndef FAUSTCLASS 
+#define FAUSTCLASS freeverbdsp
+#endif
+
+#ifdef __APPLE__ 
+#define exp10f __exp10f
+#define exp10 __exp10
+#endif
+
+class freeverbdsp : public dsp {
+       
+ private:
+       
+       int fSampleRate;
+       float fConst0;
+       float fConst1;
+       FAUSTFLOAT fVslider0;
+       float fConst2;
+       FAUSTFLOAT fVslider1;
+       float fRec9[2];
+       FAUSTFLOAT fVslider2;
+       int IOTA;
+       float fVec0[8192];
+       int iConst3;
+       float fRec8[2];
+       float fRec11[2];
+       float fVec1[8192];
+       int iConst4;
+       float fRec10[2];
+       float fRec13[2];
+       float fVec2[8192];
+       int iConst5;
+       float fRec12[2];
+       float fRec15[2];
+       float fVec3[8192];
+       int iConst6;
+       float fRec14[2];
+       float fRec17[2];
+       float fVec4[8192];
+       int iConst7;
+       float fRec16[2];
+       float fRec19[2];
+       float fVec5[8192];
+       int iConst8;
+       float fRec18[2];
+       float fRec21[2];
+       float fVec6[8192];
+       int iConst9;
+       float fRec20[2];
+       float fRec23[2];
+       float fVec7[8192];
+       int iConst10;
+       float fRec22[2];
+       float fVec8[2048];
+       int iConst11;
+       int iConst12;
+       float fRec6[2];
+       float fVec9[2048];
+       int iConst13;
+       int iConst14;
+       float fRec4[2];
+       float fVec10[2048];
+       int iConst15;
+       int iConst16;
+       float fRec2[2];
+       float fVec11[1024];
+       int iConst17;
+       int iConst18;
+       float fRec0[2];
+       float fRec33[2];
+       float fVec12[8192];
+       float fConst19;
+       FAUSTFLOAT fVslider3;
+       float fRec32[2];
+       float fRec35[2];
+       float fVec13[8192];
+       float fRec34[2];
+       float fRec37[2];
+       float fVec14[8192];
+       float fRec36[2];
+       float fRec39[2];
+       float fVec15[8192];
+       float fRec38[2];
+       float fRec41[2];
+       float fVec16[8192];
+       float fRec40[2];
+       float fRec43[2];
+       float fVec17[8192];
+       float fRec42[2];
+       float fRec45[2];
+       float fVec18[8192];
+       float fRec44[2];
+       float fRec47[2];
+       float fVec19[8192];
+       float fRec46[2];
+       float fVec20[2048];
+       float fRec30[2];
+       float fVec21[2048];
+       float fRec28[2];
+       float fVec22[2048];
+       float fRec26[2];
+       float fVec23[2048];
+       float fRec24[2];
+       
+ public:
+       
+       void metadata(Meta* m) { 
+               m->declare("author", "Romain Michon");
+               m->declare("delays.lib/name", "Faust Delay Library");
+               m->declare("delays.lib/version", "0.1");
+               m->declare("description", "Freeverb implementation in Faust, from the Faust Library's dm.freeverb_demo in demos.lib");
+               m->declare("filename", "freeverbdsp.dsp");
+               m->declare("filters.lib/allpass_comb:author", "Julius O. Smith III");
+               m->declare("filters.lib/allpass_comb:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/allpass_comb:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/lowpass0_highpass1", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/name", "Faust Filters Library");
+               m->declare("license", "LGPL");
+               m->declare("maths.lib/author", "GRAME");
+               m->declare("maths.lib/copyright", "GRAME");
+               m->declare("maths.lib/license", "LGPL with exception");
+               m->declare("maths.lib/name", "Faust Math Library");
+               m->declare("maths.lib/version", "2.3");
+               m->declare("name", "freeverb");
+               m->declare("platform.lib/name", "Generic Platform Library");
+               m->declare("platform.lib/version", "0.1");
+               m->declare("reverbs.lib/name", "Faust Reverb Library");
+               m->declare("reverbs.lib/version", "0.0");
+               m->declare("version", "0.0");
+       }
+
+       virtual int getNumInputs() {
+               return 2;
+       }
+       virtual int getNumOutputs() {
+               return 2;
+       }
+       virtual int getInputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       case 1: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       virtual int getOutputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       case 1: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       
+       static void classInit(int sample_rate) {
+       }
+       
+       virtual void instanceConstants(int sample_rate) {
+               fSampleRate = sample_rate;
+               fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate)));
+               fConst1 = (12348.0f / fConst0);
+               fConst2 = (17640.0f / fConst0);
+               iConst3 = int((0.0253061224f * fConst0));
+               iConst4 = int((0.0269387756f * fConst0));
+               iConst5 = int((0.0289569162f * fConst0));
+               iConst6 = int((0.0307482984f * fConst0));
+               iConst7 = int((0.0322448984f * fConst0));
+               iConst8 = int((0.033809524f * fConst0));
+               iConst9 = int((0.0353061222f * fConst0));
+               iConst10 = int((0.0366666652f * fConst0));
+               iConst11 = int((0.0126077095f * fConst0));
+               iConst12 = std::min<int>(1024, std::max<int>(0, (iConst11 + -1)));
+               iConst13 = int((0.00999999978f * fConst0));
+               iConst14 = std::min<int>(1024, std::max<int>(0, (iConst13 + -1)));
+               iConst15 = int((0.00773242628f * fConst0));
+               iConst16 = std::min<int>(1024, std::max<int>(0, (iConst15 + -1)));
+               iConst17 = int((0.00510204071f * fConst0));
+               iConst18 = std::min<int>(1024, std::max<int>(0, (iConst17 + -1)));
+               fConst19 = (0.00104308384f * fConst0);
+       }
+       
+       virtual void instanceResetUserInterface() {
+               fVslider0 = FAUSTFLOAT(0.10000000000000001f);
+               fVslider1 = FAUSTFLOAT(0.5f);
+               fVslider2 = FAUSTFLOAT(0.10000000000000001f);
+               fVslider3 = FAUSTFLOAT(0.5f);
+       }
+       
+       virtual void instanceClear() {
+               for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
+                       fRec9[l0] = 0.0f;
+               }
+               IOTA = 0;
+               for (int l1 = 0; (l1 < 8192); l1 = (l1 + 1)) {
+                       fVec0[l1] = 0.0f;
+               }
+               for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) {
+                       fRec8[l2] = 0.0f;
+               }
+               for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) {
+                       fRec11[l3] = 0.0f;
+               }
+               for (int l4 = 0; (l4 < 8192); l4 = (l4 + 1)) {
+                       fVec1[l4] = 0.0f;
+               }
+               for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) {
+                       fRec10[l5] = 0.0f;
+               }
+               for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) {
+                       fRec13[l6] = 0.0f;
+               }
+               for (int l7 = 0; (l7 < 8192); l7 = (l7 + 1)) {
+                       fVec2[l7] = 0.0f;
+               }
+               for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) {
+                       fRec12[l8] = 0.0f;
+               }
+               for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) {
+                       fRec15[l9] = 0.0f;
+               }
+               for (int l10 = 0; (l10 < 8192); l10 = (l10 + 1)) {
+                       fVec3[l10] = 0.0f;
+               }
+               for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) {
+                       fRec14[l11] = 0.0f;
+               }
+               for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) {
+                       fRec17[l12] = 0.0f;
+               }
+               for (int l13 = 0; (l13 < 8192); l13 = (l13 + 1)) {
+                       fVec4[l13] = 0.0f;
+               }
+               for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) {
+                       fRec16[l14] = 0.0f;
+               }
+               for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) {
+                       fRec19[l15] = 0.0f;
+               }
+               for (int l16 = 0; (l16 < 8192); l16 = (l16 + 1)) {
+                       fVec5[l16] = 0.0f;
+               }
+               for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) {
+                       fRec18[l17] = 0.0f;
+               }
+               for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) {
+                       fRec21[l18] = 0.0f;
+               }
+               for (int l19 = 0; (l19 < 8192); l19 = (l19 + 1)) {
+                       fVec6[l19] = 0.0f;
+               }
+               for (int l20 = 0; (l20 < 2); l20 = (l20 + 1)) {
+                       fRec20[l20] = 0.0f;
+               }
+               for (int l21 = 0; (l21 < 2); l21 = (l21 + 1)) {
+                       fRec23[l21] = 0.0f;
+               }
+               for (int l22 = 0; (l22 < 8192); l22 = (l22 + 1)) {
+                       fVec7[l22] = 0.0f;
+               }
+               for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) {
+                       fRec22[l23] = 0.0f;
+               }
+               for (int l24 = 0; (l24 < 2048); l24 = (l24 + 1)) {
+                       fVec8[l24] = 0.0f;
+               }
+               for (int l25 = 0; (l25 < 2); l25 = (l25 + 1)) {
+                       fRec6[l25] = 0.0f;
+               }
+               for (int l26 = 0; (l26 < 2048); l26 = (l26 + 1)) {
+                       fVec9[l26] = 0.0f;
+               }
+               for (int l27 = 0; (l27 < 2); l27 = (l27 + 1)) {
+                       fRec4[l27] = 0.0f;
+               }
+               for (int l28 = 0; (l28 < 2048); l28 = (l28 + 1)) {
+                       fVec10[l28] = 0.0f;
+               }
+               for (int l29 = 0; (l29 < 2); l29 = (l29 + 1)) {
+                       fRec2[l29] = 0.0f;
+               }
+               for (int l30 = 0; (l30 < 1024); l30 = (l30 + 1)) {
+                       fVec11[l30] = 0.0f;
+               }
+               for (int l31 = 0; (l31 < 2); l31 = (l31 + 1)) {
+                       fRec0[l31] = 0.0f;
+               }
+               for (int l32 = 0; (l32 < 2); l32 = (l32 + 1)) {
+                       fRec33[l32] = 0.0f;
+               }
+               for (int l33 = 0; (l33 < 8192); l33 = (l33 + 1)) {
+                       fVec12[l33] = 0.0f;
+               }
+               for (int l34 = 0; (l34 < 2); l34 = (l34 + 1)) {
+                       fRec32[l34] = 0.0f;
+               }
+               for (int l35 = 0; (l35 < 2); l35 = (l35 + 1)) {
+                       fRec35[l35] = 0.0f;
+               }
+               for (int l36 = 0; (l36 < 8192); l36 = (l36 + 1)) {
+                       fVec13[l36] = 0.0f;
+               }
+               for (int l37 = 0; (l37 < 2); l37 = (l37 + 1)) {
+                       fRec34[l37] = 0.0f;
+               }
+               for (int l38 = 0; (l38 < 2); l38 = (l38 + 1)) {
+                       fRec37[l38] = 0.0f;
+               }
+               for (int l39 = 0; (l39 < 8192); l39 = (l39 + 1)) {
+                       fVec14[l39] = 0.0f;
+               }
+               for (int l40 = 0; (l40 < 2); l40 = (l40 + 1)) {
+                       fRec36[l40] = 0.0f;
+               }
+               for (int l41 = 0; (l41 < 2); l41 = (l41 + 1)) {
+                       fRec39[l41] = 0.0f;
+               }
+               for (int l42 = 0; (l42 < 8192); l42 = (l42 + 1)) {
+                       fVec15[l42] = 0.0f;
+               }
+               for (int l43 = 0; (l43 < 2); l43 = (l43 + 1)) {
+                       fRec38[l43] = 0.0f;
+               }
+               for (int l44 = 0; (l44 < 2); l44 = (l44 + 1)) {
+                       fRec41[l44] = 0.0f;
+               }
+               for (int l45 = 0; (l45 < 8192); l45 = (l45 + 1)) {
+                       fVec16[l45] = 0.0f;
+               }
+               for (int l46 = 0; (l46 < 2); l46 = (l46 + 1)) {
+                       fRec40[l46] = 0.0f;
+               }
+               for (int l47 = 0; (l47 < 2); l47 = (l47 + 1)) {
+                       fRec43[l47] = 0.0f;
+               }
+               for (int l48 = 0; (l48 < 8192); l48 = (l48 + 1)) {
+                       fVec17[l48] = 0.0f;
+               }
+               for (int l49 = 0; (l49 < 2); l49 = (l49 + 1)) {
+                       fRec42[l49] = 0.0f;
+               }
+               for (int l50 = 0; (l50 < 2); l50 = (l50 + 1)) {
+                       fRec45[l50] = 0.0f;
+               }
+               for (int l51 = 0; (l51 < 8192); l51 = (l51 + 1)) {
+                       fVec18[l51] = 0.0f;
+               }
+               for (int l52 = 0; (l52 < 2); l52 = (l52 + 1)) {
+                       fRec44[l52] = 0.0f;
+               }
+               for (int l53 = 0; (l53 < 2); l53 = (l53 + 1)) {
+                       fRec47[l53] = 0.0f;
+               }
+               for (int l54 = 0; (l54 < 8192); l54 = (l54 + 1)) {
+                       fVec19[l54] = 0.0f;
+               }
+               for (int l55 = 0; (l55 < 2); l55 = (l55 + 1)) {
+                       fRec46[l55] = 0.0f;
+               }
+               for (int l56 = 0; (l56 < 2048); l56 = (l56 + 1)) {
+                       fVec20[l56] = 0.0f;
+               }
+               for (int l57 = 0; (l57 < 2); l57 = (l57 + 1)) {
+                       fRec30[l57] = 0.0f;
+               }
+               for (int l58 = 0; (l58 < 2048); l58 = (l58 + 1)) {
+                       fVec21[l58] = 0.0f;
+               }
+               for (int l59 = 0; (l59 < 2); l59 = (l59 + 1)) {
+                       fRec28[l59] = 0.0f;
+               }
+               for (int l60 = 0; (l60 < 2048); l60 = (l60 + 1)) {
+                       fVec22[l60] = 0.0f;
+               }
+               for (int l61 = 0; (l61 < 2); l61 = (l61 + 1)) {
+                       fRec26[l61] = 0.0f;
+               }
+               for (int l62 = 0; (l62 < 2048); l62 = (l62 + 1)) {
+                       fVec23[l62] = 0.0f;
+               }
+               for (int l63 = 0; (l63 < 2); l63 = (l63 + 1)) {
+                       fRec24[l63] = 0.0f;
+               }
+       }
+       
+       virtual void init(int sample_rate) {
+               classInit(sample_rate);
+               instanceInit(sample_rate);
+       }
+       virtual void instanceInit(int sample_rate) {
+               instanceConstants(sample_rate);
+               instanceResetUserInterface();
+               instanceClear();
+       }
+       
+       virtual freeverbdsp* clone() {
+               return new freeverbdsp();
+       }
+       
+       virtual int getSampleRate() {
+               return fSampleRate;
+       }
+       
+       virtual void buildUserInterface(UI* ui_interface) {
+               ui_interface->openHorizontalBox("Freeverb");
+               ui_interface->declare(0, "0", "");
+               ui_interface->openVerticalBox("0x00");
+               ui_interface->declare(&fVslider1, "0", "");
+               ui_interface->declare(&fVslider1, "style", "knob");
+               ui_interface->declare(&fVslider1, "tooltip", "Somehow control the   density of the reverb.");
+               ui_interface->addVerticalSlider("Damp", &fVslider1, 0.5f, 0.0f, 1.0f, 0.0250000004f);
+               ui_interface->declare(&fVslider0, "1", "");
+               ui_interface->declare(&fVslider0, "style", "knob");
+               ui_interface->declare(&fVslider0, "tooltip", "The room size   between 0 and 1 with 1 for the largest room.");
+               ui_interface->addVerticalSlider("RoomSize", &fVslider0, 0.100000001f, 0.0f, 1.0f, 0.0250000004f);
+               ui_interface->declare(&fVslider3, "2", "");
+               ui_interface->declare(&fVslider3, "style", "knob");
+               ui_interface->declare(&fVslider3, "tooltip", "Spatial   spread between 0 and 1 with 1 for maximum spread.");
+               ui_interface->addVerticalSlider("Stereo Spread", &fVslider3, 0.5f, 0.0f, 1.0f, 0.00999999978f);
+               ui_interface->closeBox();
+               ui_interface->declare(&fVslider2, "1", "");
+               ui_interface->declare(&fVslider2, "tooltip", "The amount of reverb applied to the signal   between 0 and 1 with 1 for the maximum amount of reverb.");
+               ui_interface->addVerticalSlider("Wet", &fVslider2, 0.100000001f, 0.0f, 1.0f, 0.0250000004f);
+               ui_interface->closeBox();
+       }
+       
+       virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+               FAUSTFLOAT* input0 = inputs[0];
+               FAUSTFLOAT* input1 = inputs[1];
+               FAUSTFLOAT* output0 = outputs[0];
+               FAUSTFLOAT* output1 = outputs[1];
+               float fSlow0 = ((fConst1 * float(fVslider0)) + 0.699999988f);
+               float fSlow1 = (fConst2 * float(fVslider1));
+               float fSlow2 = (1.0f - fSlow1);
+               float fSlow3 = float(fVslider2);
+               float fSlow4 = (0.100000001f * fSlow3);
+               float fSlow5 = (1.0f - fSlow3);
+               int iSlow6 = int((fConst19 * float(fVslider3)));
+               int iSlow7 = (iConst3 + iSlow6);
+               int iSlow8 = (iConst4 + iSlow6);
+               int iSlow9 = (iConst5 + iSlow6);
+               int iSlow10 = (iConst6 + iSlow6);
+               int iSlow11 = (iConst7 + iSlow6);
+               int iSlow12 = (iConst8 + iSlow6);
+               int iSlow13 = (iConst9 + iSlow6);
+               int iSlow14 = (iConst10 + iSlow6);
+               int iSlow15 = (iSlow6 + -1);
+               int iSlow16 = std::min<int>(1024, std::max<int>(0, (iConst11 + iSlow15)));
+               int iSlow17 = std::min<int>(1024, std::max<int>(0, (iConst13 + iSlow15)));
+               int iSlow18 = std::min<int>(1024, std::max<int>(0, (iConst15 + iSlow15)));
+               int iSlow19 = std::min<int>(1024, std::max<int>(0, (iConst17 + iSlow15)));
+               for (int i = 0; (i < count); i = (i + 1)) {
+                       float fTemp0 = float(input0[i]);
+                       float fTemp1 = float(input1[i]);
+                       fRec9[0] = ((fSlow1 * fRec9[1]) + (fSlow2 * fRec8[1]));
+                       float fTemp2 = (fSlow4 * (fTemp0 + fTemp1));
+                       fVec0[(IOTA & 8191)] = ((fSlow0 * fRec9[0]) + fTemp2);
+                       fRec8[0] = fVec0[((IOTA - iConst3) & 8191)];
+                       fRec11[0] = ((fSlow1 * fRec11[1]) + (fSlow2 * fRec10[1]));
+                       fVec1[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec11[0]));
+                       fRec10[0] = fVec1[((IOTA - iConst4) & 8191)];
+                       fRec13[0] = ((fSlow1 * fRec13[1]) + (fSlow2 * fRec12[1]));
+                       fVec2[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec13[0]));
+                       fRec12[0] = fVec2[((IOTA - iConst5) & 8191)];
+                       fRec15[0] = ((fSlow1 * fRec15[1]) + (fSlow2 * fRec14[1]));
+                       fVec3[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec15[0]));
+                       fRec14[0] = fVec3[((IOTA - iConst6) & 8191)];
+                       fRec17[0] = ((fSlow1 * fRec17[1]) + (fSlow2 * fRec16[1]));
+                       fVec4[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec17[0]));
+                       fRec16[0] = fVec4[((IOTA - iConst7) & 8191)];
+                       fRec19[0] = ((fSlow1 * fRec19[1]) + (fSlow2 * fRec18[1]));
+                       fVec5[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec19[0]));
+                       fRec18[0] = fVec5[((IOTA - iConst8) & 8191)];
+                       fRec21[0] = ((fSlow1 * fRec21[1]) + (fSlow2 * fRec20[1]));
+                       fVec6[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec21[0]));
+                       fRec20[0] = fVec6[((IOTA - iConst9) & 8191)];
+                       fRec23[0] = ((fSlow1 * fRec23[1]) + (fSlow2 * fRec22[1]));
+                       fVec7[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec23[0]));
+                       fRec22[0] = fVec7[((IOTA - iConst10) & 8191)];
+                       float fTemp3 = ((((((((fRec8[0] + fRec10[0]) + fRec12[0]) + fRec14[0]) + fRec16[0]) + fRec18[0]) + fRec20[0]) + fRec22[0]) + (0.5f * fRec6[1]));
+                       fVec8[(IOTA & 2047)] = fTemp3;
+                       fRec6[0] = fVec8[((IOTA - iConst12) & 2047)];
+                       float fRec7 = (0.0f - (0.5f * fTemp3));
+                       float fTemp4 = (fRec6[1] + (fRec7 + (0.5f * fRec4[1])));
+                       fVec9[(IOTA & 2047)] = fTemp4;
+                       fRec4[0] = fVec9[((IOTA - iConst14) & 2047)];
+                       float fRec5 = (0.0f - (0.5f * fTemp4));
+                       float fTemp5 = (fRec4[1] + (fRec5 + (0.5f * fRec2[1])));
+                       fVec10[(IOTA & 2047)] = fTemp5;
+                       fRec2[0] = fVec10[((IOTA - iConst16) & 2047)];
+                       float fRec3 = (0.0f - (0.5f * fTemp5));
+                       float fTemp6 = (fRec2[1] + (fRec3 + (0.5f * fRec0[1])));
+                       fVec11[(IOTA & 1023)] = fTemp6;
+                       fRec0[0] = fVec11[((IOTA - iConst18) & 1023)];
+                       float fRec1 = (0.0f - (0.5f * fTemp6));
+                       output0[i] = FAUSTFLOAT(((fRec1 + fRec0[1]) + (fSlow5 * fTemp0)));
+                       fRec33[0] = ((fSlow1 * fRec33[1]) + (fSlow2 * fRec32[1]));
+                       fVec12[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec33[0]));
+                       fRec32[0] = fVec12[((IOTA - iSlow7) & 8191)];
+                       fRec35[0] = ((fSlow1 * fRec35[1]) + (fSlow2 * fRec34[1]));
+                       fVec13[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec35[0]));
+                       fRec34[0] = fVec13[((IOTA - iSlow8) & 8191)];
+                       fRec37[0] = ((fSlow1 * fRec37[1]) + (fSlow2 * fRec36[1]));
+                       fVec14[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec37[0]));
+                       fRec36[0] = fVec14[((IOTA - iSlow9) & 8191)];
+                       fRec39[0] = ((fSlow1 * fRec39[1]) + (fSlow2 * fRec38[1]));
+                       fVec15[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec39[0]));
+                       fRec38[0] = fVec15[((IOTA - iSlow10) & 8191)];
+                       fRec41[0] = ((fSlow1 * fRec41[1]) + (fSlow2 * fRec40[1]));
+                       fVec16[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec41[0]));
+                       fRec40[0] = fVec16[((IOTA - iSlow11) & 8191)];
+                       fRec43[0] = ((fSlow1 * fRec43[1]) + (fSlow2 * fRec42[1]));
+                       fVec17[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec43[0]));
+                       fRec42[0] = fVec17[((IOTA - iSlow12) & 8191)];
+                       fRec45[0] = ((fSlow1 * fRec45[1]) + (fSlow2 * fRec44[1]));
+                       fVec18[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec45[0]));
+                       fRec44[0] = fVec18[((IOTA - iSlow13) & 8191)];
+                       fRec47[0] = ((fSlow1 * fRec47[1]) + (fSlow2 * fRec46[1]));
+                       fVec19[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec47[0]));
+                       fRec46[0] = fVec19[((IOTA - iSlow14) & 8191)];
+                       float fTemp7 = ((((((((fRec32[0] + fRec34[0]) + fRec36[0]) + fRec38[0]) + fRec40[0]) + fRec42[0]) + fRec44[0]) + fRec46[0]) + (0.5f * fRec30[1]));
+                       fVec20[(IOTA & 2047)] = fTemp7;
+                       fRec30[0] = fVec20[((IOTA - iSlow16) & 2047)];
+                       float fRec31 = (0.0f - (0.5f * fTemp7));
+                       float fTemp8 = (fRec30[1] + (fRec31 + (0.5f * fRec28[1])));
+                       fVec21[(IOTA & 2047)] = fTemp8;
+                       fRec28[0] = fVec21[((IOTA - iSlow17) & 2047)];
+                       float fRec29 = (0.0f - (0.5f * fTemp8));
+                       float fTemp9 = (fRec28[1] + (fRec29 + (0.5f * fRec26[1])));
+                       fVec22[(IOTA & 2047)] = fTemp9;
+                       fRec26[0] = fVec22[((IOTA - iSlow18) & 2047)];
+                       float fRec27 = (0.0f - (0.5f * fTemp9));
+                       float fTemp10 = (fRec26[1] + (fRec27 + (0.5f * fRec24[1])));
+                       fVec23[(IOTA & 2047)] = fTemp10;
+                       fRec24[0] = fVec23[((IOTA - iSlow19) & 2047)];
+                       float fRec25 = (0.0f - (0.5f * fTemp10));
+                       output1[i] = FAUSTFLOAT(((fRec25 + fRec24[1]) + (fSlow5 * fTemp1)));
+                       fRec9[1] = fRec9[0];
+                       IOTA = (IOTA + 1);
+                       fRec8[1] = fRec8[0];
+                       fRec11[1] = fRec11[0];
+                       fRec10[1] = fRec10[0];
+                       fRec13[1] = fRec13[0];
+                       fRec12[1] = fRec12[0];
+                       fRec15[1] = fRec15[0];
+                       fRec14[1] = fRec14[0];
+                       fRec17[1] = fRec17[0];
+                       fRec16[1] = fRec16[0];
+                       fRec19[1] = fRec19[0];
+                       fRec18[1] = fRec18[0];
+                       fRec21[1] = fRec21[0];
+                       fRec20[1] = fRec20[0];
+                       fRec23[1] = fRec23[0];
+                       fRec22[1] = fRec22[0];
+                       fRec6[1] = fRec6[0];
+                       fRec4[1] = fRec4[0];
+                       fRec2[1] = fRec2[0];
+                       fRec0[1] = fRec0[0];
+                       fRec33[1] = fRec33[0];
+                       fRec32[1] = fRec32[0];
+                       fRec35[1] = fRec35[0];
+                       fRec34[1] = fRec34[0];
+                       fRec37[1] = fRec37[0];
+                       fRec36[1] = fRec36[0];
+                       fRec39[1] = fRec39[0];
+                       fRec38[1] = fRec38[0];
+                       fRec41[1] = fRec41[0];
+                       fRec40[1] = fRec40[0];
+                       fRec43[1] = fRec43[0];
+                       fRec42[1] = fRec42[0];
+                       fRec45[1] = fRec45[0];
+                       fRec44[1] = fRec44[0];
+                       fRec47[1] = fRec47[0];
+                       fRec46[1] = fRec46[0];
+                       fRec30[1] = fRec30[0];
+                       fRec28[1] = fRec28[0];
+                       fRec26[1] = fRec26[0];
+                       fRec24[1] = fRec24[0];
+               }
+       }
+
+};
+
+#endif
diff --git a/src/freeverbmonodsp.h b/src/freeverbmonodsp.h
new file mode 100644 (file)
index 0000000..5de618e
--- /dev/null
@@ -0,0 +1,2154 @@
+/* ------------------------------------------------------------
+name: "freeverbmonodsp"
+Code generated with Faust 2.28.6 (https://faust.grame.fr)
+Compilation options: -lang cpp -inpl -scal -ftz 0
+------------------------------------------------------------ */
+
+#ifndef  __freeverbmonodsp_H__
+#define  __freeverbmonodsp_H__
+
+// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME
+//
+// faust2header.cpp - FAUST Architecture File
+// This is a simple variation of matlabplot.cpp in the Faust distribution
+// aimed at creating a simple C++ header file (.h) containing a Faust DSP.
+// See the Makefile for how to use it.
+
+/************************** BEGIN dsp.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __dsp__
+#define __dsp__
+
+#include <string>
+#include <vector>
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+struct UI;
+struct Meta;
+
+/**
+ * DSP memory manager.
+ */
+
+struct dsp_memory_manager {
+    
+    virtual ~dsp_memory_manager() {}
+    
+    virtual void* allocate(size_t size) = 0;
+    virtual void destroy(void* ptr) = 0;
+    
+};
+
+/**
+* Signal processor definition.
+*/
+
+class dsp {
+
+    public:
+
+        dsp() {}
+        virtual ~dsp() {}
+
+        /* Return instance number of audio inputs */
+        virtual int getNumInputs() = 0;
+    
+        /* Return instance number of audio outputs */
+        virtual int getNumOutputs() = 0;
+    
+        /**
+         * Trigger the ui_interface parameter with instance specific calls
+         * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI.
+         *
+         * @param ui_interface - the user interface builder
+         */
+        virtual void buildUserInterface(UI* ui_interface) = 0;
+    
+        /* Returns the sample rate currently used by the instance */
+        virtual int getSampleRate() = 0;
+    
+        /**
+         * Global init, calls the following methods:
+         * - static class 'classInit': static tables initialization
+         * - 'instanceInit': constants and instance state initialization
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void init(int sample_rate) = 0;
+
+        /**
+         * Init instance state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceInit(int sample_rate) = 0;
+
+        /**
+         * Init instance constant state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceConstants(int sample_rate) = 0;
+    
+        /* Init default control parameters values */
+        virtual void instanceResetUserInterface() = 0;
+    
+        /* Init instance state (delay lines...) */
+        virtual void instanceClear() = 0;
+        /**
+         * Return a clone of the instance.
+         *
+         * @return a copy of the instance on success, otherwise a null pointer.
+         */
+        virtual dsp* clone() = 0;
+    
+        /**
+         * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata.
+         *
+         * @param m - the Meta* meta user
+         */
+        virtual void metadata(Meta* m) = 0;
+    
+        /**
+         * DSP instance computation, to be called with successive in/out audio buffers.
+         *
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         *
+         */
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0;
+    
+        /**
+         * DSP instance computation: alternative method to be used by subclasses.
+         *
+         * @param date_usec - the timestamp in microsec given by audio driver.
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         *
+         */
+        virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); }
+       
+};
+
+/**
+ * Generic DSP decorator.
+ */
+
+class decorator_dsp : public dsp {
+
+    protected:
+
+        dsp* fDSP;
+
+    public:
+
+        decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {}
+        virtual ~decorator_dsp() { delete fDSP; }
+
+        virtual int getNumInputs() { return fDSP->getNumInputs(); }
+        virtual int getNumOutputs() { return fDSP->getNumOutputs(); }
+        virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); }
+        virtual int getSampleRate() { return fDSP->getSampleRate(); }
+        virtual void init(int sample_rate) { fDSP->init(sample_rate); }
+        virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); }
+        virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); }
+        virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); }
+        virtual void instanceClear() { fDSP->instanceClear(); }
+        virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); }
+        virtual void metadata(Meta* m) { fDSP->metadata(m); }
+        // Beware: subclasses usually have to overload the two 'compute' methods
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); }
+        virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); }
+    
+};
+
+/**
+ * DSP factory class.
+ */
+
+class dsp_factory {
+    
+    protected:
+    
+        // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory);
+        virtual ~dsp_factory() {}
+    
+    public:
+    
+        virtual std::string getName() = 0;
+        virtual std::string getSHAKey() = 0;
+        virtual std::string getDSPCode() = 0;
+        virtual std::string getCompileOptions() = 0;
+        virtual std::vector<std::string> getLibraryList() = 0;
+        virtual std::vector<std::string> getIncludePathnames() = 0;
+    
+        virtual dsp* createDSPInstance() = 0;
+    
+        virtual void setMemoryManager(dsp_memory_manager* manager) = 0;
+        virtual dsp_memory_manager* getMemoryManager() = 0;
+    
+};
+
+/**
+ * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
+ * flags to avoid costly denormals.
+ */
+
+#ifdef __SSE__
+    #include <xmmintrin.h>
+    #ifdef __SSE2__
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
+    #else
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
+    #endif
+#else
+    #define AVOIDDENORMALS
+#endif
+
+#endif
+/**************************  END  dsp.h **************************/
+
+/************************** BEGIN APIUI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef API_UI_H
+#define API_UI_H
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <map>
+
+/************************** BEGIN meta.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __meta__
+#define __meta__
+
+struct Meta
+{
+    virtual ~Meta() {};
+    virtual void declare(const char* key, const char* value) = 0;
+    
+};
+
+#endif
+/**************************  END  meta.h **************************/
+/************************** BEGIN UI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __UI_H__
+#define __UI_H__
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+/*******************************************************************************
+ * UI : Faust DSP User Interface
+ * User Interface as expected by the buildUserInterface() method of a DSP.
+ * This abstract class contains only the method that the Faust compiler can
+ * generate to describe a DSP user interface.
+ ******************************************************************************/
+
+struct Soundfile;
+
+template <typename REAL>
+struct UIReal
+{
+    UIReal() {}
+    virtual ~UIReal() {}
+    
+    // -- widget's layouts
+    
+    virtual void openTabBox(const char* label) = 0;
+    virtual void openHorizontalBox(const char* label) = 0;
+    virtual void openVerticalBox(const char* label) = 0;
+    virtual void closeBox() = 0;
+    
+    // -- active widgets
+    
+    virtual void addButton(const char* label, REAL* zone) = 0;
+    virtual void addCheckButton(const char* label, REAL* zone) = 0;
+    virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    
+    // -- passive widgets
+    
+    virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    
+    // -- soundfiles
+    
+    virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0;
+    
+    // -- metadata declarations
+    
+    virtual void declare(REAL* zone, const char* key, const char* val) {}
+};
+
+struct UI : public UIReal<FAUSTFLOAT>
+{
+    UI() {}
+    virtual ~UI() {}
+};
+
+#endif
+/**************************  END  UI.h **************************/
+/************************** BEGIN PathBuilder.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef FAUST_PATHBUILDER_H
+#define FAUST_PATHBUILDER_H
+
+#include <vector>
+#include <string>
+#include <algorithm>
+
+/*******************************************************************************
+ * PathBuilder : Faust User Interface
+ * Helper class to build complete hierarchical path for UI items.
+ ******************************************************************************/
+
+class PathBuilder
+{
+
+    protected:
+    
+        std::vector<std::string> fControlsLevel;
+       
+    public:
+    
+        PathBuilder() {}
+        virtual ~PathBuilder() {}
+    
+        std::string buildPath(const std::string& label) 
+        {
+            std::string res = "/";
+            for (size_t i = 0; i < fControlsLevel.size(); i++) {
+                res += fControlsLevel[i];
+                res += "/";
+            }
+            res += label;
+            std::replace(res.begin(), res.end(), ' ', '_');
+            return res;
+        }
+    
+        std::string buildLabel(std::string label)
+        {
+            std::replace(label.begin(), label.end(), ' ', '_');
+            return label;
+        }
+    
+        void pushLabel(const std::string& label) { fControlsLevel.push_back(label); }
+        void popLabel() { fControlsLevel.pop_back(); }
+    
+};
+
+#endif  // FAUST_PATHBUILDER_H
+/**************************  END  PathBuilder.h **************************/
+/************************** BEGIN ValueConverter.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __ValueConverter__
+#define __ValueConverter__
+
+/***************************************************************************************
+                                                               ValueConverter.h
+                            (GRAME, Copyright 2015-2019)
+
+Set of conversion objects used to map user interface values (for example a gui slider
+delivering values between 0 and 1) to faust values (for example a vslider between
+20 and 20000) using a log scale.
+
+-- Utilities
+
+Range(lo,hi) : clip a value x between lo and hi
+Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2
+Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2
+
+-- Value Converters
+
+ValueConverter::ui2faust(x)
+ValueConverter::faust2ui(x)
+
+-- ValueConverters used for sliders depending of the scale
+
+LinearValueConverter(umin, umax, fmin, fmax)
+LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments
+LogValueConverter(umin, umax, fmin, fmax)
+ExpValueConverter(umin, umax, fmin, fmax)
+
+-- ValueConverters used for accelerometers based on 3 points
+
+AccUpConverter(amin, amid, amax, fmin, fmid, fmax)             -- curve 0
+AccDownConverter(amin, amid, amax, fmin, fmid, fmax)   -- curve 1
+AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2
+AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3
+
+-- lists of ZoneControl are used to implement accelerometers metadata for each axes
+
+ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter
+
+-- ZoneReader are used to implement screencolor metadata
+
+ZoneReader(zone, valueConverter) : a zone with a data converter
+
+****************************************************************************************/
+
+#include <float.h>
+#include <algorithm>    // std::max
+#include <cmath>
+#include <vector>
+#include <assert.h>
+
+//--------------------------------------------------------------------------------------
+// Interpolator(lo,hi,v1,v2)
+// Maps a value x between lo and hi to a value y between v1 and v2
+// y = v1 + (x-lo)/(hi-lo)*(v2-v1)
+// y = v1 + (x-lo) * coef              with coef = (v2-v1)/(hi-lo)
+// y = v1 + x*coef - lo*coef
+// y = v1 - lo*coef + x*coef
+// y = offset + x*coef                         with offset = v1 - lo*coef
+//--------------------------------------------------------------------------------------
+class Interpolator
+{
+    private:
+
+        //--------------------------------------------------------------------------------------
+        // Range(lo,hi) clip a value between lo and hi
+        //--------------------------------------------------------------------------------------
+        struct Range
+        {
+            double fLo;
+            double fHi;
+
+            Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {}
+            double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; }
+        };
+
+
+        Range fRange;
+        double fCoef;
+        double fOffset;
+
+    public:
+
+        Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi)
+        {
+            if (hi != lo) {
+                // regular case
+                fCoef = (v2-v1)/(hi-lo);
+                fOffset = v1 - lo*fCoef;
+            } else {
+                // degenerate case, avoids division by zero
+                fCoef = 0;
+                fOffset = (v1+v2)/2;
+            }
+        }
+        double operator()(double v)
+        {
+            double x = fRange(v);
+            return  fOffset + x*fCoef;
+        }
+
+        void getLowHigh(double& amin, double& amax)
+        {
+            amin = fRange.fLo;
+            amax = fRange.fHi;
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Interpolator3pt(lo,mi,hi,v1,vm,v2)
+// Map values between lo mid hi to values between v1 vm v2
+//--------------------------------------------------------------------------------------
+class Interpolator3pt
+{
+
+    private:
+
+        Interpolator fSegment1;
+        Interpolator fSegment2;
+        double fMid;
+
+    public:
+
+        Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) :
+            fSegment1(lo, mi, v1, vm),
+            fSegment2(mi, hi, vm, v2),
+            fMid(mi) {}
+        double operator()(double x) { return  (x < fMid) ? fSegment1(x) : fSegment2(x); }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fSegment1.getLowHigh(amin, amid);
+            fSegment2.getLowHigh(amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Abstract ValueConverter class. Converts values between UI and Faust representations
+//--------------------------------------------------------------------------------------
+class ValueConverter
+{
+
+    public:
+
+        virtual ~ValueConverter() {}
+        virtual double ui2faust(double x) = 0;
+        virtual double faust2ui(double x) = 0;
+};
+
+//--------------------------------------------------------------------------------------
+// A converter than can be updated
+//--------------------------------------------------------------------------------------
+
+class UpdatableValueConverter : public ValueConverter {
+    
+    protected:
+        
+        bool fActive;
+        
+    public:
+        
+        UpdatableValueConverter():fActive(true)
+        {}
+        virtual ~UpdatableValueConverter()
+        {}
+        
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0;
+        virtual void getMappingValues(double& amin, double& amid, double& amax) = 0;
+        
+        void setActive(bool on_off) { fActive = on_off; }
+        bool getActive() { return fActive; }
+    
+};
+
+
+//--------------------------------------------------------------------------------------
+// Linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter : public ValueConverter
+{
+    
+    private:
+        
+        Interpolator fUI2F;
+        Interpolator fF2UI;
+        
+    public:
+        
+        LinearValueConverter(double umin, double umax, double fmin, double fmax) :
+            fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax)
+        {}
+        
+        LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.)
+        {}
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Two segments linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter2 : public UpdatableValueConverter
+{
+    
+    private:
+    
+        Interpolator3pt fUI2F;
+        Interpolator3pt fF2UI;
+        
+    public:
+    
+        LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) :
+            fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax)
+        {}
+        
+        LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.)
+        {}
+    
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max)
+        {
+            fUI2F = Interpolator3pt(amin, amid, amax, min, init, max);
+            fF2UI = Interpolator3pt(min, init, max, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fUI2F.getMappingValues(amin, amid, amax);
+        }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Logarithmic conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LogValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        LogValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Exponential conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class ExpValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        ExpValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up curve (curve 0)
+//--------------------------------------------------------------------------------------
+class AccUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt fA2F;
+        Interpolator3pt fF2A;
+
+    public:
+
+        AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmid,fmax),
+            fF2A(fmin,fmid,fmax,amin,amid,amax)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down curve (curve 1)
+//--------------------------------------------------------------------------------------
+class AccDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator3pt        fF2A;
+
+    public:
+
+        AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmid,fmin),
+            fF2A(fmin,fmid,fmax,amax,amid,amin)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+             //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up-Down curve (curve 2)
+//--------------------------------------------------------------------------------------
+class AccUpDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmax,fmin),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down-Up curve (curve 3)
+//--------------------------------------------------------------------------------------
+class AccDownUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmin,fmax),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Base class for ZoneControl
+//--------------------------------------------------------------------------------------
+class ZoneControl
+{
+
+    protected:
+
+        FAUSTFLOAT*    fZone;
+
+    public:
+
+        ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {}
+        virtual ~ZoneControl() {}
+
+        virtual void update(double v) const {}
+
+        virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {}
+        virtual void getMappingValues(double& amin, double& amid, double& amax) {}
+
+        FAUSTFLOAT* getZone() { return fZone; }
+
+        virtual void setActive(bool on_off) {}
+        virtual bool getActive() { return false; }
+
+        virtual int getCurve() { return -1; }
+
+};
+
+//--------------------------------------------------------------------------------------
+//  Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class ConverterZoneControl : public ZoneControl
+{
+
+    protected:
+
+        ValueConverter* fValueConverter;
+
+    public:
+
+        ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {}
+        virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere...
+
+        virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); }
+
+        ValueConverter* getConverter() { return fValueConverter; }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Association of a zone and a four value converter, each one for each possible curve.
+// Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class CurveZoneControl : public ZoneControl
+{
+
+    private:
+
+        std::vector<UpdatableValueConverter*> fValueConverters;
+        int fCurve;
+
+    public:
+
+        CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0)
+        {
+            assert(curve >= 0 && curve <= 3);
+            fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max));
+            fCurve = curve;
+        }
+        virtual ~CurveZoneControl()
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                delete(*it);
+            }
+        }
+        void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); }
+
+        void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max)
+        {
+            fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max);
+            fCurve = curve;
+        }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fValueConverters[fCurve]->getMappingValues(amin, amid, amax);
+        }
+
+        void setActive(bool on_off)
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                (*it)->setActive(on_off);
+            }
+        }
+
+        int getCurve() { return fCurve; }
+};
+
+class ZoneReader
+{
+
+    private:
+
+        FAUSTFLOAT* fZone;
+        Interpolator fInterpolator;
+
+    public:
+
+        ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {}
+
+        virtual ~ZoneReader() {}
+
+        int getValue()
+        {
+            return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127;
+        }
+
+};
+
+#endif
+/**************************  END  ValueConverter.h **************************/
+
+class APIUI : public PathBuilder, public Meta, public UI
+{
+    public:
+    
+        enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph };
+  
+    protected:
+    
+        enum { kLin = 0, kLog = 1, kExp = 2 };
+    
+        int fNumParameters;
+        std::vector<std::string> fPaths;
+        std::vector<std::string> fLabels;
+        std::map<std::string, int> fPathMap;
+        std::map<std::string, int> fLabelMap;
+        std::vector<ValueConverter*> fConversion;
+        std::vector<FAUSTFLOAT*> fZone;
+        std::vector<FAUSTFLOAT> fInit;
+        std::vector<FAUSTFLOAT> fMin;
+        std::vector<FAUSTFLOAT> fMax;
+        std::vector<FAUSTFLOAT> fStep;
+        std::vector<ItemType> fItemType;
+        std::vector<std::map<std::string, std::string> > fMetaData;
+        std::vector<ZoneControl*> fAcc[3];
+        std::vector<ZoneControl*> fGyr[3];
+
+        // Screen color control
+        // "...[screencolor:red]..." etc.
+        bool fHasScreenControl;      // true if control screen color metadata
+        ZoneReader* fRedReader;
+        ZoneReader* fGreenReader;
+        ZoneReader* fBlueReader;
+
+        // Current values controlled by metadata
+        std::string fCurrentUnit;
+        int fCurrentScale;
+        std::string fCurrentAcc;
+        std::string fCurrentGyr;
+        std::string fCurrentColor;
+        std::string fCurrentTooltip;
+        std::map<std::string, std::string> fCurrentMetadata;
+    
+        // Add a generic parameter
+        virtual void addParameter(const char* label,
+                                FAUSTFLOAT* zone,
+                                FAUSTFLOAT init,
+                                FAUSTFLOAT min,
+                                FAUSTFLOAT max,
+                                FAUSTFLOAT step,
+                                ItemType type)
+        {
+            std::string path = buildPath(label);
+            fPathMap[path] = fLabelMap[label] = fNumParameters++;
+            fPaths.push_back(path);
+            fLabels.push_back(label);
+            fZone.push_back(zone);
+            fInit.push_back(init);
+            fMin.push_back(min);
+            fMax.push_back(max);
+            fStep.push_back(step);
+            fItemType.push_back(type);
+            
+            // handle scale metadata
+            switch (fCurrentScale) {
+                case kLin:
+                    fConversion.push_back(new LinearValueConverter(0, 1, min, max));
+                    break;
+                case kLog:
+                    fConversion.push_back(new LogValueConverter(0, 1, min, max));
+                    break;
+                case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max));
+                    break;
+            }
+            fCurrentScale = kLin;
+            
+            if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) {
+                std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n";
+            }
+
+            // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentAcc.size() > 0) {
+                std::istringstream iss(fCurrentAcc);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl;
+                }
+                fCurrentAcc = "";
+            }
+       
+            // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentGyr.size() > 0) {
+                std::istringstream iss(fCurrentGyr);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl;
+                }
+                fCurrentGyr = "";
+            }
+        
+            // handle screencolor metadata "...[screencolor:red|green|blue|white]..."
+            if (fCurrentColor.size() > 0) {
+                if ((fCurrentColor == "red") && (fRedReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "green") && (fGreenReader == 0)) {
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) {
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else {
+                    std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl;
+                }
+            }
+            fCurrentColor = "";
+            
+            fMetaData.push_back(fCurrentMetadata);
+            fCurrentMetadata.clear();
+        }
+
+        int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val)
+        {
+            FAUSTFLOAT* zone = fZone[p];
+            for (size_t i = 0; i < table[val].size(); i++) {
+                if (zone == table[val][i]->getZone()) return int(i);
+            }
+            return -1;
+        }
+    
+        void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            // Deactivates everywhere..
+            if (id1 != -1) table[0][id1]->setActive(false);
+            if (id2 != -1) table[1][id2]->setActive(false);
+            if (id3 != -1) table[2][id3]->setActive(false);
+            
+            if (val == -1) { // Means: no more mapping...
+                // So stay all deactivated...
+            } else {
+                int id4 = getZoneIndex(table, p, val);
+                if (id4 != -1) {
+                    // Reactivate the one we edit...
+                    table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]);
+                    table[val][id4]->setActive(true);
+                } else {
+                    // Allocate a new CurveZoneControl which is 'active' by default
+                    FAUSTFLOAT* zone = fZone[p];
+                    table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]));
+                }
+            }
+        }
+    
+        void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            if (id1 != -1) {
+                val = 0;
+                curve = table[val][id1]->getCurve();
+                table[val][id1]->getMappingValues(amin, amid, amax);
+            } else if (id2 != -1) {
+                val = 1;
+                curve = table[val][id2]->getCurve();
+                table[val][id2]->getMappingValues(amin, amid, amax);
+            } else if (id3 != -1) {
+                val = 2;
+                curve = table[val][id3]->getCurve();
+                table[val][id3]->getMappingValues(amin, amid, amax);
+            } else {
+                val = -1; // No mapping
+                curve = 0;
+                amin = -100.;
+                amid = 0.;
+                amax = 100.;
+            }
+        }
+
+     public:
+    
+        enum Type { kAcc = 0, kGyr = 1, kNoType };
+   
+        APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin)
+        {}
+
+        virtual ~APIUI()
+        {
+            for (auto& it : fConversion) delete it;
+            for (int i = 0; i < 3; i++) {
+                for (auto& it : fAcc[i]) delete it;
+                for (auto& it : fGyr[i]) delete it;
+            }
+            delete fRedReader;
+            delete fGreenReader;
+            delete fBlueReader;
+        }
+    
+        // -- widget's layouts
+
+        virtual void openTabBox(const char* label) { pushLabel(label); }
+        virtual void openHorizontalBox(const char* label) { pushLabel(label); }
+        virtual void openVerticalBox(const char* label) { pushLabel(label); }
+        virtual void closeBox() { popLabel(); }
+
+        // -- active widgets
+
+        virtual void addButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kButton);
+        }
+
+        virtual void addCheckButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kCheckButton);
+        }
+
+        virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kVSlider);
+        }
+
+        virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kHSlider);
+        }
+
+        virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kNumEntry);
+        }
+
+        // -- passive widgets
+
+        virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph);
+        }
+
+        virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph);
+        }
+    
+        // -- soundfiles
+    
+        virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {}
+
+        // -- metadata declarations
+
+        virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val)
+        {
+            // Keep metadata
+            fCurrentMetadata[key] = val;
+            
+            if (strcmp(key, "scale") == 0) {
+                if (strcmp(val, "log") == 0) {
+                    fCurrentScale = kLog;
+                } else if (strcmp(val, "exp") == 0) {
+                    fCurrentScale = kExp;
+                } else {
+                    fCurrentScale = kLin;
+                }
+            } else if (strcmp(key, "unit") == 0) {
+                fCurrentUnit = val;
+            } else if (strcmp(key, "acc") == 0) {
+                fCurrentAcc = val;
+            } else if (strcmp(key, "gyr") == 0) {
+                fCurrentGyr = val;
+            } else if (strcmp(key, "screencolor") == 0) {
+                fCurrentColor = val; // val = "red", "green", "blue" or "white"
+            } else if (strcmp(key, "tooltip") == 0) {
+                fCurrentTooltip = val;
+            }
+        }
+
+        virtual void declare(const char* key, const char* val)
+        {}
+
+               //-------------------------------------------------------------------------------
+               // Simple API part
+               //-------------------------------------------------------------------------------
+               int getParamsCount() { return fNumParameters; }
+        int getParamIndex(const char* path)
+        {
+            if (fPathMap.find(path) != fPathMap.end()) {
+                return fPathMap[path];
+            } else if (fLabelMap.find(path) != fLabelMap.end()) {
+                return fLabelMap[path];
+            } else {
+                return -1;
+            }
+        }
+        const char* getParamAddress(int p) { return fPaths[p].c_str(); }
+        const char* getParamLabel(int p) { return fLabels[p].c_str(); }
+        std::map<const char*, const char*> getMetadata(int p)
+        {
+            std::map<const char*, const char*> res;
+            std::map<std::string, std::string> metadata = fMetaData[p];
+            for (auto it : metadata) {
+                res[it.first.c_str()] = it.second.c_str();
+            }
+            return res;
+        }
+
+        const char* getMetadata(int p, const char* key)
+        {
+            return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : "";
+        }
+        FAUSTFLOAT getParamMin(int p) { return fMin[p]; }
+        FAUSTFLOAT getParamMax(int p) { return fMax[p]; }
+        FAUSTFLOAT getParamStep(int p) { return fStep[p]; }
+        FAUSTFLOAT getParamInit(int p) { return fInit[p]; }
+
+        FAUSTFLOAT* getParamZone(int p) { return fZone[p]; }
+        FAUSTFLOAT getParamValue(int p) { return *fZone[p]; }
+        void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; }
+
+        double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); }
+        void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); }
+
+        double value2ratio(int p, double r)    { return fConversion[p]->faust2ui(r); }
+        double ratio2value(int p, double r)    { return fConversion[p]->ui2faust(r); }
+    
+        /**
+         * Return the control type (kAcc, kGyr, or -1) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the type
+         */
+        Type getParamType(int p)
+        {
+            if (p >= 0) {
+                if (getZoneIndex(fAcc, p, 0) != -1
+                    || getZoneIndex(fAcc, p, 1) != -1
+                    || getZoneIndex(fAcc, p, 2) != -1) {
+                    return kAcc;
+                } else if (getZoneIndex(fGyr, p, 0) != -1
+                           || getZoneIndex(fGyr, p, 1) != -1
+                           || getZoneIndex(fGyr, p, 2) != -1) {
+                    return kGyr;
+                }
+            }
+            return kNoType;
+        }
+    
+        /**
+         * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the Item type
+         */
+        ItemType getParamItemType(int p)
+        {
+            return fItemType[p];
+        }
+   
+        /**
+         * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @param value - the new value
+         *
+         */
+        void propagateAcc(int acc, double value)
+        {
+            for (size_t i = 0; i < fAcc[acc].size(); i++) {
+                fAcc[acc][i]->update(value);
+            }
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax)
+        {
+            setConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax)
+        {
+             setConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - the acc value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+
+        /**
+         * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param gyr - the gyr value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param value - the new value
+         *
+         */
+        void propagateGyr(int gyr, double value)
+        {
+            for (size_t i = 0; i < fGyr[gyr].size(); i++) {
+                fGyr[gyr][i]->update(value);
+            }
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the accelerometer
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @return the number of zones
+         *
+         */
+        int getAccCount(int acc)
+        {
+            return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0;
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the gyroscope
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param the number of zones
+         *
+         */
+        int getGyrCount(int gyr)
+        {
+            return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0;
+        }
+   
+        // getScreenColor() : -1 means no screen color control (no screencolor metadata found)
+        // otherwise return 0x00RRGGBB a ready to use color
+        int getScreenColor()
+        {
+            if (fHasScreenControl) {
+                int r = (fRedReader) ? fRedReader->getValue() : 0;
+                int g = (fGreenReader) ? fGreenReader->getValue() : 0;
+                int b = (fBlueReader) ? fBlueReader->getValue() : 0;
+                return (r<<16) | (g<<8) | b;
+            } else {
+                return -1;
+            }
+        }
+};
+
+#endif
+/**************************  END  APIUI.h **************************/
+
+// NOTE: "faust -scn name" changes the last line above to
+// #include <faust/name/name.h>
+
+//----------------------------------------------------------------------------
+//  FAUST Generated Code
+//----------------------------------------------------------------------------
+
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif 
+
+#include <algorithm>
+#include <cmath>
+#include <math.h>
+
+
+#ifndef FAUSTCLASS 
+#define FAUSTCLASS freeverbmonodsp
+#endif
+
+#ifdef __APPLE__ 
+#define exp10f __exp10f
+#define exp10 __exp10
+#endif
+
+class freeverbmonodsp : public dsp {
+       
+ private:
+       
+       FAUSTFLOAT fVslider0;
+       int fSampleRate;
+       float fConst0;
+       float fConst1;
+       FAUSTFLOAT fVslider1;
+       float fConst2;
+       FAUSTFLOAT fVslider2;
+       float fRec9[2];
+       int IOTA;
+       float fVec0[8192];
+       int iConst3;
+       float fConst4;
+       FAUSTFLOAT fVslider3;
+       float fRec8[2];
+       float fRec11[2];
+       float fVec1[8192];
+       int iConst5;
+       float fRec10[2];
+       float fRec13[2];
+       float fVec2[8192];
+       int iConst6;
+       float fRec12[2];
+       float fRec15[2];
+       float fVec3[8192];
+       int iConst7;
+       float fRec14[2];
+       float fRec17[2];
+       float fVec4[8192];
+       int iConst8;
+       float fRec16[2];
+       float fRec19[2];
+       float fVec5[8192];
+       int iConst9;
+       float fRec18[2];
+       float fRec21[2];
+       float fVec6[8192];
+       int iConst10;
+       float fRec20[2];
+       float fRec23[2];
+       float fVec7[8192];
+       int iConst11;
+       float fRec22[2];
+       float fVec8[2048];
+       int iConst12;
+       float fRec6[2];
+       float fVec9[2048];
+       int iConst13;
+       float fRec4[2];
+       float fVec10[2048];
+       int iConst14;
+       float fRec2[2];
+       float fVec11[2048];
+       int iConst15;
+       float fRec0[2];
+       float fRec33[2];
+       float fVec12[8192];
+       float fRec32[2];
+       float fRec35[2];
+       float fVec13[8192];
+       float fRec34[2];
+       float fRec37[2];
+       float fVec14[8192];
+       float fRec36[2];
+       float fRec39[2];
+       float fVec15[8192];
+       float fRec38[2];
+       float fRec41[2];
+       float fVec16[8192];
+       float fRec40[2];
+       float fRec43[2];
+       float fVec17[8192];
+       float fRec42[2];
+       float fRec45[2];
+       float fVec18[8192];
+       float fRec44[2];
+       float fRec47[2];
+       float fVec19[8192];
+       float fRec46[2];
+       float fVec20[2048];
+       int iConst16;
+       float fRec30[2];
+       float fVec21[2048];
+       int iConst17;
+       float fRec28[2];
+       float fVec22[2048];
+       int iConst18;
+       float fRec26[2];
+       float fVec23[1024];
+       int iConst19;
+       float fRec24[2];
+       
+ public:
+       
+       void metadata(Meta* m) { 
+               m->declare("delays.lib/name", "Faust Delay Library");
+               m->declare("delays.lib/version", "0.1");
+               m->declare("filename", "freeverbmonodsp.dsp");
+               m->declare("filters.lib/allpass_comb:author", "Julius O. Smith III");
+               m->declare("filters.lib/allpass_comb:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/allpass_comb:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/lowpass0_highpass1", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/name", "Faust Filters Library");
+               m->declare("freeverbdsp.dsp/author", "Romain Michon");
+               m->declare("freeverbdsp.dsp/description", "Freeverb implementation in Faust, from the Faust Library's dm.freeverb_demo in demos.lib");
+               m->declare("freeverbdsp.dsp/license", "LGPL");
+               m->declare("freeverbdsp.dsp/name", "freeverb");
+               m->declare("freeverbdsp.dsp/version", "0.0");
+               m->declare("maths.lib/author", "GRAME");
+               m->declare("maths.lib/copyright", "GRAME");
+               m->declare("maths.lib/license", "LGPL with exception");
+               m->declare("maths.lib/name", "Faust Math Library");
+               m->declare("maths.lib/version", "2.3");
+               m->declare("name", "freeverbmonodsp");
+               m->declare("platform.lib/name", "Generic Platform Library");
+               m->declare("platform.lib/version", "0.1");
+               m->declare("reverbs.lib/name", "Faust Reverb Library");
+               m->declare("reverbs.lib/version", "0.0");
+       }
+
+       virtual int getNumInputs() {
+               return 1;
+       }
+       virtual int getNumOutputs() {
+               return 1;
+       }
+       virtual int getInputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       virtual int getOutputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       
+       static void classInit(int sample_rate) {
+       }
+       
+       virtual void instanceConstants(int sample_rate) {
+               fSampleRate = sample_rate;
+               fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate)));
+               fConst1 = (12348.0f / fConst0);
+               fConst2 = (17640.0f / fConst0);
+               iConst3 = int((0.0253061224f * fConst0));
+               fConst4 = (0.00104308384f * fConst0);
+               iConst5 = int((0.0269387756f * fConst0));
+               iConst6 = int((0.0289569162f * fConst0));
+               iConst7 = int((0.0307482984f * fConst0));
+               iConst8 = int((0.0322448984f * fConst0));
+               iConst9 = int((0.033809524f * fConst0));
+               iConst10 = int((0.0353061222f * fConst0));
+               iConst11 = int((0.0366666652f * fConst0));
+               iConst12 = int((0.0126077095f * fConst0));
+               iConst13 = int((0.00999999978f * fConst0));
+               iConst14 = int((0.00773242628f * fConst0));
+               iConst15 = int((0.00510204071f * fConst0));
+               iConst16 = std::min<int>(1024, std::max<int>(0, (iConst12 + -1)));
+               iConst17 = std::min<int>(1024, std::max<int>(0, (iConst13 + -1)));
+               iConst18 = std::min<int>(1024, std::max<int>(0, (iConst14 + -1)));
+               iConst19 = std::min<int>(1024, std::max<int>(0, (iConst15 + -1)));
+       }
+       
+       virtual void instanceResetUserInterface() {
+               fVslider0 = FAUSTFLOAT(0.10000000000000001f);
+               fVslider1 = FAUSTFLOAT(0.10000000000000001f);
+               fVslider2 = FAUSTFLOAT(0.5f);
+               fVslider3 = FAUSTFLOAT(0.5f);
+       }
+       
+       virtual void instanceClear() {
+               for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
+                       fRec9[l0] = 0.0f;
+               }
+               IOTA = 0;
+               for (int l1 = 0; (l1 < 8192); l1 = (l1 + 1)) {
+                       fVec0[l1] = 0.0f;
+               }
+               for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) {
+                       fRec8[l2] = 0.0f;
+               }
+               for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) {
+                       fRec11[l3] = 0.0f;
+               }
+               for (int l4 = 0; (l4 < 8192); l4 = (l4 + 1)) {
+                       fVec1[l4] = 0.0f;
+               }
+               for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) {
+                       fRec10[l5] = 0.0f;
+               }
+               for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) {
+                       fRec13[l6] = 0.0f;
+               }
+               for (int l7 = 0; (l7 < 8192); l7 = (l7 + 1)) {
+                       fVec2[l7] = 0.0f;
+               }
+               for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) {
+                       fRec12[l8] = 0.0f;
+               }
+               for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) {
+                       fRec15[l9] = 0.0f;
+               }
+               for (int l10 = 0; (l10 < 8192); l10 = (l10 + 1)) {
+                       fVec3[l10] = 0.0f;
+               }
+               for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) {
+                       fRec14[l11] = 0.0f;
+               }
+               for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) {
+                       fRec17[l12] = 0.0f;
+               }
+               for (int l13 = 0; (l13 < 8192); l13 = (l13 + 1)) {
+                       fVec4[l13] = 0.0f;
+               }
+               for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) {
+                       fRec16[l14] = 0.0f;
+               }
+               for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) {
+                       fRec19[l15] = 0.0f;
+               }
+               for (int l16 = 0; (l16 < 8192); l16 = (l16 + 1)) {
+                       fVec5[l16] = 0.0f;
+               }
+               for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) {
+                       fRec18[l17] = 0.0f;
+               }
+               for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) {
+                       fRec21[l18] = 0.0f;
+               }
+               for (int l19 = 0; (l19 < 8192); l19 = (l19 + 1)) {
+                       fVec6[l19] = 0.0f;
+               }
+               for (int l20 = 0; (l20 < 2); l20 = (l20 + 1)) {
+                       fRec20[l20] = 0.0f;
+               }
+               for (int l21 = 0; (l21 < 2); l21 = (l21 + 1)) {
+                       fRec23[l21] = 0.0f;
+               }
+               for (int l22 = 0; (l22 < 8192); l22 = (l22 + 1)) {
+                       fVec7[l22] = 0.0f;
+               }
+               for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) {
+                       fRec22[l23] = 0.0f;
+               }
+               for (int l24 = 0; (l24 < 2048); l24 = (l24 + 1)) {
+                       fVec8[l24] = 0.0f;
+               }
+               for (int l25 = 0; (l25 < 2); l25 = (l25 + 1)) {
+                       fRec6[l25] = 0.0f;
+               }
+               for (int l26 = 0; (l26 < 2048); l26 = (l26 + 1)) {
+                       fVec9[l26] = 0.0f;
+               }
+               for (int l27 = 0; (l27 < 2); l27 = (l27 + 1)) {
+                       fRec4[l27] = 0.0f;
+               }
+               for (int l28 = 0; (l28 < 2048); l28 = (l28 + 1)) {
+                       fVec10[l28] = 0.0f;
+               }
+               for (int l29 = 0; (l29 < 2); l29 = (l29 + 1)) {
+                       fRec2[l29] = 0.0f;
+               }
+               for (int l30 = 0; (l30 < 2048); l30 = (l30 + 1)) {
+                       fVec11[l30] = 0.0f;
+               }
+               for (int l31 = 0; (l31 < 2); l31 = (l31 + 1)) {
+                       fRec0[l31] = 0.0f;
+               }
+               for (int l32 = 0; (l32 < 2); l32 = (l32 + 1)) {
+                       fRec33[l32] = 0.0f;
+               }
+               for (int l33 = 0; (l33 < 8192); l33 = (l33 + 1)) {
+                       fVec12[l33] = 0.0f;
+               }
+               for (int l34 = 0; (l34 < 2); l34 = (l34 + 1)) {
+                       fRec32[l34] = 0.0f;
+               }
+               for (int l35 = 0; (l35 < 2); l35 = (l35 + 1)) {
+                       fRec35[l35] = 0.0f;
+               }
+               for (int l36 = 0; (l36 < 8192); l36 = (l36 + 1)) {
+                       fVec13[l36] = 0.0f;
+               }
+               for (int l37 = 0; (l37 < 2); l37 = (l37 + 1)) {
+                       fRec34[l37] = 0.0f;
+               }
+               for (int l38 = 0; (l38 < 2); l38 = (l38 + 1)) {
+                       fRec37[l38] = 0.0f;
+               }
+               for (int l39 = 0; (l39 < 8192); l39 = (l39 + 1)) {
+                       fVec14[l39] = 0.0f;
+               }
+               for (int l40 = 0; (l40 < 2); l40 = (l40 + 1)) {
+                       fRec36[l40] = 0.0f;
+               }
+               for (int l41 = 0; (l41 < 2); l41 = (l41 + 1)) {
+                       fRec39[l41] = 0.0f;
+               }
+               for (int l42 = 0; (l42 < 8192); l42 = (l42 + 1)) {
+                       fVec15[l42] = 0.0f;
+               }
+               for (int l43 = 0; (l43 < 2); l43 = (l43 + 1)) {
+                       fRec38[l43] = 0.0f;
+               }
+               for (int l44 = 0; (l44 < 2); l44 = (l44 + 1)) {
+                       fRec41[l44] = 0.0f;
+               }
+               for (int l45 = 0; (l45 < 8192); l45 = (l45 + 1)) {
+                       fVec16[l45] = 0.0f;
+               }
+               for (int l46 = 0; (l46 < 2); l46 = (l46 + 1)) {
+                       fRec40[l46] = 0.0f;
+               }
+               for (int l47 = 0; (l47 < 2); l47 = (l47 + 1)) {
+                       fRec43[l47] = 0.0f;
+               }
+               for (int l48 = 0; (l48 < 8192); l48 = (l48 + 1)) {
+                       fVec17[l48] = 0.0f;
+               }
+               for (int l49 = 0; (l49 < 2); l49 = (l49 + 1)) {
+                       fRec42[l49] = 0.0f;
+               }
+               for (int l50 = 0; (l50 < 2); l50 = (l50 + 1)) {
+                       fRec45[l50] = 0.0f;
+               }
+               for (int l51 = 0; (l51 < 8192); l51 = (l51 + 1)) {
+                       fVec18[l51] = 0.0f;
+               }
+               for (int l52 = 0; (l52 < 2); l52 = (l52 + 1)) {
+                       fRec44[l52] = 0.0f;
+               }
+               for (int l53 = 0; (l53 < 2); l53 = (l53 + 1)) {
+                       fRec47[l53] = 0.0f;
+               }
+               for (int l54 = 0; (l54 < 8192); l54 = (l54 + 1)) {
+                       fVec19[l54] = 0.0f;
+               }
+               for (int l55 = 0; (l55 < 2); l55 = (l55 + 1)) {
+                       fRec46[l55] = 0.0f;
+               }
+               for (int l56 = 0; (l56 < 2048); l56 = (l56 + 1)) {
+                       fVec20[l56] = 0.0f;
+               }
+               for (int l57 = 0; (l57 < 2); l57 = (l57 + 1)) {
+                       fRec30[l57] = 0.0f;
+               }
+               for (int l58 = 0; (l58 < 2048); l58 = (l58 + 1)) {
+                       fVec21[l58] = 0.0f;
+               }
+               for (int l59 = 0; (l59 < 2); l59 = (l59 + 1)) {
+                       fRec28[l59] = 0.0f;
+               }
+               for (int l60 = 0; (l60 < 2048); l60 = (l60 + 1)) {
+                       fVec22[l60] = 0.0f;
+               }
+               for (int l61 = 0; (l61 < 2); l61 = (l61 + 1)) {
+                       fRec26[l61] = 0.0f;
+               }
+               for (int l62 = 0; (l62 < 1024); l62 = (l62 + 1)) {
+                       fVec23[l62] = 0.0f;
+               }
+               for (int l63 = 0; (l63 < 2); l63 = (l63 + 1)) {
+                       fRec24[l63] = 0.0f;
+               }
+       }
+       
+       virtual void init(int sample_rate) {
+               classInit(sample_rate);
+               instanceInit(sample_rate);
+       }
+       virtual void instanceInit(int sample_rate) {
+               instanceConstants(sample_rate);
+               instanceResetUserInterface();
+               instanceClear();
+       }
+       
+       virtual freeverbmonodsp* clone() {
+               return new freeverbmonodsp();
+       }
+       
+       virtual int getSampleRate() {
+               return fSampleRate;
+       }
+       
+       virtual void buildUserInterface(UI* ui_interface) {
+               ui_interface->openHorizontalBox("Freeverb");
+               ui_interface->declare(0, "0", "");
+               ui_interface->openVerticalBox("0x00");
+               ui_interface->declare(&fVslider2, "0", "");
+               ui_interface->declare(&fVslider2, "style", "knob");
+               ui_interface->declare(&fVslider2, "tooltip", "Somehow control the   density of the reverb.");
+               ui_interface->addVerticalSlider("Damp", &fVslider2, 0.5f, 0.0f, 1.0f, 0.0250000004f);
+               ui_interface->declare(&fVslider1, "1", "");
+               ui_interface->declare(&fVslider1, "style", "knob");
+               ui_interface->declare(&fVslider1, "tooltip", "The room size   between 0 and 1 with 1 for the largest room.");
+               ui_interface->addVerticalSlider("RoomSize", &fVslider1, 0.100000001f, 0.0f, 1.0f, 0.0250000004f);
+               ui_interface->declare(&fVslider3, "2", "");
+               ui_interface->declare(&fVslider3, "style", "knob");
+               ui_interface->declare(&fVslider3, "tooltip", "Spatial   spread between 0 and 1 with 1 for maximum spread.");
+               ui_interface->addVerticalSlider("Stereo Spread", &fVslider3, 0.5f, 0.0f, 1.0f, 0.00999999978f);
+               ui_interface->closeBox();
+               ui_interface->declare(&fVslider0, "1", "");
+               ui_interface->declare(&fVslider0, "tooltip", "The amount of reverb applied to the signal   between 0 and 1 with 1 for the maximum amount of reverb.");
+               ui_interface->addVerticalSlider("Wet", &fVslider0, 0.100000001f, 0.0f, 1.0f, 0.0250000004f);
+               ui_interface->closeBox();
+       }
+       
+       virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+               FAUSTFLOAT* input0 = inputs[0];
+               FAUSTFLOAT* output0 = outputs[0];
+               float fSlow0 = float(fVslider0);
+               float fSlow1 = (0.200000003f * fSlow0);
+               float fSlow2 = ((fConst1 * float(fVslider1)) + 0.699999988f);
+               float fSlow3 = (fConst2 * float(fVslider2));
+               float fSlow4 = (1.0f - fSlow3);
+               int iSlow5 = int((fConst4 * float(fVslider3)));
+               int iSlow6 = (iConst3 + iSlow5);
+               int iSlow7 = (iConst5 + iSlow5);
+               int iSlow8 = (iConst6 + iSlow5);
+               int iSlow9 = (iConst7 + iSlow5);
+               int iSlow10 = (iConst8 + iSlow5);
+               int iSlow11 = (iConst9 + iSlow5);
+               int iSlow12 = (iConst10 + iSlow5);
+               int iSlow13 = (iConst11 + iSlow5);
+               int iSlow14 = (iSlow5 + -1);
+               int iSlow15 = std::min<int>(1024, std::max<int>(0, (iConst12 + iSlow14)));
+               int iSlow16 = std::min<int>(1024, std::max<int>(0, (iConst13 + iSlow14)));
+               int iSlow17 = std::min<int>(1024, std::max<int>(0, (iConst14 + iSlow14)));
+               int iSlow18 = std::min<int>(1024, std::max<int>(0, (iConst15 + iSlow14)));
+               float fSlow19 = (2.0f * (1.0f - fSlow0));
+               for (int i = 0; (i < count); i = (i + 1)) {
+                       float fTemp0 = float(input0[i]);
+                       float fTemp1 = (fSlow1 * fTemp0);
+                       fRec9[0] = ((fSlow3 * fRec9[1]) + (fSlow4 * fRec8[1]));
+                       fVec0[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec9[0]));
+                       fRec8[0] = fVec0[((IOTA - iSlow6) & 8191)];
+                       fRec11[0] = ((fSlow3 * fRec11[1]) + (fSlow4 * fRec10[1]));
+                       fVec1[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec11[0]));
+                       fRec10[0] = fVec1[((IOTA - iSlow7) & 8191)];
+                       fRec13[0] = ((fSlow3 * fRec13[1]) + (fSlow4 * fRec12[1]));
+                       fVec2[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec13[0]));
+                       fRec12[0] = fVec2[((IOTA - iSlow8) & 8191)];
+                       fRec15[0] = ((fSlow3 * fRec15[1]) + (fSlow4 * fRec14[1]));
+                       fVec3[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec15[0]));
+                       fRec14[0] = fVec3[((IOTA - iSlow9) & 8191)];
+                       fRec17[0] = ((fSlow3 * fRec17[1]) + (fSlow4 * fRec16[1]));
+                       fVec4[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec17[0]));
+                       fRec16[0] = fVec4[((IOTA - iSlow10) & 8191)];
+                       fRec19[0] = ((fSlow3 * fRec19[1]) + (fSlow4 * fRec18[1]));
+                       fVec5[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec19[0]));
+                       fRec18[0] = fVec5[((IOTA - iSlow11) & 8191)];
+                       fRec21[0] = ((fSlow3 * fRec21[1]) + (fSlow4 * fRec20[1]));
+                       fVec6[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec21[0]));
+                       fRec20[0] = fVec6[((IOTA - iSlow12) & 8191)];
+                       fRec23[0] = ((fSlow3 * fRec23[1]) + (fSlow4 * fRec22[1]));
+                       fVec7[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec23[0]));
+                       fRec22[0] = fVec7[((IOTA - iSlow13) & 8191)];
+                       float fTemp2 = ((((((((fRec8[0] + fRec10[0]) + fRec12[0]) + fRec14[0]) + fRec16[0]) + fRec18[0]) + fRec20[0]) + fRec22[0]) + (0.5f * fRec6[1]));
+                       fVec8[(IOTA & 2047)] = fTemp2;
+                       fRec6[0] = fVec8[((IOTA - iSlow15) & 2047)];
+                       float fRec7 = (0.0f - (0.5f * fTemp2));
+                       float fTemp3 = (fRec6[1] + (fRec7 + (0.5f * fRec4[1])));
+                       fVec9[(IOTA & 2047)] = fTemp3;
+                       fRec4[0] = fVec9[((IOTA - iSlow16) & 2047)];
+                       float fRec5 = (0.0f - (0.5f * fTemp3));
+                       float fTemp4 = (fRec4[1] + (fRec5 + (0.5f * fRec2[1])));
+                       fVec10[(IOTA & 2047)] = fTemp4;
+                       fRec2[0] = fVec10[((IOTA - iSlow17) & 2047)];
+                       float fRec3 = (0.0f - (0.5f * fTemp4));
+                       float fTemp5 = (fRec2[1] + (fRec3 + (0.5f * fRec0[1])));
+                       fVec11[(IOTA & 2047)] = fTemp5;
+                       fRec0[0] = fVec11[((IOTA - iSlow18) & 2047)];
+                       float fRec1 = (0.0f - (0.5f * fTemp5));
+                       fRec33[0] = ((fSlow3 * fRec33[1]) + (fSlow4 * fRec32[1]));
+                       fVec12[(IOTA & 8191)] = ((fSlow2 * fRec33[0]) + fTemp1);
+                       fRec32[0] = fVec12[((IOTA - iConst3) & 8191)];
+                       fRec35[0] = ((fSlow3 * fRec35[1]) + (fSlow4 * fRec34[1]));
+                       fVec13[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec35[0]));
+                       fRec34[0] = fVec13[((IOTA - iConst5) & 8191)];
+                       fRec37[0] = ((fSlow3 * fRec37[1]) + (fSlow4 * fRec36[1]));
+                       fVec14[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec37[0]));
+                       fRec36[0] = fVec14[((IOTA - iConst6) & 8191)];
+                       fRec39[0] = ((fSlow3 * fRec39[1]) + (fSlow4 * fRec38[1]));
+                       fVec15[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec39[0]));
+                       fRec38[0] = fVec15[((IOTA - iConst7) & 8191)];
+                       fRec41[0] = ((fSlow3 * fRec41[1]) + (fSlow4 * fRec40[1]));
+                       fVec16[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec41[0]));
+                       fRec40[0] = fVec16[((IOTA - iConst8) & 8191)];
+                       fRec43[0] = ((fSlow3 * fRec43[1]) + (fSlow4 * fRec42[1]));
+                       fVec17[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec43[0]));
+                       fRec42[0] = fVec17[((IOTA - iConst9) & 8191)];
+                       fRec45[0] = ((fSlow3 * fRec45[1]) + (fSlow4 * fRec44[1]));
+                       fVec18[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec45[0]));
+                       fRec44[0] = fVec18[((IOTA - iConst10) & 8191)];
+                       fRec47[0] = ((fSlow3 * fRec47[1]) + (fSlow4 * fRec46[1]));
+                       fVec19[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec47[0]));
+                       fRec46[0] = fVec19[((IOTA - iConst11) & 8191)];
+                       float fTemp6 = ((((((((fRec32[0] + fRec34[0]) + fRec36[0]) + fRec38[0]) + fRec40[0]) + fRec42[0]) + fRec44[0]) + fRec46[0]) + (0.5f * fRec30[1]));
+                       fVec20[(IOTA & 2047)] = fTemp6;
+                       fRec30[0] = fVec20[((IOTA - iConst16) & 2047)];
+                       float fRec31 = (0.0f - (0.5f * fTemp6));
+                       float fTemp7 = (fRec30[1] + (fRec31 + (0.5f * fRec28[1])));
+                       fVec21[(IOTA & 2047)] = fTemp7;
+                       fRec28[0] = fVec21[((IOTA - iConst17) & 2047)];
+                       float fRec29 = (0.0f - (0.5f * fTemp7));
+                       float fTemp8 = (fRec28[1] + (fRec29 + (0.5f * fRec26[1])));
+                       fVec22[(IOTA & 2047)] = fTemp8;
+                       fRec26[0] = fVec22[((IOTA - iConst18) & 2047)];
+                       float fRec27 = (0.0f - (0.5f * fTemp8));
+                       float fTemp9 = (fRec26[1] + (fRec27 + (0.5f * fRec24[1])));
+                       fVec23[(IOTA & 1023)] = fTemp9;
+                       fRec24[0] = fVec23[((IOTA - iConst19) & 1023)];
+                       float fRec25 = (0.0f - (0.5f * fTemp9));
+                       output0[i] = FAUSTFLOAT((fRec0[1] + ((fRec24[1] + (fRec25 + fRec1)) + (fSlow19 * fTemp0))));
+                       fRec9[1] = fRec9[0];
+                       IOTA = (IOTA + 1);
+                       fRec8[1] = fRec8[0];
+                       fRec11[1] = fRec11[0];
+                       fRec10[1] = fRec10[0];
+                       fRec13[1] = fRec13[0];
+                       fRec12[1] = fRec12[0];
+                       fRec15[1] = fRec15[0];
+                       fRec14[1] = fRec14[0];
+                       fRec17[1] = fRec17[0];
+                       fRec16[1] = fRec16[0];
+                       fRec19[1] = fRec19[0];
+                       fRec18[1] = fRec18[0];
+                       fRec21[1] = fRec21[0];
+                       fRec20[1] = fRec20[0];
+                       fRec23[1] = fRec23[0];
+                       fRec22[1] = fRec22[0];
+                       fRec6[1] = fRec6[0];
+                       fRec4[1] = fRec4[0];
+                       fRec2[1] = fRec2[0];
+                       fRec0[1] = fRec0[0];
+                       fRec33[1] = fRec33[0];
+                       fRec32[1] = fRec32[0];
+                       fRec35[1] = fRec35[0];
+                       fRec34[1] = fRec34[0];
+                       fRec37[1] = fRec37[0];
+                       fRec36[1] = fRec36[0];
+                       fRec39[1] = fRec39[0];
+                       fRec38[1] = fRec38[0];
+                       fRec41[1] = fRec41[0];
+                       fRec40[1] = fRec40[0];
+                       fRec43[1] = fRec43[0];
+                       fRec42[1] = fRec42[0];
+                       fRec45[1] = fRec45[0];
+                       fRec44[1] = fRec44[0];
+                       fRec47[1] = fRec47[0];
+                       fRec46[1] = fRec46[0];
+                       fRec30[1] = fRec30[0];
+                       fRec28[1] = fRec28[0];
+                       fRec26[1] = fRec26[0];
+                       fRec24[1] = fRec24[0];
+               }
+       }
+
+};
+
+#endif
index f898c63f4f9ef0ae8af47e83dc3188a080fc45c8..0157e903f7ba975f881f23942f42a5010b33d08c 100644 (file)
@@ -110,7 +110,13 @@ win32 {
 
 DESTDIR = .
 QMAKE_CLEAN += -r ./jacktrip ./jacktrip_debug ./release ./debug
-target.path = /usr/bin
+
+# isEmpty(PREFIX) will allow path to be changed during the command line
+# call to qmake, e.g. qmake PREFIX=/usr
+isEmpty(PREFIX) {
+ PREFIX = /usr/local
+}
+target.path = $$PREFIX/bin/
 INSTALLS += target
 
 # for plugins
@@ -120,11 +126,18 @@ INCLUDEPATH += ../faust-src-lair
 HEADERS += DataProtocol.h \
            JMess.h \
            JackTrip.h \
+           Effects.h \
+           Compressor.h \
+           CompressorPresets.h \
+           Limiter.h \
+           Reverb.h \
+           AudioTester.h \
            jacktrip_globals.h \
            jacktrip_types.h \
            JackTripThread.h \
            JackTripWorker.h \
            JackTripWorkerMessages.h \
+           JitterBuffer.h \
            LoopBack.h \
            NetKS.h \
            PacketHeader.h \
@@ -136,7 +149,10 @@ HEADERS += DataProtocol.h \
            ThreadPoolTest.h \
            UdpDataProtocol.h \
            UdpHubListener.h \
-           AudioInterface.h
+           AudioInterface.h \
+           compressordsp.h \
+           limiterdsp.h \
+           freeverbdsp.h
 
 !nojack {
 HEADERS += JackAudioInterface.h
@@ -144,11 +160,16 @@ HEADERS += JackAudioInterface.h
 SOURCES += DataProtocol.cpp \
            JMess.cpp \
            JackTrip.cpp \
+           Compressor.cpp \
+           Limiter.cpp \
+           Reverb.cpp \
+           AudioTester.cpp \
            jacktrip_globals.cpp \
            jacktrip_main.cpp \
            jacktrip_tests.cpp \
            JackTripThread.cpp \
            JackTripWorker.cpp \
+           JitterBuffer.cpp \
            LoopBack.cpp \
            PacketHeader.cpp \
            ProcessPlugin.cpp \
@@ -162,7 +183,7 @@ SOURCES += DataProtocol.cpp \
 SOURCES += JackAudioInterface.cpp
 }
 
-# RtAduio Input
+# RtAudio Input
 win32 {
   INCLUDEPATH += ../externals/rtaudio-4.1.1/include
   DEPENDPATH += ../externals/rtaudio-4.1.1/include
index 5bd9a54d29a733efd15323d9bfa085a90701b40e..7e75994bb1a74a5865c46c56047546bc09ad1faa 100644 (file)
@@ -151,6 +151,7 @@ void setRealtimeProcessPriority()
 #ifdef __UBUNTU__
     priority = 95; // anything higher is silently ignored by Ubuntu 18.04
 #endif
+    priority = 3;
 
     struct sched_param sp = { .sched_priority = priority };
 
index 4c98f170611bc4a376ac672d1cf7946f5f11e781..ab66d2ba0144f7b2b5105f894fbbb66463b1f311 100644 (file)
@@ -44,7 +44,7 @@
 /// \todo Add this namespace
 //namespace JackTrip
 
-const char* const gVersion = "1.2.2"; ///< JackTrip version
+const char* const gVersion = "1.3.0"; ///< JackTrip version
 
 //*******************************************************************************
 /// \name Default Values
@@ -126,6 +126,7 @@ extern int gVerboseFlag; ///< Verbose mode flag declaration
 //@{
 const int gJackBitResolution = 32; ///< Audio Bit Resolution of the Jack Server
 const QString gJackDefaultClientName = "JackTrip";
+const int gMaxRemoteNameLength = 64;
 //@}
 
 
index 5e172a880376e63c6d42cbf896d6a5e9efcc9195..bd4ed14a97d584daa6872d5f2b6753ceaa19f132 100644 (file)
 #include <iostream>
 
 #include <QCoreApplication>
-#include <QDebug>
+#include <QScopedPointer>
+#include <iostream>
+#include <signal.h>
+#include "jacktrip_globals.h"
+#include "Settings.h"
+#include "UdpHubListener.h"
 #include <QLoggingCategory>
 
-#include "JackAudioInterface.h"
-#include "UdpDataProtocol.h"
-#include "RingBuffer.h"
-#include "JackTrip.h"
-#include "Settings.h"
-//#include "TestRingBuffer.h"
-#include "LoopBack.h"
-#include "PacketHeader.h"
-//#include "JackTripThread.h"
-#ifdef __RT_AUDIO__
-#include "RtAudioInterface.h"
-#endif
-#include "jacktrip_tests.cpp"
-#include "jacktrip_globals.h"
+void qtMessageHandler(__attribute__((unused)) QtMsgType type, __attribute__((unused)) const QMessageLogContext &context, const QString &msg)
+{
+    std::cerr << msg.toStdString() << std::endl;
+}
+
+#if defined (__LINUX__) || (__MAC_OSX__)
+static int setupUnixSignalHandler(void (*handler)(int))
+{
+    //Setup our SIGINT handler.
+    struct sigaction sigInt;
+    sigInt.sa_handler = handler;
+    sigemptyset(&sigInt.sa_mask);
+    sigInt.sa_flags = 0;
+    sigInt.sa_flags |= SA_RESTART;
 
+    int result = 0;
+    if (sigaction(SIGINT, &sigInt, 0)) {
+        std::cout << "Unable to register SIGINT handler" << std::endl;
+        result |= 1;
+    }
+    if (sigaction(SIGTERM, &sigInt, 0)) {
+        std::cout << "Unable to register SIGTERM handler" << std::endl;
+        result |= 2;
+    }
+    return result;
+}
+#else
+bool isHubServer = false;
 
-void qtMessageHandler(QtMsgType /*type*/, const QMessageLogContext& /*context*/, const QString& msg)
+BOOL WINAPI windowsCtrlHandler(DWORD fdwCtrlType)
 {
-    std::cerr << msg.toStdString() << std::endl;
+    switch (fdwCtrlType) {
+        case CTRL_C_EVENT:
+            if (isHubServer) {
+                UdpHubListener::sigIntHandler(0);
+            } else {
+                JackTrip::sigIntHandler(0);
+            }
+            return true;
+        default:
+            return false;
+    }
 }
+#endif
 
-int main(int argc, char** argv)
+int main(int argc, char *argv[])
 {
     QCoreApplication app(argc, argv);
+    QScopedPointer<JackTrip> jackTrip;
+    QScopedPointer<UdpHubListener> udpHub;
+    
     QLoggingCategory::setFilterRules(QStringLiteral("*.debug=true"));
     qInstallMessageHandler(qtMessageHandler);
 
-    bool testing = false;
-    if ( argc > 1 ) {
-        if ( !strcmp(argv[1], "test") ) {
-            testing = true;
-        }
-    }
-
-    if ( testing ) {
-        std::cout << "=========TESTING=========" << std::endl;
-        //main_tests(argc, argv); // test functions
-        JackTrip jacktrip;
-        //RtAudioInterface rtaudio(&jacktrip);
-        //rtaudio.setup();
-        //rtaudio.listAllInterfaces();
-        //rtaudio.printDeviceInfo(0);
-
-        //while (true) sleep(9999);
-    }
-    else {
-        // catch all potential exeptions
-        try
-        {
-            // Get Settings from user
-            // ----------------------
-            Settings* settings = new Settings;
-            settings->parseInput(argc, argv);
-            settings->startJackTrip();
-        }
-        catch ( const std::exception & e )
-        {
-            std::cerr << "ERROR:" << std::endl;
-            std::cerr << e.what() << std::endl;
-            std::cerr << "Exiting JackTrip..." << std::endl;
-            std::cerr << gPrintSeparator << std::endl;
-            return -1;
+    try {
+        Settings settings;
+        settings.parseInput(argc, argv);
+        
+        //Either start our hub server or our jacktrip process as appropriate.
+        if (settings.isHubServer()) {
+            udpHub.reset(settings.getConfiguredHubServer());
+            if (gVerboseFlag) std::cout << "Settings:startJackTrip before udphub->start" << std::endl;
+            QObject::connect(udpHub.data(), &UdpHubListener::signalStopped, &app,
+                             &QCoreApplication::quit, Qt::QueuedConnection);
+            QObject::connect(udpHub.data(), &UdpHubListener::signalError, &app,
+                             &QCoreApplication::quit, Qt::QueuedConnection);
+#if defined (__LINUX__) || (__MAC_OSX__)
+            setupUnixSignalHandler(UdpHubListener::sigIntHandler);
+#else
+            isHubServer = true;
+            SetConsoleCtrlHandler(windowsCtrlHandler, true);
+#endif
+            udpHub->start();
+        } else {
+            jackTrip.reset(settings.getConfiguredJackTrip());
+            if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->startProcess" << std::endl;
+            QObject::connect(jackTrip.data(), &JackTrip::signalProcessesStopped, &app,
+                             &QCoreApplication::quit, Qt::QueuedConnection);
+            QObject::connect(jackTrip.data(), &JackTrip::signalError, &app,
+                             &QCoreApplication::quit, Qt::QueuedConnection);
+#if defined (__LINUX__) || (__MAC_OSX__)
+            setupUnixSignalHandler(JackTrip::sigIntHandler);
+#else
+            std::cout << SetConsoleCtrlHandler(windowsCtrlHandler, true) << std::endl;
+#endif
+#ifdef WAIRTOHUB // WAIR
+            jackTrip->startProcess(0); // for WAIR compatibility, ID in jack client name
+#else
+            jackTrip->startProcess();
+#endif // endwhere
         }
+        
+        if (gVerboseFlag) std::cout << "step 6" << std::endl;
+        if (gVerboseFlag) std::cout << "jmain before app->exec()" << std::endl;
+    } catch (const std::exception &e) {
+        std::cerr << "ERROR:" << std::endl;
+        std::cerr << e.what() << std::endl;
+        std::cerr << "Exiting JackTrip..." << std::endl;
+        std::cerr << gPrintSeparator << std::endl;
+        return -1;
     }
-    if (gVerboseFlag) std::cout << "step 6" << std::endl;
-    if (gVerboseFlag) std::cout << "jacktrip_main before app.exec()" << std::endl;
-
+    
     return app.exec();
 }
diff --git a/src/jacktrip_types_alt.h b/src/jacktrip_types_alt.h
new file mode 100644 (file)
index 0000000..2ce854b
--- /dev/null
@@ -0,0 +1,7 @@
+typedef float sample_t;
+typedef unsigned char uint8_t;
+typedef unsigned short int uint16_t;
+typedef unsigned int uint32_t;
+typedef signed char int8_t;
+typedef short int int16_t;
+typedef int int32_t;
diff --git a/src/limiterdsp.h b/src/limiterdsp.h
new file mode 100644 (file)
index 0000000..21ab621
--- /dev/null
@@ -0,0 +1,1751 @@
+/* ------------------------------------------------------------
+name: "limiterdsp"
+Code generated with Faust 2.28.6 (https://faust.grame.fr)
+Compilation options: -lang cpp -inpl -scal -ftz 0
+------------------------------------------------------------ */
+
+#ifndef  __limiterdsp_H__
+#define  __limiterdsp_H__
+
+// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME
+//
+// faust2header.cpp - FAUST Architecture File
+// This is a simple variation of matlabplot.cpp in the Faust distribution
+// aimed at creating a simple C++ header file (.h) containing a Faust DSP.
+// See the Makefile for how to use it.
+
+/************************** BEGIN dsp.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __dsp__
+#define __dsp__
+
+#include <string>
+#include <vector>
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+struct UI;
+struct Meta;
+
+/**
+ * DSP memory manager.
+ */
+
+struct dsp_memory_manager {
+    
+    virtual ~dsp_memory_manager() {}
+    
+    virtual void* allocate(size_t size) = 0;
+    virtual void destroy(void* ptr) = 0;
+    
+};
+
+/**
+* Signal processor definition.
+*/
+
+class dsp {
+
+    public:
+
+        dsp() {}
+        virtual ~dsp() {}
+
+        /* Return instance number of audio inputs */
+        virtual int getNumInputs() = 0;
+    
+        /* Return instance number of audio outputs */
+        virtual int getNumOutputs() = 0;
+    
+        /**
+         * Trigger the ui_interface parameter with instance specific calls
+         * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI.
+         *
+         * @param ui_interface - the user interface builder
+         */
+        virtual void buildUserInterface(UI* ui_interface) = 0;
+    
+        /* Returns the sample rate currently used by the instance */
+        virtual int getSampleRate() = 0;
+    
+        /**
+         * Global init, calls the following methods:
+         * - static class 'classInit': static tables initialization
+         * - 'instanceInit': constants and instance state initialization
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void init(int sample_rate) = 0;
+
+        /**
+         * Init instance state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceInit(int sample_rate) = 0;
+
+        /**
+         * Init instance constant state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceConstants(int sample_rate) = 0;
+    
+        /* Init default control parameters values */
+        virtual void instanceResetUserInterface() = 0;
+    
+        /* Init instance state (delay lines...) */
+        virtual void instanceClear() = 0;
+        /**
+         * Return a clone of the instance.
+         *
+         * @return a copy of the instance on success, otherwise a null pointer.
+         */
+        virtual dsp* clone() = 0;
+    
+        /**
+         * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata.
+         *
+         * @param m - the Meta* meta user
+         */
+        virtual void metadata(Meta* m) = 0;
+    
+        /**
+         * DSP instance computation, to be called with successive in/out audio buffers.
+         *
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         *
+         */
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0;
+    
+        /**
+         * DSP instance computation: alternative method to be used by subclasses.
+         *
+         * @param date_usec - the timestamp in microsec given by audio driver.
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         *
+         */
+        virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); }
+       
+};
+
+/**
+ * Generic DSP decorator.
+ */
+
+class decorator_dsp : public dsp {
+
+    protected:
+
+        dsp* fDSP;
+
+    public:
+
+        decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {}
+        virtual ~decorator_dsp() { delete fDSP; }
+
+        virtual int getNumInputs() { return fDSP->getNumInputs(); }
+        virtual int getNumOutputs() { return fDSP->getNumOutputs(); }
+        virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); }
+        virtual int getSampleRate() { return fDSP->getSampleRate(); }
+        virtual void init(int sample_rate) { fDSP->init(sample_rate); }
+        virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); }
+        virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); }
+        virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); }
+        virtual void instanceClear() { fDSP->instanceClear(); }
+        virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); }
+        virtual void metadata(Meta* m) { fDSP->metadata(m); }
+        // Beware: subclasses usually have to overload the two 'compute' methods
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); }
+        virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); }
+    
+};
+
+/**
+ * DSP factory class.
+ */
+
+class dsp_factory {
+    
+    protected:
+    
+        // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory);
+        virtual ~dsp_factory() {}
+    
+    public:
+    
+        virtual std::string getName() = 0;
+        virtual std::string getSHAKey() = 0;
+        virtual std::string getDSPCode() = 0;
+        virtual std::string getCompileOptions() = 0;
+        virtual std::vector<std::string> getLibraryList() = 0;
+        virtual std::vector<std::string> getIncludePathnames() = 0;
+    
+        virtual dsp* createDSPInstance() = 0;
+    
+        virtual void setMemoryManager(dsp_memory_manager* manager) = 0;
+        virtual dsp_memory_manager* getMemoryManager() = 0;
+    
+};
+
+/**
+ * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
+ * flags to avoid costly denormals.
+ */
+
+#ifdef __SSE__
+    #include <xmmintrin.h>
+    #ifdef __SSE2__
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
+    #else
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
+    #endif
+#else
+    #define AVOIDDENORMALS
+#endif
+
+#endif
+/**************************  END  dsp.h **************************/
+
+/************************** BEGIN APIUI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef API_UI_H
+#define API_UI_H
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <map>
+
+/************************** BEGIN meta.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __meta__
+#define __meta__
+
+struct Meta
+{
+    virtual ~Meta() {};
+    virtual void declare(const char* key, const char* value) = 0;
+    
+};
+
+#endif
+/**************************  END  meta.h **************************/
+/************************** BEGIN UI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __UI_H__
+#define __UI_H__
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+/*******************************************************************************
+ * UI : Faust DSP User Interface
+ * User Interface as expected by the buildUserInterface() method of a DSP.
+ * This abstract class contains only the method that the Faust compiler can
+ * generate to describe a DSP user interface.
+ ******************************************************************************/
+
+struct Soundfile;
+
+template <typename REAL>
+struct UIReal
+{
+    UIReal() {}
+    virtual ~UIReal() {}
+    
+    // -- widget's layouts
+    
+    virtual void openTabBox(const char* label) = 0;
+    virtual void openHorizontalBox(const char* label) = 0;
+    virtual void openVerticalBox(const char* label) = 0;
+    virtual void closeBox() = 0;
+    
+    // -- active widgets
+    
+    virtual void addButton(const char* label, REAL* zone) = 0;
+    virtual void addCheckButton(const char* label, REAL* zone) = 0;
+    virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    
+    // -- passive widgets
+    
+    virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    
+    // -- soundfiles
+    
+    virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0;
+    
+    // -- metadata declarations
+    
+    virtual void declare(REAL* zone, const char* key, const char* val) {}
+};
+
+struct UI : public UIReal<FAUSTFLOAT>
+{
+    UI() {}
+    virtual ~UI() {}
+};
+
+#endif
+/**************************  END  UI.h **************************/
+/************************** BEGIN PathBuilder.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef FAUST_PATHBUILDER_H
+#define FAUST_PATHBUILDER_H
+
+#include <vector>
+#include <string>
+#include <algorithm>
+
+/*******************************************************************************
+ * PathBuilder : Faust User Interface
+ * Helper class to build complete hierarchical path for UI items.
+ ******************************************************************************/
+
+class PathBuilder
+{
+
+    protected:
+    
+        std::vector<std::string> fControlsLevel;
+       
+    public:
+    
+        PathBuilder() {}
+        virtual ~PathBuilder() {}
+    
+        std::string buildPath(const std::string& label) 
+        {
+            std::string res = "/";
+            for (size_t i = 0; i < fControlsLevel.size(); i++) {
+                res += fControlsLevel[i];
+                res += "/";
+            }
+            res += label;
+            std::replace(res.begin(), res.end(), ' ', '_');
+            return res;
+        }
+    
+        std::string buildLabel(std::string label)
+        {
+            std::replace(label.begin(), label.end(), ' ', '_');
+            return label;
+        }
+    
+        void pushLabel(const std::string& label) { fControlsLevel.push_back(label); }
+        void popLabel() { fControlsLevel.pop_back(); }
+    
+};
+
+#endif  // FAUST_PATHBUILDER_H
+/**************************  END  PathBuilder.h **************************/
+/************************** BEGIN ValueConverter.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __ValueConverter__
+#define __ValueConverter__
+
+/***************************************************************************************
+                                                               ValueConverter.h
+                            (GRAME, Copyright 2015-2019)
+
+Set of conversion objects used to map user interface values (for example a gui slider
+delivering values between 0 and 1) to faust values (for example a vslider between
+20 and 20000) using a log scale.
+
+-- Utilities
+
+Range(lo,hi) : clip a value x between lo and hi
+Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2
+Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2
+
+-- Value Converters
+
+ValueConverter::ui2faust(x)
+ValueConverter::faust2ui(x)
+
+-- ValueConverters used for sliders depending of the scale
+
+LinearValueConverter(umin, umax, fmin, fmax)
+LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments
+LogValueConverter(umin, umax, fmin, fmax)
+ExpValueConverter(umin, umax, fmin, fmax)
+
+-- ValueConverters used for accelerometers based on 3 points
+
+AccUpConverter(amin, amid, amax, fmin, fmid, fmax)             -- curve 0
+AccDownConverter(amin, amid, amax, fmin, fmid, fmax)   -- curve 1
+AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2
+AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3
+
+-- lists of ZoneControl are used to implement accelerometers metadata for each axes
+
+ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter
+
+-- ZoneReader are used to implement screencolor metadata
+
+ZoneReader(zone, valueConverter) : a zone with a data converter
+
+****************************************************************************************/
+
+#include <float.h>
+#include <algorithm>    // std::max
+#include <cmath>
+#include <vector>
+#include <assert.h>
+
+//--------------------------------------------------------------------------------------
+// Interpolator(lo,hi,v1,v2)
+// Maps a value x between lo and hi to a value y between v1 and v2
+// y = v1 + (x-lo)/(hi-lo)*(v2-v1)
+// y = v1 + (x-lo) * coef              with coef = (v2-v1)/(hi-lo)
+// y = v1 + x*coef - lo*coef
+// y = v1 - lo*coef + x*coef
+// y = offset + x*coef                         with offset = v1 - lo*coef
+//--------------------------------------------------------------------------------------
+class Interpolator
+{
+    private:
+
+        //--------------------------------------------------------------------------------------
+        // Range(lo,hi) clip a value between lo and hi
+        //--------------------------------------------------------------------------------------
+        struct Range
+        {
+            double fLo;
+            double fHi;
+
+            Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {}
+            double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; }
+        };
+
+
+        Range fRange;
+        double fCoef;
+        double fOffset;
+
+    public:
+
+        Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi)
+        {
+            if (hi != lo) {
+                // regular case
+                fCoef = (v2-v1)/(hi-lo);
+                fOffset = v1 - lo*fCoef;
+            } else {
+                // degenerate case, avoids division by zero
+                fCoef = 0;
+                fOffset = (v1+v2)/2;
+            }
+        }
+        double operator()(double v)
+        {
+            double x = fRange(v);
+            return  fOffset + x*fCoef;
+        }
+
+        void getLowHigh(double& amin, double& amax)
+        {
+            amin = fRange.fLo;
+            amax = fRange.fHi;
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Interpolator3pt(lo,mi,hi,v1,vm,v2)
+// Map values between lo mid hi to values between v1 vm v2
+//--------------------------------------------------------------------------------------
+class Interpolator3pt
+{
+
+    private:
+
+        Interpolator fSegment1;
+        Interpolator fSegment2;
+        double fMid;
+
+    public:
+
+        Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) :
+            fSegment1(lo, mi, v1, vm),
+            fSegment2(mi, hi, vm, v2),
+            fMid(mi) {}
+        double operator()(double x) { return  (x < fMid) ? fSegment1(x) : fSegment2(x); }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fSegment1.getLowHigh(amin, amid);
+            fSegment2.getLowHigh(amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Abstract ValueConverter class. Converts values between UI and Faust representations
+//--------------------------------------------------------------------------------------
+class ValueConverter
+{
+
+    public:
+
+        virtual ~ValueConverter() {}
+        virtual double ui2faust(double x) = 0;
+        virtual double faust2ui(double x) = 0;
+};
+
+//--------------------------------------------------------------------------------------
+// A converter than can be updated
+//--------------------------------------------------------------------------------------
+
+class UpdatableValueConverter : public ValueConverter {
+    
+    protected:
+        
+        bool fActive;
+        
+    public:
+        
+        UpdatableValueConverter():fActive(true)
+        {}
+        virtual ~UpdatableValueConverter()
+        {}
+        
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0;
+        virtual void getMappingValues(double& amin, double& amid, double& amax) = 0;
+        
+        void setActive(bool on_off) { fActive = on_off; }
+        bool getActive() { return fActive; }
+    
+};
+
+
+//--------------------------------------------------------------------------------------
+// Linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter : public ValueConverter
+{
+    
+    private:
+        
+        Interpolator fUI2F;
+        Interpolator fF2UI;
+        
+    public:
+        
+        LinearValueConverter(double umin, double umax, double fmin, double fmax) :
+            fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax)
+        {}
+        
+        LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.)
+        {}
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Two segments linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter2 : public UpdatableValueConverter
+{
+    
+    private:
+    
+        Interpolator3pt fUI2F;
+        Interpolator3pt fF2UI;
+        
+    public:
+    
+        LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) :
+            fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax)
+        {}
+        
+        LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.)
+        {}
+    
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max)
+        {
+            fUI2F = Interpolator3pt(amin, amid, amax, min, init, max);
+            fF2UI = Interpolator3pt(min, init, max, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fUI2F.getMappingValues(amin, amid, amax);
+        }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Logarithmic conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LogValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        LogValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Exponential conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class ExpValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        ExpValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up curve (curve 0)
+//--------------------------------------------------------------------------------------
+class AccUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt fA2F;
+        Interpolator3pt fF2A;
+
+    public:
+
+        AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmid,fmax),
+            fF2A(fmin,fmid,fmax,amin,amid,amax)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down curve (curve 1)
+//--------------------------------------------------------------------------------------
+class AccDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator3pt        fF2A;
+
+    public:
+
+        AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmid,fmin),
+            fF2A(fmin,fmid,fmax,amax,amid,amin)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+             //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up-Down curve (curve 2)
+//--------------------------------------------------------------------------------------
+class AccUpDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmax,fmin),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down-Up curve (curve 3)
+//--------------------------------------------------------------------------------------
+class AccDownUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmin,fmax),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Base class for ZoneControl
+//--------------------------------------------------------------------------------------
+class ZoneControl
+{
+
+    protected:
+
+        FAUSTFLOAT*    fZone;
+
+    public:
+
+        ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {}
+        virtual ~ZoneControl() {}
+
+        virtual void update(double v) const {}
+
+        virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {}
+        virtual void getMappingValues(double& amin, double& amid, double& amax) {}
+
+        FAUSTFLOAT* getZone() { return fZone; }
+
+        virtual void setActive(bool on_off) {}
+        virtual bool getActive() { return false; }
+
+        virtual int getCurve() { return -1; }
+
+};
+
+//--------------------------------------------------------------------------------------
+//  Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class ConverterZoneControl : public ZoneControl
+{
+
+    protected:
+
+        ValueConverter* fValueConverter;
+
+    public:
+
+        ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {}
+        virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere...
+
+        virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); }
+
+        ValueConverter* getConverter() { return fValueConverter; }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Association of a zone and a four value converter, each one for each possible curve.
+// Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class CurveZoneControl : public ZoneControl
+{
+
+    private:
+
+        std::vector<UpdatableValueConverter*> fValueConverters;
+        int fCurve;
+
+    public:
+
+        CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0)
+        {
+            assert(curve >= 0 && curve <= 3);
+            fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max));
+            fCurve = curve;
+        }
+        virtual ~CurveZoneControl()
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                delete(*it);
+            }
+        }
+        void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); }
+
+        void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max)
+        {
+            fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max);
+            fCurve = curve;
+        }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fValueConverters[fCurve]->getMappingValues(amin, amid, amax);
+        }
+
+        void setActive(bool on_off)
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                (*it)->setActive(on_off);
+            }
+        }
+
+        int getCurve() { return fCurve; }
+};
+
+class ZoneReader
+{
+
+    private:
+
+        FAUSTFLOAT* fZone;
+        Interpolator fInterpolator;
+
+    public:
+
+        ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {}
+
+        virtual ~ZoneReader() {}
+
+        int getValue()
+        {
+            return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127;
+        }
+
+};
+
+#endif
+/**************************  END  ValueConverter.h **************************/
+
+class APIUI : public PathBuilder, public Meta, public UI
+{
+    public:
+    
+        enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph };
+  
+    protected:
+    
+        enum { kLin = 0, kLog = 1, kExp = 2 };
+    
+        int fNumParameters;
+        std::vector<std::string> fPaths;
+        std::vector<std::string> fLabels;
+        std::map<std::string, int> fPathMap;
+        std::map<std::string, int> fLabelMap;
+        std::vector<ValueConverter*> fConversion;
+        std::vector<FAUSTFLOAT*> fZone;
+        std::vector<FAUSTFLOAT> fInit;
+        std::vector<FAUSTFLOAT> fMin;
+        std::vector<FAUSTFLOAT> fMax;
+        std::vector<FAUSTFLOAT> fStep;
+        std::vector<ItemType> fItemType;
+        std::vector<std::map<std::string, std::string> > fMetaData;
+        std::vector<ZoneControl*> fAcc[3];
+        std::vector<ZoneControl*> fGyr[3];
+
+        // Screen color control
+        // "...[screencolor:red]..." etc.
+        bool fHasScreenControl;      // true if control screen color metadata
+        ZoneReader* fRedReader;
+        ZoneReader* fGreenReader;
+        ZoneReader* fBlueReader;
+
+        // Current values controlled by metadata
+        std::string fCurrentUnit;
+        int fCurrentScale;
+        std::string fCurrentAcc;
+        std::string fCurrentGyr;
+        std::string fCurrentColor;
+        std::string fCurrentTooltip;
+        std::map<std::string, std::string> fCurrentMetadata;
+    
+        // Add a generic parameter
+        virtual void addParameter(const char* label,
+                                FAUSTFLOAT* zone,
+                                FAUSTFLOAT init,
+                                FAUSTFLOAT min,
+                                FAUSTFLOAT max,
+                                FAUSTFLOAT step,
+                                ItemType type)
+        {
+            std::string path = buildPath(label);
+            fPathMap[path] = fLabelMap[label] = fNumParameters++;
+            fPaths.push_back(path);
+            fLabels.push_back(label);
+            fZone.push_back(zone);
+            fInit.push_back(init);
+            fMin.push_back(min);
+            fMax.push_back(max);
+            fStep.push_back(step);
+            fItemType.push_back(type);
+            
+            // handle scale metadata
+            switch (fCurrentScale) {
+                case kLin:
+                    fConversion.push_back(new LinearValueConverter(0, 1, min, max));
+                    break;
+                case kLog:
+                    fConversion.push_back(new LogValueConverter(0, 1, min, max));
+                    break;
+                case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max));
+                    break;
+            }
+            fCurrentScale = kLin;
+            
+            if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) {
+                std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n";
+            }
+
+            // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentAcc.size() > 0) {
+                std::istringstream iss(fCurrentAcc);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl;
+                }
+                fCurrentAcc = "";
+            }
+       
+            // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentGyr.size() > 0) {
+                std::istringstream iss(fCurrentGyr);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl;
+                }
+                fCurrentGyr = "";
+            }
+        
+            // handle screencolor metadata "...[screencolor:red|green|blue|white]..."
+            if (fCurrentColor.size() > 0) {
+                if ((fCurrentColor == "red") && (fRedReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "green") && (fGreenReader == 0)) {
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) {
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else {
+                    std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl;
+                }
+            }
+            fCurrentColor = "";
+            
+            fMetaData.push_back(fCurrentMetadata);
+            fCurrentMetadata.clear();
+        }
+
+        int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val)
+        {
+            FAUSTFLOAT* zone = fZone[p];
+            for (size_t i = 0; i < table[val].size(); i++) {
+                if (zone == table[val][i]->getZone()) return int(i);
+            }
+            return -1;
+        }
+    
+        void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            // Deactivates everywhere..
+            if (id1 != -1) table[0][id1]->setActive(false);
+            if (id2 != -1) table[1][id2]->setActive(false);
+            if (id3 != -1) table[2][id3]->setActive(false);
+            
+            if (val == -1) { // Means: no more mapping...
+                // So stay all deactivated...
+            } else {
+                int id4 = getZoneIndex(table, p, val);
+                if (id4 != -1) {
+                    // Reactivate the one we edit...
+                    table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]);
+                    table[val][id4]->setActive(true);
+                } else {
+                    // Allocate a new CurveZoneControl which is 'active' by default
+                    FAUSTFLOAT* zone = fZone[p];
+                    table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]));
+                }
+            }
+        }
+    
+        void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            if (id1 != -1) {
+                val = 0;
+                curve = table[val][id1]->getCurve();
+                table[val][id1]->getMappingValues(amin, amid, amax);
+            } else if (id2 != -1) {
+                val = 1;
+                curve = table[val][id2]->getCurve();
+                table[val][id2]->getMappingValues(amin, amid, amax);
+            } else if (id3 != -1) {
+                val = 2;
+                curve = table[val][id3]->getCurve();
+                table[val][id3]->getMappingValues(amin, amid, amax);
+            } else {
+                val = -1; // No mapping
+                curve = 0;
+                amin = -100.;
+                amid = 0.;
+                amax = 100.;
+            }
+        }
+
+     public:
+    
+        enum Type { kAcc = 0, kGyr = 1, kNoType };
+   
+        APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin)
+        {}
+
+        virtual ~APIUI()
+        {
+            for (auto& it : fConversion) delete it;
+            for (int i = 0; i < 3; i++) {
+                for (auto& it : fAcc[i]) delete it;
+                for (auto& it : fGyr[i]) delete it;
+            }
+            delete fRedReader;
+            delete fGreenReader;
+            delete fBlueReader;
+        }
+    
+        // -- widget's layouts
+
+        virtual void openTabBox(const char* label) { pushLabel(label); }
+        virtual void openHorizontalBox(const char* label) { pushLabel(label); }
+        virtual void openVerticalBox(const char* label) { pushLabel(label); }
+        virtual void closeBox() { popLabel(); }
+
+        // -- active widgets
+
+        virtual void addButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kButton);
+        }
+
+        virtual void addCheckButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kCheckButton);
+        }
+
+        virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kVSlider);
+        }
+
+        virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kHSlider);
+        }
+
+        virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kNumEntry);
+        }
+
+        // -- passive widgets
+
+        virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph);
+        }
+
+        virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph);
+        }
+    
+        // -- soundfiles
+    
+        virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {}
+
+        // -- metadata declarations
+
+        virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val)
+        {
+            // Keep metadata
+            fCurrentMetadata[key] = val;
+            
+            if (strcmp(key, "scale") == 0) {
+                if (strcmp(val, "log") == 0) {
+                    fCurrentScale = kLog;
+                } else if (strcmp(val, "exp") == 0) {
+                    fCurrentScale = kExp;
+                } else {
+                    fCurrentScale = kLin;
+                }
+            } else if (strcmp(key, "unit") == 0) {
+                fCurrentUnit = val;
+            } else if (strcmp(key, "acc") == 0) {
+                fCurrentAcc = val;
+            } else if (strcmp(key, "gyr") == 0) {
+                fCurrentGyr = val;
+            } else if (strcmp(key, "screencolor") == 0) {
+                fCurrentColor = val; // val = "red", "green", "blue" or "white"
+            } else if (strcmp(key, "tooltip") == 0) {
+                fCurrentTooltip = val;
+            }
+        }
+
+        virtual void declare(const char* key, const char* val)
+        {}
+
+               //-------------------------------------------------------------------------------
+               // Simple API part
+               //-------------------------------------------------------------------------------
+               int getParamsCount() { return fNumParameters; }
+        int getParamIndex(const char* path)
+        {
+            if (fPathMap.find(path) != fPathMap.end()) {
+                return fPathMap[path];
+            } else if (fLabelMap.find(path) != fLabelMap.end()) {
+                return fLabelMap[path];
+            } else {
+                return -1;
+            }
+        }
+        const char* getParamAddress(int p) { return fPaths[p].c_str(); }
+        const char* getParamLabel(int p) { return fLabels[p].c_str(); }
+        std::map<const char*, const char*> getMetadata(int p)
+        {
+            std::map<const char*, const char*> res;
+            std::map<std::string, std::string> metadata = fMetaData[p];
+            for (auto it : metadata) {
+                res[it.first.c_str()] = it.second.c_str();
+            }
+            return res;
+        }
+
+        const char* getMetadata(int p, const char* key)
+        {
+            return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : "";
+        }
+        FAUSTFLOAT getParamMin(int p) { return fMin[p]; }
+        FAUSTFLOAT getParamMax(int p) { return fMax[p]; }
+        FAUSTFLOAT getParamStep(int p) { return fStep[p]; }
+        FAUSTFLOAT getParamInit(int p) { return fInit[p]; }
+
+        FAUSTFLOAT* getParamZone(int p) { return fZone[p]; }
+        FAUSTFLOAT getParamValue(int p) { return *fZone[p]; }
+        void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; }
+
+        double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); }
+        void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); }
+
+        double value2ratio(int p, double r)    { return fConversion[p]->faust2ui(r); }
+        double ratio2value(int p, double r)    { return fConversion[p]->ui2faust(r); }
+    
+        /**
+         * Return the control type (kAcc, kGyr, or -1) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the type
+         */
+        Type getParamType(int p)
+        {
+            if (p >= 0) {
+                if (getZoneIndex(fAcc, p, 0) != -1
+                    || getZoneIndex(fAcc, p, 1) != -1
+                    || getZoneIndex(fAcc, p, 2) != -1) {
+                    return kAcc;
+                } else if (getZoneIndex(fGyr, p, 0) != -1
+                           || getZoneIndex(fGyr, p, 1) != -1
+                           || getZoneIndex(fGyr, p, 2) != -1) {
+                    return kGyr;
+                }
+            }
+            return kNoType;
+        }
+    
+        /**
+         * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the Item type
+         */
+        ItemType getParamItemType(int p)
+        {
+            return fItemType[p];
+        }
+   
+        /**
+         * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @param value - the new value
+         *
+         */
+        void propagateAcc(int acc, double value)
+        {
+            for (size_t i = 0; i < fAcc[acc].size(); i++) {
+                fAcc[acc][i]->update(value);
+            }
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax)
+        {
+            setConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax)
+        {
+             setConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - the acc value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+
+        /**
+         * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param gyr - the gyr value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param value - the new value
+         *
+         */
+        void propagateGyr(int gyr, double value)
+        {
+            for (size_t i = 0; i < fGyr[gyr].size(); i++) {
+                fGyr[gyr][i]->update(value);
+            }
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the accelerometer
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @return the number of zones
+         *
+         */
+        int getAccCount(int acc)
+        {
+            return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0;
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the gyroscope
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param the number of zones
+         *
+         */
+        int getGyrCount(int gyr)
+        {
+            return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0;
+        }
+   
+        // getScreenColor() : -1 means no screen color control (no screencolor metadata found)
+        // otherwise return 0x00RRGGBB a ready to use color
+        int getScreenColor()
+        {
+            if (fHasScreenControl) {
+                int r = (fRedReader) ? fRedReader->getValue() : 0;
+                int g = (fGreenReader) ? fGreenReader->getValue() : 0;
+                int b = (fBlueReader) ? fBlueReader->getValue() : 0;
+                return (r<<16) | (g<<8) | b;
+            } else {
+                return -1;
+            }
+        }
+};
+
+#endif
+/**************************  END  APIUI.h **************************/
+
+// NOTE: "faust -scn name" changes the last line above to
+// #include <faust/name/name.h>
+
+//----------------------------------------------------------------------------
+//  FAUST Generated Code
+//----------------------------------------------------------------------------
+
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif 
+
+#include <algorithm>
+#include <cmath>
+#include <math.h>
+
+
+#ifndef FAUSTCLASS 
+#define FAUSTCLASS limiterdsp
+#endif
+
+#ifdef __APPLE__ 
+#define exp10f __exp10f
+#define exp10 __exp10
+#endif
+
+class limiterdsp : public dsp {
+       
+ private:
+       
+       int fSampleRate;
+       float fConst0;
+       float fConst1;
+       float fConst2;
+       float fConst3;
+       int iRec5[2];
+       FAUSTFLOAT fHslider0;
+       int IOTA;
+       float fVec0[32];
+       float fRec4[2];
+       int iRec2[2];
+       float fRec1[2];
+       float fConst4;
+       float fConst5;
+       float fRec0[2];
+       int iConst6;
+       
+ public:
+       
+       void metadata(Meta* m) { 
+               m->declare("analyzers.lib/name", "Faust Analyzer Library");
+               m->declare("analyzers.lib/version", "0.1");
+               m->declare("basics.lib/name", "Faust Basic Element Library");
+               m->declare("basics.lib/version", "0.1");
+               m->declare("compressors.lib/limiter_lad_N:author", "Dario Sanfilippo");
+               m->declare("compressors.lib/limiter_lad_N:copyright", "Copyright (C) 2020 Dario Sanfilippo       <sanfilippo.dario@gmail.com>");
+               m->declare("compressors.lib/limiter_lad_N:license", "GPLv3 license");
+               m->declare("compressors.lib/limiter_lad_mono:author", "Dario Sanfilippo");
+               m->declare("compressors.lib/limiter_lad_mono:copyright", "Copyright (C) 2020 Dario Sanfilippo       <sanfilippo.dario@gmail.com>");
+               m->declare("compressors.lib/limiter_lad_mono:license", "GPLv3 license");
+               m->declare("compressors.lib/name", "Faust Compressor Effect Library");
+               m->declare("compressors.lib/version", "0.0");
+               m->declare("filename", "limiterdsp.dsp");
+               m->declare("maths.lib/author", "GRAME");
+               m->declare("maths.lib/copyright", "GRAME");
+               m->declare("maths.lib/license", "LGPL with exception");
+               m->declare("maths.lib/name", "Faust Math Library");
+               m->declare("maths.lib/version", "2.3");
+               m->declare("name", "limiterdsp");
+               m->declare("platform.lib/name", "Generic Platform Library");
+               m->declare("platform.lib/version", "0.1");
+               m->declare("routes.lib/name", "Faust Signal Routing Library");
+               m->declare("routes.lib/version", "0.2");
+               m->declare("signals.lib/name", "Faust Signal Routing Library");
+               m->declare("signals.lib/version", "0.0");
+       }
+
+       virtual int getNumInputs() {
+               return 1;
+       }
+       virtual int getNumOutputs() {
+               return 1;
+       }
+       virtual int getInputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       virtual int getOutputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       
+       static void classInit(int sample_rate) {
+       }
+       
+       virtual void instanceConstants(int sample_rate) {
+               fSampleRate = sample_rate;
+               fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate)));
+               fConst1 = std::exp((0.0f - (100000.0f / fConst0)));
+               fConst2 = (1.0f - fConst1);
+               fConst3 = (0.100000001f * fConst0);
+               fConst4 = std::exp((0.0f - (4.0f / fConst0)));
+               fConst5 = (1.0f - fConst4);
+               iConst6 = int((9.99999975e-05f * fConst0));
+       }
+       
+       virtual void instanceResetUserInterface() {
+               fHslider0 = FAUSTFLOAT(2.0f);
+       }
+       
+       virtual void instanceClear() {
+               for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
+                       iRec5[l0] = 0;
+               }
+               IOTA = 0;
+               for (int l1 = 0; (l1 < 32); l1 = (l1 + 1)) {
+                       fVec0[l1] = 0.0f;
+               }
+               for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) {
+                       fRec4[l2] = 0.0f;
+               }
+               for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) {
+                       iRec2[l3] = 0;
+               }
+               for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) {
+                       fRec1[l4] = 0.0f;
+               }
+               for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) {
+                       fRec0[l5] = 0.0f;
+               }
+       }
+       
+       virtual void init(int sample_rate) {
+               classInit(sample_rate);
+               instanceInit(sample_rate);
+       }
+       virtual void instanceInit(int sample_rate) {
+               instanceConstants(sample_rate);
+               instanceResetUserInterface();
+               instanceClear();
+       }
+       
+       virtual limiterdsp* clone() {
+               return new limiterdsp();
+       }
+       
+       virtual int getSampleRate() {
+               return fSampleRate;
+       }
+       
+       virtual void buildUserInterface(UI* ui_interface) {
+               ui_interface->openVerticalBox("limiterdsp");
+               ui_interface->declare(&fHslider0, "0", "");
+               ui_interface->addHorizontalSlider("NumClientsAssumed", &fHslider0, 2.0f, 1.0f, 64.0f, 1.0f);
+               ui_interface->closeBox();
+       }
+       
+       virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+               FAUSTFLOAT* input0 = inputs[0];
+               FAUSTFLOAT* output0 = outputs[0];
+               float fSlow0 = (1.0f / std::sqrt(float(fHslider0)));
+               for (int i = 0; (i < count); i = (i + 1)) {
+                       float fTemp0 = float(input0[i]);
+                       iRec5[0] = ((iRec5[1] + 1) % int(std::max<float>(1.0f, (fConst3 * float(iRec2[1])))));
+                       float fTemp1 = (fSlow0 * fTemp0);
+                       fVec0[(IOTA & 31)] = fTemp1;
+                       float fTemp2 = std::fabs(fTemp1);
+                       fRec4[0] = std::max<float>((float((iRec5[0] > 0)) * fRec4[1]), fTemp2);
+                       iRec2[0] = (fRec4[0] >= fTemp2);
+                       float fRec3 = fRec4[0];
+                       fRec1[0] = ((fConst1 * fRec1[1]) + (fConst2 * fRec3));
+                       float fTemp3 = std::fabs(fRec1[0]);
+                       fRec0[0] = std::max<float>(fTemp3, ((fConst4 * fRec0[1]) + (fConst5 * fTemp3)));
+                       output0[i] = FAUSTFLOAT((std::min<float>(1.0f, (0.5f / std::max<float>(fRec0[0], 1.1920929e-07f))) * fVec0[((IOTA - iConst6) & 31)]));
+                       iRec5[1] = iRec5[0];
+                       IOTA = (IOTA + 1);
+                       fRec4[1] = fRec4[0];
+                       iRec2[1] = iRec2[0];
+                       fRec1[1] = fRec1[0];
+                       fRec0[1] = fRec0[0];
+               }
+       }
+
+};
+
+#endif
diff --git a/src/makeXcodeproj.sh b/src/makeXcodeproj.sh
new file mode 100755 (executable)
index 0000000..c1dcf31
--- /dev/null
@@ -0,0 +1 @@
+qmake -spec macx-xcode jacktrip.pro
diff --git a/src/zitarevdsp.h b/src/zitarevdsp.h
new file mode 100644 (file)
index 0000000..96067d4
--- /dev/null
@@ -0,0 +1,2375 @@
+/* ------------------------------------------------------------
+name: "zitarevdsp"
+Code generated with Faust 2.28.6 (https://faust.grame.fr)
+Compilation options: -lang cpp -inpl -scal -ftz 0
+------------------------------------------------------------ */
+
+#ifndef  __zitarevdsp_H__
+#define  __zitarevdsp_H__
+
+// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME
+//
+// faust2header.cpp - FAUST Architecture File
+// This is a simple variation of matlabplot.cpp in the Faust distribution
+// aimed at creating a simple C++ header file (.h) containing a Faust DSP.
+// See the Makefile for how to use it.
+
+/************************** BEGIN dsp.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __dsp__
+#define __dsp__
+
+#include <string>
+#include <vector>
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+struct UI;
+struct Meta;
+
+/**
+ * DSP memory manager.
+ */
+
+struct dsp_memory_manager {
+    
+    virtual ~dsp_memory_manager() {}
+    
+    virtual void* allocate(size_t size) = 0;
+    virtual void destroy(void* ptr) = 0;
+    
+};
+
+/**
+* Signal processor definition.
+*/
+
+class dsp {
+
+    public:
+
+        dsp() {}
+        virtual ~dsp() {}
+
+        /* Return instance number of audio inputs */
+        virtual int getNumInputs() = 0;
+    
+        /* Return instance number of audio outputs */
+        virtual int getNumOutputs() = 0;
+    
+        /**
+         * Trigger the ui_interface parameter with instance specific calls
+         * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI.
+         *
+         * @param ui_interface - the user interface builder
+         */
+        virtual void buildUserInterface(UI* ui_interface) = 0;
+    
+        /* Returns the sample rate currently used by the instance */
+        virtual int getSampleRate() = 0;
+    
+        /**
+         * Global init, calls the following methods:
+         * - static class 'classInit': static tables initialization
+         * - 'instanceInit': constants and instance state initialization
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void init(int sample_rate) = 0;
+
+        /**
+         * Init instance state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceInit(int sample_rate) = 0;
+
+        /**
+         * Init instance constant state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceConstants(int sample_rate) = 0;
+    
+        /* Init default control parameters values */
+        virtual void instanceResetUserInterface() = 0;
+    
+        /* Init instance state (delay lines...) */
+        virtual void instanceClear() = 0;
+        /**
+         * Return a clone of the instance.
+         *
+         * @return a copy of the instance on success, otherwise a null pointer.
+         */
+        virtual dsp* clone() = 0;
+    
+        /**
+         * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata.
+         *
+         * @param m - the Meta* meta user
+         */
+        virtual void metadata(Meta* m) = 0;
+    
+        /**
+         * DSP instance computation, to be called with successive in/out audio buffers.
+         *
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         *
+         */
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0;
+    
+        /**
+         * DSP instance computation: alternative method to be used by subclasses.
+         *
+         * @param date_usec - the timestamp in microsec given by audio driver.
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         *
+         */
+        virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); }
+       
+};
+
+/**
+ * Generic DSP decorator.
+ */
+
+class decorator_dsp : public dsp {
+
+    protected:
+
+        dsp* fDSP;
+
+    public:
+
+        decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {}
+        virtual ~decorator_dsp() { delete fDSP; }
+
+        virtual int getNumInputs() { return fDSP->getNumInputs(); }
+        virtual int getNumOutputs() { return fDSP->getNumOutputs(); }
+        virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); }
+        virtual int getSampleRate() { return fDSP->getSampleRate(); }
+        virtual void init(int sample_rate) { fDSP->init(sample_rate); }
+        virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); }
+        virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); }
+        virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); }
+        virtual void instanceClear() { fDSP->instanceClear(); }
+        virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); }
+        virtual void metadata(Meta* m) { fDSP->metadata(m); }
+        // Beware: subclasses usually have to overload the two 'compute' methods
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); }
+        virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); }
+    
+};
+
+/**
+ * DSP factory class.
+ */
+
+class dsp_factory {
+    
+    protected:
+    
+        // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory);
+        virtual ~dsp_factory() {}
+    
+    public:
+    
+        virtual std::string getName() = 0;
+        virtual std::string getSHAKey() = 0;
+        virtual std::string getDSPCode() = 0;
+        virtual std::string getCompileOptions() = 0;
+        virtual std::vector<std::string> getLibraryList() = 0;
+        virtual std::vector<std::string> getIncludePathnames() = 0;
+    
+        virtual dsp* createDSPInstance() = 0;
+    
+        virtual void setMemoryManager(dsp_memory_manager* manager) = 0;
+        virtual dsp_memory_manager* getMemoryManager() = 0;
+    
+};
+
+/**
+ * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
+ * flags to avoid costly denormals.
+ */
+
+#ifdef __SSE__
+    #include <xmmintrin.h>
+    #ifdef __SSE2__
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
+    #else
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
+    #endif
+#else
+    #define AVOIDDENORMALS
+#endif
+
+#endif
+/**************************  END  dsp.h **************************/
+
+/************************** BEGIN APIUI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef API_UI_H
+#define API_UI_H
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <map>
+
+/************************** BEGIN meta.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __meta__
+#define __meta__
+
+struct Meta
+{
+    virtual ~Meta() {};
+    virtual void declare(const char* key, const char* value) = 0;
+    
+};
+
+#endif
+/**************************  END  meta.h **************************/
+/************************** BEGIN UI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __UI_H__
+#define __UI_H__
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+/*******************************************************************************
+ * UI : Faust DSP User Interface
+ * User Interface as expected by the buildUserInterface() method of a DSP.
+ * This abstract class contains only the method that the Faust compiler can
+ * generate to describe a DSP user interface.
+ ******************************************************************************/
+
+struct Soundfile;
+
+template <typename REAL>
+struct UIReal
+{
+    UIReal() {}
+    virtual ~UIReal() {}
+    
+    // -- widget's layouts
+    
+    virtual void openTabBox(const char* label) = 0;
+    virtual void openHorizontalBox(const char* label) = 0;
+    virtual void openVerticalBox(const char* label) = 0;
+    virtual void closeBox() = 0;
+    
+    // -- active widgets
+    
+    virtual void addButton(const char* label, REAL* zone) = 0;
+    virtual void addCheckButton(const char* label, REAL* zone) = 0;
+    virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    
+    // -- passive widgets
+    
+    virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    
+    // -- soundfiles
+    
+    virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0;
+    
+    // -- metadata declarations
+    
+    virtual void declare(REAL* zone, const char* key, const char* val) {}
+};
+
+struct UI : public UIReal<FAUSTFLOAT>
+{
+    UI() {}
+    virtual ~UI() {}
+};
+
+#endif
+/**************************  END  UI.h **************************/
+/************************** BEGIN PathBuilder.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef FAUST_PATHBUILDER_H
+#define FAUST_PATHBUILDER_H
+
+#include <vector>
+#include <string>
+#include <algorithm>
+
+/*******************************************************************************
+ * PathBuilder : Faust User Interface
+ * Helper class to build complete hierarchical path for UI items.
+ ******************************************************************************/
+
+class PathBuilder
+{
+
+    protected:
+    
+        std::vector<std::string> fControlsLevel;
+       
+    public:
+    
+        PathBuilder() {}
+        virtual ~PathBuilder() {}
+    
+        std::string buildPath(const std::string& label) 
+        {
+            std::string res = "/";
+            for (size_t i = 0; i < fControlsLevel.size(); i++) {
+                res += fControlsLevel[i];
+                res += "/";
+            }
+            res += label;
+            std::replace(res.begin(), res.end(), ' ', '_');
+            return res;
+        }
+    
+        std::string buildLabel(std::string label)
+        {
+            std::replace(label.begin(), label.end(), ' ', '_');
+            return label;
+        }
+    
+        void pushLabel(const std::string& label) { fControlsLevel.push_back(label); }
+        void popLabel() { fControlsLevel.pop_back(); }
+    
+};
+
+#endif  // FAUST_PATHBUILDER_H
+/**************************  END  PathBuilder.h **************************/
+/************************** BEGIN ValueConverter.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __ValueConverter__
+#define __ValueConverter__
+
+/***************************************************************************************
+                                                               ValueConverter.h
+                            (GRAME, Copyright 2015-2019)
+
+Set of conversion objects used to map user interface values (for example a gui slider
+delivering values between 0 and 1) to faust values (for example a vslider between
+20 and 20000) using a log scale.
+
+-- Utilities
+
+Range(lo,hi) : clip a value x between lo and hi
+Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2
+Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2
+
+-- Value Converters
+
+ValueConverter::ui2faust(x)
+ValueConverter::faust2ui(x)
+
+-- ValueConverters used for sliders depending of the scale
+
+LinearValueConverter(umin, umax, fmin, fmax)
+LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments
+LogValueConverter(umin, umax, fmin, fmax)
+ExpValueConverter(umin, umax, fmin, fmax)
+
+-- ValueConverters used for accelerometers based on 3 points
+
+AccUpConverter(amin, amid, amax, fmin, fmid, fmax)             -- curve 0
+AccDownConverter(amin, amid, amax, fmin, fmid, fmax)   -- curve 1
+AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2
+AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3
+
+-- lists of ZoneControl are used to implement accelerometers metadata for each axes
+
+ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter
+
+-- ZoneReader are used to implement screencolor metadata
+
+ZoneReader(zone, valueConverter) : a zone with a data converter
+
+****************************************************************************************/
+
+#include <float.h>
+#include <algorithm>    // std::max
+#include <cmath>
+#include <vector>
+#include <assert.h>
+
+//--------------------------------------------------------------------------------------
+// Interpolator(lo,hi,v1,v2)
+// Maps a value x between lo and hi to a value y between v1 and v2
+// y = v1 + (x-lo)/(hi-lo)*(v2-v1)
+// y = v1 + (x-lo) * coef              with coef = (v2-v1)/(hi-lo)
+// y = v1 + x*coef - lo*coef
+// y = v1 - lo*coef + x*coef
+// y = offset + x*coef                         with offset = v1 - lo*coef
+//--------------------------------------------------------------------------------------
+class Interpolator
+{
+    private:
+
+        //--------------------------------------------------------------------------------------
+        // Range(lo,hi) clip a value between lo and hi
+        //--------------------------------------------------------------------------------------
+        struct Range
+        {
+            double fLo;
+            double fHi;
+
+            Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {}
+            double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; }
+        };
+
+
+        Range fRange;
+        double fCoef;
+        double fOffset;
+
+    public:
+
+        Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi)
+        {
+            if (hi != lo) {
+                // regular case
+                fCoef = (v2-v1)/(hi-lo);
+                fOffset = v1 - lo*fCoef;
+            } else {
+                // degenerate case, avoids division by zero
+                fCoef = 0;
+                fOffset = (v1+v2)/2;
+            }
+        }
+        double operator()(double v)
+        {
+            double x = fRange(v);
+            return  fOffset + x*fCoef;
+        }
+
+        void getLowHigh(double& amin, double& amax)
+        {
+            amin = fRange.fLo;
+            amax = fRange.fHi;
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Interpolator3pt(lo,mi,hi,v1,vm,v2)
+// Map values between lo mid hi to values between v1 vm v2
+//--------------------------------------------------------------------------------------
+class Interpolator3pt
+{
+
+    private:
+
+        Interpolator fSegment1;
+        Interpolator fSegment2;
+        double fMid;
+
+    public:
+
+        Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) :
+            fSegment1(lo, mi, v1, vm),
+            fSegment2(mi, hi, vm, v2),
+            fMid(mi) {}
+        double operator()(double x) { return  (x < fMid) ? fSegment1(x) : fSegment2(x); }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fSegment1.getLowHigh(amin, amid);
+            fSegment2.getLowHigh(amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Abstract ValueConverter class. Converts values between UI and Faust representations
+//--------------------------------------------------------------------------------------
+class ValueConverter
+{
+
+    public:
+
+        virtual ~ValueConverter() {}
+        virtual double ui2faust(double x) = 0;
+        virtual double faust2ui(double x) = 0;
+};
+
+//--------------------------------------------------------------------------------------
+// A converter than can be updated
+//--------------------------------------------------------------------------------------
+
+class UpdatableValueConverter : public ValueConverter {
+    
+    protected:
+        
+        bool fActive;
+        
+    public:
+        
+        UpdatableValueConverter():fActive(true)
+        {}
+        virtual ~UpdatableValueConverter()
+        {}
+        
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0;
+        virtual void getMappingValues(double& amin, double& amid, double& amax) = 0;
+        
+        void setActive(bool on_off) { fActive = on_off; }
+        bool getActive() { return fActive; }
+    
+};
+
+
+//--------------------------------------------------------------------------------------
+// Linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter : public ValueConverter
+{
+    
+    private:
+        
+        Interpolator fUI2F;
+        Interpolator fF2UI;
+        
+    public:
+        
+        LinearValueConverter(double umin, double umax, double fmin, double fmax) :
+            fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax)
+        {}
+        
+        LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.)
+        {}
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Two segments linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter2 : public UpdatableValueConverter
+{
+    
+    private:
+    
+        Interpolator3pt fUI2F;
+        Interpolator3pt fF2UI;
+        
+    public:
+    
+        LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) :
+            fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax)
+        {}
+        
+        LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.)
+        {}
+    
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max)
+        {
+            fUI2F = Interpolator3pt(amin, amid, amax, min, init, max);
+            fF2UI = Interpolator3pt(min, init, max, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fUI2F.getMappingValues(amin, amid, amax);
+        }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Logarithmic conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LogValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        LogValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Exponential conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class ExpValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        ExpValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up curve (curve 0)
+//--------------------------------------------------------------------------------------
+class AccUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt fA2F;
+        Interpolator3pt fF2A;
+
+    public:
+
+        AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmid,fmax),
+            fF2A(fmin,fmid,fmax,amin,amid,amax)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down curve (curve 1)
+//--------------------------------------------------------------------------------------
+class AccDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator3pt        fF2A;
+
+    public:
+
+        AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmid,fmin),
+            fF2A(fmin,fmid,fmax,amax,amid,amin)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+             //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up-Down curve (curve 2)
+//--------------------------------------------------------------------------------------
+class AccUpDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmax,fmin),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down-Up curve (curve 3)
+//--------------------------------------------------------------------------------------
+class AccDownUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmin,fmax),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Base class for ZoneControl
+//--------------------------------------------------------------------------------------
+class ZoneControl
+{
+
+    protected:
+
+        FAUSTFLOAT*    fZone;
+
+    public:
+
+        ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {}
+        virtual ~ZoneControl() {}
+
+        virtual void update(double v) const {}
+
+        virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {}
+        virtual void getMappingValues(double& amin, double& amid, double& amax) {}
+
+        FAUSTFLOAT* getZone() { return fZone; }
+
+        virtual void setActive(bool on_off) {}
+        virtual bool getActive() { return false; }
+
+        virtual int getCurve() { return -1; }
+
+};
+
+//--------------------------------------------------------------------------------------
+//  Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class ConverterZoneControl : public ZoneControl
+{
+
+    protected:
+
+        ValueConverter* fValueConverter;
+
+    public:
+
+        ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {}
+        virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere...
+
+        virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); }
+
+        ValueConverter* getConverter() { return fValueConverter; }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Association of a zone and a four value converter, each one for each possible curve.
+// Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class CurveZoneControl : public ZoneControl
+{
+
+    private:
+
+        std::vector<UpdatableValueConverter*> fValueConverters;
+        int fCurve;
+
+    public:
+
+        CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0)
+        {
+            assert(curve >= 0 && curve <= 3);
+            fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max));
+            fCurve = curve;
+        }
+        virtual ~CurveZoneControl()
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                delete(*it);
+            }
+        }
+        void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); }
+
+        void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max)
+        {
+            fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max);
+            fCurve = curve;
+        }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fValueConverters[fCurve]->getMappingValues(amin, amid, amax);
+        }
+
+        void setActive(bool on_off)
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                (*it)->setActive(on_off);
+            }
+        }
+
+        int getCurve() { return fCurve; }
+};
+
+class ZoneReader
+{
+
+    private:
+
+        FAUSTFLOAT* fZone;
+        Interpolator fInterpolator;
+
+    public:
+
+        ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {}
+
+        virtual ~ZoneReader() {}
+
+        int getValue()
+        {
+            return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127;
+        }
+
+};
+
+#endif
+/**************************  END  ValueConverter.h **************************/
+
+class APIUI : public PathBuilder, public Meta, public UI
+{
+    public:
+    
+        enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph };
+  
+    protected:
+    
+        enum { kLin = 0, kLog = 1, kExp = 2 };
+    
+        int fNumParameters;
+        std::vector<std::string> fPaths;
+        std::vector<std::string> fLabels;
+        std::map<std::string, int> fPathMap;
+        std::map<std::string, int> fLabelMap;
+        std::vector<ValueConverter*> fConversion;
+        std::vector<FAUSTFLOAT*> fZone;
+        std::vector<FAUSTFLOAT> fInit;
+        std::vector<FAUSTFLOAT> fMin;
+        std::vector<FAUSTFLOAT> fMax;
+        std::vector<FAUSTFLOAT> fStep;
+        std::vector<ItemType> fItemType;
+        std::vector<std::map<std::string, std::string> > fMetaData;
+        std::vector<ZoneControl*> fAcc[3];
+        std::vector<ZoneControl*> fGyr[3];
+
+        // Screen color control
+        // "...[screencolor:red]..." etc.
+        bool fHasScreenControl;      // true if control screen color metadata
+        ZoneReader* fRedReader;
+        ZoneReader* fGreenReader;
+        ZoneReader* fBlueReader;
+
+        // Current values controlled by metadata
+        std::string fCurrentUnit;
+        int fCurrentScale;
+        std::string fCurrentAcc;
+        std::string fCurrentGyr;
+        std::string fCurrentColor;
+        std::string fCurrentTooltip;
+        std::map<std::string, std::string> fCurrentMetadata;
+    
+        // Add a generic parameter
+        virtual void addParameter(const char* label,
+                                FAUSTFLOAT* zone,
+                                FAUSTFLOAT init,
+                                FAUSTFLOAT min,
+                                FAUSTFLOAT max,
+                                FAUSTFLOAT step,
+                                ItemType type)
+        {
+            std::string path = buildPath(label);
+            fPathMap[path] = fLabelMap[label] = fNumParameters++;
+            fPaths.push_back(path);
+            fLabels.push_back(label);
+            fZone.push_back(zone);
+            fInit.push_back(init);
+            fMin.push_back(min);
+            fMax.push_back(max);
+            fStep.push_back(step);
+            fItemType.push_back(type);
+            
+            // handle scale metadata
+            switch (fCurrentScale) {
+                case kLin:
+                    fConversion.push_back(new LinearValueConverter(0, 1, min, max));
+                    break;
+                case kLog:
+                    fConversion.push_back(new LogValueConverter(0, 1, min, max));
+                    break;
+                case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max));
+                    break;
+            }
+            fCurrentScale = kLin;
+            
+            if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) {
+                std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n";
+            }
+
+            // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentAcc.size() > 0) {
+                std::istringstream iss(fCurrentAcc);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl;
+                }
+                fCurrentAcc = "";
+            }
+       
+            // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentGyr.size() > 0) {
+                std::istringstream iss(fCurrentGyr);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl;
+                }
+                fCurrentGyr = "";
+            }
+        
+            // handle screencolor metadata "...[screencolor:red|green|blue|white]..."
+            if (fCurrentColor.size() > 0) {
+                if ((fCurrentColor == "red") && (fRedReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "green") && (fGreenReader == 0)) {
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) {
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else {
+                    std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl;
+                }
+            }
+            fCurrentColor = "";
+            
+            fMetaData.push_back(fCurrentMetadata);
+            fCurrentMetadata.clear();
+        }
+
+        int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val)
+        {
+            FAUSTFLOAT* zone = fZone[p];
+            for (size_t i = 0; i < table[val].size(); i++) {
+                if (zone == table[val][i]->getZone()) return int(i);
+            }
+            return -1;
+        }
+    
+        void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            // Deactivates everywhere..
+            if (id1 != -1) table[0][id1]->setActive(false);
+            if (id2 != -1) table[1][id2]->setActive(false);
+            if (id3 != -1) table[2][id3]->setActive(false);
+            
+            if (val == -1) { // Means: no more mapping...
+                // So stay all deactivated...
+            } else {
+                int id4 = getZoneIndex(table, p, val);
+                if (id4 != -1) {
+                    // Reactivate the one we edit...
+                    table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]);
+                    table[val][id4]->setActive(true);
+                } else {
+                    // Allocate a new CurveZoneControl which is 'active' by default
+                    FAUSTFLOAT* zone = fZone[p];
+                    table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]));
+                }
+            }
+        }
+    
+        void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            if (id1 != -1) {
+                val = 0;
+                curve = table[val][id1]->getCurve();
+                table[val][id1]->getMappingValues(amin, amid, amax);
+            } else if (id2 != -1) {
+                val = 1;
+                curve = table[val][id2]->getCurve();
+                table[val][id2]->getMappingValues(amin, amid, amax);
+            } else if (id3 != -1) {
+                val = 2;
+                curve = table[val][id3]->getCurve();
+                table[val][id3]->getMappingValues(amin, amid, amax);
+            } else {
+                val = -1; // No mapping
+                curve = 0;
+                amin = -100.;
+                amid = 0.;
+                amax = 100.;
+            }
+        }
+
+     public:
+    
+        enum Type { kAcc = 0, kGyr = 1, kNoType };
+   
+        APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin)
+        {}
+
+        virtual ~APIUI()
+        {
+            for (auto& it : fConversion) delete it;
+            for (int i = 0; i < 3; i++) {
+                for (auto& it : fAcc[i]) delete it;
+                for (auto& it : fGyr[i]) delete it;
+            }
+            delete fRedReader;
+            delete fGreenReader;
+            delete fBlueReader;
+        }
+    
+        // -- widget's layouts
+
+        virtual void openTabBox(const char* label) { pushLabel(label); }
+        virtual void openHorizontalBox(const char* label) { pushLabel(label); }
+        virtual void openVerticalBox(const char* label) { pushLabel(label); }
+        virtual void closeBox() { popLabel(); }
+
+        // -- active widgets
+
+        virtual void addButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kButton);
+        }
+
+        virtual void addCheckButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kCheckButton);
+        }
+
+        virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kVSlider);
+        }
+
+        virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kHSlider);
+        }
+
+        virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kNumEntry);
+        }
+
+        // -- passive widgets
+
+        virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph);
+        }
+
+        virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph);
+        }
+    
+        // -- soundfiles
+    
+        virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {}
+
+        // -- metadata declarations
+
+        virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val)
+        {
+            // Keep metadata
+            fCurrentMetadata[key] = val;
+            
+            if (strcmp(key, "scale") == 0) {
+                if (strcmp(val, "log") == 0) {
+                    fCurrentScale = kLog;
+                } else if (strcmp(val, "exp") == 0) {
+                    fCurrentScale = kExp;
+                } else {
+                    fCurrentScale = kLin;
+                }
+            } else if (strcmp(key, "unit") == 0) {
+                fCurrentUnit = val;
+            } else if (strcmp(key, "acc") == 0) {
+                fCurrentAcc = val;
+            } else if (strcmp(key, "gyr") == 0) {
+                fCurrentGyr = val;
+            } else if (strcmp(key, "screencolor") == 0) {
+                fCurrentColor = val; // val = "red", "green", "blue" or "white"
+            } else if (strcmp(key, "tooltip") == 0) {
+                fCurrentTooltip = val;
+            }
+        }
+
+        virtual void declare(const char* key, const char* val)
+        {}
+
+               //-------------------------------------------------------------------------------
+               // Simple API part
+               //-------------------------------------------------------------------------------
+               int getParamsCount() { return fNumParameters; }
+        int getParamIndex(const char* path)
+        {
+            if (fPathMap.find(path) != fPathMap.end()) {
+                return fPathMap[path];
+            } else if (fLabelMap.find(path) != fLabelMap.end()) {
+                return fLabelMap[path];
+            } else {
+                return -1;
+            }
+        }
+        const char* getParamAddress(int p) { return fPaths[p].c_str(); }
+        const char* getParamLabel(int p) { return fLabels[p].c_str(); }
+        std::map<const char*, const char*> getMetadata(int p)
+        {
+            std::map<const char*, const char*> res;
+            std::map<std::string, std::string> metadata = fMetaData[p];
+            for (auto it : metadata) {
+                res[it.first.c_str()] = it.second.c_str();
+            }
+            return res;
+        }
+
+        const char* getMetadata(int p, const char* key)
+        {
+            return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : "";
+        }
+        FAUSTFLOAT getParamMin(int p) { return fMin[p]; }
+        FAUSTFLOAT getParamMax(int p) { return fMax[p]; }
+        FAUSTFLOAT getParamStep(int p) { return fStep[p]; }
+        FAUSTFLOAT getParamInit(int p) { return fInit[p]; }
+
+        FAUSTFLOAT* getParamZone(int p) { return fZone[p]; }
+        FAUSTFLOAT getParamValue(int p) { return *fZone[p]; }
+        void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; }
+
+        double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); }
+        void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); }
+
+        double value2ratio(int p, double r)    { return fConversion[p]->faust2ui(r); }
+        double ratio2value(int p, double r)    { return fConversion[p]->ui2faust(r); }
+    
+        /**
+         * Return the control type (kAcc, kGyr, or -1) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the type
+         */
+        Type getParamType(int p)
+        {
+            if (p >= 0) {
+                if (getZoneIndex(fAcc, p, 0) != -1
+                    || getZoneIndex(fAcc, p, 1) != -1
+                    || getZoneIndex(fAcc, p, 2) != -1) {
+                    return kAcc;
+                } else if (getZoneIndex(fGyr, p, 0) != -1
+                           || getZoneIndex(fGyr, p, 1) != -1
+                           || getZoneIndex(fGyr, p, 2) != -1) {
+                    return kGyr;
+                }
+            }
+            return kNoType;
+        }
+    
+        /**
+         * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the Item type
+         */
+        ItemType getParamItemType(int p)
+        {
+            return fItemType[p];
+        }
+   
+        /**
+         * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @param value - the new value
+         *
+         */
+        void propagateAcc(int acc, double value)
+        {
+            for (size_t i = 0; i < fAcc[acc].size(); i++) {
+                fAcc[acc][i]->update(value);
+            }
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax)
+        {
+            setConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax)
+        {
+             setConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - the acc value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+
+        /**
+         * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param gyr - the gyr value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param value - the new value
+         *
+         */
+        void propagateGyr(int gyr, double value)
+        {
+            for (size_t i = 0; i < fGyr[gyr].size(); i++) {
+                fGyr[gyr][i]->update(value);
+            }
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the accelerometer
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @return the number of zones
+         *
+         */
+        int getAccCount(int acc)
+        {
+            return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0;
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the gyroscope
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param the number of zones
+         *
+         */
+        int getGyrCount(int gyr)
+        {
+            return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0;
+        }
+   
+        // getScreenColor() : -1 means no screen color control (no screencolor metadata found)
+        // otherwise return 0x00RRGGBB a ready to use color
+        int getScreenColor()
+        {
+            if (fHasScreenControl) {
+                int r = (fRedReader) ? fRedReader->getValue() : 0;
+                int g = (fGreenReader) ? fGreenReader->getValue() : 0;
+                int b = (fBlueReader) ? fBlueReader->getValue() : 0;
+                return (r<<16) | (g<<8) | b;
+            } else {
+                return -1;
+            }
+        }
+};
+
+#endif
+/**************************  END  APIUI.h **************************/
+
+// NOTE: "faust -scn name" changes the last line above to
+// #include <faust/name/name.h>
+
+//----------------------------------------------------------------------------
+//  FAUST Generated Code
+//----------------------------------------------------------------------------
+
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif 
+
+#include <algorithm>
+#include <cmath>
+#include <math.h>
+
+static float zitarevdsp_faustpower2_f(float value) {
+       return (value * value);
+}
+
+#ifndef FAUSTCLASS 
+#define FAUSTCLASS zitarevdsp
+#endif
+
+#ifdef __APPLE__ 
+#define exp10f __exp10f
+#define exp10 __exp10
+#endif
+
+class zitarevdsp : public dsp {
+       
+ private:
+       
+       int IOTA;
+       float fVec0[16384];
+       float fVec1[16384];
+       FAUSTFLOAT fVslider0;
+       float fRec0[2];
+       FAUSTFLOAT fVslider1;
+       float fRec1[2];
+       int fSampleRate;
+       float fConst0;
+       float fConst1;
+       FAUSTFLOAT fVslider2;
+       FAUSTFLOAT fVslider3;
+       FAUSTFLOAT fVslider4;
+       FAUSTFLOAT fVslider5;
+       float fConst2;
+       float fConst3;
+       FAUSTFLOAT fVslider6;
+       FAUSTFLOAT fVslider7;
+       FAUSTFLOAT fVslider8;
+       float fConst4;
+       FAUSTFLOAT fVslider9;
+       float fRec15[2];
+       float fRec14[2];
+       float fVec2[32768];
+       float fConst5;
+       int iConst6;
+       float fConst7;
+       FAUSTFLOAT fVslider10;
+       float fVec3[2048];
+       int iConst8;
+       float fRec12[2];
+       float fConst9;
+       float fConst10;
+       float fRec19[2];
+       float fRec18[2];
+       float fVec4[32768];
+       float fConst11;
+       int iConst12;
+       float fVec5[4096];
+       int iConst13;
+       float fRec16[2];
+       float fConst14;
+       float fConst15;
+       float fRec23[2];
+       float fRec22[2];
+       float fVec6[16384];
+       float fConst16;
+       int iConst17;
+       float fVec7[4096];
+       int iConst18;
+       float fRec20[2];
+       float fConst19;
+       float fConst20;
+       float fRec27[2];
+       float fRec26[2];
+       float fVec8[32768];
+       float fConst21;
+       int iConst22;
+       float fVec9[4096];
+       int iConst23;
+       float fRec24[2];
+       float fConst24;
+       float fConst25;
+       float fRec31[2];
+       float fRec30[2];
+       float fVec10[16384];
+       float fConst26;
+       int iConst27;
+       float fVec11[2048];
+       int iConst28;
+       float fRec28[2];
+       float fConst29;
+       float fConst30;
+       float fRec35[2];
+       float fRec34[2];
+       float fVec12[16384];
+       float fConst31;
+       int iConst32;
+       float fVec13[4096];
+       int iConst33;
+       float fRec32[2];
+       float fConst34;
+       float fConst35;
+       float fRec39[2];
+       float fRec38[2];
+       float fVec14[16384];
+       float fConst36;
+       int iConst37;
+       float fVec15[4096];
+       int iConst38;
+       float fRec36[2];
+       float fConst39;
+       float fConst40;
+       float fRec43[2];
+       float fRec42[2];
+       float fVec16[16384];
+       float fConst41;
+       int iConst42;
+       float fVec17[2048];
+       int iConst43;
+       float fRec40[2];
+       float fRec4[3];
+       float fRec5[3];
+       float fRec6[3];
+       float fRec7[3];
+       float fRec8[3];
+       float fRec9[3];
+       float fRec10[3];
+       float fRec11[3];
+       float fRec3[3];
+       float fRec2[3];
+       float fRec45[3];
+       float fRec44[3];
+       
+ public:
+       
+       void metadata(Meta* m) { 
+               m->declare("basics.lib/name", "Faust Basic Element Library");
+               m->declare("basics.lib/version", "0.1");
+               m->declare("delays.lib/name", "Faust Delay Library");
+               m->declare("delays.lib/version", "0.1");
+               m->declare("filename", "zitarevdsp.dsp");
+               m->declare("filters.lib/allpass_comb:author", "Julius O. Smith III");
+               m->declare("filters.lib/allpass_comb:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/allpass_comb:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/fir:author", "Julius O. Smith III");
+               m->declare("filters.lib/fir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/fir:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/iir:author", "Julius O. Smith III");
+               m->declare("filters.lib/iir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/iir:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/lowpass0_highpass1", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/lowpass0_highpass1:author", "Julius O. Smith III");
+               m->declare("filters.lib/lowpass:author", "Julius O. Smith III");
+               m->declare("filters.lib/lowpass:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/lowpass:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/name", "Faust Filters Library");
+               m->declare("filters.lib/peak_eq_rm:author", "Julius O. Smith III");
+               m->declare("filters.lib/peak_eq_rm:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/peak_eq_rm:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/tf1:author", "Julius O. Smith III");
+               m->declare("filters.lib/tf1:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/tf1:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/tf1s:author", "Julius O. Smith III");
+               m->declare("filters.lib/tf1s:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/tf1s:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/tf2:author", "Julius O. Smith III");
+               m->declare("filters.lib/tf2:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/tf2:license", "MIT-style STK-4.3 license");
+               m->declare("maths.lib/author", "GRAME");
+               m->declare("maths.lib/copyright", "GRAME");
+               m->declare("maths.lib/license", "LGPL with exception");
+               m->declare("maths.lib/name", "Faust Math Library");
+               m->declare("maths.lib/version", "2.3");
+               m->declare("name", "zitarevdsp");
+               m->declare("platform.lib/name", "Generic Platform Library");
+               m->declare("platform.lib/version", "0.1");
+               m->declare("reverbs.lib/name", "Faust Reverb Library");
+               m->declare("reverbs.lib/version", "0.0");
+               m->declare("routes.lib/name", "Faust Signal Routing Library");
+               m->declare("routes.lib/version", "0.2");
+               m->declare("signals.lib/name", "Faust Signal Routing Library");
+               m->declare("signals.lib/version", "0.0");
+       }
+
+       virtual int getNumInputs() {
+               return 2;
+       }
+       virtual int getNumOutputs() {
+               return 2;
+       }
+       virtual int getInputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       case 1: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       virtual int getOutputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       case 1: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       
+       static void classInit(int sample_rate) {
+       }
+       
+       virtual void instanceConstants(int sample_rate) {
+               fSampleRate = sample_rate;
+               fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate)));
+               fConst1 = (6.28318548f / fConst0);
+               fConst2 = std::floor(((0.219990999f * fConst0) + 0.5f));
+               fConst3 = ((0.0f - (6.90775537f * fConst2)) / fConst0);
+               fConst4 = (3.14159274f / fConst0);
+               fConst5 = std::floor(((0.0191229992f * fConst0) + 0.5f));
+               iConst6 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst2 - fConst5))));
+               fConst7 = (0.00100000005f * fConst0);
+               iConst8 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst5 + -1.0f))));
+               fConst9 = std::floor(((0.256891012f * fConst0) + 0.5f));
+               fConst10 = ((0.0f - (6.90775537f * fConst9)) / fConst0);
+               fConst11 = std::floor(((0.0273330007f * fConst0) + 0.5f));
+               iConst12 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst9 - fConst11))));
+               iConst13 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst11 + -1.0f))));
+               fConst14 = std::floor(((0.192303002f * fConst0) + 0.5f));
+               fConst15 = ((0.0f - (6.90775537f * fConst14)) / fConst0);
+               fConst16 = std::floor(((0.0292910002f * fConst0) + 0.5f));
+               iConst17 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst14 - fConst16))));
+               iConst18 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst16 + -1.0f))));
+               fConst19 = std::floor(((0.210389003f * fConst0) + 0.5f));
+               fConst20 = ((0.0f - (6.90775537f * fConst19)) / fConst0);
+               fConst21 = std::floor(((0.0244210009f * fConst0) + 0.5f));
+               iConst22 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst19 - fConst21))));
+               iConst23 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst21 + -1.0f))));
+               fConst24 = std::floor(((0.125f * fConst0) + 0.5f));
+               fConst25 = ((0.0f - (6.90775537f * fConst24)) / fConst0);
+               fConst26 = std::floor(((0.0134579996f * fConst0) + 0.5f));
+               iConst27 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst24 - fConst26))));
+               iConst28 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst26 + -1.0f))));
+               fConst29 = std::floor(((0.127837002f * fConst0) + 0.5f));
+               fConst30 = ((0.0f - (6.90775537f * fConst29)) / fConst0);
+               fConst31 = std::floor(((0.0316039994f * fConst0) + 0.5f));
+               iConst32 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst29 - fConst31))));
+               iConst33 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst31 + -1.0f))));
+               fConst34 = std::floor(((0.174713001f * fConst0) + 0.5f));
+               fConst35 = ((0.0f - (6.90775537f * fConst34)) / fConst0);
+               fConst36 = std::floor(((0.0229039993f * fConst0) + 0.5f));
+               iConst37 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst34 - fConst36))));
+               iConst38 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst36 + -1.0f))));
+               fConst39 = std::floor(((0.153128996f * fConst0) + 0.5f));
+               fConst40 = ((0.0f - (6.90775537f * fConst39)) / fConst0);
+               fConst41 = std::floor(((0.0203460008f * fConst0) + 0.5f));
+               iConst42 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst39 - fConst41))));
+               iConst43 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst41 + -1.0f))));
+       }
+       
+       virtual void instanceResetUserInterface() {
+               fVslider0 = FAUSTFLOAT(-3.0f);
+               fVslider1 = FAUSTFLOAT(0.0f);
+               fVslider2 = FAUSTFLOAT(1500.0f);
+               fVslider3 = FAUSTFLOAT(0.0f);
+               fVslider4 = FAUSTFLOAT(315.0f);
+               fVslider5 = FAUSTFLOAT(0.0f);
+               fVslider6 = FAUSTFLOAT(2.0f);
+               fVslider7 = FAUSTFLOAT(6000.0f);
+               fVslider8 = FAUSTFLOAT(3.0f);
+               fVslider9 = FAUSTFLOAT(200.0f);
+               fVslider10 = FAUSTFLOAT(60.0f);
+       }
+       
+       virtual void instanceClear() {
+               IOTA = 0;
+               for (int l0 = 0; (l0 < 16384); l0 = (l0 + 1)) {
+                       fVec0[l0] = 0.0f;
+               }
+               for (int l1 = 0; (l1 < 16384); l1 = (l1 + 1)) {
+                       fVec1[l1] = 0.0f;
+               }
+               for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) {
+                       fRec0[l2] = 0.0f;
+               }
+               for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) {
+                       fRec1[l3] = 0.0f;
+               }
+               for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) {
+                       fRec15[l4] = 0.0f;
+               }
+               for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) {
+                       fRec14[l5] = 0.0f;
+               }
+               for (int l6 = 0; (l6 < 32768); l6 = (l6 + 1)) {
+                       fVec2[l6] = 0.0f;
+               }
+               for (int l7 = 0; (l7 < 2048); l7 = (l7 + 1)) {
+                       fVec3[l7] = 0.0f;
+               }
+               for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) {
+                       fRec12[l8] = 0.0f;
+               }
+               for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) {
+                       fRec19[l9] = 0.0f;
+               }
+               for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) {
+                       fRec18[l10] = 0.0f;
+               }
+               for (int l11 = 0; (l11 < 32768); l11 = (l11 + 1)) {
+                       fVec4[l11] = 0.0f;
+               }
+               for (int l12 = 0; (l12 < 4096); l12 = (l12 + 1)) {
+                       fVec5[l12] = 0.0f;
+               }
+               for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) {
+                       fRec16[l13] = 0.0f;
+               }
+               for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) {
+                       fRec23[l14] = 0.0f;
+               }
+               for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) {
+                       fRec22[l15] = 0.0f;
+               }
+               for (int l16 = 0; (l16 < 16384); l16 = (l16 + 1)) {
+                       fVec6[l16] = 0.0f;
+               }
+               for (int l17 = 0; (l17 < 4096); l17 = (l17 + 1)) {
+                       fVec7[l17] = 0.0f;
+               }
+               for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) {
+                       fRec20[l18] = 0.0f;
+               }
+               for (int l19 = 0; (l19 < 2); l19 = (l19 + 1)) {
+                       fRec27[l19] = 0.0f;
+               }
+               for (int l20 = 0; (l20 < 2); l20 = (l20 + 1)) {
+                       fRec26[l20] = 0.0f;
+               }
+               for (int l21 = 0; (l21 < 32768); l21 = (l21 + 1)) {
+                       fVec8[l21] = 0.0f;
+               }
+               for (int l22 = 0; (l22 < 4096); l22 = (l22 + 1)) {
+                       fVec9[l22] = 0.0f;
+               }
+               for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) {
+                       fRec24[l23] = 0.0f;
+               }
+               for (int l24 = 0; (l24 < 2); l24 = (l24 + 1)) {
+                       fRec31[l24] = 0.0f;
+               }
+               for (int l25 = 0; (l25 < 2); l25 = (l25 + 1)) {
+                       fRec30[l25] = 0.0f;
+               }
+               for (int l26 = 0; (l26 < 16384); l26 = (l26 + 1)) {
+                       fVec10[l26] = 0.0f;
+               }
+               for (int l27 = 0; (l27 < 2048); l27 = (l27 + 1)) {
+                       fVec11[l27] = 0.0f;
+               }
+               for (int l28 = 0; (l28 < 2); l28 = (l28 + 1)) {
+                       fRec28[l28] = 0.0f;
+               }
+               for (int l29 = 0; (l29 < 2); l29 = (l29 + 1)) {
+                       fRec35[l29] = 0.0f;
+               }
+               for (int l30 = 0; (l30 < 2); l30 = (l30 + 1)) {
+                       fRec34[l30] = 0.0f;
+               }
+               for (int l31 = 0; (l31 < 16384); l31 = (l31 + 1)) {
+                       fVec12[l31] = 0.0f;
+               }
+               for (int l32 = 0; (l32 < 4096); l32 = (l32 + 1)) {
+                       fVec13[l32] = 0.0f;
+               }
+               for (int l33 = 0; (l33 < 2); l33 = (l33 + 1)) {
+                       fRec32[l33] = 0.0f;
+               }
+               for (int l34 = 0; (l34 < 2); l34 = (l34 + 1)) {
+                       fRec39[l34] = 0.0f;
+               }
+               for (int l35 = 0; (l35 < 2); l35 = (l35 + 1)) {
+                       fRec38[l35] = 0.0f;
+               }
+               for (int l36 = 0; (l36 < 16384); l36 = (l36 + 1)) {
+                       fVec14[l36] = 0.0f;
+               }
+               for (int l37 = 0; (l37 < 4096); l37 = (l37 + 1)) {
+                       fVec15[l37] = 0.0f;
+               }
+               for (int l38 = 0; (l38 < 2); l38 = (l38 + 1)) {
+                       fRec36[l38] = 0.0f;
+               }
+               for (int l39 = 0; (l39 < 2); l39 = (l39 + 1)) {
+                       fRec43[l39] = 0.0f;
+               }
+               for (int l40 = 0; (l40 < 2); l40 = (l40 + 1)) {
+                       fRec42[l40] = 0.0f;
+               }
+               for (int l41 = 0; (l41 < 16384); l41 = (l41 + 1)) {
+                       fVec16[l41] = 0.0f;
+               }
+               for (int l42 = 0; (l42 < 2048); l42 = (l42 + 1)) {
+                       fVec17[l42] = 0.0f;
+               }
+               for (int l43 = 0; (l43 < 2); l43 = (l43 + 1)) {
+                       fRec40[l43] = 0.0f;
+               }
+               for (int l44 = 0; (l44 < 3); l44 = (l44 + 1)) {
+                       fRec4[l44] = 0.0f;
+               }
+               for (int l45 = 0; (l45 < 3); l45 = (l45 + 1)) {
+                       fRec5[l45] = 0.0f;
+               }
+               for (int l46 = 0; (l46 < 3); l46 = (l46 + 1)) {
+                       fRec6[l46] = 0.0f;
+               }
+               for (int l47 = 0; (l47 < 3); l47 = (l47 + 1)) {
+                       fRec7[l47] = 0.0f;
+               }
+               for (int l48 = 0; (l48 < 3); l48 = (l48 + 1)) {
+                       fRec8[l48] = 0.0f;
+               }
+               for (int l49 = 0; (l49 < 3); l49 = (l49 + 1)) {
+                       fRec9[l49] = 0.0f;
+               }
+               for (int l50 = 0; (l50 < 3); l50 = (l50 + 1)) {
+                       fRec10[l50] = 0.0f;
+               }
+               for (int l51 = 0; (l51 < 3); l51 = (l51 + 1)) {
+                       fRec11[l51] = 0.0f;
+               }
+               for (int l52 = 0; (l52 < 3); l52 = (l52 + 1)) {
+                       fRec3[l52] = 0.0f;
+               }
+               for (int l53 = 0; (l53 < 3); l53 = (l53 + 1)) {
+                       fRec2[l53] = 0.0f;
+               }
+               for (int l54 = 0; (l54 < 3); l54 = (l54 + 1)) {
+                       fRec45[l54] = 0.0f;
+               }
+               for (int l55 = 0; (l55 < 3); l55 = (l55 + 1)) {
+                       fRec44[l55] = 0.0f;
+               }
+       }
+       
+       virtual void init(int sample_rate) {
+               classInit(sample_rate);
+               instanceInit(sample_rate);
+       }
+       virtual void instanceInit(int sample_rate) {
+               instanceConstants(sample_rate);
+               instanceResetUserInterface();
+               instanceClear();
+       }
+       
+       virtual zitarevdsp* clone() {
+               return new zitarevdsp();
+       }
+       
+       virtual int getSampleRate() {
+               return fSampleRate;
+       }
+       
+       virtual void buildUserInterface(UI* ui_interface) {
+               ui_interface->declare(0, "0", "");
+               ui_interface->declare(0, "tooltip", "~ ZITA REV1 FEEDBACK DELAY NETWORK (FDN) & SCHROEDER  ALLPASS-COMB REVERBERATOR (8x8). See Faust's reverbs.lib for documentation and  references");
+               ui_interface->openHorizontalBox("Zita_Rev1");
+               ui_interface->declare(0, "1", "");
+               ui_interface->openHorizontalBox("Input");
+               ui_interface->declare(&fVslider10, "1", "");
+               ui_interface->declare(&fVslider10, "style", "knob");
+               ui_interface->declare(&fVslider10, "tooltip", "Delay in ms   before reverberation begins");
+               ui_interface->declare(&fVslider10, "unit", "ms");
+               ui_interface->addVerticalSlider("In Delay", &fVslider10, 60.0f, 20.0f, 100.0f, 1.0f);
+               ui_interface->closeBox();
+               ui_interface->declare(0, "2", "");
+               ui_interface->openHorizontalBox("Decay Times in Bands (see tooltips)");
+               ui_interface->declare(&fVslider9, "1", "");
+               ui_interface->declare(&fVslider9, "scale", "log");
+               ui_interface->declare(&fVslider9, "style", "knob");
+               ui_interface->declare(&fVslider9, "tooltip", "Crossover frequency (Hz) separating low and middle frequencies");
+               ui_interface->declare(&fVslider9, "unit", "Hz");
+               ui_interface->addVerticalSlider("LF X", &fVslider9, 200.0f, 50.0f, 1000.0f, 1.0f);
+               ui_interface->declare(&fVslider8, "2", "");
+               ui_interface->declare(&fVslider8, "scale", "log");
+               ui_interface->declare(&fVslider8, "style", "knob");
+               ui_interface->declare(&fVslider8, "tooltip", "T60 = time (in seconds) to decay 60dB in low-frequency band");
+               ui_interface->declare(&fVslider8, "unit", "s");
+               ui_interface->addVerticalSlider("Low RT60", &fVslider8, 3.0f, 1.0f, 8.0f, 0.100000001f);
+               ui_interface->declare(&fVslider6, "3", "");
+               ui_interface->declare(&fVslider6, "scale", "log");
+               ui_interface->declare(&fVslider6, "style", "knob");
+               ui_interface->declare(&fVslider6, "tooltip", "T60 = time (in seconds) to decay 60dB in middle band");
+               ui_interface->declare(&fVslider6, "unit", "s");
+               ui_interface->addVerticalSlider("Mid RT60", &fVslider6, 2.0f, 1.0f, 8.0f, 0.100000001f);
+               ui_interface->declare(&fVslider7, "4", "");
+               ui_interface->declare(&fVslider7, "scale", "log");
+               ui_interface->declare(&fVslider7, "style", "knob");
+               ui_interface->declare(&fVslider7, "tooltip", "Frequency (Hz) at which the high-frequency T60 is half the middle-band's T60");
+               ui_interface->declare(&fVslider7, "unit", "Hz");
+               ui_interface->addVerticalSlider("HF Damping", &fVslider7, 6000.0f, 1500.0f, 23520.0f, 1.0f);
+               ui_interface->closeBox();
+               ui_interface->declare(0, "3", "");
+               ui_interface->openHorizontalBox("RM Peaking Equalizer 1");
+               ui_interface->declare(&fVslider4, "1", "");
+               ui_interface->declare(&fVslider4, "scale", "log");
+               ui_interface->declare(&fVslider4, "style", "knob");
+               ui_interface->declare(&fVslider4, "tooltip", "Center-frequency of second-order Regalia-Mitra peaking equalizer section 1");
+               ui_interface->declare(&fVslider4, "unit", "Hz");
+               ui_interface->addVerticalSlider("Eq1 Freq", &fVslider4, 315.0f, 40.0f, 2500.0f, 1.0f);
+               ui_interface->declare(&fVslider5, "2", "");
+               ui_interface->declare(&fVslider5, "style", "knob");
+               ui_interface->declare(&fVslider5, "tooltip", "Peak level   in dB of second-order Regalia-Mitra peaking equalizer section 1");
+               ui_interface->declare(&fVslider5, "unit", "dB");
+               ui_interface->addVerticalSlider("Eq1 Level", &fVslider5, 0.0f, -15.0f, 15.0f, 0.100000001f);
+               ui_interface->closeBox();
+               ui_interface->declare(0, "4", "");
+               ui_interface->openHorizontalBox("RM Peaking Equalizer 2");
+               ui_interface->declare(&fVslider2, "1", "");
+               ui_interface->declare(&fVslider2, "scale", "log");
+               ui_interface->declare(&fVslider2, "style", "knob");
+               ui_interface->declare(&fVslider2, "tooltip", "Center-frequency of second-order Regalia-Mitra peaking equalizer section 2");
+               ui_interface->declare(&fVslider2, "unit", "Hz");
+               ui_interface->addVerticalSlider("Eq2 Freq", &fVslider2, 1500.0f, 160.0f, 10000.0f, 1.0f);
+               ui_interface->declare(&fVslider3, "2", "");
+               ui_interface->declare(&fVslider3, "style", "knob");
+               ui_interface->declare(&fVslider3, "tooltip", "Peak level   in dB of second-order Regalia-Mitra peaking equalizer section 2");
+               ui_interface->declare(&fVslider3, "unit", "dB");
+               ui_interface->addVerticalSlider("Eq2 Level", &fVslider3, 0.0f, -15.0f, 15.0f, 0.100000001f);
+               ui_interface->closeBox();
+               ui_interface->declare(0, "5", "");
+               ui_interface->openHorizontalBox("Output");
+               ui_interface->declare(&fVslider1, "1", "");
+               ui_interface->declare(&fVslider1, "style", "knob");
+               ui_interface->declare(&fVslider1, "tooltip", "Dry/Wet Mix: 0 = dry, 1 = wet");
+               ui_interface->addVerticalSlider("Wet", &fVslider1, 0.0f, 0.0f, 1.0f, 0.00999999978f);
+               ui_interface->declare(&fVslider0, "2", "");
+               ui_interface->declare(&fVslider0, "style", "knob");
+               ui_interface->declare(&fVslider0, "tooltip", "Output scale   factor");
+               ui_interface->declare(&fVslider0, "unit", "dB");
+               ui_interface->addVerticalSlider("Level", &fVslider0, -3.0f, -70.0f, 20.0f, 0.100000001f);
+               ui_interface->closeBox();
+               ui_interface->closeBox();
+       }
+       
+       virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+               FAUSTFLOAT* input0 = inputs[0];
+               FAUSTFLOAT* input1 = inputs[1];
+               FAUSTFLOAT* output0 = outputs[0];
+               FAUSTFLOAT* output1 = outputs[1];
+               float fSlow0 = (0.00100000005f * std::pow(10.0f, (0.0500000007f * float(fVslider0))));
+               float fSlow1 = (0.00100000005f * float(fVslider1));
+               float fSlow2 = float(fVslider2);
+               float fSlow3 = std::pow(10.0f, (0.0500000007f * float(fVslider3)));
+               float fSlow4 = (fConst1 * (fSlow2 / std::sqrt(std::max<float>(0.0f, fSlow3))));
+               float fSlow5 = ((1.0f - fSlow4) / (fSlow4 + 1.0f));
+               float fSlow6 = float(fVslider4);
+               float fSlow7 = std::pow(10.0f, (0.0500000007f * float(fVslider5)));
+               float fSlow8 = (fConst1 * (fSlow6 / std::sqrt(std::max<float>(0.0f, fSlow7))));
+               float fSlow9 = ((1.0f - fSlow8) / (fSlow8 + 1.0f));
+               float fSlow10 = float(fVslider6);
+               float fSlow11 = std::exp((fConst3 / fSlow10));
+               float fSlow12 = zitarevdsp_faustpower2_f(fSlow11);
+               float fSlow13 = std::cos((fConst1 * float(fVslider7)));
+               float fSlow14 = (1.0f - (fSlow12 * fSlow13));
+               float fSlow15 = (1.0f - fSlow12);
+               float fSlow16 = (fSlow14 / fSlow15);
+               float fSlow17 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow14) / zitarevdsp_faustpower2_f(fSlow15)) + -1.0f)));
+               float fSlow18 = (fSlow16 - fSlow17);
+               float fSlow19 = (fSlow11 * (fSlow17 + (1.0f - fSlow16)));
+               float fSlow20 = float(fVslider8);
+               float fSlow21 = ((std::exp((fConst3 / fSlow20)) / fSlow11) + -1.0f);
+               float fSlow22 = (1.0f / std::tan((fConst4 * float(fVslider9))));
+               float fSlow23 = (1.0f / (fSlow22 + 1.0f));
+               float fSlow24 = (1.0f - fSlow22);
+               int iSlow25 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst7 * float(fVslider10)))));
+               float fSlow26 = std::exp((fConst10 / fSlow10));
+               float fSlow27 = zitarevdsp_faustpower2_f(fSlow26);
+               float fSlow28 = (1.0f - (fSlow27 * fSlow13));
+               float fSlow29 = (1.0f - fSlow27);
+               float fSlow30 = (fSlow28 / fSlow29);
+               float fSlow31 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow28) / zitarevdsp_faustpower2_f(fSlow29)) + -1.0f)));
+               float fSlow32 = (fSlow30 - fSlow31);
+               float fSlow33 = (fSlow26 * (fSlow31 + (1.0f - fSlow30)));
+               float fSlow34 = ((std::exp((fConst10 / fSlow20)) / fSlow26) + -1.0f);
+               float fSlow35 = std::exp((fConst15 / fSlow10));
+               float fSlow36 = zitarevdsp_faustpower2_f(fSlow35);
+               float fSlow37 = (1.0f - (fSlow36 * fSlow13));
+               float fSlow38 = (1.0f - fSlow36);
+               float fSlow39 = (fSlow37 / fSlow38);
+               float fSlow40 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow37) / zitarevdsp_faustpower2_f(fSlow38)) + -1.0f)));
+               float fSlow41 = (fSlow39 - fSlow40);
+               float fSlow42 = (fSlow35 * (fSlow40 + (1.0f - fSlow39)));
+               float fSlow43 = ((std::exp((fConst15 / fSlow20)) / fSlow35) + -1.0f);
+               float fSlow44 = std::exp((fConst20 / fSlow10));
+               float fSlow45 = zitarevdsp_faustpower2_f(fSlow44);
+               float fSlow46 = (1.0f - (fSlow45 * fSlow13));
+               float fSlow47 = (1.0f - fSlow45);
+               float fSlow48 = (fSlow46 / fSlow47);
+               float fSlow49 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow46) / zitarevdsp_faustpower2_f(fSlow47)) + -1.0f)));
+               float fSlow50 = (fSlow48 - fSlow49);
+               float fSlow51 = (fSlow44 * (fSlow49 + (1.0f - fSlow48)));
+               float fSlow52 = ((std::exp((fConst20 / fSlow20)) / fSlow44) + -1.0f);
+               float fSlow53 = std::exp((fConst25 / fSlow10));
+               float fSlow54 = zitarevdsp_faustpower2_f(fSlow53);
+               float fSlow55 = (1.0f - (fSlow54 * fSlow13));
+               float fSlow56 = (1.0f - fSlow54);
+               float fSlow57 = (fSlow55 / fSlow56);
+               float fSlow58 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow55) / zitarevdsp_faustpower2_f(fSlow56)) + -1.0f)));
+               float fSlow59 = (fSlow57 - fSlow58);
+               float fSlow60 = (fSlow53 * (fSlow58 + (1.0f - fSlow57)));
+               float fSlow61 = ((std::exp((fConst25 / fSlow20)) / fSlow53) + -1.0f);
+               float fSlow62 = std::exp((fConst30 / fSlow10));
+               float fSlow63 = zitarevdsp_faustpower2_f(fSlow62);
+               float fSlow64 = (1.0f - (fSlow63 * fSlow13));
+               float fSlow65 = (1.0f - fSlow63);
+               float fSlow66 = (fSlow64 / fSlow65);
+               float fSlow67 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow64) / zitarevdsp_faustpower2_f(fSlow65)) + -1.0f)));
+               float fSlow68 = (fSlow66 - fSlow67);
+               float fSlow69 = (fSlow62 * (fSlow67 + (1.0f - fSlow66)));
+               float fSlow70 = ((std::exp((fConst30 / fSlow20)) / fSlow62) + -1.0f);
+               float fSlow71 = std::exp((fConst35 / fSlow10));
+               float fSlow72 = zitarevdsp_faustpower2_f(fSlow71);
+               float fSlow73 = (1.0f - (fSlow72 * fSlow13));
+               float fSlow74 = (1.0f - fSlow72);
+               float fSlow75 = (fSlow73 / fSlow74);
+               float fSlow76 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow73) / zitarevdsp_faustpower2_f(fSlow74)) + -1.0f)));
+               float fSlow77 = (fSlow75 - fSlow76);
+               float fSlow78 = (fSlow71 * (fSlow76 + (1.0f - fSlow75)));
+               float fSlow79 = ((std::exp((fConst35 / fSlow20)) / fSlow71) + -1.0f);
+               float fSlow80 = std::exp((fConst40 / fSlow10));
+               float fSlow81 = zitarevdsp_faustpower2_f(fSlow80);
+               float fSlow82 = (1.0f - (fSlow81 * fSlow13));
+               float fSlow83 = (1.0f - fSlow81);
+               float fSlow84 = (fSlow82 / fSlow83);
+               float fSlow85 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow82) / zitarevdsp_faustpower2_f(fSlow83)) + -1.0f)));
+               float fSlow86 = (fSlow84 - fSlow85);
+               float fSlow87 = (fSlow80 * (fSlow85 + (1.0f - fSlow84)));
+               float fSlow88 = ((std::exp((fConst40 / fSlow20)) / fSlow80) + -1.0f);
+               float fSlow89 = (0.0f - (std::cos((fConst1 * fSlow6)) * (fSlow9 + 1.0f)));
+               float fSlow90 = (0.0f - (std::cos((fConst1 * fSlow2)) * (fSlow5 + 1.0f)));
+               for (int i = 0; (i < count); i = (i + 1)) {
+                       float fTemp0 = float(input0[i]);
+                       fVec0[(IOTA & 16383)] = fTemp0;
+                       float fTemp1 = float(input1[i]);
+                       fVec1[(IOTA & 16383)] = fTemp1;
+                       fRec0[0] = (fSlow0 + (0.999000013f * fRec0[1]));
+                       fRec1[0] = (fSlow1 + (0.999000013f * fRec1[1]));
+                       fRec15[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec15[1]) - (fRec11[1] + fRec11[2]))));
+                       fRec14[0] = ((fSlow18 * fRec14[1]) + (fSlow19 * (fRec11[1] + (fSlow21 * fRec15[0]))));
+                       fVec2[(IOTA & 32767)] = ((0.353553385f * fRec14[0]) + 9.99999968e-21f);
+                       float fTemp2 = (0.300000012f * fVec1[((IOTA - iSlow25) & 16383)]);
+                       float fTemp3 = (((0.600000024f * fRec12[1]) + fVec2[((IOTA - iConst6) & 32767)]) - fTemp2);
+                       fVec3[(IOTA & 2047)] = fTemp3;
+                       fRec12[0] = fVec3[((IOTA - iConst8) & 2047)];
+                       float fRec13 = (0.0f - (0.600000024f * fTemp3));
+                       fRec19[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec19[1]) - (fRec7[1] + fRec7[2]))));
+                       fRec18[0] = ((fSlow32 * fRec18[1]) + (fSlow33 * (fRec7[1] + (fSlow34 * fRec19[0]))));
+                       fVec4[(IOTA & 32767)] = ((0.353553385f * fRec18[0]) + 9.99999968e-21f);
+                       float fTemp4 = (((0.600000024f * fRec16[1]) + fVec4[((IOTA - iConst12) & 32767)]) - fTemp2);
+                       fVec5[(IOTA & 4095)] = fTemp4;
+                       fRec16[0] = fVec5[((IOTA - iConst13) & 4095)];
+                       float fRec17 = (0.0f - (0.600000024f * fTemp4));
+                       fRec23[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec23[1]) - (fRec9[1] + fRec9[2]))));
+                       fRec22[0] = ((fSlow41 * fRec22[1]) + (fSlow42 * (fRec9[1] + (fSlow43 * fRec23[0]))));
+                       fVec6[(IOTA & 16383)] = ((0.353553385f * fRec22[0]) + 9.99999968e-21f);
+                       float fTemp5 = (fVec6[((IOTA - iConst17) & 16383)] + (fTemp2 + (0.600000024f * fRec20[1])));
+                       fVec7[(IOTA & 4095)] = fTemp5;
+                       fRec20[0] = fVec7[((IOTA - iConst18) & 4095)];
+                       float fRec21 = (0.0f - (0.600000024f * fTemp5));
+                       fRec27[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec27[1]) - (fRec5[1] + fRec5[2]))));
+                       fRec26[0] = ((fSlow50 * fRec26[1]) + (fSlow51 * (fRec5[1] + (fSlow52 * fRec27[0]))));
+                       fVec8[(IOTA & 32767)] = ((0.353553385f * fRec26[0]) + 9.99999968e-21f);
+                       float fTemp6 = (fTemp2 + ((0.600000024f * fRec24[1]) + fVec8[((IOTA - iConst22) & 32767)]));
+                       fVec9[(IOTA & 4095)] = fTemp6;
+                       fRec24[0] = fVec9[((IOTA - iConst23) & 4095)];
+                       float fRec25 = (0.0f - (0.600000024f * fTemp6));
+                       fRec31[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec31[1]) - (fRec10[1] + fRec10[2]))));
+                       fRec30[0] = ((fSlow59 * fRec30[1]) + (fSlow60 * (fRec10[1] + (fSlow61 * fRec31[0]))));
+                       fVec10[(IOTA & 16383)] = ((0.353553385f * fRec30[0]) + 9.99999968e-21f);
+                       float fTemp7 = (0.300000012f * fVec0[((IOTA - iSlow25) & 16383)]);
+                       float fTemp8 = (fVec10[((IOTA - iConst27) & 16383)] - (fTemp7 + (0.600000024f * fRec28[1])));
+                       fVec11[(IOTA & 2047)] = fTemp8;
+                       fRec28[0] = fVec11[((IOTA - iConst28) & 2047)];
+                       float fRec29 = (0.600000024f * fTemp8);
+                       fRec35[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec35[1]) - (fRec6[1] + fRec6[2]))));
+                       fRec34[0] = ((fSlow68 * fRec34[1]) + (fSlow69 * (fRec6[1] + (fSlow70 * fRec35[0]))));
+                       fVec12[(IOTA & 16383)] = ((0.353553385f * fRec34[0]) + 9.99999968e-21f);
+                       float fTemp9 = (fVec12[((IOTA - iConst32) & 16383)] - (fTemp7 + (0.600000024f * fRec32[1])));
+                       fVec13[(IOTA & 4095)] = fTemp9;
+                       fRec32[0] = fVec13[((IOTA - iConst33) & 4095)];
+                       float fRec33 = (0.600000024f * fTemp9);
+                       fRec39[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec39[1]) - (fRec8[1] + fRec8[2]))));
+                       fRec38[0] = ((fSlow77 * fRec38[1]) + (fSlow78 * (fRec8[1] + (fSlow79 * fRec39[0]))));
+                       fVec14[(IOTA & 16383)] = ((0.353553385f * fRec38[0]) + 9.99999968e-21f);
+                       float fTemp10 = ((fTemp7 + fVec14[((IOTA - iConst37) & 16383)]) - (0.600000024f * fRec36[1]));
+                       fVec15[(IOTA & 4095)] = fTemp10;
+                       fRec36[0] = fVec15[((IOTA - iConst38) & 4095)];
+                       float fRec37 = (0.600000024f * fTemp10);
+                       fRec43[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec43[1]) - (fRec4[1] + fRec4[2]))));
+                       fRec42[0] = ((fSlow86 * fRec42[1]) + (fSlow87 * (fRec4[1] + (fSlow88 * fRec43[0]))));
+                       fVec16[(IOTA & 16383)] = ((0.353553385f * fRec42[0]) + 9.99999968e-21f);
+                       float fTemp11 = ((fVec16[((IOTA - iConst42) & 16383)] + fTemp7) - (0.600000024f * fRec40[1]));
+                       fVec17[(IOTA & 2047)] = fTemp11;
+                       fRec40[0] = fVec17[((IOTA - iConst43) & 2047)];
+                       float fRec41 = (0.600000024f * fTemp11);
+                       float fTemp12 = (fRec41 + fRec37);
+                       float fTemp13 = (fRec29 + (fRec33 + fTemp12));
+                       fRec4[0] = (fRec12[1] + (fRec16[1] + (fRec20[1] + (fRec24[1] + (fRec28[1] + (fRec32[1] + (fRec36[1] + (fRec40[1] + (fRec13 + (fRec17 + (fRec21 + (fRec25 + fTemp13))))))))))));
+                       fRec5[0] = ((fRec28[1] + (fRec32[1] + (fRec36[1] + (fRec40[1] + fTemp13)))) - (fRec12[1] + (fRec16[1] + (fRec20[1] + (fRec24[1] + (fRec13 + (fRec17 + (fRec25 + fRec21))))))));
+                       float fTemp14 = (fRec33 + fRec29);
+                       fRec6[0] = ((fRec20[1] + (fRec24[1] + (fRec36[1] + (fRec40[1] + (fRec21 + (fRec25 + fTemp12)))))) - (fRec12[1] + (fRec16[1] + (fRec28[1] + (fRec32[1] + (fRec13 + (fRec17 + fTemp14)))))));
+                       fRec7[0] = ((fRec12[1] + (fRec16[1] + (fRec36[1] + (fRec40[1] + (fRec13 + (fRec17 + fTemp12)))))) - (fRec20[1] + (fRec24[1] + (fRec28[1] + (fRec32[1] + (fRec21 + (fRec25 + fTemp14)))))));
+                       float fTemp15 = (fRec41 + fRec33);
+                       float fTemp16 = (fRec37 + fRec29);
+                       fRec8[0] = ((fRec16[1] + (fRec24[1] + (fRec32[1] + (fRec40[1] + (fRec17 + (fRec25 + fTemp15)))))) - (fRec12[1] + (fRec20[1] + (fRec28[1] + (fRec36[1] + (fRec13 + (fRec21 + fTemp16)))))));
+                       fRec9[0] = ((fRec12[1] + (fRec20[1] + (fRec32[1] + (fRec40[1] + (fRec13 + (fRec21 + fTemp15)))))) - (fRec16[1] + (fRec24[1] + (fRec28[1] + (fRec36[1] + (fRec17 + (fRec25 + fTemp16)))))));
+                       float fTemp17 = (fRec41 + fRec29);
+                       float fTemp18 = (fRec37 + fRec33);
+                       fRec10[0] = ((fRec12[1] + (fRec24[1] + (fRec28[1] + (fRec40[1] + (fRec13 + (fRec25 + fTemp17)))))) - (fRec16[1] + (fRec20[1] + (fRec32[1] + (fRec36[1] + (fRec17 + (fRec21 + fTemp18)))))));
+                       fRec11[0] = ((fRec16[1] + (fRec20[1] + (fRec28[1] + (fRec40[1] + (fRec17 + (fRec21 + fTemp17)))))) - (fRec12[1] + (fRec24[1] + (fRec32[1] + (fRec36[1] + (fRec13 + (fRec25 + fTemp18)))))));
+                       float fTemp19 = (0.370000005f * (fRec5[0] + fRec6[0]));
+                       float fTemp20 = (fSlow89 * fRec3[1]);
+                       fRec3[0] = (fTemp19 - (fTemp20 + (fSlow9 * fRec3[2])));
+                       float fTemp21 = (fSlow9 * fRec3[0]);
+                       float fTemp22 = (0.5f * ((fTemp21 + (fRec3[2] + (fTemp19 + fTemp20))) + (fSlow7 * ((fTemp21 + (fTemp20 + fRec3[2])) - fTemp19))));
+                       float fTemp23 = (fSlow90 * fRec2[1]);
+                       fRec2[0] = (fTemp22 - (fTemp23 + (fSlow5 * fRec2[2])));
+                       float fTemp24 = (fSlow5 * fRec2[0]);
+                       float fTemp25 = (1.0f - fRec1[0]);
+                       output0[i] = FAUSTFLOAT((fRec0[0] * ((0.5f * (fRec1[0] * ((fTemp24 + (fRec2[2] + (fTemp22 + fTemp23))) + (fSlow3 * ((fTemp24 + (fTemp23 + fRec2[2])) - fTemp22))))) + (fTemp0 * fTemp25))));
+                       float fTemp26 = (0.370000005f * (fRec5[0] - fRec6[0]));
+                       float fTemp27 = (fSlow89 * fRec45[1]);
+                       fRec45[0] = (fTemp26 - (fTemp27 + (fSlow9 * fRec45[2])));
+                       float fTemp28 = (fSlow9 * fRec45[0]);
+                       float fTemp29 = (0.5f * ((fTemp28 + (fRec45[2] + (fTemp26 + fTemp27))) + (fSlow7 * ((fTemp28 + (fTemp27 + fRec45[2])) - fTemp26))));
+                       float fTemp30 = (fSlow90 * fRec44[1]);
+                       fRec44[0] = (fTemp29 - (fTemp30 + (fSlow5 * fRec44[2])));
+                       float fTemp31 = (fSlow5 * fRec44[0]);
+                       output1[i] = FAUSTFLOAT((fRec0[0] * ((0.5f * (fRec1[0] * ((fTemp31 + (fRec44[2] + (fTemp29 + fTemp30))) + (fSlow3 * ((fTemp31 + (fTemp30 + fRec44[2])) - fTemp29))))) + (fTemp1 * fTemp25))));
+                       IOTA = (IOTA + 1);
+                       fRec0[1] = fRec0[0];
+                       fRec1[1] = fRec1[0];
+                       fRec15[1] = fRec15[0];
+                       fRec14[1] = fRec14[0];
+                       fRec12[1] = fRec12[0];
+                       fRec19[1] = fRec19[0];
+                       fRec18[1] = fRec18[0];
+                       fRec16[1] = fRec16[0];
+                       fRec23[1] = fRec23[0];
+                       fRec22[1] = fRec22[0];
+                       fRec20[1] = fRec20[0];
+                       fRec27[1] = fRec27[0];
+                       fRec26[1] = fRec26[0];
+                       fRec24[1] = fRec24[0];
+                       fRec31[1] = fRec31[0];
+                       fRec30[1] = fRec30[0];
+                       fRec28[1] = fRec28[0];
+                       fRec35[1] = fRec35[0];
+                       fRec34[1] = fRec34[0];
+                       fRec32[1] = fRec32[0];
+                       fRec39[1] = fRec39[0];
+                       fRec38[1] = fRec38[0];
+                       fRec36[1] = fRec36[0];
+                       fRec43[1] = fRec43[0];
+                       fRec42[1] = fRec42[0];
+                       fRec40[1] = fRec40[0];
+                       fRec4[2] = fRec4[1];
+                       fRec4[1] = fRec4[0];
+                       fRec5[2] = fRec5[1];
+                       fRec5[1] = fRec5[0];
+                       fRec6[2] = fRec6[1];
+                       fRec6[1] = fRec6[0];
+                       fRec7[2] = fRec7[1];
+                       fRec7[1] = fRec7[0];
+                       fRec8[2] = fRec8[1];
+                       fRec8[1] = fRec8[0];
+                       fRec9[2] = fRec9[1];
+                       fRec9[1] = fRec9[0];
+                       fRec10[2] = fRec10[1];
+                       fRec10[1] = fRec10[0];
+                       fRec11[2] = fRec11[1];
+                       fRec11[1] = fRec11[0];
+                       fRec3[2] = fRec3[1];
+                       fRec3[1] = fRec3[0];
+                       fRec2[2] = fRec2[1];
+                       fRec2[1] = fRec2[0];
+                       fRec45[2] = fRec45[1];
+                       fRec45[1] = fRec45[0];
+                       fRec44[2] = fRec44[1];
+                       fRec44[1] = fRec44[0];
+               }
+       }
+
+};
+
+#endif
diff --git a/src/zitarevmonodsp.h b/src/zitarevmonodsp.h
new file mode 100644 (file)
index 0000000..190453a
--- /dev/null
@@ -0,0 +1,2357 @@
+/* ------------------------------------------------------------
+name: "zitarevmonodsp"
+Code generated with Faust 2.28.6 (https://faust.grame.fr)
+Compilation options: -lang cpp -inpl -scal -ftz 0
+------------------------------------------------------------ */
+
+#ifndef  __zitarevmonodsp_H__
+#define  __zitarevmonodsp_H__
+
+// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME
+//
+// faust2header.cpp - FAUST Architecture File
+// This is a simple variation of matlabplot.cpp in the Faust distribution
+// aimed at creating a simple C++ header file (.h) containing a Faust DSP.
+// See the Makefile for how to use it.
+
+/************************** BEGIN dsp.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __dsp__
+#define __dsp__
+
+#include <string>
+#include <vector>
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+struct UI;
+struct Meta;
+
+/**
+ * DSP memory manager.
+ */
+
+struct dsp_memory_manager {
+    
+    virtual ~dsp_memory_manager() {}
+    
+    virtual void* allocate(size_t size) = 0;
+    virtual void destroy(void* ptr) = 0;
+    
+};
+
+/**
+* Signal processor definition.
+*/
+
+class dsp {
+
+    public:
+
+        dsp() {}
+        virtual ~dsp() {}
+
+        /* Return instance number of audio inputs */
+        virtual int getNumInputs() = 0;
+    
+        /* Return instance number of audio outputs */
+        virtual int getNumOutputs() = 0;
+    
+        /**
+         * Trigger the ui_interface parameter with instance specific calls
+         * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI.
+         *
+         * @param ui_interface - the user interface builder
+         */
+        virtual void buildUserInterface(UI* ui_interface) = 0;
+    
+        /* Returns the sample rate currently used by the instance */
+        virtual int getSampleRate() = 0;
+    
+        /**
+         * Global init, calls the following methods:
+         * - static class 'classInit': static tables initialization
+         * - 'instanceInit': constants and instance state initialization
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void init(int sample_rate) = 0;
+
+        /**
+         * Init instance state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceInit(int sample_rate) = 0;
+
+        /**
+         * Init instance constant state
+         *
+         * @param sample_rate - the sampling rate in Hertz
+         */
+        virtual void instanceConstants(int sample_rate) = 0;
+    
+        /* Init default control parameters values */
+        virtual void instanceResetUserInterface() = 0;
+    
+        /* Init instance state (delay lines...) */
+        virtual void instanceClear() = 0;
+        /**
+         * Return a clone of the instance.
+         *
+         * @return a copy of the instance on success, otherwise a null pointer.
+         */
+        virtual dsp* clone() = 0;
+    
+        /**
+         * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata.
+         *
+         * @param m - the Meta* meta user
+         */
+        virtual void metadata(Meta* m) = 0;
+    
+        /**
+         * DSP instance computation, to be called with successive in/out audio buffers.
+         *
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
+         *
+         */
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0;
+    
+        /**
+         * DSP instance computation: alternative method to be used by subclasses.
+         *
+         * @param date_usec - the timestamp in microsec given by audio driver.
+         * @param count - the number of frames to compute
+         * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
+         *
+         */
+        virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); }
+       
+};
+
+/**
+ * Generic DSP decorator.
+ */
+
+class decorator_dsp : public dsp {
+
+    protected:
+
+        dsp* fDSP;
+
+    public:
+
+        decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {}
+        virtual ~decorator_dsp() { delete fDSP; }
+
+        virtual int getNumInputs() { return fDSP->getNumInputs(); }
+        virtual int getNumOutputs() { return fDSP->getNumOutputs(); }
+        virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); }
+        virtual int getSampleRate() { return fDSP->getSampleRate(); }
+        virtual void init(int sample_rate) { fDSP->init(sample_rate); }
+        virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); }
+        virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); }
+        virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); }
+        virtual void instanceClear() { fDSP->instanceClear(); }
+        virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); }
+        virtual void metadata(Meta* m) { fDSP->metadata(m); }
+        // Beware: subclasses usually have to overload the two 'compute' methods
+        virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); }
+        virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); }
+    
+};
+
+/**
+ * DSP factory class.
+ */
+
+class dsp_factory {
+    
+    protected:
+    
+        // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory);
+        virtual ~dsp_factory() {}
+    
+    public:
+    
+        virtual std::string getName() = 0;
+        virtual std::string getSHAKey() = 0;
+        virtual std::string getDSPCode() = 0;
+        virtual std::string getCompileOptions() = 0;
+        virtual std::vector<std::string> getLibraryList() = 0;
+        virtual std::vector<std::string> getIncludePathnames() = 0;
+    
+        virtual dsp* createDSPInstance() = 0;
+    
+        virtual void setMemoryManager(dsp_memory_manager* manager) = 0;
+        virtual dsp_memory_manager* getMemoryManager() = 0;
+    
+};
+
+/**
+ * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
+ * flags to avoid costly denormals.
+ */
+
+#ifdef __SSE__
+    #include <xmmintrin.h>
+    #ifdef __SSE2__
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
+    #else
+        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
+    #endif
+#else
+    #define AVOIDDENORMALS
+#endif
+
+#endif
+/**************************  END  dsp.h **************************/
+
+/************************** BEGIN APIUI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef API_UI_H
+#define API_UI_H
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <map>
+
+/************************** BEGIN meta.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __meta__
+#define __meta__
+
+struct Meta
+{
+    virtual ~Meta() {};
+    virtual void declare(const char* key, const char* value) = 0;
+    
+};
+
+#endif
+/**************************  END  meta.h **************************/
+/************************** BEGIN UI.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __UI_H__
+#define __UI_H__
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+/*******************************************************************************
+ * UI : Faust DSP User Interface
+ * User Interface as expected by the buildUserInterface() method of a DSP.
+ * This abstract class contains only the method that the Faust compiler can
+ * generate to describe a DSP user interface.
+ ******************************************************************************/
+
+struct Soundfile;
+
+template <typename REAL>
+struct UIReal
+{
+    UIReal() {}
+    virtual ~UIReal() {}
+    
+    // -- widget's layouts
+    
+    virtual void openTabBox(const char* label) = 0;
+    virtual void openHorizontalBox(const char* label) = 0;
+    virtual void openVerticalBox(const char* label) = 0;
+    virtual void closeBox() = 0;
+    
+    // -- active widgets
+    
+    virtual void addButton(const char* label, REAL* zone) = 0;
+    virtual void addCheckButton(const char* label, REAL* zone) = 0;
+    virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
+    
+    // -- passive widgets
+    
+    virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
+    
+    // -- soundfiles
+    
+    virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0;
+    
+    // -- metadata declarations
+    
+    virtual void declare(REAL* zone, const char* key, const char* val) {}
+};
+
+struct UI : public UIReal<FAUSTFLOAT>
+{
+    UI() {}
+    virtual ~UI() {}
+};
+
+#endif
+/**************************  END  UI.h **************************/
+/************************** BEGIN PathBuilder.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef FAUST_PATHBUILDER_H
+#define FAUST_PATHBUILDER_H
+
+#include <vector>
+#include <string>
+#include <algorithm>
+
+/*******************************************************************************
+ * PathBuilder : Faust User Interface
+ * Helper class to build complete hierarchical path for UI items.
+ ******************************************************************************/
+
+class PathBuilder
+{
+
+    protected:
+    
+        std::vector<std::string> fControlsLevel;
+       
+    public:
+    
+        PathBuilder() {}
+        virtual ~PathBuilder() {}
+    
+        std::string buildPath(const std::string& label) 
+        {
+            std::string res = "/";
+            for (size_t i = 0; i < fControlsLevel.size(); i++) {
+                res += fControlsLevel[i];
+                res += "/";
+            }
+            res += label;
+            std::replace(res.begin(), res.end(), ' ', '_');
+            return res;
+        }
+    
+        std::string buildLabel(std::string label)
+        {
+            std::replace(label.begin(), label.end(), ' ', '_');
+            return label;
+        }
+    
+        void pushLabel(const std::string& label) { fControlsLevel.push_back(label); }
+        void popLabel() { fControlsLevel.pop_back(); }
+    
+};
+
+#endif  // FAUST_PATHBUILDER_H
+/**************************  END  PathBuilder.h **************************/
+/************************** BEGIN ValueConverter.h **************************/
+/************************************************************************
+ FAUST Architecture File
+ Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
+ ---------------------------------------------------------------------
+ This Architecture section is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3 of
+ the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ EXCEPTION : As a special exception, you may create a larger work
+ that contains this FAUST architecture section and distribute
+ that work under terms of your choice, so long as this FAUST
+ architecture section is not modified.
+ ************************************************************************/
+
+#ifndef __ValueConverter__
+#define __ValueConverter__
+
+/***************************************************************************************
+                                                               ValueConverter.h
+                            (GRAME, Copyright 2015-2019)
+
+Set of conversion objects used to map user interface values (for example a gui slider
+delivering values between 0 and 1) to faust values (for example a vslider between
+20 and 20000) using a log scale.
+
+-- Utilities
+
+Range(lo,hi) : clip a value x between lo and hi
+Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2
+Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2
+
+-- Value Converters
+
+ValueConverter::ui2faust(x)
+ValueConverter::faust2ui(x)
+
+-- ValueConverters used for sliders depending of the scale
+
+LinearValueConverter(umin, umax, fmin, fmax)
+LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments
+LogValueConverter(umin, umax, fmin, fmax)
+ExpValueConverter(umin, umax, fmin, fmax)
+
+-- ValueConverters used for accelerometers based on 3 points
+
+AccUpConverter(amin, amid, amax, fmin, fmid, fmax)             -- curve 0
+AccDownConverter(amin, amid, amax, fmin, fmid, fmax)   -- curve 1
+AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2
+AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3
+
+-- lists of ZoneControl are used to implement accelerometers metadata for each axes
+
+ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter
+
+-- ZoneReader are used to implement screencolor metadata
+
+ZoneReader(zone, valueConverter) : a zone with a data converter
+
+****************************************************************************************/
+
+#include <float.h>
+#include <algorithm>    // std::max
+#include <cmath>
+#include <vector>
+#include <assert.h>
+
+//--------------------------------------------------------------------------------------
+// Interpolator(lo,hi,v1,v2)
+// Maps a value x between lo and hi to a value y between v1 and v2
+// y = v1 + (x-lo)/(hi-lo)*(v2-v1)
+// y = v1 + (x-lo) * coef              with coef = (v2-v1)/(hi-lo)
+// y = v1 + x*coef - lo*coef
+// y = v1 - lo*coef + x*coef
+// y = offset + x*coef                         with offset = v1 - lo*coef
+//--------------------------------------------------------------------------------------
+class Interpolator
+{
+    private:
+
+        //--------------------------------------------------------------------------------------
+        // Range(lo,hi) clip a value between lo and hi
+        //--------------------------------------------------------------------------------------
+        struct Range
+        {
+            double fLo;
+            double fHi;
+
+            Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {}
+            double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; }
+        };
+
+
+        Range fRange;
+        double fCoef;
+        double fOffset;
+
+    public:
+
+        Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi)
+        {
+            if (hi != lo) {
+                // regular case
+                fCoef = (v2-v1)/(hi-lo);
+                fOffset = v1 - lo*fCoef;
+            } else {
+                // degenerate case, avoids division by zero
+                fCoef = 0;
+                fOffset = (v1+v2)/2;
+            }
+        }
+        double operator()(double v)
+        {
+            double x = fRange(v);
+            return  fOffset + x*fCoef;
+        }
+
+        void getLowHigh(double& amin, double& amax)
+        {
+            amin = fRange.fLo;
+            amax = fRange.fHi;
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Interpolator3pt(lo,mi,hi,v1,vm,v2)
+// Map values between lo mid hi to values between v1 vm v2
+//--------------------------------------------------------------------------------------
+class Interpolator3pt
+{
+
+    private:
+
+        Interpolator fSegment1;
+        Interpolator fSegment2;
+        double fMid;
+
+    public:
+
+        Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) :
+            fSegment1(lo, mi, v1, vm),
+            fSegment2(mi, hi, vm, v2),
+            fMid(mi) {}
+        double operator()(double x) { return  (x < fMid) ? fSegment1(x) : fSegment2(x); }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fSegment1.getLowHigh(amin, amid);
+            fSegment2.getLowHigh(amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Abstract ValueConverter class. Converts values between UI and Faust representations
+//--------------------------------------------------------------------------------------
+class ValueConverter
+{
+
+    public:
+
+        virtual ~ValueConverter() {}
+        virtual double ui2faust(double x) = 0;
+        virtual double faust2ui(double x) = 0;
+};
+
+//--------------------------------------------------------------------------------------
+// A converter than can be updated
+//--------------------------------------------------------------------------------------
+
+class UpdatableValueConverter : public ValueConverter {
+    
+    protected:
+        
+        bool fActive;
+        
+    public:
+        
+        UpdatableValueConverter():fActive(true)
+        {}
+        virtual ~UpdatableValueConverter()
+        {}
+        
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0;
+        virtual void getMappingValues(double& amin, double& amid, double& amax) = 0;
+        
+        void setActive(bool on_off) { fActive = on_off; }
+        bool getActive() { return fActive; }
+    
+};
+
+
+//--------------------------------------------------------------------------------------
+// Linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter : public ValueConverter
+{
+    
+    private:
+        
+        Interpolator fUI2F;
+        Interpolator fF2UI;
+        
+    public:
+        
+        LinearValueConverter(double umin, double umax, double fmin, double fmax) :
+            fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax)
+        {}
+        
+        LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.)
+        {}
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Two segments linear conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LinearValueConverter2 : public UpdatableValueConverter
+{
+    
+    private:
+    
+        Interpolator3pt fUI2F;
+        Interpolator3pt fF2UI;
+        
+    public:
+    
+        LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) :
+            fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax)
+        {}
+        
+        LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.)
+        {}
+    
+        virtual double ui2faust(double x) { return fUI2F(x); }
+        virtual double faust2ui(double x) { return fF2UI(x); }
+    
+        virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max)
+        {
+            fUI2F = Interpolator3pt(amin, amid, amax, min, init, max);
+            fF2UI = Interpolator3pt(min, init, max, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fUI2F.getMappingValues(amin, amid, amax);
+        }
+    
+};
+
+//--------------------------------------------------------------------------------------
+// Logarithmic conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class LogValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        LogValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Exponential conversion between ui and Faust values
+//--------------------------------------------------------------------------------------
+class ExpValueConverter : public LinearValueConverter
+{
+
+    public:
+
+        ExpValueConverter(double umin, double umax, double fmin, double fmax) :
+            LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax)))
+        {}
+
+        virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); }
+        virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up curve (curve 0)
+//--------------------------------------------------------------------------------------
+class AccUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt fA2F;
+        Interpolator3pt fF2A;
+
+    public:
+
+        AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmid,fmax),
+            fF2A(fmin,fmid,fmax,amin,amid,amax)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down curve (curve 1)
+//--------------------------------------------------------------------------------------
+class AccDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator3pt        fF2A;
+
+    public:
+
+        AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmid,fmin),
+            fF2A(fmin,fmid,fmax,amax,amid,amin)
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+             //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin);
+            fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using an Up-Down curve (curve 2)
+//--------------------------------------------------------------------------------------
+class AccUpDownConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmin,fmax,fmin),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Convert accelerometer or gyroscope values to Faust values
+// Using a Down-Up curve (curve 3)
+//--------------------------------------------------------------------------------------
+class AccDownUpConverter : public UpdatableValueConverter
+{
+
+    private:
+
+        Interpolator3pt        fA2F;
+        Interpolator fF2A;
+
+    public:
+
+        AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
+            fA2F(amin,amid,amax,fmax,fmin,fmax),
+            fF2A(fmin,fmax,amin,amax)                          // Special, pseudo inverse of a non monotonic function
+        {}
+
+        virtual double ui2faust(double x) { return fA2F(x); }
+        virtual double faust2ui(double x) { return fF2A(x); }
+
+        virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
+        {
+            //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
+            fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax);
+            fF2A = Interpolator(fmin, fmax, amin, amax);
+        }
+
+        virtual void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fA2F.getMappingValues(amin, amid, amax);
+        }
+};
+
+//--------------------------------------------------------------------------------------
+// Base class for ZoneControl
+//--------------------------------------------------------------------------------------
+class ZoneControl
+{
+
+    protected:
+
+        FAUSTFLOAT*    fZone;
+
+    public:
+
+        ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {}
+        virtual ~ZoneControl() {}
+
+        virtual void update(double v) const {}
+
+        virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {}
+        virtual void getMappingValues(double& amin, double& amid, double& amax) {}
+
+        FAUSTFLOAT* getZone() { return fZone; }
+
+        virtual void setActive(bool on_off) {}
+        virtual bool getActive() { return false; }
+
+        virtual int getCurve() { return -1; }
+
+};
+
+//--------------------------------------------------------------------------------------
+//  Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class ConverterZoneControl : public ZoneControl
+{
+
+    protected:
+
+        ValueConverter* fValueConverter;
+
+    public:
+
+        ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {}
+        virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere...
+
+        virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); }
+
+        ValueConverter* getConverter() { return fValueConverter; }
+
+};
+
+//--------------------------------------------------------------------------------------
+// Association of a zone and a four value converter, each one for each possible curve.
+// Useful to implement accelerometers metadata as a list of ZoneControl for each axes
+//--------------------------------------------------------------------------------------
+class CurveZoneControl : public ZoneControl
+{
+
+    private:
+
+        std::vector<UpdatableValueConverter*> fValueConverters;
+        int fCurve;
+
+    public:
+
+        CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0)
+        {
+            assert(curve >= 0 && curve <= 3);
+            fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max));
+            fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max));
+            fCurve = curve;
+        }
+        virtual ~CurveZoneControl()
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                delete(*it);
+            }
+        }
+        void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); }
+
+        void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max)
+        {
+            fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max);
+            fCurve = curve;
+        }
+
+        void getMappingValues(double& amin, double& amid, double& amax)
+        {
+            fValueConverters[fCurve]->getMappingValues(amin, amid, amax);
+        }
+
+        void setActive(bool on_off)
+        {
+            std::vector<UpdatableValueConverter*>::iterator it;
+            for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
+                (*it)->setActive(on_off);
+            }
+        }
+
+        int getCurve() { return fCurve; }
+};
+
+class ZoneReader
+{
+
+    private:
+
+        FAUSTFLOAT* fZone;
+        Interpolator fInterpolator;
+
+    public:
+
+        ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {}
+
+        virtual ~ZoneReader() {}
+
+        int getValue()
+        {
+            return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127;
+        }
+
+};
+
+#endif
+/**************************  END  ValueConverter.h **************************/
+
+class APIUI : public PathBuilder, public Meta, public UI
+{
+    public:
+    
+        enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph };
+  
+    protected:
+    
+        enum { kLin = 0, kLog = 1, kExp = 2 };
+    
+        int fNumParameters;
+        std::vector<std::string> fPaths;
+        std::vector<std::string> fLabels;
+        std::map<std::string, int> fPathMap;
+        std::map<std::string, int> fLabelMap;
+        std::vector<ValueConverter*> fConversion;
+        std::vector<FAUSTFLOAT*> fZone;
+        std::vector<FAUSTFLOAT> fInit;
+        std::vector<FAUSTFLOAT> fMin;
+        std::vector<FAUSTFLOAT> fMax;
+        std::vector<FAUSTFLOAT> fStep;
+        std::vector<ItemType> fItemType;
+        std::vector<std::map<std::string, std::string> > fMetaData;
+        std::vector<ZoneControl*> fAcc[3];
+        std::vector<ZoneControl*> fGyr[3];
+
+        // Screen color control
+        // "...[screencolor:red]..." etc.
+        bool fHasScreenControl;      // true if control screen color metadata
+        ZoneReader* fRedReader;
+        ZoneReader* fGreenReader;
+        ZoneReader* fBlueReader;
+
+        // Current values controlled by metadata
+        std::string fCurrentUnit;
+        int fCurrentScale;
+        std::string fCurrentAcc;
+        std::string fCurrentGyr;
+        std::string fCurrentColor;
+        std::string fCurrentTooltip;
+        std::map<std::string, std::string> fCurrentMetadata;
+    
+        // Add a generic parameter
+        virtual void addParameter(const char* label,
+                                FAUSTFLOAT* zone,
+                                FAUSTFLOAT init,
+                                FAUSTFLOAT min,
+                                FAUSTFLOAT max,
+                                FAUSTFLOAT step,
+                                ItemType type)
+        {
+            std::string path = buildPath(label);
+            fPathMap[path] = fLabelMap[label] = fNumParameters++;
+            fPaths.push_back(path);
+            fLabels.push_back(label);
+            fZone.push_back(zone);
+            fInit.push_back(init);
+            fMin.push_back(min);
+            fMax.push_back(max);
+            fStep.push_back(step);
+            fItemType.push_back(type);
+            
+            // handle scale metadata
+            switch (fCurrentScale) {
+                case kLin:
+                    fConversion.push_back(new LinearValueConverter(0, 1, min, max));
+                    break;
+                case kLog:
+                    fConversion.push_back(new LogValueConverter(0, 1, min, max));
+                    break;
+                case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max));
+                    break;
+            }
+            fCurrentScale = kLin;
+            
+            if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) {
+                std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n";
+            }
+
+            // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentAcc.size() > 0) {
+                std::istringstream iss(fCurrentAcc);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl;
+                }
+                fCurrentAcc = "";
+            }
+       
+            // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..."
+            if (fCurrentGyr.size() > 0) {
+                std::istringstream iss(fCurrentGyr);
+                int axe, curve;
+                double amin, amid, amax;
+                iss >> axe >> curve >> amin >> amid >> amax;
+
+                if ((0 <= axe) && (axe < 3) &&
+                    (0 <= curve) && (curve < 4) &&
+                    (amin < amax) && (amin <= amid) && (amid <= amax))
+                {
+                    fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
+                } else {
+                    std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl;
+                }
+                fCurrentGyr = "";
+            }
+        
+            // handle screencolor metadata "...[screencolor:red|green|blue|white]..."
+            if (fCurrentColor.size() > 0) {
+                if ((fCurrentColor == "red") && (fRedReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "green") && (fGreenReader == 0)) {
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) {
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) {
+                    fRedReader = new ZoneReader(zone, min, max);
+                    fGreenReader = new ZoneReader(zone, min, max);
+                    fBlueReader = new ZoneReader(zone, min, max);
+                    fHasScreenControl = true;
+                } else {
+                    std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl;
+                }
+            }
+            fCurrentColor = "";
+            
+            fMetaData.push_back(fCurrentMetadata);
+            fCurrentMetadata.clear();
+        }
+
+        int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val)
+        {
+            FAUSTFLOAT* zone = fZone[p];
+            for (size_t i = 0; i < table[val].size(); i++) {
+                if (zone == table[val][i]->getZone()) return int(i);
+            }
+            return -1;
+        }
+    
+        void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            // Deactivates everywhere..
+            if (id1 != -1) table[0][id1]->setActive(false);
+            if (id2 != -1) table[1][id2]->setActive(false);
+            if (id3 != -1) table[2][id3]->setActive(false);
+            
+            if (val == -1) { // Means: no more mapping...
+                // So stay all deactivated...
+            } else {
+                int id4 = getZoneIndex(table, p, val);
+                if (id4 != -1) {
+                    // Reactivate the one we edit...
+                    table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]);
+                    table[val][id4]->setActive(true);
+                } else {
+                    // Allocate a new CurveZoneControl which is 'active' by default
+                    FAUSTFLOAT* zone = fZone[p];
+                    table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]));
+                }
+            }
+        }
+    
+        void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax)
+        {
+            int id1 = getZoneIndex(table, p, 0);
+            int id2 = getZoneIndex(table, p, 1);
+            int id3 = getZoneIndex(table, p, 2);
+            
+            if (id1 != -1) {
+                val = 0;
+                curve = table[val][id1]->getCurve();
+                table[val][id1]->getMappingValues(amin, amid, amax);
+            } else if (id2 != -1) {
+                val = 1;
+                curve = table[val][id2]->getCurve();
+                table[val][id2]->getMappingValues(amin, amid, amax);
+            } else if (id3 != -1) {
+                val = 2;
+                curve = table[val][id3]->getCurve();
+                table[val][id3]->getMappingValues(amin, amid, amax);
+            } else {
+                val = -1; // No mapping
+                curve = 0;
+                amin = -100.;
+                amid = 0.;
+                amax = 100.;
+            }
+        }
+
+     public:
+    
+        enum Type { kAcc = 0, kGyr = 1, kNoType };
+   
+        APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin)
+        {}
+
+        virtual ~APIUI()
+        {
+            for (auto& it : fConversion) delete it;
+            for (int i = 0; i < 3; i++) {
+                for (auto& it : fAcc[i]) delete it;
+                for (auto& it : fGyr[i]) delete it;
+            }
+            delete fRedReader;
+            delete fGreenReader;
+            delete fBlueReader;
+        }
+    
+        // -- widget's layouts
+
+        virtual void openTabBox(const char* label) { pushLabel(label); }
+        virtual void openHorizontalBox(const char* label) { pushLabel(label); }
+        virtual void openVerticalBox(const char* label) { pushLabel(label); }
+        virtual void closeBox() { popLabel(); }
+
+        // -- active widgets
+
+        virtual void addButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kButton);
+        }
+
+        virtual void addCheckButton(const char* label, FAUSTFLOAT* zone)
+        {
+            addParameter(label, zone, 0, 0, 1, 1, kCheckButton);
+        }
+
+        virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kVSlider);
+        }
+
+        virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kHSlider);
+        }
+
+        virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+        {
+            addParameter(label, zone, init, min, max, step, kNumEntry);
+        }
+
+        // -- passive widgets
+
+        virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph);
+        }
+
+        virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
+        {
+            addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph);
+        }
+    
+        // -- soundfiles
+    
+        virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {}
+
+        // -- metadata declarations
+
+        virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val)
+        {
+            // Keep metadata
+            fCurrentMetadata[key] = val;
+            
+            if (strcmp(key, "scale") == 0) {
+                if (strcmp(val, "log") == 0) {
+                    fCurrentScale = kLog;
+                } else if (strcmp(val, "exp") == 0) {
+                    fCurrentScale = kExp;
+                } else {
+                    fCurrentScale = kLin;
+                }
+            } else if (strcmp(key, "unit") == 0) {
+                fCurrentUnit = val;
+            } else if (strcmp(key, "acc") == 0) {
+                fCurrentAcc = val;
+            } else if (strcmp(key, "gyr") == 0) {
+                fCurrentGyr = val;
+            } else if (strcmp(key, "screencolor") == 0) {
+                fCurrentColor = val; // val = "red", "green", "blue" or "white"
+            } else if (strcmp(key, "tooltip") == 0) {
+                fCurrentTooltip = val;
+            }
+        }
+
+        virtual void declare(const char* key, const char* val)
+        {}
+
+               //-------------------------------------------------------------------------------
+               // Simple API part
+               //-------------------------------------------------------------------------------
+               int getParamsCount() { return fNumParameters; }
+        int getParamIndex(const char* path)
+        {
+            if (fPathMap.find(path) != fPathMap.end()) {
+                return fPathMap[path];
+            } else if (fLabelMap.find(path) != fLabelMap.end()) {
+                return fLabelMap[path];
+            } else {
+                return -1;
+            }
+        }
+        const char* getParamAddress(int p) { return fPaths[p].c_str(); }
+        const char* getParamLabel(int p) { return fLabels[p].c_str(); }
+        std::map<const char*, const char*> getMetadata(int p)
+        {
+            std::map<const char*, const char*> res;
+            std::map<std::string, std::string> metadata = fMetaData[p];
+            for (auto it : metadata) {
+                res[it.first.c_str()] = it.second.c_str();
+            }
+            return res;
+        }
+
+        const char* getMetadata(int p, const char* key)
+        {
+            return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : "";
+        }
+        FAUSTFLOAT getParamMin(int p) { return fMin[p]; }
+        FAUSTFLOAT getParamMax(int p) { return fMax[p]; }
+        FAUSTFLOAT getParamStep(int p) { return fStep[p]; }
+        FAUSTFLOAT getParamInit(int p) { return fInit[p]; }
+
+        FAUSTFLOAT* getParamZone(int p) { return fZone[p]; }
+        FAUSTFLOAT getParamValue(int p) { return *fZone[p]; }
+        void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; }
+
+        double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); }
+        void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); }
+
+        double value2ratio(int p, double r)    { return fConversion[p]->faust2ui(r); }
+        double ratio2value(int p, double r)    { return fConversion[p]->ui2faust(r); }
+    
+        /**
+         * Return the control type (kAcc, kGyr, or -1) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the type
+         */
+        Type getParamType(int p)
+        {
+            if (p >= 0) {
+                if (getZoneIndex(fAcc, p, 0) != -1
+                    || getZoneIndex(fAcc, p, 1) != -1
+                    || getZoneIndex(fAcc, p, 2) != -1) {
+                    return kAcc;
+                } else if (getZoneIndex(fGyr, p, 0) != -1
+                           || getZoneIndex(fGyr, p, 1) != -1
+                           || getZoneIndex(fGyr, p, 2) != -1) {
+                    return kGyr;
+                }
+            }
+            return kNoType;
+        }
+    
+        /**
+         * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter
+         *
+         * @param p - the UI parameter index
+         *
+         * @return the Item type
+         */
+        ItemType getParamItemType(int p)
+        {
+            return fItemType[p];
+        }
+   
+        /**
+         * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @param value - the new value
+         *
+         */
+        void propagateAcc(int acc, double value)
+        {
+            for (size_t i = 0; i < fAcc[acc].size(); i++) {
+                fAcc[acc][i]->update(value);
+            }
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax)
+        {
+            setConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping")
+         * @param curve - between 0 and 3
+         * @param amin - mapping 'min' point
+         * @param amid - mapping 'middle' point
+         * @param amax - mapping 'max' point
+         *
+         */
+        void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax)
+        {
+             setConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param acc - the acc value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fAcc, p, acc, curve, amin, amid, amax);
+        }
+
+        /**
+         * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter.
+         *
+         * @param p - the UI parameter index
+         * @param gyr - the gyr value to be retrieved (-1 means "no mapping")
+         * @param curve - the curve value to be retrieved
+         * @param amin - the amin value to be retrieved
+         * @param amid - the amid value to be retrieved
+         * @param amax - the amax value to be retrieved
+         *
+         */
+        void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax)
+        {
+            getConverter(fGyr, p, gyr, curve, amin, amid, amax);
+        }
+    
+        /**
+         * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones.
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param value - the new value
+         *
+         */
+        void propagateGyr(int gyr, double value)
+        {
+            for (size_t i = 0; i < fGyr[gyr].size(); i++) {
+                fGyr[gyr][i]->update(value);
+            }
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the accelerometer
+         *
+         * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
+         * @return the number of zones
+         *
+         */
+        int getAccCount(int acc)
+        {
+            return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0;
+        }
+    
+        /**
+         * Get the number of FAUSTFLOAT* zones controlled with the gyroscope
+         *
+         * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
+         * @param the number of zones
+         *
+         */
+        int getGyrCount(int gyr)
+        {
+            return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0;
+        }
+   
+        // getScreenColor() : -1 means no screen color control (no screencolor metadata found)
+        // otherwise return 0x00RRGGBB a ready to use color
+        int getScreenColor()
+        {
+            if (fHasScreenControl) {
+                int r = (fRedReader) ? fRedReader->getValue() : 0;
+                int g = (fGreenReader) ? fGreenReader->getValue() : 0;
+                int b = (fBlueReader) ? fBlueReader->getValue() : 0;
+                return (r<<16) | (g<<8) | b;
+            } else {
+                return -1;
+            }
+        }
+};
+
+#endif
+/**************************  END  APIUI.h **************************/
+
+// NOTE: "faust -scn name" changes the last line above to
+// #include <faust/name/name.h>
+
+//----------------------------------------------------------------------------
+//  FAUST Generated Code
+//----------------------------------------------------------------------------
+
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif 
+
+#include <algorithm>
+#include <cmath>
+#include <math.h>
+
+static float zitarevmonodsp_faustpower2_f(float value) {
+       return (value * value);
+}
+
+#ifndef FAUSTCLASS 
+#define FAUSTCLASS zitarevmonodsp
+#endif
+
+#ifdef __APPLE__ 
+#define exp10f __exp10f
+#define exp10 __exp10
+#endif
+
+class zitarevmonodsp : public dsp {
+       
+ private:
+       
+       int IOTA;
+       float fVec0[16384];
+       FAUSTFLOAT fVslider0;
+       float fRec0[2];
+       FAUSTFLOAT fVslider1;
+       float fRec1[2];
+       int fSampleRate;
+       float fConst0;
+       float fConst1;
+       FAUSTFLOAT fVslider2;
+       FAUSTFLOAT fVslider3;
+       FAUSTFLOAT fVslider4;
+       FAUSTFLOAT fVslider5;
+       float fConst2;
+       float fConst3;
+       FAUSTFLOAT fVslider6;
+       FAUSTFLOAT fVslider7;
+       FAUSTFLOAT fVslider8;
+       float fConst4;
+       FAUSTFLOAT fVslider9;
+       float fRec15[2];
+       float fRec14[2];
+       float fVec1[32768];
+       float fConst5;
+       int iConst6;
+       float fConst7;
+       FAUSTFLOAT fVslider10;
+       float fVec2[2048];
+       int iConst8;
+       float fRec12[2];
+       float fConst9;
+       float fConst10;
+       float fRec19[2];
+       float fRec18[2];
+       float fVec3[32768];
+       float fConst11;
+       int iConst12;
+       float fVec4[4096];
+       int iConst13;
+       float fRec16[2];
+       float fConst14;
+       float fConst15;
+       float fRec23[2];
+       float fRec22[2];
+       float fVec5[16384];
+       float fConst16;
+       int iConst17;
+       float fVec6[4096];
+       int iConst18;
+       float fRec20[2];
+       float fConst19;
+       float fConst20;
+       float fRec27[2];
+       float fRec26[2];
+       float fVec7[32768];
+       float fConst21;
+       int iConst22;
+       float fVec8[4096];
+       int iConst23;
+       float fRec24[2];
+       float fConst24;
+       float fConst25;
+       float fRec31[2];
+       float fRec30[2];
+       float fVec9[16384];
+       float fConst26;
+       int iConst27;
+       float fVec10[2048];
+       int iConst28;
+       float fRec28[2];
+       float fConst29;
+       float fConst30;
+       float fRec35[2];
+       float fRec34[2];
+       float fVec11[16384];
+       float fConst31;
+       int iConst32;
+       float fVec12[4096];
+       int iConst33;
+       float fRec32[2];
+       float fConst34;
+       float fConst35;
+       float fRec39[2];
+       float fRec38[2];
+       float fVec13[16384];
+       float fConst36;
+       int iConst37;
+       float fVec14[4096];
+       int iConst38;
+       float fRec36[2];
+       float fConst39;
+       float fConst40;
+       float fRec43[2];
+       float fRec42[2];
+       float fVec15[16384];
+       float fConst41;
+       int iConst42;
+       float fVec16[2048];
+       int iConst43;
+       float fRec40[2];
+       float fRec4[3];
+       float fRec5[3];
+       float fRec6[3];
+       float fRec7[3];
+       float fRec8[3];
+       float fRec9[3];
+       float fRec10[3];
+       float fRec11[3];
+       float fRec3[3];
+       float fRec2[3];
+       float fRec45[3];
+       float fRec44[3];
+       
+ public:
+       
+       void metadata(Meta* m) { 
+               m->declare("basics.lib/name", "Faust Basic Element Library");
+               m->declare("basics.lib/version", "0.1");
+               m->declare("delays.lib/name", "Faust Delay Library");
+               m->declare("delays.lib/version", "0.1");
+               m->declare("filename", "zitarevmonodsp.dsp");
+               m->declare("filters.lib/allpass_comb:author", "Julius O. Smith III");
+               m->declare("filters.lib/allpass_comb:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/allpass_comb:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/fir:author", "Julius O. Smith III");
+               m->declare("filters.lib/fir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/fir:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/iir:author", "Julius O. Smith III");
+               m->declare("filters.lib/iir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/iir:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/lowpass0_highpass1", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/lowpass0_highpass1:author", "Julius O. Smith III");
+               m->declare("filters.lib/lowpass:author", "Julius O. Smith III");
+               m->declare("filters.lib/lowpass:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/lowpass:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/name", "Faust Filters Library");
+               m->declare("filters.lib/peak_eq_rm:author", "Julius O. Smith III");
+               m->declare("filters.lib/peak_eq_rm:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/peak_eq_rm:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/tf1:author", "Julius O. Smith III");
+               m->declare("filters.lib/tf1:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/tf1:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/tf1s:author", "Julius O. Smith III");
+               m->declare("filters.lib/tf1s:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/tf1s:license", "MIT-style STK-4.3 license");
+               m->declare("filters.lib/tf2:author", "Julius O. Smith III");
+               m->declare("filters.lib/tf2:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
+               m->declare("filters.lib/tf2:license", "MIT-style STK-4.3 license");
+               m->declare("maths.lib/author", "GRAME");
+               m->declare("maths.lib/copyright", "GRAME");
+               m->declare("maths.lib/license", "LGPL with exception");
+               m->declare("maths.lib/name", "Faust Math Library");
+               m->declare("maths.lib/version", "2.3");
+               m->declare("name", "zitarevmonodsp");
+               m->declare("platform.lib/name", "Generic Platform Library");
+               m->declare("platform.lib/version", "0.1");
+               m->declare("reverbs.lib/name", "Faust Reverb Library");
+               m->declare("reverbs.lib/version", "0.0");
+               m->declare("routes.lib/name", "Faust Signal Routing Library");
+               m->declare("routes.lib/version", "0.2");
+               m->declare("signals.lib/name", "Faust Signal Routing Library");
+               m->declare("signals.lib/version", "0.0");
+       }
+
+       virtual int getNumInputs() {
+               return 1;
+       }
+       virtual int getNumOutputs() {
+               return 1;
+       }
+       virtual int getInputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       virtual int getOutputRate(int channel) {
+               int rate;
+               switch ((channel)) {
+                       case 0: {
+                               rate = 1;
+                               break;
+                       }
+                       default: {
+                               rate = -1;
+                               break;
+                       }
+               }
+               return rate;
+       }
+       
+       static void classInit(int sample_rate) {
+       }
+       
+       virtual void instanceConstants(int sample_rate) {
+               fSampleRate = sample_rate;
+               fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate)));
+               fConst1 = (6.28318548f / fConst0);
+               fConst2 = std::floor(((0.219990999f * fConst0) + 0.5f));
+               fConst3 = ((0.0f - (6.90775537f * fConst2)) / fConst0);
+               fConst4 = (3.14159274f / fConst0);
+               fConst5 = std::floor(((0.0191229992f * fConst0) + 0.5f));
+               iConst6 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst2 - fConst5))));
+               fConst7 = (0.00100000005f * fConst0);
+               iConst8 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst5 + -1.0f))));
+               fConst9 = std::floor(((0.256891012f * fConst0) + 0.5f));
+               fConst10 = ((0.0f - (6.90775537f * fConst9)) / fConst0);
+               fConst11 = std::floor(((0.0273330007f * fConst0) + 0.5f));
+               iConst12 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst9 - fConst11))));
+               iConst13 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst11 + -1.0f))));
+               fConst14 = std::floor(((0.192303002f * fConst0) + 0.5f));
+               fConst15 = ((0.0f - (6.90775537f * fConst14)) / fConst0);
+               fConst16 = std::floor(((0.0292910002f * fConst0) + 0.5f));
+               iConst17 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst14 - fConst16))));
+               iConst18 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst16 + -1.0f))));
+               fConst19 = std::floor(((0.210389003f * fConst0) + 0.5f));
+               fConst20 = ((0.0f - (6.90775537f * fConst19)) / fConst0);
+               fConst21 = std::floor(((0.0244210009f * fConst0) + 0.5f));
+               iConst22 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst19 - fConst21))));
+               iConst23 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst21 + -1.0f))));
+               fConst24 = std::floor(((0.125f * fConst0) + 0.5f));
+               fConst25 = ((0.0f - (6.90775537f * fConst24)) / fConst0);
+               fConst26 = std::floor(((0.0134579996f * fConst0) + 0.5f));
+               iConst27 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst24 - fConst26))));
+               iConst28 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst26 + -1.0f))));
+               fConst29 = std::floor(((0.127837002f * fConst0) + 0.5f));
+               fConst30 = ((0.0f - (6.90775537f * fConst29)) / fConst0);
+               fConst31 = std::floor(((0.0316039994f * fConst0) + 0.5f));
+               iConst32 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst29 - fConst31))));
+               iConst33 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst31 + -1.0f))));
+               fConst34 = std::floor(((0.174713001f * fConst0) + 0.5f));
+               fConst35 = ((0.0f - (6.90775537f * fConst34)) / fConst0);
+               fConst36 = std::floor(((0.0229039993f * fConst0) + 0.5f));
+               iConst37 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst34 - fConst36))));
+               iConst38 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst36 + -1.0f))));
+               fConst39 = std::floor(((0.153128996f * fConst0) + 0.5f));
+               fConst40 = ((0.0f - (6.90775537f * fConst39)) / fConst0);
+               fConst41 = std::floor(((0.0203460008f * fConst0) + 0.5f));
+               iConst42 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst39 - fConst41))));
+               iConst43 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst41 + -1.0f))));
+       }
+       
+       virtual void instanceResetUserInterface() {
+               fVslider0 = FAUSTFLOAT(-3.0f);
+               fVslider1 = FAUSTFLOAT(0.0f);
+               fVslider2 = FAUSTFLOAT(1500.0f);
+               fVslider3 = FAUSTFLOAT(0.0f);
+               fVslider4 = FAUSTFLOAT(315.0f);
+               fVslider5 = FAUSTFLOAT(0.0f);
+               fVslider6 = FAUSTFLOAT(2.0f);
+               fVslider7 = FAUSTFLOAT(6000.0f);
+               fVslider8 = FAUSTFLOAT(3.0f);
+               fVslider9 = FAUSTFLOAT(200.0f);
+               fVslider10 = FAUSTFLOAT(60.0f);
+       }
+       
+       virtual void instanceClear() {
+               IOTA = 0;
+               for (int l0 = 0; (l0 < 16384); l0 = (l0 + 1)) {
+                       fVec0[l0] = 0.0f;
+               }
+               for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) {
+                       fRec0[l1] = 0.0f;
+               }
+               for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) {
+                       fRec1[l2] = 0.0f;
+               }
+               for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) {
+                       fRec15[l3] = 0.0f;
+               }
+               for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) {
+                       fRec14[l4] = 0.0f;
+               }
+               for (int l5 = 0; (l5 < 32768); l5 = (l5 + 1)) {
+                       fVec1[l5] = 0.0f;
+               }
+               for (int l6 = 0; (l6 < 2048); l6 = (l6 + 1)) {
+                       fVec2[l6] = 0.0f;
+               }
+               for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) {
+                       fRec12[l7] = 0.0f;
+               }
+               for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) {
+                       fRec19[l8] = 0.0f;
+               }
+               for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) {
+                       fRec18[l9] = 0.0f;
+               }
+               for (int l10 = 0; (l10 < 32768); l10 = (l10 + 1)) {
+                       fVec3[l10] = 0.0f;
+               }
+               for (int l11 = 0; (l11 < 4096); l11 = (l11 + 1)) {
+                       fVec4[l11] = 0.0f;
+               }
+               for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) {
+                       fRec16[l12] = 0.0f;
+               }
+               for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) {
+                       fRec23[l13] = 0.0f;
+               }
+               for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) {
+                       fRec22[l14] = 0.0f;
+               }
+               for (int l15 = 0; (l15 < 16384); l15 = (l15 + 1)) {
+                       fVec5[l15] = 0.0f;
+               }
+               for (int l16 = 0; (l16 < 4096); l16 = (l16 + 1)) {
+                       fVec6[l16] = 0.0f;
+               }
+               for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) {
+                       fRec20[l17] = 0.0f;
+               }
+               for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) {
+                       fRec27[l18] = 0.0f;
+               }
+               for (int l19 = 0; (l19 < 2); l19 = (l19 + 1)) {
+                       fRec26[l19] = 0.0f;
+               }
+               for (int l20 = 0; (l20 < 32768); l20 = (l20 + 1)) {
+                       fVec7[l20] = 0.0f;
+               }
+               for (int l21 = 0; (l21 < 4096); l21 = (l21 + 1)) {
+                       fVec8[l21] = 0.0f;
+               }
+               for (int l22 = 0; (l22 < 2); l22 = (l22 + 1)) {
+                       fRec24[l22] = 0.0f;
+               }
+               for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) {
+                       fRec31[l23] = 0.0f;
+               }
+               for (int l24 = 0; (l24 < 2); l24 = (l24 + 1)) {
+                       fRec30[l24] = 0.0f;
+               }
+               for (int l25 = 0; (l25 < 16384); l25 = (l25 + 1)) {
+                       fVec9[l25] = 0.0f;
+               }
+               for (int l26 = 0; (l26 < 2048); l26 = (l26 + 1)) {
+                       fVec10[l26] = 0.0f;
+               }
+               for (int l27 = 0; (l27 < 2); l27 = (l27 + 1)) {
+                       fRec28[l27] = 0.0f;
+               }
+               for (int l28 = 0; (l28 < 2); l28 = (l28 + 1)) {
+                       fRec35[l28] = 0.0f;
+               }
+               for (int l29 = 0; (l29 < 2); l29 = (l29 + 1)) {
+                       fRec34[l29] = 0.0f;
+               }
+               for (int l30 = 0; (l30 < 16384); l30 = (l30 + 1)) {
+                       fVec11[l30] = 0.0f;
+               }
+               for (int l31 = 0; (l31 < 4096); l31 = (l31 + 1)) {
+                       fVec12[l31] = 0.0f;
+               }
+               for (int l32 = 0; (l32 < 2); l32 = (l32 + 1)) {
+                       fRec32[l32] = 0.0f;
+               }
+               for (int l33 = 0; (l33 < 2); l33 = (l33 + 1)) {
+                       fRec39[l33] = 0.0f;
+               }
+               for (int l34 = 0; (l34 < 2); l34 = (l34 + 1)) {
+                       fRec38[l34] = 0.0f;
+               }
+               for (int l35 = 0; (l35 < 16384); l35 = (l35 + 1)) {
+                       fVec13[l35] = 0.0f;
+               }
+               for (int l36 = 0; (l36 < 4096); l36 = (l36 + 1)) {
+                       fVec14[l36] = 0.0f;
+               }
+               for (int l37 = 0; (l37 < 2); l37 = (l37 + 1)) {
+                       fRec36[l37] = 0.0f;
+               }
+               for (int l38 = 0; (l38 < 2); l38 = (l38 + 1)) {
+                       fRec43[l38] = 0.0f;
+               }
+               for (int l39 = 0; (l39 < 2); l39 = (l39 + 1)) {
+                       fRec42[l39] = 0.0f;
+               }
+               for (int l40 = 0; (l40 < 16384); l40 = (l40 + 1)) {
+                       fVec15[l40] = 0.0f;
+               }
+               for (int l41 = 0; (l41 < 2048); l41 = (l41 + 1)) {
+                       fVec16[l41] = 0.0f;
+               }
+               for (int l42 = 0; (l42 < 2); l42 = (l42 + 1)) {
+                       fRec40[l42] = 0.0f;
+               }
+               for (int l43 = 0; (l43 < 3); l43 = (l43 + 1)) {
+                       fRec4[l43] = 0.0f;
+               }
+               for (int l44 = 0; (l44 < 3); l44 = (l44 + 1)) {
+                       fRec5[l44] = 0.0f;
+               }
+               for (int l45 = 0; (l45 < 3); l45 = (l45 + 1)) {
+                       fRec6[l45] = 0.0f;
+               }
+               for (int l46 = 0; (l46 < 3); l46 = (l46 + 1)) {
+                       fRec7[l46] = 0.0f;
+               }
+               for (int l47 = 0; (l47 < 3); l47 = (l47 + 1)) {
+                       fRec8[l47] = 0.0f;
+               }
+               for (int l48 = 0; (l48 < 3); l48 = (l48 + 1)) {
+                       fRec9[l48] = 0.0f;
+               }
+               for (int l49 = 0; (l49 < 3); l49 = (l49 + 1)) {
+                       fRec10[l49] = 0.0f;
+               }
+               for (int l50 = 0; (l50 < 3); l50 = (l50 + 1)) {
+                       fRec11[l50] = 0.0f;
+               }
+               for (int l51 = 0; (l51 < 3); l51 = (l51 + 1)) {
+                       fRec3[l51] = 0.0f;
+               }
+               for (int l52 = 0; (l52 < 3); l52 = (l52 + 1)) {
+                       fRec2[l52] = 0.0f;
+               }
+               for (int l53 = 0; (l53 < 3); l53 = (l53 + 1)) {
+                       fRec45[l53] = 0.0f;
+               }
+               for (int l54 = 0; (l54 < 3); l54 = (l54 + 1)) {
+                       fRec44[l54] = 0.0f;
+               }
+       }
+       
+       virtual void init(int sample_rate) {
+               classInit(sample_rate);
+               instanceInit(sample_rate);
+       }
+       virtual void instanceInit(int sample_rate) {
+               instanceConstants(sample_rate);
+               instanceResetUserInterface();
+               instanceClear();
+       }
+       
+       virtual zitarevmonodsp* clone() {
+               return new zitarevmonodsp();
+       }
+       
+       virtual int getSampleRate() {
+               return fSampleRate;
+       }
+       
+       virtual void buildUserInterface(UI* ui_interface) {
+               ui_interface->declare(0, "0", "");
+               ui_interface->declare(0, "tooltip", "~ ZITA REV1 FEEDBACK DELAY NETWORK (FDN) & SCHROEDER  ALLPASS-COMB REVERBERATOR (8x8). See Faust's reverbs.lib for documentation and  references");
+               ui_interface->openHorizontalBox("Zita_Rev1");
+               ui_interface->declare(0, "1", "");
+               ui_interface->openHorizontalBox("Input");
+               ui_interface->declare(&fVslider10, "1", "");
+               ui_interface->declare(&fVslider10, "style", "knob");
+               ui_interface->declare(&fVslider10, "tooltip", "Delay in ms   before reverberation begins");
+               ui_interface->declare(&fVslider10, "unit", "ms");
+               ui_interface->addVerticalSlider("In Delay", &fVslider10, 60.0f, 20.0f, 100.0f, 1.0f);
+               ui_interface->closeBox();
+               ui_interface->declare(0, "2", "");
+               ui_interface->openHorizontalBox("Decay Times in Bands (see tooltips)");
+               ui_interface->declare(&fVslider9, "1", "");
+               ui_interface->declare(&fVslider9, "scale", "log");
+               ui_interface->declare(&fVslider9, "style", "knob");
+               ui_interface->declare(&fVslider9, "tooltip", "Crossover frequency (Hz) separating low and middle frequencies");
+               ui_interface->declare(&fVslider9, "unit", "Hz");
+               ui_interface->addVerticalSlider("LF X", &fVslider9, 200.0f, 50.0f, 1000.0f, 1.0f);
+               ui_interface->declare(&fVslider8, "2", "");
+               ui_interface->declare(&fVslider8, "scale", "log");
+               ui_interface->declare(&fVslider8, "style", "knob");
+               ui_interface->declare(&fVslider8, "tooltip", "T60 = time (in seconds) to decay 60dB in low-frequency band");
+               ui_interface->declare(&fVslider8, "unit", "s");
+               ui_interface->addVerticalSlider("Low RT60", &fVslider8, 3.0f, 1.0f, 8.0f, 0.100000001f);
+               ui_interface->declare(&fVslider6, "3", "");
+               ui_interface->declare(&fVslider6, "scale", "log");
+               ui_interface->declare(&fVslider6, "style", "knob");
+               ui_interface->declare(&fVslider6, "tooltip", "T60 = time (in seconds) to decay 60dB in middle band");
+               ui_interface->declare(&fVslider6, "unit", "s");
+               ui_interface->addVerticalSlider("Mid RT60", &fVslider6, 2.0f, 1.0f, 8.0f, 0.100000001f);
+               ui_interface->declare(&fVslider7, "4", "");
+               ui_interface->declare(&fVslider7, "scale", "log");
+               ui_interface->declare(&fVslider7, "style", "knob");
+               ui_interface->declare(&fVslider7, "tooltip", "Frequency (Hz) at which the high-frequency T60 is half the middle-band's T60");
+               ui_interface->declare(&fVslider7, "unit", "Hz");
+               ui_interface->addVerticalSlider("HF Damping", &fVslider7, 6000.0f, 1500.0f, 23520.0f, 1.0f);
+               ui_interface->closeBox();
+               ui_interface->declare(0, "3", "");
+               ui_interface->openHorizontalBox("RM Peaking Equalizer 1");
+               ui_interface->declare(&fVslider4, "1", "");
+               ui_interface->declare(&fVslider4, "scale", "log");
+               ui_interface->declare(&fVslider4, "style", "knob");
+               ui_interface->declare(&fVslider4, "tooltip", "Center-frequency of second-order Regalia-Mitra peaking equalizer section 1");
+               ui_interface->declare(&fVslider4, "unit", "Hz");
+               ui_interface->addVerticalSlider("Eq1 Freq", &fVslider4, 315.0f, 40.0f, 2500.0f, 1.0f);
+               ui_interface->declare(&fVslider5, "2", "");
+               ui_interface->declare(&fVslider5, "style", "knob");
+               ui_interface->declare(&fVslider5, "tooltip", "Peak level   in dB of second-order Regalia-Mitra peaking equalizer section 1");
+               ui_interface->declare(&fVslider5, "unit", "dB");
+               ui_interface->addVerticalSlider("Eq1 Level", &fVslider5, 0.0f, -15.0f, 15.0f, 0.100000001f);
+               ui_interface->closeBox();
+               ui_interface->declare(0, "4", "");
+               ui_interface->openHorizontalBox("RM Peaking Equalizer 2");
+               ui_interface->declare(&fVslider2, "1", "");
+               ui_interface->declare(&fVslider2, "scale", "log");
+               ui_interface->declare(&fVslider2, "style", "knob");
+               ui_interface->declare(&fVslider2, "tooltip", "Center-frequency of second-order Regalia-Mitra peaking equalizer section 2");
+               ui_interface->declare(&fVslider2, "unit", "Hz");
+               ui_interface->addVerticalSlider("Eq2 Freq", &fVslider2, 1500.0f, 160.0f, 10000.0f, 1.0f);
+               ui_interface->declare(&fVslider3, "2", "");
+               ui_interface->declare(&fVslider3, "style", "knob");
+               ui_interface->declare(&fVslider3, "tooltip", "Peak level   in dB of second-order Regalia-Mitra peaking equalizer section 2");
+               ui_interface->declare(&fVslider3, "unit", "dB");
+               ui_interface->addVerticalSlider("Eq2 Level", &fVslider3, 0.0f, -15.0f, 15.0f, 0.100000001f);
+               ui_interface->closeBox();
+               ui_interface->declare(0, "5", "");
+               ui_interface->openHorizontalBox("Output");
+               ui_interface->declare(&fVslider1, "1", "");
+               ui_interface->declare(&fVslider1, "style", "knob");
+               ui_interface->declare(&fVslider1, "tooltip", "Dry/Wet Mix: 0 = dry, 1 = wet");
+               ui_interface->addVerticalSlider("Wet", &fVslider1, 0.0f, 0.0f, 1.0f, 0.00999999978f);
+               ui_interface->declare(&fVslider0, "2", "");
+               ui_interface->declare(&fVslider0, "style", "knob");
+               ui_interface->declare(&fVslider0, "tooltip", "Output scale   factor");
+               ui_interface->declare(&fVslider0, "unit", "dB");
+               ui_interface->addVerticalSlider("Level", &fVslider0, -3.0f, -70.0f, 20.0f, 0.100000001f);
+               ui_interface->closeBox();
+               ui_interface->closeBox();
+       }
+       
+       virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+               FAUSTFLOAT* input0 = inputs[0];
+               FAUSTFLOAT* output0 = outputs[0];
+               float fSlow0 = (0.00100000005f * std::pow(10.0f, (0.0500000007f * float(fVslider0))));
+               float fSlow1 = (0.00100000005f * float(fVslider1));
+               float fSlow2 = float(fVslider2);
+               float fSlow3 = std::pow(10.0f, (0.0500000007f * float(fVslider3)));
+               float fSlow4 = (fConst1 * (fSlow2 / std::sqrt(std::max<float>(0.0f, fSlow3))));
+               float fSlow5 = ((1.0f - fSlow4) / (fSlow4 + 1.0f));
+               float fSlow6 = float(fVslider4);
+               float fSlow7 = std::pow(10.0f, (0.0500000007f * float(fVslider5)));
+               float fSlow8 = (fConst1 * (fSlow6 / std::sqrt(std::max<float>(0.0f, fSlow7))));
+               float fSlow9 = ((1.0f - fSlow8) / (fSlow8 + 1.0f));
+               float fSlow10 = float(fVslider6);
+               float fSlow11 = std::exp((fConst3 / fSlow10));
+               float fSlow12 = zitarevmonodsp_faustpower2_f(fSlow11);
+               float fSlow13 = std::cos((fConst1 * float(fVslider7)));
+               float fSlow14 = (1.0f - (fSlow12 * fSlow13));
+               float fSlow15 = (1.0f - fSlow12);
+               float fSlow16 = (fSlow14 / fSlow15);
+               float fSlow17 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow14) / zitarevmonodsp_faustpower2_f(fSlow15)) + -1.0f)));
+               float fSlow18 = (fSlow16 - fSlow17);
+               float fSlow19 = (fSlow11 * (fSlow17 + (1.0f - fSlow16)));
+               float fSlow20 = float(fVslider8);
+               float fSlow21 = ((std::exp((fConst3 / fSlow20)) / fSlow11) + -1.0f);
+               float fSlow22 = (1.0f / std::tan((fConst4 * float(fVslider9))));
+               float fSlow23 = (1.0f / (fSlow22 + 1.0f));
+               float fSlow24 = (1.0f - fSlow22);
+               int iSlow25 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst7 * float(fVslider10)))));
+               float fSlow26 = std::exp((fConst10 / fSlow10));
+               float fSlow27 = zitarevmonodsp_faustpower2_f(fSlow26);
+               float fSlow28 = (1.0f - (fSlow27 * fSlow13));
+               float fSlow29 = (1.0f - fSlow27);
+               float fSlow30 = (fSlow28 / fSlow29);
+               float fSlow31 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow28) / zitarevmonodsp_faustpower2_f(fSlow29)) + -1.0f)));
+               float fSlow32 = (fSlow30 - fSlow31);
+               float fSlow33 = (fSlow26 * (fSlow31 + (1.0f - fSlow30)));
+               float fSlow34 = ((std::exp((fConst10 / fSlow20)) / fSlow26) + -1.0f);
+               float fSlow35 = std::exp((fConst15 / fSlow10));
+               float fSlow36 = zitarevmonodsp_faustpower2_f(fSlow35);
+               float fSlow37 = (1.0f - (fSlow36 * fSlow13));
+               float fSlow38 = (1.0f - fSlow36);
+               float fSlow39 = (fSlow37 / fSlow38);
+               float fSlow40 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow37) / zitarevmonodsp_faustpower2_f(fSlow38)) + -1.0f)));
+               float fSlow41 = (fSlow39 - fSlow40);
+               float fSlow42 = (fSlow35 * (fSlow40 + (1.0f - fSlow39)));
+               float fSlow43 = ((std::exp((fConst15 / fSlow20)) / fSlow35) + -1.0f);
+               float fSlow44 = std::exp((fConst20 / fSlow10));
+               float fSlow45 = zitarevmonodsp_faustpower2_f(fSlow44);
+               float fSlow46 = (1.0f - (fSlow45 * fSlow13));
+               float fSlow47 = (1.0f - fSlow45);
+               float fSlow48 = (fSlow46 / fSlow47);
+               float fSlow49 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow46) / zitarevmonodsp_faustpower2_f(fSlow47)) + -1.0f)));
+               float fSlow50 = (fSlow48 - fSlow49);
+               float fSlow51 = (fSlow44 * (fSlow49 + (1.0f - fSlow48)));
+               float fSlow52 = ((std::exp((fConst20 / fSlow20)) / fSlow44) + -1.0f);
+               float fSlow53 = std::exp((fConst25 / fSlow10));
+               float fSlow54 = zitarevmonodsp_faustpower2_f(fSlow53);
+               float fSlow55 = (1.0f - (fSlow54 * fSlow13));
+               float fSlow56 = (1.0f - fSlow54);
+               float fSlow57 = (fSlow55 / fSlow56);
+               float fSlow58 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow55) / zitarevmonodsp_faustpower2_f(fSlow56)) + -1.0f)));
+               float fSlow59 = (fSlow57 - fSlow58);
+               float fSlow60 = (fSlow53 * (fSlow58 + (1.0f - fSlow57)));
+               float fSlow61 = ((std::exp((fConst25 / fSlow20)) / fSlow53) + -1.0f);
+               float fSlow62 = std::exp((fConst30 / fSlow10));
+               float fSlow63 = zitarevmonodsp_faustpower2_f(fSlow62);
+               float fSlow64 = (1.0f - (fSlow63 * fSlow13));
+               float fSlow65 = (1.0f - fSlow63);
+               float fSlow66 = (fSlow64 / fSlow65);
+               float fSlow67 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow64) / zitarevmonodsp_faustpower2_f(fSlow65)) + -1.0f)));
+               float fSlow68 = (fSlow66 - fSlow67);
+               float fSlow69 = (fSlow62 * (fSlow67 + (1.0f - fSlow66)));
+               float fSlow70 = ((std::exp((fConst30 / fSlow20)) / fSlow62) + -1.0f);
+               float fSlow71 = std::exp((fConst35 / fSlow10));
+               float fSlow72 = zitarevmonodsp_faustpower2_f(fSlow71);
+               float fSlow73 = (1.0f - (fSlow72 * fSlow13));
+               float fSlow74 = (1.0f - fSlow72);
+               float fSlow75 = (fSlow73 / fSlow74);
+               float fSlow76 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow73) / zitarevmonodsp_faustpower2_f(fSlow74)) + -1.0f)));
+               float fSlow77 = (fSlow75 - fSlow76);
+               float fSlow78 = (fSlow71 * (fSlow76 + (1.0f - fSlow75)));
+               float fSlow79 = ((std::exp((fConst35 / fSlow20)) / fSlow71) + -1.0f);
+               float fSlow80 = std::exp((fConst40 / fSlow10));
+               float fSlow81 = zitarevmonodsp_faustpower2_f(fSlow80);
+               float fSlow82 = (1.0f - (fSlow81 * fSlow13));
+               float fSlow83 = (1.0f - fSlow81);
+               float fSlow84 = (fSlow82 / fSlow83);
+               float fSlow85 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow82) / zitarevmonodsp_faustpower2_f(fSlow83)) + -1.0f)));
+               float fSlow86 = (fSlow84 - fSlow85);
+               float fSlow87 = (fSlow80 * (fSlow85 + (1.0f - fSlow84)));
+               float fSlow88 = ((std::exp((fConst40 / fSlow20)) / fSlow80) + -1.0f);
+               float fSlow89 = (0.0f - (std::cos((fConst1 * fSlow6)) * (fSlow9 + 1.0f)));
+               float fSlow90 = (0.0f - (std::cos((fConst1 * fSlow2)) * (fSlow5 + 1.0f)));
+               for (int i = 0; (i < count); i = (i + 1)) {
+                       float fTemp0 = float(input0[i]);
+                       fVec0[(IOTA & 16383)] = fTemp0;
+                       fRec0[0] = (fSlow0 + (0.999000013f * fRec0[1]));
+                       fRec1[0] = (fSlow1 + (0.999000013f * fRec1[1]));
+                       fRec15[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec15[1]) - (fRec11[1] + fRec11[2]))));
+                       fRec14[0] = ((fSlow18 * fRec14[1]) + (fSlow19 * (fRec11[1] + (fSlow21 * fRec15[0]))));
+                       fVec1[(IOTA & 32767)] = ((0.353553385f * fRec14[0]) + 9.99999968e-21f);
+                       float fTemp1 = (0.300000012f * fVec0[((IOTA - iSlow25) & 16383)]);
+                       float fTemp2 = (((0.600000024f * fRec12[1]) + fVec1[((IOTA - iConst6) & 32767)]) - fTemp1);
+                       fVec2[(IOTA & 2047)] = fTemp2;
+                       fRec12[0] = fVec2[((IOTA - iConst8) & 2047)];
+                       float fRec13 = (0.0f - (0.600000024f * fTemp2));
+                       fRec19[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec19[1]) - (fRec7[1] + fRec7[2]))));
+                       fRec18[0] = ((fSlow32 * fRec18[1]) + (fSlow33 * (fRec7[1] + (fSlow34 * fRec19[0]))));
+                       fVec3[(IOTA & 32767)] = ((0.353553385f * fRec18[0]) + 9.99999968e-21f);
+                       float fTemp3 = (((0.600000024f * fRec16[1]) + fVec3[((IOTA - iConst12) & 32767)]) - fTemp1);
+                       fVec4[(IOTA & 4095)] = fTemp3;
+                       fRec16[0] = fVec4[((IOTA - iConst13) & 4095)];
+                       float fRec17 = (0.0f - (0.600000024f * fTemp3));
+                       fRec23[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec23[1]) - (fRec9[1] + fRec9[2]))));
+                       fRec22[0] = ((fSlow41 * fRec22[1]) + (fSlow42 * (fRec9[1] + (fSlow43 * fRec23[0]))));
+                       fVec5[(IOTA & 16383)] = ((0.353553385f * fRec22[0]) + 9.99999968e-21f);
+                       float fTemp4 = (fVec5[((IOTA - iConst17) & 16383)] + (fTemp1 + (0.600000024f * fRec20[1])));
+                       fVec6[(IOTA & 4095)] = fTemp4;
+                       fRec20[0] = fVec6[((IOTA - iConst18) & 4095)];
+                       float fRec21 = (0.0f - (0.600000024f * fTemp4));
+                       fRec27[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec27[1]) - (fRec5[1] + fRec5[2]))));
+                       fRec26[0] = ((fSlow50 * fRec26[1]) + (fSlow51 * (fRec5[1] + (fSlow52 * fRec27[0]))));
+                       fVec7[(IOTA & 32767)] = ((0.353553385f * fRec26[0]) + 9.99999968e-21f);
+                       float fTemp5 = (fVec7[((IOTA - iConst22) & 32767)] + (fTemp1 + (0.600000024f * fRec24[1])));
+                       fVec8[(IOTA & 4095)] = fTemp5;
+                       fRec24[0] = fVec8[((IOTA - iConst23) & 4095)];
+                       float fRec25 = (0.0f - (0.600000024f * fTemp5));
+                       fRec31[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec31[1]) - (fRec10[1] + fRec10[2]))));
+                       fRec30[0] = ((fSlow59 * fRec30[1]) + (fSlow60 * (fRec10[1] + (fSlow61 * fRec31[0]))));
+                       fVec9[(IOTA & 16383)] = ((0.353553385f * fRec30[0]) + 9.99999968e-21f);
+                       float fTemp6 = (fVec9[((IOTA - iConst27) & 16383)] - (fTemp1 + (0.600000024f * fRec28[1])));
+                       fVec10[(IOTA & 2047)] = fTemp6;
+                       fRec28[0] = fVec10[((IOTA - iConst28) & 2047)];
+                       float fRec29 = (0.600000024f * fTemp6);
+                       fRec35[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec35[1]) - (fRec6[1] + fRec6[2]))));
+                       fRec34[0] = ((fSlow68 * fRec34[1]) + (fSlow69 * (fRec6[1] + (fSlow70 * fRec35[0]))));
+                       fVec11[(IOTA & 16383)] = ((0.353553385f * fRec34[0]) + 9.99999968e-21f);
+                       float fTemp7 = (fVec11[((IOTA - iConst32) & 16383)] - (fTemp1 + (0.600000024f * fRec32[1])));
+                       fVec12[(IOTA & 4095)] = fTemp7;
+                       fRec32[0] = fVec12[((IOTA - iConst33) & 4095)];
+                       float fRec33 = (0.600000024f * fTemp7);
+                       fRec39[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec39[1]) - (fRec8[1] + fRec8[2]))));
+                       fRec38[0] = ((fSlow77 * fRec38[1]) + (fSlow78 * (fRec8[1] + (fSlow79 * fRec39[0]))));
+                       fVec13[(IOTA & 16383)] = ((0.353553385f * fRec38[0]) + 9.99999968e-21f);
+                       float fTemp8 = ((fTemp1 + fVec13[((IOTA - iConst37) & 16383)]) - (0.600000024f * fRec36[1]));
+                       fVec14[(IOTA & 4095)] = fTemp8;
+                       fRec36[0] = fVec14[((IOTA - iConst38) & 4095)];
+                       float fRec37 = (0.600000024f * fTemp8);
+                       fRec43[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec43[1]) - (fRec4[1] + fRec4[2]))));
+                       fRec42[0] = ((fSlow86 * fRec42[1]) + (fSlow87 * (fRec4[1] + (fSlow88 * fRec43[0]))));
+                       fVec15[(IOTA & 16383)] = ((0.353553385f * fRec42[0]) + 9.99999968e-21f);
+                       float fTemp9 = ((fVec15[((IOTA - iConst42) & 16383)] + fTemp1) - (0.600000024f * fRec40[1]));
+                       fVec16[(IOTA & 2047)] = fTemp9;
+                       fRec40[0] = fVec16[((IOTA - iConst43) & 2047)];
+                       float fRec41 = (0.600000024f * fTemp9);
+                       float fTemp10 = (fRec41 + fRec37);
+                       float fTemp11 = (fRec29 + (fRec33 + fTemp10));
+                       fRec4[0] = (fRec12[1] + (fRec16[1] + (fRec20[1] + (fRec24[1] + (fRec28[1] + (fRec32[1] + (fRec36[1] + (fRec40[1] + (fRec13 + (fRec17 + (fRec21 + (fRec25 + fTemp11))))))))))));
+                       fRec5[0] = ((fRec28[1] + (fRec32[1] + (fRec36[1] + (fRec40[1] + fTemp11)))) - (fRec12[1] + (fRec16[1] + (fRec20[1] + (fRec24[1] + (fRec13 + (fRec17 + (fRec25 + fRec21))))))));
+                       float fTemp12 = (fRec33 + fRec29);
+                       fRec6[0] = ((fRec20[1] + (fRec24[1] + (fRec36[1] + (fRec40[1] + (fRec21 + (fRec25 + fTemp10)))))) - (fRec12[1] + (fRec16[1] + (fRec28[1] + (fRec32[1] + (fRec13 + (fRec17 + fTemp12)))))));
+                       fRec7[0] = ((fRec12[1] + (fRec16[1] + (fRec36[1] + (fRec40[1] + (fRec13 + (fRec17 + fTemp10)))))) - (fRec20[1] + (fRec24[1] + (fRec28[1] + (fRec32[1] + (fRec21 + (fRec25 + fTemp12)))))));
+                       float fTemp13 = (fRec41 + fRec33);
+                       float fTemp14 = (fRec37 + fRec29);
+                       fRec8[0] = ((fRec16[1] + (fRec24[1] + (fRec32[1] + (fRec40[1] + (fRec17 + (fRec25 + fTemp13)))))) - (fRec12[1] + (fRec20[1] + (fRec28[1] + (fRec36[1] + (fRec13 + (fRec21 + fTemp14)))))));
+                       fRec9[0] = ((fRec12[1] + (fRec20[1] + (fRec32[1] + (fRec40[1] + (fRec13 + (fRec21 + fTemp13)))))) - (fRec16[1] + (fRec24[1] + (fRec28[1] + (fRec36[1] + (fRec17 + (fRec25 + fTemp14)))))));
+                       float fTemp15 = (fRec41 + fRec29);
+                       float fTemp16 = (fRec37 + fRec33);
+                       fRec10[0] = ((fRec12[1] + (fRec24[1] + (fRec28[1] + (fRec40[1] + (fRec13 + (fRec25 + fTemp15)))))) - (fRec16[1] + (fRec20[1] + (fRec32[1] + (fRec36[1] + (fRec17 + (fRec21 + fTemp16)))))));
+                       fRec11[0] = ((fRec16[1] + (fRec20[1] + (fRec28[1] + (fRec40[1] + (fRec17 + (fRec21 + fTemp15)))))) - (fRec12[1] + (fRec24[1] + (fRec32[1] + (fRec36[1] + (fRec13 + (fRec25 + fTemp16)))))));
+                       float fTemp17 = (0.370000005f * (fRec5[0] + fRec6[0]));
+                       float fTemp18 = (fSlow89 * fRec3[1]);
+                       fRec3[0] = (fTemp17 - (fTemp18 + (fSlow9 * fRec3[2])));
+                       float fTemp19 = (fSlow9 * fRec3[0]);
+                       float fTemp20 = (0.5f * ((fTemp19 + (fRec3[2] + (fTemp17 + fTemp18))) + (fSlow7 * ((fTemp19 + (fTemp18 + fRec3[2])) - fTemp17))));
+                       float fTemp21 = (fSlow90 * fRec2[1]);
+                       fRec2[0] = (fTemp20 - (fTemp21 + (fSlow5 * fRec2[2])));
+                       float fTemp22 = (fSlow5 * fRec2[0]);
+                       float fTemp23 = (fTemp0 * (1.0f - fRec1[0]));
+                       float fTemp24 = (0.370000005f * (fRec5[0] - fRec6[0]));
+                       float fTemp25 = (fSlow89 * fRec45[1]);
+                       fRec45[0] = (fTemp24 - (fTemp25 + (fSlow9 * fRec45[2])));
+                       float fTemp26 = (fSlow9 * fRec45[0]);
+                       float fTemp27 = (0.5f * ((fTemp26 + (fRec45[2] + (fTemp24 + fTemp25))) + (fSlow7 * ((fTemp26 + (fTemp25 + fRec45[2])) - fTemp24))));
+                       float fTemp28 = (fSlow90 * fRec44[1]);
+                       fRec44[0] = (fTemp27 - (fTemp28 + (fSlow5 * fRec44[2])));
+                       float fTemp29 = (fSlow5 * fRec44[0]);
+                       output0[i] = FAUSTFLOAT((fRec0[0] * (((0.5f * (fRec1[0] * ((fTemp22 + (fRec2[2] + (fTemp20 + fTemp21))) + (fSlow3 * ((fTemp22 + (fTemp21 + fRec2[2])) - fTemp20))))) + fTemp23) + (fTemp23 + (0.5f * (fRec1[0] * ((fTemp29 + (fRec44[2] + (fTemp27 + fTemp28))) + (fSlow3 * ((fTemp29 + (fTemp28 + fRec44[2])) - fTemp27)))))))));
+                       IOTA = (IOTA + 1);
+                       fRec0[1] = fRec0[0];
+                       fRec1[1] = fRec1[0];
+                       fRec15[1] = fRec15[0];
+                       fRec14[1] = fRec14[0];
+                       fRec12[1] = fRec12[0];
+                       fRec19[1] = fRec19[0];
+                       fRec18[1] = fRec18[0];
+                       fRec16[1] = fRec16[0];
+                       fRec23[1] = fRec23[0];
+                       fRec22[1] = fRec22[0];
+                       fRec20[1] = fRec20[0];
+                       fRec27[1] = fRec27[0];
+                       fRec26[1] = fRec26[0];
+                       fRec24[1] = fRec24[0];
+                       fRec31[1] = fRec31[0];
+                       fRec30[1] = fRec30[0];
+                       fRec28[1] = fRec28[0];
+                       fRec35[1] = fRec35[0];
+                       fRec34[1] = fRec34[0];
+                       fRec32[1] = fRec32[0];
+                       fRec39[1] = fRec39[0];
+                       fRec38[1] = fRec38[0];
+                       fRec36[1] = fRec36[0];
+                       fRec43[1] = fRec43[0];
+                       fRec42[1] = fRec42[0];
+                       fRec40[1] = fRec40[0];
+                       fRec4[2] = fRec4[1];
+                       fRec4[1] = fRec4[0];
+                       fRec5[2] = fRec5[1];
+                       fRec5[1] = fRec5[0];
+                       fRec6[2] = fRec6[1];
+                       fRec6[1] = fRec6[0];
+                       fRec7[2] = fRec7[1];
+                       fRec7[1] = fRec7[0];
+                       fRec8[2] = fRec8[1];
+                       fRec8[1] = fRec8[0];
+                       fRec9[2] = fRec9[1];
+                       fRec9[1] = fRec9[0];
+                       fRec10[2] = fRec10[1];
+                       fRec10[1] = fRec10[0];
+                       fRec11[2] = fRec11[1];
+                       fRec11[1] = fRec11[0];
+                       fRec3[2] = fRec3[1];
+                       fRec3[1] = fRec3[0];
+                       fRec2[2] = fRec2[1];
+                       fRec2[1] = fRec2[0];
+                       fRec45[2] = fRec45[1];
+                       fRec45[1] = fRec45[0];
+                       fRec44[2] = fRec44[1];
+                       fRec44[1] = fRec44[0];
+               }
+       }
+
+};
+
+#endif