summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>2022-03-07 19:33:37 +0100
committerIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>2022-03-07 19:33:37 +0100
commit98e24db30fdaf5b07d14b9f411bd835ecd96e5de (patch)
treeeeaae853ee19dbe577d0189fa57a1128bbb6ebb8
parent2eca06839daae7d78b8143b6c29ba51f4b93e2d1 (diff)
parente5e207088ba5998820287321b5b2e8a367405657 (diff)
Update upstream source from tag 'upstream/1.5.2+ds0'
Update to upstream version '1.5.2+ds0' with Debian dir 601b13054a74065ff4fd16c3c0853604e0dc7e81
-rw-r--r--CMakeLists.txt2
-rwxr-xr-xbuild7
-rw-r--r--docs/Install.md2
-rw-r--r--docs/changelog.yml16
-rw-r--r--jacktrip.pro4
-rwxr-xr-xmacos/assemble_app.sh87
-rw-r--r--meson.build2
-rw-r--r--src/JackTrip.cpp100
-rw-r--r--src/JackTrip.h1
-rw-r--r--src/PacketHeader.cpp2
-rw-r--r--src/PoolBuffer.cpp522
-rw-r--r--src/Regulator.cpp658
-rw-r--r--src/Regulator.h (renamed from src/PoolBuffer.h)131
-rw-r--r--src/UdpDataProtocol.cpp138
-rw-r--r--src/gui/qjacktrip.cpp79
-rw-r--r--src/gui/qjacktrip.h5
-rw-r--r--src/gui/qjacktrip.ui236
-rw-r--r--src/gui/qjacktrip_novs.ui184
-rw-r--r--src/jacktrip_globals.cpp31
-rw-r--r--src/jacktrip_globals.h2
-rw-r--r--src/main.cpp13
21 files changed, 1255 insertions, 967 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 47f4c32..40cae87 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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 90c0a52..1f8d5ca 100755
--- 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
diff --git a/docs/Install.md b/docs/Install.md
index 7acdb37..f44fa5c 100644
--- a/docs/Install.md
+++ b/docs/Install.md
@@ -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"
diff --git a/docs/changelog.yml b/docs/changelog.yml
index 9935aeb..1db1e26 100644
--- a/docs/changelog.yml
+++ b/docs/changelog.yml
@@ -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:
diff --git a/jacktrip.pro b/jacktrip.pro
index c811c80..3bb1cdd 100644
--- a/jacktrip.pro
+++ b/jacktrip.pro
@@ -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 \
diff --git a/macos/assemble_app.sh b/macos/assemble_app.sh
index d52a3f8..aa7c4b4 100755
--- a/macos/assemble_app.sh
+++ b/macos/assemble_app.sh
@@ -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
diff --git a/meson.build b/meson.build
index 3de6dad..4293f56 100644
--- a/meson.build
+++ b/meson.build
@@ -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',
diff --git a/src/JackTrip.cpp b/src/JackTrip.cpp
index 661a87b..11df4a9 100644
--- a/src/JackTrip.cpp
+++ b/src/JackTrip.cpp
@@ -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"
@@ -57,8 +57,10 @@
#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();
diff --git a/src/JackTrip.h b/src/JackTrip.h
index d2bde18..0c2ec25 100644
--- a/src/JackTrip.h
+++ b/src/JackTrip.h
@@ -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; }
diff --git a/src/PacketHeader.cpp b/src/PacketHeader.cpp
index 6653e01..cbd3bdf 100644
--- a/src/PacketHeader.cpp
+++ b/src/PacketHeader.cpp
@@ -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
index 343c284..0000000
--- a/src/PoolBuffer.cpp
+++ /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/Regulator.cpp b/src/Regulator.cpp
new file mode 100644
index 0000000..1eb8d84
--- /dev/null
+++ b/src/Regulator.cpp
@@ -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/PoolBuffer.h b/src/Regulator.h
index b7c4d7a..eb3da37 100644
--- a/src/PoolBuffer.h
+++ b/src/Regulator.h
@@ -30,7 +30,7 @@
//*****************************************************************
/**
- * \file PoolBuffer.h
+ * \file Regulator.h
* \author Chris Chafe
* \date May 2021
*/
@@ -40,23 +40,37 @@
// 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__
+#ifndef __REGULATOR_H__
+#define __REGULATOR_H__
+
+#include <math.h>
#include <QDebug>
#include <QElapsedTimer>
#include "AudioInterface.h"
#include "RingBuffer.h"
-using std::vector;
-#include <math.h>
+
+//#define GUIBS3
+#ifdef GUIBS3
+#include <QWidget>
+
+#include "herlpergui.h"
+#include "ui_herlpergui.h"
+#endif
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);
+ 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
@@ -65,27 +79,32 @@ class ChanData
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;
+ 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);
+ StdDev(int w, int id);
void reset();
- void tick();
- QElapsedTimer* mTimer;
- vector<double> data;
+ double tick();
+ QElapsedTimer mTimer;
+ std::vector<double> data;
double mean;
double var;
// double varRunning;
int window;
+ int mId;
double acc;
double min;
double max;
@@ -93,20 +112,29 @@ class StdDev
double lastMean;
double lastMin;
double lastMax;
+ double lastStdDev;
double longTermStdDev;
double longTermStdDevAcc;
int longTermCnt;
+ int plcUnderruns;
+ int lastPlcUnderruns;
};
-class PoolBuffer : public RingBuffer
+#ifdef GUIBS3
+class Regulator
+ : public QObject
+ , public RingBuffer
{
- // Q_OBJECT;
-
+ Q_OBJECT;
+#else
+class Regulator : public RingBuffer
+{
+#endif
public:
- PoolBuffer(int sample_rate, int channels, int bit_res, int FPP, int qLen);
- virtual ~PoolBuffer() {}
+ Regulator(int sample_rate, int channels, int bit_res, int FPP, int qLen);
+ virtual ~Regulator();
- bool pushPacket(const int8_t* buf);
+ 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))
@@ -114,9 +142,9 @@ class PoolBuffer : public RingBuffer
// if (!mJackTrip->writeAudioBuffer(src, host_buf_size, gap_size))
virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot,
[[maybe_unused]] int unused,
- [[maybe_unused]] int unused2)
+ [[maybe_unused]] int seq_num)
{
- pushPacket(ptrToSlot);
+ pushPacket(ptrToSlot, seq_num);
return (true);
}
@@ -124,36 +152,53 @@ class PoolBuffer : public RingBuffer
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 mPoolSize;
+ 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);
- vector<sample_t> mFadeUp;
- vector<sample_t> mFadeDown;
+ std::vector<sample_t> mFadeUp;
+ std::vector<sample_t> mFadeDown;
bool mLastWasGlitch;
- unsigned int mOutgoingCnt;
- int mBytes;
- vector<int8_t*> mIncomingDat;
+ std::vector<int8_t*> mSlots;
int8_t* mZeros;
- QElapsedTimer* mTimer0;
- unsigned int mIncomingCnt;
- vector<int> mIndexPool;
- int mQlen;
- int mGlitchCnt;
- int mGlitchMax;
- vector<ChanData*> mChanData;
- StdDev* stdDev;
+ 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 //__POOLUFFER_H__
+#endif //__REGULATOR_H__
diff --git a/src/UdpDataProtocol.cpp b/src/UdpDataProtocol.cpp
index 5752448..c1226ec 100644
--- a/src/UdpDataProtocol.cpp
+++ b/src/UdpDataProtocol.cpp
@@ -35,7 +35,7 @@
* \date June 2008
*/
-//#define __MANUAL_POLL__
+//#define MANUAL_POLL
#include "UdpDataProtocol.h"
@@ -51,17 +51,18 @@
#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;
diff --git a/src/gui/qjacktrip.cpp b/src/gui/qjacktrip.cpp
index ef2c438..3330d25 100644
--- a/src/gui/qjacktrip.cpp
+++ b/src/gui/qjacktrip.cpp
@@ -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));
}
}
diff --git a/src/gui/qjacktrip.h b/src/gui/qjacktrip.h
index 77286b4..27528a7 100644
--- a/src/gui/qjacktrip.h
+++ b/src/gui/qjacktrip.h
@@ -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();
diff --git a/src/gui/qjacktrip.ui b/src/gui/qjacktrip.ui
index d206d60..52dc336 100644
--- a/src/gui/qjacktrip.ui
+++ b/src/gui/qjacktrip.ui
@@ -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">
diff --git a/src/gui/qjacktrip_novs.ui b/src/gui/qjacktrip_novs.ui
index 69bd2c3..6e6ce34 100644
--- a/src/gui/qjacktrip_novs.ui
+++ b/src/gui/qjacktrip_novs.ui
@@ -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>
diff --git a/src/jacktrip_globals.cpp b/src/jacktrip_globals.cpp
index ce2c04d..6a31fbf 100644
--- a/src/jacktrip_globals.cpp
+++ b/src/jacktrip_globals.cpp
@@ -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
diff --git a/src/jacktrip_globals.h b/src/jacktrip_globals.h
index 369a968..7513b1f 100644
--- a/src/jacktrip_globals.h
+++ b/src/jacktrip_globals.h
@@ -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
diff --git a/src/main.cpp b/src/main.cpp
index 3858720..6ff7909 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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;