summaryrefslogtreecommitdiff
path: root/src/Effects.h
diff options
context:
space:
mode:
authorIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>2021-01-20 09:10:34 +0100
committerIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>2021-01-20 09:10:34 +0100
commit6acadc7e322d4be0e696efd0ad7719be9f66abe9 (patch)
tree157923cb8752b4de6f8f46c1462bda3ffa2d9be8 /src/Effects.h
parent9ddd3730560abd20b2eed088a0b8a2fc53b31816 (diff)
New upstream version 1.3.0+ds0
Diffstat (limited to 'src/Effects.h')
-rw-r--r--src/Effects.h571
1 files changed, 571 insertions, 0 deletions
diff --git a/src/Effects.h b/src/Effects.h
new file mode 100644
index 0000000..086d40e
--- /dev/null
+++ b/src/Effects.h
@@ -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;
+ }
+
+};