# QtCreator user files
*.pro.user
*.pro.user.*
+
+# build directory
+builddir/
--- /dev/null
+os:
+ - linux
+ - osx
+
+language:
+ - cpp
+
+# force updated ubuntu distribution with up-to-date gcc
+dist: bionic
+
+branches:
+ - master
+
+env:
+ global:
+ - PKGS_OSX="jack qt rt-audio"
+
+addons:
+ apt:
+ update: true
+ packages:
+ - qt5-default
+ - qttools5-dev-tools
+ - libjack-dev
+ - librtaudio-dev
+
+before_script:
+ # manually install brew packages as addon: homebrew is broken in the default osx image
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install $PKGS_OSX; fi
+ # mac: workaround for homebrew qmake not being symlinked into $PATH
+ # see https://stackoverflow.com/questions/48847505/why-cant-i-use-qmake-on-mac-after-installing-it
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export PATH="/usr/local/opt/qt/bin:$PATH"; fi
+
+script:
+ - cd src
+ - ./build
+
+after_success:
+ - echo "Success!"
+
+after_failure:
+ - echo "Something went wrong :("
---
-master
+1.2.1 (master) (branch name will be deprecated)
+(main and dev) (new branches, respectively for stable and development)
+- (added) src/build script builds in ../builddir
+- (fixed) refactor "Master" to be "Hub"
+- (fixed) 1.2.1 correctly versioned and tagged
---
1.2 (release candidate, not yet tagged)
$ make release\r
\r
\r
-If you want to install (using Terminal): on the /src directory type:\r
+If you want to install (using Terminal): on the /builddir directory type:\r
\r
$ sudo cp jacktrip /usr/local/bin/\r
(enter your password when prompted)\r
--- /dev/null
+# Build and Installation Instructions with Meson
+
+## Install Dependencies
+
+Fedora:
+dnf install meson qt5-qtbase-devel rtaudio-devel jack-audio-connection-kit-devel
+
+Debian/Ubuntu:
+apt install meson build-essential qtbase5-dev librtaudio-dev libjack-jackd2-dev
+
+MacOS with brew (not tested):
+brew install meson qt rt-audio jack
+
+## Build
+
+Prepare your build directory (by default debug and nonoptimized):
+meson builddir
+
+Now build with:
+ninja -C builddir
+
+Install with:
+ninja -C builddir install
}
//*******************************************************************************
void JMess::connectSpawnedPorts(int nChans, int hubPatch)
-// called from UdpMasterListener::connectMesh
+// called from UdpHubListener::connectMesh
{
QString IPS[gMAX_WAIRS];
int ctr = 0;
// this is brute force, does not look at individual clients, just patches the whole ensemble
// each time
void JMess::connectTUB(int /*nChans*/)
-// called from UdpMasterListener::connectPatch
+// called from UdpHubListener::connectPatch
{
for (int i = 0; i<=gMAX_TUB-gMIN_TUB; i++) // last IP decimal octet
for (int l = 1; l<=1; l++) // mono for now // chans are 1-based, 1...2
## Links ##
* Preliminary [Documentation](http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/) and [API](http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/annotated.html).
* Subscribe to the [Mailing List](http://groups.google.com/group/jacktrip-users).
- * [CCRMA](http://ccrma.stanford.edu/) .
+ * [CCRMA](http://ccrma.stanford.edu/).
* [SoundWIRE group](http://ccrma.stanford.edu/groups/soundwire/).
* [Juan-Pablo Caceres](https://ccrma.stanford.edu/~jcaceres/).
--- /dev/null
+project('jacktrip', 'cpp', version: '1.2')
+qt5 = import('qt5')
+qt5_dep = dependency('qt5', modules: ['Core', 'Network'])
+jack_dep = dependency('jack')
+rtaudio_dep = dependency('rtaudio')
+thread_dep = dependency('threads')
+
+defines = []
+if host_machine.system() == 'linux'
+ defines += '-D__LINUX__'
+elif host_machine.system() == 'osx'
+ defines += '-D__MAC_OSX__'
+elif host_machine.system() == 'windows'
+ defines += '-D__WIN_32__'
+endif
+
+moc_h = ['src/DataProtocol.h',
+ 'src/JackTrip.h',
+ 'src/JackTripWorker.h',
+ 'src/JackTripWorkerMessages.h',
+ 'src/NetKS.h',
+ 'src/PacketHeader.h',
+ 'src/Settings.h',
+ 'src/UdpDataProtocol.h',
+ 'src/UdpHubListener.h']
+moc_files = qt5.preprocess(moc_headers : moc_h)
+
+src = ['src/DataProtocol.cpp',
+ 'src/JMess.cpp',
+ 'src/JackTrip.cpp',
+ 'src/jacktrip_globals.cpp',
+ 'src/jacktrip_main.cpp',
+ 'src/JackTripThread.cpp',
+ 'src/JackTripWorker.cpp',
+ 'src/LoopBack.cpp',
+ 'src/PacketHeader.cpp',
+ 'src/ProcessPlugin.cpp',
+ 'src/RingBuffer.cpp',
+ 'src/Settings.cpp',
+ 'src/UdpDataProtocol.cpp',
+ 'src/UdpHubListener.cpp',
+ 'src/AudioInterface.cpp',
+ 'src/JackAudioInterface.cpp']
+
+executable('jacktrip', src, moc_files, dependencies: [qt5_dep, jack_dep, rtaudio_dep, thread_dep], cpp_args: defines, install: true )
virtual void setSocket(int &socket) = 0;
#endif
+ struct PktStat {
+ uint32_t tot;
+ uint32_t lost;
+ uint32_t outOfOrder;
+ uint32_t revived;
+ uint32_t statCount;
+ };
+ virtual bool getStats(PktStat*) {return false;}
+
signals:
void signalError(const char* error_message);
#include "jacktrip_globals.h"
#include <QDebug>
+// sJackMutex definition
+QMutex JMess::sJMessMutex;
//-------------------------------------------------------------------------------
/*! \brief Constructs a JMess object that has a jack client.
}
//*******************************************************************************
void JMess::connectSpawnedPorts(int nChans, int hubPatch)
-// called from UdpMasterListener::connectMesh
+// called from UdpHubListener::connectMesh
{
+
+ QMutexLocker locker(&sJMessMutex);
+
QString IPS[gMAX_WAIRS];
int ctr = 0;
// qDebug() << ports[out_i] << systemPort << s;
}
}
- for (int i = 0; i<ctr; i++) qDebug() << IPS[i];
+// for (int i = 0; i<ctr; i++) qDebug() << IPS[i];
disconnectAll();
int k = 0;
if ((hubPatch == JackTrip::CLIENTECHO)||(hubPatch == JackTrip::FULLMIX)) k = i;
else if (hubPatch == JackTrip::CLIENTFOFI) k = (j+(i+1))%ctr;
for (int l = 1; l<=nChans; l++) { // chans are 1-based
- qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l)
- <<"with " << IPS[k]+":send_"+QString::number(l);
+// qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l)
+// <<"with " << IPS[k]+":send_"+QString::number(l);
QString left = IPS[i] +
":receive_" + QString::number(l);
for (int j = 0; j<jLimit; j++) {
k = (j+(i+1))%ctr;
for (int l = 1; l<=nChans; l++) { // chans are 1-based
- qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l)
- <<"with " << IPS[k]+":send_"+QString::number(l);
+// qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l)
+// <<"with " << IPS[k]+":send_"+QString::number(l);
QString left = IPS[i] +
":receive_" + QString::number(l);
// this is brute force, does not look at individual clients, just patches the whole ensemble
// each time
void JMess::connectTUB(int /*nChans*/)
-// called from UdpMasterListener::connectPatch
+// called from UdpHubListener::connectPatch
{
for (int i = 0; i<=gMAX_TUB-gMIN_TUB; i++) // last IP decimal octet
for (int l = 1; l<=1; l++) // mono for now // chans are 1-based, 1...2
//#include <QXmlSimpleReader>
//#include <QXmlInputSource>
//#include <QXmlContentHandler>
+#include <QMutexLocker>
#include <jack/jack.h>
//OuputPortN InputPortN
QVector<QVector<QString> > mConnectedPorts;
QVector<QVector<QString> > mPortsToConnect;
+ static QMutex sJMessMutex; ///< Mutex to make thread safe jack functions that are not
};
#endif
#include <QHostInfo>
#include <QThread>
#include <QTcpSocket>
+#include <QTimer>
+#include <QDateTime>
using std::cout; using std::endl;
mReceiverPeerPort(receiver_peer_port),
mTcpServerPort(4464),
mRedundancy(redundancy),
- mJackClientName("JackTrip"),
+ mJackClientName(gJackDefaultClientName),
mConnectionMode(JackTrip::NORMAL),
mReceivedConnection(false),
mTcpConnectionError(false),
mStopped(false),
- mConnectDefaultAudioPorts(true)
+ mConnectDefaultAudioPorts(true),
+ mIOStatLogStream(std::cout.rdbuf())
{
createHeader(mPacketHeaderType);
}
//*******************************************************************************
void JackTrip::setupAudio(
- #ifdef WAIRTOMASTER // WAIR
+ #ifdef WAIRTOHUB // WAIR
int ID
#endif // endwhere
)
#endif // endwhere
mAudioBitResolution);
-#ifdef WAIRTOMASTER // WAIR
- qDebug() << "mPeerAddress" << mPeerAddress << mPeerAddress.contains(gDOMAIN_TRIPLE);
- QString VARIABLE_AUDIO_NAME = WAIR_AUDIO_NAME; // legacy for WAIR
- QByteArray tmp = QString(mPeerAddress).replace(":", ".").toLatin1();
- if(mPeerAddress.toStdString()!="")
- mJackClientName = tmp.constData();
- std::cout << "WAIR ID " << ID << " jacktrip client name set to=" <<
- mJackClientName << std::endl;
-
+#ifdef WAIRTOHUB // WAIR
+ //Set our Jack client name if we're a hub server or a custom name hasn't been set
+ if ( mPeerAddress.toStdString() != "" &&
+ (mJackClientName == gJackDefaultClientName || mJackTripMode == SERVERPINGSERVER)) {
+ mJackClientName = QString(mPeerAddress).replace(":", ".").toLatin1().constData();
+ }
+// std::cout << "WAIR ID " << ID << " jacktrip client name set to=" <<
+// mJackClientName << std::endl;
#endif // endwhere
mAudioInterface->setClientName(mJackClientName);
//*******************************************************************************
void JackTrip::startProcess(
- #ifdef WAIRTOMASTER // WAIR
+ #ifdef WAIRTOHUB // WAIR
int ID
#endif // endwhere
- ) throw(std::invalid_argument)
+ )
{ //signal that catches ctrl c in rtaudio-asio mode
#if defined (__WIN_32__)
if (signal(SIGINT, sigint_handler) == SIG_ERR) {
if (gVerboseFlag) std::cout << " JackTrip:startProcess before checkIfPortIsBinded(mReceiverBindPort)" << std::endl;
#if defined __WIN_32__
- //cc fixed windows crash with this print statement!
- qDebug() << "before mJackTrip->startProcess"
- << mReceiverBindPort<< mSenderBindPort;
- // msleep(2000);
+ //cc fixed windows crash with this print statement! hope to delete
+// qDebug() << "before mJackTrip->startProcess" << mReceiverBindPort<< mSenderBindPort;
#endif
checkIfPortIsBinded(mReceiverBindPort);
if (gVerboseFlag) std::cout << " JackTrip:startProcess before checkIfPortIsBinded(mSenderBindPort)" << std::endl;
// ------------------------------
if (gVerboseFlag) std::cout << " JackTrip:startProcess before setupAudio" << std::endl;
setupAudio(
- #ifdef WAIRTOMASTER // wair
+ #ifdef WAIRTOHUB // wair
ID
#endif // endwhere
);
if (mConnectDefaultAudioPorts) { mAudioInterface->connectDefaultPorts(); }
}
+//*******************************************************************************
+void JackTrip::startIOStatTimer(int timeout_sec, const std::ostream& log_stream)
+{
+ mIOStatLogStream.rdbuf(log_stream.rdbuf());
+ QTimer *timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), this, SLOT(onStatTimer()));
+ timer->start(timeout_sec*1000);
+}
+
+//*******************************************************************************
+void JackTrip::onStatTimer()
+{
+ DataProtocol::PktStat pkt_stat;
+ if (!mDataProtocolReceiver->getStats(&pkt_stat)) {
+ return;
+ }
+ bool reset = (0 == pkt_stat.statCount);
+ RingBuffer::IOStat recv_io_stat;
+ if (!mReceiveRingBuffer->getStats(&recv_io_stat, reset)) {
+ return;
+ }
+ RingBuffer::IOStat send_io_stat;
+ if (!mSendRingBuffer->getStats(&send_io_stat, reset)) {
+ return;
+ }
+ QString now = QDateTime::currentDateTime().toString(Qt::ISODate);
+ int32_t skew = recv_io_stat.underruns - recv_io_stat.overflows
+ - pkt_stat.lost + pkt_stat.revived;
+
+ static QMutex mutex;
+ QMutexLocker locker(&mutex);
+ mIOStatLogStream << now.toLocal8Bit().constData()
+ << " " << getPeerAddress().toLocal8Bit().constData()
+ << " send: "
+ << send_io_stat.underruns
+ << "/" << send_io_stat.overflows
+ << " recv: "
+ << recv_io_stat.underruns
+ << "/" << recv_io_stat.overflows
+ << " prot: "
+ << pkt_stat.lost
+ << "/" << pkt_stat.outOfOrder
+ << "/" << pkt_stat.revived
+ << " tot: "
+ << pkt_stat.tot
+ << " skew: " << skew
+ << endl;
+}
//*******************************************************************************
void JackTrip::stop()
//*******************************************************************************
-void JackTrip::clientStart() throw(std::invalid_argument)
+void JackTrip::clientStart()
{
// For the Client mode, the peer (or server) address has to be specified by the user
if ( mPeerAddress.isEmpty() ) {
//*******************************************************************************
int JackTrip::serverStart(bool timeout, int udpTimeout) // udpTimeout unused
-throw(std::invalid_argument, std::runtime_error)
{
// Set the peer address
if ( !mPeerAddress.isEmpty() ) {
//*******************************************************************************
-int JackTrip::clientPingToServerStart() throw(std::invalid_argument)
+int JackTrip::clientPingToServerStart()
{
//mConnectionMode = JackTrip::KSTRONG;
//mConnectionMode = JackTrip::JAMTEST;
/// \brief Start the processing threads
virtual void startProcess(
- #ifdef WAIRTOMASTER // wair
+ #ifdef WAIRTOHUB // wair
int ID
#endif // endwhere
- ) throw(std::invalid_argument);
+ );
/// \brief Stop the processing threads
virtual void stop();
void printTextTest() {std::cout << "=== JackTrip PRINT ===" << std::endl;}
void printTextTest2() {std::cout << "=== JackTrip PRINT2 ===" << std::endl;}
+ void startIOStatTimer(int timeout_sec, const std::ostream& log_stream);
+
public slots:
/// \brief Slot to stop all the processes and threads
virtual void slotStopProcesses()
{ std::cout << "=== TESTING ===" << std::endl; }
void slotReceivedConnectionFromPeer()
{ mReceivedConnection = true; }
+ void onStatTimer();
signals:
/// \brief Set the AudioInteface object
virtual void setupAudio(
- #ifdef WAIRTOMASTER // WAIR
+ #ifdef WAIRTOHUB // WAIR
int ID
#endif // endwhere
);
/// \brief Set the RingBuffer objects
void setupRingBuffers();
/// \brief Starts for the CLIENT mode
- void clientStart() throw(std::invalid_argument);
+ void clientStart();
/// \brief Starts for the SERVER mode
/// \param timout Set the server to timeout after 2 seconds if no client connections are received.
/// Usefull for the multithreaded server
/// \return 0 on success, -1 on error
- int serverStart(bool timeout = false, int udpTimeout = gTimeOutMultiThreadedServer)
- throw(std::invalid_argument, std::runtime_error);
+ int serverStart(bool timeout = false, int udpTimeout = gTimeOutMultiThreadedServer);
/// \brief Stats for the Client to Ping Server
/// \return -1 on error, 0 on success
- virtual int clientPingToServerStart() throw(std::invalid_argument);
+ virtual int clientPingToServerStart();
private:
//void bindReceiveSocket(QUdpSocket& UdpSocket, int bind_port,
volatile bool mStopped;
bool mConnectDefaultAudioPorts; ///< Connect or not default audio ports
+ std::ostream mIOStatLogStream;
};
#endif
#include "JackTripWorker.h"
#include "JackTrip.h"
-#include "UdpMasterListener.h"
+#include "UdpHubListener.h"
#include "NetKS.h"
#include "LoopBack.h"
+#include "Settings.h"
#ifdef WAIR // wair
#include "dcblock2gain.dsp.h"
#endif // endwhere
using std::cout; using std::endl;
//*******************************************************************************
-JackTripWorker::JackTripWorker(UdpMasterListener* udpmasterlistener, int BufferQueueLength, JackTrip::underrunModeT UnderRunMode) :
- mUdpMasterListener(udpmasterlistener),
+JackTripWorker::JackTripWorker(UdpHubListener* udpmasterlistener, int BufferQueueLength, JackTrip::underrunModeT UnderRunMode) :
+ mUdpHubListener(udpmasterlistener),
m_connectDefaultAudioPorts(false),
mBufferQueueLength(BufferQueueLength),
mUnderRunMode(UnderRunMode),
//*******************************************************************************
JackTripWorker::~JackTripWorker()
{
- //delete mUdpMasterListener;
+ //delete mUdpHubListener;
}
// Create and setup JackTrip Object
//JackTrip jacktrip(JackTrip::SERVER, JackTrip::UDP, mNumChans, 2);
if (gVerboseFlag) cout << "---> JackTripWorker: Creating jacktrip objects..." << endl;
+ Settings* settings = mUdpHubListener->getSettings();
#ifdef WAIR // WAIR
// forces BufferQueueLength to 2
// need to parse numNetChans from incoming header
// but force to 16 for now
#define FORCEBUFFERQ 2
- if (mUdpMasterListener->isWAIR()) { // invoked with -Sw
+ if (mUdpHubListener->isWAIR()) { // invoked with -Sw
mWAIR = true;
mNumNetRevChans = NUMNETREVCHANSbecauseNOTINRECEIVEDheader;
} else {};
if (gVerboseFlag) cout << "---> JackTripWorker: setJackTripFromClientHeader..." << endl;
int PeerConnectionMode = setJackTripFromClientHeader(jacktrip);
if ( PeerConnectionMode == -1 ) {
- mUdpMasterListener->releaseThread(mID);
+ mUdpHubListener->releaseThread(mID);
{ QMutexLocker locker(&mMutex); mSpawning = false; }
return;
}
// Start Threads and event loop
if (gVerboseFlag) cout << "---> JackTripWorker: startProcess..." << endl;
jacktrip.startProcess(
- #ifdef WAIRTOMASTER // wair
+ #ifdef WAIRTOHUB // wair
mID
#endif // endwhere
);
+ if (0 != settings->getIOStatTimeout()) {
+ jacktrip.startIOStatTimer(settings->getIOStatTimeout(), settings->getIOStatStream());
+ }
// if (gVerboseFlag) cout << "---> JackTripWorker: start..." << endl;
// jacktrip.start(); // ########### JamTest Only #################
std::cerr << "Couldn't send thread to the Pool" << endl;
std::cerr << e.what() << endl;
std::cerr << gPrintSeparator << endl;
- mUdpMasterListener->releaseThread(mID);
+ mUdpHubListener->releaseThread(mID);
{ QMutexLocker locker(&mMutex); mSpawning = false; }
return;
}
{
QMutexLocker locker(&mMutex);
- mUdpMasterListener->releaseThread(mID);
+ mUdpHubListener->releaseThread(mID);
}
cout << "JackTrip ID = " << mID << " released from the THREAD POOL" << endl;
#include "jacktrip_globals.h"
//class JackTrip; // forward declaration
-class UdpMasterListener; // forward declaration
+class UdpHubListener; // forward declaration
/** \brief Prototype of the worker class that will be cloned through sending threads to the
public:
/// \brief The class constructor
- JackTripWorker(UdpMasterListener* udpmasterlistener, int BufferQueueLength = gDefaultQueueLength, JackTrip::underrunModeT UnderRunMode = JackTrip::WAVETABLE);
+ JackTripWorker(UdpHubListener* udpmasterlistener, int BufferQueueLength = gDefaultQueueLength, JackTrip::underrunModeT UnderRunMode = JackTrip::WAVETABLE);
/// \brief The class destructor
virtual ~JackTripWorker();
int setJackTripFromClientHeader(JackTrip& jacktrip);
JackTrip::connectionModeT getConnectionModeFromHeader();
- UdpMasterListener* mUdpMasterListener; ///< Master Listener Socket
+ UdpHubListener* mUdpHubListener; ///< Hub Listener Socket
//QHostAddress mClientAddress; ///< Client Address
QString mClientAddress;
uint16_t mServerPort; ///< Server Ephemeral Incomming Port to use with Client
mWritePosition = ( (NumSlots/2) * SlotSize ) % mTotalSize;
// Udpate Full Slots accordingly
mFullSlots = (NumSlots/2);
+ mUnderruns = 0;
+ mOverflows = 0;
}
//mFullSlots += mNumSlots/2;
// There's nothing new to read, so we clear the whole buffer (Set the entire buffer to 0)
std::memset(mRingBuffer, 0, mTotalSize);
+ ++mUnderruns;
}
//mReadPosition = ( mWritePosition + ( (mNumSlots/2) * mSlotSize ) ) % mTotalSize;
mReadPosition = ( mReadPosition + ( (mNumSlots/2) * mSlotSize ) ) % mTotalSize;
mFullSlots -= mNumSlots/2;
+ mOverflows += mNumSlots/2 + 1;
}
cout << "mWritePosition = " << mWritePosition << endl;
cout << "mFullSlots = " << mFullSlots << endl;
}
+
+//*******************************************************************************
+bool RingBuffer::getStats(RingBuffer::IOStat* stat, bool reset)
+{
+ if (reset) {
+ mUnderruns = 0;
+ mOverflows = 0;
+ }
+ stat->underruns = mUnderruns;
+ stat->overflows = mOverflows;
+ return true;
+}
#include "jacktrip_types.h"
+#include <atomic>
+
//using namespace JackTripNamespace;
*/
void readSlotNonBlocking(int8_t* ptrToReadSlot);
+ struct IOStat {
+ uint32_t underruns;
+ uint32_t overflows;
+ };
+ virtual bool getStats(IOStat* stat, bool reset);
protected:
QMutex mMutex; ///< Mutex to protect read and write operations
QWaitCondition mBufferIsNotFull; ///< Buffer not full condition to monitor threads
QWaitCondition mBufferIsNotEmpty; ///< Buffer not empty condition to monitor threads
+ std::atomic<uint32_t> mUnderruns;
+ std::atomic<uint32_t> mOverflows;
};
#endif
#include "Stk16.dsp.h"
#endif // endwhere
-#include "UdpMasterListener.h"
+#include "UdpHubListener.h"
#include "JackTripWorker.h"
#include "jacktrip_globals.h"
mChanfeDefaultID(0),
mChanfeDefaultBS(false),
mHubConnectionMode(JackTrip::SERVERTOCLIENT),
- mConnectDefaultAudioPorts(true)
+ mConnectDefaultAudioPorts(true),
+ mIOStatTimeout(0)
{}
//*******************************************************************************
{ "version", no_argument, NULL, 'v' }, // Version Number
{ "verbose", no_argument, NULL, 'V' }, // Verbose mode
{ "hubpatch", required_argument, NULL, 'p' }, // Set hubConnectionMode for auto patch in Jack
+ { "iostat", required_argument, NULL, 'I' }, // Set IO stat timeout
+ { "iostatlog", required_argument, NULL, 'G' }, // Set IO stat log file
{ "help", no_argument, NULL, 'h' }, // Print Help
{ NULL, 0, NULL, 0 }
};
case 'v':
//-------------------------------------------------------
cout << "JackTrip VERSION: " << gVersion << endl;
- cout << "Copyright (c) 2008-2018 Juan-Pablo Caceres, Chris Chafe." << endl;
+ cout << "Copyright (c) 2008-2020 Juan-Pablo Caceres, Chris Chafe." << endl;
cout << "SoundWIRE group at CCRMA, Stanford University" << endl;
cout << "" << endl;
std::exit(0);
printUsage();
std::exit(1); }
break;
+ case 'I': // IO Stat timeout
+ //-------------------------------------------------------
+ mIOStatTimeout = atoi(optarg);
+ if (0 > mIOStatTimeout) {
+ std::cerr << "--iostat ERROR: negative timeout." << endl;
+ printUsage();
+ std::exit(1);
+ }
+ break;
+ case 'G': // IO Stat log file
+ //-------------------------------------------------------
+ mIOStatStream.open(optarg);
+ if (!mIOStatStream.is_open()) {
+ std::cerr << "--iostatlog FAILED to open " << optarg
+ << " for writing." << endl;
+ printUsage();
+ std::exit(1);
+ }
+ break;
case 'h':
//-------------------------------------------------------
printUsage();
cout << "" << endl;
cout << "JackTrip: A System for High-Quality Audio Network Performance" << endl;
cout << "over the Internet" << endl;
- cout << "Copyright (c) 2008-2018 Juan-Pablo Caceres, Chris Chafe." << endl;
+ cout << "Copyright (c) 2008-2020 Juan-Pablo Caceres, Chris Chafe." << endl;
cout << "SoundWIRE group at CCRMA, Stanford University" << endl;
cout << "VERSION: " << gVersion << endl;
cout << "" << endl;
cout << " -j, --jamlink Run in JamLink Mode (Connect to a JamLink Box)" << endl;
cout << " --clientname Change default client name (default: JackTrip)" << endl;
cout << " --localaddress Change default local host IP address (default: 127.0.0.1)" << endl;
- cout << " --nojackportsconnect Don't connect default audio ports in jack" << endl;
+ cout << " --nojackportsconnect Don't connect default audio ports in jack, including not doing hub auto audio patch in HUB SERVER mode." << endl;
cout << endl;
cout << "ARGUMENTS TO USE JACKTRIP WITHOUT JACK:" << endl;
cout << " --rtaudio Use system's default sound system instead of Jack" << endl;
cout << " --bufsize # Set the buffer size, works on --rtaudio mode only (default: 128)" << endl;
cout << " --deviceid # The rtaudio device id --rtaudio mode only (default: 0)" << endl;
cout << endl;
+ cout << "ARGUMENTS TO DISPLAY IO STATISTICS:" << endl;
+ cout << " --iostat <time_in_secs> Turn on IO stat reporting with specified interval (in seconds)" << endl;
+ cout << " --iostatlog <log_file> Save stat log into a file (default: print in stdout)" << endl;
+ cout << endl;
cout << "HELP ARGUMENTS: " << endl;
cout << " -v, --version Prints Version Number" << endl;
cout << " -V, --verbose Verbose mode, prints debug messages" << endl;
/// \todo Change this, just here to test
if ( mJackTripServer ) {
- UdpMasterListener* udpmaster = new UdpMasterListener;
+ UdpHubListener* udpmaster = new UdpHubListener;
+ udpmaster->setSettings(this);
#ifdef WAIR // WAIR
udpmaster->setWAIR(mWAIR);
#endif // endwhere
// Start JackTrip
if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->startProcess" << std::endl;
mJackTrip->startProcess(
- #ifdef WAIRTOMASTER // WAIR
+ #ifdef WAIRTOHUB // WAIR
0 // for WAIR compatibility, ID in jack client name
#endif // endwhere
);
+ if (0 < getIOStatTimeout()) {
+ mJackTrip->startIOStatTimer(getIOStatTimeout(), getIOStatStream());
+ }
// if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->start" << std::endl;
// this is a noop
// mJackTrip->start();
#define __SETTINGS_H__
#include <cstdlib>
+#include <fstream>
#include "DataProtocol.h"
void printUsage();
bool getLoopBack() { return mLoopBack; }
+ int getIOStatTimeout() const {return mIOStatTimeout;}
+ const std::ostream& getIOStatStream() const
+ {
+ return mIOStatStream.is_open() ? (std::ostream&)mIOStatStream : std::cout;
+ }
public slots:
unsigned int mAudioBufferSize;
unsigned int mHubConnectionMode;
bool mConnectDefaultAudioPorts; ///< Connect or not jack audio ports
+ int mIOStatTimeout;
+ std::ofstream mIOStatStream;
};
#endif
#include <winsock2.h> //cc need SD_SEND
#include <ws2tcpip.h> // for IPv6
#endif
-#if defined (__LINUX__) || (__MAC__OSX__)
+#if defined (__LINUX__) || (__MAC_OSX__)
#include <sys/socket.h> // for POSIX Sockets
#endif
//*******************************************************************************
-void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP) throw(std::invalid_argument)
+void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP)
{
// Get DNS Address
-#if defined (__LINUX__) || (__MAC__OSX__)
+#if defined (__LINUX__) || (__MAC_OSX__)
//Don't make the following code conditional on windows
//(Addresses a weird timing bug when in hub client mode)
if (!mPeerAddress.setAddress(peerHostOrIP)) {
}
//cout << "UdpDataProtocol::setPeerAddress IP Address Number: "
// << mPeerAddress.toString().toStdString() << endl;
-#if defined (__LINUX__) || (__MAC__OSX__)
+#if defined (__LINUX__) || (__MAC_OSX__)
}
#endif
}
#if defined (__WIN_32__)
-void UdpDataProtocol::setSocket(SOCKET &socket) throw(std::runtime_error)
+void UdpDataProtocol::setSocket(SOCKET &socket)
#else
-void UdpDataProtocol::setSocket(int &socket) throw(std::runtime_error)
+void UdpDataProtocol::setSocket(int &socket)
#endif
{
//If we haven't been passed a valid socket, then we should bind one.
//*******************************************************************************
#if defined (__WIN_32__)
-SOCKET UdpDataProtocol::bindSocket() throw(std::runtime_error)
+SOCKET UdpDataProtocol::bindSocket()
#else
-int UdpDataProtocol::bindSocket() throw(std::runtime_error)
+int UdpDataProtocol::bindSocket()
#endif
{
QMutexLocker locker(&sUdpMutex);
WSADATA wsaData;
int err;
- wVersionRequested = MAKEWORD( 1, 1 );
+ wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
// Confirm that the Windows Sockets DLL supports 1.1. or higher
- if ( LOBYTE( wsaData.wVersion ) != 1 ||
- HIBYTE( wsaData.wVersion ) != 1 ) {
+ if ( LOBYTE( wsaData.wVersion ) != 2 ||
+ HIBYTE( wsaData.wVersion ) != 2 ) {
// Tell the user that we couldn't find a useable
// winsock.dll.
WSACleanup( );
uint16_t current_seq_num = 0; // Store current sequence number
uint16_t last_seq_num = 0; // Store last package sequence number
uint16_t newer_seq_num = 0; // Store newer sequence number
+ mTotCount = 0;
+ mLostCount = 0;
+ mOutOfOrderCount = 0;
+ mRevivedCount = 0;
+ mStatCount = 0;
if (gVerboseFlag) std::cout << "step 8" << std::endl;
while ( !mStopped )
break; }
case SENDER : {
- //Make sure we don't start sending packets too soon.
- QThread::msleep(100);
- //-----------------------------------------------------------------------------------
while ( !mStopped )
{
// OLD CODE WITHOUT REDUNDANCY -----------------------------------------------------
{
int wait_time = 30; // msec
if ( !(wait_msec%wait_time) ) {
- std::cerr << "UDP waiting too long (more than " << wait_time << "ms)..." << endl;
+ std::cerr << "UDP waiting too long (more than " << wait_time << "ms) for " << mPeerAddress.toString().toStdString() << "..." << endl;
}
}
mJackTrip->getPeerSequenceNumber(full_redundant_packet);
current_seq_num = newer_seq_num;
+ if (0 != last_seq_num) {
+ int16_t lost = newer_seq_num - last_seq_num - 1;
+ if (0 > lost) {
+ // Out of order packet, should be ignored
+ ++mOutOfOrderCount;
+ return;
+ }
+ else if (0 != lost) {
+ mLostCount += lost;
+ }
+ mTotCount += 1 + lost;
+ }
//cout << current_seq_num << " ";
int redun_last_index = 0;
mJackTrip->getPeerSequenceNumber( full_redundant_packet + (i*full_packet_size) );
//cout << current_seq_num << " ";
}
+ mRevivedCount += redun_last_index;
//cout << endl;
last_seq_num = newer_seq_num; // Save last read packet
}
}
+//*******************************************************************************
+bool UdpDataProtocol::getStats(DataProtocol::PktStat* stat)
+{
+ if (0 == mStatCount) {
+ mLostCount = 0;
+ mOutOfOrderCount = 0;
+ mRevivedCount = 0;
+ }
+ stat->tot = mTotCount;
+ stat->lost = mLostCount;
+ stat->outOfOrder = mOutOfOrderCount;
+ stat->revived = mRevivedCount;
+ stat->statCount = mStatCount++;
+ return true;
+}
+
//*******************************************************************************
void UdpDataProtocol::sendPacketRedundancy(int8_t* full_redundant_packet,
int full_redundant_packet_size,
/** \brief Set the Peer address to connect to
* \param peerHostOrIP IPv4 number or host name
*/
- void setPeerAddress(const char* peerHostOrIP) throw(std::invalid_argument);
+ void setPeerAddress(const char* peerHostOrIP);
#if defined (__WIN_32__)
- void setSocket(SOCKET &socket) throw(std::runtime_error);
+ void setSocket(SOCKET &socket);
#else
- void setSocket(int &socket) throw(std::runtime_error);
+ void setSocket(int &socket);
#endif
/** \brief Receives a packet. It blocks until a packet is received
*/
virtual void run();
+ virtual bool getStats(PktStat* stat);
private slots:
void printUdpWaitedTooLong(int wait_msec);
/** \brief Binds the UDP socket to the available address and specified port
*/
#if defined (__WIN_32__)
- SOCKET bindSocket() throw(std::runtime_error);
+ SOCKET bindSocket();
#else
- int bindSocket() throw(std::runtime_error);
+ int bindSocket();
#endif
/** \brief This function blocks until data is available for reading in the
unsigned int mUdpRedundancyFactor; ///< Factor of redundancy
static QMutex sUdpMutex; ///< Mutex to make thread safe the binding process
+
+ std::atomic<uint32_t> mTotCount;
+ std::atomic<uint32_t> mLostCount;
+ std::atomic<uint32_t> mOutOfOrderCount;
+ std::atomic<uint32_t> mRevivedCount;
+ uint32_t mStatCount;
};
#endif // __UDPDATAPROTOCOL_H__
--- /dev/null
+//*****************************************************************
+/*
+ JackTrip: A System for High-Quality Audio Network Performance
+ over the Internet
+
+ Copyright (c) 2008 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 UdpHubListener.cpp
+ * \author Juan-Pablo Caceres and Chris Chafe
+ * \date September 2008
+ */
+
+#include <iostream>
+#include <cstdlib>
+#include <stdexcept>
+#include <cstring>
+
+#include <QTcpServer>
+#include <QTcpSocket>
+#include <QStringList>
+#include <QMutexLocker>
+
+#include "UdpHubListener.h"
+#include "JackTripWorker.h"
+#include "jacktrip_globals.h"
+
+using std::cout; using std::endl;
+
+
+//*******************************************************************************
+UdpHubListener::UdpHubListener(int server_port) :
+ //mJTWorker(NULL),
+ mServerPort(server_port),
+ mStopped(false),
+ #ifdef WAIR // wair
+ mWAIR(false),
+ #endif // endwhere
+ mTotalRunningThreads(0),
+ m_connectDefaultAudioPorts(false)
+{
+ // Register JackTripWorker with the master listener
+ //mJTWorker = new JackTripWorker(this);
+ mJTWorkers = new QVector<JackTripWorker*>;
+ for (int i = 0; i<gMaxThreads; i++) {
+ mJTWorkers->insert(i, NULL);
+ }
+
+ qDebug() << "mThreadPool default maxThreadCount =" << mThreadPool.maxThreadCount();
+ mThreadPool.setMaxThreadCount(mThreadPool.maxThreadCount() * 16);
+ qDebug() << "mThreadPool maxThreadCount set to" << mThreadPool.maxThreadCount();
+
+ //mJTWorkers = new JackTripWorker(this);
+ mThreadPool.setExpiryTimeout(3000); // msec (-1) = forever
+ // Inizialize IP addresses
+ for (int i = 0; i<gMaxThreads; i++) {
+ mActiveAddress[i].address = ""; // Address strings
+ mActiveAddress[i].port = 0;
+ }
+ // Set the base dynamic port
+ // The Dynamic and/or Private Ports are those from 49152 through 65535
+ // mBasePort = ( rand() % ( (65535 - gMaxThreads) - 49152 ) ) + 49152;
+
+ // SoundWIRE ports open are UDP 61000-62000
+ mBasePort = 61000;
+
+ mUnderRunMode = JackTrip::WAVETABLE;
+ mBufferQueueLength = gDefaultQueueLength;
+}
+
+
+//*******************************************************************************
+UdpHubListener::~UdpHubListener()
+{
+ QMutexLocker lock(&mMutex);
+ mThreadPool.waitForDone();
+ //delete mJTWorker;
+ for (int i = 0; i<gMaxThreads; i++) {
+ delete mJTWorkers->at(i);
+ }
+ delete mJTWorkers;
+}
+
+
+//*******************************************************************************
+// Now that the first handshake is with TCP server, if the addreess/peer port of
+// the client is already on the thread pool, it means that a new connection is
+// requested (the old was desconnected). So we have to remove that thread from
+// the pool and then connect again.
+void UdpHubListener::run()
+{
+ mStopped = false;
+
+ QHostAddress PeerAddress; // Object to store peer address
+ int peer_udp_port; // Peer listening port
+ int server_udp_port; // Server assigned udp port
+
+ // Create and bind the TCP server
+ // ------------------------------
+ QTcpServer TcpServer;
+ if ( !TcpServer.listen(QHostAddress::Any, mServerPort) ) {
+ std::cerr << "TCP Socket Server ERROR: " << TcpServer.errorString().toStdString() << endl;
+ std::exit(1);
+ }
+
+ const int tcpTimeout = 5*1000;
+
+
+ cout << "JackTrip HUB SERVER: TCP Server Listening in Port = " << TcpServer.serverPort() << endl;
+ while ( !mStopped )
+ {
+ cout << "JackTrip HUB SERVER: Waiting for client connections..." << endl;
+ if(m_connectDefaultAudioPorts)
+ {
+ cout << "JackTrip HUB SERVER: Hub auto audio patch setting = " << mHubPatch << endl;
+ } else {
+ cout << "JackTrip HUB SERVER: Hub auto audio patch disabled " << endl;
+ }
+ cout << "=======================================================" << endl;
+ while ( !TcpServer.hasPendingConnections() && !TcpServer.waitForNewConnection(1000) )
+ { if (mStopped) { return; } } // block until a new connection is received
+ cout << "JackTrip HUB SERVER: Client Connection Received!" << endl;
+
+ // Control loop to be able to exit if UDPs or TCPs error ocurr
+ for (int dum = 0; dum<1; dum++) {
+ QTcpSocket *clientConnection = TcpServer.nextPendingConnection();
+ if ( !clientConnection->waitForConnected(tcpTimeout) ) {
+ std::cerr << clientConnection->errorString().toStdString() << endl;
+ break;
+ }
+ PeerAddress = clientConnection->peerAddress();
+ cout << "JackTrip HUB SERVER: Client Connect Received from Address : "
+ << PeerAddress.toString().toStdString() << endl;
+
+ // Get UDP port from client
+ // ------------------------
+ peer_udp_port = readClientUdpPort(clientConnection);
+ if ( peer_udp_port == 0 ) { break; }
+ cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl;
+
+ // Check is client is new or not
+ // -----------------------------
+ // Check if Address is not already in the thread pool
+ // check by comparing address strings (To handle IPv4 and IPv6.)
+ int id = isNewAddress(PeerAddress.toString(), peer_udp_port);
+ // If the address is not new, we need to remove the client from the pool
+ // before re-starting the connection
+
+ if (id == -1) {
+ int id_remove;
+ id_remove = getPoolID(PeerAddress.toString(), peer_udp_port);
+ // stop the thread
+ mJTWorkers->at(id_remove)->stopThread();
+ // block until the thread has been removed from the pool
+ while ( isNewAddress(PeerAddress.toString(), peer_udp_port) == -1 ) {
+ cout << "JackTrip HUB SERVER: Removing JackTripWorker from pool..." << endl;
+ QThread::msleep(10);
+ }
+ // Get a new ID for this client
+ //id = isNewAddress(PeerAddress.toIPv4Address(), peer_udp_port);
+ id = getPoolID(PeerAddress.toString(), peer_udp_port);
+ }
+ // Assign server port and send it to Client
+ server_udp_port = mBasePort+id;
+ if ( sendUdpPort(clientConnection, server_udp_port) == 0 ) {
+ clientConnection->close();
+ delete clientConnection;
+ releaseThread(id);
+ break;
+ }
+
+ // Close and Delete the socket
+ // ---------------------------
+ clientConnection->close();
+ delete clientConnection;
+ cout << "JackTrip HUB SERVER: Client TCP Connection Closed!" << endl;
+
+ // Spawn Thread to Pool
+ // --------------------
+ // Register JackTripWorker with the master listener
+ delete mJTWorkers->at(id); // just in case the Worker was previously created
+ mJTWorkers->replace(id, new JackTripWorker(this, mBufferQueueLength, mUnderRunMode));
+ // redirect port and spawn listener
+ cout << "JackTrip HUB SERVER: Spawning JackTripWorker..." << endl;
+ {
+ QMutexLocker lock(&mMutex);
+ mJTWorkers->at(id)->setJackTrip(id,
+ mActiveAddress[id].address,
+ server_udp_port,
+ mActiveAddress[id].port,
+ 1,
+ m_connectDefaultAudioPorts
+ ); /// \todo temp default to 1 channel
+
+// qDebug() << "mPeerAddress" << id << mActiveAddress[id].address << mActiveAddress[id].port;
+ }
+ //send one thread to the pool
+ cout << "JackTrip HUB SERVER: Starting JackTripWorker..." << endl;
+ mThreadPool.start(mJTWorkers->at(id), QThread::TimeCriticalPriority);
+ // wait until one is complete before another spawns
+ while (mJTWorkers->at(id)->isSpawning()) { QThread::msleep(10); }
+ //mTotalRunningThreads++;
+ cout << "JackTrip HUB SERVER: Total Running Threads: " << mTotalRunningThreads << endl;
+ cout << "===============================================================" << endl;
+ QThread::msleep(100);
+#ifdef WAIR // WAIR
+ if (isWAIR()) connectMesh(true); // invoked with -Sw
+#endif // endwhere
+
+// qDebug() << "mPeerAddress" << mActiveAddress[id].address << mActiveAddress[id].port;
+
+ connectPatch(true);
+ }
+ }
+
+ /*
+ // Create objects on the stack
+ QUdpSocket HubUdpSocket;
+ QHostAddress PeerAddress;
+ uint16_t peer_port; // Ougoing Peer port, in case they're not using the default
+
+ // Bind the socket to the well known port
+ bindUdpSocket(HubUdpSocket, mServerPort);
+
+ char buf[1];
+ cout << "Server Listening in UDP Port: " << mServerPort << endl;
+ cout << "Waiting for client..." << endl;
+ cout << "=======================================================" << endl;
+ while ( !mStopped )
+ {
+ //cout << "WAITING........................." << endl;
+ while ( HubUdpSocket.hasPendingDatagrams() )
+ {
+ cout << "Received request from Client!" << endl;
+ // Get Client IP Address and outgoing port from packet
+ int rv = HubUdpSocket.readDatagram(buf, 1, &PeerAddress, &peer_port);
+ cout << "Peer Port in Server ==== " << peer_port << endl;
+ if (rv < 0) { std::cerr << "ERROR: Bad UDP packet read..." << endl; }
+
+ /// \todo Get number of channels in the client from header
+
+ // check by comparing 32-bit addresses
+ /// \todo Add the port number in the comparison
+ cout << "peer_portpeer_portpeer_port === " << peer_port << endl;
+ int id = isNewAddress(PeerAddress.toIPv4Address(), peer_port);
+
+ //cout << "IDIDIDIDIDDID === " << id << endl;
+
+ // If the address is new, create a new thread in the pool
+ if (id >= 0) // old address is -1
+ {
+ // redirect port and spawn listener
+ sendToPoolPrototype(id);
+ // wait until one is complete before another spawns
+ while (mJTWorker->isSpawning()) { QThread::msleep(10); }
+ mTotalRunningThreads++;
+ cout << "Total Running Threads: " << mTotalRunningThreads << endl;
+ cout << "=======================================================" << endl;
+ }
+ //cout << "ENDDDDDDDDDDDDDDDDDd === " << id << endl;
+ }
+ QThread::msleep(100);
+ }
+ */
+}
+
+
+//*******************************************************************************
+// Returns 0 on error
+int UdpHubListener::readClientUdpPort(QTcpSocket* clientConnection)
+{
+ // Read the size of the package
+ // ----------------------------
+ //tcpClient.waitForReadyRead();
+ cout << "JackTrip HUB SERVER: Reading UDP port from Client..." << endl;
+ while (clientConnection->bytesAvailable() < (int)sizeof(uint16_t)) {
+ if (!clientConnection->waitForReadyRead()) {
+ std::cerr << "TCP Socket ERROR: " << clientConnection->errorString().toStdString() << endl;
+ return 0;
+ }
+ }
+
+ if (gVerboseFlag) cout << "Ready To Read From Client!" << endl;
+ // Read UDP Port Number from Server
+ // --------------------------------
+ int udp_port;
+ int size = sizeof(udp_port);
+ char port_buf[size];
+ clientConnection->read(port_buf, size);
+ std::memcpy(&udp_port, port_buf, size);
+ return udp_port;
+}
+
+
+//*******************************************************************************
+int UdpHubListener::sendUdpPort(QTcpSocket* clientConnection, int udp_port)
+{
+ // Send Port Number to Client
+ // --------------------------
+ char port_buf[sizeof(udp_port)];
+ std::memcpy(port_buf, &udp_port, sizeof(udp_port));
+ clientConnection->write(port_buf, sizeof(udp_port));
+ while ( clientConnection->bytesToWrite() > 0 ) {
+ if ( clientConnection->state() == QAbstractSocket::ConnectedState ) {
+ clientConnection->waitForBytesWritten(-1);
+ }
+ else {
+ return 0;
+ }
+ }
+ return 1;
+ cout << "Port sent to Client" << endl;
+}
+
+
+//*******************************************************************************
+/*
+void UdpHubListener::sendToPoolPrototype(int id)
+{
+ mJTWorker->setJackTrip(id, mActiveAddress[id][0],
+ mBasePort+(2*id), mActiveAddress[id][1],
+ 1); /// \todo temp default to 1 channel
+ mThreadPool.start(mJTWorker, QThread::TimeCriticalPriority); //send one thread to the pool
+}
+*/
+
+
+//*******************************************************************************
+void UdpHubListener::bindUdpSocket(QUdpSocket& udpsocket, int port)
+{
+ // QHostAddress::Any : let the kernel decide the active address
+ if ( !udpsocket.bind(QHostAddress::Any,
+ port, QUdpSocket::DefaultForPlatform) ) {
+ //std::cerr << "ERROR: could not bind UDP socket" << endl;
+ //std::exit(1);
+ throw std::runtime_error("Could not bind UDP socket. It may be already binded.");
+ }
+ else {
+ cout << "UDP Socket Receiving in Port: " << port << endl;
+ }
+}
+
+
+//*******************************************************************************
+// check by comparing 32-bit addresses
+int UdpHubListener::isNewAddress(QString address, uint16_t port)
+{
+ QMutexLocker lock(&mMutex);
+ bool busyAddress = false;
+ int id = 0;
+
+ /*
+ while ( !busyAddress && (id<mThreadPool.activeThreadCount()) )
+ {
+ if ( address==mActiveAddress[id][0] && port==mActiveAddress[id][1]) { busyAddress = true; }
+ id++;
+ }
+ */
+ for (int i = 0; i<gMaxThreads; i++) {
+ if ( address==mActiveAddress[i].address && port==mActiveAddress[i].port) {
+ id = i;
+ busyAddress = true;
+ }
+ }
+ if ( !busyAddress ) {
+ /*
+ mActiveAddress[id][0] = address;
+ mActiveAddress[id][1] = port;
+ } else {
+ */
+ id = 0;
+ bool foundEmptyAddress = false;
+ while ( !foundEmptyAddress && (id<gMaxThreads) ) {
+ if ( mActiveAddress[id].address.isEmpty() && (mActiveAddress[id].port == 0) ) {
+ foundEmptyAddress = true;
+ mActiveAddress[id].address = address;
+ mActiveAddress[id].port = port;
+ } else {
+ id++;
+ }
+ }
+ }
+ if (!busyAddress) {
+ mTotalRunningThreads++;
+ }
+ return ((busyAddress) ? -1 : id);
+}
+
+
+//*******************************************************************************
+int UdpHubListener::getPoolID(QString address, uint16_t port)
+{
+ QMutexLocker lock(&mMutex);
+ //for (int id = 0; id<mThreadPool.activeThreadCount(); id++ )
+ for (int id = 0; id<gMaxThreads; id++ )
+ {
+ if ( address==mActiveAddress[id].address && port==mActiveAddress[id].port)
+ { return id; }
+ }
+ return -1;
+}
+
+
+//*******************************************************************************
+int UdpHubListener::releaseThread(int id)
+{
+ QMutexLocker lock(&mMutex);
+ mActiveAddress[id].address = "";
+ mActiveAddress[id].port = 0;
+ mTotalRunningThreads--;
+#ifdef WAIR // wair
+ if (isWAIR()) connectMesh(false); // invoked with -Sw
+#endif // endwhere
+ if (getHubPatch()) connectPatch(false); // invoked with -p > 0
+ return 0; /// \todo Check if we really need to return an argument here
+}
+
+#ifdef WAIR // wair
+#include "JMess.h"
+//*******************************************************************************
+void UdpHubListener::connectMesh(bool spawn)
+{
+ cout << ((spawn)?"spawning":"releasing") << " jacktripWorker so change mesh" << endl;
+ JMess tmp;
+ tmp.connectSpawnedPorts(gDefaultNumInChannels); // change gDefaultNumInChannels if more than stereo LAIR interconnects
+ // tmp.disconnectAll();
+ // enumerateRunningThreadIDs();
+}
+
+//*******************************************************************************
+void UdpHubListener::enumerateRunningThreadIDs()
+{
+ for (int id = 0; id<gMaxThreads; id++ )
+ {
+ if ( !mActiveAddress[id].address.isEmpty() )
+ { qDebug() << id; }
+ }
+}
+#endif // endwhere
+
+#include "JMess.h"
+void UdpHubListener::connectPatch(bool spawn)
+{
+ if(m_connectDefaultAudioPorts) {
+ cout << ((spawn)?"spawning":"releasing") << " jacktripWorker so change patch" << endl;
+ } else {
+ cout << ((spawn)?"spawning":"releasing") << " jacktripWorker" << endl;
+ }
+ JMess tmp;
+ // default is patch 0, which connects server audio to all clients
+ // these are the other cases:
+ if (getHubPatch() == JackTrip::RESERVEDMATRIX) // special patch for TU Berlin ensemble
+ tmp.connectTUB(gDefaultNumInChannels);
+ else if ((getHubPatch() == JackTrip::CLIENTECHO) || // client loopback for testing
+ (getHubPatch() == JackTrip::CLIENTFOFI) || // all clients to all clients except self
+ (getHubPatch() == JackTrip::FULLMIX)) // all clients to all clients including self
+ tmp.connectSpawnedPorts(gDefaultNumInChannels,getHubPatch());
+ // FIXME: need change to gDefaultNumInChannels if more than stereo
+}
+
+// TODO:
+// USE bool QAbstractSocket::isValid () const to check if socket is connect. if not, exit loop
--- /dev/null
+//*****************************************************************
+/*
+ JackTrip: A System for High-Quality Audio Network Performance
+ over the Internet
+
+ Copyright (c) 2008 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 UdpHubListener.h
+ * \author Juan-Pablo Caceres and Chris Chafe
+ * \date September 2008
+ */
+
+#ifndef __UDPHUBLISTENER_H__
+#define __UDPHUBLISTENER_H__
+
+#include <iostream>
+#include <stdexcept>
+
+#include <QThread>
+#include <QThreadPool>
+#include <QUdpSocket>
+#include <QHostAddress>
+#include <QTcpSocket>
+#include <QTcpServer>
+#include <QMutex>
+
+#include "JackTrip.h"
+#include "jacktrip_types.h"
+#include "jacktrip_globals.h"
+class JackTripWorker; // forward declaration
+class Settings;
+
+typedef struct {
+ QString address;
+ int16_t port;
+} addressPortPair;
+
+/** \brief Hub UDP listener on the Server.
+ *
+ * This creates a server that will listen on the well know port (the server port) and will
+ * spawn JackTrip threads into the Thread pool. Clients request a connection.
+ */
+class UdpHubListener : public QThread
+{
+ Q_OBJECT;
+
+public:
+ UdpHubListener(int server_port = gServerUdpPort);
+ virtual ~UdpHubListener();
+
+ /// \brief Implements the Thread Loop. To start the thread, call start()
+ /// ( DO NOT CALL run() )
+ void run();
+
+ /// \brief Stops the execution of the Thread
+ void stop() { mStopped = true; }
+
+ int releaseThread(int id);
+
+ void setConnectDefaultAudioPorts(bool connectDefaultAudioPorts) { m_connectDefaultAudioPorts = connectDefaultAudioPorts; }
+
+ void setSettings(Settings* s) {m_settings = s;}
+ Settings* getSettings() const {return m_settings;}
+
+private slots:
+ void testReceive()
+ { std::cout << "========= TEST RECEIVE SLOT ===========" << std::endl; }
+
+signals:
+ void Listening();
+ void ClientAddressSet();
+ void signalRemoveThread(int id);
+
+
+private:
+ /** \brief Binds a QUdpSocket. It chooses the available (active) interface.
+ * \param udpsocket a QUdpSocket
+ * \param port Port number
+ */
+ static void bindUdpSocket(QUdpSocket& udpsocket, int port);
+
+ int readClientUdpPort(QTcpSocket* clientConnection);
+ int sendUdpPort(QTcpSocket* clientConnection, int udp_port);
+
+
+ /** \brief Send the JackTripWorker to the thread pool. This will run
+ * until it's done. We still have control over the prototype class.
+ * \param id Identification Number
+ */
+ //void sendToPoolPrototype(int id);
+
+ /** \brief Check if address is already handled, if not add to array
+ * \param address as string (IPv4 or IPv6)
+ * \return -1 if address is busy, id number if not
+ */
+ int isNewAddress(QString address, uint16_t port);
+
+ /** \brief Returns the ID of the client in the pool. If the client
+ * is not in the pool yet, returns -1.
+ */
+ int getPoolID(QString address, uint16_t port);
+
+ //QUdpSocket mUdpHubSocket; ///< The UDP socket
+ //QHostAddress mPeerAddress; ///< The Peer Address
+
+ //JackTripWorker* mJTWorker; ///< Class that will be used as prototype
+ QVector<JackTripWorker*>* mJTWorkers; ///< Vector of JackTripWorker s
+ QThreadPool mThreadPool; ///< The Thread Pool
+
+ int mServerPort; //< Server known port number
+ int mBasePort;
+ addressPortPair mActiveAddress[gMaxThreads]; ///< Active address pool addresses
+ QHash<QString, uint16_t> mActiveAddressPortPair;
+
+ /// Boolean stop the execution of the thread
+ volatile bool mStopped;
+ int mTotalRunningThreads; ///< Number of Threads running in the pool
+ QMutex mMutex;
+ JackTrip::underrunModeT mUnderRunMode;
+ int mBufferQueueLength;
+
+ bool m_connectDefaultAudioPorts;
+ Settings* m_settings;
+
+#ifdef WAIR // wair
+ bool mWAIR;
+ void connectMesh(bool spawn);
+ void enumerateRunningThreadIDs();
+public :
+ void setWAIR(int b) {mWAIR = b;}
+ bool isWAIR() {return mWAIR;}
+#endif // endwhere
+ void connectPatch(bool spawn);
+public :
+ unsigned int mHubPatch;
+ void setHubPatch(unsigned int p) {mHubPatch = p;}
+ unsigned int getHubPatch() {return mHubPatch;}
+
+ void setUnderRunMode(JackTrip::underrunModeT UnderRunMode) { mUnderRunMode = UnderRunMode; }
+ void setBufferQueueLength(int BufferQueueLength) { mBufferQueueLength = BufferQueueLength; }
+};
+
+
+#endif //__UDPHUBLISTENER_H__
+++ /dev/null
-//*****************************************************************
-/*
- JackTrip: A System for High-Quality Audio Network Performance
- over the Internet
-
- Copyright (c) 2008 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 UdpMasterListener.cpp
- * \author Juan-Pablo Caceres and Chris Chafe
- * \date September 2008
- */
-
-#include <iostream>
-#include <cstdlib>
-#include <stdexcept>
-#include <cstring>
-
-#include <QTcpServer>
-#include <QTcpSocket>
-#include <QStringList>
-#include <QMutexLocker>
-
-#include "UdpMasterListener.h"
-#include "JackTripWorker.h"
-#include "jacktrip_globals.h"
-
-using std::cout; using std::endl;
-
-
-//*******************************************************************************
-UdpMasterListener::UdpMasterListener(int server_port) :
- //mJTWorker(NULL),
- mServerPort(server_port),
- mStopped(false),
- #ifdef WAIR // wair
- mWAIR(false),
- #endif // endwhere
- mTotalRunningThreads(0),
- m_connectDefaultAudioPorts(false)
-{
- // Register JackTripWorker with the master listener
- //mJTWorker = new JackTripWorker(this);
- mJTWorkers = new QVector<JackTripWorker*>;
- for (int i = 0; i<gMaxThreads; i++) {
- mJTWorkers->insert(i, NULL);
- }
-
-
- //mJTWorkers = new JackTripWorker(this);
- mThreadPool.setExpiryTimeout(3000); // msec (-1) = forever
- // Inizialize IP addresses
- for (int i = 0; i<gMaxThreads; i++) {
- mActiveAddress[i].address = ""; // Address strings
- mActiveAddress[i].port = 0;
- }
- // Set the base dynamic port
- // The Dynamic and/or Private Ports are those from 49152 through 65535
- // mBasePort = ( rand() % ( (65535 - gMaxThreads) - 49152 ) ) + 49152;
-
- // SoundWIRE ports open are UDP 61000-62000
- mBasePort = 61000;
-
- mUnderRunMode = JackTrip::WAVETABLE;
- mBufferQueueLength = gDefaultQueueLength;
-}
-
-
-//*******************************************************************************
-UdpMasterListener::~UdpMasterListener()
-{
- QMutexLocker lock(&mMutex);
- mThreadPool.waitForDone();
- //delete mJTWorker;
- for (int i = 0; i<gMaxThreads; i++) {
- delete mJTWorkers->at(i);
- }
- delete mJTWorkers;
-}
-
-
-//*******************************************************************************
-// Now that the first handshake is with TCP server, if the addreess/peer port of
-// the client is already on the thread pool, it means that a new connection is
-// requested (the old was desconnected). So we have to remove that thread from
-// the pool and then connect again.
-void UdpMasterListener::run()
-{
- mStopped = false;
-
- QHostAddress PeerAddress; // Object to store peer address
- int peer_udp_port; // Peer listening port
- int server_udp_port; // Server assigned udp port
-
- // Create and bind the TCP server
- // ------------------------------
- QTcpServer TcpServer;
- if ( !TcpServer.listen(QHostAddress::Any, mServerPort) ) {
- std::cerr << "TCP Socket Server ERROR: " << TcpServer.errorString().toStdString() << endl;
- std::exit(1);
- }
-
- const int tcpTimeout = 5*1000;
-
-
- cout << "JackTrip HUB SERVER: TCP Server Listening in Port = " << TcpServer.serverPort() << endl;
- while ( !mStopped )
- {
- cout << "JackTrip HUB SERVER: Waiting for client connections..." << endl;
- cout << "JackTrip HUB SERVER: Hub auto audio patch setting = " << mHubPatch << endl;
- cout << "=======================================================" << endl;
- while ( !TcpServer.waitForNewConnection(1000) )
- { if (mStopped) { return; } } // block until a new connection is received
- cout << "JackTrip HUB SERVER: Client Connection Received!" << endl;
-
- // Control loop to be able to exit if UDPs or TCPs error ocurr
- for (int dum = 0; dum<1; dum++) {
- QTcpSocket *clientConnection = TcpServer.nextPendingConnection();
- if ( !clientConnection->waitForConnected(tcpTimeout) ) {
- std::cerr << clientConnection->errorString().toStdString() << endl;
- break;
- }
- PeerAddress = clientConnection->peerAddress();
- cout << "JackTrip HUB SERVER: Client Connect Received from Address : "
- << PeerAddress.toString().toStdString() << endl;
-
- // Get UDP port from client
- // ------------------------
- peer_udp_port = readClientUdpPort(clientConnection);
- if ( peer_udp_port == 0 ) { break; }
- cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl;
-
- // Check is client is new or not
- // -----------------------------
- // Check if Address is not already in the thread pool
- // check by comparing address strings (To handle IPv4 and IPv6.)
- int id = isNewAddress(PeerAddress.toString(), peer_udp_port);
- // If the address is not new, we need to remove the client from the pool
- // before re-starting the connection
-
- if (id == -1) {
- int id_remove;
- id_remove = getPoolID(PeerAddress.toString(), peer_udp_port);
- // stop the thread
- mJTWorkers->at(id_remove)->stopThread();
- // block until the thread has been removed from the pool
- while ( isNewAddress(PeerAddress.toString(), peer_udp_port) == -1 ) {
- cout << "JackTrip HUB SERVER: Removing JackTripWorker from pool..." << endl;
- QThread::msleep(10);
- }
- // Get a new ID for this client
- //id = isNewAddress(PeerAddress.toIPv4Address(), peer_udp_port);
- id = getPoolID(PeerAddress.toString(), peer_udp_port);
- }
- // Assign server port and send it to Client
- server_udp_port = mBasePort+id;
- if ( sendUdpPort(clientConnection, server_udp_port) == 0 ) {
- clientConnection->close();
- delete clientConnection;
- releaseThread(id);
- break;
- }
-
- // Close and Delete the socket
- // ---------------------------
- clientConnection->close();
- delete clientConnection;
- cout << "JackTrip HUB SERVER: Client TCP Connection Closed!" << endl;
-
- // Spawn Thread to Pool
- // --------------------
- // Register JackTripWorker with the master listener
- delete mJTWorkers->at(id); // just in case the Worker was previously created
- mJTWorkers->replace(id, new JackTripWorker(this, mBufferQueueLength, mUnderRunMode));
- // redirect port and spawn listener
- cout << "JackTrip HUB SERVER: Spawning JackTripWorker..." << endl;
- {
- QMutexLocker lock(&mMutex);
- mJTWorkers->at(id)->setJackTrip(id,
- mActiveAddress[id].address,
- server_udp_port,
- mActiveAddress[id].port,
- 1,
- m_connectDefaultAudioPorts
- ); /// \todo temp default to 1 channel
-
- qDebug() << "mPeerAddress" << id << mActiveAddress[id].address << mActiveAddress[id].port;
- }
- //send one thread to the pool
- cout << "JackTrip HUB SERVER: Starting JackTripWorker..." << endl;
- mThreadPool.start(mJTWorkers->at(id), QThread::TimeCriticalPriority);
- // wait until one is complete before another spawns
- while (mJTWorkers->at(id)->isSpawning()) { QThread::msleep(10); }
- //mTotalRunningThreads++;
- cout << "JackTrip HUB SERVER: Total Running Threads: " << mTotalRunningThreads << endl;
- cout << "===============================================================" << endl;
- QThread::msleep(100);
-#ifdef WAIR // WAIR
- if (isWAIR()) connectMesh(true); // invoked with -Sw
-#endif // endwhere
-
- qDebug() << "mPeerAddress" << mActiveAddress[id].address << mActiveAddress[id].port;
-
- connectPatch(true);
- }
- }
-
- /*
- // Create objects on the stack
- QUdpSocket MasterUdpSocket;
- QHostAddress PeerAddress;
- uint16_t peer_port; // Ougoing Peer port, in case they're not using the default
-
- // Bind the socket to the well known port
- bindUdpSocket(MasterUdpSocket, mServerPort);
-
- char buf[1];
- cout << "Server Listening in UDP Port: " << mServerPort << endl;
- cout << "Waiting for client..." << endl;
- cout << "=======================================================" << endl;
- while ( !mStopped )
- {
- //cout << "WAITING........................." << endl;
- while ( MasterUdpSocket.hasPendingDatagrams() )
- {
- cout << "Received request from Client!" << endl;
- // Get Client IP Address and outgoing port from packet
- int rv = MasterUdpSocket.readDatagram(buf, 1, &PeerAddress, &peer_port);
- cout << "Peer Port in Server ==== " << peer_port << endl;
- if (rv < 0) { std::cerr << "ERROR: Bad UDP packet read..." << endl; }
-
- /// \todo Get number of channels in the client from header
-
- // check by comparing 32-bit addresses
- /// \todo Add the port number in the comparison
- cout << "peer_portpeer_portpeer_port === " << peer_port << endl;
- int id = isNewAddress(PeerAddress.toIPv4Address(), peer_port);
-
- //cout << "IDIDIDIDIDDID === " << id << endl;
-
- // If the address is new, create a new thread in the pool
- if (id >= 0) // old address is -1
- {
- // redirect port and spawn listener
- sendToPoolPrototype(id);
- // wait until one is complete before another spawns
- while (mJTWorker->isSpawning()) { QThread::msleep(10); }
- mTotalRunningThreads++;
- cout << "Total Running Threads: " << mTotalRunningThreads << endl;
- cout << "=======================================================" << endl;
- }
- //cout << "ENDDDDDDDDDDDDDDDDDd === " << id << endl;
- }
- QThread::msleep(100);
- }
- */
-}
-
-
-//*******************************************************************************
-// Returns 0 on error
-int UdpMasterListener::readClientUdpPort(QTcpSocket* clientConnection)
-{
- // Read the size of the package
- // ----------------------------
- //tcpClient.waitForReadyRead();
- cout << "JackTrip HUB SERVER: Reading UDP port from Client..." << endl;
- while (clientConnection->bytesAvailable() < (int)sizeof(uint16_t)) {
- if (!clientConnection->waitForReadyRead()) {
- std::cerr << "TCP Socket ERROR: " << clientConnection->errorString().toStdString() << endl;
- return 0;
- }
- }
-
- if (gVerboseFlag) cout << "Ready To Read From Client!" << endl;
- // Read UDP Port Number from Server
- // --------------------------------
- int udp_port;
- int size = sizeof(udp_port);
- char port_buf[size];
- clientConnection->read(port_buf, size);
- std::memcpy(&udp_port, port_buf, size);
- return udp_port;
-}
-
-
-//*******************************************************************************
-int UdpMasterListener::sendUdpPort(QTcpSocket* clientConnection, int udp_port)
-{
- // Send Port Number to Client
- // --------------------------
- char port_buf[sizeof(udp_port)];
- std::memcpy(port_buf, &udp_port, sizeof(udp_port));
- clientConnection->write(port_buf, sizeof(udp_port));
- while ( clientConnection->bytesToWrite() > 0 ) {
- if ( clientConnection->state() == QAbstractSocket::ConnectedState ) {
- clientConnection->waitForBytesWritten(-1);
- }
- else {
- return 0;
- }
- }
- return 1;
- cout << "Port sent to Client" << endl;
-}
-
-
-//*******************************************************************************
-/*
-void UdpMasterListener::sendToPoolPrototype(int id)
-{
- mJTWorker->setJackTrip(id, mActiveAddress[id][0],
- mBasePort+(2*id), mActiveAddress[id][1],
- 1); /// \todo temp default to 1 channel
- mThreadPool.start(mJTWorker, QThread::TimeCriticalPriority); //send one thread to the pool
-}
-*/
-
-
-//*******************************************************************************
-void UdpMasterListener::bindUdpSocket(QUdpSocket& udpsocket, int port) throw(std::runtime_error)
-{
- // QHostAddress::Any : let the kernel decide the active address
- if ( !udpsocket.bind(QHostAddress::Any,
- port, QUdpSocket::DefaultForPlatform) ) {
- //std::cerr << "ERROR: could not bind UDP socket" << endl;
- //std::exit(1);
- throw std::runtime_error("Could not bind UDP socket. It may be already binded.");
- }
- else {
- cout << "UDP Socket Receiving in Port: " << port << endl;
- }
-}
-
-
-//*******************************************************************************
-// check by comparing 32-bit addresses
-int UdpMasterListener::isNewAddress(QString address, uint16_t port)
-{
- QMutexLocker lock(&mMutex);
- bool busyAddress = false;
- int id = 0;
-
- /*
- while ( !busyAddress && (id<mThreadPool.activeThreadCount()) )
- {
- if ( address==mActiveAddress[id][0] && port==mActiveAddress[id][1]) { busyAddress = true; }
- id++;
- }
- */
- for (int i = 0; i<gMaxThreads; i++) {
- if ( address==mActiveAddress[i].address && port==mActiveAddress[i].port) {
- id = i;
- busyAddress = true;
- }
- }
- if ( !busyAddress ) {
- /*
- mActiveAddress[id][0] = address;
- mActiveAddress[id][1] = port;
- } else {
- */
- id = 0;
- bool foundEmptyAddress = false;
- while ( !foundEmptyAddress && (id<gMaxThreads) ) {
- if ( mActiveAddress[id].address.isEmpty() && (mActiveAddress[id].port == 0) ) {
- foundEmptyAddress = true;
- mActiveAddress[id].address = address;
- mActiveAddress[id].port = port;
- } else {
- id++;
- }
- }
- }
- if (!busyAddress) {
- mTotalRunningThreads++;
- }
- return ((busyAddress) ? -1 : id);
-}
-
-
-//*******************************************************************************
-int UdpMasterListener::getPoolID(QString address, uint16_t port)
-{
- QMutexLocker lock(&mMutex);
- //for (int id = 0; id<mThreadPool.activeThreadCount(); id++ )
- for (int id = 0; id<gMaxThreads; id++ )
- {
- if ( address==mActiveAddress[id].address && port==mActiveAddress[id].port)
- { return id; }
- }
- return -1;
-}
-
-
-//*******************************************************************************
-int UdpMasterListener::releaseThread(int id)
-{
- QMutexLocker lock(&mMutex);
- mActiveAddress[id].address = "";
- mActiveAddress[id].port = 0;
- mTotalRunningThreads--;
-#ifdef WAIR // wair
- if (isWAIR()) connectMesh(false); // invoked with -Sw
-#endif // endwhere
- if (getHubPatch()) connectPatch(false); // invoked with -p > 0
- return 0; /// \todo Check if we really need to return an argument here
-}
-
-#ifdef WAIR // wair
-#include "JMess.h"
-//*******************************************************************************
-void UdpMasterListener::connectMesh(bool spawn)
-{
- cout << ((spawn)?"spawning":"releasing") << " jacktripWorker so change mesh" << endl;
- JMess tmp;
- tmp.connectSpawnedPorts(gDefaultNumInChannels); // change gDefaultNumInChannels if more than stereo LAIR interconnects
- // tmp.disconnectAll();
- // enumerateRunningThreadIDs();
-}
-
-//*******************************************************************************
-void UdpMasterListener::enumerateRunningThreadIDs()
-{
- for (int id = 0; id<gMaxThreads; id++ )
- {
- if ( !mActiveAddress[id].address.isEmpty() )
- { qDebug() << id; }
- }
-}
-#endif // endwhere
-
-#include "JMess.h"
-void UdpMasterListener::connectPatch(bool spawn)
-{
- cout << ((spawn)?"spawning":"releasing") << " jacktripWorker so change patch" << endl;
- JMess tmp;
- // default is patch 0, which connects server audio to all clients
- // these are the other cases:
- if (getHubPatch() == JackTrip::RESERVEDMATRIX) // special patch for TU Berlin ensemble
- tmp.connectTUB(gDefaultNumInChannels);
- else if ((getHubPatch() == JackTrip::CLIENTECHO) || // client loopback for testing
- (getHubPatch() == JackTrip::CLIENTFOFI) || // all clients to all clients except self
- (getHubPatch() == JackTrip::FULLMIX)) // all clients to all clients including self
- tmp.connectSpawnedPorts(gDefaultNumInChannels,getHubPatch());
- // FIXME: need change to gDefaultNumInChannels if more than stereo
-}
-
-// TODO:
-// USE bool QAbstractSocket::isValid () const to check if socket is connect. if not, exit loop
+++ /dev/null
-//*****************************************************************
-/*
- JackTrip: A System for High-Quality Audio Network Performance
- over the Internet
-
- Copyright (c) 2008 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 UdpMasterListener.h
- * \author Juan-Pablo Caceres and Chris Chafe
- * \date September 2008
- */
-
-#ifndef __UDPMASTERLISTENER_H__
-#define __UDPMASTERLISTENER_H__
-
-#include <iostream>
-#include <stdexcept>
-
-#include <QThread>
-#include <QThreadPool>
-#include <QUdpSocket>
-#include <QHostAddress>
-#include <QTcpSocket>
-#include <QTcpServer>
-#include <QMutex>
-
-#include "JackTrip.h"
-#include "jacktrip_types.h"
-#include "jacktrip_globals.h"
-class JackTripWorker; // forward declaration
-
-typedef struct {
- QString address;
- int16_t port;
-} addressPortPair;
-
-/** \brief Master UDP listener on the Server.
- *
- * This creates a server that will listen on the well know port (the server port) and will
- * spawn JackTrip threads into the Thread pool. Clients request a connection.
- */
-class UdpMasterListener : public QThread
-{
- Q_OBJECT;
-
-public:
- UdpMasterListener(int server_port = gServerUdpPort);
- virtual ~UdpMasterListener();
-
- /// \brief Implements the Thread Loop. To start the thread, call start()
- /// ( DO NOT CALL run() )
- void run();
-
- /// \brief Stops the execution of the Thread
- void stop() { mStopped = true; }
-
- int releaseThread(int id);
-
- void setConnectDefaultAudioPorts(bool connectDefaultAudioPorts) { m_connectDefaultAudioPorts = connectDefaultAudioPorts; }
-
-private slots:
- void testReceive()
- { std::cout << "========= TEST RECEIVE SLOT ===========" << std::endl; }
-
-signals:
- void Listening();
- void ClientAddressSet();
- void signalRemoveThread(int id);
-
-
-private:
- /** \brief Binds a QUdpSocket. It chooses the available (active) interface.
- * \param udpsocket a QUdpSocket
- * \param port Port number
- */
- static void bindUdpSocket(QUdpSocket& udpsocket, int port) throw(std::runtime_error);
-
- int readClientUdpPort(QTcpSocket* clientConnection);
- int sendUdpPort(QTcpSocket* clientConnection, int udp_port);
-
-
- /** \brief Send the JackTripWorker to the thread pool. This will run
- * until it's done. We still have control over the prototype class.
- * \param id Identification Number
- */
- //void sendToPoolPrototype(int id);
-
- /** \brief Check if address is already handled, if not add to array
- * \param address as string (IPv4 or IPv6)
- * \return -1 if address is busy, id number if not
- */
- int isNewAddress(QString address, uint16_t port);
-
- /** \brief Returns the ID of the client in the pool. If the client
- * is not in the pool yet, returns -1.
- */
- int getPoolID(QString address, uint16_t port);
-
- //QUdpSocket mUdpMasterSocket; ///< The UDP socket
- //QHostAddress mPeerAddress; ///< The Peer Address
-
- //JackTripWorker* mJTWorker; ///< Class that will be used as prototype
- QVector<JackTripWorker*>* mJTWorkers; ///< Vector of JackTripWorker s
- QThreadPool mThreadPool; ///< The Thread Pool
-
- int mServerPort; //< Server known port number
- int mBasePort;
- addressPortPair mActiveAddress[gMaxThreads]; ///< Active address pool addresses
- QHash<QString, uint16_t> mActiveAddressPortPair;
-
- /// Boolean stop the execution of the thread
- volatile bool mStopped;
- int mTotalRunningThreads; ///< Number of Threads running in the pool
- QMutex mMutex;
- JackTrip::underrunModeT mUnderRunMode;
- int mBufferQueueLength;
-
- bool m_connectDefaultAudioPorts;
-
-#ifdef WAIR // wair
- bool mWAIR;
- void connectMesh(bool spawn);
- void enumerateRunningThreadIDs();
-public :
- void setWAIR(int b) {mWAIR = b;}
- bool isWAIR() {return mWAIR;}
-#endif // endwhere
- void connectPatch(bool spawn);
-public :
- unsigned int mHubPatch;
- void setHubPatch(unsigned int p) {mHubPatch = p;}
- unsigned int getHubPatch() {return mHubPatch;}
-
- void setUnderRunMode(JackTrip::underrunModeT UnderRunMode) { mUnderRunMode = UnderRunMode; }
- void setBufferQueueLength(int BufferQueueLength) { mBufferQueueLength = BufferQueueLength; }
-};
-
-
-#endif //__UDPMASTERLISTENER_H__
if hash qmake-qt5 2>/dev/null; then
echo "Using qmake-qt5"
QCMD=qmake-qt5
- elif hash qmake-qt4 2>/dev/null; then
- echo "Using qmake-qt4"
- QCMD=qmake-qt4
elif hash qmake 2>/dev/null; then #in case qt was compiled by user
echo "Using qmake"
QCMD=qmake
fi
# Build
+mkdir -p ../builddir
+cd ../builddir
if [[ $1 == 'nojack' ]]; then
echo "Building without Jack"
- $QCMD -spec $QSPEC -config nojack jacktrip.pro
+ $QCMD -spec $QSPEC -config nojack ../src/jacktrip.pro
make clean
- $QCMD -spec $QSPEC -config nojack jacktrip.pro
+ $QCMD -spec $QSPEC -config nojack ../src/jacktrip.pro
make release
else
- $QCMD -spec $QSPEC jacktrip.pro
+ $QCMD -spec $QSPEC ../src/jacktrip.pro
make clean
- $QCMD -spec $QSPEC jacktrip.pro
+ $QCMD -spec $QSPEC ../src/jacktrip.pro
make release
fi
QT -= gui
QT += network
-# rc.1.2 switch enables experimental wair build, merge some of it with WAIRTOMASTER
+# rc.1.2 switch enables experimental wair build, merge some of it with WAIRTOHUB
# DEFINES += WAIR
-DEFINES += WAIRTOMASTER
+DEFINES += WAIRTOHUB
# http://wiki.qtcentre.org/index.php?title=Undocumented_qmake#Custom_tools
#cc DEFINES += __RT_AUDIO__
TestRingBuffer.h \
ThreadPoolTest.h \
UdpDataProtocol.h \
- UdpMasterListener.h \
+ UdpHubListener.h \
AudioInterface.h
!nojack {
RingBuffer.cpp \
Settings.cpp \
UdpDataProtocol.cpp \
- UdpMasterListener.cpp \
+ UdpHubListener.cpp \
AudioInterface.cpp
!nojack {
/// \todo Add this namespace
//namespace JackTrip
-const char* const gVersion = "1.2beta2"; ///< JackTrip version
+const char* const gVersion = "1.2.1"; ///< JackTrip version
//*******************************************************************************
/// \name Default Values
#define PROTOCOL_STACK QHostAddress::AnyIPv4 // as opposed to Any
// #define WAIR_AUDIO_NAME "JackTrip" // for jack connection
const QString WAIR_AUDIO_NAME = QString("JackTrip"); // keep legacy for WAIR
-const int gMAX_WAIRS = 10; // jmess revision needed for string parse if > 1 digit
+const int gMAX_WAIRS = 128; // FIXME, should agree with maxThreadCount
+// jmess revision needed for string parse if > 1 digit
// hubpatch = 3 for TUB ensemble patching
///////////////////////////////
const uint32_t gDefaultBufferSizeInSamples = 128;
const QString gDefaultLocalAddress = QString();
const int gDefaultRedundancy = 1;
-const int gTimeOutMultiThreadedServer = 5000; // seconds
+const int gTimeOutMultiThreadedServer = 10000; // seconds
const int gWaitCounter = 60;
//@}
/// \name JackAudio
//@{
const int gJackBitResolution = 32; ///< Audio Bit Resolution of the Jack Server
+const char* const gJackDefaultClientName = "JackTrip";
//@}
/// \name JackTrip Server parameters
//@{
/// Maximum Threads that can be run at the same time
-const int gMaxThreads = 290; // some pthread limit around 297?
+const int gMaxThreads = 1024;
/// Public well-known UDP port to where the clients will connect
const int gServerUdpPort = 4464;