From b30cf4d557ffb616f63f0ba06917d576c56ad4ac Mon Sep 17 00:00:00 2001
From: =?utf8?q?IOhannes=20m=20zm=C3=B6lnig=20=28Debian/GNU=29?=
Date: Sun, 11 Feb 2024 10:57:36 +0100
Subject: [PATCH] New upstream version 2.2.2+ds
---
LICENSE.md | 3 +
docs/About/License.md | 6 +-
docs/Build/Linux.md | 1 +
docs/changelog.yml | 7 ++
releases/edge/mac-manifests.json | 30 ++++++
releases/edge/win-manifests.json | 30 ++++++
releases/stable/linux-manifests.json | 20 ++++
releases/stable/mac-manifests.json | 20 ++++
releases/stable/win-manifests.json | 20 ++++
src/AudioInterface.cpp | 16 +--
src/AudioInterface.h | 6 ++
src/JackAudioInterface.cpp | 6 +-
src/Regulator.cpp | 5 +-
src/RtAudioInterface.cpp | 6 +-
src/UdpDataProtocol.cpp | 14 +--
src/gui/Browse.qml | 1 +
src/gui/Footer.qml | 20 ++--
src/gui/Permissions.qml | 7 +-
src/gui/Recommendations.qml | 14 +--
src/gui/Setup.qml | 2 +-
src/gui/Studio.qml | 12 +--
src/gui/about.cpp | 30 ++++--
src/gui/virtualstudio.cpp | 152 +++++++++++----------------
src/gui/virtualstudio.h | 11 +-
src/gui/vs.qml | 7 +-
src/gui/vsAudio.cpp | 49 ++++++++-
src/gui/vsAudio.h | 9 ++
src/gui/vsDeeplink.cpp | 19 ++--
src/jacktrip_globals.h | 3 +-
29 files changed, 344 insertions(+), 182 deletions(-)
diff --git a/LICENSE.md b/LICENSE.md
index 513cb6c..0169495 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -16,6 +16,9 @@ JackTrip uses Qt library throughout the project so the resulting binaries are
also subject to Qt's license. The builds provided on GitHub's Releases page use
open source distribution of Qt, licensed under LGPL.
+Windows builds of JackTrip may include support for ASIO.
+ASIO is a trademark and software of Steinberg Media Technologies GmbH.
+
Using JackTrip to join Virtual Studios on Windows computers may use AVC (h264)
video encoders and decoders subject to the AVC Patent Portfolio License.
diff --git a/docs/About/License.md b/docs/About/License.md
index 9535e11..159323b 100644
--- a/docs/About/License.md
+++ b/docs/About/License.md
@@ -11,4 +11,8 @@
---
### LGPL License
---8<-- "LICENSES/LGPL-3.0-only.txt"
\ No newline at end of file
+--8<-- "LICENSES/LGPL-3.0-only.txt"
+
+---
+### AVC License
+--8<-- "LICENSES/AVC.txt"
\ No newline at end of file
diff --git a/docs/Build/Linux.md b/docs/Build/Linux.md
index c8b54b0..985228d 100644
--- a/docs/Build/Linux.md
+++ b/docs/Build/Linux.md
@@ -43,6 +43,7 @@ apt install qtbase5-dev qtbase5-dev-tools qtchooser qt5-qmake qttools5-dev libqt
### Ubuntu and Debian/Raspbian (Qt6)
```sh
apt install --no-install-recommends build-essential autoconf automake libtool make libjack-jackd2-dev git help2man libclang-dev libdbus-1-dev libdbus-1-dev python3-jinja2
+apt install -y libqt6core6 libqt6gui6 libqt6network6 libqt6widgets6 libqt6qml6 libqt6qmlcore6 libqt6quick6 libqt6quickcontrols2-6 libqt6svg6 libqt6webchannel6 libqt6webengine6-data libqt6webenginecore6 libqt6webenginecore6-bin libqt6webenginequick6 libqt6websockets6 libqt6shadertools6 qt6-qpa-plugins qml6-module-qtquick-controls qml6-module-qtqml-workerscript qml6-module-qtquick-templates qml6-module-qtquick-layouts qml6-module-qt5compat-graphicaleffects qml6-module-qtwebchannel qml6-module-qtwebengine qml6-module-qtquick-window
apt install qt6-base-dev qt6-base-dev-tools qmake6 qt6-tools-dev qt6-declarative-dev qt6-webengine-dev qt6-webview-dev qt6-webview-plugins libqt6svg6-dev libqt6websockets6-dev libqt6core5compat6-dev libqt6shadertools6-dev libgl1-mesa-dev
# for GUI builds
apt install libfreetype6-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev libx11-xcb-dev libdrm-dev libglu1-mesa-dev libwayland-dev libwayland-egl1-mesa libgles2-mesa-dev libwayland-server0 libwayland-egl-backend-dev libxcb1-dev libxext-dev libfontconfig1-dev libxrender-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev '^libxcb.*-dev' libxcb-render-util0-dev libxcomposite-dev libgtk-3-dev
diff --git a/docs/changelog.yml b/docs/changelog.yml
index 0d888b4..ff94aa1 100644
--- a/docs/changelog.yml
+++ b/docs/changelog.yml
@@ -1,3 +1,10 @@
+- Version: "2.2.2"
+ Date: 2024-02-09
+ Description:
+ - (updated) VS Mode updated network connection thresholds
+ - (updated) VS Mode improved sample rate flexibility for Windows
+ - (fixed) VS Mode inconsistent deep link handling on Windows
+ - (fixed) Throttle console errors for UDP waiting too long
- Version: "2.2.1"
Date: 2024-01-29
Description:
diff --git a/releases/edge/mac-manifests.json b/releases/edge/mac-manifests.json
index 09fa255..618dcf9 100644
--- a/releases/edge/mac-manifests.json
+++ b/releases/edge/mac-manifests.json
@@ -1,6 +1,36 @@
{
"app_name": "JackTrip",
"releases": [
+ {
+ "version": "2.2.1",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.1",
+ "download": {
+ "date": "2024-01-30T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.1-macOS-x64-signed-installer.pkg",
+ "downloadSize": "177373978",
+ "sha256": "d02e5de0cee389ee39c789ad6fe8859823944cf2c6af15f8d80249f3134f4653"
+ }
+ },
+ {
+ "version": "2.2.0",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.0",
+ "download": {
+ "date": "2024-01-22T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.0-macOS-x64-signed-installer.pkg",
+ "downloadSize": "177373618",
+ "sha256": "ecef1ac2ae1fd3f2da40017f5da1fca6d966946a016ba80116ca960d88f04a53"
+ }
+ },
+ {
+ "version": "2.2.0-beta1",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.0-beta1",
+ "download": {
+ "date": "2024-01-17T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.0-beta1-macOS-x64-signed-installer.pkg",
+ "downloadSize": "177373175",
+ "sha256": "6512e524d022eebe5b2e928d008c0fdb85dbaa453179952ee232f0d73d0d68eb"
+ }
+ },
{
"version": "2.1.0",
"changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.1.0",
diff --git a/releases/edge/win-manifests.json b/releases/edge/win-manifests.json
index 17eb017..b44d3a1 100644
--- a/releases/edge/win-manifests.json
+++ b/releases/edge/win-manifests.json
@@ -1,6 +1,36 @@
{
"app_name": "JackTrip",
"releases": [
+ {
+ "version": "2.2.1",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.1",
+ "download": {
+ "date": "2024-01-30T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.1-Windows-x64-signed-installer.msi",
+ "downloadSize": "108511232",
+ "sha256": "193825d24745cd5a052ae57f1345b02924fc269aa69324428e7a177b9c58aa05"
+ }
+ },
+ {
+ "version": "2.2.0",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.0",
+ "download": {
+ "date": "2024-01-22T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.0-Windows-x64-signed-installer.msi",
+ "downloadSize": "108511232",
+ "sha256": "92aeb6ba74fcb5cade48962aa4696a77fb7b2434622c22097cfa9da037b32fb3"
+ }
+ },
+ {
+ "version": "2.2.0-beta1",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.0-beta1",
+ "download": {
+ "date": "2024-01-17T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.0-beta1-Windows-x64-signed-installer.msi",
+ "downloadSize": "108511232",
+ "sha256": "24401a0adaf8753f68d4303bda0d08fed35032168254ab7445766216cfb73980"
+ }
+ },
{
"version": "2.1.0",
"changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.1.0",
diff --git a/releases/stable/linux-manifests.json b/releases/stable/linux-manifests.json
index 26ec828..13c1bd7 100644
--- a/releases/stable/linux-manifests.json
+++ b/releases/stable/linux-manifests.json
@@ -1,6 +1,26 @@
{
"app_name": "JackTrip",
"releases": [
+ {
+ "version": "2.2.1",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.1",
+ "download": {
+ "date": "2024-01-30T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.1-Linux-x64-binary.zip",
+ "downloadSize": "1239788",
+ "sha256": "bfd986377b54c1ab84f16e0c0fd5ca61ed50e6cec281f7505e95d2b663af32f7"
+ }
+ },
+ {
+ "version": "2.2.0",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.0",
+ "download": {
+ "date": "2024-01-22T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.0-Linux-x64-binary.zip",
+ "downloadSize": "1239784",
+ "sha256": "c5ce96f64ea204f1a17a951e9d39fd247e925fd21f55ae48a8bc27d6767cf675"
+ }
+ },
{
"version": "2.1.0",
"changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.1.0",
diff --git a/releases/stable/mac-manifests.json b/releases/stable/mac-manifests.json
index 78ddfd4..e62442b 100644
--- a/releases/stable/mac-manifests.json
+++ b/releases/stable/mac-manifests.json
@@ -1,6 +1,26 @@
{
"app_name": "JackTrip",
"releases": [
+ {
+ "version": "2.2.1",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.1",
+ "download": {
+ "date": "2024-01-30T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.1-macOS-x64-signed-installer.pkg",
+ "downloadSize": "177373978",
+ "sha256": "d02e5de0cee389ee39c789ad6fe8859823944cf2c6af15f8d80249f3134f4653"
+ }
+ },
+ {
+ "version": "2.2.0",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.0",
+ "download": {
+ "date": "2024-01-22T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.0-macOS-x64-signed-installer.pkg",
+ "downloadSize": "177373618",
+ "sha256": "ecef1ac2ae1fd3f2da40017f5da1fca6d966946a016ba80116ca960d88f04a53"
+ }
+ },
{
"version": "2.1.0",
"changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.1.0",
diff --git a/releases/stable/win-manifests.json b/releases/stable/win-manifests.json
index 3e34349..0656ed8 100644
--- a/releases/stable/win-manifests.json
+++ b/releases/stable/win-manifests.json
@@ -1,6 +1,26 @@
{
"app_name": "JackTrip",
"releases": [
+ {
+ "version": "2.2.1",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.1",
+ "download": {
+ "date": "2024-01-30T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.1-Windows-x64-signed-installer.msi",
+ "downloadSize": "108511232",
+ "sha256": "193825d24745cd5a052ae57f1345b02924fc269aa69324428e7a177b9c58aa05"
+ }
+ },
+ {
+ "version": "2.2.0",
+ "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.2.0",
+ "download": {
+ "date": "2024-01-22T00:00:00Z",
+ "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.2.0-Windows-x64-signed-installer.msi",
+ "downloadSize": "108511232",
+ "sha256": "92aeb6ba74fcb5cade48962aa4696a77fb7b2434622c22097cfa9da037b32fb3"
+ }
+ },
{
"version": "2.1.0",
"changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.1.0",
diff --git a/src/AudioInterface.cpp b/src/AudioInterface.cpp
index 1d7dc81..0f684f1 100644
--- a/src/AudioInterface.cpp
+++ b/src/AudioInterface.cpp
@@ -95,15 +95,15 @@ AudioInterface::~AudioInterface()
delete[] mAPInBuffer[i];
}
#endif // endwhere
- for (auto* i : qAsConst(mProcessPluginsFromNetwork)) {
+ for (auto* i : std::as_const(mProcessPluginsFromNetwork)) {
i->disconnect();
delete i;
}
- for (auto* i : qAsConst(mProcessPluginsToNetwork)) {
+ for (auto* i : std::as_const(mProcessPluginsToNetwork)) {
i->disconnect();
delete i;
}
- for (auto* i : qAsConst(mProcessPluginsToMonitor)) {
+ for (auto* i : std::as_const(mProcessPluginsToMonitor)) {
i->disconnect();
delete i;
}
@@ -206,7 +206,7 @@ void AudioInterface::audioInputCallback(QVarLengthArray& in_buffer,
#endif // not WAIR
// process incoming signal from audio interface using process plugins
- for (auto* p : qAsConst(mProcessPluginsToNetwork)) {
+ for (auto* p : std::as_const(mProcessPluginsToNetwork)) {
if (p->getInited()) {
p->compute(n_frames, in_buffer.data(), in_buffer.data());
}
@@ -268,7 +268,7 @@ void AudioInterface::audioOutputCallback(QVarLengthArray& out_buffer,
/// with one. do it chaining outputs to inputs in the buffers. May need a tempo buffer
#ifndef WAIR // NOT WAIR:
- for (auto* p : qAsConst(mProcessPluginsFromNetwork)) {
+ for (auto* p : std::as_const(mProcessPluginsFromNetwork)) {
if (p->getInited()) {
p->compute(n_frames, out_buffer.data(), out_buffer.data());
}
@@ -728,17 +728,17 @@ void AudioInterface::initPlugins(bool verbose)
<< ") at sampling rate " << mSampleRate << "\n";
}
- for (ProcessPlugin* plugin : qAsConst(mProcessPluginsFromNetwork)) {
+ for (ProcessPlugin* plugin : std::as_const(mProcessPluginsFromNetwork)) {
plugin->setOutgoingToNetwork(false);
plugin->updateNumChannels(nChansIn, nChansOut);
plugin->init(mSampleRate, mBufferSizeInSamples);
}
- for (ProcessPlugin* plugin : qAsConst(mProcessPluginsToNetwork)) {
+ for (ProcessPlugin* plugin : std::as_const(mProcessPluginsToNetwork)) {
plugin->setOutgoingToNetwork(true);
plugin->updateNumChannels(nChansIn, nChansOut);
plugin->init(mSampleRate, mBufferSizeInSamples);
}
- for (ProcessPlugin* plugin : qAsConst(mProcessPluginsToMonitor)) {
+ for (ProcessPlugin* plugin : std::as_const(mProcessPluginsToMonitor)) {
plugin->setOutgoingToNetwork(false);
plugin->updateNumChannels(nChansMon, nChansMon);
plugin->init(mSampleRate, mBufferSizeInSamples);
diff --git a/src/AudioInterface.h b/src/AudioInterface.h
index a9ee255..6ed62be 100644
--- a/src/AudioInterface.h
+++ b/src/AudioInterface.h
@@ -40,6 +40,7 @@
#include
#include
+#include
#include "AudioTester.h"
#include "ProcessPlugin.h"
@@ -51,6 +52,9 @@ class JackTrip;
// using namespace JackTripNamespace;
+// callback function for audio interface errors
+typedef std::function AudioErrorCallback;
+
/** \brief Base Class that provides an interface with audio
*/
class AudioInterface
@@ -265,6 +269,7 @@ class AudioInterface
virtual void setLoopBack(bool b) { mLoopBack = b; }
virtual void enableBroadcastOutput() {}
virtual void setAudioTesterP(AudioTester* atp) { mAudioTesterP = atp; }
+ void setErrorCallback(AudioErrorCallback c) { mErrorCallback = c; }
//------------------------------------------------------------------
//--------------GETTERS---------------------------------------------
@@ -367,6 +372,7 @@ class AudioInterface
std::string mWarningHelpUrl;
std::string mErrorHelpUrl;
bool mHighLatencyFlag;
+ AudioErrorCallback mErrorCallback;
};
#endif // __AUDIOINTERFACE_H__
diff --git a/src/JackAudioInterface.cpp b/src/JackAudioInterface.cpp
index f638de2..7221827 100644
--- a/src/JackAudioInterface.cpp
+++ b/src/JackAudioInterface.cpp
@@ -283,7 +283,11 @@ void JackAudioInterface::jackShutdown(jack_status_t /*code*/, const char* reason
errorMsg += reason;
}
if (arg != nullptr) {
- static_cast(arg)->mErrorMsg = errorMsg;
+ JackAudioInterface* ifPtr = static_cast(arg);
+ ifPtr->mErrorMsg = errorMsg;
+ if (ifPtr->mErrorCallback) {
+ ifPtr->mErrorCallback(errorMsg);
+ }
}
std::cerr << errorMsg << std::endl;
JackTrip::sAudioStopped = true;
diff --git a/src/Regulator.cpp b/src/Regulator.cpp
index efb9c70..a58d6e6 100644
--- a/src/Regulator.cpp
+++ b/src/Regulator.cpp
@@ -97,7 +97,6 @@ constexpr double AutoMax = 250.0; // msec bounds on insane IPI, like ethernet
constexpr double AutoInitDur = 3000.0; // kick in auto after this many msec
constexpr double AutoInitValFactor =
0.5; // scale for initial mMsecTolerance during init phase if unspecified
-constexpr double MaxWaitTime = 30; // msec
// tweak
constexpr int WindowDivisor = 8; // for faster auto tracking
@@ -505,7 +504,7 @@ PACKETOK : {
UNDERRUN : {
pullStat->plcUnderruns++; // count late
if ((mLastSeqNumOut == lastSeqNumIn)
- && ((now - mIncomingTiming[mLastSeqNumOut]) > MaxWaitTime)) {
+ && ((now - mIncomingTiming[mLastSeqNumOut]) > gUdpWaitTimeout)) {
goto ZERO_OUTPUT;
}
// "good underrun", not a stuck client
@@ -827,7 +826,7 @@ bool StdDev::tick()
// discard measurements that exceed the max wait time
// this prevents temporary outages from skewing jitter metrics
- if (msElapsed > MaxWaitTime)
+ if (msElapsed > gUdpWaitTimeout)
return false;
if (ctr != window) {
diff --git a/src/RtAudioInterface.cpp b/src/RtAudioInterface.cpp
index 41268f8..fc17538 100644
--- a/src/RtAudioInterface.cpp
+++ b/src/RtAudioInterface.cpp
@@ -610,7 +610,11 @@ void RtAudioInterface::errorCallback(RtAudioErrorType errorType,
errorMsg += errorText;
}
if (arg != nullptr) {
- static_cast(arg)->mErrorMsg = errorMsg;
+ RtAudioInterface* ifPtr = static_cast(arg);
+ ifPtr->mErrorMsg = errorText;
+ if (ifPtr->mErrorCallback) {
+ ifPtr->mErrorCallback(errorText);
+ }
}
std::cerr << errorMsg << std::endl;
JackTrip::sAudioStopped = true;
diff --git a/src/UdpDataProtocol.cpp b/src/UdpDataProtocol.cpp
index 73f975a..656fc85 100644
--- a/src/UdpDataProtocol.cpp
+++ b/src/UdpDataProtocol.cpp
@@ -672,7 +672,7 @@ void UdpDataProtocol::run()
// This QT method gave me a lot of trouble, so I replaced it with my own 'waitForReady'
// that uses signals and slots and can also report with packets have not
// arrive for a longer time
- //timeout = UdpSocket.waitForReadyRead(30);
+ //timeout = UdpSocket.waitForReadyRead(gUdpWaitTimeout);
// timeout = cc unused!
#if defined (MANUAL_POLL)
waitForReady(60000); //60 seconds
@@ -757,7 +757,7 @@ void UdpDataProtocol::run()
// Send exit packet (with 1 redundant packet).
cout << "sending exit packet" << endl;
- QByteArray exitPacket = QByteArray(mControlPacketSize, 0xff);
+ QByteArray exitPacket = QByteArray(mControlPacketSize, static_cast(0xff));
sendPacket(exitPacket.constData(), mControlPacketSize);
sendPacket(exitPacket.constData(), mControlPacketSize);
emit signalCeaseTransmission();
@@ -800,10 +800,12 @@ void UdpDataProtocol::waitForReady(int timeout_msec)
//*******************************************************************************
void UdpDataProtocol::printUdpWaitedTooLong(int wait_msec)
{
- int wait_time = 30; // msec
- if (!(wait_msec % wait_time)) {
- std::cerr << "UDP waiting too long (more than " << wait_time << "ms) for "
- << mPeerAddress.toString().toStdString() << "..." << endl;
+ if (!(wait_msec % gUdpWaitTimeout)) {
+ // only log error once per gap in audio, rather than every 30ms
+ if (wait_msec <= gUdpWaitTimeout) {
+ std::cerr << "UDP waiting too long (more than " << gUdpWaitTimeout << "ms) for "
+ << mPeerAddress.toString().toStdString() << "..." << endl;
+ }
emit signalUdpWaitingTooLong();
}
}
diff --git a/src/gui/Browse.qml b/src/gui/Browse.qml
index 18db844..d5bb6dd 100644
--- a/src/gui/Browse.qml
+++ b/src/gui/Browse.qml
@@ -86,6 +86,7 @@ Item {
connected: false
studioId: modelData.id ? modelData.id : ""
inviteKeyString: modelData.inviteKey ? modelData.inviteKey : ""
+ sampleRate: modelData.sampleRate
}
section { property: "modelData.type"; criteria: ViewSection.FullString; delegate: SectionHeading {} }
diff --git a/src/gui/Footer.qml b/src/gui/Footer.qml
index 6b76643..18f8292 100644
--- a/src/gui/Footer.qml
+++ b/src/gui/Footer.qml
@@ -10,6 +10,7 @@ Rectangle {
color: backgroundColour
clip: true
+ property string statsOrange: "#b26a00"
property string connectionStateColor: getConnectionStateColor()
property variant networkStatsText: getNetworkStatsText()
@@ -44,20 +45,15 @@ Rectangle {
texts[1] = "" + minRtt + " ms - " + maxRtt + " ms, avg " + avgRtt + " ms";
let quality = "Poor";
let color = meterRed;
- if (avgRtt <= 25) {
- if (maxRtt <= 30) {
- quality = "Excellent";
- color = meterGreen;
- } else {
- quality = "Good";
- color = meterGreen;
- }
- } else if (avgRtt <= 30) {
- quality = "Good";
+ if (avgRtt < 10 && maxRtt < 15) {
+ quality = "Excellent";
color = meterGreen;
- } else if (avgRtt <= 35) {
- quality = "Fair";
+ } else if (avgRtt < 20 && maxRtt < 30) {
+ quality = "Good";
color = meterYellow;
+ } else if (avgRtt < 30 && maxRtt < 40) {
+ quality = "Fair";
+ color = statsOrange;
}
texts[0] = quality
diff --git a/src/gui/Permissions.qml b/src/gui/Permissions.qml
index 6c53314..da538c6 100644
--- a/src/gui/Permissions.qml
+++ b/src/gui/Permissions.qml
@@ -189,13 +189,10 @@ Item {
function onMicPermissionUpdated() {
if (permissions.micPermission === "granted") {
- if (virtualstudio.studioToJoin.toString() === "") {
+ if (virtualstudio.studioToJoin === "") {
virtualstudio.windowState = "browse";
- } else if (virtualstudio.showDeviceSetup) {
- virtualstudio.windowState = "setup";
- audio.startAudio();
} else {
- virtualstudio.windowState = "connected";
+ virtualstudio.windowState = virtualstudio.showDeviceSetup ? "setup" : "connected";
virtualstudio.joinStudio();
}
}
diff --git a/src/gui/Recommendations.qml b/src/gui/Recommendations.qml
index f6a3123..b8de3a1 100644
--- a/src/gui/Recommendations.qml
+++ b/src/gui/Recommendations.qml
@@ -471,13 +471,10 @@ Item {
virtualstudio.saveSettings();
if (permissions.micPermission !== "granted") {
virtualstudio.windowState = "permissions";
- } else if (virtualstudio.studioToJoin.toString() === "") {
+ } else if (virtualstudio.studioToJoin === "") {
virtualstudio.windowState = "browse";
- } else if (virtualstudio.showDeviceSetup) {
- virtualstudio.windowState = "setup";
- audio.startAudio();
} else {
- virtualstudio.windowState = "connected";
+ virtualstudio.windowState = virtualstudio.showDeviceSetup ? "setup" : "connected";
virtualstudio.joinStudio();
}
}
@@ -509,13 +506,10 @@ Item {
virtualstudio.saveSettings();
if (permissions.micPermission !== "granted") {
virtualstudio.windowState = "permissions";
- } else if (virtualstudio.studioToJoin.toString() === "") {
+ } else if (virtualstudio.studioToJoin === "") {
virtualstudio.windowState = "browse";
- } else if (virtualstudio.showDeviceSetup) {
- virtualstudio.windowState = "setup";
- audio.startAudio();
} else {
- virtualstudio.windowState = "connected";
+ virtualstudio.windowState = virtualstudio.showDeviceSetup ? "setup" : "connected";
virtualstudio.joinStudio();
}
}
diff --git a/src/gui/Setup.qml b/src/gui/Setup.qml
index 16b5662..e0b9ef9 100644
--- a/src/gui/Setup.qml
+++ b/src/gui/Setup.qml
@@ -136,7 +136,7 @@ Item {
}
enabled: !Boolean(audio.devicesError) && audio.backendAvailable && audio.audioReady
onClicked: {
- audio.stopAudio(true);
+ virtualstudio.studioToJoin = virtualstudio.currentStudio.id;
virtualstudio.windowState = "connected";
virtualstudio.saveSettings();
virtualstudio.joinStudio();
diff --git a/src/gui/Studio.qml b/src/gui/Studio.qml
index 91f8397..e6eb55b 100644
--- a/src/gui/Studio.qml
+++ b/src/gui/Studio.qml
@@ -14,6 +14,7 @@ Rectangle {
property string studioName: "Test Studio"
property string studioId: ""
property string inviteKeyString: ""
+ property int sampleRate: 48000
property bool publicStudio: false
property bool admin: false
property bool available: true
@@ -197,14 +198,9 @@ Rectangle {
}
visible: !connected
onClicked: {
- virtualstudio.studioToJoin = `jacktrip://join/${studioId}`
- if (virtualstudio.showDeviceSetup) {
- virtualstudio.windowState = "setup";
- audio.startAudio();
- } else {
- virtualstudio.windowState = "connected";
- virtualstudio.joinStudio();
- }
+ virtualstudio.studioToJoin = studioId;
+ virtualstudio.windowState = virtualstudio.showDeviceSetup ? "setup" : "connected";
+ virtualstudio.joinStudio();
}
Image {
id: join
diff --git a/src/gui/about.cpp b/src/gui/about.cpp
index 02b982d..5c25979 100644
--- a/src/gui/about.cpp
+++ b/src/gui/about.cpp
@@ -50,19 +50,29 @@ About::About(QWidget* parent) : QDialog(parent), m_ui(new Ui::About)
this->done(0);
});
+ // Replace %VERSION% and %QTVERSION%
m_ui->aboutLabel->setText(
m_ui->aboutLabel->text().replace(QLatin1String("%VERSION%"), gVersion));
m_ui->aboutLabel->setText(
m_ui->aboutLabel->text().replace(QLatin1String("%QTVERSION%"), qVersion()));
+
+ // Replace %LICENSE%
+ QString licenseText;
+#if defined(_WIN32) && defined(RT_AUDIO)
+ licenseText = QLatin1String(
+ "This build of JackTrip includes support for ASIO. ASIO is a trademark and "
+ "software of Steinberg Media Technologies GmbH.
");
+#endif
#ifdef QT_OPENSOURCE
- m_ui->aboutLabel->setText(m_ui->aboutLabel->text().replace(
- QLatin1String("%LICENSE%"),
- QLatin1String("This build of JackTrip is subject to LGPL license. ")));
-#else
- m_ui->aboutLabel->setText(m_ui->aboutLabel->text().replace("%LICENSE%", ""));
+ licenseText += QLatin1String("This build of JackTrip is subject to LGPL license. ");
#endif
+ m_ui->aboutLabel->setText(
+ m_ui->aboutLabel->text().replace(QLatin1String("%LICENSE%"), licenseText));
+
+ // Replace %BUILD%
+ QString buildString;
if (!s_buildType.isEmpty() || !s_buildID.isEmpty()) {
- QString buildString = QStringLiteral("
(");
+ buildString = QStringLiteral("
(");
if (!s_buildType.isEmpty()) {
buildString.append(s_buildType);
if (!s_buildID.isEmpty()) {
@@ -72,12 +82,10 @@ About::About(QWidget* parent) : QDialog(parent), m_ui(new Ui::About)
buildString.append(QStringLiteral("Build %1").arg(s_buildID));
}
buildString.append(")");
- m_ui->aboutLabel->setText(
- m_ui->aboutLabel->text().replace(QLatin1String("%BUILD%"), buildString));
- } else {
- m_ui->aboutLabel->setText(m_ui->aboutLabel->text().replace(
- QLatin1String("%BUILD%"), QLatin1String("")));
}
+ m_ui->aboutLabel->setText(
+ m_ui->aboutLabel->text().replace(QLatin1String("%BUILD%"), buildString));
+
#ifdef __APPLE__
m_ui->aboutImage->setPixmap(QPixmap(":/qjacktrip/about@2x.png"));
#endif
diff --git a/src/gui/virtualstudio.cpp b/src/gui/virtualstudio.cpp
index 78c3fed..e11ac21 100644
--- a/src/gui/virtualstudio.cpp
+++ b/src/gui/virtualstudio.cpp
@@ -582,16 +582,16 @@ void VirtualStudio::setTestMode(bool test)
emit testModeChanged();
}
-QUrl VirtualStudio::studioToJoin()
+QString VirtualStudio::studioToJoin()
{
return m_studioToJoin;
}
-void VirtualStudio::setStudioToJoin(const QUrl& url)
+void VirtualStudio::setStudioToJoin(const QString& id)
{
- if (m_studioToJoin == url)
+ if (m_studioToJoin == id)
return;
- m_studioToJoin = url;
+ m_studioToJoin = id;
emit studioToJoinChanged();
}
@@ -620,36 +620,31 @@ QString VirtualStudio::failedMessage()
void VirtualStudio::joinStudio()
{
+ // nothing to do unless on setup or connected windows with studio to join
+ if ((m_windowState != "setup" && m_windowState != "connected")
+ || !m_auth->isAuthenticated() || m_studioToJoin.isEmpty())
+ return;
+
+ // make sure we've retrieved a list of servers
QMutexLocker locker(&m_refreshMutex);
- bool authenticated = m_auth->isAuthenticated();
- if (!authenticated || m_studioToJoin.isEmpty() || m_servers.isEmpty()) {
+ if (m_servers.isEmpty()) {
// No servers yet. Making sure we have them.
// getServerList emits refreshFinished which
// will come back to this function.
- if (authenticated && !m_studioToJoin.isEmpty() && m_servers.isEmpty()) {
- locker.unlock();
- getServerList(true);
- }
+ locker.unlock();
+ getServerList(true);
return;
}
- if (m_windowState != "connected") {
- return; // on audio setup screen before joining the studio
- }
- QString scheme = m_studioToJoin.scheme();
- QString path = m_studioToJoin.path();
- QString url = m_studioToJoin.toString();
- setStudioToJoin(QUrl(""));
+ // pop studioToJoin
+ const QString targetId = m_studioToJoin;
+ setStudioToJoin("");
+ emit studioToJoinChanged();
- m_failedMessage = "";
- if (scheme != "jacktrip" || path.length() <= 1) {
- m_failedMessage = "Invalid join request received: " + url;
- emit failedMessageChanged();
- emit failed();
- return;
- }
- QString targetId = path.remove(0, 1);
+ // stop audio if already running (settings or setup windows)
+ m_audioConfigPtr->stopAudio(true);
+ // find and populate data for current studio
VsServerInfoPointer sPtr;
for (const VsServerInfoPointer& s : m_servers) {
if (s->id() == targetId) {
@@ -659,14 +654,24 @@ void VirtualStudio::joinStudio()
}
locker.unlock();
- if (!sPtr.isNull()) {
- connectToStudio(*sPtr);
+ if (sPtr.isNull()) {
+ m_failedMessage = "Unable to find studio " + targetId;
+ emit failedMessageChanged();
+ emit failed();
return;
}
- m_failedMessage = "Unable to find studio " + targetId;
- emit failedMessageChanged();
- emit failed();
+ m_currentStudio = *sPtr;
+ emit currentStudioChanged();
+
+ if (m_windowState == "setup") {
+ m_audioConfigPtr->setSampleRate(m_currentStudio.sampleRate());
+ m_audioConfigPtr->startAudio();
+ return;
+ }
+
+ // m_windowState == "connected"
+ connectToStudio();
}
void VirtualStudio::toStandard()
@@ -806,15 +811,13 @@ void VirtualStudio::saveSettings()
m_audioConfigPtr->saveSettings();
}
-void VirtualStudio::connectToStudio(VsServerInfo& studio)
+void VirtualStudio::connectToStudio()
{
m_refreshTimer.stop();
m_networkStats = QJsonObject();
emit networkStatsChanged();
- m_currentStudio = studio;
- emit currentStudioChanged();
m_onConnectedScreen = true;
m_studioSocketPtr.reset(new VsWebSocket(
@@ -914,6 +917,7 @@ void VirtualStudio::completeConnection()
return;
}
jackTrip->setIOStatTimeout(m_cliSettings->getIOStatTimeout());
+ m_audioConfigPtr->setSampleRate(jackTrip->getSampleRate());
// this passes ownership to JackTrip
jackTrip->setAudioInterface(m_audioConfigPtr->newAudioInterface(jackTrip));
@@ -1104,20 +1108,34 @@ void VirtualStudio::openLink(const QString& link)
void VirtualStudio::handleDeeplinkRequest(const QUrl& link)
{
// check link is valid
- if (link.scheme() != QLatin1String("jacktrip")
- || link.host() != QLatin1String("join")) {
- qDebug() << "Ignoring invalid deeplink to " << link;
+ QString studioId;
+ if (link.scheme() != QLatin1String("jacktrip") || link.path().length() <= 1) {
+ qDebug() << "Ignoring invalid deeplink to" << link;
+ return;
+ }
+ if (link.host() == QLatin1String("join")) {
+ studioId = link.path().remove(0, 1);
+ } else if (link.host().isEmpty() && link.path().startsWith("join/")) {
+ studioId = link.path().remove(0, 5);
+ } else {
+ qDebug() << "Ignoring invalid deeplink to" << link;
return;
}
// check if already connected (ignore)
if (m_windowState == "connected" || m_windowState == "change_devices") {
- qDebug() << "Already connected; ignoring deeplink to " << link;
+ qDebug() << "Already connected; ignoring deeplink to" << link;
return;
}
- qDebug() << "Handling deeplink to " << link;
- setStudioToJoin(link);
+ if (m_windowState == "setup"
+ && (m_studioToJoin == studioId || m_currentStudio.id() == studioId)) {
+ qDebug() << "Already preparing to connect; ignoring deeplink to" << link;
+ return;
+ }
+
+ qDebug() << "Handling deeplink to" << link;
+ setStudioToJoin(studioId);
raiseToTop();
// Switch to virtual studio mode, if necessary
@@ -1132,21 +1150,7 @@ void VirtualStudio::handleDeeplinkRequest(const QUrl& link)
}
}
- // special case if on settings screen
- if (m_windowState == "settings") {
- if (showDeviceSetup()) {
- // audio is already active, so we can just flip screens
- setWindowState("setup");
- } else {
- // we need to stop audio before connecting
- setWindowState("connected");
- m_audioConfigPtr->stopAudio(true);
- joinStudio();
- }
- return;
- }
-
- // special case if on create_studio screen:
+ // automatically navigate if on certain window screens
// note that the studio creation happens inside of the web view,
// and the app doesn't really know anything about it. we depend
// on the web app triggering a deep link join event, which is
@@ -1154,27 +1158,15 @@ void VirtualStudio::handleDeeplinkRequest(const QUrl& link)
// noticed yet, so we don't join right away; otherwise we'd just
// get an unknown studio error. instead, we trigger a refresh and
// rely on it to kick off the join afterwards.
- if (m_windowState == "create_studio") {
- refreshStudios(0, true);
- if (showDeviceSetup()) {
- setWindowState("setup");
- m_audioConfigPtr->startAudio();
- } else {
- setWindowState("connected");
- }
- return;
- }
-
- // special case if on browsing and failed screens
- if (m_windowState == "browse" || m_windowState == "failed") {
+ if (m_windowState == "browse" || m_windowState == "create_studio"
+ || m_windowState == "settings" || m_windowState == "setup"
+ || m_windowState == "failed") {
if (showDeviceSetup()) {
setWindowState("setup");
- m_audioConfigPtr->startAudio();
} else {
setWindowState("connected");
- joinStudio();
}
- return;
+ refreshStudios(0, true);
}
// otherwise, assume we are on setup screens and can let the normal flow handle it
@@ -1267,7 +1259,7 @@ void VirtualStudio::connectionFinished()
} else {
m_audioConfigPtr->validateDevices(true);
}
- connectToStudio(m_currentStudio);
+ connectToStudio();
}
return;
}
@@ -1714,24 +1706,6 @@ void VirtualStudio::getUserMetadata()
});
}
-void VirtualStudio::stopStudio()
-{
- if (m_currentStudio.id() == "") {
- return;
- }
-
- QJsonObject json = {{QLatin1String("enabled"), false}};
- QJsonDocument request = QJsonDocument(json);
- m_currentStudio.setHost(QLatin1String(""));
- QNetworkReply* reply = m_api->updateServer(m_currentStudio.id(), request.toJson());
- connect(reply, &QNetworkReply::finished, this, [=]() {
- if (m_isExiting && !m_jackTripRunning) {
- emit signalExit();
- }
- reply->deleteLater();
- });
-}
-
bool VirtualStudio::readyToJoin()
{
// FTUX shows warnings and device setup views
diff --git a/src/gui/virtualstudio.h b/src/gui/virtualstudio.h
index d665166..f5d60e1 100644
--- a/src/gui/virtualstudio.h
+++ b/src/gui/virtualstudio.h
@@ -86,7 +86,7 @@ class VirtualStudio : public QObject
Q_PROPERTY(
QVector serverModel READ getServerModel NOTIFY serverModelChanged)
Q_PROPERTY(VsServerInfo* currentStudio READ currentStudio NOTIFY currentStudioChanged)
- Q_PROPERTY(QUrl studioToJoin READ studioToJoin WRITE setStudioToJoin NOTIFY
+ Q_PROPERTY(QString studioToJoin READ studioToJoin WRITE setStudioToJoin NOTIFY
studioToJoinChanged)
Q_PROPERTY(QJsonObject regions READ regions NOTIFY regionsChanged)
Q_PROPERTY(QJsonObject userMetadata READ userMetadata NOTIFY userMetadataChanged)
@@ -163,8 +163,8 @@ class VirtualStudio : public QObject
void setCollapseDeviceControls(bool collapseDeviceControls);
bool testMode();
void setTestMode(bool test);
- QUrl studioToJoin();
- void setStudioToJoin(const QUrl& url);
+ QString studioToJoin();
+ void setStudioToJoin(const QString& id);
bool showDeviceSetup();
void setShowDeviceSetup(bool show);
bool showWarnings();
@@ -264,9 +264,8 @@ class VirtualStudio : public QObject
void getSubscriptions();
void getRegions();
void getUserMetadata();
- void stopStudio();
bool readyToJoin();
- void connectToStudio(VsServerInfo& studio);
+ void connectToStudio();
void completeConnection();
private:
@@ -299,7 +298,7 @@ class VirtualStudio : public QObject
QTimer m_heartbeatTimer;
QTimer m_networkOutageTimer;
QMutex m_refreshMutex;
- QUrl m_studioToJoin;
+ QString m_studioToJoin;
QString m_updateChannel;
QString m_refreshToken;
QString m_userId;
diff --git a/src/gui/vs.qml b/src/gui/vs.qml
index 4607c15..9eee794 100644
--- a/src/gui/vs.qml
+++ b/src/gui/vs.qml
@@ -286,13 +286,10 @@ Rectangle {
}
if (virtualstudio.showWarnings) {
virtualstudio.windowState = "recommendations";
- } else if (virtualstudio.studioToJoin.toString() === "") {
+ } else if (virtualstudio.studioToJoin === "") {
virtualstudio.windowState = "browse";
- } else if (virtualstudio.showDeviceSetup) {
- virtualstudio.windowState = "setup";
- audio.startAudio();
} else {
- virtualstudio.windowState = "connected";
+ virtualstudio.windowState = virtualstudio.showDeviceSetup ? "setup" : "connected";
virtualstudio.joinStudio();
}
}
diff --git a/src/gui/vsAudio.cpp b/src/gui/vsAudio.cpp
index ef4ebed..3529d31 100644
--- a/src/gui/vsAudio.cpp
+++ b/src/gui/vsAudio.cpp
@@ -96,6 +96,13 @@ VsAudio::VsAudio(QObject* parent)
QJsonArray::fromStringList(QStringList(QLatin1String(""))))
, m_inputMixModeComboModel(QJsonArray::fromStringList(QStringList(QLatin1String(""))))
, m_audioWorkerPtr(new VsAudioWorker(this))
+ , m_workerThreadPtr(nullptr)
+ , m_inputMeterPluginPtr(nullptr)
+ , m_outputMeterPluginPtr(nullptr)
+ , m_inputVolumePluginPtr(nullptr)
+ , m_outputVolumePluginPtr(nullptr)
+ , m_monitorPluginPtr(nullptr)
+ , mHasErrors(false)
{
loadSettings();
@@ -261,6 +268,14 @@ void VsAudio::setFeedbackDetectionEnabled(bool enabled)
emit feedbackDetectionEnabledChanged();
}
+void VsAudio::setSampleRate(int sampleRate)
+{
+ if (m_audioSampleRate == sampleRate)
+ return;
+ m_audioSampleRate = sampleRate;
+ emit sampleRateChanged();
+}
+
void VsAudio::setBufferSize(int bufSize)
{
if (m_audioBufferSize == bufSize)
@@ -841,6 +856,11 @@ AudioInterface* VsAudio::newAudioInterface(JackTrip* jackTripPtr)
if (ifPtr == nullptr)
return ifPtr;
+ mHasErrors = false;
+ ifPtr->setErrorCallback([this, jackTripPtr](const std::string& errorText) {
+ this->errorCallback(errorText, jackTripPtr);
+ });
+
// AudioInterface::setup() can return a different buffer size
// if the audio interface doesn't support the one that was requested
if (ifPtr->getBufferSizeInSamples() != uint32_t(getBufferSize())) {
@@ -889,9 +909,7 @@ AudioInterface* VsAudio::newJackAudioInterface([[maybe_unused]] JackTrip* jackTr
jackTripPtr != nullptr, jackTripPtr);
ifPtr->setClientName(QStringLiteral("JackTrip"));
#if defined(__unix__)
- AudioInterface::setPipewireLatency(
- getBufferSize(),
- jackTripPtr == nullptr ? 48000 : jackTripPtr->getSampleRate());
+ AudioInterface::setPipewireLatency(getBufferSize(), getSampleRate());
#endif
ifPtr->setup(true);
}
@@ -919,7 +937,7 @@ AudioInterface* VsAudio::newRtAudioInterface([[maybe_unused]] JackTrip* jackTrip
inputChans, outputChans,
static_cast(getInputMixMode()),
m_audioBitResolution, jackTripPtr != nullptr, jackTripPtr);
- ifPtr->setSampleRate(jackTripPtr == nullptr ? 48000 : jackTripPtr->getSampleRate());
+ ifPtr->setSampleRate(getSampleRate());
ifPtr->setInputDevice(getInputDevice().toStdString());
ifPtr->setOutputDevice(getOutputDevice().toStdString());
ifPtr->setBufferSizeInSamples(getBufferSize());
@@ -944,6 +962,29 @@ AudioInterface* VsAudio::newRtAudioInterface([[maybe_unused]] JackTrip* jackTrip
return ifPtr;
}
+void VsAudio::errorCallback(const std::string& errorText,
+ [[maybe_unused]] JackTrip* jackTripPtr)
+{
+ const QString errorMsg(QString::fromStdString(errorText));
+ setDevicesErrorMsg(errorMsg);
+#ifdef _WIN32
+ // handle special case for Windows ASIO drivers that trigger
+ // asynchronous errors shortly after you try opening the
+ // RtAudio stream with a different sample rate (only for audio tester)
+ if (jackTripPtr == nullptr && getUseRtAudio()
+ && errorMsg.contains("sample rate changed")) {
+ // only refresh devices once
+ if (mHasErrors)
+ return;
+ mHasErrors = true;
+ // asynchronously refresh devices
+ refreshDevices(false);
+ }
+#else
+ mHasErrors = true;
+#endif
+}
+
// VsAudioWorker methods
VsAudioWorker::VsAudioWorker(VsAudio* ptr) : m_parentPtr(ptr) {}
diff --git a/src/gui/vsAudio.h b/src/gui/vsAudio.h
index e6e68dd..c91beb5 100644
--- a/src/gui/vsAudio.h
+++ b/src/gui/vsAudio.h
@@ -78,6 +78,8 @@ class VsAudio : public QObject
Q_PROPERTY(bool backendAvailable READ backendAvailable CONSTANT)
Q_PROPERTY(QString audioBackend READ getAudioBackend WRITE setAudioBackend NOTIFY
audioBackendChanged)
+ Q_PROPERTY(
+ int sampleRate READ getSampleRate WRITE setSampleRate NOTIFY sampleRateChanged)
Q_PROPERTY(
int bufferSize READ getBufferSize WRITE setBufferSize NOTIFY bufferSizeChanged)
Q_PROPERTY(int bufferStrategy READ getBufferStrategy WRITE setBufferStrategy NOTIFY
@@ -168,6 +170,7 @@ class VsAudio : public QObject
{
return getUseRtAudio() ? QStringLiteral("RtAudio") : QStringLiteral("JACK");
}
+ int getSampleRate() const { return m_audioSampleRate; }
int getBufferSize() const { return m_audioBufferSize; }
int getBufferStrategy() const { return m_bufferStrategy; }
int getNumInputChannels() const { return getUseRtAudio() ? m_numInputChannels : 2; }
@@ -222,6 +225,7 @@ class VsAudio : public QObject
// setters for state shared with QML
void setFeedbackDetectionEnabled(bool enabled);
void setAudioBackend(const QString& backend);
+ void setSampleRate(int sampleRate);
void setBufferSize(int bufSize);
void setBufferStrategy(int bufStrategy);
void setNumInputChannels(int numChannels);
@@ -258,6 +262,7 @@ class VsAudio : public QObject
void signalScanningDevicesChanged();
void deviceModelsInitializedChanged(bool initialized);
void audioBackendChanged(bool useRtAudio);
+ void sampleRateChanged();
void bufferSizeChanged();
void bufferStrategyChanged();
void numInputChannelsChanged(int numChannels);
@@ -314,6 +319,7 @@ class VsAudio : public QObject
void updateDeviceMessages(AudioInterface& audioInterface);
AudioInterface* newJackAudioInterface(JackTrip* jackTripPtr = nullptr);
AudioInterface* newRtAudioInterface(JackTrip* jackTripPtr = nullptr);
+ void errorCallback(const std::string& errorText, JackTrip* jackTripPtr = nullptr);
// range for volume meters
static constexpr float m_meterMax = 0.0;
@@ -329,6 +335,7 @@ class VsAudio : public QObject
bool m_scanningDevices = false;
bool m_feedbackDetectionEnabled = true;
bool m_deviceModelsInitialized = false;
+ int m_audioSampleRate = gDefaultSampleRate;
int m_audioBufferSize =
gDefaultBufferSizeInSamples; ///< Audio buffer size to process on each callback
int m_bufferStrategy = 0;
@@ -370,6 +377,7 @@ class VsAudio : public QObject
Volume* m_inputVolumePluginPtr;
Volume* m_outputVolumePluginPtr;
Monitor* m_monitorPluginPtr;
+ bool mHasErrors; ///< true if one or more error callbacks have been triggered
#ifndef NO_FEEDBACK
Analyzer* m_outputAnalyzerPluginPtr;
@@ -430,6 +438,7 @@ class VsAudioWorker : public QObject
int getNumOutputChannels() const { return m_parentPtr->getNumOutputChannels(); }
int getBaseInputChannel() const { return m_parentPtr->getBaseInputChannel(); }
int getBaseOutputChannel() const { return m_parentPtr->getBaseOutputChannel(); }
+ int getSampleRate() const { return m_parentPtr->getSampleRate(); }
int getBufferSize() const { return m_parentPtr->getBufferSize(); }
int getInputMixMode() const { return m_parentPtr->getInputMixMode(); }
const QString& getInputDevice() const { return m_parentPtr->getInputDevice(); }
diff --git a/src/gui/vsDeeplink.cpp b/src/gui/vsDeeplink.cpp
index 53bb88d..d3cafd1 100644
--- a/src/gui/vsDeeplink.cpp
+++ b/src/gui/vsDeeplink.cpp
@@ -132,7 +132,7 @@ void VsDeeplink::connectionReceived()
m_readyToExit = true;
}
- m_instanceCheckSocket->flush();
+ m_instanceCheckSocket->waitForBytesWritten();
m_instanceCheckSocket->disconnectFromServer(); // remove next
// let main thread know we are finished
@@ -171,21 +171,20 @@ void VsDeeplink::handleDeeplinkRequest()
// Receive URL from 2nd instance
QLocalSocket* connectedSocket = m_instanceServer->nextPendingConnection();
- if (!connectedSocket->waitForConnected()) {
- qDebug() << "Never received connection";
+ if (connectedSocket == nullptr || !connectedSocket->waitForConnected()) {
+ qDebug() << "Deeplink socket: never received connection";
return;
}
- if (!connectedSocket->waitForReadyRead()) {
- qDebug() << "Never ready to read";
- if (!(connectedSocket->bytesAvailable() > 0)) {
- qDebug() << "Not ready and no bytes available";
- return;
- }
+ if (!connectedSocket->waitForReadyRead()
+ && connectedSocket->bytesAvailable() <= 0) {
+ qDebug() << "Deeplink socket: not ready and no bytes available: "
+ << connectedSocket->errorString();
+ return;
}
if (connectedSocket->bytesAvailable() < (int)sizeof(quint16)) {
- qDebug() << "no bytes available";
+ qDebug() << "Deeplink socket: ready but no bytes available";
break;
}
diff --git a/src/jacktrip_globals.h b/src/jacktrip_globals.h
index 5e85b54..400bafe 100644
--- a/src/jacktrip_globals.h
+++ b/src/jacktrip_globals.h
@@ -40,7 +40,7 @@
#include "AudioInterface.h"
-constexpr const char* const gVersion = "2.2.1"; ///< JackTrip version
+constexpr const char* const gVersion = "2.2.2"; ///< JackTrip version
//*******************************************************************************
/// \name Default Values
@@ -88,6 +88,7 @@ constexpr const char* gDefaultLocalAddress = "";
constexpr int gDefaultRedundancy = 1;
constexpr int gTimeOutMultiThreadedServer = 10000; // seconds
constexpr int gWaitCounter = 60;
+constexpr int gUdpWaitTimeout = 30; // milliseconds
//@}
//*******************************************************************************
--
2.30.2