From: IOhannes m zmölnig Date: Wed, 20 Jan 2021 08:10:34 +0000 (+0100) Subject: New upstream version 1.3.0+ds0 X-Git-Tag: archive/raspbian/2.5.1+ds-1+rpi1~1^2~9^2~28 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=6acadc7e322d4be0e696efd0ad7719be9f66abe9;p=jacktrip.git New upstream version 1.3.0+ds0 --- diff --git a/.travis.yml b/.travis.yml index 2fe76ba..c6ff4a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,9 @@ language: dist: bionic branches: + - main - dev - + env: global: - PKGS_OSX="jack qt rt-audio" diff --git a/CHANGESLOG.txt b/CHANGESLOG.txt index 0039872..139ba35 100644 --- a/CHANGESLOG.txt +++ b/CHANGESLOG.txt @@ -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 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 - - -//------------------------------------------------------------------------------- -/*! \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 OutputInput(2); - - // this->setConnectedPorts(); - - // root = jmess_xml.createElement("jmess"); - // for (QVector >::iterator it = mConnectedPorts.begin(); - // it != mConnectedPorts.end(); ++it) { - // OutputInput = *it; - // //cout << "Output ===> " < " <> 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 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[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 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 OutputInput(2); - - this->setConnectedPorts(); - - for (QVector >::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 : " - // << qPrintable(jmess.tagName()) << endl; - // return 1; - // } - - - // QVector OutputInput(2); - // //First check for 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 OutputInput(2); - - // if ( !(this->parseXML(xmlInFile)) ) { - // for (QVector >::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 index 28901e0..0000000 Binary files a/WIN10BUILDINSTRUCTIONS.pdf and /dev/null differ diff --git a/faust-src/Makefile b/faust-src/Makefile index 14ffe57..2eebaf7 100644 --- a/faust-src/Makefile +++ b/faust-src/Makefile @@ -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 index 0000000..e2304de --- /dev/null +++ b/faust-src/Makefile.headers @@ -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 index 0000000..9caf104 --- /dev/null +++ b/faust-src/README-Limiter.md @@ -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 index 0000000..7384714 --- /dev/null +++ b/faust-src/compressor-limiter-test.dsp @@ -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 index 0000000..646ddc9 --- /dev/null +++ b/faust-src/compressordsp.dsp @@ -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 index 0000000..3404470 --- /dev/null +++ b/faust-src/faust2header.cpp @@ -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 + +#include + +// NOTE: "faust -scn name" changes the last line above to +// #include + +//---------------------------------------------------------------------------- +// FAUST Generated Code +//---------------------------------------------------------------------------- + +<> + +<> diff --git a/faust-src/freeverbdsp.dsp b/faust-src/freeverbdsp.dsp new file mode 100644 index 0000000..354a885 --- /dev/null +++ b/faust-src/freeverbdsp.dsp @@ -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 index 0000000..8008c48 --- /dev/null +++ b/faust-src/freeverbmonodsp.dsp @@ -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 index 0000000..e177d64 --- /dev/null +++ b/faust-src/limiterdsp.dsp @@ -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 index 0000000..8d236b5 --- /dev/null +++ b/faust-src/limitertest.dsp @@ -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 index 0000000..64577e4 --- /dev/null +++ b/faust-src/tests/compressor-limiter-test.dsp @@ -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 index 0000000..831bfd0 --- /dev/null +++ b/faust-src/tests/compressor2dsp.dsp @@ -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 index 0000000..b98a83c --- /dev/null +++ b/faust-src/zitarevdsp.dsp @@ -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 +// +// +//------------------------------------------------------------ +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 index 0000000..2a293d4 --- /dev/null +++ b/faust-src/zitarevmonodsp.dsp @@ -0,0 +1,2 @@ +process = _ <: _,_ : component("zitarevdsp.dsp") : _,_ :> _; + diff --git a/meson.build b/meson.build index 6debd45..c713097 100644 --- a/meson.build +++ b/meson.build @@ -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 index 0000000..77af9a3 --- /dev/null +++ b/scripts/hubMode/art.sh @@ -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 index 0000000..598c8d3 --- /dev/null +++ b/scripts/hubMode/startJacktripHubClient.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# bash script for jacktrip automation, Chris Chafe +# startJacktripHubClient.sh +# +# /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 do, chmod +x