set(noupdater TRUE)
endif ()
+if (psi)
+ set(novs TRUE)
+endif ()
+
if (novs)
+ add_compile_definitions(NO_VS)
set(QRC_FILE "src/gui/qjacktrip_novs.qrc")
else ()
set(QRC_FILE "src/gui/qjacktrip.qrc")
endif ()
+if (noupdater)
+ add_compile_definitions(NO_UPDATER)
+endif ()
+
if (psi)
add_compile_definitions(PSI)
- set(novs TRUE)
if (novs)
add_compile_definitions(BUILD_TYPE="psi-borg.org NO_VS binary")
else ()
file(WRITE "${QRC_FILE}" "${QRC_CONTENTS}")
endif ()
-if (novs)
- add_compile_definitions(NO_VS)
-endif ()
-
-if (noupdater)
- add_compile_definitions(NO_UPDATER)
-endif ()
-
add_compile_definitions(QT_OPENSOURCE)
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
src/gui/about.cpp
src/gui/messageDialog.cpp
src/gui/textbuf.cpp
+ src/gui/vuMeter.cpp
+ src/Meter.cpp
)
if (NOT novs)
src/gui/vsUrlHandler.cpp
src/gui/vsWebSocket.cpp
src/gui/qjacktrip.qrc
- src/Meter.cpp
src/Volume.cpp
src/Tone.cpp
# Need to include this for AUTOMOC to do its thing
### Fedora
```sh
-dnf install qt5-qtbase-devel qt5-qtnetworkauth-devel qt5-qtquickcontrols2-devel qt5-qtsvg-devel
+dnf install qt5-qtbase-devel qt5-qtnetworkauth-devel qt5-qtwebsockets-devel qt5-qtquickcontrols2-devel qt5-qtsvg-devel
dnf groupinstall "C Development Tools and Libraries"
dnf groupinstall "Development Tools"
dnf install "pkgconfig(jack)" alsa-lib-devel git help2man
### Ubuntu and Debian/Raspbian
```sh
apt install --no-install-recommends build-essential qt5-default autoconf automake libtool make libjack-jackd2-dev git help2man
-apt install qjackctl qt5-qmake qttools5-dev libqt5svg5-dev libqt5networkauth5-dev qtdeclarative5-dev qml-module-qtquick-controls
+apt install qjackctl qt5-qmake qttools5-dev libqt5svg5-dev libqt5networkauth5-dev libqt5websockets5-dev qtdeclarative5-dev qml-module-qtquick-controls
apt install librtaudio-dev # if building with RtAudio
```
+- Version: "1.6.7"
+ Date: 2022-11-03
+ Description:
+ - (added) volume meters in classic mode
+ - (added) release-acquire ordering for Regular
+ - (added) audio driver support article in VS mode
+ - (added) regulatorthread
+ - (added) studios page first time UI
+ - (added) non-asio audio devices can be used on windows
+ - (updated) dependency list in documentation
+ - (updated) windows opens jacktrip after install
+ - (updated) Move to overlapped I/O for Windows Networking
+ - (updated) New default device behavior in Virtual Studio mode
+ - (fixed) opening links from Virtual Studio
+ - (fixed) send capture volume as int
+ - (fixed) connection issues for servers without reverse dns
+ - (fixed) flatpak build errors
+ - (fixed) ventura updater crash
+ - (fixed) uninitialized delete issue
+ - (remvoed) extraneous call to readSlotNonBlocking
- Version: "1.6.6"
Date: 2022-11-01
Description:
!nogui {
LIBS += -framework Foundation
CONFIG += objective_c
+ !novs {
+ LIBS += -framework AVFoundation
+ }
}
}
HEADERS += src/gui/about.h \
src/gui/messageDialog.h \
src/gui/qjacktrip.h \
- src/gui/textbuf.h
+ src/gui/textbuf.h \
+ src/gui/vuMeter.h
!novs {
HEADERS += src/gui/virtualstudio.h \
src/gui/vsDevice.h \
src/gui/vsServerInfo.h \
src/gui/vsQuickView.h \
src/gui/vsWebSocket.h \
+ src/gui/vsPermissions.h \
src/gui/vsPinger.h \
src/gui/vsPing.h \
src/gui/vsUrlHandler.h \
SOURCES += src/gui/messageDialog.cpp \
src/gui/qjacktrip.cpp \
src/gui/about.cpp \
- src/gui/textbuf.cpp
+ src/gui/textbuf.cpp \
+ src/gui/vuMeter.cpp
!novs {
SOURCES += src/gui/virtualstudio.cpp \
src/gui/vsDevice.cpp \
src/gui/vsServerInfo.cpp \
src/gui/vsQuickView.cpp \
src/gui/vsWebSocket.cpp \
+ src/gui/vsPermissions.cpp \
src/gui/vsPinger.cpp \
src/gui/vsPing.cpp \
src/gui/vsUrlHandler.cpp
macx {
HEADERS += src/gui/NoNap.h
OBJECTIVE_SOURCES += src/gui/NoNap.mm
+ !novs {
+ HEADERS += src/gui/vsMacPermissions.h
+ OBJECTIVE_SOURCES += src/gui/vsMacPermissions.mm
+ }
}
- FORMS += src/gui/qjacktrip.ui src/gui/about.ui src/gui/messageDialog.ui
+ FORMS += src/gui/qjacktrip.ui \
+ src/gui/about.ui \
+ src/gui/messageDialog.ui
novs {
RESOURCES += src/gui/qjacktrip_novs.qrc
} else {
app-id: org.jacktrip.JackTrip.Devel
runtime: org.kde.Platform
-runtime-version: '5.15-21.08'
+runtime-version: '5.15-22.08'
sdk: org.kde.Sdk
command: jacktrip
finish-args:
- /lib/python3.8
- /share/man
modules:
- - shared-modules/linux-audio/jack2.json
- name: python3-pyyaml
buildsystem: simple
cleanup: [ "*" ]
- -Dprofile=development
sources:
- type: git
+ disable-submodules: true
url: https://github.com/jacktrip/jacktrip.git
branch: dev
app-id: org.jacktrip.JackTrip.Devel
runtime: org.kde.Platform
-runtime-version: '5.15-21.08'
+runtime-version: '5.15-22.08'
sdk: org.kde.Sdk
command: jacktrip
finish-args:
- /lib/python3.8
- /share/man
modules:
- - shared-modules/linux-audio/jack2.json
- name: python3-pyyaml
buildsystem: simple
cleanup: [ "*" ]
- -Dprofile=development
sources:
- type: git
+ disable-submodules: true
url: {{ env['REPO'] }}
branch: {{ env['REF'] }}
app-id: org.jacktrip.JackTrip
runtime: org.kde.Platform
-runtime-version: '5.15-21.08'
+runtime-version: '5.15-22.08'
sdk: org.kde.Sdk
command: jacktrip
finish-args:
- /lib/python3.8
- /share/man
modules:
- - shared-modules/linux-audio/jack2.json
- name: python3-pyyaml
buildsystem: simple
cleanup: [ "*" ]
buildsystem: meson
sources:
- type: git
+ disable-submodules: true
url: https://github.com/jacktrip/jacktrip.git
branch: main
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
+ <key>com.apple.security.cs.allow-jit</key>
+ <true/>
</dict>
</plist>
'src/Tone.h',
'src/JackTripWorker.h',
'src/PacketHeader.h',
+ 'src/Regulator.h',
'src/Settings.h',
'src/UdpDataProtocol.h',
'src/UdpHubListener.h',
'src/gui/qjacktrip.cpp',
'src/gui/about.cpp',
'src/gui/messageDialog.cpp',
- 'src/gui/textbuf.cpp'
+ 'src/gui/textbuf.cpp',
+ 'src/gui/vuMeter.cpp'
]
moc_h += [
'src/gui/about.h',
'src/gui/qjacktrip.h',
'src/gui/messageDialog.h',
- 'src/gui/textbuf.h'
+ 'src/gui/textbuf.h',
+ 'src/gui/vuMeter.h'
]
ui_h += [
'src/gui/qjacktrip.ui',
'src/gui/vsQuickView.cpp',
'src/gui/vsWebSocket.cpp',
'src/gui/vsUrlHandler.cpp',
+ 'src/gui/vsPermissions.cpp',
'src/gui/vsPinger.cpp',
'src/gui/vsPing.cpp'
]
'src/gui/vsServerInfo.h',
'src/gui/vsQuickView.h',
'src/gui/vsWebSocket.h',
+ 'src/gui/vsPermissions.h',
'src/gui/vsPinger.h',
'src/gui/vsPing.h',
'src/gui/vsUrlHandler.h',
'src/gui/vsQmlClipboard.h',
'src/JTApplication.h'
]
+
+ if host_machine.system() == 'darwin'
+ moc_h += ['src/gui/vsMacPermissions.h']
+ endif
+
qt5_dep = dependency('qt5', modules: ['Core', 'Gui', 'Network', 'Widgets', 'Quick', 'Qml', 'Svg', 'NetworkAuth', 'WebSockets'], include_type: 'system')
qres = ['src/gui/qjacktrip.qrc']
endif
add_languages('objcpp')
endif
+if host_machine.system() == 'darwin' and get_option('novs') == false
+ src += ['src/gui/vsMacPermissions.mm']
+ apple_av_dep = dependency('appleframeworks', modules : ['avfoundation'])
+ deps += apple_av_dep
+endif
+
jacktrip = executable('jacktrip', src, prepro_files, include_directories: incdirs, dependencies: deps, c_args: c_defines, cpp_args: defines, install: true )
help2man = find_program('help2man', required: false)
{
"app_name": "JackTrip",
"releases": [
+ {
+ "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",
{
"app_name": "JackTrip",
"releases": [
+ {
+ "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",
{
"app_name": "JackTrip",
"releases": [
+ {
+ "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",
{
"app_name": "JackTrip",
"releases": [
+ {
+ "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",
{
"app_name": "JackTrip",
"releases": [
+ {
+ "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",
}
//*******************************************************************************
-void AudioInterface::setup()
+void AudioInterface::setup(bool /*verbose*/)
{
// Allocate buffer memory to read and write
mSizeInBytesPerChannel = getSizeInBytesPerChannel();
mProcessPluginsFromNetwork.append(plugin);
}
-void AudioInterface::initPlugins()
+void AudioInterface::initPlugins(bool verbose)
{
int nPlugins = mProcessPluginsFromNetwork.size() + mProcessPluginsToNetwork.size();
if (nPlugins > 0) {
- std::cout << "Initializing Faust plugins (have " << nPlugins
- << ") at sampling rate " << mSampleRate << "\n";
+ if (verbose) {
+ std::cout << "Initializing Faust plugins (have " << nPlugins
+ << ") at sampling rate " << mSampleRate << "\n";
+ }
+
for (ProcessPlugin* plugin : qAsConst(mProcessPluginsFromNetwork)) {
plugin->setOutgoingToNetwork(false);
plugin->updateNumChannels(mNumInChans, mNumOutChans);
return sample_rate;
}
+
+//*******************************************************************************
+void AudioInterface::setDevicesWarningMsg(warningMessageT msg)
+{
+ switch (msg) {
+ case DEVICE_WARN_LATENCY:
+ mWarningMsg =
+ "The selected devices don't support low latency. You can use them, but you "
+ "will experience audio delay. Make sure you have up to date drivers from the "
+ "manufacturer!";
+#ifdef _WIN32
+ mWarningHelpUrl = "https://help.jacktrip.org/hc/en-us/articles/4409919243155";
+#else
+ mWarningHelpUrl = "";
+#endif
+ break;
+ default:
+ mWarningMsg = "";
+ mWarningHelpUrl = "";
+ break;
+ }
+
+ return;
+}
+
+//*******************************************************************************
+void AudioInterface::setDevicesErrorMsg(errorMessageT msg)
+{
+ mErrorMsg = msg;
+ switch (msg) {
+ case DEVICE_ERR_INCOMPATIBLE:
+ mErrorMsg =
+ "The two devices you have selected are not compatible. Please select a "
+ "different pair of devices.";
+#ifdef _WIN32
+ mErrorHelpUrl = "https://help.jacktrip.org/hc/en-us/articles/4409919243155";
+#else
+ mErrorHelpUrl = "";
+#endif
+ break;
+ case DEVICE_ERR_NO_INPUTS:
+ mErrorMsg = "JackTrip couldn't find any input devices!";
+ mErrorHelpUrl = "";
+ break;
+ case DEVICE_ERR_NO_OUTPUTS:
+ mErrorMsg = "JackTrip couldn't find any output devices!";
+ mErrorHelpUrl = "";
+ break;
+ case DEVICE_ERR_NO_DEVICES:
+ mErrorMsg = "JackTrip couldn't find any audio devices!";
+ mErrorHelpUrl = "";
+ break;
+ default:
+ mErrorMsg = "";
+ mErrorHelpUrl = "";
+ break;
+ }
+ return;
+}
+
+//*******************************************************************************
+std::string AudioInterface::getDevicesWarningMsg()
+{
+ return mWarningMsg;
+}
+
+//*******************************************************************************
+std::string AudioInterface::getDevicesErrorMsg()
+{
+ return mErrorMsg;
+}
+
+//*******************************************************************************
+std::string AudioInterface::getDevicesWarningHelpUrl()
+{
+ return mWarningHelpUrl;
+}
+
+//*******************************************************************************
+std::string AudioInterface::getDevicesErrorHelpUrl()
+{
+ return mErrorHelpUrl;
+}
\ No newline at end of file
UNDEF ///< Undefined
};
+ enum warningMessageT { DEVICE_WARN_NONE, DEVICE_WARN_LATENCY };
+
+ enum errorMessageT {
+ DEVICE_ERR_NONE,
+ DEVICE_ERR_INCOMPATIBLE,
+ DEVICE_ERR_NO_INPUTS,
+ DEVICE_ERR_NO_OUTPUTS,
+ DEVICE_ERR_NO_DEVICES
+ };
+
/** \brief The class constructor
* \param jacktrip Pointer to the JackTrip class that connects all classes (mediator)
* \param NumInChans Number of Input Channels
* Packet Size, Bit Resolution, etc... Sub-classes should also call the parent
* method to ensure correct inizialization.
*/
- virtual void setup();
+ virtual void setup(bool verbose = true);
/// \brief Tell the audio server that we are ready to roll. The
/// process-callback will start running. This runs on its own thread.
/// \return 0 on success, otherwise a non-zero error code
- virtual int startProcess() const = 0;
+ virtual int startProcess() = 0;
/// \brief Stops the process-callback thread
/// \return 0 on success, otherwise a non-zero error code
- virtual int stopProcess() const = 0;
+ virtual int stopProcess() = 0;
/** \brief Process callback. Subclass should call this callback after obtaining the
in_buffer and out_buffer pointers.
* \param in_buffer Array of input audio samplers for each channel. The user
* Initialize all ProcessPlugin modules.
* The audio sampling rate (mSampleRate) must be set at this time.
*/
- void initPlugins();
+ void initPlugins(bool verbose = true);
virtual void connectDefaultPorts() = 0;
/** \brief Convert a 32bit number (sample_t) into one of the bit resolution
* supported (audioBitResolutionT).
* \return Sample Rate in Hz
*/
static int getSampleRateFromType(samplingRateT rate_type);
+ std::string getDevicesWarningMsg();
+ std::string getDevicesErrorMsg();
+ std::string getDevicesWarningHelpUrl();
+ std::string getDevicesErrorHelpUrl();
+
//------------------------------------------------------------------
private:
AudioTester* mAudioTesterP{nullptr};
protected:
+ void setDevicesWarningMsg(warningMessageT msg);
+ void setDevicesErrorMsg(errorMessageT msg);
+
bool mProcessingAudio; ///< Set when processing an audio callback buffer pair
const uint32_t MAX_AUDIO_BUFFER_SIZE = 8192;
+
+ std::string mWarningMsg;
+ std::string mErrorMsg;
+ std::string mWarningHelpUrl;
+ std::string mErrorHelpUrl;
};
#endif // __AUDIOINTERFACE_H__
JackAudioInterface::~JackAudioInterface() {}
//*******************************************************************************
-void JackAudioInterface::setup()
+void JackAudioInterface::setup(bool verbose)
{
setupClient();
- AudioInterface::setup();
+ AudioInterface::setup(verbose);
setProcessCallback();
}
}
//*******************************************************************************
-int JackAudioInterface::startProcess() const
+int JackAudioInterface::startProcess()
{
// Tell the JACK server that we are ready to roll. Our
// process() callback will start running now.
}
//*******************************************************************************
-int JackAudioInterface::stopProcess() const
+int JackAudioInterface::stopProcess()
{
QMutexLocker locker(&sJackMutex);
int code = (jack_deactivate(mClient));
virtual ~JackAudioInterface();
/// \brief Setup the client
- virtual void setup();
+ virtual void setup(bool verbose = true);
/** \brief Tell the JACK server that we are ready to roll. The
* process-callback will start running. This runs on its own thread.
* \return 0 on success, otherwise a non-zero error code
*/
- virtual int startProcess() const;
+ virtual int startProcess();
/** \brief Stops the process-callback thread
* \return 0 on success, otherwise a non-zero error code
*/
- virtual int stopProcess() const;
+ virtual int stopProcess();
/// \brief Connect the default ports, capture to sends, and receives to playback
void connectDefaultPorts();
, mStopOnTimeout(false)
, mSendRingBuffer(NULL)
, mReceiveRingBuffer(NULL)
+ , mRegulatorThreadPtr(NULL)
+ , mRegulatorWorkerPtr(NULL)
, mReceiverBindPort(receiver_bind_port)
, mSenderPeerPort(sender_peer_port)
, mSenderBindPort(sender_bind_port)
delete mDataProtocolReceiver;
delete mAudioInterface;
delete mPacketHeader;
+ delete mRegulatorWorkerPtr;
+ delete mRegulatorThreadPtr;
delete mSendRingBuffer;
delete mReceiveRingBuffer;
}
if (gVerboseFlag)
std::cout << " JackTrip:setupAudio before mAudioInterface->setup"
<< std::endl;
- mAudioInterface->setup();
+ mAudioInterface->setup(true);
if (gVerboseFlag)
std::cout << " JackTrip:setupAudio before mAudioInterface->getSampleRate"
<< std::endl;
mAudioInterface->setInputDevice(mInputDeviceName);
mAudioInterface->setOutputDevice(mOutputDeviceName);
mAudioInterface->setBufferSizeInSamples(mAudioBufferSize);
- mAudioInterface->setup();
+ mAudioInterface->setup(true);
// Setup might have reduced number of channels
mNumAudioChansIn = mAudioInterface->getNumInputChannels();
mNumAudioChansOut = mAudioInterface->getNumOutputChannels();
mAudioInterface->setInputDevice(mInputDeviceName);
mAudioInterface->setOutputDevice(mOutputDeviceName);
mAudioInterface->setBufferSizeInSamples(mAudioBufferSize);
- mAudioInterface->setup();
+ mAudioInterface->setup(true);
// Setup might have reduced number of channels
mNumAudioChansIn = mAudioInterface->getNumInputChannels();
mNumAudioChansOut = mAudioInterface->getNumOutputChannels();
ID
#endif // endwhere
);
+
+ if (mAudioInterface->getDevicesErrorMsg() != "") {
+ stop();
+ return;
+ }
+
// cc redundant with instance creator createHeader(mPacketHeaderType); next line
// fixme
createHeader(mPacketHeaderType);
mAudioInterface->appendProcessPluginToNetwork(i);
}
- mAudioInterface->initPlugins(); // mSampleRate known now, which plugins require
+ if (mBufferStrategy == 3) {
+ mRegulatorThreadPtr = new QThread();
+ mRegulatorThreadPtr->setObjectName("RegulatorThread");
+ Regulator* regulatorPtr = reinterpret_cast<Regulator*>(mReceiveRingBuffer);
+ RegulatorWorker* workerPtr = new RegulatorWorker(regulatorPtr);
+ workerPtr->moveToThread(mRegulatorThreadPtr);
+ QObject::connect(this, &JackTrip::signalReceivedNetworkPacket, workerPtr,
+ &RegulatorWorker::pullPacket, Qt::QueuedConnection);
+ mRegulatorThreadPtr->start();
+ mRegulatorWorkerPtr = workerPtr;
+ }
+
+ mAudioInterface->initPlugins(true); // mSampleRate known now, which plugins require
mAudioInterface->startProcess(); // Tell JACK server we are ready for audio flow now
if (mConnectDefaultAudioPorts) {
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();
{
mDataProtocolSender->wait();
mDataProtocolReceiver->wait();
+ if (mRegulatorThreadPtr != nullptr) {
+ mRegulatorThreadPtr->wait();
+ }
}
//*******************************************************************************
int getReceivePacketSizeInBytes() const;
virtual void sendNetworkPacket(const int8_t* ptrToSlot)
{
- mSendRingBuffer->insertSlotNonBlocking(ptrToSlot, 0, 0);
+ mSendRingBuffer->insertSlotNonBlocking(ptrToSlot, 0, 0, 0);
}
virtual void receiveBroadcastPacket(int8_t* ptrToReadSlot)
{
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)
{
mSendRingBuffer->readSlotBlocking(ptrToReadSlot);
}
- virtual bool writeAudioBuffer(const int8_t* ptrToSlot, int len, int lostLen)
- {
- return mReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot, len, lostLen);
- }
- virtual bool writeAudioBufferRegulator(const int8_t* ptrToSlot, int len, int seq,
- int lostLen)
+ virtual bool writeAudioBuffer(const int8_t* ptrToSlot, int len, int lostLen, int seq)
{
- return mReceiveRingBuffer->insertSlotNonBlockingRegulator(ptrToSlot, len, seq,
- lostLen);
+ return mReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot, len, lostLen, seq);
}
uint32_t getBufferSizeInSamples() const
{
void signalUdpWaitingTooLong();
void signalQueueLengthChanged(int queueLength);
void signalAudioStarted();
+ void signalReceivedNetworkPacket();
public:
/// \brief Set the AudioInteface object
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
}
//*******************************************************************************
-bool JitterBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen)
+bool JitterBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen,
+ [[maybe_unused]] int seq_num)
{
if (0 == len) {
len = mSlotSize;
int channels, int bit_res);
virtual ~JitterBuffer() {}
- virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen);
+ virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen,
+ int seq_num);
virtual void readSlotNonBlocking(int8_t* ptrToReadSlot);
virtual void readBroadcastSlot(int8_t* ptrToReadSlot);
if (gVerboseFlag)
cout << "mHist = " << mHist << " at " << mFPP << "\n";
mBytes = mFPP * mNumChannels * mBitResolutionMode;
- mXfrBuffer = new int8_t[mBytes];
+ mPullQueue = new int8_t[mBytes * 2];
+ mXfrBuffer = mPullQueue;
mPacketCnt = 0; // burg initialization
+ mNextPacket.store(mPullQueue + mBytes, std::memory_order_release);
mFadeUp.resize(mFPP, 0.0);
mFadeDown.resize(mFPP, 0.0);
for (int i = 0; i < mFPP; i++) {
changeGlobal_3(LostWindowMax);
changeGlobal_2(NumSlotsMax); // need hg if running GUI
if (m_b_BroadcastQueueLength) {
- m_b_ReceiveRingBuffer = new JitterBuffer(
+ m_b_BroadcastRingBuffer = new JitterBuffer(
mFPP, qLen, 48000, 1, m_b_BroadcastQueueLength, mNumChannels, mAudioBitRes);
qDebug() << "Broadcast started in Regulator with packet queue of"
<< m_b_BroadcastQueueLength;
Regulator::~Regulator()
{
- delete[] mXfrBuffer;
+ delete[] mPullQueue;
delete[] mZeros;
delete[] mAssembledPacket;
delete pushStat;
delete[] slot;
};
if (m_b_BroadcastQueueLength)
- delete m_b_ReceiveRingBuffer;
+ delete m_b_BroadcastRingBuffer;
}
void Regulator::setFPPratio()
};
//*******************************************************************************
-void Regulator::pullPacket(int8_t* buf)
+void Regulator::pullPacket()
{
QMutexLocker locker(&mMutex);
mSkip = 0;
memcpy(mXfrBuffer, mZeros, mBytes);
OUTPUT:
- memcpy(buf, mXfrBuffer, mBytes);
+ // swap positions of mXfrBuffer and mNextPacket
+ mNextPacket.store(mXfrBuffer, std::memory_order_release);
+ if (mXfrBuffer == mPullQueue) {
+ mXfrBuffer = mPullQueue + mBytes;
+ } else {
+ mXfrBuffer = mPullQueue;
+ }
};
//*******************************************************************************
#include <QDebug>
#include <QElapsedTimer>
+#include <atomic>
+#include <cstring>
#include "AudioInterface.h"
#include "RingBuffer.h"
// if (!mJackTrip->writeAudioBuffer(src, host_buf_size, last_seq_num))
// instead of
// if (!mJackTrip->writeAudioBuffer(src, host_buf_size, gap_size))
- virtual bool insertSlotNonBlockingRegulator(const int8_t* ptrToSlot,
- [[maybe_unused]] int len,
- [[maybe_unused]] int seq_num, int lostLen)
+ virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen,
+ int seq_num)
{
shimFPP(ptrToSlot, len, seq_num);
if (m_b_BroadcastQueueLength)
- m_b_ReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot, len, lostLen);
+ m_b_BroadcastRingBuffer->insertSlotNonBlocking(ptrToSlot, len, lostLen,
+ seq_num);
return (true);
}
- void pullPacket(int8_t* buf);
+ // called by RegulatorWorker after each audio callback, to prep next packet
+ void pullPacket();
+
+ virtual void readSlotNonBlocking(int8_t* ptrToReadSlot)
+ {
+ ::memcpy(ptrToReadSlot, mNextPacket.load(std::memory_order_acquire), mBytes);
+ }
- virtual void readSlotNonBlocking(int8_t* ptrToReadSlot) { pullPacket(ptrToReadSlot); }
virtual void readBroadcastSlot(int8_t* ptrToReadSlot)
{
- m_b_ReceiveRingBuffer->readSlotNonBlocking(ptrToReadSlot);
- m_b_ReceiveRingBuffer->readBroadcastSlot(ptrToReadSlot);
+ m_b_BroadcastRingBuffer->readBroadcastSlot(ptrToReadSlot);
}
// virtual QString getStats(uint32_t statCount, uint32_t lostCount);
BurgAlgorithm ba;
int mBytes;
int mBytesPeerPacket;
+ int8_t* mPullQueue;
int8_t* mXfrBuffer;
+ std::atomic<const void*> mNextPacket;
int8_t* mAssembledPacket;
int mPacketCnt;
sample_t bitsToSample(int ch, int frame);
void changeGlobal_2(int);
void changeGlobal_3(int);
void printParams();
- /// Pointer for the Receive RingBuffer
- RingBuffer* m_b_ReceiveRingBuffer;
+
+ /// Pointer for the Broadcast RingBuffer
+ RingBuffer* m_b_BroadcastRingBuffer;
int m_b_BroadcastQueueLength;
};
+
+class RegulatorWorker : public QObject
+{
+ Q_OBJECT;
+
+ public:
+ RegulatorWorker(Regulator* rPtr) : mRegulatorPtr(rPtr) {}
+ virtual ~RegulatorWorker() {}
+
+ public slots:
+ void pullPacket()
+ {
+ if (mRegulatorPtr != nullptr) {
+ mRegulatorPtr->pullPacket();
+ }
+ }
+
+ private:
+ Regulator* mRegulatorPtr;
+};
+
#endif //__REGULATOR_H__
}
//*******************************************************************************
-bool RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen)
+bool RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen,
+ [[maybe_unused]] int seq_num)
{
if (len != mSlotSize && 0 != len) {
// RingBuffer does not support mixed buf sizes
return true;
}
-//*******************************************************************************
-bool RingBuffer::insertSlotNonBlockingRegulator([[maybe_unused]] const int8_t* ptrToSlot,
- [[maybe_unused]] int len,
- [[maybe_unused]] int seq_num,
- [[maybe_unused]] int lostLen)
-{
- return true;
-}
-
//*******************************************************************************
void RingBuffer::readSlotNonBlocking(int8_t* ptrToReadSlot)
{
/** \brief Same as insertSlotBlocking but non-blocking (asynchronous)
* \param ptrToSlot Pointer to slot to insert into the RingBuffer
*/
- virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen);
-
- /** \brief Same as insertSlotNonBlocking but seq_num for Regulator
- * \param ptrToSlot Pointer to slot to insert into the RingBuffer
- */
- virtual bool insertSlotNonBlockingRegulator(const int8_t* ptrToSlot, int len,
- int seq_num, int lostLen);
+ virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen,
+ int seq_num);
/** \brief Same as readSlotBlocking but non-blocking (asynchronous)
* \param ptrToReadSlot Pointer to read slot from the RingBuffer
#include "RtAudioInterface.h"
+#include <QString>
#include <cstdlib>
#include "JackTrip.h"
//*******************************************************************************
RtAudioInterface::~RtAudioInterface()
{
- delete mRtAudio;
+ if (mRtAudio != NULL) {
+ delete mRtAudio;
+ }
}
//*******************************************************************************
-void RtAudioInterface::setup()
+void RtAudioInterface::setup(bool verbose)
{
// Initialize Buffer array to read and write audio and members
mNumInChans = getNumInputChannels();
cout << "Setting Up RtAudio Interface" << endl;
cout << gPrintSeparator << endl;
- mRtAudio = new RtAudio;
- int deviceId_input;
- int deviceId_output;
- unsigned int n_devices = mRtAudio->getDeviceCount();
- if (n_devices < 1) {
+ AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE);
+ AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NONE);
+
+ int index_in = -1;
+ int index_out = -1;
+ std::string api_in;
+ std::string api_out;
+
+ QStringList all_input_devices;
+ QStringList all_output_devices;
+ getDeviceList(&all_input_devices, NULL, true);
+ getDeviceList(&all_output_devices, NULL, false);
+
+ unsigned int n_devices_input = all_input_devices.size();
+ unsigned int n_devices_output = all_output_devices.size();
+ unsigned int n_devices_total = n_devices_input + n_devices_output;
+
+ RtAudio* rtAudioIn;
+ RtAudio* rtAudioOut;
+
+ // unsigned int n_devices = mRtAudio->getDeviceCount();
+ if (n_devices_total < 1) {
+ AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NO_DEVICES);
cout << "No audio devices found!" << endl;
std::exit(0);
} else {
- deviceId_input = getDeviceID();
- if (deviceId_input < 0) {
- auto inName = getInputDevice();
- deviceId_input = getDeviceIdFromName(inName, true);
- if (!inName.empty() && (deviceId_input < 0)) {
- throw std::runtime_error("Requested input device \"" + inName
- + "\" not found.");
- }
+ // Locate the selected input audio device
+ auto inName = getInputDevice();
+ getDeviceInfoFromName(inName, &index_in, &api_in, true);
+ if (!inName.empty() && (index_in < 0)) {
+ throw std::runtime_error("Requested input device \"" + inName
+ + "\" not found.");
}
- if (deviceId_input < 0) {
- cout << "Selecting default INPUT device" << endl;
- if (mRtAudio->getCurrentApi() == RtAudio::LINUX_PULSE) {
- deviceId_input = getDefaultDevice(true);
+ rtAudioIn = new RtAudio(RtAudio::getCompiledApiByName(api_in));
+
+ // The selected input audio device is not available, so select the default device
+ if (index_in < 0) {
+ // reset rtAudioIn using the system default
+ delete rtAudioIn;
+ rtAudioIn = new RtAudio;
+ api_in = RtAudio::getApiName(rtAudioIn->getCurrentApi());
+
+ // Edge case for Linux Pulse Audio
+ if (rtAudioIn->getCurrentApi() == RtAudio::LINUX_PULSE) {
+ index_in = getDefaultDeviceForLinuxPulseAudio(true);
} else {
- deviceId_input = mRtAudio->getDefaultInputDevice();
+ index_in = rtAudioIn->getDefaultInputDevice();
}
+
+ cout << "Selected default INPUT device" << endl;
+ } else {
+ cout << "Selected INPUT device " << inName << endl;
}
- deviceId_output = getDeviceID();
- if (deviceId_output < 0) {
- auto outName = getOutputDevice();
- deviceId_output = getDeviceIdFromName(outName, false);
- if (!outName.empty() && (deviceId_output < 0)) {
- throw std::runtime_error("Requested output device \"" + outName
- + "\" not found.");
- }
+ // Locate the selected output audio device
+ auto outName = getOutputDevice();
+ getDeviceInfoFromName(outName, &index_out, &api_out, false);
+ if (!outName.empty() && (index_out < 0)) {
+ throw std::runtime_error("Requested output device \"" + outName
+ + "\" not found.");
}
- if (deviceId_output < 0) {
- cout << "Selecting default OUTPUT device" << endl;
- if (mRtAudio->getCurrentApi() == RtAudio::LINUX_PULSE) {
- deviceId_output = getDefaultDevice(false);
+ rtAudioOut = new RtAudio(RtAudio::getCompiledApiByName(api_out));
+
+ // The selected output audio device is not available, so select the default device
+ if (index_out < 0) {
+ // reset rtAudioIn using the system default
+ delete rtAudioOut;
+ rtAudioOut = new RtAudio;
+ api_out = RtAudio::getApiName(rtAudioOut->getCurrentApi());
+
+ // Edge case for Linux Pulse Audio
+ if (rtAudioOut->getCurrentApi() == RtAudio::LINUX_PULSE) {
+ index_out = getDefaultDeviceForLinuxPulseAudio(false);
} else {
- deviceId_output = mRtAudio->getDefaultOutputDevice();
+ index_out = rtAudioOut->getDefaultOutputDevice();
}
+
+ cout << "Selected default OUTPUT device" << endl;
+
+ } else {
+ cout << "Selected OUTPUT device " << outName << endl;
}
}
- auto dev_info_input = mRtAudio->getDeviceInfo(deviceId_input);
- auto dev_info_output = mRtAudio->getDeviceInfo(deviceId_output);
+ auto dev_info_input = rtAudioIn->getDeviceInfo(index_in);
+ auto dev_info_output = rtAudioOut->getDeviceInfo(index_out);
if (static_cast<unsigned int>(getNumInputChannels()) > dev_info_input.inputChannels) {
setNumInputChannels(dev_info_input.inputChannels);
setNumOutputChannels(dev_info_output.outputChannels);
}
- cout << "INPUT DEVICE:" << endl;
- printDeviceInfo(deviceId_input);
- cout << gPrintSeparator << endl;
- cout << "OUTPUT DEVICE:" << endl;
- printDeviceInfo(deviceId_output);
- cout << gPrintSeparator << endl;
+ if (verbose) {
+ cout << "INPUT DEVICE:" << endl;
+ printDeviceInfo(api_in, index_in);
+
+ cout << gPrintSeparator << endl;
+ cout << "OUTPUT DEVICE:" << endl;
+
+ printDeviceInfo(api_out, index_out);
+ cout << gPrintSeparator << endl;
+ }
+
+ if (n_devices_input == 0) {
+ AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NO_INPUTS);
+ } else if (n_devices_output == 0) {
+ AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NO_OUTPUTS);
+ }
+
+ delete rtAudioIn;
+ delete rtAudioOut;
+ if (api_in == api_out) {
+ mRtAudio = new RtAudio(RtAudio::getCompiledApiByName(api_in));
+#ifdef _WIN32
+ if (api_in != "asio") {
+ AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_LATENCY);
+ AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NONE);
+ }
+#endif
+ } else {
+ AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE);
+ AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_INCOMPATIBLE);
+ mRtAudio = NULL;
+ }
RtAudio::StreamParameters in_params, out_params;
- in_params.deviceId = deviceId_input;
- out_params.deviceId = deviceId_output;
+ in_params.deviceId = index_in;
+ out_params.deviceId = index_out;
in_params.nChannels = getNumInputChannels();
out_params.nChannels = getNumOutputChannels();
unsigned int bufferFrames = getBufferSizeInSamples(); // mBufferSize;
try {
- // IMPORTANT NOTE: It's VERY important to remember to pass this
- // as the user data in the process callback, otherwise member won't
+ // IMPORTANT NOTE: It's VERY important to remember to pass "this"
+ // to the user data in the process callback, otherwise member won't
// be accessible
- mRtAudio->openStream(&out_params, &in_params, RTAUDIO_FLOAT32, sampleRate,
- &bufferFrames, &RtAudioInterface::wrapperRtAudioCallback,
- this, &options, &RtAudioInterface::RtAudioErrorCallback);
+ if (mRtAudio != NULL) {
+ mRtAudio->openStream(&out_params, &in_params, RTAUDIO_FLOAT32, sampleRate,
+ &bufferFrames, &RtAudioInterface::wrapperRtAudioCallback,
+ this, &options, &RtAudioInterface::RtAudioErrorCallback);
+ }
+
setBufferSize(bufferFrames);
} catch (RtAudioError& e) {
- std::cout << '\n' << e.getMessage() << '\n' << std::endl;
+ std::cout << e.getMessage() << '\n' << std::endl;
throw std::runtime_error(e.getMessage());
}
// Setup parent class
- AudioInterface::setup();
-}
-
-//*******************************************************************************
-void RtAudioInterface::listAllInterfaces()
-{
- RtAudio rtaudio;
- if (rtaudio.getDeviceCount() < 1) {
- cout << "No audio devices found!" << endl;
- } else {
- for (unsigned int i = 0; i < rtaudio.getDeviceCount(); i++) {
- printDeviceInfo(i);
- cout << gPrintSeparator << endl;
- }
- }
+ AudioInterface::setup(verbose);
}
//*******************************************************************************
void RtAudioInterface::printDevices()
{
- // TODO: evenntually list devices for all RtAudio-compiled backends
- RtAudio audio;
- audio.showWarnings(false);
- cout << "Available audio devices: " << endl;
- unsigned int devices = audio.getDeviceCount();
- RtAudio::DeviceInfo info;
- for (unsigned int i = 0; i < devices; i++) {
- info = audio.getDeviceInfo(i);
- if (info.probed == true) {
- std::cout << i << ": \"" << info.name << "\" ";
- std::cout << "(" << info.inputChannels << " ins, " << info.outputChannels
- << " outs)" << endl;
- }
- }
-}
-
-//*******************************************************************************
-int RtAudioInterface::getDeviceIdFromName(std::string deviceName, bool isInput)
-{
- RtAudio rtaudio;
- for (unsigned int i = 0; i < rtaudio.getDeviceCount(); i++) {
- auto info = rtaudio.getDeviceInfo(i);
- if (info.probed == true) {
- if (info.name == deviceName) {
- if (isInput && info.inputChannels > 0) {
- return i;
- } else if (!isInput && info.outputChannels > 0) {
- return i;
- }
+ std::vector<RtAudio::Api> apis;
+ RtAudio::getCompiledApi(apis);
+
+ for (uint32_t i = 0; i < apis.size(); i++) {
+ RtAudio rtaudio(apis.at(i));
+ unsigned int devices = rtaudio.getDeviceCount();
+ for (unsigned int j = 0; j < devices; j++) {
+ RtAudio::DeviceInfo info = rtaudio.getDeviceInfo(j);
+ if (info.probed == true) {
+ std::cout << "[" << RtAudio::getApiDisplayName(rtaudio.getCurrentApi())
+ << " - " << j << "]"
+ << ": \"";
+ std::cout << info.name << "\" ";
+ std::cout << "(" << info.inputChannels << " ins, " << info.outputChannels
+ << " outs)" << endl;
}
}
}
- return -1;
}
//*******************************************************************************
// Once this functinoality is provided upstream and in the distributions'
// package managers, the following function can be removed and the default device
// can be obtained by calls to getDefaultInputDevice() / getDefaultOutputDevice()
-unsigned int RtAudioInterface::getDefaultDevice(bool isInput)
+unsigned int RtAudioInterface::getDefaultDeviceForLinuxPulseAudio(bool isInput)
{
RtAudio rtaudio;
for (unsigned int i = 0; i < rtaudio.getDeviceCount(); i++) {
}
//*******************************************************************************
-void RtAudioInterface::printDeviceInfo(unsigned int deviceId)
+void RtAudioInterface::printDeviceInfo(std::string api, unsigned int deviceIndex)
{
- RtAudio rtaudio;
- RtAudio::DeviceInfo info;
- int i = deviceId;
- info = rtaudio.getDeviceInfo(i);
- std::vector<unsigned int> sampleRates;
- cout << "Audio Device [" << i << "] : " << info.name << endl;
+ RtAudio rtaudio(RtAudio::getCompiledApiByName(api));
+ RtAudio::DeviceInfo info = rtaudio.getDeviceInfo(deviceIndex);
+ std::vector<unsigned int> sampleRates = info.sampleRates;
+
+ cout << "Audio Device [" << RtAudio::getApiDisplayName(rtaudio.getCurrentApi())
+ << " - " << deviceIndex << "] : " << info.name << endl;
cout << " Output Channels : " << info.outputChannels << endl;
cout << " Input Channels : " << info.inputChannels << endl;
- sampleRates = info.sampleRates;
cout << " Supported Sampling Rates: ";
- for (unsigned int ii = 0; ii < sampleRates.size(); ii++) {
- cout << sampleRates[ii] << " ";
+ for (unsigned int i = 0; i < sampleRates.size(); i++) {
+ cout << sampleRates[i] << " ";
}
cout << endl;
if (info.isDefaultOutput) {
unsigned int nFrames, double /*streamTime*/,
RtAudioStreamStatus /*status*/)
{
- sample_t* inputBuffer_sample = (sample_t*)inputBuffer;
- sample_t* outputBuffer_sample = (sample_t*)outputBuffer;
-
- // Get input and output buffers
- //-------------------------------------------------------------------
- for (int i = 0; i < mNumInChans; i++) {
- // Input Ports are READ ONLY
- mInBuffer[i] = inputBuffer_sample + (nFrames * i);
- }
- for (int i = 0; i < mNumOutChans; i++) {
- // Output Ports are WRITABLE
- mOutBuffer[i] = outputBuffer_sample + (nFrames * i);
+ // TODO: this function may need more changes. As-is I'm not sure this will work
+
+ sample_t* inputBuffer_sample = NULL;
+ sample_t* outputBuffer_sample = NULL;
+
+ inputBuffer_sample = (sample_t*)inputBuffer;
+ outputBuffer_sample = (sample_t*)outputBuffer;
+
+ if (inputBuffer_sample != NULL && outputBuffer_sample != NULL) {
+ // Get input and output buffers
+ //-------------------------------------------------------------------
+ for (int i = 0; i < mNumInChans; i++) {
+ // Input Ports are READ ONLY
+ mInBuffer[i] = inputBuffer_sample + (nFrames * i);
+ }
+ for (int i = 0; i < mNumOutChans; i++) {
+ // Output Ports are WRITABLE
+ mOutBuffer[i] = outputBuffer_sample + (nFrames * i);
+ }
+
+ AudioInterface::callback(mInBuffer, mOutBuffer, nFrames);
}
- AudioInterface::callback(mInBuffer, mOutBuffer, nFrames);
return 0;
}
unsigned int nFrames, double streamTime,
RtAudioStreamStatus status, void* userData)
{
- return static_cast<RtAudioInterface*>(userData)->RtAudioCallback(
- outputBuffer, inputBuffer, nFrames, streamTime, status);
+ RtAudioInterface* interface = static_cast<RtAudioInterface*>(userData);
+ return interface->RtAudioCallback(outputBuffer, inputBuffer, nFrames, streamTime,
+ status);
}
//*******************************************************************************
const std::string& errorText)
{
if ((type != RtAudioError::WARNING) && (type != RtAudioError::DEBUG_WARNING)) {
- std::cout << '\n' << errorText << '\n' << std::endl;
+ std::cout << errorText << '\n' << std::endl;
throw std::runtime_error(errorText);
+ } else if (type == RtAudioError::WARNING) {
+ std::cout << errorText << '\n' << std::endl;
+ } else if (type == RtAudioError::DEBUG_WARNING) {
+ std::cout << errorText << '\n' << std::endl;
}
}
//*******************************************************************************
-int RtAudioInterface::startProcess() const
+int RtAudioInterface::startProcess()
{
try {
- mRtAudio->startStream();
+ if (mRtAudio != NULL) {
+ mRtAudio->startStream();
+ }
} catch (RtAudioError& e) {
- std::cout << '\n' << e.getMessage() << '\n' << std::endl;
+ std::cout << e.getMessage() << '\n' << std::endl;
return (-1);
}
return (0);
}
//*******************************************************************************
-int RtAudioInterface::stopProcess() const
+int RtAudioInterface::stopProcess()
{
try {
- mRtAudio->closeStream();
+ if (mRtAudio != NULL) {
+ mRtAudio->closeStream();
+ AudioInterface::setDevicesWarningMsg(AudioInterface::DEVICE_WARN_NONE);
+ AudioInterface::setDevicesErrorMsg(AudioInterface::DEVICE_ERR_NONE);
+ }
} catch (RtAudioError& e) {
- std::cout << '\n' << e.getMessage() << '\n' << std::endl;
+ std::cout << e.getMessage() << '\n' << std::endl;
return (-1);
}
return 0;
}
+
+//*******************************************************************************
+void RtAudioInterface::getDeviceList(QStringList* list, QStringList* categories,
+ bool isInput)
+{
+ RtAudio baseRtAudio;
+ RtAudio::Api baseRtAudioApi = baseRtAudio.getCurrentApi();
+
+ // Add (default)
+ list->clear();
+ list->append(QStringLiteral("(default)"));
+ if (categories != NULL) {
+#ifdef _WIN32
+ switch (baseRtAudioApi) {
+ case RtAudio::WINDOWS_ASIO:
+ categories->append(QStringLiteral("Low-Latency (ASIO)"));
+ break;
+ case RtAudio::WINDOWS_WASAPI:
+ categories->append(QStringLiteral("All Devices (Non-ASIO)"));
+ break;
+ case RtAudio::WINDOWS_DS:
+ categories->append(QStringLiteral("All Devices (Non-ASIO)"));
+ break;
+ default:
+ categories->append(QStringLiteral(""));
+ break;
+ }
+#else
+ categories->append(QStringLiteral(""));
+#endif
+ }
+
+ // Explicitly add default device
+ QString defaultDeviceName = "";
+ uint32_t defaultDeviceIdx;
+ if (isInput) {
+ defaultDeviceIdx = baseRtAudio.getDefaultInputDevice();
+ } else {
+ defaultDeviceIdx = baseRtAudio.getDefaultOutputDevice();
+ }
+
+ if (defaultDeviceIdx != 0) {
+ RtAudio::DeviceInfo info = baseRtAudio.getDeviceInfo(defaultDeviceIdx);
+ defaultDeviceName = QString::fromStdString(info.name);
+ }
+
+ if (defaultDeviceName != "") {
+ list->append(defaultDeviceName);
+ if (categories != NULL) {
+#ifdef _WIN32
+ switch (baseRtAudioApi) {
+ case RtAudio::WINDOWS_ASIO:
+ categories->append(QStringLiteral("Low-Latency (ASIO)"));
+ break;
+ case RtAudio::WINDOWS_WASAPI:
+ categories->append(QStringLiteral("All Devices (Non-ASIO)"));
+ break;
+ case RtAudio::WINDOWS_DS:
+ categories->append(QStringLiteral("All Devices (Non-ASIO)"));
+ break;
+ default:
+ categories->append(QStringLiteral(""));
+ break;
+ }
+#else
+ categories->append(QStringLiteral(""));
+#endif
+ }
+ }
+
+ std::vector<RtAudio::Api> apis;
+ RtAudio::getCompiledApi(apis);
+
+ for (uint32_t i = 0; i < apis.size(); i++) {
+ RtAudio::Api api = apis.at(i);
+ RtAudio rtaudio(api);
+ unsigned int devices = rtaudio.getDeviceCount();
+ for (unsigned int j = 0; j < devices; j++) {
+ RtAudio::DeviceInfo info = rtaudio.getDeviceInfo(j);
+ if (info.probed == true) {
+ // Don't include duplicate entries
+ if (list->contains(QString::fromStdString(info.name))) {
+ continue;
+ }
+
+ // Skip the default device, since we already added it
+ if (QString::fromStdString(info.name) == defaultDeviceName
+ && api == baseRtAudioApi) {
+ continue;
+ }
+
+ if (isInput && info.inputChannels > 0) {
+ list->append(QString::fromStdString(info.name));
+ } else if (!isInput && info.outputChannels > 0) {
+ list->append(QString::fromStdString(info.name));
+ }
+
+ if (categories == NULL) {
+ continue;
+ }
+
+#ifdef _WIN32
+ switch (api) {
+ case RtAudio::WINDOWS_ASIO:
+ categories->append("Low-Latency (ASIO)");
+ break;
+ case RtAudio::WINDOWS_WASAPI:
+ categories->append("All Devices (Non-ASIO)");
+ break;
+ case RtAudio::WINDOWS_DS:
+ categories->append("All Devices (Non-ASIO)");
+ break;
+ default:
+ categories->append("");
+ break;
+ }
+#else
+ categories->append("");
+#endif
+ }
+ }
+ }
+}
+
+//*******************************************************************************
+void RtAudioInterface::getDeviceInfoFromName(std::string deviceName, int* index,
+ std::string* api, bool isInput)
+{
+ std::vector<RtAudio::Api> apis;
+ RtAudio::getCompiledApi(apis);
+
+ for (uint32_t i = 0; i < apis.size(); i++) {
+ RtAudio rtaudio(apis.at(i));
+ unsigned int devices = rtaudio.getDeviceCount();
+ for (unsigned int j = 0; j < devices; j++) {
+ RtAudio::DeviceInfo info = rtaudio.getDeviceInfo(j);
+ if (info.probed == true && deviceName == info.name) {
+ if ((isInput && info.inputChannels > 0)
+ || (!isInput && info.outputChannels > 0)) {
+ *index = j;
+ *api = RtAudio::getApiName(rtaudio.getCurrentApi());
+ return;
+ }
+ }
+ }
+ }
+
+ *index = -1;
+ *api = "";
+ return;
+}
\ No newline at end of file
#include <RtAudio.h>
+#include <QQueue>
+
#include "AudioInterface.h"
#include "jacktrip_globals.h"
class JackTrip; // Forward declaration
virtual ~RtAudioInterface();
/// \brief List all available audio interfaces, with its properties
- virtual void listAllInterfaces();
static void printDevices();
- virtual int getDeviceIdFromName(std::string deviceName, bool isInput);
- virtual void setup();
- virtual int startProcess() const;
- virtual int stopProcess() const;
+ virtual void setup(bool verbose = true);
+ virtual int startProcess();
+ virtual int stopProcess();
/// \brief This has no effect in RtAudio
virtual void connectDefaultPorts() {}
+ static void getDeviceList(QStringList* list, QStringList* categories, bool isInput);
+ static void getDeviceInfoFromName(std::string deviceName, int* index,
+ std::string* api, bool isInput);
+
//--------------SETTERS---------------------------------------------
/// \brief This has no effect in RtAudio
virtual void setClientName(const QString& /*ClientName*/) {}
RtAudioStreamStatus status, void* userData);
static void RtAudioErrorCallback(RtAudioError::Type type,
const std::string& errorText);
- void printDeviceInfo(unsigned int deviceId);
+ void printDeviceInfo(std::string api, unsigned int deviceId);
int mNumInChans; ///< Number of Input Channels
int mNumOutChans; ///< Number of Output Channels
mInBuffer; ///< Vector of Input buffers/channel read from JACK
QVarLengthArray<float*>
mOutBuffer; ///< Vector of Output buffer/channel to write to JACK
- RtAudio* mRtAudio; ///< RtAudio class
- unsigned int getDefaultDevice(bool isInput);
+ RtAudio* mRtAudio; ///< RtAudio class if the input and output device are the same
+ unsigned int getDefaultDeviceForLinuxPulseAudio(bool isInput);
};
#endif // __RTAUDIOINTERFACE_H__
void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP)
{
// Get DNS Address
-#ifndef _WIN32
- // Don't make the following code conditional on windows
- //(Addresses a weird timing bug when in hub client mode)
if (!mPeerAddress.setAddress(peerHostOrIP)) {
-#endif
QHostInfo info = QHostInfo::fromName(peerHostOrIP);
if (!info.addresses().isEmpty()) {
// use the first IP address
}
// cout << "UdpDataProtocol::setPeerAddress IP Address Number: "
// << mPeerAddress.toString().toStdString() << endl;
-#ifndef _WIN32
}
-#endif
// check if the ip address is valid
if (mPeerAddress.protocol() == QAbstractSocket::IPv6Protocol) {
return sock_fd;
}
+void UdpDataProtocol::processControlPacket(const char* buf)
+{
+ // Control signal (currently just check for exit packet);
+ bool exit = true;
+ for (int i = 0; i < mControlPacketSize; i++) {
+ if (buf[i] != char(0xff)) {
+ exit = false;
+ i = mControlPacketSize;
+ }
+ }
+ if (exit && !mStopSignalSent) {
+ mStopSignalSent = true;
+ emit signalCeaseTransmission(QStringLiteral("Peer Stopped"));
+ std::cout << "Peer Stopped" << std::endl;
+ }
+}
+
//*******************************************************************************
int UdpDataProtocol::receivePacket(char* buf, const size_t n)
{
int n_bytes = ::recv(mSocket, buf, n, 0);
if (n_bytes == mControlPacketSize) {
- // Control signal (currently just check for exit packet);
- bool exit = true;
- for (int i = 0; i < mControlPacketSize; i++) {
- if (buf[i] != char(0xff)) {
- exit = false;
- i = mControlPacketSize;
- }
- }
- if (exit && !mStopSignalSent) {
- mStopSignalSent = true;
- emit signalCeaseTransmission(QStringLiteral("Peer Stopped"));
- std::cout << "Peer Stopped" << std::endl;
- }
+ processControlPacket(buf);
return 0;
}
return n_bytes;
mRevivedCount = 0;
mStatCount = 0;
- //Set up our platform specific polling mechanism. (kqueue, epoll)
-#if !defined (MANUAL_POLL) && !defined (_WIN32)
-#ifdef __linux__
+ //Set up our platform specific polling mechanism. (kqueue, epoll, overlapped I/O)
+#if !defined (MANUAL_POLL)
+#if defined (__linux__)
int epollfd = epoll_create1(0);
struct epoll_event change, event;
change.events = EPOLLIN;
change.data.fd = mSocket;
epoll_ctl(epollfd, EPOLL_CTL_ADD, mSocket, &change);
+#elif defined (_WIN32)
+ WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS];
+ WSAOVERLAPPED socketOverlapped;
+ WSABUF dataBuf;
+ dataBuf.len = full_redundant_packet_size;
+ dataBuf.buf = reinterpret_cast<char *>(full_redundant_packet);
+ DWORD recvBytes = 0, flags = 0, index, bytesTransferred = 0;
+
+ eventArray[0] = WSACreateEvent();
+ if (eventArray == WSA_INVALID_EVENT) {
+ emit signalError("Unable to set up network event monitoring");
+ cout << "ERROR: Unable to set up network event monitoring" << endl;
+ mStopped = true;
+ }
+ ZeroMemory(&socketOverlapped, sizeof(WSAOVERLAPPED));
+ socketOverlapped.hEvent = eventArray[0];
+
+ if (WSARecv(mSocket, &dataBuf, 1, &recvBytes, &flags, &socketOverlapped, NULL) == SOCKET_ERROR) {
+ int result = WSAGetLastError();
+ if (result != WSA_IO_PENDING) {
+ emit signalError("Unable to listen for incoming network packets");
+ cout << "ERROR: Unable to listen for incoming network packets" << endl;
+ mStopped = true;
+ }
+ }
#else
int kq = kqueue();
struct kevent change;
// arrive for a longer time
//timeout = UdpSocket.waitForReadyRead(30);
// timeout = cc unused!
-#if defined (_WIN32) || defined (MANUAL_POLL)
+#if defined (MANUAL_POLL)
waitForReady(60000); //60 seconds
- receivePacketRedundancy(full_redundant_packet, full_redundant_packet_size,
- full_packet_size, current_seq_num, last_seq_num,
- newer_seq_num);
- }
+ if (receivePacket(reinterpret_cast<char *>(full_redundant_packet), full_redundant_packet_size) > 0) {
+ receivePacketRedundancy(full_redundant_packet, full_redundant_packet_size,
+ full_packet_size, current_seq_num, last_seq_num,
+ newer_seq_num);
+ }
+ }
#else
// OLD CODE WITHOUT REDUNDANCY----------------------------------------------------
*/
//----------------------------------------------------------------------------------
-#ifdef __linux__
+#if defined(_WIN32)
+ index = WSAWaitForMultipleEvents(1, eventArray, FALSE, 10, FALSE);
+ if (index == WSA_WAIT_TIMEOUT) {
+ waitTime += 10;
+ emit signalWaitingTooLong(waitTime);
+ } else {
+ waitTime = 0;
+ WSAResetEvent(eventArray[index - WSA_WAIT_EVENT_0]);
+ WSAGetOverlappedResult(mSocket, &socketOverlapped, &bytesTransferred, FALSE, &flags);
+ if (bytesTransferred == mControlPacketSize) {
+ processControlPacket(reinterpret_cast<char *>(full_redundant_packet));
+ } else if (bytesTransferred > 0 ){
+ receivePacketRedundancy(full_redundant_packet, full_redundant_packet_size,
+ full_packet_size, current_seq_num, last_seq_num,
+ newer_seq_num);
+ }
+ WSARecv(mSocket, &dataBuf, 1, &recvBytes, &flags, &socketOverlapped, NULL);
+ }
+#else
+#if defined(__linux__)
int n = epoll_wait(epollfd, &event, 1, 10);
#else
int n = kevent(kq, &change, 1, &event, 1, &timeout);
#endif
if (n > 0) {
waitTime = 0;
- receivePacketRedundancy(full_redundant_packet, full_redundant_packet_size,
- full_packet_size, current_seq_num, last_seq_num,
- newer_seq_num);
+ if (receivePacket(reinterpret_cast<char *>(full_redundant_packet), full_redundant_packet_size) > 0) {
+ receivePacketRedundancy(full_redundant_packet, full_redundant_packet_size,
+ full_packet_size, current_seq_num, last_seq_num,
+ newer_seq_num);
+ }
} else {
waitTime += 10;
emit signalWaitingTooLong(waitTime);
}
+#endif
}
-#ifdef __linux__
+#if defined(__linux__)
close(epollfd);
+#elif defined(_WIN32)
+ WSACloseEvent(eventArray);
#else
close(kq);
#endif
-#endif // _WIN32 || MANUAL_POLL
+#endif // MANUAL_POLL
break; }
case SENDER : {
//*******************************************************************************
void UdpDataProtocol::receivePacketRedundancy(
- int8_t* full_redundant_packet, int full_redundant_packet_size, int full_packet_size,
+ int8_t* full_redundant_packet, [[maybe_unused]] int full_redundant_packet_size, int full_packet_size,
uint16_t& current_seq_num, uint16_t& last_seq_num, uint16_t& newer_seq_num)
{
- // This is blocking until we get a packet...
- if (receivePacket(reinterpret_cast<char*>(full_redundant_packet),
- full_redundant_packet_size)
- <= 0) {
- return;
- }
-
if (0.0 < mSimulatedLossRate || 0.0 < mSimulatedJitterRate) {
double x = mUniformDist(mRndEngine);
// Drop packets
src = dst;
}
int ok = true; // send audio buf to
- ok = (mJackTrip->getBufferStrategy() !=3) ? // ring or jitter
- mJackTrip->writeAudioBuffer(src, host_buf_size, gap_size)
- : mJackTrip->writeAudioBufferRegulator(src, host_buf_size, last_seq_num, gap_size);
+ ok = mJackTrip->writeAudioBuffer(src, host_buf_size, gap_size, last_seq_num);
if (!ok) {
emit signalError("Local and Peer buffer settings are incompatible");
cout << "ERROR: Local and Peer buffer settings are incompatible" << endl;
bool UdpDataProtocol::datagramAvailable()
{
//Currently using a simplified version of the way QUdpSocket checks for datagrams.
- //TODO: Consider changing to use poll() or select().
char c;
#if defined(_WIN32)
//Need to use the winsock version of the function for MSG_PEEK
void setSocket(int& socket);
#endif
+ void processControlPacket(const char* buf);
+
/** \brief Receives a packet. It blocks until a packet is received
*
* This function makes sure we receive a complete packet
* \param n size of packet to receive
* \return number of bytes read, -1 on error
*/
- // virtual int receivePacket(char* buf, const size_t n);
virtual int receivePacket(char* buf, const size_t n);
/** \brief Sends a packet
property int extraSettingsButtonWidth: 16
property int emptyListMessageWidth: 450
property int createMessageTopMargin: 16
- property int createButtonTopMargin: 48
+ property int createButtonTopMargin: 24
property int fontBig: 28
property int fontMedium: 11
Text {
id: emptyListMessage
visible: parent.count == 0 && !virtualstudio.showCreateStudio
- text: "No studios found that match your filter criteria"
+ text: "No studios found that match your filter criteria."
font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
color: textColour
width: emptyListMessageWidth
anchors.verticalCenter: parent.verticalCenter
}
+ Button {
+ id: resetFiltersButton
+ background: Rectangle {
+ radius: 6 * virtualstudio.uiScale
+ color: resetFiltersButton.down ? buttonPressedColour : (resetFiltersButton.hovered ? buttonHoverColour : buttonColour)
+ border.width: 1
+ border.color: resetFiltersButton.down ? buttonPressedStroke : (resetFiltersButton.hovered ? buttonHoverStroke : buttonStroke)
+ }
+ visible: parent.count == 0 && !virtualstudio.showCreateStudio
+ onClicked: {
+ virtualstudio.showSelfHosted = false;
+ virtualstudio.showInactive = true;
+ refreshing = true;
+ refresh();
+ }
+ anchors.top: emptyListMessage.bottom
+ anchors.topMargin: createButtonTopMargin
+ anchors.horizontalCenter: emptyListMessage.horizontalCenter
+ width: 120 * virtualstudio.uiScale; height: 32 * virtualstudio.uiScale
+ Text {
+ text: "Reset Filters"
+ font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+ anchors {horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
+ color: textColour
+ }
+ }
+
Rectangle {
id: newUserEmptyList
anchors.fill: parent
Text {
id: createStudioMessage
- text: "JackTrip works by connecting your computer's audio to a Virtual Studio. Create your first Studio to get started!"
+ text: "Looks like you're not a member of any studios!\nHave the studio owner send you an invite link, or create your own studio to invite others."
font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
color: textColour
width: emptyListMessageWidth
border.width: 1
border.color: settingsButton.down ? buttonPressedStroke : (settingsButton.hovered ? buttonHoverStroke : buttonStroke)
}
- onClicked: window.state = "settings"
+ onClicked: virtualstudio.windowState = "settings"
display: AbstractButton.TextBesideIcon
font {
family: "Poppins";
}
Image {
+ id: jtlogo
x: parent.width - (49 * virtualstudio.uiScale); y: 16 * virtualstudio.uiScale
width: 32 * virtualstudio.uiScale; height: 59 * virtualstudio.uiScale
source: "logo.svg"
+ sourceSize: Qt.size(jtlogo.width,jtlogo.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
Text {
source: "mic.svg"
x: 0; y: 0
width: 18 * virtualstudio.uiScale; height: 28 * virtualstudio.uiScale
+ sourceSize: Qt.size(mic.width,mic.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
Colorize {
anchors.top: inputDeviceHeader.bottom
anchors.left: inputDeviceHeader.left
text: virtualstudio.audioBackend == "JACK" ?
- virtualstudio.audioBackend : inputComboModel[virtualstudio.inputDevice]
+ virtualstudio.audioBackend : inputComboModel.filter(item => item.type === "element")[virtualstudio.inputDevice].text
font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
color: textColour
elide: Text.ElideRight
source: "headphones.svg"
x: 0; y: 0
width: 28 * virtualstudio.uiScale; height: 28 * virtualstudio.uiScale
+ sourceSize: Qt.size(headphones.width,headphones.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
Colorize {
anchors.top: outputDeviceHeader.bottom
anchors.left: outputDeviceHeader.left
text: virtualstudio.audioBackend == "JACK" ?
- virtualstudio.audioBackend : outputComboModel[virtualstudio.outputDevice]
+ virtualstudio.audioBackend : outputComboModel.filter(item => item.type === "element")[virtualstudio.outputDevice].text
font {family: "Poppins"; pixelSize: fontTiny * virtualstudio.fontScale * virtualstudio.uiScale }
color: textColour
elide: Text.ElideRight
width: 11.57 * virtualstudio.uiScale; height: 18 * virtualstudio.uiScale
anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter }
source: virtualstudio.inputMuted ? "micoff.svg" : "mic.svg"
+ sourceSize: Qt.size(micMute.width,micMute.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
Colorize {
anchors.fill: micMute
source: "network.svg"
x: 0; y: 0
width: 28 * virtualstudio.uiScale; height: 28 * virtualstudio.uiScale
+ sourceSize: Qt.size(network.width,network.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
Colorize {
border.color: buttonStroke
layer.enabled: !backButton.down
}
- onClicked: { window.state = "browse" }
+ onClicked: { virtualstudio.windowState = "browse" }
width: 256 * virtualstudio.uiScale
height: 42 * virtualstudio.uiScale
anchors.horizontalCenter: parent.horizontalCenter
property string buttonPressedStroke: virtualstudio.darkMode ? "#6F6C6C" : "#B0B5B5"
Image {
+ id: jtlogo
source: "logo.svg"
anchors.horizontalCenter: parent.horizontalCenter
y: 35 * virtualstudio.uiScale
width: 50 * virtualstudio.uiScale; height: 92 * virtualstudio.uiScale
+ sourceSize: Qt.size(jtlogo.width,jtlogo.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
Text {
color: shadowColour
}
}
- onClicked: { window.state = "login"; virtualstudio.toVirtualStudio(); }
+ onClicked: { virtualstudio.windowState = "login"; virtualstudio.toVirtualStudio(); }
x: parent.width / 2 - (265 * virtualstudio.uiScale); y: 290 * virtualstudio.uiScale
width: 234 * virtualstudio.uiScale; height: 49 * virtualstudio.uiScale
Text {
color: shadowColour
}
}
- onClicked: { window.state = "login"; virtualstudio.toStandard(); }
+ onClicked: { virtualstudio.windowState = "login"; virtualstudio.toStandard(); }
x: parent.width / 2 + (32 * virtualstudio.uiScale); y: 290 * virtualstudio.uiScale
width: 234 * virtualstudio.uiScale; height: 49 * virtualstudio.uiScale
Text {
source: "logo.svg"
x: parent.width / 2 - (150 * virtualstudio.uiScale); y: 110 * virtualstudio.uiScale
width: 42 * virtualstudio.uiScale; height: 76 * virtualstudio.uiScale
+ sourceSize: Qt.size(loginLogo.width,loginLogo.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
Image {
color: shadowColour
}
}
- onClicked: { window.state = "start" }
+ onClicked: { virtualstudio.windowState = "start" }
anchors.horizontalCenter: parent.horizontalCenter
y: 401 * virtualstudio.uiScale
width: 263 * virtualstudio.uiScale; height: 64 * virtualstudio.uiScale
property int clipWidth: 10 * virtualstudio.uiScale
required property bool clipped
- property string meterColor: virtualstudio.darkMode ? "#5B5858" : "#D3D4D4"
+ property bool enabled: true
+ property string meterColor: enabled ? (virtualstudio.darkMode ? "#5B5858" : "#D3D4D4") : "#EAECEC"
property string meterGreen: "#61C554"
property string meterYellow: "#F5BF4F"
function getBoxColor (idx, level) {
+ if (!enabled) {
+ return meterColor;
+ }
+
// Case where the meter should be filled
if (level > (idx / bins)) {
let fillColor = meterGreen;
--- /dev/null
+<svg width="260" height="250" viewBox="0 0 260 250" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="1" y="1" width="258" height="248" rx="9" stroke="#0F0D0D" stroke-width="2"/>
+<rect x="18" y="205" width="108" height="26" rx="5" stroke="#0F0D0D" stroke-width="2"/>
+<path d="M42.1189 222H44.9021C47.4861 222 48.9158 220.4 48.9158 217.775V217.764C48.9158 215.145 47.4802 213.545 44.9021 213.545H42.1189V222ZM42.9216 221.273V214.271H44.8552C46.9177 214.271 48.0955 215.59 48.0955 217.77V217.781C48.0955 219.961 46.9236 221.273 44.8552 221.273H42.9216ZM53.151 222.105C54.9147 222.105 56.0221 220.857 56.0221 218.848V218.836C56.0221 216.826 54.9147 215.584 53.151 215.584C51.3873 215.584 50.2857 216.826 50.2857 218.836V218.848C50.2857 220.857 51.3873 222.105 53.151 222.105ZM53.151 221.414C51.8502 221.414 51.0768 220.441 51.0768 218.848V218.836C51.0768 217.242 51.8502 216.275 53.151 216.275C54.4518 216.275 55.2252 217.242 55.2252 218.836V218.848C55.2252 220.441 54.4518 221.414 53.151 221.414ZM57.6323 222H58.4057V218.25C58.4057 217.061 59.1616 216.275 60.2749 216.275C61.3706 216.275 61.9155 216.891 61.9155 218.092V222H62.6889V217.898C62.6889 216.428 61.8862 215.584 60.4799 215.584C59.4956 215.584 58.8159 216.035 58.4819 216.797H58.4057V215.689H57.6323V222ZM64.598 216.662H65.1898L66.0335 213.545H65.1312L64.598 216.662ZM70.0871 222.041C70.2746 222.041 70.4503 222.023 70.6261 221.994V221.326C70.4679 221.344 70.3507 221.35 70.175 221.35C69.4601 221.35 69.1847 221.027 69.1847 220.254V216.34H70.6261V215.689H69.1847V214.049H68.382V215.689H67.3683V216.34H68.382V220.43C68.382 221.578 68.88 222.041 70.0871 222.041ZM74.8251 222H75.6688L76.6063 219.41H80.1629L81.1004 222H81.9442L78.7801 213.545H77.9833L74.8251 222ZM78.3465 214.594H78.4168L79.911 218.719H76.8524L78.3465 214.594ZM83.3845 222H84.1579V213.176H83.3845V222ZM86.2076 222H86.981V213.176H86.2076V222ZM91.5209 222.105C93.2846 222.105 94.392 220.857 94.392 218.848V218.836C94.392 216.826 93.2846 215.584 91.5209 215.584C89.7572 215.584 88.6557 216.826 88.6557 218.836V218.848C88.6557 220.857 89.7572 222.105 91.5209 222.105ZM91.5209 221.414C90.2201 221.414 89.4467 220.441 89.4467 218.848V218.836C89.4467 217.242 90.2201 216.275 91.5209 216.275C92.8217 216.275 93.5951 217.242 93.5951 218.836V218.848C93.5951 220.441 92.8217 221.414 91.5209 221.414ZM96.969 222H97.7776L99.3303 216.762H99.4065L100.959 222H101.774L103.573 215.689H102.799L101.387 221.045H101.317L99.758 215.689H98.9905L97.4319 221.045H97.3616L95.9612 215.689H95.1819L96.969 222Z" fill="#272525"/>
+<path d="M141.5 205H239.5C242.261 205 244.5 207.239 244.5 210V226C244.5 228.761 242.261 231 239.5 231H141.5C138.739 231 136.5 228.761 136.5 226V210C136.5 207.239 138.739 205 141.5 205Z" fill="white" stroke="#0F0D0D" stroke-width="2"/>
+<path d="M185.77 213.328C183.286 213.328 181.698 215.027 181.698 217.77C181.698 220.512 183.262 222.217 185.77 222.217C188.272 222.217 189.831 220.512 189.831 217.77C189.831 215.033 188.266 213.328 185.77 213.328ZM185.77 214.881C187.147 214.881 188.026 216 188.026 217.77C188.026 219.533 187.147 220.664 185.77 220.664C184.381 220.664 183.508 219.533 183.508 217.77C183.508 216 184.399 214.881 185.77 214.881ZM193.228 222V219.451L194.066 218.461L196.521 222H198.642L195.343 217.254L198.425 213.545H196.456L193.333 217.312H193.228V213.545H191.458V222H193.228Z" fill="#272525"/>
+<path d="M20.1581 104.84L18.9647 108.814H20.7801L21.5164 104.84H20.1581ZM23.0716 104.84L21.9735 108.814H23.7889L24.43 104.84H23.0716ZM24.6637 111.309C24.6637 113.105 25.8951 114.235 27.774 114.235C29.7164 114.235 30.8844 113.13 30.8844 111.156V104.84H28.9674V111.144C28.9674 112.045 28.5357 112.521 27.7486 112.521C27.0123 112.521 26.5299 112.045 26.5172 111.309H24.6637ZM35.3011 112.718C34.66 112.718 34.2093 112.4 34.2093 111.88C34.2093 111.378 34.5965 111.093 35.39 111.036L36.8055 110.947V111.461C36.8055 112.172 36.1581 112.718 35.3011 112.718ZM34.6917 114.108C35.5995 114.108 36.3612 113.727 36.723 113.086H36.8373V114H38.6273V109.22C38.6273 107.722 37.5799 106.846 35.7264 106.846C33.9681 106.846 32.7811 107.659 32.6605 108.954H34.3617C34.514 108.535 34.9583 108.306 35.6249 108.306C36.3866 108.306 36.8055 108.636 36.8055 109.22V109.792L35.1107 109.893C33.3714 109.995 32.4002 110.731 32.4002 112C32.4002 113.283 33.346 114.108 34.6917 114.108ZM46.586 109.468C46.4337 107.875 45.3229 106.846 43.4693 106.846C41.2794 106.846 40.0479 108.16 40.0479 110.483C40.0479 112.832 41.2857 114.152 43.4693 114.152C45.2911 114.152 46.4337 113.143 46.586 111.575H44.8595C44.7198 112.267 44.2247 112.635 43.4693 112.635C42.4791 112.635 41.9142 111.88 41.9142 110.483C41.9142 109.106 42.4728 108.363 43.4693 108.363C44.2564 108.363 44.7325 108.801 44.8595 109.468H46.586ZM50.0062 109.734H49.8919V104.326H48.0448V114H49.8919V111.677L50.3998 111.156L52.4437 114H54.6336L51.7835 110.001L54.4496 106.999H52.3485L50.0062 109.734ZM59.7169 114V106.478H62.4654V104.84H55.0514V106.478H57.7999V114H59.7169ZM63.1878 114H65.035V110.122C65.035 109.138 65.7269 108.541 66.7362 108.541C67.0345 108.541 67.4662 108.598 67.6122 108.655V106.973C67.4535 106.916 67.1424 106.884 66.8885 106.884C65.9999 106.884 65.2762 107.417 65.0921 108.116H64.9779V106.999H63.1878V114ZM68.8107 114H70.6578V106.999H68.8107V114ZM69.7374 106.021C70.3976 106.021 70.8292 105.634 70.8292 105.088C70.8292 104.536 70.3976 104.155 69.7374 104.155C69.0709 104.155 68.6456 104.536 68.6456 105.088C68.6456 105.634 69.0709 106.021 69.7374 106.021ZM76.5218 106.884C75.557 106.884 74.7635 107.36 74.3827 108.147H74.2684V106.999H72.4784V116.323H74.3255V112.927H74.4398C74.7826 113.67 75.5506 114.108 76.5536 114.108C78.3055 114.108 79.3783 112.756 79.3783 110.496C79.3783 108.23 78.2928 106.884 76.5218 106.884ZM75.8934 112.565C74.9159 112.565 74.3065 111.785 74.3065 110.502C74.3065 109.22 74.9159 108.433 75.8998 108.433C76.8837 108.433 77.4803 109.214 77.4803 110.496C77.4803 111.791 76.89 112.565 75.8934 112.565ZM82.2145 108.814L83.3063 104.84H81.4972L80.8497 108.814H82.2145ZM85.128 108.814L86.3151 104.84H84.506L83.7633 108.814H85.128ZM100.95 106.999H99.1158L98.0748 112.02H97.9606L96.7291 106.999H94.9645L93.7394 112.02H93.6251L92.5841 106.999H90.7115L92.5778 114H94.5201L95.7579 109.169H95.8722L97.1227 114H99.0904L100.95 106.999ZM105.031 114.152C107.182 114.152 108.477 112.788 108.477 110.496C108.477 108.224 107.163 106.846 105.031 106.846C102.898 106.846 101.584 108.23 101.584 110.496C101.584 112.788 102.879 114.152 105.031 114.152ZM105.031 112.642C104.04 112.642 103.482 111.861 103.482 110.496C103.482 109.15 104.047 108.357 105.031 108.357C106.008 108.357 106.579 109.15 106.579 110.496C106.579 111.854 106.015 112.642 105.031 112.642ZM116.271 106.999H114.424V111.036C114.424 111.969 113.929 112.546 113.008 112.546C112.158 112.546 111.72 112.051 111.72 111.086V106.999H109.873V111.562C109.873 113.188 110.812 114.152 112.323 114.152C113.383 114.152 114.037 113.689 114.367 112.876H114.481V114H116.271V106.999ZM118.136 114H119.983V104.326H118.136V114ZM124.362 114.108C125.333 114.108 126.127 113.657 126.501 112.902H126.615V114H128.405V104.326H126.558V108.116H126.45C126.089 107.341 125.308 106.884 124.362 106.884C122.604 106.884 121.512 108.262 121.512 110.49C121.512 112.724 122.597 114.108 124.362 114.108ZM124.99 108.433C125.974 108.433 126.577 109.227 126.577 110.502C126.577 111.785 125.981 112.565 124.99 112.565C124 112.565 123.41 111.791 123.41 110.496C123.41 109.214 124.006 108.433 124.99 108.433ZM133.633 114H135.481V104.326H133.633V114ZM137.365 114H139.212V106.999H137.365V114ZM138.291 106.021C138.952 106.021 139.383 105.634 139.383 105.088C139.383 104.536 138.952 104.155 138.291 104.155C137.625 104.155 137.2 104.536 137.2 105.088C137.2 105.634 137.625 106.021 138.291 106.021ZM143.057 109.734H142.943V104.326H141.096V114H142.943V111.677L143.451 111.156L145.495 114H147.685L144.835 110.001L147.501 106.999H145.4L143.057 109.734ZM151.41 108.262C152.273 108.262 152.831 108.839 152.87 109.766H149.886C149.95 108.858 150.553 108.262 151.41 108.262ZM152.908 112.007C152.711 112.47 152.209 112.73 151.479 112.73C150.515 112.73 149.905 112.083 149.88 111.042V110.947H154.685V110.382C154.685 108.16 153.466 106.846 151.403 106.846C149.321 106.846 148.039 108.255 148.039 110.534C148.039 112.807 149.296 114.152 151.429 114.152C153.142 114.152 154.349 113.327 154.628 112.007H152.908ZM159.976 105.335V107.068H158.885V108.522H159.976V112.108C159.976 113.473 160.649 114.025 162.35 114.025C162.706 114.025 163.049 113.987 163.277 113.943V112.534C163.1 112.553 162.973 112.559 162.731 112.559C162.103 112.559 161.824 112.28 161.824 111.67V108.522H163.277V107.068H161.824V105.335H159.976ZM167.821 114.152C169.973 114.152 171.268 112.788 171.268 110.496C171.268 108.224 169.954 106.846 167.821 106.846C165.688 106.846 164.374 108.23 164.374 110.496C164.374 112.788 165.669 114.152 167.821 114.152ZM167.821 112.642C166.831 112.642 166.272 111.861 166.272 110.496C166.272 109.15 166.837 108.357 167.821 108.357C168.798 108.357 169.37 109.15 169.37 110.496C169.37 111.854 168.805 112.642 167.821 112.642ZM178.552 112.718C177.911 112.718 177.461 112.4 177.461 111.88C177.461 111.378 177.848 111.093 178.641 111.036L180.057 110.947V111.461C180.057 112.172 179.409 112.718 178.552 112.718ZM177.943 114.108C178.851 114.108 179.612 113.727 179.974 113.086H180.089V114H181.879V109.22C181.879 107.722 180.831 106.846 178.978 106.846C177.219 106.846 176.032 107.659 175.912 108.954H177.613C177.765 108.535 178.21 108.306 178.876 108.306C179.638 108.306 180.057 108.636 180.057 109.22V109.792L178.362 109.893C176.623 109.995 175.652 110.731 175.652 112C175.652 113.283 176.597 114.108 177.943 114.108ZM189.837 109.468C189.685 107.875 188.574 106.846 186.721 106.846C184.531 106.846 183.299 108.16 183.299 110.483C183.299 112.832 184.537 114.152 186.721 114.152C188.542 114.152 189.685 113.143 189.837 111.575H188.111C187.971 112.267 187.476 112.635 186.721 112.635C185.73 112.635 185.165 111.88 185.165 110.483C185.165 109.106 185.724 108.363 186.721 108.363C187.508 108.363 187.984 108.801 188.111 109.468H189.837ZM197.428 109.468C197.276 107.875 196.165 106.846 194.311 106.846C192.121 106.846 190.89 108.16 190.89 110.483C190.89 112.832 192.128 114.152 194.311 114.152C196.133 114.152 197.276 113.143 197.428 111.575H195.701C195.562 112.267 195.067 112.635 194.311 112.635C193.321 112.635 192.756 111.88 192.756 110.483C192.756 109.106 193.315 108.363 194.311 108.363C195.098 108.363 195.574 108.801 195.701 109.468H197.428ZM201.851 108.262C202.714 108.262 203.273 108.839 203.311 109.766H200.328C200.391 108.858 200.994 108.262 201.851 108.262ZM203.349 112.007C203.152 112.47 202.651 112.73 201.921 112.73C200.956 112.73 200.347 112.083 200.321 111.042V110.947H205.126V110.382C205.126 108.16 203.908 106.846 201.845 106.846C199.763 106.846 198.48 108.255 198.48 110.534C198.48 112.807 199.737 114.152 201.87 114.152C203.584 114.152 204.79 113.327 205.069 112.007H203.349ZM206.433 109.055C206.433 110.134 207.093 110.782 208.451 111.08L209.721 111.366C210.337 111.499 210.603 111.708 210.603 112.051C210.603 112.502 210.108 112.8 209.391 112.8C208.654 112.8 208.204 112.527 208.064 112.051H206.261C206.388 113.397 207.506 114.152 209.353 114.152C211.187 114.152 212.438 113.238 212.438 111.842C212.438 110.794 211.828 110.223 210.47 109.925L209.156 109.639C208.508 109.493 208.21 109.284 208.21 108.941C208.21 108.497 208.699 108.198 209.359 108.198C210.045 108.198 210.47 108.478 210.565 108.928H212.273C212.171 107.589 211.124 106.846 209.346 106.846C207.601 106.846 206.433 107.729 206.433 109.055ZM213.617 109.055C213.617 110.134 214.277 110.782 215.636 111.08L216.905 111.366C217.521 111.499 217.788 111.708 217.788 112.051C217.788 112.502 217.292 112.8 216.575 112.8C215.839 112.8 215.388 112.527 215.248 112.051H213.446C213.573 113.397 214.69 114.152 216.537 114.152C218.371 114.152 219.622 113.238 219.622 111.842C219.622 110.794 219.013 110.223 217.654 109.925L216.34 109.639C215.693 109.493 215.394 109.284 215.394 108.941C215.394 108.497 215.883 108.198 216.543 108.198C217.229 108.198 217.654 108.478 217.749 108.928H219.457C219.355 107.589 218.308 106.846 216.531 106.846C214.785 106.846 213.617 107.729 213.617 109.055ZM224.913 105.335V107.068H223.822V108.522H224.913V112.108C224.913 113.473 225.586 114.025 227.288 114.025C227.643 114.025 227.986 113.987 228.214 113.943V112.534C228.037 112.553 227.91 112.559 227.668 112.559C227.04 112.559 226.761 112.28 226.761 111.67V108.522H228.214V107.068H226.761V105.335H224.913ZM229.768 114H231.615V109.982C231.615 109.074 232.149 108.458 233.075 108.458C233.913 108.458 234.364 108.96 234.364 109.931V114H236.211V109.493C236.211 107.811 235.316 106.865 233.812 106.865C232.783 106.865 232.022 107.348 231.698 108.147H231.584V104.326H229.768V114ZM240.983 108.262C241.847 108.262 242.405 108.839 242.443 109.766H239.46C239.523 108.858 240.126 108.262 240.983 108.262ZM242.481 112.007C242.285 112.47 241.783 112.73 241.053 112.73C240.088 112.73 239.479 112.083 239.454 111.042V110.947H244.259V110.382C244.259 108.16 243.04 106.846 240.977 106.846C238.895 106.846 237.613 108.255 237.613 110.534C237.613 112.807 238.87 114.152 241.002 114.152C242.716 114.152 243.922 113.327 244.202 112.007H242.481ZM91.5539 130H93.4011V125.785C93.4011 125.004 93.9153 124.439 94.6389 124.439C95.3625 124.439 95.7942 124.865 95.7942 125.607V130H97.5715V125.715C97.5715 124.973 98.0539 124.439 98.803 124.439C99.5837 124.439 99.9709 124.852 99.9709 125.684V130H101.818V125.195C101.818 123.754 100.948 122.846 99.552 122.846C98.5744 122.846 97.7683 123.36 97.4446 124.141H97.3303C97.051 123.329 96.3782 122.846 95.3943 122.846C94.4739 122.846 93.7439 123.341 93.4582 124.141H93.344V122.999H91.5539V130ZM103.575 130H105.422V122.999H103.575V130ZM104.502 122.021C105.162 122.021 105.594 121.634 105.594 121.088C105.594 120.536 105.162 120.155 104.502 120.155C103.835 120.155 103.41 120.536 103.41 121.088C103.41 121.634 103.835 122.021 104.502 122.021ZM113.438 125.468C113.286 123.875 112.175 122.846 110.322 122.846C108.132 122.846 106.9 124.16 106.9 126.483C106.9 128.832 108.138 130.152 110.322 130.152C112.143 130.152 113.286 129.143 113.438 127.575H111.712C111.572 128.267 111.077 128.635 110.322 128.635C109.331 128.635 108.766 127.88 108.766 126.483C108.766 125.106 109.325 124.363 110.322 124.363C111.109 124.363 111.585 124.801 111.712 125.468H113.438ZM114.833 130H116.681V126.122C116.681 125.138 117.373 124.541 118.382 124.541C118.68 124.541 119.112 124.598 119.258 124.655V122.973C119.099 122.916 118.788 122.884 118.534 122.884C117.646 122.884 116.922 123.417 116.738 124.116H116.624V122.999H114.833V130ZM123.383 130.152C125.534 130.152 126.829 128.788 126.829 126.496C126.829 124.224 125.515 122.846 123.383 122.846C121.25 122.846 119.936 124.23 119.936 126.496C119.936 128.788 121.231 130.152 123.383 130.152ZM123.383 128.642C122.392 128.642 121.834 127.861 121.834 126.496C121.834 125.15 122.399 124.357 123.383 124.357C124.36 124.357 124.931 125.15 124.931 126.496C124.931 127.854 124.366 128.642 123.383 128.642ZM132.332 122.884C131.367 122.884 130.573 123.36 130.192 124.147H130.078V122.999H128.288V132.323H130.135V128.927H130.25C130.592 129.67 131.36 130.108 132.363 130.108C134.115 130.108 135.188 128.756 135.188 126.496C135.188 124.23 134.103 122.884 132.332 122.884ZM131.703 128.565C130.726 128.565 130.116 127.785 130.116 126.502C130.116 125.22 130.726 124.433 131.709 124.433C132.693 124.433 133.29 125.214 133.29 126.496C133.29 127.791 132.7 128.565 131.703 128.565ZM136.704 130H138.551V125.982C138.551 125.074 139.084 124.458 140.011 124.458C140.849 124.458 141.3 124.96 141.3 125.931V130H143.147V125.493C143.147 123.811 142.252 122.865 140.747 122.865C139.719 122.865 138.957 123.348 138.634 124.147H138.519V120.326H136.704V130ZM147.995 130.152C150.147 130.152 151.442 128.788 151.442 126.496C151.442 124.224 150.128 122.846 147.995 122.846C145.862 122.846 144.548 124.23 144.548 126.496C144.548 128.788 145.843 130.152 147.995 130.152ZM147.995 128.642C147.005 128.642 146.446 127.861 146.446 126.496C146.446 125.15 147.011 124.357 147.995 124.357C148.973 124.357 149.544 125.15 149.544 126.496C149.544 127.854 148.979 128.642 147.995 128.642ZM152.901 130H154.748V125.963C154.748 125.042 155.275 124.439 156.132 124.439C157.008 124.439 157.42 124.947 157.42 125.912V130H159.267V125.474C159.267 123.798 158.429 122.846 156.874 122.846C155.84 122.846 155.129 123.335 154.805 124.122H154.691V122.999H152.901V130ZM164.04 124.262C164.903 124.262 165.461 124.839 165.5 125.766H162.516C162.58 124.858 163.183 124.262 164.04 124.262ZM165.538 128.007C165.341 128.47 164.839 128.73 164.109 128.73C163.145 128.73 162.535 128.083 162.51 127.042V126.947H167.315V126.382C167.315 124.16 166.096 122.846 164.033 122.846C161.951 122.846 160.669 124.255 160.669 126.534C160.669 128.807 161.926 130.152 164.059 130.152C165.772 130.152 166.979 129.327 167.258 128.007H165.538ZM170.113 130.171C170.799 130.171 171.237 129.708 171.237 129.067C171.237 128.426 170.799 127.969 170.113 127.969C169.434 127.969 168.99 128.426 168.99 129.067C168.99 129.708 169.434 130.171 170.113 130.171Z" fill="#272525"/>
+<path d="M23.7968 160V152.494H26.5214V151.545H20.0175V152.494H22.7421V160H23.7968ZM28.032 160H29.0398V156.262C29.0398 155.195 29.6609 154.48 30.7917 154.48C31.7468 154.48 32.2507 155.037 32.2507 156.156V160H33.2585V155.91C33.2585 154.428 32.4148 153.572 31.0788 153.572C30.112 153.572 29.4499 153.982 29.1335 154.68H29.0398V151.176H28.032V160ZM35.0738 160H36.0816V153.684H35.0738V160ZM35.5777 152.4C35.9644 152.4 36.2808 152.084 36.2808 151.697C36.2808 151.311 35.9644 150.994 35.5777 150.994C35.191 150.994 34.8746 151.311 34.8746 151.697C34.8746 152.084 35.191 152.4 35.5777 152.4ZM37.809 155.412C37.809 156.326 38.3481 156.836 39.5317 157.123L40.6157 157.387C41.2895 157.551 41.6176 157.844 41.6176 158.277C41.6176 158.857 41.0082 159.262 40.1586 159.262C39.35 159.262 38.8461 158.922 38.6762 158.389H37.6391C37.7504 159.438 38.7172 160.111 40.1235 160.111C41.559 160.111 42.6547 159.332 42.6547 158.201C42.6547 157.293 42.0805 156.777 40.8911 156.49L39.9184 156.256C39.1743 156.074 38.8227 155.805 38.8227 155.371C38.8227 154.809 39.4086 154.428 40.1586 154.428C40.9203 154.428 41.4125 154.762 41.5473 155.266H42.5434C42.4086 154.229 41.4887 153.572 40.1645 153.572C38.8227 153.572 37.809 154.363 37.809 155.412ZM49.4728 159.227C48.7404 159.227 48.1954 158.852 48.1954 158.207C48.1954 157.574 48.6173 157.24 49.5783 157.176L51.2775 157.064V157.645C51.2775 158.547 50.5099 159.227 49.4728 159.227ZM49.2853 160.111C50.129 160.111 50.8204 159.742 51.2306 159.068H51.3243V160H52.2853V155.676C52.2853 154.363 51.424 153.572 49.8829 153.572C48.5353 153.572 47.5392 154.24 47.4044 155.254H48.424C48.5646 154.756 49.0919 154.469 49.8478 154.469C50.7911 154.469 51.2775 154.896 51.2775 155.676V156.25L49.4552 156.361C47.9845 156.449 47.1525 157.1 47.1525 158.23C47.1525 159.385 48.0607 160.111 49.2853 160.111ZM57.1767 153.572C56.3154 153.572 55.5596 154.012 55.1553 154.738H55.0615V153.684H54.1006V162.109H55.1084V159.051H55.2021C55.5478 159.719 56.2744 160.111 57.1767 160.111C58.7822 160.111 59.831 158.816 59.831 156.842C59.831 154.855 58.7881 153.572 57.1767 153.572ZM56.9365 159.203C55.7998 159.203 55.0791 158.289 55.0791 156.842C55.0791 155.389 55.7998 154.48 56.9424 154.48C58.0967 154.48 58.7881 155.365 58.7881 156.842C58.7881 158.318 58.0967 159.203 56.9365 159.203ZM64.4412 153.572C63.5799 153.572 62.8241 154.012 62.4198 154.738H62.326V153.684H61.3651V162.109H62.3729V159.051H62.4666C62.8123 159.719 63.5389 160.111 64.4412 160.111C66.0467 160.111 67.0955 158.816 67.0955 156.842C67.0955 154.855 66.0526 153.572 64.4412 153.572ZM64.201 159.203C63.0643 159.203 62.3436 158.289 62.3436 156.842C62.3436 155.389 63.0643 154.48 64.2069 154.48C65.3612 154.48 66.0526 155.365 66.0526 156.842C66.0526 158.318 65.3612 159.203 64.201 159.203ZM71.9683 160H72.9761V156.086C72.9761 155.195 73.6734 154.551 74.6343 154.551C74.8335 154.551 75.1968 154.586 75.2788 154.609V153.602C75.1499 153.584 74.939 153.572 74.7749 153.572C73.937 153.572 73.2105 154.006 73.023 154.621H72.9292V153.684H71.9683V160ZM78.8343 154.463C79.8363 154.463 80.5043 155.201 80.5277 156.32H77.0472C77.1234 155.201 77.8265 154.463 78.8343 154.463ZM80.4984 158.365C80.2347 158.922 79.684 159.221 78.8695 159.221C77.7972 159.221 77.1 158.43 77.0472 157.182V157.135H81.5883V156.748C81.5883 154.785 80.5511 153.572 78.8461 153.572C77.1117 153.572 75.9984 154.861 75.9984 156.848C75.9984 158.846 77.0941 160.111 78.8461 160.111C80.2289 160.111 81.2015 159.449 81.5062 158.365H80.4984ZM85.4485 153.572C83.8488 153.572 82.8059 154.861 82.8059 156.842C82.8059 158.834 83.8371 160.111 85.4485 160.111C86.3391 160.111 87.0188 159.742 87.4231 159.051H87.5168V162.109H88.5363V153.684H87.5637V154.738H87.4699C87.0949 154.029 86.3098 153.572 85.4485 153.572ZM85.677 159.203C84.5285 159.203 83.8488 158.324 83.8488 156.842C83.8488 155.365 84.5344 154.48 85.6828 154.48C86.8254 154.48 87.5461 155.395 87.5461 156.842C87.5461 158.295 86.8313 159.203 85.677 159.203ZM95.4962 153.684H94.4883V157.422C94.4883 158.529 93.879 159.191 92.7657 159.191C91.7579 159.191 91.336 158.664 91.336 157.527V153.684H90.3282V157.773C90.3282 159.268 91.0665 160.111 92.4844 160.111C93.4512 160.111 94.1251 159.713 94.4415 159.01H94.5352V160H95.4962V153.684ZM97.37 160H98.3778V153.684H97.37V160ZM97.8739 152.4C98.2607 152.4 98.5771 152.084 98.5771 151.697C98.5771 151.311 98.2607 150.994 97.8739 150.994C97.4872 150.994 97.1708 151.311 97.1708 151.697C97.1708 152.084 97.4872 152.4 97.8739 152.4ZM100.252 160H101.26V156.086C101.26 155.195 101.957 154.551 102.918 154.551C103.117 154.551 103.48 154.586 103.562 154.609V153.602C103.433 153.584 103.222 153.572 103.058 153.572C102.22 153.572 101.494 154.006 101.306 154.621H101.213V153.684H100.252V160ZM107.118 154.463C108.12 154.463 108.788 155.201 108.811 156.32H105.331C105.407 155.201 106.11 154.463 107.118 154.463ZM108.782 158.365C108.518 158.922 107.967 159.221 107.153 159.221C106.081 159.221 105.383 158.43 105.331 157.182V157.135H109.872V156.748C109.872 154.785 108.835 153.572 107.13 153.572C105.395 153.572 104.282 154.861 104.282 156.848C104.282 158.846 105.378 160.111 107.13 160.111C108.512 160.111 109.485 159.449 109.79 158.365H108.782ZM111.259 155.412C111.259 156.326 111.798 156.836 112.982 157.123L114.066 157.387C114.74 157.551 115.068 157.844 115.068 158.277C115.068 158.857 114.458 159.262 113.609 159.262C112.8 159.262 112.296 158.922 112.126 158.389H111.089C111.201 159.438 112.167 160.111 113.574 160.111C115.009 160.111 116.105 159.332 116.105 158.201C116.105 157.293 115.531 156.777 114.341 156.49L113.369 156.256C112.624 156.074 112.273 155.805 112.273 155.371C112.273 154.809 112.859 154.428 113.609 154.428C114.371 154.428 114.863 154.762 114.998 155.266H115.994C115.859 154.229 114.939 153.572 113.615 153.572C112.273 153.572 111.259 154.363 111.259 155.412ZM120.978 160H121.986V156.086C121.986 155.195 122.624 154.48 123.45 154.48C124.247 154.48 124.769 154.961 124.769 155.711V160H125.777V155.939C125.777 155.137 126.362 154.48 127.241 154.48C128.132 154.48 128.571 154.938 128.571 155.869V160H129.579V155.635C129.579 154.311 128.859 153.572 127.569 153.572C126.696 153.572 125.976 154.012 125.636 154.68H125.542C125.249 154.023 124.652 153.572 123.796 153.572C122.952 153.572 122.319 153.977 122.032 154.68H121.939V153.684H120.978V160ZM131.395 160H132.402V153.684H131.395V160ZM131.898 152.4C132.285 152.4 132.602 152.084 132.602 151.697C132.602 151.311 132.285 150.994 131.898 150.994C131.512 150.994 131.195 151.311 131.195 151.697C131.195 152.084 131.512 152.4 131.898 152.4ZM139.503 155.617C139.327 154.492 138.39 153.572 136.854 153.572C135.085 153.572 133.96 154.85 133.96 156.818C133.96 158.828 135.091 160.111 136.86 160.111C138.378 160.111 139.321 159.256 139.503 158.096H138.483C138.296 158.811 137.704 159.203 136.854 159.203C135.729 159.203 135.003 158.277 135.003 156.818C135.003 155.389 135.718 154.48 136.854 154.48C137.763 154.48 138.319 154.99 138.483 155.617H139.503ZM140.943 160H141.951V156.086C141.951 155.195 142.648 154.551 143.609 154.551C143.808 154.551 144.172 154.586 144.254 154.609V153.602C144.125 153.584 143.914 153.572 143.75 153.572C142.912 153.572 142.185 154.006 141.998 154.621H141.904V153.684H140.943V160ZM147.885 160.111C149.684 160.111 150.797 158.869 150.797 156.842C150.797 154.809 149.684 153.572 147.885 153.572C146.086 153.572 144.973 154.809 144.973 156.842C144.973 158.869 146.086 160.111 147.885 160.111ZM147.885 159.203C146.69 159.203 146.016 158.336 146.016 156.842C146.016 155.342 146.69 154.48 147.885 154.48C149.081 154.48 149.754 155.342 149.754 156.842C149.754 158.336 149.081 159.203 147.885 159.203ZM155.408 153.572C154.546 153.572 153.79 154.012 153.386 154.738H153.292V153.684H152.331V162.109H153.339V159.051H153.433C153.779 159.719 154.505 160.111 155.408 160.111C157.013 160.111 158.062 158.816 158.062 156.842C158.062 154.855 157.019 153.572 155.408 153.572ZM155.167 159.203C154.031 159.203 153.31 158.289 153.31 156.842C153.31 155.389 154.031 154.48 155.173 154.48C156.328 154.48 157.019 155.365 157.019 156.842C157.019 158.318 156.328 159.203 155.167 159.203ZM159.655 160H160.662V156.262C160.662 155.195 161.283 154.48 162.414 154.48C163.369 154.48 163.873 155.037 163.873 156.156V160H164.881V155.91C164.881 154.428 164.037 153.572 162.701 153.572C161.735 153.572 161.073 153.982 160.756 154.68H160.662V151.176H159.655V160ZM169.269 160.111C171.067 160.111 172.181 158.869 172.181 156.842C172.181 154.809 171.067 153.572 169.269 153.572C167.47 153.572 166.357 154.809 166.357 156.842C166.357 158.869 167.47 160.111 169.269 160.111ZM169.269 159.203C168.073 159.203 167.4 158.336 167.4 156.842C167.4 155.342 168.073 154.48 169.269 154.48C170.464 154.48 171.138 155.342 171.138 156.842C171.138 158.336 170.464 159.203 169.269 159.203ZM173.715 160H174.723V156.262C174.723 155.154 175.373 154.48 176.381 154.48C177.389 154.48 177.869 155.02 177.869 156.156V160H178.877V155.91C178.877 154.41 178.086 153.572 176.668 153.572C175.701 153.572 175.086 153.982 174.769 154.68H174.676V153.684H173.715V160ZM183.194 154.463C184.196 154.463 184.864 155.201 184.887 156.32H181.407C181.483 155.201 182.186 154.463 183.194 154.463ZM184.858 158.365C184.595 158.922 184.044 159.221 183.229 159.221C182.157 159.221 181.46 158.43 181.407 157.182V157.135H185.948V156.748C185.948 154.785 184.911 153.572 183.206 153.572C181.471 153.572 180.358 154.861 180.358 156.848C180.358 158.846 181.454 160.111 183.206 160.111C184.589 160.111 185.561 159.449 185.866 158.365H184.858ZM192.766 159.227C192.034 159.227 191.489 158.852 191.489 158.207C191.489 157.574 191.911 157.24 192.872 157.176L194.571 157.064V157.645C194.571 158.547 193.803 159.227 192.766 159.227ZM192.579 160.111C193.422 160.111 194.114 159.742 194.524 159.068H194.618V160H195.579V155.676C195.579 154.363 194.717 153.572 193.176 153.572C191.829 153.572 190.833 154.24 190.698 155.254H191.717C191.858 154.756 192.385 154.469 193.141 154.469C194.084 154.469 194.571 154.896 194.571 155.676V156.25L192.749 156.361C191.278 156.449 190.446 157.1 190.446 158.23C190.446 159.385 191.354 160.111 192.579 160.111ZM202.62 155.617C202.445 154.492 201.507 153.572 199.972 153.572C198.202 153.572 197.077 154.85 197.077 156.818C197.077 158.828 198.208 160.111 199.978 160.111C201.495 160.111 202.439 159.256 202.62 158.096H201.601C201.413 158.811 200.822 159.203 199.972 159.203C198.847 159.203 198.12 158.277 198.12 156.818C198.12 155.389 198.835 154.48 199.972 154.48C200.88 154.48 201.437 154.99 201.601 155.617H202.62ZM209.287 155.617C209.111 154.492 208.174 153.572 206.639 153.572C204.869 153.572 203.744 154.85 203.744 156.818C203.744 158.828 204.875 160.111 206.645 160.111C208.162 160.111 209.106 159.256 209.287 158.096H208.268C208.08 158.811 207.488 159.203 206.639 159.203C205.514 159.203 204.787 158.277 204.787 156.818C204.787 155.389 205.502 154.48 206.639 154.48C207.547 154.48 208.104 154.99 208.268 155.617H209.287ZM213.247 154.463C214.249 154.463 214.917 155.201 214.94 156.32H211.46C211.536 155.201 212.239 154.463 213.247 154.463ZM214.911 158.365C214.647 158.922 214.097 159.221 213.282 159.221C212.21 159.221 211.513 158.43 211.46 157.182V157.135H216.001V156.748C216.001 154.785 214.964 153.572 213.259 153.572C211.524 153.572 210.411 154.861 210.411 156.848C210.411 158.846 211.507 160.111 213.259 160.111C214.642 160.111 215.614 159.449 215.919 158.365H214.911ZM217.389 155.412C217.389 156.326 217.928 156.836 219.111 157.123L220.195 157.387C220.869 157.551 221.197 157.844 221.197 158.277C221.197 158.857 220.588 159.262 219.738 159.262C218.93 159.262 218.426 158.922 218.256 158.389H217.219C217.33 159.438 218.297 160.111 219.703 160.111C221.139 160.111 222.234 159.332 222.234 158.201C222.234 157.293 221.66 156.777 220.471 156.49L219.498 156.256C218.754 156.074 218.402 155.805 218.402 155.371C218.402 154.809 218.988 154.428 219.738 154.428C220.5 154.428 220.992 154.762 221.127 155.266H222.123C221.988 154.229 221.068 153.572 219.744 153.572C218.402 153.572 217.389 154.363 217.389 155.412ZM223.505 155.412C223.505 156.326 224.044 156.836 225.227 157.123L226.311 157.387C226.985 157.551 227.313 157.844 227.313 158.277C227.313 158.857 226.704 159.262 225.854 159.262C225.046 159.262 224.542 158.922 224.372 158.389H223.335C223.446 159.438 224.413 160.111 225.819 160.111C227.255 160.111 228.35 159.332 228.35 158.201C228.35 157.293 227.776 156.777 226.587 156.49L225.614 156.256C224.87 156.074 224.518 155.805 224.518 155.371C224.518 154.809 225.104 154.428 225.854 154.428C226.616 154.428 227.108 154.762 227.243 155.266H228.239C228.104 154.229 227.184 153.572 225.86 153.572C224.518 153.572 223.505 154.363 223.505 155.412ZM233.645 152.049V153.684H232.625V154.527H233.645V158.359C233.645 159.566 234.166 160.047 235.467 160.047C235.666 160.047 235.86 160.023 236.059 159.988V159.139C235.872 159.156 235.772 159.162 235.59 159.162C234.934 159.162 234.653 158.846 234.653 158.102V154.527H236.059V153.684H234.653V152.049H233.645ZM240.036 160.111C241.835 160.111 242.949 158.869 242.949 156.842C242.949 154.809 241.835 153.572 240.036 153.572C238.238 153.572 237.124 154.809 237.124 156.842C237.124 158.869 238.238 160.111 240.036 160.111ZM240.036 159.203C238.841 159.203 238.167 158.336 238.167 156.842C238.167 155.342 238.841 154.48 240.036 154.48C241.232 154.48 241.906 155.342 241.906 156.842C241.906 158.336 241.232 159.203 240.036 159.203ZM27.91 173.227C27.1776 173.227 26.6327 172.852 26.6327 172.207C26.6327 171.574 27.0546 171.24 28.0155 171.176L29.7147 171.064V171.645C29.7147 172.547 28.9472 173.227 27.91 173.227ZM27.7225 174.111C28.5663 174.111 29.2577 173.742 29.6679 173.068H29.7616V174H30.7225V169.676C30.7225 168.363 29.8612 167.572 28.3202 167.572C26.9725 167.572 25.9765 168.24 25.8417 169.254H26.8612C27.0018 168.756 27.5292 168.469 28.285 168.469C29.2284 168.469 29.7147 168.896 29.7147 169.676V170.25L27.8925 170.361C26.4218 170.449 25.5897 171.1 25.5897 172.23C25.5897 173.385 26.4979 174.111 27.7225 174.111ZM32.5964 174H33.6042V165.176H32.5964V174ZM35.5719 174H36.5797V165.176H35.5719V174ZM41.0844 174.111C42.8832 174.111 43.9965 172.869 43.9965 170.842C43.9965 168.809 42.8832 167.572 41.0844 167.572C39.2856 167.572 38.1723 168.809 38.1723 170.842C38.1723 172.869 39.2856 174.111 41.0844 174.111ZM41.0844 173.203C39.8891 173.203 39.2153 172.336 39.2153 170.842C39.2153 169.342 39.8891 168.48 41.0844 168.48C42.2797 168.48 42.9535 169.342 42.9535 170.842C42.9535 172.336 42.2797 173.203 41.0844 173.203ZM53.2415 167.684H52.2278L50.9856 172.734H50.8919L49.4798 167.684H48.513L47.1009 172.734H47.0071L45.7649 167.684H44.7454L46.5149 174H47.5345L48.9407 169.113H49.0345L50.4466 174H51.472L53.2415 167.684ZM58.3017 166.049V167.684H57.2822V168.527H58.3017V172.359C58.3017 173.566 58.8232 174.047 60.124 174.047C60.3232 174.047 60.5166 174.023 60.7158 173.988V173.139C60.5283 173.156 60.4287 173.162 60.247 173.162C59.5908 173.162 59.3095 172.846 59.3095 172.102V168.527H60.7158V167.684H59.3095V166.049H58.3017ZM62.2498 174H63.2576V170.262C63.2576 169.195 63.8787 168.48 65.0096 168.48C65.9647 168.48 66.4686 169.037 66.4686 170.156V174H67.4764V169.91C67.4764 168.428 66.6326 167.572 65.2967 167.572C64.3299 167.572 63.6678 167.982 63.3514 168.68H63.2576V165.176H62.2498V174ZM71.7878 168.463C72.7897 168.463 73.4577 169.201 73.4811 170.32H70.0007C70.0768 169.201 70.78 168.463 71.7878 168.463ZM73.4518 172.365C73.1882 172.922 72.6374 173.221 71.8229 173.221C70.7507 173.221 70.0534 172.43 70.0007 171.182V171.135H74.5417V170.748C74.5417 168.785 73.5046 167.572 71.7995 167.572C70.0651 167.572 68.9518 168.861 68.9518 170.848C68.9518 172.846 70.0475 174.111 71.7995 174.111C73.1823 174.111 74.155 173.449 74.4596 172.365H73.4518ZM80.4457 167.684H79.4379V174.328C79.4379 175.025 79.2035 175.307 78.4886 175.307H78.3363V176.168H78.5121C79.8363 176.168 80.4457 175.611 80.4457 174.311V167.684ZM79.9418 166.4C80.3285 166.4 80.6449 166.084 80.6449 165.697C80.6449 165.311 80.3285 164.994 79.9418 164.994C79.555 164.994 79.2386 165.311 79.2386 165.697C79.2386 166.084 79.555 166.4 79.9418 166.4ZM84.2649 173.227C83.5324 173.227 82.9875 172.852 82.9875 172.207C82.9875 171.574 83.4094 171.24 84.3703 171.176L86.0695 171.064V171.645C86.0695 172.547 85.302 173.227 84.2649 173.227ZM84.0774 174.111C84.9211 174.111 85.6125 173.742 86.0227 173.068H86.1164V174H87.0774V169.676C87.0774 168.363 86.216 167.572 84.675 167.572C83.3274 167.572 82.3313 168.24 82.1965 169.254H83.216C83.3567 168.756 83.884 168.469 84.6399 168.469C85.5832 168.469 86.0695 168.896 86.0695 169.676V170.25L84.2473 170.361C82.7766 170.449 81.9445 171.1 81.9445 172.23C81.9445 173.385 82.8528 174.111 84.0774 174.111ZM94.1192 169.617C93.9434 168.492 93.0059 167.572 91.4708 167.572C89.7012 167.572 88.5762 168.85 88.5762 170.818C88.5762 172.828 89.7071 174.111 91.4766 174.111C92.9942 174.111 93.9376 173.256 94.1192 172.096H93.0997C92.9122 172.811 92.3204 173.203 91.4708 173.203C90.3458 173.203 89.6192 172.277 89.6192 170.818C89.6192 169.389 90.334 168.48 91.4708 168.48C92.379 168.48 92.9356 168.99 93.0997 169.617H94.1192ZM96.7196 170.443H96.6259V165.176H95.6181V174H96.6259V171.604L97.2294 171.041L99.5966 174H100.88L97.9794 170.391L100.698 167.684H99.4618L96.7196 170.443ZM105.219 169.412C105.219 170.326 105.758 170.836 106.942 171.123L108.026 171.387C108.7 171.551 109.028 171.844 109.028 172.277C109.028 172.857 108.419 173.262 107.569 173.262C106.76 173.262 106.256 172.922 106.087 172.389H105.049C105.161 173.438 106.128 174.111 107.534 174.111C108.969 174.111 110.065 173.332 110.065 172.201C110.065 171.293 109.491 170.777 108.301 170.49L107.329 170.256C106.585 170.074 106.233 169.805 106.233 169.371C106.233 168.809 106.819 168.428 107.569 168.428C108.331 168.428 108.823 168.762 108.958 169.266H109.954C109.819 168.229 108.899 167.572 107.575 167.572C106.233 167.572 105.219 168.363 105.219 169.412ZM114.119 168.463C115.121 168.463 115.789 169.201 115.812 170.32H112.332C112.408 169.201 113.111 168.463 114.119 168.463ZM115.783 172.365C115.519 172.922 114.968 173.221 114.154 173.221C113.082 173.221 112.384 172.43 112.332 171.182V171.135H116.873V170.748C116.873 168.785 115.835 167.572 114.13 167.572C112.396 167.572 111.283 168.861 111.283 170.848C111.283 172.846 112.378 174.111 114.13 174.111C115.513 174.111 116.486 173.449 116.79 172.365H115.783ZM118.407 174H119.414V170.086C119.414 169.195 120.112 168.551 121.073 168.551C121.272 168.551 121.635 168.586 121.717 168.609V167.602C121.588 167.584 121.377 167.572 121.213 167.572C120.375 167.572 119.649 168.006 119.461 168.621H119.367V167.684H118.407V174ZM128.085 167.684H127.007L125.278 172.887H125.185L123.456 167.684H122.378L124.716 174H125.747L128.085 167.684ZM131.67 168.463C132.672 168.463 133.34 169.201 133.363 170.32H129.883C129.959 169.201 130.662 168.463 131.67 168.463ZM133.334 172.365C133.07 172.922 132.52 173.221 131.705 173.221C130.633 173.221 129.936 172.43 129.883 171.182V171.135H134.424V170.748C134.424 168.785 133.387 167.572 131.682 167.572C129.947 167.572 128.834 168.861 128.834 170.848C128.834 172.846 129.93 174.111 131.682 174.111C133.064 174.111 134.037 173.449 134.342 172.365H133.334ZM135.958 174H136.966V170.086C136.966 169.195 137.663 168.551 138.624 168.551C138.823 168.551 139.186 168.586 139.268 168.609V167.602C139.14 167.584 138.929 167.572 138.765 167.572C137.927 167.572 137.2 168.006 137.013 168.621H136.919V167.684H135.958V174ZM144.241 166.049V167.684H143.221V168.527H144.241V172.359C144.241 173.566 144.762 174.047 146.063 174.047C146.262 174.047 146.456 174.023 146.655 173.988V173.139C146.467 173.156 146.368 173.162 146.186 173.162C145.53 173.162 145.249 172.846 145.249 172.102V168.527H146.655V167.684H145.249V166.049H144.241ZM150.632 174.111C152.431 174.111 153.544 172.869 153.544 170.842C153.544 168.809 152.431 167.572 150.632 167.572C148.833 167.572 147.72 168.809 147.72 170.842C147.72 172.869 148.833 174.111 150.632 174.111ZM150.632 173.203C149.437 173.203 148.763 172.336 148.763 170.842C148.763 169.342 149.437 168.48 150.632 168.48C151.828 168.48 152.501 169.342 152.501 170.842C152.501 172.336 151.828 173.203 150.632 173.203ZM163.644 169.617C163.468 168.492 162.53 167.572 160.995 167.572C159.226 167.572 158.101 168.85 158.101 170.818C158.101 172.828 159.232 174.111 161.001 174.111C162.519 174.111 163.462 173.256 163.644 172.096H162.624C162.437 172.811 161.845 173.203 160.995 173.203C159.87 173.203 159.144 172.277 159.144 170.818C159.144 169.389 159.858 168.48 160.995 168.48C161.903 168.48 162.46 168.99 162.624 169.617H163.644ZM167.029 173.227C166.297 173.227 165.752 172.852 165.752 172.207C165.752 171.574 166.174 171.24 167.135 171.176L168.834 171.064V171.645C168.834 172.547 168.066 173.227 167.029 173.227ZM166.842 174.111C167.685 174.111 168.377 173.742 168.787 173.068H168.881V174H169.842V169.676C169.842 168.363 168.98 167.572 167.439 167.572C166.092 167.572 165.096 168.24 164.961 169.254H165.98C166.121 168.756 166.648 168.469 167.404 168.469C168.348 168.469 168.834 168.896 168.834 169.676V170.25L167.012 170.361C165.541 170.449 164.709 171.1 164.709 172.23C164.709 173.385 165.617 174.111 166.842 174.111ZM174.733 167.572C173.872 167.572 173.116 168.012 172.712 168.738H172.618V167.684H171.657V176.109H172.665V173.051H172.759C173.104 173.719 173.831 174.111 174.733 174.111C176.339 174.111 177.387 172.816 177.387 170.842C177.387 168.855 176.345 167.572 174.733 167.572ZM174.493 173.203C173.356 173.203 172.636 172.289 172.636 170.842C172.636 169.389 173.356 168.48 174.499 168.48C175.653 168.48 176.345 169.365 176.345 170.842C176.345 172.318 175.653 173.203 174.493 173.203ZM179.343 166.049V167.684H178.324V168.527H179.343V172.359C179.343 173.566 179.865 174.047 181.166 174.047C181.365 174.047 181.558 174.023 181.757 173.988V173.139C181.57 173.156 181.47 173.162 181.289 173.162C180.632 173.162 180.351 172.846 180.351 172.102V168.527H181.757V167.684H180.351V166.049H179.343ZM188.342 167.684H187.334V171.422C187.334 172.529 186.725 173.191 185.612 173.191C184.604 173.191 184.182 172.664 184.182 171.527V167.684H183.174V171.773C183.174 173.268 183.913 174.111 185.331 174.111C186.297 174.111 186.971 173.713 187.288 173.01H187.381V174H188.342V167.684ZM190.193 174H191.201V170.086C191.201 169.195 191.898 168.551 192.859 168.551C193.058 168.551 193.421 168.586 193.503 168.609V167.602C193.374 167.584 193.163 167.572 192.999 167.572C192.161 167.572 191.435 168.006 191.247 168.621H191.154V167.684H190.193V174ZM197.059 168.463C198.061 168.463 198.729 169.201 198.752 170.32H195.272C195.348 169.201 196.051 168.463 197.059 168.463ZM198.723 172.365C198.459 172.922 197.908 173.221 197.094 173.221C196.022 173.221 195.324 172.43 195.272 171.182V171.135H199.813V170.748C199.813 168.785 198.776 167.572 197.07 167.572C195.336 167.572 194.223 168.861 194.223 170.848C194.223 172.846 195.319 174.111 197.07 174.111C198.453 174.111 199.426 173.449 199.731 172.365H198.723ZM206.631 173.227C205.898 173.227 205.353 172.852 205.353 172.207C205.353 171.574 205.775 171.24 206.736 171.176L208.435 171.064V171.645C208.435 172.547 207.668 173.227 206.631 173.227ZM206.443 174.111C207.287 174.111 207.978 173.742 208.389 173.068H208.482V174H209.443V169.676C209.443 168.363 208.582 167.572 207.041 167.572C205.693 167.572 204.697 168.24 204.562 169.254H205.582C205.723 168.756 206.25 168.469 207.006 168.469C207.949 168.469 208.435 168.896 208.435 169.676V170.25L206.613 170.361C205.142 170.449 204.31 171.1 204.31 172.23C204.31 173.385 205.219 174.111 206.443 174.111ZM216.368 167.684H215.36V171.422C215.36 172.529 214.751 173.191 213.637 173.191C212.63 173.191 212.208 172.664 212.208 171.527V167.684H211.2V171.773C211.2 173.268 211.938 174.111 213.356 174.111C214.323 174.111 214.997 173.713 215.313 173.01H215.407V174H216.368V167.684ZM220.556 174.111C221.429 174.111 222.179 173.695 222.578 172.992H222.671V174H223.632V165.176H222.625V168.68H222.537C222.179 167.988 221.435 167.572 220.556 167.572C218.951 167.572 217.902 168.861 217.902 170.842C217.902 172.828 218.939 174.111 220.556 174.111ZM220.791 168.48C221.933 168.48 222.648 169.395 222.648 170.842C222.648 172.301 221.939 173.203 220.791 173.203C219.636 173.203 218.945 172.318 218.945 170.842C218.945 169.371 219.642 168.48 220.791 168.48ZM225.565 174H226.573V167.684H225.565V174ZM226.069 166.4C226.455 166.4 226.772 166.084 226.772 165.697C226.772 165.311 226.455 164.994 226.069 164.994C225.682 164.994 225.366 165.311 225.366 165.697C225.366 166.084 225.682 166.4 226.069 166.4ZM231.042 174.111C232.841 174.111 233.954 172.869 233.954 170.842C233.954 168.809 232.841 167.572 231.042 167.572C229.243 167.572 228.13 168.809 228.13 170.842C228.13 172.869 229.243 174.111 231.042 174.111ZM231.042 173.203C229.847 173.203 229.173 172.336 229.173 170.842C229.173 169.342 229.847 168.48 231.042 168.48C232.238 168.48 232.911 169.342 232.911 170.842C232.911 172.336 232.238 173.203 231.042 173.203ZM236.203 174.059C236.625 174.059 236.965 173.713 236.965 173.297C236.965 172.875 236.625 172.535 236.203 172.535C235.787 172.535 235.442 172.875 235.442 173.297C235.442 173.713 235.787 174.059 236.203 174.059Z" fill="#272525"/>
+</svg>
property int fontBig: 28
property int fontMedium: 13
property int fontSmall: 11
+ property int fontExtraSmall: 8
property int leftMargin: 48
property int rightMargin: 16
property string buttonStroke: virtualstudio.darkMode ? "#80827D7D" : "#40979797"
property string buttonHoverStroke: virtualstudio.darkMode ? "#7B7777" : "#BABCBC"
property string buttonPressedStroke: virtualstudio.darkMode ? "#827D7D" : "#BABCBC"
+ property string warningTextColour: "#DB0A0A"
+ property string linkText: virtualstudio.darkMode ? "#8B8D8D" : "#272525"
+
+ property string errorFlagColour: "#DB0A0A"
+
+ property string disabledButtonTextColour: virtualstudio.darkMode ? "#827D7D" : "#BABCBC"
property string settingsGroupView: "Audio"
y: header.height-1
modal: false
interactive: false
- visible: window.state == "settings"
+ visible: virtualstudio.windowState == "settings"
background: Rectangle {
border.color: "#33979797"
id: audioBtn
text: "Audio"
width: parent.width
- contentItem: Label {
- text: audioBtn.text
- font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- color: textColour
+ contentItem: Item {
+ implicitWidth: audioButtonText.implicitWidth
+ implicitHeight: audioButtonText.implicitHeight
+
+ Label {
+ id: audioButtonText
+ text: audioBtn.text
+ width: Boolean(virtualstudio.devicesError) ? parent.width - 16 * virtualstudio.uiScale : parent.width
+ font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: textColour
+ }
+
+ Rectangle {
+ id: audioDevicesErrorFlag
+ anchors.left: audioButtonText.right
+ anchors.verticalCenter: audioButtonText.verticalCenter
+ anchors.rightMargin: 16 * virtualstudio.uiScale
+ width: 8 * virtualstudio.uiScale
+ height: 8 * virtualstudio.uiScale
+ color: errorFlagColour
+ radius: 4 * virtualstudio.uiScale
+ visible: Boolean(virtualstudio.devicesError)
+ }
}
background: Rectangle {
width: parent.width
id: appearanceBtn
text: "Appearance"
width: parent.width
- contentItem: Label {
- text: appearanceBtn.text
- font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- color: textColour
+ contentItem: Item {
+ implicitWidth: appearanceButtonText.implicitWidth
+ implicitHeight: appearanceButtonText.implicitHeight
+
+ Label {
+ id: appearanceButtonText
+ text: appearanceBtn.text
+ width: parent.width
+ font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: textColour
+ }
}
+
+
background: Rectangle {
width: parent.width
color: appearanceBtn.down ? buttonPressedColour : (appearanceBtn.hovered || settingsGroupView == "Appearance" ? buttonHoverColour : backgroundColour)
id: advancedBtn
text: "Advanced"
width: parent.width
- contentItem: Label {
- text: advancedBtn.text
- font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- color: textColour
+ contentItem: Item {
+ implicitWidth: advancedButtonText.implicitWidth
+ implicitHeight: advancedButtonText.implicitHeight
+
+ Label {
+ id: advancedButtonText
+ text: advancedBtn.text
+ width: parent.width
+ font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: textColour
+ }
}
background: Rectangle {
width: parent.width
id: profileBtn
text: "Profile"
width: parent.width
- contentItem: Label {
- text: profileBtn.text
- font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- color: textColour
+ contentItem: Item {
+
+ implicitWidth: profileButtonText.implicitWidth
+ implicitHeight: profileButtonText.implicitHeight
+
+ Label {
+ id: profileButtonText
+ text: profileBtn.text
+ width: parent.width
+ font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: textColour
+ }
}
background: Rectangle {
width: parent.width
model: backendComboModel
currentIndex: virtualstudio.audioBackend == "JACK" ? 0 : 1
onActivated: { virtualstudio.audioBackend = currentText }
- x: 234 * virtualstudio.uiScale; y: 100 * virtualstudio.uiScale
+ x: 234 * virtualstudio.uiScale; y: 48 * virtualstudio.uiScale
width: parent.width - x - (16 * virtualstudio.uiScale); height: 36 * virtualstudio.uiScale
visible: virtualstudio.selectableBackend
}
color: textColour
}
- ComboBox {
- id: inputCombo
- model: inputComboModel
- currentIndex: virtualstudio.inputDevice
- onActivated: { virtualstudio.inputDevice = currentIndex }
- x: 234 * virtualstudio.uiScale; y: virtualstudio.uiScale * (virtualstudio.selectableBackend ? 148 : 100)
- width: parent.width - x - (16 * virtualstudio.uiScale); height: 36 * virtualstudio.uiScale
+ Text {
+ anchors.verticalCenter: outputCombo.verticalCenter
+ x: leftMargin * virtualstudio.uiScale
+ text: "Output Device"
+ font { family: "Poppins"; pixelSize: 13 * virtualstudio.fontScale * virtualstudio.uiScale }
visible: virtualstudio.audioBackend != "JACK"
- }
-
- Meter {
- id: inputDeviceMeters
- anchors.left: backendCombo.left
- anchors.right: parent.right
- anchors.rightMargin: rightMargin * virtualstudio.uiScale
- y: virtualstudio.audioBackend != "JACK" ? inputCombo.y + 48 * virtualstudio.uiScale : virtualstudio.uiScale * (virtualstudio.selectableBackend ? 148 : 100)
- height: 100 * virtualstudio.uiScale
- model: inputMeterModel
- clipped: inputClipped
+ color: textColour
}
ComboBox {
id: outputCombo
model: outputComboModel
- currentIndex: virtualstudio.outputDevice
- onActivated: { virtualstudio.outputDevice = currentIndex }
- x: backendCombo.x; y: inputDeviceMeters.y + (48 * virtualstudio.uiScale)
+ currentIndex: (() => {
+ let count = 0;
+ for (let i = 0; i < outputCombo.model.length; i++) {
+ if (outputCombo.model[i].type === "element") {
+ count++;
+ }
+
+ if (count > virtualstudio.outputDevice) {
+ return i;
+ }
+ }
+
+ return 0;
+ })()
+ x: 234 * virtualstudio.uiScale; y: virtualstudio.uiScale * (virtualstudio.selectableBackend ? 96 : 48)
width: backendCombo.width; height: backendCombo.height
visible: virtualstudio.audioBackend != "JACK"
- }
+ delegate: ItemDelegate {
+ required property var modelData
+ required property int index
- Text {
- anchors.verticalCenter: inputCombo.verticalCenter
- x: leftMargin * virtualstudio.uiScale
- text: "Input Device"
- font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
- visible: virtualstudio.audioBackend != "JACK"
- color: textColour
- }
+ leftPadding: 0
- Text {
- anchors.verticalCenter: outputCombo.verticalCenter
- x: leftMargin * virtualstudio.uiScale
- text: "Output Device"
- font { family: "Poppins"; pixelSize: 13 * virtualstudio.fontScale * virtualstudio.uiScale }
- visible: virtualstudio.audioBackend != "JACK"
- color: textColour
+ 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 = index - outputCombo.model.filter((elem, idx) => idx < index && elem.type === "header").length
+ }
+ }
+ }
+ }
+ contentItem: Text {
+ leftPadding: 12
+ font: outputCombo.font
+ horizontalAlignment: Text.AlignHLeft
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ text: outputCombo.model[outputCombo.currentIndex].text
+ }
}
Button {
onClicked: { virtualstudio.playOutputAudio() }
width: 216 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
x: parent.width - (232 * virtualstudio.uiScale)
- y: virtualstudio.audioBackend != "JACK" ? outputCombo.y + (60 * virtualstudio.uiScale) : inputDeviceMeters.y + (48 * virtualstudio.uiScale)
+ y: virtualstudio.audioBackend != "JACK" ? outputCombo.y + (48 * virtualstudio.uiScale) : outputCombo.y + (48 * virtualstudio.uiScale)
Text {
text: "Test Output Audio"
font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
}
}
+ Text {
+ anchors.verticalCenter: inputCombo.verticalCenter
+ x: leftMargin * virtualstudio.uiScale; y: testOutputAudioButton.y + (48 * virtualstudio.uiScale)
+ text: "Input Device"
+ font { family: "Poppins"; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+ visible: virtualstudio.audioBackend != "JACK"
+ color: textColour
+ }
+
+ ComboBox {
+ id: inputCombo
+ model: inputComboModel
+ currentIndex: (() => {
+ let count = 0;
+ for (let i = 0; i < inputCombo.model.length; i++) {
+ if (inputCombo.model[i].type === "element") {
+ count++;
+ }
+
+ if (count > virtualstudio.inputDevice) {
+ return i;
+ }
+ }
+
+ return 0;
+ })()
+ x: backendCombo.x; y: testOutputAudioButton.y + (48 * virtualstudio.uiScale)
+ width: parent.width - x - (16 * virtualstudio.uiScale); height: 36 * virtualstudio.uiScale
+ visible: virtualstudio.audioBackend != "JACK"
+ delegate: ItemDelegate {
+ 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 = index - inputCombo.model.filter((elem, idx) => idx < index && elem.type === "header").length
+ }
+ }
+ }
+ }
+ contentItem: Text {
+ leftPadding: 12
+ font: inputCombo.font
+ horizontalAlignment: Text.AlignHLeft
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ text: inputCombo.model[inputCombo.currentIndex].text
+ }
+ }
+
+ Meter {
+ id: inputDeviceMeters
+ anchors.left: backendCombo.left
+ anchors.right: parent.right
+ anchors.rightMargin: rightMargin * virtualstudio.uiScale
+ y: virtualstudio.audioBackend != "JACK" ? inputCombo.y + 48 * virtualstudio.uiScale : virtualstudio.uiScale * (virtualstudio.selectableBackend ? 112 : 64)
+ height: 100 * virtualstudio.uiScale
+ model: inputMeterModel
+ clipped: inputClipped
+ enabled: !Boolean(virtualstudio.devicesError)
+ }
+
Button {
id: refreshButton
background: Rectangle {
border.color: refreshButton.down ? buttonPressedStroke : (refreshButton.hovered ? buttonHoverStroke : buttonStroke)
}
onClicked: { virtualstudio.refreshDevices() }
- x: parent.width - (232 * virtualstudio.uiScale); y: testOutputAudioButton.y + (48 * virtualstudio.uiScale)
+ x: parent.width - (232 * virtualstudio.uiScale); y: inputDeviceMeters.y + (48 * virtualstudio.uiScale)
width: 216 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
visible: virtualstudio.audioBackend != "JACK"
Text {
}
}
+ Text {
+ id: devicesWarningOrError
+ x: leftMargin * virtualstudio.uiScale
+ y: virtualstudio.audioBackend != "JACK" ? refreshButton.y + (48 * virtualstudio.uiScale) : testOutputAudioButton.y + (48 * virtualstudio.uiScale)
+ width: parent.width - (64 * virtualstudio.uiScale)
+ textFormat: Text.RichText
+ text: (virtualstudio.devicesError || virtualstudio.devicesWarning)
+ + ((virtualstudio.devicesErrorHelpUrl || virtualstudio.devicesWarningHelpUrl)
+ ? ` <a style="color: ${linkText};" href=${virtualstudio.devicesErrorHelpUrl || virtualstudio.devicesWarningHelpUrl}>Learn More.</a>`
+ : ""
+ )
+ onLinkActivated: link => {
+ virtualstudio.openLink(link)
+ }
+ horizontalAlignment: Text.AlignHLeft
+ wrapMode: Text.WordWrap
+ color: warningTextColour
+ font { family: "Poppins"; pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+ visible: Boolean(virtualstudio.devicesError || virtualstudio.devicesWarning);
+ }
+
Rectangle {
id: divider
x: leftMargin * virtualstudio.uiScale
- y: virtualstudio.audioBackend != "JACK" ? refreshButton.y + (48 * virtualstudio.uiScale) : testOutputAudioButton.y + (48 * virtualstudio.uiScale)
+ y: Boolean(virtualstudio.devicesError || virtualstudio.devicesWarning) ? devicesWarningOrError.y + (60 * virtualstudio.uiScale) : refreshButton.y + (60 * virtualstudio.uiScale)
width: parent.width - x - (16 * virtualstudio.uiScale); height: 1 * virtualstudio.uiScale
color: textColour
visible: virtualstudio.audioBackend != "JACK"
border.width: 1
border.color: modeButton.down ? buttonPressedStroke : (modeButton.hovered ? buttonHoverStroke : buttonStroke)
}
- onClicked: { window.state = "login"; virtualstudio.toStandard(); }
+ onClicked: { virtualstudio.windowState = "login"; virtualstudio.toStandard(); }
x: 234 * virtualstudio.uiScale; y: 100 * virtualstudio.uiScale
width: 216 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
Text {
border.width: 1
border.color: logoutButton.down ? buttonPressedStroke : (logoutButton.hovered ? buttonHoverStroke : buttonStroke)
}
- onClicked: { window.state = "login"; virtualstudio.logout() }
+ onClicked: { virtualstudio.windowState = "login"; virtualstudio.logout() }
anchors.horizontalCenter: parent.horizontalCenter
y: editButton.y + (48 * virtualstudio.uiScale)
width: 260 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
border.width: 1
border.color: testModeButton.down ? buttonPressedStroke : (testModeButton.hovered ? buttonHoverStroke : buttonStroke)
}
- onClicked: { virtualstudio.testMode = !virtualstudio.testMode; window.state = "login"; virtualstudio.logout() }
+ onClicked: { virtualstudio.testMode = !virtualstudio.testMode; virtualstudio.windowState = "login"; virtualstudio.logout() }
anchors.horizontalCenter: parent.horizontalCenter
y: logoutButton.y + (48 * virtualstudio.uiScale)
width: 260 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
border.width: 1
border.color: cancelButton.down ? buttonPressedStroke : (cancelButton.hovered ? buttonHoverStroke : buttonStroke)
}
- onClicked: { window.state = "browse"; virtualstudio.revertSettings() }
+ onClicked: { virtualstudio.windowState = "browse"; virtualstudio.revertSettings() }
anchors.verticalCenter: parent.verticalCenter
x: parent.width - (230 * virtualstudio.uiScale)
width: buttonWidth * virtualstudio.uiScale; height: buttonHeight * virtualstudio.uiScale
Button {
id: saveButton
+ enabled: !Boolean(virtualstudio.devicesError)
background: Rectangle {
radius: 6 * virtualstudio.uiScale
color: saveButton.down ? buttonPressedColour : (saveButton.hovered ? buttonHoverColour : buttonColour)
border.width: 1
border.color: saveButton.down ? buttonPressedStroke : (saveButton.hovered ? buttonHoverStroke : buttonStroke)
}
- onClicked: { window.state = "browse"; virtualstudio.applySettings() }
+ onClicked: { virtualstudio.windowState = "browse"; virtualstudio.applySettings() }
anchors.verticalCenter: parent.verticalCenter
x: parent.width - (119 * virtualstudio.uiScale)
width: buttonWidth * virtualstudio.uiScale; height: buttonHeight * virtualstudio.uiScale
text: "Save"
font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
- color: textColour
+ color: Boolean(virtualstudio.devicesError) ? disabledButtonTextColour : textColour
}
}
}
property string saveButtonText: "#DB0A0A"
property string checkboxStroke: "#0062cc"
property string checkboxPressedStroke: "#007AFF"
+ property string disabledButtonText: "#D3D4D4"
+ property string linkText: virtualstudio.darkMode ? "#8B8D8D" : "#272525"
property bool currShowWarnings: virtualstudio.showWarnings
- property string warningScreen: virtualstudio.showWarnings ? "ethernet" : "acknowledged"
-
+ property string warningScreen: virtualstudio.showWarnings ? "ethernet" : ( permissions.micPermission == "unknown" ? "microphone" : "acknowledged")
+
Item {
id: ethernetWarningItem
width: parent.width; height: parent.height
color: saveButtonShadow
}
}
- onClicked: { virtualstudio.showWarnings = currShowWarnings; warningScreen = "acknowledged" }
+ onClicked: {
+ if (permissions.micPermission == "unknown") {
+ virtualstudio.showWarnings = currShowWarnings; warningScreen = "microphone"
+ } else {
+ virtualstudio.showWarnings = currShowWarnings; warningScreen = "acknowledged"
+ }
+ }
anchors.right: parent.right
anchors.rightMargin: 16 * virtualstudio.uiScale
anchors.bottomMargin: 16 * virtualstudio.uiScale
}
}
+ Item {
+ id: requestMicPermissionsItem
+ width: parent.width; height: parent.height
+ visible: warningScreen == "microphone" && permissions.micPermission == "unknown"
+
+ Image {
+ id: microphonePrompt
+ source: "Prompt.svg"
+ width: 260
+ height: 250
+ y: 60
+ anchors.horizontalCenter: parent.horizontalCenter
+ sourceSize: Qt.size(microphonePrompt.width,microphonePrompt.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
+ }
+
+ Image {
+ id: micLogo
+ source: "logo.svg"
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: microphonePrompt.top
+ anchors.topMargin: 18 * virtualstudio.uiScale
+ width: 32 * virtualstudio.uiScale; height: 59 * virtualstudio.uiScale
+ sourceSize: Qt.size(micLogo.width,micLogo.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
+ }
+
+ Colorize {
+ anchors.fill: microphonePrompt
+ source: microphonePrompt
+ hue: 0
+ saturation: 0
+ lightness: imageLightnessValue
+ }
+
+ Button {
+ id: showPromptButton
+ width: 112 * virtualstudio.uiScale
+ height: 30 * virtualstudio.uiScale
+ background: Rectangle {
+ radius: 6 * virtualstudio.uiScale
+ color: showPromptButton.down ? saveButtonPressedColour : saveButtonBackgroundColour
+ border.width: 2
+ border.color: showPromptButton.down ? saveButtonPressedStroke : saveButtonStroke
+ layer.enabled: showPromptButton.hovered && !showPromptButton.down
+ layer.effect: DropShadow {
+ horizontalOffset: 1 * virtualstudio.uiScale
+ verticalOffset: 1 * virtualstudio.uiScale
+ radius: 8.0 * virtualstudio.uiScale
+ samples: 17
+ color: saveButtonShadow
+ }
+ }
+ onClicked: {
+ permissions.getMicPermission();
+ }
+ anchors.right: microphonePrompt.right
+ anchors.rightMargin: 13.5 * virtualstudio.uiScale
+ anchors.bottomMargin: 17 * virtualstudio.uiScale
+ anchors.bottom: microphonePrompt.bottom
+ Text {
+ text: "OK"
+ font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
+ font.weight: Font.Bold
+ color: saveButtonText
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+
+ Text {
+ id: micPermissionsHeader
+ text: "JackTrip needs your sounds!"
+ font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+ color: textColour
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: microphonePrompt.bottom
+ anchors.topMargin: 48 * virtualstudio.uiScale
+ }
+
+ Text {
+ id: micPermissionsSubheader1
+ text: "JackTrip requires permission to use your microphone."
+ font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+ color: textColour
+ width: 400
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: micPermissionsHeader.bottom
+ anchors.topMargin: 32 * virtualstudio.uiScale
+ }
+
+ Text {
+ id: micPermissionsSubheader2
+ text: "Click ‘OK’ to give JackTrip access to your microphone, instrument, or other audio device."
+ font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+ color: textColour
+ width: 400
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: micPermissionsSubheader1.bottom
+ anchors.topMargin: 24 * virtualstudio.uiScale
+ }
+ }
+
+ Item {
+ id: noMicItem
+ width: parent.width; height: parent.height
+ visible: (warningScreen == "acknowledged" || warningScreen == "microphone") && permissions.micPermission == "denied"
+
+ Image {
+ id: noMic
+ source: "micoff.svg"
+ width: 109.27
+ height: 170
+ y: 60
+ anchors.horizontalCenter: parent.horizontalCenter
+ sourceSize: Qt.size(noMic.width,noMic.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
+ }
+
+ Colorize {
+ anchors.fill: noMic
+ source: noMic
+ hue: 0
+ saturation: 0
+ lightness: imageLightnessValue
+ }
+
+ Button {
+ id: openSettingsButton
+ background: Rectangle {
+ radius: 6 * virtualstudio.uiScale
+ color: openSettingsButton.down ? saveButtonPressedColour : saveButtonBackgroundColour
+ border.width: 1
+ border.color: openSettingsButton.down ? saveButtonPressedStroke : saveButtonStroke
+ layer.enabled: openSettingsButton.hovered && !openSettingsButton.down
+ layer.effect: DropShadow {
+ horizontalOffset: 1 * virtualstudio.uiScale
+ verticalOffset: 1 * virtualstudio.uiScale
+ radius: 8.0 * virtualstudio.uiScale
+ samples: 17
+ color: saveButtonShadow
+ }
+ }
+ onClicked: {
+ permissions.openSystemPrivacy();
+ }
+ anchors.right: parent.right
+ anchors.rightMargin: 16 * virtualstudio.uiScale
+ anchors.bottomMargin: 16 * virtualstudio.uiScale
+ anchors.bottom: parent.bottom
+ width: 200 * virtualstudio.uiScale; height: 30 * virtualstudio.uiScale
+ Text {
+ text: "Open Privacy Settings"
+ font.family: "Poppins"
+ font.pixelSize: 11 * virtualstudio.fontScale * virtualstudio.uiScale
+ font.weight: Font.Bold
+ color: saveButtonText
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+
+ Text {
+ id: noMicHeader
+ text: "JackTrip can't hear you!"
+ font { family: "Poppins"; weight: Font.Bold; pixelSize: fontMedium * virtualstudio.fontScale * virtualstudio.uiScale }
+ color: textColour
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: noMic.bottom
+ anchors.topMargin: 48 * virtualstudio.uiScale
+ }
+
+ Text {
+ id: noMicSubheader1
+ text: "JackTrip requires permission to use your microphone."
+ font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+ color: textColour
+ width: 400
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: noMicHeader.bottom
+ anchors.topMargin: 32 * virtualstudio.uiScale
+ }
+
+ Text {
+ id: noMicSubheader2
+ text: "Click 'Open Privacy Settings' to give JackTrip permission to access your microphone, instrument, or other audio device."
+ font { family: "Poppins"; pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale }
+ color: textColour
+ width: 400
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: noMicSubheader1.bottom
+ anchors.topMargin: 24 * virtualstudio.uiScale
+ }
+ }
+
Item {
id: setupItem
width: parent.width; height: parent.height
- visible: warningScreen == "acknowledged"
+ visible: (warningScreen == "acknowledged" || warningScreen == "microphone") && permissions.micPermission == "granted"
Text {
id: pageTitle
ComboBox {
id: outputCombo
model: outputComboModel
- currentIndex: virtualstudio.outputDevice
- onActivated: { virtualstudio.outputDevice = currentIndex }
+ currentIndex: (() => {
+ let count = 0;
+ for (let i = 0; i < outputCombo.model.length; i++) {
+ if (outputCombo.model[i].type === "element") {
+ count++;
+ }
+
+ if (count > virtualstudio.outputDevice) {
+ return i;
+ }
+ }
+
+ return 0;
+ })()
x: backendCombo.x; y: backendCombo.y + virtualstudio.uiScale * (virtualstudio.selectableBackend ? 48 : 0)
width: backendCombo.width; height: backendCombo.height
visible: virtualstudio.audioBackend != "JACK"
+ 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 = index - outputCombo.model.filter((elem, idx) => idx < index && elem.type === "header").length
+ }
+ }
+ }
+ }
+ contentItem: Text {
+ leftPadding: 12
+ font: outputCombo.font
+ horizontalAlignment: Text.AlignHLeft
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ text: outputCombo.model[outputCombo.currentIndex].text
+ }
}
Text {
ComboBox {
id: inputCombo
model: inputComboModel
- currentIndex: virtualstudio.inputDevice
- onActivated: { virtualstudio.inputDevice = currentIndex }
+ currentIndex: (() => {
+ let count = 0;
+ for (let i = 0; i < inputCombo.model.length; i++) {
+ if (inputCombo.model[i].type === "element") {
+ count++;
+ }
+
+ if (count > virtualstudio.inputDevice) {
+ return i;
+ }
+ }
+
+ return 0;
+ })()
anchors.right: parent.right
anchors.rightMargin: rightMargin * virtualstudio.uiScale
y: testOutputAudioButton.y + (48 * virtualstudio.uiScale)
width: parent.width - (234 * virtualstudio.uiScale); height: 36 * virtualstudio.uiScale
visible: virtualstudio.audioBackend != "JACK"
+ 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 = index - inputCombo.model.filter((elem, idx) => idx < index && elem.type === "header").length
+ }
+ }
+ }
+ }
+ contentItem: Text {
+ leftPadding: 12
+ font: inputCombo.font
+ horizontalAlignment: Text.AlignHLeft
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ text: inputCombo.model[inputCombo.currentIndex].text
+ }
}
Text {
height: 100 * virtualstudio.uiScale
model: inputMeterModel
clipped: inputClipped
+ enabled: !Boolean(virtualstudio.devicesError)
}
Slider {
}
Text {
- anchors.left: outputLabel.left
- anchors.right: outputCombo.right
- anchors.leftMargin: 16 * virtualstudio.uiScale
+ anchors.left: inputLabel.left
+ anchors.right: refreshButton.left
anchors.rightMargin: 16 * virtualstudio.uiScale
- anchors.bottom: parent.bottom
+ anchors.top: refreshButton.top
anchors.bottomMargin: 60 * virtualstudio.uiScale
- text: "JackTrip on Windows requires use of an audio device with ASIO drivers. If you do not see your device, you may need to install drivers from your manufacturer."
- horizontalAlignment: Text.AlignHCenter
+ textFormat: Text.RichText
+ text: (virtualstudio.devicesError || virtualstudio.devicesWarning)
+ + ((virtualstudio.devicesErrorHelpUrl || virtualstudio.devicesWarningHelpUrl)
+ ? ` <a style="color: ${linkText};" href=${virtualstudio.devicesErrorHelpUrl || virtualstudio.devicesWarningHelpUrl}>Learn More.</a>`
+ : ""
+ )
+ onLinkActivated: link => {
+ virtualstudio.openLink(link)
+ }
+ horizontalAlignment: Text.AlignHLeft
wrapMode: Text.WordWrap
color: warningText
font { family: "Poppins"; pixelSize: fontExtraSmall * virtualstudio.fontScale * virtualstudio.uiScale }
- visible: Qt.platform.os == "windows" && virtualstudio.audioBackend != "JACK"
+ visible: Boolean(virtualstudio.devicesError) || Boolean(virtualstudio.devicesWarning);
}
Button {
color: saveButtonShadow
}
}
- onClicked: { window.state = "browse"; virtualstudio.applySettings() }
+ enabled: !Boolean(virtualstudio.devicesError)
+ onClicked: { virtualstudio.windowState = "browse"; virtualstudio.applySettings() }
anchors.right: parent.right
anchors.rightMargin: rightMargin * virtualstudio.uiScale
anchors.bottomMargin: rightMargin * virtualstudio.uiScale
font.family: "Poppins"
font.pixelSize: fontSmall * virtualstudio.fontScale * virtualstudio.uiScale
font.weight: Font.Bold
- color: saveButtonText
+ color: !Boolean(virtualstudio.devicesError) ? saveButtonText : disabledButtonText
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
}
Image {
+ id: wedge
source: available ? "wedge.svg" : "wedge_inactive.svg"
x: 6; y: 0; width: 52 * virtualstudio.uiScale; height: 83 * virtualstudio.uiScale
+ sourceSize: Qt.size(wedge.width,wedge.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
Image {
+ id: studioLogo
source: "logo.svg"
x: 8; y: 11; width: 32 * virtualstudio.uiScale; height: 59 * virtualstudio.uiScale
+ sourceSize: Qt.size(studioLogo.width,studioLogo.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
Rectangle {
radius: 2 * virtualstudio.uiScale
color: publicStudio ? "#0095FF" : "#FF9800"
Image {
+ id: pubPriv
source: publicStudio ? "public.svg" : "private.svg"
x: 1 * virtualstudio.uiScale; y: x; width: 12 * virtualstudio.uiScale; height: width
+ sourceSize: Qt.size(pubPriv.width,pubPriv.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
}
visible: connected || canConnect || canStart
onClicked: {
if (!connected) {
- window.state = "connected";
+ virtualstudio.windowState = "connected";
virtualstudio.connectToStudio(index);
} else {
virtualstudio.disconnect();
}
}
Image {
+ id: joinLeave
width: 22 * virtualstudio.uiScale; height: 20 * virtualstudio.uiScale
anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter }
source: connected ? "leave.svg" : "join.svg"
+ sourceSize: Qt.size(joinLeave.width,joinLeave.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
}
}
visible: true
Image {
+ id: shareImg
width: 20 * virtualstudio.uiScale; height: width
anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter }
source: "share.svg"
+ sourceSize: Qt.size(shareImg.width,shareImg.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
ToolTip {
parent: inviteButton
}
visible: manageable
Image {
+ id: manageImg
width: 20 * virtualstudio.uiScale; height: width
anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter }
source: "manage.svg"
+ sourceSize: Qt.size(manageImg.width,manageImg.height)
+ fillMode: Image.PreserveAspectFit
+ smooth: true
}
}
#include "../Compressor.h"
#include "../CompressorPresets.h"
#include "../Limiter.h"
+#include "../Meter.h"
#include "../Reverb.h"
QJackTrip::QJackTrip(int argc, bool suppressCommandlineWarning, QWidget* parent)
m_ui->requireAuthGroupBox->setVisible(false);
m_ui->backendWarningLabel->setVisible(false);
m_ui->vsModeButton->setVisible(false);
+ m_ui->inputGroupBox->setVisible(false);
+ m_ui->outputGroupBox->setVisible(false);
+
+ m_inputLayout.reset(new QGridLayout(m_ui->inputGroupBox));
+ m_outputLayout.reset(new QGridLayout(m_ui->outputGroupBox));
#ifdef RT_AUDIO
connect(m_ui->backendComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
// Append any plugins
appendPlugins(m_jackTrip.data(), m_ui->channelSendSpinBox->value(),
m_ui->channelRecvSpinBox->value());
+ // Setup meters (also currently using faust plugins).
+ createMeters(m_ui->channelSendSpinBox->value(),
+ (m_ui->channelRecvSpinBox->value()));
QObject::connect(m_jackTrip.data(), &JackTrip::signalProcessesStopped, this,
&QJackTrip::processFinished, Qt::QueuedConnection);
&QJackTrip::udpWaitingTooLong, Qt::QueuedConnection);
QObject::connect(m_jackTrip.data(), &JackTrip::signalQueueLengthChanged, this,
&QJackTrip::queueLengthChanged, Qt::QueuedConnection);
+
m_ui->statusBar->showMessage(QStringLiteral("Waiting for Peer..."));
m_ui->disconnectButton->setEnabled(true);
#ifdef WAIRTOHUB // WAIR
}
}
+void QJackTrip::updatedInputMeasurements(const QVector<float> valuesInDb)
+{
+ for (int i = 0; i < m_inputMeters.count(); i++) {
+ // Determine decibel reading
+ qreal dB = m_meterMin;
+ if (i < valuesInDb.size()) {
+ dB = std::max(m_meterMin, valuesInDb.at(i));
+ }
+
+ // Produce a normalized value from 0 to 1
+ float level = (dB - m_meterMin) / (m_meterMax - m_meterMin);
+ m_inputMeters.at(i)->setLevel(level);
+ }
+}
+
+void QJackTrip::updatedOutputMeasurements(const QVector<float> valuesInDb)
+{
+ for (int i = 0; i < m_outputMeters.count(); i++) {
+ // Determine decibel reading
+ qreal dB = m_meterMin;
+ if (i < valuesInDb.size()) {
+ dB = std::max(m_meterMin, valuesInDb.at(i));
+ }
+
+ // Produce a normalized value from 0 to 1
+ float level = (dB - m_meterMin) / (m_meterMax - m_meterMin);
+ m_outputMeters.at(i)->setLevel(level);
+ }
+}
+
#ifndef NO_VS
void QJackTrip::virtualStudioMode()
{
void QJackTrip::enableUi(bool enabled)
{
- m_ui->optionsTabWidget->setEnabled(enabled);
+ if (m_ui->typeComboBox->currentIndex() == HUB_SERVER) {
+ m_ui->optionsTabWidget->setEnabled(enabled);
+ } else {
+ if (enabled) {
+ m_ui->inputGroupBox->setVisible(false);
+ m_ui->outputGroupBox->setVisible(false);
+ removeMeters();
+ m_ui->optionsTabWidget->setVisible(true);
+ } else {
+ m_ui->optionsTabWidget->setVisible(false);
+ m_ui->inputGroupBox->setVisible(true);
+ m_ui->outputGroupBox->setVisible(true);
+ }
+ }
m_ui->typeLabel->setEnabled(enabled);
m_ui->typeComboBox->setEnabled(enabled);
m_ui->addressLabel->setEnabled(
}
}
+void QJackTrip::createMeters(quint32 inputChannels, quint32 outputChannels)
+{
+ // These pointers are also deleted by AudioInterface.
+ Meter* inputMeter = new Meter(inputChannels);
+ Meter* outputMeter = new Meter(outputChannels);
+ m_jackTrip->appendProcessPluginToNetwork(inputMeter);
+ m_jackTrip->appendProcessPluginFromNetwork(outputMeter);
+
+ // Create our widgets.
+ for (quint32 i = 0; i < inputChannels; i++) {
+ VuMeter* meter = new VuMeter(this);
+ m_inputMeters.append(meter);
+ QLabel* label = new QLabel(QString::number(i + 1), this);
+ m_inputLabels.append(label);
+ label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
+ m_inputLayout->addWidget(label, i, 0, 1, 1);
+ m_inputLayout->addWidget(meter, i, 1, 1, 1);
+ }
+ // Effectively add a spacer at the bottom.
+ m_inputLayout->setRowStretch(inputChannels, 100);
+
+ for (quint32 i = 0; i < outputChannels; i++) {
+ VuMeter* meter = new VuMeter(this);
+ m_outputMeters.append(meter);
+ QLabel* label = new QLabel(QString::number(i + 1), this);
+ m_outputLabels.append(label);
+ label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
+ m_outputLayout->addWidget(label, i, 0, 1, 1);
+ m_outputLayout->addWidget(meter, i, 1, 1, 1);
+ }
+ m_outputLayout->setRowStretch(outputChannels, 100);
+
+ QObject::connect(inputMeter, &Meter::onComputedVolumeMeasurements, this,
+ &QJackTrip::updatedInputMeasurements);
+ QObject::connect(outputMeter, &Meter::onComputedVolumeMeasurements, this,
+ &QJackTrip::updatedOutputMeasurements);
+}
+
+void QJackTrip::removeMeters()
+{
+ m_inputLayout->setRowStretch(m_inputMeters.count(), 0);
+ m_outputLayout->setRowStretch(m_outputMeters.count(), 0);
+ for (int i = 0; i < m_inputLabels.count(); i++) {
+ delete m_inputLabels.at(i);
+ }
+ for (int i = 0; i < m_inputMeters.count(); i++) {
+ delete m_inputMeters.at(i);
+ }
+ for (int i = 0; i < m_outputLabels.count(); i++) {
+ delete m_outputLabels.at(i);
+ }
+ for (int i = 0; i < m_outputMeters.count(); i++) {
+ delete m_outputMeters.at(i);
+ }
+ m_inputLabels.clear();
+ m_inputMeters.clear();
+ m_outputLabels.clear();
+ m_outputMeters.clear();
+}
+
QString QJackTrip::commandLineFromCurrentOptions()
{
QString commandLine = QStringLiteral("jacktrip");
#ifdef RT_AUDIO
void QJackTrip::populateDeviceMenu(QComboBox* menu, bool isInput)
{
- RtAudio audio;
QString previousString = menu->currentText();
menu->clear();
- // std::cout << "previousString: " << previousString.toStdString() << std::endl;
menu->addItem(QStringLiteral("(default)"));
- unsigned int devices = audio.getDeviceCount();
- RtAudio::DeviceInfo info;
- for (unsigned int i = 0; i < devices; i++) {
- info = audio.getDeviceInfo(i);
- if (info.probed == true) {
- if (isInput && info.inputChannels > 0) {
- menu->addItem(QString::fromStdString(info.name));
- } else if (!isInput && info.outputChannels > 0) {
- menu->addItem(QString::fromStdString(info.name));
+
+ std::vector<RtAudio::Api> apis;
+ RtAudio::getCompiledApi(apis);
+
+ for (uint32_t i = 0; i < apis.size(); i++) {
+ RtAudio rtaudio(apis.at(i));
+ unsigned int devices = rtaudio.getDeviceCount();
+ for (unsigned int j = 0; j < devices; j++) {
+ RtAudio::DeviceInfo info = rtaudio.getDeviceInfo(j);
+ if (info.probed == true) {
+ // Don't include duplicate entries
+ if (menu->findText(QString::fromStdString(info.name)) != -1) {
+ continue;
+ }
+
+ if (isInput && info.inputChannels > 0) {
+ menu->addItem(QString::fromStdString(info.name));
+ } else if (!isInput && info.outputChannels > 0) {
+ menu->addItem(QString::fromStdString(info.name));
+ }
}
}
}
+
// set the previous value
menu->setCurrentText(previousString);
}
#include <QByteArray>
#include <QCloseEvent>
+#include <QGridLayout>
#include <QLabel>
#include <QMainWindow>
#include <QMutex>
#include "../JackTrip.h"
#include "../UdpHubListener.h"
#include "messageDialog.h"
+#include "vuMeter.h"
#ifdef __APPLE__
#include "NoNap.h"
void start();
void stop();
void exit();
+ void updatedInputMeasurements(const QVector<float> valuesInDb);
+ void updatedOutputMeasurements(const QVector<float> valuesInDb);
#ifndef NO_VS
void virtualStudioMode();
#endif
#endif
void appendPlugins(JackTrip* jackTrip, int numSendChannels, int numRecvChannels);
+ void createMeters(quint32 inputChannels, quint32 outputChannels);
+ void removeMeters();
QString commandLineFromCurrentOptions();
void showCommandLineMessageBox();
QScopedPointer<QNetworkAccessManager> m_netManager;
QScopedPointer<MessageDialog> m_statsDialog;
QScopedPointer<MessageDialog> m_debugDialog;
+ QScopedPointer<QGridLayout> m_inputLayout;
+ QScopedPointer<QGridLayout> m_outputLayout;
std::ostream m_realCout;
std::ostream m_realCerr;
bool m_jackTripRunning;
bool m_isExiting;
bool m_exitSent;
+ float m_meterMax = 0.0;
+ float m_meterMin = -64.0;
+
+ QList<VuMeter*> m_inputMeters;
+ QList<QLabel*> m_inputLabels;
+ QList<VuMeter*> m_outputMeters;
+ QList<QLabel*> m_outputLabels;
+
QMutex m_requestMutex;
QString m_IPv6Address;
bool m_hasIPv4Reply;
<file>ethernet.png</file>
<file>ohno.png</file>
<file>headphones.svg</file>
+ <file>Prompt.svg</file>
<file>network.svg</file>
<file>jacktrip.png</file>
<file>jacktrip white.png</file>
<x>0</x>
<y>0</y>
<width>409</width>
- <height>913</height>
+ <height>961</height>
</rect>
</property>
<property name="windowTitle">
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QGridLayout" name="gridLayout">
- <item row="0" column="1">
- <widget class="QComboBox" name="typeComboBox">
+ <item row="3" column="1">
+ <widget class="QLabel" name="ipLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="toolTip">
- <string>To connect to a p2p (peer to peer) server you need to run as a p2p client.
-To connect to a hub server you need to run as a hub client.</string>
+ <string>If running as a server, this is the address you should supply to the other clients.
+(You will need to enable any port forwarding on your router manually.)</string>
</property>
- <property name="currentIndex">
- <number>2</number>
+ <property name="text">
+ <string>Looking up external IP address...</string>
</property>
- <item>
- <property name="text">
- <string>P2P Client</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>P2P Server</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Hub Client</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Hub Server</string>
- </property>
- </item>
</widget>
</item>
- <item row="6" column="0" colspan="2">
+ <item row="8" column="0" colspan="2">
<layout class="QHBoxLayout" name="buttonLayout">
<item>
<widget class="QPushButton" name="connectButton">
</item>
</layout>
</item>
- <item row="3" column="1">
- <widget class="QLabel" name="ipLabel">
- <property name="toolTip">
- <string>If running as a server, this is the address you should supply to the other clients.
-(You will need to enable any port forwarding on your router manually.)</string>
- </property>
- <property name="text">
- <string>Looking up external IP address...</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QComboBox" name="addressComboBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="toolTip">
- <string>Enter the IP address or the hostname of the server you want to connect to.</string>
- </property>
- <property name="editable">
- <bool>true</bool>
- </property>
- <property name="maxCount">
- <number>5</number>
- </property>
- <property name="insertPolicy">
- <enum>QComboBox::NoInsert</enum>
+ <item row="6" column="0" colspan="2">
+ <widget class="QGroupBox" name="inputGroupBox">
+ <property name="title">
+ <string>Input Channels</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QGroupBox" name="channelGroupBox">
<layout class="QGridLayout" name="gridLayout_9">
- <item row="3" column="0">
- <widget class="QLabel" name="channelRecvLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>&Received from network:</string>
- </property>
- <property name="buddy">
- <cstring>channelRecvSpinBox</cstring>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
+ <item row="4" column="1">
<widget class="QSpinBox" name="channelRecvSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
</property>
</widget>
</item>
- <item row="4" column="1">
- <widget class="QSpinBox" name="channelSendSpinBox">
- <property name="toolTip">
- <string>Number of audio channels to send to the network.</string>
- </property>
- <property name="minimum">
- <number>1</number>
- </property>
- <property name="value">
- <number>2</number>
- </property>
- </widget>
- </item>
<item row="4" column="0">
- <widget class="QLabel" name="channelSendLabel">
+ <widget class="QLabel" name="channelRecvLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="text">
- <string>&Sent to network:</string>
+ <string>&Received from network:</string>
</property>
<property name="buddy">
- <cstring>channelSendSpinBox</cstring>
+ <cstring>channelRecvSpinBox</cstring>
</property>
</widget>
</item>
<string>&Number of channels</string>
</property>
<property name="buddy">
- <cstring>channelRecvSpinBox</cstring>
+ <cstring>channelSendSpinBox</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="channelSendLabel">
+ <property name="text">
+ <string>&Sent to network:</string>
+ </property>
+ <property name="buddy">
+ <cstring>channelSendSpinBox</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QSpinBox" name="channelSendSpinBox">
+ <property name="toolTip">
+ <string>Number of audio channels to send to the network.</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>2</number>
</property>
</widget>
</item>
</widget>
</widget>
</item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="typeComboBox">
+ <property name="toolTip">
+ <string>To connect to a p2p (peer to peer) server you need to run as a p2p client.
+To connect to a hub server you need to run as a hub client.</string>
+ </property>
+ <property name="currentIndex">
+ <number>2</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>P2P Client</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>P2P Server</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Hub Client</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Hub Server</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="addressComboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Enter the IP address or the hostname of the server you want to connect to.</string>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="maxCount">
+ <number>5</number>
+ </property>
+ <property name="insertPolicy">
+ <enum>QComboBox::NoInsert</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0" colspan="2">
+ <widget class="QGroupBox" name="outputGroupBox">
+ <property name="title">
+ <string>Output Channels</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<tabstop>disconnectButton</tabstop>
<tabstop>exitButton</tabstop>
<tabstop>optionsTabWidget</tabstop>
- <tabstop>channelRecvSpinBox</tabstop>
<tabstop>channelSendSpinBox</tabstop>
+ <tabstop>channelRecvSpinBox</tabstop>
<tabstop>autoPatchComboBox</tabstop>
<tabstop>patchServerCheckBox</tabstop>
<tabstop>upmixCheckBox</tabstop>
<RCC>
<qresource prefix="qjacktrip">
- <file>about@2x.png</file>
- <file>about.png</file>
- <file>icon.png</file>
+ <file alias="about@2x.png">alt/about@2x.png</file>
+ <file alias="about.png">alt/about.png</file>
+ <file alias="icon.png">alt/icon.png</file>
</qresource>
</RCC>
m_inMuted = settings.value(QStringLiteral("InMuted"), false).toBool();
m_outMuted = settings.value(QStringLiteral("OutMuted"), false).toBool();
#ifdef RT_AUDIO
- m_useRtAudio = settings.value(QStringLiteral("Backend"), 0).toInt() == 1;
+ m_useRtAudio = settings.value(QStringLiteral("Backend"), 1).toInt() == 1;
m_inputDevice = settings.value(QStringLiteral("InputDevice"), "").toString();
m_outputDevice = settings.value(QStringLiteral("OutputDevice"), "").toString();
m_bufferSize = settings.value(QStringLiteral("BufferSize"), 128).toInt();
QVariant::fromValue(m_servers));
m_view.engine()->rootContext()->setContextProperty(QStringLiteral("audioInterface"),
m_vsAudioInterface.data());
+ // Add permissions for Mac
+#ifdef __APPLE__
+ m_permissions.reset(new VsMacPermissions());
+ m_view.engine()->rootContext()->setContextProperty(
+ QStringLiteral("permissions"), QVariant::fromValue(m_permissions.data()));
+ if (m_permissions->micPermissionChecked()
+ && m_permissions->micPermission() == "unknown") {
+ m_permissions->getMicPermission();
+ }
+ connect(m_permissions.data(), &VsMacPermissions::micPermissionUpdated, this,
+ &VirtualStudio::startAudio);
+#else
+ m_permissions.reset(new VsPermissions());
+ m_view.engine()->rootContext()->setContextProperty(
+ QStringLiteral("permissions"), QVariant::fromValue(m_permissions.data()));
+#endif
m_view.engine()->rootContext()->setContextProperty(
QStringLiteral("inputMeterModel"), QVariant::fromValue(QVector<float>()));
// thread
connect(this, &VirtualStudio::refreshFinished, this, &VirtualStudio::joinStudio,
Qt::QueuedConnection);
+ connect(
+ this, &VirtualStudio::studioToJoinChanged, this,
+ [&]() {
+ if (!m_studioToJoin.isEmpty()) {
+ // join studio when studio to join changes
+ if (readyToJoin()) {
+ joinStudio();
+ }
+ }
+ },
+ Qt::QueuedConnection);
}
void VirtualStudio::setStandardWindow(QSharedPointer<QJackTrip> window)
{
#ifdef RT_AUDIO
if (m_useRtAudio) {
- int index = m_inputDeviceList.indexOf(m_inputDevice);
+ QStringList filteredInputDeviceList;
+ for (int i = 0; i < m_inputDeviceList.size(); i++) {
+ if (m_inputDeviceList.at(i) != "(default)") {
+ filteredInputDeviceList += m_inputDeviceList.at(i);
+ }
+ }
+
+ int index = filteredInputDeviceList.indexOf(m_inputDevice);
return index >= 0 ? index : 0;
}
#endif
return;
}
#ifdef RT_AUDIO
- m_inputDevice = m_inputDeviceList.at(device);
+ std::cout << "Setting Input Device: " << device << std::endl;
+ QStringList filteredInputDeviceList;
+ for (int i = 0; i < m_inputDeviceList.size(); i++) {
+ if (m_inputDeviceList.at(i) != "(default)") {
+ filteredInputDeviceList += m_inputDeviceList.at(i);
+ }
+ }
+
+ m_inputDevice = filteredInputDeviceList.at(device);
emit inputDeviceSelected(m_inputDevice);
#endif
}
{
#ifdef RT_AUDIO
if (m_useRtAudio) {
- int index = m_outputDeviceList.indexOf(m_outputDevice);
+ QStringList filteredOutputDeviceList;
+ for (int i = 0; i < m_outputDeviceList.size(); i++) {
+ if (m_outputDeviceList.at(i) != "(default)") {
+ filteredOutputDeviceList += m_outputDeviceList.at(i);
+ }
+ }
+
+ int index = filteredOutputDeviceList.indexOf(m_outputDevice);
return index >= 0 ? index : 0;
}
#endif
return;
}
#ifdef RT_AUDIO
- m_outputDevice = m_outputDeviceList.at(device);
+ QStringList filteredOutputDeviceList;
+ for (int i = 0; i < m_outputDeviceList.size(); i++) {
+ if (m_outputDeviceList.at(i) != "(default)") {
+ filteredOutputDeviceList += m_outputDeviceList.at(i);
+ }
+ }
+
+ m_outputDevice = filteredOutputDeviceList.at(device);
emit outputDeviceSelected(m_outputDevice);
#endif
}
+QString VirtualStudio::devicesWarning()
+{
+ return m_devicesWarningMsg;
+}
+
+QString VirtualStudio::devicesError()
+{
+ return m_devicesErrorMsg;
+}
+
+QString VirtualStudio::devicesWarningHelpUrl()
+{
+ return m_devicesWarningHelpUrl;
+}
+
+QString VirtualStudio::devicesErrorHelpUrl()
+{
+ return m_devicesErrorHelpUrl;
+}
+
float VirtualStudio::inputVolume()
{
return m_inMultiplier;
m_showDeviceSetup = show;
}
+QString VirtualStudio::windowState()
+{
+ return m_windowState;
+}
+
+void VirtualStudio::setWindowState(QString state)
+{
+ m_windowState = state;
+ emit windowStateUpdated();
+}
+
bool VirtualStudio::showWarnings()
{
return m_showWarnings;
if (!m_studioToJoin.isEmpty()) {
// device setup view proceeds warning view
// if device setup is shown, do not immediately join
- if (!m_showDeviceSetup) {
+ if (readyToJoin()) {
// We're done waiting to be on the browse page
- m_shouldJoin = true;
joinStudio();
}
}
void VirtualStudio::setStudioToJoin(const QUrl& url)
{
m_studioToJoin = url;
+ emit studioToJoinChanged();
}
bool VirtualStudio::noUpdater()
return m_failedMessage;
}
-bool VirtualStudio::shouldJoin()
-{
- return m_shouldJoin;
-}
-
-void VirtualStudio::setShouldJoin(bool join)
-{
- m_shouldJoin = join;
-}
-
void VirtualStudio::joinStudio()
{
if (!m_authenticated || m_studioToJoin.isEmpty() || m_servers.isEmpty()) {
return;
}
- if (!m_shouldJoin) {
- // Not time to join yet.
- // Waiting until joinStudio is called and m_shouldJoin is true.
- return;
- }
-
QString scheme = m_studioToJoin.scheme();
QString path = m_studioToJoin.path();
QString url = m_studioToJoin.toString();
void VirtualStudio::refreshDevices()
{
#ifdef RT_AUDIO
- getDeviceList(&m_inputDeviceList, true);
- getDeviceList(&m_outputDeviceList, false);
- m_view.engine()->rootContext()->setContextProperty(
- QStringLiteral("inputComboModel"), QVariant::fromValue(m_inputDeviceList));
- m_view.engine()->rootContext()->setContextProperty(
- QStringLiteral("outputComboModel"), QVariant::fromValue(m_outputDeviceList));
+ RtAudioInterface::getDeviceList(&m_inputDeviceList, &m_inputDeviceCategories, true);
+ RtAudioInterface::getDeviceList(&m_outputDeviceList, &m_outputDeviceCategories,
+ false);
+
+ QVariant inputComboModel =
+ formatDeviceList(m_inputDeviceList, m_inputDeviceCategories);
+ QVariant outputComboModel =
+ formatDeviceList(m_outputDeviceList, m_outputDeviceCategories);
+ m_view.engine()->rootContext()->setContextProperty(QStringLiteral("inputComboModel"),
+ inputComboModel);
+ m_view.engine()->rootContext()->setContextProperty(QStringLiteral("outputComboModel"),
+ outputComboModel);
// Make sure we keep our current settings if the device still exists
if (!m_inputDeviceList.contains(m_inputDevice)) {
// which can display upon opening the app from join link
if (!m_studioToJoin.isEmpty()) {
// We're done waiting to be on the browse page
- m_shouldJoin = true;
joinStudio();
}
}
m_vsAudioInterface->startProcess();
}
+
+ m_connectionState = QStringLiteral("Disconnected");
+ emit connectionStateChanged();
}
void VirtualStudio::manageStudio(int studioIndex)
about.exec();
}
+void VirtualStudio::openLink(const QString& link)
+{
+ QUrl url = QUrl(link);
+ QDesktopServices::openUrl(url);
+}
+
void VirtualStudio::exit()
{
m_refreshTimer.stop();
m_device = new VsDevice(m_authenticator.data(), m_testMode);
m_device->registerApp();
- if (m_vsAudioInterface.isNull()) {
- m_vsAudioInterface.reset(new VsAudioInterface());
- m_view.engine()->rootContext()->setContextProperty(
- QStringLiteral("audioInterface"), m_vsAudioInterface.data());
+#ifdef __APPLE__
+ if (m_permissions->micPermission() == "granted") {
+ startAudio();
}
-#ifdef RT_AUDIO
- m_vsAudioInterface->setInputDevice(m_inputDevice);
- m_vsAudioInterface->setOutputDevice(m_outputDevice);
- m_vsAudioInterface->setAudioInterfaceMode(m_useRtAudio);
+#else
+ startAudio();
#endif
- m_vsAudioInterface->setupAudio();
-
- 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);
- connect(this, &VirtualStudio::audioBackendChanged, m_vsAudioInterface.data(),
- &VsAudioInterface::setAudioInterfaceMode);
- connect(this, &VirtualStudio::triggerPlayOutputAudio, m_vsAudioInterface.data(),
- &VsAudioInterface::triggerPlayback);
- connect(m_vsAudioInterface.data(), &VsAudioInterface::newVolumeMeterMeasurements,
- this, &VirtualStudio::updatedInputVuMeasurements);
- connect(m_vsAudioInterface.data(), &VsAudioInterface::errorToProcess, this,
- &VirtualStudio::processError);
-
- m_vsAudioInterface->setupPlugins();
-
- m_view.engine()->rootContext()->setContextProperty(
- QStringLiteral("inputMeterModel"),
- QVariant::fromValue(QVector<float>(m_vsAudioInterface->getNumInputChannels())));
-
- m_vsAudioInterface->startProcess();
if (m_userId.isEmpty()) {
getUserId();
if (!m_studioToJoin.isEmpty()) {
// FTUX shows warnings and device setup views
// if any of these enabled, do not immediately join
- if (!m_showDeviceSetup) {
- // Don't need to set m_shouldJoin because it's default true
+ if (readyToJoin()) {
+ // We should join in this case
joinStudio();
}
}
return;
}
+void VirtualStudio::updatedDevicesErrorMsg(const QString& msg)
+{
+ m_devicesErrorMsg = msg;
+ emit devicesErrorChanged();
+ return;
+}
+
+void VirtualStudio::updatedDevicesWarningMsg(const QString& msg)
+{
+ m_devicesWarningMsg = msg;
+ emit devicesWarningChanged();
+ return;
+}
+
+void VirtualStudio::updatedDevicesErrorHelpUrl(const QString& url)
+{
+ m_devicesErrorHelpUrl = url;
+ emit devicesErrorHelpUrlChanged();
+ return;
+}
+
+void VirtualStudio::updatedDevicesWarningHelpUrl(const QString& url)
+{
+ m_devicesWarningHelpUrl = url;
+ emit devicesWarningHelpUrlChanged();
+ return;
+}
+
void VirtualStudio::updatedInputVuMeasurements(const QVector<float>& valuesInDecibels)
{
QJsonArray uiValues;
});
}
-#ifdef RT_AUDIO
-void VirtualStudio::getDeviceList(QStringList* list, bool isInput)
-{
- RtAudio audio;
- list->clear();
- list->append(QStringLiteral("(default)"));
-
- unsigned int devices = audio.getDeviceCount();
- RtAudio::DeviceInfo info;
- for (unsigned int i = 0; i < devices; i++) {
- info = audio.getDeviceInfo(i);
- if (info.probed == true) {
- if (isInput && info.inputChannels > 0) {
- list->append(QString::fromStdString(info.name));
- } else if (!isInput && info.outputChannels > 0) {
- list->append(QString::fromStdString(info.name));
- }
- }
+void VirtualStudio::startAudio()
+{
+#ifdef __APPLE__
+ if (m_permissions->micPermission() != "granted") {
+ return;
}
-}
#endif
+ if (m_vsAudioInterface.isNull()) {
+ m_vsAudioInterface.reset(new VsAudioInterface());
+ m_view.engine()->rootContext()->setContextProperty(
+ QStringLiteral("audioInterface"), m_vsAudioInterface.data());
+ }
+#ifdef RT_AUDIO
+ m_vsAudioInterface->setInputDevice(m_inputDevice);
+ m_vsAudioInterface->setOutputDevice(m_outputDevice);
+ m_vsAudioInterface->setAudioInterfaceMode(m_useRtAudio);
+#endif
+ connect(m_vsAudioInterface.data(), &VsAudioInterface::devicesErrorMsgChanged, this,
+ &VirtualStudio::updatedDevicesErrorMsg);
+ connect(m_vsAudioInterface.data(), &VsAudioInterface::devicesWarningMsgChanged, this,
+ &VirtualStudio::updatedDevicesWarningMsg);
+ connect(m_vsAudioInterface.data(), &VsAudioInterface::devicesErrorHelpUrlChanged,
+ this, &VirtualStudio::updatedDevicesErrorHelpUrl);
+ connect(m_vsAudioInterface.data(), &VsAudioInterface::devicesWarningHelpUrlChanged,
+ this, &VirtualStudio::updatedDevicesWarningHelpUrl);
+ m_vsAudioInterface->setupAudio();
+
+ 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);
+ connect(this, &VirtualStudio::audioBackendChanged, m_vsAudioInterface.data(),
+ &VsAudioInterface::setAudioInterfaceMode);
+ connect(this, &VirtualStudio::triggerPlayOutputAudio, m_vsAudioInterface.data(),
+ &VsAudioInterface::triggerPlayback);
+ connect(m_vsAudioInterface.data(), &VsAudioInterface::newVolumeMeterMeasurements,
+ this, &VirtualStudio::updatedInputVuMeasurements);
+ connect(m_vsAudioInterface.data(), &VsAudioInterface::errorToProcess, this,
+ &VirtualStudio::processError);
+
+ m_vsAudioInterface->setupPlugins();
+
+ m_view.engine()->rootContext()->setContextProperty(
+ QStringLiteral("inputMeterModel"),
+ QVariant::fromValue(QVector<float>(m_vsAudioInterface->getNumInputChannels())));
+
+ m_vsAudioInterface->startProcess();
+}
void VirtualStudio::stopStudio()
{
});
}
+bool VirtualStudio::readyToJoin()
+{
+ return m_windowState == "browse"
+ && (m_connectionState == QStringLiteral("Waiting")
+ || m_connectionState == QStringLiteral("Disconnected"));
+}
+
+#ifdef RT_AUDIO
+QVariant VirtualStudio::formatDeviceList(const QStringList& devices,
+ const QStringList& categories)
+{
+ QStringList filteredDevices;
+ QStringList filteredCategories;
+
+ for (int i = 0; i < devices.size(); i++) {
+ if (!devices[i].contains("(default)")) {
+ filteredDevices += devices[i];
+ filteredCategories += categories[i];
+ }
+ }
+
+ QStringList uniqueCategories = QStringList(filteredCategories);
+ uniqueCategories.removeDuplicates();
+
+ bool containsCategories = true;
+ if (uniqueCategories.size() == 0) {
+ containsCategories = false;
+ } else if (uniqueCategories.size() == 1 && uniqueCategories.at(0) == "") {
+ containsCategories = false;
+ }
+
+ QVariantList items = QVariantList();
+ for (int i = 0; i < uniqueCategories.size(); i++) {
+ QString category = uniqueCategories.at(i);
+
+ if (containsCategories) {
+ QJsonObject header = QJsonObject();
+ header.insert(QString::fromStdString("text"), uniqueCategories.at(i));
+ header.insert(QString::fromStdString("type"),
+ QString::fromStdString("header"));
+ items.push_back(QVariant(QJsonValue(header)));
+ }
+
+ for (int j = 0; j < filteredDevices.size(); j++) {
+ if (filteredCategories.at(j).toStdString() == category.toStdString()) {
+ QJsonObject element = QJsonObject();
+ element.insert(QString::fromStdString("text"), filteredDevices.at(j));
+ element.insert(QString::fromStdString("type"),
+ QString::fromStdString("element"));
+ items.push_back(QVariant(QJsonValue(element)));
+ }
+ }
+ }
+
+ return QVariant(items);
+}
+#endif
+
VirtualStudio::~VirtualStudio()
{
for (int i = 0; i < m_servers.count(); i++) {
#ifdef __APPLE__
#include "NoNap.h"
+#include "vsMacPermissions.h"
+#else
+#include "vsPermissions.h"
#endif
class QJackTrip;
int inputDevice READ inputDevice WRITE setInputDevice NOTIFY inputDeviceChanged)
Q_PROPERTY(int outputDevice READ outputDevice WRITE setOutputDevice NOTIFY
outputDeviceChanged)
+
+ Q_PROPERTY(QString devicesWarning READ devicesWarning NOTIFY devicesWarningChanged)
+ Q_PROPERTY(QString devicesError READ devicesError NOTIFY devicesErrorChanged)
+ Q_PROPERTY(QString devicesWarningHelpUrl READ devicesWarningHelpUrl NOTIFY
+ devicesWarningHelpUrlChanged)
+ Q_PROPERTY(QString devicesErrorHelpUrl READ devicesErrorHelpUrl NOTIFY
+ devicesErrorHelpUrlChanged)
+
Q_PROPERTY(
int bufferSize READ bufferSize WRITE setBufferSize NOTIFY bufferSizeChanged)
Q_PROPERTY(int bufferStrategy READ bufferStrategy WRITE setBufferStrategy NOTIFY
Q_PROPERTY(bool noUpdater READ noUpdater CONSTANT)
Q_PROPERTY(bool psiBuild READ psiBuild CONSTANT)
Q_PROPERTY(QString failedMessage READ failedMessage NOTIFY failedMessageChanged)
- Q_PROPERTY(
- bool shouldJoin READ shouldJoin WRITE setShouldJoin NOTIFY shouldJoinChanged)
Q_PROPERTY(
float inputVolume READ inputVolume WRITE setInputVolume NOTIFY updatedInputVolume)
Q_PROPERTY(float outputVolume READ outputVolume WRITE setOutputVolume NOTIFY
updatedOutputVolume)
Q_PROPERTY(
bool inputMuted READ inputMuted WRITE setInputMuted NOTIFY updatedInputMuted)
+ Q_PROPERTY(QString windowState READ windowState WRITE setWindowState NOTIFY
+ windowStateUpdated)
public:
explicit VirtualStudio(bool firstRun = false, QObject* parent = nullptr);
void setInputDevice(int device);
int outputDevice();
void setOutputDevice(int device);
+ QString devicesWarning();
+ QString devicesError();
+ QString devicesWarningHelpUrl();
+ QString devicesErrorHelpUrl();
int bufferSize();
void setBufferSize(int index);
int bufferStrategy();
bool noUpdater();
bool psiBuild();
QString failedMessage();
- bool shouldJoin();
- void setShouldJoin(bool join);
float inputVolume();
float outputVolume();
bool inputMuted();
bool outputMuted();
+ QString windowState();
public slots:
void toStandard();
void createStudio();
void editProfile();
void showAbout();
+ void openLink(const QString& url);
void updatedInputVuMeasurements(const QVector<float>& valuesInDecibels);
void updatedOutputVuMeasurements(const QVector<float>& valuesInDecibels);
void setInputVolume(float multiplier);
void setOutputVolume(float multiplier);
void setInputMuted(bool muted);
void setOutputMuted(bool muted);
+ void setWindowState(QString state);
void exit();
signals:
void outputDeviceChanged(QString device);
void inputDeviceSelected(QString device);
void outputDeviceSelected(QString device);
+ void devicesWarningChanged();
+ void devicesErrorChanged();
+ void devicesWarningHelpUrlChanged();
+ void devicesErrorHelpUrlChanged();
void triggerPlayOutputAudio();
void bufferSizeChanged();
void bufferStrategyChanged();
void signalExit();
void periodicRefresh();
void failedMessageChanged();
- void shouldJoinChanged();
+ void studioToJoinChanged();
void updatedInputVolume(float multiplier);
void updatedOutputVolume(float multiplier);
void updatedInputMuted(bool muted);
void updatedOutputMuted(bool muted);
+ void windowStateUpdated();
private slots:
void slotAuthSucceded();
void launchBrowser(const QUrl& url);
void joinStudio();
void updatedStats(const QJsonObject& stats);
+ void startAudio();
+ void updatedDevicesErrorMsg(const QString& msg);
+ void updatedDevicesWarningMsg(const QString& msg);
+ void updatedDevicesErrorHelpUrl(const QString& url);
+ void updatedDevicesWarningHelpUrl(const QString& url);
private:
void setupAuthenticator();
void getSubscriptions();
void getRegions();
void getUserMetadata();
+ void stopStudio();
+ bool readyToJoin();
#ifdef RT_AUDIO
- void getDeviceList(QStringList* list, bool isInput);
+ QVariant formatDeviceList(const QStringList& devices, const QStringList& categories);
#endif
- void stopStudio();
bool m_showFirstRun = false;
bool m_checkSsl = true;
- bool m_shouldJoin = true;
QString m_updateChannel;
QString m_refreshToken;
QString m_userId;
QTimer m_inputClipTimer;
QTimer m_outputClipTimer;
+ QString m_devicesWarningMsg = QStringLiteral("");
+ QString m_devicesErrorMsg = QStringLiteral("");
+ QString m_devicesWarningHelpUrl = QStringLiteral("");
+ QString m_devicesErrorHelpUrl = QStringLiteral("");
+ QString m_windowState = QStringLiteral("login");
+
float m_meterMax = 0.0;
float m_meterMin = -64.0;
#ifdef RT_AUDIO
QStringList m_inputDeviceList;
QStringList m_outputDeviceList;
+ QStringList m_inputDeviceCategories;
+ QStringList m_outputDeviceCategories;
QString m_inputDevice;
QString m_outputDevice;
quint16 m_bufferSize;
#ifdef __APPLE__
NoNap m_noNap;
#endif
+
+ QSharedPointer<VsPermissions> m_permissions;
};
#endif // VIRTUALSTUDIO_H
width: 696
height: 577
color: backgroundColour
- state: virtualstudio.showFirstRun ? "start" : "login"
+ state: virtualstudio.showFirstRun ? "start" : virtualstudio.windowState
anchors.fill: parent
id: window
target: virtualstudio
onAuthSucceeded: {
if (virtualstudio.showDeviceSetup) {
- virtualstudio.shouldJoin = false;
- window.state = "setup";
+ virtualstudio.windowState = "setup";
} else {
- virtualstudio.shouldJoin = true;
- window.state = "browse";
+ virtualstudio.windowState = "browse";
}
}
onAuthFailed: {
loginScreen.failTextVisible = true;
}
onConnected: {
- window.state = "connected";
+ virtualstudio.windowState = "connected";
}
onFailed: {
- window.state = "failed";
+ virtualstudio.windowState = "failed";
}
onDisconnected: {
- window.state = "browse";
+ virtualstudio.windowState = "browse";
}
}
}
if (gVerboseFlag)
std::cout << " JackTrip:setupAudio before m_audioInterface->setup"
<< std::endl;
- m_audioInterface->setup();
+ m_audioInterface->setup(true);
+
+ std::string devicesWarningMsg = m_audioInterface->getDevicesWarningMsg();
+ std::string devicesErrorMsg = m_audioInterface->getDevicesErrorMsg();
+
+ if (devicesWarningMsg != "") {
+ qDebug() << "Devices Warning: "
+ << QString::fromStdString(devicesWarningMsg);
+ }
+
+ if (devicesErrorMsg != "") {
+ qDebug() << "Devices Error: " << QString::fromStdString(devicesErrorMsg);
+ }
+
+ updateDevicesWarningMsg(QString::fromStdString(devicesWarningMsg));
+ updateDevicesErrorMsg(QString::fromStdString(devicesErrorMsg));
+
if (gVerboseFlag)
std::cout
<< " JackTrip:setupAudio before m_audioInterface->getSampleRate"
m_audioInterface->setInputDevice(m_inputDeviceName);
m_audioInterface->setOutputDevice(m_outputDeviceName);
m_audioInterface->setBufferSizeInSamples(m_audioBufferSize);
- m_audioInterface->setup();
+
+ m_audioInterface->setup(true);
// Setup might have reduced number of channels
m_numAudioChansIn = m_audioInterface->getNumInputChannels();
m_numAudioChansOut = m_audioInterface->getNumOutputChannels();
// Setup might have changed buffer size
m_audioBufferSize = m_audioInterface->getBufferSizeInSamples();
+
+ std::string devicesWarningMsg = m_audioInterface->getDevicesWarningMsg();
+ std::string devicesErrorMsg = m_audioInterface->getDevicesErrorMsg();
+
+ if (devicesWarningMsg != "") {
+ qDebug() << "Devices Warning: "
+ << QString::fromStdString(devicesWarningMsg);
+ }
+
+ if (devicesErrorMsg != "") {
+ qDebug() << "Devices Error: " << QString::fromStdString(devicesErrorMsg);
+ }
+
+ updateDevicesWarningMsg(QString::fromStdString(devicesWarningMsg));
+ updateDevicesErrorMsg(QString::fromStdString(devicesErrorMsg));
+
#endif
#endif
} else if (m_audioInterfaceMode == VsAudioInterface::RTAUDIO) {
m_audioInterface->setInputDevice(m_inputDeviceName);
m_audioInterface->setOutputDevice(m_outputDeviceName);
m_audioInterface->setBufferSizeInSamples(m_audioBufferSize);
- m_audioInterface->setup();
+
+ m_audioInterface->setup(true);
// Setup might have reduced number of channels
m_numAudioChansIn = m_audioInterface->getNumInputChannels();
m_numAudioChansOut = m_audioInterface->getNumOutputChannels();
// Setup might have changed buffer size
m_audioBufferSize = m_audioInterface->getBufferSizeInSamples();
+
+ std::string devicesWarningMsg = m_audioInterface->getDevicesWarningMsg();
+ std::string devicesErrorMsg = m_audioInterface->getDevicesErrorMsg();
+ std::string devicesWarningHelpUrl =
+ m_audioInterface->getDevicesWarningHelpUrl();
+ std::string devicesErrorHelpUrl = m_audioInterface->getDevicesErrorHelpUrl();
+
+ if (devicesWarningMsg != "") {
+ qDebug() << "Devices Warning: "
+ << QString::fromStdString(devicesWarningMsg);
+ if (devicesWarningHelpUrl != "") {
+ qDebug() << "Learn More: "
+ << QString::fromStdString(devicesWarningHelpUrl);
+ }
+ }
+
+ if (devicesErrorMsg != "") {
+ qDebug() << "Devices Error: " << QString::fromStdString(devicesErrorMsg);
+ if (devicesErrorHelpUrl != "") {
+ qDebug() << "Learn More: "
+ << QString::fromStdString(devicesErrorHelpUrl);
+ }
+ }
+
+ updateDevicesWarningMsg(QString::fromStdString(devicesWarningMsg));
+ updateDevicesErrorMsg(QString::fromStdString(devicesErrorMsg));
+ updateDevicesWarningHelpUrl(QString::fromStdString(devicesWarningHelpUrl));
+ updateDevicesErrorHelpUrl(QString::fromStdString(devicesErrorHelpUrl));
#endif
}
{
if (!m_audioInterface.isNull() && !m_audioActive) {
try {
- m_audioInterface->initPlugins();
+ m_audioInterface->initPlugins(false);
m_audioInterface->startProcess();
if (m_audioInterfaceMode == VsAudioInterface::JACK) {
m_audioInterface->connectDefaultPorts();
settings.endGroup();
emit updatedOutputMuted(muted);
}
+
+void VsAudioInterface::updateDevicesErrorMsg(const QString& msg)
+{
+ emit devicesErrorMsgChanged(msg);
+ return;
+}
+
+void VsAudioInterface::updateDevicesWarningMsg(const QString& msg)
+{
+ emit devicesWarningMsgChanged(msg);
+ return;
+}
+
+void VsAudioInterface::updateDevicesWarningHelpUrl(const QString& url)
+{
+ emit devicesWarningHelpUrlChanged(url);
+ return;
+}
+
+void VsAudioInterface::updateDevicesErrorHelpUrl(const QString& url)
+{
+ emit devicesErrorHelpUrlChanged(url);
+ return;
+}
void modeUpdated();
void newVolumeMeterMeasurements(QVector<float> values);
void errorToProcess(const QString& errorMessage);
+ void devicesErrorMsgChanged(const QString& msg);
+ void devicesWarningMsgChanged(const QString& msg);
+ void devicesErrorHelpUrlChanged(const QString& url);
+ void devicesWarningHelpUrlChanged(const QString& url);
private slots:
// void refreshAudioStream();
Volume* m_inputVolumePlugin;
Volume* m_outputVolumePlugin;
Tone* m_outputTonePlugin;
+
+ void updateDevicesErrorMsg(const QString& msg);
+ void updateDevicesWarningMsg(const QString& msg);
+ void updateDevicesErrorHelpUrl(const QString& url);
+ void updateDevicesWarningHelpUrl(const QString& url);
};
#endif // VSDAUDIOINTERFACE_H
// Set server levels to stored versions
QJsonObject json = {
- {QLatin1String("captureVolume"), m_captureVolume * 100.0},
+ {QLatin1String("captureVolume"), (int)(m_captureVolume * 100.0)},
{QLatin1String("captureMute"), m_captureMute},
};
QJsonDocument request = QJsonDocument(json);
{
// Add latest volume and mute values to heartbeat body
QJsonObject json = {
- {QLatin1String("captureVolume"), (int)(m_captureVolume * 100)},
+ {QLatin1String("captureVolume"), (int)(m_captureVolume * 100.0)},
{QLatin1String("captureMute"), m_captureMute},
};
QJsonDocument request = QJsonDocument(json);
--- /dev/null
+//*****************************************************************
+/*
+ JackTrip: A System for High-Quality Audio Network Performance
+ over the Internet
+
+ Copyright (c) 2008-2021 Juan-Pablo Caceres, Chris Chafe.
+ SoundWIRE group at CCRMA, Stanford University.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+/**
+ * \file vsMacPermissions.h
+ * \author Matt Horton
+ * \date Oct 2022
+ */
+
+#ifndef __VSMACPERMISSIONS_H__
+#define __VSMACPERMISSIONS_H__
+
+#include <objc/objc.h>
+
+#include <QDebug>
+#include <QObject>
+#include <QString>
+
+#include "vsPermissions.h"
+
+class VsMacPermissions : public VsPermissions
+{
+ Q_OBJECT
+
+ public:
+ explicit VsMacPermissions();
+
+ bool micPermissionChecked() override;
+ Q_INVOKABLE void getMicPermission() override;
+ Q_INVOKABLE void openSystemPrivacy();
+
+ private:
+ QString m_micPermission = "unknown";
+ bool m_micPermissionChecked = false;
+};
+
+#endif // __VSMACPERMISSIONS_H__
--- /dev/null
+//*****************************************************************
+/*
+ JackTrip: A System for High-Quality Audio Network Performance
+ over the Internet
+
+ Copyright (c) 2008-2021 Juan-Pablo Caceres, Chris Chafe.
+ SoundWIRE group at CCRMA, Stanford University.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+/**
+ * \file vsMacPermissions.mm
+ * \author Matt Horton
+ * \date Oct 2022
+ */
+
+#include "vsMacPermissions.h"
+#include <Foundation/Foundation.h>
+#include <AVFoundation/AVFoundation.h>
+#include <QDesktopServices>
+#include <QSettings>
+#include <QUrl>
+
+VsMacPermissions::VsMacPermissions()
+{
+ QSettings settings;
+ settings.beginGroup(QStringLiteral("VirtualStudio"));
+ m_micPermissionChecked = settings.value(QStringLiteral("MicPermissionChecked"), false).toBool();
+ settings.endGroup();
+}
+
+bool VsMacPermissions::micPermissionChecked()
+{
+ if (m_micPermissionChecked) {
+ getMicPermission();
+ }
+ return m_micPermissionChecked;
+}
+
+void VsMacPermissions::getMicPermission()
+{
+ if (@available(macOS 10.14, *)) {
+ // Request permission to access.
+ switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio])
+ {
+ case AVAuthorizationStatusAuthorized:
+ {
+ // The user has previously granted access.
+ setMicPermission(QStringLiteral("granted"));
+ break;
+ }
+ case AVAuthorizationStatusNotDetermined:
+ {
+ // The app hasn't yet asked the user for access.
+ [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
+ if (granted) {
+ setMicPermission(QStringLiteral("granted"));
+ } else {
+ setMicPermission(QStringLiteral("denied"));
+ }
+ }];
+ setMicPermission(QStringLiteral("unknown"));
+ break;
+ }
+ case AVAuthorizationStatusDenied:
+ {
+ // The user has previously denied access.
+ setMicPermission(QStringLiteral("denied"));
+ }
+ case AVAuthorizationStatusRestricted:
+ {
+ // The user can't grant access due to restrictions.
+ setMicPermission(QStringLiteral("denied"));
+ }
+ }
+ } else {
+ setMicPermission(QStringLiteral("granted"));
+ }
+}
+
+void VsMacPermissions::openSystemPrivacy()
+{
+ QDesktopServices::openUrl(QUrl("x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone"));
+}
--- /dev/null
+//*****************************************************************
+/*
+ JackTrip: A System for High-Quality Audio Network Performance
+ over the Internet
+
+ Copyright (c) 2008-2021 Juan-Pablo Caceres, Chris Chafe.
+ SoundWIRE group at CCRMA, Stanford University.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+/**
+ * \file vsPermissions.mm
+ * \author Matt Horton
+ * \date Oct 2022
+ */
+
+#include "vsPermissions.h"
+
+#include <QDesktopServices>
+#include <QSettings>
+#include <QUrl>
+
+QString VsPermissions::micPermission()
+{
+ return m_micPermission;
+}
+
+bool VsPermissions::micPermissionChecked()
+{
+ return m_micPermissionChecked;
+}
+
+void VsPermissions::getMicPermission()
+{
+ setMicPermission("granted");
+}
+
+void VsPermissions::setMicPermission(QString status)
+{
+ m_micPermission = status;
+ m_micPermissionChecked = true;
+ emit micPermissionUpdated();
+
+ QSettings settings;
+ settings.beginGroup(QStringLiteral("VirtualStudio"));
+ settings.setValue(QStringLiteral("MicPermissionChecked"), m_micPermissionChecked);
+ settings.endGroup();
+}
--- /dev/null
+//*****************************************************************
+/*
+ JackTrip: A System for High-Quality Audio Network Performance
+ over the Internet
+
+ Copyright (c) 2008-2021 Juan-Pablo Caceres, Chris Chafe.
+ SoundWIRE group at CCRMA, Stanford University.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+*/
+//*****************************************************************
+/**
+ * \file vsPermissions.h
+ * \author Matt Horton
+ * \date Nov 2022
+ */
+
+#ifndef __VSPERMISSIONS_H__
+#define __VSPERMISSIONS_H__
+
+#include <QDebug>
+#include <QObject>
+#include <QString>
+
+class VsPermissions : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString micPermission READ micPermission NOTIFY micPermissionUpdated)
+
+ public:
+ VsPermissions() = default; // define here and there
+
+ QString micPermission(); // define here
+ virtual bool micPermissionChecked(); // define here and there
+ Q_INVOKABLE virtual void getMicPermission();
+ void setMicPermission(QString status); // define here
+
+ signals:
+ void micPermissionUpdated(); // leave here
+
+ protected:
+#if __APPLE__
+ QString m_micPermission = "unknown";
+ bool m_micPermissionChecked = false;
+#else
+ QString m_micPermission = "granted";
+ bool m_micPermissionChecked = true;
+#endif
+};
+
+#endif // __VSPERMISSIONS_H__
--- /dev/null
+//*****************************************************************
+/*
+ QJackTrip: Bringing a graphical user interface to JackTrip, a
+ system for high quality audio network performance over the
+ internet.
+
+ Copyright (c) 2022 Aaron Wyatt.
+
+ This file is part of QJackTrip.
+
+ QJackTrip is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ QJackTrip 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QJackTrip. If not, see <https://www.gnu.org/licenses/>.
+*/
+//*****************************************************************
+
+#include "vuMeter.h"
+
+#include <cmath>
+
+VuMeter::VuMeter(QWidget* parent) : QWidget(parent), m_level(0)
+{
+ m_greenOn.setRgb(97, 197, 84);
+ m_greenOff.setRgb(29, 67, 24);
+ m_yellowOn.setRgb(245, 191, 79);
+ m_yellowOff.setRgb(85, 65, 22);
+ m_redOn.setRgb(242, 27, 27);
+ m_redOff.setRgb(84, 4, 4);
+}
+
+void VuMeter::setLevel(qreal level)
+{
+ m_level = level;
+ update();
+}
+
+void VuMeter::paintEvent([[maybe_unused]] QPaintEvent* event)
+{
+ quint32 binWidth = std::floor((width() - ((m_bins - 1) * m_margins)) / m_bins);
+ QPainter painter(this);
+
+ painter.setPen(Qt::NoPen);
+ quint32 level = std::round(m_level * (m_bins + 1));
+ for (quint32 i = 0; i < m_bins; i++) {
+ bool on = level > i;
+ if (on) {
+ if (i < 9) {
+ painter.setBrush(m_greenOn);
+ } else if (i < 12) {
+ painter.setBrush(m_yellowOn);
+ } else {
+ painter.setBrush(m_redOn);
+ }
+ } else {
+ if (i < 9) {
+ painter.setBrush(m_greenOff);
+ } else if (i < 12) {
+ painter.setBrush(m_yellowOff);
+ } else {
+ painter.setBrush(m_redOff);
+ }
+ }
+
+ painter.drawRect((binWidth + m_margins) * i, 0, binWidth, height());
+ }
+}
--- /dev/null
+//*****************************************************************
+/*
+ QJackTrip: Bringing a graphical user interface to JackTrip, a
+ system for high quality audio network performance over the
+ internet.
+
+ Copyright (c) 2022 Aaron Wyatt.
+
+ This file is part of QJackTrip.
+
+ QJackTrip is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ QJackTrip 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QJackTrip. If not, see <https://www.gnu.org/licenses/>.
+*/
+//*****************************************************************
+
+#ifndef VUMETER_H
+#define VUMETER_H
+
+#include <QPainter>
+#include <QWidget>
+
+class VuMeter : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ VuMeter(QWidget* parent = nullptr);
+ ~VuMeter() override = default;
+
+ void setLevel(qreal level);
+
+ protected:
+ void paintEvent(QPaintEvent* event) override;
+
+ private:
+ qreal m_level;
+ QColor m_greenOn;
+ QColor m_greenOff;
+ QColor m_yellowOn;
+ QColor m_yellowOff;
+ QColor m_redOn;
+ QColor m_redOff;
+
+ quint32 m_bins = 15;
+ quint32 m_margins = 2;
+};
+
+#endif // VUMETER_H
#include "AudioInterface.h"
-constexpr const char* const gVersion = "1.6.6"; ///< JackTrip version
+constexpr const char* const gVersion = "1.6.7"; ///< JackTrip version
//*******************************************************************************
/// \name Default Values
Event="DoAction" \r
Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>\r
</UI>\r
+ <Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1"/>\r
<Icon Id='jacktrip.exe' SourceFile='jacktrip.exe' />\r
</Product>\r
</Wix>\r