New upstream version 1.5.2+ds0
authorIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>
Mon, 7 Mar 2022 18:33:36 +0000 (19:33 +0100)
committerIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>
Mon, 7 Mar 2022 18:33:36 +0000 (19:33 +0100)
22 files changed:
CMakeLists.txt
build
docs/Install.md
docs/changelog.yml
jacktrip.pro
macos/assemble_app.sh
meson.build
src/JackTrip.cpp
src/JackTrip.h
src/PacketHeader.cpp
src/PoolBuffer.cpp [deleted file]
src/PoolBuffer.h [deleted file]
src/Regulator.cpp [new file with mode: 0644]
src/Regulator.h [new file with mode: 0644]
src/UdpDataProtocol.cpp
src/gui/qjacktrip.cpp
src/gui/qjacktrip.h
src/gui/qjacktrip.ui
src/gui/qjacktrip_novs.ui
src/jacktrip_globals.cpp
src/jacktrip_globals.h
src/main.cpp

index 47f4c321feb62027ed81b3ed8d05daa5d235815f..40cae871e7d62959a98dca8fdbed3cbf7b6c9c32 100644 (file)
@@ -82,7 +82,7 @@ set(qjacktrip_SRC
   src/PacketHeader.cpp
   src/RingBuffer.cpp
   src/JitterBuffer.cpp
-  src/PoolBuffer.cpp
+  src/Regulator.cpp
   src/Compressor.cpp
   src/Limiter.cpp
   src/Reverb.cpp
diff --git a/build b/build
index 90c0a52eb6b673fa15695df87ee6f9f08717d199..1f8d5ca6f19b7e3f1231833abd9a243a979678f2 100755 (executable)
--- a/build
+++ b/build
@@ -94,6 +94,10 @@ MCMD=make
 elif [[ $platform == 'macosx' ]]; then
   QCMD=qmake
   QSPEC=macx-clang
+  arch=`arch`
+  if [[ "$arch" == 'arm64' ]]; then
+    QSPEC=macx-clang-arm64
+  fi  
   # if qmake is not in path, try homebrew qt5
   if ! command -v $QCMD &> /dev/null; then
     # echo "qmake not found in \$PATH, searching for homebrew qt5"
@@ -144,6 +148,9 @@ fi
 mkdir -p builddir
 cd builddir
 
+# exit if build commands fail
+set -e
+
 # Build
 echo "qmake command:"
 echo "$QCMD -spec $QSPEC $CONFIG $PRO_FILE" $UNKNOWN_OPTIONS
index 7acdb37848676c1ee5b0a0d51ccfb1aceaef5792..f44fa5ccddd757c73b9fb636b5d87ec33b3dedc1 100644 (file)
@@ -1,6 +1,6 @@
 # Installation
 ## Linux
-On Linux the easiest way to install JackTrip is to use the distribution's package manager.
+On Linux the easiest way to install JackTrip is to use the distribution's package manager. However, this may not be the most up-to-date version. For the most recent version, go to the Github releases page below.
 
 === "Fedora"
 
index 9935aeb7d28a49764f3695030c2c71311aac8825..1db1e2637c89018656138db02fd321122c860361 100644 (file)
@@ -1,3 +1,19 @@
+- Version: "1.5.2"
+  Date: 2022-03-02
+  Description:
+  - (update) RingBuffer replaced by Regulator for experimental buffer strategy 3
+  - (update) call out old jacktrip versions in package repos on Debian and others
+  - (update) first attempt to support BSDs (especially FreeBSD)
+  - (update) add signing to macOS packaging script
+  - (update) Rename __MANUAL_POLL__ TO MANUAL_POLL
+  - (update) RtAudio warning changes
+  - (update) Use bundled rtaudio for Windows release builds
+  - (update) Incorporate meson build in the main GHA file
+  - (fixed) build script doesn't exit if build fails
+  - (fixed) false error message for JACK 1.9.20 on M1 builds
+  - (fixed) Don't connect our UDP socket
+  - (fixed) Fix GUI command line warning
+  - (fixed) Limiter allocation in GUI
 - Version: "1.5.1"
   Date: 2022-01-07
   Description:
index c811c8056d4f1ef94f08adad41d4cdce609de74d..3bb1cddf0955c962e8ca3888df68fd95de0f1d05 100644 (file)
@@ -198,7 +198,7 @@ HEADERS += src/DataProtocol.h \
            src/Compressor.h \
            src/CompressorPresets.h \
            src/Limiter.h \
-           src/PoolBuffer.h \
+           src/Regulator.h \
            src/Reverb.h \
            src/AudioTester.h \
            src/jacktrip_globals.h \
@@ -242,7 +242,7 @@ SOURCES += src/DataProtocol.cpp \
            src/JackTrip.cpp \
            src/Compressor.cpp \
            src/Limiter.cpp \
-           src/PoolBuffer.cpp \
+           src/Regulator.cpp \
            src/Reverb.cpp \
            src/AudioTester.cpp \
            src/jacktrip_globals.cpp \
index d52a3f80d43eaca33908c59913847cc4b18700c7..aa7c4b47f3eedf1079819dd6d1723cbc618750ee 100755 (executable)
@@ -3,9 +3,11 @@
 APPNAME="JackTrip"
 BUNDLE_ID="org.jacktrip.jacktrip"
 BUILD_INSTALLER=false
+NOTARIZE=false
 
 #If you're lazy like I am, you can pre-populate these variables to save you stuffing about with command line options.
 #CERTIFICATE=""
+#PACKAGE_CERT=""
 #USERNAME=""
 #PASSWORD=""
 #ASC_PROVIDER=""
@@ -13,14 +15,20 @@ BINARY="../builddir/jacktrip"
 
 OPTIND=1
 
-while getopts ":ihc:u:p:a:b:" opt; do
+while getopts ":inhc:d:u:p:a:b:" opt; do
     case $opt in
       i)
         BUILD_INSTALLER=true
         ;;
+      n)
+        NOTARIZE=true
+        ;;
       c)
         CERTIFICATE=$OPTARG
         ;;
+      d)
+        PACKAGE_CERT=$OPTARG
+        ;;
       u)
         USERNAME=$OPTARG
         ;;
@@ -40,17 +48,23 @@ while getopts ":ihc:u:p:a:b:" opt; do
         echo "JackTrip App Bundle assembly script."
         echo "Copyright (C) 2020-2021 Aaron Wyatt et al."
         echo "Relased under the GNU GPLv3 License."
-        echo ""
-        echo "Usage: ./assemble-app.sh [options]"
-        echo ""
+        echo
+        echo "Usage: ./assemble-app.sh [options] [appname] [bundlename]"
+        echo
         echo "Options:"
         echo " -b <filename>      The binary file to be placed in the app bundle. (Defaults to ../builddir/jacktrip)"
         echo " -i                 Build an installer package as well. (Requires Packages to be installed.)"
-        echo " -c <certname>      Name of the developer certificate to use for signing (No signing by default.)"
+        echo " -n                 Send a notarization request to Apple. (Only takes effect if building an installer.)"
+        echo " -c <certname>      Name of the developer certificate to use for code signing. (No signing by default.)"
+        echo " -d <certname>      Name of the certificate to use for package signing. (No signing by default.)"
         echo " -u <username>      Apple ID username (email address) for installer notarization."
         echo " -p <password>      App specific password for installer notarization."
         echo " -a <ascprovider>   ASC provider for notarization. (Only required if you belong to multiple dev teams.)"
         echo " -h                 Display this help screen and exit."
+        echo
+        echo "By default, appname is set to JackTrip and bundlename is org.jacktrip.jacktrip."
+        echo "(These should be left as is for official builds.)"
         exit 0
         ;;
       :)
@@ -107,7 +121,7 @@ if [ ! -z "$DYNAMIC_QT" ]; then
     fi
 fi
 
-[ "$BUILD_INSTALLER" = true ] || exit 0
+[ $BUILD_INSTALLER = true ] || exit 0
 
 # If you have Packages installed, you can build an installer for the newly created app bundle.
 [ -z $(which packagesbuild) ] && { echo "You need to have Packages installed to build a package."; exit 1; }
@@ -128,7 +142,6 @@ cat ../LICENSES/LGPL-3.0-only.txt >> "$LICENSE_PATH"
 sed -i '' "s/# //" "$LICENSE_PATH" # remove markdown header
 perl -ane 'chop;print "\n\n" if(/^\s*$/); map{print "$_ ";}@F;' "$LICENSE_PATH" > tmp && mv tmp "$LICENSE_PATH" # unwrap lines
 
-
 # prepare readme
 README_PATH="package/readme.txt"
 cp ../README.md "$README_PATH"
@@ -141,21 +154,61 @@ sed -i '' "s/%BUNDLENAME%/$APPNAME/" package/JackTrip.pkgproj
 sed -i '' "s/%BUNDLEID%/$BUNDLE_ID/" package/JackTrip.pkgproj
 
 packagesbuild package/JackTrip.pkgproj
+if pkgutil --check-signature package/build/JackTrip.pkg; then
+    echo "Package already signed."
+    SIGNED=true
+else
+    if [ ! -z "$PACKAGE_CERT" ]; then
+        echo "Signing package."
+        if productsign --sign "$PACKAGE_CERT" package/build/JackTrip.pkg package/build/JackTrip-signed.pkg; then
+            mv package/build/JackTrip-signed.pkg package/build/JackTrip.pkg
+            SIGNED=true
+        else
+            echo "Unable to sign package."
+            exit 1
+        fi
+    else
+        SIGNED=false
+    fi
+fi 
+
+[ $NOTARIZE = true ] || exit 0
 
-# Offer to submit a notarization request to apple if we have the required credentials.
-if [ -z "$CERTIFICATE" ] || [ -z "$USERNAME" ] || [ -z "$PASSWORD" ]; then
+# Submit a notarization request to apple if we have the required credentials.
+if [ -z "$CERTIFICATE" ] || [ -z "$USERNAME" ] || [ -z "$PASSWORD" ] || [ $SIGNED = false ] ; then
     echo "Not sending notarization request: incomplete credentials."
-    exit 0
+    exit 1
 fi
 
 ASC=""
 if [ ! -z "$ASC_PROVIDER" ]; then
-    ASC=" --asc-provider \"$ASC_PROVIDER\""
+    ASC=" --asc-provider $ASC_PROVIDER"
 fi
+CREDENTIALS="--username $USERNAME --password $PASSWORD$ASC"
 
-read -n1 -rsp "Press any key to submit a notarization request to apple..."
-echo
-xcrun altool --notarize-app --primary-bundle-id "$BUNDLE_ID" --username "$USERNAME" --password "$PASSWORD"$ASC --file "package/build/$APPNAME.pkg"
-read -n1 -rsp "Press any key to staple the notarization once it's been approved..."
-echo
-xcrun stapler staple "package/build/$APPNAME.pkg"
+echo "Sending notarization request"
+UUID=$(xcrun altool --notarize-app --primary-bundle-id "$BUNDLE_ID" $CREDENTIALS --file "package/build/$APPNAME.pkg" | awk '/RequestUUID/{print $NF}')
+if [ -z "$UUID" ]; then
+    echo "Error sending notarization request"
+    exit 1
+fi
+echo "Package uploaded"
+echo "Request UUID is $UUID"
+echo "Awaiting response from Apple"
+STATUS="in progress"
+ELAPSED=0
+while [ "$STATUS" = "in progress" ]; do
+    sleep 60
+    ((ELAPSED++))
+    STATUS=$(xcrun altool --notarization-info "$UUID" $CREDENTIALS | awk '/Status:/{for(i = 2; i <= NF - 1; i++) printf $i" "; print $NF}')
+    echo "Waited $ELAPSED minute(s). Current status: $STATUS"
+done
+#read -n1 -rsp "Press any key to staple the notarization once it's been approved..."
+#echo
+
+if [ "$STATUS" = "success" ]; then
+    xcrun stapler staple "package/build/$APPNAME.pkg"
+else
+    echo "Notarization failed"
+    exit 1
+fi
index 3de6dad223ab57c475f4fd969802fcd881911685..4293f568755d6eafd55be54fa8318822e71ea6c4 100644 (file)
@@ -27,7 +27,7 @@ src = [       'src/DataProtocol.cpp',
        'src/PacketHeader.cpp',
        'src/RingBuffer.cpp',
        'src/JitterBuffer.cpp',
-       'src/PoolBuffer.cpp',
+       'src/Regulator.cpp',
        'src/Settings.cpp',
        'src/UdpDataProtocol.cpp',
        'src/UdpHubListener.cpp',
index 661a87ba88dcc30fec70aa6a1cfef2ced89ff303..11df4a93a61b6ce7222645609b335e6827ca4a8a 100644 (file)
@@ -42,7 +42,7 @@
 #endif
 #include "Auth.h"
 #include "JitterBuffer.h"
-#include "PoolBuffer.h"
+#include "Regulator.h"
 #include "RingBufferWavetable.h"
 #include "UdpDataProtocol.h"
 #include "jacktrip_globals.h"
 #include <QTimer>
 #include <QtEndian>
 #include <cstdlib>
+#include <iomanip>
 #include <iostream>
 #include <stdexcept>
+using std::setw;
 
 using std::cout;
 using std::endl;
@@ -379,11 +381,9 @@ void JackTrip::setupRingBuffers()
             mSendRingBuffer =
                 new RingBuffer(audio_input_slot_size, gDefaultOutputQueueLength);
             mReceiveRingBuffer =
-                new PoolBuffer(mSampleRate, mNumAudioChansIn, mAudioBitResolution,
-                               mAudioBufferSize, mBufferQueueLength);
-            //            connect(mReceiveRingBuffer, SIGNAL(print(QString)), this,
-            //            SLOT(onStatTimer(QString))); connect(mReceiveRingBuffer,
-            //            SIGNAL(printStats(QString)), this, SLOT(onStatTimer(QString)));
+                new Regulator(mSampleRate, mNumAudioChansIn, mAudioBitResolution,
+                              mAudioBufferSize, mBufferQueueLength);
+            // bufStrategy 3, mBufferQueueLength is in integer msec not packets
 
             mPacketHeader->setBufferRequiresSameSettings(true);
         } else {
@@ -629,22 +629,72 @@ void JackTrip::onStatTimer()
     if (!mAudioTesterP.isNull() && mAudioTesterP->getEnabled()) {
         mIOStatLogStream << "\n";
     }
-    mIOStatLogStream << now.toLocal8Bit().constData() << " "
-                     << getPeerAddress().toLocal8Bit().constData()
-                     << " send: " << send_io_stat.underruns << "/"
-                     << send_io_stat.overflows << " recv: " << recv_io_stat.underruns
-                     << "/" << recv_io_stat.overflows << " prot: " << pkt_stat.lost << "/"
-                     << pkt_stat.outOfOrder << "/" << pkt_stat.revived
-                     << " tot: " << pkt_stat.tot << " 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;
+    if (getBufferStrategy() != 3)
+        mIOStatLogStream << now.toLocal8Bit().constData() << " "
+                         << getPeerAddress().toLocal8Bit().constData()
+                         << " send: " << send_io_stat.underruns << "/"
+                         << send_io_stat.overflows << " recv: " << recv_io_stat.underruns
+                         << "/" << recv_io_stat.overflows << " prot: " << pkt_stat.lost
+                         << "/" << pkt_stat.outOfOrder << "/" << pkt_stat.revived
+                         << " tot: " << pkt_stat.tot << " 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;
+    else {  // bufstrategy 3
+        mIOStatLogStream
+            << now.toLocal8Bit().constData() << " "
+            << getPeerAddress().toLocal8Bit().constData()
+            << " send: " << send_io_stat.underruns << "/" << send_io_stat.overflows
+            << " Pull underruns: "
+            << recv_io_stat.underruns  // pullStat->lastPlcUnderruns;
+#define INVFLOATFACTOR 0.001
+            << "\nPUSH -- SD avg/last: " << setw(5)
+            << INVFLOATFACTOR * recv_io_stat.overflows  // pushStat->longTermStdDev;
+            << " / " << setw(5)
+            << INVFLOATFACTOR * recv_io_stat.buf_dec_overflows  // pushStat->lastStdDev;
+            << " \t mean/min/max: " << setw(5)
+            << INVFLOATFACTOR * recv_io_stat.skew  // pushStat->lastMean;
+            << " / " << setw(5)
+            << INVFLOATFACTOR * recv_io_stat.skew_raw  // pushStat->lastMin;
+            << " / " << setw(5)
+            << INVFLOATFACTOR * recv_io_stat.level  // pushStat->lastMax;
+
+            << "\nPULL -- SD avg/last: " << setw(5)
+            << INVFLOATFACTOR * recv_io_stat.buf_dec_pktloss  // pullStat->longTermStdDev;
+            << " / " << setw(5)
+            << INVFLOATFACTOR * recv_io_stat.broadcast_delta  // pullStat->lastStdDev;
+            << " \t mean/min/max: " << setw(5)
+            << INVFLOATFACTOR * recv_io_stat.buf_inc_underrun  // pullStat->lastMean;
+            << " / " << setw(5)
+            << INVFLOATFACTOR * recv_io_stat.buf_inc_compensate  // pullStat->lastMin;
+            << " / " << setw(5)
+            << INVFLOATFACTOR * recv_io_stat.broadcast_skew  // pullStat->lastMax;
+
+            //                     << "/" << recv_io_stat.overflows << " prot: " <<
+            //                     pkt_stat.lost << "/"
+            //                     << pkt_stat.outOfOrder << "/" << pkt_stat.revived
+            << " \n tot: "
+            << pkt_stat.tot
+            //                     << " 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
+            << "\n"
+            << endl;
+    }
 }
 
 void JackTrip::receivedConnectionTCP()
@@ -985,7 +1035,9 @@ void JackTrip::tcpTimerTick()
 //*******************************************************************************
 void JackTrip::stop(const QString& errorMessage)
 {
-    mStopped = true;
+    // Take a snapshot of sJackStopped
+    bool serverStopped = sJackStopped;
+    mStopped           = true;
     // Make sure we're only run once
     if (mHasShutdown) {
         return;
@@ -1009,7 +1061,7 @@ void JackTrip::stop(const QString& errorMessage)
     cout << gPrintSeparator << endl;
 
     // Emit the jack stopped signal
-    if (sJackStopped) {
+    if (serverStopped) {
         emit signalError(QStringLiteral("The Jack Server was shut down!"));
     } else if (errorMessage.isEmpty()) {
         emit signalProcessesStopped();
index d2bde18070d4fe4b5d386691edef8c5701223608..0c2ec258a427b190962ff2ddc7358abf190188df 100644 (file)
@@ -306,6 +306,7 @@ class JackTrip : public QObject
     {
         mDataProtocolReceiver = DataProtocolReceiver;
     }
+    virtual int getBufferStrategy() const noexcept { return mBufferStrategy; }
 
     virtual RingBuffer* getSendRingBuffer() const { return mSendRingBuffer; }
     virtual RingBuffer* getReceiveRingBuffer() const { return mReceiveRingBuffer; }
index 6653e01d1d41a35589f0d02ac45d55c083414d7f..cbd3bdf2dc7b21350a6098b767daf4f59305dc15 100644 (file)
@@ -37,7 +37,7 @@
 
 #include "PacketHeader.h"
 
-#if defined(__linux__) || defined(__APPLE__)
+#ifndef _WIN32
 #include <sys/time.h>
 #endif
 
diff --git a/src/PoolBuffer.cpp b/src/PoolBuffer.cpp
deleted file mode 100644 (file)
index 343c284..0000000
+++ /dev/null
@@ -1,522 +0,0 @@
-//*****************************************************************
-/*
-  JackTrip: A System for High-Quality Audio Network Performance
-  over the Internet
-
-  Copyright (c) 2021 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 PoolBuffer.cpp
- * \author Chris Chafe
- * \date May 2021
- */
-
-// EXPERIMENTAL for testing in JackTrip v1.4.0
-// runs ok from FPP 16 up to 512, but don't try 1024 yet
-// in / out channels are the same -- mono, stereo and -n3 tested fine
-
-// for example, server
-// jacktrip -S --udprt  -p1 --bufstrategy 3 -q2
-// and client
-// jacktrip -C cmn9.stanford.edu --udprt --bufstrategy 3 -q2
-
-#include "PoolBuffer.h"
-
-#include <iomanip>
-
-#include "jacktrip_globals.h"
-using std::cout;
-using std::endl;
-using std::setw;
-
-#define STDDEVINDOW     200  // packets
-#define STDDEV2POOLSIZE 30.0
-#define MAXPOOLSIZE     6000  // insanely large pool, 2 sec FPP16
-#define HIST            6     // at FPP32
-#define OUT(x, ch, s)   sampleToBits(x, ch, s)
-// from listening tests iteration performs better than '=' operator
-#define PACKETSAMP (int s = 0; s < mFPP; s++)
-
-//*******************************************************************************
-PoolBuffer::PoolBuffer(int /*sample_rate*/, int channels, int bit_res, int FPP, int qLen)
-    : RingBuffer(0, 0)
-    , mNumChannels(channels)
-    , mAudioBitRes(bit_res)
-    , mFPP(FPP)
-    , mPoolSize(qLen)
-    , mQlen(qLen)
-{
-    switch (mAudioBitRes) {  // int from JitterBuffer to AudioInterface enum
-    case 1:
-        mBitResolutionMode = AudioInterface::audioBitResolutionT::BIT8;
-        break;
-    case 2:
-        mBitResolutionMode = AudioInterface::audioBitResolutionT::BIT16;
-        break;
-    case 3:
-        mBitResolutionMode = AudioInterface::audioBitResolutionT::BIT24;
-        break;
-    case 4:
-        mBitResolutionMode = AudioInterface::audioBitResolutionT::BIT32;
-        break;
-    }
-    mHist            = 6 * 32;                // samples, from original settings
-    double histFloat = mHist / (double)mFPP;  // packets for other FPP
-    mHist            = (int)histFloat;
-    if (!mHist)
-        mHist++;  // min packets
-    else if (mHist > 6)
-        mHist = 6;  // max packets
-    if (gVerboseFlag)
-        cout << "mHist = " << mHist << " at " << mFPP << "\n";
-    //    qDebug() << "mHist =" << mHist << "@" << mFPP;
-    mBytes     = mFPP * mNumChannels * mBitResolutionMode;
-    mXfrBuffer = new int8_t[mBytes];
-    mPacketCnt = 0;  // burg
-    mFadeUp.resize(mFPP, 0.0);
-    mFadeDown.resize(mFPP, 0.0);
-    for (int i = 0; i < mFPP; i++) {
-        mFadeUp[i]   = (double)i / (double)mFPP;
-        mFadeDown[i] = 1.0 - mFadeUp[i];
-    }
-    mLastWasGlitch = false;
-    mOutgoingCnt   = 0;
-    for (int i = 0; i < mPoolSize; i++) {
-        int8_t* tmp = new int8_t[mBytes];
-        mIncomingDat.push_back(tmp);
-    }
-    mZeros = new int8_t[mBytes];
-    for
-        PACKETSAMP OUT(0.0, 0, s);
-    for
-        PACKETSAMP OUT(0.0, 1, s);
-    memcpy(mZeros, mXfrBuffer, mBytes);
-    mIncomingCnt = 0;
-    mIndexPool.resize(mPoolSize);
-    for (int i = 0; i < mPoolSize; i++)
-        mIndexPool[i] = -1;
-    mTimer0 = new QElapsedTimer();
-    mTimer0->start();
-    mGlitchCnt = 0;
-    mGlitchMax = mHist * 2 * mFPP;  // tested with 400 @ FPP32
-    for (int i = 0; i < mNumChannels; i++) {
-        ChanData* tmp = new ChanData(i, mFPP, mHist);
-        mChanData.push_back(tmp);
-    }
-    stdDev = new StdDev(STDDEVINDOW);
-}
-
-//*******************************************************************************
-bool PoolBuffer::pushPacket(const int8_t* buf)
-{
-    QMutexLocker locker(&mMutex);
-
-    mIncomingCnt++;
-    if (mGlitchCnt > mGlitchMax) {
-        //        double elapsed0 = (double)mTimer0->nsecsElapsed() / 1000000.0;
-        //        qDebug() << mGlitchCnt << mIncomingCnt << mOutgoingCnt
-        //                 << elapsed0/1000.0 << "\n";
-        mIncomingCnt = mOutgoingCnt;
-        mGlitchCnt   = 0;
-    }
-    int oldest      = 214748364;  // not so BIGNUM, approx. 2^31-1 / 10
-    int oldestIndex = 0;
-    for (int i = 0; i < mPoolSize; i++) {
-        if (mIndexPool[i] < oldest) {
-            oldest      = mIndexPool[i];
-            oldestIndex = i;
-        }
-    }
-    mIndexPool[oldestIndex] = mIncomingCnt;
-    memcpy(mIncomingDat[oldestIndex], buf, mBytes);
-    //        qDebug() << oldestIndex << mIndexPool[oldestIndex];
-
-    stdDev->tick();
-    if (stdDev->longTermStdDevAcc > 0.0) {
-        double FPPfactor = 0.25 + (32 / (double)mFPP);
-        int newPoolSize  = (int)(stdDev->longTermStdDev * STDDEV2POOLSIZE * FPPfactor);
-        if (newPoolSize > mPoolSize) {
-            if (newPoolSize > MAXPOOLSIZE)
-                newPoolSize = MAXPOOLSIZE;  // avoid insanely large pool
-            mIndexPool.resize(newPoolSize);
-            for (int i = mPoolSize; i < newPoolSize; i++) {
-                int8_t* tmp = new int8_t[mBytes];
-                mIncomingDat.push_back(tmp);
-            }
-            if (gVerboseFlag)
-                cout << "growing to " << newPoolSize << " from " << mPoolSize << "\n";
-            mPoolSize = newPoolSize;
-        }
-        if (newPoolSize != mPoolSize) {
-            if (newPoolSize < mQlen)
-                newPoolSize = mQlen;  // avoid insanely small pool
-            if (gVerboseFlag)
-                cout << "shrinking to " << newPoolSize << " from " << mPoolSize << "\n";
-            mPoolSize = newPoolSize;
-        }
-        //            qDebug() << (int) (stdDev->longTermStdDev*30.0);
-    }
-    return true;
-};
-
-//*******************************************************************************
-void PoolBuffer::pullPacket(int8_t* buf)
-{
-    QMutexLocker locker(&mMutex);
-    mOutgoingCnt++;  // will saturate in 33 days at FPP 32
-    //    (/ (* (- (expt 2 32) 1) (/ 32 48000.0)) (* 60 60 24))
-    bool glitch     = false;
-    int target      = mOutgoingCnt - mQlen;
-    int targetIndex = mPoolSize;
-    int oldest      = 999999;
-    int oldestIndex = 0;
-    for (int i = 0; i < mPoolSize; i++) {
-        if (mIndexPool[i] == target) {
-            targetIndex = i;
-        }
-        if (mIndexPool[i] < oldest) {
-            oldest      = mIndexPool[i];
-            oldestIndex = i;
-        }
-    }
-    if (targetIndex == mPoolSize) {
-        //            qDebug() << " ";
-        //            qDebug() << "!available" << target;
-        //            for ( int i = 0; i < POOLSIZE; i++ ) qDebug() << i << mIndexPool[i];
-        //            qDebug() << " ";
-        targetIndex             = oldestIndex;
-        mIndexPool[targetIndex] = -1;
-        glitch                  = true;
-        mGlitchCnt++;
-        //            QThread::usleep(450); force lateness for debugging
-    } else {
-        mIndexPool[targetIndex] = 0;
-        memcpy(mXfrBuffer, mIncomingDat[targetIndex], mBytes);
-    }
-    if (mIncomingCnt) {
-        processPacket(glitch);
-    } else {
-        memcpy(mXfrBuffer, mZeros, mBytes);
-    }
-    memcpy(buf, mXfrBuffer, mBytes);
-};
-
-//*******************************************************************************
-void PoolBuffer::processPacket(bool glitch)
-{
-    for (int ch = 0; ch < mNumChannels; ch++)
-        processChannel(ch, glitch, mPacketCnt, mLastWasGlitch);
-    mLastWasGlitch = glitch;
-    mPacketCnt++;
-}
-
-//*******************************************************************************
-void PoolBuffer::processChannel(int ch, bool glitch, int packetCnt, bool lastWasGlitch)
-{
-    //    if(glitch) qDebug() << "glitch"; else fprintf(stderr,".");
-
-    ChanData* cd = mChanData[ch];
-    for
-        PACKETSAMP cd->mTruth[s] = bitsToSample(ch, s);
-    if (packetCnt) {
-        for (int i = 0; i < mHist; i++) {
-            for
-                PACKETSAMP cd->mTrain[s + ((mHist - (i + 1)) * mFPP)] =
-                    cd->mLastPackets[i][s];
-        }
-
-        // GET LINEAR PREDICTION COEFFICIENTS
-        ba.train(cd->mCoeffs, cd->mTrain);
-
-        // LINEAR PREDICT DATA
-        vector<sample_t> tail(cd->mTrain);
-
-        ba.predict(cd->mCoeffs, tail);  // resizes to TRAINSAMPS-2 + TRAINSAMPS
-
-        for (int i = 0; i < (cd->trainSamps - 1); i++)
-            cd->mPrediction[i] = tail[i + cd->trainSamps];
-
-        for
-            PACKETSAMP cd->mXfadedPred[s] =
-                cd->mTruth[s] * mFadeUp[s] + cd->mNextPred[s] * mFadeDown[s];
-
-        for
-            PACKETSAMP
-        OUT((glitch) ? cd->mPrediction[s]
-                     : ((lastWasGlitch) ? cd->mXfadedPred[s] : cd->mTruth[s]),
-            ch, s);
-
-        for
-            PACKETSAMP cd->mNextPred[s] = cd->mPrediction[s + mFPP];
-    }
-
-    // if mPacketCnt==0 initialization follows
-    for (int i = mHist - 1; i > 0; i--) {
-        for
-            PACKETSAMP cd->mLastPackets[i][s] = cd->mLastPackets[i - 1][s];
-    }
-
-    // will only be able to glitch if mPacketCnt>0
-    for
-        PACKETSAMP cd->mLastPackets[0][s] =
-            ((!glitch) || (packetCnt < mHist)) ? cd->mTruth[s] : cd->mPrediction[s];
-}
-
-//*******************************************************************************
-// copped from AudioInterface.cpp
-
-sample_t PoolBuffer::bitsToSample(int ch, int frame)
-{
-    sample_t sample = 0.0;
-    AudioInterface::fromBitToSampleConversion(
-        &mXfrBuffer[(frame * mBitResolutionMode * mNumChannels)
-                    + (ch * mBitResolutionMode)],
-        &sample, mBitResolutionMode);
-    return sample;
-}
-
-void PoolBuffer::sampleToBits(sample_t sample, int ch, int frame)
-{
-    AudioInterface::fromSampleToBitConversion(
-        &sample,
-        &mXfrBuffer[(frame * mBitResolutionMode * mNumChannels)
-                    + (ch * mBitResolutionMode)],
-        mBitResolutionMode);
-}
-
-//*******************************************************************************
-bool BurgAlgorithm::classify(double d)
-{
-    bool tmp = false;
-    switch (fpclassify(d)) {
-    case FP_INFINITE:
-        qDebug() << ("infinite");
-        tmp = true;
-        break;
-    case FP_NAN:
-        qDebug() << ("NaN");
-        tmp = true;
-        break;
-    case FP_ZERO:
-        //      qDebug() <<  ("zero");
-        tmp = true;
-        break;
-    case FP_SUBNORMAL:
-        qDebug() << ("subnormal");
-        tmp = true;
-        break;
-        //    case FP_NORMAL:    qDebug() <<  ("normal");    break;
-    }
-    //  if (signbit(d)) qDebug() <<  (" negative\n"); else qDebug() <<  (" positive or
-    //  unsigned\n");
-    return tmp;
-}
-
-void BurgAlgorithm::train(vector<long double>& coeffs, const vector<float>& x)
-{
-    // GET SIZE FROM INPUT VECTORS
-    size_t N = x.size() - 1;
-    size_t m = coeffs.size();
-
-    //        if (x.size() < m) qDebug() << "time_series should have more elements than
-    //        the AR order is";
-
-    // INITIALIZE Ak
-    vector<long double> Ak(m + 1, 0.0);
-    Ak[0] = 1.0;
-
-    // INITIALIZE f and b
-    vector<long double> f;
-    f.resize(x.size());
-    for (unsigned int i = 0; i < x.size(); i++)
-        f[i] = x[i];
-    vector<long double> b(f);
-
-    // INITIALIZE Dk
-    long double Dk = 0.0;
-    for (size_t j = 0; j <= N; j++)  // CC: N is $#x-1 in C++ but $#x in perl
-    {
-        Dk += 2.00001 * f[j] * f[j];  // CC: needs more damping than orig 2.0
-    }
-    Dk -= f[0] * f[0] + b[N] * b[N];
-
-    //    qDebug() << "Dk" << qStringFromLongDouble1(Dk);
-    //        if ( classify(Dk) )
-    //        { qDebug() << pCnt << "init";
-    //        }
-
-    // BURG RECURSION
-    for (size_t k = 0; k < m; k++) {
-        // COMPUTE MU
-        long double mu = 0.0;
-        for (size_t n = 0; n <= N - k - 1; n++) {
-            mu += f[n + k + 1] * b[n];
-        }
-
-        if (Dk == 0.0)
-            Dk = 0.0000001;  // CC: from testing, needs eps
-        //            if ( classify(Dk) ) qDebug() << pCnt << "run";
-
-        mu *= -2.0 / Dk;
-        //            if ( isnan(Dk) )  { qDebug() << "k" << k; }
-        //            if (Dk==0.0) qDebug() << "k" << k << "Dk==0";
-
-        // UPDATE Ak
-        for (size_t n = 0; n <= (k + 1) / 2; n++) {
-            long double t1 = Ak[n] + mu * Ak[k + 1 - n];
-            long double t2 = Ak[k + 1 - n] + mu * Ak[n];
-            Ak[n]          = t1;
-            Ak[k + 1 - n]  = t2;
-        }
-
-        // UPDATE f and b
-        for (size_t n = 0; n <= N - k - 1; n++) {
-            long double t1 = f[n + k + 1] + mu * b[n];  // were double
-            long double t2 = b[n] + mu * f[n + k + 1];
-            f[n + k + 1]   = t1;
-            b[n]           = t2;
-        }
-
-        // UPDATE Dk
-        Dk = (1.0 - mu * mu) * Dk - f[k + 1] * f[k + 1] - b[N - k - 1] * b[N - k - 1];
-    }
-    // ASSIGN COEFFICIENTS
-    coeffs.assign(++Ak.begin(), Ak.end());
-}
-
-void BurgAlgorithm::predict(vector<long double>& coeffs, vector<float>& tail)
-{
-    size_t m = coeffs.size();
-    //    qDebug() << "tail.at(0)" << tail[0]*32768;
-    //    qDebug() << "tail.at(1)" << tail[1]*32768;
-    tail.resize(m + tail.size());
-    //    qDebug() << "tail.at(m)" << tail[m]*32768;
-    //    qDebug() << "tail.at(...end...)" << tail[tail.size()-1]*32768;
-    //    qDebug() << "m" << m << "tail.size()" << tail.size();
-    for (size_t i = m; i < tail.size(); i++) {
-        tail[i] = 0.0;
-        for (size_t j = 0; j < m; j++) {
-            tail[i] -= coeffs[j] * tail[i - 1 - j];
-        }
-    }
-}
-
-//*******************************************************************************
-ChanData::ChanData(int i, int FPP, int hist) : ch(i)
-{
-    trainSamps = (hist * FPP);
-    mTruth.resize(FPP, 0.0);
-    mXfadedPred.resize(FPP, 0.0);
-    mNextPred.resize(FPP, 0.0);
-    for (int i = 0; i < hist; i++) {
-        vector<sample_t> tmp(FPP, 0.0);
-        mLastPackets.push_back(tmp);
-    }
-    mTrain.resize(trainSamps, 0.0);
-    mPrediction.resize(trainSamps - 1, 0.0);  // ORDER
-    mCoeffs.resize(trainSamps - 2, 0.0);
-}
-
-//*******************************************************************************
-StdDev::StdDev(int w) : window(w)
-{
-    reset();
-    longTermStdDev    = 0.0;
-    longTermStdDevAcc = 0.0;
-    longTermCnt       = 0;
-    lastMean          = 0.0;
-    lastMin           = 0;
-    lastMax           = 0;
-    mTimer            = new QElapsedTimer();
-    mTimer->start();
-    data.resize(w, 0.0);
-}
-
-void StdDev::reset()
-{
-    mean = 0.0;
-    //        varRunning = 0.0;
-    acc = 0.0;
-    min = 999999.0;
-    max = 0.0;
-    ctr = 0;
-};
-
-void StdDev::tick()
-{  // stdDev based on mean of last windowful
-    double msElapsed = (double)mTimer->nsecsElapsed() / 1000000.0;
-    mTimer->start();
-    if (ctr != window) {
-        data[ctr] = msElapsed;
-        if (msElapsed < min)
-            min = msElapsed;
-        else if (msElapsed > max)
-            max = msElapsed;
-        acc += msElapsed;
-        //        double tmp = msElapsed - mean; // last window
-        //        varRunning += (tmp*tmp);
-        ctr++;
-    } else {
-        mean       = (double)acc / (double)window;
-        double var = 0.0;
-        for (int i = 0; i < window; i++) {
-            double tmp = data[i] - mean;
-            var += (tmp * tmp);
-        }
-        //        varRunning /= (double) window;
-        //        stdDev = sqrt(varRunning);
-        //        qDebug() << mean << min << max << stdDev;
-        var /= (double)window;
-        double stdDev = sqrt(var);
-        if (longTermCnt) {
-            longTermStdDevAcc += stdDev;
-            longTermStdDev = longTermStdDevAcc / (double)longTermCnt;
-            //            qDebug() << mean << min << max << stdDev << longTermStdDev;
-            if (gVerboseFlag)
-                cout << setw(10) << mean << setw(10) << min << setw(10) << max << setw(10)
-                     << stdDev << setw(10) << longTermStdDev << endl;
-        } else if (gVerboseFlag)
-            cout << "printing from PoolBuffer->stdDev->tick:\n (mean / min / max / "
-                    "stdDev / longTermStdDev) \n";
-
-        longTermCnt++;
-        //            QString out;
-        //            out += (QString::number(msNow) + QString("\t"));
-        //            out += (QString::number(mean) + QString("\t"));
-        //            out += (QString::number(min) + QString("\t"));
-        //            out += (QString::number(max) + QString("\t"));
-        //            out += (QString::number(stdDev) + QString("\t"));
-        //                        emit printStats(out);
-        // build-jacktrip-Desktop-Release/jacktrip -C cmn9.stanford.edu --bufstrategy 3 -I
-        // 1 -G /tmp/iostat.log plot 'iostat.log' u  1:2 w l, 'iostat.log' u  1:3 w l,
-        // 'iostat.log' u  1:4 w l, 'iostat.log' u  1:5 w l,
-        lastMean = mean;
-        lastMin  = min;
-        lastMax  = max;
-        reset();
-    }
-}
diff --git a/src/PoolBuffer.h b/src/PoolBuffer.h
deleted file mode 100644 (file)
index b7c4d7a..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-//*****************************************************************
-/*
-  JackTrip: A System for High-Quality Audio Network Performance
-  over the Internet
-
-  Copyright (c) 2021 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 PoolBuffer.h
- * \author Chris Chafe
- * \date May 2021
- */
-
-// EXPERIMENTAL for testing in JackTrip v1.4.0
-// Initial references and starter code
-// http://www.emptyloop.com/technotes/A%20tutorial%20on%20Burg's%20method,%20algorithm%20and%20recursion.pdf
-// https://metacpan.org/source/SYP/Algorithm-Burg-0.001/README
-
-#ifndef __POOLUFFER_H__
-#define __POOLUFFER_H__
-
-#include <QDebug>
-#include <QElapsedTimer>
-
-#include "AudioInterface.h"
-#include "RingBuffer.h"
-using std::vector;
-#include <math.h>
-
-class BurgAlgorithm
-{
-   public:
-    bool classify(double d);
-    void train(vector<long double>& coeffs, const vector<float>& x);
-    void predict(vector<long double>& coeffs, vector<float>& tail);
-};
-
-class ChanData
-{
-   public:
-    ChanData(int i, int FPP, int hist);
-    int ch;
-    int trainSamps;
-    vector<sample_t> mTruth;
-    vector<sample_t> mTrain;
-    vector<sample_t> mPrediction;  // ORDER
-    vector<long double> mCoeffs;
-    vector<sample_t> mXfadedPred;
-    vector<sample_t> mNextPred;
-    vector<vector<sample_t>> mLastPackets;
-};
-
-class StdDev
-{
-   public:
-    StdDev(int w);
-    void reset();
-    void tick();
-    QElapsedTimer* mTimer;
-    vector<double> data;
-    double mean;
-    double var;
-    //    double varRunning;
-    int window;
-    double acc;
-    double min;
-    double max;
-    int ctr;
-    double lastMean;
-    double lastMin;
-    double lastMax;
-    double longTermStdDev;
-    double longTermStdDevAcc;
-    int longTermCnt;
-};
-
-class PoolBuffer : public RingBuffer
-{
-    //    Q_OBJECT;
-
-   public:
-    PoolBuffer(int sample_rate, int channels, int bit_res, int FPP, int qLen);
-    virtual ~PoolBuffer() {}
-
-    bool pushPacket(const int8_t* buf);
-    // can hijack unused2 to propagate incoming seq num if needed
-    // option is in UdpDataProtocol
-    // if (!mJackTrip->writeAudioBuffer(src, host_buf_size, last_seq_num))
-    // instread of
-    // if (!mJackTrip->writeAudioBuffer(src, host_buf_size, gap_size))
-    virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot,
-                                       [[maybe_unused]] int unused,
-                                       [[maybe_unused]] int unused2)
-    {
-        pushPacket(ptrToSlot);
-        return (true);
-    }
-
-    void pullPacket(int8_t* buf);
-
-    virtual void readSlotNonBlocking(int8_t* ptrToReadSlot) { pullPacket(ptrToReadSlot); }
-
-   private:
-    void processPacket(bool glitch);
-    void processChannel(int ch, bool glitch, int packetCnt, bool lastWasGlitch);
-    int mNumChannels;
-    int mAudioBitRes;
-    int mFPP;
-
-    int mPoolSize;
-    int mHist;
-    AudioInterface::audioBitResolutionT mBitResolutionMode;
-    BurgAlgorithm ba;
-    int8_t* mXfrBuffer;
-    int mPacketCnt;
-    sample_t bitsToSample(int ch, int frame);
-    void sampleToBits(sample_t sample, int ch, int frame);
-    vector<sample_t> mFadeUp;
-    vector<sample_t> mFadeDown;
-    bool mLastWasGlitch;
-    unsigned int mOutgoingCnt;
-    int mBytes;
-    vector<int8_t*> mIncomingDat;
-    int8_t* mZeros;
-    QElapsedTimer* mTimer0;
-    unsigned int mIncomingCnt;
-    vector<int> mIndexPool;
-    int mQlen;
-    int mGlitchCnt;
-    int mGlitchMax;
-    vector<ChanData*> mChanData;
-    StdDev* stdDev;
-};
-
-#endif  //__POOLUFFER_H__
diff --git a/src/Regulator.cpp b/src/Regulator.cpp
new file mode 100644 (file)
index 0000000..1eb8d84
--- /dev/null
@@ -0,0 +1,658 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2021 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 Regulator.cpp
+ * \author Chris Chafe
+ * \date May-Sep 2021
+ */
+
+// EXPERIMENTAL for testing in JackTrip v1.5.0
+// requires server and client have same FPP
+// runs ok from FPP 16 up to 1024
+// number of in / out channels should be the same
+// mono, stereo and -n3 tested fine
+
+// ./jacktrip -S --udprt -p1 --bufstrategy 3  -I 1 -q10
+// PIPEWIRE_LATENCY=32/48000 ./jacktrip -C <SERV> --udprt --bufstrategy 3 -I 1 -q4
+
+// at 48000 / 32 = 2.667 ms total roundtrip latency
+// local loopback test with 4 terminals running and the following jmess file
+// jacktrip -S --udprt --nojackportsconnect -q1 --bufstrategy 3
+// jacktrip -C localhost --udprt --nojackportsconnect -q1  --bufstrategy 3
+// use jack_iodelay
+// use jmess -s delay.xml and jmess -c delay.xml
+
+// tested outgoing loss impairments with
+// sudo tc qdisc add dev lo root netem loss 2%
+// sudo tc qdisc del dev lo root netem loss 2%
+// tested jitter impairments with
+// for wifi
+// sudo tc qdisc add dev lo root netem slot distribution pareto 0.1ms 3.0ms
+// sudo tc qdisc del dev lo root netem slot distribution pareto 0.1ms 3.0ms
+// for wired cmn9
+// sudo tc qdisc add dev lo root netem slot distribution pareto 0.2ms 0.3ms
+// sudo tc qdisc del dev lo root netem slot distribution pareto 0.2ms 0.3ms
+
+#include "Regulator.h"
+
+#include <iomanip>
+#include <sstream>
+
+#include "jacktrip_globals.h"
+using std::cout;
+using std::endl;
+using std::setw;
+
+// constants... tested for now
+constexpr int HIST          = 6;    // at FPP32
+constexpr int ModSeqNumInit = 256;  // bounds on seqnums, 65536 is max in packet header
+constexpr int NumSlotsMax   = 128;  // mNumSlots looped for recent arrivals
+constexpr int LostWindowMax = 32;   // mLostWindow looped for recent arrivals
+//*******************************************************************************
+Regulator::Regulator(int sample_rate, int channels, int bit_res, int FPP, int qLen)
+    : RingBuffer(0, 0)
+    , mNumChannels(channels)
+    , mAudioBitRes(bit_res)
+    , mFPP(FPP)
+    , mSampleRate(sample_rate)
+    , mMsecTolerance((double)qLen)
+{
+    switch (mAudioBitRes) {  // int from JitterBuffer to AudioInterface enum
+    case 1:
+        mBitResolutionMode = AudioInterface::audioBitResolutionT::BIT8;
+        break;
+    case 2:
+        mBitResolutionMode = AudioInterface::audioBitResolutionT::BIT16;
+        break;
+    case 3:
+        mBitResolutionMode = AudioInterface::audioBitResolutionT::BIT24;
+        break;
+    case 4:
+        mBitResolutionMode = AudioInterface::audioBitResolutionT::BIT32;
+        break;
+    }
+    mHist            = HIST * 32;             // samples, from original settings
+    double histFloat = mHist / (double)mFPP;  // packets for other FPP
+    mHist            = (int)histFloat;
+    if (mHist < 2)
+        mHist = 2;  // min packets for prediction, needs at least 2
+    else if (mHist > 6)
+        mHist = 6;  // max packets, keep a lid on CPU load
+    if (gVerboseFlag)
+        cout << "mHist = " << mHist << " at " << mFPP << "\n";
+    mBytes     = mFPP * mNumChannels * mBitResolutionMode;
+    mXfrBuffer = new int8_t[mBytes];
+    mPacketCnt = 0;  // burg initialization
+    mFadeUp.resize(mFPP, 0.0);
+    mFadeDown.resize(mFPP, 0.0);
+    for (int i = 0; i < mFPP; i++) {
+        mFadeUp[i]   = (double)i / (double)mFPP;
+        mFadeDown[i] = 1.0 - mFadeUp[i];
+    }
+    mLastWasGlitch = false;
+    mPacketDurMsec = 1000.0 * (double)mFPP / (double)mSampleRate;
+    if (mMsecTolerance < mPacketDurMsec)
+        mMsecTolerance = mPacketDurMsec;  // absolute minimum
+    mNumSlots = NumSlotsMax;  //((int)ceil(mMsecTolerance / mPacketDurMsec)) + PADSLOTS;
+
+    for (int i = 0; i < mNumSlots; i++) {
+        int8_t* tmp = new int8_t[mBytes];
+        mSlots.push_back(tmp);
+    }
+    for (int i = 0; i < mNumChannels; i++) {
+        ChanData* tmp = new ChanData(i, mFPP, mHist);
+        mChanData.push_back(tmp);
+        for (int s = 0; s < mFPP; s++)
+            sampleToBits(0.0, i, s);  // zero all channels in mXfrBuffer
+    }
+    mZeros = new int8_t[mBytes];
+    memcpy(mZeros, mXfrBuffer, mBytes);
+    pushStat       = new StdDev((int)(floor(48000.0 / (double)mFPP)), 1);
+    pullStat       = new StdDev((int)(floor(48000.0 / (double)mFPP)), 2);
+    mLastLostCount = 0;  // for stats
+    mIncomingTimer.start();
+    mLastSeqNumIn  = -1;
+    mLastSeqNumOut = -1;
+    mPhasor.resize(mNumChannels, 0.0);
+    mIncomingTiming.resize(ModSeqNumInit);
+    for (int i = 0; i < ModSeqNumInit; i++)
+        mIncomingTiming[i] = 0.0;
+    mModSeqNum = mNumSlots * 2;
+#ifdef GUIBS3
+    // hg for GUI
+    hg = new HerlperGUI(qApp->activeWindow());
+    connect(hg, SIGNAL(moved(double)), this, SLOT(changeGlobal(double)));
+    connect(hg, SIGNAL(moved_2(int)), this, SLOT(changeGlobal_2(int)));
+    connect(hg, SIGNAL(moved_3(int)), this, SLOT(changeGlobal_3(int)));
+#endif
+    changeGlobal_3(LostWindowMax);
+    changeGlobal_2(NumSlotsMax);  // need hg if running GUI
+    changeGlobal((double)qLen);
+}
+
+void Regulator::changeGlobal(double x)
+{  // mMsecTolerance
+    mMsecTolerance = x;
+    printParams();
+}
+
+void Regulator::changeGlobal_2(int x)
+{  // mNumSlots
+    mNumSlots = x;
+    if (!mNumSlots)
+        mNumSlots = 1;
+    if (mNumSlots > NumSlotsMax)
+        mNumSlots = NumSlotsMax;
+    mModSeqNum = mNumSlots * 2;
+    printParams();
+}
+
+void Regulator::changeGlobal_3(int x)
+{  // mLostWindow
+    mLostWindow = x;
+    printParams();
+}
+
+void Regulator::printParams()
+{
+    qDebug() << "mMsecTolerance" << mMsecTolerance << "mNumSlots" << mNumSlots
+             << "mModSeqNum" << mModSeqNum << "mLostWindow" << mLostWindow;
+#ifdef GUIBS3
+    updateGUI((int)mMsecTolerance, mNumSlots);
+#endif
+};
+
+#ifdef GUIBS3
+void Regulator::updateGUI(double msTol, int nSlots)
+{
+    hg->updateDisplay(msTol, nSlots, 0);  // need to remove last param
+}
+#endif
+
+Regulator::~Regulator()
+{
+    delete mXfrBuffer;
+    delete mZeros;
+    for (int i = 0; i < mNumChannels; i++)
+        delete mChanData[i];
+}
+//*******************************************************************************
+void Regulator::pushPacket(const int8_t* buf, int seq_num)
+{
+    QMutexLocker locker(&mMutex);
+    seq_num %= mModSeqNum;
+    // if (seq_num==0) return;   // if (seq_num==1) return; // impose regular loss
+    mIncomingTiming[seq_num] =
+        mMsecTolerance + (double)mIncomingTimer.nsecsElapsed() / 1000000.0;
+    mLastSeqNumIn = seq_num;
+    if (mLastSeqNumIn != -1)
+        memcpy(mSlots[mLastSeqNumIn % mNumSlots], buf, mBytes);
+    pushStat->tick();
+};
+
+//*******************************************************************************
+void Regulator::pullPacket(int8_t* buf)
+{
+    QMutexLocker locker(&mMutex);
+    mSkip = 0;
+    if (mLastSeqNumIn == -1) {
+        goto ZERO_OUTPUT;
+    } else {
+        mLastSeqNumOut++;
+        mLastSeqNumOut %= mModSeqNum;
+        double now = (double)mIncomingTimer.nsecsElapsed() / 1000000.0;
+        for (int i = mLostWindow; i >= 0; i--) {
+            int next = mLastSeqNumIn - i;
+            if (next < 0)
+                next += mModSeqNum;
+            if (mIncomingTiming[next] < mIncomingTiming[mLastSeqNumOut])
+                continue;
+            mSkip = next - mLastSeqNumOut;
+            if (mSkip < 0)
+                mSkip += mModSeqNum;
+            mLastSeqNumOut = next;
+            if (mIncomingTiming[next] > now) {
+                memcpy(mXfrBuffer, mSlots[mLastSeqNumOut % mNumSlots], mBytes);
+                goto PACKETOK;
+            }
+        }
+        goto UNDERRUN;
+    }
+
+PACKETOK : {
+    if (mSkip)
+        processPacket(true);
+    else
+        processPacket(false);
+    goto OUTPUT;
+}
+
+UNDERRUN : {
+    processPacket(true);
+    pullStat->plcUnderruns++;  // count late
+    goto OUTPUT;
+}
+
+ZERO_OUTPUT:
+    memcpy(mXfrBuffer, mZeros, mBytes);
+
+OUTPUT:
+    memcpy(buf, mXfrBuffer, mBytes);
+    pullStat->tick();
+};
+
+//*******************************************************************************
+void Regulator::processPacket(bool glitch)
+{
+    for (int ch = 0; ch < mNumChannels; ch++)
+        processChannel(ch, glitch, mPacketCnt, mLastWasGlitch);
+    mLastWasGlitch = glitch;
+    mPacketCnt++;
+    // 32 bit is good for days:  (/ (* (- (expt 2 32) 1) (/ 32 48000.0)) (* 60 60 24))
+}
+
+//*******************************************************************************
+void Regulator::processChannel(int ch, bool glitch, int packetCnt, bool lastWasGlitch)
+{
+    //    if(glitch) qDebug() << "glitch"; else fprintf(stderr,".");
+    ChanData* cd = mChanData[ch];
+    for (int s = 0; s < mFPP; s++)
+        cd->mTruth[s] = bitsToSample(ch, s);
+    if (packetCnt) {
+        // always update mTrain
+        for (int i = 0; i < mHist; i++) {
+            for (int s = 0; s < mFPP; s++)
+                cd->mTrain[s + ((mHist - (i + 1)) * mFPP)] = cd->mLastPackets[i][s];
+        }
+        if (glitch) {
+            // GET LINEAR PREDICTION COEFFICIENTS
+            ba.train(cd->mCoeffs, cd->mTrain);
+
+            // LINEAR PREDICT DATA
+            cd->mTail = cd->mTrain;
+
+            ba.predict(cd->mCoeffs, cd->mTail);  // resizes to TRAINSAMPS-2 + TRAINSAMPS
+
+            for (int i = 0; i < (cd->trainSamps - 1); i++)
+                cd->mPrediction[i] = cd->mTail[i + cd->trainSamps];
+        }
+        // cross fade last prediction with mTruth
+        if (lastWasGlitch)
+            for (int s = 0; s < mFPP; s++)
+                cd->mXfadedPred[s] =
+                    cd->mTruth[s] * mFadeUp[s] + cd->mLastPred[s] * mFadeDown[s];
+        for (int s = 0; s < mFPP; s++)
+            sampleToBits((glitch)
+                             ? cd->mPrediction[s]
+                             : ((lastWasGlitch) ? cd->mXfadedPred[s] : cd->mTruth[s]),
+                         ch, s);
+        if (glitch) {
+            for (int s = 0; s < mFPP; s++)
+                cd->mLastPred[s] = cd->mPrediction[s + mFPP];
+        }
+    }
+
+    // copy down history
+
+    for (int i = mHist - 1; i > 0; i--) {
+        for (int s = 0; s < mFPP; s++)
+            cd->mLastPackets[i][s] = cd->mLastPackets[i - 1][s];
+    }
+
+    // add prediction  or current input to history, the former checking if primed
+
+    for (int s = 0; s < mFPP; s++)
+        cd->mLastPackets[0][s] =
+            //                ((!glitch) || (packetCnt < mHist)) ? cd->mTruth[s] :
+            //                cd->mPrediction[s];
+            ((glitch) ? ((packetCnt >= mHist) ? cd->mPrediction[s] : cd->mTruth[s])
+                      : ((lastWasGlitch) ? cd->mXfadedPred[s] : cd->mTruth[s]));
+
+    // diagnostic output
+    /////////////////////
+    if (false)
+        for (int s = 0; s < mFPP; s++) {
+            sampleToBits(0.7 * sin(mPhasor[ch]), ch, s);
+            mPhasor[ch] += (!ch) ? 0.1 : 0.11;
+        }
+    /////////////////////
+}
+
+//*******************************************************************************
+// copped from AudioInterface.cpp
+
+sample_t Regulator::bitsToSample(int ch, int frame)
+{
+    sample_t sample = 0.0;
+    AudioInterface::fromBitToSampleConversion(
+        &mXfrBuffer[(frame * mBitResolutionMode * mNumChannels)
+                    + (ch * mBitResolutionMode)],
+        &sample, mBitResolutionMode);
+    return sample;
+}
+
+void Regulator::sampleToBits(sample_t sample, int ch, int frame)
+{
+    AudioInterface::fromSampleToBitConversion(
+        &sample,
+        &mXfrBuffer[(frame * mBitResolutionMode * mNumChannels)
+                    + (ch * mBitResolutionMode)],
+        mBitResolutionMode);
+}
+
+//*******************************************************************************
+bool BurgAlgorithm::classify(double d)
+{
+    bool tmp = false;
+    switch (fpclassify(d)) {
+    case FP_INFINITE:
+        qDebug() << ("infinite");
+        tmp = true;
+        break;
+    case FP_NAN:
+        qDebug() << ("NaN");
+        tmp = true;
+        break;
+    case FP_ZERO:
+        //      qDebug() <<  ("zero");
+        tmp = true;
+        break;
+    case FP_SUBNORMAL:
+        qDebug() << ("subnormal");
+        tmp = true;
+        break;
+        //    case FP_NORMAL:    qDebug() <<  ("normal");    break;
+    }
+    //  if (signbit(d)) qDebug() <<  (" negative\n"); else qDebug() <<  (" positive or
+    //  unsigned\n");
+    return tmp;
+}
+
+void BurgAlgorithm::train(std::vector<long double>& coeffs, const std::vector<float>& x)
+{
+    // GET SIZE FROM INPUT VECTORS
+    size_t N = x.size() - 1;
+    size_t m = coeffs.size();
+
+    //        if (x.size() < m) qDebug() << "time_series should have more elements than
+    //        the AR order is";
+
+    // INITIALIZE Ak
+    //    vector<long double> Ak(m + 1, 0.0);
+    Ak.assign(m + 1, 0.0);
+    Ak[0] = 1.0;
+
+    // INITIALIZE f and b
+    //    vector<long double> f;
+    f.resize(x.size());
+    for (unsigned int i = 0; i < x.size(); i++)
+        f[i] = x[i];
+    //    vector<long double> b(f);
+    b = f;
+
+    // INITIALIZE Dk
+    long double Dk = 0.0;
+    for (size_t j = 0; j <= N; j++)  // CC: N is $#x-1 in C++ but $#x in perl
+    {
+        Dk += 2.00001 * f[j] * f[j];  // CC: needs more damping than orig 2.0
+    }
+    Dk -= f[0] * f[0] + b[N] * b[N];
+
+    //    qDebug() << "Dk" << qStringFromLongDouble1(Dk);
+    //        if ( classify(Dk) )
+    //        { qDebug() << pCnt << "init";
+    //        }
+
+    // BURG RECURSION
+    for (size_t k = 0; k < m; k++) {
+        // COMPUTE MU
+        long double mu = 0.0;
+        for (size_t n = 0; n <= N - k - 1; n++) {
+            mu += f[n + k + 1] * b[n];
+        }
+
+        if (Dk == 0.0)
+            Dk = 0.0000001;  // CC: from testing, needs eps
+        //            if ( classify(Dk) ) qDebug() << pCnt << "run";
+
+        mu *= -2.0 / Dk;
+        //            if ( isnan(Dk) )  { qDebug() << "k" << k; }
+        //            if (Dk==0.0) qDebug() << "k" << k << "Dk==0";
+
+        // UPDATE Ak
+        for (size_t n = 0; n <= (k + 1) / 2; n++) {
+            long double t1 = Ak[n] + mu * Ak[k + 1 - n];
+            long double t2 = Ak[k + 1 - n] + mu * Ak[n];
+            Ak[n]          = t1;
+            Ak[k + 1 - n]  = t2;
+        }
+
+        // UPDATE f and b
+        for (size_t n = 0; n <= N - k - 1; n++) {
+            long double t1 = f[n + k + 1] + mu * b[n];  // were double
+            long double t2 = b[n] + mu * f[n + k + 1];
+            f[n + k + 1]   = t1;
+            b[n]           = t2;
+        }
+
+        // UPDATE Dk
+        Dk = (1.0 - mu * mu) * Dk - f[k + 1] * f[k + 1] - b[N - k - 1] * b[N - k - 1];
+    }
+    // ASSIGN COEFFICIENTS
+    coeffs.assign(++Ak.begin(), Ak.end());
+}
+
+void BurgAlgorithm::predict(std::vector<long double>& coeffs, std::vector<float>& tail)
+{
+    size_t m = coeffs.size();
+    //    qDebug() << "tail.at(0)" << tail[0]*32768;
+    //    qDebug() << "tail.at(1)" << tail[1]*32768;
+    tail.resize(m + tail.size());
+    //    qDebug() << "tail.at(m)" << tail[m]*32768;
+    //    qDebug() << "tail.at(...end...)" << tail[tail.size()-1]*32768;
+    //    qDebug() << "m" << m << "tail.size()" << tail.size();
+    for (size_t i = m; i < tail.size(); i++) {
+        tail[i] = 0.0;
+        for (size_t j = 0; j < m; j++) {
+            tail[i] -= coeffs[j] * tail[i - 1 - j];
+        }
+    }
+}
+
+//*******************************************************************************
+ChanData::ChanData(int i, int FPP, int hist) : ch(i)
+{
+    trainSamps = (hist * FPP);
+    mTruth.resize(FPP, 0.0);
+    mXfadedPred.resize(FPP, 0.0);
+    mLastPred.resize(FPP, 0.0);
+    for (int i = 0; i < hist; i++) {
+        std::vector<sample_t> tmp(FPP, 0.0);
+        mLastPackets.push_back(tmp);
+    }
+    mTrain.resize(trainSamps, 0.0);
+    mPrediction.resize(trainSamps - 1, 0.0);  // ORDER
+    mCoeffs.resize(trainSamps - 2, 0.0);
+    mCrossFadeDown.resize(FPP, 0.0);
+    mCrossFadeUp.resize(FPP, 0.0);
+    mCrossfade.resize(FPP, 0.0);
+}
+
+//*******************************************************************************
+StdDev::StdDev(int w, int id) : window(w), mId(id)
+{
+    reset();
+    longTermStdDev    = 0.0;
+    longTermStdDevAcc = 0.0;
+    longTermCnt       = 0;
+    lastMean          = 0.0;
+    lastMin           = 0;
+    lastMax           = 0;
+    lastPlcUnderruns  = 0;
+    mTimer.start();
+    data.resize(w, 0.0);
+}
+
+void StdDev::reset()
+{
+    mean = 0.0;
+    //        varRunning = 0.0;
+    acc          = 0.0;
+    min          = 999999.0;
+    max          = 0.0;
+    ctr          = 0;
+    plcUnderruns = 0;
+};
+
+double StdDev::tick()
+{
+    double msElapsed = (double)mTimer.nsecsElapsed() / 1000000.0;
+    mTimer.start();
+    if (ctr != window) {
+        data[ctr] = msElapsed;
+        if (msElapsed < min)
+            min = msElapsed;
+        else if (msElapsed > max)
+            max = msElapsed;
+        acc += msElapsed;
+        ctr++;
+    } else {
+        mean       = (double)acc / (double)window;
+        double var = 0.0;
+        for (int i = 0; i < window; i++) {
+            double tmp = data[i] - mean;
+            var += (tmp * tmp);
+        }
+        var /= (double)window;
+        double stdDev = sqrt(var);
+        if (longTermCnt) {
+            longTermStdDevAcc += stdDev;
+            longTermStdDev = longTermStdDevAcc / (double)longTermCnt;
+            if (gVerboseFlag)
+                cout << setw(10) << mean << setw(10) << lastMin << setw(10) << max
+                     << setw(10) << stdDev << setw(10) << longTermStdDev << " " << mId
+                     << endl;
+        } else if (gVerboseFlag)
+            cout << "printing directly from Regulator->stdDev->tick:\n (mean / min / "
+                    "max / "
+                    "stdDev / longTermStdDev) \n";
+
+        longTermCnt++;
+        lastMean         = mean;
+        lastMin          = min;
+        lastMax          = max;
+        lastStdDev       = stdDev;
+        lastPlcUnderruns = plcUnderruns;
+        reset();
+    }
+    return msElapsed;
+}
+//*******************************************************************************
+bool Regulator::getStats(RingBuffer::IOStat* stat, bool reset)
+{
+    QMutexLocker locker(&mMutex);
+    if (reset) {  // all are unused
+        mUnderruns        = 0;
+        mOverflows        = 0;
+        mSkew0            = mLevel;
+        mSkewRaw          = 0;
+        mBufDecOverflow   = 0;
+        mBufDecPktLoss    = 0;
+        mBufIncUnderrun   = 0;
+        mBufIncCompensate = 0;
+        mBroadcastSkew    = 0;
+    }
+    // hijack  of  struct IOStat {
+    stat->underruns = pullStat->lastPlcUnderruns;
+#define FLOATFACTOR 1000.0
+    stat->overflows         = FLOATFACTOR * pushStat->longTermStdDev;
+    stat->skew              = FLOATFACTOR * pushStat->lastMean;
+    stat->skew_raw          = FLOATFACTOR * pushStat->lastMin;
+    stat->level             = FLOATFACTOR * pushStat->lastMax;
+    stat->buf_dec_overflows = FLOATFACTOR * pushStat->lastStdDev;
+
+    stat->buf_dec_pktloss    = FLOATFACTOR * pullStat->longTermStdDev;
+    stat->buf_inc_underrun   = FLOATFACTOR * pullStat->lastMean;
+    stat->buf_inc_compensate = FLOATFACTOR * pullStat->lastMin;
+    stat->broadcast_skew     = FLOATFACTOR * pullStat->lastMax;
+    stat->broadcast_delta    = FLOATFACTOR * pullStat->lastStdDev;
+    // unused
+    //        int32_t autoq_corr;
+    //        int32_t autoq_rate;
+    return true;
+}
+/*
+QString Regulator::getStats(uint32_t statCount, uint32_t lostCount)
+{
+    // formatting floats in columns looks better with std::stringstream than with
+    // QTextStream
+    QString tmp;
+    if (!statCount) {
+        tmp = QString("Regulator: inter-packet intervals msec\n");
+        tmp += "                 (window of last ";
+        tmp += QString::number(pullStat->window);
+        tmp += " packets)\n";
+        tmp +=
+                "secs   avgStdDev (mean       min       max     stdDev) "
+                "PLC(under over  skipped) lost\n";
+    } else {
+        uint32_t lost  = lostCount - mLastLostCount;
+        mLastLostCount = lostCount;
+#define PDBL(x)  << setw(10) << (QString("%1").arg(pushStat->x, 0, 'f', 2)).toStdString()
+#define PDBL2(x) << setw(10) << (QString("%1").arg(pullStat->x, 0, 'f', 2)).toStdString()
+        std::stringstream logger;
+        logger << setw(2)
+               << statCount
+                  PDBL(longTermStdDev) PDBL(lastMean) PDBL(lastMin) PDBL(lastMax)
+PDBL(lastStdDev)
+               << setw(8) << pushStat->lastPlcSkipped
+          #ifndef GUIBS3
+                  // comment out this next line for GUI because...
+               << endl
+                  // ...print all stats in one line when running in GUI because can't
+handle extra crlf
+                  // and... to actually see the two lines, need to run it in terminal
+          #endif
+                  ;
+        tmp = QString::fromStdString(logger.str());
+        std::stringstream logger2;
+        logger2 << setw(2)
+                << "" PDBL2(longTermStdDev) PDBL2(lastMean) PDBL2(lastMin) PDBL2(lastMax)
+                   PDBL2(lastStdDev)
+                << setw(8) << pullStat->lastPlcUnderruns << setw(8)
+                << pullStat->lastPlcOverruns << setw(8) << pullStat->lastPlcSkipped
+                << setw(8) << lost << endl;
+        tmp += QString::fromStdString(logger2.str());
+    }
+    return tmp;
+}
+*/
diff --git a/src/Regulator.h b/src/Regulator.h
new file mode 100644 (file)
index 0000000..eb3da37
--- /dev/null
@@ -0,0 +1,204 @@
+//*****************************************************************
+/*
+  JackTrip: A System for High-Quality Audio Network Performance
+  over the Internet
+
+  Copyright (c) 2021 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 Regulator.h
+ * \author Chris Chafe
+ * \date May 2021
+ */
+
+// EXPERIMENTAL for testing in JackTrip v1.4.0
+// Initial references and starter code
+// http://www.emptyloop.com/technotes/A%20tutorial%20on%20Burg's%20method,%20algorithm%20and%20recursion.pdf
+// https://metacpan.org/source/SYP/Algorithm-Burg-0.001/README
+
+#ifndef __REGULATOR_H__
+#define __REGULATOR_H__
+
+#include <math.h>
+
+#include <QDebug>
+#include <QElapsedTimer>
+
+#include "AudioInterface.h"
+#include "RingBuffer.h"
+
+//#define GUIBS3
+#ifdef GUIBS3
+#include <QWidget>
+
+#include "herlpergui.h"
+#include "ui_herlpergui.h"
+#endif
+
+class BurgAlgorithm
+{
+   public:
+    bool classify(double d);
+    void train(std::vector<long double>& coeffs, const std::vector<float>& x);
+    void predict(std::vector<long double>& coeffs, std::vector<float>& tail);
+
+   private:
+    // the following are class members to minimize heap memory allocations
+    std::vector<long double> Ak;
+    std::vector<long double> f;
+    std::vector<long double> b;
+};
+
+class ChanData
+{
+   public:
+    ChanData(int i, int FPP, int hist);
+    int ch;
+    int trainSamps;
+    std::vector<sample_t> mTruth;
+    std::vector<sample_t> mTrain;
+    std::vector<sample_t> mTail;
+    std::vector<sample_t> mPrediction;  // ORDER
+    std::vector<long double> mCoeffs;
+    std::vector<sample_t> mXfadedPred;
+    std::vector<sample_t> mLastPred;
+    std::vector<std::vector<sample_t>> mLastPackets;
+    std::vector<sample_t> mCrossFadeDown;
+    std::vector<sample_t> mCrossFadeUp;
+    std::vector<sample_t> mCrossfade;
+};
+
+class StdDev
+{
+   public:
+    StdDev(int w, int id);
+    void reset();
+    double tick();
+    QElapsedTimer mTimer;
+    std::vector<double> data;
+    double mean;
+    double var;
+    //    double varRunning;
+    int window;
+    int mId;
+    double acc;
+    double min;
+    double max;
+    int ctr;
+    double lastMean;
+    double lastMin;
+    double lastMax;
+    double lastStdDev;
+    double longTermStdDev;
+    double longTermStdDevAcc;
+    int longTermCnt;
+    int plcUnderruns;
+    int lastPlcUnderruns;
+};
+
+#ifdef GUIBS3
+class Regulator
+    : public QObject
+    , public RingBuffer
+{
+    Q_OBJECT;
+#else
+class Regulator : public RingBuffer
+{
+#endif
+   public:
+    Regulator(int sample_rate, int channels, int bit_res, int FPP, int qLen);
+    virtual ~Regulator();
+
+    void pushPacket(const int8_t* buf, int seq_num);
+    // can hijack unused2 to propagate incoming seq num if needed
+    // option is in UdpDataProtocol
+    // if (!mJackTrip->writeAudioBuffer(src, host_buf_size, last_seq_num))
+    // instread of
+    // if (!mJackTrip->writeAudioBuffer(src, host_buf_size, gap_size))
+    virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot,
+                                       [[maybe_unused]] int unused,
+                                       [[maybe_unused]] int seq_num)
+    {
+        pushPacket(ptrToSlot, seq_num);
+        return (true);
+    }
+
+    void pullPacket(int8_t* buf);
+
+    virtual void readSlotNonBlocking(int8_t* ptrToReadSlot) { pullPacket(ptrToReadSlot); }
+
+    //    virtual QString getStats(uint32_t statCount, uint32_t lostCount);
+    virtual bool getStats(IOStat* stat, bool reset);
+
+   private:
+    void processPacket(bool glitch);
+    void processChannel(int ch, bool glitch, int packetCnt, bool lastWasGlitch);
+    int mNumChannels;
+    int mAudioBitRes;
+    int mFPP;
+    int mSampleRate;
+    uint32_t mLastLostCount;
+    int mNumSlots;
+    int mHist;
+    AudioInterface::audioBitResolutionT mBitResolutionMode;
+    BurgAlgorithm ba;
+    int mBytes;
+    int8_t* mXfrBuffer;
+    int mPacketCnt;
+    sample_t bitsToSample(int ch, int frame);
+    void sampleToBits(sample_t sample, int ch, int frame);
+    std::vector<sample_t> mFadeUp;
+    std::vector<sample_t> mFadeDown;
+    bool mLastWasGlitch;
+    std::vector<int8_t*> mSlots;
+    int8_t* mZeros;
+    double mMsecTolerance;
+    std::vector<ChanData*> mChanData;
+    StdDev* pushStat;
+    StdDev* pullStat;
+    QElapsedTimer mIncomingTimer;
+    int mLastSeqNumIn;
+    int mLastSeqNumOut;
+    double mPacketDurMsec;
+    std::vector<double> mPhasor;
+    std::vector<double> mIncomingTiming;
+    int mModSeqNum;
+    int mLostWindow;
+    int mSkip;
+    std::vector<bool> mIncomingLost;
+#ifdef GUIBS3
+    HerlperGUI* hg;
+    void updateGUI(double msTol, int nSlots, int lostWin);
+   public slots:
+#endif
+    void changeGlobal(double);
+    void changeGlobal_2(int);
+    void changeGlobal_3(int);
+    void printParams();
+};
+#endif  //__REGULATOR_H__
index 5752448079a175a3bb2d00cd10c21f0c69de991a..c1226ec126b60d01c3b51364c0e3e6860751f34a 100644 (file)
@@ -35,7 +35,7 @@
  * \date June 2008
  */
 
-//#define __MANUAL_POLL__
+//#define MANUAL_POLL
 
 #include "UdpDataProtocol.h"
 
 #ifdef _WIN32
 //#include <winsock.h>
 #include <winsock2.h>  //cc need SD_SEND
-#endif
-#if defined(__linux__) || defined(__APPLE__)
+#else
 #include <sys/fcntl.h>
 #include <sys/socket.h>  // for POSIX Sockets
 #include <unistd.h>
-#endif
-#if defined(__APPLE__) && !defined(__MANUAL_POLL__)
-#include <sys/event.h>
-#elif defined(__linux__) && !defined(__MANUAL_POLL__)
+#ifndef MANUAL_POLL
+#ifdef __linux__
 #include <sys/epoll.h>
-#endif
+#else
+#include <sys/event.h>
+#endif  // __linux__
+#endif  // MANUAL_POLL
+#endif  // _WIN32
 
 using std::cout;
 using std::endl;
@@ -123,7 +124,7 @@ UdpDataProtocol::~UdpDataProtocol()
 void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP)
 {
     // Get DNS Address
-#if defined(__linux__) || defined(__APPLE__)
+#ifndef _WIN32
     // Don't make the following code conditional on windows
     //(Addresses a weird timing bug when in hub client mode)
     if (!mPeerAddress.setAddress(peerHostOrIP)) {
@@ -135,7 +136,7 @@ void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP)
         }
         // cout << "UdpDataProtocol::setPeerAddress IP Address Number: "
         //    << mPeerAddress.toString().toStdString() << endl;
-#if defined(__linux__) || defined(__APPLE__)
+#ifndef _WIN32
     }
 #endif
 
@@ -207,7 +208,7 @@ int UdpDataProtocol::bindSocket()
 {
     QMutexLocker locker(&sUdpMutex);
 
-#if defined _WIN32
+#ifdef _WIN32
     WORD wVersionRequested;
     WSADATA wsaData;
     int err;
@@ -232,9 +233,7 @@ int UdpDataProtocol::bindSocket()
     }
 
     SOCKET sock_fd;
-#endif
-
-#if defined(__linux__) || defined(__APPLE__)
+#else
     int sock_fd;
 #endif
 
@@ -262,18 +261,16 @@ int UdpDataProtocol::bindSocket()
 
     // Set socket to be reusable, this is platform dependent
     int one = 1;
-#if defined(__linux__)
+#if defined(_WIN32)
+    // make address/port reusable
+    setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one));
+#elif defined(__linux__)
     ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
-#endif
-#if defined(__APPLE__)
+#else
     // This option is not avialable on Linux, and without it MAC OS X
     // has problems rebinding a socket
     ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
 #endif
-#if defined(_WIN32)
-    // make address/port reusable
-    setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one));
-#endif
 
     // Bind the Socket
     if (mIPv6) {
@@ -286,60 +283,9 @@ int UdpDataProtocol::bindSocket()
         }
     }
 
-    // To be able to use the two UDP sockets bound to the same port number,
-    // we connect the receiver and issue a SHUT_WR.
-
-    // This didn't work for IPv6, so we'll instead share a full duplex socket.
-    /*if (mRunMode == SENDER) {
-        // We use the sender as an unconnected UDP socket
-        UdpSocket.setSocketDescriptor(sock_fd, QUdpSocket::BoundState,
-                                      QUdpSocket::WriteOnly);
-    }*/
-    if (!mIPv6) {
-        // Connect only if we're using IPv4.
-        // (Connecting presents an issue when a host has multiple IP addresses and the
-        // peer decides to send from a different address. While this generally won't be a
-        // problem for IPv4, it will for IPv6.)
-        if ((::connect(sock_fd, (struct sockaddr*)&mPeerAddr, sizeof(mPeerAddr))) < 0) {
-            throw std::runtime_error("ERROR: Could not connect UDP socket");
-        }
-#if defined(__linux__) || defined(__APPLE__)
-        // if ( (::shutdown(sock_fd,SHUT_WR)) < 0)
-        //{ throw std::runtime_error("ERROR: Could shutdown SHUT_WR UDP socket"); }
-#endif
-#if defined _WIN32
-        /*int shut_sr = shutdown(sock_fd, SD_SEND);  //shut down sender's receive function
-        if ( shut_sr< 0)
-        {
-            fprintf(stderr, "ERROR: Could not shutdown SD_SEND UDP socket");
-            throw std::runtime_error("ERROR: Could not shutdown SD_SEND UDP socket");
-        }*/
-#endif
-    }
-
+    // Return our file descriptor so the socket can be shared for a
+    // full duplex connection.
     return sock_fd;
-
-    // OLD CODE WITHOUT POSIX FIX--------------------------------------------------
-    /*
-  /// \todo if port is already used, try binding in a different port
-  QUdpSocket::BindMode bind_mode;
-  if (mRunMode == RECEIVER) {
-    bind_mode = QUdpSocket::DontShareAddress; }
-  else if (mRunMode == SENDER) { //Share sender socket
-    bind_mode = QUdpSocket::ShareAddress; }
-
-  // QHostAddress::Any : let the kernel decide the active address
-  if ( !UdpSocket.bind(QHostAddress::Any, mBindPort, bind_mode) ) {
-    throw std::runtime_error("Could not bind UDP socket. It may be already binded.");
-  }
-  else {
-    if ( mRunMode == RECEIVER ) {
-      cout << "UDP Socket Receiving in Port: " << mBindPort << endl;
-      cout << gPrintSeparator << endl;
-    }
-  }
-  */
-    // ----------------------------------------------------------------------------
 }
 
 //*******************************************************************************
@@ -388,7 +334,8 @@ functions. DWORD n_bytes; WSABUF buffer; int error; buffer.len = n; buffer.buf =
         n_bytes = ::sendto(mSocket, buf, n, 0, (struct sockaddr*)&mPeerAddr6,
                            sizeof(mPeerAddr6));
     } else {
-        n_bytes = ::send(mSocket, buf, n, 0);
+        n_bytes =
+            ::sendto(mSocket, buf, n, 0, (struct sockaddr*)&mPeerAddr, sizeof(mPeerAddr));
     }
     return n_bytes;
     //#endif
@@ -639,8 +586,14 @@ void UdpDataProtocol::run()
         mStatCount               = 0;
 
         //Set up our platform specific polling mechanism. (kqueue, epoll)
-#if !defined (__MANUAL_POLL__) && !defined (_WIN32)
-#if defined (__APPLE__)
+#if !defined (MANUAL_POLL) && !defined (_WIN32)
+#ifdef __linux__
+        int epollfd = epoll_create1(0);
+        struct epoll_event change, event;
+        change.events = EPOLLIN;
+        change.data.fd = mSocket;
+        epoll_ctl(epollfd, EPOLL_CTL_ADD, mSocket, &change);
+#else
         int kq = kqueue();
         struct kevent change;
         struct kevent event;
@@ -648,15 +601,9 @@ void UdpDataProtocol::run()
         struct timespec timeout;
         timeout.tv_sec = 0;
         timeout.tv_nsec = 10000000;
-#else
-        int epollfd = epoll_create1(0);
-        struct epoll_event change, event;
-        change.events = EPOLLIN;
-        change.data.fd = mSocket;
-        epoll_ctl(epollfd, EPOLL_CTL_ADD, mSocket, &change);
 #endif
         int waitTime = 0;
-#endif // __MANUAL_POLL__
+#endif // MANUAL_POLL
 
         if (gVerboseFlag) std::cout << "step 8" << std::endl;
         while (!mStopped) {
@@ -666,7 +613,7 @@ void UdpDataProtocol::run()
             // arrive for a longer time
             //timeout = UdpSocket.waitForReadyRead(30);
             //        timeout = cc unused!
-#if defined (_WIN32) || defined (__MANUAL_POLL__)
+#if defined (_WIN32) || defined (MANUAL_POLL)
             waitForReady(60000); //60 seconds
             receivePacketRedundancy(full_redundant_packet, full_redundant_packet_size,
                                     full_packet_size, current_seq_num, last_seq_num,
@@ -688,10 +635,10 @@ void UdpDataProtocol::run()
         */
             //----------------------------------------------------------------------------------
 
-#ifdef __APPLE__
-            int n = kevent(kq, &change, 1, &event, 1, &timeout);
-#else
+#ifdef __linux__
             int n = epoll_wait(epollfd, &event, 1, 10);
+#else
+            int n = kevent(kq, &change, 1, &event, 1, &timeout);
 #endif
             if (n > 0) {
                 waitTime = 0;
@@ -703,12 +650,12 @@ void UdpDataProtocol::run()
                 emit signalWaitingTooLong(waitTime);
             }
         }
-#ifdef __APPLE__
-        close(kq);
-#else
+#ifdef __linux__
         close(epollfd);
+#else
+        close(kq);
 #endif
-#endif // _WIN32 || __MANUAL_POLL__
+#endif // _WIN32 || MANUAL_POLL
         break; }
 
     case SENDER : {
@@ -861,7 +808,12 @@ void UdpDataProtocol::receivePacketRedundancy(
             }
             src = dst;
         }
-        if (!mJackTrip->writeAudioBuffer(src, host_buf_size, gap_size)) {
+        int ok = true; // send audio buf to
+        ok = (mJackTrip->getBufferStrategy() !=3) ? // ring or jitter
+                    mJackTrip->writeAudioBuffer(src, host_buf_size, gap_size)
+                  : // regulator needs matching local and peer buffer settings
+                    mJackTrip->writeAudioBuffer(src, host_buf_size, last_seq_num);
+        if (!ok) {
             emit signalError("Local and Peer buffer settings are incompatible");
             cout << "ERROR: Local and Peer buffer settings are incompatible" << endl;
             mStopped = true;
index ef2c438cdfbafbbdcfd36a9314fbb96b7881cc58..3330d2560559878e19431f28180bff29d507401c 100644 (file)
@@ -52,7 +52,7 @@
 #include "../Limiter.h"
 #include "../Reverb.h"
 
-QJackTrip::QJackTrip(QWidget* parent)
+QJackTrip::QJackTrip(int argc, QWidget* parent)
     : QMainWindow(parent)
 #ifdef NO_JTVS
     , m_ui(new Ui::QJackTrip)
@@ -67,7 +67,7 @@ QJackTrip::QJackTrip(QWidget* parent)
     , m_jackTripRunning(false)
     , m_isExiting(false)
     , m_hasIPv4Reply(false)
-    , m_argc(1)
+    , m_argc(argc)
     , m_hideWarning(false)
 {
     m_ui->setupUi(this);
@@ -233,6 +233,7 @@ QJackTrip::QJackTrip(QWidget* parent)
     m_ui->basePortSpinBox->setVisible(false);
     m_ui->autoPatchGroupBox->setVisible(false);
     m_ui->requireAuthGroupBox->setVisible(false);
+    m_ui->backendWarningLabel->setVisible(false);
 
 #ifdef RT_AUDIO
     connect(m_ui->backendComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
@@ -247,6 +248,7 @@ QJackTrip::QJackTrip(QWidget* parent)
                     m_ui->outputDeviceComboBox->setEnabled(true);
                     m_ui->outputDeviceLabel->setEnabled(true);
                     m_ui->refreshDevicesButton->setEnabled(true);
+                    m_ui->backendWarningLabel->setVisible(true);
                     populateDeviceMenu(m_ui->inputDeviceComboBox, true);
                     populateDeviceMenu(m_ui->outputDeviceComboBox, false);
                 } else {
@@ -259,6 +261,7 @@ QJackTrip::QJackTrip(QWidget* parent)
                     m_ui->outputDeviceComboBox->setEnabled(false);
                     m_ui->outputDeviceLabel->setEnabled(false);
                     m_ui->refreshDevicesButton->setEnabled(false);
+                    m_ui->backendWarningLabel->setVisible(false);
                 }
             });
     connect(m_ui->refreshDevicesButton, &QPushButton::clicked, this, [=]() {
@@ -272,6 +275,17 @@ QJackTrip::QJackTrip(QWidget* parent)
     }
 #endif
 
+    // One of our arguments will always be --gui, so if that's the only one
+    // then we don't need to show the warning message.
+    if ((!gVerboseFlag && m_argc > 2) || m_argc > 3) {
+        QMessageBox msgBox;
+        msgBox.setText(
+            "The GUI version of JackTrip currently ignores any command line "
+            "options other than the verbose option (-V).\n\nThis may change in future.");
+        msgBox.setWindowTitle(QStringLiteral("Command line options"));
+        msgBox.exec();
+    }
+
     migrateSettings();
     loadSettings();
 
@@ -296,6 +310,7 @@ QJackTrip::QJackTrip(QWidget* parent)
     // Check if Jack is actually available
     if (have_libjack() != 0) {
 #ifdef RT_AUDIO
+        bool usingRtAudioAlready = m_ui->backendComboBox->currentIndex() == 1;
         m_ui->backendComboBox->setCurrentIndex(1);
         m_ui->backendComboBox->setEnabled(false);
         m_ui->backendLabel->setEnabled(false);
@@ -305,7 +320,11 @@ QJackTrip::QJackTrip(QWidget* parent)
             m_ui->typeComboBox->setCurrentIndex(P2P_SERVER);
         }
         m_ui->typeComboBox->removeItem(HUB_SERVER);
+        m_ui->backendWarningLabel->setText(
+            "JACK was not found. This means that only the RtAudio backend is available "
+            "and that JackTrip cannot be run in hub server mode.");
 
+#ifdef NO_JTVS
         QSettings settings;
         settings.beginGroup(QStringLiteral("Audio"));
         if (!settings.value(QStringLiteral("HideJackWarning"), false).toBool()) {
@@ -313,9 +332,11 @@ QJackTrip::QJackTrip(QWidget* parent)
                 new QCheckBox(QStringLiteral("Don't show this warning again"));
             QMessageBox msgBox;
             msgBox.setText(
-                "An installation of JACK was not found. Only the RtAudio\nbackend will "
-                "be available. (Hub Server mode is not\ncurrently supported in this "
-                "configuration.");
+                "An installation of JACK was not found. JackTrip will still run using "
+                "a different audio backend (RtAudio) but some more advanced features, "
+                "like the ability to run your own hub server, will not be available."
+                "\n\n(If you install JACK at a later stage, these features will "
+                "automatically be re-enabled.)");
             msgBox.setWindowTitle(QStringLiteral("JACK Not Available"));
             msgBox.setCheckBox(dontBugMe);
             QObject::connect(dontBugMe, &QCheckBox::stateChanged, this, [=]() {
@@ -325,13 +346,26 @@ QJackTrip::QJackTrip(QWidget* parent)
             if (m_hideWarning) {
                 settings.setValue(QStringLiteral("HideJackWarning"), true);
             }
+            if (!usingRtAudioAlready) {
+                settings.setValue(QStringLiteral("UsingFallback"), true);
+            }
         }
         settings.endGroup();
-#else
+    } else {
+        // If we've fallen back to RtAudio before and JACK is now installed, use JACK.
+        QSettings settings;
+        settings.beginGroup(QStringLiteral("Audio"));
+        if (settings.value(QStringLiteral("UsingFallback"), false).toBool()) {
+            m_ui->backendComboBox->setCurrentIndex(0);
+            settings.setValue(QStringLiteral("UsingFallback"), false);
+        }
+        settings.endGroup();
+#endif  // NO_JTVS
+#else   // RT_AUDIO
         QMessageBox msgBox;
         msgBox.setText(
-            "An installation of JACK was not found, and no other audio\nbackends are "
-            "available. JackTrip will not be able to start.\n(Please install JACK to fix "
+            "An installation of JACK was not found, and no other audio backends are "
+            "available. JackTrip will not be able to start. (Please install JACK to fix "
             "this.)");
         msgBox.setWindowTitle("JACK Not Available");
         msgBox.exec();
@@ -381,27 +415,6 @@ void QJackTrip::resizeEvent(QResizeEvent* event)
     m_ui->authDisclaimerLabel->setMinimumHeight(rect.height());
 }
 
-void QJackTrip::showEvent(QShowEvent* event)
-{
-    QMainWindow::showEvent(event);
-
-    // One of our arguments will always be --gui, so if that's the only one
-    // then we don't need to show the warning message.
-    if ((!gVerboseFlag && m_argc > 2) || m_argc > 3) {
-        QMessageBox msgBox;
-        msgBox.setText(
-            "The GUI version of JackTrip currently\nignores any command line "
-            "options other\nthan the verbose option (-V).\n\nThis may change in future.");
-        msgBox.setWindowTitle(QStringLiteral("Command line options"));
-        msgBox.exec();
-    }
-}
-
-void QJackTrip::setArgc(int argc)
-{
-    m_argc = argc;
-}
-
 void QJackTrip::processFinished()
 {
     if (!m_jackTripRunning) {
@@ -1326,12 +1339,12 @@ void QJackTrip::appendPlugins(JackTrip* jackTrip, int numSendChannels,
     }
 
     // Limiters go last in the plugin sequence.
-    if (m_ui->inLimiterCheckBox->isChecked()) {
-        jackTrip->appendProcessPluginFromNetwork(new Limiter(numSendChannels, 1));
-    }
     if (m_ui->outLimiterCheckBox->isChecked()) {
         jackTrip->appendProcessPluginToNetwork(
-            new Limiter(numRecvChannels, m_ui->outClientsSpinBox->value()));
+            new Limiter(numSendChannels, m_ui->outClientsSpinBox->value()));
+    }
+    if (m_ui->inLimiterCheckBox->isChecked()) {
+        jackTrip->appendProcessPluginFromNetwork(new Limiter(numRecvChannels, 1));
     }
 }
 
index 77286b46b395bb8b032e8df3718782786be533a0..27528a7d096ceeb2cad38762e7dacb134b948f41 100644 (file)
@@ -63,14 +63,11 @@ class QJackTrip : public QMainWindow
     Q_OBJECT
 
    public:
-    explicit QJackTrip(QWidget* parent = nullptr);
+    explicit QJackTrip(int argc = 0, QWidget* parent = nullptr);
     ~QJackTrip() override;
 
     void closeEvent(QCloseEvent* event) override;
     void resizeEvent(QResizeEvent* event) override;
-    void showEvent(QShowEvent* event) override;
-
-    void setArgc(int argc);
 
    signals:
     void signalExit();
index d206d60c8d540dd9596daf41f89a02d5dc206cd8..52dc336979120f66dcb9a6fbe5945dc4d068d0fd 100644 (file)
@@ -999,6 +999,13 @@ play from this machine. (Available in client fan out/in and full mix modes.)</st
         <string>Audio Backend</string>
        </attribute>
        <layout class="QGridLayout" name="gridLayout_11">
+        <item row="3" column="1">
+         <widget class="QComboBox" name="inputDeviceComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
         <item row="0" column="0">
          <widget class="QLabel" name="backendLabel">
           <property name="sizePolicy">
@@ -1015,156 +1022,101 @@ play from this machine. (Available in client fan out/in and full mix modes.)</st
           </property>
          </widget>
         </item>
-        <item row="0" column="1">
-         <widget class="QComboBox" name="backendComboBox">
-          <property name="toolTip">
-           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Choose the audio backend to use. JACK is the default and is well tested, but requires the JACK audio server to be installed.&lt;/p&gt;&lt;p&gt;RtAudio is still a work in progress, but it works with your operating system's native audio drivers and requires no additional software.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-          </property>
-          <item>
-           <property name="text">
-            <string>JACK</string>
-           </property>
-          </item>
-          <item>
-           <property name="text">
-            <string>RtAudio</string>
-           </property>
-          </item>
-         </widget>
-        </item>
-        <item row="1" column="0">
-         <widget class="QLabel" name="sampleRateLabel">
-          <property name="enabled">
-           <bool>false</bool>
-          </property>
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="text">
-           <string>&amp;Sampling Rate:</string>
-          </property>
-          <property name="buddy">
-           <cstring>sampleRateComboBox</cstring>
-          </property>
-         </widget>
-        </item>
-        <item row="1" column="1">
-         <widget class="QComboBox" name="sampleRateComboBox">
+        <item row="2" column="1">
+         <widget class="QComboBox" name="bufferSizeComboBox">
           <property name="enabled">
            <bool>false</bool>
           </property>
           <property name="toolTip">
-           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the audio sample rate to use with the RtAudio backend. This setting should be the same on both ends of the connection.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-          </property>
-          <property name="currentText">
-           <string>48000</string>
+           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the driver's buffer size to use with the RtAudio backend.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
           </property>
           <property name="currentIndex">
            <number>3</number>
           </property>
           <item>
            <property name="text">
-            <string>22050</string>
+            <string>16</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>32000</string>
+            <string>32</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>44100</string>
+            <string>64</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>48000</string>
+            <string>128</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>88200</string>
+            <string>256</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>96000</string>
+            <string>512</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>192000</string>
+            <string>1024</string>
            </property>
           </item>
          </widget>
         </item>
-        <item row="2" column="0">
-         <widget class="QLabel" name="bufferSizeLabel">
-          <property name="enabled">
-           <bool>false</bool>
-          </property>
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="text">
-           <string>&amp;Buffer Size:</string>
-          </property>
-          <property name="buddy">
-           <cstring>bufferSizeComboBox</cstring>
-          </property>
-         </widget>
-        </item>
-        <item row="2" column="1">
-         <widget class="QComboBox" name="bufferSizeComboBox">
+        <item row="1" column="1">
+         <widget class="QComboBox" name="sampleRateComboBox">
           <property name="enabled">
            <bool>false</bool>
           </property>
           <property name="toolTip">
-           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the driver's buffer size to use with the RtAudio backend.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the audio sample rate to use with the RtAudio backend. This setting should be the same on both ends of the connection.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+          </property>
+          <property name="currentText">
+           <string>48000</string>
           </property>
           <property name="currentIndex">
            <number>3</number>
           </property>
           <item>
            <property name="text">
-            <string>16</string>
+            <string>22050</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>32</string>
+            <string>32000</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>64</string>
+            <string>44100</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>128</string>
+            <string>48000</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>256</string>
+            <string>88200</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>512</string>
+            <string>96000</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>1024</string>
+            <string>192000</string>
            </property>
           </item>
          </widget>
@@ -1188,40 +1140,7 @@ play from this machine. (Available in client fan out/in and full mix modes.)</st
           </property>
          </widget>
         </item>
-        <item row="3" column="1">
-         <widget class="QComboBox" name="inputDeviceComboBox">
-          <property name="enabled">
-           <bool>false</bool>
-          </property>
-         </widget>
-        </item>
-        <item row="4" column="0">
-         <widget class="QLabel" name="outputDeviceLabel">
-          <property name="enabled">
-           <bool>false</bool>
-          </property>
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="text">
-           <string>&amp;Output Device:</string>
-          </property>
-          <property name="buddy">
-           <cstring>outputDeviceComboBox</cstring>
-          </property>
-         </widget>
-        </item>
-        <item row="4" column="1">
-         <widget class="QComboBox" name="outputDeviceComboBox">
-          <property name="enabled">
-           <bool>false</bool>
-          </property>
-         </widget>
-        </item>
-        <item row="5" column="1">
+        <item row="6" column="1">
          <spacer name="backendSpacer">
           <property name="orientation">
            <enum>Qt::Vertical</enum>
@@ -1234,7 +1153,7 @@ play from this machine. (Available in client fan out/in and full mix modes.)</st
           </property>
          </spacer>
         </item>
-        <item row="8" column="0" colspan="2">
+        <item row="9" column="0" colspan="2">
          <layout class="QHBoxLayout" name="deviceManagementLayout">
           <item>
            <spacer name="backendTabSpacer">
@@ -1258,6 +1177,97 @@ play from this machine. (Available in client fan out/in and full mix modes.)</st
           </item>
          </layout>
         </item>
+        <item row="4" column="0">
+         <widget class="QLabel" name="outputDeviceLabel">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>&amp;Output Device:</string>
+          </property>
+          <property name="buddy">
+           <cstring>outputDeviceComboBox</cstring>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="0">
+         <widget class="QLabel" name="bufferSizeLabel">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>&amp;Buffer Size:</string>
+          </property>
+          <property name="buddy">
+           <cstring>bufferSizeComboBox</cstring>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1">
+         <widget class="QComboBox" name="backendComboBox">
+          <property name="toolTip">
+           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Choose the audio backend to use. JACK is the default and is well tested, but requires the JACK audio server to be installed.&lt;/p&gt;&lt;p&gt;RtAudio is still a work in progress, but it works with your operating system's native audio drivers and requires no additional software.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+          </property>
+          <item>
+           <property name="text">
+            <string>JACK</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>RtAudio</string>
+           </property>
+          </item>
+         </widget>
+        </item>
+        <item row="1" column="0">
+         <widget class="QLabel" name="sampleRateLabel">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>&amp;Sampling Rate:</string>
+          </property>
+          <property name="buddy">
+           <cstring>sampleRateComboBox</cstring>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="1">
+         <widget class="QComboBox" name="outputDeviceComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="5" column="0" colspan="2">
+         <widget class="QLabel" name="backendWarningLabel">
+          <property name="text">
+           <string>These settings are ignored in Hub Server mode which requires JACK to operate.</string>
+          </property>
+          <property name="wordWrap">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
        </layout>
       </widget>
       <widget class="QWidget" name="JitterTab">
index 69bd2c3e3c0e6a5de89e2cd959ac3744284b64dd..6e6ce34a39f5f197c97b7a10a951909a848e7e7b 100644 (file)
@@ -999,21 +999,29 @@ play from this machine. (Available in client fan out/in and full mix modes.)</st
         <string>Audio Backend</string>
        </attribute>
        <layout class="QGridLayout" name="gridLayout_11">
-        <item row="0" column="0">
-         <widget class="QLabel" name="backendLabel">
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="text">
-           <string>Audio &amp;Backend:</string>
-          </property>
-          <property name="buddy">
-           <cstring>backendComboBox</cstring>
-          </property>
-         </widget>
+        <item row="9" column="0" colspan="2">
+         <layout class="QHBoxLayout" name="deviceManagementLayout">
+          <item>
+           <spacer name="backendTabSpacer">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>40</width>
+              <height>20</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+          <item>
+           <widget class="QPushButton" name="refreshDevicesButton">
+            <property name="text">
+             <string>&amp;Refresh Device List</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
         </item>
         <item row="0" column="1">
          <widget class="QComboBox" name="backendComboBox">
@@ -1032,8 +1040,28 @@ play from this machine. (Available in client fan out/in and full mix modes.)</st
           </item>
          </widget>
         </item>
-        <item row="1" column="0">
-         <widget class="QLabel" name="sampleRateLabel">
+        <item row="3" column="1">
+         <widget class="QComboBox" name="inputDeviceComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="6" column="1">
+         <spacer name="backendSpacer">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>20</width>
+            <height>444</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="3" column="0">
+         <widget class="QLabel" name="inputDeviceLabel">
           <property name="enabled">
            <bool>false</bool>
           </property>
@@ -1044,64 +1072,77 @@ play from this machine. (Available in client fan out/in and full mix modes.)</st
            </sizepolicy>
           </property>
           <property name="text">
-           <string>&amp;Sampling Rate:</string>
+           <string>&amp;Input Device:</string>
           </property>
           <property name="buddy">
-           <cstring>sampleRateComboBox</cstring>
+           <cstring>inputDeviceComboBox</cstring>
           </property>
          </widget>
         </item>
-        <item row="1" column="1">
-         <widget class="QComboBox" name="sampleRateComboBox">
+        <item row="2" column="1">
+         <widget class="QComboBox" name="bufferSizeComboBox">
           <property name="enabled">
            <bool>false</bool>
           </property>
           <property name="toolTip">
-           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the audio sample rate to use with the RtAudio backend. This setting should be the same on both ends of the connection.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-          </property>
-          <property name="currentText">
-           <string>48000</string>
+           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the driver's buffer size to use with the RtAudio backend.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
           </property>
           <property name="currentIndex">
            <number>3</number>
           </property>
           <item>
            <property name="text">
-            <string>22050</string>
+            <string>16</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>32000</string>
+            <string>32</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>44100</string>
+            <string>64</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>48000</string>
+            <string>128</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>88200</string>
+            <string>256</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>96000</string>
+            <string>512</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>192000</string>
+            <string>1024</string>
            </property>
           </item>
          </widget>
         </item>
+        <item row="0" column="0">
+         <widget class="QLabel" name="backendLabel">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>Audio &amp;Backend:</string>
+          </property>
+          <property name="buddy">
+           <cstring>backendComboBox</cstring>
+          </property>
+         </widget>
+        </item>
         <item row="2" column="0">
          <widget class="QLabel" name="bufferSizeLabel">
           <property name="enabled">
@@ -1121,56 +1162,59 @@ play from this machine. (Available in client fan out/in and full mix modes.)</st
           </property>
          </widget>
         </item>
-        <item row="2" column="1">
-         <widget class="QComboBox" name="bufferSizeComboBox">
+        <item row="1" column="1">
+         <widget class="QComboBox" name="sampleRateComboBox">
           <property name="enabled">
            <bool>false</bool>
           </property>
           <property name="toolTip">
-           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the driver's buffer size to use with the RtAudio backend.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+           <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the audio sample rate to use with the RtAudio backend. This setting should be the same on both ends of the connection.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+          </property>
+          <property name="currentText">
+           <string>48000</string>
           </property>
           <property name="currentIndex">
            <number>3</number>
           </property>
           <item>
            <property name="text">
-            <string>16</string>
+            <string>22050</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>32</string>
+            <string>32000</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>64</string>
+            <string>44100</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>128</string>
+            <string>48000</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>256</string>
+            <string>88200</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>512</string>
+            <string>96000</string>
            </property>
           </item>
           <item>
            <property name="text">
-            <string>1024</string>
+            <string>192000</string>
            </property>
           </item>
          </widget>
         </item>
-        <item row="3" column="0">
-         <widget class="QLabel" name="inputDeviceLabel">
+        <item row="1" column="0">
+         <widget class="QLabel" name="sampleRateLabel">
           <property name="enabled">
            <bool>false</bool>
           </property>
@@ -1181,17 +1225,10 @@ play from this machine. (Available in client fan out/in and full mix modes.)</st
            </sizepolicy>
           </property>
           <property name="text">
-           <string>&amp;Input Device:</string>
+           <string>&amp;Sampling Rate:</string>
           </property>
           <property name="buddy">
-           <cstring>inputDeviceComboBox</cstring>
-          </property>
-         </widget>
-        </item>
-        <item row="3" column="1">
-         <widget class="QComboBox" name="inputDeviceComboBox">
-          <property name="enabled">
-           <bool>false</bool>
+           <cstring>sampleRateComboBox</cstring>
           </property>
          </widget>
         </item>
@@ -1221,42 +1258,15 @@ play from this machine. (Available in client fan out/in and full mix modes.)</st
           </property>
          </widget>
         </item>
-        <item row="5" column="1">
-         <spacer name="backendSpacer">
-          <property name="orientation">
-           <enum>Qt::Vertical</enum>
+        <item row="5" column="0" colspan="2">
+         <widget class="QLabel" name="backendWarningLabel">
+          <property name="text">
+           <string>These settings are ignored in hub server mode which requires JACK to operate.</string>
           </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>20</width>
-            <height>444</height>
-           </size>
+          <property name="wordWrap">
+           <bool>true</bool>
           </property>
-         </spacer>
-        </item>
-        <item row="8" column="0" colspan="2">
-         <layout class="QHBoxLayout" name="deviceManagementLayout">
-          <item>
-           <spacer name="backendTabSpacer">
-            <property name="orientation">
-             <enum>Qt::Horizontal</enum>
-            </property>
-            <property name="sizeHint" stdset="0">
-             <size>
-              <width>40</width>
-              <height>20</height>
-             </size>
-            </property>
-           </spacer>
-          </item>
-          <item>
-           <widget class="QPushButton" name="refreshDevicesButton">
-            <property name="text">
-             <string>&amp;Refresh Device List</string>
-            </property>
-           </widget>
-          </item>
-         </layout>
+         </widget>
         </item>
        </layout>
       </widget>
index ce2c04d404745193a202bc16b62a1f00fe21092f..6a31fbfb60abbee85a60f45580b2fb8073ac75d2 100644 (file)
@@ -149,17 +149,25 @@ void setRealtimeProcessPriority(int bufferSize, int sampleRate)
     return;
 }
 
-#endif  //__APPLE__
-
-#if defined(__linux__)
+#elif defined(_WIN32)
+void setRealtimeProcessPriority()
+{
+    if (SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS) == 0) {
+        std::cerr << "Failed to set process priority class." << std::endl;
+    }
+    if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) == 0) {
+        std::cerr << "Failed to set thread priority." << std::endl;
+    }
+}
+#else
 //*******************************************************************************
 void setRealtimeProcessPriority()
 {
     int priority = sched_get_priority_max(SCHED_FIFO);  // 99 is the highest possible
 #ifdef __UBUNTU__
-    priority = 95;  // anything higher is silently ignored by Ubuntu 18.04
+    priority     = 95;  // anything higher is silently ignored by Ubuntu 18.04
 #endif
-    priority = 3;
+    priority     = 3;
 
     struct sched_param sp = {.sched_priority = priority};
 
@@ -168,16 +176,5 @@ void setRealtimeProcessPriority()
         ;
     }
 }
-#endif  //__linux__
 
-#if defined(_WIN32)
-void setRealtimeProcessPriority()
-{
-    if (SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS) == 0) {
-        std::cerr << "Failed to set process priority class." << std::endl;
-    }
-    if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) == 0) {
-        std::cerr << "Failed to set thread priority." << std::endl;
-    }
-}
-#endif  //_WIN32
+#endif
index 369a9684d580c7ad0f59fe709fe160e358f7ff12..7513b1fc799a9fecb14222cc8bc3c59a5711f92d 100644 (file)
@@ -40,7 +40,7 @@
 
 #include "AudioInterface.h"
 
-constexpr const char* const gVersion = "1.5.1";  ///< JackTrip version
+constexpr const char* const gVersion = "1.5.2";  ///< JackTrip version
 
 //*******************************************************************************
 /// \name Default Values
index 38587205aa2949ded476a1b392f21174de5ec362..6ff79097d3a10de3bcd8e0aabe10f78ae2eed99b 100644 (file)
@@ -97,7 +97,7 @@ QCoreApplication* createApplication(int& argc, char* argv[])
             return new QCoreApplication(argc, argv);
         }
 #else
-#ifdef __linux__
+#if defined(__unix__)
         // Check if X or Wayland environment variables are set.
         if (std::getenv("WAYLAND_DISPLAY") == nullptr
             && std::getenv("DISPLAY") == nullptr) {
@@ -108,7 +108,7 @@ QCoreApplication* createApplication(int& argc, char* argv[])
                       << std::endl;
             std::exit(1);
         }
-#endif  // __linux__
+#endif
         return new QApplication(argc, argv);
 #endif  // NO_GUI
     } else {
@@ -123,7 +123,7 @@ void qtMessageHandler([[maybe_unused]] QtMsgType type,
     std::cerr << msg.toStdString() << std::endl;
 }
 
-#if defined(__linux__) || defined(__APPLE__)
+#ifndef _WIN32
 static int setupUnixSignalHandler(void (*handler)(int))
 {
     // Setup our SIGINT handler.
@@ -235,8 +235,7 @@ int main(int argc, char* argv[])
             gVerboseFlag = true;
         }
 
-        window.reset(new QJackTrip);
-        window->setArgc(argc);
+        window.reset(new QJackTrip(argc));
         QObject::connect(window.data(), &QJackTrip::signalExit, app.data(),
                          &QCoreApplication::quit, Qt::QueuedConnection);
         window->show();
@@ -260,7 +259,7 @@ int main(int argc, char* argv[])
                                  Qt::QueuedConnection);
                 QObject::connect(udpHub.data(), &UdpHubListener::signalError, app.data(),
                                  &QCoreApplication::quit, Qt::QueuedConnection);
-#if defined(__linux__) || defined(__APPLE__)
+#ifndef _WIN32
                 setupUnixSignalHandler(UdpHubListener::sigIntHandler);
 #else
             isHubServer = true;
@@ -277,7 +276,7 @@ int main(int argc, char* argv[])
                                  Qt::QueuedConnection);
                 QObject::connect(jackTrip.data(), &JackTrip::signalError, app.data(),
                                  &QCoreApplication::quit, Qt::QueuedConnection);
-#if defined(__linux__) || defined(__APPLE__)
+#ifndef _WIN32
                 setupUnixSignalHandler(JackTrip::sigIntHandler);
 #else
             std::cout << SetConsoleCtrlHandler(windowsCtrlHandler, true) << std::endl;