+- Version: "1.5.0"
+ Date: 2022-01-03
+ Description:
+ - (added) option to upmix mono clients to stereo in hub server mode patching
- Version: "1.4.3"
Date: 2021-12-18
Description:
*/
#include "Patcher.h"
-
-Patcher::Patcher() : m_patchMode(JackTrip::SERVERTOCLIENT), m_jackClient(nullptr) {}
+#include <QVector>
void Patcher::setPatchMode(JackTrip::hubConnectionModeT patchMode)
{
m_patchMode = patchMode;
}
+void Patcher::setStereoUpmix(bool upmix)
+{
+ m_steroUpmix = upmix;
+}
+
void Patcher::registerClient(const QString& clientName)
{
QMutexLocker locker(&m_connectionMutex);
outPorts = jack_get_ports(m_jackClient, NULL, NULL, JackPortIsOutput);
inPorts = jack_get_ports(m_jackClient, NULL, NULL, JackPortIsInput);
- // Start with our receiving ports.
+ // Find the ports belonging to our client.
+ QVector<const char*> clientOutPorts;
+ QVector<const char*> clientInPorts;
+
for (int i = 0; outPorts[i]; i++) {
- QString client = QString(outPorts[i]).section(":", 0, 0);
- if (client == clientName) {
- QString channel = QString(outPorts[i]).section("_", -1, -1);
- for (int j = 0; inPorts[j]; j++) {
- // First check if this is one of our other clients. (Fan out/in and full
- // mix.)
- if (m_patchMode == JackTrip::CLIENTFOFI
- || m_patchMode == JackTrip::FULLMIX) {
- if (m_clients.contains(QString(inPorts[j]).section(":", 0, 0))
- && QString(inPorts[j]).section("_", -1, -1) == channel
- && !QString(outPorts[i]).contains("broadcast")) {
- jack_connect(m_jackClient, outPorts[i], inPorts[j]);
+ // Exclude broadcast ports.
+ if (QString(outPorts[i]).section(":", 0, 0) == clientName
+ && !QString(outPorts[i]).contains("broadcast")) {
+ clientOutPorts.append(outPorts[i]);
+ }
+ }
+
+ for (int i = 0; inPorts[i]; i++) {
+ if (QString(inPorts[i]).section(":", 0, 0) == clientName) {
+ clientInPorts.append(inPorts[i]);
+ }
+ }
+
+ bool clientIsMono = (clientOutPorts.count() == 1);
+
+ // Start with our receiving ports.
+ for (int i = 0; i < clientOutPorts.count(); i++) {
+ QString channel = QString(clientOutPorts.at(i)).section("_", -1, -1);
+ for (int j = 0; inPorts[j]; j++) {
+ QString otherClient = QString(inPorts[j]).section(":", 0, 0);
+ QString otherChannel = QString(inPorts[j]).section("_", -1, -1);
+
+ // First check if this is one of our other clients. (Fan out/in and full mix.)
+ if (m_patchMode == JackTrip::CLIENTFOFI || m_patchMode == JackTrip::FULLMIX) {
+ if (m_clients.contains(otherClient) && otherChannel == channel) {
+ jack_connect(m_jackClient, clientOutPorts.at(i), inPorts[j]);
+ } else if (m_steroUpmix && clientIsMono) {
+ // Deal with the special case of stereo upmix
+ if (m_clients.contains(otherClient) && otherChannel == "2") {
+ jack_connect(m_jackClient, clientOutPorts.at(i), inPorts[j]);
}
}
- // Then check if it's our registering client. (Client Echo and full mix.)
- if (m_patchMode == JackTrip::CLIENTECHO
- || m_patchMode == JackTrip::FULLMIX) {
- if (QString(inPorts[j]).section(":", 0, 0) == clientName
- && QString(inPorts[j]).section("_", -1, -1) == channel
- && !QString(outPorts[i]).contains("broadcast")) {
- jack_connect(m_jackClient, outPorts[i], inPorts[j]);
+ }
+
+ // Then check if it's our registering client. (Client Echo and full mix.)
+ if (m_patchMode == JackTrip::CLIENTECHO || m_patchMode == JackTrip::FULLMIX) {
+ if (otherClient == clientName && otherChannel == channel) {
+ jack_connect(m_jackClient, clientOutPorts.at(i), inPorts[j]);
+ } else if (m_steroUpmix && clientIsMono) {
+ if (otherClient == clientName && otherChannel == "2") {
+ jack_connect(m_jackClient, clientOutPorts.at(i), inPorts[j]);
}
}
}
}
// Then our sending ports. We only need to check for other clients here.
- //(Any loopback connections will have been made in the previous loop.)
+ // (Any loopback connections will have been made in the previous loop.)
if (m_patchMode == JackTrip::CLIENTFOFI || m_patchMode == JackTrip::FULLMIX) {
- for (int i = 0; inPorts[i]; i++) {
- QString client = QString(inPorts[i]).section(":", 0, 0);
- if (client == clientName) {
- QString channel = QString(inPorts[i]).section("_", -1, -1);
- for (int j = 0; outPorts[j]; j++) {
- if (m_clients.contains(QString(outPorts[j]).section(":", 0, 0))
- && QString(outPorts[j]).section("_", -1, -1) == channel
- && !QString(outPorts[j]).contains("broadcast")) {
- jack_connect(m_jackClient, outPorts[j], inPorts[i]);
+ for (int i = 0; i < clientInPorts.count(); i++) {
+ QString channel = QString(clientInPorts.at(i)).section("_", -1, -1);
+ for (int j = 0; outPorts[j]; j++) {
+ QString otherClient = QString(outPorts[j]).section(":", 0, 0);
+ QString otherChannel = QString(outPorts[j]).section("_", -1, -1);
+ if (m_clients.contains(otherClient)
+ && !QString(outPorts[j]).contains("broadcast")) {
+ if (otherChannel == channel ||
+ (m_steroUpmix && channel == "2" && m_monoClients.contains(otherClient))) {
+ jack_connect(m_jackClient, outPorts[j], clientInPorts.at(i));
}
}
}
}
m_clients.append(clientName);
+ if (clientIsMono) {
+ m_monoClients.append(clientName);
+ }
jack_free(outPorts);
jack_free(inPorts);
}
{
QMutexLocker locker(&m_connectionMutex);
m_clients.removeAll(clientName);
+ m_monoClients.removeAll(clientName);
}
void Patcher::shutdownCallback(void* arg)
Q_OBJECT
public:
- Patcher();
+ Patcher() = default;
virtual ~Patcher();
void setPatchMode(JackTrip::hubConnectionModeT patchMode);
+ void setStereoUpmix(bool upmix);
void registerClient(const QString& clientName);
void unregisterClient(const QString& clientName);
private:
QStringList m_clients;
- JackTrip::hubConnectionModeT m_patchMode;
+ QStringList m_monoClients;
+ JackTrip::hubConnectionModeT m_patchMode = JackTrip::SERVERTOCLIENT;
+ bool m_steroUpmix = false;
- jack_client_t* m_jackClient;
+ jack_client_t* m_jackClient = nullptr;
jack_status_t m_status;
QMutex m_connectionMutex;
{"verbose", no_argument, NULL, 'V'}, // Verbose mode
{"hubpatch", required_argument, NULL,
'p'}, // Set hubConnectionMode for auto patch in Jack
+ {"upmix", no_argument, NULL, 'u'}, // Upmix mono clients when patching
{"iostat", required_argument, NULL, 'I'}, // Set IO stat timeout
{"iostatlog", required_argument, NULL, 'G'}, // Set IO stat log file
{"effects", required_argument, NULL,
int ch;
while (
(ch = getopt_long(argc, argv,
- "n:N:H:sc:SC:o:B:P:U:q:r:b:ztlwjeJ:K:RTd:F:p:DvVhI:G:f:O:a:x:A",
+ "n:N:H:sc:SC:o:B:P:U:q:r:b:ztlwjeJ:K:RTd:F:p:uDvVhI:G:f:O:a:x:A",
longopts, NULL))
!= -1)
switch (ch) {
std::exit(1);
}
break;
+ case 'u':
+ mStereoUpmix = true;
+ break;
case 'I': // IO Stat timeout
//-------------------------------------------------------
mIOStatTimeout = atoi(optarg);
"2=client fan out/in but not loopback, 3=reserved for TUB, 4=full mix, 5=no "
"auto patching (default: 0)"
<< endl;
+ cout << " -u, --upmix Upmix mono clients to stereo when "
+ "patching in HUB SERVER mode"
+ << endl;
cout << " -z, --zerounderrun Set buffer to zeros when underrun "
"occurs (default: wavetable)"
<< endl;
udpHub->setWAIR(mWAIR);
#endif // endwhere
udpHub->setHubPatch(mHubConnectionMode);
+ udpHub->setStereoUpmix(mStereoUpmix);
// Connect default audio ports must be set after the connection mode.
udpHub->setConnectDefaultAudioPorts(mConnectDefaultAudioPorts);
// Set buffers to zero when underrun
std::string mInputDeviceName, mOutputDeviceName;
#endif
unsigned int mHubConnectionMode = JackTrip::SERVERTOCLIENT;
+ bool mStereoUpmix = false;
bool mConnectDefaultAudioPorts = true; ///< Connect or not jack audio ports
int mIOStatTimeout = 0;
QSharedPointer<std::ostream> mIOStatStream;
#ifndef __NO_JACK__
Patcher mPatcher;
#endif
+ bool mStereoUpmix;
int mIOStatTimeout;
QSharedPointer<std::ostream> mIOStatStream;
}
}
unsigned int getHubPatch() { return mHubPatch; }
+
+ void setStereoUpmix([[maybe_unused]] bool upmix) {
+#ifndef __NO_JACK__
+ mPatcher.setStereoUpmix(upmix);
+#endif
+ }
void setUnderRunMode(JackTrip::underrunModeT UnderRunMode)
{
//(loadSettings will take care of the UI in all other cases.)
m_ui->basePortLabel->setVisible(false);
m_ui->basePortSpinBox->setVisible(false);
+ m_ui->upmixCheckBox->setVisible(false);
m_ui->requireAuthGroupBox->setVisible(false);
#ifdef __RT_AUDIO__
m_ui->timeoutCheckBox->setVisible(false);
m_ui->autoPatchComboBox->setVisible(true);
m_ui->autoPatchLabel->setVisible(true);
+ m_ui->upmixCheckBox->setVisible(true);
m_ui->requireAuthGroupBox->setVisible(true);
advancedOptionsForHubServer(true);
int index = findTab("Plugins");
} else {
m_ui->autoPatchComboBox->setVisible(false);
m_ui->autoPatchLabel->setVisible(false);
+ m_ui->upmixCheckBox->setVisible(false);
m_ui->requireAuthGroupBox->setVisible(false);
m_ui->channelGroupBox->setVisible(true);
m_ui->timeoutCheckBox->setVisible(true);
}
m_udpHub->setHubPatch(hubConnectionMode);
+ m_udpHub->setStereoUpmix(m_ui->upmixCheckBox->isChecked());
if (m_ui->zeroCheckBox->isChecked()) {
// Set buffers to zero when underrun
}
m_ui->autoPatchComboBox->setCurrentIndex(settings.value("AutoPatchMode", 0).toInt());
+ m_ui->upmixCheckBox->setChecked(settings.value("StereoUpmix", false).toBool());
m_ui->zeroCheckBox->setChecked(settings.value("ZeroUnderrun", false).toBool());
m_ui->timeoutCheckBox->setChecked(settings.value("Timeout", false).toBool());
m_ui->clientNameEdit->setText(settings.value("ClientName", "").toString());
settings.setValue("ChannelsSend", m_ui->channelSendSpinBox->value());
settings.setValue("ChannelsRecv", m_ui->channelRecvSpinBox->value());
settings.setValue("AutoPatchMode", m_ui->autoPatchComboBox->currentIndex());
+ settings.setValue("StereoUpmix", m_ui->upmixCheckBox->isChecked());
settings.setValue("ZeroUnderrun", m_ui->zeroCheckBox->isChecked());
settings.setValue("Timeout", m_ui->timeoutCheckBox->isChecked());
settings.setValue("ClientName", m_ui->clientNameEdit->text());
if (hubConnectionMode > 0) {
commandLine.append(QString(" -p %1").arg(hubConnectionMode));
}
+ if (m_ui->upmixCheckBox->isChecked()) {
+ commandLine.append(" -u");
+ }
} else {
if (m_ui->channelSendSpinBox->value() != gDefaultNumInChannels
|| m_ui->channelRecvSpinBox->value() != gDefaultNumOutChannels) {
</property>
</widget>
</item>
+ <item row="4" column="0" colspan="3">
+ <widget class="QCheckBox" name="upmixCheckBox">
+ <property name="text">
+ <string>&Upmix mono clients to stereo</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="advancedTab">
<tabstop>channelRecvSpinBox</tabstop>
<tabstop>channelSendSpinBox</tabstop>
<tabstop>autoPatchComboBox</tabstop>
+ <tabstop>upmixCheckBox</tabstop>
<tabstop>zeroCheckBox</tabstop>
<tabstop>timeoutCheckBox</tabstop>
<tabstop>requireAuthCheckBox</tabstop>
<tabstop>outCompressorCheckBox</tabstop>
<tabstop>outLimiterCheckBox</tabstop>
<tabstop>outClientsSpinBox</tabstop>
+ <tabstop>verboseCheckBox</tabstop>
</tabstops>
<resources>
<include location="qjacktrip.qrc"/>
</property>
</widget>
</item>
+ <item row="4" column="0" colspan="3">
+ <widget class="QCheckBox" name="upmixCheckBox">
+ <property name="text">
+ <string>&Upmix mono clients to stereo</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="advancedTab">
<tabstop>channelRecvSpinBox</tabstop>
<tabstop>channelSendSpinBox</tabstop>
<tabstop>autoPatchComboBox</tabstop>
+ <tabstop>upmixCheckBox</tabstop>
<tabstop>zeroCheckBox</tabstop>
<tabstop>timeoutCheckBox</tabstop>
<tabstop>requireAuthCheckBox</tabstop>
#include "AudioInterface.h"
-constexpr const char* const gVersion = "1.4.3"; ///< JackTrip version
+constexpr const char* const gVersion = "1.5.0"; ///< JackTrip version
//*******************************************************************************
/// \name Default Values