New upstream version 2.0.2+ds
authorIOhannes m zmölnig (Debian/GNU) <umlaeute@debian.org>
Tue, 5 Sep 2023 19:48:21 +0000 (21:48 +0200)
committerIOhannes m zmölnig (Debian/GNU) <umlaeute@debian.org>
Tue, 5 Sep 2023 19:48:21 +0000 (21:48 +0200)
30 files changed:
docs/Build/Linux.md
docs/changelog.yml
meson.build
meson_options.txt
releases/edge/mac-manifests.json
releases/edge/win-manifests.json
releases/stable/linux-manifests.json
releases/stable/mac-manifests.json
releases/stable/win-manifests.json
src/AudioInterface.cpp
src/AudioInterface.h
src/JackAudioInterface.cpp
src/RtAudioInterface.cpp
src/Settings.cpp
src/gui/AudioSettings.qml
src/gui/ChangeDevices.qml
src/gui/DeviceControls.qml
src/gui/DeviceControlsGroup.qml
src/gui/Meter.qml
src/gui/Recommendations.qml
src/gui/Settings.qml
src/gui/VolumeSlider.qml
src/gui/qjacktrip.cpp
src/gui/virtualstudio.cpp
src/gui/virtualstudio.h
src/gui/vsApi.cpp
src/gui/vsAudio.cpp
src/gui/vsPinger.cpp
src/gui/vsWebSocket.cpp
src/jacktrip_globals.h

index 4b56d37e349ae444fafc9384cd9ccff4403bd2d1..92058724ff1ade20ac3390ca16e407c841d31bd2 100644 (file)
@@ -105,6 +105,8 @@ the following parameters:
 
 * `qtversion`: Choose to build with either Qt5 or Qt6
 
+* `buildinfo`: Additional info used to describe the build
+
 For example:
 ```sh
 $ meson setup -Drtaudio=enabled builddir
index 2419558418f4c0339ba2de7a36ea412812282b30..e61bd7219ebba205323009856358957933728fad 100644 (file)
@@ -1,3 +1,14 @@
+- Version: "2.0.2"
+  Date: 2023-09-01
+  Description:
+  - (added) VS Mode latency categories for Linux audio devices
+  - (added) VS Mode audio warnings for high latency Linux devices
+  - (updated) Improved support for Pipewire latency on Linux
+  - (fixed) Crash on Windows when using the JACK audio backend
+  - (fixed) Include ALSA support for Linux builds using meson
+  - (fixed) VS Mode overlapping UI elements with max scaling
+  - (fixed) Don't require git to be present for meson builds
+  - (fixed) Linux man page description and meson build errors
 - Version: "2.0.1"
   Date: 2023-08-29
   Description:
index d4d2c946440bf9a4f4ae00d1d9a145cdf4f7e027..7b58a642a3a314e5987fbe3c95db725d9ab1d0ff 100644 (file)
@@ -36,12 +36,20 @@ if get_option('buildtype') == 'release'
        c_defines += ['-DNDEBUG']
 endif
 
-git_tags_cmd = run_command('git', 'describe', '--tags', check: false)
-git_hash_cmd = run_command('git', 'rev-parse', '--short', 'HEAD', check: false)
-if git_tags_cmd.returncode() == 0 and git_hash_cmd.returncode() == 0
-       git_tags = git_tags_cmd.stdout().strip()
-       git_hash = git_hash_cmd.stdout().strip()
-       defines += ['-DJACKTRIP_BUILD_INFO=' + git_tags + '-' + git_hash]
+build_info = get_option('buildinfo')
+git = find_program('git', required: false)
+if build_info == '' and git.found()
+       git_tags_cmd = run_command(git, 'describe', '--tags', check: false)
+       git_hash_cmd = run_command(git, 'rev-parse', '--short', 'HEAD', check: false)
+       if git_tags_cmd.returncode() == 0 and git_hash_cmd.returncode() == 0
+               git_tags = git_tags_cmd.stdout().strip()
+               git_hash = git_hash_cmd.stdout().strip()
+               build_info = git_tags + '-' + git_hash
+       endif
+endif
+if build_info != ''
+       message('Build info: ' + build_info)
+       defines += ['-DJACKTRIP_BUILD_INFO=' + build_info]
 endif
 
 src = [        'src/DataProtocol.cpp',
@@ -290,6 +298,7 @@ if (host_machine.system() == 'linux')
        if help2man.found()
                gzip = find_program('gzip', required: false)
                help2man_opts = [
+                       '--name="high-quality system for audio network performances"',
                        '--no-info',
                        '--section=1']
                manfile = custom_target('jacktrip.1',
@@ -299,8 +308,9 @@ if (host_machine.system() == 'linux')
                        install_dir: get_option('mandir') / 'man1')
                if gzip.found()
                        custom_target('jacktrip.1.gz',
+                               input: manfile,
                                output: 'jacktrip.1.gz',
-                               command: [gzip, '-k', manfile],
+                               command: [gzip, '-k', '-f', '@INPUT@'],
                                install: true,
                                install_dir: get_option('mandir') / 'man1')
                endif
index f4753a5a5e9b9542b126d47be314dadc3c654bcb..ca4f707251719f7963f12b73d020307ceab4e5fa 100644 (file)
@@ -8,4 +8,5 @@ option('vsftux', type : 'boolean', value : 'false', description: 'Build with Vir
 option('noupdater', type : 'boolean', value : 'false', description: 'Build without auto-update support')
 option('nofeedback', type : 'boolean', value : 'false', description: 'Build without feedback detection')
 option('profile', type: 'combo', choices: ['default', 'development'], value: 'default', description: 'Choose build profile / Sets desktop id accordingly')
-option('qtversion', type : 'combo', choices: ['', '5', '6'], description: 'Choose to build with either Qt5 or Qt6')
\ No newline at end of file
+option('qtversion', type : 'combo', choices: ['', '5', '6'], description: 'Choose to build with either Qt5 or Qt6')
+option('buildinfo', type : 'string', value : '', yield : true, description: 'Additional info used to describe the build')
\ No newline at end of file
index 1c78c87a53cc23b3474d5ed8ed5b26934a715de1..c9fdcc69f502bfe274b69707932a00bb6862c090 100644 (file)
@@ -1,6 +1,16 @@
 {
   "app_name": "JackTrip",
   "releases": [
+    {
+      "version": "2.0.1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.1",
+      "download": {
+        "date": "2023-08-30T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.1-macOS-x64-signed-installer.pkg",
+        "downloadSize": "177272934",
+        "sha256": "5f12f512cd372beb01c6c888c8bbbaf4c1e5cf61881db9cd91c5d2c8582531b5"
+      }
+    },
     {
       "version": "2.0.0",
       "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0",
index de0d8a442dab811c628fea756d7062b570ccbef9..7bc7e8688530c485edd688e78910692bdeb46292 100644 (file)
@@ -1,6 +1,16 @@
 {
   "app_name": "JackTrip",
   "releases": [
+    {
+      "version": "2.0.1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.1",
+      "download": {
+        "date": "2023-08-30T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.1-Windows-x64-signed-installer.msi",
+        "downloadSize": "95846400",
+        "sha256": "aeb8934b7ef5ab274a135f575b13f16e1e93b42cfc631cfefa611477ae092c23"
+      }
+    },
     {
       "version": "2.0.0",
       "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0",
index 6bbe9b19d7c53e689d9c06cfbc1d842345b91c01..955cfbc0f85294b63f9dba1b8e3a6a449be4e05d 100644 (file)
@@ -1,6 +1,16 @@
 {
   "app_name": "JackTrip",
   "releases": [
+    {
+      "version": "2.0.1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.1",
+      "download": {
+        "date": "2023-08-30T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.1-Linux-x64-binary.zip",
+        "downloadSize": "1216267",
+        "sha256": "eb7f10108e4cd2ef09b3df8b83f67187dc9174ac632dcb0ddd61087e65c3f0cb"
+      }
+    },
     {
       "version": "2.0.0",
       "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0",
index 4ef3dce1ee5d71019e27c2984616292b42d5a3f8..72b0e188f6749575de5d9b09de8260674b56bac4 100644 (file)
@@ -1,6 +1,16 @@
 {
   "app_name": "JackTrip",
   "releases": [
+    {
+      "version": "2.0.1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.1",
+      "download": {
+        "date": "2023-08-30T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.1-macOS-x64-signed-installer.pkg",
+        "downloadSize": "177272934",
+        "sha256": "5f12f512cd372beb01c6c888c8bbbaf4c1e5cf61881db9cd91c5d2c8582531b5"
+      }
+    },
     {
       "version": "2.0.0",
       "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0",
index 8ab9198fef096ddaadf7581b383575cc635f1765..019d67231a5a4ba59f0963c54ee693eb73c76a03 100644 (file)
@@ -1,6 +1,16 @@
 {
   "app_name": "JackTrip",
   "releases": [
+    {
+      "version": "2.0.1",
+      "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.1",
+      "download": {
+        "date": "2023-08-30T00:00:00Z",
+        "url": "https://files.jacktrip.org/app-builds/JackTrip-v2.0.1-Windows-x64-signed-installer.msi",
+        "downloadSize": "95846400",
+        "sha256": "aeb8934b7ef5ab274a135f575b13f16e1e93b42cfc631cfefa611477ae092c23"
+      }
+    },
     {
       "version": "2.0.0",
       "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v2.0.0",
index 15f9952c82cacc89248e3eeb29dc584b522db1c3..af36cfe3ff6cc141c29b0f4d93266d85e7c01380 100644 (file)
@@ -741,6 +741,18 @@ void AudioInterface::fromBitToSampleConversion(
     }
 }
 
+//*******************************************************************************
+void AudioInterface::setPipewireLatency(unsigned int bufferSize, unsigned int sampleRate)
+{
+    if (bufferSize == 0 || sampleRate == 0)
+        return;
+#if defined(__unix__)
+    char latency_env[40];
+    sprintf(latency_env, "%d/%d", bufferSize, sampleRate);
+    setenv("PIPEWIRE_LATENCY", latency_env, 1);
+#endif
+}
+
 //*******************************************************************************
 void AudioInterface::appendProcessPluginToNetwork(ProcessPlugin* plugin)
 {
@@ -953,6 +965,13 @@ void AudioInterface::setDevicesWarningMsg(warningMessageT msg)
         mWarningHelpUrl  = "https://help.jacktrip.org/hc/en-us/articles/4409919243155";
         mHighLatencyFlag = true;
         break;
+    case DEVICE_WARN_ALSA_LATENCY:
+        mWarningMsg =
+            "You audio device drivers may cause high latency or audio delay. Use "
+            "JACK backend or Linux ALSA drivers to reduce audio delays.";
+        mWarningHelpUrl  = "";
+        mHighLatencyFlag = true;
+        break;
     default:
         mWarningMsg      = "";
         mWarningHelpUrl  = "";
index 80eb8a15087fb3079ce3475845260519ffaa69d9..7511ccb818d1d7490b3b7e96ee4561a43bc99810 100644 (file)
@@ -79,7 +79,8 @@ class AudioInterface
     enum warningMessageT {
         DEVICE_WARN_NONE,
         DEVICE_WARN_BUFFER_LATENCY,
-        DEVICE_WARN_ASIO_LATENCY
+        DEVICE_WARN_ASIO_LATENCY,
+        DEVICE_WARN_ALSA_LATENCY
     };
 
     enum errorMessageT {
@@ -192,6 +193,9 @@ class AudioInterface
         const int8_t* const input, sample_t* output,
         const AudioInterface::audioBitResolutionT sourceBitResolution);
 
+    /** \brief Sets PIPEWIRE_LATENCY environment variable on unix */
+    static void setPipewireLatency(unsigned int bufferSize, unsigned int sampleRate);
+
     //--------------SETTERS---------------------------------------------
     virtual void setInputChannels(QVarLengthArray<int> inputChans)
     {
index 136f14d0acc1a471c03d60558cd3c1b2db88d8e6..349bd957a9f67c36776e1ea7587b26b2feb9b057 100644 (file)
@@ -356,7 +356,7 @@ void JackAudioInterface::connectDefaultPorts()
                 break;
             }
         }
-        std::free(ports);
+        jack_free(ports);
     }
 
     // Get physical input (playback) ports
@@ -374,6 +374,6 @@ void JackAudioInterface::connectDefaultPorts()
                 break;
             }
         }
-        std::free(ports);
+        jack_free(ports);
     }
 }
index 0b63d13d31298fb286f84d499cb20156cd29a586..890809d89231ec37557bd4ed3bc8bb9ef154d3ad 100644 (file)
@@ -60,8 +60,8 @@ void RtAudioDevice::print() const
 //*******************************************************************************
 void RtAudioDevice::printVerbose() const
 {
-    cout << "Audio Device  [" << this->api << " - " << this->ID << "] : " << this->name
-         << endl;
+    cout << "Audio Device  [" << RtAudio::getApiDisplayName(this->api) << " - "
+         << this->ID << "] : " << this->name << endl;
     cout << "  Output Channels : " << this->outputChannels << endl;
     cout << "  Input Channels  : " << this->inputChannels << endl;
     cout << "  Supported Sampling Rates: ";
@@ -230,6 +230,13 @@ void RtAudioInterface::setup(bool verbose)
             AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE);
             AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_SAME_ASIO);
         }
+#else
+        if (in_device.api == RtAudio::LINUX_PULSE
+            || in_device.api == RtAudio::LINUX_OSS) {
+            AudioInterface::setDevicesWarningMsg(
+                AudioInterface::DEVICE_WARN_ALSA_LATENCY);
+            AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NONE);
+        }
 #endif
     } else {
         AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE);
index fa0e167ee568db71db489819521d178f082b249f..ab35547e5c0b85a74d6e5618b2e358d684277251 100644 (file)
@@ -1125,9 +1125,7 @@ JackTrip* Settings::getConfiguredJackTrip()
 
 #if defined(__unix__)
     if (mChangeDefaultBS or mChangeDefaultSR) {
-        char latency_env[40];
-        sprintf(latency_env, "%d/%d", mAudioBufferSize, mSampleRate);
-        setenv("PIPEWIRE_LATENCY", latency_env, 1);
+        AudioInterface::setPipewireLatency(mAudioBufferSize, mSampleRate);
     }
 #endif
 
index ebf77753943ef964fe3d444defa51da255d1a04c..469f39ef349ad7c6ee04050b503d1ca117e8f45e 100644 (file)
@@ -161,8 +161,8 @@ Rectangle {
                                 outputCombo.currentIndex = index
                                 outputCombo.popup.close()
                                 audio.outputDevice = modelData.text
-                                if (modelData.category === "Low-Latency (ASIO)") {
-                                    let inputComboIdx = inputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text);
+                                if (modelData.category.startsWith("Low-Latency")) {
+                                    let inputComboIdx = inputCombo.model.findIndex(it => it.category.startsWith("Low-Latency") && it.text === modelData.text);
                                     if (inputComboIdx !== null && inputComboIdx !== undefined) {
                                         inputCombo.currentIndex = inputComboIdx;
                                         audio.inputDevice = modelData.text
@@ -346,8 +346,8 @@ Rectangle {
                                 inputCombo.currentIndex = index
                                 inputCombo.popup.close()
                                 audio.inputDevice = modelData.text
-                                if (modelData.category === "Low-Latency (ASIO)") {
-                                    let outputComboIdx = outputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text);
+                                if (modelData.category.startsWith("Low-Latency")) {
+                                    let outputComboIdx = outputCombo.model.findIndex(it => it.category.startsWith("Low-Latency") && it.text === modelData.text);
                                     if (outputComboIdx !== null && outputComboIdx !== undefined) {
                                         outputCombo.currentIndex = outputComboIdx;
                                         audio.outputDevice = modelData.text
index 9eea3c94378fd884011567ecb4a4d56267d1b486..76a2ae78784164e6263d1ef2f640b374d0c81fa4 100644 (file)
@@ -162,8 +162,8 @@ Rectangle {
                                 outputCombo.currentIndex = index
                                 outputCombo.popup.close()
                                 audio.outputDevice = modelData.text
-                                if (modelData.category === "Low-Latency (ASIO)") {
-                                    let inputComboIdx = inputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text);
+                                if (modelData.category.startsWith("Low-Latency")) {
+                                    let inputComboIdx = inputCombo.model.findIndex(it => it.category.startsWith("Low-Latency") && it.text === modelData.text);
                                     if (inputComboIdx !== null && inputComboIdx !== undefined) {
                                         inputCombo.currentIndex = inputComboIdx;
                                         audio.inputDevice = modelData.text
@@ -299,8 +299,8 @@ Rectangle {
                                 inputCombo.currentIndex = index
                                 inputCombo.popup.close()
                                 audio.inputDevice = modelData.text
-                                if (modelData.category === "Low-Latency (ASIO)") {
-                                    let outputComboIdx = outputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text);
+                                if (modelData.category.startsWith("Low-Latency")) {
+                                    let outputComboIdx = outputCombo.model.findIndex(it => it.category.startsWith("Low-Latency") && it.text === modelData.text);
                                     if (outputComboIdx !== null && outputComboIdx !== undefined) {
                                         outputCombo.currentIndex = outputComboIdx;
                                         audio.outputDevice = modelData.text
index 8dd779ba2becfc52ab721c0c8ef21e140add1ee8..4a84c2b33812a38b652f56ed538f5d3b6d668211 100644 (file)
@@ -68,7 +68,7 @@ Item {
 
     Component {
         id: inputControls
-        
+
         ColumnLayout {
             anchors.fill: parent
             spacing: 2
@@ -127,7 +127,7 @@ Item {
 
                 Item {
                     Layout.fillHeight: true
-                    Layout.preferredWidth: 100
+                    Layout.preferredWidth: 100 * virtualstudio.uiScale
 
                     Loader {
                         id: typeIconIndicator
@@ -163,8 +163,8 @@ Item {
 
                     Meter {
                         anchors.fill: parent
-                        anchors.topMargin: 5 * virtualstudio.uiScale
-                        anchors.rightMargin: 8 * virtualstudio.uiScale
+                        anchors.topMargin: 5
+                        anchors.rightMargin: 8
                         model: isInput ? audio.inputMeterLevels : audio.outputMeterLevels
                         clipped: isInput ? audio.inputClipped : audio.outputClipped
                         enabled: true
index aaa8c0b6b34a41f68f3672de2dd61b2714b6d960..d0bb21650d251b9289404f43b4c0dc63337e1cef 100644 (file)
@@ -8,7 +8,7 @@ Rectangle {
     property string disabledButtonText: "#D3D4D4"
     property string saveButtonText: "#DB0A0A"
     property int minifiedHeight: 36
-    property int fullHeight: 80
+    property int fullHeight: 84
 
     id: deviceControlsGroup
     width: parent.width
index 16d0d4c9693047ffc7e7b1a5a8f71743b0ecd903..292cef96af6965f7784d3236a662f4eb21c86eb5 100644 (file)
@@ -6,7 +6,7 @@ Item {
     property int bins: Math.max(15, width/20)
     property int innerMargin: 2 * virtualstudio.uiScale
     property int boxRadius: 3 * virtualstudio.uiScale
-    property int boxThickness: 12 * virtualstudio.uiScale
+    property int boxThickness: 12
     required property bool clipped
     property bool enabled: true
     property string meterColor: enabled ? (virtualstudio.darkMode ? "#5B5858" : "#D3D4D4") : (virtualstudio.darkMode ? "#7b7979" : "#EAECEC")
index b0bf0394c9e78a6ce99c5d2b678aed74f87643dd..f6a3123366ef46c58b6794545d227203abbfdd82 100644 (file)
@@ -68,7 +68,7 @@ Item {
             anchors.rightMargin: 32 * virtualstudio.uiScale
             anchors.verticalCenter: parent.verticalCenter
         }
-        
+
         Text {
             id: gettingStartedText2
             visible: recommendationScreen === "fiber"
@@ -449,7 +449,7 @@ Item {
 
         Item {
             id: acknowledgedButtonsContainer
-            width: 320
+            width: 320 * virtualstudio.uiScale
 
             anchors.top: acknowledgedSubheader.bottom
             anchors.topMargin: 64 * virtualstudio.uiScale
@@ -493,7 +493,6 @@ Item {
                 }
             }
 
-
             Button {
                 id: acknowledgedNoButton
                 anchors.right: parent.right
index 312f56837f9c07cd49f240745a509dff78ff281e..0831e01ac6729f1498867aae30ce18597cfcc356 100644 (file)
@@ -464,7 +464,6 @@ Item {
                 audio.restartAudio();
             }
             font.family: "Poppins"
-            enabled: audio.audioBackend != "JACK"
         }
 
         Text {
@@ -472,7 +471,6 @@ Item {
             x: 48 * virtualstudio.uiScale
             text: "Buffer Size"
             font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
-            visible: audio.audioBackend != "JACK"
             color: textColour
         }
 
index b076199f97c4df5f9c2dd0b50c5e066dfc027a77..1f2a467c59ee452005de042cc516bdd1042ef44e 100644 (file)
@@ -46,8 +46,8 @@ Item {
             anchors.left: showLabel ? tooltip.right : parent.left
             anchors.leftMargin: showLabel ? 8 * virtualstudio.uiScale : 0
             anchors.verticalCenter: label.verticalCenter
-            width: 16 * virtualstudio.uiScale
-            height: 16 * virtualstudio.uiScale
+            width: 16
+            height: 16
             icon.source: "quiet.svg"
             color: iconColor
         }
@@ -56,8 +56,8 @@ Item {
             id: louderIcon
             anchors.right: parent.right
             anchors.verticalCenter: label.verticalCenter
-            width: 18 * virtualstudio.uiScale
-            height: 18 * virtualstudio.uiScale
+            width: 18
+            height: 18
             icon.source: "loud.svg"
             color: iconColor
         }
@@ -110,8 +110,8 @@ Item {
             handle: Rectangle {
                 x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width)
                 y: slider.topPadding + slider.availableHeight / 2 - height / 2
-                implicitWidth: 20 * virtualstudio.uiScale
-                implicitHeight: 20 * virtualstudio.uiScale
+                implicitWidth: 18 * virtualstudio.uiScale
+                implicitHeight: 18 * virtualstudio.uiScale
                 radius: implicitWidth / 2
                 color: slider.pressed ? sliderPressedColour : "white"
                 border.width: 3
index 86b6ee40121384551a3e7171d96210796d888616..0cfbabee1701a63b247ac1215c1a483220960e0f 100644 (file)
@@ -930,11 +930,11 @@ void QJackTrip::start()
 
 #ifdef RT_AUDIO
             if (m_ui->backendComboBox->currentIndex() == 1) {
+                unsigned int bufferSize = m_ui->bufferSizeComboBox->currentText().toInt();
+                unsigned int sampleRate = m_ui->sampleRateComboBox->currentText().toInt();
                 m_jackTrip->setAudiointerfaceMode(JackTrip::RTAUDIO);
-                m_jackTrip->setSampleRate(
-                    m_ui->sampleRateComboBox->currentText().toInt());
-                m_jackTrip->setAudioBufferSizeInSamples(
-                    m_ui->bufferSizeComboBox->currentText().toInt());
+                m_jackTrip->setSampleRate(sampleRate);
+                m_jackTrip->setAudioBufferSizeInSamples(bufferSize);
                 // we assume that first entry is "(default)"
                 if (m_ui->inputDeviceComboBox->currentIndex() == 0) {
                     m_jackTrip->setInputDevice("");
@@ -948,6 +948,7 @@ void QJackTrip::start()
                     m_jackTrip->setOutputDevice(
                         m_ui->outputDeviceComboBox->currentText().toStdString());
                 }
+                AudioInterface::setPipewireLatency(bufferSize, sampleRate);
             }
 #endif
 
index 0da9a3bfa8e85ed4d7b4c5f6917fc598cc859901..a1fd4728e23ea307f2d166c7c5bf77db8ba503b5 100644 (file)
@@ -496,7 +496,6 @@ void VirtualStudio::setTestMode(bool test)
         m_devicePtr->disconnect();
         m_devicePtr.reset();
     }
-    m_webChannelServer->close();
 
     m_testMode = test;
 
@@ -512,6 +511,9 @@ void VirtualStudio::setTestMode(bool test)
     settings.remove(QStringLiteral("UserId"));
     settings.endGroup();
 
+    // stop timers, clear data, etc.
+    resetState();
+
     // clear user data
     m_userMetadata = QJsonObject();
     m_userId.clear();
@@ -621,9 +623,10 @@ void VirtualStudio::toStandard()
     m_uiMode = QJackTrip::STANDARD;
     settings.setValue(QStringLiteral("UiMode"), m_uiMode);
 
-    m_webChannelServer->close();
-    m_refreshTimer.stop();
-    m_heartbeatTimer.stop();
+    // stop timers, clear data, etc.
+    resetState();
+    setWindowState(QStringLiteral("start"));
+    m_auth->logout();
 
     if (m_showFirstRun) {
         m_showFirstRun = false;
@@ -663,8 +666,6 @@ void VirtualStudio::logout()
         m_devicePtr.reset();
     }
 
-    m_webChannelServer->close();
-
     QUrl logoutURL = QUrl("https://auth.jacktrip.org/v2/logout");
     QUrlQuery query;
     query.addQueryItem(QStringLiteral("client_id"), AUTH_CLIENT_ID);
@@ -687,6 +688,9 @@ void VirtualStudio::logout()
     settings.remove(QStringLiteral("UserId"));
     settings.endGroup();
 
+    // stop timers, clear data, etc.
+    resetState();
+
     // clear user data
     m_refreshToken.clear();
     m_userMetadata = QJsonObject();
@@ -1081,10 +1085,9 @@ void VirtualStudio::exit()
     m_isExiting = true;
     emit isExitingChanged();
 
-    m_startTimer.stop();
-    m_refreshTimer.stop();
-    m_heartbeatTimer.stop();
-    m_networkOutageTimer.stop();
+    // stop timers, clear data, etc.
+    resetState();
+
     if (m_onConnectedScreen) {
         // manually disconnect on self-managed studios
         if (!m_currentStudio.id().isEmpty() && !m_currentStudio.isManaged()) {
@@ -1236,8 +1239,6 @@ void VirtualStudio::processError(const QString& errorMessage)
     }
     msgBox.exec();
 
-    if (shouldSwitchToRtAudio)
-        m_audioConfigPtr->setAudioBackend("RtAudio");
     if (m_jackTripRunning)
         connectionFinished();
 }
@@ -1331,6 +1332,16 @@ void VirtualStudio::sendHeartbeat()
     }
 }
 
+void VirtualStudio::resetState()
+{
+    m_webChannelServer->close();
+    m_refreshTimer.stop();
+    m_heartbeatTimer.stop();
+    m_startTimer.stop();
+    m_networkOutageTimer.stop();
+    m_firstRefresh = true;
+}
+
 void VirtualStudio::getServerList(bool signalRefresh, int index)
 {
     QMutexLocker refreshLock(&m_refreshMutex);
@@ -1518,6 +1529,10 @@ void VirtualStudio::getServerList(bool signalRefresh, int index)
 
 void VirtualStudio::getSubscriptions()
 {
+    if (m_userId.isEmpty()) {
+        qDebug() << "Invalid user ID";
+        return;
+    }
     QNetworkReply* reply = m_api->getSubscriptions(m_userId);
     connect(reply, &QNetworkReply::finished, this, [&, reply]() {
         if (reply->error() != QNetworkReply::NoError) {
index e5b1ab216ae64feb365005cdc691fa0bf7e6b138..076fa3c4c818f04d0ad73f1f8c50be76652cdf65 100644 (file)
@@ -256,6 +256,7 @@ class VirtualStudio : public QObject
     void exit();
 
    private:
+    void resetState();
     void getServerList(bool signalRefresh = false, int index = -1);
     void getSubscriptions();
     void getRegions();
index a177e8f15145c388b59913b985bf45179754092b..2a50de29aba978d595671a9ded1c1ca831bd30dc 100644 (file)
@@ -37,6 +37,8 @@
 
 #include "vsApi.h"
 
+#include "../jacktrip_globals.h"
+
 VsApi::VsApi(QNetworkAccessManager* networkAccessManager)
 {
     m_networkAccessManager = networkAccessManager;
@@ -112,6 +114,8 @@ QNetworkReply* VsApi::deleteDevice(const QString& deviceId)
 QNetworkReply* VsApi::get(const QUrl& url)
 {
     QNetworkRequest request = QNetworkRequest(url);
+    request.setRawHeader(QByteArray("User-Agent"),
+                         QString("JackTrip/%1 (Qt)").arg(gVersion).toUtf8());
     request.setRawHeader(QByteArray("Authorization"),
                          QString("Bearer %1").arg(m_accessToken).toUtf8());
 
@@ -122,6 +126,8 @@ QNetworkReply* VsApi::get(const QUrl& url)
 QNetworkReply* VsApi::post(const QUrl& url, const QByteArray& data)
 {
     QNetworkRequest request = QNetworkRequest(url);
+    request.setRawHeader(QByteArray("User-Agent"),
+                         QString("JackTrip/%1 (Qt)").arg(gVersion).toUtf8());
     request.setRawHeader(QByteArray("Authorization"),
                          QString("Bearer %1").arg(m_accessToken).toUtf8());
     request.setRawHeader(QByteArray("Content-Type"),
@@ -134,6 +140,8 @@ QNetworkReply* VsApi::post(const QUrl& url, const QByteArray& data)
 QNetworkReply* VsApi::put(const QUrl& url, const QByteArray& data)
 {
     QNetworkRequest request = QNetworkRequest(url);
+    request.setRawHeader(QByteArray("User-Agent"),
+                         QString("JackTrip/%1 (Qt)").arg(gVersion).toUtf8());
     request.setRawHeader(QByteArray("Authorization"),
                          QString("Bearer %1").arg(m_accessToken).toUtf8());
     request.setRawHeader(QByteArray("Content-Type"),
@@ -145,6 +153,8 @@ QNetworkReply* VsApi::put(const QUrl& url, const QByteArray& data)
 QNetworkReply* VsApi::deleteResource(const QUrl& url)
 {
     QNetworkRequest request = QNetworkRequest(url);
+    request.setRawHeader(QByteArray("User-Agent"),
+                         QString("JackTrip/%1 (Qt)").arg(gVersion).toUtf8());
     request.setRawHeader(QByteArray("Authorization"),
                          QString("Bearer %1").arg(m_accessToken).toUtf8());
 
index f1087470353719dff46bedb15361ed8a7f8b39dd..4227ac40a6e90a2cdb504ef04e557afab93a4c52 100644 (file)
@@ -236,7 +236,6 @@ void VsAudio::setAudioBackend(const QString& backend)
         if (getUseRtAudio())
             return;
         m_backend = AudioBackendType::RTAUDIO;
-        refreshDevices();
     } else {
         if (!getUseRtAudio())
             return;
@@ -253,7 +252,7 @@ void VsAudio::setFeedbackDetectionEnabled(bool enabled)
     emit feedbackDetectionEnabledChanged();
 }
 
-void VsAudio::setBufferSize([[maybe_unused]] int bufSize)
+void VsAudio::setBufferSize(int bufSize)
 {
     if (m_audioBufferSize == bufSize)
         return;
@@ -790,6 +789,10 @@ AudioInterface* VsAudio::newAudioInterface(JackTrip* jackTripPtr)
 {
     AudioInterface* ifPtr = nullptr;
 
+#if defined(__unix__)
+    AudioInterface::setPipewireLatency(getBufferSize(), m_sampleRate);
+#endif
+
     // Create AudioInterface Client Object
     if (isBackendAvailable<AudioInterfaceMode::ALL>() && jackIsAvailable()) {
         // all backends area available
@@ -860,9 +863,9 @@ AudioInterface* VsAudio::newAudioInterface(JackTrip* jackTripPtr)
 
 AudioInterface* VsAudio::newJackAudioInterface([[maybe_unused]] JackTrip* jackTripPtr)
 {
-    static const int numJackChannels = 2;
-    AudioInterface* ifPtr            = nullptr;
+    AudioInterface* ifPtr = nullptr;
 #ifndef NO_JACK
+    static const int numJackChannels = 2;
     if constexpr (isBackendAvailable<AudioInterfaceMode::ALL>()
                   || isBackendAvailable<AudioInterfaceMode::JACK>()) {
         QVarLengthArray<int> inputChans;
@@ -970,6 +973,7 @@ void VsAudioWorker::openAudioInterface()
                 updateDeviceModels();
             } else {
                 m_parentPtr->setAudioBackend("RtAudio");
+                updateDeviceModels();
             }
         }
 #endif
@@ -984,20 +988,13 @@ void VsAudioWorker::openAudioInterface()
     }
 
     if (m_audioInterfacePtr.isNull()) {
-        emit signalError(QStringLiteral("Failed to initialize audio interface"));
         return;
     }
 
     // initialize plugins and start the audio callback process
     m_audioInterfacePtr->initPlugins(false);
     m_audioInterfacePtr->startProcess();
-
-    if (m_parentPtr->m_backend == VsAudio::AudioBackendType::JACK) {
-        // this crashes on windows
-#ifndef _WIN32
-        m_audioInterfacePtr->connectDefaultPorts();
-#endif
-    }
+    m_audioInterfacePtr->connectDefaultPorts();
 
     m_parentPtr->updateDeviceMessages(*m_audioInterfacePtr);
     m_parentPtr->setAudioReady(true);
@@ -1115,24 +1112,29 @@ void VsAudioWorker::getDeviceList(const QVector<RtAudioDevice>& devices,
             channels.append(devices[n].outputChannels);
         }
 
-#ifdef _WIN32
         switch (devices[n].api) {
         case RtAudio::WINDOWS_ASIO:
             categories.append("Low-Latency (ASIO)");
             break;
         case RtAudio::WINDOWS_WASAPI:
-            categories.append("High-Latency (Non-ASIO)");
+            categories.append("High-Latency (WASAPI)");
             break;
         case RtAudio::WINDOWS_DS:
-            categories.append("High-Latency (Non-ASIO)");
+            categories.append("High-Latency (DirectSound)");
+            break;
+        case RtAudio::LINUX_ALSA:
+            categories.append("Low-Latency (ALSA)");
+            break;
+        case RtAudio::LINUX_PULSE:
+            categories.append("High-Latency (Pulse)");
+            break;
+        case RtAudio::LINUX_OSS:
+            categories.append("High-Latency (OSS)");
             break;
         default:
             categories.append("");
             break;
         }
-#else
-        categories.append("");
-#endif
     }
 }
 
index 8e30df3016c3470bf4ea833d389963eb2ba62622..95207b681f4e547ba14bd3e7a366037df316b4f8 100644 (file)
@@ -58,8 +58,14 @@ VsPinger::VsPinger(QString scheme, QString host, QString path)
     connect(&mSocket, &QWebSocket::binaryMessageReceived, this,
             &VsPinger::onReceivePingMessage);
     connect(&mSocket, &QWebSocket::connected, this, &VsPinger::onConnected);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+    connect(&mSocket,
+            QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::errorOccurred), this,
+            &VsPinger::onError);
+#else
     connect(&mSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error),
             this, &VsPinger::onError);
+#endif
     connect(&mTimer, &QTimer::timeout, this, &VsPinger::onPingTimer);
 }
 
index d217cc865862f60e0e3e072779f11a9f6752989b..87c9ab0e2b804936714717178e36f50379be1c43 100644 (file)
@@ -55,9 +55,15 @@ VsWebSocket::VsWebSocket(const QUrl& url, QString token, QString apiPrefix,
     connect(m_webSocket.get(),
             QOverload<const QList<QSslError>&>::of(&QWebSocket::sslErrors), this,
             &VsWebSocket::onSslErrors);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+    connect(m_webSocket.get(),
+            QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::errorOccurred), this,
+            &VsWebSocket::onError);
+#else
     connect(m_webSocket.get(),
             QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this,
             &VsWebSocket::onError);
+#endif
     connect(m_webSocket.get(), &QWebSocket::textMessageReceived, this,
             &VsWebSocket::textMessageReceived);
 }
index 8cee2524aeb9a7c5ccaeedffb8297b08124f49b4..16bad7b513d8dd9d110f1ea7c03a82a733760a22 100644 (file)
@@ -40,7 +40,7 @@
 
 #include "AudioInterface.h"
 
-constexpr const char* const gVersion = "2.0.1";  ///< JackTrip version
+constexpr const char* const gVersion = "2.0.2";  ///< JackTrip version
 
 //*******************************************************************************
 /// \name Default Values