From c974a18498cf99dc884429566ccc44d342035f28 Mon Sep 17 00:00:00 2001 From: =?utf8?q?IOhannes=20m=20zm=C3=B6lnig=20=28Debian/GNU=29?= Date: Wed, 17 May 2023 22:13:45 +0200 Subject: [PATCH] New upstream version 1.9.0+ds --- CMakeLists.txt | 6 + README.md | 2 + build | 31 +- docs/changelog.yml | 24 + faust-src/monitordsp.dsp | 15 + jacktrip.pro | 6 + linux/README.md | 18 + linux/qt/1d21c39.diff | 38 + macos/qt/configure.json.patch | 20 + meson.build | 6 + meson_options.txt | 1 + releases/edge/mac-manifests.json | 656 ++++---- releases/edge/win-manifests.json | 658 ++++---- releases/stable/linux-manifests.json | 156 +- releases/stable/mac-manifests.json | 238 +-- releases/stable/win-manifests.json | 238 +-- scripts/update_manifests.sh | 20 + src/AudioInterface.cpp | 149 +- src/AudioInterface.h | 15 +- src/Compressor.cpp | 78 +- src/Compressor.h | 71 +- src/JackTrip.cpp | 65 +- src/JackTrip.h | 17 +- src/Limiter.cpp | 77 +- src/Limiter.h | 71 +- src/Meter.cpp | 52 +- src/Meter.h | 39 +- src/Monitor.cpp | 153 ++ src/Monitor.h | 84 + src/Regulator.cpp | 71 +- src/Regulator.h | 45 +- src/Reverb.cpp | 104 +- src/Reverb.h | 110 +- src/RtAudioInterface.cpp | 11 +- src/Settings.cpp | 4 +- src/StereoToMono.cpp | 20 +- src/StereoToMono.h | 16 +- src/Tone.cpp | 39 +- src/Tone.h | 26 +- src/Volume.cpp | 51 +- src/Volume.h | 28 +- src/gui/AudioSettings.qml | 24 +- src/gui/Connected.qml | 937 +++++++++++- src/gui/Login.qml | 31 +- src/gui/Setup.qml | 1 - src/gui/qjacktrip.qrc | 1 + src/gui/qjacktrip.ui | 7 +- src/gui/virtualstudio.cpp | 206 ++- src/gui/virtualstudio.h | 30 +- src/gui/vsAudioInterface.cpp | 5 + src/gui/vsAudioInterface.h | 1 + src/gui/vsDevice.cpp | 72 +- src/gui/vsDevice.h | 7 + src/gui/vsftux.qml | 128 ++ src/jacktrip_globals.h | 5 +- src/monitordsp.h | 2116 ++++++++++++++++++++++++++ 56 files changed, 5685 insertions(+), 1415 deletions(-) create mode 100644 faust-src/monitordsp.dsp create mode 100644 linux/README.md create mode 100644 linux/qt/1d21c39.diff create mode 100644 macos/qt/configure.json.patch create mode 100755 scripts/update_manifests.sh create mode 100644 src/Monitor.cpp create mode 100644 src/Monitor.h create mode 100644 src/gui/vsftux.qml create mode 100644 src/monitordsp.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d96731b..cdafd5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ set(nogui FALSE) set(rtaudio TRUE) set(weakjack TRUE) set(novs FALSE) +set(vsftux FALSE) set(noupdater FALSE) set(psi FALSE) set(QtVersion "5") @@ -37,6 +38,10 @@ else () set(QRC_FILE "src/gui/qjacktrip.qrc") endif () +if (vsftux) + add_compile_definitions(VS_FTUX) +endif () + if (noupdater) add_compile_definitions(NO_UPDATER) endif () @@ -191,6 +196,7 @@ if (NOT nogui) src/gui/vsWebSocket.cpp src/gui/vsPermissions.cpp src/gui/qjacktrip.qrc + src/Monitor.cpp src/Volume.cpp src/Tone.cpp # Need to include this for AUTOMOC to do its thing diff --git a/README.md b/README.md index 46a08f2..f508d91 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,5 @@ It supports any number of channels (as many as the computer/network can handle) It runs on several platforms, such as Linux, macOS, Windows or FreeBSD. You can use it between any combination of machines e.g., one end using Linux can connect to another using macOS. Further information and instructions are available on https://jacktrip.github.io/jacktrip/. + +Please report any security concerns to vulnerabilities@jacktrip.org diff --git a/build b/build index e1261fb..0e9c128 100755 --- a/build +++ b/build @@ -4,14 +4,15 @@ # Parse command line options clean=1 install=0 +jobs=1 CONFIG="DEFINES+=JACKTRIP_BUILD_INFO=\\\"$(git describe --tags)-$(git rev-parse --short HEAD)\\\"" -UNKNOWN_OPTIONS= +UNKNOWN_OPTIONS=() BUILD_RTAUDIO=0 RTAUDIO=0 NO_SYSTEM_RTAUDIO=0 PRO_FILE="../jacktrip.pro" HELP_STR="usage:\n -./build [noclean nojack rtaudio nogui novs static install [-config static]]\n\n +./build [noclean nojack rtaudio nogui novs vsftux static install [-config static]]\n\n options:\n noclean - do not run \"make clean\" first\n nojack - build without jack\n @@ -19,13 +20,14 @@ rtaudio - build with RtAudio\n no-system-rtaudio - use bundled RtAudio library even if it's available in the system\n nogui - build without the gui\n novs - build without Virtual Studio support\n +vsftux - build with Virtual Studio first launch experience\n noupdater - build without auto-update support\n static - build with static libraries\n weakjack - build with weak linking of jack libraries\n install - install jacktrip in system location (uses sudo)\n " while [[ "$#" -gt 0 ]]; do - if [[ -z "$UNKNOWN_OPTIONS" ]]; then + if [[ ${#UNKNOWN_OPTIONS[@]} -eq 0 ]]; then case $1 in noclean) clean=0 ;; nojack) @@ -46,6 +48,10 @@ while [[ "$#" -gt 0 ]]; do echo "Building without Virtual Studio support" CONFIG="-config novs $CONFIG" ;; + vsftux) + echo "Building with Virtual Studio first launch experience" + CONFIG="-config vsftux $CONFIG" + ;; noupdater) echo "Building without auto-update support" CONFIG="-config noupdater $CONFIG" @@ -62,15 +68,22 @@ while [[ "$#" -gt 0 ]]; do echo "Will install JackTrip in system location" install=1 ;; + -j*) + jobs=$(echo $1 |sed 's,-j0*\([0-9]*\),\1,') + if [[ $jobs -le 1 ]]; then + jobs=1 + fi + echo "Will build using $jobs make jobs" + ;; -h|--help) echo -e $HELP_STR; exit ;; - *) UNKNOWN_OPTIONS="$UNKNOWN_OPTIONS $1" ;; + *) UNKNOWN_OPTIONS+=("$1") ;; esac shift else case $1 in - *) UNKNOWN_OPTIONS="$UNKNOWN_OPTIONS $1" ;; + *) UNKNOWN_OPTIONS+=("$1") ;; esac shift fi @@ -164,15 +177,13 @@ cd builddir set -e # Build -QCMDARGS=(-spec $QSPEC $CONFIG "$UNKNOWN_OPTIONS" $PRO_FILE) echo "qmake command:" -echo $QCMD ${QCMDARGS[@]} +echo $QCMD -spec $QSPEC $CONFIG "${UNKNOWN_OPTIONS[@]}" $PRO_FILE +$QCMD -spec $QSPEC $CONFIG "${UNKNOWN_OPTIONS[@]}" $PRO_FILE if [[ $clean == 1 ]]; then - $QCMD ${QCMDARGS[@]} $MCMD clean fi -$QCMD ${QCMDARGS[@]} -$MCMD release +$MCMD -j$jobs release if [[ "$install" == 1 ]]; then echo "*** Installing JackTrip ***" echo "We need elevated privileges to install JackTrip in the system location" diff --git a/docs/changelog.yml b/docs/changelog.yml index 8db1bd5..fdf94fa 100644 --- a/docs/changelog.yml +++ b/docs/changelog.yml @@ -1,3 +1,27 @@ +- Version: "1.9.0" + Date: 2023-05-05 + Description: + - (added) buffer strategy 4 to run PLC in audio callback + - (added) VS Mode - change audio devices while connected + - (added) universal binary for macOS + - (added) tooltips, sliders, and positioning of connected interface + - (added) emails for vulnerability reporting + - (added) local monitoring + - (added) VS mode - Error message when single studio limit reached + - (updated) regulator thread uses real-time priority + - (updated) VS mode - use buffer strategy 4 + - (updated) VS mode - Default to PLC + - (updated) VS Mode - PLC auto queue has 5ms headroom + - (updated) Enforcing using the same ASIO device on Windows + - (updated) VS Mode - JTL builds hide the yes/no screen on first launch + - (updated) GHA - wait for static Qt builds rather than failing + - (updated) VS Mode - "all devices" is now "high latency" + - (updated) VS Mode - Warning text for non-ASIO Windows devices + - (updated) Faust-generate code moved out of headers + - (fixed) PLC bugs + - (fixed) VS Mode - changing devices while connected refreshes device lists + - (fixed) play test tone on Linux + - (fixed) static openssl on Linux - Version: "1.8.1" Date: 2023-03-29 Description: diff --git a/faust-src/monitordsp.dsp b/faust-src/monitordsp.dsp new file mode 100644 index 0000000..7984412 --- /dev/null +++ b/faust-src/monitordsp.dsp @@ -0,0 +1,15 @@ +declare name "monitor"; +declare version "1.0"; +declare author "Dominick Hing, adapted from 'Volume Control' by Matt Horton"; +declare license "MIT Style STK-4.2"; +declare description "Volume Control Faust Plugin for JackTrip, based on Faust examples"; + + +import("stdfaust.lib"); +mute = checkbox("[1] Mute"); +gain(v) = v : ba.db2linear : si.smoo : _; +gainVMute(v) = _ * gain(v), 0 : select2(mute) : _; +zeroCutoff(v) = _ , 0 : select2(v == -40) : _; +volume = hslider("[0] Volume", 0, -40, 0, 0.1); + +process = _,_ <: vgroup("Monitor", _ : gainVMute(volume) : zeroCutoff(volume)), _ : +; diff --git a/jacktrip.pro b/jacktrip.pro index 9df06dd..fa63a53 100644 --- a/jacktrip.pro +++ b/jacktrip.pro @@ -35,6 +35,9 @@ nogui { QT += quickcontrols2 QT += svg QT += websockets + vsftux { + DEFINES += VS_FTUX + } } noupdater|linux-g++|linux-g++-64 { DEFINES += NO_UPDATER @@ -63,6 +66,7 @@ nojack { # INCLUDEPATH+=/usr/include/stk # LIBS += -L/usr/local/lib -ljack -lstk -lm LIBS += -L/usr/local/lib -lm + QMAKE_CXXFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden weakjack { message(Building with weak linking of JACK) INCLUDEPATH += externals/weakjack @@ -207,6 +211,7 @@ HEADERS += src/DataProtocol.h \ src/Regulator.h \ src/Reverb.h \ src/Meter.h \ + src/Monitor.h \ src/Volume.h \ src/Tone.h \ src/StereoToMono.h \ @@ -280,6 +285,7 @@ SOURCES += src/DataProtocol.cpp \ src/Regulator.cpp \ src/Reverb.cpp \ src/Meter.cpp \ + src/Monitor.cpp \ src/StereoToMono.cpp \ src/Volume.cpp \ src/Tone.cpp \ diff --git a/linux/README.md b/linux/README.md new file mode 100644 index 0000000..9cc9013 --- /dev/null +++ b/linux/README.md @@ -0,0 +1,18 @@ +# JackTrip is a multi-machine audio system used for network music performance over the Internet. + +See LICENSE.md for license information. + +To install JackTrip as a Linux desktop application: + +``` +sudo cp jacktrip /usr/local/bin +mkdir -p $HOME/.local/share/applications $HOME/.local/share/icons/hicolor/scalable/apps $HOME/.local/share/icons/hicolor/48x48/apps +cp org.jacktrip.JackTrip.svg $HOME/.local/share/icons/hicolor/scalable/apps/ +cp org.jacktrip.JackTrip.png $HOME/.local/share/icons/hicolor/48x48/apps/ +desktop-file-install --dir=$HOME/.local/share/applications org.jacktrip.JackTrip.desktop +update-desktop-database $HOME/.local/share/applications +``` + +Further information and instructions are available on https://jacktrip.github.io/jacktrip/. + +Please report any security concerns to vulnerabilities@jacktrip.org diff --git a/linux/qt/1d21c39.diff b/linux/qt/1d21c39.diff new file mode 100644 index 0000000..f2eca05 --- /dev/null +++ b/linux/qt/1d21c39.diff @@ -0,0 +1,38 @@ +diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h +index 43b5bf6..7160825 100644 +--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h ++++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h +@@ -237,7 +237,6 @@ + EVP_PKEY_CTX *q_EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e); + void q_EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx); + int q_EVP_PKEY_param_check(EVP_PKEY_CTX *ctx); +-int q_EVP_PKEY_base_id(EVP_PKEY *a); + int q_RSA_bits(RSA *a); + Q_AUTOTEST_EXPORT int q_OPENSSL_sk_num(OPENSSL_STACK *a); + Q_AUTOTEST_EXPORT void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *)); +@@ -383,6 +382,17 @@ + int q_EC_GROUP_get_degree(const EC_GROUP* g); + #endif // OPENSSL_NO_EC + ++// Here we have the ones that make difference between OpenSSL pre/post v3: ++#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 ++X509 *q_SSL_get1_peer_certificate(SSL *a); ++#define q_SSL_get_peer_certificate q_SSL_get1_peer_certificate ++int q_EVP_PKEY_get_base_id(const EVP_PKEY *pkey); ++#define q_EVP_PKEY_base_id q_EVP_PKEY_get_base_id ++#else ++X509 *q_SSL_get_peer_certificate(SSL *a); ++int q_EVP_PKEY_base_id(EVP_PKEY *a); ++#endif // OPENSSL_VERSION_MAJOR >= 3 ++ + DSA *q_DSA_new(); + void q_DSA_free(DSA *a); + X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c); +@@ -510,7 +520,6 @@ + int q_SSL_version(const SSL *a); + int q_SSL_get_error(SSL *a, int b); + STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a); +-X509 *q_SSL_get_peer_certificate(SSL *a); + long q_SSL_get_verify_result(const SSL *a); + SSL *q_SSL_new(SSL_CTX *a); + SSL_CTX *q_SSL_get_SSL_CTX(SSL *a); diff --git a/macos/qt/configure.json.patch b/macos/qt/configure.json.patch new file mode 100644 index 0000000..30bd49b --- /dev/null +++ b/macos/qt/configure.json.patch @@ -0,0 +1,20 @@ +--- configure.json 2023-04-22 13:05:35 ++++ configure.json.new 2023-04-22 13:05:35 +@@ -1258,7 +1258,6 @@ + }, + "neon": { + "label": "NEON", +- "condition": "(arch.arm || arch.arm64) && subarch.neon", + "output": [ + "privateConfig", + { "type": "define", "name": "QT_COMPILER_SUPPORTS_NEON", "value": 1 } +@@ -1614,8 +1613,7 @@ + }, + { + "type": "feature", +- "args": "neon", +- "condition": "arch.arm || arch.arm64" ++ "args": "neon" + }, + { + "type": "feature", diff --git a/meson.build b/meson.build index 4ebb6e6..a579e02 100644 --- a/meson.build +++ b/meson.build @@ -41,6 +41,7 @@ src = [ 'src/DataProtocol.cpp', 'src/Compressor.cpp', 'src/Limiter.cpp', 'src/Meter.cpp', + 'src/Monitor.cpp', 'src/Volume.cpp', 'src/Tone.cpp', 'src/StereoToMono.cpp', @@ -53,6 +54,7 @@ moc_h = ['src/DataProtocol.h', 'src/JackTrip.h', 'src/ProcessPlugin.h', 'src/Meter.h', + 'src/Monitor.h', 'src/StereoToMono.h', 'src/Volume.h', 'src/Tone.h', @@ -170,6 +172,10 @@ else qres = ['src/gui/qjacktrip.qrc'] endif + if get_option('vsftux') == true + defines += '-DVS_FTUX' + endif + if get_option('noupdater') == true or host_machine.system() == 'linux' defines += '-DNO_UPDATER' else diff --git a/meson_options.txt b/meson_options.txt index 87cd612..596fc99 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -4,6 +4,7 @@ option('jack', type : 'feature', value : 'auto', description: 'Build with JACK B option('weakjack', type : 'boolean', value : 'false', description: 'Weak link JACK library') option('nogui', type : 'boolean', value : 'false', description: 'Build without graphical user interface') option('novs', type : 'boolean', value : 'false', description: 'Build without Virtual Studio support') +option('vsftux', type : 'boolean', value : 'false', description: 'Build with Virtual Studio first launch experience') option('noupdater', type : 'boolean', value : 'false', description: 'Build without auto-update support') option('profile', type: 'combo', choices: ['default', 'development'], value: 'default', description: 'Choose build profile / Sets desktop id accordingly') option('qtversion', type : 'combo', choices: ['5', '6'], description: 'Choose to build with either Qt5 or Qt6') \ No newline at end of file diff --git a/releases/edge/mac-manifests.json b/releases/edge/mac-manifests.json index fb7033d..8172474 100644 --- a/releases/edge/mac-manifests.json +++ b/releases/edge/mac-manifests.json @@ -1,305 +1,355 @@ { - "app_name": "JackTrip", - "releases": [ - { - "version": "1.8.0", - "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0", - "download": { - "date": "2023-03-18T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-macOS-x64-signed-installer.pkg", - "downloadSize": "11771203", - "sha256": "dfee21d5e91a35baaf3b58891188f72f2cb894b2bf796ac70350a2ef9d3fb68c" - } - }, - { - "version": "1.8.0-beta1", - "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0-beta1", - "download": { - "date": "2023-03-01T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-beta1-macOS-x64-installer.pkg", - "downloadSize": 11761503, - "sha256": "3159d5625d42db7925867949880eceef581f4991959f85bf4203c2ae15fc623f" - } - }, - { - "version": "1.7.1", - "changelog": "Video button, bug fixes, Linux build fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1", - "download": { - "date": "2023-02-10T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.1-macOS-x64-installer.pkg", - "downloadSize": 11679783, - "sha256": "027d1d3aeb4aaca79b21824371a0bfb915b8c43afe924a8ec2be92df719a431f" - } - }, - { - "version": "1.7.1-beta1", - "changelog": "Video button, bug fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1-beta1", - "download": { - "date": "2023-02-03T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.7.1-beta1/JackTrip-v1.7.1-beta1-macOS-x64-installer.pkg", - "downloadSize": 11678822, - "sha256": "1dcc8b54dd67741137582638ce04359404848d5f258c9fae7ab1946730e9381d" - } - }, - { - "version": "1.7.0", - "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0", - "download": { - "date": "2023-01-24T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.0-macOS-x64-installer.pkg", - "downloadSize": 11530594, - "sha256": "0e5731c2ad71aa4bd28ccf9311e312f2386c42b02abbca142777dfb06ea1b427" - } - }, - { - "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": "2023-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": "2023-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": "2023-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": { - "date": "2022-12-02T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-macOS-x64-installer.pkg", - "downloadSize": 11517151, - "sha256": "38c25788895ec404bdb4b6148114cad8af31b65e5189ca08819d1e954e2ef4e7" - } - }, - { - "version": "1.6.7-rc.2", - "changelog": "Virtual Studio first time UI, Non-ASIO devices on Windows, Windows can't connect fix: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7-rc.2", - "download": { - "date": "2022-11-29T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.7-rc.2/JackTrip-v1.6.7-rc.2-macOS-x64-installer.pkg", - "downloadSize": 11516784, - "sha256": "4fe2e4dbd67986926b6f738cd4d8a22b801fd3cd50aeebee13d2e1de0310b160" - } - }, - { - "version": "1.6.7-rc.1", - "changelog": "New networking code on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7-rc.1", - "download": { - "date": "2022-11-07T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-rc.1-macOS-x64-installer.pkg", - "downloadSize": 11481444, - "sha256": "aa07023d130129684dc8d1e395d04eabea9709db32459e203c4fb7cf9759976e" - } - }, - { - "version": "1.6.6", - "changelog": "Adding volume controls in Virtual Studio, preliminary QT6 support, classic mode GUI updates, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.6", - "download": { - "date": "2022-11-02T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.6-macOS-x64-installer.pkg", - "downloadSize": 11481324, - "sha256": "e99149ea9bbfb94a2000cfd3848013e44bb8d23e783198005681c2038bcbd313" - } - }, - { - "version": "1.6.5-rc.1", - "changelog": "Adding volume controls, mute, early Qt6 support, and bug fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.5-rc1", - "download": { - "date": "2022-10-21T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.5-rc1/JackTrip-v1.6.5-rc1-macOS-x64-installer.pkg", - "downloadSize": 11481049, - "sha256": "cea18dd189d84eb9ca3be115819c597900e7bba7771185b61308518aa0e3d716" - } - }, - { - "version": "1.6.4", - "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4", - "download": { - "date": "2022-09-16T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.4-macOS-x64-installer.pkg", - "downloadSize": 11554866, - "sha256": "e5898f3ff57fc2d734590decb4f82a28ad9208a33cad97152f119716cdcea1a9" - } - }, - { - "version": "1.6.4-rc.2", - "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4-rc2", - "download": { - "date": "2022-09-08T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.4-rc2/JackTrip-v1.6.4-rc2-macOS-x64-installer.pkg", - "downloadSize": 11554117, - "sha256": "c849c22583883027f94141b3878cebc85295c0a617640449d4f66862982f75c0" - } - }, - { - "version": "1.6.4-rc.1", - "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4-rc1", - "download": { - "date": "2022-09-01T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.4-rc1/JackTrip-v1.6.4-rc1-macOS-x64-installer.pkg", - "downloadSize": 11548759, - "sha256": "6e8af76766b164e40e7a44842a14e640cb3c0fac7b9b7067f9c75048d3354496" - } - }, - { - "version": "1.6.3", - "changelog": "Fixes around Linux desktop file and hub server mode: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.3", - "download": { - "date": "2022-08-23T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.3-macOS-x64-installer.pkg", - "downloadSize": 11533023, - "sha256": "0b597c62544e9813949f859cc7b7c13bef893f6896f96b34a19889faf4855116" - } - }, - { - "version": "1.6.2", - "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2", - "download": { - "date": "2022-08-17T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.2-macOS-x64-installer.pkg", - "downloadSize": 11536442, - "sha256": "450222f4db1922275e07286d0be6ea2df2873a8a39c3b23096bcfbce342b7b3c" - } - }, - { - "version": "1.6.2-rc.3", - "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc3", - "download": { - "date": "2022-08-15T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc3/JackTrip-v1.6.2-rc3-macOS-x64-installer.pkg", - "downloadSize": 11536485, - "sha256": "accf625c8c797c13bde01fb50fe5bbb87fe4eefd0ae8ef06b74034e1cde6f22b" - } - }, - { - "version": "1.6.2-rc.2", - "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc2", - "download": { - "date": "2022-08-09T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc2/JackTrip-v1.6.2-rc2-macOS-x64-installer.pkg", - "downloadSize": 11531462, - "sha256": "a8b5418992045a5d08bfce1e7a412a1ad8414f9d7ea770564f2bba0caa83297b" - } - }, - { - "version": "1.6.2-rc.1", - "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc1", - "download": { - "date": "2022-08-06T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc1/JackTrip-v1.6.2-rc1-macOS-x64-installer.pkg", - "downloadSize": 11534071, - "sha256": "9a2200d157c4bb308b0b5ba5854ee5af17ae74991f7aa94fa5a0da19282cc571" - } - }, - { - "version": "1.6.1", - "changelog": "Bugfixes around UDP timeout and 'Logging In' screen navigation. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.1", - "download": { - "date": "2022-06-21T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.1-macOS-x64-installer.pkg", - "downloadSize": 11476305, - "sha256": "eaf05c842d6b3ae799208a40b37da1cdb13e3700dcbbd97443c80cad81f4d2ac" - } - }, - { - "version": "1.6.0", - "changelog": "Full integration with JackTrip Virtual Studio. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.0", - "download": { - "date": "2022-06-01T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-macOS-x64-installer.pkg", - "downloadSize": 11474299, - "sha256": "27259600ecd879106ebbf97754d72d6236075a049eafa0de6271d33f753f13e4" - } - }, - { - "version": "1.6.0-rc.5", - "changelog": "Release candidate 5 for 1.6.0", - "download": { - "date": "2022-05-30T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.5/JackTrip-v1.6.0-rc.5-macOS-x64-installer.pkg", - "downloadSize": 11474262, - "sha256": "8289530a8e6ef1f772776c7078679e2dac146f366cfc4e8c09e0ad16865fe274" - } - }, - { - "version": "1.6.0-rc.4", - "changelog": "Release candidate 4 for 1.6.0", - "download": { - "date": "2022-05-29T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.4/JackTrip-v1.6.0-rc.4-macOS-x64-installer.pkg", - "downloadSize": 11460550, - "sha256": "38d817f3e8cc61b707392ce74cee8ab46da9c8eb2086ea2b3f0c79496caf70a8" - } - }, - { - "version": "1.6.0-rc.3", - "changelog": "Release candidate 3 for 1.6.0", - "download": { - "date": "2022-05-27T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-rc.3-macOS-x64-installer.pkg", - "downloadSize": 11460230, - "sha256": "c9614964974d61c062d905f01c7d30ab04a697562ecfba6264392aebe7161051" - } - }, - { - "version": "1.6.0-rc.2", - "changelog": "Release candidate 2 for 1.6.0", - "download": { - "date": "2022-05-26T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-rc.2-macOS-x64-signed-installer.pkg", - "downloadSize": 11460155, - "sha256": "ad508680115f73036da3a5328ddf0841b86620406406e0ffaa4b982e24a27771" - } - }, - { - "version": "1.6.0-rc.1", - "changelog": "Release candidate 1 for 1.6.0", - "download": { - "date": "2022-05-23T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.1/JackTrip-v1.6.0-rc.1-macOS-x64-installer.pkg", - "downloadSize": 11076221, - "sha256": "071cda0ce59361e474a04db00beec41e92d2d823dab71e3fab179faf89f6fd7e" - } - } - ] + "app_name": "JackTrip", + "releases": [ + { + "version": "1.9.0-beta3", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.9.0-beta3", + "download": { + "date": "2023-05-05T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.9.0-beta3-macOS-x64-signed-installer.pkg", + "downloadSize": "22811032", + "sha256": "0d6d0f34c4c99fb03810ea86360a8ac93874121db897b2ba23132acf9fdefc50" + } + }, + { + "version": "1.9.0-beta2", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.9.0-beta2", + "download": { + "date": "2023-04-25T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.9.0-beta2-macOS-x64-signed-installer.pkg", + "downloadSize": "22776100", + "sha256": "99ea878f0fbd5913516cfd73e2403c61d181b03c7e4f251e8075b01fbd78ecd7" + } + }, + { + "version": "1.9.0-beta1", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.9.0-beta1", + "download": { + "date": "2023-04-18T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.9.0-beta1-macOS-x64-signed-installer.pkg", + "downloadSize": "11768911", + "sha256": "a2f38e04cd03c3b8e549e7e82644df11dd1d59c03aa00806a02aa6ff7c76c1b0" + } + }, + { + "version": "1.8.1", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.1", + "download": { + "date": "2023-04-03T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.1-macOS-x64-signed-installer.pkg", + "downloadSize": "11754694", + "sha256": "d073ff5f2b90b5dd86ccd07c5d30d61d7be776c5e3c0083e6f665f78fea48298" + } + }, + { + "version": "1.8.0", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0", + "download": { + "date": "2023-03-18T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-macOS-x64-signed-installer.pkg", + "downloadSize": "11771203", + "sha256": "dfee21d5e91a35baaf3b58891188f72f2cb894b2bf796ac70350a2ef9d3fb68c" + } + }, + { + "version": "1.8.0-beta2", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0-beta2", + "download": { + "date": "2023-03-10T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-beta2-macOS-x64-signed-installer.pkg", + "downloadSize": "11769049", + "sha256": "9ffbf4c2c7b9419cd7e5000efe1441ca3eb0e44dfcdecd0347c561e7eecc4e36" + } + }, + { + "version": "1.8.0-beta1", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0-beta1", + "download": { + "date": "2023-03-01T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-beta1-macOS-x64-installer.pkg", + "downloadSize": 11761503, + "sha256": "3159d5625d42db7925867949880eceef581f4991959f85bf4203c2ae15fc623f" + } + }, + { + "version": "1.7.1", + "changelog": "Video button, bug fixes, Linux build fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1", + "download": { + "date": "2023-02-10T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.1-macOS-x64-installer.pkg", + "downloadSize": 11679783, + "sha256": "027d1d3aeb4aaca79b21824371a0bfb915b8c43afe924a8ec2be92df719a431f" + } + }, + { + "version": "1.7.1-beta1", + "changelog": "Video button, bug fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1-beta1", + "download": { + "date": "2023-02-03T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.7.1-beta1/JackTrip-v1.7.1-beta1-macOS-x64-installer.pkg", + "downloadSize": 11678822, + "sha256": "1dcc8b54dd67741137582638ce04359404848d5f258c9fae7ab1946730e9381d" + } + }, + { + "version": "1.7.0", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0", + "download": { + "date": "2023-01-24T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.0-macOS-x64-installer.pkg", + "downloadSize": 11530594, + "sha256": "0e5731c2ad71aa4bd28ccf9311e312f2386c42b02abbca142777dfb06ea1b427" + } + }, + { + "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": "2023-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": "2023-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": "2023-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": { + "date": "2022-12-02T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-macOS-x64-installer.pkg", + "downloadSize": 11517151, + "sha256": "38c25788895ec404bdb4b6148114cad8af31b65e5189ca08819d1e954e2ef4e7" + } + }, + { + "version": "1.6.7-rc.2", + "changelog": "Virtual Studio first time UI, Non-ASIO devices on Windows, Windows can't connect fix: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7-rc.2", + "download": { + "date": "2022-11-29T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.7-rc.2/JackTrip-v1.6.7-rc.2-macOS-x64-installer.pkg", + "downloadSize": 11516784, + "sha256": "4fe2e4dbd67986926b6f738cd4d8a22b801fd3cd50aeebee13d2e1de0310b160" + } + }, + { + "version": "1.6.7-rc.1", + "changelog": "New networking code on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7-rc.1", + "download": { + "date": "2022-11-07T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-rc.1-macOS-x64-installer.pkg", + "downloadSize": 11481444, + "sha256": "aa07023d130129684dc8d1e395d04eabea9709db32459e203c4fb7cf9759976e" + } + }, + { + "version": "1.6.6", + "changelog": "Adding volume controls in Virtual Studio, preliminary QT6 support, classic mode GUI updates, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.6", + "download": { + "date": "2022-11-02T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.6-macOS-x64-installer.pkg", + "downloadSize": 11481324, + "sha256": "e99149ea9bbfb94a2000cfd3848013e44bb8d23e783198005681c2038bcbd313" + } + }, + { + "version": "1.6.5-rc.1", + "changelog": "Adding volume controls, mute, early Qt6 support, and bug fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.5-rc1", + "download": { + "date": "2022-10-21T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.5-rc1/JackTrip-v1.6.5-rc1-macOS-x64-installer.pkg", + "downloadSize": 11481049, + "sha256": "cea18dd189d84eb9ca3be115819c597900e7bba7771185b61308518aa0e3d716" + } + }, + { + "version": "1.6.4", + "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4", + "download": { + "date": "2022-09-16T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.4-macOS-x64-installer.pkg", + "downloadSize": 11554866, + "sha256": "e5898f3ff57fc2d734590decb4f82a28ad9208a33cad97152f119716cdcea1a9" + } + }, + { + "version": "1.6.4-rc.2", + "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4-rc2", + "download": { + "date": "2022-09-08T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.4-rc2/JackTrip-v1.6.4-rc2-macOS-x64-installer.pkg", + "downloadSize": 11554117, + "sha256": "c849c22583883027f94141b3878cebc85295c0a617640449d4f66862982f75c0" + } + }, + { + "version": "1.6.4-rc.1", + "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4-rc1", + "download": { + "date": "2022-09-01T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.4-rc1/JackTrip-v1.6.4-rc1-macOS-x64-installer.pkg", + "downloadSize": 11548759, + "sha256": "6e8af76766b164e40e7a44842a14e640cb3c0fac7b9b7067f9c75048d3354496" + } + }, + { + "version": "1.6.3", + "changelog": "Fixes around Linux desktop file and hub server mode: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.3", + "download": { + "date": "2022-08-23T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.3-macOS-x64-installer.pkg", + "downloadSize": 11533023, + "sha256": "0b597c62544e9813949f859cc7b7c13bef893f6896f96b34a19889faf4855116" + } + }, + { + "version": "1.6.2", + "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2", + "download": { + "date": "2022-08-17T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.2-macOS-x64-installer.pkg", + "downloadSize": 11536442, + "sha256": "450222f4db1922275e07286d0be6ea2df2873a8a39c3b23096bcfbce342b7b3c" + } + }, + { + "version": "1.6.2-rc.3", + "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc3", + "download": { + "date": "2022-08-15T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc3/JackTrip-v1.6.2-rc3-macOS-x64-installer.pkg", + "downloadSize": 11536485, + "sha256": "accf625c8c797c13bde01fb50fe5bbb87fe4eefd0ae8ef06b74034e1cde6f22b" + } + }, + { + "version": "1.6.2-rc.2", + "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc2", + "download": { + "date": "2022-08-09T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc2/JackTrip-v1.6.2-rc2-macOS-x64-installer.pkg", + "downloadSize": 11531462, + "sha256": "a8b5418992045a5d08bfce1e7a412a1ad8414f9d7ea770564f2bba0caa83297b" + } + }, + { + "version": "1.6.2-rc.1", + "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc1", + "download": { + "date": "2022-08-06T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc1/JackTrip-v1.6.2-rc1-macOS-x64-installer.pkg", + "downloadSize": 11534071, + "sha256": "9a2200d157c4bb308b0b5ba5854ee5af17ae74991f7aa94fa5a0da19282cc571" + } + }, + { + "version": "1.6.1", + "changelog": "Bugfixes around UDP timeout and 'Logging In' screen navigation. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.1", + "download": { + "date": "2022-06-21T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.1-macOS-x64-installer.pkg", + "downloadSize": 11476305, + "sha256": "eaf05c842d6b3ae799208a40b37da1cdb13e3700dcbbd97443c80cad81f4d2ac" + } + }, + { + "version": "1.6.0", + "changelog": "Full integration with JackTrip Virtual Studio. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.0", + "download": { + "date": "2022-06-01T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-macOS-x64-installer.pkg", + "downloadSize": 11474299, + "sha256": "27259600ecd879106ebbf97754d72d6236075a049eafa0de6271d33f753f13e4" + } + }, + { + "version": "1.6.0-rc.5", + "changelog": "Release candidate 5 for 1.6.0", + "download": { + "date": "2022-05-30T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.5/JackTrip-v1.6.0-rc.5-macOS-x64-installer.pkg", + "downloadSize": 11474262, + "sha256": "8289530a8e6ef1f772776c7078679e2dac146f366cfc4e8c09e0ad16865fe274" + } + }, + { + "version": "1.6.0-rc.4", + "changelog": "Release candidate 4 for 1.6.0", + "download": { + "date": "2022-05-29T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.4/JackTrip-v1.6.0-rc.4-macOS-x64-installer.pkg", + "downloadSize": 11460550, + "sha256": "38d817f3e8cc61b707392ce74cee8ab46da9c8eb2086ea2b3f0c79496caf70a8" + } + }, + { + "version": "1.6.0-rc.3", + "changelog": "Release candidate 3 for 1.6.0", + "download": { + "date": "2022-05-27T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-rc.3-macOS-x64-installer.pkg", + "downloadSize": 11460230, + "sha256": "c9614964974d61c062d905f01c7d30ab04a697562ecfba6264392aebe7161051" + } + }, + { + "version": "1.6.0-rc.2", + "changelog": "Release candidate 2 for 1.6.0", + "download": { + "date": "2022-05-26T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-rc.2-macOS-x64-signed-installer.pkg", + "downloadSize": 11460155, + "sha256": "ad508680115f73036da3a5328ddf0841b86620406406e0ffaa4b982e24a27771" + } + }, + { + "version": "1.6.0-rc.1", + "changelog": "Release candidate 1 for 1.6.0", + "download": { + "date": "2022-05-23T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.1/JackTrip-v1.6.0-rc.1-macOS-x64-installer.pkg", + "downloadSize": 11076221, + "sha256": "071cda0ce59361e474a04db00beec41e92d2d823dab71e3fab179faf89f6fd7e" + } + } + ] } diff --git a/releases/edge/win-manifests.json b/releases/edge/win-manifests.json index 0fa745b..031295f 100644 --- a/releases/edge/win-manifests.json +++ b/releases/edge/win-manifests.json @@ -1,305 +1,355 @@ { - "app_name": "JackTrip", - "releases": [ - { - "version": "1.8.0", - "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0", - "download": { - "date": "2023-03-18T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-Windows-x64-signed-installer.msi", - "downloadSize": "45699072", - "sha256": "4b6705a2e8af7f9a516fefaf119d5e6cadf93e8f40964cd52b635ced5745b267" - } - }, - { - "version": "1.8.0-beta1", - "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0-beta1", - "download": { - "date": "2023-03-01T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-beta1-Windows-x64-installer.msi", - "downloadSize": 45568000, - "sha256": "61641b72fe27389ab755580d5b94fa2de993ad42967af0c5c765743ca8b30602" - } - }, - { - "version": "1.7.1", - "changelog": "Video button, bug fixes, Linux build fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1", - "download": { - "date": "2023-02-10T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.1-Windows-x64-installer.msi", - "downloadSize": 45330432, - "sha256": "fb5d756afcd471ca8ae45b05b411235026f23f2178893d85ac12f39c3f66a01a" - } - }, - { - "version": "1.7.1-beta1", - "changelog": "Video button, bug fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1-beta1", - "download": { - "date": "2023-02-03T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.7.1-beta1/JackTrip-v1.7.1-beta1-Windows-x64-installer.msi", - "downloadSize": 45326336, - "sha256": "eeb16bc11957413fb74da852b9e0fa3cb8c84cb2945d5c992a40f3e9b526c294" - } - }, - { - "version": "1.7.0", - "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0", - "download": { - "date": "2023-01-24T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.0-Windows-x64-installer.msi", - "downloadSize": 44572672, - "sha256": "a1890fe10de484f423a17118031d898abacc9b9eb2ccd35bdb4351e9411ff866" - } - }, - { - "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": "2023-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": "2023-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": "2023-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": { - "date": "2022-12-02T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-Windows-x64-installer.msi", - "downloadSize": 44535808, - "sha256": "75464575311da5521e011da8d2f2b5aff1379edb4dee9dcb90e1750dcc97f2f9" - } - }, - { - "version": "1.6.7-rc.2", - "changelog": "Virtual Studio first time UI, Non-ASIO devices on Windows, Windows can't connect fix: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7-rc.2", - "download": { - "date": "2022-11-29T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.7-rc.2/JackTrip-v1.6.7-rc.2-Windows-x64-installer.msi", - "downloadSize": 44531712, - "sha256": "656716e1e665187474bda00c89348a405018a69830557b4722e382187ee367e1" - } - }, - { - "version": "1.6.7-rc.1", - "changelog": "New networking code on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7-rc.1", - "download": { - "date": "2022-11-07T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-rc.1-Windows-x64-installer.msi", - "downloadSize": 44412928, - "sha256": "a0485d45c615c435fc4d6b0840d0bf8a74f990001eeacfe26a74d61afbd72dfd" - } - }, - { - "version": "1.6.6", - "changelog": "Adding volume controls in Virtual Studio, preliminary QT6 support, classic mode GUI updates, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.6", - "download": { - "date": "2022-11-02T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.6-Windows-x64-installer.msi", - "downloadSize": 44408832, - "sha256": "828a1f43254db187a33601cc809551617db83e60a51dad3e992c30270967de0c" - } - }, - { - "version": "1.6.5-rc.1", - "changelog": "Adding volume controls, mute, early Qt6 support, and bug fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.5-rc1", - "download": { - "date": "2022-10-21T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.5-rc1/JackTrip-v1.6.5-rc1-Windows-x64-installer.msi", - "downloadSize": 44408832, - "sha256": "99404fa7bf1a07df76a1b1048c7a0e64a0d5f552e2ca5dfb12e7cbaac0c6fb24" - } - }, - { - "version": "1.6.4", - "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4", - "download": { - "date": "2022-09-16T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.4-Windows-x64-installer.msi", - "downloadSize": 43663360, - "sha256": "58dcbba584e1cc82373b8aa159800ad360eec7933ce9462e413ca347f09f3c26" - } - }, - { - "version": "1.6.4-rc.2", - "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4-rc2", - "download": { - "date": "2022-09-08T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.4-rc2/JackTrip-v1.6.4-rc2-Windows-x64-installer.msi", - "downloadSize": 43663360, - "sha256": "fc782f0f9547ff096eb99891745e5d80056090b05a179f0209bd7785b1b808a6" - } - }, - { - "version": "1.6.4-rc.1", - "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4-rc1", - "download": { - "date": "2022-09-01T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.4-rc1/JackTrip-v1.6.4-rc1-Windows-x64-installer.msi", - "downloadSize": 43651072, - "sha256": "fbe0f883429119b4c878e0dff4bb2c79f2ac0d995b9c947f7eb8cc70fa59bc67" - } - }, - { - "version": "1.6.3", - "changelog": "Fixes around Linux desktop file and hub server mode: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.3", - "download": { - "date": "2022-08-23T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.3-Windows-x64-installer.msi", - "downloadSize": 43606016, - "sha256": "83a4def2a8c8fde24d147d39e70f60ee144e9425828f34eda2340c23ce72b1da" - } - }, - { - "version": "1.6.2", - "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2", - "download": { - "date": "2022-08-17T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.2-Windows-x64-installer.msi", - "downloadSize": 43606016, - "sha256": "2bb06fe3624df447d56da0d72fef1f4328346fd92c6dffccf66ba37253ec5e62" - } - }, - { - "version": "1.6.2-rc.3", - "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc3", - "download": { - "date": "2022-08-15T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc3/JackTrip-v1.6.2-rc3-Windows-x64-installer.msi", - "downloadSize": 43606016, - "sha256": "62771ca5efbf2e91fa4cd347214e6e517b76c032a8895ca80bcbc2fa765ab81a" - } - }, - { - "version": "1.6.2-rc.2", - "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc2", - "download": { - "date": "2022-08-09T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc2/JackTrip-v1.6.2-rc2-Windows-x64-installer.msi", - "downloadSize": 43606016, - "sha256": "ff88acd1804362589478366a620d12be302071dba9781ea38ed6a8343c94c16d" - } - }, - { - "version": "1.6.2-rc.1", - "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc1", - "download": { - "date": "2022-08-06T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc1/JackTrip-v1.6.2-rc1-Windows-x64-installer.msi", - "downloadSize": 43601920, - "sha256": "f1412de0b13ff7599353a10aec8f2b69e9831a37103187f8fa68334c8f8f09de" - } - }, - { - "version": "1.6.1", - "changelog": "Bugfixes around UDP timeout and 'Logging In' screen navigation. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.1", - "download": { - "date": "2022-06-21T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.1-Windows-x64-installer.msi", - "downloadSize": 43368448, - "sha256": "8eac390617488d849c0356e3305c96a59bbe46a8174d02b0321bb1dc86774b87" - } - }, - { - "version": "1.6.0", - "changelog": "Full integration with JackTrip Virtual Studio. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.0", - "download": { - "date": "2022-06-01T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-Windows-x64-installer.msi", - "downloadSize": 43364352, - "sha256": "9562ab654202bfc432e05caa3bd2bf1d0b52c50581b0a567f0546983fe46c078" - } - }, - { - "version": "1.6.0-rc.5", - "changelog": "Release candidate 5 for 1.6.0", - "download": { - "date": "2022-05-30T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.5/JackTrip-v1.6.0-rc.5-Windows-x64-installer.msi", - "downloadSize": 43364352, - "sha256": "d84e6e5d21cf31f5dd48e9dcc0c1a44fe7a37d977f94b6ff63d5e381745e5a44" - } - }, - { - "version": "1.6.0-rc.4", - "changelog": "Release candidate 4 for 1.6.0", - "download": { - "date": "2022-05-29T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.4/JackTrip-v1.6.0-rc.4-Windows-x64-installer.msi", - "downloadSize": 43126784, - "sha256": "cdb0ef906cf0d6047289838bf013b31a626cdd74dd4f75d6c1c4c3adbc9cd41d" - } - }, - { - "version": "1.6.0-rc.3", - "changelog": "Release candidate 3 for 1.6.0", - "download": { - "date": "2022-05-27T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-rc.3-Windows-x64-installer.msi", - "downloadSize": 43118592, - "sha256": "dceaf670a67cf1541007db82c5ce937b25370a7140e48192b94470f575fc4988" - } - }, - { - "version": "1.6.0-rc.2", - "changelog": "Release candidate 2 for 1.6.0", - "download": { - "date": "2022-05-26T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-rc.2-Windows-x64-signed-installer.msi", - "downloadSize": 43114496, - "sha256": "b1a7adc8dc0fb47f59515790e8531dd10838d799bacb4b5653192ed621bca208" - } - }, - { - "version": "1.6.0-rc.1", - "changelog": "Release candidate 1 for 1.6.0", - "download": { - "date": "2022-05-23T00:00:00Z", - "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.1/JackTrip-v1.6.0-rc.1-Windows-x64-installer.msi", - "downloadSize": 43081728, - "sha256": "240f8b495ec5057228be922da80829a3718b474bafc2ba2d77750643abd1005c" - } - } - ] -} \ No newline at end of file + "app_name": "JackTrip", + "releases": [ + { + "version": "1.9.0-beta3", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.9.0-beta3", + "download": { + "date": "2023-05-05T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.9.0-beta3-Windows-x64-signed-installer.msi", + "downloadSize": "46383104", + "sha256": "7db404d0fe9d062409d9f1ebe3382ab99aa9638f82c9951cbc442dab91e42f21" + } + }, + { + "version": "1.9.0-beta2", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.9.0-beta2", + "download": { + "date": "2023-04-25T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.9.0-beta2-Windows-x64-signed-installer.msi", + "downloadSize": "46546944", + "sha256": "ee75aa4bb3e617f055b9a5b64c2bf98006d774845a4f1cdc247add7dfc1ba9fc" + } + }, + { + "version": "1.9.0-beta1", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.9.0-beta1", + "download": { + "date": "2023-04-18T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.9.0-beta1-Windows-x64-signed-installer.msi", + "downloadSize": "46530560", + "sha256": "80fac822af3e826743c501715681430fdecda9799dd6e8cf478e8b87f0d0d9c5" + } + }, + { + "version": "1.8.1", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.1", + "download": { + "date": "2023-04-03T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.1-Windows-x64-signed-installer.msi", + "downloadSize": "46456832", + "sha256": "55da405ea55a3567a672eef0f5f3699c264f456e13dd04d2a55d90bd3d1a2f55" + } + }, + { + "version": "1.8.0", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0", + "download": { + "date": "2023-03-18T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-Windows-x64-signed-installer.msi", + "downloadSize": "45699072", + "sha256": "4b6705a2e8af7f9a516fefaf119d5e6cadf93e8f40964cd52b635ced5745b267" + } + }, + { + "version": "1.8.0-beta2", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0-beta2", + "download": { + "date": "2023-03-10T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-beta2-Windows-x64-signed-installer.msi", + "downloadSize": "45694976", + "sha256": "8d080a009b5583fc4360dfefb2efaa74966c2d76d9a982bba36c42c8a0e536fd" + } + }, + { + "version": "1.8.0-beta1", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0-beta1", + "download": { + "date": "2023-03-01T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-beta1-Windows-x64-installer.msi", + "downloadSize": 45568000, + "sha256": "61641b72fe27389ab755580d5b94fa2de993ad42967af0c5c765743ca8b30602" + } + }, + { + "version": "1.7.1", + "changelog": "Video button, bug fixes, Linux build fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1", + "download": { + "date": "2023-02-10T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.1-Windows-x64-installer.msi", + "downloadSize": 45330432, + "sha256": "fb5d756afcd471ca8ae45b05b411235026f23f2178893d85ac12f39c3f66a01a" + } + }, + { + "version": "1.7.1-beta1", + "changelog": "Video button, bug fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1-beta1", + "download": { + "date": "2023-02-03T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.7.1-beta1/JackTrip-v1.7.1-beta1-Windows-x64-installer.msi", + "downloadSize": 45326336, + "sha256": "eeb16bc11957413fb74da852b9e0fa3cb8c84cb2945d5c992a40f3e9b526c294" + } + }, + { + "version": "1.7.0", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0", + "download": { + "date": "2023-01-24T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.0-Windows-x64-installer.msi", + "downloadSize": 44572672, + "sha256": "a1890fe10de484f423a17118031d898abacc9b9eb2ccd35bdb4351e9411ff866" + } + }, + { + "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": "2023-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": "2023-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": "2023-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": { + "date": "2022-12-02T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-Windows-x64-installer.msi", + "downloadSize": 44535808, + "sha256": "75464575311da5521e011da8d2f2b5aff1379edb4dee9dcb90e1750dcc97f2f9" + } + }, + { + "version": "1.6.7-rc.2", + "changelog": "Virtual Studio first time UI, Non-ASIO devices on Windows, Windows can't connect fix: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7-rc.2", + "download": { + "date": "2022-11-29T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.7-rc.2/JackTrip-v1.6.7-rc.2-Windows-x64-installer.msi", + "downloadSize": 44531712, + "sha256": "656716e1e665187474bda00c89348a405018a69830557b4722e382187ee367e1" + } + }, + { + "version": "1.6.7-rc.1", + "changelog": "New networking code on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.7-rc.1", + "download": { + "date": "2022-11-07T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-rc.1-Windows-x64-installer.msi", + "downloadSize": 44412928, + "sha256": "a0485d45c615c435fc4d6b0840d0bf8a74f990001eeacfe26a74d61afbd72dfd" + } + }, + { + "version": "1.6.6", + "changelog": "Adding volume controls in Virtual Studio, preliminary QT6 support, classic mode GUI updates, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.6", + "download": { + "date": "2022-11-02T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.6-Windows-x64-installer.msi", + "downloadSize": 44408832, + "sha256": "828a1f43254db187a33601cc809551617db83e60a51dad3e992c30270967de0c" + } + }, + { + "version": "1.6.5-rc.1", + "changelog": "Adding volume controls, mute, early Qt6 support, and bug fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.5-rc1", + "download": { + "date": "2022-10-21T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.5-rc1/JackTrip-v1.6.5-rc1-Windows-x64-installer.msi", + "downloadSize": 44408832, + "sha256": "99404fa7bf1a07df76a1b1048c7a0e64a0d5f552e2ca5dfb12e7cbaac0c6fb24" + } + }, + { + "version": "1.6.4", + "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4", + "download": { + "date": "2022-09-16T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.4-Windows-x64-installer.msi", + "downloadSize": 43663360, + "sha256": "58dcbba584e1cc82373b8aa159800ad360eec7933ce9462e413ca347f09f3c26" + } + }, + { + "version": "1.6.4-rc.2", + "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4-rc2", + "download": { + "date": "2022-09-08T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.4-rc2/JackTrip-v1.6.4-rc2-Windows-x64-installer.msi", + "downloadSize": 43663360, + "sha256": "fc782f0f9547ff096eb99891745e5d80056090b05a179f0209bd7785b1b808a6" + } + }, + { + "version": "1.6.4-rc.1", + "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4-rc1", + "download": { + "date": "2022-09-01T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.4-rc1/JackTrip-v1.6.4-rc1-Windows-x64-installer.msi", + "downloadSize": 43651072, + "sha256": "fbe0f883429119b4c878e0dff4bb2c79f2ac0d995b9c947f7eb8cc70fa59bc67" + } + }, + { + "version": "1.6.3", + "changelog": "Fixes around Linux desktop file and hub server mode: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.3", + "download": { + "date": "2022-08-23T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.3-Windows-x64-installer.msi", + "downloadSize": 43606016, + "sha256": "83a4def2a8c8fde24d147d39e70f60ee144e9425828f34eda2340c23ce72b1da" + } + }, + { + "version": "1.6.2", + "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2", + "download": { + "date": "2022-08-17T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.2-Windows-x64-installer.msi", + "downloadSize": 43606016, + "sha256": "2bb06fe3624df447d56da0d72fef1f4328346fd92c6dffccf66ba37253ec5e62" + } + }, + { + "version": "1.6.2-rc.3", + "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc3", + "download": { + "date": "2022-08-15T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc3/JackTrip-v1.6.2-rc3-Windows-x64-installer.msi", + "downloadSize": 43606016, + "sha256": "62771ca5efbf2e91fa4cd347214e6e517b76c032a8895ca80bcbc2fa765ab81a" + } + }, + { + "version": "1.6.2-rc.2", + "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc2", + "download": { + "date": "2022-08-09T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc2/JackTrip-v1.6.2-rc2-Windows-x64-installer.msi", + "downloadSize": 43606016, + "sha256": "ff88acd1804362589478366a620d12be302071dba9781ea38ed6a8343c94c16d" + } + }, + { + "version": "1.6.2-rc.1", + "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2-rc1", + "download": { + "date": "2022-08-06T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.2-rc1/JackTrip-v1.6.2-rc1-Windows-x64-installer.msi", + "downloadSize": 43601920, + "sha256": "f1412de0b13ff7599353a10aec8f2b69e9831a37103187f8fa68334c8f8f09de" + } + }, + { + "version": "1.6.1", + "changelog": "Bugfixes around UDP timeout and 'Logging In' screen navigation. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.1", + "download": { + "date": "2022-06-21T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.1-Windows-x64-installer.msi", + "downloadSize": 43368448, + "sha256": "8eac390617488d849c0356e3305c96a59bbe46a8174d02b0321bb1dc86774b87" + } + }, + { + "version": "1.6.0", + "changelog": "Full integration with JackTrip Virtual Studio. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.0", + "download": { + "date": "2022-06-01T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-Windows-x64-installer.msi", + "downloadSize": 43364352, + "sha256": "9562ab654202bfc432e05caa3bd2bf1d0b52c50581b0a567f0546983fe46c078" + } + }, + { + "version": "1.6.0-rc.5", + "changelog": "Release candidate 5 for 1.6.0", + "download": { + "date": "2022-05-30T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.5/JackTrip-v1.6.0-rc.5-Windows-x64-installer.msi", + "downloadSize": 43364352, + "sha256": "d84e6e5d21cf31f5dd48e9dcc0c1a44fe7a37d977f94b6ff63d5e381745e5a44" + } + }, + { + "version": "1.6.0-rc.4", + "changelog": "Release candidate 4 for 1.6.0", + "download": { + "date": "2022-05-29T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.4/JackTrip-v1.6.0-rc.4-Windows-x64-installer.msi", + "downloadSize": 43126784, + "sha256": "cdb0ef906cf0d6047289838bf013b31a626cdd74dd4f75d6c1c4c3adbc9cd41d" + } + }, + { + "version": "1.6.0-rc.3", + "changelog": "Release candidate 3 for 1.6.0", + "download": { + "date": "2022-05-27T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-rc.3-Windows-x64-installer.msi", + "downloadSize": 43118592, + "sha256": "dceaf670a67cf1541007db82c5ce937b25370a7140e48192b94470f575fc4988" + } + }, + { + "version": "1.6.0-rc.2", + "changelog": "Release candidate 2 for 1.6.0", + "download": { + "date": "2022-05-26T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-rc.2-Windows-x64-signed-installer.msi", + "downloadSize": 43114496, + "sha256": "b1a7adc8dc0fb47f59515790e8531dd10838d799bacb4b5653192ed621bca208" + } + }, + { + "version": "1.6.0-rc.1", + "changelog": "Release candidate 1 for 1.6.0", + "download": { + "date": "2022-05-23T00:00:00Z", + "url": "https://github.com/jacktrip/jacktrip/releases/download/v1.6.0-rc.1/JackTrip-v1.6.0-rc.1-Windows-x64-installer.msi", + "downloadSize": 43081728, + "sha256": "240f8b495ec5057228be922da80829a3718b474bafc2ba2d77750643abd1005c" + } + } + ] +} diff --git a/releases/stable/linux-manifests.json b/releases/stable/linux-manifests.json index 4b4156c..7a2c4fb 100644 --- a/releases/stable/linux-manifests.json +++ b/releases/stable/linux-manifests.json @@ -1,75 +1,85 @@ { - "app_name": "JackTrip", - "releases": [ - { - "version": "1.8.0", - "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0", - "download": { - "date": "2023-03-18T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-Linux-x64-binary.zip", - "downloadSize": "36167636", - "sha256": "6f14273ffd5526d576a184f4559adb4124def8760d0b9ba60c43b0fa75b2d1a5" - } - }, - { - "version": "1.7.1", - "changelog": "Video button, bug fixes, Linux build fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1", - "download": { - "date": "2023-02-10T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.1-Linux-x64-binary.zip", - "downloadSize": 34338503, - "sha256": "4298e1edd561815630e3c6573660eeea15b199a12742ab1b90d43a6e3522b632" - } - }, - { - "version": "1.7.0", - "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0", - "download": { - "date": "2023-01-24T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.0-Linux-x64-binary.zip", - "downloadSize": 32554865, - "sha256": "13b3781f6dca0713eb135c9352c23cf0f40094603357ee5d5a940868597e8bb8" - } - }, - { - "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": { - "date": "2022-12-02T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-Linux-x64-binary.zip", - "downloadSize": 30495373, - "sha256": "09421562aeeefe40bb15945bf1e8e03b3f905ea383ce0a9ddd1f93f099141cfd" - } - }, - { - "version": "1.6.6", - "changelog": "Adding volume controls in Virtual Studio, preliminary QT6 support, classic mode GUI updates, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.6", - "download": { - "date": "2022-11-02T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.6-Linux-x64-binary.zip", - "downloadSize": 29837298, - "sha256": "25b15f3208521530b18e2096de722fefe5318149aa296610f8c5eec147586e0a" - } - }, - { - "version": "1.6.4", - "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4", - "download": { - "date": "2022-09-16T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.4-Linux-x64-binary.zip", - "downloadSize": 22233485, - "sha256": "f1b9585e4eb72c07c735ee6f4dc94ad1d1a4c70474799eb1111bb5a39a99e295" - } - } - ] + "app_name": "JackTrip", + "releases": [ + { + "version": "1.8.1", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.1", + "download": { + "date": "2023-04-03T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.1-Linux-x64-binary.zip", + "downloadSize": "36128967", + "sha256": "c0c082942ae2010553ba01c51c23b4ba01d8e5d4f69d3d2d2e56e183156d6f07" + } + }, + { + "version": "1.8.0", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0", + "download": { + "date": "2023-03-18T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-Linux-x64-binary.zip", + "downloadSize": "36167636", + "sha256": "6f14273ffd5526d576a184f4559adb4124def8760d0b9ba60c43b0fa75b2d1a5" + } + }, + { + "version": "1.7.1", + "changelog": "Video button, bug fixes, Linux build fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1", + "download": { + "date": "2023-02-10T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.1-Linux-x64-binary.zip", + "downloadSize": 34338503, + "sha256": "4298e1edd561815630e3c6573660eeea15b199a12742ab1b90d43a6e3522b632" + } + }, + { + "version": "1.7.0", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0", + "download": { + "date": "2023-01-24T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.0-Linux-x64-binary.zip", + "downloadSize": 32554865, + "sha256": "13b3781f6dca0713eb135c9352c23cf0f40094603357ee5d5a940868597e8bb8" + } + }, + { + "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": { + "date": "2022-12-02T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-Linux-x64-binary.zip", + "downloadSize": 30495373, + "sha256": "09421562aeeefe40bb15945bf1e8e03b3f905ea383ce0a9ddd1f93f099141cfd" + } + }, + { + "version": "1.6.6", + "changelog": "Adding volume controls in Virtual Studio, preliminary QT6 support, classic mode GUI updates, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.6", + "download": { + "date": "2022-11-02T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.6-Linux-x64-binary.zip", + "downloadSize": 29837298, + "sha256": "25b15f3208521530b18e2096de722fefe5318149aa296610f8c5eec147586e0a" + } + }, + { + "version": "1.6.4", + "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4", + "download": { + "date": "2022-09-16T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.4-Linux-x64-binary.zip", + "downloadSize": 22233485, + "sha256": "f1b9585e4eb72c07c735ee6f4dc94ad1d1a4c70474799eb1111bb5a39a99e295" + } + } + ] } diff --git a/releases/stable/mac-manifests.json b/releases/stable/mac-manifests.json index c94527a..5277dd4 100644 --- a/releases/stable/mac-manifests.json +++ b/releases/stable/mac-manifests.json @@ -1,115 +1,125 @@ { - "app_name": "JackTrip", - "releases": [ - { - "version": "1.8.0", - "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0", - "download": { - "date": "2023-03-18T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-macOS-x64-signed-installer.pkg", - "downloadSize": "11771203", - "sha256": "dfee21d5e91a35baaf3b58891188f72f2cb894b2bf796ac70350a2ef9d3fb68c" - } - }, - { - "version": "1.7.1", - "changelog": "Video button, bug fixes, Linux build fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1", - "download": { - "date": "2023-02-10T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.1-macOS-x64-installer.pkg", - "downloadSize": 11679783, - "sha256": "027d1d3aeb4aaca79b21824371a0bfb915b8c43afe924a8ec2be92df719a431f" - } - }, - { - "version": "1.7.0", - "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0", - "download": { - "date": "2023-01-24T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.0-macOS-x64-installer.pkg", - "downloadSize": 11530594, - "sha256": "0e5731c2ad71aa4bd28ccf9311e312f2386c42b02abbca142777dfb06ea1b427" - } - }, - { - "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": { - "date": "2022-12-02T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-macOS-x64-installer.pkg", - "downloadSize": 11517151, - "sha256": "38c25788895ec404bdb4b6148114cad8af31b65e5189ca08819d1e954e2ef4e7" - } - }, - { - "version": "1.6.6", - "changelog": "Adding volume controls in Virtual Studio, preliminary QT6 support, classic mode GUI updates, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.6", - "download": { - "date": "2022-11-02T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.6-macOS-x64-installer.pkg", - "downloadSize": 11481324, - "sha256": "e99149ea9bbfb94a2000cfd3848013e44bb8d23e783198005681c2038bcbd313" - } - }, - { - "version": "1.6.4", - "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4", - "download": { - "date": "2022-09-16T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.4-macOS-x64-installer.pkg", - "downloadSize": 11554866, - "sha256": "e5898f3ff57fc2d734590decb4f82a28ad9208a33cad97152f119716cdcea1a9" - } - }, - { - "version": "1.6.3", - "changelog": "Fixes around Linux desktop file and hub server mode: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.3", - "download": { - "date": "2022-08-23T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.3-macOS-x64-installer.pkg", - "downloadSize": 11533023, - "sha256": "0b597c62544e9813949f859cc7b7c13bef893f6896f96b34a19889faf4855116" - } - }, - { - "version": "1.6.2", - "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2", - "download": { - "date": "2022-08-17T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.2-macOS-x64-installer.pkg", - "downloadSize": 11536442, - "sha256": "450222f4db1922275e07286d0be6ea2df2873a8a39c3b23096bcfbce342b7b3c" - } - }, - { - "version": "1.6.1", - "changelog": "Bugfixes around UDP timeout and 'Logging In' screen navigation. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.1", - "download": { - "date": "2022-06-21T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.1-macOS-x64-installer.pkg", - "downloadSize": 11476305, - "sha256": "eaf05c842d6b3ae799208a40b37da1cdb13e3700dcbbd97443c80cad81f4d2ac" - } - }, - { - "version": "1.6.0", - "changelog": "Full integration with JackTrip Virtual Studio. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.0", - "download": { - "date": "2022-06-01T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-macOS-x64-installer.pkg", - "downloadSize": 11474299, - "sha256": "27259600ecd879106ebbf97754d72d6236075a049eafa0de6271d33f753f13e4" - } - } - ] -} \ No newline at end of file + "app_name": "JackTrip", + "releases": [ + { + "version": "1.8.1", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.1", + "download": { + "date": "2023-04-03T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.1-macOS-x64-signed-installer.pkg", + "downloadSize": "11754694", + "sha256": "d073ff5f2b90b5dd86ccd07c5d30d61d7be776c5e3c0083e6f665f78fea48298" + } + }, + { + "version": "1.8.0", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0", + "download": { + "date": "2023-03-18T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-macOS-x64-signed-installer.pkg", + "downloadSize": "11771203", + "sha256": "dfee21d5e91a35baaf3b58891188f72f2cb894b2bf796ac70350a2ef9d3fb68c" + } + }, + { + "version": "1.7.1", + "changelog": "Video button, bug fixes, Linux build fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1", + "download": { + "date": "2023-02-10T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.1-macOS-x64-installer.pkg", + "downloadSize": 11679783, + "sha256": "027d1d3aeb4aaca79b21824371a0bfb915b8c43afe924a8ec2be92df719a431f" + } + }, + { + "version": "1.7.0", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0", + "download": { + "date": "2023-01-24T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.0-macOS-x64-installer.pkg", + "downloadSize": 11530594, + "sha256": "0e5731c2ad71aa4bd28ccf9311e312f2386c42b02abbca142777dfb06ea1b427" + } + }, + { + "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": { + "date": "2022-12-02T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-macOS-x64-installer.pkg", + "downloadSize": 11517151, + "sha256": "38c25788895ec404bdb4b6148114cad8af31b65e5189ca08819d1e954e2ef4e7" + } + }, + { + "version": "1.6.6", + "changelog": "Adding volume controls in Virtual Studio, preliminary QT6 support, classic mode GUI updates, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.6", + "download": { + "date": "2022-11-02T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.6-macOS-x64-installer.pkg", + "downloadSize": 11481324, + "sha256": "e99149ea9bbfb94a2000cfd3848013e44bb8d23e783198005681c2038bcbd313" + } + }, + { + "version": "1.6.4", + "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4", + "download": { + "date": "2022-09-16T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.4-macOS-x64-installer.pkg", + "downloadSize": 11554866, + "sha256": "e5898f3ff57fc2d734590decb4f82a28ad9208a33cad97152f119716cdcea1a9" + } + }, + { + "version": "1.6.3", + "changelog": "Fixes around Linux desktop file and hub server mode: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.3", + "download": { + "date": "2022-08-23T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.3-macOS-x64-installer.pkg", + "downloadSize": 11533023, + "sha256": "0b597c62544e9813949f859cc7b7c13bef893f6896f96b34a19889faf4855116" + } + }, + { + "version": "1.6.2", + "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2", + "download": { + "date": "2022-08-17T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.2-macOS-x64-installer.pkg", + "downloadSize": 11536442, + "sha256": "450222f4db1922275e07286d0be6ea2df2873a8a39c3b23096bcfbce342b7b3c" + } + }, + { + "version": "1.6.1", + "changelog": "Bugfixes around UDP timeout and 'Logging In' screen navigation. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.1", + "download": { + "date": "2022-06-21T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.1-macOS-x64-installer.pkg", + "downloadSize": 11476305, + "sha256": "eaf05c842d6b3ae799208a40b37da1cdb13e3700dcbbd97443c80cad81f4d2ac" + } + }, + { + "version": "1.6.0", + "changelog": "Full integration with JackTrip Virtual Studio. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.0", + "download": { + "date": "2022-06-01T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-macOS-x64-installer.pkg", + "downloadSize": 11474299, + "sha256": "27259600ecd879106ebbf97754d72d6236075a049eafa0de6271d33f753f13e4" + } + } + ] +} diff --git a/releases/stable/win-manifests.json b/releases/stable/win-manifests.json index 38b892f..b29034e 100644 --- a/releases/stable/win-manifests.json +++ b/releases/stable/win-manifests.json @@ -1,115 +1,125 @@ { - "app_name": "JackTrip", - "releases": [ - { - "version": "1.8.0", - "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0", - "download": { - "date": "2023-03-18T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-Windows-x64-signed-installer.msi", - "downloadSize": "45699072", - "sha256": "4b6705a2e8af7f9a516fefaf119d5e6cadf93e8f40964cd52b635ced5745b267" - } - }, - { - "version": "1.7.1", - "changelog": "Video button, bug fixes, Linux build fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1", - "download": { - "date": "2023-02-10T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.1-Windows-x64-installer.msi", - "downloadSize": 45330432, - "sha256": "fb5d756afcd471ca8ae45b05b411235026f23f2178893d85ac12f39c3f66a01a" - } - }, - { - "version": "1.7.0", - "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0", - "download": { - "date": "2023-01-24T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.0-Windows-x64-installer.msi", - "downloadSize": 44572672, - "sha256": "a1890fe10de484f423a17118031d898abacc9b9eb2ccd35bdb4351e9411ff866" - } - }, - { - "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": { - "date": "2022-12-02T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-Windows-x64-installer.msi", - "downloadSize": 44535808, - "sha256": "75464575311da5521e011da8d2f2b5aff1379edb4dee9dcb90e1750dcc97f2f9" - } - }, - { - "version": "1.6.6", - "changelog": "Adding volume controls in Virtual Studio, preliminary QT6 support, classic mode GUI updates, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.6", - "download": { - "date": "2022-11-02T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.6-Windows-x64-installer.msi", - "downloadSize": 44408832, - "sha256": "828a1f43254db187a33601cc809551617db83e60a51dad3e992c30270967de0c" - } - }, - { - "version": "1.6.4", - "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4", - "download": { - "date": "2022-09-16T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.4-Windows-x64-installer.msi", - "downloadSize": 43663360, - "sha256": "58dcbba584e1cc82373b8aa159800ad360eec7933ce9462e413ca347f09f3c26" - } - }, - { - "version": "1.6.3", - "changelog": "Fixes around Linux desktop file and hub server mode: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.3", - "download": { - "date": "2022-08-23T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.3-Windows-x64-installer.msi", - "downloadSize": 43606016, - "sha256": "83a4def2a8c8fde24d147d39e70f60ee144e9425828f34eda2340c23ce72b1da" - } - }, - { - "version": "1.6.2", - "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2", - "download": { - "date": "2022-08-17T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.2-Windows-x64-installer.msi", - "downloadSize": 43606016, - "sha256": "2bb06fe3624df447d56da0d72fef1f4328346fd92c6dffccf66ba37253ec5e62" - } - }, - { - "version": "1.6.1", - "changelog": "Bugfixes around UDP timeout and 'Logging In' screen navigation. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.1", - "download": { - "date": "2022-06-21T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.1-Windows-x64-installer.msi", - "downloadSize": 43368448, - "sha256": "8eac390617488d849c0356e3305c96a59bbe46a8174d02b0321bb1dc86774b87" - } - }, - { - "version": "1.6.0", - "changelog": "Full integration with JackTrip Virtual Studio. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.0", - "download": { - "date": "2022-06-01T00:00:00Z", - "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-Windows-x64-installer.msi", - "downloadSize": 43364352, - "sha256": "9562ab654202bfc432e05caa3bd2bf1d0b52c50581b0a567f0546983fe46c078" - } - } - ] -} \ No newline at end of file + "app_name": "JackTrip", + "releases": [ + { + "version": "1.8.1", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.1", + "download": { + "date": "2023-04-03T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.1-Windows-x64-signed-installer.msi", + "downloadSize": "46456832", + "sha256": "55da405ea55a3567a672eef0f5f3699c264f456e13dd04d2a55d90bd3d1a2f55" + } + }, + { + "version": "1.8.0", + "changelog": "Full changelog at https://github.com/jacktrip/jacktrip/releases/tag/v1.8.0", + "download": { + "date": "2023-03-18T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.8.0-Windows-x64-signed-installer.msi", + "downloadSize": "45699072", + "sha256": "4b6705a2e8af7f9a516fefaf119d5e6cadf93e8f40964cd52b635ced5745b267" + } + }, + { + "version": "1.7.1", + "changelog": "Video button, bug fixes, Linux build fixes, and Qt upgrades: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.1", + "download": { + "date": "2023-02-10T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.1-Windows-x64-installer.msi", + "downloadSize": 45330432, + "sha256": "fb5d756afcd471ca8ae45b05b411235026f23f2178893d85ac12f39c3f66a01a" + } + }, + { + "version": "1.7.0", + "changelog": "Start/join inactive studios, fix crashes and hanging UI on Windows: https://github.com/jacktrip/jacktrip/releases/tag/v1.7.0", + "download": { + "date": "2023-01-24T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.7.0-Windows-x64-installer.msi", + "downloadSize": 44572672, + "sha256": "a1890fe10de484f423a17118031d898abacc9b9eb2ccd35bdb4351e9411ff866" + } + }, + { + "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": { + "date": "2022-12-02T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.7-Windows-x64-installer.msi", + "downloadSize": 44535808, + "sha256": "75464575311da5521e011da8d2f2b5aff1379edb4dee9dcb90e1750dcc97f2f9" + } + }, + { + "version": "1.6.6", + "changelog": "Adding volume controls in Virtual Studio, preliminary QT6 support, classic mode GUI updates, and fixes: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.6", + "download": { + "date": "2022-11-02T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.6-Windows-x64-installer.msi", + "downloadSize": 44408832, + "sha256": "828a1f43254db187a33601cc809551617db83e60a51dad3e992c30270967de0c" + } + }, + { + "version": "1.6.4", + "changelog": "Adding volume meters, bugfixes around Windows hanging, device disconnects, and PLC: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.4", + "download": { + "date": "2022-09-16T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.4-Windows-x64-installer.msi", + "downloadSize": 43663360, + "sha256": "58dcbba584e1cc82373b8aa159800ad360eec7933ce9462e413ca347f09f3c26" + } + }, + { + "version": "1.6.3", + "changelog": "Fixes around Linux desktop file and hub server mode: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.3", + "download": { + "date": "2022-08-23T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.3-Windows-x64-installer.msi", + "downloadSize": 43606016, + "sha256": "83a4def2a8c8fde24d147d39e70f60ee144e9425828f34eda2340c23ce72b1da" + } + }, + { + "version": "1.6.2", + "changelog": "Ability to open app via URL schemes, displaying latency stats, and Virtual Studio device support. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.2", + "download": { + "date": "2022-08-17T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.2-Windows-x64-installer.msi", + "downloadSize": 43606016, + "sha256": "2bb06fe3624df447d56da0d72fef1f4328346fd92c6dffccf66ba37253ec5e62" + } + }, + { + "version": "1.6.1", + "changelog": "Bugfixes around UDP timeout and 'Logging In' screen navigation. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.1", + "download": { + "date": "2022-06-21T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.1-Windows-x64-installer.msi", + "downloadSize": 43368448, + "sha256": "8eac390617488d849c0356e3305c96a59bbe46a8174d02b0321bb1dc86774b87" + } + }, + { + "version": "1.6.0", + "changelog": "Full integration with JackTrip Virtual Studio. Learn more here: https://github.com/jacktrip/jacktrip/releases/tag/v1.6.0", + "download": { + "date": "2022-06-01T00:00:00Z", + "url": "https://files.jacktrip.org/app-builds/JackTrip-v1.6.0-Windows-x64-installer.msi", + "downloadSize": 43364352, + "sha256": "9562ab654202bfc432e05caa3bd2bf1d0b52c50581b0a567f0546983fe46c078" + } + } + ] +} diff --git a/scripts/update_manifests.sh b/scripts/update_manifests.sh new file mode 100755 index 0000000..f76bf1b --- /dev/null +++ b/scripts/update_manifests.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +EDGE_PLATFORMS="mac win" +STABLE_PLATFORMS="$EDGE_PLATFORMS linux" + +function update_manifest { + NAME="$1/$2-manifests.json" + echo "Updating $NAME" + curl -s -o "releases/$NAME" "https://files.jacktrip.org/app-releases/$NAME" +} + +for x in $EDGE_PLATFORMS +do + update_manifest edge $x +done + +for y in $STABLE_PLATFORMS +do + update_manifest stable $y +done diff --git a/src/AudioInterface.cpp b/src/AudioInterface.cpp index c91ecfd..e32bb01 100644 --- a/src/AudioInterface.cpp +++ b/src/AudioInterface.cpp @@ -77,8 +77,10 @@ AudioInterface::AudioInterface(QVarLengthArray InputChans, #ifndef WAIR // cc // Initialize and assign memory for ProcessPlugins Buffers + int monitorChans = std::min(mInputChans.size(), mOutputChans.size()); mInProcessBuffer.resize(mInputChans.size()); mOutProcessBuffer.resize(mOutputChans.size()); + mMonProcessBuffer.resize(monitorChans); // Set pointer to NULL for (int i = 0; i < mInProcessBuffer.size(); i++) { mInProcessBuffer[i] = NULL; @@ -86,18 +88,25 @@ AudioInterface::AudioInterface(QVarLengthArray InputChans, for (int i = 0; i < mOutProcessBuffer.size(); i++) { mOutProcessBuffer[i] = NULL; } + for (int i = 0; i < monitorChans; i++) { + mMonProcessBuffer[i] = NULL; + } #else // WAIR int iCnt = (mInputChans.size() > mNumNetRevChans) ? mInputChans.size() : mNumNetRevChans; int oCnt = (mOutputChans.size() > mNumNetRevChans) ? mOutputChans.size() : mNumNetRevChans; int aCnt = (mNumNetRevChans) ? mInputChans.size() : 0; + int mCnt = std::min(iCnt, oCnt); for (int i = 0; i < iCnt; i++) { mInProcessBuffer[i] = NULL; } for (int i = 0; i < oCnt; i++) { mOutProcessBuffer[i] = NULL; } + for (int i = 0; i < mCnt; i++) { + mMonProcessBuffer[i] = NULL; + } for (int i = 0; i < aCnt; i++) { mAPInBuffer[i] = NULL; } @@ -109,6 +118,7 @@ AudioInterface::AudioInterface(QVarLengthArray InputChans, new sample_t[MAX_AUDIO_BUFFER_SIZE]; // required for processing audio input } + // Not used in this class but may be needed by subclasses mNumInChans = mInputChans.size(); mNumOutChans = mOutputChans.size(); } @@ -126,6 +136,9 @@ AudioInterface::~AudioInterface() for (int i = 0; i < mOutProcessBuffer.size(); i++) { delete[] mOutProcessBuffer[i]; } + for (int i = 0; i < mMonProcessBuffer.size(); i++) { + delete[] mMonProcessBuffer[i]; + } #else // WAIR for (int i = 0; i < mInProcessBuffer.size(); i++) { delete[] mInProcessBuffer[i]; @@ -133,6 +146,9 @@ AudioInterface::~AudioInterface() for (int i = 0; i < mOutProcessBuffer.size(); i++) { delete[] mOutProcessBuffer[i]; } + for (int i = 0; i < mMonProcessBuffer.size(); i++) { + delete[] mMonProcessBuffer[i]; + } for (int i = 0; i < mAPInBuffer.size(); i++) { delete[] mAPInBuffer[i]; } @@ -143,6 +159,9 @@ AudioInterface::~AudioInterface() for (auto* i : qAsConst(mProcessPluginsToNetwork)) { delete i; } + for (auto* i : qAsConst(mProcessPluginsToMonitor)) { + delete i; + } for (int i = 0; i < mInBufCopy.size(); i++) { delete[] mInBufCopy[i]; } @@ -151,13 +170,17 @@ AudioInterface::~AudioInterface() //******************************************************************************* void AudioInterface::setup(bool /*verbose*/) { - int nChansIn = mInputChans.size(); - int nChansOut = mOutputChans.size(); + int nChansIn = mInputChans.size(); + int nChansOut = mOutputChans.size(); + int nChansMon = + std::min(nChansIn, nChansOut); // Note: Should be 2 when mixing stereo-to-mono inputMixModeT inputMixMode = mInputMixMode; if (inputMixMode == MIXTOMONO) { nChansIn = 1; } - + if (inputMixMode == MONO) { + nChansMon = nChansOut; + } // Allocate buffer memory to read and write mSizeInBytesPerChannel = getSizeInBytesPerChannel(); @@ -178,6 +201,7 @@ void AudioInterface::setup(bool /*verbose*/) if (mNumNetRevChans) { mInProcessBuffer.resize(mNumNetRevChans); mOutProcessBuffer.resize(mNumNetRevChans); + mMonProcessBuffer.resize(mNumNetRevChans); mAPInBuffer.resize(nChansIn); mNetInBuffer.resize(mNumNetRevChans); } else // don't change sizes @@ -185,6 +209,7 @@ void AudioInterface::setup(bool /*verbose*/) { mInProcessBuffer.resize(nChansIn); mOutProcessBuffer.resize(nChansOut); + mMonProcessBuffer.resize(nChansMon); } int nframes = getBufferSizeInSamples(); @@ -200,6 +225,11 @@ void AudioInterface::setup(bool /*verbose*/) // set memory to 0 std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * nframes); } + for (int i = 0; i < nChansMon; i++) { + mMonProcessBuffer[i] = new sample_t[nframes]; + // set memory to 0 + std::memset(mMonProcessBuffer[i], 0, sizeof(sample_t) * nframes); + } #else // WAIR for (int i = 0; i < ((mNumNetRevChans) ? mNumNetRevChans : nChansIn); i++) { mInProcessBuffer[i] = new sample_t[nframes]; @@ -211,6 +241,11 @@ void AudioInterface::setup(bool /*verbose*/) // set memory to 0 std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * nframes); } + for (int i = 0; i < ((mNumNetRevChans) ? mNumNetRevChans : nChansMon); i++) { + mMonProcessBuffer[i] = new sample_t[nframes]; + // set memory to 0 + std::memset(mMonitorProcess[i], 0, sizeof(sample_t) * nframes); + } for (int i = 0; i < ((mNumNetRevChans) ? nChansIn : 0); i++) { mAPInBuffer[i] = new sample_t[nframes]; // set memory to 0 @@ -235,11 +270,17 @@ void AudioInterface::callback(QVarLengthArray& in_buffer, QVarLengthArray& out_buffer, unsigned int n_frames) { - int nChansIn = mInputChans.size(); + int nChansIn = mInputChans.size(); + int nChansOut = mOutputChans.size(); + int nChansMon = + std::min(nChansIn, nChansOut); // Note: Should be 2 when mixing stereo-to-mono inputMixModeT inputMixMode = mInputMixMode; if (inputMixMode == MIXTOMONO) { nChansIn = 1; } + if (inputMixMode == MONO) { + nChansMon = nChansOut; + } // Allocate the Process Callback //------------------------------------------------------------------- // 1) First, process incoming packets @@ -314,7 +355,9 @@ void AudioInterface::callback(QVarLengthArray& in_buffer, // mAudioTesterP will be nullptr for hub server's JackTripWorker instances: bool audioTesting = (mAudioTesterP && mAudioTesterP->getEnabled()); int nop = mProcessPluginsToNetwork.size(); // number of OUTGOING processing modules - if (nop > 0 || audioTesting) { // cannot modify in_buffer, so make a copy + if (nop > 0 || audioTesting + || mProcessPluginsToMonitor.size() + > 0) { // cannot modify in_buffer, so make a copy // in_buffer is "in" from local audio hardware via JACK if (mInBufCopy.size() < nChansIn) { // created in constructor above std::cerr << "*** AudioInterface.cpp: Number of Input Channels changed - " @@ -336,6 +379,31 @@ void AudioInterface::callback(QVarLengthArray& in_buffer, p->compute(n_frames, mInBufCopy.data(), mInBufCopy.data()); } } + + for (int i = 0; i < nChansMon; i++) { + if ((mInputChans.size() == 2 && mInputMixMode == AudioInterface::MIXTOMONO) + || (mInputMixMode == AudioInterface::MONO)) { + // if using mix-to-mono, in_buffer[0] should already contain the mixed + // audio, so copy it to the monitor buffer. See RtAudioInterface.cpp + + // likewise if using mono, we simply copy the input to every monitor + // channel + std::memcpy(mMonProcessBuffer[i], in_buffer[0], + sizeof(sample_t) * n_frames); + } else { + // otherwise, copy each channel individually + std::memcpy(mMonProcessBuffer[i], in_buffer[i], + sizeof(sample_t) * n_frames); + } + } + for (int i = 0; i < mProcessPluginsToMonitor.size(); i++) { + ProcessPlugin* p = mProcessPluginsToMonitor[i]; + if (p->getInited()) { + // note: for monitor plugins, the output is out_buffer (to the speakers) + p->compute(n_frames, mMonProcessBuffer.data(), out_buffer.data()); + } + } + if (audioTesting) { mAudioTesterP->writeImpulse( mInBufCopy, @@ -718,16 +786,60 @@ void AudioInterface::appendProcessPluginFromNetwork(ProcessPlugin* plugin) mProcessPluginsFromNetwork.append(plugin); } -void AudioInterface::initPlugins(bool verbose) +void AudioInterface::appendProcessPluginToMonitor(ProcessPlugin* plugin) { - int nChansIn = mInputChans.size(); - int nChansOut = mOutputChans.size(); + if (not plugin) { + return; + } + int nChansIn = mInputChans.size(); + int nChansOut = mOutputChans.size(); + int nChansMon = + std::min(nChansIn, nChansOut); // Note: Should be 2 when mixing stereo-to-mono inputMixModeT inputMixMode = mInputMixMode; if (inputMixMode == MIXTOMONO) { nChansIn = 1; } + if (inputMixMode == MONO) { + nChansMon = nChansOut; + } + if (plugin->getNumInputs() > nChansMon) { + std::cerr + << "*** AudioInterface.cpp: appendProcessPluginToMonitor: ProcessPlugin " + << typeid(plugin).name() << " REJECTED due to having " + << plugin->getNumInputs() + << " inputs, while the monitor audio input requires " << nChansMon + << " outputs\n"; + return; + } - int nPlugins = mProcessPluginsFromNetwork.size() + mProcessPluginsToNetwork.size(); + if (plugin->getNumOutputs() > nChansMon) { + std::cerr + << "*** AudioInterface.cpp: appendProcessPluginToMonitor: ProcessPlugin " + << typeid(plugin).name() << " REJECTED due to having " + << plugin->getNumOutputs() + << " inputs, while the monitor audio output requires " << nChansMon + << " outputs\n"; + return; + } + + mProcessPluginsToMonitor.append(plugin); +} + +void AudioInterface::initPlugins(bool verbose) +{ + int nChansIn = mInputChans.size(); + int nChansOut = mOutputChans.size(); + int nChansMon = + std::min(nChansIn, nChansOut); // Note: Should be 2 when mixing stereo-to-mono + inputMixModeT inputMixMode = mInputMixMode; + if (inputMixMode == MIXTOMONO) { + nChansIn = 1; + } + if (inputMixMode == MONO) { + nChansMon = nChansOut; + } + int nPlugins = mProcessPluginsFromNetwork.size() + mProcessPluginsToNetwork.size() + + mProcessPluginsToMonitor.size(); if (nPlugins > 0) { if (verbose) { std::cout << "Initializing Faust plugins (have " << nPlugins @@ -744,6 +856,11 @@ void AudioInterface::initPlugins(bool verbose) plugin->updateNumChannels(nChansIn, nChansOut); plugin->init(mSampleRate); } + for (ProcessPlugin* plugin : qAsConst(mProcessPluginsToMonitor)) { + plugin->setOutgoingToNetwork(false); + plugin->updateNumChannels(nChansMon, nChansMon); + plugin->init(mSampleRate); + } } } @@ -818,10 +935,9 @@ void AudioInterface::setDevicesWarningMsg(warningMessageT msg) switch (msg) { case DEVICE_WARN_LATENCY: mWarningMsg = - "The currently selected devices don't support low latency. You can use them, " - "but you " - "may experience audio delay. Make sure you have up to date drivers from the " - "manufacturer!"; + "The selected Input and Output devices are Non-ASIO and may cause high " + "latency or audio delay. Installing ASIO drivers and using ASIO Input and " + "Output devices will lower audio delays for a 'same room experience'."; #ifdef _WIN32 mWarningHelpUrl = "https://help.jacktrip.org/hc/en-us/articles/4409919243155"; #else @@ -864,6 +980,13 @@ void AudioInterface::setDevicesErrorMsg(errorMessageT msg) mErrorMsg = "JackTrip couldn't find any audio devices!"; mErrorHelpUrl = ""; break; +#ifdef _WIN32 + case DEVICE_ERR_SAME_ASIO: + mErrorMsg = + "When using ASIO, please select the same device for your input and output."; + mErrorHelpUrl = "https://help.jacktrip.org/hc/en-us/articles/4409919243155"; + break; +#endif default: mErrorMsg = ""; mErrorHelpUrl = ""; diff --git a/src/AudioInterface.h b/src/AudioInterface.h index b5ad1be..1018912 100644 --- a/src/AudioInterface.h +++ b/src/AudioInterface.h @@ -83,7 +83,10 @@ class AudioInterface DEVICE_ERR_INCOMPATIBLE, DEVICE_ERR_NO_INPUTS, DEVICE_ERR_NO_OUTPUTS, - DEVICE_ERR_NO_DEVICES + DEVICE_ERR_NO_DEVICES, +#ifdef _WIN32 + DEVICE_ERR_SAME_ASIO +#endif }; enum inputMixModeT : int { @@ -158,6 +161,10 @@ class AudioInterface * Initialize all ProcessPlugin modules. * The audio sampling rate (mSampleRate) must be set at this time. */ + virtual void appendProcessPluginToMonitor(ProcessPlugin* plugin); + /** \brief appendProcessPluginFromNetwork(): + * Appends plugins used for local monitoring + */ void initPlugins(bool verbose = true); virtual void connectDefaultPorts() = 0; /** \brief Convert a 32bit number (sample_t) into one of the bit resolution @@ -282,10 +289,14 @@ class AudioInterface mProcessPluginsFromNetwork; ///< Vector of ProcessPlugins QVector mProcessPluginsToNetwork; ///< Vector of ProcessPlugins + QVector + mProcessPluginsToMonitor; ///< Vector of ProcessPlugins QVarLengthArray mInProcessBuffer; ///< Vector of Input buffers/channel for ProcessPlugin QVarLengthArray - mOutProcessBuffer; ///< Vector of Output buffers/channel for ProcessPlugin + mOutProcessBuffer; ///< Vector of Output buffers/channel for ProcessPlugin + QVarLengthArray + mMonProcessBuffer; ///< Vector of Monitor buffers/channel for ProcessPlugin int8_t* mAudioInputPacket; ///< Packet containing all the channels to read from the ///< RingBuffer int8_t* mAudioOutputPacket; ///< Packet containing all the channels to send to the diff --git a/src/Compressor.cpp b/src/Compressor.cpp index e0cb324..9164cbf 100644 --- a/src/Compressor.cpp +++ b/src/Compressor.cpp @@ -37,6 +37,81 @@ #include "Compressor.h" +#include "compressordsp.h" + +//******************************************************************************* +Compressor::Compressor(int numchans, // xtor + bool verboseIn, float ratioIn, float thresholdDBIn, + float attackMSIn, float releaseMSIn, float makeUpGainDBIn) + : mNumChannels(numchans) + , ratio(ratioIn) + , thresholdDB(thresholdDBIn) + , attackMS(attackMSIn) + , releaseMS(releaseMSIn) + , makeUpGainDB(makeUpGainDBIn) +{ + setVerbose(verboseIn); + // presets.push_back(std::make_unique(ratio,thresholdDB,attackMS,releaseMS,makeUpGainDB)); + for (int i = 0; i < mNumChannels; i++) { + compressordsp* dsp_ptr = new compressordsp; + APIUI* ui_ptr = new APIUI; + compressorP.push_back(dsp_ptr); + compressorUIP.push_back(ui_ptr); // #included in compressordsp.h + dsp_ptr->buildUserInterface(ui_ptr); + } +} + +//******************************************************************************* +Compressor::~Compressor() +{ + for (int i = 0; i < mNumChannels; i++) { + delete static_cast(compressorP[i]); + delete static_cast(compressorUIP[i]); + } + compressorP.clear(); + compressorUIP.clear(); +} + +//******************************************************************************* +void Compressor::setParamAllChannels(const char pName[], float p) +{ + for (int i = 0; i < mNumChannels; i++) { + APIUI* ui_ptr = static_cast(compressorUIP[i]); + int ndx = ui_ptr->getParamIndex(pName); + if (ndx >= 0) { + ui_ptr->setParamValue(ndx, p); + if (verbose) { + std::cout << "Compressor.h: parameter " << pName << " set to " << p + << " on audio channel " << i << "\n"; + } + } else { + std::cerr << "*** Compressor.h: Could not find parameter named " << pName + << "\n"; + } + } +} + +//******************************************************************************* +void Compressor::init(int samplingRate) +{ + ProcessPlugin::init(samplingRate); + if (samplingRate != fSamplingFreq) { + std::cerr << "Sampling rate not set by superclass!\n"; + std::exit(1); + } + fs = float(fSamplingFreq); + for (int i = 0; i < mNumChannels; i++) { + static_cast(compressorP[i]) + ->init(fs); // compression filter parameters depend on sampling rate + } + setParamAllChannels("Ratio", ratio); + setParamAllChannels("Threshold", thresholdDB); + setParamAllChannels("Attack", attackMS); + setParamAllChannels("Release", releaseMS); + setParamAllChannels("MakeUpGain", makeUpGainDB); + inited = true; +} + //******************************************************************************* void Compressor::compute(int nframes, float** inputs, float** outputs) { @@ -50,6 +125,7 @@ void Compressor::compute(int nframes, float** inputs, float** outputs) init(fSamplingFreq); } for (int i = 0; i < mNumChannels; i++) { - compressorP[i]->compute(nframes, &inputs[i], &outputs[i]); + static_cast(compressorP[i]) + ->compute(nframes, &inputs[i], &outputs[i]); } } diff --git a/src/Compressor.h b/src/Compressor.h index 84fe58e..8472a7e 100644 --- a/src/Compressor.h +++ b/src/Compressor.h @@ -46,7 +46,6 @@ #include "CompressorPresets.h" #include "ProcessPlugin.h" -#include "compressordsp.h" /** \brief A Compressor reduces the output dynamic range when the * signal level exceeds the threshold. @@ -59,22 +58,7 @@ class Compressor : public ProcessPlugin Compressor(int numchans, // xtor bool verboseIn = false, float ratioIn = 2.0f, float thresholdDBIn = -24.0f, float attackMSIn = 15.0f, float releaseMSIn = 40.0f, - float makeUpGainDBIn = 2.0f) - : mNumChannels(numchans) - , ratio(ratioIn) - , thresholdDB(thresholdDBIn) - , attackMS(attackMSIn) - , releaseMS(releaseMSIn) - , makeUpGainDB(makeUpGainDBIn) - { - setVerbose(verboseIn); - // presets.push_back(std::make_unique(ratio,thresholdDB,attackMS,releaseMS,makeUpGainDB)); - for (int i = 0; i < mNumChannels; i++) { - compressorP.push_back(new compressordsp); - compressorUIP.push_back(new APIUI); // #included in compressordsp.h - compressorP[i]->buildUserInterface(compressorUIP[i]); - } - } + float makeUpGainDBIn = 2.0f); Compressor(int numchans, // xtor bool verboseIn = false, CompressorPreset preset = CompressorPresets::voice) @@ -82,55 +66,14 @@ class Compressor : public ProcessPlugin preset.attackMS, preset.releaseMS, preset.makeUpGainDB) { } + /// \brief The class destructor - virtual ~Compressor() - { - for (int i = 0; i < mNumChannels; i++) { - delete compressorP[i]; - delete compressorUIP[i]; - } - compressorP.clear(); - compressorUIP.clear(); - } + virtual ~Compressor(); // void setParamAllChannels(std::string& pName, float p) { - void setParamAllChannels(const char pName[], float p) - { - for (int i = 0; i < mNumChannels; i++) { - int ndx = compressorUIP[i]->getParamIndex(pName); - if (ndx >= 0) { - compressorUIP[i]->setParamValue(ndx, p); - if (verbose) { - std::cout << "Compressor.h: parameter " << pName << " set to " << p - << " on audio channel " << i << "\n"; - } - } else { - std::cerr << "*** Compressor.h: Could not find parameter named " << pName - << "\n"; - } - } - } - - void init(int samplingRate) override - { - ProcessPlugin::init(samplingRate); - if (samplingRate != fSamplingFreq) { - std::cerr << "Sampling rate not set by superclass!\n"; - std::exit(1); - } - fs = float(fSamplingFreq); - for (int i = 0; i < mNumChannels; i++) { - compressorP[i]->init( - fs); // compression filter parameters depend on sampling rate - } - setParamAllChannels("Ratio", ratio); - setParamAllChannels("Threshold", thresholdDB); - setParamAllChannels("Attack", attackMS); - setParamAllChannels("Release", releaseMS); - setParamAllChannels("MakeUpGain", makeUpGainDB); - inited = true; - } + void setParamAllChannels(const char pName[], float p); + void init(int samplingRate) override; int getNumInputs() override { return (mNumChannels); } int getNumOutputs() override { return (mNumChannels); } void compute(int nframes, float** inputs, float** outputs) override; @@ -139,8 +82,8 @@ class Compressor : public ProcessPlugin private: float fs; int mNumChannels; - std::vector compressorP; - std::vector compressorUIP; + std::vector compressorP; + std::vector compressorUIP; float ratio; float thresholdDB; float attackMS; diff --git a/src/JackTrip.cpp b/src/JackTrip.cpp index 8732c6c..69cad3a 100644 --- a/src/JackTrip.cpp +++ b/src/JackTrip.cpp @@ -122,8 +122,6 @@ JackTrip::JackTrip(jacktripModeT JacktripMode, dataProtocolT DataProtocolType, , mStopOnTimeout(false) , mSendRingBuffer(NULL) , mReceiveRingBuffer(NULL) - , mRegulatorThreadPtr(NULL) - , mRegulatorWorkerPtr(NULL) , mReceiverBindPort(receiver_bind_port) , mSenderPeerPort(sender_peer_port) , mSenderBindPort(sender_bind_port) @@ -161,12 +159,11 @@ JackTrip::JackTrip(jacktripModeT JacktripMode, dataProtocolT DataProtocolType, JackTrip::~JackTrip() { // wait(); + stop(); delete mDataProtocolSender; delete mDataProtocolReceiver; delete mAudioInterface; delete mPacketHeader; - delete mRegulatorWorkerPtr; - delete mRegulatorThreadPtr; delete mSendRingBuffer; delete mReceiveRingBuffer; } @@ -429,14 +426,15 @@ void JackTrip::setupRingBuffers() mReceiveRingBuffer = new RingBuffer(audio_output_slot_size, mBufferQueueLength); mPacketHeader->setBufferRequiresSameSettings(true); - } else if (mBufferStrategy == 3) { + } else if ((mBufferStrategy == 3) || (mBufferStrategy == 4)) { + bool use_worker_thread = (mBufferStrategy == 3); cout << "Using experimental buffer strategy " << mBufferStrategy - << "-- Regulator with PLC" << endl; - - mReceiveRingBuffer = - new Regulator(mNumAudioChansOut, mAudioBitResolution, mAudioBufferSize, - mBufferQueueLength, mBroadcastQueueLength); - // bufStrategy 3, mBufferQueueLength is in integer msec not packets + << "-- Regulator with PLC (worker=" + << (use_worker_thread ? "true" : "false") << ")" << endl; + mReceiveRingBuffer = new Regulator(mNumAudioChansOut, mAudioBitResolution, + mAudioBufferSize, mBufferQueueLength, + use_worker_thread, mBroadcastQueueLength); + // bufStrategy 3 or 4, mBufferQueueLength is in integer msec not packets mPacketHeader->setBufferRequiresSameSettings(false); // = asym is default @@ -485,6 +483,15 @@ void JackTrip::appendProcessPluginFromNetwork(ProcessPlugin* plugin) } } +//******************************************************************************* +void JackTrip::appendProcessPluginToMonitor(ProcessPlugin* plugin) +{ + if (plugin) { + mProcessPluginsToMonitor.append(plugin); // ownership transferred + // mAudioInterface->appendProcessPluginFromNetwork(plugin); + } +} + //******************************************************************************* void JackTrip::startProcess( #ifdef WAIRTOHUB // WAIR @@ -539,8 +546,10 @@ void JackTrip::startProcess( #endif // endwhere ); - if (mAudioInterface->getDevicesErrorMsg() != "") { - stop(); + QString audioInterfaceError = + QString::fromStdString(mAudioInterface->getDevicesErrorMsg()); + if (audioInterfaceError != "") { + stop(audioInterfaceError); return; } @@ -664,16 +673,8 @@ void JackTrip::completeConnection() mAudioInterface->appendProcessPluginToNetwork(i); } - if (mBufferStrategy == 3) { - mRegulatorThreadPtr = new QThread(); - mRegulatorThreadPtr->setObjectName("RegulatorThread"); - Regulator* regulatorPtr = reinterpret_cast(mReceiveRingBuffer); - RegulatorWorker* workerPtr = new RegulatorWorker(regulatorPtr); - workerPtr->moveToThread(mRegulatorThreadPtr); - QObject::connect(this, &JackTrip::signalReceivedNetworkPacket, workerPtr, - &RegulatorWorker::pullPacket, Qt::QueuedConnection); - mRegulatorThreadPtr->start(); - mRegulatorWorkerPtr = workerPtr; + for (auto& i : mProcessPluginsToMonitor) { + mAudioInterface->appendProcessPluginToMonitor(i); } mAudioInterface->initPlugins(true); // mSampleRate known now, which plugins require @@ -1156,7 +1157,10 @@ void JackTrip::tcpTimerTick() serverHostAddress = info.addresses().constFirst(); } } - mTcpClient.connectToHost(serverHostAddress, mTcpServerPort); + + if (mTcpClient.state() == QAbstractSocket::UnconnectedState) { + mTcpClient.connectToHost(serverHostAddress, mTcpServerPort); + } mRetryTimer.start(); } @@ -1174,12 +1178,6 @@ void JackTrip::stop(const QString& errorMessage) mHasShutdown = true; std::cout << "Stopping JackTrip..." << std::endl; - if (mRegulatorThreadPtr != nullptr) { - // Stop the Regulator thread - mRegulatorThreadPtr->quit(); - mRegulatorThreadPtr->wait(); - } - if (mDataProtocolSender != nullptr) { // Stop The Sender mDataProtocolSender->stop(); @@ -1214,9 +1212,6 @@ void JackTrip::waitThreads() { mDataProtocolSender->wait(); mDataProtocolReceiver->wait(); - if (mRegulatorThreadPtr != nullptr) { - mRegulatorThreadPtr->wait(); - } } //******************************************************************************* @@ -1369,7 +1364,9 @@ int JackTrip::clientPingToServerStart() mRetryTimer.start(); } - mTcpClient.connectToHost(serverHostAddress, mTcpServerPort); + if (mTcpClient.state() == QAbstractSocket::UnconnectedState) { + mTcpClient.connectToHost(serverHostAddress, mTcpServerPort); + } if (gVerboseFlag) cout << "Connecting to TCP Server at " diff --git a/src/JackTrip.h b/src/JackTrip.h index 932f32d..731c6bb 100644 --- a/src/JackTrip.h +++ b/src/JackTrip.h @@ -38,7 +38,7 @@ #ifndef __JACKTRIP_H__ #define __JACKTRIP_H__ -//#include //for shared_ptr +// #include //for shared_ptr #include #include #include @@ -58,7 +58,7 @@ #include "PacketHeader.h" #include "RingBuffer.h" -//#include +// #include /** \brief Main class to creates a SERVER (to listen) or a CLIENT (to connect * to a listening server) to send audio streams in the network. * @@ -175,6 +175,7 @@ class JackTrip : public QObject // void appendProcessPlugin(const std::tr1::shared_ptr plugin); virtual void appendProcessPluginToNetwork(ProcessPlugin* plugin); virtual void appendProcessPluginFromNetwork(ProcessPlugin* plugin); + virtual void appendProcessPluginToMonitor(ProcessPlugin* plugin); /// \brief Start the processing threads virtual void startProcess( @@ -392,10 +393,6 @@ class JackTrip : public QObject virtual void receiveNetworkPacket(int8_t* ptrToReadSlot) { mReceiveRingBuffer->readSlotNonBlocking(ptrToReadSlot); - if (mBufferStrategy == 3) { - // trigger next packet using RegulatorThread - emit signalReceivedNetworkPacket(); - } } virtual void readAudioBuffer(int8_t* ptrToReadSlot) { @@ -587,7 +584,6 @@ class JackTrip : public QObject void signalUdpWaitingTooLong(); void signalQueueLengthChanged(int queueLength); void signalAudioStarted(); - void signalReceivedNetworkPacket(); public: /// \brief Set the AudioInteface object @@ -659,10 +655,6 @@ class JackTrip : public QObject RingBuffer* mSendRingBuffer; /// Pointer for the Receive RingBuffer RingBuffer* mReceiveRingBuffer; - /// thread used to pull packets from Regulator (if mBufferStrategy==3) - QThread* mRegulatorThreadPtr; - /// worker used to pull packets from Regulator (if mBufferStrategy==3) - QObject* mRegulatorWorkerPtr; int mReceiverBindPort; ///< Incoming (receiving) port for local machine int mSenderPeerPort; ///< Incoming (receiving) port for peer machine @@ -686,7 +678,8 @@ class JackTrip : public QObject mProcessPluginsFromNetwork; ///< Vector of ProcessPlugins QVector mProcessPluginsToNetwork; ///< Vector of ProcessPlugins - + QVector + mProcessPluginsToMonitor; ///< Vector of ProcessPlugins QTimer mTimeoutTimer; QTimer mRetryTimer; int mRetries; diff --git a/src/Limiter.cpp b/src/Limiter.cpp index b23b5f0..a203ebc 100644 --- a/src/Limiter.cpp +++ b/src/Limiter.cpp @@ -39,6 +39,78 @@ #include "Limiter.h" #include "jacktrip_types.h" +#include "limiterdsp.h" + +//******************************************************************************* +Limiter::Limiter(int numchans, int numclients, bool verboseFlag) + : mNumChannels(numchans) + , mNumClients(numclients) + , warningAmp(0.0) + , warnCount(0) + , peakMagnitude(0.0) + , nextWarning(1) +{ + setVerbose(verboseFlag); + for (int i = 0; i < mNumChannels; i++) { + limiterdsp* dsp_ptr = new limiterdsp; + APIUI* ui_ptr = new APIUI; + limiterP.push_back(dsp_ptr); + limiterUIP.push_back(ui_ptr); // #included in limiterdsp.h + dsp_ptr->buildUserInterface(ui_ptr); +#ifdef SINE_TEST + limitertest* test_ptr = new limitertest; + ui_ptr = new APIUI; + limiterTestP.push_back(test_ptr); + limiterTestUIP.push_back(ui_ptr); // #included in limitertest.h + test_ptr->buildUserInterface(ui_ptr); +#endif + } + // std::cout << "Limiter: constructed for " + // << mNumChannels << " channels and " + // << mNumClients << " assumed clients\n"; +} + +//******************************************************************************* +Limiter::~Limiter() +{ + for (int i = 0; i < mNumChannels; i++) { + delete static_cast(limiterP[i]); + delete static_cast(limiterUIP[i]); + } + limiterP.clear(); + limiterUIP.clear(); +} + +//******************************************************************************* +void Limiter::init(int samplingRate) +{ + ProcessPlugin::init(samplingRate); + if (samplingRate != fSamplingFreq) { + std::cerr << "Sampling rate not set by superclass!\n"; + std::exit(1); + } + fs = float(fSamplingFreq); + for (int i = 0; i < mNumChannels; i++) { + static_cast(limiterP[i]) + ->init(fs); // compression filter parameters depend on sampling rate + APIUI* ui_ptr = static_cast(limiterUIP[i]); + int ndx = ui_ptr->getParamIndex("NumClientsAssumed"); + ui_ptr->setParamValue(ndx, mNumClients); +#ifdef SINE_TEST + static_cast(limiterTestP[i]) + ->init(fs); // oscillator parameters depend on sampling rate + ui_ptr = static_cast(limiterTestUIP[i]); + ndx = ui_ptr->getParamIndex("Amp"); + ui_ptr->setParamValue(ndx, 0.2); + ndx = ui_ptr->getParamIndex("Freq"); + float sineFreq = + 110.0 * pow(1.5, double(i)) + * (mNumClients > 1 ? 1.25 : 1.0); // Maj 7 chord for stereo in & out + ui_ptr->setParamValue(ndx, sineFreq); +#endif + } + inited = true; +} //******************************************************************************* void Limiter::compute(int nframes, float** inputs, float** outputs) @@ -61,9 +133,10 @@ void Limiter::compute(int nframes, float** inputs, float** outputs) checkAmplitudes(nframes, inputs[i]); // we presently do one check across all channels } - limiterP[i]->compute(nframes, &inputs[i], &outputs[i]); + static_cast(limiterP[i])->compute(nframes, &inputs[i], &outputs[i]); #ifdef SINE_TEST - limiterTestP[i]->compute(nframes, faustSigs, faustSigs); + static_cast(limiterTestP[i]) + ->compute(nframes, faustSigs, faustSigs); for (int n = 0; n < nframes; n++) { outputs[i][n] = outputs[i][n] + sineTestOut[n]; } diff --git a/src/Limiter.h b/src/Limiter.h index fea43bd..1cf690d 100644 --- a/src/Limiter.h +++ b/src/Limiter.h @@ -49,11 +49,11 @@ #endif #include +#include #include #include #include "ProcessPlugin.h" -#include "limiterdsp.h" /** \brief The Limiter class confines the output dynamic range to a * "dynamic range lane" determined by the assumed number of clients. @@ -62,67 +62,12 @@ class Limiter : public ProcessPlugin { public: /// \brief The class constructor sets the number of channels to limit - Limiter(int numchans, int numclients, bool verboseFlag = false) // xtor - : mNumChannels(numchans) - , mNumClients(numclients) - , warningAmp(0.0) - , warnCount(0) - , peakMagnitude(0.0) - , nextWarning(1) - { - setVerbose(verboseFlag); - for (int i = 0; i < mNumChannels; i++) { - limiterP.push_back(new limiterdsp); - limiterUIP.push_back(new APIUI); // #included in limiterdsp.h - limiterP[i]->buildUserInterface(limiterUIP[i]); -#ifdef SINE_TEST - limiterTestP.push_back(new limitertest); - limiterTestUIP.push_back(new APIUI); // #included in limitertest.h - limiterTestP[i]->buildUserInterface(limiterTestUIP[i]); -#endif - } - // std::cout << "Limiter: constructed for " - // << mNumChannels << " channels and " - // << mNumClients << " assumed clients\n"; - } + Limiter(int numchans, int numclients, bool verboseFlag = false); /// \brief The class destructor - virtual ~Limiter() - { - for (int i = 0; i < mNumChannels; i++) { - delete limiterP[i]; - delete limiterUIP[i]; - } - limiterP.clear(); - limiterUIP.clear(); - } + virtual ~Limiter(); - void init(int samplingRate) override - { - ProcessPlugin::init(samplingRate); - if (samplingRate != fSamplingFreq) { - std::cerr << "Sampling rate not set by superclass!\n"; - std::exit(1); - } - fs = float(fSamplingFreq); - for (int i = 0; i < mNumChannels; i++) { - limiterP[i]->init( - fs); // compression filter parameters depend on sampling rate - int ndx = limiterUIP[i]->getParamIndex("NumClientsAssumed"); - limiterUIP[i]->setParamValue(ndx, mNumClients); -#ifdef SINE_TEST - limiterTestP[i]->init(fs); // oscillator parameters depend on sampling rate - ndx = limiterTestUIP[i]->getParamIndex("Amp"); - limiterTestUIP[i]->setParamValue(ndx, 0.2); - ndx = limiterTestUIP[i]->getParamIndex("Freq"); - float sineFreq = - 110.0 * pow(1.5, double(i)) - * (mNumClients > 1 ? 1.25 : 1.0); // Maj 7 chord for stereo in & out - limiterTestUIP[i]->setParamValue(ndx, sineFreq); -#endif - } - inited = true; - } + void init(int samplingRate) override; int getNumInputs() override { return (mNumChannels); } int getNumOutputs() override { return (mNumChannels); } void compute(int nframes, float** inputs, float** outputs) override; @@ -190,11 +135,11 @@ class Limiter : public ProcessPlugin float fs; int mNumChannels; int mNumClients; - std::vector limiterP; - std::vector limiterUIP; + std::vector limiterP; + std::vector limiterUIP; #ifdef SINE_TEST - std::vector limiterTestP; - std::vector limiterTestUIP; + std::vector limiterTestP; + std::vector limiterTestUIP; #endif double warningAmp; uint32_t warnCount; diff --git a/src/Meter.cpp b/src/Meter.cpp index 031e284..a14f7ce 100644 --- a/src/Meter.cpp +++ b/src/Meter.cpp @@ -38,7 +38,37 @@ #include "Meter.h" +#include + #include "jacktrip_types.h" +#include "meterdsp.h" + +//******************************************************************************* +Meter::Meter(int numchans, bool verboseFlag) : mNumChannels(numchans) +{ + setVerbose(verboseFlag); + for (int i = 0; i < mNumChannels; i++) { + meterP.push_back(new meterdsp); + } +} + +//******************************************************************************* +Meter::~Meter() +{ + for (int i = 0; i < mNumChannels; i++) { + delete static_cast(meterP[i]); + } + meterP.clear(); + if (mValues) { + delete mValues; + } + if (mOutValues) { + delete mOutValues; + } + if (mBuffer) { + delete mBuffer; + } +} //******************************************************************************* void Meter::init(int samplingRate) @@ -51,7 +81,7 @@ void Meter::init(int samplingRate) fs = float(fSamplingFreq); for (int i = 0; i < mNumChannels; i++) { - meterP[i]->init(fs); + static_cast(meterP[i])->init(fs); } /* Set meter values to the default floor */ @@ -69,14 +99,8 @@ void Meter::init(int samplingRate) } //******************************************************************************* -void Meter::compute(int nframes, float** inputs, float** /*_*/) +void Meter::compute(int nframes, float** inputs, float** outputs) { - // Note that the second parameter is unused. This is because all of the ProcessPlugins - // require the same function signature for the compute() function and is normally used - // for the faust plugin output. However, this plugin is not supposed to modify the - // signal itself like the other plugins (e.g. Limiter) do, so we don't want to write - // to this buffer. We just need to report the VU meter output - if (not inited) { std::cerr << "*** Meter " << this << ": init never called! Doing it now.\n"; if (fSamplingFreq <= 0) { @@ -87,6 +111,15 @@ void Meter::compute(int nframes, float** inputs, float** /*_*/) init(fSamplingFreq); } + // Will measure inputs by default unless mMeasureOutputBuffer = true, + // in which case the plugin will measure from the outputs. This is useful when + // measuring with a monitor, since AudioInterface.cpp expects monitoring plugins + // to behave differently than input and output chain plugins + float** measuringBuffer = inputs; + if (mIsMonitoringMeter) { + measuringBuffer = outputs; + } + if (mBufSize < nframes) { if (mBuffer) { delete mBuffer; @@ -97,7 +130,8 @@ void Meter::compute(int nframes, float** inputs, float** /*_*/) for (int i = 0; i < mNumChannels; i++) { /* Run the signal through Faust */ - meterP[i]->compute(nframes, &inputs[i], &mBuffer); + static_cast(meterP[i])->compute(nframes, &measuringBuffer[i], + &mBuffer); /* Use the existing value of mValues[i] as the threshold - this will be reset to the default floor of -80dB diff --git a/src/Meter.h b/src/Meter.h index fa4269b..c4eb5f1 100644 --- a/src/Meter.h +++ b/src/Meter.h @@ -41,11 +41,9 @@ #include #include -#include #include #include "ProcessPlugin.h" -#include "meterdsp.h" /** \brief The Meter class measures the live audio loudness level */ @@ -55,33 +53,10 @@ class Meter : public ProcessPlugin public: /// \brief The class constructor sets the number of channels to measure - Meter(int numchans, bool verboseFlag = false) : mNumChannels(numchans) - { - setVerbose(verboseFlag); - for (int i = 0; i < mNumChannels; i++) { - meterP.push_back(new meterdsp); - // meterUIP.push_back(new APIUI); - // meterP[i]->buildUserInterface(meterUIP[i]); - } - } + Meter(int numchans, bool verboseFlag = false); /// \brief The class destructor - virtual ~Meter() - { - for (int i = 0; i < mNumChannels; i++) { - delete meterP[i]; - } - meterP.clear(); - if (mValues) { - delete mValues; - } - if (mOutValues) { - delete mOutValues; - } - if (mBuffer) { - delete mBuffer; - } - } + virtual ~Meter(); void init(int samplingRate) override; int getNumInputs() override { return (mNumChannels); } @@ -91,13 +66,21 @@ class Meter : public ProcessPlugin void updateNumChannels(int nChansIn, int nChansOut) override; + void setIsMonitoringMeter(bool isMonitoringMeter) + { + mIsMonitoringMeter = isMonitoringMeter; + }; + bool getIsMonitoringMeter() { return mIsMonitoringMeter; }; + private: void setupValues(); float fs; + bool mIsMonitoringMeter = false; + int mNumChannels; float threshold = -80.0; - std::vector meterP; + std::vector meterP; bool hasProcessedAudio = false; QTimer mTimer; diff --git a/src/Monitor.cpp b/src/Monitor.cpp new file mode 100644 index 0000000..d963183 --- /dev/null +++ b/src/Monitor.cpp @@ -0,0 +1,153 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Julius Smith, 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 Monitor.cpp + * \author Dominick Hing + * \date May 2023 + * \license MIT + */ + +#include "Monitor.h" + +#include + +#include "jacktrip_types.h" +#include "monitordsp.h" + +//******************************************************************************* +Monitor::Monitor(int numchans, bool verboseFlag) : mNumChannels(numchans) +{ + setVerbose(verboseFlag); + for (int i = 0; i < mNumChannels; i++) { + monitordsp* dsp_ptr = new monitordsp; + APIUI* ui_ptr = new APIUI; + monitorP.push_back(dsp_ptr); + monitorUIP.push_back(ui_ptr); // #included in monitordsp.h + dsp_ptr->buildUserInterface(ui_ptr); + } +} + +//******************************************************************************* +Monitor::~Monitor() +{ + for (int i = 0; i < mNumChannels; i++) { + delete static_cast(monitorP[i]); + delete static_cast(monitorUIP[i]); + } + monitorP.clear(); + monitorUIP.clear(); +} + +//******************************************************************************* +void Monitor::init(int samplingRate) +{ + ProcessPlugin::init(samplingRate); + if (samplingRate != fSamplingFreq) { + std::cerr << "Sampling rate not set by superclass!\n"; + std::exit(1); + } + fs = float(fSamplingFreq); + + for (int i = 0; i < mNumChannels; i++) { + static_cast(monitorP[i]) + ->init(fs); // compression filter parameters depend on sampling rate + APIUI* ui_ptr = static_cast(monitorUIP[i]); + int ndx = ui_ptr->getParamIndex("Volume"); + ui_ptr->setParamValue(ndx, mVolMultiplier); + ndx = ui_ptr->getParamIndex("Mute"); + ui_ptr->setParamValue(ndx, 0); + } + inited = true; +} + +//******************************************************************************* +void Monitor::compute(int nframes, float** inputs, float** outputs) +{ + if (not inited) { + std::cerr << "*** Monitor " << this << ": init never called! Doing it now.\n"; + if (fSamplingFreq <= 0) { + fSamplingFreq = 48000; + std::cout << "Monitor " << this + << ": *** HAD TO GUESS the sampling rate (chose 48000 Hz) ***\n"; + } + init(fSamplingFreq); + } + + if (mBufSize < nframes) { + if (mOutBufferInput) { + delete mOutBufferInput; + } + + if (mInBufferInput) { + delete mInBufferInput; + } + + mBufSize = nframes; + mOutBufferInput = new float[mBufSize]; + mInBufferInput = new float[mBufSize]; + } + + std::vector buffer{mInBufferInput, mOutBufferInput}; + for (int i = 0; i < mNumChannels; i++) { + // copy inputs and outputs into a separate memory buffer + memcpy(mInBufferInput, inputs[i], nframes * sizeof(float)); + memcpy(mOutBufferInput, outputs[i], nframes * sizeof(float)); + + /* Run the signal through Faust */ + static_cast(monitorP[i]) + ->compute(nframes, buffer.data(), &outputs[i]); + } +} + +//******************************************************************************* +void Monitor::updateNumChannels(int nChansIn, int nChansOut) +{ + if (outgoingPluginToNetwork) { + mNumChannels = nChansIn; + } else { + mNumChannels = nChansOut; + } +} + +//******************************************************************************* +void Monitor::volumeUpdated(float multiplier) +{ + // maps 0.0-1.0 to a -40 dB to 0 dB range + // update if volumedsp.dsp and/or volumedsp.h + // change their ranges + mVolMultiplier = 40.0 * multiplier - 40.0; + for (int i = 0; i < mNumChannels; i++) { + APIUI* ui_ptr = static_cast(monitorUIP[i]); + int ndx = ui_ptr->getParamIndex("Volume"); + ui_ptr->setParamValue(ndx, mVolMultiplier); + } +} \ No newline at end of file diff --git a/src/Monitor.h b/src/Monitor.h new file mode 100644 index 0000000..69cdb4c --- /dev/null +++ b/src/Monitor.h @@ -0,0 +1,84 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Julius Smith, 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 Monitor.h + * \author Dominick Hing + * \date May 2023 + * \license MIT + */ + +#ifndef __MONITOR_H__ +#define __MONITOR_H__ + +#include +#include + +#include "ProcessPlugin.h" + +/** \brief The Monitor plugin adds a portion of the input signal multiplied by a + * constant factor to the output signal + */ +class Monitor : public ProcessPlugin +{ + Q_OBJECT; + + public: + /// \brief The class constructor sets the number of channels to use + Monitor(int numchans, bool verboseFlag = false); + + /// \brief The class destructor + virtual ~Monitor(); + + void init(int samplingRate) override; + int getNumInputs() override { return (mNumChannels); } + int getNumOutputs() override { return (mNumChannels); } + void compute(int nframes, float** inputs, float** outputs) override; + const char* getName() const override { return "Monitor"; }; + + void updateNumChannels(int nChansIn, int nChansOut) override; + + public slots: + void volumeUpdated(float multiplier); + + private: + std::vector monitorP; + std::vector monitorUIP; + float fs; + int mNumChannels; + float mVolMultiplier = 0.0; + + float* mOutBufferInput = nullptr; + float* mInBufferInput = nullptr; + int mBufSize = 0; +}; + +#endif \ No newline at end of file diff --git a/src/Regulator.cpp b/src/Regulator.cpp index 5b88bcd..72c52b1 100644 --- a/src/Regulator.cpp +++ b/src/Regulator.cpp @@ -105,14 +105,18 @@ constexpr double AutoInitValFactor = constexpr int WindowDivisor = 8; // for faster auto tracking constexpr int MaxFPP = 1024; // tested up to this FPP //******************************************************************************* -Regulator::Regulator(int rcvChannels, int bit_res, int FPP, int qLen, int bqLen) +Regulator::Regulator(int rcvChannels, int bit_res, int FPP, int qLen, + bool use_worker_thread, int bqLen) : RingBuffer(0, 0) , mNumChannels(rcvChannels) , mAudioBitRes(bit_res) , mFPP(FPP) , mMsecTolerance((double)qLen) // handle non-auto mode, expects positive qLen , mAuto(false) + , mUseWorkerThread(use_worker_thread) , m_b_BroadcastQueueLength(bqLen) + , mRegulatorThreadPtr(NULL) + , mRegulatorWorkerPtr(NULL) { // catch settings that are compute bound using long HIST // hub client rcvChannels is set from client's settings parameters @@ -155,11 +159,13 @@ Regulator::Regulator(int rcvChannels, int bit_res, int FPP, int qLen, int bqLen) if (gVerboseFlag) cout << "mHist = " << mHist << " at " << mFPP << "\n"; - mBytes = mFPP * mNumChannels * mBitResolutionMode; - mPullQueue = new int8_t[mBytes * 2]; - mXfrBuffer = mPullQueue; - mPacketCnt = 0; // burg initialization - mNextPacket.store(mPullQueue + mBytes, std::memory_order_release); + mBytes = mFPP * mNumChannels * mBitResolutionMode; + mPullQueue = new int8_t[mBytes * 2]; + mXfrBuffer = mPullQueue; + mPacketCnt = 0; // burg initialization + mLastPacket = nullptr; + mNextPacket.store(mLastPacket, std::memory_order_release); + mWorkerUnderruns = 0; mFadeUp.resize(mFPP, 0.0); mFadeDown.resize(mFPP, 0.0); for (int i = 0; i < mFPP; i++) { @@ -211,6 +217,14 @@ Regulator::Regulator(int rcvChannels, int bit_res, int FPP, int qLen, int bqLen) << m_b_BroadcastQueueLength; // have not implemented the mJackTrip->queueLengthChanged functionality } + if (mUseWorkerThread) { + mRegulatorThreadPtr = new QThread(); + mRegulatorThreadPtr->setObjectName("RegulatorThread"); + RegulatorWorker* workerPtr = new RegulatorWorker(this); + workerPtr->moveToThread(mRegulatorThreadPtr); + mRegulatorThreadPtr->start(); + mRegulatorWorkerPtr = workerPtr; + } } void Regulator::changeGlobal(double x) @@ -255,6 +269,14 @@ Regulator::~Regulator() }; if (m_b_BroadcastQueueLength) delete m_b_BroadcastRingBuffer; + if (mRegulatorWorkerPtr != nullptr) + delete mRegulatorWorkerPtr; + if (mRegulatorThreadPtr != nullptr) { + // Stop the Regulator thread + mRegulatorThreadPtr->quit(); + mRegulatorThreadPtr->wait(); + delete mRegulatorThreadPtr; + } } void Regulator::setFPPratio() @@ -350,6 +372,8 @@ void Regulator::shimFPP(const int8_t* buf, int len, int seq_num) //******************************************************************************* void Regulator::pushPacket(const int8_t* buf, int seq_num) { + if (m_b_BroadcastQueueLength) + m_b_BroadcastRingBuffer->insertSlotNonBlocking(buf, mBytes, 0, seq_num); QMutexLocker locker(&mMutex); seq_num %= mModSeqNum; // if (seq_num==0) return; // impose regular loss @@ -360,6 +384,13 @@ void Regulator::pushPacket(const int8_t* buf, int seq_num) memcpy(mSlots[mLastSeqNumIn % mNumSlots], buf, mBytes); }; +//******************************************************************************* +void Regulator::pullPacket(int8_t* buf) +{ // only for mBufferStrategy == 4, not using workerThread + pullPacket(); + memcpy(buf, mXfrBuffer, mBytes); +} + //******************************************************************************* void Regulator::pullPacket() { @@ -776,6 +807,29 @@ void StdDev::tick() } } +void Regulator::readSlotNonBlocking(int8_t* ptrToReadSlot) +{ + if (mUseWorkerThread) { + // use separate worker thread for PLC + const void* ptrToPacket = mNextPacket.load(std::memory_order_acquire); + if (ptrToPacket == mLastPacket) { + mWorkerUnderruns++; + ::memset(ptrToReadSlot, 0, mBytes); + if (ptrToPacket == nullptr) { + // first time run + mRegulatorWorkerPtr->startPullingNextPacket(); + } + } else { + ::memcpy(ptrToReadSlot, ptrToPacket, mBytes); + mLastPacket = ptrToPacket; + mRegulatorWorkerPtr->startPullingNextPacket(); + } + } else { + // use jack callback thread to perform PLC + pullPacket(ptrToReadSlot); + } +} + //******************************************************************************* bool Regulator::getStats(RingBuffer::IOStat* stat, bool reset) { @@ -792,6 +846,11 @@ bool Regulator::getStats(RingBuffer::IOStat* stat, bool reset) mBroadcastSkew = 0; } + if (mUseWorkerThread) { + cout << "PLC worker underruns: " << mWorkerUnderruns << endl; + mWorkerUnderruns = 0; + } + // hijack of struct IOStat { stat->underruns = pullStat->lastPlcUnderruns + pullStat->lastPlcOverruns; #define FLOATFACTOR 1000.0 diff --git a/src/Regulator.h b/src/Regulator.h index 1cad71b..bea0eb7 100644 --- a/src/Regulator.h +++ b/src/Regulator.h @@ -51,6 +51,10 @@ #include "AudioInterface.h" #include "RingBuffer.h" +#include "jacktrip_globals.h" + +// forward declaration +class RegulatorWorker; class BurgAlgorithm { @@ -123,7 +127,8 @@ class StdDev class Regulator : public RingBuffer { public: - Regulator(int rcvChannels, int bit_res, int FPP, int qLen, int bqLen); + Regulator(int rcvChannels, int bit_res, int FPP, int qLen, bool use_worker_thread, + int bqLen); virtual ~Regulator(); void shimFPP(const int8_t* buf, int len, int seq_num); @@ -133,23 +138,18 @@ class Regulator : public RingBuffer // if (!mJackTrip->writeAudioBuffer(src, host_buf_size, last_seq_num)) // instead of // if (!mJackTrip->writeAudioBuffer(src, host_buf_size, gap_size)) - virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen, - int seq_num) + virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot, int len, + [[maybe_unused]] int lostLen, int seq_num) { shimFPP(ptrToSlot, len, seq_num); - if (m_b_BroadcastQueueLength) - m_b_BroadcastRingBuffer->insertSlotNonBlocking(ptrToSlot, len, lostLen, - seq_num); return (true); } - // called by RegulatorWorker after each audio callback, to prep next packet + void pullPacket(int8_t* buf); + void pullPacket(); - virtual void readSlotNonBlocking(int8_t* ptrToReadSlot) - { - ::memcpy(ptrToReadSlot, mNextPacket.load(std::memory_order_acquire), mBytes); - } + virtual void readSlotNonBlocking(int8_t* ptrToReadSlot); virtual void readBroadcastSlot(int8_t* ptrToReadSlot) { @@ -177,6 +177,8 @@ class Regulator : public RingBuffer int mBytesPeerPacket; int8_t* mPullQueue; int8_t* mXfrBuffer; + const void* mLastPacket; + int mWorkerUnderruns; std::atomic mNextPacket; int8_t* mAssembledPacket; int mPacketCnt; @@ -208,6 +210,7 @@ class Regulator : public RingBuffer double mAutoHeadroom; double mFPPdurMsec; double mPeerFPPdurMsec; + bool mUseWorkerThread; void changeGlobal(double); void changeGlobal_2(int); void changeGlobal_3(int); @@ -216,6 +219,11 @@ class Regulator : public RingBuffer /// Pointer for the Broadcast RingBuffer RingBuffer* m_b_BroadcastRingBuffer; int m_b_BroadcastQueueLength; + + /// thread used to pull packets from Regulator (if mBufferStrategy==3) + QThread* mRegulatorThreadPtr; + /// worker used to pull packets from Regulator (if mBufferStrategy==3) + RegulatorWorker* mRegulatorWorkerPtr; }; class RegulatorWorker : public QObject @@ -223,8 +231,20 @@ class RegulatorWorker : public QObject Q_OBJECT; public: - RegulatorWorker(Regulator* rPtr) : mRegulatorPtr(rPtr) {} + RegulatorWorker(Regulator* rPtr) : mRegulatorPtr(rPtr) + { + QObject::connect(this, &RegulatorWorker::startup, this, + &RegulatorWorker::setRealtimePriority, Qt::QueuedConnection); + QObject::connect(this, &RegulatorWorker::signalPullPacket, this, + &RegulatorWorker::pullPacket, Qt::QueuedConnection); + emit startup(); + } virtual ~RegulatorWorker() {} + void startPullingNextPacket() { emit signalPullPacket(); } + + signals: + void signalPullPacket(); + void startup(); public slots: void pullPacket() @@ -233,6 +253,7 @@ class RegulatorWorker : public QObject mRegulatorPtr->pullPacket(); } } + void setRealtimePriority() { setRealtimeProcessPriority(); } private: Regulator* mRegulatorPtr; diff --git a/src/Reverb.cpp b/src/Reverb.cpp index 3f1774d..ec80a67 100644 --- a/src/Reverb.cpp +++ b/src/Reverb.cpp @@ -37,7 +37,102 @@ #include "Reverb.h" +#include "freeverbdsp.h" // stereo in and out +#include "freeverbmonodsp.h" // mono in and out (there is no mono to stereo case in jacktrip as yet) #include "jacktrip_types.h" +#include "zitarevdsp.h" // stereo in and out +#include "zitarevmonodsp.h" // mono in and out + +//******************************************************************************* +Reverb::Reverb(int numInChans, int numOutChans, float reverbLevel, bool verboseFlag) + : mNumInChannels(numInChans), mNumOutChannels(numOutChans), mReverbLevel(reverbLevel) +{ + setVerbose(verboseFlag); + if (mNumInChannels < 1) { + std::cerr << "*** Reverb.h: must have at least one input audio channels\n"; + mNumInChannels = 1; + } + if (mNumInChannels > 2) { + std::cerr << "*** Reverb.h: limiting number of audio output channels to 2\n"; + mNumInChannels = 2; + } +#if 0 +std::cout << "Reverb: constructed for " + << mNumInChannels << " input channels and " + << mNumOutChannels << " output channels with reverb level = " + << mReverbLevel << "\n"; +#endif + + if (mReverbLevel <= 1.0) { // freeverb: + freeverbStereoP = new freeverbdsp; // stereo input and output + freeverbMonoP = new freeverbmonodsp; // mono input, stereo output + freeverbStereoUIP = new APIUI; // #included in *dsp.h + freeverbMonoUIP = new APIUI; + static_cast(freeverbStereoP) + ->buildUserInterface(static_cast(freeverbStereoUIP)); + static_cast(freeverbMonoP) + ->buildUserInterface(static_cast(freeverbMonoUIP)); + // std::cout << "Using freeverb\n"; + } else { + zitarevStereoP = new zitarevdsp; // stereo input and output + zitarevMonoP = new zitarevmonodsp; // mono input, stereo output + zitarevStereoUIP = new APIUI; + zitarevMonoUIP = new APIUI; + static_cast(zitarevStereoP) + ->buildUserInterface(static_cast(zitarevStereoUIP)); + static_cast(zitarevMonoP) + ->buildUserInterface(static_cast(zitarevMonoUIP)); + // std::cout << "Using zitarev\n"; + } +} + +//******************************************************************************* +Reverb::~Reverb() +{ + if (mReverbLevel <= 1.0) { // freeverb: + delete static_cast(freeverbStereoP); + delete static_cast(freeverbMonoP); + delete static_cast(freeverbStereoUIP); + delete static_cast(freeverbMonoUIP); + } else { + delete static_cast(zitarevStereoP); + delete static_cast(zitarevMonoP); + delete static_cast(zitarevStereoUIP); + delete static_cast(zitarevMonoUIP); + } +} + +//******************************************************************************* +void Reverb::init(int samplingRate) +{ + ProcessPlugin::init(samplingRate); + // std::cout << "Reverb: init(" << samplingRate << ")\n"; + if (samplingRate != fSamplingFreq) { + std::cerr << "Sampling rate not set by superclass!\n"; + std::exit(1); + } + fs = float(fSamplingFreq); + if (mReverbLevel <= 1.0) { // freeverb: + static_cast(freeverbStereoP) + ->init(fs); // compression filter parameters depend on sampling rate + static_cast(freeverbMonoP) + ->init(fs); // compression filter parameters depend on sampling rate + int ndx = static_cast(freeverbStereoUIP)->getParamIndex("Wet"); + static_cast(freeverbStereoUIP)->setParamValue(ndx, mReverbLevel); + static_cast(freeverbMonoUIP)->setParamValue(ndx, mReverbLevel); + } else { // zitarev: + static_cast(zitarevStereoP) + ->init(fs); // compression filter parameters depend on sampling rate + static_cast(zitarevMonoP) + ->init(fs); // compression filter parameters depend on sampling rate + int ndx = static_cast(zitarevStereoUIP)->getParamIndex("Wet"); + float zitaLevel = + mReverbLevel - 1.0f; // range within zitarev is 0 to 1 (our version only) + static_cast(zitarevStereoUIP)->setParamValue(ndx, zitaLevel); + static_cast(zitarevMonoUIP)->setParamValue(ndx, zitaLevel); + } + inited = true; +} //******************************************************************************* void Reverb::compute(int nframes, float** inputs, float** outputs) @@ -53,17 +148,18 @@ void Reverb::compute(int nframes, float** inputs, float** outputs) } if (mReverbLevel <= 1.0) { if (mNumInChannels == 1) { - freeverbMonoP->compute(nframes, inputs, outputs); + static_cast(freeverbMonoP) + ->compute(nframes, inputs, outputs); } else { assert(mNumInChannels == 2); - freeverbStereoP->compute(nframes, inputs, outputs); + static_cast(freeverbStereoP)->compute(nframes, inputs, outputs); } } else { if (mNumInChannels == 1) { - zitarevMonoP->compute(nframes, inputs, outputs); + static_cast(zitarevMonoP)->compute(nframes, inputs, outputs); } else { assert(mNumInChannels == 2); - zitarevStereoP->compute(nframes, inputs, outputs); + static_cast(zitarevStereoP)->compute(nframes, inputs, outputs); } } } diff --git a/src/Reverb.h b/src/Reverb.h index 8970e08..17e0089 100644 --- a/src/Reverb.h +++ b/src/Reverb.h @@ -43,13 +43,7 @@ #include -//#define SINE_TEST - #include "ProcessPlugin.h" -#include "freeverbdsp.h" // stereo in and out -#include "freeverbmonodsp.h" // mono in and out (there is no mono to stereo case in jacktrip as yet) -#include "zitarevdsp.h" // stereo in and out -#include "zitarevmonodsp.h" // mono in and out /** \brief A Reverb is an echo-based delay effect, * providing a virtual acoustic listening space. @@ -59,92 +53,12 @@ class Reverb : public ProcessPlugin public: /// \brief The class constructor sets the number of channels to limit Reverb(int numInChans, int numOutChans, float reverbLevel = 1.0, - bool verboseFlag = false) // xtor - : mNumInChannels(numInChans) - , mNumOutChannels(numOutChans) - , mReverbLevel(reverbLevel) - { - setVerbose(verboseFlag); - if (mNumInChannels < 1) { - std::cerr << "*** Reverb.h: must have at least one input audio channels\n"; - mNumInChannels = 1; - } - if (mNumInChannels > 2) { - std::cerr << "*** Reverb.h: limiting number of audio output channels to 2\n"; - mNumInChannels = 2; - } -#if 0 - std::cout << "Reverb: constructed for " - << mNumInChannels << " input channels and " - << mNumOutChannels << " output channels with reverb level = " - << mReverbLevel << "\n"; -#endif - - if (mReverbLevel <= 1.0) { // freeverb: - freeverbStereoP = new freeverbdsp; // stereo input and output - freeverbMonoP = new freeverbmonodsp; // mono input, stereo output - freeverbStereoUIP = new APIUI; // #included in *dsp.h - freeverbMonoUIP = new APIUI; - freeverbStereoP->buildUserInterface(freeverbStereoUIP); - freeverbMonoP->buildUserInterface(freeverbMonoUIP); - // std::cout << "Using freeverb\n"; - } else { - zitarevStereoP = new zitarevdsp; // stereo input and output - zitarevMonoP = new zitarevmonodsp; // mono input, stereo output - zitarevStereoUIP = new APIUI; - zitarevMonoUIP = new APIUI; - zitarevStereoP->buildUserInterface(zitarevStereoUIP); - zitarevMonoP->buildUserInterface(zitarevMonoUIP); - // std::cout << "Using zitarev\n"; - } - } + bool verboseFlag = false); /// \brief The class destructor - virtual ~Reverb() - { - if (mReverbLevel <= 1.0) { // freeverb: - delete freeverbStereoP; - delete freeverbMonoP; - delete freeverbStereoUIP; - delete freeverbMonoUIP; - } else { - delete zitarevStereoP; - delete zitarevMonoP; - delete zitarevStereoUIP; - delete zitarevMonoUIP; - } - } - - void init(int samplingRate) override - { - ProcessPlugin::init(samplingRate); - // std::cout << "Reverb: init(" << samplingRate << ")\n"; - if (samplingRate != fSamplingFreq) { - std::cerr << "Sampling rate not set by superclass!\n"; - std::exit(1); - } - fs = float(fSamplingFreq); - if (mReverbLevel <= 1.0) { // freeverb: - freeverbStereoP->init( - fs); // compression filter parameters depend on sampling rate - freeverbMonoP->init( - fs); // compression filter parameters depend on sampling rate - int ndx = freeverbStereoUIP->getParamIndex("Wet"); - freeverbStereoUIP->setParamValue(ndx, mReverbLevel); - freeverbMonoUIP->setParamValue(ndx, mReverbLevel); - } else { // zitarev: - zitarevStereoP->init( - fs); // compression filter parameters depend on sampling rate - zitarevMonoP->init( - fs); // compression filter parameters depend on sampling rate - int ndx = zitarevStereoUIP->getParamIndex("Wet"); - float zitaLevel = - mReverbLevel - 1.0f; // range within zitarev is 0 to 1 (our version only) - zitarevStereoUIP->setParamValue(ndx, zitaLevel); - zitarevMonoUIP->setParamValue(ndx, zitaLevel); - } - inited = true; - } + virtual ~Reverb(); + + void init(int samplingRate) override; int getNumInputs() override { return (mNumInChannels); } int getNumOutputs() override { return (mNumOutChannels); } void compute(int nframes, float** inputs, float** outputs) override; @@ -157,15 +71,15 @@ class Reverb : public ProcessPlugin float mReverbLevel; - freeverbdsp* freeverbStereoP; - freeverbmonodsp* freeverbMonoP; - APIUI* freeverbStereoUIP; - APIUI* freeverbMonoUIP; + void* freeverbStereoP; + void* freeverbMonoP; + void* freeverbStereoUIP; + void* freeverbMonoUIP; - zitarevdsp* zitarevStereoP; - zitarevmonodsp* zitarevMonoP; - APIUI* zitarevStereoUIP; - APIUI* zitarevMonoUIP; + void* zitarevStereoP; + void* zitarevMonoP; + void* zitarevStereoUIP; + void* zitarevMonoUIP; }; #endif diff --git a/src/RtAudioInterface.cpp b/src/RtAudioInterface.cpp index 9be6c60..b4d7a24 100644 --- a/src/RtAudioInterface.cpp +++ b/src/RtAudioInterface.cpp @@ -238,6 +238,9 @@ void RtAudioInterface::setup(bool verbose) if (api_in != "asio") { AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_LATENCY); AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NONE); + } else if (api_in == "asio" && index_in != index_out) { + AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE); + AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_SAME_ASIO); } #endif } else { @@ -515,10 +518,10 @@ void RtAudioInterface::getDeviceList(QStringList* list, QStringList* categories, categories->append(QStringLiteral("Low-Latency (ASIO)")); break; case RtAudio::WINDOWS_WASAPI: - categories->append(QStringLiteral("All Devices (Non-ASIO)")); + categories->append(QStringLiteral("High-Latency (Non-ASIO)")); break; case RtAudio::WINDOWS_DS: - categories->append(QStringLiteral("All Devices (Non-ASIO)")); + categories->append(QStringLiteral("High-Latency (Non-ASIO)")); break; default: categories->append(QStringLiteral("")); @@ -595,10 +598,10 @@ void RtAudioInterface::getDeviceList(QStringList* list, QStringList* categories, categories->append("Low-Latency (ASIO)"); break; case RtAudio::WINDOWS_WASAPI: - categories->append("All Devices (Non-ASIO)"); + categories->append("High-Latency (Non-ASIO)"); break; case RtAudio::WINDOWS_DS: - categories->append("All Devices (Non-ASIO)"); + categories->append("High-Latency (Non-ASIO)"); break; default: categories->append(""); diff --git a/src/Settings.cpp b/src/Settings.cpp index 21d119b..fa0e167 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -536,7 +536,7 @@ void Settings::parseInput(int argc, char** argv) break; case OPT_BUFSTRATEGY: // Buf strategy mBufferStrategy = atoi(optarg); - if (-1 > mBufferStrategy || 3 < mBufferStrategy) { + if (-1 > mBufferStrategy || 4 < mBufferStrategy) { std::cerr << "Unsupported buffer strategy " << optarg << endl; printUsage(); std::exit(1); @@ -862,7 +862,7 @@ void Settings::printUsage() cout << " -D, --nojackportsconnect Don't connect default audio ports " "in jack" << endl; - cout << " --bufstrategy # (0, 1, 2) Use alternative jitter buffer" + cout << " --bufstrategy # (0, 1, 2, 3, 4) Use alternative jitter buffer" << endl; cout << " --broadcast Duplicate receive ports with the specified broadcast_queue length. " "Broadcast outputs have higher latency but less packet loss.\n"; diff --git a/src/StereoToMono.cpp b/src/StereoToMono.cpp index bd78e21..5018bdc 100644 --- a/src/StereoToMono.cpp +++ b/src/StereoToMono.cpp @@ -37,9 +37,23 @@ #include "StereoToMono.h" -#include +#include #include "jacktrip_types.h" +#include "stereotomonodsp.h" + +//******************************************************************************* +StereoToMono::StereoToMono(bool verboseFlag) +{ + setVerbose(verboseFlag); + stereoToMonoP = new stereotomonodsp; +} + +//******************************************************************************* +StereoToMono::~StereoToMono() +{ + delete static_cast(stereoToMonoP); +} //******************************************************************************* void StereoToMono::init(int samplingRate) @@ -51,7 +65,7 @@ void StereoToMono::init(int samplingRate) } fs = float(fSamplingFreq); - stereoToMonoP->init(fs); + static_cast(stereoToMonoP)->init(fs); inited = true; } @@ -69,5 +83,5 @@ void StereoToMono::compute(int nframes, float** inputs, float** outputs) } init(fSamplingFreq); } - stereoToMonoP->compute(nframes, inputs, outputs); + static_cast(stereoToMonoP)->compute(nframes, inputs, outputs); } \ No newline at end of file diff --git a/src/StereoToMono.h b/src/StereoToMono.h index 3fdbcaf..04c7437 100644 --- a/src/StereoToMono.h +++ b/src/StereoToMono.h @@ -40,12 +40,8 @@ #define __STEREOTOMONO_H__ #include -#include -#include -#include #include "ProcessPlugin.h" -#include "stereotomonodsp.h" /** \brief The Meter class measures the live audio loudness level */ @@ -55,14 +51,10 @@ class StereoToMono : public ProcessPlugin public: /// \brief The class constructor sets the number of channels to measure - StereoToMono(bool verboseFlag = false) - { - setVerbose(verboseFlag); - stereoToMonoP = new stereotomonodsp; - } + StereoToMono(bool verboseFlag = false); /// \brief The class destructor - virtual ~StereoToMono() { delete stereoToMonoP; } + virtual ~StereoToMono(); void init(int samplingRate) override; int getNumInputs() override { return 2; } @@ -72,9 +64,7 @@ class StereoToMono : public ProcessPlugin private: float fs; - // int mNumChannels; - stereotomonodsp* stereoToMonoP; - // bool hasProcessedAudio = false; + void* stereoToMonoP; }; #endif \ No newline at end of file diff --git a/src/Tone.cpp b/src/Tone.cpp index 6b7e5a1..0f39eea 100644 --- a/src/Tone.cpp +++ b/src/Tone.cpp @@ -38,7 +38,34 @@ #include "Tone.h" +#include + #include "jacktrip_types.h" +#include "tonedsp.h" + +//******************************************************************************* +Tone::Tone(int numchans, bool verboseFlag) : mNumChannels(numchans) +{ + setVerbose(verboseFlag); + for (int i = 0; i < mNumChannels; i++) { + tonedsp* dsp_ptr = new tonedsp; + APIUI* ui_ptr = new APIUI; + toneP.push_back(dsp_ptr); + toneUIP.push_back(ui_ptr); + dsp_ptr->buildUserInterface(ui_ptr); + } +} + +//******************************************************************************* +Tone::~Tone() +{ + for (int i = 0; i < mNumChannels; i++) { + delete static_cast(toneP[i]); + delete static_cast(toneUIP[i]); + } + toneP.clear(); + toneUIP.clear(); +} //******************************************************************************* void Tone::init(int samplingRate) @@ -51,7 +78,8 @@ void Tone::init(int samplingRate) fs = float(fSamplingFreq); for (int i = 0; i < mNumChannels; i++) { - toneP[i]->init(fs); // compression filter parameters depend on sampling rate + static_cast(toneP[i])->init( + fs); // compression filter parameters depend on sampling rate } inited = true; } @@ -71,7 +99,7 @@ void Tone::compute(int nframes, float** inputs, float** outputs) for (int i = 0; i < mNumChannels; i++) { /* Run the signal through Faust */ - toneP[i]->compute(nframes, &inputs[i], &outputs[i]); + static_cast(toneP[i])->compute(nframes, &inputs[i], &outputs[i]); } } @@ -88,8 +116,9 @@ void Tone::updateNumChannels(int nChansIn, int nChansOut) void Tone::triggerPlayback() { for (int i = 0; i < mNumChannels; i++) { - int ndx = toneUIP[i]->getParamIndex("gate"); - int v = toneUIP[i]->getParamValue(ndx); - toneUIP[i]->setParamValue(ndx, v + 1); + APIUI* ui_ptr = static_cast(toneUIP[i]); + int ndx = ui_ptr->getParamIndex("gate"); + int v = ui_ptr->getParamValue(ndx); + ui_ptr->setParamValue(ndx, v + 1); } } diff --git a/src/Tone.h b/src/Tone.h index 36ce568..86418d2 100644 --- a/src/Tone.h +++ b/src/Tone.h @@ -40,11 +40,9 @@ #define __TONE_H__ #include -#include #include #include "ProcessPlugin.h" -#include "tonedsp.h" /** \brief The Tone plugin plays some arbitrary sample audio */ @@ -54,26 +52,10 @@ class Tone : public ProcessPlugin public: /// \brief The class constructor sets the number of channels to measure - Tone(int numchans, bool verboseFlag = false) : mNumChannels(numchans) - { - setVerbose(verboseFlag); - for (int i = 0; i < mNumChannels; i++) { - toneP.push_back(new tonedsp); - toneUIP.push_back(new APIUI); // #included in tonedsp.h - toneP[i]->buildUserInterface(toneUIP[i]); - } - } + Tone(int numchans, bool verboseFlag = false); /// \brief The class destructor - virtual ~Tone() - { - for (int i = 0; i < mNumChannels; i++) { - delete toneP[i]; - delete toneUIP[i]; - } - toneP.clear(); - toneUIP.clear(); - } + virtual ~Tone(); void init(int samplingRate) override; int getNumInputs() override { return (mNumChannels); } @@ -87,8 +69,8 @@ class Tone : public ProcessPlugin void triggerPlayback(); private: - std::vector toneP; - std::vector toneUIP; + std::vector toneP; + std::vector toneUIP; float fs; int mNumChannels; }; diff --git a/src/Volume.cpp b/src/Volume.cpp index d290f3e..e01f3f7 100644 --- a/src/Volume.cpp +++ b/src/Volume.cpp @@ -38,9 +38,34 @@ #include "Volume.h" -#include +#include #include "jacktrip_types.h" +#include "volumedsp.h" + +//******************************************************************************* +Volume::Volume(int numchans, bool verboseFlag) : mNumChannels(numchans) +{ + setVerbose(verboseFlag); + for (int i = 0; i < mNumChannels; i++) { + volumedsp* dsp_ptr = new volumedsp; + APIUI* ui_ptr = new APIUI; + volumeP.push_back(dsp_ptr); + volumeUIP.push_back(ui_ptr); // #included in volumedsp.h + dsp_ptr->buildUserInterface(ui_ptr); + } +} + +//******************************************************************************* +Volume::~Volume() +{ + for (int i = 0; i < mNumChannels; i++) { + delete static_cast(volumeP[i]); + delete static_cast(volumeUIP[i]); + } + volumeP.clear(); + volumeUIP.clear(); +} //******************************************************************************* void Volume::init(int samplingRate) @@ -53,11 +78,13 @@ void Volume::init(int samplingRate) fs = float(fSamplingFreq); for (int i = 0; i < mNumChannels; i++) { - volumeP[i]->init(fs); // compression filter parameters depend on sampling rate - int ndx = volumeUIP[i]->getParamIndex("Volume"); - volumeUIP[i]->setParamValue(ndx, mVolMultiplier); - ndx = volumeUIP[i]->getParamIndex("Mute"); - volumeUIP[i]->setParamValue(ndx, isMuted ? 1 : 0); + static_cast(volumeP[i]) + ->init(fs); // compression filter parameters depend on sampling rate + APIUI* ui_ptr = static_cast(volumeUIP[i]); + int ndx = ui_ptr->getParamIndex("Volume"); + ui_ptr->setParamValue(ndx, mVolMultiplier); + ndx = ui_ptr->getParamIndex("Mute"); + ui_ptr->setParamValue(ndx, isMuted ? 1 : 0); } inited = true; } @@ -77,7 +104,7 @@ void Volume::compute(int nframes, float** inputs, float** outputs) for (int i = 0; i < mNumChannels; i++) { /* Run the signal through Faust */ - volumeP[i]->compute(nframes, &inputs[i], &outputs[i]); + static_cast(volumeP[i])->compute(nframes, &inputs[i], &outputs[i]); } } @@ -98,8 +125,9 @@ void Volume::volumeUpdated(float multiplier) // change their ranges mVolMultiplier = 40.0 * multiplier - 40.0; for (int i = 0; i < mNumChannels; i++) { - int ndx = volumeUIP[i]->getParamIndex("Volume"); - volumeUIP[i]->setParamValue(ndx, mVolMultiplier); + APIUI* ui_ptr = static_cast(volumeUIP[i]); + int ndx = ui_ptr->getParamIndex("Volume"); + ui_ptr->setParamValue(ndx, mVolMultiplier); } } @@ -107,7 +135,8 @@ void Volume::muteUpdated(bool muted) { isMuted = muted; for (int i = 0; i < mNumChannels; i++) { - int ndx = volumeUIP[i]->getParamIndex("Mute"); - volumeUIP[i]->setParamValue(ndx, isMuted ? 1 : 0); + APIUI* ui_ptr = static_cast(volumeUIP[i]); + int ndx = ui_ptr->getParamIndex("Mute"); + ui_ptr->setParamValue(ndx, isMuted ? 1 : 0); } } \ No newline at end of file diff --git a/src/Volume.h b/src/Volume.h index 59548bb..bdc2ffd 100644 --- a/src/Volume.h +++ b/src/Volume.h @@ -40,13 +40,9 @@ #define __VOLUME_H__ #include -#include -#include -#include #include #include "ProcessPlugin.h" -#include "volumedsp.h" /** \brief The Volume plugin adjusts the level of the signal via multiplication */ @@ -56,26 +52,10 @@ class Volume : public ProcessPlugin public: /// \brief The class constructor sets the number of channels to measure - Volume(int numchans, bool verboseFlag = false) : mNumChannels(numchans) - { - setVerbose(verboseFlag); - for (int i = 0; i < mNumChannels; i++) { - volumeP.push_back(new volumedsp); - volumeUIP.push_back(new APIUI); // #included in volumedsp.h - volumeP[i]->buildUserInterface(volumeUIP[i]); - } - } + Volume(int numchans, bool verboseFlag = false); /// \brief The class destructor - virtual ~Volume() - { - for (int i = 0; i < mNumChannels; i++) { - delete volumeP[i]; - delete volumeUIP[i]; - } - volumeP.clear(); - volumeUIP.clear(); - } + virtual ~Volume(); void init(int samplingRate) override; int getNumInputs() override { return (mNumChannels); } @@ -90,8 +70,8 @@ class Volume : public ProcessPlugin void muteUpdated(bool muted); private: - std::vector volumeP; - std::vector volumeUIP; + std::vector volumeP; + std::vector volumeUIP; float fs; int mNumChannels; float mVolMultiplier = 0.0; diff --git a/src/gui/AudioSettings.qml b/src/gui/AudioSettings.qml index aa902f1..37366ca 100644 --- a/src/gui/AudioSettings.qml +++ b/src/gui/AudioSettings.qml @@ -196,6 +196,14 @@ Rectangle { outputCombo.currentIndex = index outputCombo.popup.close() virtualstudio.outputDevice = modelData.text + if (modelData.category === "Low-Latency (ASIO)") { + let inputComboIdx = inputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text); + if (inputComboIdx !== null && inputComboIdx !== undefined) { + inputCombo.currentIndex = inputComboIdx; + virtualstudio.inputDevice = modelData.text + } + } + virtualstudio.restartAudio() virtualstudio.validateDevicesState() } } @@ -321,7 +329,7 @@ Rectangle { anchors.right: outputCombo.horizontalCenter anchors.rightMargin: 8 * virtualstudio.uiScale anchors.top: outputChannelsLabel.bottom - anchors.topMargin: 12 * virtualstudio.uiScale + anchors.topMargin: 4 * virtualstudio.uiScale model: outputChannelsComboModel currentIndex: (() => { let idx = outputChannelsComboModel.findIndex(elem => elem.baseChannel === virtualstudio.baseOutputChannel @@ -442,7 +450,7 @@ Rectangle { Text { anchors.centerIn: parent font { family: "Poppins"; pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale} - text: qsTr("Send audio to the studio (microphone, instrument, mixer, etc.)") + text: qsTr("Audio sent to the studio (microphone, instrument, mixer, etc.)") color: toolTipTextColour } } @@ -497,6 +505,14 @@ Rectangle { inputCombo.currentIndex = index inputCombo.popup.close() virtualstudio.inputDevice = modelData.text + if (modelData.category === "Low-Latency (ASIO)") { + let outputComboIdx = outputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text); + if (outputComboIdx !== null && outputComboIdx !== undefined) { + outputCombo.currentIndex = outputComboIdx; + virtualstudio.outputDevice = modelData.text + } + } + virtualstudio.restartAudio() virtualstudio.validateDevicesState() } } @@ -631,7 +647,7 @@ Rectangle { anchors.right: inputCombo.horizontalCenter anchors.rightMargin: 8 * virtualstudio.uiScale anchors.top: inputChannelsLabel.bottom - anchors.topMargin: 12 * virtualstudio.uiScale + anchors.topMargin: 4 * virtualstudio.uiScale model: inputChannelsComboModel currentIndex: (() => { let idx = inputChannelsComboModel.findIndex(elem => elem.baseChannel === virtualstudio.baseInputChannel @@ -689,7 +705,7 @@ Rectangle { anchors.right: inputCombo.right anchors.rightMargin: 8 * virtualstudio.uiScale anchors.top: inputMixModeLabel.bottom - anchors.topMargin: 12 * virtualstudio.uiScale + anchors.topMargin: 4 * virtualstudio.uiScale model: inputMixModeComboModel currentIndex: (() => { let idx = inputMixModeComboModel.findIndex(elem => elem.value === virtualstudio.inputMixMode); diff --git a/src/gui/Connected.qml b/src/gui/Connected.qml index c152818..166249c 100644 --- a/src/gui/Connected.qml +++ b/src/gui/Connected.qml @@ -15,6 +15,7 @@ Item { property int fontTiny: 8 property int bodyMargin: 60 + property int rightMargin: 16 property int bottomToolTipMargin: 8 property int rightToolTipMargin: 4 @@ -32,6 +33,11 @@ Item { property string browserButtonStroke: virtualstudio.darkMode ? "#80827D7D" : "#40979797" property string browserButtonHoverStroke: virtualstudio.darkMode ? "#7B7777" : "#BABCBC" property string browserButtonPressedStroke: virtualstudio.darkMode ? "#827D7D" : "#BABCBC" + property string saveButtonBackgroundColour: "#F2F3F3" + property string saveButtonPressedColour: "#E7E8E8" + property string saveButtonStroke: "#EAEBEB" + property string saveButtonPressedStroke: "#B0B5B5" + property string saveButtonText: "#DB0A0A" property string muteButtonMutedColor: "#FCB6B6" property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D" @@ -55,6 +61,34 @@ Item { property string meterYellow: "#F5BF4F" property string meterRed: "#F21B1B" + property bool isUsingRtAudio: virtualstudio.audioBackend == "RtAudio" + + function getCurrentInputDeviceIndex () { + if (virtualstudio.inputDevice === "") { + return inputComboModel.findIndex(elem => elem.type === "element"); + } + + let idx = inputComboModel.findIndex(elem => elem.type === "element" && elem.text === virtualstudio.inputDevice); + if (idx < 0) { + idx = inputComboModel.findIndex(elem => elem.type === "element"); + } + + return idx; + } + + function getCurrentOutputDeviceIndex() { + if (virtualstudio.outputDevice === "") { + return outputComboModel.findIndex(elem => elem.type === "element"); + } + + let idx = outputComboModel.findIndex(elem => elem.type === "element" && elem.text === virtualstudio.outputDevice); + if (idx < 0) { + idx = outputComboModel.findIndex(elem => elem.type === "element"); + } + + return idx; + } + function getNetworkStatsText (networkStats) { let minRtt = networkStats.minRtt; let maxRtt = networkStats.maxRtt; @@ -87,6 +121,16 @@ Item { return texts; } + Connections { + target: virtualstudio + function onInputDeviceChanged() { + inputCombo.currentIndex = getCurrentInputDeviceIndex(); + } + function onOutputDeviceChanged() { + outputCombo.currentIndex = getCurrentOutputDeviceIndex(); + } + } + Image { id: jtlogo x: parent.width - (49 * virtualstudio.uiScale); y: 16 * virtualstudio.uiScale @@ -119,10 +163,621 @@ Item { inviteKeyString: virtualstudio.currentStudio >= 0 ? serverModel[virtualstudio.currentStudio].inviteKey : "" } + Item { + id: deviceSettings + visible: showReadyScreen && isUsingRtAudio + x: bodyMargin * virtualstudio.uiScale; y: 192 * virtualstudio.uiScale + width: parent.width - (2 * x) + height: 384 * virtualstudio.uiScale + clip: true + + Button { + id: deviceSettingsButton + background: Rectangle { + radius: 6 * virtualstudio.uiScale + color: deviceSettingsButton.down ? browserButtonPressedColour : (deviceSettingsButton.hovered ? browserButtonHoverColour : browserButtonColour) + } + onClicked: popup.open() + anchors.right: parent.right + width: 144 * virtualstudio.uiScale; height: 24 * virtualstudio.uiScale + + Text { + text: "Change Device Settings" + font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale} + anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter } + color: textColour + } + } + + Popup { + id: popup + padding: 1 + width: parent.width + height: parent.height + anchors.centerIn: parent + modal: true + focus: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + onClosed: { + virtualstudio.applySettings() + } + + background: Rectangle { + anchors.fill: parent + color: "transparent" + radius: 6 * virtualstudio.uiScale + border.width: 1 + border.color: buttonStroke + clip: true + } + + contentItem: Rectangle { + width: parent.width + height: parent.height + color: backgroundColour + radius: 6 * virtualstudio.uiScale + + Item { + id: usingRtAudio + anchors.top: parent.top + anchors.topMargin: 24 * virtualstudio.uiScale + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.leftMargin: 24 * virtualstudio.uiScale + anchors.right: parent.right + + visible: isUsingRtAudio + + Rectangle { + id: leftSpacer + x: 0; y: 0 + width: 144 * virtualstudio.uiScale + height: 0 + color: "transparent" + } + + Text { + id: outputLabel + x: 0; y: 0 + text: "Output Device" + font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale } + color: textColour + } + + Image { + id: outputHelpIcon + anchors.left: outputLabel.right + anchors.bottom: outputLabel.top + anchors.bottomMargin: -8 * virtualstudio.uiScale + source: "help.svg" + sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale) + fillMode: Image.PreserveAspectFit + smooth: true + + property bool showToolTip: false + + Colorize { + anchors.fill: parent + source: parent + hue: 0 + saturation: 0 + lightness: virtualstudio.darkMode ? 0.8 : 0.2 + } + + MouseArea { + id: outputMouseArea + anchors.fill: parent + hoverEnabled: true + onEntered: outputHelpIcon.showToolTip = true + onExited: outputHelpIcon.showToolTip = false + } + + ToolTip { + visible: outputHelpIcon.showToolTip + contentItem: Rectangle { + color: toolTipBackgroundColour + radius: 3 + anchors.fill: parent + anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale + anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale + layer.enabled: true + border.width: 1 + border.color: buttonStroke + + Text { + anchors.centerIn: parent + font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale} + text: qsTr("How you'll hear the studio audio") + color: toolTipTextColour + } + } + background: Rectangle { + color: "transparent" + } + } + } + + Image { + id: headphonesIcon + anchors.left: outputLabel.left + anchors.top: outputLabel.bottom + anchors.topMargin: bottomToolTipMargin * virtualstudio.uiScale + source: "headphones.svg" + sourceSize: Qt.size(28 * virtualstudio.uiScale, 28 * virtualstudio.uiScale) + fillMode: Image.PreserveAspectFit + smooth: true + + Colorize { + anchors.fill: parent + source: parent + hue: 0 + saturation: 0 + lightness: virtualstudio.darkMode ? 1 : 0 + } + } + + ComboBox { + id: outputCombo + anchors.left: leftSpacer.right + anchors.verticalCenter: outputLabel.verticalCenter + anchors.rightMargin: rightMargin * virtualstudio.uiScale + width: parent.width - leftSpacer.width - rightMargin * virtualstudio.uiScale + enabled: virtualstudio.connectionState == "Connected" + model: outputComboModel + currentIndex: getCurrentOutputDeviceIndex() + delegate: ItemDelegate { + required property var modelData + required property int index + + leftPadding: 0 + + width: parent.width + contentItem: Text { + leftPadding: modelData.type === "element" && outputCombo.model.filter(it => it.type === "header").length > 0 ? 24 : 12 + text: modelData.text + font.bold: modelData.type === "header" + } + highlighted: outputCombo.highlightedIndex === index + MouseArea { + anchors.fill: parent + onClicked: { + if (modelData.type == "element") { + outputCombo.currentIndex = index + outputCombo.popup.close() + virtualstudio.outputDevice = modelData.text + if (modelData.category === "Low-Latency (ASIO)") { + let inputComboIdx = inputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text); + if (inputComboIdx !== null && inputComboIdx !== undefined) { + inputCombo.currentIndex = inputComboIdx; + virtualstudio.inputDevice = modelData.text + } + } + virtualstudio.validateDevicesState() + } + } + } + } + contentItem: Text { + leftPadding: 12 + font: outputCombo.font + horizontalAlignment: Text.AlignHLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + text: outputCombo.model[outputCombo.currentIndex].text ? outputCombo.model[outputCombo.currentIndex].text : "" + } + } + + Text { + id: outputChannelsLabel + anchors.left: outputCombo.left + anchors.right: outputCombo.horizontalCenter + anchors.top: outputCombo.bottom + anchors.topMargin: 12 * virtualstudio.uiScale + textFormat: Text.RichText + text: "Output Channel(s)" + font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale } + color: textColour + } + + ComboBox { + id: outputChannelsCombo + anchors.left: outputCombo.left + anchors.right: outputCombo.horizontalCenter + anchors.rightMargin: 8 * virtualstudio.uiScale + anchors.top: outputChannelsLabel.bottom + anchors.topMargin: 4 * virtualstudio.uiScale + enabled: virtualstudio.connectionState == "Connected" + model: outputChannelsComboModel + currentIndex: (() => { + let idx = outputChannelsComboModel.findIndex(elem => elem.baseChannel === virtualstudio.baseOutputChannel + && elem.numChannels === virtualstudio.numOutputChannels); + if (idx < 0) { + idx = 0; + } + return idx; + })() + delegate: ItemDelegate { + required property var modelData + required property int index + width: parent.width + contentItem: Text { + text: modelData.label + } + highlighted: outputChannelsCombo.highlightedIndex === index + MouseArea { + anchors.fill: parent + onClicked: { + outputChannelsCombo.currentIndex = index + outputChannelsCombo.popup.close() + virtualstudio.baseOutputChannel = modelData.baseChannel + virtualstudio.numOutputChannels = modelData.numChannels + virtualstudio.validateDevicesState() + } + } + } + contentItem: Text { + leftPadding: 12 + font: inputCombo.font + horizontalAlignment: Text.AlignHLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + text: outputChannelsCombo.model[outputChannelsCombo.currentIndex].label || "" + } + } + + Text { + id: inputLabel + anchors.left: outputLabel.left + anchors.top: outputChannelsCombo.bottom + anchors.topMargin: 32 * virtualstudio.uiScale + text: "Input Device" + font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale } + color: textColour + } + + Image { + id: inputHelpIcon + anchors.left: inputLabel.right + anchors.bottom: inputLabel.top + anchors.bottomMargin: -8 * virtualstudio.uiScale + source: "help.svg" + sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale) + fillMode: Image.PreserveAspectFit + smooth: true + + property bool showToolTip: false + + Colorize { + anchors.fill: parent + source: parent + hue: 0 + saturation: 0 + lightness: virtualstudio.darkMode ? 0.8 : 0.2 + } + + MouseArea { + id: inputMouseArea + anchors.fill: parent + hoverEnabled: true + onEntered: inputHelpIcon.showToolTip = true + onExited: inputHelpIcon.showToolTip = false + } + + ToolTip { + visible: inputHelpIcon.showToolTip + contentItem: Rectangle { + color: toolTipBackgroundColour + radius: 3 + anchors.fill: parent + anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale + anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale + layer.enabled: true + border.width: 1 + border.color: buttonStroke + + Text { + anchors.centerIn: parent + font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale} + text: qsTr("Audio sent to the studio (microphone, instrument, mixer, etc.)") + color: toolTipTextColour + } + } + background: Rectangle { + color: "transparent" + } + } + } + + Image { + id: microphoneIcon + anchors.left: inputLabel.left + anchors.top: inputLabel.bottom + anchors.topMargin: bottomToolTipMargin * virtualstudio.uiScale + source: "mic.svg" + sourceSize: Qt.size(32 * virtualstudio.uiScale, 32 * virtualstudio.uiScale) + fillMode: Image.PreserveAspectFit + smooth: true + + Colorize { + anchors.fill: parent + source: parent + hue: 0 + saturation: 0 + lightness: virtualstudio.darkMode ? 1 : 0 + } + } + + ComboBox { + id: inputCombo + model: inputComboModel + currentIndex: getCurrentInputDeviceIndex() + anchors.left: outputCombo.left + anchors.right: outputCombo.right + anchors.verticalCenter: inputLabel.verticalCenter + enabled: virtualstudio.connectionState == "Connected" + delegate: ItemDelegate { + required property var modelData + required property int index + + leftPadding: 0 + + width: parent.width + contentItem: Text { + leftPadding: modelData.type === "element" && inputCombo.model.filter(it => it.type === "header").length > 0 ? 24 : 12 + text: modelData.text + font.bold: modelData.type === "header" + } + highlighted: inputCombo.highlightedIndex === index + MouseArea { + anchors.fill: parent + onClicked: { + if (modelData.type == "element") { + inputCombo.currentIndex = index + inputCombo.popup.close() + virtualstudio.inputDevice = modelData.text + if (modelData.category === "Low-Latency (ASIO)") { + let outputComboIdx = outputCombo.model.findIndex(it => it.category === "Low-Latency (ASIO)" && it.text === modelData.text); + if (outputComboIdx !== null && outputComboIdx !== undefined) { + outputCombo.currentIndex = outputComboIdx; + virtualstudio.outputDevice = modelData.text + } + } + virtualstudio.validateDevicesState() + } + } + } + } + contentItem: Text { + leftPadding: 12 + font: inputCombo.font + horizontalAlignment: Text.AlignHLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + text: inputCombo.model[inputCombo.currentIndex].text ? inputCombo.model[inputCombo.currentIndex].text : "" + } + } + + Text { + id: inputChannelsLabel + anchors.left: inputCombo.left + anchors.right: inputCombo.horizontalCenter + anchors.top: inputCombo.bottom + anchors.topMargin: 12 * virtualstudio.uiScale + textFormat: Text.RichText + text: "Input Channel(s)" + font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale } + color: textColour + } + + ComboBox { + id: inputChannelsCombo + anchors.left: inputCombo.left + anchors.right: inputCombo.horizontalCenter + anchors.rightMargin: 8 * virtualstudio.uiScale + anchors.top: inputChannelsLabel.bottom + anchors.topMargin: 4 * virtualstudio.uiScale + enabled: virtualstudio.connectionState == "Connected" + model: inputChannelsComboModel + currentIndex: (() => { + let idx = inputChannelsComboModel.findIndex(elem => elem.baseChannel === virtualstudio.baseInputChannel + && elem.numChannels === virtualstudio.numInputChannels); + if (idx < 0) { + idx = 0; + } + return idx; + })() + delegate: ItemDelegate { + required property var modelData + required property int index + width: parent.width + contentItem: Text { + text: modelData.label + } + highlighted: inputChannelsCombo.highlightedIndex === index + MouseArea { + anchors.fill: parent + onClicked: { + inputChannelsCombo.currentIndex = index + inputChannelsCombo.popup.close() + virtualstudio.baseInputChannel = modelData.baseChannel + virtualstudio.numInputChannels = modelData.numChannels + virtualstudio.validateDevicesState() + } + } + } + contentItem: Text { + leftPadding: 12 + font: inputCombo.font + horizontalAlignment: Text.AlignHLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + text: inputChannelsCombo.model[inputChannelsCombo.currentIndex].label || "" + } + } + + Text { + id: inputMixModeLabel + anchors.left: inputCombo.horizontalCenter + anchors.right: inputCombo.right + anchors.rightMargin: 8 * virtualstudio.uiScale + anchors.top: inputCombo.bottom + anchors.topMargin: 12 * virtualstudio.uiScale + textFormat: Text.RichText + text: "Mono / Stereo" + font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale } + color: textColour + } + + ComboBox { + id: inputMixModeCombo + anchors.left: inputCombo.horizontalCenter + anchors.right: inputCombo.right + anchors.rightMargin: 8 * virtualstudio.uiScale + anchors.top: inputMixModeLabel.bottom + anchors.topMargin: 4 * virtualstudio.uiScale + enabled: virtualstudio.connectionState == "Connected" + model: inputMixModeComboModel + currentIndex: (() => { + let idx = inputMixModeComboModel.findIndex(elem => elem.value === virtualstudio.inputMixMode); + if (idx < 0) { + idx = 0; + } + return idx; + })() + delegate: ItemDelegate { + required property var modelData + required property int index + width: parent.width + contentItem: Text { + text: modelData.label + } + highlighted: inputMixModeCombo.highlightedIndex === index + MouseArea { + anchors.fill: parent + onClicked: { + inputMixModeCombo.currentIndex = index + inputMixModeCombo.popup.close() + virtualstudio.inputMixMode = inputMixModeComboModel[index].value + virtualstudio.validateDevicesState() + } + } + } + contentItem: Text { + leftPadding: 12 + font: inputCombo.font + horizontalAlignment: Text.AlignHLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + text: inputMixModeCombo.model[inputMixModeCombo.currentIndex].label || "" + } + } + + Text { + id: inputChannelHelpMessage + anchors.left: inputChannelsCombo.left + anchors.leftMargin: 2 * virtualstudio.uiScale + anchors.right: inputChannelsCombo.right + anchors.top: inputChannelsCombo.bottom + anchors.topMargin: 8 * virtualstudio.uiScale + textFormat: Text.RichText + wrapMode: Text.WordWrap + text: "Choose up to 2 channels" + font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale } + color: textColour + } + + Text { + id: inputMixModeHelpMessage + anchors.left: inputMixModeCombo.left + anchors.leftMargin: 2 * virtualstudio.uiScale + anchors.right: inputMixModeCombo.right + anchors.top: inputMixModeCombo.bottom + anchors.topMargin: 8 * virtualstudio.uiScale + textFormat: Text.RichText + wrapMode: Text.WordWrap + text: (() => { + if (virtualstudio.inputMixMode === 2) { + return "Treat the channels as Left and Right signals, coming through each speaker separately."; + } else if (virtualstudio.inputMixMode === 3) { + return "Combine the channels into one central channel coming through both speakers."; + } else if (virtualstudio.inputMixMode === 1) { + return "Send a single channel of audio"; + } else { + return ""; + } + })() + font { family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale } + color: textColour + } + + Button { + id: closePopupButton + anchors.right: parent.right + anchors.rightMargin: rightMargin * virtualstudio.uiScale + anchors.bottomMargin: rightMargin * virtualstudio.uiScale + anchors.bottom: parent.bottom + width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale + onClicked: popup.close() + + background: Rectangle { + radius: 6 * virtualstudio.uiScale + color: closePopupButton.down ? browserButtonPressedColour : (closePopupButton.hovered ? browserButtonHoverColour : browserButtonColour) + border.width: 1 + border.color: closePopupButton.down ? browserButtonPressedStroke : (closePopupButton.hovered ? browserButtonHoverStroke : browserButtonStroke) + } + + Text { + text: "Close" + font.family: "Poppins" + font.pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale + font.weight: Font.Bold + color: !Boolean(virtualstudio.devicesError) && virtualstudio.backendAvailable ? saveButtonText : disabledButtonText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + + Button { + id: refreshButton + text: "Refresh Devices" + anchors.right: closePopupButton.left + anchors.rightMargin: 8 * virtualstudio.uiScale + anchors.bottomMargin: rightMargin * virtualstudio.uiScale + anchors.bottom: parent.bottom + width: 150 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale + enabled: virtualstudio.connectionState == "Connected" + + palette.buttonText: textColour + background: Rectangle { + radius: 6 * virtualstudio.uiScale + color: refreshButton.down ? browserButtonPressedColour : (refreshButton.hovered ? browserButtonHoverColour : browserButtonColour) + border.width: 1 + border.color: refreshButton.down ? browserButtonPressedStroke : (refreshButton.hovered ? browserButtonHoverStroke : browserButtonStroke) + } + + icon { + source: "refresh.svg"; + color: textColour; + } + display: AbstractButton.TextBesideIcon + onClicked: { + virtualstudio.validateDevicesState(); + } + + font { + family: "Poppins" + pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale + } + } + } + } + } + } + Item { id: inputDevice visible: showReadyScreen - x: bodyMargin * virtualstudio.uiScale; y: 230 * virtualstudio.uiScale + x: bodyMargin * virtualstudio.uiScale; y: 240 * virtualstudio.uiScale width: Math.min(parent.width / 2, 320 * virtualstudio.uiScale) - x height: 100 * virtualstudio.uiScale clip: true @@ -174,8 +829,9 @@ Item { visible: showReadyScreen x: bodyMargin * virtualstudio.uiScale; y: 320 * virtualstudio.uiScale width: Math.min(parent.width / 2, 320 * virtualstudio.uiScale) - x - height: 100 * virtualstudio.uiScale + height: 124 * virtualstudio.uiScale clip: true + anchors.top: inputDevice.bottom Image { id: headphones @@ -222,7 +878,7 @@ Item { Item { id: inputControls visible: showReadyScreen - x: inputDevice.x + inputDevice.width; y: 230 * virtualstudio.uiScale + x: inputDevice.x + inputDevice.width; y: 240 * virtualstudio.uiScale width: parent.width - inputDevice.width - 2 * bodyMargin * virtualstudio.uiScale Meter { @@ -244,8 +900,9 @@ Item { padding: 0 y: inputDeviceMeters.y + 36 * virtualstudio.uiScale anchors.left: inputMute.right + anchors.right: inputStudioText.left anchors.leftMargin: 8 * virtualstudio.uiScale - anchors.right: inputDeviceMeters.right + anchors.rightMargin: 16 * virtualstudio.uiScale opacity: virtualstudio.inputMuted ? 0.3 : 1 background: Rectangle { @@ -334,6 +991,73 @@ Item { } } } + + Text { + id: inputStudioText + width: 40 * virtualstudio.uiScale + height: 24 + horizontalAlignment: Text.AlignRight + anchors.right: inputDeviceMeters.right + anchors.verticalCenter: inputSlider.verticalCenter + topPadding: 4 * virtualstudio.uiScale + rightPadding: 4 * virtualstudio.uiScale + text: "Send" + font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale; bold: true } + color: textColour + } + + Image { + id: inputStudioHelpIcon + anchors.left: inputStudioText.right + anchors.verticalCenter: inputStudioText.verticalCenter + anchors.bottomMargin: -8 * virtualstudio.uiScale + source: "help.svg" + sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale) + fillMode: Image.PreserveAspectFit + smooth: true + + property bool showToolTip: false + + Colorize { + anchors.fill: parent + source: parent + hue: 0 + saturation: 0 + lightness: virtualstudio.darkMode ? 0.8 : 0.2 + } + + MouseArea { + id: inputStudioMouseArea + anchors.fill: parent + hoverEnabled: true + onEntered: inputStudioHelpIcon.showToolTip = true + onExited: inputStudioHelpIcon.showToolTip = false + } + + ToolTip { + visible: inputStudioHelpIcon.showToolTip + contentItem: Rectangle { + color: toolTipBackgroundColour + radius: 3 + anchors.fill: parent + anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale + anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale + layer.enabled: true + border.width: 1 + border.color: buttonStroke + + Text { + anchors.centerIn: parent + font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale} + text: qsTr("How loudly other participants hear you") + color: toolTipTextColour + } + } + background: Rectangle { + color: "transparent" + } + } + } } Item { @@ -341,6 +1065,7 @@ Item { visible: showReadyScreen x: outputDevice.x + outputDevice.width; y: 320 * virtualstudio.uiScale width: parent.width - inputDevice.width - 2 * bodyMargin * virtualstudio.uiScale + anchors.top: inputDevice.bottom Meter { id: outputDeviceMeters @@ -360,7 +1085,8 @@ Item { padding: 0 y: outputDeviceMeters.y + 36 * virtualstudio.uiScale anchors.left: outputDeviceMeters.left - anchors.right: outputDeviceMeters.right + anchors.right: outputStudioText.left + anchors.rightMargin: 16 * virtualstudio.uiScale background: Rectangle { x: outputSlider.leftPadding @@ -390,14 +1116,190 @@ Item { border.color: buttonStroke } } + + Slider { + id: monitorSlider + from: 0.0 + value: virtualstudio ? virtualstudio.monitorVolume : 0.5 + onMoved: { virtualstudio.monitorVolume = value } + to: 1.0 + padding: 0 + y: outputSlider.y + 36 * virtualstudio.uiScale + anchors.left: outputDeviceMeters.left + anchors.right: outputMonText.left + anchors.rightMargin: 16 * virtualstudio.uiScale + + background: Rectangle { + x: monitorSlider.leftPadding + y: monitorSlider.topPadding + monitorSlider.availableHeight / 2 - height / 2 + implicitWidth: parent.width + implicitHeight: 6 + width: monitorSlider.availableWidth + height: implicitHeight + radius: 4 + color: sliderTrackColour + + Rectangle { + width: monitorSlider.visualPosition * parent.width + height: parent.height + color: sliderActiveTrackColour + radius: 4 + } + } + + handle: Rectangle { + x: monitorSlider.leftPadding + monitorSlider.visualPosition * (monitorSlider.availableWidth - width) + y: monitorSlider.topPadding + monitorSlider.availableHeight / 2 - height / 2 + implicitWidth: 26 * virtualstudio.uiScale + implicitHeight: 26 * virtualstudio.uiScale + radius: 13 * virtualstudio.uiScale + color: monitorSlider.pressed ? sliderPressedColour : sliderColour + border.color: buttonStroke + } + } + + Text { + id: outputStudioText + width: 40 * virtualstudio.uiScale + height: 24 + horizontalAlignment: Text.AlignRight + anchors.right: outputDeviceMeters.right + anchors.verticalCenter: outputSlider.verticalCenter + topPadding: 4 * virtualstudio.uiScale + rightPadding: 4 * virtualstudio.uiScale + text: "Studio" + font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale; bold: true } + color: textColour + } + + Image { + id: outputStudioHelpIcon + anchors.left: outputStudioText.right + anchors.verticalCenter: outputStudioText.verticalCenter + anchors.bottomMargin: -8 * virtualstudio.uiScale + source: "help.svg" + sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale) + fillMode: Image.PreserveAspectFit + smooth: true + + property bool showToolTip: false + + Colorize { + anchors.fill: parent + source: parent + hue: 0 + saturation: 0 + lightness: virtualstudio.darkMode ? 0.8 : 0.2 + } + + MouseArea { + id: outputStudioMouseArea + anchors.fill: parent + hoverEnabled: true + onEntered: outputStudioHelpIcon.showToolTip = true + onExited: outputStudioHelpIcon.showToolTip = false + } + + ToolTip { + visible: outputStudioHelpIcon.showToolTip + contentItem: Rectangle { + color: toolTipBackgroundColour + radius: 3 + anchors.fill: parent + anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale + anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale + layer.enabled: true + border.width: 1 + border.color: buttonStroke + + Text { + anchors.centerIn: parent + font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale} + text: qsTr("How loudly you hear other participants") + color: toolTipTextColour + } + } + background: Rectangle { + color: "transparent" + } + } + } + + Text { + id: outputMonText + width: 40 * virtualstudio.uiScale + height: 24 + horizontalAlignment: Text.AlignRight + anchors.right: outputDeviceMeters.right + anchors.verticalCenter: monitorSlider.verticalCenter + topPadding: 4 * virtualstudio.uiScale + rightPadding: 4 * virtualstudio.uiScale + text: "Monitor" + font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale; bold: true } + color: textColour + } + + Image { + id: outputMonHelpIcon + anchors.left: outputMonText.right + anchors.verticalCenter: outputMonText.verticalCenter + anchors.bottomMargin: -8 * virtualstudio.uiScale + source: "help.svg" + sourceSize: Qt.size(12 * virtualstudio.uiScale, 12 * virtualstudio.uiScale) + fillMode: Image.PreserveAspectFit + smooth: true + + property bool showToolTip: false + + Colorize { + anchors.fill: parent + source: parent + hue: 0 + saturation: 0 + lightness: virtualstudio.darkMode ? 0.8 : 0.2 + } + + MouseArea { + id: outputMonMouseArea + anchors.fill: parent + hoverEnabled: true + onEntered: outputMonHelpIcon.showToolTip = true + onExited: outputMonHelpIcon.showToolTip = false + } + + ToolTip { + visible: outputMonHelpIcon.showToolTip + contentItem: Rectangle { + color: toolTipBackgroundColour + radius: 3 + anchors.fill: parent + anchors.bottomMargin: bottomToolTipMargin * virtualstudio.uiScale + anchors.rightMargin: rightToolTipMargin * virtualstudio.uiScale + layer.enabled: true + border.width: 1 + border.color: buttonStroke + + Text { + anchors.centerIn: parent + font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale} + text: qsTr("How loudly you hear yourself") + color: toolTipTextColour + } + } + background: Rectangle { + color: "transparent" + } + } + } } Item { id: networkStatsHeader visible: showReadyScreen - x: bodyMargin * virtualstudio.uiScale; y: 410 * virtualstudio.uiScale + x: bodyMargin * virtualstudio.uiScale; y: 450 * virtualstudio.uiScale width: Math.min(parent.width / 2, 320 * virtualstudio.uiScale) - x height: 128 * virtualstudio.uiScale + anchors.top: outputDevice.bottom Image { id: network @@ -430,9 +1332,10 @@ Item { Item { id: networkStatsText visible: showReadyScreen - x: networkStatsHeader.x + networkStatsHeader.width; y: 410 * virtualstudio.uiScale + x: networkStatsHeader.x + networkStatsHeader.width; y: 450 * virtualstudio.uiScale width: parent.width - networkStatsHeader.width - 2 * bodyMargin * virtualstudio.uiScale height: 72 * virtualstudio.uiScale + anchors.top: outputDevice.bottom Text { id: netstat0 @@ -566,6 +1469,26 @@ Item { text: "You will be automatically connected to the studio when it is ready." wrapMode: Text.WordWrap } + + Text { + id: connectedErrorMessage1 + x: 0 + width: parent.width + color: warningTextColour + anchors.top: waitingText1.bottom + anchors.topMargin: 16 * virtualstudio.uiScale + anchors.bottomMargin: 16 * virtualstudio.uiScale + visible: parent.isAdmin && Boolean(virtualstudio.connectedErrorMsg) + textFormat: Text.RichText + text: virtualstudio.connectedErrorMsg == "one-studio-limit-reached" + ? `Your current plan allows you to use 1 studio at a time. Contact us to use multiple studios at a time.` + : "" + onLinkActivated: link => { + virtualstudio.openLink(link) + } + font {family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale } + wrapMode: Text.WordWrap + } } Item { diff --git a/src/gui/Login.qml b/src/gui/Login.qml index c4b2b25..1151626 100644 --- a/src/gui/Login.qml +++ b/src/gui/Login.qml @@ -11,6 +11,7 @@ Item { } property bool failTextVisible: false + property bool showBackButton: true property string backgroundColour: virtualstudio.darkMode ? "#272525" : "#FAFBFB" property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D" @@ -89,9 +90,9 @@ Item { border.color: loginButton.down ? buttonPressedStroke : (loginButton.hovered ? buttonHoverStroke : buttonStroke) layer.enabled: !loginButton.down } - onClicked: { failTextVisible = false; virtualstudio.login() } + onClicked: { virtualstudio.showFirstRun = false; failTextVisible = false; virtualstudio.login() } anchors.horizontalCenter: parent.horizontalCenter - y: 321 * virtualstudio.uiScale + y: showBackButton ? 321 * virtualstudio.uiScale : 371 * virtualstudio.uiScale width: 263 * virtualstudio.uiScale; height: 64 * virtualstudio.uiScale Text { text: "Sign In" @@ -107,6 +108,7 @@ Item { Button { id: backButton + visible: showBackButton background: Rectangle { radius: 6 * virtualstudio.uiScale color: backButton.down ? buttonPressedColour : (backButton.hovered ? buttonHoverColour : buttonColour) @@ -126,6 +128,29 @@ Item { anchors.verticalCenter: parent.verticalCenter color: backButton.down ? buttonTextPressed : (backButton.hovered ? buttonTextHover : buttonTextColour) } - visible: true + } + + + Button { + id: classicModeButton + visible: !showBackButton && virtualstudio.showFirstRun && virtualstudio.vsFtux + background: Rectangle { + radius: 6 * virtualstudio.uiScale + color: classicModeButton.down ? buttonPressedColour : (classicModeButton.hovered ? buttonHoverColour : backgroundColour) + border.width: 0 + layer.enabled: !classicModeButton.down + } + onClicked: { virtualstudio.windowState = "login"; virtualstudio.toStandard(); } + anchors.horizontalCenter: parent.horizontalCenter + y: 600 * virtualstudio.uiScale + width: 160 * virtualstudio.uiScale; height: 32 * virtualstudio.uiScale + Text { + text: "Use Classic Mode" + font.family: "Poppins" + font.pixelSize: 9 * virtualstudio.fontScale * virtualstudio.uiScale + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: classicModeButton.down ? buttonTextPressed : (classicModeButton.hovered ? buttonTextHover : textColour) + } } } diff --git a/src/gui/Setup.qml b/src/gui/Setup.qml index 99c1ca5..2400622 100644 --- a/src/gui/Setup.qml +++ b/src/gui/Setup.qml @@ -30,7 +30,6 @@ Item { property string sliderPressedColour: virtualstudio.darkMode ? "#ACAFAF" : "#DEE0E0" property string sliderTrackColour: virtualstudio.darkMode ? "#5B5858" : "light gray" property string sliderActiveTrackColour: virtualstudio.darkMode ? "light gray" : "black" - property string saveButtonShadow: "#80A1A1A1" property string saveButtonBackgroundColour: "#F2F3F3" property string saveButtonPressedColour: "#E7E8E8" property string saveButtonStroke: "#EAEBEB" diff --git a/src/gui/qjacktrip.qrc b/src/gui/qjacktrip.qrc index 34117a0..0a76a76 100644 --- a/src/gui/qjacktrip.qrc +++ b/src/gui/qjacktrip.qrc @@ -6,6 +6,7 @@ vs.qml + vsftux.qml FirstLaunch.qml Login.qml Studio.qml diff --git a/src/gui/qjacktrip.ui b/src/gui/qjacktrip.ui index b562774..5880c94 100644 --- a/src/gui/qjacktrip.ui +++ b/src/gui/qjacktrip.ui @@ -1408,7 +1408,12 @@ for better quality at the expense of latency. - 3 (experimental) + 3 (experimental - in own thread) + + + + + 4 (experimental - in callback) diff --git a/src/gui/virtualstudio.cpp b/src/gui/virtualstudio.cpp index fb168ef..7189f4c 100644 --- a/src/gui/virtualstudio.cpp +++ b/src/gui/virtualstudio.cpp @@ -212,7 +212,7 @@ VirtualStudio::VirtualStudio(bool firstRun, QObject* parent) QVariantList() << QVariant(QJsonValue(outputChannelsComboElement))))); #endif - m_bufferStrategy = settings.value(QStringLiteral("BufferStrategy"), 0).toInt(); + m_bufferStrategy = settings.value(QStringLiteral("BufferStrategy"), 2).toInt(); settings.endGroup(); #ifdef USE_WEAK_JACK @@ -275,7 +275,11 @@ VirtualStudio::VirtualStudio(bool firstRun, QObject* parent) QStringLiteral("backendComboModel"), QVariant::fromValue(QStringList() << QStringLiteral("JACK") << QStringLiteral("RtAudio"))); +#ifdef VS_FTUX + m_view.setSource(QUrl(QStringLiteral("qrc:/vs/vsftux.qml"))); +#else m_view.setSource(QUrl(QStringLiteral("qrc:/vs/vs.qml"))); +#endif // VS_FTUX m_view.setMinimumSize(QSize(594, 519)); // m_view.setMaximumSize(QSize(696, 577)); m_view.setResizeMode(QQuickView::SizeRootObjectToView); @@ -417,7 +421,6 @@ void VirtualStudio::setInputDevice([[maybe_unused]] const QString& device) #ifdef RT_AUDIO m_inputDevice = device; emit inputDeviceChanged(m_inputDevice, false); - emit inputDeviceSelected(m_inputDevice); #endif } @@ -490,7 +493,6 @@ void VirtualStudio::setOutputDevice([[maybe_unused]] const QString& device) #ifdef RT_AUDIO m_outputDevice = device; emit outputDeviceChanged(m_outputDevice, false); - emit outputDeviceSelected(m_outputDevice); #endif } @@ -594,6 +596,17 @@ QString VirtualStudio::devicesErrorHelpUrl() return m_devicesErrorHelpUrl; } +QString VirtualStudio::connectedErrorMsg() +{ + return m_connectedErrorMsg; +} + +void VirtualStudio::setConnectedErrorMsg(const QString& msg) +{ + m_connectedErrorMsg = msg; + emit connectedErrorMsgChanged(); +} + float VirtualStudio::inputVolume() { return m_inMultiplier; @@ -604,6 +617,11 @@ float VirtualStudio::outputVolume() return m_outMultiplier; } +float VirtualStudio::monitorVolume() +{ + return m_monMultiplier; +} + bool VirtualStudio::inputMuted() { return m_inMuted; @@ -614,6 +632,11 @@ bool VirtualStudio::outputMuted() return m_outMuted; } +bool VirtualStudio::monitorMuted() +{ + return m_monMuted; +} + bool VirtualStudio::audioActivated() { return m_audioActivated; @@ -654,6 +677,16 @@ void VirtualStudio::setOutputVolume(float multiplier) emit updatedOutputVolume(multiplier); } +void VirtualStudio::setMonitorVolume(float multiplier) +{ + m_monMultiplier = multiplier; + QSettings settings; + settings.beginGroup(QStringLiteral("Audio")); + settings.setValue(QStringLiteral("MonMultiplier"), m_monMultiplier); + settings.endGroup(); + emit updatedMonitorVolume(multiplier); +} + void VirtualStudio::setInputMuted(bool muted) { m_inMuted = muted; @@ -674,6 +707,16 @@ void VirtualStudio::setOutputMuted(bool muted) emit updatedOutputMuted(muted); } +void VirtualStudio::setMonitorMuted(bool muted) +{ + m_monMuted = muted; + QSettings settings; + settings.beginGroup(QStringLiteral("Audio")); + settings.setValue(QStringLiteral("MonMuted"), m_monMuted ? 1 : 0); + settings.endGroup(); + emit updatedMonitorMuted(muted); +} + int VirtualStudio::bufferSize() { #ifdef RT_AUDIO @@ -834,6 +877,11 @@ void VirtualStudio::setApiHost(QString host) emit apiHostChanged(); } +bool VirtualStudio::vsFtux() +{ + return m_vsFtux; +} + bool VirtualStudio::showWarnings() { return m_showWarnings; @@ -1089,6 +1137,20 @@ void VirtualStudio::refreshDevices() setAudioReady(false); } + refreshRtAudioDevices(); + validateDevicesState(); + if (!m_vsAudioInterface.isNull()) { + restartAudio(); + } +#endif +} + +void VirtualStudio::refreshRtAudioDevices() +{ + if (!m_useRtAudio) { + return; + } +#ifdef RT_AUDIO RtAudioInterface::getDeviceList(&m_inputDeviceList, &m_inputDeviceCategories, &m_inputDeviceChannels, true); RtAudioInterface::getDeviceList(&m_outputDeviceList, &m_outputDeviceCategories, @@ -1102,10 +1164,6 @@ void VirtualStudio::refreshDevices() inputComboModel); m_view.engine()->rootContext()->setContextProperty(QStringLiteral("outputComboModel"), outputComboModel); - validateDevicesState(); - if (!m_vsAudioInterface.isNull()) { - restartAudio(); - } #endif } @@ -1113,6 +1171,9 @@ void VirtualStudio::validateDevicesState() { validateInputDevicesState(); validateOutputDevicesState(); + if (m_useRtAudio && m_connectionState == "Connected") { + triggerReconnect(); + } } void VirtualStudio::validateInputDevicesState() @@ -1130,8 +1191,8 @@ void VirtualStudio::validateInputDevicesState() if (m_inputDevice == QStringLiteral("") || m_inputDeviceList.indexOf(m_inputDevice) == -1) { m_inputDevice = m_inputDeviceList[0]; - emit inputDeviceChanged(m_inputDevice, false); } + emit inputDeviceChanged(m_inputDevice, false); // Given the currently selected input device, reset the available input channel // options @@ -1281,8 +1342,8 @@ void VirtualStudio::validateOutputDevicesState() if (m_outputDevice == QStringLiteral("") || m_outputDeviceList.indexOf(m_outputDevice) == -1) { m_outputDevice = m_outputDeviceList[0]; - emit outputDeviceChanged(m_outputDevice, false); } + emit outputDeviceChanged(m_outputDevice, false); // Given the currently selected output device, reset the available output channel // options @@ -1462,6 +1523,10 @@ void VirtualStudio::connectToStudio(int studioIndex) } else { completeConnection(); } + + if (m_device != nullptr) { + m_device->setReconnect(false); + } } void VirtualStudio::completeConnection() @@ -1479,29 +1544,34 @@ void VirtualStudio::completeConnection() m_connectionState = QStringLiteral("Preparing audio..."); emit connectionStateChanged(); try { - std::string input = ""; - std::string output = ""; - int buffer_size = 0; + std::string input = ""; + std::string output = ""; + int buffer_size = 0; + int inputMixMode = -1; + int baseInputChannel = 0; + int numInputChannels = 2; + int baseOutputChannel = 0; + int numOutputChannels = 2; #ifdef RT_AUDIO if (m_useRtAudio) { - input = m_inputDevice.toStdString(); - output = m_outputDevice.toStdString(); - buffer_size = m_bufferSize; + input = m_inputDevice.toStdString(); + output = m_outputDevice.toStdString(); + buffer_size = m_bufferSize; + inputMixMode = m_inputMixMode; + baseInputChannel = m_baseInputChannel; + numInputChannels = m_numInputChannels; + baseOutputChannel = m_baseOutputChannel; + numOutputChannels = m_numOutputChannels; } - int inputMixMode = m_inputMixMode; -#else - int inputMixMode = -1; -#endif - JackTrip* jackTrip = m_device->initJackTrip( - m_useRtAudio, input, output, -#ifdef RT_AUDIO - m_baseInputChannel, m_numInputChannels, m_baseOutputChannel, - m_numOutputChannels, -#else - 0, 2, 0, 2, // default to 2 channels for input and 2 channels for output - // starting at channel 0 #endif - inputMixMode, buffer_size, m_bufferStrategy, studioInfo); + int bufferStrategy = m_bufferStrategy; + if (bufferStrategy == 2) { + bufferStrategy = 3; + } + JackTrip* jackTrip = + m_device->initJackTrip(m_useRtAudio, input, output, baseInputChannel, + numInputChannels, baseOutputChannel, numOutputChannels, + inputMixMode, buffer_size, bufferStrategy, studioInfo); if (jackTrip == 0) { processError("Could not bind port"); return; @@ -1533,27 +1603,41 @@ void VirtualStudio::completeConnection() connect(this, &VirtualStudio::updatedInputMuted, m_inputVolumePlugin, &Volume::muteUpdated); - // Setup output meter - m_outputMeter = new Meter(jackTrip->getNumOutputChannels()); - jackTrip->appendProcessPluginFromNetwork(m_outputMeter); - connect(m_outputMeter, &Meter::onComputedVolumeMeasurements, this, - &VirtualStudio::updatedOutputVuMeasurements); - // Setup input meter m_inputMeter = new Meter(jackTrip->getNumInputChannels()); jackTrip->appendProcessPluginToNetwork(m_inputMeter); connect(m_inputMeter, &Meter::onComputedVolumeMeasurements, this, &VirtualStudio::updatedInputVuMeasurements); + // Setup monitor + // Note: Constructor determines how many internal monitor buffers to allocate + m_monitor = new Monitor( + std::max(jackTrip->getNumInputChannels(), jackTrip->getNumOutputChannels())); + jackTrip->appendProcessPluginToMonitor(m_monitor); + connect(this, &VirtualStudio::updatedMonitorVolume, m_monitor, + &Monitor::volumeUpdated); + + // Setup output meter + // Note: Add this to monitor process to include self-volume + m_outputMeter = new Meter(jackTrip->getNumOutputChannels()); + m_outputMeter->setIsMonitoringMeter(true); + jackTrip->appendProcessPluginToMonitor(m_outputMeter); + connect(m_outputMeter, &Meter::onComputedVolumeMeasurements, this, + &VirtualStudio::updatedOutputVuMeasurements); + // Grab previous levels QSettings settings; settings.beginGroup(QStringLiteral("Audio")); m_inMultiplier = settings.value(QStringLiteral("InMultiplier"), 1).toFloat(); m_outMultiplier = settings.value(QStringLiteral("OutMultiplier"), 1).toFloat(); + m_monMultiplier = settings.value(QStringLiteral("MonMultiplier"), 0).toFloat(); m_inMuted = settings.value(QStringLiteral("InMuted"), false).toBool(); m_outMuted = settings.value(QStringLiteral("OutMuted"), false).toBool(); + m_monMuted = settings.value(QStringLiteral("MonMuted"), false).toBool(); + emit updatedInputVolume(m_inMultiplier); emit updatedOutputVolume(m_outMultiplier); + emit updatedMonitorVolume(m_monMultiplier); emit updatedInputMuted(m_inMuted); emit updatedOutputMuted(m_outMuted); @@ -1592,10 +1676,24 @@ void VirtualStudio::completeConnection() #endif } +void VirtualStudio::triggerReconnect() +{ + if (m_jackTripRunning) { + m_connectionState = QStringLiteral("Reconnecting..."); + emit connectionStateChanged(); + m_retryPeriodTimer.stop(); + m_retryPeriod = false; + if (m_device != nullptr) { + m_device->setReconnect(true); + } + } +} + void VirtualStudio::disconnect() { m_connectionState = QStringLiteral("Disconnecting..."); emit connectionStateChanged(); + setConnectedErrorMsg(""); m_retryPeriodTimer.stop(); m_retryPeriod = false; @@ -1623,6 +1721,7 @@ void VirtualStudio::disconnect() emit connectionStateChanged(); // cleanup + m_monitor = nullptr; m_inputMeter = nullptr; m_outputMeter = nullptr; m_inputVolumePlugin = nullptr; @@ -1655,9 +1754,21 @@ void VirtualStudio::manageStudio(int studioIndex, bool start) request.toJson()); connect(reply, &QNetworkReply::finished, this, [&, reply]() { if (reply->error() != QNetworkReply::NoError) { - m_connectionState = QStringLiteral("Unable to Start Studio"); + m_connectionState = QStringLiteral("Unable to Start Studio"); + QJsonDocument errorDoc = QJsonDocument::fromJson(reply->readAll()); + if (!errorDoc.isNull()) { + QJsonObject errorObj = errorDoc.object(); + if (errorObj.contains("error")) { + QString errorMessage = errorObj.value("error").toString(); + if (errorMessage.contains( + "Only one studio may be running at a time")) { + setConnectedErrorMsg("one-studio-limit-reached"); + } + } + } emit connectionStateChanged(); } else { + setConnectedErrorMsg(""); QByteArray response = reply->readAll(); QJsonDocument serverState = QJsonDocument::fromJson(response); if (serverState.object()[QStringLiteral("status")].toString() @@ -1778,6 +1889,8 @@ void VirtualStudio::slotAuthSucceded() &VirtualStudio::setOutputVolume); connect(m_device, &VsDevice::updatedPlaybackMuteFromServer, this, &VirtualStudio::setOutputMuted); + connect(m_device, &VsDevice::updatedMonitorVolume, this, + &VirtualStudio::setMonitorVolume); connect(this, &VirtualStudio::updatedInputVolume, m_device, &VsDevice::updateCaptureVolume); connect(this, &VirtualStudio::updatedInputMuted, m_device, @@ -1786,6 +1899,8 @@ void VirtualStudio::slotAuthSucceded() &VsDevice::updatePlaybackVolume); connect(this, &VirtualStudio::updatedOutputMuted, m_device, &VsDevice::updatePlaybackMute); + connect(this, &VirtualStudio::updatedMonitorVolume, m_device, + &VsDevice::updateMonitorVolume); } void VirtualStudio::slotAuthFailed() @@ -1796,6 +1911,17 @@ void VirtualStudio::slotAuthFailed() void VirtualStudio::processFinished() { + if (m_device != nullptr && m_device->reconnect()) { + if (m_device != nullptr && m_device->hasTerminated()) { + if (m_useRtAudio) { + refreshRtAudioDevices(); + validateInputDevicesState(); + validateOutputDevicesState(); + } + connectToStudio(m_currentStudio); + } + return; + } // use disconnect function to handle reset of all internal flags and timers disconnect(); @@ -2433,12 +2559,9 @@ void VirtualStudio::startAudio() connect(this, &VirtualStudio::inputDeviceChanged, m_vsAudioInterface.data(), &VsAudioInterface::setInputDevice); - connect(this, &VirtualStudio::inputDeviceSelected, m_vsAudioInterface.data(), - &VsAudioInterface::setInputDevice); connect(this, &VirtualStudio::outputDeviceChanged, m_vsAudioInterface.data(), &VsAudioInterface::setOutputDevice); - connect(this, &VirtualStudio::outputDeviceSelected, m_vsAudioInterface.data(), - &VsAudioInterface::setOutputDevice); + #ifdef RT_AUDIO connect(this, &VirtualStudio::baseInputChannelChanged, m_vsAudioInterface.data(), &VsAudioInterface::setBaseInputChannel); @@ -2594,9 +2717,10 @@ QVariant VirtualStudio::formatDeviceList(const QStringList& devices, if (containsCategories) { QJsonObject header = QJsonObject(); - header.insert(QString::fromStdString("text"), uniqueCategories.at(i)); + header.insert(QString::fromStdString("text"), category); header.insert(QString::fromStdString("type"), QString::fromStdString("header")); + header.insert(QString::fromStdString("category"), category); items.push_back(QVariant(QJsonValue(header))); } @@ -2607,6 +2731,7 @@ QVariant VirtualStudio::formatDeviceList(const QStringList& devices, element.insert(QString::fromStdString("type"), QString::fromStdString("element")); element.insert(QString::fromStdString("channels"), channels.at(j)); + element.insert(QString::fromStdString("category"), category); items.push_back(QVariant(QJsonValue(element))); } } @@ -2622,6 +2747,7 @@ VirtualStudio::~VirtualStudio() delete m_servers.at(i); } + delete m_monitor; delete m_inputMeter; delete m_outputMeter; delete m_outputVolumePlugin; diff --git a/src/gui/virtualstudio.h b/src/gui/virtualstudio.h index eb097d5..0641505 100644 --- a/src/gui/virtualstudio.h +++ b/src/gui/virtualstudio.h @@ -49,6 +49,7 @@ #include "../JackTrip.h" #include "../Meter.h" +#include "../Monitor.h" #include "../Volume.h" #include "vsAudioInterface.h" #include "vsConstants.h" @@ -104,6 +105,8 @@ class VirtualStudio : public QObject devicesWarningHelpUrlChanged) Q_PROPERTY(QString devicesErrorHelpUrl READ devicesErrorHelpUrl NOTIFY devicesErrorHelpUrlChanged) + Q_PROPERTY( + QString connectedErrorMsg READ connectedErrorMsg NOTIFY connectedErrorMsgChanged) Q_PROPERTY( int bufferSize READ bufferSize WRITE setBufferSize NOTIFY bufferSizeChanged) @@ -140,6 +143,8 @@ class VirtualStudio : public QObject float inputVolume READ inputVolume WRITE setInputVolume NOTIFY updatedInputVolume) Q_PROPERTY(float outputVolume READ outputVolume WRITE setOutputVolume NOTIFY updatedOutputVolume) + Q_PROPERTY(float monitorVolume READ monitorVolume WRITE setMonitorVolume NOTIFY + updatedMonitorVolume) Q_PROPERTY( bool inputMuted READ inputMuted WRITE setInputMuted NOTIFY updatedInputMuted) Q_PROPERTY(bool audioActivated READ audioActivated WRITE setAudioActivated NOTIFY @@ -150,6 +155,7 @@ class VirtualStudio : public QObject Q_PROPERTY(QString windowState READ windowState WRITE setWindowState NOTIFY windowStateUpdated) Q_PROPERTY(QString apiHost READ apiHost WRITE setApiHost NOTIFY apiHostChanged) + Q_PROPERTY(bool vsFtux READ vsFtux CONSTANT) public: explicit VirtualStudio(bool firstRun = false, QObject* parent = nullptr); @@ -194,6 +200,8 @@ class VirtualStudio : public QObject QString devicesError(); QString devicesWarningHelpUrl(); QString devicesErrorHelpUrl(); + QString connectedErrorMsg(); + void setConnectedErrorMsg(const QString& msg); int bufferSize(); void setBufferSize(int index); int bufferStrategy(); @@ -231,8 +239,10 @@ class VirtualStudio : public QObject QString failedMessage(); float inputVolume(); float outputVolume(); + float monitorVolume(); bool inputMuted(); bool outputMuted(); + bool monitorMuted(); Q_INVOKABLE void restartAudio(); bool audioActivated(); bool audioReady(); @@ -240,6 +250,7 @@ class VirtualStudio : public QObject QString windowState(); QString apiHost(); void setApiHost(QString host); + bool vsFtux(); public slots: void toStandard(); @@ -248,6 +259,7 @@ class VirtualStudio : public QObject void logout(); void refreshStudios(int index, bool signalRefresh = false); void refreshDevices(); + void refreshRtAudioDevices(); void validateDevicesState(); void validateInputDevicesState(); void validateOutputDevicesState(); @@ -256,6 +268,7 @@ class VirtualStudio : public QObject void applySettings(); void connectToStudio(int studioIndex); void completeConnection(); + void triggerReconnect(); void disconnect(); void manageStudio(int studioIndex, bool start = false); void launchVideo(int studioIndex); @@ -267,8 +280,10 @@ class VirtualStudio : public QObject void updatedOutputVuMeasurements(const float* valuesInDecibels, int numChannels); void setInputVolume(float multiplier); void setOutputVolume(float multiplier); + void setMonitorVolume(float multiplier); void setInputMuted(bool muted); void setOutputMuted(bool muted); + void setMonitorMuted(bool muted); void setAudioActivated(bool activated); void setAudioReady(bool ready); void setWindowState(QString state); @@ -290,8 +305,6 @@ class VirtualStudio : public QObject void numInputChannelsChanged(int numChannels, bool shouldRestart = true); void inputMixModeChanged(int mode, 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 baseOutputChannelChanged(int baseChannel, bool shouldRestart = true); void numOutputChannelsChanged(int numChannels, bool shouldRestart = true); void previousInputChanged(); @@ -300,6 +313,7 @@ class VirtualStudio : public QObject void devicesErrorChanged(); void devicesWarningHelpUrlChanged(); void devicesErrorHelpUrlChanged(); + void connectedErrorMsgChanged(); void triggerPlayOutputAudio(); void bufferSizeChanged(); void bufferStrategyChanged(); @@ -324,8 +338,10 @@ class VirtualStudio : public QObject void studioToJoinChanged(); void updatedInputVolume(float multiplier); void updatedOutputVolume(float multiplier); + void updatedMonitorVolume(float multiplier); void updatedInputMuted(bool muted); void updatedOutputMuted(bool muted); + void updatedMonitorMuted(bool muted); void audioActivatedChanged(); void audioReadyChanged(); void windowStateUpdated(); @@ -429,6 +445,7 @@ class VirtualStudio : public QObject Meter* m_inputTestMeter; Volume* m_inputVolumePlugin; Volume* m_outputVolumePlugin; + Monitor* m_monitor; QTimer m_inputClipTimer; QTimer m_outputClipTimer; @@ -437,17 +454,26 @@ class VirtualStudio : public QObject QString m_devicesWarningHelpUrl = QStringLiteral(""); QString m_devicesErrorHelpUrl = QStringLiteral(""); QString m_windowState = QStringLiteral("login"); + QString m_connectedErrorMsg = QStringLiteral(""); float m_meterMax = 0.0; float m_meterMin = -64.0; float m_inMultiplier = 1.0; float m_outMultiplier = 1.0; + float m_monMultiplier = 1.0; bool m_inMuted = false; bool m_outMuted = false; + bool m_monMuted = false; QSharedPointer m_vsAudioInterface; +#ifdef VS_FTUX + bool m_vsFtux = true; +#else + bool m_vsFtux = false; +#endif + #ifdef RT_AUDIO QStringList m_inputDeviceList; QStringList m_outputDeviceList; diff --git a/src/gui/vsAudioInterface.cpp b/src/gui/vsAudioInterface.cpp index a27a6c7..f335f0d 100644 --- a/src/gui/vsAudioInterface.cpp +++ b/src/gui/vsAudioInterface.cpp @@ -327,6 +327,11 @@ void VsAudioInterface::addOutputPlugin(ProcessPlugin* plugin) m_audioInterface->appendProcessPluginFromNetwork(plugin); } +void VsAudioInterface::addMonitorPlugin(ProcessPlugin* plugin) +{ + m_audioInterface->appendProcessPluginToMonitor(plugin); +} + void VsAudioInterface::setInputDevice(QString deviceName, bool shouldRestart) { m_inputDeviceName = deviceName.toStdString(); diff --git a/src/gui/vsAudioInterface.h b/src/gui/vsAudioInterface.h index 6a6d94a..1b3c6a0 100644 --- a/src/gui/vsAudioInterface.h +++ b/src/gui/vsAudioInterface.h @@ -78,6 +78,7 @@ class VsAudioInterface : public QObject void startProcess(); void addInputPlugin(ProcessPlugin* plugin); void addOutputPlugin(ProcessPlugin* plugin); + void addMonitorPlugin(ProcessPlugin* plugin); int getNumInputChannels(); int getNumOutputChannels(); void setupPlugins(); diff --git a/src/gui/vsDevice.cpp b/src/gui/vsDevice.cpp index 08c79f2..1803787 100644 --- a/src/gui/vsDevice.cpp +++ b/src/gui/vsDevice.cpp @@ -58,6 +58,8 @@ VsDevice::VsDevice(QOAuth2AuthorizationCodeFlow* authenticator, bool testMode, m_playbackVolume = (float)settings.value(QStringLiteral("OutMultiplier"), 1.0).toDouble(); m_playbackMute = settings.value(QStringLiteral("OutMuted"), false).toBool(); + m_monitorVolume = + (float)settings.value(QStringLiteral("MonMultiplier"), 0).toDouble(); settings.endGroup(); m_sendVolumeTimer = new QTimer(this); @@ -76,7 +78,7 @@ VsDevice::VsDevice(QOAuth2AuthorizationCodeFlow* authenticator, bool testMode, {QLatin1String("captureMute"), m_captureMute}, {QLatin1String("playbackVolume"), (int)(m_playbackVolume * 100.0)}, {QLatin1String("playbackMute"), m_playbackMute}, - }; + {QLatin1String("monitorVolume"), (int)(m_monitorVolume * 100.0)}}; QJsonDocument request = QJsonDocument(json); QNetworkReply* reply = m_authenticator->put( @@ -112,6 +114,12 @@ VsDevice::VsDevice(QOAuth2AuthorizationCodeFlow* authenticator, bool testMode, deviceState.object()[QStringLiteral("playbackMute")].toBool(); emit updatedPlaybackVolumeFromServer(m_playbackVolume); emit updatedPlaybackMuteFromServer(m_playbackMute); + + // monitor volume + m_monitorVolume = + (float)(deviceState.object()[QStringLiteral("monitorVolume")].toDouble() + / 100.0); + emit updatedMonitorVolume(m_monitorVolume); } QSettings settings; @@ -120,6 +128,7 @@ VsDevice::VsDevice(QOAuth2AuthorizationCodeFlow* authenticator, bool testMode, settings.setValue(QStringLiteral("InMuted"), m_captureMute); settings.setValue(QStringLiteral("OutMultiplier"), m_playbackVolume); settings.setValue(QStringLiteral("OutMuted"), m_playbackMute); + settings.setValue(QStringLiteral("MonMultiplier"), m_monitorVolume); settings.endGroup(); reply->deleteLater(); @@ -297,6 +306,31 @@ void VsDevice::sendHeartbeat() } } +bool VsDevice::reconnect() +{ + return m_reconnect; +} + +void VsDevice::setReconnect(bool reconnect) +{ + m_reconnect = reconnect; + if (reconnect) { + stopPinger(); + if (m_webSocket != nullptr && m_webSocket->isValid()) { + m_webSocket->closeSocket(); + } + if (!m_jackTrip.isNull()) { + m_jackTrip->stop(); + m_jackTrip.reset(); + } + } +} + +bool VsDevice::hasTerminated() +{ + return m_jackTrip.isNull(); +} + // setServerId updates the emulated device with the provided serverId void VsDevice::setServerId(QString serverId) { @@ -313,6 +347,7 @@ void VsDevice::setServerId(QString serverId) reply->deleteLater(); return; } + m_deviceAgentConfig.insert("serverId", serverId); reply->deleteLater(); }); } @@ -325,7 +360,7 @@ void VsDevice::sendLevels() {QLatin1String("captureMute"), m_captureMute}, {QLatin1String("playbackVolume"), (int)(m_playbackVolume * 100.0)}, {QLatin1String("playbackMute"), m_playbackMute}, - }; + {QLatin1String("monitorVolume"), (int)(m_monitorVolume * 100.0)}}; QJsonDocument request = QJsonDocument(json); QNetworkReply* reply = m_authenticator->put( QStringLiteral("https://%1/api/devices/%2").arg(m_apiHost, m_appID), @@ -375,9 +410,9 @@ JackTrip* VsDevice::initJackTrip( m_jackTrip->setRemoteClientName(m_appID); // increment m_bufferStrategy by 1 for array-index mapping m_jackTrip->setBufferStrategy(bufferStrategy + 1); - if (bufferStrategy == 2) { + if (bufferStrategy == 2 || bufferStrategy == 3) { // use -q auto3 for loss concealment - m_jackTrip->setBufferQueueLength(-3); + m_jackTrip->setBufferQueueLength(-5); } else { // use -q auto m_jackTrip->setBufferQueueLength(-500); @@ -410,8 +445,12 @@ void VsDevice::startJackTrip() void VsDevice::stopJackTrip() { if (!m_jackTrip.isNull()) { + if (m_webSocket != nullptr && m_webSocket->isValid()) { + m_webSocket->closeSocket(); + } setServerId(""); m_jackTrip->stop(); + m_jackTrip.reset(); } } @@ -509,13 +548,29 @@ void VsDevice::updatePlaybackMute(bool muted) } } +// updateMonitorVolume sets VsDevice's monitor to the provided float +void VsDevice::updateMonitorVolume(float multiplier) +{ + if (multiplier == m_monitorVolume) { + return; + } + + m_monitorVolume = multiplier; + + if (m_sendVolumeTimer) { + m_sendVolumeTimer->start(200); + } +} + // terminateJackTrip is a slot intended to be triggered on jacktrip process signals void VsDevice::terminateJackTrip() { if (!enabled()) { setServerId(""); } - m_jackTrip.reset(); + if (!m_jackTrip.isNull()) { + m_jackTrip.reset(); + } } // onTextMessageReceived is a slot intended to be triggered by new incoming WSS messages @@ -559,6 +614,13 @@ void VsDevice::onTextMessageReceived(const QString& message) emit updatedPlaybackMuteFromServer(m_playbackMute); } + // monitor volume + newVolume = (float)(newState["monitorVolume"].toDouble() / 100.0); + if (newVolume != m_monitorVolume) { + m_monitorVolume = newVolume; + emit updatedMonitorVolume(m_monitorVolume); + } + reconcileAgentConfig(newState); } diff --git a/src/gui/vsDevice.h b/src/gui/vsDevice.h index b13a733..c730109 100644 --- a/src/gui/vsDevice.h +++ b/src/gui/vsDevice.h @@ -65,6 +65,9 @@ class VsDevice : public QObject void registerApp(); void removeApp(); void sendHeartbeat(); + bool reconnect(); + void setReconnect(bool reconnect); + bool hasTerminated(); void setServerId(QString studioID); JackTrip* initJackTrip(bool useRtAudio, std::string input, std::string output, int baseInputChannel, int numChannelsIn, int baseOutputChannel, @@ -83,12 +86,14 @@ class VsDevice : public QObject void updatedCaptureMuteFromServer(bool muted); void updatedPlaybackVolumeFromServer(float multiplier); void updatedPlaybackMuteFromServer(bool muted); + void updatedMonitorVolume(float multiplier); public slots: void updateCaptureVolume(float multiplier); void updateCaptureMute(bool muted); void updatePlaybackVolume(float multiplier); void updatePlaybackMute(bool muted); + void updateMonitorVolume(float multiplier); private slots: void terminateJackTrip(); @@ -118,7 +123,9 @@ class VsDevice : public QObject bool m_captureMute = false; float m_playbackVolume = 1.0; bool m_playbackMute = false; + float m_monitorVolume = 0; QTimer* m_sendVolumeTimer; + bool m_reconnect = false; }; #endif // VSDEVICE_H diff --git a/src/gui/vsftux.qml b/src/gui/vsftux.qml new file mode 100644 index 0000000..be0a7ff --- /dev/null +++ b/src/gui/vsftux.qml @@ -0,0 +1,128 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 + +Rectangle { + property string backgroundColour: virtualstudio.darkMode ? "#272525" : "#FAFBFB" + property string textColour: virtualstudio.darkMode ? "#FAFBFB" : "#0F0D0D" + + width: 696 + height: 577 + color: backgroundColour + state: virtualstudio.windowState + anchors.fill: parent + + id: window + states: [ + State { + name: "login" + PropertyChanges { target: loginScreen; x: 0; failTextVisible: false } + PropertyChanges { target: setupScreen; x: window.width } + PropertyChanges { target: browseScreen; x: window.width } + PropertyChanges { target: settingsScreen; x: window.width } + PropertyChanges { target: connectedScreen; x: window.width } + PropertyChanges { target: failedScreen; x: window.width } + }, + + State { + name: "setup" + PropertyChanges { target: loginScreen; x: -loginScreen.width } + PropertyChanges { target: setupScreen; x: 0 } + PropertyChanges { target: browseScreen; x: window.width } + PropertyChanges { target: settingsScreen; x: window.width } + PropertyChanges { target: connectedScreen; x: window.width } + PropertyChanges { target: failedScreen; x: window.width } + }, + + State { + name: "browse" + PropertyChanges { target: loginScreen; x: -loginScreen.width } + PropertyChanges { target: setupScreen; x: -setupScreen.width } + PropertyChanges { target: browseScreen; x: 0 } + PropertyChanges { target: settingsScreen; x: window.width } + PropertyChanges { target: connectedScreen; x: window.width } + PropertyChanges { target: failedScreen; x: window.width } + }, + + State { + name: "settings" + PropertyChanges { target: loginScreen; x: -loginScreen.width } + PropertyChanges { target: setupScreen; x: -setupScreen.width } + PropertyChanges { target: browseScreen; x: -browseScreen.width } + PropertyChanges { target: settingsScreen; x: 0 } + PropertyChanges { target: connectedScreen; x: window.width } + PropertyChanges { target: failedScreen; x: window.width } + }, + + State { + name: "connected" + PropertyChanges { target: loginScreen; x: -loginScreen.width } + PropertyChanges { target: setupScreen; x: -setupScreen.width } + PropertyChanges { target: browseScreen; x: -browseScreen.width } + PropertyChanges { target: settingsScreen; x: window.width } + PropertyChanges { target: connectedScreen; x: 0 } + PropertyChanges { target: failedScreen; x: window.width } + }, + + State { + name: "failed" + PropertyChanges { target: loginScreen; x: -loginScreen.width } + PropertyChanges { target: setupScreen; x: -setupScreen.width } + PropertyChanges { target: browseScreen; x: -browseScreen.width } + PropertyChanges { target: settingsScreen; x: window.width } + PropertyChanges { target: connectedScreen; x: window.width } + PropertyChanges { target: failedScreen; x: 0 } + } + ] + + transitions: Transition { + NumberAnimation { properties: "x"; duration: 800; easing.type: Easing.InOutQuad } + } + + Setup { + id: setupScreen + } + + Browse { + id: browseScreen + } + + Login { + id: loginScreen + showBackButton: false + } + + Settings { + id: settingsScreen + } + + Connected { + id: connectedScreen + } + + Failed { + id: failedScreen + } + + Connections { + target: virtualstudio + function onAuthSucceeded() { + if (virtualstudio.showDeviceSetup) { + virtualstudio.windowState = "setup"; + } else { + virtualstudio.windowState = "browse"; + } + } + function onAuthFailed() { + loginScreen.failTextVisible = true; + } + function onConnected() { + virtualstudio.windowState = "connected"; + } + function onFailed() { + virtualstudio.windowState = "failed"; + } + function onDisconnected() { + virtualstudio.windowState = "browse"; + } + } +} diff --git a/src/jacktrip_globals.h b/src/jacktrip_globals.h index a1e4805..a7dd828 100644 --- a/src/jacktrip_globals.h +++ b/src/jacktrip_globals.h @@ -40,7 +40,7 @@ #include "AudioInterface.h" -constexpr const char* const gVersion = "1.8.1"; ///< JackTrip version +constexpr const char* const gVersion = "1.9.0"; ///< JackTrip version //******************************************************************************* /// \name Default Values @@ -126,7 +126,8 @@ constexpr int gMaxRemoteNameLength = 64; /// \name Global Functions #ifdef __APPLE__ -void setRealtimeProcessPriority(int bufferSize, int sampleRate); +void setRealtimeProcessPriority(int bufferSize = gDefaultBufferSizeInSamples, + int sampleRate = gDefaultSampleRate); #else void setRealtimeProcessPriority(); #endif diff --git a/src/monitordsp.h b/src/monitordsp.h new file mode 100644 index 0000000..c99d5db --- /dev/null +++ b/src/monitordsp.h @@ -0,0 +1,2116 @@ +/* ------------------------------------------------------------ +author: "Dominick Hing, adapted from 'Volume Control' by Matt Horton" +license: "MIT Style STK-4.2" +name: "monitor" +version: "1.0" +Code generated with Faust 2.54.9 (https://faust.grame.fr) +Compilation options: -a faust2header.cpp -lang cpp -i -inpl -cn monitordsp -es 1 -mcd 16 -single -ftz 0 +------------------------------------------------------------ */ + +#ifndef __monitordsp_H__ +#define __monitordsp_H__ + +// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME +// +// faust2header.cpp - FAUST Architecture File +// This is a simple variation of matlabplot.cpp in the Faust distribution +// aimed at creating a simple C++ header file (.h) containing a Faust DSP. +// See the Makefile for how to use it. + +/************************** BEGIN dsp.h ******************************** + FAUST Architecture File + Copyright (C) 2003-2022 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __dsp__ +#define __dsp__ + +#include +#include + +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2022 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ***************************************************************************/ + +#ifndef __export__ +#define __export__ + +#define FAUSTVERSION "2.54.9" + +// Use FAUST_API for code that is part of the external API but is also compiled in faust and libfaust +// Use LIBFAUST_API for code that is compiled in faust and libfaust + +#ifdef _WIN32 + #pragma warning (disable: 4251) + #ifdef FAUST_EXE + #define FAUST_API + #define LIBFAUST_API + #elif FAUST_LIB + #define FAUST_API __declspec(dllexport) + #define LIBFAUST_API __declspec(dllexport) + #else + #define FAUST_API + #define LIBFAUST_API + #endif +#else + #ifdef FAUST_EXE + #define FAUST_API + #define LIBFAUST_API + #else + #define FAUST_API __attribute__((visibility("default"))) + #define LIBFAUST_API __attribute__((visibility("default"))) + #endif +#endif + +#endif + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +struct FAUST_API UI; +struct FAUST_API Meta; + +/** + * DSP memory manager. + */ + +struct FAUST_API dsp_memory_manager { + + virtual ~dsp_memory_manager() {} + + /** + * 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*/) {} + + /** + * Give the Memory Manager information on a given memory zone. + * @param size - the size in bytes of the memory zone + * @param reads - the number of Read access to the zone used to compute one frame + * @param writes - the number of Write access to the zone used to compute one frame + */ + virtual void info(size_t /*size*/, size_t /*reads*/, size_t /*writes*/) {} + + /** + * Inform the Memory Manager that all memory zones have been described, + * to possibly start a 'compute the best allocation strategy' step. + */ + virtual void end() {} + + /** + * Allocate a memory zone. + * @param size - the memory zone size in bytes + */ + virtual void* allocate(size_t size) = 0; + + /** + * Destroy a memory zone. + * @param ptr - the memory zone pointer to be deallocated + */ + virtual void destroy(void* ptr) = 0; + +}; + +/** +* Signal processor definition. +*/ + +class FAUST_API dsp { + + public: + + dsp() {} + virtual ~dsp() {} + + /* Return instance number of audio inputs */ + virtual int getNumInputs() = 0; + + /* Return instance number of audio outputs */ + virtual int getNumOutputs() = 0; + + /** + * Trigger the ui_interface parameter with instance specific calls + * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI. + * + * @param ui_interface - the user interface builder + */ + virtual void buildUserInterface(UI* ui_interface) = 0; + + /* Return the sample rate currently used by the instance */ + virtual int getSampleRate() = 0; + + /** + * Global init, calls the following methods: + * - static class 'classInit': static tables initialization + * - 'instanceInit': constants and instance state initialization + * + * @param sample_rate - the sampling rate in Hz + */ + virtual void init(int sample_rate) = 0; + + /** + * Init instance state + * + * @param sample_rate - the sampling rate in Hz + */ + virtual void instanceInit(int sample_rate) = 0; + + /** + * Init instance constant state + * + * @param sample_rate - the sampling rate in Hz + */ + virtual void instanceConstants(int sample_rate) = 0; + + /* Init default control parameters values */ + virtual void instanceResetUserInterface() = 0; + + /* Init instance state (like delay lines...) but keep the control parameter values */ + virtual void instanceClear() = 0; + + /** + * Return a clone of the instance. + * + * @return a copy of the instance on success, otherwise a null pointer. + */ + virtual dsp* clone() = 0; + + /** + * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata. + * + * @param m - the Meta* meta user + */ + virtual void metadata(Meta* m) = 0; + + /** + * DSP instance computation, to be called with successive in/out audio buffers. + * + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * + */ + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0; + + /** + * DSP instance computation: alternative method to be used by subclasses. + * + * @param date_usec - the timestamp in microsec given by audio driver. + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * + */ + virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } + +}; + +/** + * Generic DSP decorator. + */ + +class FAUST_API decorator_dsp : public dsp { + + protected: + + dsp* fDSP; + + public: + + decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {} + virtual ~decorator_dsp() { delete fDSP; } + + virtual int getNumInputs() { return fDSP->getNumInputs(); } + virtual int getNumOutputs() { return fDSP->getNumOutputs(); } + virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); } + virtual int getSampleRate() { return fDSP->getSampleRate(); } + virtual void init(int sample_rate) { fDSP->init(sample_rate); } + virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); } + virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); } + virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); } + virtual void instanceClear() { fDSP->instanceClear(); } + virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); } + virtual void metadata(Meta* m) { fDSP->metadata(m); } + // Beware: subclasses usually have to overload the two 'compute' methods + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); } + virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); } + +}; + +/** + * DSP factory class, used with LLVM and Interpreter backends + * to create DSP instances from a compiled DSP program. + */ + +class FAUST_API dsp_factory { + + protected: + + // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory); + virtual ~dsp_factory() {} + + public: + + virtual std::string getName() = 0; + virtual std::string getSHAKey() = 0; + virtual std::string getDSPCode() = 0; + virtual std::string getCompileOptions() = 0; + virtual std::vector getLibraryList() = 0; + virtual std::vector getIncludePathnames() = 0; + virtual std::vector getWarningMessages() = 0; + + virtual dsp* createDSPInstance() = 0; + + virtual void setMemoryManager(dsp_memory_manager* manager) = 0; + virtual dsp_memory_manager* getMemoryManager() = 0; + +}; + +// Denormal handling + +#if defined (__SSE__) +#include +#endif + +class FAUST_API ScopedNoDenormals { + + private: + + intptr_t fpsr = 0; + + void setFpStatusRegister(intptr_t fpsr_aux) noexcept + { + #if defined (__arm64__) || defined (__aarch64__) + asm volatile("msr fpcr, %0" : : "ri" (fpsr_aux)); + #elif defined (__SSE__) + // The volatile keyword here is needed to workaround a bug in AppleClang 13.0 + // which aggressively optimises away the variable otherwise + volatile uint32_t fpsr_w = static_cast(fpsr_aux); + _mm_setcsr(fpsr_w); + #endif + } + + void getFpStatusRegister() noexcept + { + #if defined (__arm64__) || defined (__aarch64__) + asm volatile("mrs %0, fpcr" : "=r" (fpsr)); + #elif defined (__SSE__) + fpsr = static_cast(_mm_getcsr()); + #endif + } + + public: + + ScopedNoDenormals() noexcept + { + #if defined (__arm64__) || defined (__aarch64__) + intptr_t mask = (1 << 24 /* FZ */); + #elif defined (__SSE__) + #if defined (__SSE2__) + intptr_t mask = 0x8040; + #else + intptr_t mask = 0x8000; + #endif + #else + intptr_t mask = 0x0000; + #endif + getFpStatusRegister(); + setFpStatusRegister(fpsr | mask); + } + + ~ScopedNoDenormals() noexcept + { + setFpStatusRegister(fpsr); + } + +}; + +#define AVOIDDENORMALS ScopedNoDenormals ftz_scope; + +#endif + +/************************** END dsp.h **************************/ +/************************** BEGIN APIUI.h ***************************** +FAUST Architecture File +Copyright (C) 2003-2022 GRAME, Centre National de Creation Musicale +--------------------------------------------------------------------- +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +EXCEPTION : As a special exception, you may create a larger work +that contains this FAUST architecture section and distribute +that work under terms of your choice, so long as this FAUST +architecture section is not modified. +************************************************************************/ + +#ifndef API_UI_H +#define API_UI_H + +#include +#include +#include +#include +#include + +/************************** BEGIN meta.h ******************************* + FAUST Architecture File + Copyright (C) 2003-2022 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __meta__ +#define __meta__ + + +/** + The base class of Meta handler to be used in dsp::metadata(Meta* m) method to retrieve (key, value) metadata. + */ +struct FAUST_API Meta { + virtual ~Meta() {} + virtual void declare(const char* key, const char* value) = 0; +}; + +#endif +/************************** END meta.h **************************/ +/************************** BEGIN UI.h ***************************** + FAUST Architecture File + Copyright (C) 2003-2022 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ********************************************************************/ + +#ifndef __UI_H__ +#define __UI_H__ + + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +/******************************************************************************* + * UI : Faust DSP User Interface + * User Interface as expected by the buildUserInterface() method of a DSP. + * This abstract class contains only the method that the Faust compiler can + * generate to describe a DSP user interface. + ******************************************************************************/ + +struct Soundfile; + +template +struct FAUST_API UIReal { + + UIReal() {} + virtual ~UIReal() {} + + // -- widget's layouts + + virtual void openTabBox(const char* label) = 0; + virtual void openHorizontalBox(const char* label) = 0; + virtual void openVerticalBox(const char* label) = 0; + virtual void closeBox() = 0; + + // -- active widgets + + virtual void addButton(const char* label, REAL* zone) = 0; + virtual void addCheckButton(const char* label, REAL* zone) = 0; + virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0; + + // -- metadata declarations + + virtual void declare(REAL* /*zone*/, const char* /*key*/, const char* /*val*/) {} + + // To be used by LLVM client + virtual int sizeOfFAUSTFLOAT() { return sizeof(FAUSTFLOAT); } +}; + +struct FAUST_API UI : public UIReal { + UI() {} + virtual ~UI() {} +}; + +#endif +/************************** END UI.h **************************/ +/************************** BEGIN PathBuilder.h ************************** + FAUST Architecture File + Copyright (C) 2003-2022 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __PathBuilder__ +#define __PathBuilder__ + +#include +#include +#include +#include +#include +#include + + +/******************************************************************************* + * PathBuilder : Faust User Interface + * Helper class to build complete hierarchical path for UI items. + ******************************************************************************/ + +class FAUST_API PathBuilder { + + protected: + + std::vector fControlsLevel; + std::vector fFullPaths; + std::map fFull2Short; // filled by computeShortNames() + + /** + * @brief check if a character is acceptable for an ID + * + * @param c + * @return true is the character is acceptable for an ID + */ + bool isIDChar(char c) const + { + return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')); + } + + /** + * @brief remove all "/0x00" parts + * + * @param src + * @return modified string + */ + std::string remove0x00(const std::string& src) const + { + return std::regex_replace(src, std::regex("/0x00"), ""); + } + + /** + * @brief replace all non ID char with '_' (one '_' may replace several non ID char) + * + * @param src + * @return modified string + */ + std::string str2ID(const std::string& src) const + { + std::string dst; + bool need_underscore = false; + for (char c : src) { + if (isIDChar(c) || (c == '/')) { + if (need_underscore) { + dst.push_back('_'); + need_underscore = false; + } + dst.push_back(c); + } else { + need_underscore = true; + } + } + return dst; + } + + /** + * @brief Keep only the last n slash-parts + * + * @param src + * @param n : 1 indicates the last slash-part + * @return modified string + */ + std::string cut(const std::string& src, int n) const + { + std::string rdst; + for (int i = int(src.length())-1; i >= 0; i--) { + char c = src[i]; + if (c != '/') { + rdst.push_back(c); + } else if (n == 1) { + std::string dst; + for (int j = int(rdst.length())-1; j >= 0; j--) { + dst.push_back(rdst[j]); + } + return dst; + } else { + n--; + rdst.push_back(c); + } + } + return src; + } + + void addFullPath(const std::string& label) { fFullPaths.push_back(buildPath(label)); } + + /** + * @brief Compute the mapping between full path and short names + */ + void computeShortNames() + { + std::vector uniquePaths; // all full paths transformed but made unique with a prefix + std::map unique2full; // all full paths transformed but made unique with a prefix + char num_buffer[16]; + int pnum = 0; + + for (const auto& s : fFullPaths) { + sprintf(num_buffer, "%d", pnum++); + std::string u = "/P" + std::string(num_buffer) + str2ID(remove0x00(s)); + uniquePaths.push_back(u); + unique2full[u] = s; // remember the full path associated to a unique path + } + + std::map uniquePath2level; // map path to level + for (const auto& s : uniquePaths) uniquePath2level[s] = 1; // we init all levels to 1 + bool have_collisions = true; + + while (have_collisions) { + // compute collision list + std::set collisionSet; + std::map short2full; + have_collisions = false; + for (const auto& it : uniquePath2level) { + std::string u = it.first; + int n = it.second; + std::string shortName = cut(u, n); + auto p = short2full.find(shortName); + if (p == short2full.end()) { + // no collision + short2full[shortName] = u; + } else { + // we have a collision, add the two paths to the collision set + have_collisions = true; + collisionSet.insert(u); + collisionSet.insert(p->second); + } + } + for (const auto& s : collisionSet) uniquePath2level[s]++; // increase level of colliding path + } + + for (const auto& it : uniquePath2level) { + std::string u = it.first; + int n = it.second; + std::string shortName = replaceCharList(cut(u, n), {'/'}, '_'); + fFull2Short[unique2full[u]] = shortName; + } + } + + std::string replaceCharList(const std::string& str, const std::vector& ch1, char ch2) + { + auto beg = ch1.begin(); + auto end = ch1.end(); + std::string res = str; + for (size_t i = 0; i < str.length(); ++i) { + if (std::find(beg, end, str[i]) != end) res[i] = ch2; + } + return res; + } + + public: + + PathBuilder() {} + virtual ~PathBuilder() {} + + // Return true for the first level of groups + bool pushLabel(const std::string& label) { fControlsLevel.push_back(label); return fControlsLevel.size() == 1;} + + // Return true for the last level of groups + bool popLabel() { fControlsLevel.pop_back(); return fControlsLevel.size() == 0; } + + std::string buildPath(const std::string& label) + { + std::string res = "/"; + for (size_t i = 0; i < fControlsLevel.size(); i++) { + res = res + fControlsLevel[i] + "/"; + } + res += label; + return replaceCharList(res, {' ', '#', '*', ',', '?', '[', ']', '{', '}', '(', ')'}, '_'); + } + +}; + +#endif // __PathBuilder__ +/************************** END PathBuilder.h **************************/ +/************************** BEGIN ValueConverter.h ******************** + FAUST Architecture File + Copyright (C) 2003-2022 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ********************************************************************/ + +#ifndef __ValueConverter__ +#define __ValueConverter__ + +/*************************************************************************************** + ValueConverter.h + (GRAME, Copyright 2015-2019) + + Set of conversion objects used to map user interface values (for example a gui slider + delivering values between 0 and 1) to faust values (for example a vslider between + 20 and 20000) using a log scale. + + -- Utilities + + Range(lo,hi) : clip a value x between lo and hi + Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2 + Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2 + + -- Value Converters + + ValueConverter::ui2faust(x) + ValueConverter::faust2ui(x) + + -- ValueConverters used for sliders depending of the scale + + LinearValueConverter(umin, umax, fmin, fmax) + LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments + LogValueConverter(umin, umax, fmin, fmax) + ExpValueConverter(umin, umax, fmin, fmax) + + -- ValueConverters used for accelerometers based on 3 points + + AccUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 0 + AccDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 1 + AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2 + AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3 + + -- lists of ZoneControl are used to implement accelerometers metadata for each axes + + ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter + + -- ZoneReader are used to implement screencolor metadata + + ZoneReader(zone, valueConverter) : a zone with a data converter + +****************************************************************************************/ + +#include +#include // std::max +#include +#include +#include + + +//-------------------------------------------------------------------------------------- +// Interpolator(lo,hi,v1,v2) +// Maps a value x between lo and hi to a value y between v1 and v2 +// y = v1 + (x-lo)/(hi-lo)*(v2-v1) +// y = v1 + (x-lo) * coef with coef = (v2-v1)/(hi-lo) +// y = v1 + x*coef - lo*coef +// y = v1 - lo*coef + x*coef +// y = offset + x*coef with offset = v1 - lo*coef +//-------------------------------------------------------------------------------------- +class FAUST_API Interpolator { + + private: + + //-------------------------------------------------------------------------------------- + // Range(lo,hi) clip a value between lo and hi + //-------------------------------------------------------------------------------------- + struct Range + { + double fLo; + double fHi; + + Range(double x, double y) : fLo(std::min(x,y)), fHi(std::max(x,y)) {} + double operator()(double x) { return (xfHi) ? fHi : x; } + }; + + + Range fRange; + double fCoef; + double fOffset; + + public: + + Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi) + { + if (hi != lo) { + // regular case + fCoef = (v2-v1)/(hi-lo); + fOffset = v1 - lo*fCoef; + } else { + // degenerate case, avoids division by zero + fCoef = 0; + fOffset = (v1+v2)/2; + } + } + double operator()(double v) + { + double x = fRange(v); + return fOffset + x*fCoef; + } + + void getLowHigh(double& amin, double& amax) + { + amin = fRange.fLo; + amax = fRange.fHi; + } +}; + +//-------------------------------------------------------------------------------------- +// Interpolator3pt(lo,mi,hi,v1,vm,v2) +// Map values between lo mid hi to values between v1 vm v2 +//-------------------------------------------------------------------------------------- +class FAUST_API Interpolator3pt { + + private: + + Interpolator fSegment1; + Interpolator fSegment2; + double fMid; + + public: + + Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) : + fSegment1(lo, mi, v1, vm), + fSegment2(mi, hi, vm, v2), + fMid(mi) {} + double operator()(double x) { return (x < fMid) ? fSegment1(x) : fSegment2(x); } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fSegment1.getLowHigh(amin, amid); + fSegment2.getLowHigh(amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Abstract ValueConverter class. Converts values between UI and Faust representations +//-------------------------------------------------------------------------------------- +class FAUST_API ValueConverter { + + public: + + virtual ~ValueConverter() {} + virtual double ui2faust(double x) { return x; }; + virtual double faust2ui(double x) { return x; }; +}; + +//-------------------------------------------------------------------------------------- +// A converter than can be updated +//-------------------------------------------------------------------------------------- + +class FAUST_API UpdatableValueConverter : public ValueConverter { + + protected: + + bool fActive; + + public: + + UpdatableValueConverter():fActive(true) + {} + virtual ~UpdatableValueConverter() + {} + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0; + virtual void getMappingValues(double& amin, double& amid, double& amax) = 0; + + void setActive(bool on_off) { fActive = on_off; } + bool getActive() { return fActive; } + +}; + +//-------------------------------------------------------------------------------------- +// Linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class FAUST_API LinearValueConverter : public ValueConverter { + + private: + + Interpolator fUI2F; + Interpolator fF2UI; + + public: + + LinearValueConverter(double umin, double umax, double fmin, double fmax) : + fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax) + {} + + LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.) + {} + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + +}; + +//-------------------------------------------------------------------------------------- +// Two segments linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class FAUST_API LinearValueConverter2 : public UpdatableValueConverter { + + private: + + Interpolator3pt fUI2F; + Interpolator3pt fF2UI; + + public: + + LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) : + fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax) + {} + + LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.) + {} + + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) + { + fUI2F = Interpolator3pt(amin, amid, amax, min, init, max); + fF2UI = Interpolator3pt(min, init, max, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fUI2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Logarithmic conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class FAUST_API LogValueConverter : public LinearValueConverter { + + public: + + LogValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::log(std::max(DBL_MIN, fmin)), std::log(std::max(DBL_MIN, fmax))) + {} + + virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max(x, DBL_MIN))); } + +}; + +//-------------------------------------------------------------------------------------- +// Exponential conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class FAUST_API ExpValueConverter : public LinearValueConverter { + + public: + + ExpValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::min(DBL_MAX, std::exp(fmin)), std::min(DBL_MAX, std::exp(fmax))) + {} + + virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min(DBL_MAX, std::exp(x))); } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up curve (curve 0) +//-------------------------------------------------------------------------------------- +class FAUST_API AccUpConverter : public UpdatableValueConverter { + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmid,fmax), + fF2A(fmin,fmid,fmax,amin,amid,amax) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax); + fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down curve (curve 1) +//-------------------------------------------------------------------------------------- +class FAUST_API AccDownConverter : public UpdatableValueConverter { + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmid,fmin), + fF2A(fmin,fmid,fmax,amax,amid,amin) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin); + fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up-Down curve (curve 2) +//-------------------------------------------------------------------------------------- +class FAUST_API AccUpDownConverter : public UpdatableValueConverter { + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccUpDownConverter(double amin, double amid, double amax, double fmin, double /*fmid*/, double fmax) : + fA2F(amin,amid,amax,fmin,fmax,fmin), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double /*fmid*/, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down-Up curve (curve 3) +//-------------------------------------------------------------------------------------- +class FAUST_API AccDownUpConverter : public UpdatableValueConverter { + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccDownUpConverter(double amin, double amid, double amax, double fmin, double /*fmid*/, double fmax) : + fA2F(amin,amid,amax,fmax,fmin,fmax), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double /*fmid*/, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Base class for ZoneControl +//-------------------------------------------------------------------------------------- +class FAUST_API ZoneControl { + + protected: + + FAUSTFLOAT* fZone; + + public: + + ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {} + virtual ~ZoneControl() {} + + virtual void update(double /*v*/) const {} + + virtual void setMappingValues(int /*curve*/, double /*amin*/, double /*amid*/, double /*amax*/, double /*min*/, double /*init*/, double /*max*/) {} + virtual void getMappingValues(double& /*amin*/, double& /*amid*/, double& /*amax*/) {} + + FAUSTFLOAT* getZone() { return fZone; } + + virtual void setActive(bool /*on_off*/) {} + virtual bool getActive() { return false; } + + virtual int getCurve() { return -1; } + +}; + +//-------------------------------------------------------------------------------------- +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class FAUST_API ConverterZoneControl : public ZoneControl { + + protected: + + ValueConverter* fValueConverter; + + public: + + ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {} + virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere... + + virtual void update(double v) const { *fZone = FAUSTFLOAT(fValueConverter->ui2faust(v)); } + + ValueConverter* getConverter() { return fValueConverter; } + +}; + +//-------------------------------------------------------------------------------------- +// Association of a zone and a four value converter, each one for each possible curve. +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class FAUST_API CurveZoneControl : public ZoneControl { + + private: + + std::vector fValueConverters; + int fCurve; + + public: + + CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0) + { + assert(curve >= 0 && curve <= 3); + fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max)); + fCurve = curve; + } + virtual ~CurveZoneControl() + { + for (const auto& it : fValueConverters) { delete it; } + } + void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = FAUSTFLOAT(fValueConverters[fCurve]->ui2faust(v)); } + + void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) + { + fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max); + fCurve = curve; + } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fValueConverters[fCurve]->getMappingValues(amin, amid, amax); + } + + void setActive(bool on_off) + { + for (const auto& it : fValueConverters) { it->setActive(on_off); } + } + + int getCurve() { return fCurve; } +}; + +class FAUST_API ZoneReader { + + private: + + FAUSTFLOAT* fZone; + Interpolator fInterpolator; + + public: + + ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {} + + virtual ~ZoneReader() {} + + int getValue() + { + return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127; + } + +}; + +#endif +/************************** END ValueConverter.h **************************/ + +typedef unsigned int uint; + +class APIUI : public PathBuilder, public Meta, public UI +{ + public: + enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph }; + enum Type { kAcc = 0, kGyr = 1, kNoType }; + + protected: + + enum Mapping { kLin = 0, kLog = 1, kExp = 2 }; + + struct Item { + std::string fLabel; + std::string fShortname; + std::string fPath; + ValueConverter* fConversion; + FAUSTFLOAT* fZone; + FAUSTFLOAT fInit; + FAUSTFLOAT fMin; + FAUSTFLOAT fMax; + FAUSTFLOAT fStep; + ItemType fItemType; + + Item(const std::string& label, + const std::string& short_name, + const std::string& path, + ValueConverter* conversion, + FAUSTFLOAT* zone, + FAUSTFLOAT init, + FAUSTFLOAT min, + FAUSTFLOAT max, + FAUSTFLOAT step, + ItemType item_type) + :fLabel(label), fShortname(short_name), fPath(path), fConversion(conversion), + fZone(zone), fInit(init), fMin(min), fMax(max), fStep(step), fItemType(item_type) + {} + }; + std::vector fItems; + + std::vector > fMetaData; + std::vector fAcc[3]; + std::vector fGyr[3]; + + // Screen color control + // "...[screencolor:red]..." etc. + bool fHasScreenControl; // true if control screen color metadata + ZoneReader* fRedReader; + ZoneReader* fGreenReader; + ZoneReader* fBlueReader; + + // Current values controlled by metadata + std::string fCurrentUnit; + int fCurrentScale; + std::string fCurrentAcc; + std::string fCurrentGyr; + std::string fCurrentColor; + std::string fCurrentTooltip; + std::map fCurrentMetadata; + + // Add a generic parameter + virtual void addParameter(const char* label, + FAUSTFLOAT* zone, + FAUSTFLOAT init, + FAUSTFLOAT min, + FAUSTFLOAT max, + FAUSTFLOAT step, + ItemType type) + { + std::string path = buildPath(label); + fFullPaths.push_back(path); + + // handle scale metadata + ValueConverter* converter = nullptr; + switch (fCurrentScale) { + case kLin: + converter = new LinearValueConverter(0, 1, min, max); + break; + case kLog: + converter = new LogValueConverter(0, 1, min, max); + break; + case kExp: + converter = new ExpValueConverter(0, 1, min, max); + break; + } + fCurrentScale = kLin; + + fItems.push_back(Item(label, "", path, converter, zone, init, min, max, step, type)); + + if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) { + fprintf(stderr, "warning : 'acc' and 'gyr' metadata used for the same %s parameter !!\n", label); + } + + // handle acc metadata "...[acc : ]..." + if (fCurrentAcc.size() > 0) { + std::istringstream iss(fCurrentAcc); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + fprintf(stderr, "incorrect acc metadata : %s \n", fCurrentAcc.c_str()); + } + fCurrentAcc = ""; + } + + // handle gyr metadata "...[gyr : ]..." + if (fCurrentGyr.size() > 0) { + std::istringstream iss(fCurrentGyr); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + fprintf(stderr, "incorrect gyr metadata : %s \n", fCurrentGyr.c_str()); + } + fCurrentGyr = ""; + } + + // handle screencolor metadata "...[screencolor:red|green|blue|white]..." + if (fCurrentColor.size() > 0) { + if ((fCurrentColor == "red") && (fRedReader == nullptr)) { + fRedReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "green") && (fGreenReader == nullptr)) { + fGreenReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "blue") && (fBlueReader == nullptr)) { + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "white") && (fRedReader == nullptr) && (fGreenReader == nullptr) && (fBlueReader == nullptr)) { + fRedReader = new ZoneReader(zone, min, max); + fGreenReader = new ZoneReader(zone, min, max); + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else { + fprintf(stderr, "incorrect screencolor metadata : %s \n", fCurrentColor.c_str()); + } + } + fCurrentColor = ""; + + fMetaData.push_back(fCurrentMetadata); + fCurrentMetadata.clear(); + } + + int getZoneIndex(std::vector* table, int p, int val) + { + FAUSTFLOAT* zone = fItems[uint(p)].fZone; + for (size_t i = 0; i < table[val].size(); i++) { + if (zone == table[val][i]->getZone()) return int(i); + } + return -1; + } + + void setConverter(std::vector* table, int p, int val, int curve, double amin, double amid, double amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + // Deactivates everywhere.. + if (id1 != -1) table[0][uint(id1)]->setActive(false); + if (id2 != -1) table[1][uint(id2)]->setActive(false); + if (id3 != -1) table[2][uint(id3)]->setActive(false); + + if (val == -1) { // Means: no more mapping... + // So stay all deactivated... + } else { + int id4 = getZoneIndex(table, p, val); + if (id4 != -1) { + // Reactivate the one we edit... + table[val][uint(id4)]->setMappingValues(curve, amin, amid, amax, fItems[uint(p)].fMin, fItems[uint(p)].fInit, fItems[uint(p)].fMax); + table[val][uint(id4)]->setActive(true); + } else { + // Allocate a new CurveZoneControl which is 'active' by default + FAUSTFLOAT* zone = fItems[uint(p)].fZone; + table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fItems[uint(p)].fMin, fItems[uint(p)].fInit, fItems[uint(p)].fMax)); + } + } + } + + void getConverter(std::vector* table, int p, int& val, int& curve, double& amin, double& amid, double& amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + if (id1 != -1) { + val = 0; + curve = table[val][uint(id1)]->getCurve(); + table[val][uint(id1)]->getMappingValues(amin, amid, amax); + } else if (id2 != -1) { + val = 1; + curve = table[val][uint(id2)]->getCurve(); + table[val][uint(id2)]->getMappingValues(amin, amid, amax); + } else if (id3 != -1) { + val = 2; + curve = table[val][uint(id3)]->getCurve(); + table[val][uint(id3)]->getMappingValues(amin, amid, amax); + } else { + val = -1; // No mapping + curve = 0; + amin = -100.; + amid = 0.; + amax = 100.; + } + } + + public: + + APIUI() : fHasScreenControl(false), fRedReader(nullptr), fGreenReader(nullptr), fBlueReader(nullptr), fCurrentScale(kLin) + {} + + virtual ~APIUI() + { + for (const auto& it : fItems) delete it.fConversion; + for (int i = 0; i < 3; i++) { + for (const auto& it : fAcc[i]) delete it; + for (const auto& it : fGyr[i]) delete it; + } + delete fRedReader; + delete fGreenReader; + delete fBlueReader; + } + + // -- widget's layouts + + virtual void openTabBox(const char* label) { pushLabel(label); } + virtual void openHorizontalBox(const char* label) { pushLabel(label); } + virtual void openVerticalBox(const char* label) { pushLabel(label); } + virtual void closeBox() + { + if (popLabel()) { + // Shortnames can be computed when all fullnames are known + computeShortNames(); + // Fill 'shortname' field for each item + for (const auto& it : fFull2Short) { + int index = getParamIndex(it.first.c_str()); + fItems[index].fShortname = it.second; + } + } + } + + // -- active widgets + + virtual void addButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kButton); + } + + virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kCheckButton); + } + + virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kVSlider); + } + + virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kHSlider); + } + + virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kNumEntry); + } + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0f, kHBargraph); + } + + virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0f, kVBargraph); + } + + // -- soundfiles + + virtual void addSoundfile(const char* /*label*/, const char* /*filename*/, Soundfile** /*sf_zone*/) {} + + // -- metadata declarations + + virtual void declare(FAUSTFLOAT* /*zone*/, const char* key, const char* val) + { + // Keep metadata + fCurrentMetadata[key] = val; + + if (strcmp(key, "scale") == 0) { + if (strcmp(val, "log") == 0) { + fCurrentScale = kLog; + } else if (strcmp(val, "exp") == 0) { + fCurrentScale = kExp; + } else { + fCurrentScale = kLin; + } + } else if (strcmp(key, "unit") == 0) { + fCurrentUnit = val; + } else if (strcmp(key, "acc") == 0) { + fCurrentAcc = val; + } else if (strcmp(key, "gyr") == 0) { + fCurrentGyr = val; + } else if (strcmp(key, "screencolor") == 0) { + fCurrentColor = val; // val = "red", "green", "blue" or "white" + } else if (strcmp(key, "tooltip") == 0) { + fCurrentTooltip = val; + } + } + + virtual void declare(const char* /*key*/, const char* /*val*/) + {} + + //------------------------------------------------------------------------------- + // Simple API part + //------------------------------------------------------------------------------- + + /** + * Return the number of parameters in the UI. + * + * @return the number of parameters + */ + int getParamsCount() { return int(fItems.size()); } + + /** + * Return the param index. + * + * @param str - the UI parameter label/shortname/path + * + * @return the param index + */ + int getParamIndex(const char* str) + { + std::string path = std::string(str); + auto it = find_if(fItems.begin(), fItems.end(), + [=](const Item& it) { return (it.fLabel == path) || (it.fShortname == path) || (it.fPath == path); }); + return (it != fItems.end()) ? int(it - fItems.begin()) : -1; + } + + /** + * Return the param label. + * + * @param p - the UI parameter index + * + * @return the param label + */ + const char* getParamLabel(int p) { return fItems[uint(p)].fLabel.c_str(); } + + /** + * Return the param shortname. + * + * @param p - the UI parameter index + * + * @return the param shortname + */ + const char* getParamShortname(int p) { return fItems[uint(p)].fShortname.c_str(); } + + /** + * Return the param path. + * + * @param p - the UI parameter index + * + * @return the param path + */ + const char* getParamAddress(int p) { return fItems[uint(p)].fPath.c_str(); } + + /** + * Return the param metadata. + * + * @param p - the UI parameter index + * + * @return the param metadata as a map + */ + std::map getMetadata(int p) + { + std::map res; + std::map metadata = fMetaData[uint(p)]; + for (const auto& it : metadata) { + res[it.first.c_str()] = it.second.c_str(); + } + return res; + } + + /** + * Return the param metadata value. + * + * @param p - the UI parameter index + * @param key - the UI parameter index + * + * @return the param metadata value associate to the key + */ + const char* getMetadata(int p, const char* key) + { + return (fMetaData[uint(p)].find(key) != fMetaData[uint(p)].end()) ? fMetaData[uint(p)][key].c_str() : ""; + } + + /** + * Return the param minimum value. + * + * @param p - the UI parameter index + * + * @return the param minimum value + */ + FAUSTFLOAT getParamMin(int p) { return fItems[uint(p)].fMin; } + + /** + * Return the param maximum value. + * + * @param p - the UI parameter index + * + * @return the param maximum value + */ + FAUSTFLOAT getParamMax(int p) { return fItems[uint(p)].fMax; } + + /** + * Return the param step value. + * + * @param p - the UI parameter index + * + * @return the param step value + */ + FAUSTFLOAT getParamStep(int p) { return fItems[uint(p)].fStep; } + + /** + * Return the param init value. + * + * @param p - the UI parameter index + * + * @return the param init value + */ + FAUSTFLOAT getParamInit(int p) { return fItems[uint(p)].fInit; } + + /** + * Return the param memory zone. + * + * @param p - the UI parameter index + * + * @return the param memory zone. + */ + FAUSTFLOAT* getParamZone(int p) { return fItems[uint(p)].fZone; } + + /** + * Return the param value. + * + * @param p - the UI parameter index + * + * @return the param value. + */ + FAUSTFLOAT getParamValue(int p) { return *fItems[uint(p)].fZone; } + + /** + * Return the param value. + * + * @param str - the UI parameter label/shortname/path + * + * @return the param value. + */ + FAUSTFLOAT getParamValue(const char* str) + { + int index = getParamIndex(str); + if (index >= 0) { + return getParamValue(index); + } else { + fprintf(stderr, "getParamValue : '%s' not found\n", (str == nullptr ? "NULL" : str)); + return FAUSTFLOAT(0); + } + } + + /** + * Set the param value. + * + * @param p - the UI parameter index + * @param v - the UI parameter value + * + */ + void setParamValue(int p, FAUSTFLOAT v) { *fItems[uint(p)].fZone = v; } + + /** + * Set the param value. + * + * @param p - the UI parameter label/shortname/path + * @param v - the UI parameter value + * + */ + void setParamValue(const char* path, FAUSTFLOAT v) + { + int index = getParamIndex(path); + if (index >= 0) { + setParamValue(index, v); + } else { + fprintf(stderr, "setParamValue : '%s' not found\n", (path == nullptr ? "NULL" : path)); + } + } + + double getParamRatio(int p) { return fItems[uint(p)].fConversion->faust2ui(*fItems[uint(p)].fZone); } + void setParamRatio(int p, double r) { *fItems[uint(p)].fZone = FAUSTFLOAT(fItems[uint(p)].fConversion->ui2faust(r)); } + + double value2ratio(int p, double r) { return fItems[uint(p)].fConversion->faust2ui(r); } + double ratio2value(int p, double r) { return fItems[uint(p)].fConversion->ui2faust(r); } + + /** + * Return the control type (kAcc, kGyr, or -1) for a given parameter. + * + * @param p - the UI parameter index + * + * @return the type + */ + Type getParamType(int p) + { + if (p >= 0) { + if (getZoneIndex(fAcc, p, 0) != -1 + || getZoneIndex(fAcc, p, 1) != -1 + || getZoneIndex(fAcc, p, 2) != -1) { + return kAcc; + } else if (getZoneIndex(fGyr, p, 0) != -1 + || getZoneIndex(fGyr, p, 1) != -1 + || getZoneIndex(fGyr, p, 2) != -1) { + return kGyr; + } + } + return kNoType; + } + + /** + * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter. + * + * @param p - the UI parameter index + * + * @return the Item type + */ + ItemType getParamItemType(int p) + { + return fItems[uint(p)].fItemType; + } + + /** + * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @param value - the new value + * + */ + void propagateAcc(int acc, double value) + { + for (size_t i = 0; i < fAcc[acc].size(); i++) { + fAcc[acc][i]->update(value); + } + } + + /** + * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping") + * @param curve - between 0 and 3 (0: up, 1: down, 2: up and down, 2: down and up) + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax) + { + setConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping") + * @param curve - between 0 and 3 (0: up, 1: down, 2: up and down, 2: down and up) + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax) + { + setConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - the acc value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved (between 0 and 3) + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param gyr - the gyr value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved (between 0 and 3) + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param value - the new value + * + */ + void propagateGyr(int gyr, double value) + { + for (size_t i = 0; i < fGyr[gyr].size(); i++) { + fGyr[gyr][i]->update(value); + } + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the accelerometer. + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @return the number of zones + * + */ + int getAccCount(int acc) + { + return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0; + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the gyroscope. + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param the number of zones + * + */ + int getGyrCount(int gyr) + { + return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0; + } + + /** + * Get the requested screen color. + * + * -1 means no screen color control (no screencolor metadata found) + * otherwise return 0x00RRGGBB a ready to use color + * + */ + int getScreenColor() + { + if (fHasScreenControl) { + int r = (fRedReader) ? fRedReader->getValue() : 0; + int g = (fGreenReader) ? fGreenReader->getValue() : 0; + int b = (fBlueReader) ? fBlueReader->getValue() : 0; + return (r<<16) | (g<<8) | b; + } else { + return -1; + } + } + +}; + +#endif +/************************** END APIUI.h **************************/ + +// NOTE: "faust -scn name" changes the last line above to +// #include + +//---------------------------------------------------------------------------- +// FAUST Generated Code +//---------------------------------------------------------------------------- + + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +#include +#include +#include +#include + +#ifndef FAUSTCLASS +#define FAUSTCLASS monitordsp +#endif + +#ifdef __APPLE__ +#define exp10f __exp10f +#define exp10 __exp10 +#endif + +#if defined(_WIN32) +#define RESTRICT __restrict +#else +#define RESTRICT __restrict__ +#endif + + +class monitordsp : public dsp { + + private: + + FAUSTFLOAT fHslider0; + FAUSTFLOAT fCheckbox0; + int fSampleRate; + float fConst0; + float fConst1; + float fRec0[2]; + + public: + + void metadata(Meta* m) { + m->declare("author", "Dominick Hing, adapted from 'Volume Control' by Matt Horton"); + m->declare("basics.lib/name", "Faust Basic Element Library"); + m->declare("basics.lib/version", "0.9"); + m->declare("compile_options", "-a faust2header.cpp -lang cpp -i -inpl -cn monitordsp -es 1 -mcd 16 -single -ftz 0"); + m->declare("description", "Volume Control Faust Plugin for JackTrip, based on Faust examples"); + m->declare("filename", "monitordsp.dsp"); + m->declare("license", "MIT Style STK-4.2"); + m->declare("maths.lib/author", "GRAME"); + m->declare("maths.lib/copyright", "GRAME"); + m->declare("maths.lib/license", "LGPL with exception"); + m->declare("maths.lib/name", "Faust Math Library"); + m->declare("maths.lib/version", "2.5"); + m->declare("name", "monitor"); + m->declare("platform.lib/name", "Generic Platform Library"); + m->declare("platform.lib/version", "0.3"); + m->declare("signals.lib/name", "Faust Signal Routing Library"); + m->declare("signals.lib/version", "0.3"); + m->declare("version", "1.0"); + } + + virtual int getNumInputs() { + return 2; + } + virtual int getNumOutputs() { + return 1; + } + + static void classInit(int /*sample_rate*/) { + } + + virtual void instanceConstants(int sample_rate) { + fSampleRate = sample_rate; + fConst0 = 44.1f / std::min(1.92e+05f, std::max(1.0f, float(fSampleRate))); + fConst1 = 1.0f - fConst0; + } + + virtual void instanceResetUserInterface() { + fHslider0 = FAUSTFLOAT(0.0f); + fCheckbox0 = FAUSTFLOAT(0.0f); + } + + virtual void instanceClear() { + for (int l0 = 0; l0 < 2; l0 = l0 + 1) { + fRec0[l0] = 0.0f; + } + } + + virtual void init(int sample_rate) { + classInit(sample_rate); + instanceInit(sample_rate); + } + virtual void instanceInit(int sample_rate) { + instanceConstants(sample_rate); + instanceResetUserInterface(); + instanceClear(); + } + + virtual monitordsp* clone() { + return new monitordsp(); + } + + virtual int getSampleRate() { + return fSampleRate; + } + + virtual void buildUserInterface(UI* ui_interface) { + ui_interface->openVerticalBox("Monitor"); + ui_interface->declare(&fHslider0, "0", ""); + ui_interface->addHorizontalSlider("Volume", &fHslider0, FAUSTFLOAT(0.0f), FAUSTFLOAT(-4e+01f), FAUSTFLOAT(0.0f), FAUSTFLOAT(0.1f)); + ui_interface->declare(&fCheckbox0, "1", ""); + ui_interface->addCheckButton("Mute", &fCheckbox0); + ui_interface->closeBox(); + } + + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { + FAUSTFLOAT* input0 = inputs[0]; + FAUSTFLOAT* input1 = inputs[1]; + FAUSTFLOAT* output0 = outputs[0]; + float fSlow0 = float(fHslider0); + int iSlow1 = fSlow0 == -4e+01f; + int iSlow2 = int(float(fCheckbox0)); + float fSlow3 = fConst0 * std::pow(1e+01f, 0.05f * fSlow0); + for (int i0 = 0; i0 < count; i0 = i0 + 1) { + float fTemp0 = float(input1[i0]); + float fTemp1 = float(input0[i0]); + fRec0[0] = fSlow3 + fConst1 * fRec0[1]; + output0[i0] = FAUSTFLOAT(fTemp0 + ((iSlow1) ? 0.0f : ((iSlow2) ? 0.0f : fTemp1 * fRec0[0]))); + fRec0[1] = fRec0[0]; + } + } + +}; + + +#endif -- 2.30.2