diff -Nru jacktrip-1.6.8+ds/build jacktrip-1.7.0+ds/build --- jacktrip-1.6.8+ds/build 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/build 2023-01-24 19:46:32.000000000 +0000 @@ -4,7 +4,7 @@ # Parse command line options clean=1 install=0 -CONFIG= +CONFIG="DEFINES+=JACKTRIP_BUILD_INFO=\\\"$(git describe --tags)-$(git rev-parse --short HEAD)\\\"" UNKNOWN_OPTIONS= BUILD_RTAUDIO=0 RTAUDIO=0 diff -Nru jacktrip-1.6.8+ds/CMakeLists.txt jacktrip-1.7.0+ds/CMakeLists.txt --- jacktrip-1.6.8+ds/CMakeLists.txt 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/CMakeLists.txt 2023-01-24 19:46:32.000000000 +0000 @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.12) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.14) +set(CMAKE_CXX_STANDARD 17) project(QJackTrip) set(nogui FALSE) @@ -186,6 +187,7 @@ src/gui/vsAudioInterface.cpp src/gui/vsUrlHandler.cpp src/gui/vsWebSocket.cpp + src/gui/vsPermissions.cpp src/gui/qjacktrip.qrc src/Volume.cpp src/Tone.cpp @@ -193,6 +195,9 @@ src/JTApplication.h src/gui/vsQmlClipboard.h ) + if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set (qjacktrip_SRC ${qjacktrip_SRC} src/gui/vsMacPermissions.mm) + endif () else () set (qjacktrip_SRC ${qjacktrip_SRC} src/gui/qjacktrip_novs.qrc) endif () @@ -219,6 +224,9 @@ set (qjacktrip_SRC ${qjacktrip_SRC} src/gui/NoNap.mm) set (CMAKE_C_FLAGS "-x objective-c") set (CMAKE_EXE_LINKER_FLAGS "-framework Foundation") + if (NOT novs) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AVFoundation") + endif () endif () endif () diff -Nru jacktrip-1.6.8+ds/debian/changelog jacktrip-1.7.0+ds/debian/changelog --- jacktrip-1.6.8+ds/debian/changelog 2022-12-11 21:12:23.000000000 +0000 +++ jacktrip-1.7.0+ds/debian/changelog 2023-01-27 10:00:45.000000000 +0000 @@ -1,3 +1,14 @@ +jacktrip (1.7.0+ds-1) unstable; urgency=medium + + * New upstream version 1.7.0+ds + * Use wrapper-script to launch jacktrip from .desktop + * Simplify 'clean' target + * Update d/copyright + + Re-generate d/copyright_hints + * Bump standards version to 4.6.2 + + -- IOhannes m zmölnig (Debian/GNU) Fri, 27 Jan 2023 11:00:45 +0100 + jacktrip (1.6.8+ds-1) unstable; urgency=medium * New upstream version 1.6.8+ds diff -Nru jacktrip-1.6.8+ds/debian/clean jacktrip-1.7.0+ds/debian/clean --- jacktrip-1.6.8+ds/debian/clean 1970-01-01 00:00:00.000000000 +0000 +++ jacktrip-1.7.0+ds/debian/clean 2023-01-27 10:00:45.000000000 +0000 @@ -0,0 +1 @@ +debian/build/ diff -Nru jacktrip-1.6.8+ds/debian/control jacktrip-1.7.0+ds/debian/control --- jacktrip-1.6.8+ds/debian/control 2022-12-11 21:12:23.000000000 +0000 +++ jacktrip-1.7.0+ds/debian/control 2023-01-27 10:00:45.000000000 +0000 @@ -19,7 +19,7 @@ python3-yaml:native, qtbase5-dev, qtdeclarative5-dev, -Standards-Version: 4.6.1 +Standards-Version: 4.6.2 Rules-Requires-Root: no Homepage: https://github.com/jacktrip/jacktrip/ Vcs-Git: https://salsa.debian.org/multimedia-team/jacktrip.git diff -Nru jacktrip-1.6.8+ds/debian/copyright jacktrip-1.7.0+ds/debian/copyright --- jacktrip-1.6.8+ds/debian/copyright 2022-12-11 21:12:23.000000000 +0000 +++ jacktrip-1.7.0+ds/debian/copyright 2023-01-27 10:00:45.000000000 +0000 @@ -38,7 +38,7 @@ License: GPL-3+ Files: debian/* -Copyright: 2010-2022, IOhannes m zmölnig +Copyright: 2010-2023, IOhannes m zmölnig License: Expat License: Expat diff -Nru jacktrip-1.6.8+ds/debian/copyright_hints jacktrip-1.7.0+ds/debian/copyright_hints --- jacktrip-1.6.8+ds/debian/copyright_hints 2022-12-11 21:12:23.000000000 +0000 +++ jacktrip-1.7.0+ds/debian/copyright_hints 2023-01-27 10:00:45.000000000 +0000 @@ -169,6 +169,8 @@ src/gui/qjacktrip.ui src/gui/qjacktrip_novs.qrc src/gui/share.svg + src/gui/start.svg + src/gui/video.svg src/gui/vs.qml src/gui/vsQmlClipboard.h src/gui/wedge.svg @@ -231,6 +233,7 @@ src/UdpDataProtocol.h src/UdpHubListener.cpp src/UdpHubListener.h + src/gui/AudioInterfaceMode.h src/gui/virtualstudio.cpp src/gui/virtualstudio.h src/gui/vsAudioInterface.cpp @@ -394,7 +397,7 @@ Files: src/Settings.cpp Copyright: 2008-2021, Juan-Pablo Caceres, Chris Chafe. - 2008-2021, Juan-Pablo Caceres, Chris Chafe." << endl; + 2008-2022, Juan-Pablo Caceres, Chris Chafe." << endl; License: Expat and/or LGPL FIXME diff -Nru jacktrip-1.6.8+ds/debian/jacktrip-gui.install jacktrip-1.7.0+ds/debian/jacktrip-gui.install --- jacktrip-1.6.8+ds/debian/jacktrip-gui.install 2022-12-11 21:12:23.000000000 +0000 +++ jacktrip-1.7.0+ds/debian/jacktrip-gui.install 2023-01-27 10:00:45.000000000 +0000 @@ -1 +1,2 @@ +debian/jacktrip-gui-launcher usr/share/jacktrip/ usr/ diff -Nru jacktrip-1.6.8+ds/debian/jacktrip-gui-launcher jacktrip-1.7.0+ds/debian/jacktrip-gui-launcher --- jacktrip-1.6.8+ds/debian/jacktrip-gui-launcher 1970-01-01 00:00:00.000000000 +0000 +++ jacktrip-1.7.0+ds/debian/jacktrip-gui-launcher 2023-01-27 10:00:45.000000000 +0000 @@ -0,0 +1,7 @@ +#!/bin/sh +if [ -z "$1" ]; then + exec /usr/bin/jacktrip-gui +else + exec /usr/bin/jacktrip-gui --gui --deeplink "$1" +fi + diff -Nru jacktrip-1.6.8+ds/debian/patches/gui-launcher.patch jacktrip-1.7.0+ds/debian/patches/gui-launcher.patch --- jacktrip-1.6.8+ds/debian/patches/gui-launcher.patch 1970-01-01 00:00:00.000000000 +0000 +++ jacktrip-1.7.0+ds/debian/patches/gui-launcher.patch 2023-01-27 10:00:45.000000000 +0000 @@ -0,0 +1,18 @@ +Description: Use wrapper-script to launch jacktrip-gui from .desktop +Author: IOhannes m zmölnig +Origin: Debian +Bug: https://github.com/jacktrip/jacktrip/issues/802 +Last-Update: 2023-01-27 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- jacktrip.orig/linux/org.jacktrip.JackTrip.desktop.in ++++ jacktrip/linux/org.jacktrip.JackTrip.desktop.in +@@ -3,7 +3,7 @@ + Name=JackTrip@name_suffix@ + Comment=Network Music Performance over the Internet + Comment[fr]=Performance de musique en réseau sur internet +-Exec=/bin/bash -c "if [ -z ""\$"1" ]; then jacktrip; else jacktrip --gui --deeplink ""\$"1"; fi" /bin/bash %u ++Exec=/usr/share/jacktrip/jacktrip-gui-launcher %u + Icon=@icon@ + Terminal=false + StartupWMClass=@wmclass@ diff -Nru jacktrip-1.6.8+ds/debian/patches/series jacktrip-1.7.0+ds/debian/patches/series --- jacktrip-1.6.8+ds/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ jacktrip-1.7.0+ds/debian/patches/series 2023-01-27 10:00:45.000000000 +0000 @@ -0,0 +1 @@ +gui-launcher.patch diff -Nru jacktrip-1.6.8+ds/debian/rules jacktrip-1.7.0+ds/debian/rules --- jacktrip-1.6.8+ds/debian/rules 2022-12-11 21:12:23.000000000 +0000 +++ jacktrip-1.7.0+ds/debian/rules 2023-01-27 10:00:45.000000000 +0000 @@ -18,12 +18,6 @@ %: dh $@ --sourcedirectory=$(DEB_SRCDIR) --buildsystem=meson -override_dh_clean: - dh_clean - rm -rf $(FLAVORS:%=$(builddir)%) - -rmdir debian/build/ - rm -f $(FLAVORS:%=jacktrip-%.1) - #.PHONY: $(patsubst %,configure_%,$(FLAVORS)) override_dh_auto_configure-arch: $(patsubst %,configure_%,$(FLAVORS)) configure_%: diff -Nru jacktrip-1.6.8+ds/docs/changelog.yml jacktrip-1.7.0+ds/docs/changelog.yml --- jacktrip-1.6.8+ds/docs/changelog.yml 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/docs/changelog.yml 2023-01-24 19:46:32.000000000 +0000 @@ -1,3 +1,24 @@ +- Version: "1.7.0" + Date: 2023-01-20 + Description: + - (added) VS Mode - Start and join inactive studios + - (added) JackTrip now prints build info on running from console + - (added) VS Mode - supports changing output volume from the server + - (added) VS Mode - Link to video on VS web when connected + - (updated) signing now happens in the main build workflow + - (updated) VS mode sorts active studios above inactive studios + - (updated) cmake build + - (updated) meson builds will fail if no backend is enabled + - (updated) replaced many ifdefs with if constexpr + - (updated) After signing out of VS mode, you will be asked to sign back in on the web + - (fixed) network stats failing after studio start + - (fixed) occasional immediate disconnects + - (fixed) segfault issue due to ifdefs + - (fixed) Hanging UI on Windows + - (fixed) turned a comment warning into an appropriate error + - (fixed) VS Mode - Join issue withs studios started in app + - (fixed) hanging app after refreshing studios + - (fixed) VS Mode - TCP 19 error after starting a studio - Version: "1.6.8" Date: 2022-12-05 Description: diff -Nru jacktrip-1.6.8+ds/jacktrip.pro jacktrip-1.7.0+ds/jacktrip.pro --- jacktrip-1.6.8+ds/jacktrip.pro 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/jacktrip.pro 2023-01-24 19:46:32.000000000 +0000 @@ -2,7 +2,7 @@ # Created by Juan-Pablo Caceres #****************************** -CONFIG += c++11 console +CONFIG += c++17 console CONFIG -= app_bundle CONFIG += qt thread debug_and_release build_all @@ -151,7 +151,7 @@ win32 { message(Building on win32) #cc CONFIG += x86 console - CONFIG += c++11 console + CONFIG += c++17 console exists("C:\Program Files\JACK2") { message("using Jack in C:\Program Files\JACK2") INCLUDEPATH += "C:\Program Files\JACK2\include" diff -Nru jacktrip-1.6.8+ds/meson.build jacktrip-1.7.0+ds/meson.build --- jacktrip-1.6.8+ds/meson.build 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/meson.build 2023-01-24 19:46:32.000000000 +0000 @@ -182,6 +182,13 @@ deps += rtaudio_dep endif +if rtaudio_dep.found() == false and jack_dep.found() == false + error(''' + JackTrip requires at least one available audio backend. Install the + appropriate library or enable the appropriate backends using meson + configure.''') +endif + if host_machine.system() == 'darwin' src += ['src/gui/NoNap.mm'] # Adding CoreAudio here is a workaround and should be removed diff -Nru jacktrip-1.6.8+ds/releases/edge/mac-manifests.json jacktrip-1.7.0+ds/releases/edge/mac-manifests.json --- jacktrip-1.6.8+ds/releases/edge/mac-manifests.json 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/releases/edge/mac-manifests.json 2023-01-24 19:46:32.000000000 +0000 @@ -2,6 +2,56 @@ "app_name": "JackTrip", "releases": [ { + "version": "1.7.0-rc1", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0-rc1", + "download": { + "date": "2022-01-20T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.7.0-rc1/JackTrip-v1.7.0-rc1-macOS-x64-installer.pkg", + "downloadSize": 11530680, + "sha256": "406134ee2017bcb762f968f893bc28463149d7567dd33b92f963ca9e09608636" + } + }, + { + "version": "1.6.9-beta3", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.9-beta3", + "download": { + "date": "2022-01-18T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.9-beta3/JackTrip-v1.6.9-beta3-macOS-x64-installer.pkg", + "downloadSize": 11528836, + "sha256": "30689d83641377c1594e1db44d8e6cf75a45780969381d02c11ede81a175561f" + } + }, + { + "version": "1.6.9-beta2", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.9-beta2", + "download": { + "date": "2022-01-10T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.9-beta2/JackTrip-v1.6.9-beta2-macOS-x64-installer.pkg", + "downloadSize": 11528427, + "sha256": "884e5c0cf3ea5bc82b348a739df3ba11238074a9552dcb1d1f250f484be89b77" + } + }, + { + "version": "1.6.9-beta1", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.9-beta1", + "download": { + "date": "2022-12-16T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.9-beta1-macOS-x64-installer.pkg", + "downloadSize": 11527211, + "sha256": "636055dee6fb84286cc73a95c887dd39c4a6d14780c451504db87860738b2278" + } + }, + { + "version": "1.6.8", + "changelog": "Critical bug fix: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.8", + "download": { + "date": "2022-12-05T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.8-macOS-x64-installer.pkg", + "downloadSize": 11517093, + "sha256": "5c040b2caa1e7dea97d7f48fd888f532a1c30da7385535b2d4a2805551bc5f71" + } + }, + { "version": "1.6.7", "changelog": "Updated Windows networking, enabling all audio devices on Windows, new default device behavior, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7", "download": { @@ -202,4 +252,4 @@ } } ] -} \ No newline at end of file +} diff -Nru jacktrip-1.6.8+ds/releases/edge/win-manifests.json jacktrip-1.7.0+ds/releases/edge/win-manifests.json --- jacktrip-1.6.8+ds/releases/edge/win-manifests.json 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/releases/edge/win-manifests.json 2023-01-24 19:46:32.000000000 +0000 @@ -2,6 +2,56 @@ "app_name": "JackTrip", "releases": [ { + "version": "1.7.0-rc1", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0-rc1", + "download": { + "date": "2022-01-20T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.7.0-rc1/JackTrip-v1.7.0-rc1-Windows-x64-installer.msi", + "downloadSize": 44556288, + "sha256": "edba383791a598954d129d39024e87f3c062985d10f47dbea43f3d226ee37c6c" + } + }, + { + "version": "1.6.9-beta3", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.9-beta3", + "download": { + "date": "2022-01-10T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.9-beta3/JackTrip-v1.6.9-beta3-Windows-x64-installer.msi", + "downloadSize": 44552192, + "sha256": "f0d8157d99da5ecfa3fb21e6bb039ea48052fc0792b114ca4f40ae0a554a4852" + } + }, + { + "version": "1.6.9-beta2", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.9-beta2", + "download": { + "date": "2022-01-10T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.9-beta2/JackTrip-v1.6.9-beta2-Windows-x64-installer.msi", + "downloadSize": 44548096, + "sha256": "a8e7c9b353d953df894a827e2856baa71f89dc8fa835a5f3eb8422a94ffff5b5" + } + }, + { + "version": "1.6.9-beta1", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.9-beta1", + "download": { + "date": "2022-12-16T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.9-beta1-Windows-x64-installer.msi", + "downloadSize": 44548096, + "sha256": "c5cfbfc1c7650d685489974b69f005671ceb44a1301006d33ceeda45fc00f7c7" + } + }, + { + "version": "1.6.8", + "changelog": "Critical bug fix: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.8", + "download": { + "date": "2022-12-05T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.8-Windows-x64-installer.msi", + "downloadSize": 44539904, + "sha256": "e4ac16e7a66b4d656eea4e88f703fe156d656aedc16ad7f63ec570d82c27ed54" + } + }, + { "version": "1.6.7", "changelog": "Updated Windows networking, enabling all audio devices on Windows, new default device behavior, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7", "download": { diff -Nru jacktrip-1.6.8+ds/releases/stable/linux-manifests.json jacktrip-1.7.0+ds/releases/stable/linux-manifests.json --- jacktrip-1.6.8+ds/releases/stable/linux-manifests.json 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/releases/stable/linux-manifests.json 2023-01-24 19:46:32.000000000 +0000 @@ -2,6 +2,16 @@ "app_name": "JackTrip", "releases": [ { + "version": "1.6.8", + "changelog": "Critical bug fix: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.8", + "download": { + "date": "2022-12-05T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.8-Linux-x64-binary.zip", + "downloadSize": 30496801, + "sha256": "c2fcbf86f8ad4db862431f9ccf6b52b275b3cf5fc3bc2f7eea7ac803ee7ca590" + } + }, + { "version": "1.6.7", "changelog": "Updated Windows networking, enabling all audio devices on Windows, new default device behavior, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7", "download": { diff -Nru jacktrip-1.6.8+ds/releases/stable/mac-manifests.json jacktrip-1.7.0+ds/releases/stable/mac-manifests.json --- jacktrip-1.6.8+ds/releases/stable/mac-manifests.json 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/releases/stable/mac-manifests.json 2023-01-24 19:46:32.000000000 +0000 @@ -2,6 +2,16 @@ "app_name": "JackTrip", "releases": [ { + "version": "1.6.8", + "changelog": "Critical bug fix: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.8", + "download": { + "date": "2022-12-05T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.8-macOS-x64-installer.pkg", + "downloadSize": 11517093, + "sha256": "5c040b2caa1e7dea97d7f48fd888f532a1c30da7385535b2d4a2805551bc5f71" + } + }, + { "version": "1.6.7", "changelog": "Updated Windows networking, enabling all audio devices on Windows, new default device behavior, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7", "download": { diff -Nru jacktrip-1.6.8+ds/releases/stable/win-manifests.json jacktrip-1.7.0+ds/releases/stable/win-manifests.json --- jacktrip-1.6.8+ds/releases/stable/win-manifests.json 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/releases/stable/win-manifests.json 2023-01-24 19:46:32.000000000 +0000 @@ -2,6 +2,16 @@ "app_name": "JackTrip", "releases": [ { + "version": "1.6.8", + "changelog": "Critical bug fix: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.8", + "download": { + "date": "2022-12-05T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.8-Windows-x64-installer.msi", + "downloadSize": 44539904, + "sha256": "e4ac16e7a66b4d656eea4e88f703fe156d656aedc16ad7f63ec570d82c27ed54" + } + }, + { "version": "1.6.7", "changelog": "Updated Windows networking, enabling all audio devices on Windows, new default device behavior, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7", "download": { diff -Nru jacktrip-1.6.8+ds/src/freeverbmonodsp.h jacktrip-1.7.0+ds/src/freeverbmonodsp.h --- jacktrip-1.6.8+ds/src/freeverbmonodsp.h 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/freeverbmonodsp.h 2023-01-24 19:46:32.000000000 +0000 @@ -116,7 +116,7 @@ * Inform the Memory Manager with the number of expected memory zones. * @param count - the number of expected memory zones */ - virtual void begin(size_t /*count*?) {} + virtual void begin(size_t /*count*/) {} /** * Give the Memory Manager information on a given memory zone. diff -Nru jacktrip-1.6.8+ds/src/gui/AudioInterfaceMode.h jacktrip-1.7.0+ds/src/gui/AudioInterfaceMode.h --- jacktrip-1.6.8+ds/src/gui/AudioInterfaceMode.h 1970-01-01 00:00:00.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/AudioInterfaceMode.h 2023-01-24 19:46:32.000000000 +0000 @@ -0,0 +1,83 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008-2022 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 AudioInterfaceMode.h + * \author Matt Horton + * \date December 2022 + */ + +enum class AudioInterfaceMode { + JACK, ///< Jack Mode + RTAUDIO, ///< RtAudio Mode + ALL, + NONE +}; + +#ifdef RT_AUDIO +#ifndef NO_JACK +constexpr AudioInterfaceMode mode = AudioInterfaceMode::ALL; +#else +constexpr AudioInterfaceMode mode = AudioInterfaceMode::RTAUDIO; +#endif +#else +#ifndef NO_JACK +constexpr AudioInterfaceMode mode = AudioInterfaceMode::JACK; +#else +constexpr AudioInterfaceMode mode = AudioInterfaceMode::NONE; +#endif +#endif + +template +constexpr auto isBackendAvailable() +{ + if constexpr (backend == AudioInterfaceMode::RTAUDIO) { + if (mode == AudioInterfaceMode::RTAUDIO || mode == AudioInterfaceMode::ALL) { + return true; + } else { + return false; + } + } else if constexpr (backend == AudioInterfaceMode::JACK) { + if (mode == AudioInterfaceMode::JACK || mode == AudioInterfaceMode::ALL) { + return true; + } else { + return false; + } + } else if constexpr (backend == AudioInterfaceMode::ALL) { + if (mode == AudioInterfaceMode::ALL) { + return true; + } else { + return false; + } + } else { + return false; + } +} \ No newline at end of file diff -Nru jacktrip-1.6.8+ds/src/gui/Browse.qml jacktrip-1.7.0+ds/src/gui/Browse.qml --- jacktrip-1.6.8+ds/src/gui/Browse.qml 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/Browse.qml 2023-01-24 19:46:32.000000000 +0000 @@ -294,7 +294,12 @@ border.width: 1 border.color: settingsButton.down ? buttonPressedStroke : (settingsButton.hovered ? buttonHoverStroke : buttonStroke) } - onClicked: virtualstudio.windowState = "settings" + Timer { + id: restartAudioTimer + interval: 805; running: false; repeat: false + onTriggered: virtualstudio.audioActivated = true; + } + onClicked: { virtualstudio.windowState = "settings"; restartAudioTimer.restart(); } display: AbstractButton.TextBesideIcon font { family: "Poppins"; diff -Nru jacktrip-1.6.8+ds/src/gui/Connected.qml jacktrip-1.7.0+ds/src/gui/Connected.qml --- jacktrip-1.6.8+ds/src/gui/Connected.qml 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/Connected.qml 2023-01-24 19:46:32.000000000 +0000 @@ -5,9 +5,9 @@ Item { width: parent.width; height: parent.height clip: true - + property bool connecting: false - + property int leftHeaderMargin: 16 property int fontBig: 28 property int fontMedium: 12 @@ -17,8 +17,22 @@ property int bodyMargin: 60 property int bottomToolTipMargin: 8 property int rightToolTipMargin: 4 - + + property string studioStatus: (virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].status : "") + property bool showReadyScreen: studioStatus === "Ready" + property bool showStartingScreen: studioStatus === "Starting" + property bool showStoppingScreen: (virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].isManageable && !serverModel[virtualstudio.currentStudio].enabled && serverModel[virtualstudio.currentStudio].cloudId !== "" : false) + property bool showWaitingScreen: !showStoppingScreen && !showStartingScreen && !showReadyScreen + property string buttonColour: virtualstudio.darkMode ? "#494646" : "#EAECEC" + + property string browserButtonColour: virtualstudio.darkMode ? "#494646" : "#EAECEC" + property string browserButtonHoverColour: virtualstudio.darkMode ? "#5B5858" : "#D3D4D4" + property string browserButtonPressedColour: virtualstudio.darkMode ? "#524F4F" : "#DEE0E0" + property string browserButtonStroke: virtualstudio.darkMode ? "#80827D7D" : "#40979797" + property string browserButtonHoverStroke: virtualstudio.darkMode ? "#7B7777" : "#BABCBC" + property string browserButtonPressedStroke: virtualstudio.darkMode ? "#827D7D" : "#BABCBC" + property string muteButtonMutedColor: "#FCB6B6" property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D" property string meterColor: virtualstudio.darkMode ? "gray" : "#E0E0E0" @@ -78,15 +92,15 @@ fillMode: Image.PreserveAspectFit smooth: true } - + Text { id: heading - text: virtualstudio.connectionState + text: studioStatus === "Starting" ? "Starting..." : virtualstudio.connectionState x: leftHeaderMargin * virtualstudio.uiScale; y: 34 * virtualstudio.uiScale font { family: "Poppins"; weight: Font.Bold; pixelSize: fontBig * virtualstudio.fontScale * virtualstudio.uiScale } color: textColour } - + Studio { x: leftHeaderMargin * virtualstudio.uiScale; y: 96 * virtualstudio.uiScale width: parent.width - (2 * x) @@ -103,6 +117,7 @@ Item { id: inputDevice + visible: showReadyScreen x: bodyMargin * virtualstudio.uiScale; y: 230 * virtualstudio.uiScale width: Math.min(parent.width / 2, 320 * virtualstudio.uiScale) - x height: 100 * virtualstudio.uiScale @@ -152,6 +167,7 @@ Item { id: outputDevice + visible: showReadyScreen x: bodyMargin * virtualstudio.uiScale; y: 320 * virtualstudio.uiScale width: Math.min(parent.width / 2, 320 * virtualstudio.uiScale) - x height: 100 * virtualstudio.uiScale @@ -201,6 +217,7 @@ Item { id: inputControls + visible: showReadyScreen x: inputDevice.x + inputDevice.width; y: 230 * virtualstudio.uiScale width: parent.width - inputDevice.width - 2 * bodyMargin * virtualstudio.uiScale @@ -303,6 +320,7 @@ Item { id: outputControls + visible: showReadyScreen x: outputDevice.x + outputDevice.width; y: 320 * virtualstudio.uiScale width: parent.width - inputDevice.width - 2 * bodyMargin * virtualstudio.uiScale @@ -339,6 +357,7 @@ Item { id: networkStatsHeader + visible: showReadyScreen x: bodyMargin * virtualstudio.uiScale; y: 410 * virtualstudio.uiScale width: Math.min(parent.width / 2, 320 * virtualstudio.uiScale) - x height: 128 * virtualstudio.uiScale @@ -373,6 +392,7 @@ Item { id: networkStatsText + visible: showReadyScreen x: networkStatsHeader.x + networkStatsHeader.width; y: 410 * virtualstudio.uiScale width: parent.width - networkStatsHeader.width - 2 * bodyMargin * virtualstudio.uiScale height: 128 * virtualstudio.uiScale @@ -395,4 +415,126 @@ color: textColour } } + + Item { + id: waitingScreen + visible: showWaitingScreen + x: bodyMargin * virtualstudio.uiScale; y: 230 * virtualstudio.uiScale + width: parent.width - (2 * x) + + property bool isManageable: (virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].isManageable : false) + + Text { + id: waitingText0 + x: 0 + width: parent.width + color: textColour + font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } + text: parent.isManageable + ? "Waiting for this studio to start. Please start the studio using one of the options below." + : "This studio is currently inactive. Please contact an owner or admin for this studio to start it." + wrapMode: Text.WordWrap + } + + Item { + id: startButtonsBox + anchors.top: waitingText0.bottom + anchors.topMargin: 16 * virtualstudio.uiScale + anchors.bottomMargin: 16 * virtualstudio.uiScale + visible: parent.isManageable + + height: 64 * virtualstudio.uiScale + + Button { + id: startStudioNowButton + anchors.verticalCenter: startButtonsBox.verticalCenter + x: 0 + onClicked: { + virtualstudio.manageStudio(-1, true) + } + + width: 210 * virtualstudio.uiScale; height: 45 * virtualstudio.uiScale + background: Rectangle { + radius: 6 * virtualstudio.uiScale + color: startStudioNowButton.down ? browserButtonPressedColour : (startStudioNowButton.hovered ? browserButtonHoverColour : browserButtonColour) + border.width: 1 + border.color: startStudioNowButton.down ? browserButtonPressedStroke : (startStudioNowButton.hovered ? browserButtonHoverStroke : browserButtonStroke) + } + + Text { + text: "Start Studio" + font.family: "Poppins" + font.pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: textColour + } + } + + Text { + id: startStudioInBrowserText + anchors.verticalCenter: startStudioNowButton.verticalCenter + anchors.left: startStudioNowButton.right + anchors.leftMargin: 24 * virtualstudio.uiScale + width: 240 * virtualstudio.uiScale + textFormat: Text.RichText + text:`Change Settings and Start` + + onLinkActivated: link => { + virtualstudio.openLink(link) + } + horizontalAlignment: Text.AlignHLeft + wrapMode: Text.WordWrap + font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale } + } + } + + Text { + id: waitingText1 + x: 0 + width: parent.width + color: textColour + anchors.top: parent.isManageable ? startButtonsBox.bottom : waitingText0.bottom + anchors.topMargin: 16 * virtualstudio.uiScale + anchors.bottomMargin: 16 * virtualstudio.uiScale + visible: parent.isManageable + font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } + text: "You will be automatically connected to the studio when it is ready." + wrapMode: Text.WordWrap + } + } + + Item { + id: studioStartingScreen + visible: showStartingScreen + x: bodyMargin * virtualstudio.uiScale; y: 230 * virtualstudio.uiScale + width: parent.width - (2 * x) + + Text { + id: studioStartingText0 + x: 0 + width: parent.width + color: textColour + font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } + text: "This studio is currently starting up. You will be connected automatically when it is ready." + wrapMode: Text.WordWrap + } + } + + Item { + id: studioStoppingScreen + visible: showStoppingScreen + x: bodyMargin * virtualstudio.uiScale; y: 230 * virtualstudio.uiScale + width: parent.width - (2 * x) + + Text { + id: studioStoppingText0 + x: 0 + width: parent.width + color: textColour + font {family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } + text: "This studio is shutting down, please wait to start it again." + wrapMode: Text.WordWrap + } + } } diff -Nru jacktrip-1.6.8+ds/src/gui/join.svg jacktrip-1.7.0+ds/src/gui/join.svg --- jacktrip-1.6.8+ds/src/gui/join.svg 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/join.svg 2023-01-24 19:46:32.000000000 +0000 @@ -1,3 +1,3 @@ - + diff -Nru jacktrip-1.6.8+ds/src/gui/qjacktrip.qrc jacktrip-1.7.0+ds/src/gui/qjacktrip.qrc --- jacktrip-1.6.8+ds/src/gui/qjacktrip.qrc 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/qjacktrip.qrc 2023-01-24 19:46:32.000000000 +0000 @@ -25,6 +25,7 @@ leave.svg manage.svg share.svg + start.svg cog.svg mic.svg micoff.svg @@ -33,6 +34,7 @@ headphones.svg Prompt.svg network.svg + video.svg jacktrip.png jacktrip white.png JTOriginal.png diff -Nru jacktrip-1.6.8+ds/src/gui/Settings.qml jacktrip-1.7.0+ds/src/gui/Settings.qml --- jacktrip-1.6.8+ds/src/gui/Settings.qml 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/Settings.qml 2023-01-24 19:46:32.000000000 +0000 @@ -245,7 +245,18 @@ text: "Using JACK for audio input and output. Use QjackCtl to adjust your sample rate, buffer, and device settings." font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } wrapMode: Text.WordWrap - visible: virtualstudio.audioBackend == "JACK" && !virtualstudio.selectableBackend + visible: virtualstudio.audioBackend == "JACK" && !virtualstudio.selectableBackend && virtualstudio.backendAvailable + color: textColour + } + + Text { + id: noBackendLabel + x: leftMargin * virtualstudio.uiScale; y: 150 * virtualstudio.uiScale + width: parent.width - x - (16 * virtualstudio.uiScale) + text: "JackTrip has been compiled without an audio backend. Please rebuild with the rtaudio flag or without the nojack flag." + font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } + wrapMode: Text.WordWrap + visible: !virtualstudio.backendAvailable color: textColour } @@ -308,7 +319,7 @@ horizontalAlignment: Text.AlignHLeft verticalAlignment: Text.AlignVCenter elide: Text.ElideRight - text: outputCombo.model[outputCombo.currentIndex].text + text: outputCombo.model[outputCombo.currentIndex].text ? outputCombo.model[outputCombo.currentIndex].text : "" } } @@ -323,21 +334,25 @@ onClicked: { virtualstudio.playOutputAudio() } width: 216 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale x: parent.width - (232 * virtualstudio.uiScale) - y: virtualstudio.audioBackend != "JACK" ? outputCombo.y + (48 * virtualstudio.uiScale) : outputCombo.y + (48 * virtualstudio.uiScale) + y: virtualstudio.audioBackend != "JACK" ? outputCombo.y + (48 * virtualstudio.uiScale) : jackLabel.y + (72 * virtualstudio.uiScale) Text { text: "Test Output Audio" font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale } anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter } color: textColour } + visible: virtualstudio.audioReady } Text { + id: inputLabel anchors.verticalCenter: inputCombo.verticalCenter - x: leftMargin * virtualstudio.uiScale; y: testOutputAudioButton.y + (48 * virtualstudio.uiScale) + x: leftMargin * virtualstudio.uiScale + anchors.top: virtualstudio.audioBackend != "JACK" ? inputCombo.top : inputDeviceMeters.top + anchors.topMargin: virtualstudio.audioBackend != "JACK" ? (inputCombo.height - inputLabel.height)/2 : 0 text: "Input Device" font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } - visible: virtualstudio.audioBackend != "JACK" + visible: virtualstudio.backendAvailable color: textColour } @@ -361,7 +376,7 @@ x: backendCombo.x; y: testOutputAudioButton.y + (48 * virtualstudio.uiScale) width: parent.width - x - (16 * virtualstudio.uiScale); height: 36 * virtualstudio.uiScale visible: virtualstudio.audioBackend != "JACK" - delegate: ItemDelegate { + delegate: ItemDelegate { required property var modelData required property int index @@ -391,7 +406,7 @@ horizontalAlignment: Text.AlignHLeft verticalAlignment: Text.AlignVCenter elide: Text.ElideRight - text: inputCombo.model[inputCombo.currentIndex].text + text: inputCombo.model[inputCombo.currentIndex].text ? inputCombo.model[inputCombo.currentIndex].text : "" } } @@ -400,11 +415,24 @@ anchors.left: backendCombo.left anchors.right: parent.right anchors.rightMargin: rightMargin * virtualstudio.uiScale - y: virtualstudio.audioBackend != "JACK" ? inputCombo.y + 48 * virtualstudio.uiScale : virtualstudio.uiScale * (virtualstudio.selectableBackend ? 112 : 64) + y: virtualstudio.audioBackend != "JACK" ? inputCombo.y + 48 * virtualstudio.uiScale : testOutputAudioButton.y + 72 * virtualstudio.uiScale height: 100 * virtualstudio.uiScale model: inputMeterModel clipped: inputClipped enabled: !Boolean(virtualstudio.devicesError) + visible: virtualstudio.audioReady + } + + Text { + anchors.left: backendCombo.left + anchors.right: parent.right + anchors.rightMargin: rightMargin * virtualstudio.uiScale + y: virtualstudio.audioBackend != "JACK" ? inputCombo.y + 48 * virtualstudio.uiScale : virtualstudio.uiScale * (virtualstudio.selectableBackend ? 112 : 64) + height: 100 * virtualstudio.uiScale + text: "Preparing audio..." + font { family: "Poppins"; pixelSize: 13 * virtualstudio.fontScale * virtualstudio.uiScale } + visible: virtualstudio.audioBackend != "JACK" && !virtualstudio.audioReady + color: textColour } Button { @@ -720,7 +748,12 @@ border.width: 1 border.color: cancelButton.down ? buttonPressedStroke : (cancelButton.hovered ? buttonHoverStroke : buttonStroke) } - onClicked: { virtualstudio.windowState = "browse"; virtualstudio.revertSettings() } + onClicked: { + virtualstudio.windowState = "browse"; + inputCombo.currentIndex = virtualstudio.previousInput; + outputCombo.currentIndex = virtualstudio.previousOutput; + virtualstudio.revertSettings() + } anchors.verticalCenter: parent.verticalCenter x: parent.width - (230 * virtualstudio.uiScale) width: buttonWidth * virtualstudio.uiScale; height: buttonHeight * virtualstudio.uiScale diff -Nru jacktrip-1.6.8+ds/src/gui/Setup.qml jacktrip-1.7.0+ds/src/gui/Setup.qml --- jacktrip-1.6.8+ds/src/gui/Setup.qml 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/Setup.qml 2023-01-24 19:46:32.000000000 +0000 @@ -575,7 +575,18 @@ text: "Using JACK for audio input and output. Use QjackCtl to adjust your sample rate, buffer, and device settings." font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } wrapMode: Text.WordWrap - visible: virtualstudio.audioBackend == "JACK" && !virtualstudio.selectableBackend + visible: virtualstudio.audioBackend == "JACK" && !virtualstudio.selectableBackend && virtualstudio.backendAvailable + color: textColour + } + + Text { + id: noBackendLabel + x: leftMargin * virtualstudio.uiScale; y: 150 * virtualstudio.uiScale + width: parent.width - x - (16 * virtualstudio.uiScale) + text: "JackTrip has been compiled without an audio backend. Please rebuild with the rtaudio flag or without the nojack flag." + font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } + wrapMode: Text.WordWrap + visible: !virtualstudio.backendAvailable color: textColour } @@ -629,7 +640,7 @@ horizontalAlignment: Text.AlignHLeft verticalAlignment: Text.AlignVCenter elide: Text.ElideRight - text: outputCombo.model[outputCombo.currentIndex].text + text: outputCombo.model[outputCombo.currentIndex].text ? outputCombo.model[outputCombo.currentIndex].text : "" } } @@ -640,6 +651,7 @@ text: virtualstudio.audioBackend != "JACK" ? "Output Device" : "Output Volume" font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } color: textColour + visible: virtualstudio.backendAvailable } Slider { @@ -649,7 +661,7 @@ onMoved: { audioInterface.outputVolume = value } to: 1.0 padding: 0 - y: virtualstudio.audioBackend != "JACK" ? outputCombo.y + 48 * virtualstudio.uiScale : backendCombo.y + virtualstudio.uiScale * (virtualstudio.selectableBackend ? 60 : 0) + y: virtualstudio.audioBackend != "JACK" ? outputCombo.y + 48 * virtualstudio.uiScale : jackLabel.y + 72 * virtualstudio.uiScale anchors.left: outputCombo.left anchors.right: parent.right anchors.rightMargin: rightMargin * virtualstudio.uiScale @@ -662,6 +674,7 @@ color: outputSlider.pressed ? sliderPressedColour : sliderColour border.color: buttonStroke } + visible: virtualstudio.backendAvailable } Button { @@ -677,7 +690,7 @@ anchors.rightMargin: rightMargin * virtualstudio.uiScale y: outputSlider.y + (36 * virtualstudio.uiScale) width: 216 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale - visible: virtualstudio.audioBackend != "JACK" + visible: virtualstudio.audioReady Text { text: "Test Output Audio" font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale } @@ -738,7 +751,7 @@ horizontalAlignment: Text.AlignHLeft verticalAlignment: Text.AlignVCenter elide: Text.ElideRight - text: inputCombo.model[inputCombo.currentIndex].text + text: inputCombo.model[inputCombo.currentIndex].text ? inputCombo.model[inputCombo.currentIndex].text : "" } } @@ -750,6 +763,7 @@ text: virtualstudio.audioBackend != "JACK" ? "Input Device" : "Input Volume" font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } color: textColour + visible: virtualstudio.backendAvailable } Meter { @@ -757,11 +771,12 @@ anchors.left: backendCombo.left anchors.right: parent.right anchors.rightMargin: rightMargin * virtualstudio.uiScale - y: virtualstudio.audioBackend != "JACK" ? inputCombo.y + 72 * virtualstudio.uiScale : outputSlider.y + (72 * virtualstudio.uiScale) + y: virtualstudio.audioBackend != "JACK" ? inputCombo.y + 72 * virtualstudio.uiScale : testOutputAudioButton.y + (72 * virtualstudio.uiScale) height: 100 * virtualstudio.uiScale model: inputMeterModel clipped: inputClipped enabled: !Boolean(virtualstudio.devicesError) + visible: virtualstudio.backendAvailable } Slider { @@ -784,6 +799,7 @@ color: inputSlider.pressed ? sliderPressedColour : sliderColour border.color: buttonStroke } + visible: virtualstudio.backendAvailable } Button { @@ -800,7 +816,7 @@ anchors.topMargin: 18 * virtualstudio.uiScale anchors.top: inputSlider.bottom width: 216 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale - visible: virtualstudio.audioBackend != "JACK" + visible: virtualstudio.audioBackend != "JACK" && virtualstudio.backendAvailable Text { text: "Refresh Devices" font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale } @@ -847,7 +863,7 @@ color: saveButtonShadow } } - enabled: !Boolean(virtualstudio.devicesError) + enabled: !Boolean(virtualstudio.devicesError) && virtualstudio.backendAvailable onClicked: { virtualstudio.windowState = "browse"; virtualstudio.applySettings() } anchors.right: parent.right anchors.rightMargin: rightMargin * virtualstudio.uiScale @@ -859,7 +875,7 @@ font.family: "Poppins" font.pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale font.weight: Font.Bold - color: !Boolean(virtualstudio.devicesError) ? saveButtonText : disabledButtonText + color: !Boolean(virtualstudio.devicesError) && virtualstudio.backendAvailable ? saveButtonText : disabledButtonText anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter } @@ -868,6 +884,7 @@ CheckBox { id: showAgainCheckbox checked: virtualstudio.showDeviceSetup + visible: virtualstudio.backendAvailable text: qsTr("Ask again next time") anchors.right: saveButton.left anchors.rightMargin: 16 * virtualstudio.uiScale diff -Nru jacktrip-1.6.8+ds/src/gui/start.svg jacktrip-1.7.0+ds/src/gui/start.svg --- jacktrip-1.6.8+ds/src/gui/start.svg 1970-01-01 00:00:00.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/start.svg 2023-01-24 19:46:32.000000000 +0000 @@ -0,0 +1 @@ + \ No newline at end of file diff -Nru jacktrip-1.6.8+ds/src/gui/Studio.qml jacktrip-1.7.0+ds/src/gui/Studio.qml --- jacktrip-1.6.8+ds/src/gui/Studio.qml 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/Studio.qml 2023-01-24 19:46:32.000000000 +0000 @@ -45,14 +45,36 @@ property string shadowColour: virtualstudio.darkMode ? "#40000000" : "#80A1A1A1" property string toolTipBackgroundColour: inviteCopied ? "#57B147" : (virtualstudio.darkMode ? "#323232" : "#F3F3F3") property string toolTipTextColour: inviteCopied ? "#FAFBFB" : textColour - property string joinColour: virtualstudio.darkMode ? (connected ? "#FCB6B6" : "#E2EBE0") : (connected ? "#FCB6B6" : "#C4F4BE") - property string joinHoverColour: virtualstudio.darkMode ? (connected ? "#D49696" : "#BAC7B8") : (connected ? "#E3A4A4" : "#B0DCAB") - property string joinPressedColour: virtualstudio.darkMode ? (connected ? "#F2AEAE" : "#D8E2D6") : (connected ? "#EFADAD" : "#BAE8B5") - property string joinStroke: virtualstudio.darkMode ? (connected ? "#A65959" : "#748F70") : (connected ? "#C95E5E" : "#5DB752") - property string manageColour: virtualstudio.darkMode ? "#F0F1F1" : "#EAEBEB" - property string manageHoverColour: virtualstudio.darkMode ? "#CCCDCD" : "#D3D3D3" - property string managePressedColour: virtualstudio.darkMode ? "#E4E5E5" : "#EAEBEB" - property string manageStroke: virtualstudio.darkMode ? "#8B8D8D" : "#949494" + + property string baseButtonColour: virtualstudio.darkMode ? "#F0F1F1" : "#EAEBEB" + property string baseButtonHoverColour: virtualstudio.darkMode ? "#CCCDCD" : "#D3D3D3" + property string baseButtonPressedColour: virtualstudio.darkMode ? "#E4E5E5" : "#EAEBEB" + property string baseButtonStroke: virtualstudio.darkMode ? "#8B8D8D" : "#949494" + + property string joinAvailableColour: virtualstudio.darkMode ? "#E2EBE0" : "#C4F4BE" + property string joinAvailableHoverColour: virtualstudio.darkMode ? "#BAC7B8" : "#B0DCAB" + property string joinAvailablePressedColour: virtualstudio.darkMode ? "#D8E2D6" : "#BAE8B5" + property string joinAvailableStroke: virtualstudio.darkMode ? "#748F70" : "#5DB752" + + property string joinUnavailableColour: baseButtonColour + property string joinUnavailableHoverColour: baseButtonHoverColour + property string joinUnavailablePressedColour: baseButtonPressedColour + property string joinUnavailableStroke: baseButtonStroke + + property string startColour: virtualstudio.darkMode ? "#E2EBE0" : "#C4F4BE" + property string startHoverColour: virtualstudio.darkMode ? "#BAC7B8" : "#B0DCAB" + property string startPressedColour: virtualstudio.darkMode ? "#D8E2D6" : "#BAE8B5" + property string startStroke: virtualstudio.darkMode ? "#748F70" : "#5DB752" + + property string manageColour: baseButtonColour + property string manageHoverColour: baseButtonHoverColour + property string managePressedColour: baseButtonPressedColour + property string manageStroke: baseButtonStroke + + property string leaveColour: virtualstudio.darkMode ? "#FCB6B6" : "#FCB6B6" + property string leaveHoverColour: virtualstudio.darkMode ? "#D49696" : "#E3A4A4" + property string leavePressedColour: virtualstudio.darkMode ? "#F2AEAE" : "#EFADAD" + property string leaveStroke: virtualstudio.darkMode ? "#A65959" : "#C95E5E" Clipboard { id: clipboard @@ -172,25 +194,47 @@ y: topMargin * virtualstudio.uiScale; width: 40 * virtualstudio.uiScale; height: width background: Rectangle { radius: width / 2 - color: joinButton.down ? joinPressedColour : (joinButton.hovered ? joinHoverColour : joinColour) + color: available ? (joinButton.down ? joinAvailablePressedColour : (joinButton.hovered ? joinAvailableHoverColour : joinAvailableColour)) + : (joinButton.down ? joinUnavailablePressedColour : (joinButton.hovered ? joinUnavailableHoverColour : joinUnavailableColour)) border.width: joinButton.down ? 1 : 0 - border.color: joinStroke + border.color: available ? joinAvailableStroke : joinUnavailableStroke } - visible: connected || canConnect || canStart + visible: !connected onClicked: { - if (!connected) { - virtualstudio.windowState = "connected"; - virtualstudio.connectToStudio(index); - } else { - virtualstudio.disconnect(); - } + virtualstudio.windowState = "connected"; + virtualstudio.connectToStudio(index); } Image { - id: joinLeave + id: join width: 22 * virtualstudio.uiScale; height: 20 * virtualstudio.uiScale anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter } - source: connected ? "leave.svg" : "join.svg" - sourceSize: Qt.size(joinLeave.width,joinLeave.height) + source: "join.svg" + sourceSize: Qt.size(join.width,join.height) + fillMode: Image.PreserveAspectFit + smooth: true + } + } + + Button { + id: leaveButton + x: manageable ? parent.width - (219 * virtualstudio.uiScale) : parent.width - (142 * virtualstudio.uiScale) + y: topMargin * virtualstudio.uiScale; width: 40 * virtualstudio.uiScale; height: width + background: Rectangle { + radius: width / 2 + color: leaveButton.down ? leavePressedColour : (leaveButton.hovered ? leaveHoverColour : leaveColour) + border.width: leaveButton.down ? 1 : 0 + border.color: leaveStroke + } + visible: connected + onClicked: { + virtualstudio.disconnect(); + } + Image { + id: leave + width: 22 * virtualstudio.uiScale; height: 20 * virtualstudio.uiScale + anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter } + source: "leave.svg" + sourceSize: Qt.size(leave.width,leave.height) fillMode: Image.PreserveAspectFit smooth: true } @@ -199,9 +243,9 @@ Text { anchors.horizontalCenter: joinButton.horizontalCenter y: 56 * virtualstudio.uiScale - text: connected ? "Leave" : available ? "Join" : "Start" + text: connected ? "Leave" : "Join" font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale} - visible: connected || canConnect || canStart + visible: true color: textColour } @@ -296,10 +340,12 @@ border.color: manageStroke } onClicked: { - if (!connected) { - virtualstudio.manageStudio(index) + if (manageable && connected) { + virtualstudio.launchVideo(-1) + } else if (connected) { + virtualstudio.manageStudio(-1); } else { - virtualstudio.manageStudio(-1) + virtualstudio.manageStudio(index); } } visible: manageable @@ -307,7 +353,7 @@ id: manageImg width: 20 * virtualstudio.uiScale; height: width anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter } - source: "manage.svg" + source: manageable && connected ? "video.svg" : "manage.svg" sourceSize: Qt.size(manageImg.width,manageImg.height) fillMode: Image.PreserveAspectFit smooth: true @@ -317,7 +363,7 @@ Text { anchors.horizontalCenter: manageButton.horizontalCenter y: 56 * virtualstudio.uiScale - text: "Manage" + text: manageable && connected ? "Video" : "Manage" font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale } visible: manageable color: textColour diff -Nru jacktrip-1.6.8+ds/src/gui/video.svg jacktrip-1.7.0+ds/src/gui/video.svg --- jacktrip-1.6.8+ds/src/gui/video.svg 1970-01-01 00:00:00.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/video.svg 2023-01-24 19:46:32.000000000 +0000 @@ -0,0 +1 @@ + \ No newline at end of file diff -Nru jacktrip-1.6.8+ds/src/gui/virtualstudio.cpp jacktrip-1.7.0+ds/src/gui/virtualstudio.cpp --- jacktrip-1.6.8+ds/src/gui/virtualstudio.cpp 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/virtualstudio.cpp 2023-01-24 19:46:32.000000000 +0000 @@ -121,6 +121,10 @@ refreshDevices(); m_previousInput = m_inputDevice; m_previousOutput = m_outputDevice; + + if constexpr (!isBackendAvailable()) { + m_selectableBackend = false; + } #else m_selectableBackend = false; m_vsAudioInterface.reset(new VsAudioInterface()); @@ -203,7 +207,6 @@ m_view.resize(696 * m_uiScale, 577 * m_uiScale); // Connect our timers - connect(&m_startTimer, &QTimer::timeout, this, &VirtualStudio::checkForHostname); connect(&m_retryPeriodTimer, &QTimer::timeout, this, &VirtualStudio::endRetryPeriod); connect(&m_refreshTimer, &QTimer::timeout, this, [&]() { m_refreshMutex.lock(); @@ -223,14 +226,14 @@ // thread connect(this, &VirtualStudio::refreshFinished, this, &VirtualStudio::joinStudio, Qt::QueuedConnection); + + connect(this, &VirtualStudio::audioActivatedChanged, this, + &VirtualStudio::toggleAudio, Qt::QueuedConnection); connect( this, &VirtualStudio::studioToJoinChanged, this, [&]() { if (!m_studioToJoin.isEmpty()) { - // join studio when studio to join changes - if (readyToJoin()) { - joinStudio(); - } + joinStudio(); } }, Qt::QueuedConnection); @@ -348,6 +351,7 @@ } m_inputDevice = filteredInputDeviceList.at(device); + emit inputDeviceChanged(m_inputDevice, false); emit inputDeviceSelected(m_inputDevice); #endif } @@ -384,10 +388,83 @@ } m_outputDevice = filteredOutputDeviceList.at(device); + emit outputDeviceChanged(m_outputDevice, false); emit outputDeviceSelected(m_outputDevice); #endif } +int VirtualStudio::previousInput() +{ +#ifdef RT_AUDIO + if (m_useRtAudio) { + QStringList filteredInputDeviceList; + for (int i = 0; i < m_inputDeviceList.size(); i++) { + if (m_inputDeviceList.at(i) != "(default)") { + filteredInputDeviceList += m_inputDeviceList.at(i); + } + } + + int index = filteredInputDeviceList.indexOf(m_previousInput); + return index >= 0 ? index : 0; + } +#endif + return 0; +} + +void VirtualStudio::setPreviousInput([[maybe_unused]] int device) +{ + if (!m_useRtAudio) { + return; + } +#ifdef RT_AUDIO + QStringList filteredInputDeviceList; + for (int i = 0; i < m_inputDeviceList.size(); i++) { + if (m_inputDeviceList.at(i) != "(default)") { + filteredInputDeviceList += m_inputDeviceList.at(i); + } + } + + m_previousInput = filteredInputDeviceList.at(device); + emit previousInputChanged(); +#endif +} + +int VirtualStudio::previousOutput() +{ +#ifdef RT_AUDIO + if (m_useRtAudio) { + QStringList filteredOutputDeviceList; + for (int i = 0; i < m_outputDeviceList.size(); i++) { + if (m_outputDeviceList.at(i) != "(default)") { + filteredOutputDeviceList += m_outputDeviceList.at(i); + } + } + + int index = filteredOutputDeviceList.indexOf(m_previousOutput); + return index >= 0 ? index : 0; + } +#endif + return 0; +} + +void VirtualStudio::setPreviousOutput([[maybe_unused]] int device) +{ + if (!m_useRtAudio) { + return; + } +#ifdef RT_AUDIO + QStringList filteredOutputDeviceList; + for (int i = 0; i < m_outputDeviceList.size(); i++) { + if (m_outputDeviceList.at(i) != "(default)") { + filteredOutputDeviceList += m_outputDeviceList.at(i); + } + } + + m_previousOutput = filteredOutputDeviceList.at(device); + emit previousOutputChanged(); +#endif +} + QString VirtualStudio::devicesWarning() { return m_devicesWarningMsg; @@ -428,6 +505,26 @@ return m_outMuted; } +bool VirtualStudio::audioActivated() +{ + return m_audioActivated; +} + +bool VirtualStudio::audioReady() +{ + return m_audioReady; +} + +bool VirtualStudio::backendAvailable() +{ + if constexpr ((isBackendAvailable() + || isBackendAvailable())) { + return true; + } else { + return false; + } +} + void VirtualStudio::setInputVolume(float multiplier) { m_inMultiplier = multiplier; @@ -507,6 +604,18 @@ settings.endGroup(); } +void VirtualStudio::setAudioActivated(bool activated) +{ + m_audioActivated = activated; + emit audioActivatedChanged(); +} + +void VirtualStudio::setAudioReady(bool ready) +{ + m_audioReady = ready; + emit audioReadyChanged(); +} + int VirtualStudio::currentStudio() { return m_currentStudio; @@ -605,6 +714,17 @@ emit windowStateUpdated(); } +QString VirtualStudio::apiHost() +{ + return m_apiHost; +} + +void VirtualStudio::setApiHost(QString host) +{ + m_apiHost = host; + emit apiHostChanged(); +} + bool VirtualStudio::showWarnings() { return m_showWarnings; @@ -620,12 +740,7 @@ emit showWarningsChanged(); // attempt to join studio if requested if (!m_studioToJoin.isEmpty()) { - // device setup view proceeds warning view - // if device setup is shown, do not immediately join - if (readyToJoin()) { - // We're done waiting to be on the browse page - joinStudio(); - } + joinStudio(); } } @@ -727,6 +842,12 @@ return; } + // FTUX shows warnings and device setup views + // if any of these enabled, do not immediately join + if (!readyToJoin()) { + return; + } + QString scheme = m_studioToJoin.scheme(); QString path = m_studioToJoin.path(); QString url = m_studioToJoin.toString(); @@ -785,6 +906,7 @@ (*parameters)[QStringLiteral("code")] = QUrl::fromPercentEncoding(code); } else if (stage == QAbstractOAuth2::Stage::RequestingAuthorization) { parameters->insert(QStringLiteral("audience"), AUTH_AUDIENCE); + parameters->insert(QStringLiteral("prompt"), QStringLiteral("login")); } if (!parameters->contains("client_id")) { parameters->insert("client_id", AUTH_CLIENT_ID); @@ -860,8 +982,8 @@ m_outputDevice = QStringLiteral("(default)"); } - emit inputDeviceChanged(m_inputDevice); - emit outputDeviceChanged(m_outputDevice); + emit inputDeviceChanged(m_inputDevice, false); + emit outputDeviceChanged(m_outputDevice, false); #endif } @@ -874,16 +996,21 @@ { m_uiScale = m_previousUiScale; emit uiScaleChanged(); + + setAudioActivated(false); #ifdef RT_AUDIO // Restore our previous settings m_inputDevice = m_previousInput; m_outputDevice = m_previousOutput; m_bufferSize = m_previousBuffer; m_useRtAudio = m_previousUseRtAudio; - emit inputDeviceChanged(m_inputDevice); - emit outputDeviceChanged(m_outputDevice); + + emit inputDeviceChanged(m_inputDevice, false); + emit outputDeviceChanged(m_outputDevice, false); emit bufferSizeChanged(); - emit audioBackendChanged(m_useRtAudio); + if (m_useRtAudio != m_previousUseRtAudio) { + emit audioBackendChanged(m_useRtAudio, false); + } #endif } @@ -891,6 +1018,9 @@ { m_previousUiScale = m_uiScale; emit newScale(); + + setAudioActivated(false); + QSettings settings; settings.beginGroup(QStringLiteral("VirtualStudio")); settings.setValue(QStringLiteral("UiScale"), m_uiScale); @@ -909,8 +1039,10 @@ m_previousInput = m_inputDevice; m_previousOutput = m_outputDevice; - emit inputDeviceChanged(m_inputDevice); - emit outputDeviceChanged(m_outputDevice); + emit previousInputChanged(); + emit previousOutputChanged(); + emit inputDeviceChanged(m_inputDevice, false); + emit outputDeviceChanged(m_outputDevice, false); #endif // attempt to join studio if requested @@ -938,48 +1070,21 @@ emit currentStudioChanged(); m_onConnectedScreen = true; - // Check if we have an address for our server - if (studioInfo->host().isEmpty()) { - // EXPERIMENTAL CODE. (It shouldn't be possible to arrive here.) - if (studioInfo->isManageable()) { - m_connectionState = QStringLiteral("Starting Studio..."); - emit connectionStateChanged(); - - // Send a put request to start our studio - m_startedStudio = true; - QString expiration = - QDateTime::currentDateTimeUtc().addSecs(60 * 60).toString(Qt::ISODate); - QJsonObject json = {{QLatin1String("enabled"), true}, - {QLatin1String("expiresAt"), expiration}}; - QJsonDocument request = QJsonDocument(json); - - QNetworkReply* reply = - m_authenticator->put(QStringLiteral("https://%1/api/servers/%2") - .arg(m_apiHost, studioInfo->id()), - request.toJson()); - connect(reply, &QNetworkReply::finished, this, [&, reply]() { - if (reply->error() != QNetworkReply::NoError) { - m_connectionState = QStringLiteral("Unable to Start Studio"); - emit connectionStateChanged(); - } else { - QByteArray response = reply->readAll(); - QJsonDocument serverState = QJsonDocument::fromJson(response); - if (serverState.object()[QStringLiteral("status")].toString() - == QLatin1String("Starting")) { - // Start our timer to check for our hostname - m_startTimer.setInterval(5000); - m_startTimer.start(); - } - } - reply->deleteLater(); + m_studioSocket = new VsWebSocket( + QUrl(QStringLiteral("wss://%1/api/servers/%2?auth_code=%3") + .arg(m_apiHost, studioInfo->id(), m_authenticator->token())), + m_authenticator->token(), QString(), QString()); + connect(m_studioSocket, &VsWebSocket::textMessageReceived, this, + [&](QString message) { + handleWebsocketMessage(message); }); - } else { - m_connectionState = QStringLiteral("Unable to Start Studio"); - emit connectionStateChanged(); - m_startedStudio = false; - } + m_studioSocket->openSocket(); + + // Check if we have an address for our server + if (studioInfo->status() != "Ready" && studioInfo->isManageable() == true) { + m_connectionState = QStringLiteral("Waiting..."); + emit connectionStateChanged(); } else { - m_startedStudio = false; completeConnection(); } } @@ -990,10 +1095,14 @@ return; } + VsServerInfo* studioInfo = static_cast(m_servers.at(m_currentStudio)); + if (studioInfo->status() == QStringLiteral("Disabled")) { + return; + } + m_jackTripRunning = true; m_connectionState = QStringLiteral("Preparing audio..."); emit connectionStateChanged(); - VsServerInfo* studioInfo = static_cast(m_servers.at(m_currentStudio)); try { std::string input = ""; std::string output = ""; @@ -1022,10 +1131,7 @@ &VirtualStudio::receivedConnectionFromPeer, Qt::QueuedConnection); - // Stop VsAudioInterface - if (!m_vsAudioInterface.isNull()) { - m_vsAudioInterface->closeAudio(); - } + setAudioActivated(false); // Setup output volume m_outputVolumePlugin = new Volume(jackTrip->getNumOutputChannels()); @@ -1110,35 +1216,13 @@ m_retryPeriod = false; if (m_jackTripRunning) { - if (m_startedStudio) { - VsServerInfo* studioInfo = - static_cast(m_servers.at(m_currentStudio)); - QMessageBox msgBox; - msgBox.setText(QStringLiteral("Do you want to stop the current studio?")); - msgBox.setWindowTitle(QStringLiteral("Stop Studio")); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::Yes); - int ret = msgBox.exec(); - if (ret == QMessageBox::Yes) { - studioInfo->setHost(QLatin1String("")); - stopStudio(); - } - } - m_device->stopPinger(); m_device->stopJackTrip(); - } else if (m_startedStudio) { - m_startTimer.stop(); - stopStudio(); - if (!m_isExiting) { - emit disconnected(); - m_onConnectedScreen = false; - } } else { // How did we get here? This shouldn't be possible, but include for safety. if (m_isExiting) { emit signalExit(); - } else { + } else if (m_onConnectedScreen) { emit disconnected(); m_onConnectedScreen = false; } @@ -1151,31 +1235,58 @@ m_refreshTimer.start(); } - // Start VsAudioInterface again - if (!m_vsAudioInterface.isNull()) { - m_vsAudioInterface->setupAudio(); - m_vsAudioInterface->setupPlugins(); - - m_view.engine()->rootContext()->setContextProperty( - QStringLiteral("inputMeterModel"), - QVariant::fromValue( - QVector(m_vsAudioInterface->getNumInputChannels()))); + m_connectionState = QStringLiteral("Disconnected"); + emit connectionStateChanged(); +} - m_vsAudioInterface->startProcess(); +void VirtualStudio::manageStudio(int studioIndex, bool start) +{ + if (studioIndex == -1) { + // We're here from a connected screen. Use our current studio. + studioIndex = m_currentStudio; } - m_connectionState = QStringLiteral("Disconnected"); - emit connectionStateChanged(); + QUrl url; + if (!start) { + url = QUrl(QStringLiteral("https://%1/studios/%2") + .arg(m_apiHost, + static_cast(m_servers.at(studioIndex))->id())); + } else { + QString expiration = + QDateTime::currentDateTimeUtc().addSecs(60 * 30).toString(Qt::ISODate); + QJsonObject json = {{QLatin1String("enabled"), true}, + {QLatin1String("expiresAt"), expiration}}; + QJsonDocument request = QJsonDocument(json); + + QNetworkReply* reply = m_authenticator->put( + QStringLiteral("https://%1/api/servers/%2") + .arg(m_apiHost, + static_cast(m_servers.at(studioIndex))->id()), + request.toJson()); + connect(reply, &QNetworkReply::finished, this, [&, reply]() { + if (reply->error() != QNetworkReply::NoError) { + m_connectionState = QStringLiteral("Unable to Start Studio"); + emit connectionStateChanged(); + } else { + QByteArray response = reply->readAll(); + QJsonDocument serverState = QJsonDocument::fromJson(response); + if (serverState.object()[QStringLiteral("status")].toString() + == QLatin1String("Starting")) {} + } + reply->deleteLater(); + }); + } + QDesktopServices::openUrl(url); } -void VirtualStudio::manageStudio(int studioIndex) +void VirtualStudio::launchVideo(int studioIndex) { if (studioIndex == -1) { // We're here from a connected screen. Use our current studio. studioIndex = m_currentStudio; } QUrl url = QUrl( - QStringLiteral("https://%1/studios/%2") + QStringLiteral("https://%1/studios/%2/live") .arg(m_apiHost, static_cast(m_servers.at(studioIndex))->id())); QDesktopServices::openUrl(url); } @@ -1242,13 +1353,12 @@ m_device = new VsDevice(m_authenticator.data(), m_testMode); m_device->registerApp(); -#ifdef __APPLE__ - if (m_permissions->micPermission() == "granted") { - startAudio(); + if (m_showDeviceSetup) { + if constexpr (isBackendAvailable() + || isBackendAvailable()) { + setAudioActivated(true); + } } -#else - startAudio(); -#endif if (m_userId.isEmpty()) { getUserId(); @@ -1266,20 +1376,25 @@ // attempt to join studio if requested if (!m_studioToJoin.isEmpty()) { - // FTUX shows warnings and device setup views - // if any of these enabled, do not immediately join - if (readyToJoin()) { - // We should join in this case - joinStudio(); - } + joinStudio(); } connect(m_device, &VsDevice::updateNetworkStats, this, &VirtualStudio::updatedStats); - connect(m_device, &VsDevice::updatedVolumeFromServer, this, + connect(m_device, &VsDevice::updatedCaptureVolumeFromServer, this, &VirtualStudio::setInputVolume); - connect(m_device, &VsDevice::updatedMuteFromServer, this, + connect(m_device, &VsDevice::updatedCaptureMuteFromServer, this, &VirtualStudio::setInputMuted); - connect(this, &VirtualStudio::updatedInputVolume, m_device, &VsDevice::updateVolume); - connect(this, &VirtualStudio::updatedInputMuted, m_device, &VsDevice::updateMute); + connect(m_device, &VsDevice::updatedPlaybackVolumeFromServer, this, + &VirtualStudio::setOutputVolume); + connect(m_device, &VsDevice::updatedPlaybackMuteFromServer, this, + &VirtualStudio::setOutputMuted); + connect(this, &VirtualStudio::updatedInputVolume, m_device, + &VsDevice::updateCaptureVolume); + connect(this, &VirtualStudio::updatedInputMuted, m_device, + &VsDevice::updateCaptureMute); + connect(this, &VirtualStudio::updatedOutputVolume, m_device, + &VsDevice::updatePlaybackVolume); + connect(this, &VirtualStudio::updatedOutputMuted, m_device, + &VsDevice::updatePlaybackMute); } void VirtualStudio::slotAuthFailed() @@ -1290,6 +1405,9 @@ void VirtualStudio::processFinished() { + // use disconnect function to handle reset of all internal flags and timers + disconnect(); + // reset network statistics m_networkStats = QJsonObject(); @@ -1298,12 +1416,6 @@ return; } - if (m_retryPeriod && m_startedStudio) { - // Retry if necessary. - completeConnection(); - return; - } - if (!m_jackTripRunning) { return; } @@ -1311,8 +1423,14 @@ m_jackTripRunning = false; m_connectionState = QStringLiteral("Disconnected"); emit connectionStateChanged(); - emit disconnected(); - m_onConnectedScreen = false; + + // if this occurs on the setup or settings screen (for example, due to an issue with + // devices) then don't emit disconnected, as that would move you back to the "Browse" + // screen + if (m_onConnectedScreen) { + m_onConnectedScreen = false; + emit disconnected(); + } #ifdef __APPLE__ m_noNap.enableNap(); #endif @@ -1358,42 +1476,34 @@ emit connected(); } -void VirtualStudio::checkForHostname() +void VirtualStudio::handleWebsocketMessage(const QString& msg) { - if (m_currentStudio < 0) { - return; - } - + QJsonObject serverState = QJsonDocument::fromJson(msg.toUtf8()).object(); + QString serverStatus = serverState[QStringLiteral("status")].toString(); + bool serverEnabled = serverState[QStringLiteral("enabled")].toBool(); + QString serverCloudId = serverState[QStringLiteral("cloudId")].toString(); VsServerInfo* studioInfo = static_cast(m_servers.at(m_currentStudio)); - QNetworkReply* reply = m_authenticator->get( - QStringLiteral("https://%1/api/servers/%2").arg(m_apiHost, studioInfo->id())); - connect(reply, &QNetworkReply::finished, this, [&, reply, studioInfo]() { - if (reply->error() != QNetworkReply::NoError) { - m_connectionState = QStringLiteral("Unable to Start Studio"); - emit connectionStateChanged(); - - // Stop our timer - m_startTimer.stop(); - } else { - QByteArray response = reply->readAll(); - QJsonDocument serverState = QJsonDocument::fromJson(response); - if (serverState.object()[QStringLiteral("status")].toString() - == QLatin1String("Ready")) { - // Ready to connect - m_startTimer.stop(); - studioInfo->setHost( - serverState.object()[QStringLiteral("serverHost")].toString()); - studioInfo->setPort( - serverState.object()[QStringLiteral("serverPort")].toInt()); - m_retryPeriod = true; - m_retryPeriodTimer.setInterval(15000); - m_retryPeriodTimer.start(); + studioInfo->setStatus(serverStatus); + studioInfo->setEnabled(serverEnabled); + studioInfo->setCloudId(serverCloudId); + if (!m_jackTripRunning) { + if (serverStatus == QLatin1String("Ready") && m_onConnectedScreen) { + studioInfo->setHost(serverState[QStringLiteral("serverHost")].toString()); + studioInfo->setPort(serverState[QStringLiteral("serverPort")].toInt()); + studioInfo->setSessionId(serverState[QStringLiteral("sessionId")].toString()); + + // Call completeConnection after a short timeout + m_startTimer.setInterval(1000); + m_startTimer.setSingleShot(true); + connect(&m_startTimer, &QTimer::timeout, this, [&]() { completeConnection(); - } + }); + + m_startTimer.start(); } - reply->deleteLater(); - ; - }); + } + + emit currentStudioChanged(); } void VirtualStudio::endRetryPeriod() @@ -1543,6 +1653,7 @@ } else if (stage == QAbstractOAuth2::Stage::RequestingAuthorization) { parameters->insert(QStringLiteral("audience"), QStringLiteral("https://api.jacktrip.org")); + parameters->insert(QStringLiteral("prompt"), QStringLiteral("login")); } }); @@ -1579,6 +1690,9 @@ { QMutexLocker locker(&m_refreshMutex); if (!m_allowRefresh || m_refreshInProgress) { + if (signalRefresh) { + emit refreshFinished(index); + } return; } else { m_refreshInProgress = true; @@ -1597,6 +1711,9 @@ reply, &QNetworkReply::finished, this, [&, reply, topServerId, firstLoad, signalRefresh]() { if (reply->error() != QNetworkReply::NoError) { + if (signalRefresh) { + emit refreshFinished(index); + } std::cout << "Error: " << reply->errorString().toStdString() << std::endl; emit authFailed(); reply->deleteLater(); @@ -1606,6 +1723,9 @@ QByteArray response = reply->readAll(); QJsonDocument serverList = QJsonDocument::fromJson(response); if (!serverList.isArray()) { + if (signalRefresh) { + emit refreshFinished(index); + } std::cout << "Error: Not an array" << std::endl; QMutexLocker locker(&m_refreshMutex); m_refreshInProgress = false; @@ -1642,7 +1762,7 @@ } continue; } - if (activeStudio || (serverInfo->isManageable() && m_showInactive)) { + if (activeStudio || m_showInactive) { serverInfo->setName( servers.at(i)[QStringLiteral("name")].toString()); serverInfo->setHost( @@ -1668,6 +1788,14 @@ servers.at(i)[QStringLiteral("sessionId")].toString()); serverInfo->setInviteKey( servers.at(i)[QStringLiteral("inviteKey")].toString()); + serverInfo->setCloudId( + servers.at(i)[QStringLiteral("cloudId")].toString()); + serverInfo->setEnabled( + servers.at(i)[QStringLiteral("enabled")].toBool()); + serverInfo->setIsOwner( + servers.at(i)[QStringLiteral("owner")].toBool()); + serverInfo->setIsAdmin( + servers.at(i)[QStringLiteral("admin")].toBool()); if (servers.at(i)[QStringLiteral("owner")].toBool()) { yourServers.append(serverInfo); serverInfo->setSection(VsServerInfo::YOUR_STUDIOS); @@ -1683,18 +1811,18 @@ std::sort(yourServers.begin(), yourServers.end(), [](QObject* first, QObject* second) { - return static_cast(first)->name() - < static_cast(second)->name(); + return *static_cast(first) + < *static_cast(second); }); std::sort(subServers.begin(), subServers.end(), [](QObject* first, QObject* second) { - return static_cast(first)->name() - < static_cast(second)->name(); + return *static_cast(first) + < *static_cast(second); }); std::sort(pubServers.begin(), pubServers.end(), [](QObject* first, QObject* second) { - return static_cast(first)->name() - < static_cast(second)->name(); + return *static_cast(first) + < *static_cast(second); }); // If we don't have any owned servers, move the JackTrip logo to an @@ -1725,6 +1853,9 @@ // request going out and the response. if (!m_allowRefresh) { m_refreshInProgress = false; + if (signalRefresh) { + emit refreshFinished(index); + } return; } m_servers.clear(); @@ -1870,8 +2001,8 @@ QStringLiteral("audioInterface"), m_vsAudioInterface.data()); } #ifdef RT_AUDIO - m_vsAudioInterface->setInputDevice(m_inputDevice); - m_vsAudioInterface->setOutputDevice(m_outputDevice); + m_vsAudioInterface->setInputDevice(m_inputDevice, false); + m_vsAudioInterface->setOutputDevice(m_outputDevice, false); m_vsAudioInterface->setAudioInterfaceMode(m_useRtAudio); #endif connect(m_vsAudioInterface.data(), &VsAudioInterface::devicesErrorMsgChanged, this, @@ -1903,6 +2034,9 @@ m_vsAudioInterface->setupPlugins(); + m_audioReady = true; + emit audioReadyChanged(); + m_view.engine()->rootContext()->setContextProperty( QStringLiteral("inputMeterModel"), QVariant::fromValue(QVector(m_vsAudioInterface->getNumInputChannels()))); @@ -1910,6 +2044,61 @@ m_vsAudioInterface->startProcess(); } +void VirtualStudio::restartAudio() +{ +#ifdef __APPLE__ + if (m_permissions->micPermission() != "granted") { + return; + } +#endif + // Start VsAudioInterface again + if (!m_vsAudioInterface.isNull()) { + m_vsAudioInterface->setupAudio(); + m_vsAudioInterface->setupPlugins(); + + m_audioReady = true; + emit audioReadyChanged(); + + m_view.engine()->rootContext()->setContextProperty( + QStringLiteral("inputMeterModel"), + QVariant::fromValue( + QVector(m_vsAudioInterface->getNumInputChannels()))); + + m_vsAudioInterface->startProcess(); + } else { + startAudio(); + } +} + +void VirtualStudio::stopAudio() +{ + // Stop VsAudioInterface + if (!m_vsAudioInterface.isNull()) { + m_vsAudioInterface->closeAudio(); + setAudioReady(false); + } +} + +void VirtualStudio::toggleAudio() +{ +#ifdef __APPLE__ + if (m_permissions->micPermission() != "granted") { + return; + } +#endif + + if constexpr (!(isBackendAvailable() + || isBackendAvailable())) { + return; + } + + if (!m_audioActivated) { + stopAudio(); + } else { + restartAudio(); + } +} + void VirtualStudio::stopStudio() { if (m_currentStudio < 0) { @@ -1933,8 +2122,10 @@ bool VirtualStudio::readyToJoin() { + // FTUX shows warnings and device setup views + // if any of these enabled, do not immediately join return m_windowState == "browse" - && (m_connectionState == QStringLiteral("Waiting") + && (m_connectionState == QStringLiteral("Waiting...") || m_connectionState == QStringLiteral("Disconnected")); } @@ -1998,6 +2189,7 @@ delete m_inputMeter; delete m_outputMeter; delete m_inputTestMeter; + delete m_studioSocket; QDesktopServices::unsetUrlHandler("jacktrip"); } diff -Nru jacktrip-1.6.8+ds/src/gui/virtualstudio.h jacktrip-1.7.0+ds/src/gui/virtualstudio.h --- jacktrip-1.6.8+ds/src/gui/virtualstudio.h 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/virtualstudio.h 2023-01-24 19:46:32.000000000 +0000 @@ -82,6 +82,10 @@ int inputDevice READ inputDevice WRITE setInputDevice NOTIFY inputDeviceChanged) Q_PROPERTY(int outputDevice READ outputDevice WRITE setOutputDevice NOTIFY outputDeviceChanged) + Q_PROPERTY(int previousInput READ previousInput WRITE setPreviousInput NOTIFY + previousInputChanged) + Q_PROPERTY(int previousOutput READ previousOutput WRITE setPreviousOutput NOTIFY + previousOutputChanged) Q_PROPERTY(QString devicesWarning READ devicesWarning NOTIFY devicesWarningChanged) Q_PROPERTY(QString devicesError READ devicesError NOTIFY devicesErrorChanged) @@ -125,8 +129,14 @@ updatedOutputVolume) Q_PROPERTY( bool inputMuted READ inputMuted WRITE setInputMuted NOTIFY updatedInputMuted) + Q_PROPERTY(bool audioActivated READ audioActivated WRITE setAudioActivated NOTIFY + audioActivatedChanged) + Q_PROPERTY( + bool audioReady READ audioReady WRITE setAudioReady NOTIFY audioReadyChanged) + Q_PROPERTY(bool backendAvailable READ backendAvailable CONSTANT) Q_PROPERTY(QString windowState READ windowState WRITE setWindowState NOTIFY windowStateUpdated) + Q_PROPERTY(QString apiHost READ apiHost WRITE setApiHost NOTIFY apiHostChanged) public: explicit VirtualStudio(bool firstRun = false, QObject* parent = nullptr); @@ -148,6 +158,10 @@ void setInputDevice(int device); int outputDevice(); void setOutputDevice(int device); + int previousInput(); + void setPreviousInput(int device); + int previousOutput(); + void setPreviousOutput(int device); QString devicesWarning(); QString devicesError(); QString devicesWarningHelpUrl(); @@ -191,7 +205,13 @@ float outputVolume(); bool inputMuted(); bool outputMuted(); + Q_INVOKABLE void restartAudio(); + bool audioActivated(); + bool audioReady(); + bool backendAvailable(); QString windowState(); + QString apiHost(); + void setApiHost(QString host); public slots: void toStandard(); @@ -206,7 +226,8 @@ void connectToStudio(int studioIndex); void completeConnection(); void disconnect(); - void manageStudio(int studioIndex); + void manageStudio(int studioIndex, bool start = false); + void launchVideo(int studioIndex); void createStudio(); void editProfile(); void showAbout(); @@ -217,6 +238,8 @@ void setOutputVolume(float multiplier); void setInputMuted(bool muted); void setOutputMuted(bool muted); + void setAudioActivated(bool activated); + void setAudioReady(bool ready); void setWindowState(QString state); void exit(); @@ -230,11 +253,13 @@ void showFirstRunChanged(); void hasRefreshTokenChanged(); void logoSectionChanged(); - void audioBackendChanged(bool useRtAudio); - void inputDeviceChanged(QString device); - void outputDeviceChanged(QString device); - void inputDeviceSelected(QString device); - void outputDeviceSelected(QString device); + void audioBackendChanged(bool useRtAudio, bool shouldRestart = true); + void inputDeviceChanged(QString device, bool shouldRestart = true); + void outputDeviceChanged(QString device, bool shouldRestart = true); + void inputDeviceSelected(QString device, bool shouldRestart = true); + void outputDeviceSelected(QString device, bool shouldRestart = true); + void previousInputChanged(); + void previousOutputChanged(); void devicesWarningChanged(); void devicesErrorChanged(); void devicesWarningHelpUrlChanged(); @@ -265,7 +290,10 @@ void updatedOutputVolume(float multiplier); void updatedInputMuted(bool muted); void updatedOutputMuted(bool muted); + void audioActivatedChanged(); + void audioReadyChanged(); void windowStateUpdated(); + void apiHostChanged(); private slots: void slotAuthSucceded(); @@ -273,7 +301,7 @@ void processFinished(); void processError(const QString& errorMessage); void receivedConnectionFromPeer(); - void checkForHostname(); + void handleWebsocketMessage(const QString& msg); void endRetryPeriod(); void launchBrowser(const QUrl& url); void joinStudio(); @@ -295,6 +323,8 @@ void getRegions(); void getUserMetadata(); void stopStudio(); + void toggleAudio(); + void stopAudio(); bool readyToJoin(); #ifdef RT_AUDIO QVariant formatDeviceList(const QStringList& devices, const QStringList& categories); @@ -317,11 +347,11 @@ bool m_selectableBackend = true; bool m_useRtAudio = false; int m_currentStudio = -1; - QString m_connectionState = QStringLiteral("Waiting"); + QString m_connectionState = QStringLiteral("Waiting..."); QScopedPointer m_jackTrip; + VsWebSocket* m_studioSocket = NULL; QTimer m_startTimer; QTimer m_retryPeriodTimer; - bool m_startedStudio = false; bool m_retryPeriod; bool m_jackTripRunning = false; @@ -352,7 +382,9 @@ bool m_testMode = false; QString m_failedMessage = ""; QUrl m_studioToJoin; - bool m_authenticated = false; + bool m_authenticated = false; + bool m_audioActivated = false; + bool m_audioReady = false; Meter* m_inputMeter; Meter* m_outputMeter; diff -Nru jacktrip-1.6.8+ds/src/gui/vsAudioInterface.cpp jacktrip-1.7.0+ds/src/gui/vsAudioInterface.cpp --- jacktrip-1.6.8+ds/src/gui/vsAudioInterface.cpp 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/vsAudioInterface.cpp 2023-01-24 19:46:32.000000000 +0000 @@ -57,17 +57,28 @@ , m_inputDeviceName("") , m_outputDeviceName("") , m_audioBufferSize(gDefaultBufferSizeInSamples) +#ifdef RT_AUDIO , m_audioInterfaceMode(VsAudioInterface::RTAUDIO) +#else + , m_audioInterfaceMode(VsAudioInterface::JACK) +#endif { QSettings settings; settings.beginGroup(QStringLiteral("Audio")); - m_inMultiplier = settings.value(QStringLiteral("InMultiplier"), 1).toFloat(); - m_outMultiplier = settings.value(QStringLiteral("OutMultiplier"), 1).toFloat(); - m_inMuted = settings.value(QStringLiteral("InMuted"), false).toBool(); - m_outMuted = settings.value(QStringLiteral("OutMuted"), false).toBool(); - m_audioInterfaceMode = (settings.value(QStringLiteral("Backend"), 0).toInt() == 1) - ? VsAudioInterface::RTAUDIO - : VsAudioInterface::JACK; + m_inMultiplier = settings.value(QStringLiteral("InMultiplier"), 1).toFloat(); + m_outMultiplier = settings.value(QStringLiteral("OutMultiplier"), 1).toFloat(); + m_inMuted = settings.value(QStringLiteral("InMuted"), false).toBool(); + m_outMuted = settings.value(QStringLiteral("OutMuted"), false).toBool(); + if constexpr (isBackendAvailable()) { + m_audioInterfaceMode = (settings.value(QStringLiteral("Backend"), 0).toInt() == 1) + ? VsAudioInterface::RTAUDIO + : VsAudioInterface::JACK; + } else if constexpr (isBackendAvailable()) { + m_audioInterfaceMode = VsAudioInterface::RTAUDIO; + } else { + m_audioInterfaceMode = VsAudioInterface::JACK; + } + m_inputDeviceName = settings.value(QStringLiteral("InputDevice"), "").toString().toStdString(); m_outputDeviceName = @@ -99,131 +110,30 @@ // Create AudioInterface Client Object if (m_audioInterfaceMode == VsAudioInterface::JACK) { -#ifndef NO_JACK - if (gVerboseFlag) - std::cout << " JackTrip:setupAudio before new JackAudioInterface" - << std::endl; - m_audioInterface.reset(new JackAudioInterface( - m_numAudioChansIn, m_numAudioChansOut, m_audioBitResolution)); - - m_audioInterface->setClientName(QStringLiteral("JackTrip")); - - if (gVerboseFlag) - std::cout << " JackTrip:setupAudio before m_audioInterface->setup" - << std::endl; - m_audioInterface->setup(true); - - std::string devicesWarningMsg = m_audioInterface->getDevicesWarningMsg(); - std::string devicesErrorMsg = m_audioInterface->getDevicesErrorMsg(); - - if (devicesWarningMsg != "") { - qDebug() << "Devices Warning: " - << QString::fromStdString(devicesWarningMsg); - } - - if (devicesErrorMsg != "") { - qDebug() << "Devices Error: " << QString::fromStdString(devicesErrorMsg); - } - - updateDevicesWarningMsg(QString::fromStdString(devicesWarningMsg)); - updateDevicesErrorMsg(QString::fromStdString(devicesErrorMsg)); - - if (gVerboseFlag) - std::cout - << " JackTrip:setupAudio before m_audioInterface->getSampleRate" - << std::endl; - m_sampleRate = m_audioInterface->getSampleRate(); - if (gVerboseFlag) - std::cout << " JackTrip:setupAudio before m_audioInterface->getDeviceID" - << std::endl; - m_deviceID = m_audioInterface->getDeviceID(); - if (gVerboseFlag) - std::cout << " JackTrip:setupAudio before " - "m_audioInterface->getBufferSizeInSamples" - << std::endl; - m_audioBufferSize = m_audioInterface->getBufferSizeInSamples(); -#endif //__NON_JACK__ -#ifdef NO_JACK /// \todo FIX THIS REPETITION OF CODE -#ifdef RT_AUDIO - std::cout << "Warning: using non jack version, RtAudio will be used instead" - << std::endl; - m_audioInterface.reset(new RtAudioInterface( - m_numAudioChansIn, m_numAudioChansOut, m_audioBitResolution)); - m_audioInterface->setSampleRate(m_sampleRate); - m_audioInterface->setDeviceID(m_deviceID); - m_audioInterface->setInputDevice(m_inputDeviceName); - m_audioInterface->setOutputDevice(m_outputDeviceName); - m_audioInterface->setBufferSizeInSamples(m_audioBufferSize); - - m_audioInterface->setup(true); - // Setup might have reduced number of channels - m_numAudioChansIn = m_audioInterface->getNumInputChannels(); - m_numAudioChansOut = m_audioInterface->getNumOutputChannels(); - // Setup might have changed buffer size - m_audioBufferSize = m_audioInterface->getBufferSizeInSamples(); - - std::string devicesWarningMsg = m_audioInterface->getDevicesWarningMsg(); - std::string devicesErrorMsg = m_audioInterface->getDevicesErrorMsg(); - - if (devicesWarningMsg != "") { - qDebug() << "Devices Warning: " - << QString::fromStdString(devicesWarningMsg); - } - - if (devicesErrorMsg != "") { - qDebug() << "Devices Error: " << QString::fromStdString(devicesErrorMsg); - } - - updateDevicesWarningMsg(QString::fromStdString(devicesWarningMsg)); - updateDevicesErrorMsg(QString::fromStdString(devicesErrorMsg)); - -#endif -#endif - } else if (m_audioInterfaceMode == VsAudioInterface::RTAUDIO) { -#ifdef RT_AUDIO - m_audioInterface.reset(new RtAudioInterface( - m_numAudioChansIn, m_numAudioChansOut, m_audioBitResolution)); - m_audioInterface->setSampleRate(m_sampleRate); - m_audioInterface->setDeviceID(m_deviceID); - m_audioInterface->setInputDevice(m_inputDeviceName); - m_audioInterface->setOutputDevice(m_outputDeviceName); - m_audioInterface->setBufferSizeInSamples(m_audioBufferSize); - - m_audioInterface->setup(true); - // Setup might have reduced number of channels - m_numAudioChansIn = m_audioInterface->getNumInputChannels(); - m_numAudioChansOut = m_audioInterface->getNumOutputChannels(); - // Setup might have changed buffer size - m_audioBufferSize = m_audioInterface->getBufferSizeInSamples(); - - std::string devicesWarningMsg = m_audioInterface->getDevicesWarningMsg(); - std::string devicesErrorMsg = m_audioInterface->getDevicesErrorMsg(); - std::string devicesWarningHelpUrl = - m_audioInterface->getDevicesWarningHelpUrl(); - std::string devicesErrorHelpUrl = m_audioInterface->getDevicesErrorHelpUrl(); - - if (devicesWarningMsg != "") { - qDebug() << "Devices Warning: " - << QString::fromStdString(devicesWarningMsg); - if (devicesWarningHelpUrl != "") { - qDebug() << "Learn More: " - << QString::fromStdString(devicesWarningHelpUrl); + if constexpr (isBackendAvailable() + || isBackendAvailable()) { + setupJackAudio(); + } else { + if constexpr (isBackendAvailable()) { + setupRtAudio(); + } else { + throw std::runtime_error( + "JackTrip was compiled without RtAudio and can't find JACK. In " + "order to use JackTrip, you'll need to install JACK or rebuild " + "with RtAudio support."); + std::exit(1); } } - - if (devicesErrorMsg != "") { - qDebug() << "Devices Error: " << QString::fromStdString(devicesErrorMsg); - if (devicesErrorHelpUrl != "") { - qDebug() << "Learn More: " - << QString::fromStdString(devicesErrorHelpUrl); - } + } else if (m_audioInterfaceMode == VsAudioInterface::RTAUDIO) { + if constexpr (isBackendAvailable()) { + setupRtAudio(); + } else { + throw std::runtime_error( + "JackTrip was compiled without RtAudio and can't find JACK. In order " + "to use JackTrip, you'll need to install JACK or rebuild with " + "RtAudio support."); + std::exit(1); } - - updateDevicesWarningMsg(QString::fromStdString(devicesWarningMsg)); - updateDevicesErrorMsg(QString::fromStdString(devicesErrorMsg)); - updateDevicesWarningHelpUrl(QString::fromStdString(devicesWarningHelpUrl)); - updateDevicesErrorHelpUrl(QString::fromStdString(devicesErrorHelpUrl)); -#endif } std::cout << "The Sampling Rate is: " << m_sampleRate << std::endl; @@ -243,6 +153,111 @@ } } +void VsAudioInterface::setupJackAudio() +{ +#ifndef NO_JACK + if constexpr (isBackendAvailable() + || isBackendAvailable()) { + if (gVerboseFlag) + std::cout << " JackTrip:setupAudio before new JackAudioInterface" + << std::endl; + m_audioInterface.reset(new JackAudioInterface( + m_numAudioChansIn, m_numAudioChansOut, m_audioBitResolution)); + + m_audioInterface->setClientName(QStringLiteral("JackTrip")); + + if (gVerboseFlag) + std::cout << " JackTrip:setupAudio before m_audioInterface->setup" + << std::endl; + m_audioInterface->setup(true); + + std::string devicesWarningMsg = m_audioInterface->getDevicesWarningMsg(); + std::string devicesErrorMsg = m_audioInterface->getDevicesErrorMsg(); + + if (devicesWarningMsg != "") { + qDebug() << "Devices Warning: " << QString::fromStdString(devicesWarningMsg); + } + + if (devicesErrorMsg != "") { + qDebug() << "Devices Error: " << QString::fromStdString(devicesErrorMsg); + } + + updateDevicesWarningMsg(QString::fromStdString(devicesWarningMsg)); + updateDevicesErrorMsg(QString::fromStdString(devicesErrorMsg)); + + if (gVerboseFlag) + std::cout << " JackTrip:setupAudio before m_audioInterface->getSampleRate" + << std::endl; + m_sampleRate = m_audioInterface->getSampleRate(); + if (gVerboseFlag) + std::cout << " JackTrip:setupAudio before m_audioInterface->getDeviceID" + << std::endl; + m_deviceID = m_audioInterface->getDeviceID(); + if (gVerboseFlag) + std::cout << " JackTrip:setupAudio before " + "m_audioInterface->getBufferSizeInSamples" + << std::endl; + m_audioBufferSize = m_audioInterface->getBufferSizeInSamples(); + } else { + return; + } +#else + return; +#endif +} + +void VsAudioInterface::setupRtAudio() +{ +#ifdef RT_AUDIO + if constexpr (isBackendAvailable() + || isBackendAvailable()) { + m_audioInterface.reset(new RtAudioInterface(m_numAudioChansIn, m_numAudioChansOut, + m_audioBitResolution)); + m_audioInterface->setSampleRate(m_sampleRate); + m_audioInterface->setDeviceID(m_deviceID); + m_audioInterface->setInputDevice(m_inputDeviceName); + m_audioInterface->setOutputDevice(m_outputDeviceName); + m_audioInterface->setBufferSizeInSamples(m_audioBufferSize); + + m_audioInterface->setup(true); + // Setup might have reduced number of channels + m_numAudioChansIn = m_audioInterface->getNumInputChannels(); + m_numAudioChansOut = m_audioInterface->getNumOutputChannels(); + // Setup might have changed buffer size + m_audioBufferSize = m_audioInterface->getBufferSizeInSamples(); + + std::string devicesWarningMsg = m_audioInterface->getDevicesWarningMsg(); + std::string devicesErrorMsg = m_audioInterface->getDevicesErrorMsg(); + std::string devicesWarningHelpUrl = m_audioInterface->getDevicesWarningHelpUrl(); + std::string devicesErrorHelpUrl = m_audioInterface->getDevicesErrorHelpUrl(); + + if (devicesWarningMsg != "") { + qDebug() << "Devices Warning: " << QString::fromStdString(devicesWarningMsg); + if (devicesWarningHelpUrl != "") { + qDebug() << "Learn More: " + << QString::fromStdString(devicesWarningHelpUrl); + } + } + + if (devicesErrorMsg != "") { + qDebug() << "Devices Error: " << QString::fromStdString(devicesErrorMsg); + if (devicesErrorHelpUrl != "") { + qDebug() << "Learn More: " << QString::fromStdString(devicesErrorHelpUrl); + } + } + + updateDevicesWarningMsg(QString::fromStdString(devicesWarningMsg)); + updateDevicesErrorMsg(QString::fromStdString(devicesErrorMsg)); + updateDevicesWarningHelpUrl(QString::fromStdString(devicesWarningHelpUrl)); + updateDevicesErrorHelpUrl(QString::fromStdString(devicesErrorHelpUrl)); + } else { + return; + } +#else + return; +#endif +} + void VsAudioInterface::closeAudio() { if (!m_audioInterface.isNull()) { @@ -286,7 +301,7 @@ m_audioInterface->appendProcessPluginFromNetwork(plugin); } -void VsAudioInterface::setInputDevice(QString deviceName) +void VsAudioInterface::setInputDevice(QString deviceName, bool shouldRestart) { m_inputDeviceName = deviceName.toStdString(); if (m_inputDeviceName == "(default)") { @@ -295,13 +310,13 @@ if (!m_audioInterface.isNull()) { m_audioInterface->setInputDevice(m_inputDeviceName); - if (m_audioActive) { + if (m_audioActive && shouldRestart) { emit settingsUpdated(); } } } -void VsAudioInterface::setOutputDevice(QString deviceName) +void VsAudioInterface::setOutputDevice(QString deviceName, bool shouldRestart) { m_outputDeviceName = deviceName.toStdString(); if (m_outputDeviceName == "(default)") { @@ -310,20 +325,24 @@ if (!m_audioInterface.isNull()) { m_audioInterface->setOutputDevice(m_outputDeviceName); - if (m_audioActive) { + if (m_audioActive && shouldRestart) { emit settingsUpdated(); } } } -void VsAudioInterface::setAudioInterfaceMode(bool useRtAudio) +void VsAudioInterface::setAudioInterfaceMode(bool useRtAudio, bool shouldRestart) { if (useRtAudio) { +#ifdef RT_AUDIO m_audioInterfaceMode = VsAudioInterface::RTAUDIO; +#endif } else { +#ifndef NO_JACK m_audioInterfaceMode = VsAudioInterface::JACK; +#endif } - if (!m_audioInterface.isNull() || m_hasBeenActive) { + if ((!m_audioInterface.isNull() || m_hasBeenActive) && shouldRestart) { emit modeUpdated(); } } @@ -373,9 +392,11 @@ try { m_audioInterface->initPlugins(false); m_audioInterface->startProcess(); +#ifndef NO_JACK if (m_audioInterfaceMode == VsAudioInterface::JACK) { m_audioInterface->connectDefaultPorts(); } +#endif } catch (const std::exception& e) { emit errorToProcess(QString::fromUtf8(e.what())); } diff -Nru jacktrip-1.6.8+ds/src/gui/vsAudioInterface.h jacktrip-1.7.0+ds/src/gui/vsAudioInterface.h --- jacktrip-1.6.8+ds/src/gui/vsAudioInterface.h 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/vsAudioInterface.h 2023-01-24 19:46:32.000000000 +0000 @@ -54,6 +54,7 @@ #include "../Tone.h" #include "../Volume.h" #include "../jacktrip_globals.h" +#include "AudioInterfaceMode.h" class VsAudioInterface : public QObject { @@ -91,9 +92,9 @@ }; public slots: - void setInputDevice(QString deviceName); - void setOutputDevice(QString deviceName); - void setAudioInterfaceMode(bool useRtAudio); + void setInputDevice(QString deviceName, bool shouldRestart = true); + void setOutputDevice(QString deviceName, bool shouldRestart = true); + void setAudioInterfaceMode(bool useRtAudio, bool shouldRestart = true); void setInputVolume(float multiplier); void setOutputVolume(float multiplier); void setInputMuted(bool muted); @@ -120,6 +121,9 @@ void processMeterMeasurements(QVector values); private: + void setupJackAudio(); + void setupRtAudio(); + float m_inMultiplier = 1.0; float m_outMultiplier = 1.0; bool m_inMuted = false; diff -Nru jacktrip-1.6.8+ds/src/gui/vsDevice.cpp jacktrip-1.7.0+ds/src/gui/vsDevice.cpp --- jacktrip-1.6.8+ds/src/gui/vsDevice.cpp 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/vsDevice.cpp 2023-01-24 19:46:32.000000000 +0000 @@ -55,6 +55,9 @@ m_captureVolume = (float)settings.value(QStringLiteral("InMultiplier"), 1.0).toDouble(); m_captureMute = settings.value(QStringLiteral("InMuted"), false).toBool(); + m_playbackVolume = + (float)settings.value(QStringLiteral("OutMultiplier"), 1.0).toDouble(); + m_playbackMute = settings.value(QStringLiteral("OutMuted"), false).toBool(); settings.endGroup(); m_sendVolumeTimer = new QTimer(this); @@ -71,6 +74,8 @@ QJsonObject json = { {QLatin1String("captureVolume"), (int)(m_captureVolume * 100.0)}, {QLatin1String("captureMute"), m_captureMute}, + {QLatin1String("playbackVolume"), (int)(m_playbackVolume * 100.0)}, + {QLatin1String("playbackMute"), m_playbackMute}, }; QJsonDocument request = QJsonDocument(json); @@ -84,31 +89,37 @@ reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if (!statusCode.isValid()) { std::cout << "Error: " << reply->errorString().toStdString() << std::endl; - // TODO: Fix me - // emit authFailed(); reply->deleteLater(); return; } } else { QByteArray response = reply->readAll(); QJsonDocument deviceState = QJsonDocument::fromJson(response); - float deviceCaptureVol = + + // capture (input) volume + m_captureVolume = (float)(deviceState.object()[QStringLiteral("captureVolume")].toDouble() / 100.0); - float deviceCaptureMute = - deviceState.object()[QStringLiteral("captureMute")].toBool(); - - m_captureVolume = deviceCaptureVol; - emit updatedVolumeFromServer(m_captureVolume); - - m_captureMute = deviceCaptureMute; - emit updatedMuteFromServer(m_captureMute); + m_captureMute = deviceState.object()[QStringLiteral("captureMute")].toBool(); + emit updatedCaptureVolumeFromServer(m_captureVolume); + emit updatedCaptureMuteFromServer(m_captureMute); + + // playback (output) volume + m_playbackVolume = + (float)(deviceState.object()[QStringLiteral("playbackVolume")].toDouble() + / 100.0); + m_playbackMute = + deviceState.object()[QStringLiteral("playbackMute")].toBool(); + emit updatedPlaybackVolumeFromServer(m_playbackVolume); + emit updatedPlaybackMuteFromServer(m_playbackMute); } QSettings settings; settings.beginGroup(QStringLiteral("Audio")); settings.setValue(QStringLiteral("InMultiplier"), m_captureVolume); settings.setValue(QStringLiteral("InMuted"), m_captureMute); + settings.setValue(QStringLiteral("OutMultiplier"), m_playbackVolume); + settings.setValue(QStringLiteral("OutMuted"), m_playbackMute); settings.endGroup(); reply->deleteLater(); @@ -132,8 +143,6 @@ reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if (!statusCode.isValid()) { std::cout << "Error: " << reply->errorString().toStdString() << std::endl; - // TODO: Fix me - // emit authFailed(); reply->deleteLater(); return; } @@ -152,8 +161,6 @@ } else { // Other error status. Won't create device. std::cout << "Error: " << reply->errorString().toStdString() << std::endl; - // TODO: Fix me - // emit authFailed(); reply->deleteLater(); return; } @@ -184,8 +191,6 @@ connect(reply, &QNetworkReply::finished, this, [=]() { if (reply->error() != QNetworkReply::NoError) { std::cout << "Error: " << reply->errorString().toStdString() << std::endl; - // TODO: Fix me - // emit authFailed(); reply->deleteLater(); return; } else { @@ -280,8 +285,6 @@ connect(reply, &QNetworkReply::finished, this, [=]() { if (reply->error() != QNetworkReply::NoError) { std::cout << "Error: " << reply->errorString().toStdString() << std::endl; - // TODO: Fix me - // emit authFailed(); reply->deleteLater(); return; } else { @@ -307,8 +310,6 @@ connect(reply, &QNetworkReply::finished, this, [=]() { if (reply->error() != QNetworkReply::NoError) { std::cout << "Error: " << reply->errorString().toStdString() << std::endl; - // TODO: Fix me - // emit authFailed(); reply->deleteLater(); return; } @@ -322,6 +323,8 @@ QJsonObject json = { {QLatin1String("captureVolume"), (int)(m_captureVolume * 100.0)}, {QLatin1String("captureMute"), m_captureMute}, + {QLatin1String("playbackVolume"), (int)(m_playbackVolume * 100.0)}, + {QLatin1String("playbackMute"), m_playbackMute}, }; QJsonDocument request = QJsonDocument(json); QNetworkReply* reply = m_authenticator->put( @@ -330,8 +333,6 @@ connect(reply, &QNetworkReply::finished, this, [=]() { if (reply->error() != QNetworkReply::NoError) { std::cout << "Error: " << reply->errorString().toStdString() << std::endl; - // TODO: Fix me - // emit authFailed(); reply->deleteLater(); return; } @@ -415,11 +416,13 @@ return; } for (auto it = newObject.constBegin(); it != newObject.constEnd(); it++) { + // if currently enabled but new config is not enabled, disconnect immediately + if (enabled() && it.key() == "enabled" && !it.value().toBool() + && !m_jackTrip.isNull()) { + stopJackTrip(); + } m_deviceAgentConfig.insert(it.key(), it.value()); } - if (!enabled() && !m_jackTrip.isNull()) { - stopJackTrip(); - } } // initPinger intializes the pinger used to generate network latency statistics for @@ -445,8 +448,8 @@ } } -// updateVolume sets VsDevice's capture volume to the provided float -void VsDevice::updateVolume(float multiplier) +// updateCaptureVolume sets VsDevice's capture (input) volume to the provided float +void VsDevice::updateCaptureVolume(float multiplier) { if (multiplier == m_captureVolume) { return; @@ -458,8 +461,8 @@ } } -// updateMute sets VsDevice's capture mute to the provided boolean -void VsDevice::updateMute(bool muted) +// updateCaptureMute sets VsDevice's capture (input) mute to the provided boolean +void VsDevice::updateCaptureMute(bool muted) { if (muted == m_captureMute) { return; @@ -471,6 +474,32 @@ } } +// updatePlaybackVolume sets VsDevice's playback (output) volume to the provided float +void VsDevice::updatePlaybackVolume(float multiplier) +{ + if (multiplier == m_playbackVolume) { + return; + } + m_playbackVolume = multiplier; + + if (m_sendVolumeTimer) { + m_sendVolumeTimer->start(200); + } +} + +// updatePlaybackMute sets VsDevice's playback (output) mute to the provided boolean +void VsDevice::updatePlaybackMute(bool muted) +{ + if (muted == m_playbackMute) { + return; + } + m_playbackMute = muted; + + if (m_sendVolumeTimer) { + m_sendVolumeTimer->start(200); + } +} + // terminateJackTrip is a slot intended to be triggered on jacktrip process signals void VsDevice::terminateJackTrip() { @@ -493,17 +522,32 @@ m_pinger->start(); } - bool newMute = newState["captureMute"].toBool(); - float newCaptureVolume = (float)(newState["captureVolume"].toDouble() / 100.0); - - if (newCaptureVolume != m_captureVolume) { - m_captureVolume = newCaptureVolume; - emit updatedVolumeFromServer(m_captureVolume); + // capture (input) volume + bool newMute = newState["captureMute"].toBool(); + float newVolume = (float)(newState["captureVolume"].toDouble() / 100.0); + + if (newVolume != m_captureVolume) { + m_captureVolume = newVolume; + emit updatedCaptureVolumeFromServer(m_captureVolume); } if (newMute != m_captureMute) { m_captureMute = newMute; - emit updatedMuteFromServer(m_captureMute); + emit updatedCaptureMuteFromServer(m_captureMute); + } + + // playback (output) volume + newMute = newState["playbackMute"].toBool(); + newVolume = (float)(newState["playbackVolume"].toDouble() / 100.0); + + if (newVolume != m_playbackVolume) { + m_playbackVolume = newVolume; + emit updatedPlaybackVolumeFromServer(m_playbackVolume); + } + + if (newMute != m_playbackMute) { + m_playbackMute = newMute; + emit updatedPlaybackMuteFromServer(m_playbackMute); } reconcileAgentConfig(newState); @@ -575,8 +619,6 @@ connect(reply, &QNetworkReply::finished, this, [=]() { if (reply->error() != QNetworkReply::NoError) { std::cout << "Error: " << reply->errorString().toStdString() << std::endl; - // TODO: Fix me - // emit authFailed(); reply->deleteLater(); return; } else { diff -Nru jacktrip-1.6.8+ds/src/gui/vsDevice.h jacktrip-1.7.0+ds/src/gui/vsDevice.h --- jacktrip-1.6.8+ds/src/gui/vsDevice.h 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/vsDevice.h 2023-01-24 19:46:32.000000000 +0000 @@ -77,12 +77,16 @@ signals: void updateNetworkStats(QJsonObject stats); - void updatedVolumeFromServer(float multiplier); - void updatedMuteFromServer(bool muted); + void updatedCaptureVolumeFromServer(float multiplier); + void updatedCaptureMuteFromServer(bool muted); + void updatedPlaybackVolumeFromServer(float multiplier); + void updatedPlaybackMuteFromServer(bool muted); public slots: - void updateVolume(float multiplier); - void updateMute(bool muted); + void updateCaptureVolume(float multiplier); + void updateCaptureMute(bool muted); + void updatePlaybackVolume(float multiplier); + void updatePlaybackMute(bool muted); private slots: void terminateJackTrip(); @@ -107,8 +111,10 @@ QScopedPointer m_jackTrip; QOAuth2AuthorizationCodeFlow* m_authenticator; QRandomGenerator m_randomizer; - float m_captureVolume = 1.0; - bool m_captureMute = false; + float m_captureVolume = 1.0; + bool m_captureMute = false; + float m_playbackVolume = 1.0; + bool m_playbackMute = false; QTimer* m_sendVolumeTimer; }; diff -Nru jacktrip-1.6.8+ds/src/gui/vsServerInfo.cpp jacktrip-1.7.0+ds/src/gui/vsServerInfo.cpp --- jacktrip-1.6.8+ds/src/gui/vsServerInfo.cpp 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/vsServerInfo.cpp 2023-01-24 19:46:32.000000000 +0000 @@ -44,7 +44,7 @@ return m_section; } -QString VsServerInfo::type() +QString VsServerInfo::type() const { if (m_section == YOUR_STUDIOS) { return QStringLiteral("Your Studios"); @@ -60,7 +60,7 @@ m_section = section; } -QString VsServerInfo::name() +QString VsServerInfo::name() const { return m_name; } @@ -70,28 +70,24 @@ m_name = name; } -QString VsServerInfo::host() +QString VsServerInfo::host() const { return m_host; } -QString VsServerInfo::status() +QString VsServerInfo::status() const { return m_status; } -bool VsServerInfo::canConnect() +bool VsServerInfo::canConnect() const { return !m_host.isEmpty() && m_status == "Ready"; } -bool VsServerInfo::canStart() +bool VsServerInfo::canStart() const { -#ifdef PSI - return true; -#else - return false; -#endif + return m_owner || m_admin; } void VsServerInfo::setHost(const QString& host) @@ -106,7 +102,7 @@ emit canConnectChanged(); } -quint16 VsServerInfo::port() +quint16 VsServerInfo::port() const { return m_port; } @@ -116,7 +112,37 @@ m_port = port; } -bool VsServerInfo::isPublic() +bool VsServerInfo::enabled() const +{ + return m_enabled; +} + +void VsServerInfo::setEnabled(bool enabled) +{ + m_enabled = enabled; +} + +bool VsServerInfo::isOwner() const +{ + return m_owner; +} + +void VsServerInfo::setIsOwner(bool owner) +{ + m_owner = owner; +} + +bool VsServerInfo::isAdmin() const +{ + return m_admin; +} + +void VsServerInfo::setIsAdmin(bool admin) +{ + m_admin = admin; +} + +bool VsServerInfo::isPublic() const { return m_isPublic; } @@ -126,12 +152,12 @@ m_isPublic = isPublic; } -QString VsServerInfo::region() +QString VsServerInfo::region() const { return m_region; } -QString VsServerInfo::flag() +QString VsServerInfo::flag() const { QStringList parts = m_region.split(QStringLiteral("-")); if (parts.count() > 1) { @@ -145,7 +171,7 @@ return QStringLiteral("flags/US.svg"); } -QString VsServerInfo::location() +QString VsServerInfo::location() const { return m_region; } @@ -155,7 +181,7 @@ m_region = region; } -bool VsServerInfo::isManageable() +bool VsServerInfo::isManageable() const { return m_isManageable; } @@ -165,7 +191,7 @@ m_isManageable = isManageable; } -quint16 VsServerInfo::period() +quint16 VsServerInfo::period() const { return m_period; } @@ -175,7 +201,7 @@ m_period = period; } -quint32 VsServerInfo::sampleRate() +quint32 VsServerInfo::sampleRate() const { return m_sampleRate; } @@ -185,7 +211,7 @@ m_sampleRate = sampleRate; } -quint16 VsServerInfo::queueBuffer() +quint16 VsServerInfo::queueBuffer() const { return m_queueBuffer; } @@ -195,7 +221,7 @@ m_queueBuffer = queueBuffer; } -QString VsServerInfo::bannerURL() +QString VsServerInfo::bannerURL() const { return m_bannerURL; } @@ -205,7 +231,7 @@ m_bannerURL = bannerURL; } -QString VsServerInfo::id() +QString VsServerInfo::id() const { return m_id; } @@ -215,7 +241,7 @@ m_id = id; } -QString VsServerInfo::sessionId() +QString VsServerInfo::sessionId() const { return m_sessionId; } @@ -225,7 +251,7 @@ m_sessionId = sessionId; } -QString VsServerInfo::inviteKey() +QString VsServerInfo::inviteKey() const { return m_inviteKey; } @@ -235,4 +261,26 @@ m_inviteKey = inviteKey; } +QString VsServerInfo::cloudId() const +{ + return m_cloudId; +} + +void VsServerInfo::setCloudId(const QString& cloudId) +{ + m_cloudId = cloudId; +} + +bool VsServerInfo::operator<(const VsServerInfo& other) const +{ + if (status() == QStringLiteral("Ready")) { + if (other.status() != QStringLiteral("Ready")) { + return true; + } + } else if (other.status() == QStringLiteral("Ready")) { + return false; + } + return name() < other.name(); +} + VsServerInfo::~VsServerInfo() = default; diff -Nru jacktrip-1.6.8+ds/src/gui/vsServerInfo.h jacktrip-1.7.0+ds/src/gui/vsServerInfo.h --- jacktrip-1.6.8+ds/src/gui/vsServerInfo.h 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/gui/vsServerInfo.h 2023-01-24 19:46:32.000000000 +0000 @@ -59,6 +59,8 @@ Q_PROPERTY(quint32 sampleRate READ sampleRate CONSTANT) Q_PROPERTY(quint16 queueBuffer READ queueBuffer CONSTANT) Q_PROPERTY(QString status READ status CONSTANT) + Q_PROPERTY(bool enabled READ enabled CONSTANT) + Q_PROPERTY(QString cloudId READ cloudId CONSTANT) Q_PROPERTY(QString id READ id CONSTANT) Q_PROPERTY(QString inviteKey READ inviteKey CONSTANT) @@ -69,40 +71,49 @@ ~VsServerInfo() override; serverSectionT section(); - QString type(); + QString type() const; void setSection(serverSectionT section); - QString name(); + QString name() const; void setName(const QString& name); - QString host(); - bool canConnect(); - bool canStart(); + QString host() const; + bool canConnect() const; + bool canStart() const; void setHost(const QString& host); - quint16 port(); + quint16 port() const; void setPort(quint16 port); - bool isPublic(); + bool enabled() const; + void setEnabled(bool enabled); + bool isOwner() const; + void setIsOwner(bool owner); + bool isAdmin() const; + void setIsAdmin(bool admin); + bool isPublic() const; void setIsPublic(bool isPublic); - QString region(); - QString flag(); - QString location(); + QString region() const; + QString flag() const; + QString location() const; void setRegion(const QString& region); - bool isManageable(); + bool isManageable() const; void setIsManageable(bool isManageable); - quint16 period(); + quint16 period() const; void setPeriod(quint16 period); - quint32 sampleRate(); + quint32 sampleRate() const; void setSampleRate(quint32 sampleRate); - quint16 queueBuffer(); + quint16 queueBuffer() const; void setQueueBuffer(quint16 queueBuffer); - QString bannerURL(); + QString bannerURL() const; void setBannerURL(const QString& bannerURL); - QString id(); + QString id() const; void setId(const QString& id); - QString sessionId(); + QString sessionId() const; void setSessionId(const QString& sessionId); - QString status(); + QString status() const; void setStatus(const QString& status); - QString inviteKey(); + QString inviteKey() const; void setInviteKey(const QString& inviteKey); + QString cloudId() const; + void setCloudId(const QString& cloudId); + bool operator<(const VsServerInfo& other) const; signals: void canConnectChanged(); @@ -112,6 +123,9 @@ QString m_name; QString m_host; quint16 m_port; + bool m_enabled; + bool m_owner; + bool m_admin; bool m_isPublic; QString m_region; bool m_isManageable; @@ -122,6 +136,7 @@ QString m_id; QString m_sessionId; QString m_status; + QString m_cloudId; QString m_inviteKey; /* Remaining JSON fields @@ -132,17 +147,11 @@ "size": "c5.large", "mixBranch": "main", "mixCode": "SimpleMix(~maxClients).masterVolume_(1).connect.start;", - "enabled": true, - "admin": true, - "cloudId": "string", - "owner": true, "ownerId": "string", - "status": "Ready", "subStatus": "Active", "createdAt": "2021-09-07T17:15:38Z", "expiresAt": "2021-09-07T17:15:38Z", "updatedAt": "2021-09-07T17:15:38Z" - "inviteKey": "invitestring", */ }; diff -Nru jacktrip-1.6.8+ds/src/JackTrip.cpp jacktrip-1.7.0+ds/src/JackTrip.cpp --- jacktrip-1.6.8+ds/src/JackTrip.cpp 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/JackTrip.cpp 2023-01-24 19:46:32.000000000 +0000 @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -126,6 +127,8 @@ , mUseAuth(false) , mRedundancy(redundancy) , mTimeoutTimer(this) + , mRetryTimer(this) + , mRetries(0) , mSleepTime(100) , mElapsedTime(0) , mEndTime(0) @@ -734,7 +737,7 @@ return; } mAwaitingTcp = false; - mTimeoutTimer.stop(); + mRetryTimer.stop(); } if (gVerboseFlag) cout << "TCP Socket Connected to Server!" << endl; @@ -898,6 +901,15 @@ completeConnection(); } +void JackTrip::receivedErrorTCP(QAbstractSocket::SocketError socketError) +{ + if (socketError != QAbstractSocket::ConnectionRefusedError) { + mTcpClient.close(); + mRetryTimer.stop(); + stop(QStringLiteral("TCP Socket Error: ") + QString::number(socketError)); + } +} + void JackTrip::connectionSecured() { // Now that the connection is encrypted, send out port, and credentials. @@ -1047,18 +1059,48 @@ // Stop everything. mAwaitingTcp = false; mTcpClient.close(); - mTimeoutTimer.stop(); + mRetryTimer.stop(); stop(); + return; } - mElapsedTime += mSleepTime; + mElapsedTime += mRetryTimer.interval(); if (mEndTime > 0 && mElapsedTime >= mEndTime) { mAwaitingTcp = false; mTcpClient.close(); - mTimeoutTimer.stop(); + mRetryTimer.stop(); cout << "JackTrip Server Timed Out!" << endl; stop(QStringLiteral("Initial TCP Connection Timed Out")); + return; } + + // Use randomized exponential backoff to reconnect the TCP client + QRandomGenerator randomizer; + mRetries++; + // exponential backoff sleep with 6s maximum + jitter + int newInterval = 2000 * pow(2, mRetries); + newInterval = std::min(newInterval, 6000); + newInterval += randomizer.bounded(0, 500); + QString now = QDateTime::currentDateTime().toString(Qt::ISODate); + qDebug() << "Sleep time " << newInterval << " ms at " << now; + mRetryTimer.setInterval(newInterval); + + qDebug() << "Connection timed out. Retrying again using exponential backoff"; + + mTcpClient.abort(); + + // Create Socket Objects + QHostAddress serverHostAddress; + if (!serverHostAddress.setAddress(mPeerAddress)) { + QHostInfo info = QHostInfo::fromName(mPeerAddress); + if (!info.addresses().isEmpty()) { + // use the first IP address + serverHostAddress = info.addresses().constFirst(); + } + } + mTcpClient.connectToHost(serverHostAddress, mTcpServerPort); + + mRetryTimer.start(); } //******************************************************************************* @@ -1247,16 +1289,27 @@ // ---------------------------------------------- connect(&mTcpClient, &QTcpSocket::readyRead, this, &JackTrip::receivedDataTCP); connect(&mTcpClient, &QTcpSocket::connected, this, &JackTrip::receivedConnectionTCP); + // Enable CI builds on Ubuntu 20.04 with Qt 5.12.8 +#ifdef __linux__ + connect(&mTcpClient, + QOverload::of(&QAbstractSocket::error), this, + &JackTrip::receivedErrorTCP); +#else + connect(&mTcpClient, &QTcpSocket::errorOccurred, this, &JackTrip::receivedErrorTCP); +#endif { QMutexLocker lock(&mTimerMutex); + QRandomGenerator randomizer; mAwaitingTcp = true; mElapsedTime = 0; - mEndTime = 5000; // Timeout after 5 seconds. - mTimeoutTimer.setInterval(mSleepTime); - mTimeoutTimer.disconnect(); - connect(&mTimeoutTimer, &QTimer::timeout, this, &JackTrip::tcpTimerTick); - mTimeoutTimer.start(); + mEndTime = 30000; // Timeout after 30 seconds. + mRetryTimer.setInterval(randomizer.bounded(0, 2000 * pow(2, mRetries))); + mRetryTimer.setSingleShot(true); + mRetryTimer.disconnect(); + connect(&mRetryTimer, &QTimer::timeout, this, &JackTrip::tcpTimerTick); + mRetryTimer.start(); } + mTcpClient.connectToHost(serverHostAddress, mTcpServerPort); if (gVerboseFlag) diff -Nru jacktrip-1.6.8+ds/src/jacktrip_globals.h jacktrip-1.7.0+ds/src/jacktrip_globals.h --- jacktrip-1.6.8+ds/src/jacktrip_globals.h 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/jacktrip_globals.h 2023-01-24 19:46:32.000000000 +0000 @@ -40,7 +40,7 @@ #include "AudioInterface.h" -constexpr const char* const gVersion = "1.6.8"; ///< JackTrip version +constexpr const char* const gVersion = "1.7.0"; ///< JackTrip version //******************************************************************************* /// \name Default Values diff -Nru jacktrip-1.6.8+ds/src/JackTrip.h jacktrip-1.7.0+ds/src/JackTrip.h --- jacktrip-1.6.8+ds/src/JackTrip.h 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/JackTrip.h 2023-01-24 19:46:32.000000000 +0000 @@ -567,6 +567,7 @@ private slots: void receivedConnectionTCP(); void receivedDataTCP(); + void receivedErrorTCP(QAbstractSocket::SocketError socketError); void connectionSecured(); void receivedDataUDP(); void udpTimerTick(); @@ -682,6 +683,8 @@ mProcessPluginsToNetwork; ///< Vector of ProcessPlugins QTimer mTimeoutTimer; + QTimer mRetryTimer; + int mRetries; int mSleepTime; int mElapsedTime; int mEndTime; diff -Nru jacktrip-1.6.8+ds/src/RtAudioInterface.cpp jacktrip-1.7.0+ds/src/RtAudioInterface.cpp --- jacktrip-1.6.8+ds/src/RtAudioInterface.cpp 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/RtAudioInterface.cpp 2023-01-24 19:46:32.000000000 +0000 @@ -100,8 +100,8 @@ unsigned int n_devices_output = all_output_devices.size(); unsigned int n_devices_total = n_devices_input + n_devices_output; - RtAudio* rtAudioIn; - RtAudio* rtAudioOut; + RtAudio* rtAudioIn = NULL; + RtAudio* rtAudioOut = NULL; // unsigned int n_devices = mRtAudio->getDeviceCount(); if (n_devices_total < 1) { @@ -257,6 +257,11 @@ RtAudio::getCompiledApi(apis); for (uint32_t i = 0; i < apis.size(); i++) { +#ifdef _WIN32 + if (apis.at(i) == RtAudio::UNIX_JACK) { + continue; + } +#endif RtAudio rtaudio(apis.at(i)); unsigned int devices = rtaudio.getDeviceCount(); for (unsigned int j = 0; j < devices; j++) { @@ -483,6 +488,11 @@ RtAudio::getCompiledApi(apis); for (uint32_t i = 0; i < apis.size(); i++) { +#ifdef _WIN32 + if (apis.at(i) == RtAudio::UNIX_JACK) { + continue; + } +#endif RtAudio::Api api = apis.at(i); RtAudio rtaudio(api); unsigned int devices = rtaudio.getDeviceCount(); @@ -500,6 +510,14 @@ continue; } + if (QString::fromStdString(info.name) == "JackRouter") { + continue; + } + + if (info.probed == false) { + continue; + } + if (isInput && info.inputChannels > 0) { list->append(QString::fromStdString(info.name)); } else if (!isInput && info.outputChannels > 0) { @@ -541,6 +559,11 @@ RtAudio::getCompiledApi(apis); for (uint32_t i = 0; i < apis.size(); i++) { +#ifdef _WIN32 + if (apis.at(i) == RtAudio::UNIX_JACK) { + continue; + } +#endif RtAudio rtaudio(apis.at(i)); unsigned int devices = rtaudio.getDeviceCount(); for (unsigned int j = 0; j < devices; j++) { diff -Nru jacktrip-1.6.8+ds/src/Settings.cpp jacktrip-1.7.0+ds/src/Settings.cpp --- jacktrip-1.6.8+ds/src/Settings.cpp 2022-12-05 23:46:20.000000000 +0000 +++ jacktrip-1.7.0+ds/src/Settings.cpp 2023-01-24 19:46:32.000000000 +0000 @@ -67,6 +67,12 @@ #include "RtAudioInterface.h" #endif +#ifdef JACKTRIP_BUILD_INFO +#define STR(s) #s +#define TO_STRING(s) STR(s) +#define PRINT_BUILD_INFO cout << "Build Info: " << TO_STRING(JACKTRIP_BUILD_INFO) << endl; +#endif + //#include "ThreadPoolTest.h" using std::cout; @@ -89,7 +95,9 @@ OPT_NUMSEND, OPT_APPENDTHREADID, OPT_LISTDEVICES, - OPT_AUDIODEVICE + OPT_AUDIODEVICE, + OPT_AUDIOINPUTDEVICE, + OPT_AUDIOOUTPUTDEVICE }; //******************************************************************************* @@ -153,6 +161,10 @@ 'd'}, // Set RTAudio device id to use (DEPRECATED) {"audiodevice", required_argument, NULL, OPT_AUDIODEVICE}, // Set RTAudio devices by name + {"audioinputdevice", required_argument, NULL, + OPT_AUDIOINPUTDEVICE}, // Set RTAudio input device by name + {"audiooutputdevice", required_argument, NULL, + OPT_AUDIOOUTPUTDEVICE}, // Set RTAudio output device by name {"listdevices", no_argument, NULL, OPT_LISTDEVICES}, // Set RTAudio device id to use {"bufsize", required_argument, NULL, 'F'}, // Set buffer Size @@ -403,6 +415,12 @@ //------------------------------------------------------- setDevicesByString(optarg); break; + case OPT_AUDIOINPUTDEVICE: + mInputDeviceName = optarg; + break; + case OPT_AUDIOOUTPUTDEVICE: + mOutputDeviceName = optarg; + break; case OPT_LISTDEVICES: // List audio devices //------------------------------------------------------- RtAudioInterface::printDevices(); @@ -416,7 +434,10 @@ case 'v': //------------------------------------------------------- cout << "JackTrip VERSION: " << gVersion << endl; - cout << "Copyright (c) 2008-2021 Juan-Pablo Caceres, Chris Chafe." << endl; +#ifdef JACKTRIP_BUILD_INFO + PRINT_BUILD_INFO +#endif + cout << "Copyright (c) 2008-2022 Juan-Pablo Caceres, Chris Chafe." << endl; cout << "SoundWIRE group at CCRMA, Stanford University" << endl; #ifdef QT_OPENSOURCE cout << "This build of JackTrip is subject to LGPL license." << endl; @@ -688,7 +709,7 @@ cout << "" << endl; cout << "JackTrip: A System for High-Quality Audio Network Performance" << endl; cout << "over the Internet" << endl; - cout << "Copyright (c) 2008-2021 Juan-Pablo Caceres, Chris Chafe." << endl; + cout << "Copyright (c) 2008-2022 Juan-Pablo Caceres, Chris Chafe." << endl; cout << "SoundWIRE group at CCRMA, Stanford University" << endl; #ifdef QT_OPENSOURCE cout << "This build of JackTrip is subject to LGPL license." << endl; @@ -816,6 +837,8 @@ << endl; cout << " --audiodevice \"input-output device name\"" << endl; cout << " --audiodevice \"input device name\",\"output device name\"" << endl; + cout << " --audioinputdevice \"input device name\"" << endl; + cout << " --audiooutputdevice \"output device name\"" << endl; cout << " Set audio device to use; if not set, " "the default device will be used" << endl;