From bb196518815b161cac4cd4acfdc8d6fa75e2c6de Mon Sep 17 00:00:00 2001 From: =?utf8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 9 Jun 2020 21:03:25 +0200 Subject: [PATCH] New upstream version 1.2 --- .gitignore | 3 + CHANGESLOG.txt | 18 +- INSTALL.txt | 124 ++-- JMess.cpp | 475 +++++++++++++++ LICENSE | 23 + README.md | 24 + documentation/html_footer.html | 4 +- src/AudioInterface.cpp | 697 ++++++++++++++-------- src/AudioInterface.h | 221 +++---- src/DataProtocol.cpp | 12 +- src/DataProtocol.h | 150 ++--- src/DataProtocolPOSIX.cpp.tmp | 208 +++---- src/DataProtocolPOSIX.h.tmp | 136 ++--- src/JMess.cpp | 476 +++++++++++++++ src/JMess.h | 91 +++ src/JackAudioInterface.cpp | 362 ++++++------ src/JackAudioInterface.h | 154 ++--- src/JackTrip.cpp | 963 +++++++++++++++++-------------- src/JackTrip.h | 790 +++++++++++++------------ src/JackTripThread.cpp | 38 +- src/JackTripThread.h | 22 +- src/JackTripWorker.cpp | 401 +++++++------ src/JackTripWorker.h | 114 ++-- src/JackTripWorkerMessages.h | 42 +- src/LoopBack.cpp | 17 +- src/LoopBack.h | 22 +- src/NetKS.h | 133 +++-- src/PacketHeader.cpp | 322 +++++------ src/PacketHeader.h | 272 ++++----- src/ProcessPlugin.h | 44 +- src/RingBuffer.cpp | 260 ++++----- src/RingBuffer.h | 98 ++-- src/RingBufferWavetable.h | 24 +- src/RtAudioInterface.cpp | 318 +++++----- src/RtAudioInterface.h | 72 +-- src/Settings.cpp | 260 +++++++-- src/Settings.h | 93 +-- src/TestRingBuffer.h | 49 +- src/ThreadPoolTest.h | 80 +-- src/UdpDataProtocol.cpp | 886 ++++++++++++++++------------ src/UdpDataProtocol.h | 157 ++--- src/UdpDataProtocolPOSIX.cpp.tmp | 136 ++--- src/UdpDataProtocolPOSIX.h.tmp | 32 +- src/UdpMasterListener.cpp | 489 +++++++++------- src/UdpMasterListener.h | 124 ++-- src/build | 2 +- src/jacktrip.pro | 128 ++-- src/jacktrip_globals.cpp | 269 ++++----- src/jacktrip_globals.h | 64 +- src/jacktrip_main.cpp | 92 +-- src/jacktrip_tests.cpp | 60 +- src/jacktrip_types.h | 6 +- 52 files changed, 6045 insertions(+), 4012 deletions(-) create mode 100644 .gitignore create mode 100644 JMess.cpp create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/JMess.cpp create mode 100644 src/JMess.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1afe92a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# QtCreator user files +*.pro.user +*.pro.user.* diff --git a/CHANGESLOG.txt b/CHANGESLOG.txt index 446e7e5..c8dc135 100644 --- a/CHANGESLOG.txt +++ b/CHANGESLOG.txt @@ -1,3 +1,17 @@ +--- +master + +--- +1.2 (release candidate, not yet tagged) +- (added) jack patching modes (-p) for Hub Mode server (-S) +- (fixed) Compilation under ubuntu +- (removed) setRealtimeProcessPriority() +- (removed) Rtaudio mode (but still has dependencies) +- (fixed) IPv4-mapped IPv6 addressing bug +... +- (fixed) Fixed compilation for MacOSX10.11.sdk. +- (update) Updated to RtAudio 4.1.1, and using shared lib in linux. + --- 1.1 - (added) Support for RtAudio. Jacktrip can now be used without Jack @@ -20,11 +34,11 @@ --- 1.0.3 -- (added) Redundancy Algorithm for UDP Packets to to avoid glitches with packet losses +- (added) Redundancy Algorithm for UDP Packets to avoid glitches with packet losses - (fixed) Now compiles on 64bits machines - (fixed) Improved exceptions handling - (added) Basic Karplus-Strong model added as Plug-in -- (added) Some functionality reimplemented using signals and slots for +- (added) Some functionality reimplemented using signals and slots for more flexibility - (added) Multiple-Client-Server in alpha testing, expect it working in the next release diff --git a/INSTALL.txt b/INSTALL.txt index 81f3d67..2ca1624 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -3,18 +3,64 @@ Jacktrip : Build Instructions for Linux and MacOS X JackTrip: A System for High-Quality Audio Network Performance over the Internet. --- -MacOS X (10.9 or higher) Installation: +MacOS X (10.9 or higher) installation: -If you are installing on MacOS X, a binary is provided. You just need to have JackOSX installed on your machine -http://www.jackosx.com/ +If you are installing on MacOS X, a binary is provided with each release (download the latest release from https://github.com/jacktrip/jacktrip/releases before proceeding). +You need to have a working jack installation on your machine (see below). + +To install (using Terminal): + +$ git clone https://github.com/jacktrip/jacktrip.git +$ cd jacktrip/src +$ sudo cp jacktrip /usr/local/bin/ + (enter your password when prompted) -To install (using Terminal): go to bin/ directory and type: +$ sudo chmod 755 /usr/local/bin/jacktrip + (now you can run jacktrip from any directory using Terminal) - sudo cp jacktrip /usr/bin/ - (enter your password when prompted) - sudo chmod 755 /usr/bin/jacktrip - (now you can run jacktrip from any directory using Terminal) + +To build using QtCreator (tested using QtCreator 4.11 and macOS 10.13.6): + + * Open jacktrip.pro using QtCreator + * Choose a correctly configured Kit + * Install Jack: + brew install jack + brew install qjackctl + + +--- +Fedora installation (also tested on Ubuntu Studio): + +To install on Fedora (and possibly other Linux distributions), you need qt5, jack-audio-connection-kit-devel, and rtaudio. + +How to install qt5 and other needed tools on Fedora: +dnf install qt5-devel +dnf groupinstall "C Development Tools and Libraries" +dnf groupinstall "Development Tools" +dnf install jack-audio-connection-kit-devel alsa-lib-devel iperf qjackctl audacity git + +Ubuntu Studio already comes with jack and qjackctl but you may have to install libjack-dev. Other basic tools can be installed on Ubuntu with: + +sudo apt install build-essential + + +For rtaudio, if you don’t have the latest package, install from source: + +$ git clone https://github.com/thestk/rtaudio +$ cd rtaudio +$ bash autogen.sh +$ ./configure --with-jack +$ make +$ sudo make install +$ sudo ln -s /usr/local/lib/librtaudio.so.6 /lib64/ + +Then to install jacktrip: + +$ git clone https://github.com/jacktrip/jacktrip.git +$ cd jacktrip/src +$ ./build +$ sudo make install --- @@ -23,17 +69,19 @@ Dependencies: You need to have installed the libraries in your system: Qt 5.3 or higher jack-audio-connection-kit-devel +rtaudio -If you are using yum (in Fedora 8 or later) you can just install them (as root) with: - yum install jack-audio-connection-kit-devel +If you are using yum you can just install them (as root) with: + yum install jack-audio-connection-kit-devel and install qt from the qt site. If you want to build on MacOS X, you need JackOSX http://www.jackosx.com/ -and Qt 5.3 or higher. - +and Qt 5.3 or higher. + It is also possible to build without jack, see below. + --- Build: @@ -46,26 +94,26 @@ to use the app) type the following: ./build nojack If the previous script doesn't work on a different Linux flavor, try building -the Makfiles yourself. You'd need qmake (e.g., on Fedora, this command is called -qmake-qt4). Then you can build by: - qmake jacktrip.pro - make release +the Makefiles yourself. You'd need qmake. Then you can build by: +$ qmake jacktrip.pro +$ make release Or without Jack support: - qmake -config nojack jacktrip.pro - make release +$ qmake -config nojack jacktrip.pro +$ make release + +If you want to install (using Terminal): on the /src directory type: -If you want to install install (using Terminal): on the /src directory type: +$ sudo cp jacktrip /usr/local/bin/ + (enter your password when prompted) - sudo cp jacktrip /usr/bin/ - (enter your password when prompted) +$ sudo chmod 755 /usr/local/bin/jacktrip + (now you can run jacktrip from any directory using Terminal) - sudo chmod 755 /usr/bin/jacktrip - (now you can run jacktrip from any directory using Terminal) ------------------------------ -WINDOWS (XP and later) +--- +WINDOWS (XP and later) Dependencies: @@ -79,38 +127,42 @@ the default is Program Files/Jacktrip. The installer also adds the executable Jacktrip.exe in the System32 directory, so that it can be executed from a command prompt from any working directory. -This executable (jacktrip.exe) can be found in the bin directory, along with the Dynamic +This executable (jacktrip.exe) can be found in the bin directory, along with the Dynamic Link Libraries (DLLs) it links to. Building: -The easiest way to build is to download the free Qt Creator IDE from http://qt.nokia.com/products/ +The easiest way to build is to download the free Qt Creator IDE from https://www.qt.io/download since the jacktrip buildscript is written in qmake. + +Use `git clone https://github.com/jacktrip/jacktrip.git` to download a fresh copy of the repo. +Open the `src/jacktrip.pro` and configure the project. + +Make sure to select the MinGW compiler (for example the one shipped with QtCreator). +Building with Clang or Microsoft Visual Studio Compilers is currently not supported! -Make a copy of the src and externals directories into a new directory of your choice, open the jacktrip.pro -file in src, and build the project. -You can alternatively download the MinGW (Minimalist GNU for Windows), a Windows port of the GNU -compiler from http://www.mingw.org/ and use mingw32-make from a command terminal to build the makefile. +Download Jack2 from https://jackaudio.org/downloads/ +Make sure to install Jack into `C:\Program Files (x86)\Jack` (as this is the path where the jacktrip build script will look for it). -Building is not supported with Microsoft Visual Studio Compilers. +Hit build in QtCreator. -Note: compiling with modifications in the .pro file (like adding a new source or header file) requires +Note: compiling with modifications in the .pro file (like adding a new source or header file) requires qmake which is only available in the Qt Creator package. --- -BUILD WARNING +BUILD WARNING Always keep the /src and /externals directories under the same directory. --- Post Configuration -Detailed instructions at +Detailed instructions at http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/ --- Using JackTrip -Detailed instructions at +Detailed instructions at http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/ diff --git a/JMess.cpp b/JMess.cpp new file mode 100644 index 0000000..30b20dc --- /dev/null +++ b/JMess.cpp @@ -0,0 +1,475 @@ +/* + JMess: A simple utility so save your jack-audio mess. + + Copyright (C) 2007-2010 Juan-Pablo Caceres. + + 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. +*/ + + +/* + * JMess.cpp + */ + +#include "JMess.h" +#include "JackTrip.h" +#include "jacktrip_globals.h" +#include + + +//------------------------------------------------------------------------------- +/*! \brief Constructs a JMess object that has a jack client. + * + */ +//------------------------------------------------------------------------------- +JMess::JMess() +{ + //Open a client connection to the JACK server. Starting a + //new server only to list its ports seems pointless, so we + //specify JackNoStartServer. + mClient = jack_client_open ("lsp", JackNoStartServer, &mStatus); + if (mClient == NULL) { + if (mStatus & JackServerFailed) { + cerr << "JACK server not running" << endl; + } else { + cerr << "jack_client_open() failed, " + << "status = 0x%2.0x\n" << mStatus << endl; + } + exit(1); + } +} + + +//------------------------------------------------------------------------------- +/*! \brief Distructor closes the jmess jack audio client. + * + */ +//------------------------------------------------------------------------------- +JMess::~JMess() +{ + if (jack_client_close(mClient)) + cerr << "ERROR: Could not close the hidden jmess jack client." << endl; +} + + +//------------------------------------------------------------------------------- +/*! \brief Write an XML file with the name specified at xmlOutFile. + * + */ +//------------------------------------------------------------------------------- +void JMess::writeOutput(QString xmlOutFile) +{ + // QDomDocument jmess_xml; QDomElement root; + // QDomElement connection; QDomElement output; + // QDomElement input; QDomText output_name; + // QDomText input_name; + + // QVector OutputInput(2); + + // this->setConnectedPorts(); + + // root = jmess_xml.createElement("jmess"); + // for (QVector >::iterator it = mConnectedPorts.begin(); + // it != mConnectedPorts.end(); ++it) { + // OutputInput = *it; + // //cout << "Output ===> " < " <> answer; + // } + // } + // else { + // answer = "yes"; + // } + + // if (answer == "yes") { + // if (!file.open(QIODevice::WriteOnly)) { + // cerr << "Cannot open file for writing: " + // << qPrintable(file.errorString()) << endl; + // exit(1); + // } + + // QTextStream out(&file); + // jmess_xml.save(out, Indent); + // cout << qPrintable(xmlOutFile) << " written." << endl; + // } +} + + +//------------------------------------------------------------------------------- +/*! \brief Set list of ouput ports that have connections. + * + */ +//------------------------------------------------------------------------------- +void JMess::setConnectedPorts() +{ + mConnectedPorts.clear(); + + const char **ports, **connections; //vector of ports and connections + QVector OutputInput(2); //helper variable + + //Get active output ports. + ports = jack_get_ports (mClient, NULL, NULL, JackPortIsOutput); + + for (unsigned int out_i = 0; ports[out_i]; ++out_i) { + if ((connections = jack_port_get_all_connections + (mClient, jack_port_by_name(mClient, ports[out_i]))) != 0) { + for (unsigned int in_i = 0; connections[in_i]; ++in_i) { + OutputInput[0] = ports[out_i]; + // cout << "Output ===> " < " << qPrintable(OutputInput[1]) << endl; + mConnectedPorts.append(OutputInput); + } + } + } + + free(ports); +} +//******************************************************************************* +void JMess::connectSpawnedPorts(int nChans, int hubPatch) +// called from UdpMasterListener::connectMesh +{ + QString IPS[gMAX_WAIRS]; + int ctr = 0; + + const char **ports, **connections; //vector of ports and connections + QVector OutputInput(2); //helper variable + + //Get active output ports. + ports = jack_get_ports (mClient, NULL, NULL, JackPortIsOutput); + + for (unsigned int out_i = 0; ports[out_i]; ++out_i) { + // qDebug() << QString(ports[out_i]); + bool systemPort = QString(ports[out_i]).contains(QString("system")); + + QString str = QString(ports[out_i]); + // for example "171.64.197.121:receive_1" + QString s = str.section(':', 0, 0); + // qDebug() << s << systemPort; + // for example "171.64.197.121" + + bool newOne = !systemPort; + for (int i = 0; i OutputInput(2); + + this->setConnectedPorts(); + + for (QVector >::iterator it = mConnectedPorts.begin(); + it != mConnectedPorts.end(); ++it) { + OutputInput = *it; + + if (jack_disconnect(mClient, OutputInput[0].toLatin1(), OutputInput[1].toLatin1())) { + cerr << "WARNING: port: " << qPrintable(OutputInput[0]) + << "and port: " << qPrintable(OutputInput[1]) + << " could not be disconnected.\n"; + } + } + +} + + +//------------------------------------------------------------------------------- +/*! \brief Parse the XML input file. + * + * Returns 0 on success, or 1 if the file has an incorrect format or cannot + * read the file. + */ +//------------------------------------------------------------------------------- +int JMess::parseXML(QString xmlInFile) +{ + // mPortsToConnect.clear(); + // QString errorStr; + // int errorLine; + // int errorColumn; + + // QFile file(xmlInFile); + // if (!file.open(QIODevice::ReadOnly)) { + // cerr << "Cannot open file for reading: " + // << qPrintable(file.errorString()) << endl; + // return 1; + // } + + // QDomDocument doc; + // if (!doc.setContent(&file, true, &errorStr, &errorLine, + // &errorColumn)) { + // cerr << "===================================================\n" + // << "Error parsing XML input file:\n" + // << "Parse error at line " << errorLine + // << ", column " << errorColumn << "\n" + // << qPrintable(errorStr) << "\n" + // << "===================================================\n"; + // return 1; + // } + + // QDomElement jmess = doc.documentElement(); + // if (jmess.tagName() != "jmess") { + // cerr << "Error: Root tag should be : " + // << qPrintable(jmess.tagName()) << endl; + // return 1; + // } + + + // QVector OutputInput(2); + // //First check for tag + // for(QDomNode n_cntn = jmess.firstChild(); + // !n_cntn.isNull(); n_cntn = n_cntn.nextSibling()) { + // QDomElement cntn = n_cntn.toElement(); + // if (cntn.tagName() == "connection") { + // //Now check for ouput & input tag + // for(QDomNode n_sck = cntn.firstChild(); + // !n_sck.isNull(); n_sck = n_sck.nextSibling()) { + // QDomElement sck = n_sck.toElement(); + // //cout << qPrintable(sck.tagName()) << endl; + // //cout << qPrintable(sck.text()) << endl; + // if (sck.tagName() == "output") { + // OutputInput[0] = sck.text(); + // } + // else if (sck.tagName() == "input") { + // OutputInput[1] = sck.text(); + // } + // } + // mPortsToConnect.append(OutputInput); + // } + // } + + return 0; + +} + + +//------------------------------------------------------------------------------- +/*! \brief Connect ports specified in input XML file xmlInFile + * + */ +//------------------------------------------------------------------------------- +void JMess::connectPorts(QString xmlInFile) +{ + QVector OutputInput(2); + + // if ( !(this->parseXML(xmlInFile)) ) { + // for (QVector >::iterator it = mPortsToConnect.begin(); + // it != mPortsToConnect.end(); ++it) { + // OutputInput = *it; + + // if (jack_connect(mClient, OutputInput[0].toLatin1(), OutputInput[1].toLatin1())) { + // //Display a warining only if the error is not because the ports are already + // //connected, in case the program doesn't display anyting. + // if (EEXIST != + // jack_connect(mClient, OutputInput[0].toLatin1(), OutputInput[1].toLatin1())) { + // cerr << "WARNING: port: " << qPrintable(OutputInput[0]) + // << "and port: " << qPrintable(OutputInput[1]) + // << " could not be connected.\n"; + // } + // } + // } + // } + +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a843d97 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ + Copyright (c) 2020 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9490616 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# JackTrip is a Linux, Mac OSX, or Windows multi-machine audio system used for network music performance over the Internet. +It supports any number of channels (as many as the computer/network can handle) of bidirectional, high quality, uncompressed audio signal streaming. + +You can use it between any combination of machines e.g., one end using Linux can connect to another using Mac OSX. + +# Raspberry Pi + +[paper](https://lac.linuxaudio.org/2019/doc/chafe2.pdf) accompanying jacktrip demo at [Linux Audio Conference 2019](https://lac.linuxaudio.org/2019/) + +# Other Repos +jacktrip (1.0) was released on google code. When that shut down, it migrated to github (1.05, 1.1). +It then moved to the CCRMA's cm-gitlab for version 1.2. +And as of spring 2020 it moved back to GitHub for the current development. + + +## 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/) . + * [SoundWIRE group](http://ccrma.stanford.edu/groups/soundwire/). + * [Juan-Pablo Caceres](https://ccrma.stanford.edu/~jcaceres/). + +## Related Software ## +[JMess](https://github.com/jcacerec/jmess-jack): A utility to save your audio connections (mess). diff --git a/documentation/html_footer.html b/documentation/html_footer.html index 979f8b8..d23d80d 100644 --- a/documentation/html_footer.html +++ b/documentation/html_footer.html @@ -4,7 +4,7 @@

 

-

Documentation generated by Doxygen $doxygenversion on $datetime

+

Documentation generated by Doxygen $doxygenversion

© 2008 by Juan-Pablo Caceres (jcaceres at ccrma dot stanford dot edu) and Chris Chafe
@@ -13,7 +13,7 @@ Chris Chafe
Stanford University

-SourceForge.net Logo +SourceForge

diff --git a/src/AudioInterface.cpp b/src/AudioInterface.cpp index dfea25f..06fe59e 100644 --- a/src/AudioInterface.cpp +++ b/src/AudioInterface.cpp @@ -45,71 +45,155 @@ using std::cout; using std::endl; //******************************************************************************* AudioInterface::AudioInterface(JackTrip* jacktrip, int NumInChans, int NumOutChans, + #ifdef WAIR // wair + int NumNetRevChans, + #endif // endwhere audioBitResolutionT AudioBitResolution) : -mJackTrip(jacktrip), -mNumInChans(NumInChans), mNumOutChans(NumOutChans), -mAudioBitResolution(AudioBitResolution*8), -mBitResolutionMode(AudioBitResolution), -mSampleRate(gDefaultSampleRate), mBufferSizeInSamples(gDefaultBufferSizeInSamples), -mInputPacket(NULL), mOutputPacket(NULL) + mJackTrip(jacktrip), + mNumInChans(NumInChans), mNumOutChans(NumOutChans), + #ifdef WAIR // WAIR + mNumNetRevChans(NumNetRevChans), + #endif // endwhere + mAudioBitResolution(AudioBitResolution*8), + mBitResolutionMode(AudioBitResolution), + mSampleRate(gDefaultSampleRate), mBufferSizeInSamples(gDefaultBufferSizeInSamples), + mInputPacket(NULL), mOutputPacket(NULL) { - // Set pointer to NULL - for (int i = 0; i < mNumInChans; i++) { - mInProcessBuffer[i] = NULL; - } - for (int i = 0; i < mNumOutChans; i++) { - mOutProcessBuffer[i] = NULL; - } +#ifndef WAIR + //cc + // Initialize and assign memory for ProcessPlugins Buffers + mInProcessBuffer.resize(mNumInChans); + mOutProcessBuffer.resize(mNumOutChans); + // Set pointer to NULL + for (int i = 0; i < mNumInChans; i++) { + mInProcessBuffer[i] = NULL; + } + for (int i = 0; i < mNumOutChans; i++) { + mOutProcessBuffer[i] = NULL; + } +#else // WAIR + int iCnt = (mNumInChans > mNumNetRevChans) ? mNumInChans : mNumNetRevChans; + int oCnt = (mNumOutChans > mNumNetRevChans) ? mNumOutChans : mNumNetRevChans; + int aCnt = (mNumNetRevChans) ? mNumInChans : 0; + for (int i = 0; i < iCnt; i++) { + mInProcessBuffer[i] = NULL; + } + for (int i = 0; i < oCnt; i++) { + mOutProcessBuffer[i] = NULL; + } + for (int i = 0; i < aCnt; i++) { + mAPInBuffer[i] = NULL; + } +#endif // endwhere } //******************************************************************************* AudioInterface::~AudioInterface() { - delete[] mInputPacket; - delete[] mOutputPacket; - for (int i = 0; i < mNumInChans; i++) { - delete[] mInProcessBuffer[i]; - } - - for (int i = 0; i < mNumOutChans; i++) { - delete[] mOutProcessBuffer[i]; - } + delete[] mInputPacket; + delete[] mOutputPacket; +#ifndef WAIR // WAIR + for (int i = 0; i < mNumInChans; i++) { + delete[] mInProcessBuffer[i]; + } + + for (int i = 0; i < mNumOutChans; i++) { + delete[] mOutProcessBuffer[i]; + } +#else // WAIR + int iCnt = (mNumInChans > mNumNetRevChans) ? mNumInChans : mNumNetRevChans; + int oCnt = (mNumOutChans > mNumNetRevChans) ? mNumOutChans : mNumNetRevChans; + int aCnt = (mNumNetRevChans) ? mNumInChans : 0; + for (int i = 0; i < iCnt; i++) { + delete[] mInProcessBuffer[i]; + } + for (int i = 0; i < oCnt; i++) { + delete[] mOutProcessBuffer[i]; + } + for (int i = 0; i < aCnt; i++) { + delete[] mAPInBuffer[i]; + } +#endif // endwhere } //******************************************************************************* void AudioInterface::setup() { - // Allocate buffer memory to read and write - mSizeInBytesPerChannel = getSizeInBytesPerChannel(); - int size_input = mSizeInBytesPerChannel * getNumInputChannels(); - int size_output = mSizeInBytesPerChannel * getNumOutputChannels(); - mInputPacket = new int8_t[size_input]; - mOutputPacket = new int8_t[size_output]; - - // Initialize and asign memory for ProcessPlugins Buffers - mInProcessBuffer.resize(mNumInChans); - mOutProcessBuffer.resize(mNumOutChans); - - int nframes = getBufferSizeInSamples(); - for (int i = 0; i < mNumInChans; i++) { - mInProcessBuffer[i] = new sample_t[nframes]; - // set memory to 0 - std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * nframes); - } - for (int i = 0; i < mNumOutChans; i++) { - mOutProcessBuffer[i] = new sample_t[nframes]; - // set memory to 0 - std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * nframes); - } + // Allocate buffer memory to read and write + mSizeInBytesPerChannel = getSizeInBytesPerChannel(); + + int size_input = mSizeInBytesPerChannel * getNumInputChannels(); + int size_output = mSizeInBytesPerChannel * getNumOutputChannels(); +#ifdef WAIR // WAIR + if(mNumNetRevChans) // else don't change sizes + { + size_input = mSizeInBytesPerChannel * mNumNetRevChans; + size_output = mSizeInBytesPerChannel * mNumNetRevChans; + } +#endif // endwhere + mInputPacket = new int8_t[size_input]; + mOutputPacket = new int8_t[size_output]; + + // Initialize and asign memory for ProcessPlugins Buffers +#ifdef WAIR // WAIR + if(mNumNetRevChans) + { + mInProcessBuffer.resize(mNumNetRevChans); + mOutProcessBuffer.resize(mNumNetRevChans); + mAPInBuffer.resize(mNumInChans); + mNetInBuffer.resize(mNumNetRevChans); + } else // don't change sizes +#endif // endwhere + { + mInProcessBuffer.resize(mNumInChans); + mOutProcessBuffer.resize(mNumOutChans); + } + + int nframes = getBufferSizeInSamples(); + +#ifndef WAIR // WAIR + for (int i = 0; i < mNumInChans; i++) { + mInProcessBuffer[i] = new sample_t[nframes]; + // set memory to 0 + std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * nframes); + } + for (int i = 0; i < mNumOutChans; i++) { + mOutProcessBuffer[i] = new sample_t[nframes]; + // set memory to 0 + std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * nframes); + } +#else // WAIR + for (int i = 0; i < ((mNumNetRevChans)?mNumNetRevChans:mNumInChans); i++) { + mInProcessBuffer[i] = new sample_t[nframes]; + // set memory to 0 + std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * nframes); + } + for (int i = 0; i < ((mNumNetRevChans)?mNumNetRevChans:mNumOutChans); i++) { + mOutProcessBuffer[i] = new sample_t[nframes]; + // set memory to 0 + std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * nframes); + } + for (int i = 0; i < ((mNumNetRevChans)?mNumInChans:0); i++) { + mAPInBuffer[i] = new sample_t[nframes]; + // set memory to 0 + std::memset(mAPInBuffer[i], 0, sizeof(sample_t) * nframes); + } + for (int i = 0; i < mNumNetRevChans; i++) { + mNetInBuffer[i] = new sample_t[nframes]; + // set memory to 0 + std::memset(mNetInBuffer[i], 0, sizeof(sample_t) * nframes); + } +#endif // endwhere + } //******************************************************************************* size_t AudioInterface::getSizeInBytesPerChannel() const { - return (getBufferSizeInSamples() * getAudioBitResolution()/8); + return (getBufferSizeInSamples() * getAudioBitResolution()/8); } @@ -118,48 +202,138 @@ void AudioInterface::callback(QVarLengthArray& in_buffer, QVarLengthArray& out_buffer, unsigned int n_frames) { - // Allocate the Process Callback - //------------------------------------------------------------------- - // 1) First, process incoming packets - // ---------------------------------- - computeProcessFromNetwork(out_buffer, n_frames); - - // 2) Dynamically allocate ProcessPlugin processes - // ----------------------------------------------- - // The processing will be done in order of allocation - /// \todo Implement for more than one process plugin, now it just works propertely with one. - /// do it chaining outputs to inputs in the buffers. May need a tempo buffer - for (int i = 0; i < mNumInChans; i++) { - std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * n_frames); - std::memcpy(mInProcessBuffer[i], out_buffer[i], sizeof(sample_t) * n_frames); - } - for (int i = 0; i < mNumOutChans; i++) { - std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * n_frames); - } - - for (int i = 0; i < mProcessPlugins.size(); i++) { - mProcessPlugins[i]->compute(n_frames, mInProcessBuffer.data(), mOutProcessBuffer.data()); - } - - // 3) Finally, send packets to peer - // -------------------------------- - computeProcessToNetwork(in_buffer, n_frames); - - - ///************PROTORYPE FOR CELT************************** - ///******************************************************** - /* + // Allocate the Process Callback + //------------------------------------------------------------------- + // 1) First, process incoming packets + // ---------------------------------- + +#ifdef WAIR // WAIR + // qDebug() << "--" << mProcessPlugins.size(); + bool client = (mProcessPlugins.size() == 2); +#define COMBDSP 1 // client +#define APDSP 0 // client +#define DCBDSP 0 // server + for (int i = 0; i < mNumNetRevChans; i++) { + std::memset(mNetInBuffer[i], 0, sizeof(sample_t) * n_frames); + } +#endif // endwhere + + computeProcessFromNetwork(out_buffer, n_frames); +#ifdef WAIR // WAIR + // nib16 result now in mNetInBuffer +#endif // endwhere + + // 2) Dynamically allocate ProcessPlugin processes + // ----------------------------------------------- + // The processing will be done in order of allocation + /// \todo Implement for more than one process plugin, now it just works propertely with one. + /// do it chaining outputs to inputs in the buffers. May need a tempo buffer + +#ifndef WAIR // WAIR + for (int i = 0; i < mNumInChans; i++) { + std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * n_frames); + std::memcpy(mInProcessBuffer[i], out_buffer[i], sizeof(sample_t) * n_frames); + } + for (int i = 0; i < mNumOutChans; i++) { + std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * n_frames); + } + + for (int i = 0; i < mProcessPlugins.size(); i++) { + mProcessPlugins[i]->compute(n_frames, mInProcessBuffer.data(), mOutProcessBuffer.data()); + } +#else // WAIR + for (int i = 0; i < ((mNumNetRevChans)?mNumNetRevChans:mNumOutChans); i++) { + std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * n_frames); + } + for (int i = 0; i < ((mNumNetRevChans)?mNumNetRevChans:mNumInChans); i++) { + std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * n_frames); + if (mNumNetRevChans) + { + if (client) + std::memcpy(mInProcessBuffer[i], mNetInBuffer[i], sizeof(sample_t) * n_frames); + else + std::memcpy(mOutProcessBuffer[i], mNetInBuffer[i], sizeof(sample_t) * n_frames); + } + } + // nib16 to cib16 + + if (mNumNetRevChans && client) mProcessPlugins[COMBDSP]->compute(n_frames, + mInProcessBuffer.data(), mOutProcessBuffer.data()); + // compute cob16 +#endif // endwhere + + // 3) Finally, send packets to peer + // -------------------------------- + computeProcessToNetwork(in_buffer, n_frames); + +#ifdef WAIR // WAIR + // aib2 + cob16 to nob16 +#endif // endwhere + +#ifdef WAIR // WAIR + if (mNumNetRevChans) // else not wair, so skip all this + { + /////////////////////////////////////////////////////////////////////////////// +#define AP +#ifndef AP + // straight to audio out + for (int i = 0; i < mNumOutChans; i++) { + std::memset(out_buffer[i], 0, sizeof(sample_t) * n_frames); + } + for (int i = 0; i < mNumNetRevChans; i++) { + sample_t* mix_sample = out_buffer[i%mNumOutChans]; + sample_t* tmp_sample = mNetInBuffer[i]; //mNetInBuffer + for (int j = 0; j < (int)n_frames; j++) {mix_sample[j] += tmp_sample[j];} + } // nib6 to aob2 + /////////////////////////////////////////////////////////////////////////////// +#else // AP + /////////////////////////////////////////////////////////////////////////////// + // output through all-pass cascade + // AP2 is 2 channel, mixes inputs to mono, then splits to two parallel AP chains + // AP8 is 2 channel, two parallel AP chains + for (int i = 0; i < mNumInChans; i++) { + std::memset(mAPInBuffer[i], 0, sizeof(sample_t) * n_frames); + } + for (int i = 0; i < mNumNetRevChans; i++) { + sample_t* mix_sample = mAPInBuffer[i%mNumOutChans]; + sample_t* tmp_sample = mNetInBuffer[i]; + for (int j = 0; j < n_frames; j++) {mix_sample[j] += tmp_sample[j];} + } // nib16 to apib2 + for (int i = 0; i < mNumOutChans; i++) { + std::memset(out_buffer[i], 0, sizeof(sample_t) * n_frames); + } + mProcessPlugins[APDSP]->compute(n_frames, mAPInBuffer.data(), out_buffer.data()); + // compute ap2 into aob2 + + //#define ADD_DIRECT +#ifdef ADD_DIRECT + for (int i = 0; i < mNumInChans; i++) { + sample_t* mix_sample = out_buffer[i]; + sample_t* tmp_sample = in_buffer[i]; + for (int j = 0; j < n_frames; j++) {mix_sample[j] += tmp_sample[j];} + } + // add aib2 to aob2 +#endif // ADD_DIRECT +#endif // AP + /////////////////////////////////////////////////////////////////////////////// + } +#endif // endwhere + + + ///************PROTORYPE FOR CELT************************** + ///******************************************************** + /* CELTMode* mode; int* error; mode = celt_mode_create(48000, 2, 64, error); */ - //celt_mode_create(48000, 2, 64, NULL); - //unsigned char* compressed; - //CELTEncoder* celtEncoder; - //celt_encode_float(celtEncoder, mInBuffer, NULL, compressed, ); + //celt_mode_create(48000, 2, 64, NULL); + //unsigned char* compressed; + //CELTEncoder* celtEncoder; + //celt_encode_float(celtEncoder, mInBuffer, NULL, compressed, ); - ///******************************************************** - ///******************************************************** + ///******************************************************** + ///******************************************************** } @@ -171,27 +345,42 @@ void AudioInterface::callback(QVarLengthArray& in_buffer, void AudioInterface::computeProcessFromNetwork(QVarLengthArray& out_buffer, unsigned int n_frames) { - /// \todo cast *mInBuffer[i] to the bit resolution - // Output Process (from NETWORK to JACK) - // ---------------------------------------------------------------- - // Read Audio buffer from RingBuffer (read from incoming packets) - mJackTrip->receiveNetworkPacket( mOutputPacket ); - - // Extract separate channels to send to Jack - for (int i = 0; i < mNumOutChans; i++) { - //-------- - // This should be faster for 32 bits - //std::memcpy(mOutBuffer[i], &mOutputPacket[i*mSizeInBytesPerChannel], - // mSizeInBytesPerChannel); - //-------- - sample_t* tmp_sample = out_buffer[i]; //sample buffer for channel i - for (unsigned int j = 0; j < n_frames; j++) { - // Change the bit resolution on each sample - fromBitToSampleConversion( - &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], - &tmp_sample[j], mBitResolutionMode ); - } - } + /// \todo cast *mInBuffer[i] to the bit resolution + // Output Process (from NETWORK to JACK) + // ---------------------------------------------------------------- + // Read Audio buffer from RingBuffer (read from incoming packets) + mJackTrip->receiveNetworkPacket( mOutputPacket ); + +#ifdef WAIR // WAIR + if (mNumNetRevChans) + // Extract separate channels + for (int i = 0; i < mNumNetRevChans; i++) { + sample_t* tmp_sample = mNetInBuffer[i]; //sample buffer for channel i + for (unsigned int j = 0; j < n_frames; j++) { + // Change the bit resolution on each sample + fromBitToSampleConversion( + &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + &tmp_sample[j], mBitResolutionMode ); + } + } + else // not wair +#endif // endwhere + + // Extract separate channels to send to Jack + for (int i = 0; i < mNumOutChans; i++) { + //-------- + // This should be faster for 32 bits + //std::memcpy(mOutBuffer[i], &mOutputPacket[i*mSizeInBytesPerChannel], + // mSizeInBytesPerChannel); + //-------- + sample_t* tmp_sample = out_buffer[i]; //sample buffer for channel i + for (unsigned int j = 0; j < n_frames; j++) { + // Change the bit resolution on each sample + fromBitToSampleConversion( + &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + &tmp_sample[j], mBitResolutionMode ); + } + } } @@ -199,30 +388,52 @@ void AudioInterface::computeProcessFromNetwork(QVarLengthArray& out_b void AudioInterface::computeProcessToNetwork(QVarLengthArray& in_buffer, unsigned int n_frames) { - // Input Process (from JACK to NETWORK) - // ---------------------------------------------------------------- - // Concatenate all the channels from jack to form packet - for (int i = 0; i < mNumInChans; i++) { - //-------- - // This should be faster for 32 bits - //std::memcpy(&mInputPacket[i*mSizeInBytesPerChannel], mInBuffer[i], - // mSizeInBytesPerChannel); - //-------- - sample_t* tmp_sample = in_buffer[i]; //sample buffer for channel i - sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process - sample_t tmp_result; - for (unsigned int j = 0; j < n_frames; j++) { - // Change the bit resolution on each sample - // Add the input jack buffer to the buffer resulting from the output process - tmp_result = tmp_sample[j] + tmp_process_sample[j]; - fromSampleToBitConversion( - &tmp_result, - &mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], - mBitResolutionMode ); - } - } - // Send Audio buffer to Network - mJackTrip->sendNetworkPacket( mInputPacket ); + // Input Process (from JACK to NETWORK) + // ---------------------------------------------------------------- + // Concatenate all the channels from jack to form packet + +#ifdef WAIR // WAIR + if (mNumNetRevChans) + for (int i = 0; i < mNumNetRevChans; i++) { + sample_t* tmp_sample = in_buffer[i%mNumInChans]; //sample buffer for channel i + sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process + sample_t tmp_result; + for (unsigned int j = 0; j < n_frames; j++) { + // Change the bit resolution on each sample + // Add the input jack buffer to the buffer resulting from the output process +#define INGAIN (0.9999) // 0.9999 because 1.0 can saturate the fixed pt rounding on output +#define COMBGAIN (1.0) + tmp_result = INGAIN*tmp_sample[j] + COMBGAIN*tmp_process_sample[j]; + fromSampleToBitConversion( + &tmp_result, + &mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + mBitResolutionMode ); + } + } + else // not wair +#endif // endwhere + + for (int i = 0; i < mNumInChans; i++) { + //-------- + // This should be faster for 32 bits + //std::memcpy(&mInputPacket[i*mSizeInBytesPerChannel], mInBuffer[i], + // mSizeInBytesPerChannel); + //-------- + sample_t* tmp_sample = in_buffer[i]; //sample buffer for channel i + sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process + sample_t tmp_result; + for (unsigned int j = 0; j < n_frames; j++) { + // Change the bit resolution on each sample + // Add the input jack buffer to the buffer resulting from the output process + tmp_result = tmp_sample[j] + tmp_process_sample[j]; + fromSampleToBitConversion( + &tmp_result, + &mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + mBitResolutionMode ); + } + } + // Send Audio buffer to Network + mJackTrip->sendNetworkPacket( mInputPacket ); } @@ -230,92 +441,92 @@ void AudioInterface::computeProcessToNetwork(QVarLengthArray& in_buff // This function quantize from 32 bit to a lower bit resolution // 24 bit is not working yet void AudioInterface::fromSampleToBitConversion - (const sample_t* const input, - int8_t* output, - const AudioInterface::audioBitResolutionT targetBitResolution) +(const sample_t* const input, + int8_t* output, + const AudioInterface::audioBitResolutionT targetBitResolution) { - int8_t tmp_8; - uint8_t tmp_u8; // unsigned to quantize the remainder in 24bits - int16_t tmp_16; - sample_t tmp_sample; - sample_t tmp_sample16; - sample_t tmp_sample8; - switch (targetBitResolution) + int8_t tmp_8; + uint8_t tmp_u8; // unsigned to quantize the remainder in 24bits + int16_t tmp_16; + sample_t tmp_sample; + sample_t tmp_sample16; + sample_t tmp_sample8; + switch (targetBitResolution) { case BIT8 : - // 8bit integer between -128 to 127 - tmp_sample = floor( (*input) * 128.0 ); // 2^7 = 128.0 - tmp_8 = static_cast(tmp_sample); - std::memcpy(output, &tmp_8, 1); // 8bits = 1 bytes - break; + // 8bit integer between -128 to 127 + tmp_sample = floor( (*input) * 128.0 ); // 2^7 = 128.0 + tmp_8 = static_cast(tmp_sample); + std::memcpy(output, &tmp_8, 1); // 8bits = 1 bytes + break; case BIT16 : - // 16bit integer between -32768 to 32767 - tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0 - tmp_16 = static_cast(tmp_sample); - std::memcpy(output, &tmp_16, 2); // 16bits = 2 bytes - break; + // 16bit integer between -32768 to 32767 + tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0 + tmp_16 = static_cast(tmp_sample); + std::memcpy(output, &tmp_16, 2); // 16bits = 2 bytes + break; case BIT24 : - // To convert to 24 bits, we first quantize the number to 16bit - tmp_sample = (*input) * 32768.0; // 2^15 = 32768.0 - tmp_sample16 = floor(tmp_sample); - tmp_16 = static_cast(tmp_sample16); - - // Then we compute the remainder error, and quantize that part into an 8bit number - // Note that this remainder is always positive, so we use an unsigned integer - tmp_sample8 = floor ((tmp_sample - tmp_sample16) //this is a positive number, between 0.0-1.0 - * 256.0); - tmp_u8 = static_cast(tmp_sample8); - - // Finally, we copy the 16bit number in the first 2 bytes, - // and the 8bit number in the third bite - std::memcpy(output, &tmp_16, 2); // 16bits = 2 bytes - std::memcpy(output+2, &tmp_u8, 1); // 8bits = 1 bytes - break; + // To convert to 24 bits, we first quantize the number to 16bit + tmp_sample = (*input) * 32768.0; // 2^15 = 32768.0 + tmp_sample16 = floor(tmp_sample); + tmp_16 = static_cast(tmp_sample16); + + // Then we compute the remainder error, and quantize that part into an 8bit number + // Note that this remainder is always positive, so we use an unsigned integer + tmp_sample8 = floor ((tmp_sample - tmp_sample16) //this is a positive number, between 0.0-1.0 + * 256.0); + tmp_u8 = static_cast(tmp_sample8); + + // Finally, we copy the 16bit number in the first 2 bytes, + // and the 8bit number in the third bite + std::memcpy(output, &tmp_16, 2); // 16bits = 2 bytes + std::memcpy(output+2, &tmp_u8, 1); // 8bits = 1 bytes + break; case BIT32 : - std::memcpy(output, input, 4); // 32bit = 4 bytes - break; + std::memcpy(output, input, 4); // 32bit = 4 bytes + break; } } //******************************************************************************* void AudioInterface::fromBitToSampleConversion - (const int8_t* const input, - sample_t* output, - const AudioInterface::audioBitResolutionT sourceBitResolution) +(const int8_t* const input, + sample_t* output, + const AudioInterface::audioBitResolutionT sourceBitResolution) { - int8_t tmp_8; - uint8_t tmp_u8; - int16_t tmp_16; - sample_t tmp_sample; - sample_t tmp_sample16; - sample_t tmp_sample8; - switch (sourceBitResolution) + int8_t tmp_8; + uint8_t tmp_u8; + int16_t tmp_16; + sample_t tmp_sample; + sample_t tmp_sample16; + sample_t tmp_sample8; + switch (sourceBitResolution) { case BIT8 : - tmp_8 = *input; - tmp_sample = static_cast(tmp_8) / 128.0; - std::memcpy(output, &tmp_sample, 4); // 4 bytes - break; + tmp_8 = *input; + tmp_sample = static_cast(tmp_8) / 128.0; + std::memcpy(output, &tmp_sample, 4); // 4 bytes + break; case BIT16 : - tmp_16 = *( reinterpret_cast(input) ); // *((int16_t*) input); - tmp_sample = static_cast(tmp_16) / 32768.0; - std::memcpy(output, &tmp_sample, 4); // 4 bytes - break; + tmp_16 = *( reinterpret_cast(input) ); // *((int16_t*) input); + tmp_sample = static_cast(tmp_16) / 32768.0; + std::memcpy(output, &tmp_sample, 4); // 4 bytes + break; case BIT24 : - // We first extract the 16bit and 8bit number from the 3 bytes - tmp_16 = *( reinterpret_cast(input) ); - tmp_u8 = *( reinterpret_cast(input+2) ); - - // Then we recover the number - tmp_sample16 = static_cast(tmp_16); - tmp_sample8 = static_cast(tmp_u8) / 256.0; - tmp_sample = (tmp_sample16 + tmp_sample8) / 32768.0; - std::memcpy(output, &tmp_sample, 4); // 4 bytes - break; + // We first extract the 16bit and 8bit number from the 3 bytes + tmp_16 = *( reinterpret_cast(input) ); + tmp_u8 = *( reinterpret_cast(input+2) ); + + // Then we recover the number + tmp_sample16 = static_cast(tmp_16); + tmp_sample8 = static_cast(tmp_u8) / 256.0; + tmp_sample = (tmp_sample16 + tmp_sample8) / 32768.0; + std::memcpy(output, &tmp_sample, 4); // 4 bytes + break; case BIT32 : - std::memcpy(output, input, 4); // 4 bytes - break; + std::memcpy(output, input, 4); // 4 bytes + break; } } @@ -323,75 +534,73 @@ void AudioInterface::fromBitToSampleConversion //******************************************************************************* void AudioInterface::appendProcessPlugin(ProcessPlugin* plugin) { - /// \todo check that channels in ProcessPlugins are less or same that jack channels - if ( plugin->getNumInputs() ) {} - mProcessPlugins.append(plugin); + /// \todo check that channels in ProcessPlugins are less or same that jack channels + if ( plugin->getNumInputs() ) {} + mProcessPlugins.append(plugin); } //******************************************************************************* AudioInterface::samplingRateT AudioInterface::getSampleRateType() const { - uint32_t rate = getSampleRate(); - - if ( rate == 22050 ) { - return AudioInterface::SR22; } - else if ( rate == 32000 ) { - return AudioInterface::SR32; } - else if ( rate == 44100 ) { - return AudioInterface::SR44; } - else if ( rate == 48000 ) { - return AudioInterface::SR48; } - else if ( rate == 88200 ) { - return AudioInterface::SR88; } - else if ( rate == 96000 ) { - return AudioInterface::SR96; } - else if ( rate == 19200 ) { - return AudioInterface::SR192; } - - return AudioInterface::UNDEF; + uint32_t rate = getSampleRate(); + + if ( rate == 22050 ) { + return AudioInterface::SR22; } + else if ( rate == 32000 ) { + return AudioInterface::SR32; } + else if ( rate == 44100 ) { + return AudioInterface::SR44; } + else if ( rate == 48000 ) { + return AudioInterface::SR48; } + else if ( rate == 88200 ) { + return AudioInterface::SR88; } + else if ( rate == 96000 ) { + return AudioInterface::SR96; } + else if ( rate == 19200 ) { + return AudioInterface::SR192; } + + return AudioInterface::UNDEF; } //******************************************************************************* int AudioInterface::getSampleRateFromType(samplingRateT rate_type) { - int sample_rate = 0; - switch (rate_type) + int sample_rate = 0; + switch (rate_type) { case SR22 : - sample_rate = 22050; - return sample_rate; - break; + sample_rate = 22050; + return sample_rate; + break; case SR32 : - sample_rate = 32000; - return sample_rate; - break; + sample_rate = 32000; + return sample_rate; + break; case SR44 : - sample_rate = 44100; - return sample_rate; - break; + sample_rate = 44100; + return sample_rate; + break; case SR48 : - sample_rate = 48000; - return sample_rate; - break; + sample_rate = 48000; + return sample_rate; + break; case SR88 : - sample_rate = 88200; - return sample_rate; - break; + sample_rate = 88200; + return sample_rate; + break; case SR96 : - sample_rate = 96000; - return sample_rate; - break; + sample_rate = 96000; + return sample_rate; + break; case SR192 : - sample_rate = 192000; - return sample_rate; - break; + sample_rate = 192000; + return sample_rate; + break; default: - return sample_rate; - break; + return sample_rate; + break; } - return sample_rate; + return sample_rate; } - - diff --git a/src/AudioInterface.h b/src/AudioInterface.h index b35b5cd..573622d 100644 --- a/src/AudioInterface.h +++ b/src/AudioInterface.h @@ -57,156 +57,169 @@ class AudioInterface { public: - /// \brief Enum for Audio Resolution in bits - enum audioBitResolutionT { - BIT8 = 1, ///< 8 bits - BIT16 = 2, ///< 16 bits (default) - BIT24 = 3, ///< 24 bits - BIT32 = 4 ///< 32 bits - }; - - /// \brief Sampling Rates supported by JACK - enum samplingRateT { - SR22, ///< 22050 Hz - SR32, ///< 32000 Hz - SR44, ///< 44100 Hz - SR48, ///< 48000 Hz - SR88, ///< 88200 Hz - SR96, ///< 96000 Hz - SR192, ///< 192000 Hz - UNDEF ///< Undefined - }; - - /** \brief The class constructor + /// \brief Enum for Audio Resolution in bits + enum audioBitResolutionT { + BIT8 = 1, ///< 8 bits + BIT16 = 2, ///< 16 bits (default) + BIT24 = 3, ///< 24 bits + BIT32 = 4 ///< 32 bits + }; + + /// \brief Sampling Rates supported by JACK + enum samplingRateT { + SR22, ///< 22050 Hz + SR32, ///< 32000 Hz + SR44, ///< 44100 Hz + SR48, ///< 48000 Hz + SR88, ///< 88200 Hz + SR96, ///< 96000 Hz + SR192, ///< 192000 Hz + UNDEF ///< Undefined + }; + + /** \brief The class constructor * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) * \param NumInChans Number of Input Channels * \param NumOutChans Number of Output Channels * \param AudioBitResolution Audio Sample Resolutions in bits */ - AudioInterface(JackTrip* jacktrip, - int NumInChans, int NumOutChans, - AudioInterface::audioBitResolutionT AudioBitResolution = - AudioInterface::BIT16); - /// \brief The class destructor - virtual ~AudioInterface(); - - /** \brief Setup the client. This function should be called just before + AudioInterface(JackTrip* jacktrip, + int NumInChans, int NumOutChans, + #ifdef WAIR // wair + int NumNetRevChans, + #endif // endwhere + AudioInterface::audioBitResolutionT AudioBitResolution = + AudioInterface::BIT16); + /// \brief The class destructor + virtual ~AudioInterface(); + + /** \brief Setup the client. This function should be called just before * * starting the audio processes, it will setup the audio client with * the class parameters, like Sampling Rate, * Packet Size, Bit Resolution, etc... Sub-classes should also call the parent * method to ensure correct inizialization. */ - virtual void setup(); - /// \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; - /// \brief Stops the process-callback thread - /// \return 0 on success, otherwise a non-zero error code - virtual int stopProcess() const = 0; - /** \brief Process callback. Subclass should call this callback after obtaining the + virtual void setup(); + /// \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; + /// \brief Stops the process-callback thread + /// \return 0 on success, otherwise a non-zero error code + virtual int stopProcess() const = 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 * is reponsible to check that each channel has n_frames samplers * \param in_buffer Array of output audio samplers for each channel. The user * is reponsible to check that each channel has n_frames samplers */ - virtual void callback(QVarLengthArray& in_buffer, - QVarLengthArray& out_buffer, - unsigned int n_frames); - /** \brief Append a ProcessPlugin. The order of processing is determined by + virtual void callback(QVarLengthArray& in_buffer, + QVarLengthArray& out_buffer, + unsigned int n_frames); + /** \brief Append a ProcessPlugin. The order of processing is determined by * the order by which appending is done. * \param plugin a ProcesPlugin smart pointer. Create the object instance * using something like:\n * std::tr1::shared_ptr loopback(new ProcessPluginName); */ - virtual void appendProcessPlugin(ProcessPlugin* plugin); - virtual void connectDefaultPorts() = 0; - /** \brief Convert a 32bit number (sample_t) into one of the bit resolution + virtual void appendProcessPlugin(ProcessPlugin* plugin); + virtual void connectDefaultPorts() = 0; + /** \brief Convert a 32bit number (sample_t) into one of the bit resolution * supported (audioBitResolutionT). * * The result is stored in an int_8 array of the * appropriate size to hold the value. The caller is responsible to allocate * enough space to store the result. */ - static void fromSampleToBitConversion(const sample_t* const input, - int8_t* output, - const AudioInterface::audioBitResolutionT targetBitResolution); - /** \brief Convert a audioBitResolutionT bit resolution number into a + static void fromSampleToBitConversion(const sample_t* const input, + int8_t* output, + const AudioInterface::audioBitResolutionT targetBitResolution); + /** \brief Convert a audioBitResolutionT bit resolution number into a * 32bit number (sample_t) * * The result is stored in an sample_t array of the * appropriate size to hold the value. The caller is responsible to allocate * enough space to store the result. */ - static void fromBitToSampleConversion(const int8_t* const input, - sample_t* output, - const AudioInterface::audioBitResolutionT sourceBitResolution); - - //--------------SETTERS--------------------------------------------- - virtual void setNumInputChannels(int nchannels) - { mNumInChans = nchannels; } - virtual void setNumOutputChannels(int nchannels) - { mNumOutChans = nchannels; } - virtual void setSampleRate(uint32_t sample_rate) - { mSampleRate = sample_rate; } - virtual void setBufferSizeInSamples(uint32_t buf_size) - { mBufferSizeInSamples = buf_size; } - /// \brief Set Client Name to something different that the default (JackTrip) - virtual void setClientName(const char* ClientName) = 0; - //------------------------------------------------------------------ - - //--------------GETTERS--------------------------------------------- - /// \brief Get Number of Input Channels - virtual int getNumInputChannels() const { return mNumInChans; } - /// \brief Get Number of Output Channels - virtual int getNumOutputChannels() const { return mNumOutChans; } - virtual uint32_t getBufferSizeInSamples() const - { return mBufferSizeInSamples; } - virtual size_t getSizeInBytesPerChannel() const; - /// \brief Get the Jack Server Sampling Rate, in samples/second - virtual uint32_t getSampleRate() const - { return mSampleRate; } - /// \brief Get the Jack Server Sampling Rate Enum Type samplingRateT - /// \return AudioInterface::samplingRateT enum type - virtual samplingRateT getSampleRateType() const; - /** \brief Get the Audio Bit Resolution, in bits + static void fromBitToSampleConversion(const int8_t* const input, + sample_t* output, + const AudioInterface::audioBitResolutionT sourceBitResolution); + + //--------------SETTERS--------------------------------------------- + virtual void setNumInputChannels(int nchannels) + { mNumInChans = nchannels; } + virtual void setNumOutputChannels(int nchannels) + { mNumOutChans = nchannels; } + virtual void setSampleRate(uint32_t sample_rate) + { mSampleRate = sample_rate; } + virtual void setDeviceID(uint32_t device_id) + { mDeviceID = device_id; } + virtual void setBufferSizeInSamples(uint32_t buf_size) + { mBufferSizeInSamples = buf_size; } + /// \brief Set Client Name to something different that the default (JackTrip) + virtual void setClientName(const char* ClientName) = 0; + //------------------------------------------------------------------ + + //--------------GETTERS--------------------------------------------- + /// \brief Get Number of Input Channels + virtual int getNumInputChannels() const { return mNumInChans; } + /// \brief Get Number of Output Channels + virtual int getNumOutputChannels() const { return mNumOutChans; } + virtual uint32_t getBufferSizeInSamples() const + { return mBufferSizeInSamples; } + virtual uint32_t getDeviceID() const + { return mDeviceID; } + virtual size_t getSizeInBytesPerChannel() const; + /// \brief Get the Jack Server Sampling Rate, in samples/second + virtual uint32_t getSampleRate() const + { return mSampleRate; } + /// \brief Get the Jack Server Sampling Rate Enum Type samplingRateT + /// \return AudioInterface::samplingRateT enum type + virtual samplingRateT getSampleRateType() const; + /** \brief Get the Audio Bit Resolution, in bits * * This is one of the audioBitResolutionT set in construction */ - virtual int getAudioBitResolution() const { return mAudioBitResolution; } - /** \brief Helper function to get the sample rate (in Hz) for a + virtual int getAudioBitResolution() const { return mAudioBitResolution; } + /** \brief Helper function to get the sample rate (in Hz) for a * JackAudioInterface::samplingRateT * \param rate_type JackAudioInterface::samplingRateT enum type * \return Sample Rate in Hz */ - static int getSampleRateFromType(samplingRateT rate_type); - //------------------------------------------------------------------ + static int getSampleRateFromType(samplingRateT rate_type); + //------------------------------------------------------------------ private: - /// \brief Compute the process to receive packets - void computeProcessFromNetwork(QVarLengthArray& out_buffer, + /// \brief Compute the process to receive packets + void computeProcessFromNetwork(QVarLengthArray& out_buffer, + unsigned int n_frames); + /// \brief Compute the process to send packets + void computeProcessToNetwork(QVarLengthArray& in_buffer, unsigned int n_frames); - /// \brief Compute the process to send packets - void computeProcessToNetwork(QVarLengthArray& in_buffer, - unsigned int n_frames); - - JackTrip* mJackTrip; ///< JackTrip Mediator Class pointer - int mNumInChans;///< Number of Input Channels - int mNumOutChans; ///< Number of Output Channels - int mAudioBitResolution; ///< Bit resolution in audio samples - AudioInterface::audioBitResolutionT mBitResolutionMode; ///< Bit resolution (audioBitResolutionT) mode - uint32_t mSampleRate; ///< Sampling Rate - uint32_t mBufferSizeInSamples; ///< Buffer size in samples - size_t mSizeInBytesPerChannel; ///< Size in bytes per audio channel - QVector mProcessPlugins; ///< Vector of ProcesPlugins - QVarLengthArray mInProcessBuffer;///< Vector of Input buffers/channel for ProcessPlugin - QVarLengthArray mOutProcessBuffer;///< Vector of Output buffers/channel for ProcessPlugin - int8_t* mInputPacket; ///< Packet containing all the channels to read from the RingBuffer - int8_t* mOutputPacket; ///< Packet containing all the channels to send to the RingBuffer + + JackTrip* mJackTrip; ///< JackTrip Mediator Class pointer + int mNumInChans;///< Number of Input Channels + int mNumOutChans; ///< Number of Output Channels +#ifdef WAIR // wair + int mNumNetRevChans; ///< Number of Network Audio Channels (net comb filters) + QVarLengthArray mNetInBuffer; ///< Vector of Input buffers/channel read from net + QVarLengthArray mAPInBuffer; ///< Vector of Input buffers/channel for AllPass input +#endif // endwhere + int mAudioBitResolution; ///< Bit resolution in audio samples + AudioInterface::audioBitResolutionT mBitResolutionMode; ///< Bit resolution (audioBitResolutionT) mode + uint32_t mSampleRate; ///< Sampling Rate + uint32_t mDeviceID; ///< RTAudio DeviceID + uint32_t mBufferSizeInSamples; ///< Buffer size in samples + size_t mSizeInBytesPerChannel; ///< Size in bytes per audio channel + QVector mProcessPlugins; ///< Vector of ProcesPlugins + QVarLengthArray mInProcessBuffer;///< Vector of Input buffers/channel for ProcessPlugin + QVarLengthArray mOutProcessBuffer;///< Vector of Output buffers/channel for ProcessPlugin + int8_t* mInputPacket; ///< Packet containing all the channels to read from the RingBuffer + int8_t* mOutputPacket; ///< Packet containing all the channels to send to the RingBuffer }; #endif // __AUDIOINTERFACE_H__ diff --git a/src/DataProtocol.cpp b/src/DataProtocol.cpp index 3168a5f..a23120a 100644 --- a/src/DataProtocol.cpp +++ b/src/DataProtocol.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -50,9 +50,9 @@ using std::cout; using std::endl; //******************************************************************************* DataProtocol::DataProtocol(JackTrip* jacktrip, - const runModeT runmode, - int /*bind_port*/, int /*peer_port*/) : - mStopped(false), mHasPacketsToReceive(false), mRunMode(runmode), mJackTrip(jacktrip) + const runModeT runmode, + int /*bind_port*/, int /*peer_port*/) : + mStopped(false), mHasPacketsToReceive(false), mRunMode(runmode), mJackTrip(jacktrip) {} diff --git a/src/DataProtocol.h b/src/DataProtocol.h index 53f2790..816e6a8 100644 --- a/src/DataProtocol.h +++ b/src/DataProtocol.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -39,7 +39,8 @@ #define __DATAPROTOCOL_H__ #ifdef __WIN_32__ -#include +#include +#include #endif #ifndef __WIN_32__ @@ -60,7 +61,7 @@ class JackTrip; // forward declaration /** \brief Base class that defines the transmission protocol. - * + * * This base class defines most of the common method to setup and connect * sockets using the individual protocols (UDP, TCP, SCTP, etc). * @@ -91,27 +92,27 @@ class JackTrip; // forward declaration */ class DataProtocol : public QThread { - Q_OBJECT; + Q_OBJECT; public: - //----------ENUMS------------------------------------------ - /// \brief Enum to define packet header types - enum packetHeaderTypeT { - DEFAULT, ///< Default application header - JAMLINK, ///< Header to use with Jamlinks - EMPTY ///< Empty Header - }; + //----------ENUMS------------------------------------------ + /// \brief Enum to define packet header types + enum packetHeaderTypeT { + DEFAULT, ///< Default application header + JAMLINK, ///< Header to use with Jamlinks + EMPTY ///< Empty Header + }; - /// \brief Enum to define class modes, SENDER or RECEIVER - enum runModeT { - SENDER, ///< Set class as a Sender (send packets) - RECEIVER ///< Set class as a Receiver (receives packets) - }; - //--------------------------------------------------------- + /// \brief Enum to define class modes, SENDER or RECEIVER + enum runModeT { + SENDER, ///< Set class as a Sender (send packets) + RECEIVER ///< Set class as a Receiver (receives packets) + }; + //--------------------------------------------------------- - /** \brief The class constructor + /** \brief The class constructor * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) * \param runmode Sets the run mode, use either DataProtocol::SENDER or * DataProtocol::RECEIVER @@ -119,94 +120,99 @@ public: * \param bind_port Port number to bind for this socket (this is the receive or send port depending on the runmode) * \param peer_port Peer port number (this is the receive or send port depending on the runmode) */ - DataProtocol(JackTrip* jacktrip, - const runModeT runmode, - int bind_port, int peer_port); - - /// \brief The class destructor - virtual ~DataProtocol(); - - /** \brief Implements the thread loop + DataProtocol(JackTrip* jacktrip, + const runModeT runmode, + int bind_port, int peer_port); + + /// \brief The class destructor + virtual ~DataProtocol(); + + /** \brief Implements the thread loop * * Depending on the runmode, with will run a DataProtocol::SENDER thread or * DataProtocol::RECEIVER thread */ - virtual void run() = 0; + virtual void run() = 0; - /// \brief Stops the execution of the Thread - virtual void stop() { - QMutexLocker lock(&mMutex); - mStopped = true; - } + /// \brief Stops the execution of the Thread + virtual void stop() { + QMutexLocker lock(&mMutex); + mStopped = true; + } - /** \brief Sets the size of the audio part of the packets + /** \brief Sets the size of the audio part of the packets * \param size_bytes Size in bytes */ - void setAudioPacketSize(const size_t size_bytes){ mAudioPacketSize = size_bytes; } + void setAudioPacketSize(const size_t size_bytes){ mAudioPacketSize = size_bytes; } - /** \brief Get the size of the audio part of the packets + /** \brief Get the size of the audio part of the packets * \return size_bytes Size in bytes */ - size_t getAudioPacketSizeInBites() { return(mAudioPacketSize); } + size_t getAudioPacketSizeInBites() { return(mAudioPacketSize); } - /** \brief Set the peer address + /** \brief Set the peer address * \param peerHostOrIP IPv4 number or host name * \todo implement here instead of in the subclass UDP */ - virtual void setPeerAddress(const char* peerHostOrIP) = 0; + virtual void setPeerAddress(const char* peerHostOrIP) = 0; - /** \brief Set the peer incomming (receiving) port number + /** \brief Set the peer incomming (receiving) port number * \param port Port number * \todo implement here instead of in the subclass UDP */ - virtual void setPeerPort(int port) = 0; + virtual void setPeerPort(int port) = 0; - //virtual void getPeerAddressFromFirstPacket(QHostAddress& peerHostAddress, - // uint16_t& port) = 0; + //virtual void getPeerAddressFromFirstPacket(QHostAddress& peerHostAddress, + // uint16_t& port) = 0; +#if defined (__WIN_32__) + virtual void setSocket(SOCKET &socket) = 0; +#else + virtual void setSocket(int &socket) = 0; +#endif signals: - void signalError(const char* error_message); - void signalReceivedConnectionFromPeer(); + void signalError(const char* error_message); + void signalReceivedConnectionFromPeer(); protected: - /** \brief Get the Run Mode of the object + /** \brief Get the Run Mode of the object * \return SENDER or RECEIVER */ - runModeT getRunMode() const { return mRunMode; } + runModeT getRunMode() const { return mRunMode; } - /// Boolean stop the execution of the thread - volatile bool mStopped; - /// Boolean to indicate if the RECEIVER is waiting to obtain peer address - volatile bool mHasPeerAddress; - /// Boolean that indicates if a packet was received - volatile bool mHasPacketsToReceive; - QMutex mMutex; + /// Boolean stop the execution of the thread + volatile bool mStopped; + /// Boolean to indicate if the RECEIVER is waiting to obtain peer address + volatile bool mHasPeerAddress; + /// Boolean that indicates if a packet was received + volatile bool mHasPacketsToReceive; + QMutex mMutex; private: - int mLocalPort; ///< Local Port number to Bind - int mPeerPort; ///< Peer Port number to Bind - const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER - - struct sockaddr_in mLocalIPv4Addr; ///< Local IPv4 Address struct - struct sockaddr_in mPeerIPv4Addr; ///< Peer IPv4 Address struct - - /// Number of clients running to check for ports already used - /// \note Unimplemented, try to find another way to check for used ports - static int sClientsRunning; - - size_t mAudioPacketSize; ///< Packet audio part size - - - /// \todo check a better way to access the header from the subclasses + int mLocalPort; ///< Local Port number to Bind + int mPeerPort; ///< Peer Port number to Bind + const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER + + struct sockaddr_in mLocalIPv4Addr; ///< Local IPv4 Address struct + struct sockaddr_in mPeerIPv4Addr; ///< Peer IPv4 Address struct + + /// Number of clients running to check for ports already used + /// \note Unimplemented, try to find another way to check for used ports + static int sClientsRunning; + + size_t mAudioPacketSize; ///< Packet audio part size + + + /// \todo check a better way to access the header from the subclasses protected: - //PacketHeader* mHeader; ///< Packet Header - JackTrip* mJackTrip; ///< JackTrip mediator class + //PacketHeader* mHeader; ///< Packet Header + JackTrip* mJackTrip; ///< JackTrip mediator class }; diff --git a/src/DataProtocolPOSIX.cpp.tmp b/src/DataProtocolPOSIX.cpp.tmp index b92459c..2884664 100644 --- a/src/DataProtocolPOSIX.cpp.tmp +++ b/src/DataProtocolPOSIX.cpp.tmp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -50,65 +50,65 @@ using std::cout; using std::endl; //******************************************************************************* DataProtocol::DataProtocol(const runModeT runmode, - const packetHeaderTypeT headertype) : - mRunMode(runmode), mStopped(false), mHasPacketsToReceive(false), mHeader(NULL) + const packetHeaderTypeT headertype) : + mRunMode(runmode), mStopped(false), mHasPacketsToReceive(false), mHeader(NULL) { - //--------PROTOTYPE------------------------- - if ( headertype == DEFAULT ) { - mHeader = new DefaultHeader; - } - else if ( headertype == JAMLINK ) { - mHeader = new JamLinkHeader; - } - //------------------------------------------ + //--------PROTOTYPE------------------------- + if ( headertype == DEFAULT ) { + mHeader = new DefaultHeader; + } + else if ( headertype == JAMLINK ) { + mHeader = new JamLinkHeader; + } + //------------------------------------------ - // Base ports gInputPort_0 and gOutputPort_0defined at globals.h - if (mRunMode == RECEIVER) { - mLocalPort = gInputPort_0; - mPeerPort = gOutputPort_0; - } - else if (mRunMode == SENDER) { - mLocalPort = gOutputPort_0; - mPeerPort = gInputPort_0; - } + // Base ports gInputPort_0 and gOutputPort_0defined at globals.h + if (mRunMode == RECEIVER) { + mLocalPort = gInputPort_0; + mPeerPort = gOutputPort_0; + } + else if (mRunMode == SENDER) { + mLocalPort = gOutputPort_0; + mPeerPort = gInputPort_0; + } - this->setLocalIPv4Address(); + this->setLocalIPv4Address(); } //******************************************************************************* DataProtocol::~DataProtocol() { - delete mHeader; + delete mHeader; } //******************************************************************************* void DataProtocol::stop() { - mStopped = true; + mStopped = true; } //******************************************************************************* void DataProtocol::setLocalIPv4Address() { - bzero(&mLocalIPv4Addr, sizeof(mLocalIPv4Addr)); - mLocalIPv4Addr.sin_family = AF_INET;//AF_INET: IPv4 Protocol - mLocalIPv4Addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY: let the kernel decide the active address - mLocalIPv4Addr.sin_port = htons(mLocalPort);//set local port - //std::cout << "mLocalPort = " << mLocalPort << std::endl; + bzero(&mLocalIPv4Addr, sizeof(mLocalIPv4Addr)); + mLocalIPv4Addr.sin_family = AF_INET;//AF_INET: IPv4 Protocol + mLocalIPv4Addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY: let the kernel decide the active address + mLocalIPv4Addr.sin_port = htons(mLocalPort);//set local port + //std::cout << "mLocalPort = " << mLocalPort << std::endl; } //******************************************************************************* void DataProtocol::setPeerIPv4Address(const char* peerHostOrIP) { - const char* peerAddress; // dotted decimal address to use in the struct below + const char* peerAddress; // dotted decimal address to use in the struct below - /// \todo Improve this to make it work also with local ip numbers, in a LAN, - /// that don't have an assigned host name - /* + /// \todo Improve this to make it work also with local ip numbers, in a LAN, + /// that don't have an assigned host name + /* // Resolve Peer IPv4 with either doted integer IP or hostname //---------------------------------------------------------- std::cout << "Resolving Peer IPv4 address..." << std::endl; @@ -125,29 +125,29 @@ void DataProtocol::setPeerIPv4Address(const char* peerHostOrIP) } */ - // temporary implementation to make this work - /// \todo change this - peerAddress = peerHostOrIP; - - // Set the Peer IPv4 Address struct - //--------------------------------- - bzero(&mPeerIPv4Addr, sizeof(mPeerIPv4Addr)); - mPeerIPv4Addr.sin_family = AF_INET;//AF_INET: IPv4 Protocol - mPeerIPv4Addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY: let the kernel decide the active address - mPeerIPv4Addr.sin_port = htons(mPeerPort);//set Peer port - //std::cout << "mPeerPort = " << mPeerPort << std::endl; - int nPeer = inet_pton(AF_INET, peerAddress, &mPeerIPv4Addr.sin_addr); - if ( nPeer == 1 ) { - std::cout << "Successful Set Peer Address" << std::endl; - } - else if ( nPeer == 0 ) { - std::cout << "Error: Incorrect presentation format for address" << std::endl; - std::exit(1); - } - else { - std::cout << "Error: Could not set Peer Address" << std::endl; - std::exit(1); - } + // temporary implementation to make this work + /// \todo change this + peerAddress = peerHostOrIP; + + // Set the Peer IPv4 Address struct + //--------------------------------- + bzero(&mPeerIPv4Addr, sizeof(mPeerIPv4Addr)); + mPeerIPv4Addr.sin_family = AF_INET;//AF_INET: IPv4 Protocol + mPeerIPv4Addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY: let the kernel decide the active address + mPeerIPv4Addr.sin_port = htons(mPeerPort);//set Peer port + //std::cout << "mPeerPort = " << mPeerPort << std::endl; + int nPeer = inet_pton(AF_INET, peerAddress, &mPeerIPv4Addr.sin_addr); + if ( nPeer == 1 ) { + std::cout << "Successful Set Peer Address" << std::endl; + } + else if ( nPeer == 0 ) { + std::cout << "Error: Incorrect presentation format for address" << std::endl; + std::exit(1); + } + else { + std::cout << "Error: Could not set Peer Address" << std::endl; + std::exit(1); + } } @@ -155,68 +155,68 @@ void DataProtocol::setPeerIPv4Address(const char* peerHostOrIP) //******************************************************************************* void DataProtocol::setRingBuffer(std::tr1::shared_ptr RingBuffer) { - mRingBuffer = RingBuffer; + mRingBuffer = RingBuffer; } //******************************************************************************* void DataProtocol::run() { - std::cout << "Running DataProtocol Thread" << std::endl; - std::cout << gPrintSeparator << std::endl; - size_t packet_size = getAudioPacketSize(); - int8_t packet[packet_size]; - - switch ( mRunMode ) + std::cout << "Running DataProtocol Thread" << std::endl; + std::cout << gPrintSeparator << std::endl; + size_t packet_size = getAudioPacketSize(); + int8_t packet[packet_size]; + + switch ( mRunMode ) { - case RECEIVER : - //----------------------------------------------------------------------------------- - // Wait for the first packet to be ready and obtain address - // from that packet - /// \todo here is the place to read the datagram and check if the settings match - /// the local ones. Extract this information from the header - std::cout << "Waiting for Peer..." << std::endl; - this->receivePacket( (char*) packet, packet_size); // This blocks waiting for the first packet - std::cout << "Received Connection for Peer!" << std::endl; - - while ( !mStopped ) - { - //std::cout << "RECEIVING PACKETS" << std::endl; - /// \todo Set a timer to report packats arriving too late - //std::cout << "RECIEVING THREAD" << std::endl; - - this->receivePacket( (char*) packet, packet_size); - /// \todo Change this to match buffer size - //std::cout << "PACKET RECIEVED" << std::endl; - mRingBuffer->insertSlotBlocking(packet); - //std::cout << buf << std::endl; - } - break; - - - case SENDER : - //----------------------------------------------------------------------------------- - while ( !mStopped ) - { - //std::cout << "SENDING PACKETS --------------------------" << std::endl; - /// \todo This should be blocking, since we don't want to send trash - mRingBuffer->readSlotBlocking(packet); - //std::cout << "SENDING PACKETS" << std::endl; - this->sendPacket( (char*) packet, packet_size); - //std::cout << "SENDING PACKETS DONE!!!" << std::endl; - //this->sendPacket( sendtest, 64); - } - break; + case RECEIVER : + //----------------------------------------------------------------------------------- + // Wait for the first packet to be ready and obtain address + // from that packet + /// \todo here is the place to read the datagram and check if the settings match + /// the local ones. Extract this information from the header + std::cout << "Waiting for Peer..." << std::endl; + this->receivePacket( (char*) packet, packet_size); // This blocks waiting for the first packet + std::cout << "Received Connection for Peer!" << std::endl; + + while ( !mStopped ) + { + //std::cout << "RECEIVING PACKETS" << std::endl; + /// \todo Set a timer to report packats arriving too late + //std::cout << "RECIEVING THREAD" << std::endl; + + this->receivePacket( (char*) packet, packet_size); + /// \todo Change this to match buffer size + //std::cout << "PACKET RECIEVED" << std::endl; + mRingBuffer->insertSlotBlocking(packet); + //std::cout << buf << std::endl; + } + break; + + + case SENDER : + //----------------------------------------------------------------------------------- + while ( !mStopped ) + { + //std::cout << "SENDING PACKETS --------------------------" << std::endl; + /// \todo This should be blocking, since we don't want to send trash + mRingBuffer->readSlotBlocking(packet); + //std::cout << "SENDING PACKETS" << std::endl; + this->sendPacket( (char*) packet, packet_size); + //std::cout << "SENDING PACKETS DONE!!!" << std::endl; + //this->sendPacket( sendtest, 64); + } + break; } } void DataProtocol::setAudioPacketSize(size_t size_bytes) { - mAudioPacketSize = size_bytes; + mAudioPacketSize = size_bytes; } size_t DataProtocol::getAudioPacketSize() { - return(mAudioPacketSize); + return(mAudioPacketSize); } diff --git a/src/DataProtocolPOSIX.h.tmp b/src/DataProtocolPOSIX.h.tmp index c812d02..68e22bd 100644 --- a/src/DataProtocolPOSIX.h.tmp +++ b/src/DataProtocolPOSIX.h.tmp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -52,7 +52,7 @@ class PacketHeader; // forward declaration /** \brief Base class that defines the transmission protocol. - * + * * This base class defines most of the common method to setup and connect * sockets using the individual protocols (UDP, TCP, SCTP, etc). * @@ -85,129 +85,129 @@ class DataProtocol : public QThread { public: - /// \brief Enum to define packet header types - enum packetHeaderTypeT { - DEFAULT, ///< Default application header - JAMLINK ///< Header to use with Jamlinks - }; + /// \brief Enum to define packet header types + enum packetHeaderTypeT { + DEFAULT, ///< Default application header + JAMLINK ///< Header to use with Jamlinks + }; + + /// \brief Enum to define class modes, SENDER or RECEIVER + enum runModeT { + SENDER, ///< Set class as a Sender (send packets) + RECEIVER ///< Set class as a Receiver (receives packets) + }; - /// \brief Enum to define class modes, SENDER or RECEIVER - enum runModeT { - SENDER, ///< Set class as a Sender (send packets) - RECEIVER ///< Set class as a Receiver (receives packets) - }; - - /** \brief The class constructor + /** \brief The class constructor * \param runmode Sets the run mode, use either SENDER or RECEIVER */ - DataProtocol(const runModeT runmode, - const packetHeaderTypeT headertype = DEFAULT); - - /// \brief The class destructor - virtual ~DataProtocol(); - - /** \brief Sets the peer (remote) IPv4 address struct + DataProtocol(const runModeT runmode, + const packetHeaderTypeT headertype = DEFAULT); + + /// \brief The class destructor + virtual ~DataProtocol(); + + /** \brief Sets the peer (remote) IPv4 address struct * \param peerHostOrIP Either an IPv4 dotted integer number or a hostname */ - virtual void setPeerIPv4Address(const char* peerHostOrIP); + virtual void setPeerIPv4Address(const char* peerHostOrIP); - /** \brief Receive a packet from the UDPSocket + /** \brief Receive a packet from the UDPSocket * * This method has to be implemented in the sub-classes * \param buf Location at which to store the buffer * \param n size of packet to receive in bytes * \return number of bytes read, -1 on error */ - virtual size_t receivePacket(char* buf, size_t n) = 0; - - /** \brief Sends a packet + virtual size_t receivePacket(char* buf, size_t n) = 0; + + /** \brief Sends a packet * * This method has to be implemented in the sub-classes * \param buff Buffer to send * \param n size of packet to receive in bytes * \return number of bytes read, -1 on error */ - virtual size_t sendPacket(const char* buff, size_t n) = 0; + virtual size_t sendPacket(const char* buff, size_t n) = 0; - /** \brief Implements the thread loop + /** \brief Implements the thread loop * * Depending on the runmode, with will run a RECEIVE thread or * SEND thread */ - virtual void run(); + virtual void run(); - /** \brief Set the pointer to the RingBuffer that'll be use to read + /** \brief Set the pointer to the RingBuffer that'll be use to read * or write */ - void setRingBuffer(std::tr1::shared_ptr RingBuffer); + void setRingBuffer(std::tr1::shared_ptr RingBuffer); - /// \brief Stops the execution of the Thread - void stop(); + /// \brief Stops the execution of the Thread + void stop(); - /** \brief Sets the size of the audio part of the packets + /** \brief Sets the size of the audio part of the packets * \param size_bytes Size in bytes */ - void setAudioPacketSize(size_t size_bytes); + void setAudioPacketSize(size_t size_bytes); - /** \brief Get the size of the audio part of the packets + /** \brief Get the size of the audio part of the packets * \return size_bytes Size in bytes */ - size_t getAudioPacketSize(); + size_t getAudioPacketSize(); - //virtual void getIPAddressFromFirstPacket() = 0; + //virtual void getIPAddressFromFirstPacket() = 0; protected: - /** \brief Sets the local IPv4 address struct + /** \brief Sets the local IPv4 address struct * * It uses the default active device. */ - virtual void setLocalIPv4Address(); + virtual void setLocalIPv4Address(); - /** \brief Get the Run Mode of the object + /** \brief Get the Run Mode of the object * \return SENDER or RECEIVER */ - runModeT getRunMode() const { return mRunMode; }; + runModeT getRunMode() const { return mRunMode; }; - /** \brief Returns the Local machine IPv4 socket address stuct + /** \brief Returns the Local machine IPv4 socket address stuct * \return Socket address stuct */ - const sockaddr_in& getLocalIPv4AddressStruct() const { return mLocalIPv4Addr; }; - - /** \brief Returns the Peer IPv4 socket address stuct + const sockaddr_in& getLocalIPv4AddressStruct() const { return mLocalIPv4Addr; }; + + /** \brief Returns the Peer IPv4 socket address stuct * \return Socket address stuct */ - const sockaddr_in& getPeerIPv4AddressStruct() const { return mPeerIPv4Addr; }; + const sockaddr_in& getPeerIPv4AddressStruct() const { return mPeerIPv4Addr; }; private: - int mLocalPort; ///< Local Port number to Bind - int mPeerPort; ///< Peer Port number to Bind - const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER + int mLocalPort; ///< Local Port number to Bind + int mPeerPort; ///< Peer Port number to Bind + const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER + + struct sockaddr_in mLocalIPv4Addr; ///< Local IPv4 Address struct + struct sockaddr_in mPeerIPv4Addr; ///< Peer IPv4 Address struct - struct sockaddr_in mLocalIPv4Addr; ///< Local IPv4 Address struct - struct sockaddr_in mPeerIPv4Addr; ///< Peer IPv4 Address struct + /// Smart Pointer to RingBuffer to read (for SENDER) or write (for RECEIVER) + std::tr1::shared_ptr mRingBuffer; - /// Smart Pointer to RingBuffer to read (for SENDER) or write (for RECEIVER) - std::tr1::shared_ptr mRingBuffer; - - /// Boolean stop the execution of the thread - volatile bool mStopped; - /// Boolean to indicate if the RECEIVER is waiting to obtain peer address - volatile bool mHasPeerAddress; - /// Boolean that indicates if a packet was received - volatile bool mHasPacketsToReceive; + /// Boolean stop the execution of the thread + volatile bool mStopped; + /// Boolean to indicate if the RECEIVER is waiting to obtain peer address + volatile bool mHasPeerAddress; + /// Boolean that indicates if a packet was received + volatile bool mHasPacketsToReceive; - /// Number of clients running to check for ports already used - /// \note Unimplemented, try to find another way to check for used ports - static int sClientsRunning; + /// Number of clients running to check for ports already used + /// \note Unimplemented, try to find another way to check for used ports + static int sClientsRunning; - size_t mAudioPacketSize; ///< Packet audio part size + size_t mAudioPacketSize; ///< Packet audio part size - PacketHeader* mHeader; ///< Packet Header + PacketHeader* mHeader; ///< Packet Header }; #endif diff --git a/src/JMess.cpp b/src/JMess.cpp new file mode 100644 index 0000000..b49008c --- /dev/null +++ b/src/JMess.cpp @@ -0,0 +1,476 @@ +/* + JMess: A simple utility so save your jack-audio mess. + + Copyright (C) 2007-2010 Juan-Pablo Caceres. + + 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. +*/ + + +/* + * JMess.cpp + */ + +#include "JMess.h" +#include "JackTrip.h" +#include "jacktrip_globals.h" +#include + + +//------------------------------------------------------------------------------- +/*! \brief Constructs a JMess object that has a jack client. + * + */ +//------------------------------------------------------------------------------- +JMess::JMess() +{ + //Open a client connection to the JACK server. Starting a + //new server only to list its ports seems pointless, so we + //specify JackNoStartServer. + mClient = jack_client_open ("lsp", JackNoStartServer, &mStatus); + if (mClient == NULL) { + if (mStatus & JackServerFailed) { + cerr << "JACK server not running" << endl; + } else { + cerr << "jack_client_open() failed, " + << "status = 0x%2.0x\n" << mStatus << endl; + } + exit(1); + } +} + + +//------------------------------------------------------------------------------- +/*! \brief Distructor closes the jmess jack audio client. + * + */ +//------------------------------------------------------------------------------- +JMess::~JMess() +{ + if (jack_client_close(mClient)) + cerr << "ERROR: Could not close the hidden jmess jack client." << endl; +} + + +//------------------------------------------------------------------------------- +/*! \brief Write an XML file with the name specified at xmlOutFile. + * + */ +//------------------------------------------------------------------------------- +void JMess::writeOutput(QString xmlOutFile) +{ + // QDomDocument jmess_xml; QDomElement root; + // QDomElement connection; QDomElement output; + // QDomElement input; QDomText output_name; + // QDomText input_name; + + // QVector OutputInput(2); + + // this->setConnectedPorts(); + + // root = jmess_xml.createElement("jmess"); + // for (QVector >::iterator it = mConnectedPorts.begin(); + // it != mConnectedPorts.end(); ++it) { + // OutputInput = *it; + // //cout << "Output ===> " < " <> answer; + // } + // } + // else { + // answer = "yes"; + // } + + // if (answer == "yes") { + // if (!file.open(QIODevice::WriteOnly)) { + // cerr << "Cannot open file for writing: " + // << qPrintable(file.errorString()) << endl; + // exit(1); + // } + + // QTextStream out(&file); + // jmess_xml.save(out, Indent); + // cout << qPrintable(xmlOutFile) << " written." << endl; + // } +} + + +//------------------------------------------------------------------------------- +/*! \brief Set list of ouput ports that have connections. + * + */ +//------------------------------------------------------------------------------- +void JMess::setConnectedPorts() +{ + mConnectedPorts.clear(); + + const char **ports, **connections; //vector of ports and connections + QVector OutputInput(2); //helper variable + + //Get active output ports. + ports = jack_get_ports (mClient, NULL, NULL, JackPortIsOutput); + + for (unsigned int out_i = 0; ports[out_i]; ++out_i) { + if ((connections = jack_port_get_all_connections + (mClient, jack_port_by_name(mClient, ports[out_i]))) != 0) { + for (unsigned int in_i = 0; connections[in_i]; ++in_i) { + OutputInput[0] = ports[out_i]; + // cout << "Output ===> " < " << qPrintable(OutputInput[1]) << endl; + mConnectedPorts.append(OutputInput); + } + } + } + + free(ports); +} +//******************************************************************************* +void JMess::connectSpawnedPorts(int nChans, int hubPatch) +// called from UdpMasterListener::connectMesh +{ + QString IPS[gMAX_WAIRS]; + int ctr = 0; + + const char **ports, **connections; //vector of ports and connections + QVector OutputInput(2); //helper variable + + //Get active output ports. + ports = jack_get_ports (mClient, NULL, NULL, JackPortIsOutput); + + for (unsigned int out_i = 0; ports[out_i]; ++out_i) { + // qDebug() << QString(ports[out_i]); + bool systemPort = QString(ports[out_i]).contains(QString("system")); + + QString str = QString(ports[out_i]); + // for example "171.64.197.121:receive_1" + QString s = str.section(':', 0, 0); + // qDebug() << s << systemPort; + // for example "171.64.197.121" + + bool newOne = !systemPort; + for (int i = 0; i OutputInput(2); + + this->setConnectedPorts(); + + for (QVector >::iterator it = mConnectedPorts.begin(); + it != mConnectedPorts.end(); ++it) { + OutputInput = *it; + + if (jack_disconnect(mClient, OutputInput[0].toLatin1(), OutputInput[1].toLatin1())) { + cerr << "WARNING: port: " << qPrintable(OutputInput[0]) + << "and port: " << qPrintable(OutputInput[1]) + << " could not be disconnected.\n"; + } + } + +} + + +//------------------------------------------------------------------------------- +/*! \brief Parse the XML input file. + * + * Returns 0 on success, or 1 if the file has an incorrect format or cannot + * read the file. + */ +//------------------------------------------------------------------------------- +int JMess::parseXML(QString xmlInFile) +{ + // mPortsToConnect.clear(); + // QString errorStr; + // int errorLine; + // int errorColumn; + + // QFile file(xmlInFile); + // if (!file.open(QIODevice::ReadOnly)) { + // cerr << "Cannot open file for reading: " + // << qPrintable(file.errorString()) << endl; + // return 1; + // } + + // QDomDocument doc; + // if (!doc.setContent(&file, true, &errorStr, &errorLine, + // &errorColumn)) { + // cerr << "===================================================\n" + // << "Error parsing XML input file:\n" + // << "Parse error at line " << errorLine + // << ", column " << errorColumn << "\n" + // << qPrintable(errorStr) << "\n" + // << "===================================================\n"; + // return 1; + // } + + // QDomElement jmess = doc.documentElement(); + // if (jmess.tagName() != "jmess") { + // cerr << "Error: Root tag should be : " + // << qPrintable(jmess.tagName()) << endl; + // return 1; + // } + + + // QVector OutputInput(2); + // //First check for tag + // for(QDomNode n_cntn = jmess.firstChild(); + // !n_cntn.isNull(); n_cntn = n_cntn.nextSibling()) { + // QDomElement cntn = n_cntn.toElement(); + // if (cntn.tagName() == "connection") { + // //Now check for ouput & input tag + // for(QDomNode n_sck = cntn.firstChild(); + // !n_sck.isNull(); n_sck = n_sck.nextSibling()) { + // QDomElement sck = n_sck.toElement(); + // //cout << qPrintable(sck.tagName()) << endl; + // //cout << qPrintable(sck.text()) << endl; + // if (sck.tagName() == "output") { + // OutputInput[0] = sck.text(); + // } + // else if (sck.tagName() == "input") { + // OutputInput[1] = sck.text(); + // } + // } + // mPortsToConnect.append(OutputInput); + // } + // } + + return 0; + +} + + +//------------------------------------------------------------------------------- +/*! \brief Connect ports specified in input XML file xmlInFile + * + */ +//------------------------------------------------------------------------------- +void JMess::connectPorts(QString xmlInFile) +{ + QVector OutputInput(2); + + // if ( !(this->parseXML(xmlInFile)) ) { + // for (QVector >::iterator it = mPortsToConnect.begin(); + // it != mPortsToConnect.end(); ++it) { + // OutputInput = *it; + + // if (jack_connect(mClient, OutputInput[0].toLatin1(), OutputInput[1].toLatin1())) { + // //Display a warining only if the error is not because the ports are already + // //connected, in case the program doesn't display anyting. + // if (EEXIST != + // jack_connect(mClient, OutputInput[0].toLatin1(), OutputInput[1].toLatin1())) { + // cerr << "WARNING: port: " << qPrintable(OutputInput[0]) + // << "and port: " << qPrintable(OutputInput[1]) + // << " could not be connected.\n"; + // } + // } + // } + // } + +} + diff --git a/src/JMess.h b/src/JMess.h new file mode 100644 index 0000000..48d5b44 --- /dev/null +++ b/src/JMess.h @@ -0,0 +1,91 @@ +/* + JMess: A simple utility so save your jack-audio mess. + + Copyright (C) 2007-2010 Juan-Pablo Caceres. + + 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. +*/ + + +/* + * JMess.h + */ + +#ifndef __JMESS_H +#define __JMESS_H + +#include +#include +#include + +#include +#include +#include +//#include +//#include +//#include +//#include + +#include + +using namespace std; + +const int Indent = 2; + +//------------------------------------------------------------------------------- +/*! \brief Class to save and load all jack client connections. + * + * Saves an XML file with all the current jack connections. This same file can + * be loaded to connect evrything again. The XML file can also be edited. + * + * Has also an option to disconnect all the clients. + */ +//------------------------------------------------------------------------------- +class JMess { + +public: + JMess(); + virtual ~JMess(); + + void disconnectAll(); + void writeOutput(QString xmlOutFile); + void connectPorts(QString xmlInFile); + void setConnectedPorts(); + /// \brief Cross connect ports between net combs, -l LAIR mode + void connectSpawnedPorts(int nChans, int hubPatch); + void connectTUB(int nChans); + +private: + int parseXML(QString xmlInFile); + + jack_client_t *mClient; //Class client + jack_status_t mStatus; //Class client status + + //Vectors of Connected Ports and Ports to connects + //This are a matrix (Nx2) of string like this: + //OuputPort1 InputPort1 + // ... + //OuputPortN InputPortN + QVector > mConnectedPorts; + QVector > mPortsToConnect; +}; +#endif diff --git a/src/JackAudioInterface.cpp b/src/JackAudioInterface.cpp index 3c8c0ca..1e16e19 100644 --- a/src/JackAudioInterface.cpp +++ b/src/JackAudioInterface.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -55,7 +55,6 @@ using std::cout; using std::endl; - // sJackMutex definition QMutex JackAudioInterface::sJackMutex; @@ -63,17 +62,26 @@ QMutex JackAudioInterface::sJackMutex; //******************************************************************************* JackAudioInterface::JackAudioInterface(JackTrip* jacktrip, int NumInChans, int NumOutChans, + #ifdef WAIR // wair + int NumNetRevChans, + #endif // endwhere AudioInterface::audioBitResolutionT AudioBitResolution, - const char* ClienName) : -AudioInterface(jacktrip, - NumInChans, NumOutChans, - AudioBitResolution), -mNumInChans(NumInChans), mNumOutChans(NumOutChans), -//mAudioBitResolution(AudioBitResolution*8), -mBitResolutionMode(AudioBitResolution), -mClient(NULL), -mClientName(ClienName), -mJackTrip(jacktrip) + const char* ClientName) : + AudioInterface(jacktrip, + NumInChans, NumOutChans, + #ifdef WAIR // wair + NumNetRevChans, + #endif // endwhere + AudioBitResolution), + mNumInChans(NumInChans), mNumOutChans(NumOutChans), + #ifdef WAIR // WAIR + mNumNetRevChans(NumNetRevChans), + #endif // endwhere + //mAudioBitResolution(AudioBitResolution*8), + mBitResolutionMode(AudioBitResolution), + mClient(NULL), + mClientName(ClientName), + mJackTrip(jacktrip) {} @@ -85,164 +93,170 @@ JackAudioInterface::~JackAudioInterface() //******************************************************************************* void JackAudioInterface::setup() { - setupClient(); - AudioInterface::setup(); - setProcessCallback(); + setupClient(); + AudioInterface::setup(); + setProcessCallback(); } //******************************************************************************* void JackAudioInterface::setupClient() -{ - const char* client_name = mClientName; - const char* server_name = NULL; - jack_options_t options = JackNoStartServer; - jack_status_t status; - - // Try to connect to the server - /// \todo Write better warning messages. This following line displays very - /// verbose message, check how to desable them. - { - QMutexLocker locker(&sJackMutex); - mClient = jack_client_open (client_name, options, &status, server_name); - } - - if (mClient == NULL) { - //fprintf (stderr, "jack_client_open() failed, " - // "status = 0x%2.0x\n", status); - if (status & JackServerFailed) { - fprintf (stderr, "Unable to connect to JACK server\n"); - //std::cerr << "ERROR: Maybe the JACK server is not running?" << std::endl; - //std::cerr << gPrintSeparator << std::endl; +{ + const char* client_name = mClientName; + const char* server_name = NULL; + // was jack_options_t options = JackNoStartServer; + // and then jack_options_t options = JackLoadName; + jack_options_t options = JackNullOption; // from jackSimpleClient example + jack_status_t status; + + // Try to connect to the server + /// \todo Write better warning messages. This following line displays very + /// verbose message, check how to desable them. + { + QMutexLocker locker(&sJackMutex); +#ifndef WAIR // WAIR + mClient = jack_client_open (client_name, options, &status, server_name); +#else + mClient = jack_client_open (client_name, JackUseExactName, &status, server_name); +#endif // endwhere + } + + if (mClient == NULL) { + //fprintf (stderr, "jack_client_open() failed, " + // "status = 0x%2.0x\n", status); + if (status & JackServerFailed) { + fprintf (stderr, "Unable to connect to JACK server\n"); + //std::cerr << "ERROR: Maybe the JACK server is not running?" << std::endl; + //std::cerr << gPrintSeparator << std::endl; + } + //std::exit(1); + throw std::runtime_error("Maybe the JACK server is not running?"); + } + if (status & JackServerStarted) { + fprintf (stderr, "JACK server started\n"); + } + if (status & JackNameNotUnique) { + client_name = jack_get_client_name(mClient); + fprintf (stderr, "unique name `%s' assigned\n", client_name); } - //std::exit(1); - throw std::runtime_error("Maybe the JACK server is not running?"); - } - if (status & JackServerStarted) { - fprintf (stderr, "JACK server started\n"); - } - if (status & JackNameNotUnique) { - client_name = jack_get_client_name(mClient); - fprintf (stderr, "unique name `%s' assigned\n", client_name); - } - // Set function to call if Jack shuts down - jack_on_shutdown (mClient, this->jackShutdown, 0); + // Set function to call if Jack shuts down + jack_on_shutdown (mClient, this->jackShutdown, 0); - // Create input and output channels - createChannels(); + // Create input and output channels + createChannels(); - // Buffer size member - mNumFrames = getBufferSizeInSamples(); + // Buffer size member + mNumFrames = getBufferSizeInSamples(); - // Initialize Buffer array to read and write audio - mInBuffer.resize(mNumInChans); - mOutBuffer.resize(mNumOutChans); + // Initialize Buffer array to read and write audio + mInBuffer.resize(mNumInChans); + mOutBuffer.resize(mNumOutChans); } //******************************************************************************* void JackAudioInterface::createChannels() { - //Create Input Ports - mInPorts.resize(mNumInChans); - for (int i = 0; i < mNumInChans; i++) + //Create Input Ports + mInPorts.resize(mNumInChans); + for (int i = 0; i < mNumInChans; i++) { - QString inName; - QTextStream (&inName) << "send_" << i+1; - mInPorts[i] = jack_port_register (mClient, inName.toLatin1(), - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsInput, 0); + QString inName; + QTextStream (&inName) << "send_" << i+1; + mInPorts[i] = jack_port_register (mClient, inName.toLatin1(), + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); } - //Create Output Ports - mOutPorts.resize(mNumOutChans); - for (int i = 0; i < mNumInChans; i++) + //Create Output Ports + mOutPorts.resize(mNumOutChans); + for (int i = 0; i < mNumInChans; i++) { - QString outName; - QTextStream (&outName) << "receive_" << i+1; - mOutPorts[i] = jack_port_register (mClient, outName.toLatin1(), - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput, 0); + QString outName; + QTextStream (&outName) << "receive_" << i+1; + mOutPorts[i] = jack_port_register (mClient, outName.toLatin1(), + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); } } //******************************************************************************* -uint32_t JackAudioInterface::getSampleRate() const +uint32_t JackAudioInterface::getSampleRate() const { - return jack_get_sample_rate(mClient); + return jack_get_sample_rate(mClient); } //******************************************************************************* -uint32_t JackAudioInterface::getBufferSizeInSamples() const +uint32_t JackAudioInterface::getBufferSizeInSamples() const { - return jack_get_buffer_size(mClient); + return jack_get_buffer_size(mClient); } //******************************************************************************* size_t JackAudioInterface::getSizeInBytesPerChannel() const { - return (getBufferSizeInSamples() * getAudioBitResolution()/8); + return (getBufferSizeInSamples() * getAudioBitResolution()/8); } //******************************************************************************* void JackAudioInterface::setProcessCallback() { - std::cout << "Setting JACK Process Callback..." << std::endl; - if ( int code = - jack_set_process_callback(mClient, JackAudioInterface::wrapperProcessCallback, this) - ) + std::cout << "Setting JACK Process Callback..." << std::endl; + if ( int code = + jack_set_process_callback(mClient, JackAudioInterface::wrapperProcessCallback, this) + ) { - //std::cerr << "Could not set the process callback" << std::endl; - //return(code); - (void) code; // to avoid compiler warnings - throw std::runtime_error("Could not set the Jack process callback"); - //std::exit(1); + //std::cerr << "Could not set the process callback" << std::endl; + //return(code); + (void) code; // to avoid compiler warnings + throw std::runtime_error("Could not set the Jack process callback"); + //std::exit(1); } - std::cout << "SUCCESS" << std::endl; - std::cout << gPrintSeparator << std::endl; - //return(0); + std::cout << "SUCCESS" << std::endl; + std::cout << gPrintSeparator << std::endl; + //return(0); } //******************************************************************************* int JackAudioInterface::startProcess() const { - //Tell the JACK server that we are ready to roll. Our - //process() callback will start running now. - if ( int code = (jack_activate(mClient)) ) + //Tell the JACK server that we are ready to roll. Our + //process() callback will start running now. + if ( int code = (jack_activate(mClient)) ) { - std::cerr << "Cannot activate client" << std::endl; - return(code); + std::cerr << "Cannot activate client" << std::endl; + return(code); } - return(0); + return(0); } //******************************************************************************* int JackAudioInterface::stopProcess() const { - QMutexLocker locker(&sJackMutex); - int code = (jack_client_close(mClient)); - if ( code != 0 ) + QMutexLocker locker(&sJackMutex); + int code = (jack_client_close(mClient)); + if ( code != 0 ) { - std::cerr << "Cannot disconnect client" << std::endl; - return(code); + std::cerr << "Cannot disconnect client" << std::endl; + return(code); } - return(0); + return(0); } //******************************************************************************* void JackAudioInterface::jackShutdown (void*) { - //std::cout << "The Jack Server was shut down!" << std::endl; - throw std::runtime_error("The Jack Server was shut down!"); - //std::cout << "Exiting program..." << std::endl; - //std::exit(1); + //std::cout << "The Jack Server was shut down!" << std::endl; + throw std::runtime_error("The Jack Server was shut down!"); + //std::cout << "Exiting program..." << std::endl; + //std::exit(1); } @@ -250,80 +264,80 @@ void JackAudioInterface::jackShutdown (void*) //******************************************************************************* int JackAudioInterface::processCallback(jack_nframes_t nframes) { - // Get input and output buffers from JACK - //------------------------------------------------------------------- - for (int i = 0; i < mNumInChans; i++) { - // Input Ports are READ ONLY - mInBuffer[i] = (sample_t*) jack_port_get_buffer(mInPorts[i], nframes); - } - for (int i = 0; i < mNumOutChans; i++) { - // Output Ports are WRITABLE - mOutBuffer[i] = (sample_t*) jack_port_get_buffer(mOutPorts[i], nframes); - } - //------------------------------------------------------------------- - // TEST: Loopback - // To test, uncomment and send audio to client input. The same audio - // should come out as output in the first channel - //memcpy (mOutBuffer[0], mInBuffer[0], sizeof(sample_t) * nframes); - //memcpy (mOutBuffer[1], mInBuffer[1], sizeof(sample_t) * nframes); - //------------------------------------------------------------------- - - AudioInterface::callback(mInBuffer, mOutBuffer, nframes); - return 0; + // Get input and output buffers from JACK + //------------------------------------------------------------------- + for (int i = 0; i < mNumInChans; i++) { + // Input Ports are READ ONLY + mInBuffer[i] = (sample_t*) jack_port_get_buffer(mInPorts[i], nframes); + } + for (int i = 0; i < mNumOutChans; i++) { + // Output Ports are WRITABLE + mOutBuffer[i] = (sample_t*) jack_port_get_buffer(mOutPorts[i], nframes); + } + //------------------------------------------------------------------- + // TEST: Loopback + // To test, uncomment and send audio to client input. The same audio + // should come out as output in the first channel + //memcpy (mOutBuffer[0], mInBuffer[0], sizeof(sample_t) * nframes); + //memcpy (mOutBuffer[1], mInBuffer[1], sizeof(sample_t) * nframes); + //------------------------------------------------------------------- + + AudioInterface::callback(mInBuffer, mOutBuffer, nframes); + return 0; } //******************************************************************************* -int JackAudioInterface::wrapperProcessCallback(jack_nframes_t nframes, void *arg) +int JackAudioInterface::wrapperProcessCallback(jack_nframes_t nframes, void *arg) { - return static_cast(arg)->processCallback(nframes); + return static_cast(arg)->processCallback(nframes); } //******************************************************************************* void JackAudioInterface::connectDefaultPorts() { - const char** ports; - - // Get physical output (capture) ports - if ( (ports = - jack_get_ports (mClient, NULL, NULL, - JackPortIsPhysical | JackPortIsOutput)) == NULL) - { - cout << "WARING: Cannot find any physical capture ports" << endl; - } - else - { - // Connect capure ports to jacktrip send - for (int i = 0; i < mNumInChans; i++) + const char** ports; + + // Get physical output (capture) ports + if ( (ports = + jack_get_ports (mClient, NULL, NULL, + JackPortIsPhysical | JackPortIsOutput)) == NULL) { - // Check that we don't run out of capture ports - if ( ports[i] != NULL ) { - jack_connect(mClient, ports[i], jack_port_name(mInPorts[i])); - } + cout << "WARNING: Cannot find any physical capture ports" << endl; } - std::free(ports); - } - - // Get physical input (playback) ports - if ( (ports = - jack_get_ports (mClient, NULL, NULL, - JackPortIsPhysical | JackPortIsInput)) == NULL) - { - cout << "WARING: Cannot find any physical playback ports" << endl; - } - else - { - // Connect playback ports to jacktrip receive - for (int i = 0; i < mNumOutChans; i++) + else { - // Check that we don't run out of capture ports - if ( ports[i] != NULL ) { - jack_connect(mClient, jack_port_name(mOutPorts[i]), ports[i]); - } + // Connect capure ports to jacktrip send + for (int i = 0; i < mNumInChans; i++) + { + // Check that we don't run out of capture ports + if ( ports[i] != NULL ) { + jack_connect(mClient, ports[i], jack_port_name(mInPorts[i])); + } + } + std::free(ports); + } + + // Get physical input (playback) ports + if ( (ports = + jack_get_ports (mClient, NULL, NULL, + JackPortIsPhysical | JackPortIsInput)) == NULL) + { + cout << "WARNING: Cannot find any physical playback ports" << endl; + } + else + { + // Connect playback ports to jacktrip receive + for (int i = 0; i < mNumOutChans; i++) + { + // Check that we don't run out of capture ports + if ( ports[i] != NULL ) { + jack_connect(mClient, jack_port_name(mOutPorts[i]), ports[i]); + } + } + std::free(ports); } - std::free(ports); - } } @@ -400,20 +414,20 @@ int JackAudioInterface::processCallback(jack_nframes_t nframes) // -------------------------------- computeNetworkProcessToNetwork(); */ - ///************PROTORYPE FOR CELT************************** - ///******************************************************** - /* +///************PROTORYPE FOR CELT************************** +///******************************************************** +/* CELTMode* mode; int* error; mode = celt_mode_create(48000, 2, 64, error); */ - //celt_mode_create(48000, 2, 64, NULL); - //unsigned char* compressed; - //CELTEncoder* celtEncoder; - //celt_encode_float(celtEncoder, mInBuffer, NULL, compressed, ); +//celt_mode_create(48000, 2, 64, NULL); +//unsigned char* compressed; +//CELTEncoder* celtEncoder; +//celt_encode_float(celtEncoder, mInBuffer, NULL, compressed, ); - ///******************************************************** - ///******************************************************** +///******************************************************** +///******************************************************** // return 0; //} diff --git a/src/JackAudioInterface.h b/src/JackAudioInterface.h index 39ddd0c..d4b72e8 100644 --- a/src/JackAudioInterface.h +++ b/src/JackAudioInterface.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -65,59 +65,62 @@ class JackAudioInterface : public AudioInterface { public: - /** \brief The class constructor + /** \brief The class constructor * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) * \param NumInChans Number of Input Channels * \param NumOutChans Number of Output Channels * \param AudioBitResolution Audio Sample Resolutions in bits * \param ClientName Client name in Jack */ - JackAudioInterface(JackTrip* jacktrip, - int NumInChans, int NumOutChans, - AudioInterface::audioBitResolutionT AudioBitResolution = AudioInterface::BIT16, - const char* ClientName = "JackTrip"); - /// \brief The class destructor - virtual ~JackAudioInterface(); - - /// \brief Setup the client - virtual void setup(); - /** \brief Tell the JACK server that we are ready to roll. The + JackAudioInterface(JackTrip* jacktrip, + int NumInChans, int NumOutChans, + #ifdef WAIR // wair + int NumNetRevChans, + #endif // endwhere + AudioInterface::audioBitResolutionT AudioBitResolution = AudioInterface::BIT16, + const char* ClientName = "JackTrip"); + /// \brief The class destructor + virtual ~JackAudioInterface(); + + /// \brief Setup the client + virtual void setup(); + /** \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; - /** \brief Stops the process-callback thread + virtual int startProcess() const; + /** \brief Stops the process-callback thread * \return 0 on success, otherwise a non-zero error code */ - virtual int stopProcess() const; - /// \brief Connect the default ports, capture to sends, and receives to playback - void connectDefaultPorts(); - - //--------------SETTERS--------------------------------------------- - /// \brief Set Client Name to something different that the default (JackTrip) - virtual void setClientName(const char* ClientName) - { mClientName = ClientName; } - virtual void setSampleRate(uint32_t /*sample_rate*/) - { std::cout << "WARING: Setting the Sample Rate in Jack mode has no effect." << std::endl; } - virtual void setBufferSizeInSamples(uint32_t /*buf_size*/) - { std::cout << "WARING: Setting the Sample Rate in Jack mode has no effect." << std::endl; } - //------------------------------------------------------------------ - - //--------------GETTERS--------------------------------------------- - /// \brief Get the Jack Server Sampling Rate, in samples/second - virtual uint32_t getSampleRate() const; - /// \brief Get the Jack Server Buffer Size, in samples - virtual uint32_t getBufferSizeInSamples() const; - /// \brief Get the Jack Server Buffer Size, in bytes - virtual uint32_t getBufferSizeInBytes() const - { return (getBufferSizeInSamples() * getAudioBitResolution()/8); } - /// \brief Get size of each audio per channel, in bytes - virtual size_t getSizeInBytesPerChannel() const; - //------------------------------------------------------------------ + virtual int stopProcess() const; + /// \brief Connect the default ports, capture to sends, and receives to playback + void connectDefaultPorts(); + + //--------------SETTERS--------------------------------------------- + /// \brief Set Client Name to something different that the default (JackTrip) + virtual void setClientName(const char* ClientName) + { mClientName = ClientName; } + virtual void setSampleRate(uint32_t /*sample_rate*/) + { std::cout << "WARNING: Setting the Sample Rate in Jack mode has no effect." << std::endl; } + virtual void setBufferSizeInSamples(uint32_t /*buf_size*/) + { std::cout << "WARNING: Setting the Sample Rate in Jack mode has no effect." << std::endl; } + //------------------------------------------------------------------ + + //--------------GETTERS--------------------------------------------- + /// \brief Get the Jack Server Sampling Rate, in samples/second + virtual uint32_t getSampleRate() const; + /// \brief Get the Jack Server Buffer Size, in samples + virtual uint32_t getBufferSizeInSamples() const; + /// \brief Get the Jack Server Buffer Size, in bytes + virtual uint32_t getBufferSizeInBytes() const + { return (getBufferSizeInSamples() * getAudioBitResolution()/8); } + /// \brief Get size of each audio per channel, in bytes + virtual size_t getSizeInBytesPerChannel() const; + //------------------------------------------------------------------ private: - /** \brief Private method to setup a client of the Jack server. + /** \brief Private method to setup a client of the Jack server. * \exception std::runtime_error Can't start Jack * * This method is called by the class constructors. It does the following:\n @@ -125,20 +128,20 @@ private: * - Sets the shutdown process callback * - Creates the appropriate number of input and output channels */ - void setupClient(); - /// \brief Creates input and output channels in the Jack client - void createChannels(); - /** \brief JACK calls this shutdown_callback if the server ever shuts down or + void setupClient(); + /// \brief Creates input and output channels in the Jack client + void createChannels(); + /** \brief JACK calls this shutdown_callback if the server ever shuts down or * decides to disconnect the client. */ - static void jackShutdown(void*); - /** \brief Set the process callback of the member function processCallback. + static void jackShutdown(void*); + /** \brief Set the process callback of the member function processCallback. * This process will be called by the JACK server whenever there is work to be done. */ - void setProcessCallback(); - /** \brief JACK process callback - * - * This is the function to be called to process audio. This function is + void setProcessCallback(); + /** \brief JACK process callback + * + * This is the function to be called to process audio. This function is * of the type JackProcessCallback, which is defined as:\n * typedef int(* JackProcessCallback)(jack_nframes_t nframes, void *arg) * \n @@ -146,37 +149,40 @@ private: * http://jackaudio.org/files/docs/html/types_8h.html#4923142208a8e7dacf00ca7a10681d2b * for more details */ - int processCallback(jack_nframes_t nframes); - /** \brief Wrapper to cast the member processCallback to a static function pointer + int processCallback(jack_nframes_t nframes); + /** \brief Wrapper to cast the member processCallback to a static function pointer * that can be used with jack_set_process_callback * * jack_set_process_callback needs a static member function pointer. A normal * member function won't work because a this pointer is passed under the scenes. - * That's why we + * That's why we * need to cast the member funcion processCallback to the static function * wrapperProcessCallback. The callback is then set as:\n * jack_set_process_callback(mClient, JackAudioInterface::wrapperProcessCallback, * this) */ - // reference : http://article.gmane.org/gmane.comp.audio.jackit/12873 - static int wrapperProcessCallback(jack_nframes_t nframes, void *arg) ; - - int mNumInChans;///< Number of Input Channels - int mNumOutChans; ///< Number of Output Channels - int mNumFrames; ///< Buffer block size, in samples - //int mAudioBitResolution; ///< Bit resolution in audio samples - AudioInterface::audioBitResolutionT mBitResolutionMode; ///< Bit resolution (audioBitResolutionT) mode - - jack_client_t* mClient; ///< Jack Client - const char* mClientName; ///< Jack Client Name - QVarLengthArray mInPorts; ///< Vector of Input Ports (Channels) - QVarLengthArray mOutPorts; ///< Vector of Output Ports (Channels) - QVarLengthArray mInBuffer; ///< Vector of Input buffers/channel read from JACK - QVarLengthArray mOutBuffer; ///< Vector of Output buffer/channel to write to JACK - size_t mSizeInBytesPerChannel; ///< Size in bytes per audio channel - QVector mProcessPlugins; ///< Vector of ProcesPlugins - JackTrip* mJackTrip; ///< JackTrip mediator class - static QMutex sJackMutex; ///< Mutex to make thread safe jack functions that are not + // reference : http://article.gmane.org/gmane.comp.audio.jackit/12873 + static int wrapperProcessCallback(jack_nframes_t nframes, void *arg) ; + + int mNumInChans;///< Number of Input Channels + int mNumOutChans; ///< Number of Output Channels +#ifdef WAIR // WAIR + int mNumNetRevChans; ///< Number of Network Audio Channels (network comb filters +#endif // endwhere + int mNumFrames; ///< Buffer block size, in samples + //int mAudioBitResolution; ///< Bit resolution in audio samples + AudioInterface::audioBitResolutionT mBitResolutionMode; ///< Bit resolution (audioBitResolutionT) mode + + jack_client_t* mClient; ///< Jack Client + const char* mClientName; ///< Jack Client Name + QVarLengthArray mInPorts; ///< Vector of Input Ports (Channels) + QVarLengthArray mOutPorts; ///< Vector of Output Ports (Channels) + QVarLengthArray mInBuffer; ///< Vector of Input buffers/channel read from JACK + QVarLengthArray mOutBuffer; ///< Vector of Output buffer/channel to write to JACK + size_t mSizeInBytesPerChannel; ///< Size in bytes per audio channel + QVector mProcessPlugins; ///< Vector of ProcesPlugins + JackTrip* mJackTrip; ///< JackTrip mediator class + static QMutex sJackMutex; ///< Mutex to make thread safe jack functions that are not }; diff --git a/src/JackTrip.cpp b/src/JackTrip.cpp index 0ae9f39..85d91cd 100644 --- a/src/JackTrip.cpp +++ b/src/JackTrip.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -45,11 +45,11 @@ #endif #include -//#include // for usleep, sleep #include #include #include +#include #include #include @@ -59,500 +59,618 @@ using std::cout; using std::endl; //its purpose is to close the app when control c is hit by the user in rtaudio/asio4all mode #if defined __WIN_32__ void sigint_handler(int sig) - { - exit(0); - } +{ + exit(0); +} #endif //******************************************************************************* JackTrip::JackTrip(jacktripModeT JacktripMode, - dataProtocolT DataProtocolType, - int NumChans, - int BufferQueueLength, - unsigned int redundancy, - AudioInterface::audioBitResolutionT AudioBitResolution, - DataProtocol::packetHeaderTypeT PacketHeaderType, - underrunModeT UnderRunMode, - int receiver_bind_port, int sender_bind_port, - int receiver_peer_port, int sender_peer_port) : - mJackTripMode(JacktripMode), - mDataProtocol(DataProtocolType), - mPacketHeaderType(PacketHeaderType), - mAudiointerfaceMode(JackTrip::JACK), - mNumChans(NumChans), - mBufferQueueLength(BufferQueueLength), - mSampleRate(gDefaultSampleRate), - mAudioBufferSize(gDefaultBufferSizeInSamples), - mAudioBitResolution(AudioBitResolution), - mDataProtocolSender(NULL), - mDataProtocolReceiver(NULL), - mAudioInterface(NULL), - mPacketHeader(NULL), - mUnderRunMode(UnderRunMode), - mSendRingBuffer(NULL), - mReceiveRingBuffer(NULL), - mReceiverBindPort(receiver_bind_port), - mSenderPeerPort(sender_peer_port), - mSenderBindPort(sender_bind_port), - mReceiverPeerPort(receiver_peer_port), - mTcpServerPort(4464), - mRedundancy(redundancy), - mJackClientName("JackTrip"), - mConnectionMode(JackTrip::NORMAL), - mReceivedConnection(false), - mTcpConnectionError(false), - mStopped(false) + dataProtocolT DataProtocolType, + int NumChans, + #ifdef WAIR // WAIR + int NumNetRevChans, + #endif // endwhere + int BufferQueueLength, + unsigned int redundancy, + AudioInterface::audioBitResolutionT AudioBitResolution, + DataProtocol::packetHeaderTypeT PacketHeaderType, + underrunModeT UnderRunMode, + int receiver_bind_port, int sender_bind_port, + int receiver_peer_port, int sender_peer_port) : + mJackTripMode(JacktripMode), + mDataProtocol(DataProtocolType), + mPacketHeaderType(PacketHeaderType), + mAudiointerfaceMode(JackTrip::JACK), + mNumChans(NumChans), + #ifdef WAIR // WAIR + mNumNetRevChans(NumNetRevChans), + #endif // endwhere + mBufferQueueLength(BufferQueueLength), + mSampleRate(gDefaultSampleRate), + mDeviceID(gDefaultDeviceID), + mAudioBufferSize(gDefaultBufferSizeInSamples), + mAudioBitResolution(AudioBitResolution), + mDataProtocolSender(NULL), + mDataProtocolReceiver(NULL), + mAudioInterface(NULL), + mPacketHeader(NULL), + mUnderRunMode(UnderRunMode), + mSendRingBuffer(NULL), + mReceiveRingBuffer(NULL), + mReceiverBindPort(receiver_bind_port), + mSenderPeerPort(sender_peer_port), + mSenderBindPort(sender_bind_port), + mReceiverPeerPort(receiver_peer_port), + mTcpServerPort(4464), + mRedundancy(redundancy), + mJackClientName("JackTrip"), + mConnectionMode(JackTrip::NORMAL), + mReceivedConnection(false), + mTcpConnectionError(false), + mStopped(false), + mConnectDefaultAudioPorts(true) { - createHeader(mPacketHeaderType); + createHeader(mPacketHeaderType); } //******************************************************************************* JackTrip::~JackTrip() { - wait(); - delete mDataProtocolSender; - delete mDataProtocolReceiver; - delete mAudioInterface; - delete mPacketHeader; - delete mSendRingBuffer; - delete mReceiveRingBuffer; + wait(); + delete mDataProtocolSender; + delete mDataProtocolReceiver; + delete mAudioInterface; + delete mPacketHeader; + delete mSendRingBuffer; + delete mReceiveRingBuffer; } //******************************************************************************* -void JackTrip::setupAudio() +void JackTrip::setupAudio( + #ifdef WAIRTOMASTER // WAIR + int ID + #endif // endwhere + ) { - // Check if mAudioInterface has already been created or not - if (mAudioInterface != NULL) { // if it has been created, disconnet it from JACK and delete it - cout << "WARINING: JackAudio interface was setup already:" << endl; - cout << "It will be errased and setup again." << endl; - cout << gPrintSeparator << endl; - closeAudio(); - } + // Check if mAudioInterface has already been created or not + if (mAudioInterface != NULL) { // if it has been created, disconnet it from JACK and delete it + cout << "WARINING: JackAudio interface was setup already:" << endl; + cout << "It will be errased and setup again." << endl; + cout << gPrintSeparator << endl; + closeAudio(); + } - // Create AudioInterface Client Object - if ( mAudiointerfaceMode == JackTrip::JACK ) { + // Create AudioInterface Client Object + if ( mAudiointerfaceMode == JackTrip::JACK ) { #ifndef __NO_JACK__ - mAudioInterface = new JackAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); - mAudioInterface->setClientName(mJackClientName); - mAudioInterface->setup(); - mSampleRate = mAudioInterface->getSampleRate(); - mAudioBufferSize = mAudioInterface->getBufferSizeInSamples(); + if (gVerboseFlag) std::cout << " JackTrip:setupAudio before new JackAudioInterface" << std::endl; + mAudioInterface = new JackAudioInterface(this, mNumChans, mNumChans, + #ifdef WAIR // wair + mNumNetRevChans, + #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; + +#endif // endwhere + + mAudioInterface->setClientName(mJackClientName); + + if (gVerboseFlag) std::cout << " JackTrip:setupAudio before mAudioInterface->setup" << std::endl; + mAudioInterface->setup(); + mSampleRate = mAudioInterface->getSampleRate(); + mDeviceID = mAudioInterface->getDeviceID(); + mAudioBufferSize = mAudioInterface->getBufferSizeInSamples(); #endif //__NON_JACK__ #ifdef __NO_JACK__ /// \todo FIX THIS REPETITION OF CODE #ifdef __RT_AUDIO__ - cout << "Warning: using non jack version, RtAudio will be used instead" << endl; - mAudioInterface = new RtAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); - mAudioInterface->setSampleRate(mSampleRate); - mAudioInterface->setBufferSizeInSamples(mAudioBufferSize); - mAudioInterface->setup(); + cout << "Warning: using non jack version, RtAudio will be used instead" << endl; + mAudioInterface = new RtAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); + mAudioInterface->setSampleRate(mSampleRate); + mAudioInterface->setDeviceID(mDeviceID); + mAudioInterface->setBufferSizeInSamples(mAudioBufferSize); + mAudioInterface->setup(); #endif #endif - } - else if ( mAudiointerfaceMode == JackTrip::RTAUDIO ) { + } + else if ( mAudiointerfaceMode == JackTrip::RTAUDIO ) { #ifdef __RT_AUDIO__ - mAudioInterface = new RtAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); - mAudioInterface->setSampleRate(mSampleRate); - mAudioInterface->setBufferSizeInSamples(mAudioBufferSize); - mAudioInterface->setup(); + mAudioInterface = new RtAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); + mAudioInterface->setSampleRate(mSampleRate); + mAudioInterface->setDeviceID(mDeviceID); + mAudioInterface->setBufferSizeInSamples(mAudioBufferSize); + mAudioInterface->setup(); #endif - } + } - std::cout << "The Sampling Rate is: " << mSampleRate << std::endl; - std::cout << gPrintSeparator << std::endl; - int AudioBufferSizeInBytes = mAudioBufferSize*sizeof(sample_t); - std::cout << "The Audio Buffer Size is: " << mAudioBufferSize << " samples" << std::endl; - std::cout << " or: " << AudioBufferSizeInBytes - << " bytes" << std::endl; - std::cout << gPrintSeparator << std::endl; - cout << "The Number of Channels is: " << mAudioInterface->getNumInputChannels() << endl; - std::cout << gPrintSeparator << std::endl; - QThread::usleep(100); + std::cout << "The Sampling Rate is: " << mSampleRate << std::endl; + std::cout << gPrintSeparator << std::endl; + int AudioBufferSizeInBytes = mAudioBufferSize*sizeof(sample_t); + std::cout << "The Audio Buffer Size is: " << mAudioBufferSize << " samples" << std::endl; + std::cout << " or: " << AudioBufferSizeInBytes + << " bytes" << std::endl; + std::cout << gPrintSeparator << std::endl; + cout << "The Number of Channels is: " << mAudioInterface->getNumInputChannels() << endl; + std::cout << gPrintSeparator << std::endl; + cout << "The RTAudio device ID is: " << mAudioInterface->getDeviceID() << endl; + std::cout << gPrintSeparator << std::endl; + QThread::usleep(100); } //******************************************************************************* void JackTrip::closeAudio() { - //mAudioInterface->close(); - if ( mAudioInterface != NULL ) { - mAudioInterface->stopProcess(); - delete mAudioInterface; - mAudioInterface = NULL; - } + //mAudioInterface->close(); + if ( mAudioInterface != NULL ) { + mAudioInterface->stopProcess(); + delete mAudioInterface; + mAudioInterface = NULL; + } } //******************************************************************************* void JackTrip::setupDataProtocol() { - // Create DataProtocol Objects - switch (mDataProtocol) { - case UDP: - std::cout << "Using UDP Protocol" << std::endl; - std::cout << gPrintSeparator << std::endl; - QThread::usleep(100); - mDataProtocolSender = new UdpDataProtocol(this, DataProtocol::SENDER, - //mSenderPeerPort, mSenderBindPort, - mSenderBindPort, mSenderPeerPort, - mRedundancy); - mDataProtocolReceiver = new UdpDataProtocol(this, DataProtocol::RECEIVER, - mReceiverBindPort, mReceiverPeerPort, - mRedundancy); - break; - case TCP: - throw std::invalid_argument("TCP Protocol is not implemented"); - break; - case SCTP: - throw std::invalid_argument("SCTP Protocol is not implemented"); - break; - default: - throw std::invalid_argument("Protocol not defined or unimplemented"); - break; - } - - // Set Audio Packet Size - //mDataProtocolSender->setAudioPacketSize - // (mAudioInterface->getSizeInBytesPerChannel() * mNumChans); - //mDataProtocolReceiver->setAudioPacketSize - // (mAudioInterface->getSizeInBytesPerChannel() * mNumChans); - mDataProtocolSender->setAudioPacketSize(getTotalAudioPacketSizeInBytes()); - mDataProtocolReceiver->setAudioPacketSize(getTotalAudioPacketSizeInBytes()); + // Create DataProtocol Objects + switch (mDataProtocol) { + case UDP: + std::cout << "Using UDP Protocol" << std::endl; + std::cout << gPrintSeparator << std::endl; + QThread::usleep(100); + mDataProtocolSender = new UdpDataProtocol(this, DataProtocol::SENDER, + //mSenderPeerPort, mSenderBindPort, + mSenderBindPort, mSenderPeerPort, + mRedundancy); + mDataProtocolReceiver = new UdpDataProtocol(this, DataProtocol::RECEIVER, + mReceiverBindPort, mReceiverPeerPort, + mRedundancy); + break; + case TCP: + throw std::invalid_argument("TCP Protocol is not implemented"); + break; + case SCTP: + throw std::invalid_argument("SCTP Protocol is not implemented"); + break; + default: + throw std::invalid_argument("Protocol not defined or unimplemented"); + break; + } + + // Set Audio Packet Size + //mDataProtocolSender->setAudioPacketSize + // (mAudioInterface->getSizeInBytesPerChannel() * mNumChans); + //mDataProtocolReceiver->setAudioPacketSize + // (mAudioInterface->getSizeInBytesPerChannel() * mNumChans); + mDataProtocolSender->setAudioPacketSize(getTotalAudioPacketSizeInBytes()); + mDataProtocolReceiver->setAudioPacketSize(getTotalAudioPacketSizeInBytes()); } //******************************************************************************* void JackTrip::setupRingBuffers() { - // Create RingBuffers with the apprioprate size - /// \todo Make all this operations cleaner - //int total_audio_packet_size = getTotalAudioPacketSizeInBytes(); - int slot_size = getRingBuffersSlotSize(); - - switch (mUnderRunMode) { - case WAVETABLE: - mSendRingBuffer = new RingBufferWavetable(slot_size, - gDefaultOutputQueueLength); - mReceiveRingBuffer = new RingBufferWavetable(slot_size, - mBufferQueueLength); - /* + // Create RingBuffers with the apprioprate size + /// \todo Make all this operations cleaner + //int total_audio_packet_size = getTotalAudioPacketSizeInBytes(); + int slot_size = getRingBuffersSlotSize(); + + switch (mUnderRunMode) { + case WAVETABLE: + mSendRingBuffer = new RingBufferWavetable(slot_size, + gDefaultOutputQueueLength); + mReceiveRingBuffer = new RingBufferWavetable(slot_size, + mBufferQueueLength); + /* mSendRingBuffer = new RingBufferWavetable(mAudioInterface->getSizeInBytesPerChannel() * mNumChans, gDefaultOutputQueueLength); mReceiveRingBuffer = new RingBufferWavetable(mAudioInterface->getSizeInBytesPerChannel() * mNumChans, mBufferQueueLength); */ - - break; - case ZEROS: - mSendRingBuffer = new RingBuffer(slot_size, - gDefaultOutputQueueLength); - mReceiveRingBuffer = new RingBuffer(slot_size, - mBufferQueueLength); - /* + + break; + case ZEROS: + mSendRingBuffer = new RingBuffer(slot_size, + gDefaultOutputQueueLength); + mReceiveRingBuffer = new RingBuffer(slot_size, + mBufferQueueLength); + /* mSendRingBuffer = new RingBuffer(mAudioInterface->getSizeInBytesPerChannel() * mNumChans, gDefaultOutputQueueLength); mReceiveRingBuffer = new RingBuffer(mAudioInterface->getSizeInBytesPerChannel() * mNumChans, mBufferQueueLength); */ - break; - default: - throw std::invalid_argument("Underrun Mode undefined"); - break; - } + break; + default: + throw std::invalid_argument("Underrun Mode undefined"); + break; + } } //******************************************************************************* void JackTrip::setPeerAddress(const char* PeerHostOrIP) { - mPeerAddress = PeerHostOrIP; + mPeerAddress = PeerHostOrIP; } //******************************************************************************* void JackTrip::appendProcessPlugin(ProcessPlugin* plugin) { - mProcessPlugins.append(plugin); - //mAudioInterface->appendProcessPlugin(plugin); + mProcessPlugins.append(plugin); + //mAudioInterface->appendProcessPlugin(plugin); } //******************************************************************************* -void JackTrip::startProcess() throw(std::invalid_argument) +void JackTrip::startProcess( + #ifdef WAIRTOMASTER // 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) { - perror("signal"); - exit(1); - } -#endif - // Check if ports are already binded by another process on this machine - // ------------------------------------------------------------------ - checkIfPortIsBinded(mReceiverBindPort); - checkIfPortIsBinded(mSenderBindPort); - // Set all classes and parameters - // ------------------------------ - setupAudio(); - createHeader(mPacketHeaderType); - setupDataProtocol(); - setupRingBuffers(); - // Connect Signals and Slots - // ------------------------- - QObject::connect(mPacketHeader, SIGNAL(signalError(const char*)), - this, SLOT(slotStopProcesses()), Qt::QueuedConnection); - QObject::connect(mDataProtocolReceiver, SIGNAL(signalReceivedConnectionFromPeer()), - this, SLOT(slotReceivedConnectionFromPeer()), - Qt::QueuedConnection); - QObject::connect(this, SIGNAL(signalUdpTimeOut()), - this, SLOT(slotStopProcesses()), Qt::QueuedConnection); - - //QObject::connect(mDataProtocolSender, SIGNAL(signalError(const char*)), - // this, SLOT(slotStopProcesses()), Qt::QueuedConnection); - //QObject::connect(mDataProtocolReceiver, SIGNAL(signalError(const char*)), - // this, SLOT(slotStopProcesses()), Qt::QueuedConnection); - - // Start the threads for the specific mode - // --------------------------------------- - switch ( mJackTripMode ) - { - case CLIENT : - clientStart(); - break; - case SERVER : - serverStart(); - break; - case CLIENTTOPINGSERVER : - if ( clientPingToServerStart() == -1 ) { // if error on server start (-1) we return inmediatly - mTcpConnectionError = true; - slotStopProcesses(); - return; - } - break; - case SERVERPINGSERVER : - if ( serverStart(true) == -1 ) { // if error on server start (-1) we return inmediatly - slotStopProcesses(); - return; + perror("signal"); + exit(1); } - break; - default: - throw std::invalid_argument("Jacktrip Mode undefined"); - break; - } +#endif + // Check if ports are already binded by another process on this machine + // ------------------------------------------------------------------ + if (gVerboseFlag) std::cout << "step 1" << std::endl; - // Start Threads - mAudioInterface->startProcess(); + 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); +#endif + checkIfPortIsBinded(mReceiverBindPort); + if (gVerboseFlag) std::cout << " JackTrip:startProcess before checkIfPortIsBinded(mSenderBindPort)" << std::endl; + checkIfPortIsBinded(mSenderBindPort); + // Set all classes and parameters + // ------------------------------ + if (gVerboseFlag) std::cout << " JackTrip:startProcess before setupAudio" << std::endl; + setupAudio( + #ifdef WAIRTOMASTER // wair + ID + #endif // endwhere + ); + //cc redundant with instance creator createHeader(mPacketHeaderType); next line fixme + createHeader(mPacketHeaderType); + setupDataProtocol(); + setupRingBuffers(); + // Connect Signals and Slots + // ------------------------- + QObject::connect(mPacketHeader, SIGNAL(signalError(const char*)), + this, SLOT(slotStopProcesses()), Qt::QueuedConnection); + QObject::connect(mDataProtocolReceiver, SIGNAL(signalReceivedConnectionFromPeer()), + this, SLOT(slotReceivedConnectionFromPeer()), + Qt::QueuedConnection); + QObject::connect(this, SIGNAL(signalUdpTimeOut()), + this, SLOT(slotStopProcesses()), Qt::QueuedConnection); + + //QObject::connect(mDataProtocolSender, SIGNAL(signalError(const char*)), + // this, SLOT(slotStopProcesses()), Qt::QueuedConnection); + //QObject::connect(mDataProtocolReceiver, SIGNAL(signalError(const char*)), + // this, SLOT(slotStopProcesses()), Qt::QueuedConnection); + + // Start the threads for the specific mode + // --------------------------------------- + switch ( mJackTripMode ) + { + case CLIENT : + if (gVerboseFlag) std::cout << "step 2c client only" << std::endl; + if (gVerboseFlag) std::cout << " JackTrip:startProcess case CLIENT before clientStart" << std::endl; + clientStart(); + break; + case SERVER : + if (gVerboseFlag) std::cout << "step 2s server only" << std::endl; + if (gVerboseFlag) std::cout << " JackTrip:startProcess case SERVER before serverStart" << std::endl; + serverStart(); + break; + case CLIENTTOPINGSERVER : + if (gVerboseFlag) std::cout << "step 2C client only" << std::endl; + if (gVerboseFlag) std::cout << " JackTrip:startProcess case CLIENTTOPINGSERVER before clientPingToServerStart" << std::endl; + if ( clientPingToServerStart() == -1 ) { // if error on server start (-1) we return inmediatly + mTcpConnectionError = true; + slotStopProcesses(); + return; + } + break; + case SERVERPINGSERVER : + if (gVerboseFlag) std::cout << "step 2S server only (same as 2s)" << std::endl; + if (gVerboseFlag) std::cout << " JackTrip:startProcess case SERVERPINGSERVER before serverStart" << std::endl; + if ( serverStart(true) == -1 ) { // if error on server start (-1) we return inmediatly + slotStopProcesses(); + return; + } + break; + default: + throw std::invalid_argument("Jacktrip Mode undefined"); + break; + } - for (int i = 0; i < mProcessPlugins.size(); ++i) { - mAudioInterface->appendProcessPlugin(mProcessPlugins[i]); - } - mAudioInterface->connectDefaultPorts(); - mDataProtocolReceiver->start(); - QThread::msleep(1); - mDataProtocolSender->start(); + // Have the threads share a single socket that operates at full duplex. +#if defined (__WIN_32__) + SOCKET sock_fd = INVALID_SOCKET; +#else + int sock_fd = -1; +#endif + mDataProtocolReceiver->setSocket(sock_fd); + mDataProtocolSender->setSocket(sock_fd); + + // Start Threads + if (gVerboseFlag) std::cout << " JackTrip:startProcess before mDataProtocolReceiver->start" << std::endl; + mDataProtocolReceiver->start(); + QThread::msleep(1); + if (gVerboseFlag) std::cout << " JackTrip:startProcess before mDataProtocolSender->start" << std::endl; + mDataProtocolSender->start(); + /* + * changed order so that audio starts after receiver and sender + * because UdpDataProtocol:run0 before setRealtimeProcessPriority() + * causes an audio hiccup from jack JackPosixSemaphore::TimedWait err = Interrupted system call + * new QThread::msleep(1); + * to allow sender to start + */ + QThread::msleep(1); + if (gVerboseFlag) std::cout << "step 5" << std::endl; + if (gVerboseFlag) std::cout << " JackTrip:startProcess before mAudioInterface->startProcess" << std::endl; + mAudioInterface->startProcess(); + + for (int i = 0; i < mProcessPlugins.size(); ++i) { + mAudioInterface->appendProcessPlugin(mProcessPlugins[i]); + } + if (mConnectDefaultAudioPorts) { mAudioInterface->connectDefaultPorts(); } } //******************************************************************************* void JackTrip::stop() { - // Stop The Sender - mDataProtocolSender->stop(); - mDataProtocolSender->wait(); + // Stop The Sender + mDataProtocolSender->stop(); + mDataProtocolSender->wait(); - // Stop The Receiver - mDataProtocolReceiver->stop(); - mDataProtocolReceiver->wait(); + // Stop The Receiver + mDataProtocolReceiver->stop(); + mDataProtocolReceiver->wait(); - // Stop the audio processes - //mAudioInterface->stopProcess(); - closeAudio(); + // Stop the audio processes + //mAudioInterface->stopProcess(); + closeAudio(); - cout << "JackTrip Processes STOPPED!" << endl; - cout << gPrintSeparator << endl; + cout << "JackTrip Processes STOPPED!" << endl; + cout << gPrintSeparator << endl; - // Emit the jack stopped signal - emit signalProcessesStopped(); + // Emit the jack stopped signal + emit signalProcessesStopped(); } //******************************************************************************* void JackTrip::waitThreads() { - mDataProtocolSender->wait(); - mDataProtocolReceiver->wait(); + mDataProtocolSender->wait(); + mDataProtocolReceiver->wait(); } //******************************************************************************* void JackTrip::clientStart() throw(std::invalid_argument) { - // For the Client mode, the peer (or server) address has to be specified by the user - if ( mPeerAddress.isEmpty() ) { - throw std::invalid_argument("Peer Address has to be set if you run in CLIENT mode"); - } - else { - mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); - mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() ); - cout << "Peer Address set to: " << mPeerAddress.toStdString() << std::endl; - cout << gPrintSeparator << endl; - } + // For the Client mode, the peer (or server) address has to be specified by the user + if ( mPeerAddress.isEmpty() ) { + throw std::invalid_argument("Peer Address has to be set if you run in CLIENT mode"); + } + else { + mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); + mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() ); + cout << "Peer Address set to: " << mPeerAddress.toStdString() << std::endl; + cout << gPrintSeparator << endl; + } } //******************************************************************************* -int JackTrip::serverStart(bool timeout, int udpTimeout) - throw(std::invalid_argument, std::runtime_error) +int JackTrip::serverStart(bool timeout, int udpTimeout) // udpTimeout unused +throw(std::invalid_argument, std::runtime_error) { - // Set the peer address - if ( !mPeerAddress.isEmpty() ) { - std::cout << "WARNING: SERVER mode: Peer Address was set but will be deleted." << endl; - //throw std::invalid_argument("Peer Address has to be set if you run in CLIENT mode"); - mPeerAddress.clear(); - //return; - } + // Set the peer address + if ( !mPeerAddress.isEmpty() ) { + if (gVerboseFlag) std::cout << "WARNING: SERVER mode: Peer Address was set but will be deleted." << endl; + //throw std::invalid_argument("Peer Address has to be set if you run in CLIENT mode"); + mPeerAddress.clear(); + //return; + } - // Get the client address when it connects - cout << "Waiting for Connection From Client..." << endl; - QHostAddress peerHostAddress; - uint16_t peer_port; - QUdpSocket UdpSockTemp;// Create socket to wait for client + // Get the client address when it connects + QHostAddress peerHostAddress; + uint16_t peer_port; + if (gVerboseFlag) std::cout << "JackTrip:serverStart before QUdpSocket UdpSockTemp" << std::endl; + QUdpSocket UdpSockTemp;// Create socket to wait for client - // Bind the socket - if ( !UdpSockTemp.bind(QHostAddress::Any, mReceiverBindPort, - QUdpSocket::DefaultForPlatform) ) - { - std::cerr << "in JackTrip: Could not bind UDP socket. It may be already binded." << endl; - throw std::runtime_error("Could not bind UDP socket. It may be already binded."); - } - // Listen to client - int sleepTime = 100; // ms - int elapsedTime = 0; - if (timeout) { - while ( (!UdpSockTemp.hasPendingDatagrams()) && (elapsedTime <= udpTimeout) ) { - if (mStopped == true) { emit signalUdpTimeOut(); UdpSockTemp.close(); return -1; } - QThread::msleep(sleepTime); - elapsedTime += sleepTime; - } - if (!UdpSockTemp.hasPendingDatagrams()) { - emit signalUdpTimeOut(); - cout << "JackTrip Server Timed Out!" << endl; - return -1; + if (gVerboseFlag) std::cout << "JackTrip:serverStart before UdpSockTemp.bind(Any)" << std::endl; + // Bind the socket + if ( !UdpSockTemp.bind(QHostAddress::Any, mReceiverBindPort, + QUdpSocket::DefaultForPlatform) ) + { + std::cerr << "in JackTrip: Could not bind UDP socket. It may be already binded." << endl; + throw std::runtime_error("Could not bind UDP socket. It may be already binded."); } - } else { - while ( !UdpSockTemp.hasPendingDatagrams() ) { - if (mStopped == true) { emit signalUdpTimeOut(); return -1; } - QThread::msleep(sleepTime); + // Listen to client + int sleepTime = 100; // ms + int elapsedTime = 0; + if (timeout) { + while ( (!UdpSockTemp.hasPendingDatagrams()) && (elapsedTime <= udpTimeout) ) { + if (mStopped == true) { emit signalUdpTimeOut(); UdpSockTemp.close(); return -1; } + QThread::msleep(sleepTime); + elapsedTime += sleepTime; + } + if (!UdpSockTemp.hasPendingDatagrams()) { + emit signalUdpTimeOut(); + cout << "JackTrip Server Timed Out!" << endl; + return -1; + } + } else { + if (gVerboseFlag) std::cout << "JackTrip:serverStart before !UdpSockTemp.hasPendingDatagrams()" << std::endl; + cout << "Waiting for Connection From a Client..." << endl; + while ( !UdpSockTemp.hasPendingDatagrams() ) { + if (mStopped == true) { emit signalUdpTimeOut(); return -1; } + if (gVerboseFlag) std::cout << sleepTime << "ms " << std::flush; + QThread::msleep(sleepTime); + } } - } - char buf[1]; - // set client address - UdpSockTemp.readDatagram(buf, 1, &peerHostAddress, &peer_port); - UdpSockTemp.close(); // close the socket + // char buf[1]; + // // set client address + // UdpSockTemp.readDatagram(buf, 1, &peerHostAddress, &peer_port); + // UdpSockTemp.close(); // close the socket + + // IPv6 addition from fyfe + // Get the datagram size to avoid problems with IPv6 + qint64 datagramSize = UdpSockTemp.pendingDatagramSize(); + char buf[datagramSize]; + // set client address + UdpSockTemp.readDatagram(buf, datagramSize, &peerHostAddress, &peer_port); + UdpSockTemp.close(); // close the socket - mPeerAddress = peerHostAddress.toString(); - cout << "Client Connection Received from IP : " - << qPrintable(mPeerAddress) << endl; - cout << gPrintSeparator << endl; + // Check for mapped IPv4->IPv6 addresses that look like ::ffff:x.x.x.x + if (peerHostAddress.protocol() == QAbstractSocket::IPv6Protocol) { + bool mappedIPv4; + uint32_t address = peerHostAddress.toIPv4Address(&mappedIPv4); + // If the IPv4 address is mapped to IPv6, convert it to IPv4 + if (mappedIPv4) { + QHostAddress ipv4Address = QHostAddress(address); + mPeerAddress = ipv4Address.toString(); + } else { + mPeerAddress = peerHostAddress.toString(); + } + } + else { + mPeerAddress = peerHostAddress.toString(); + } - // Set the peer address to send packets (in the protocol sender) - mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().constData() ); - mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().constData() ); - // We reply to the same port the peer sent the packets from - // This way we can go through NAT - // Because of the NAT traversal scheme, the portn need to be - // "symetric", e.g.: - // from Client to Server : src = 4474, dest = 4464 - // from Server to Client : src = 4464, dest = 4474 - mDataProtocolSender->setPeerPort(peer_port); - mDataProtocolReceiver->setPeerPort(peer_port); - setPeerPorts(peer_port); - return 0; + // Set the peer address to send packets (in the protocol sender) + if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolSender->setPeerAddress()" << std::endl; + mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().constData() ); + if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolReceiver->setPeerAddress()" << std::endl; + mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().constData() ); + // We reply to the same port the peer sent the packets from + // This way we can go through NAT + // Because of the NAT traversal scheme, the portn need to be + // "symetric", e.g.: + // from Client to Server : src = 4474, dest = 4464 + // from Server to Client : src = 4464, dest = 4474 + // no -- all are the same -- 4464 + if (gVerboseFlag) std::cout << "JackTrip:serverStart before setting all peer_port instances to " << peer_port << std::endl; + mDataProtocolSender->setPeerPort(peer_port); + mDataProtocolReceiver->setPeerPort(peer_port); + setPeerPorts(peer_port); + return 0; } //******************************************************************************* int JackTrip::clientPingToServerStart() throw(std::invalid_argument) { - //mConnectionMode = JackTrip::KSTRONG; - //mConnectionMode = JackTrip::JAMTEST; - - // Set Peer (server in this case) address - // -------------------------------------- - // For the Client mode, the peer (or server) address has to be specified by the user - if ( mPeerAddress.isEmpty() ) { - throw std::invalid_argument("Peer Address has to be set if you run in CLIENTTOPINGSERVER mode"); - return -1; - } + //mConnectionMode = JackTrip::KSTRONG; + //mConnectionMode = JackTrip::JAMTEST; + + // Set Peer (server in this case) address + // -------------------------------------- + // For the Client mode, the peer (or server) address has to be specified by the user + if ( mPeerAddress.isEmpty() ) { + throw std::invalid_argument("Peer Address has to be set if you run in CLIENTTOPINGSERVER mode"); + return -1; + } - // Creat Socket Objects - // -------------------- - QTcpSocket tcpClient; - QHostAddress serverHostAddress; - serverHostAddress.setAddress(mPeerAddress); - - // Connect Socket to Server and wait for response - // ---------------------------------------------- - tcpClient.connectToHost(serverHostAddress, mTcpServerPort); - cout << "Connecting to TCP Server..." << endl; - if (!tcpClient.waitForConnected()) { - std::cerr << "TCP Socket ERROR: " << tcpClient.errorString().toStdString() << endl; - //std::exit(1); - return -1; - } - cout << "TCP Socket Connected to Server!" << endl; - emit signalTcpClientConnected(); + // Create Socket Objects + // -------------------- + QTcpSocket tcpClient; + QHostAddress serverHostAddress; + if (!serverHostAddress.setAddress(mPeerAddress)) { + QHostInfo info = QHostInfo::fromName(mPeerAddress); + if (!info.addresses().isEmpty()) { + // use the first IP address + serverHostAddress = info.addresses().first(); + } + } - // Send Client Port Number to Server - // --------------------------------- - char port_buf[sizeof(mReceiverBindPort)]; - std::memcpy(port_buf, &mReceiverBindPort, sizeof(mReceiverBindPort)); + // Connect Socket to Server and wait for response + // ---------------------------------------------- + tcpClient.connectToHost(serverHostAddress, mTcpServerPort); + if (gVerboseFlag) cout << "Connecting to TCP Server..." << endl; + if (!tcpClient.waitForConnected()) { + std::cerr << "TCP Socket ERROR: " << tcpClient.errorString().toStdString() << endl; + //std::exit(1); + return -1; + } + if (gVerboseFlag) cout << "TCP Socket Connected to Server!" << endl; + emit signalTcpClientConnected(); - tcpClient.write(port_buf, sizeof(mReceiverBindPort)); - while ( tcpClient.bytesToWrite() > 0 ) { - tcpClient.waitForBytesWritten(-1); - } - cout << "Port sent to Client" << endl; - - // Read the size of the package - // ---------------------------- - cout << "Reading UDP port from Server..." << endl; - while (tcpClient.bytesAvailable() < (int)sizeof(uint16_t)) { - if (!tcpClient.waitForReadyRead()) { - std::cerr << "TCP Socket ERROR: " << tcpClient.errorString().toStdString() << endl; - //std::exit(1); - return -1; + // Send Client Port Number to Server + // --------------------------------- + char port_buf[sizeof(mReceiverBindPort)]; + std::memcpy(port_buf, &mReceiverBindPort, sizeof(mReceiverBindPort)); + + tcpClient.write(port_buf, sizeof(mReceiverBindPort)); + while ( tcpClient.bytesToWrite() > 0 ) { + tcpClient.waitForBytesWritten(-1); } - } - cout << "Ready To Read From Socket!" << endl; - - // Read UDP Port Number from Server - // -------------------------------- - uint32_t udp_port; - int size = sizeof(udp_port); - //char port_buf[size]; - tcpClient.read(port_buf, size); - std::memcpy(&udp_port, port_buf, size); - //cout << "Received UDP Port Number: " << udp_port << endl; - - // Close the TCP Socket - // -------------------- - tcpClient.close(); // Close the socket - //cout << "TCP Socket Closed!" << endl; - cout << "Connection Succesfull!" << endl; - - // Set with the received UDP port - // ------------------------------ - setPeerPorts(udp_port); - mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() ); - mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); - mDataProtocolSender->setPeerPort(udp_port); - mDataProtocolReceiver->setPeerPort(udp_port); - cout << "Server Address set to: " << mPeerAddress.toStdString() << " Port: " << udp_port << std::endl; - cout << gPrintSeparator << endl; - return 0; + if (gVerboseFlag) cout << "Port sent to Server" << endl; + + // Read the size of the package + // ---------------------------- + if (gVerboseFlag) cout << "Reading UDP port from Server..." << endl; + while (tcpClient.bytesAvailable() < (int)sizeof(uint16_t)) { + if (!tcpClient.waitForReadyRead()) { + std::cerr << "TCP Socket ERROR: " << tcpClient.errorString().toStdString() << endl; + //std::exit(1); + return -1; + } + } + if (gVerboseFlag) cout << "Ready To Read From Socket!" << endl; + + // Read UDP Port Number from Server + // -------------------------------- + uint32_t udp_port; + int size = sizeof(udp_port); + //char port_buf[size]; + tcpClient.read(port_buf, size); + std::memcpy(&udp_port, port_buf, size); + //cout << "Received UDP Port Number: " << udp_port << endl; + + // Close the TCP Socket + // -------------------- + tcpClient.close(); // Close the socket + //cout << "TCP Socket Closed!" << endl; + if (gVerboseFlag) cout << "Connection Succesfull!" << endl; + + // Set with the received UDP port + // ------------------------------ + setPeerPorts(udp_port); + mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() ); + mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); + mDataProtocolSender->setPeerPort(udp_port); + mDataProtocolReceiver->setPeerPort(udp_port); + cout << "Server Address set to: " << mPeerAddress.toStdString() << " Port: " << udp_port << std::endl; + cout << gPrintSeparator << endl; + return 0; - /* + /* else { // Set the peer address mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); @@ -596,7 +714,7 @@ int JackTrip::clientPingToServerStart() throw(std::invalid_argument) cout << "Server port now set to: " << server_port << endl; cout << gPrintSeparator << endl; mDataProtocolSender->setPeerPort(server_port); - + // Start Threads //mAudioInterface->connectDefaultPorts(); mDataProtocolSender->start(); @@ -665,78 +783,79 @@ throw(std::runtime_error) //******************************************************************************* void JackTrip::createHeader(const DataProtocol::packetHeaderTypeT headertype) { - delete mPacketHeader; //Just in case it has already been allocated - switch (headertype) { - case DataProtocol::DEFAULT : - mPacketHeader = new DefaultHeader(this); - break; - case DataProtocol::JAMLINK : - mPacketHeader = new JamLinkHeader(this); - break; - case DataProtocol::EMPTY : - mPacketHeader = new EmptyHeader(this); - break; - default : - throw std::invalid_argument("Undefined Header Type"); - break; - } + delete mPacketHeader; //Just in case it has already been allocated + switch (headertype) { + case DataProtocol::DEFAULT : + mPacketHeader = new DefaultHeader(this); + break; + case DataProtocol::JAMLINK : + mPacketHeader = new JamLinkHeader(this); + break; + case DataProtocol::EMPTY : + mPacketHeader = new EmptyHeader(this); + break; + default : + throw std::invalid_argument("Undefined Header Type"); + break; + } } //******************************************************************************* void JackTrip::putHeaderInPacket(int8_t* full_packet, int8_t* audio_packet) { - mPacketHeader->fillHeaderCommonFromAudio(); - mPacketHeader->putHeaderInPacket(full_packet); - - int8_t* audio_part; - audio_part = full_packet + mPacketHeader->getHeaderSizeInBytes(); - //std::memcpy(audio_part, audio_packet, mAudioInterface->getBufferSizeInBytes()); - //std::memcpy(audio_part, audio_packet, mAudioInterface->getSizeInBytesPerChannel() * mNumChans); - std::memcpy(audio_part, audio_packet, getTotalAudioPacketSizeInBytes()); + mPacketHeader->fillHeaderCommonFromAudio(); + mPacketHeader->putHeaderInPacket(full_packet); + + int8_t* audio_part; + audio_part = full_packet + mPacketHeader->getHeaderSizeInBytes(); + //std::memcpy(audio_part, audio_packet, mAudioInterface->getBufferSizeInBytes()); + //std::memcpy(audio_part, audio_packet, mAudioInterface->getSizeInBytesPerChannel() * mNumChans); + std::memcpy(audio_part, audio_packet, getTotalAudioPacketSizeInBytes()); } //******************************************************************************* int JackTrip::getPacketSizeInBytes() { - //return (mAudioInterface->getBufferSizeInBytes() + mPacketHeader->getHeaderSizeInBytes()); - //return (mAudioInterface->getSizeInBytesPerChannel() * mNumChans + - //mPacketHeader->getHeaderSizeInBytes()); - return (getTotalAudioPacketSizeInBytes() + - mPacketHeader->getHeaderSizeInBytes()); + //return (mAudioInterface->getBufferSizeInBytes() + mPacketHeader->getHeaderSizeInBytes()); + //return (mAudioInterface->getSizeInBytesPerChannel() * mNumChans + + //mPacketHeader->getHeaderSizeInBytes()); + return (getTotalAudioPacketSizeInBytes() + + mPacketHeader->getHeaderSizeInBytes()); } //******************************************************************************* void JackTrip::parseAudioPacket(int8_t* full_packet, int8_t* audio_packet) { - int8_t* audio_part; - audio_part = full_packet + mPacketHeader->getHeaderSizeInBytes(); - //std::memcpy(audio_packet, audio_part, mAudioInterface->getBufferSizeInBytes()); - //std::memcpy(audio_packet, audio_part, mAudioInterface->getSizeInBytesPerChannel() * mNumChans); - std::memcpy(audio_packet, audio_part, getTotalAudioPacketSizeInBytes()); + int8_t* audio_part; + audio_part = full_packet + mPacketHeader->getHeaderSizeInBytes(); + //std::memcpy(audio_packet, audio_part, mAudioInterface->getBufferSizeInBytes()); + //std::memcpy(audio_packet, audio_part, mAudioInterface->getSizeInBytesPerChannel() * mNumChans); + std::memcpy(audio_packet, audio_part, getTotalAudioPacketSizeInBytes()); } //******************************************************************************* void JackTrip::checkPeerSettings(int8_t* full_packet) { - mPacketHeader->checkPeerSettings(full_packet); + mPacketHeader->checkPeerSettings(full_packet); } //******************************************************************************* void JackTrip::checkIfPortIsBinded(int port) { - QUdpSocket UdpSockTemp;// Create socket to wait for client - - // Bind the socket - if ( !UdpSockTemp.bind(QHostAddress::Any, port, QUdpSocket::DontShareAddress) ) - { + QUdpSocket UdpSockTemp;// Create socket to wait for client + // Bind the socket + //cc if ( !UdpSockTemp.bind(QHostAddress::AnyIPv4, port, QUdpSocket::DontShareAddress) ) + if ( !UdpSockTemp.bind(QHostAddress::Any, port, + QUdpSocket::DontShareAddress) ) + { + UdpSockTemp.close(); // close the socket + throw std::runtime_error( + "Could not bind UDP socket. It may already be binded by another process on your machine. Try using a different port number"); + } UdpSockTemp.close(); // close the socket - throw std::runtime_error( - "Could not bind UDP socket. It may already be binded by another process on your machine. Try using a different port number"); - } - UdpSockTemp.close(); // close the socket } diff --git a/src/JackTrip.h b/src/JackTrip.h index 9084290..aa00a2c 100644 --- a/src/JackTrip.h +++ b/src/JackTrip.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -66,48 +66,57 @@ class JackTrip : public QThread { - Q_OBJECT; + Q_OBJECT; public: - //----------ENUMS------------------------------------------ - /// \brief Enum for the data Protocol. At this time only UDP is implemented - enum dataProtocolT { - UDP, ///< Use UDP (User Datagram Protocol) - TCP, ///< NOT IMPLEMENTED: Use TCP (Transmission Control Protocol) - SCTP ///< NOT IMPLEMENTED: Use SCTP (Stream Control Transmission Protocol) - }; - - /// \brief Enum for the JackTrip mode - enum jacktripModeT { - SERVER, ///< Run in Server Mode - CLIENT, ///< Run in Client Mode - CLIENTTOPINGSERVER, ///< Client of the Ping Server Mode - SERVERPINGSERVER ///< Server of the MultiThreaded JackTrip - }; - - /// \brief Enum for the JackTrip Underrun Mode, when packets - enum underrunModeT { - WAVETABLE, ///< Loops on the last received packet - ZEROS ///< Set new buffers to zero if there are no new ones - }; - - /// \brief Enum for Audio Interface Mode - enum audiointerfaceModeT { - JACK, ///< Jack Mode - RTAUDIO ///< RtAudio Mode - }; - - /// \brief Enum for Connection Mode (useful for connections to MultiClient Server) - enum connectionModeT { - NORMAL, ///< Normal Mode - KSTRONG, ///< Karplus Strong - JAMTEST ///< Karplus Strong - }; - //--------------------------------------------------------- - - - /** \brief The class Constructor with Default Parameters + //----------ENUMS------------------------------------------ + /// \brief Enum for the data Protocol. At this time only UDP is implemented + enum dataProtocolT { + UDP, ///< Use UDP (User Datagram Protocol) + TCP, ///< NOT IMPLEMENTED: Use TCP (Transmission Control Protocol) + SCTP ///< NOT IMPLEMENTED: Use SCTP (Stream Control Transmission Protocol) + }; + + /// \brief Enum for the JackTrip mode + enum jacktripModeT { + SERVER, ///< Run in Server Mode + CLIENT, ///< Run in Client Mode + CLIENTTOPINGSERVER, ///< Client of the Ping Server Mode + SERVERPINGSERVER ///< Server of the MultiThreaded JackTrip + }; + + /// \brief Enum for the JackTrip Underrun Mode, when packets + enum underrunModeT { + WAVETABLE, ///< Loops on the last received packet + ZEROS ///< Set new buffers to zero if there are no new ones + }; + + /// \brief Enum for Audio Interface Mode + enum audiointerfaceModeT { + JACK, ///< Jack Mode + RTAUDIO ///< RtAudio Mode + }; + + /// \brief Enum for Connection Mode (in packet header) + enum connectionModeT { + NORMAL, ///< Normal Mode + KSTRONG, ///< Karplus Strong + JAMTEST ///< Karplus Strong + }; + + /// \brief Enum for Hub Server Audio Connection Mode (connections to hub server are automatically patched in Jack) + enum hubConnectionModeT { + SERVERTOCLIENT, ///< Normal Mode, Sever to All Clients (but not client to any client) + CLIENTECHO, ///< Client Echo (client self-to-self) + CLIENTFOFI, ///< Client Fan Out to Clients and Fan In from Clients (but not self-to-self) + RESERVEDMATRIX, ///< Reserved for custom patch matrix (for TUB ensemble) + FULLMIX ///< Client Fan Out to Clients and Fan In from Clients (including self-to-self) + }; + //--------------------------------------------------------- + + + /** \brief The class Constructor with Default Parameters * \param JacktripMode JackTrip::CLIENT or JackTrip::SERVER * \param DataProtocolType JackTrip::dataProtocolT * \param NumChans Number of Audio Channels (same for inputs and outputs) @@ -115,353 +124,392 @@ public: * \param AudioBitResolution Audio Sample Resolutions in bits * \param redundancy redundancy factor for network data */ - JackTrip(jacktripModeT JacktripMode = CLIENT, - dataProtocolT DataProtocolType = UDP, - int NumChans = gDefaultNumInChannels, - int BufferQueueLength = gDefaultQueueLength, - unsigned int redundancy = gDefaultRedundancy, - AudioInterface::audioBitResolutionT AudioBitResolution = - AudioInterface::BIT16, - DataProtocol::packetHeaderTypeT PacketHeaderType = - DataProtocol::DEFAULT, - underrunModeT UnderRunMode = WAVETABLE, - int receiver_bind_port = gDefaultPort, - int sender_bind_port = gDefaultPort, - int receiver_peer_port = gDefaultPort, - int sender_peer_port = gDefaultPort); - - /// \brief The class destructor - virtual ~JackTrip(); - - /// \brief Starting point for the thread - virtual void run() {} - - /// \brief Set the Peer Address for jacktripModeT::CLIENT mode only - virtual void setPeerAddress(const char* PeerHostOrIP); - - /** \brief Append a process plugin. Processes will be appended in order + JackTrip(jacktripModeT JacktripMode = CLIENT, + dataProtocolT DataProtocolType = UDP, + int NumChans = gDefaultNumInChannels, + #ifdef WAIR // wair + int NumNetRevChans = 0, + #endif // endwhere + int BufferQueueLength = gDefaultQueueLength, + unsigned int redundancy = gDefaultRedundancy, + AudioInterface::audioBitResolutionT AudioBitResolution = + AudioInterface::BIT16, + DataProtocol::packetHeaderTypeT PacketHeaderType = + DataProtocol::DEFAULT, + underrunModeT UnderRunMode = WAVETABLE, + int receiver_bind_port = gDefaultPort, + int sender_bind_port = gDefaultPort, + int receiver_peer_port = gDefaultPort, + int sender_peer_port = gDefaultPort); + + /// \brief The class destructor + virtual ~JackTrip(); + + /// \brief Starting point for the thread + virtual void run() { + if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->run" << std::endl; + } + + /// \brief Set the Peer Address for jacktripModeT::CLIENT mode only + virtual void setPeerAddress(const char* PeerHostOrIP); + + /** \brief Append a process plugin. Processes will be appended in order * \param plugin Pointer to ProcessPlugin Class */ - //void appendProcessPlugin(const std::tr1::shared_ptr plugin); - virtual void appendProcessPlugin(ProcessPlugin* plugin); - - /// \brief Start the processing threads - virtual void startProcess() throw(std::invalid_argument); - - /// \brief Stop the processing threads - virtual void stop(); - - /// \brief Wait for all the threads to finish. This functions is used when JackTrip is - /// run as a thread - virtual void waitThreads(); - - /// \brief Check if UDP port is already binded - /// \param port Port number - virtual void checkIfPortIsBinded(int port); - - //------------------------------------------------------------------------------------ - /// \name Getters and Setters Methods to change parameters after construction - //@{ - // - /// \brief Sets (override) JackTrip Mode after construction - virtual void setJackTripMode(jacktripModeT JacktripMode) - { mJackTripMode = JacktripMode; } - /// \brief Sets (override) DataProtocol Type after construction - virtual void setDataProtocoType(dataProtocolT DataProtocolType) - { mDataProtocol = DataProtocolType; } - /// \brief Sets the Packet header type - virtual void setPacketHeaderType(DataProtocol::packetHeaderTypeT PacketHeaderType) - { - mPacketHeaderType = PacketHeaderType; - delete mPacketHeader; - mPacketHeader = NULL; - createHeader(mPacketHeaderType); - } - /// \brief Sets (override) Buffer Queue Length Mode after construction - virtual void setBufferQueueLength(int BufferQueueLength) - { mBufferQueueLength = BufferQueueLength; } - /// \brief Sets (override) Audio Bit Resolution after construction - virtual void setAudioBitResolution(AudioInterface::audioBitResolutionT AudioBitResolution) - { mAudioBitResolution = AudioBitResolution; } - /// \brief Sets (override) Underrun Mode - virtual void setUnderRunMode(underrunModeT UnderRunMode) - { mUnderRunMode = UnderRunMode; } - /// \brief Sets port numbers for the local and peer machine. - /// Receive port is port - virtual void setAllPorts(int port) - { - mReceiverBindPort = port; - mSenderPeerPort = port; - mSenderBindPort = port; - mReceiverPeerPort = port; - } - /// \brief Sets port numbers to bind in RECEIVER and SENDER sockets. - virtual void setBindPorts(int port) - { - mReceiverBindPort = port; - mSenderBindPort = port; - } - /// \brief Sets port numbers for the peer (remote) machine. - virtual void setPeerPorts(int port) - { - mSenderPeerPort = port; - mReceiverPeerPort = port; - } - /// \brief Set Client Name to something different that the default (JackTrip) - virtual void setClientName(const char* ClientName) - { mJackClientName = ClientName; } - /// \brief Set the number of audio channels - virtual void setNumChannels(int num_chans) - { mNumChans = num_chans; } - - virtual int getReceiverBindPort() const - { return mReceiverBindPort; } - virtual int getSenderPeerPort() const - { return mSenderPeerPort; } - virtual int getSenderBindPort() const - { return mSenderBindPort; } - virtual int getReceiverPeerPort() const - { return mReceiverPeerPort; } - - virtual DataProtocol* getDataProtocolSender() const - { return mDataProtocolSender; } - virtual DataProtocol* getDataProtocolReceiver() const - { return mDataProtocolReceiver; } - virtual void setDataProtocolSender(DataProtocol* const DataProtocolSender) - { mDataProtocolSender = DataProtocolSender; } - virtual void setDataProtocolReceiver(DataProtocol* const DataProtocolReceiver) - { mDataProtocolReceiver = DataProtocolReceiver; } - - virtual RingBuffer* getSendRingBuffer() const - { return mSendRingBuffer; } - virtual RingBuffer* getReceiveRingBuffer() const - { return mReceiveRingBuffer; } - virtual void setSendRingBuffer(RingBuffer* const SendRingBuffer) - { mSendRingBuffer = SendRingBuffer; } - virtual void setReceiveRingBuffer(RingBuffer* const ReceiveRingBuffer) - { mReceiveRingBuffer = ReceiveRingBuffer; } - - virtual void setPacketHeader(PacketHeader* const PacketHeader) - { mPacketHeader = PacketHeader; } - - virtual int getRingBuffersSlotSize() - { return getTotalAudioPacketSizeInBytes(); } - - virtual void setAudiointerfaceMode(JackTrip::audiointerfaceModeT audiointerface_mode) - { mAudiointerfaceMode = audiointerface_mode; } - virtual void setAudioInterface(AudioInterface* const AudioInterface) - { mAudioInterface = AudioInterface; } - - - void setSampleRate(uint32_t sample_rate) - { mSampleRate = sample_rate; } - void setAudioBufferSizeInSamples(uint32_t buf_size) - { mAudioBufferSize = buf_size; } - - JackTrip::connectionModeT getConnectionMode() const - { return mConnectionMode; } - void setConnectionMode(JackTrip::connectionModeT connection_mode) - { mConnectionMode = connection_mode; } - - JackTrip::jacktripModeT getJackTripMode() const - { return mJackTripMode; } - - QString getPeerAddress() const - { return mPeerAddress; } - - bool receivedConnectionFromPeer() - { return mReceivedConnection; } - - bool tcpConnectionError() - { return mTcpConnectionError; } - //@} - //------------------------------------------------------------------------------------ - - - //------------------------------------------------------------------------------------ - /// \name Mediator Functions - //@{ - /// \todo Document all these functions - virtual void createHeader(const DataProtocol::packetHeaderTypeT headertype); - void putHeaderInPacket(int8_t* full_packet, int8_t* audio_packet); - virtual int getPacketSizeInBytes(); - void parseAudioPacket(int8_t* full_packet, int8_t* audio_packet); - virtual void sendNetworkPacket(const int8_t* ptrToSlot) - { mSendRingBuffer->insertSlotNonBlocking(ptrToSlot); } - virtual void receiveNetworkPacket(int8_t* ptrToReadSlot) - { mReceiveRingBuffer->readSlotNonBlocking(ptrToReadSlot); } - virtual void readAudioBuffer(int8_t* ptrToReadSlot) - { mSendRingBuffer->readSlotBlocking(ptrToReadSlot); } - virtual void writeAudioBuffer(const int8_t* ptrToSlot) - { mReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot); } - uint32_t getBufferSizeInSamples() const - { return mAudioBufferSize; /*return mAudioInterface->getBufferSizeInSamples();*/ } - - AudioInterface::samplingRateT getSampleRateType() const - { return mAudioInterface->getSampleRateType(); } - int getSampleRate() const - { return mSampleRate; /*return mAudioInterface->getSampleRate();*/ } - - uint8_t getAudioBitResolution() const - { return mAudioBitResolution*8; /*return mAudioInterface->getAudioBitResolution();*/ } - unsigned int getNumInputChannels() const - { return mNumChans; /*return mAudioInterface->getNumInputChannels();*/ } - unsigned int getNumOutputChannels() const - { return mNumChans; /*return mAudioInterface->getNumOutputChannels();*/ } - unsigned int getNumChannels() const - { - if (getNumInputChannels() == getNumOutputChannels()) - { return getNumInputChannels(); } - else { return 0; } - } - virtual void checkPeerSettings(int8_t* full_packet); - void increaseSequenceNumber() - { mPacketHeader->increaseSequenceNumber(); } - int getSequenceNumber() const - { return mPacketHeader->getSequenceNumber(); } - - uint64_t getPeerTimeStamp(int8_t* full_packet) const - { return mPacketHeader->getPeerTimeStamp(full_packet); } - - uint16_t getPeerSequenceNumber(int8_t* full_packet) const - { return mPacketHeader->getPeerSequenceNumber(full_packet); } - - uint16_t getPeerBufferSize(int8_t* full_packet) const - { return mPacketHeader->getPeerBufferSize(full_packet); } - - uint8_t getPeerSamplingRate(int8_t* full_packet) const - { return mPacketHeader->getPeerSamplingRate(full_packet); } - - uint8_t getPeerBitResolution(int8_t* full_packet) const - { return mPacketHeader->getPeerBitResolution(full_packet); } - - uint8_t getPeerNumChannels(int8_t* full_packet) const - { return mPacketHeader->getPeerNumChannels(full_packet); } - - uint8_t getPeerConnectionMode(int8_t* full_packet) const - { return mPacketHeader->getPeerConnectionMode(full_packet); } - - size_t getSizeInBytesPerChannel() const - { return mAudioInterface->getSizeInBytesPerChannel(); } - int getHeaderSizeInBytes() const - { return mPacketHeader->getHeaderSizeInBytes(); } - virtual int getTotalAudioPacketSizeInBytes() const - { return mAudioInterface->getSizeInBytesPerChannel() * mNumChans; } - //@} - //------------------------------------------------------------------------------------ - - void printTextTest() {std::cout << "=== JackTrip PRINT ===" << std::endl;} - void printTextTest2() {std::cout << "=== JackTrip PRINT2 ===" << std::endl;} + //void appendProcessPlugin(const std::tr1::shared_ptr plugin); + virtual void appendProcessPlugin(ProcessPlugin* plugin); + + /// \brief Start the processing threads + virtual void startProcess( + #ifdef WAIRTOMASTER // wair + int ID + #endif // endwhere + ) throw(std::invalid_argument); + + /// \brief Stop the processing threads + virtual void stop(); + + /// \brief Wait for all the threads to finish. This functions is used when JackTrip is + /// run as a thread + virtual void waitThreads(); + + /// \brief Check if UDP port is already binded + /// \param port Port number + virtual void checkIfPortIsBinded(int port); + + //------------------------------------------------------------------------------------ + /// \name Getters and Setters Methods to change parameters after construction + //@{ + // + /// \brief Sets (override) JackTrip Mode after construction + virtual void setJackTripMode(jacktripModeT JacktripMode) + { mJackTripMode = JacktripMode; } + /// \brief Sets (override) DataProtocol Type after construction + virtual void setDataProtocoType(dataProtocolT DataProtocolType) + { mDataProtocol = DataProtocolType; } + /// \brief Sets the Packet header type + virtual void setPacketHeaderType(DataProtocol::packetHeaderTypeT PacketHeaderType) + { + mPacketHeaderType = PacketHeaderType; + delete mPacketHeader; + mPacketHeader = NULL; + createHeader(mPacketHeaderType); + } + /// \brief Sets (override) Buffer Queue Length Mode after construction + virtual void setBufferQueueLength(int BufferQueueLength) + { mBufferQueueLength = BufferQueueLength; } + /// \brief Sets (override) Audio Bit Resolution after construction + virtual void setAudioBitResolution(AudioInterface::audioBitResolutionT AudioBitResolution) + { mAudioBitResolution = AudioBitResolution; } + /// \brief Sets (override) Underrun Mode + virtual void setUnderRunMode(underrunModeT UnderRunMode) + { mUnderRunMode = UnderRunMode; } + /// \brief Sets port numbers for the local and peer machine. + /// Receive port is port + virtual void setAllPorts(int port) + { + mReceiverBindPort = port; + mSenderPeerPort = port; + mSenderBindPort = port; + mReceiverPeerPort = port; + } + /// \brief Sets port numbers to bind in RECEIVER and SENDER sockets. + virtual void setBindPorts(int port) + { + mReceiverBindPort = port; + mSenderBindPort = port; + } + /// \brief Sets port numbers for the peer (remote) machine. + virtual void setPeerPorts(int port) + { + mSenderPeerPort = port; + mReceiverPeerPort = port; + } + /// \brief Set Client Name to something different that the default (JackTrip) + virtual void setClientName(const char* ClientName) + { mJackClientName = ClientName; } + /// \brief Set the number of audio channels + virtual void setNumChannels(int num_chans) + { mNumChans = num_chans; } + + /// Set to connect or not default audio ports (only implemented in Jack) + virtual void setConnectDefaultAudioPorts(bool connect) + {mConnectDefaultAudioPorts = connect;} + + virtual int getReceiverBindPort() const + { return mReceiverBindPort; } + virtual int getSenderPeerPort() const + { return mSenderPeerPort; } + virtual int getSenderBindPort() const + { return mSenderBindPort; } + virtual int getReceiverPeerPort() const + { return mReceiverPeerPort; } + + virtual DataProtocol* getDataProtocolSender() const + { return mDataProtocolSender; } + virtual DataProtocol* getDataProtocolReceiver() const + { return mDataProtocolReceiver; } + virtual void setDataProtocolSender(DataProtocol* const DataProtocolSender) + { mDataProtocolSender = DataProtocolSender; } + virtual void setDataProtocolReceiver(DataProtocol* const DataProtocolReceiver) + { mDataProtocolReceiver = DataProtocolReceiver; } + + virtual RingBuffer* getSendRingBuffer() const + { return mSendRingBuffer; } + virtual RingBuffer* getReceiveRingBuffer() const + { return mReceiveRingBuffer; } + virtual void setSendRingBuffer(RingBuffer* const SendRingBuffer) + { mSendRingBuffer = SendRingBuffer; } + virtual void setReceiveRingBuffer(RingBuffer* const ReceiveRingBuffer) + { mReceiveRingBuffer = ReceiveRingBuffer; } + + virtual void setPacketHeader(PacketHeader* const PacketHeader) + { mPacketHeader = PacketHeader; } + + virtual int getRingBuffersSlotSize() + { return getTotalAudioPacketSizeInBytes(); } + + virtual void setAudiointerfaceMode(JackTrip::audiointerfaceModeT audiointerface_mode) + { mAudiointerfaceMode = audiointerface_mode; } + virtual void setAudioInterface(AudioInterface* const AudioInterface) + { mAudioInterface = AudioInterface; } + + + void setSampleRate(uint32_t sample_rate) + { mSampleRate = sample_rate; } + void setDeviceID(uint32_t device_id) + { mDeviceID = device_id; } + void setAudioBufferSizeInSamples(uint32_t buf_size) + { mAudioBufferSize = buf_size; } + + + JackTrip::connectionModeT getConnectionMode() const + { return mConnectionMode; } + void setConnectionMode(JackTrip::connectionModeT connection_mode) + { mConnectionMode = connection_mode; } + + JackTrip::hubConnectionModeT getHubConnectionModeT() const + { return mHubConnectionModeT; } + void setHubConnectionModeT(JackTrip::hubConnectionModeT connection_mode) + { mHubConnectionModeT = connection_mode; } + + JackTrip::jacktripModeT getJackTripMode() const + { return mJackTripMode; } + + QString getPeerAddress() const + { return mPeerAddress; } + + bool receivedConnectionFromPeer() + { return mReceivedConnection; } + + bool tcpConnectionError() + { return mTcpConnectionError; } + //@} + //------------------------------------------------------------------------------------ + + + //------------------------------------------------------------------------------------ + /// \name Mediator Functions + //@{ + /// \todo Document all these functions + virtual void createHeader(const DataProtocol::packetHeaderTypeT headertype); + void putHeaderInPacket(int8_t* full_packet, int8_t* audio_packet); + virtual int getPacketSizeInBytes(); + void parseAudioPacket(int8_t* full_packet, int8_t* audio_packet); + virtual void sendNetworkPacket(const int8_t* ptrToSlot) + { mSendRingBuffer->insertSlotNonBlocking(ptrToSlot); } + virtual void receiveNetworkPacket(int8_t* ptrToReadSlot) + { mReceiveRingBuffer->readSlotNonBlocking(ptrToReadSlot); } + virtual void readAudioBuffer(int8_t* ptrToReadSlot) + { mSendRingBuffer->readSlotBlocking(ptrToReadSlot); } + virtual void writeAudioBuffer(const int8_t* ptrToSlot) + { mReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot); } + uint32_t getBufferSizeInSamples() const + { return mAudioBufferSize; /*return mAudioInterface->getBufferSizeInSamples();*/ } + uint32_t getDeviceID() const + { return mDeviceID; /*return mAudioInterface->mDeviceID();*/ } + + AudioInterface::samplingRateT getSampleRateType() const + { return mAudioInterface->getSampleRateType(); } + int getSampleRate() const + { return mSampleRate; /*return mAudioInterface->getSampleRate();*/ } + + uint8_t getAudioBitResolution() const + { return mAudioBitResolution*8; /*return mAudioInterface->getAudioBitResolution();*/ } + unsigned int getNumInputChannels() const + { return mNumChans; /*return mAudioInterface->getNumInputChannels();*/ } + unsigned int getNumOutputChannels() const + { return mNumChans; /*return mAudioInterface->getNumOutputChannels();*/ } + unsigned int getNumChannels() const + { + if (getNumInputChannels() == getNumOutputChannels()) + { return getNumInputChannels(); } + else { return 0; } + } + virtual void checkPeerSettings(int8_t* full_packet); + void increaseSequenceNumber() + { mPacketHeader->increaseSequenceNumber(); } + int getSequenceNumber() const + { return mPacketHeader->getSequenceNumber(); } + + uint64_t getPeerTimeStamp(int8_t* full_packet) const + { return mPacketHeader->getPeerTimeStamp(full_packet); } + + uint16_t getPeerSequenceNumber(int8_t* full_packet) const + { return mPacketHeader->getPeerSequenceNumber(full_packet); } + + uint16_t getPeerBufferSize(int8_t* full_packet) const + { return mPacketHeader->getPeerBufferSize(full_packet); } + + uint8_t getPeerSamplingRate(int8_t* full_packet) const + { return mPacketHeader->getPeerSamplingRate(full_packet); } + + uint8_t getPeerBitResolution(int8_t* full_packet) const + { return mPacketHeader->getPeerBitResolution(full_packet); } + + uint8_t getPeerNumChannels(int8_t* full_packet) const + { return mPacketHeader->getPeerNumChannels(full_packet); } + + uint8_t getPeerConnectionMode(int8_t* full_packet) const + { return mPacketHeader->getPeerConnectionMode(full_packet); } + + size_t getSizeInBytesPerChannel() const + { return mAudioInterface->getSizeInBytesPerChannel(); } + int getHeaderSizeInBytes() const + { return mPacketHeader->getHeaderSizeInBytes(); } + virtual int getTotalAudioPacketSizeInBytes() const + { +#ifdef WAIR // WAIR + if (mNumNetRevChans) + return mAudioInterface->getSizeInBytesPerChannel() * mNumNetRevChans; + else // not wair +#endif // endwhere + return mAudioInterface->getSizeInBytesPerChannel() * mNumChans; + } + //@} + //------------------------------------------------------------------------------------ + + void printTextTest() {std::cout << "=== JackTrip PRINT ===" << std::endl;} + void printTextTest2() {std::cout << "=== JackTrip PRINT2 ===" << std::endl;} public slots: - /// \brief Slot to stop all the processes and threads - virtual void slotStopProcesses() - { - std::cout << "Stopping JackTrip..." << std::endl; - mStopped = true; - this->stop(); - } - - /** \brief This slot emits in turn the signal signalNoUdpPacketsForSeconds + /// \brief Slot to stop all the processes and threads + virtual void slotStopProcesses() + { + std::cout << "Stopping JackTrip..." << std::endl; + mStopped = true; + this->stop(); + } + + /** \brief This slot emits in turn the signal signalNoUdpPacketsForSeconds * when UDP has waited for more than 30 seconds. - * + * * It is used to remove the thread from the server. */ - void slotUdpWatingTooLong(int wait_msec) - { - int wait_time = 30000; // msec - if ( !(wait_msec%wait_time) ) { - std::cerr << "UDP WAITED MORE THAN 30 seconds." << std::endl; - emit signalNoUdpPacketsForSeconds(); + void slotUdpWaitingTooLongClientGoneProbably(int wait_msec) + { + int wait_time = 10000; // msec + if ( !(wait_msec%wait_time) ) { + std::cerr << "UDP WAITED MORE THAN 10 seconds." << std::endl; + emit signalNoUdpPacketsForSeconds(); + } } - } - void slotPrintTest() - { std::cout << "=== TESTING ===" << std::endl; } - void slotReceivedConnectionFromPeer() - { mReceivedConnection = true; } + void slotPrintTest() + { std::cout << "=== TESTING ===" << std::endl; } + void slotReceivedConnectionFromPeer() + { mReceivedConnection = true; } signals: - void signalUdpTimeOut(); - /// \brief Signal emitted when all the processes and threads are stopped - void signalProcessesStopped(); - /// \brief Signal emitted when no UDP Packets have been received for a while - void signalNoUdpPacketsForSeconds(); - void signalTcpClientConnected(); + void signalUdpTimeOut(); + /// \brief Signal emitted when all the processes and threads are stopped + void signalProcessesStopped(); + /// \brief Signal emitted when no UDP Packets have been received for a while + void signalNoUdpPacketsForSeconds(); + void signalTcpClientConnected(); public: - /// \brief Set the AudioInteface object - virtual void setupAudio(); - /// \brief Close the JackAudioInteface and disconnects it from JACK - void closeAudio(); - /// \brief Set the DataProtocol objects - virtual void setupDataProtocol(); - /// \brief Set the RingBuffer objects - void setupRingBuffers(); - /// \brief Starts for the CLIENT mode - void clientStart() throw(std::invalid_argument); - /// \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); - /// \brief Stats for the Client to Ping Server - /// \return -1 on error, 0 on success - virtual int clientPingToServerStart() throw(std::invalid_argument); + /// \brief Set the AudioInteface object + virtual void setupAudio( + #ifdef WAIRTOMASTER // WAIR + int ID + #endif // endwhere + ); + /// \brief Close the JackAudioInteface and disconnects it from JACK + void closeAudio(); + /// \brief Set the DataProtocol objects + virtual void setupDataProtocol(); + /// \brief Set the RingBuffer objects + void setupRingBuffers(); + /// \brief Starts for the CLIENT mode + void clientStart() throw(std::invalid_argument); + /// \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); + /// \brief Stats for the Client to Ping Server + /// \return -1 on error, 0 on success + virtual int clientPingToServerStart() throw(std::invalid_argument); private: - //void bindReceiveSocket(QUdpSocket& UdpSocket, int bind_port, - // QHostAddress PeerHostAddress, int peer_port) - //throw(std::runtime_error); - - - jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT - dataProtocolT mDataProtocol; ///< Data Protocol Tipe - DataProtocol::packetHeaderTypeT mPacketHeaderType; ///< Packet Header Type - JackTrip::audiointerfaceModeT mAudiointerfaceMode; - - int mNumChans; ///< Number of Channels (inputs = outputs) - int mBufferQueueLength; ///< Audio Buffer from network queue length - uint32_t mSampleRate; ///< Sample Rate - uint32_t mAudioBufferSize; ///< Audio buffer size to process on each callback - AudioInterface::audioBitResolutionT mAudioBitResolution; ///< Audio Bit Resolutions - QString mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode - - /// Pointer to Abstract Type DataProtocol that sends packets - DataProtocol* mDataProtocolSender; - /// Pointer to Abstract Type DataProtocol that receives packets - DataProtocol* mDataProtocolReceiver; - AudioInterface* mAudioInterface; ///< Interface to Jack Client - PacketHeader* mPacketHeader; ///< Pointer to Packet Header - underrunModeT mUnderRunMode; ///< underrunModeT Mode - - /// Pointer for the Send RingBuffer - RingBuffer* mSendRingBuffer; - /// Pointer for the Receive RingBuffer - RingBuffer* mReceiveRingBuffer; - - int mReceiverBindPort; ///< Incoming (receiving) port for local machine - int mSenderPeerPort; ///< Incoming (receiving) port for peer machine - int mSenderBindPort; ///< Outgoing (sending) port for local machine - int mReceiverPeerPort; ///< Outgoing (sending) port for peer machine - int mTcpServerPort; - - unsigned int mRedundancy; ///< Redundancy factor in network data - const char* mJackClientName; ///< JackAudio Client Name - - JackTrip::connectionModeT mConnectionMode; ///< Connection Mode - - QVector mProcessPlugins; ///< Vector of ProcesPlugins - - volatile bool mReceivedConnection; ///< Bool of received connection from peer - volatile bool mTcpConnectionError; - volatile bool mStopped; + //void bindReceiveSocket(QUdpSocket& UdpSocket, int bind_port, + // QHostAddress PeerHostAddress, int peer_port) + //throw(std::runtime_error); + + + jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT + dataProtocolT mDataProtocol; ///< Data Protocol Tipe + DataProtocol::packetHeaderTypeT mPacketHeaderType; ///< Packet Header Type + JackTrip::audiointerfaceModeT mAudiointerfaceMode; + + int mNumChans; ///< Number of Channels (inputs = outputs) +#ifdef WAIR // WAIR + int mNumNetRevChans; ///< Number of Network Audio Channels (net comb filters) +#endif // endwhere + int mBufferQueueLength; ///< Audio Buffer from network queue length + uint32_t mSampleRate; ///< Sample Rate + uint32_t mDeviceID; ///< RTAudio DeviceID + uint32_t mAudioBufferSize; ///< Audio buffer size to process on each callback + AudioInterface::audioBitResolutionT mAudioBitResolution; ///< Audio Bit Resolutions + QString mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode + + /// Pointer to Abstract Type DataProtocol that sends packets + DataProtocol* mDataProtocolSender; + /// Pointer to Abstract Type DataProtocol that receives packets + DataProtocol* mDataProtocolReceiver; + AudioInterface* mAudioInterface; ///< Interface to Jack Client + PacketHeader* mPacketHeader; ///< Pointer to Packet Header + underrunModeT mUnderRunMode; ///< underrunModeT Mode + + /// Pointer for the Send RingBuffer + RingBuffer* mSendRingBuffer; + /// Pointer for the Receive RingBuffer + RingBuffer* mReceiveRingBuffer; + + int mReceiverBindPort; ///< Incoming (receiving) port for local machine + int mSenderPeerPort; ///< Incoming (receiving) port for peer machine + int mSenderBindPort; ///< Outgoing (sending) port for local machine + int mReceiverPeerPort; ///< Outgoing (sending) port for peer machine + int mTcpServerPort; + + unsigned int mRedundancy; ///< Redundancy factor in network data + const char* mJackClientName; ///< JackAudio Client Name + + JackTrip::connectionModeT mConnectionMode; ///< Connection Mode + JackTrip::hubConnectionModeT mHubConnectionModeT; ///< Hub Server Jack Audio Patch Connection Mode + + QVector mProcessPlugins; ///< Vector of ProcesPlugins + + volatile bool mReceivedConnection; ///< Bool of received connection from peer + volatile bool mTcpConnectionError; + volatile bool mStopped; + + bool mConnectDefaultAudioPorts; ///< Connect or not default audio ports }; #endif - - diff --git a/src/JackTripThread.cpp b/src/JackTripThread.cpp index 5b3f52d..abe1e9c 100644 --- a/src/JackTripThread.cpp +++ b/src/JackTripThread.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -48,31 +48,31 @@ using std::cout; using std::endl; //******************************************************************************* void JackTripThread::run() { - JackTrip jacktrip(mJackTripMode); - jacktrip.setAllPorts(mPortNum); - - if ( mJackTripMode == JackTrip::CLIENT ) + JackTrip jacktrip(mJackTripMode); + jacktrip.setAllPorts(mPortNum); + + if ( mJackTripMode == JackTrip::CLIENT ) { - jacktrip.setPeerAddress(mPeerAddress); + jacktrip.setPeerAddress(mPeerAddress); } - NetKS netks; - jacktrip.appendProcessPlugin(&netks); - //netks.play(); + NetKS netks; + jacktrip.appendProcessPlugin(&netks); + //netks.play(); - //QThread::sleep(1); - jacktrip.start(); - //netks.play(); - jacktrip.wait(); + //QThread::sleep(1); + jacktrip.start(); + //netks.play(); + jacktrip.wait(); - cout << "******** AFTER JACKTRIPTHREAD START **************" << endl; - //QThread::sleep(9999999); + cout << "******** AFTER JACKTRIPTHREAD START **************" << endl; + //QThread::sleep(9999999); - /* + /* jack_client_t* mClient; const char* client_name = "JackThread"; const char* server_name = NULL; @@ -83,7 +83,7 @@ void JackTripThread::run() if (mClient == NULL) { fprintf (stderr, "jack_client_open() failed, " - "status = 0x%2.0x\n", status); + "status = 0x%2.0x\n", status); if (status & JackServerFailed) { fprintf (stderr, "Unable to connect to JACK server\n"); } diff --git a/src/JackTripThread.h b/src/JackTripThread.h index 06a85c7..fe7e6d7 100644 --- a/src/JackTripThread.h +++ b/src/JackTripThread.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -47,17 +47,17 @@ class JackTripThread : public QThread { public: - JackTripThread(JackTrip::jacktripModeT JacktripMode) : mJackTripMode(JacktripMode) {} - virtual ~JackTripThread(){} - void run(); + JackTripThread(JackTrip::jacktripModeT JacktripMode) : mJackTripMode(JacktripMode) {} + virtual ~JackTripThread(){} + void run(); - void setPort(int port_num) { mPortNum = port_num; } - void setPeerAddress(const char* PeerHostOrIP) { mPeerAddress = PeerHostOrIP; } + void setPort(int port_num) { mPortNum = port_num; } + void setPeerAddress(const char* PeerHostOrIP) { mPeerAddress = PeerHostOrIP; } private: - JackTrip::jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT - int mPortNum; - const char* mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode + JackTrip::jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT + int mPortNum; + const char* mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode }; diff --git a/src/JackTripWorker.cpp b/src/JackTripWorker.cpp index b83b5c7..84b825f 100644 --- a/src/JackTripWorker.cpp +++ b/src/JackTripWorker.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -47,6 +47,9 @@ #include "UdpMasterListener.h" #include "NetKS.h" #include "LoopBack.h" +#ifdef WAIR // wair +#include "dcblock2gain.dsp.h" +#endif // endwhere #ifdef __JAMTEST__ #include "JamTest.h" #endif @@ -54,150 +57,206 @@ using std::cout; using std::endl; //******************************************************************************* -JackTripWorker::JackTripWorker(UdpMasterListener* udpmasterlistener) : - mUdpMasterListener(NULL), - mSpawning(false), - mID(0), - mNumChans(1) +JackTripWorker::JackTripWorker(UdpMasterListener* udpmasterlistener, int BufferQueueLength, JackTrip::underrunModeT UnderRunMode) : + mUdpMasterListener(udpmasterlistener), + m_connectDefaultAudioPorts(false), + mBufferQueueLength(BufferQueueLength), + mUnderRunMode(UnderRunMode), + mSpawning(false), + mID(0), + mNumChans(1) + #ifdef WAIR // wair + ,mNumNetRevChans(0), + mWAIR(false) + #endif // endwhere { - /* From the QT Documentation: - QThreadPool supports executing the same QRunnable more than once - by calling tryStart(this) from within QRunnable::run(). If autoDelete is - enabled the QRunnable will be deleted when the last thread exits the - run function. Calling start() multiple times with the same QRunnable - when autoDelete is enabled creates a race condition and is not recommended. - */ - mUdpMasterListener = udpmasterlistener; - setAutoDelete(false); // stick around after calling run() - //mNetks = new NetKS; - //mNetks->play(); + setAutoDelete(false); // stick around after calling run() + //mNetks = new NetKS; + //mNetks->play(); } //******************************************************************************* JackTripWorker::~JackTripWorker() { - //delete mUdpMasterListener; + //delete mUdpMasterListener; } //******************************************************************************* -void JackTripWorker::setJackTrip(int id, uint32_t client_address, - uint16_t server_port, uint16_t client_port, - int num_channels) +void JackTripWorker::setJackTrip(int id, + QString client_address, + uint16_t server_port, + uint16_t client_port, + int num_channels, + bool connectDefaultAudioPorts) { - { //Start Spawning, so lock mSpawning - QMutexLocker locker(&mMutex); - mSpawning = true; - } - mID = id; - // Set the jacktrip address and ports - //mClientAddress.setAddress(client_address); - mClientAddress = client_address; - mServerPort = server_port; - mClientPort = client_port; - mNumChans = num_channels; + { //Start Spawning, so lock mSpawning + QMutexLocker locker(&mMutex); + mSpawning = true; + } + mID = id; + // Set the jacktrip address and ports + //mClientAddress.setAddress(client_address); + mClientAddress = client_address; + mServerPort = server_port; + mClientPort = client_port; + mNumChans = num_channels; + m_connectDefaultAudioPorts = connectDefaultAudioPorts; } //******************************************************************************* void JackTripWorker::run() { - /* NOTE: This is the message that qt prints when an exception is thrown: + /* NOTE: This is the message that qt prints when an exception is thrown: 'Qt Concurrent has caught an exception thrown from a worker thread. This is not supported, exceptions thrown in worker threads must be caught before control returns to Qt Concurrent.'*/ - { QMutexLocker locker(&mMutex); mSpawning = true; } - - QHostAddress ClientAddress; + { QMutexLocker locker(&mMutex); mSpawning = true; } - // Try catching any exceptions that come from JackTrip - try - { - // Local event loop. this is necesary because QRunnables don't have their own as QThreads - QEventLoop event_loop; + //QHostAddress ClientAddress; + + // Try catching any exceptions that come from JackTrip + try + { + // Local event loop. this is necesary because QRunnables don't have their own as QThreads + QEventLoop event_loop; + + // Create and setup JackTrip Object + //JackTrip jacktrip(JackTrip::SERVER, JackTrip::UDP, mNumChans, 2); + if (gVerboseFlag) cout << "---> JackTripWorker: Creating jacktrip objects..." << endl; + +#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 + mWAIR = true; + mNumNetRevChans = NUMNETREVCHANSbecauseNOTINRECEIVEDheader; + } else {}; +#endif // endwhere - // Create and setup JackTrip Object - //JackTrip jacktrip(JackTrip::SERVER, JackTrip::UDP, mNumChans, 2); - cout << "---> JackTripWorker: Creating jacktip objects..." << endl; #ifndef __JAMTEST__ - JackTrip jacktrip(JackTrip::SERVERPINGSERVER, JackTrip::UDP, mNumChans, 2); -#endif +#ifdef WAIR // WAIR + // bool tmp = mJTWorkers->at(id)->isWAIR(); + // qDebug() << "is WAIR?" << tmp ; + qDebug() << "mNumNetRevChans" << mNumNetRevChans ; + + JackTrip jacktrip(JackTrip::SERVERPINGSERVER, JackTrip::UDP, mNumChans, + mNumNetRevChans, FORCEBUFFERQ); + JackTrip * mJackTrip = &jacktrip; +#else // endwhere + JackTrip jacktrip(JackTrip::SERVERPINGSERVER, JackTrip::UDP, mNumChans, mBufferQueueLength); +#endif // not wair + +#ifdef WAIR // WAIR + // Add Plugins + if ( mWAIR ) { + cout << "Running in WAIR Mode..." << endl; + cout << gPrintSeparator << std::endl; + switch ( mNumNetRevChans ) + { + case 16 : // freeverb + mJackTrip->appendProcessPlugin(new dcblock2gain(mNumChans)); // plugin slot 0 + /////////////// + // mJackTrip->appendProcessPlugin(new comb16server(mNumNetChans)); + // -S LAIR no AP mJackTrip->appendProcessPlugin(new AP8(mNumChans)); + break; + default: + throw std::invalid_argument("Settings: mNumNetChans doesn't correspond to Faust plugin"); + break; + } + } +#endif // endwhere +#endif // ifndef __JAMTEST__ + #ifdef __JAMTEST__ - JamTest jacktrip(JackTrip::SERVERPINGSERVER); // ########### JamTest ################# - //JackTrip jacktrip(JackTrip::SERVERPINGSERVER, JackTrip::UDP, mNumChans, 2); + JamTest jacktrip(JackTrip::SERVERPINGSERVER); // ########### JamTest ################# + //JackTrip jacktrip(JackTrip::SERVERPINGSERVER, JackTrip::UDP, mNumChans, 2); #endif - // Connect signals and slots - // ------------------------- - cout << "---> JackTripWorker: Connecting signals and slots..." << endl; - // Connection to terminate JackTrip when packets haven't arrive for - // a certain amount of time - QObject::connect(&jacktrip, SIGNAL(signalNoUdpPacketsForSeconds()), - &jacktrip, SLOT(slotStopProcesses()), Qt::QueuedConnection); - // Connection to terminate the local eventloop when jacktrip is done - QObject::connect(&jacktrip, SIGNAL(signalProcessesStopped()), - &event_loop, SLOT(quit()), Qt::QueuedConnection); - QObject::connect(this, SIGNAL(signalRemoveThread()), - &jacktrip, SLOT(slotStopProcesses()), Qt::QueuedConnection); - - ClientAddress.setAddress(mClientAddress); - // If I don't type this line, I get a bus error in the next line. - // I still haven't figure out why - ClientAddress.toString().toLatin1().constData(); - jacktrip.setPeerAddress(ClientAddress.toString().toLatin1().constData()); - jacktrip.setBindPorts(mServerPort); - //jacktrip.setPeerPorts(mClientPort); - - cout << "---> JackTripWorker: setJackTripFromClientHeader..." << endl; - int PeerConnectionMode = setJackTripFromClientHeader(jacktrip); - if ( PeerConnectionMode == -1 ) { - mUdpMasterListener->releaseThread(mID); - { QMutexLocker locker(&mMutex); mSpawning = false; } - return; - } - - // Start Threads and event loop - cout << "---> JackTripWorker: startProcess..." << endl; - jacktrip.startProcess(); - cout << "---> JackTripWorker: start..." << endl; - jacktrip.start(); // ########### JamTest Only ################# - - // Thread is already spawning, so release the lock - { QMutexLocker locker(&mMutex); mSpawning = false; } - - event_loop.exec(); // Excecution will block here until exit() the QEventLoop - //-------------------------------------------------------------------------- + jacktrip.setConnectDefaultAudioPorts(m_connectDefaultAudioPorts); + + // Set our underrun mode + jacktrip.setUnderRunMode(mUnderRunMode); + + // Connect signals and slots + // ------------------------- + if (gVerboseFlag) cout << "---> JackTripWorker: Connecting signals and slots..." << endl; + // Connection to terminate JackTrip when packets haven't arrive for + // a certain amount of time + QObject::connect(&jacktrip, SIGNAL(signalNoUdpPacketsForSeconds()), + &jacktrip, SLOT(slotStopProcesses()), Qt::QueuedConnection); + // Connection to terminate the local eventloop when jacktrip is done + QObject::connect(&jacktrip, SIGNAL(signalProcessesStopped()), + &event_loop, SLOT(quit()), Qt::QueuedConnection); + QObject::connect(this, SIGNAL(signalRemoveThread()), + &jacktrip, SLOT(slotStopProcesses()), Qt::QueuedConnection); + + //ClientAddress.setAddress(mClientAddress); + // If I don't type this line, I get a bus error in the next line. + // I still haven't figure out why + //ClientAddress.toString().toLatin1().constData(); + //jacktrip.setPeerAddress(ClientAddress.toString().toLatin1().constData()); + jacktrip.setPeerAddress(mClientAddress.toLatin1().constData()); + jacktrip.setBindPorts(mServerPort); + //jacktrip.setPeerPorts(mClientPort); + + if (gVerboseFlag) cout << "---> JackTripWorker: setJackTripFromClientHeader..." << endl; + int PeerConnectionMode = setJackTripFromClientHeader(jacktrip); + if ( PeerConnectionMode == -1 ) { + mUdpMasterListener->releaseThread(mID); + { QMutexLocker locker(&mMutex); mSpawning = false; } + return; + } + + // Start Threads and event loop + if (gVerboseFlag) cout << "---> JackTripWorker: startProcess..." << endl; + jacktrip.startProcess( + #ifdef WAIRTOMASTER // wair + mID + #endif // endwhere + ); + // if (gVerboseFlag) cout << "---> JackTripWorker: start..." << endl; + // jacktrip.start(); // ########### JamTest Only ################# + + // Thread is already spawning, so release the lock + { QMutexLocker locker(&mMutex); mSpawning = false; } + + event_loop.exec(); // Excecution will block here until exit() the QEventLoop + //-------------------------------------------------------------------------- + + { QMutexLocker locker(&mMutex); mSpawning = true; } + + // wait for jacktrip to be done before exiting the Worker Thread + jacktrip.wait(); - { QMutexLocker locker(&mMutex); mSpawning = true; } + } + catch ( const std::exception & e ) + { + std::cerr << "Couldn't send thread to the Pool" << endl; + std::cerr << e.what() << endl; + std::cerr << gPrintSeparator << endl; + mUdpMasterListener->releaseThread(mID); + { QMutexLocker locker(&mMutex); mSpawning = false; } + return; + } - // wait for jacktrip to be done before exiting the Worker Thread - jacktrip.wait(); - - } - catch ( const std::exception & e ) - { - std::cerr << "Couldn't send thread to the Pool" << endl; - std::cerr << e.what() << endl; - std::cerr << gPrintSeparator << endl; - mUdpMasterListener->releaseThread(mID); - { QMutexLocker locker(&mMutex); mSpawning = false; } - return; - } - - { - QMutexLocker locker(&mMutex); - mUdpMasterListener->releaseThread(mID); - } + { + QMutexLocker locker(&mMutex); + mUdpMasterListener->releaseThread(mID); + } - cout << "JackTrip ID = " << mID << " released from the THREAD POOL" << endl; - cout << gPrintSeparator << endl; - { - // Thread is already spawning, so release the lock - QMutexLocker locker(&mMutex); - mSpawning = false; - } + cout << "JackTrip ID = " << mID << " released from the THREAD POOL" << endl; + cout << gPrintSeparator << endl; + { + // Thread is already spawning, so release the lock + QMutexLocker locker(&mMutex); + mSpawning = false; + } } @@ -205,72 +264,72 @@ void JackTripWorker::run() // returns -1 on error int JackTripWorker::setJackTripFromClientHeader(JackTrip& jacktrip) { - //QHostAddress peerHostAddress; - //uint16_t peer_port; - QUdpSocket UdpSockTemp;// Create socket to wait for client - - // Bind the socket - if ( !UdpSockTemp.bind(QHostAddress::Any, mServerPort, - QUdpSocket::DefaultForPlatform) ) - { - std::cerr << "in JackTripWorker: Could not bind UDP socket. It may be already binded." << endl; - throw std::runtime_error("Could not bind UDP socket. It may be already binded."); - } - - // Listen to client - QWaitCondition sleep; // time is in milliseconds - QMutex mutex; - int sleepTime = 100; // ms - int udpTimeout = gTimeOutMultiThreadedServer; // gTimeOutMultiThreadedServer mseconds - int elapsedTime = 0; - { - QMutexLocker lock(&mutex); - while ( (!UdpSockTemp.hasPendingDatagrams()) && (elapsedTime <= udpTimeout) ) { - sleep.wait(&mutex,sleepTime); - elapsedTime += sleepTime; - //cout << "---------> ELAPSED TIME: " << elapsedTime << endl; + //QHostAddress peerHostAddress; + //uint16_t peer_port; + QUdpSocket UdpSockTemp;// Create socket to wait for client + + // Bind the socket + if ( !UdpSockTemp.bind(QHostAddress::Any, mServerPort, + QUdpSocket::DefaultForPlatform) ) + { + std::cerr << "in JackTripWorker: Could not bind UDP socket. It may be already binded." << endl; + throw std::runtime_error("Could not bind UDP socket. It may be already binded."); + } + + // Listen to client + QWaitCondition sleep; // time is in milliseconds + QMutex mutex; + int sleepTime = 100; // ms + int udpTimeout = gTimeOutMultiThreadedServer; // gTimeOutMultiThreadedServer mseconds + int elapsedTime = 0; + { + QMutexLocker lock(&mutex); + while ( (!UdpSockTemp.hasPendingDatagrams()) && (elapsedTime <= udpTimeout) ) { + sleep.wait(&mutex,sleepTime); + elapsedTime += sleepTime; + if (gVerboseFlag) cout << "---------> ELAPSED TIME: " << elapsedTime << endl; + } } - } - // Check if we time out or not - if (!UdpSockTemp.hasPendingDatagrams()) { - std::cerr << "--->JackTripWorker: is not receiving Datagrams (timeout)" << endl; - UdpSockTemp.close(); - return -1; - } - int packet_size = UdpSockTemp.pendingDatagramSize(); - char packet[packet_size]; - UdpSockTemp.readDatagram(packet, packet_size); - UdpSockTemp.close(); // close the socket - int8_t* full_packet = reinterpret_cast(packet); - - int PeerBufferSize = jacktrip.getPeerBufferSize(full_packet); - int PeerSamplingRate = jacktrip.getPeerSamplingRate(full_packet); - int PeerBitResolution = jacktrip.getPeerBitResolution(full_packet); - int PeerNumChannels = jacktrip.getPeerNumChannels(full_packet); - int PeerConnectionMode = jacktrip.getPeerConnectionMode(full_packet); - - cout << "--->JackTripWorker: getPeerBufferSize = " << PeerBufferSize << endl; - cout << "--->JackTripWorker: getPeerSamplingRate = " << PeerSamplingRate << endl; - cout << "--->JackTripWorker: getPeerBitResolution = " << PeerBitResolution << endl; - cout << "--->JackTripWorker: getPeerNumChannels = " << PeerNumChannels << endl; - cout << "--->JackTripWorker: getPeerConnectionMode = " << PeerConnectionMode << endl; - - jacktrip.setNumChannels(PeerNumChannels); - return PeerConnectionMode; + // Check if we time out or not + if (!UdpSockTemp.hasPendingDatagrams()) { + std::cerr << "--->JackTripWorker: is not receiving Datagrams (timeout)" << endl; + UdpSockTemp.close(); + return -1; + } + int packet_size = UdpSockTemp.pendingDatagramSize(); + char packet[packet_size]; + UdpSockTemp.readDatagram(packet, packet_size); + UdpSockTemp.close(); // close the socket + int8_t* full_packet = reinterpret_cast(packet); + + int PeerBufferSize = jacktrip.getPeerBufferSize(full_packet); + int PeerSamplingRate = jacktrip.getPeerSamplingRate(full_packet); + int PeerBitResolution = jacktrip.getPeerBitResolution(full_packet); + int PeerNumChannels = jacktrip.getPeerNumChannels(full_packet); + int PeerConnectionMode = jacktrip.getPeerConnectionMode(full_packet); + + if (gVerboseFlag) cout << "--->JackTripWorker: getPeerBufferSize = " << PeerBufferSize << endl; + if (gVerboseFlag) cout << "--->JackTripWorker: getPeerSamplingRate = " << PeerSamplingRate << endl; + if (gVerboseFlag) cout << "--->JackTripWorker: getPeerBitResolution = " << PeerBitResolution << endl; + cout << "--->JackTripWorker: PeerNumChannels = " << PeerNumChannels << endl; + if (gVerboseFlag) cout << "--->JackTripWorker: getPeerConnectionMode = " << PeerConnectionMode << endl; + + jacktrip.setNumChannels(PeerNumChannels); + return PeerConnectionMode; } //******************************************************************************* bool JackTripWorker::isSpawning() { - QMutexLocker locker(&mMutex); - return mSpawning; + QMutexLocker locker(&mMutex); + return mSpawning; } //******************************************************************************* void JackTripWorker::stopThread() { - QMutexLocker locker(&mMutex); - emit signalRemoveThread(); + QMutexLocker locker(&mMutex); + emit signalRemoveThread(); } diff --git a/src/JackTripWorker.h b/src/JackTripWorker.h index adf1602..06760e5 100644 --- a/src/JackTripWorker.h +++ b/src/JackTripWorker.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -47,15 +47,16 @@ #include #include "JackTrip.h" +#include "jacktrip_globals.h" //class JackTrip; // forward declaration class UdpMasterListener; // forward declaration -/** \brief Prototype of the worker class that will be cloned through sending threads to the +/** \brief Prototype of the worker class that will be cloned through sending threads to the * Thread Pool * - * This class can be send to the ThreadPool using the start() method. Each time + * This class can be send to the ThreadPool using the start() method. Each time * it is sent, it'll became "independent" of the prototype, which means * that the prototype state can be changed, and used to send and start * another thread into the pool. setAutoDelete must be set to false @@ -65,62 +66,73 @@ class UdpMasterListener; // forward declaration // inside a QThread class JackTripWorker : public QObject, public QRunnable { - Q_OBJECT; // QRunnable is not a QObject, so I have to inherit from QObject as well - + Q_OBJECT; // QRunnable is not a QObject, so I have to inherit from QObject as well + public: - /// \brief The class constructor - JackTripWorker(UdpMasterListener* udpmasterlistener); - /// \brief The class destructor - virtual ~JackTripWorker(); - - /// \brief Implements the Thread Loop. - /// To start the thread, call start() ( DO NOT CALL run() ). - void run(); - /// \brief Check if the Thread is Spawning - /// \return true is it is spawning, false if it's already running - bool isSpawning(); - /// \brief Sets the JackTripWorker properties - /// \param id ID number - /// \param address - void setJackTrip(int id, uint32_t client_address, - uint16_t server_port, uint16_t client_port, - int num_channels); - /// Stop and remove thread from pool - void stopThread(); - int getID() - { - return mID; - } + /// \brief The class constructor + JackTripWorker(UdpMasterListener* udpmasterlistener, int BufferQueueLength = gDefaultQueueLength, JackTrip::underrunModeT UnderRunMode = JackTrip::WAVETABLE); + /// \brief The class destructor + virtual ~JackTripWorker(); + + /// \brief Implements the Thread Loop. + /// To start the thread, call start() ( DO NOT CALL run() ). + void run(); + /// \brief Check if the Thread is Spawning + /// \return true is it is spawning, false if it's already running + bool isSpawning(); + /// \brief Sets the JackTripWorker properties + /// \param id ID number + /// \param address + void setJackTrip(int id, + QString client_address, + uint16_t server_port, + uint16_t client_port, + int num_channels, + bool connectDefaultAudioPorts + ); + /// Stop and remove thread from pool + void stopThread(); + int getID() + { + return mID; + } private slots: - void slotTest() - { std::cout << "--- JackTripWorker TEST SLOT ---" << std::endl; } + void slotTest() + { std::cout << "--- JackTripWorker TEST SLOT ---" << std::endl; } signals: - void signalRemoveThread(); + void signalRemoveThread(); private: - int setJackTripFromClientHeader(JackTrip& jacktrip); - JackTrip::connectionModeT getConnectionModeFromHeader(); - - UdpMasterListener* mUdpMasterListener; ///< Master Listener Socket - //QHostAddress mClientAddress; ///< Client Address - uint32_t mClientAddress; - uint16_t mServerPort; ///< Server Ephemeral Incomming Port to use with Client - - /// Client Outgoing Port. By convention, the receving port will be mClientPort -1 - uint16_t mClientPort; - - /// Thread spawning internal lock. - /// If true, the prototype is working on creating (spawning) a new thread - volatile bool mSpawning; - QMutex mMutex; ///< Mutex to protect mSpawning - - int mID; ///< ID thread number - int mNumChans; ///< Number of Channels + int setJackTripFromClientHeader(JackTrip& jacktrip); + JackTrip::connectionModeT getConnectionModeFromHeader(); + + UdpMasterListener* mUdpMasterListener; ///< Master Listener Socket + //QHostAddress mClientAddress; ///< Client Address + QString mClientAddress; + uint16_t mServerPort; ///< Server Ephemeral Incomming Port to use with Client + bool m_connectDefaultAudioPorts; + + /// Client Outgoing Port. By convention, the receving port will be mClientPort -1 + uint16_t mClientPort; + + /// Thread spawning internal lock. + /// If true, the prototype is working on creating (spawning) a new thread + volatile bool mSpawning; + QMutex mMutex; ///< Mutex to protect mSpawning + JackTrip::underrunModeT mUnderRunMode; + int mBufferQueueLength; + + int mID; ///< ID thread number + int mNumChans; ///< Number of Channels +#ifdef WAIR // wair + int mNumNetRevChans; ///< Number of Net Channels = net combs + bool mWAIR; +#endif // endwhere }; diff --git a/src/JackTripWorkerMessages.h b/src/JackTripWorkerMessages.h index e3bef9a..5813f62 100644 --- a/src/JackTripWorkerMessages.h +++ b/src/JackTripWorkerMessages.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -45,30 +45,30 @@ class JackTripWorkerMessages : public QObject { - Q_OBJECT; + Q_OBJECT; public: - JackTripWorkerMessages() {}; - virtual ~JackTripWorkerMessages() {}; + JackTripWorkerMessages() {}; + virtual ~JackTripWorkerMessages() {}; - void play() - { - std::cout << "********** PALYING ***********************************" << std::endl; - QTimer *timer = new QTimer(this); - QObject::connect(timer, SIGNAL(timeout()), this, SLOT(slotTest()), Qt::QueuedConnection); - timer->start(300); - } + void play() + { + std::cout << "********** PALYING ***********************************" << std::endl; + QTimer *timer = new QTimer(this); + QObject::connect(timer, SIGNAL(timeout()), this, SLOT(slotTest()), Qt::QueuedConnection); + timer->start(300); + } public slots: - void slotTest() - { - std::cout << "---JackTripWorkerMessages slotTest()---" << std::endl; - } - + void slotTest() + { + std::cout << "---JackTripWorkerMessages slotTest()---" << std::endl; + } + signals: - void signalTest(); - /// Signal to stop the event loop inside the JackTripWorker Thread - void signalStopEventLoop(); + void signalTest(); + /// Signal to stop the event loop inside the JackTripWorker Thread + void signalStopEventLoop(); }; diff --git a/src/LoopBack.cpp b/src/LoopBack.cpp index b8a698e..448ce41 100644 --- a/src/LoopBack.cpp +++ b/src/LoopBack.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -49,10 +49,9 @@ using std::cout; using std::endl; //******************************************************************************* void LoopBack::compute(int nframes, float** inputs, float** outputs) { - for ( int i = 0; i < getNumInputs(); i++ ) { - // Everything that comes out, copy back to inputs - //memcpy(inputs[i], outputs[i], sizeof(sample_t) * nframes); - memcpy(outputs[i], inputs[i], sizeof(sample_t) * nframes); - } + for ( int i = 0; i < getNumInputs(); i++ ) { + // Everything that comes out, copy back to inputs + //memcpy(inputs[i], outputs[i], sizeof(sample_t) * nframes); + memcpy(outputs[i], inputs[i], sizeof(sample_t) * nframes); + } } - diff --git a/src/LoopBack.h b/src/LoopBack.h index c25de27..797898d 100644 --- a/src/LoopBack.h +++ b/src/LoopBack.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -54,17 +54,17 @@ class LoopBack : public ProcessPlugin { public: - /// \brief The class constructor sets the number of channels to connect as loopback - LoopBack(int numchans) { mNumChannels = numchans; }; - /// \brief The class destructor - virtual ~LoopBack() {}; + /// \brief The class constructor sets the number of channels to connect as loopback + LoopBack(int numchans) { mNumChannels = numchans; }; + /// \brief The class destructor + virtual ~LoopBack() {}; - virtual int getNumInputs() { return(mNumChannels); }; - virtual int getNumOutputs() { return(mNumChannels); }; - virtual void compute(int nframes, float** inputs, float** outputs); + virtual int getNumInputs() { return(mNumChannels); }; + virtual int getNumOutputs() { return(mNumChannels); }; + virtual void compute(int nframes, float** inputs, float** outputs); private: - int mNumChannels; + int mNumChannels; }; #endif diff --git a/src/NetKS.h b/src/NetKS.h index 864be52..ea3a788 100644 --- a/src/NetKS.h +++ b/src/NetKS.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -51,11 +51,11 @@ */ class NetKS : public ProcessPlugin { - Q_OBJECT; + Q_OBJECT; public: - /* + /* void play() { std::cout << "********** PALYING ***********************************" << std::endl; @@ -67,71 +67,70 @@ public: private slots: - /// \brief Stlot to excite (play) the string - void exciteString() - { - std::cout << "========= EXTICING STRING ===========" << std::endl; - fbutton0 = 1.0; - //std::cout << fbutton0 << std::endl; - QThread::usleep(280000); /// \todo Define this number based on the sampling rate and buffer size - fbutton0 = 0.0; - //std::cout << fbutton0 << std::endl; - } - - //=========== FROM FAUST =================================================== - private: - float fbutton0; - float fVec0[2]; - float fRec0[2]; - int iRec1[2]; - float fVec1[2]; - public: - virtual int getNumInputs() { return 1; } - virtual int getNumOutputs() { return 1; } - static void classInit(int /*samplingFreq*/) {} - virtual void instanceInit(int samplingFreq) { - fSamplingFreq = samplingFreq; - fbutton0 = 0.0; - for (int i=0; i<2; i++) fVec0[i] = 0; - for (int i=0; i<2; i++) fRec0[i] = 0; - for (int i=0; i<2; i++) iRec1[i] = 0; - for (int i=0; i<2; i++) fVec1[i] = 0; - } - virtual void init(int samplingFreq) { - classInit(samplingFreq); - instanceInit(samplingFreq); - } - /* - virtual void buildUserInterface(UI* interface) { - interface->openVerticalBox("excitator"); - interface->addButton("play", &fbutton0); - interface->closeBox(); - } + /// \brief Stlot to excite (play) the string + void exciteString() + { + std::cout << "========= EXTICING STRING ===========" << std::endl; + fbutton0 = 1.0; + //std::cout << fbutton0 << std::endl; + QThread::usleep(280000); /// \todo Define this number based on the sampling rate and buffer size + fbutton0 = 0.0; + //std::cout << fbutton0 << std::endl; + } + + //=========== FROM FAUST =================================================== +private: + float fbutton0; + float fVec0[2]; + float fRec0[2]; + int iRec1[2]; + float fVec1[2]; +public: + virtual int getNumInputs() { return 1; } + virtual int getNumOutputs() { return 1; } + static void classInit(int /*samplingFreq*/) {} + virtual void instanceInit(int samplingFreq) { + fSamplingFreq = samplingFreq; + fbutton0 = 0.0; + for (int i=0; i<2; i++) fVec0[i] = 0; + for (int i=0; i<2; i++) fRec0[i] = 0; + for (int i=0; i<2; i++) iRec1[i] = 0; + for (int i=0; i<2; i++) fVec1[i] = 0; + } + virtual void init(int samplingFreq) { + classInit(samplingFreq); + instanceInit(samplingFreq); + } + /* + virtual void buildUserInterface(UI* interface) { + interface->openVerticalBox("excitator"); + interface->addButton("play", &fbutton0); + interface->closeBox(); + } */ - virtual void compute (int count, float** input, float** output) { - float* input0 = input[0]; - float* output0 = output[0]; - float fSlow0 = fbutton0; - for (int i=0; i 0.000000f) + fRec0[1]) - (3.333333e-03f * (fRec0[1] > 0.000000f))); - iRec1[0] = (12345 + (1103515245 * iRec1[1])); - float fTemp0 = ((4.190951e-10f * iRec1[0]) * (fRec0[0] > 0.000000f)); - float fTemp1 = input0[i]; - fVec1[0] = (fTemp1 + fTemp0); - output0[i] = (0.500000f * ((fTemp0 + fTemp1) + fVec1[1])); - // post processing - fVec1[1] = fVec1[0]; - iRec1[1] = iRec1[0]; - fRec0[1] = fRec0[0]; - fVec0[1] = fVec0[0]; - } - } - - //============================================================================ + virtual void compute (int count, float** input, float** output) { + float* input0 = input[0]; + float* output0 = output[0]; + float fSlow0 = fbutton0; + for (int i=0; i 0.000000f) + fRec0[1]) - (3.333333e-03f * (fRec0[1] > 0.000000f))); + iRec1[0] = (12345 + (1103515245 * iRec1[1])); + float fTemp0 = ((4.190951e-10f * iRec1[0]) * (fRec0[0] > 0.000000f)); + float fTemp1 = input0[i]; + fVec1[0] = (fTemp1 + fTemp0); + output0[i] = (0.500000f * ((fTemp0 + fTemp1) + fVec1[1])); + // post processing + fVec1[1] = fVec1[0]; + iRec1[1] = iRec1[0]; + fRec0[1] = fRec0[0]; + fVec0[1] = fVec0[0]; + } + } + + //============================================================================ }; #endif // __NETKS_H__ - diff --git a/src/PacketHeader.cpp b/src/PacketHeader.cpp index 41e41dc..56ac606 100644 --- a/src/PacketHeader.cpp +++ b/src/PacketHeader.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -53,14 +53,14 @@ using std::cout; using std::endl; void GetSystemTimeAsFileTime(FILETIME*); inline int gettimeofday(struct timeval* p, void* tz /* IGNORED */) { - union { - long long ns100; /*time since 1 Jan 1601 in 100ns units */ - FILETIME ft; - } now; - GetSystemTimeAsFileTime( &(now.ft) ); - p->tv_usec=(long)((now.ns100 / 10LL) % 1000000LL ); - p->tv_sec= (long)((now.ns100-(116444736000000000LL))/10000000LL); - return 0; + union { + long long ns100; /*time since 1 Jan 1601 in 100ns units */ + FILETIME ft; + } now; + GetSystemTimeAsFileTime( &(now.ft) ); + p->tv_usec=(long)((now.ns100 / 10LL) % 1000000LL ); + p->tv_sec= (long)((now.ns100-(116444736000000000LL))/10000000LL); + return 0; } #else /* Must be defined somewhere else */ @@ -73,18 +73,18 @@ int gettimeofday(struct timeval* p, void* tz /* IGNORED */); //####################### PacketHeader ################################## //####################################################################### //*********************************************************************** -PacketHeader::PacketHeader(JackTrip* jacktrip) : - mSeqNumber(0), mJackTrip(jacktrip) +PacketHeader::PacketHeader(JackTrip* jacktrip) : + mSeqNumber(0), mJackTrip(jacktrip) {} //*********************************************************************** uint64_t PacketHeader::usecTime() { - struct timeval tv; - gettimeofday (&tv, NULL); - return ( (tv.tv_sec * 1000000) + // seconds - (tv.tv_usec) ); // plus the microseconds. Type suseconds_t, range [-1, 1000000] + struct timeval tv; + gettimeofday (&tv, NULL); + return ( (tv.tv_sec * 1000000) + // seconds + (tv.tv_usec) ); // plus the microseconds. Type suseconds_t, range [-1, 1000000] } @@ -94,109 +94,109 @@ uint64_t PacketHeader::usecTime() //####################### DefaultHeader ################################# //####################################################################### //*********************************************************************** -DefaultHeader::DefaultHeader(JackTrip* jacktrip) : - PacketHeader(jacktrip), mJackTrip(jacktrip) +DefaultHeader::DefaultHeader(JackTrip* jacktrip) : + PacketHeader(jacktrip), mJackTrip(jacktrip) { - mHeader.TimeStamp = 0; - mHeader.SeqNumber = 0; - mHeader.BufferSize = 0; - mHeader.SamplingRate = 0; - mHeader.BitResolution = 0; - //mHeader.NumInChannels = 0; - //mHeader.NumOutChannels = 0; - mHeader.NumChannels = 0; - mHeader.ConnectionMode = 0; + mHeader.TimeStamp = 0; + mHeader.SeqNumber = 0; + mHeader.BufferSize = 0; + mHeader.SamplingRate = 0; + mHeader.BitResolution = 0; + //mHeader.NumInChannels = 0; + //mHeader.NumOutChannels = 0; + mHeader.NumChannels = 0; + mHeader.ConnectionMode = 0; } //*********************************************************************** void DefaultHeader::fillHeaderCommonFromAudio() { - mHeader.TimeStamp = PacketHeader::usecTime(); - mHeader.BufferSize = mJackTrip->getBufferSizeInSamples(); - mHeader.SamplingRate = mJackTrip->getSampleRateType (); - mHeader.BitResolution = mJackTrip->getAudioBitResolution(); - mHeader.NumChannels = mJackTrip->getNumChannels(); - mHeader.ConnectionMode = static_cast(mJackTrip->getConnectionMode()); - //printHeader(); + mHeader.TimeStamp = PacketHeader::usecTime(); + mHeader.BufferSize = mJackTrip->getBufferSizeInSamples(); + mHeader.SamplingRate = mJackTrip->getSampleRateType (); + mHeader.BitResolution = mJackTrip->getAudioBitResolution(); + mHeader.NumChannels = mJackTrip->getNumChannels(); + mHeader.ConnectionMode = static_cast(mJackTrip->getConnectionMode()); + //printHeader(); } //*********************************************************************** void DefaultHeader::checkPeerSettings(int8_t* full_packet) { - bool error = false; - - DefaultHeaderStruct* peer_header; - peer_header = reinterpret_cast(full_packet); + bool error = false; + + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + + // Check Buffer Size + if ( peer_header->BufferSize != mHeader.BufferSize ) + { + std::cerr << "ERROR: Peer Buffer Size is : " << peer_header->BufferSize << endl; + std::cerr << " Local Buffer Size is : " << mHeader.BufferSize << endl; + std::cerr << "Make sure both machines use same buffer size" << endl; + std::cerr << gPrintSeparator << endl; + error = true; + } - // Check Buffer Size - if ( peer_header->BufferSize != mHeader.BufferSize ) + // Check Sampling Rate + if ( peer_header->SamplingRate != mHeader.SamplingRate ) { - std::cerr << "ERROR: Peer Buffer Size is : " << peer_header->BufferSize << endl; - std::cerr << " Local Buffer Size is : " << mHeader.BufferSize << endl; - std::cerr << "Make sure both machines use same buffer size" << endl; - std::cerr << gPrintSeparator << endl; - error = true; + std::cerr << "ERROR: Peer Sampling Rate is : " << + AudioInterface::getSampleRateFromType + ( static_cast(peer_header->SamplingRate) ) << endl; + std::cerr << " Local Sampling Rate is : " << + AudioInterface::getSampleRateFromType + ( static_cast(mHeader.SamplingRate) ) << endl; + std::cerr << "Make sure both machines use the same Sampling Rate" << endl; + std::cerr << gPrintSeparator << endl; + error = true; } - // Check Sampling Rate - if ( peer_header->SamplingRate != mHeader.SamplingRate ) - { - std::cerr << "ERROR: Peer Sampling Rate is : " << - AudioInterface::getSampleRateFromType - ( static_cast(peer_header->SamplingRate) ) << endl; - std::cerr << " Local Sampling Rate is : " << - AudioInterface::getSampleRateFromType - ( static_cast(mHeader.SamplingRate) ) << endl; - std::cerr << "Make sure both machines use the same Sampling Rate" << endl; - std::cerr << gPrintSeparator << endl; - error = true; - } - - // Check Audio Bit Resolution - if ( peer_header->BitResolution != mHeader.BitResolution ) + // Check Audio Bit Resolution + if ( peer_header->BitResolution != mHeader.BitResolution ) { - std::cerr << "ERROR: Peer Audio Bit Resolution is : " - << static_cast(peer_header->BitResolution) << endl; - std::cerr << " Local Audio Bit Resolution is : " - << static_cast(mHeader.BitResolution) << endl; - std::cerr << "Make sure both machines use the same Bit Resolution" << endl; - std::cerr << gPrintSeparator << endl; - error = true; + std::cerr << "ERROR: Peer Audio Bit Resolution is : " + << static_cast(peer_header->BitResolution) << endl; + std::cerr << " Local Audio Bit Resolution is : " + << static_cast(mHeader.BitResolution) << endl; + std::cerr << "Make sure both machines use the same Bit Resolution" << endl; + std::cerr << gPrintSeparator << endl; + error = true; } - // Exit program if error - if (error) + // Exit program if error + if (error) { - //std::cerr << "Exiting program..." << endl; - //std::exit(1); - //throw std::logic_error("Local and Peer Settings don't match"); - emit signalError("Local and Peer Settings don't match"); + //std::cerr << "Exiting program..." << endl; + //std::exit(1); + //throw std::logic_error("Local and Peer Settings don't match"); + emit signalError("Local and Peer Settings don't match"); } - /// \todo Check number of channels and other parameters + /// \todo Check number of channels and other parameters } //*********************************************************************** void DefaultHeader::printHeader() const { - cout << "Default Packet Header:" << endl; - cout << "Buffer Size = " << static_cast(mHeader.BufferSize) << endl; - // Get the sample rate in Hz form the AudioInterface::samplingRateT - int sample_rate = - AudioInterface::getSampleRateFromType - ( static_cast(mHeader.SamplingRate) ); - cout << "Sampling Rate = " << sample_rate << endl; - cout << "Audio Bit Resolutions = " << static_cast(mHeader.BitResolution) << endl; - //cout << "Number of Input Channels = " << static_cast(mHeader.NumInChannels) << endl; - //cout << "Number of Output Channels = " << static_cast(mHeader.NumOutChannels) << endl; - cout << "Number of Channels = " << static_cast(mHeader.NumChannels) << endl; - cout << "Sequence Number = " << static_cast(mHeader.SeqNumber) << endl; - cout << "Time Stamp = " << mHeader.TimeStamp << endl; - cout << "Connection Mode = " << static_cast(mHeader.ConnectionMode) << endl; - cout << gPrintSeparator << endl; - //cout << sizeof(mHeader) << endl; + cout << "Default Packet Header:" << endl; + cout << "Buffer Size = " << static_cast(mHeader.BufferSize) << endl; + // Get the sample rate in Hz form the AudioInterface::samplingRateT + int sample_rate = + AudioInterface::getSampleRateFromType + ( static_cast(mHeader.SamplingRate) ); + cout << "Sampling Rate = " << sample_rate << endl; + cout << "Audio Bit Resolutions = " << static_cast(mHeader.BitResolution) << endl; + //cout << "Number of Input Channels = " << static_cast(mHeader.NumInChannels) << endl; + //cout << "Number of Output Channels = " << static_cast(mHeader.NumOutChannels) << endl; + cout << "Number of Channels = " << static_cast(mHeader.NumChannels) << endl; + cout << "Sequence Number = " << static_cast(mHeader.SeqNumber) << endl; + cout << "Time Stamp = " << mHeader.TimeStamp << endl; + cout << "Connection Mode = " << static_cast(mHeader.ConnectionMode) << endl; + cout << gPrintSeparator << endl; + //cout << sizeof(mHeader) << endl; } @@ -204,63 +204,63 @@ void DefaultHeader::printHeader() const //*********************************************************************** uint64_t DefaultHeader::getPeerTimeStamp(int8_t* full_packet) const { - DefaultHeaderStruct* peer_header; - peer_header = reinterpret_cast(full_packet); - return peer_header->TimeStamp; + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + return peer_header->TimeStamp; } //*********************************************************************** uint16_t DefaultHeader::getPeerSequenceNumber(int8_t* full_packet) const { - DefaultHeaderStruct* peer_header; - peer_header = reinterpret_cast(full_packet); - return peer_header->SeqNumber; + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + return peer_header->SeqNumber; } //*********************************************************************** uint16_t DefaultHeader::getPeerBufferSize(int8_t* full_packet) const { - DefaultHeaderStruct* peer_header; - peer_header = reinterpret_cast(full_packet); - return peer_header->BufferSize; + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + return peer_header->BufferSize; } //*********************************************************************** uint8_t DefaultHeader::getPeerSamplingRate(int8_t* full_packet) const { - DefaultHeaderStruct* peer_header; - peer_header = reinterpret_cast(full_packet); - return peer_header->SamplingRate; + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + return peer_header->SamplingRate; } //*********************************************************************** uint8_t DefaultHeader::getPeerBitResolution(int8_t* full_packet) const { - DefaultHeaderStruct* peer_header; - peer_header = reinterpret_cast(full_packet); - return peer_header->BitResolution; + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + return peer_header->BitResolution; } //*********************************************************************** uint8_t DefaultHeader::getPeerNumChannels(int8_t* full_packet) const { - DefaultHeaderStruct* peer_header; - peer_header = reinterpret_cast(full_packet); - return peer_header->NumChannels; + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + return peer_header->NumChannels; } //*********************************************************************** uint8_t DefaultHeader::getPeerConnectionMode(int8_t* full_packet) const { - DefaultHeaderStruct* peer_header; - peer_header = reinterpret_cast(full_packet); - return static_cast(peer_header->ConnectionMode); + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + return static_cast(peer_header->ConnectionMode); } @@ -273,66 +273,66 @@ uint8_t DefaultHeader::getPeerConnectionMode(int8_t* full_packet) const //####################################################################### //*********************************************************************** JamLinkHeader::JamLinkHeader(JackTrip* jacktrip) : - PacketHeader(jacktrip), mJackTrip(jacktrip) + PacketHeader(jacktrip), mJackTrip(jacktrip) { - mHeader.Common = 0; - mHeader.SeqNumber = 0; - mHeader.TimeStamp = 0; + mHeader.Common = 0; + mHeader.SeqNumber = 0; + mHeader.TimeStamp = 0; } //*********************************************************************** void JamLinkHeader::fillHeaderCommonFromAudio() { - // Check number of channels - int num_inchannels = mJackTrip->getNumInputChannels(); - if ( num_inchannels != 1 ) { - //std::cerr << "ERROR: JamLink only support ONE channel. Run JackTrip using only one channel" - // << endl; - //std::exit(1); - //std::cerr << "WARINING: JamLink only support ONE channel. Run JackTrip using only one channel" << endl; - //throw std::logic_error("JamLink only support ONE channel. Run JackTrip using only one channel"); - emit signalError("JamLink only support ONE channel. Run JackTrip using only one channel"); - - } - - // Sampling Rate - int rate_type = mJackTrip->getSampleRateType(); - if ( rate_type != AudioInterface::SR48 ) { - //std::cerr << "WARINING: JamLink only support 48kHz for communication with JackTrip at the moment." << endl; - //throw std::logic_error("ERROR: JamLink only support 48kHz for communication with JackTrip at the moment."); - emit signalError("ERROR: JamLink only support 48kHz for communication with JackTrip at the moment."); - } - - // Check Buffer Size - int buf_size = mJackTrip->getBufferSizeInSamples(); - if ( buf_size != 64 ) { - //std::cerr << "WARINING: JamLink only support 64 buffer size for communication with JackTrip at the moment." << endl; - //throw std::logic_error("ERROR: JamLink only support 64 buffer size for communication with JackTrip at the moment."); - emit signalError("ERROR: JamLink only support 64 buffer size for communication with JackTrip at the moment."); - } - - mHeader.Common = (ETX_MONO | ETX_16BIT | ETX_XTND) + 64; - switch (rate_type) + // Check number of channels + int num_inchannels = mJackTrip->getNumInputChannels(); + if ( num_inchannels != 1 ) { + //std::cerr << "ERROR: JamLink only support ONE channel. Run JackTrip using only one channel" + // << endl; + //std::exit(1); + //std::cerr << "WARINING: JamLink only support ONE channel. Run JackTrip using only one channel" << endl; + //throw std::logic_error("JamLink only support ONE channel. Run JackTrip using only one channel"); + emit signalError("JamLink only support ONE channel. Run JackTrip using only one channel"); + + } + + // Sampling Rate + int rate_type = mJackTrip->getSampleRateType(); + if ( rate_type != AudioInterface::SR48 ) { + //std::cerr << "WARINING: JamLink only support 48kHz for communication with JackTrip at the moment." << endl; + //throw std::logic_error("ERROR: JamLink only support 48kHz for communication with JackTrip at the moment."); + emit signalError("ERROR: JamLink only support 48kHz for communication with JackTrip at the moment."); + } + + // Check Buffer Size + int buf_size = mJackTrip->getBufferSizeInSamples(); + if ( buf_size != 64 ) { + //std::cerr << "WARINING: JamLink only support 64 buffer size for communication with JackTrip at the moment." << endl; + //throw std::logic_error("ERROR: JamLink only support 64 buffer size for communication with JackTrip at the moment."); + emit signalError("ERROR: JamLink only support 64 buffer size for communication with JackTrip at the moment."); + } + + mHeader.Common = (ETX_MONO | ETX_16BIT | ETX_XTND) + 64; + switch (rate_type) { case AudioInterface::SR48 : - mHeader.Common = (mHeader.Common | ETX_48KHZ); - break; + mHeader.Common = (mHeader.Common | ETX_48KHZ); + break; case AudioInterface::SR44 : - mHeader.Common = (mHeader.Common | ETX_44KHZ); - break; + mHeader.Common = (mHeader.Common | ETX_44KHZ); + break; case AudioInterface::SR32 : - mHeader.Common = (mHeader.Common | ETX_32KHZ); - break; + mHeader.Common = (mHeader.Common | ETX_32KHZ); + break; case AudioInterface::SR22 : - mHeader.Common = (mHeader.Common | ETX_22KHZ); - break; + mHeader.Common = (mHeader.Common | ETX_22KHZ); + break; default: - //std::cerr << "ERROR: Sample rate not supported by JamLink" << endl; - //std::exit(1); - //throw std::out_of_range("Sample rate not supported by JamLink"); - emit signalError("Sample rate not supported by JamLink."); - break; + //std::cerr << "ERROR: Sample rate not supported by JamLink" << endl; + //std::exit(1); + //throw std::out_of_range("Sample rate not supported by JamLink"); + emit signalError("Sample rate not supported by JamLink."); + break; } } @@ -346,5 +346,5 @@ void JamLinkHeader::fillHeaderCommonFromAudio() //####################################################################### //*********************************************************************** EmptyHeader::EmptyHeader(JackTrip* jacktrip) : - PacketHeader(jacktrip), mJackTrip(jacktrip) + PacketHeader(jacktrip), mJackTrip(jacktrip) {} diff --git a/src/PacketHeader.h b/src/PacketHeader.h index 3988039..094d424 100644 --- a/src/PacketHeader.h +++ b/src/PacketHeader.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -57,16 +57,16 @@ struct HeaderStruct{}; struct DefaultHeaderStruct : public HeaderStruct { public: - // watch out for alignment... - uint64_t TimeStamp; ///< Time Stamp - uint16_t SeqNumber; ///< Sequence Number - uint16_t BufferSize; ///< Buffer Size in Samples - uint8_t SamplingRate; ///< Sampling Rate in JackAudioInterface::samplingRateT - uint8_t BitResolution; ///< Audio Bit Resolution - //uint8_t NumInChannels; ///< Number of Input Channels - //uint8_t NumOutChannels; ///< Number of Output Channels - uint8_t NumChannels; ///< Number of Channels, we assume input and outputs are the same - uint8_t ConnectionMode; + // watch out for alignment... + uint64_t TimeStamp; ///< Time Stamp + uint16_t SeqNumber; ///< Sequence Number + uint16_t BufferSize; ///< Buffer Size in Samples + uint8_t SamplingRate; ///< Sampling Rate in JackAudioInterface::samplingRateT + uint8_t BitResolution; ///< Audio Bit Resolution + //uint8_t NumInChannels; ///< Number of Input Channels + //uint8_t NumOutChannels; ///< Number of Output Channels + uint8_t NumChannels; ///< Number of Channels, we assume input and outputs are the same + uint8_t ConnectionMode; }; //--------------------------------------------------------- @@ -77,7 +77,7 @@ public: /* Its bit map is as follows: (b15-msb) */ /* B15:reserved, B14:extended header, B13 Stereo, B12 not 16-bit */ /* B11-B9: 0-48 Khz, 1-44 Khz, 2-32 Khz, 3-24 Khz, */ -/* 4-22 Khz, 5-16 Khz, 6-11 Khz, 7-8 Khz */ +/* 4-22 Khz, 5-16 Khz, 6-11 Khz, 7-8 Khz */ /* B8-0: Samples in packet */ /************************************************************************/ const unsigned short ETX_RSVD = (0<<15); @@ -100,10 +100,10 @@ const unsigned short ETX_8KHZ = (7<<9); /// \brief JamLink Header Struct struct JamLinkHeaderStuct : public HeaderStruct { - // watch out for alignment -- need to be on 4 byte chunks - uint16_t Common; ///< Common part of the header, 16 bit - uint16_t SeqNumber; ///< Sequence Number - uint32_t TimeStamp; ///< Time Stamp + // watch out for alignment -- need to be on 4 byte chunks + uint16_t Common; ///< Common part of the header, 16 bit + uint16_t SeqNumber; ///< Sequence Number + uint32_t TimeStamp; ///< Time Stamp }; @@ -116,61 +116,61 @@ struct JamLinkHeaderStuct : public HeaderStruct */ class PacketHeader : public QObject { - Q_OBJECT; + Q_OBJECT; public: - /// \brief The class Constructor - PacketHeader(JackTrip* jacktrip); - /// \brief The class Destructor - virtual ~PacketHeader() {} - - /// \brief Return a time stamp in microseconds - /// \return Time stamp: microseconds since midnight (0 hour), January 1, 1970 - static uint64_t usecTime(); - /// \todo Implement this using a JackTrip Method (Mediator) member instead of the - /// reference to JackAudio - virtual void fillHeaderCommonFromAudio() = 0; - /// \brief Parse the packet header and take appropriate measures (like change settings, or - /// quit the program if peer settings don't match) - virtual void parseHeader() = 0; - virtual void checkPeerSettings(int8_t* full_packet) = 0; - - virtual uint64_t getPeerTimeStamp(int8_t* full_packet) const = 0; - virtual uint16_t getPeerSequenceNumber(int8_t* full_packet) const = 0; - virtual uint16_t getPeerBufferSize(int8_t* full_packet) const = 0; - virtual uint8_t getPeerSamplingRate(int8_t* full_packet) const = 0; - virtual uint8_t getPeerBitResolution(int8_t* full_packet) const = 0; - virtual uint8_t getPeerNumChannels(int8_t* full_packet) const = 0; - virtual uint8_t getPeerConnectionMode(int8_t* full_packet) const = 0; - - /// \brief Increase sequence number for counter, a 16bit number - virtual void increaseSequenceNumber() - { mSeqNumber++; } - /// \brief Returns the current sequence number - /// \return 16bit Sequence number - virtual uint16_t getSequenceNumber() const - { return mSeqNumber; } - /// \brief Get the header size in bytes - virtual int getHeaderSizeInBytes() const = 0; - virtual void putHeaderInPacketBaseClass(int8_t* full_packet, - const HeaderStruct& header_struct) - { - std::memcpy(full_packet, reinterpret_cast(&header_struct), - getHeaderSizeInBytes() ); - } - /// \brief Put the header in buffer pointed by full_packet - /// \param full_packet Pointer to full packet (audio+header). Size must be - /// sizeof(header part) + sizeof(audio part) - virtual void putHeaderInPacket(int8_t* full_packet) = 0; + /// \brief The class Constructor + PacketHeader(JackTrip* jacktrip); + /// \brief The class Destructor + virtual ~PacketHeader() {} + + /// \brief Return a time stamp in microseconds + /// \return Time stamp: microseconds since midnight (0 hour), January 1, 1970 + static uint64_t usecTime(); + /// \todo Implement this using a JackTrip Method (Mediator) member instead of the + /// reference to JackAudio + virtual void fillHeaderCommonFromAudio() = 0; + /// \brief Parse the packet header and take appropriate measures (like change settings, or + /// quit the program if peer settings don't match) + virtual void parseHeader() = 0; + virtual void checkPeerSettings(int8_t* full_packet) = 0; + + virtual uint64_t getPeerTimeStamp(int8_t* full_packet) const = 0; + virtual uint16_t getPeerSequenceNumber(int8_t* full_packet) const = 0; + virtual uint16_t getPeerBufferSize(int8_t* full_packet) const = 0; + virtual uint8_t getPeerSamplingRate(int8_t* full_packet) const = 0; + virtual uint8_t getPeerBitResolution(int8_t* full_packet) const = 0; + virtual uint8_t getPeerNumChannels(int8_t* full_packet) const = 0; + virtual uint8_t getPeerConnectionMode(int8_t* full_packet) const = 0; + + /// \brief Increase sequence number for counter, a 16bit number + virtual void increaseSequenceNumber() + { mSeqNumber++; } + /// \brief Returns the current sequence number + /// \return 16bit Sequence number + virtual uint16_t getSequenceNumber() const + { return mSeqNumber; } + /// \brief Get the header size in bytes + virtual int getHeaderSizeInBytes() const = 0; + virtual void putHeaderInPacketBaseClass(int8_t* full_packet, + const HeaderStruct& header_struct) + { + std::memcpy(full_packet, reinterpret_cast(&header_struct), + getHeaderSizeInBytes() ); + } + /// \brief Put the header in buffer pointed by full_packet + /// \param full_packet Pointer to full packet (audio+header). Size must be + /// sizeof(header part) + sizeof(audio part) + virtual void putHeaderInPacket(int8_t* full_packet) = 0; signals: - void signalError(const char* error_message); + void signalError(const char* error_message); private: - uint16_t mSeqNumber; - JackTrip* mJackTrip; ///< JackTrip mediator class + uint16_t mSeqNumber; + JackTrip* mJackTrip; ///< JackTrip mediator class }; @@ -185,38 +185,38 @@ class DefaultHeader : public PacketHeader { public: - DefaultHeader(JackTrip* jacktrip); - virtual ~DefaultHeader() {} - - virtual void fillHeaderCommonFromAudio(); - virtual void parseHeader() {} - virtual void checkPeerSettings(int8_t* full_packet); - virtual void increaseSequenceNumber() - { mHeader.SeqNumber++; } - virtual uint16_t getSequenceNumber() const - { return mHeader.SeqNumber; } - virtual int getHeaderSizeInBytes() const { return sizeof(mHeader); } - virtual void putHeaderInPacket(int8_t* full_packet) - { putHeaderInPacketBaseClass(full_packet, mHeader); } - void printHeader() const; - uint8_t getConnectionMode() const - { return mHeader.ConnectionMode; } - uint8_t getNumChannels() const - { return mHeader.NumChannels; } - - - virtual uint64_t getPeerTimeStamp(int8_t* full_packet) const; - virtual uint16_t getPeerSequenceNumber(int8_t* full_packet) const; - virtual uint16_t getPeerBufferSize(int8_t* full_packet) const; - virtual uint8_t getPeerSamplingRate(int8_t* full_packet) const; - virtual uint8_t getPeerBitResolution(int8_t* full_packet) const; - virtual uint8_t getPeerNumChannels(int8_t* full_packet) const; - virtual uint8_t getPeerConnectionMode(int8_t* full_packet) const; + DefaultHeader(JackTrip* jacktrip); + virtual ~DefaultHeader() {} + + virtual void fillHeaderCommonFromAudio(); + virtual void parseHeader() {} + virtual void checkPeerSettings(int8_t* full_packet); + virtual void increaseSequenceNumber() + { mHeader.SeqNumber++; } + virtual uint16_t getSequenceNumber() const + { return mHeader.SeqNumber; } + virtual int getHeaderSizeInBytes() const { return sizeof(mHeader); } + virtual void putHeaderInPacket(int8_t* full_packet) + { putHeaderInPacketBaseClass(full_packet, mHeader); } + void printHeader() const; + uint8_t getConnectionMode() const + { return mHeader.ConnectionMode; } + uint8_t getNumChannels() const + { return mHeader.NumChannels; } + + + virtual uint64_t getPeerTimeStamp(int8_t* full_packet) const; + virtual uint16_t getPeerSequenceNumber(int8_t* full_packet) const; + virtual uint16_t getPeerBufferSize(int8_t* full_packet) const; + virtual uint8_t getPeerSamplingRate(int8_t* full_packet) const; + virtual uint8_t getPeerBitResolution(int8_t* full_packet) const; + virtual uint8_t getPeerNumChannels(int8_t* full_packet) const; + virtual uint8_t getPeerConnectionMode(int8_t* full_packet) const; private: - DefaultHeaderStruct mHeader;///< Default Header Struct - JackTrip* mJackTrip; ///< JackTrip mediator class + DefaultHeaderStruct mHeader;///< Default Header Struct + JackTrip* mJackTrip; ///< JackTrip mediator class }; @@ -231,30 +231,30 @@ private: class JamLinkHeader : public PacketHeader { public: - - JamLinkHeader(JackTrip* jacktrip); - virtual ~JamLinkHeader() {} - - virtual void fillHeaderCommonFromAudio(); - virtual void parseHeader() {} - virtual void checkPeerSettings(int8_t* /*full_packet*/) {} - - virtual uint64_t getPeerTimeStamp(int8_t* /*full_packet*/) const { return 0; } - virtual uint16_t getPeerSequenceNumber(int8_t* /*full_packet*/) const { return 0; } - virtual uint16_t getPeerBufferSize(int8_t* /*full_packet*/) const { return 0; } - virtual uint8_t getPeerSamplingRate(int8_t* /*full_packet*/) const { return 0; } - virtual uint8_t getPeerBitResolution(int8_t* /*full_packet*/) const { return 0; } - virtual uint8_t getPeerNumChannels(int8_t* /*full_packet*/) const { return 0; } - virtual uint8_t getPeerConnectionMode(int8_t* /*full_packet*/) const { return 0; } - - virtual void increaseSequenceNumber() {} - virtual int getHeaderSizeInBytes() const { return sizeof(mHeader); } - virtual void putHeaderInPacket(int8_t* full_packet) - { putHeaderInPacketBaseClass(full_packet, mHeader); } + + JamLinkHeader(JackTrip* jacktrip); + virtual ~JamLinkHeader() {} + + virtual void fillHeaderCommonFromAudio(); + virtual void parseHeader() {} + virtual void checkPeerSettings(int8_t* /*full_packet*/) {} + + virtual uint64_t getPeerTimeStamp(int8_t* /*full_packet*/) const { return 0; } + virtual uint16_t getPeerSequenceNumber(int8_t* /*full_packet*/) const { return 0; } + virtual uint16_t getPeerBufferSize(int8_t* /*full_packet*/) const { return 0; } + virtual uint8_t getPeerSamplingRate(int8_t* /*full_packet*/) const { return 0; } + virtual uint8_t getPeerBitResolution(int8_t* /*full_packet*/) const { return 0; } + virtual uint8_t getPeerNumChannels(int8_t* /*full_packet*/) const { return 0; } + virtual uint8_t getPeerConnectionMode(int8_t* /*full_packet*/) const { return 0; } + + virtual void increaseSequenceNumber() {} + virtual int getHeaderSizeInBytes() const { return sizeof(mHeader); } + virtual void putHeaderInPacket(int8_t* full_packet) + { putHeaderInPacketBaseClass(full_packet, mHeader); } private: - JamLinkHeaderStuct mHeader; ///< JamLink Header Struct - JackTrip* mJackTrip; ///< JackTrip mediator class + JamLinkHeaderStuct mHeader; ///< JamLink Header Struct + JackTrip* mJackTrip; ///< JackTrip mediator class }; @@ -268,28 +268,28 @@ private: class EmptyHeader : public PacketHeader { public: - - EmptyHeader(JackTrip* jacktrip); - virtual ~EmptyHeader() {} - - virtual void fillHeaderCommonFromAudio() {} - virtual void parseHeader() {} - virtual void checkPeerSettings(int8_t* /*full_packet*/) {} - virtual void increaseSequenceNumber() {} - virtual int getHeaderSizeInBytes() const { return 0; } - - virtual uint64_t getPeerTimeStamp(int8_t* /*full_packet*/) const { return 0; } - virtual uint16_t getPeerSequenceNumber(int8_t* /*full_packet*/) const { return 0; } - virtual uint16_t getPeerBufferSize(int8_t* /*full_packet*/) const { return 0; } - virtual uint8_t getPeerSamplingRate(int8_t* /*full_packet*/) const { return 0; } - virtual uint8_t getPeerBitResolution(int8_t* /*full_packet*/) const { return 0; } - virtual uint8_t getPeerNumChannels(int8_t* /*full_packet*/) const { return 0; } - virtual uint8_t getPeerConnectionMode(int8_t* /*full_packet*/) const { return 0; } - - virtual void putHeaderInPacket(int8_t* /*full_packet*/) {} + + EmptyHeader(JackTrip* jacktrip); + virtual ~EmptyHeader() {} + + virtual void fillHeaderCommonFromAudio() {} + virtual void parseHeader() {} + virtual void checkPeerSettings(int8_t* /*full_packet*/) {} + virtual void increaseSequenceNumber() {} + virtual int getHeaderSizeInBytes() const { return 0; } + + virtual uint64_t getPeerTimeStamp(int8_t* /*full_packet*/) const { return 0; } + virtual uint16_t getPeerSequenceNumber(int8_t* /*full_packet*/) const { return 0; } + virtual uint16_t getPeerBufferSize(int8_t* /*full_packet*/) const { return 0; } + virtual uint8_t getPeerSamplingRate(int8_t* /*full_packet*/) const { return 0; } + virtual uint8_t getPeerBitResolution(int8_t* /*full_packet*/) const { return 0; } + virtual uint8_t getPeerNumChannels(int8_t* /*full_packet*/) const { return 0; } + virtual uint8_t getPeerConnectionMode(int8_t* /*full_packet*/) const { return 0; } + + virtual void putHeaderInPacket(int8_t* /*full_packet*/) {} private: - JackTrip* mJackTrip; ///< JackTrip mediator class + JackTrip* mJackTrip; ///< JackTrip mediator class }; diff --git a/src/ProcessPlugin.h b/src/ProcessPlugin.h index 4c0cae8..e0b23f3 100644 --- a/src/ProcessPlugin.h +++ b/src/ProcessPlugin.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -41,41 +41,41 @@ #include #include -/** \brief Interface for the process plugins to add to the JACK callback process in +/** \brief Interface for the process plugins to add to the JACK callback process in * JackAudioInterface * * This class contains the same methods of the FAUST dsp class. A mydsp class can inherit from * this class the same way it inherits from dsp. Subclass should implement all methods - * except init, which is optional for processing that are sampling rate dependent or + * except init, which is optional for processing that are sampling rate dependent or * that need specific initialization. */ class ProcessPlugin : public QThread { public: - /// \brief The Class Constructor - ProcessPlugin() {}; - /// \brief The Class Destructor - virtual ~ProcessPlugin() {}; - - /// \brief Return Number of Input Channels - virtual int getNumInputs() = 0; - /// \brief Return Number of Output Channels - virtual int getNumOutputs() = 0; + /// \brief The Class Constructor + ProcessPlugin() {}; + /// \brief The Class Destructor + virtual ~ProcessPlugin() {}; - //virtual void buildUserInterface(UI* interface) = 0; + /// \brief Return Number of Input Channels + virtual int getNumInputs() = 0; + /// \brief Return Number of Output Channels + virtual int getNumOutputs() = 0; - /** \brief Do proper Initialization of members and class instances. By default this + //virtual void buildUserInterface(UI* interface) = 0; + + /** \brief Do proper Initialization of members and class instances. By default this * initializes the Sampling Frequency. If a class instance depends on the * sampling frequency, it should be initialize here. */ - virtual void init(int samplingRate) { fSamplingFreq = samplingRate; }; - - /// \brief Compute process - virtual void compute(int nframes, float** inputs, float** outputs) = 0; - + virtual void init(int samplingRate) { fSamplingFreq = samplingRate; }; + + /// \brief Compute process + virtual void compute(int nframes, float** inputs, float** outputs) = 0; + protected: - int fSamplingFreq; ///< Faust Data member, Sampling Rate + int fSamplingFreq; ///< Faust Data member, Sampling Rate }; #endif diff --git a/src/RingBuffer.cpp b/src/RingBuffer.cpp index a9e746c..5b95161 100644 --- a/src/RingBuffer.cpp +++ b/src/RingBuffer.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -47,170 +47,170 @@ using std::cout; using std::endl; //******************************************************************************* -RingBuffer::RingBuffer(int SlotSize, int NumSlots) : - mSlotSize(SlotSize), - mNumSlots(NumSlots), - mTotalSize(mSlotSize*mNumSlots), - mReadPosition(0), - mWritePosition(0), - mFullSlots(0), - mRingBuffer(new int8_t[mTotalSize]), - mLastReadSlot(new int8_t[mSlotSize]) +RingBuffer::RingBuffer(int SlotSize, int NumSlots) : + mSlotSize(SlotSize), + mNumSlots(NumSlots), + mTotalSize(mSlotSize*mNumSlots), + mReadPosition(0), + mWritePosition(0), + mFullSlots(0), + mRingBuffer(new int8_t[mTotalSize]), + mLastReadSlot(new int8_t[mSlotSize]) { - //QMutexLocker locker(&mMutex); // lock the mutex - - // Verify if there's enough space to for the buffers - if ( (mRingBuffer == NULL) || (mLastReadSlot == NULL) ) { - //std::cerr << "ERROR: RingBuffer out of memory!" << endl; - //std::cerr << "Exiting program..." << endl; - //std::exit(1); - throw std::length_error("RingBuffer out of memory!"); - } - - // Set the buffers to zeros - /* + //QMutexLocker locker(&mMutex); // lock the mutex + + // Verify if there's enough space to for the buffers + if ( (mRingBuffer == NULL) || (mLastReadSlot == NULL) ) { + //std::cerr << "ERROR: RingBuffer out of memory!" << endl; + //std::cerr << "Exiting program..." << endl; + //std::exit(1); + throw std::length_error("RingBuffer out of memory!"); + } + + // Set the buffers to zeros + /* for (int i=0; igetDeviceCount() < 1 ) { - cout << "No audio devices found!" << endl; - std::exit(0); - } + // Initialize Buffer array to read and write audio and members + mNumInChans = getNumInputChannels(); + mNumOutChans = getNumOutputChannels(); + mInBuffer.resize(getNumInputChannels()); + mOutBuffer.resize(getNumOutputChannels()); + + cout << "Settin Up Default RtAudio Interface" << endl; + cout << gPrintSeparator << endl; + mRtAudio = new RtAudio; + if ( mRtAudio->getDeviceCount() < 1 ) { + cout << "No audio devices found!" << endl; + std::exit(0); + } - // Get and print default devices - RtAudio::DeviceInfo info_input; - RtAudio::DeviceInfo info_output; - - int deviceId_input; int deviceId_output; - // use default devices - deviceId_input = mRtAudio->getDefaultInputDevice(); - deviceId_output = mRtAudio->getDefaultOutputDevice(); - - cout << "DEFAULT INPUT DEVICE : " << endl; - printDeviceInfo(deviceId_input); - cout << gPrintSeparator << endl; - cout << "DEFAULT OUTPUT DEVICE : " << endl; - printDeviceInfo(deviceId_output); - cout << gPrintSeparator << endl; - - RtAudio::StreamParameters in_params, out_params; - in_params.deviceId = mRtAudio->getDefaultInputDevice(); - out_params.deviceId = mRtAudio->getDefaultOutputDevice(); - in_params.nChannels = getNumInputChannels(); - out_params.nChannels = getNumOutputChannels(); - - RtAudio::StreamOptions options; - //The second flag affects linux and mac only - options.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_SCHEDULE_REALTIME; + // Get and print default devices + RtAudio::DeviceInfo info_input; + RtAudio::DeviceInfo info_output; + + uint32_t deviceId_input; uint32_t deviceId_output; + // use default devices + deviceId_input = mJackTrip->getDeviceID(); + deviceId_output = mJackTrip->getDeviceID(); + + cout << "DEFAULT INPUT DEVICE : " << endl; + printDeviceInfo(deviceId_input); + cout << gPrintSeparator << endl; + cout << "DEFAULT OUTPUT DEVICE : " << endl; + printDeviceInfo(deviceId_output); + cout << gPrintSeparator << endl; + + RtAudio::StreamParameters in_params, out_params; + in_params.deviceId = deviceId_input; + out_params.deviceId = deviceId_output; + in_params.nChannels = getNumInputChannels(); + out_params.nChannels = getNumOutputChannels(); + + RtAudio::StreamOptions options; + //The second flag affects linux and mac only + options.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_SCHEDULE_REALTIME; #ifdef __WIN_32__ - options.flags = options.flags | RTAUDIO_MINIMIZE_LATENCY; + options.flags = options.flags | RTAUDIO_MINIMIZE_LATENCY; #endif - //linux only - options.priority = 99; - - unsigned int sampleRate = getSampleRate();//mSamplingRate; - 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 memeber won't - // be accessible - mRtAudio->openStream(&out_params, &in_params, RTAUDIO_FLOAT32, - sampleRate, &bufferFrames, - &RtAudioInterface::wrapperRtAudioCallback, this, &options); - } - catch ( RtError& e ) { - std::cout << '\n' << e.getMessage() << '\n' << std::endl; - exit( 0 ); - } + //linux only + options.priority = 99; + + unsigned int sampleRate = getSampleRate();//mSamplingRate; + 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 memeber won't + // be accessible + mRtAudio->openStream(&out_params, &in_params, RTAUDIO_FLOAT32, + sampleRate, &bufferFrames, + &RtAudioInterface::wrapperRtAudioCallback, this, &options); + } + catch ( RtAudioError & e ) { + std::cout << '\n' << e.getMessage() << '\n' << std::endl; + exit( 0 ); + } - // Setup parent class - AudioInterface::setup(); + // 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; + 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; + } } - } } //******************************************************************************* void RtAudioInterface::printDeviceInfo(unsigned int deviceId) { - RtAudio rtaudio; - RtAudio::DeviceInfo info; - int i = deviceId; - info = rtaudio.getDeviceInfo(i); - std::vector sampleRates; - cout << "Audio Device [" << i << "] : " << 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; + cout << "Audio Device [" << i << "] : " << 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(userData)->RtAudioCallback(outputBuffer,inputBuffer, - nFrames, - streamTime, status); + return static_cast(userData)->RtAudioCallback(outputBuffer,inputBuffer, + nFrames, + streamTime, status); } //******************************************************************************* int RtAudioInterface::startProcess() const { - try { mRtAudio->startStream(); } - catch ( RtError& e ) { - std::cout << '\n' << e.getMessage() << '\n' << std::endl; - return(-1); - } - return(0); + try { mRtAudio->startStream(); } + catch ( RtAudioError& e ) { + std::cout << '\n' << e.getMessage() << '\n' << std::endl; + return(-1); + } + return(0); } //******************************************************************************* int RtAudioInterface::stopProcess() const { - try { mRtAudio->closeStream(); } - catch ( RtError& e ) { - std::cout << '\n' << e.getMessage() << '\n' << std::endl; - return(-1); - } - return 0; + try { mRtAudio->closeStream(); } + catch ( RtAudioError& e ) { + std::cout << '\n' << e.getMessage() << '\n' << std::endl; + return(-1); + } + return 0; } @@ -357,48 +357,48 @@ int RtAudioInterface::processCallback(jack_nframes_t nframes) - //mTestJackTrip->printTextTest(); +//mTestJackTrip->printTextTest(); - //if (mJackTrip != NULL) - // cout << "(mJackTrip != NULL)" << endl; +//if (mJackTrip != NULL) +// cout << "(mJackTrip != NULL)" << endl; - //if (mJackTrip == NULL) { cout << " === JACKTRIPNULL === " << endl; } +//if (mJackTrip == NULL) { cout << " === JACKTRIPNULL === " << endl; } - //const int8_t* caca; - //mJackTrip->sendNetworkPacket( mInputPacket ); +//const int8_t* caca; +//mJackTrip->sendNetworkPacket( mInputPacket ); - //in_buffer = mInBuffer.data(); - //mInBuffer.data() = (float*) inputBuffer; +//in_buffer = mInBuffer.data(); +//mInBuffer.data() = (float*) inputBuffer; - //mInBuffer[0] = static_cast(outputBuffer); - //mOutBuffer[0] = static_cast(inputBuffer); - //float* in_buffer = static_cast(inputBuffer); - //float* out_buffer = static_cast(outputBuffer); +//mInBuffer[0] = static_cast(outputBuffer); +//mOutBuffer[0] = static_cast(inputBuffer); +//float* in_buffer = static_cast(inputBuffer); +//float* out_buffer = static_cast(outputBuffer); - //cout << "nFrames = ==================== = = = = = = = ======== " << this->getBufferSizeInSamples() << endl; - //int8_t* input_packet = new int8_t[nFrames*2]; +//cout << "nFrames = ==================== = = = = = = = ======== " << this->getBufferSizeInSamples() << endl; +//int8_t* input_packet = new int8_t[nFrames*2]; - //tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0 +//tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0 - //JackAudioInterface::fromSampleToBitConversion(in_buffer, input_packet, BIT16); - //for (int i = 0; isendNetworkPacket(input_packet); - //cout << mJackTrip->getRingBuffersSlotSize() << endl; - //delete[] input_packet; +//JackAudioInterface::fromSampleToBitConversion(in_buffer, input_packet, BIT16); +//for (int i = 0; isendNetworkPacket(input_packet); +//cout << mJackTrip->getRingBuffersSlotSize() << endl; +//delete[] input_packet; - //mOutputPacket = static_cast(inputBuffer); - //mInputPacket = static_cast(outputBuffer); +//mOutputPacket = static_cast(inputBuffer); +//mInputPacket = static_cast(outputBuffer); - // Allocate the Process Callback - //------------------------------------------------------------------- - // 1) First, process incoming packets - // ---------------------------------- - /* +// Allocate the Process Callback +//------------------------------------------------------------------- +// 1) First, process incoming packets +// ---------------------------------- +/* mJackTrip->receiveNetworkPacket( mOutputPacket ); // Extract separate channels to send to Jack for (int i = 0; i < getNumInputChannels(); i++) { @@ -412,12 +412,12 @@ int RtAudioInterface::processCallback(jack_nframes_t nframes) */ - // 3) Finally, send packets to peer - // -------------------------------- - // Input Process (from JACK to NETWORK) - // ---------------------------------------------------------------- - // Concatenate all the channels from jack to form packet - /* +// 3) Finally, send packets to peer +// -------------------------------- +// Input Process (from JACK to NETWORK) +// ---------------------------------------------------------------- +// Concatenate all the channels from jack to form packet +/* for (int i = 0; i < getNumOutputChannels(); i++) { //-------- // This should be faster for 32 bits @@ -447,6 +447,6 @@ int RtAudioInterface::processCallback(jack_nframes_t nframes) //mInRingBuffer->insertSlotNonBlocking( mInputPacket ); mJackTrip->sendNetworkPacket( mInputPacket ); */ - //return 0; +//return 0; //} diff --git a/src/RtAudioInterface.h b/src/RtAudioInterface.h index a176e78..051892f 100644 --- a/src/RtAudioInterface.h +++ b/src/RtAudioInterface.h @@ -38,7 +38,7 @@ #ifndef __RTAUDIOINTERFACE_H__ #define __RTAUDIOINTERFACE_H__ -#include "RtAudio.h" +#include #include "AudioInterface.h" #include "jacktrip_globals.h" @@ -49,49 +49,49 @@ class RtAudioInterface : public AudioInterface { public: - /** \brief The class constructor + /** \brief The class constructor * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) * \param NumInChans Number of Input Channels * \param NumOutChans Number of Output Channels * \param AudioBitResolution Audio Sample Resolutions in bits */ - RtAudioInterface(JackTrip* jacktrip, - int NumInChans = gDefaultNumInChannels, - int NumOutChans = gDefaultNumOutChannels, - audioBitResolutionT AudioBitResolution = BIT16); - /// \brief The class destructor - virtual ~RtAudioInterface(); - - /// \brief List all avialable audio interfaces, with its properties - virtual void listAllInterfaces(); - virtual void setup(); - virtual int startProcess() const; - virtual int stopProcess() const; - /// \brief This has no effect in RtAudio - virtual void connectDefaultPorts() {} - - //--------------SETTERS--------------------------------------------- - /// \brief This has no effect in RtAudio - virtual void setClientName(const char* /*ClientName*/) {} - //------------------------------------------------------------------ - - //--------------GETTERS--------------------------------------------- - //------------------------------------------------------------------ + RtAudioInterface(JackTrip* jacktrip, + int NumInChans = gDefaultNumInChannels, + int NumOutChans = gDefaultNumOutChannels, + audioBitResolutionT AudioBitResolution = BIT16); + /// \brief The class destructor + virtual ~RtAudioInterface(); + + /// \brief List all avialable audio interfaces, with its properties + virtual void listAllInterfaces(); + virtual void setup(); + virtual int startProcess() const; + virtual int stopProcess() const; + /// \brief This has no effect in RtAudio + virtual void connectDefaultPorts() {} + + //--------------SETTERS--------------------------------------------- + /// \brief This has no effect in RtAudio + virtual void setClientName(const char* /*ClientName*/) {} + //------------------------------------------------------------------ + + //--------------GETTERS--------------------------------------------- + //------------------------------------------------------------------ private: - int RtAudioCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, - double streamTime, RtAudioStreamStatus status); - static int wrapperRtAudioCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, - double streamTime, RtAudioStreamStatus status, void *userData); - void printDeviceInfo(unsigned int deviceId); - - JackTrip* mJackTrip; ///< JackTrip Mediator Class pointer - int mNumInChans;///< Number of Input Channels - int mNumOutChans; ///< Number of Output Channels - QVarLengthArray mInBuffer; ///< Vector of Input buffers/channel read from JACK - QVarLengthArray mOutBuffer; ///< Vector of Output buffer/channel to write to JACK - RtAudio* mRtAudio; ///< RtAudio class + int RtAudioCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, + double streamTime, RtAudioStreamStatus status); + static int wrapperRtAudioCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, + double streamTime, RtAudioStreamStatus status, void *userData); + void printDeviceInfo(unsigned int deviceId); + + JackTrip* mJackTrip; ///< JackTrip Mediator Class pointer + int mNumInChans;///< Number of Input Channels + int mNumOutChans; ///< Number of Output Channels + QVarLengthArray mInBuffer; ///< Vector of Input buffers/channel read from JACK + QVarLengthArray mOutBuffer; ///< Vector of Output buffer/channel to write to JACK + RtAudio* mRtAudio; ///< RtAudio class }; #endif // __RTAUDIOINTERFACE_H__ diff --git a/src/Settings.cpp b/src/Settings.cpp index 21d9601..25caf47 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -38,6 +38,12 @@ #include "Settings.h" #include "LoopBack.h" #include "NetKS.h" + +#ifdef WAIR // wair +#include "ap8x2.dsp.h" +#include "Stk16.dsp.h" +#endif // endwhere + #include "UdpMasterListener.h" #include "JackTripWorker.h" #include "jacktrip_globals.h" @@ -65,6 +71,10 @@ Settings::Settings() : mClientName(NULL), mUnderrrunZero(false), mLoopBack(false), + #ifdef WAIR // WAIR + mNumNetRevChans(0), + mWAIR(false), + #endif // endwhere mJamLink(false), mEmptyHeader(false), mJackTripServer(false), @@ -72,7 +82,10 @@ Settings::Settings() : mRedundancy(1), mUseJack(true), mChanfeDefaultSR(false), - mChanfeDefaultBS(false) + mChanfeDefaultID(0), + mChanfeDefaultBS(false), + mHubConnectionMode(JackTrip::SERVERTOCLIENT), + mConnectDefaultAudioPorts(true) {} //******************************************************************************* @@ -96,46 +109,68 @@ void Settings::parseInput(int argc, char** argv) // options descriptor //---------------------------------------------------------------------------- static struct option longopts[] = { - // These options set a flag, has to be sepcified as a long option --verbose - { "verbose", no_argument, &gVerboseFlag, 1 }, // These options don't set a flag. - { "numchannels", required_argument, NULL, 'n' }, // Number of input and output channels - { "server", no_argument, NULL, 's' }, // Run in server mode - { "client", required_argument, NULL, 'c' }, // Run in client mode, set server IP address - { "localaddress", required_argument, NULL, 'L' }, // set local address e.g., 127.0.0.2 for second instance on same host - { "jacktripserver", no_argument, NULL, 'S' }, // Run in JamLink mode - { "pingtoserver", required_argument, NULL, 'C' }, // Run in ping to server mode, set server IP address - { "portoffset", required_argument, NULL, 'o' }, // Port Offset from 4464 - { "bindport", required_argument, NULL, 'B' }, // Port Offset from 4464 - { "peerport", required_argument, NULL, 'P' }, // Port Offset from 4464 - { "queue", required_argument, NULL, 'q' }, // Queue Length - { "redundancy", required_argument, NULL, 'r' }, // Redundancy - { "bitres", required_argument, NULL, 'b' }, // Audio Bit Resolution - { "zerounderrun", no_argument, NULL, 'z' }, // Use Underrun to Zeros Mode - { "loopback", no_argument, NULL, 'l' }, // Run in loopback mode - { "jamlink", no_argument, NULL, 'j' }, // Run in JamLink mode - { "emptyheader", no_argument, NULL, 'e' }, // Run in JamLink mode - { "clientname", required_argument, NULL, 'J' }, // Run in JamLink mode - { "rtaudio", no_argument, NULL, 'R' }, // Run in JamLink mode - { "srate", required_argument, NULL, 'T' }, // Set Sample Rate - { "bufsize", required_argument, NULL, 'F' }, // Set buffer Size - { "version", no_argument, NULL, 'v' }, // Version Number - { "help", no_argument, NULL, 'h' }, // Print Help - { NULL, 0, NULL, 0 } - }; + { "numchannels", required_argument, NULL, 'n' }, // Number of input and output channels +#ifdef WAIR // WAIR + { "wair", no_argument, NULL, 'w' }, // Run in LAIR mode, sets numnetrevchannels + { "addcombfilterlength", required_argument, NULL, 'N' }, // added comb filter length + { "combfilterfeedback", required_argument, NULL, 'H' }, // comb filter feedback +#endif // endwhere + { "server", no_argument, NULL, 's' }, // Run in server mode + { "client", required_argument, NULL, 'c' }, // Run in client mode, set server IP address + { "localaddress", required_argument, NULL, 'L' }, // set local address e.g., 127.0.0.2 for second instance on same host + { "jacktripserver", no_argument, NULL, 'S' }, // Run in JamLink mode + { "pingtoserver", required_argument, NULL, 'C' }, // Run in ping to server mode, set server IP address + { "portoffset", required_argument, NULL, 'o' }, // Port Offset from 4464 + { "bindport", required_argument, NULL, 'B' }, // Port Offset from 4464 + { "peerport", required_argument, NULL, 'P' }, // Port Offset from 4464 + { "queue", required_argument, NULL, 'q' }, // Queue Length + { "redundancy", required_argument, NULL, 'r' }, // Redundancy + { "bitres", required_argument, NULL, 'b' }, // Audio Bit Resolution + { "zerounderrun", no_argument, NULL, 'z' }, // Use Underrun to Zeros Mode + { "loopback", no_argument, NULL, 'l' }, // Run in loopback mode + { "jamlink", no_argument, NULL, 'j' }, // Run in JamLink mode + { "emptyheader", no_argument, NULL, 'e' }, // Run in JamLink mode + { "clientname", required_argument, NULL, 'J' }, // Run in JamLink mode + { "rtaudio", no_argument, NULL, 'R' }, // Run in JamLink mode + { "srate", required_argument, NULL, 'T' }, // Set Sample Rate + { "deviceid", required_argument, NULL, 'd' }, // Set RTAudio device id to use + { "bufsize", required_argument, NULL, 'F' }, // Set buffer Size + { "nojackportsconnect" , no_argument, NULL, 'D'}, // Don't connect default Audio Ports + { "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 + { "help", no_argument, NULL, 'h' }, // Print Help + { NULL, 0, NULL, 0 } +}; // Parse Command Line Arguments //---------------------------------------------------------------------------- /// \todo Specify mandatory arguments int ch; while ( (ch = getopt_long(argc, argv, - "n:sc:SC:o:B:P:q:r:b:zljeJ:RT:F:vh", longopts, NULL)) != -1 ) + "n:N:H:sc:SC:o:B:P:q:r:b:zlwjeJ:RTd:F:p:DvVh", longopts, NULL)) != -1 ) switch (ch) { case 'n': // Number of input and output channels //------------------------------------------------------- mNumChans = atoi(optarg); break; +#ifdef WAIR + case 'w': + //------------------------------------------------------- + mWAIR = true; + mNumNetRevChans = gDefaultNumNetRevChannels; // fixed amount sets number of network channels and comb filters for WAIR + break; + case 'N': + //------------------------------------------------------- + mClientAddCombLen = atoi(optarg); // cmd line comb length adjustment + break; + case 'H': // comb feedback adjustment + //------------------------------------------------------- + mClientRoomSize = atof(optarg); // cmd line comb feedback adjustment + break; +#endif // endwhere case 's': // Run in server mode //------------------------------------------------------- mJackTripMode = JackTrip::SERVER; @@ -182,7 +217,7 @@ void Settings::parseInput(int argc, char** argv) else if ( atoi(optarg) == 32 ) { mAudioBitResolution = AudioInterface::BIT32; } else { - std::cerr << "--bitres ERROR: Wrong bit resolutions: " + std::cerr << "--bitres ERROR: Wrong bit resolution: " << atoi(optarg) << " is not supported." << endl; printUsage(); std::exit(1); } @@ -190,7 +225,7 @@ void Settings::parseInput(int argc, char** argv) case 'q': //------------------------------------------------------- if ( atoi(optarg) <= 0 ) { - std::cerr << "--queue ERROR: The queue has to be equal or greater that 2" << endl; + std::cerr << "--queue ERROR: The queue has to be equal or greater than 2" << endl; printUsage(); std::exit(1); } else { @@ -236,19 +271,51 @@ void Settings::parseInput(int argc, char** argv) mChanfeDefaultSR = true; mSampleRate = atoi(optarg); break; + case 'd': // RTAudio device id + //------------------------------------------------------- + mChanfeDefaultID = true; + mDeviceID = atoi(optarg); + break; case 'F': // Buffer Size //------------------------------------------------------- mChanfeDefaultBS = true; mAudioBufferSize = atoi(optarg); break; + case 'D': + //------------------------------------------------------- + mConnectDefaultAudioPorts = false; + break; case 'v': //------------------------------------------------------- cout << "JackTrip VERSION: " << gVersion << endl; - cout << "Copyright (c) 2008-2009 Juan-Pablo Caceres, Chris Chafe." << endl; + cout << "Copyright (c) 2008-2018 Juan-Pablo Caceres, Chris Chafe." << endl; cout << "SoundWIRE group at CCRMA, Stanford University" << endl; cout << "" << endl; std::exit(0); break; + case 'V': + //------------------------------------------------------- + gVerboseFlag = true; + if (gVerboseFlag) std::cout << "Verbose mode" << std::endl; + break; + case 'p': + //------------------------------------------------------- + if ( atoi(optarg) == 0 ) { + mHubConnectionMode = JackTrip::SERVERTOCLIENT; } + else if ( atoi(optarg) == 1 ) { + mHubConnectionMode = JackTrip::CLIENTECHO; } + else if ( atoi(optarg) == 2 ) { + mHubConnectionMode = JackTrip::CLIENTFOFI; } + else if ( atoi(optarg) == 3 ) { + mHubConnectionMode = JackTrip::RESERVEDMATRIX; } + else if ( atoi(optarg) == 4 ) { + mHubConnectionMode = JackTrip::FULLMIX; } + else { + std::cerr << "-p ERROR: Wrong HubConnectionMode: " + << atoi(optarg) << " is not supported." << endl; + printUsage(); + std::exit(1); } + break; case 'h': //------------------------------------------------------- printUsage(); @@ -265,11 +332,11 @@ void Settings::parseInput(int argc, char** argv) //---------------------------------------------------------------------------- if (optind < argc) { cout << gPrintSeparator << endl; - cout << "WARINING: The following entered options have no effect" << endl; + cout << "WARINING: The following entered options have no effect." << endl; cout << " They will be ignored!" << endl; - cout << " Type jacktrip to see options." << endl; + cout << " Type 'jacktrip' to see options." << endl; for( ; optind < argc; optind++) { - printf("argument: %s\n", argv[optind]); + cout << "argument: " << argv[optind] << endl; } cout << gPrintSeparator << endl; } @@ -282,46 +349,54 @@ void Settings::printUsage() cout << "" << endl; cout << "JackTrip: A System for High-Quality Audio Network Performance" << endl; cout << "over the Internet" << endl; - cout << "Copyright (c) 2008-2015 Juan-Pablo Caceres, Chris Chafe." << endl; + cout << "Copyright (c) 2008-2018 Juan-Pablo Caceres, Chris Chafe." << endl; cout << "SoundWIRE group at CCRMA, Stanford University" << endl; cout << "VERSION: " << gVersion << endl; - cout << "-----------------------------------------------------------------------------" << endl; cout << "" << endl; cout << "Usage: jacktrip [-s|-c host] [options]" << endl; cout << "" << endl; cout << "Options: " << endl; cout << "REQUIRED ARGUMENTS: " << endl; - cout << "===================" << endl; cout << " -s, --server Run in Server Mode" << endl; - cout << " -c, --client Run in Client Mode" << endl; + cout << " -c, --client Run in Client Mode" << endl; + cout << " -S, --jacktripserver Run in Hub Server Mode" << endl; + cout << " -C, --pingtoserver Run in Hub Client Mode" << endl; cout << endl; cout << "OPTIONAL ARGUMENTS: " << endl; - cout << "===================" << endl; - cout << " -n, --numchannels # Number of Input and Output Channels (default " + cout << " -n, --numchannels # Number of Input and Output Channels (default: " << 2 << ")" << endl; - cout << " -q, --queue # (2 or more) Queue Buffer Length, in Packet Size (default " +#ifdef WAIR // WAIR + cout << " -w, --wair Run in WAIR Mode" << endl; + cout << " -N, --addcombfilterlength # comb length adjustment for WAIR (default " + << gDefaultAddCombFilterLength << ")" << endl; + cout << " -H, --combfilterfeedback # comb feedback adjustment for WAIR (default " + << gDefaultCombFilterFeedback << ")" << endl; +#endif // endwhere + cout << " -q, --queue # (2 or more) Queue Buffer Length, in Packet Size (default: " << gDefaultQueueLength << ")" << endl; - cout << " -r, --redundancy # (1 or more) Packet Redundancy to avoid glitches with packet losses (defaul 1)" + cout << " -r, --redundancy # (1 or more) Packet Redundancy to avoid glitches with packet losses (default: 1)" << endl; cout << " -o, --portoffset # Receiving port offset from base port " << gDefaultPort << endl; - cout << " --bindport # Set only the bind port number (default to 4464)" << endl; - cout << " --peerport # Set only the Peer port number (default to 4464)" << endl; - cout << " -b, --bitres # (8, 16, 24, 32) Audio Bit Rate Resolutions (default 16)" << endl; - cout << " -z, --zerounderrun Set buffer to zeros when underrun occurs (defaults to wavetable)" << endl; + cout << " --bindport # Set only the bind port number (default: 4464)" << endl; + cout << " --peerport # Set only the Peer port number (default: 4464)" << endl; + cout << " -b, --bitres # (8, 16, 24, 32) Audio Bit Rate Resolutions (default: 16)" << endl; + cout << " -p, --hubpatch # (0, 1, 2, 3, 4) Hub auto audio patch, only has effect if running HUB SERVER mode, 0=server-to-clients, 1=client loopback, 2=client fan out/in but not loopback, 3=reserved for TUB, 4=full mix (default: 0)" << endl; + cout << " -z, --zerounderrun Set buffer to zeros when underrun occurs (default: wavetable)" << endl; cout << " -l, --loopback Run in Loop-Back Mode" << endl; cout << " -j, --jamlink Run in JamLink Mode (Connect to a JamLink Box)" << endl; - cout << " --clientname Change default client name (default is JackTrip)" << endl; - cout << " --localaddress Change default local host IP address (127.0.0.1)" << 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 << endl; - cout << "ARGUMENTS TO USE IT WITHOUT JACK:" << endl; - cout << "=================================" << endl; - cout << " --rtaudio Use defaul sound system instead of Jack" << endl; - cout << " --srate # Set the sampling rate, works on --rtaudio mode only (defaults 48000)" << endl; - cout << " --bufsize # Set the buffer size, works on --rtaudio mode only (defaults 128)" << endl; + cout << "ARGUMENTS TO USE JACKTRIP WITHOUT JACK:" << endl; + cout << " --rtaudio Use system's default sound system instead of Jack" << endl; + cout << " --srate # Set the sampling rate, works on --rtaudio mode only (default: 48000)" << 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 << "HELP ARGUMENTS: " << endl; - cout << "===============" << endl; cout << " -v, --version Prints Version Number" << endl; + cout << " -V, --verbose Verbose mode, prints debug messages" << endl; cout << " -h, --help Prints this Help" << endl; cout << "" << endl; } @@ -334,6 +409,19 @@ void Settings::startJackTrip() /// \todo Change this, just here to test if ( mJackTripServer ) { UdpMasterListener* udpmaster = new UdpMasterListener; +#ifdef WAIR // WAIR + udpmaster->setWAIR(mWAIR); +#endif // endwhere + udpmaster->setHubPatch(mHubConnectionMode); + udpmaster->setConnectDefaultAudioPorts(mConnectDefaultAudioPorts); + if (gVerboseFlag) std::cout << "Settings:startJackTrip before udpmaster->start" << std::endl; + // Set buffers to zero when underrun + if ( mUnderrrunZero ) { + cout << "Setting buffers to zero when underrun..." << endl; + cout << gPrintSeparator << std::endl; + udpmaster->setUnderRunMode(JackTrip::ZEROS); + } + udpmaster->setBufferQueueLength(mBufferQueueLength); udpmaster->start(); //---Thread Pool Test-------------------------------------------- @@ -355,9 +443,19 @@ void Settings::startJackTrip() //JackTrip jacktrip(mJackTripMode, mDataProtocol, mNumChans, // mBufferQueueLength, mAudioBitResolution); +#ifdef WAIR // WAIR + if (gVerboseFlag) std::cout << "Settings:startJackTrip mNumNetRevChans = " << mNumNetRevChans << std::endl; +#endif // endwhere + if (gVerboseFlag) std::cout << "Settings:startJackTrip before new JackTrip" << std::endl; mJackTrip = new JackTrip(mJackTripMode, mDataProtocol, mNumChans, + #ifdef WAIR // wair + mNumNetRevChans, + #endif // endwhere mBufferQueueLength, mRedundancy, mAudioBitResolution); + // Set connect or not default audio ports. Only work for jack + mJackTrip->setConnectDefaultAudioPorts(mConnectDefaultAudioPorts); + // Connect Signals and Slots QObject::connect(mJackTrip, SIGNAL( signalProcessesStopped() ), this, SLOT( slotExitProgram() )); @@ -378,14 +476,16 @@ void Settings::startJackTrip() if ( mJackTripMode == JackTrip::CLIENT || mJackTripMode == JackTrip::CLIENTTOPINGSERVER ) { mJackTrip->setPeerAddress(mPeerAddress.toLatin1().data()); } -// if(mLocalAddress!=QString()) // default -// mJackTrip->setLocalAddress(QHostAddress(mLocalAddress.toLatin1().data())); -// else -// mJackTrip->setLocalAddress(QHostAddress::Any); + // if(mLocalAddress!=QString()) // default + // mJackTrip->setLocalAddress(QHostAddress(mLocalAddress.toLatin1().data())); + // else + // mJackTrip->setLocalAddress(QHostAddress::Any); // Set Ports //cout << "SETTING ALL PORTS" << endl; + if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->setBindPorts" << std::endl; mJackTrip->setBindPorts(mBindPortNum); + if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->setPeerPorts" << std::endl; mJackTrip->setPeerPorts(mPeerPortNum); // Set in JamLink Mode @@ -414,6 +514,11 @@ void Settings::startJackTrip() mJackTrip->setSampleRate(mSampleRate); } + // Chanfe defualt device ID + if (mChanfeDefaultID) { + mJackTrip->setDeviceID(mDeviceID); + } + // Chanfe default Buffer Size if (mChanfeDefaultBS) { mJackTrip->setAudioBufferSizeInSamples(mAudioBufferSize); @@ -439,9 +544,38 @@ void Settings::startJackTrip() // ------------------------------------------------------------- } +#ifdef WAIR // WAIR + if ( mWAIR ) { + cout << "Running in WAIR Mode..." << endl; + cout << gPrintSeparator << std::endl; + switch ( mNumNetRevChans ) + { + case 16 : + { + mJackTrip->appendProcessPlugin(new ap8x2(mNumChans)); // plugin slot 0 + ///////////////////////////////////////////////////////// + Stk16* plugin = new Stk16(mNumNetRevChans); + plugin->Stk16::initCombClient(mClientAddCombLen, mClientRoomSize); + mJackTrip->appendProcessPlugin(plugin); // plugin slot 1 + } + break; + default: + throw std::invalid_argument("Settings: mNumNetRevChans doesn't correspond to Faust plugin"); + break; + } + } +#endif // endwhere + // Start JackTrip - mJackTrip->startProcess(); - mJackTrip->start(); + if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->startProcess" << std::endl; + mJackTrip->startProcess( + #ifdef WAIRTOMASTER // WAIR + 0 // for WAIR compatibility, ID in jack client name + #endif // endwhere + ); + // if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->start" << std::endl; + // this is a noop + // mJackTrip->start(); /* sleep(10); diff --git a/src/Settings.h b/src/Settings.h index 90eea26..9272755 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -53,55 +53,66 @@ */ class Settings : public QThread { - Q_OBJECT; + Q_OBJECT; public: - Settings(); - virtual ~Settings(); + Settings(); + virtual ~Settings(); - /// \brief Parses command line input - void parseInput(int argc, char** argv); + /// \brief Parses command line input + void parseInput(int argc, char** argv); - void startJackTrip(); - void stopJackTrip(); + void startJackTrip(); + void stopJackTrip(); - /// \brief Prints usage help - void printUsage(); + /// \brief Prints usage help + void printUsage(); - bool getLoopBack() { return mLoopBack; } + bool getLoopBack() { return mLoopBack; } public slots: - void slotExitProgram() - { - std::cerr << "Exiting JackTrip..." << std::endl; - std::exit(1); - } + void slotExitProgram() + { + std::cerr << "Exiting JackTrip..." << std::endl; + std::exit(1); + } private: - JackTrip* mJackTrip; ///< JackTrip class - JackTrip::jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT - JackTrip::dataProtocolT mDataProtocol; ///< Data Protocol - int mNumChans; ///< Number of Channels (inputs = outputs) - int mBufferQueueLength; ///< Audio Buffer from network queue length - AudioInterface::audioBitResolutionT mAudioBitResolution; - QString mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode - int mBindPortNum; ///< Bind Port Number - int mPeerPortNum; ///< Peer Port Number - char* mClientName; ///< JackClient Name - bool mUnderrrunZero; ///< Use Underrun to Zero mode - - bool mLoopBack; ///< Loop-back mode - bool mJamLink; ///< JamLink mode - bool mEmptyHeader; ///< EmptyHeader mode - bool mJackTripServer; ///< JackTrip Server mode - QString mLocalAddress; ///< Local Address - unsigned int mRedundancy; ///< Redundancy factor for data in the network - bool mUseJack; ///< Use or not JackAduio - bool mChanfeDefaultSR; ///< Change Default Sampling Rate - bool mChanfeDefaultBS; ///< Change Default Buffer Size - unsigned int mSampleRate; - unsigned int mAudioBufferSize; + JackTrip* mJackTrip; ///< JackTrip class + JackTrip::jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT + JackTrip::dataProtocolT mDataProtocol; ///< Data Protocol + int mNumChans; ///< Number of Channels (inputs = outputs) + int mBufferQueueLength; ///< Audio Buffer from network queue length + AudioInterface::audioBitResolutionT mAudioBitResolution; + QString mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode + int mBindPortNum; ///< Bind Port Number + int mPeerPortNum; ///< Peer Port Number + char* mClientName; ///< JackClient Name + bool mUnderrrunZero; ///< Use Underrun to Zero mode + +#ifdef WAIR // wair + int mNumNetRevChans; ///< Number of Network Audio Channels (net comb filters) + int mClientAddCombLen; ///< cmd line adjustment of net comb + double mClientRoomSize; ///< freeverb room size + bool mWAIR; ///< WAIR mode +#endif // endwhere + + bool mLoopBack; ///< Loop-back mode + bool mJamLink; ///< JamLink mode + bool mEmptyHeader; ///< EmptyHeader mode + bool mJackTripServer; ///< JackTrip Server mode + QString mLocalAddress; ///< Local Address + unsigned int mRedundancy; ///< Redundancy factor for data in the network + bool mUseJack; ///< Use or not JackAduio + bool mChanfeDefaultSR; ///< Change Default Sampling Rate + bool mChanfeDefaultID; ///< Change Default device ID + bool mChanfeDefaultBS; ///< Change Default Buffer Size + unsigned int mSampleRate; + unsigned int mDeviceID; + unsigned int mAudioBufferSize; + unsigned int mHubConnectionMode; + bool mConnectDefaultAudioPorts; ///< Connect or not jack audio ports }; #endif diff --git a/src/TestRingBuffer.h b/src/TestRingBuffer.h index 136710b..0f6296d 100644 --- a/src/TestRingBuffer.h +++ b/src/TestRingBuffer.h @@ -10,19 +10,19 @@ static RingBuffer rb(2,100); class TestRingBufferWrite : public QThread { public: - - void run() - { - int8_t* writeSlot; - writeSlot = new int8_t[2]; - writeSlot[0] = *"a"; - writeSlot[1] = *"b"; - while (true) { - //std::cout << "writing BEFORE" << std::endl; - rb.insertSlotBlocking(writeSlot); - //std::cout << "writing AFTER" << std::endl; + + void run() + { + int8_t* writeSlot; + writeSlot = new int8_t[2]; + writeSlot[0] = *"a"; + writeSlot[1] = *"b"; + while (true) { + //std::cout << "writing BEFORE" << std::endl; + rb.insertSlotBlocking(writeSlot); + //std::cout << "writing AFTER" << std::endl; + } } - } }; @@ -30,20 +30,19 @@ public: class TestRingBufferRead : public QThread { public: - - void run() - { - int8_t* readSlot; - readSlot = new int8_t[2]; - while (true) { - //std::cout << "reading BEFORE" << std::endl; - rb.readSlotBlocking(readSlot); - //std::cout << "reading AFTER" << std::endl; - //std::cout << *(readSlot) << std::endl; - //std::cout << *(readSlot+1) << std::endl; + + void run() + { + int8_t* readSlot; + readSlot = new int8_t[2]; + while (true) { + //std::cout << "reading BEFORE" << std::endl; + rb.readSlotBlocking(readSlot); + //std::cout << "reading AFTER" << std::endl; + //std::cout << *(readSlot) << std::endl; + //std::cout << *(readSlot+1) << std::endl; + } } - } }; #endif - diff --git a/src/ThreadPoolTest.h b/src/ThreadPoolTest.h index 1c85653..5f7de67 100644 --- a/src/ThreadPoolTest.h +++ b/src/ThreadPoolTest.h @@ -19,58 +19,58 @@ class ThreadPoolTest : public QObject, public QRunnable -//class ThreadPoolTest : public QThread + //class ThreadPoolTest : public QThread { - Q_OBJECT; - + Q_OBJECT; + public: - ThreadPoolTest() - { - setAutoDelete(false); - } - - void run() - { - JackTripWorkerMessages jtm; - QThread testThread; - //jtm.moveToThread(&testThread); - - //QObject::connect(&jtm, SIGNAL(signalTest()), &jtm, SLOT(slotTest()), Qt::QueuedConnection); - testThread.start(); - jtm.play(); - //testThread.wait(); - - //std::cout << "--------------- BEFORE ---------------" << std::endl; - //NetKS netks; - //netks.play(); - //std::cout << "--------------- AFTER ---------------" << std::endl; - - QEventLoop loop; - //QObject::connect(this, SIGNAL(stopELoop()), &loop, SLOT(quit()), Qt::QueuedConnection); - loop.exec(); - //std::cout << "--------------- EXITING QRUNNABLE---------------" << std::endl; - /* + ThreadPoolTest() + { + setAutoDelete(false); + } + + void run() + { + JackTripWorkerMessages jtm; + QThread testThread; + //jtm.moveToThread(&testThread); + + //QObject::connect(&jtm, SIGNAL(signalTest()), &jtm, SLOT(slotTest()), Qt::QueuedConnection); + testThread.start(); + jtm.play(); + //testThread.wait(); + + //std::cout << "--------------- BEFORE ---------------" << std::endl; + //NetKS netks; + //netks.play(); + //std::cout << "--------------- AFTER ---------------" << std::endl; + + QEventLoop loop; + //QObject::connect(this, SIGNAL(stopELoop()), &loop, SLOT(quit()), Qt::QueuedConnection); + loop.exec(); + //std::cout << "--------------- EXITING QRUNNABLE---------------" << std::endl; + /* while (true) { std::cout << "Hello world from thread" << std::endl; sleep(1); } */ - } + } - void stop() - { - std::cout << "--------------- ELOOP STOP---------------" << std::endl; - emit stopELoop(); - } + void stop() + { + std::cout << "--------------- ELOOP STOP---------------" << std::endl; + emit stopELoop(); + } signals: - void stopELoop(); + void stopELoop(); private slots: - void fromServer() - { - std::cout << "--------------- SIGNAL RECEIVED ---------------" << std::endl; - } + void fromServer() + { + std::cout << "--------------- SIGNAL RECEIVED ---------------" << std::endl; + } }; diff --git a/src/UdpDataProtocol.cpp b/src/UdpDataProtocol.cpp index 0cd1369..b08ae9e 100644 --- a/src/UdpDataProtocol.cpp +++ b/src/UdpDataProtocol.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -47,7 +47,9 @@ #include #include #ifdef __WIN_32__ -#include +//#include +#include //cc need SD_SEND +#include // for IPv6 #endif #if defined (__LINUX__) || (__MAC__OSX__) #include // for POSIX Sockets @@ -66,197 +68,231 @@ QMutex UdpDataProtocol::sUdpMutex; UdpDataProtocol::UdpDataProtocol(JackTrip* jacktrip, const runModeT runmode, int bind_port, int peer_port, unsigned int udp_redundancy_factor) : -DataProtocol(jacktrip, runmode, bind_port, peer_port), -mBindPort(bind_port), mPeerPort(peer_port), -mRunMode(runmode), -mAudioPacket(NULL), mFullPacket(NULL), -mUdpRedundancyFactor(udp_redundancy_factor) + DataProtocol(jacktrip, runmode, bind_port, peer_port), + mBindPort(bind_port), mPeerPort(peer_port), + mRunMode(runmode), + mAudioPacket(NULL), mFullPacket(NULL), + mUdpRedundancyFactor(udp_redundancy_factor) { - mStopped = false; - if (mRunMode == RECEIVER) { - QObject::connect(this, SIGNAL(signalWatingTooLong(int)), - jacktrip, SLOT(slotUdpWatingTooLong(int)), Qt::QueuedConnection); - } + mStopped = false; + mIPv6 = false; + std::memset(&mPeerAddr, 0, sizeof(mPeerAddr)); + std::memset(&mPeerAddr6, 0, sizeof(mPeerAddr6)); + mPeerAddr.sin_port = htons(mPeerPort); + mPeerAddr6.sin6_port = htons(mPeerPort); + + if (mRunMode == RECEIVER) { + QObject::connect(this, SIGNAL(signalWaitingTooLong(int)), + jacktrip, SLOT(slotUdpWaitingTooLongClientGoneProbably(int)), Qt::QueuedConnection); + } } //******************************************************************************* UdpDataProtocol::~UdpDataProtocol() { - delete[] mAudioPacket; - delete[] mFullPacket; - wait(); -} + delete[] mAudioPacket; + delete[] mFullPacket; + wait(); +} //******************************************************************************* void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP) throw(std::invalid_argument) { - // Get DNS Address - QHostInfo info = QHostInfo::fromName(peerHostOrIP); - if (!info.addresses().isEmpty()) { - // use the first IP address - mPeerAddress = info.addresses().first(); - //cout << "UdpDataProtocol::setPeerAddress IP Address Number: " - // << mPeerAddress.toString().toStdString() << endl; - } + // Get DNS Address +#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)) { +#endif + QHostInfo info = QHostInfo::fromName(peerHostOrIP); + if (!info.addresses().isEmpty()) { + // use the first IP address + mPeerAddress = info.addresses().first(); + } + //cout << "UdpDataProtocol::setPeerAddress IP Address Number: " + // << mPeerAddress.toString().toStdString() << endl; +#if defined (__LINUX__) || (__MAC__OSX__) + } +#endif - // check if the ip address is valid - if ( mPeerAddress.isNull() ) { - QString error_message = "Incorrect presentation format address\n '"; - error_message.append(peerHostOrIP); - error_message.append("' is not a valid IP address or Host Name"); - //std::cerr << "ERROR: Incorrect presentation format address" << endl; - //std::cerr << "'" << peerHostOrIP <<"' does not seem to be a valid IP address" << endl; - //throw std::invalid_argument("Incorrect presentation format address"); - throw std::invalid_argument( error_message.toStdString()); - } - /* - else { - std::cout << "Peer Address set to: " - << mPeerAddress.toString().toStdString() << std::endl; - cout << gPrintSeparator << endl; - usleep(100); - } - */ + // check if the ip address is valid + if ( mPeerAddress.protocol() == QAbstractSocket::IPv6Protocol ) { + mIPv6 = true; + } else if ( mPeerAddress.protocol() != QAbstractSocket::IPv4Protocol ) { + QString error_message = "Incorrect presentation format address\n '"; + error_message.append(peerHostOrIP); + error_message.append("' is not a valid IP address or Host Name"); + //std::cerr << "ERROR: Incorrect presentation format address" << endl; + //std::cerr << "'" << peerHostOrIP <<"' does not seem to be a valid IP address" << endl; + //throw std::invalid_argument("Incorrect presentation format address"); + throw std::invalid_argument( error_message.toStdString()); + } + /* + else { + std::cout << "Peer Address set to: " + << mPeerAddress.toString().toStdString() << std::endl; + cout << gPrintSeparator << endl; + usleep(100); + } + */ + + // Save our address as an appropriate address structure + if (mIPv6) { + mPeerAddr6.sin6_family = AF_INET6; + ::inet_pton(AF_INET6, mPeerAddress.toString().toLatin1().constData(), + &mPeerAddr6.sin6_addr); + } else { + mPeerAddr.sin_family = AF_INET; + ::inet_pton(AF_INET, mPeerAddress.toString().toLatin1().constData(), + &mPeerAddr.sin_addr); + } +} + +#if defined (__WIN_32__) +void UdpDataProtocol::setSocket(SOCKET &socket) throw(std::runtime_error) +#else +void UdpDataProtocol::setSocket(int &socket) throw(std::runtime_error) +#endif +{ + //If we haven't been passed a valid socket, then we should bind one. +#if defined (__WIN_32__) + if (socket == INVALID_SOCKET) { +#else + if (socket == -1) { +#endif + try { + if (gVerboseFlag) std::cout << " UdpDataProtocol:run" << mRunMode << " before bindSocket(UdpSocket)" << std::endl; + socket = bindSocket(); // Bind Socket + } catch ( const std::exception & e ) { + emit signalError( e.what() ); + return; + } + } + mSocket = socket; } //******************************************************************************* -void UdpDataProtocol::bindSocket(QUdpSocket& UdpSocket) throw(std::runtime_error) +#if defined (__WIN_32__) +SOCKET UdpDataProtocol::bindSocket() throw(std::runtime_error) +#else +int UdpDataProtocol::bindSocket() throw(std::runtime_error) +#endif { - QMutexLocker locker(&sUdpMutex); + QMutexLocker locker(&sUdpMutex); #if defined __WIN_32__ - WORD wVersionRequested; - WSADATA wsaData; - int err; + WORD wVersionRequested; + WSADATA wsaData; + int err; - wVersionRequested = MAKEWORD( 1, 1 ); + wVersionRequested = MAKEWORD( 1, 1 ); - err = WSAStartup( wVersionRequested, &wsaData ); - if ( err != 0 ) { - // Tell the user that we couldn't find a useable - // winsock.dll. + err = WSAStartup( wVersionRequested, &wsaData ); + if ( err != 0 ) { + // Tell the user that we couldn't find a useable + // winsock.dll. - return; - } + return INVALID_SOCKET; + } - // Confirm that the Windows Sockets DLL supports 1.1. or higher + // Confirm that the Windows Sockets DLL supports 1.1. or higher - if ( LOBYTE( wsaData.wVersion ) != 1 || - HIBYTE( wsaData.wVersion ) != 1 ) { - // Tell the user that we couldn't find a useable - // winsock.dll. - WSACleanup( ); - return; - } + if ( LOBYTE( wsaData.wVersion ) != 1 || + HIBYTE( wsaData.wVersion ) != 1 ) { + // Tell the user that we couldn't find a useable + // winsock.dll. + WSACleanup( ); + return INVALID_SOCKET; + } - // Creat socket descriptor - SOCKET sock_fd; - SOCKADDR_IN local_addr; + SOCKET sock_fd; #endif #if defined ( __LINUX__ ) || (__MAC_OSX__) - int sock_fd; - //Set local IPv4 Address - struct sockaddr_in local_addr; + int sock_fd; #endif - // Creat socket descriptor - sock_fd = socket(AF_INET, SOCK_DGRAM, 0); - - //::bzero(&local_addr, sizeof(local_addr)); - std::memset(&local_addr, 0, sizeof(local_addr)); // set buffer to 0 - local_addr.sin_family = AF_INET; //AF_INET: IPv4 Protocol - local_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY: let the kernel decide the active address - local_addr.sin_port = htons(mBindPort); //set local port + //Set local IPv4 or IPv6 Address + struct sockaddr_in local_addr; + struct sockaddr_in6 local_addr6; + + // Create socket descriptor + if (mIPv6) { + sock_fd = socket(AF_INET6, SOCK_DGRAM, 0); + std::memset(&local_addr6, 0, sizeof(local_addr6)); + local_addr6.sin6_family = AF_INET6; + local_addr6.sin6_addr = in6addr_any; + local_addr6.sin6_port = htons(mBindPort); + } else { + sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + + //::bzero(&local_addr, sizeof(local_addr)); + std::memset(&local_addr, 0, sizeof(local_addr)); // set buffer to 0 + local_addr.sin_family = AF_INET; //AF_INET: IPv4 Protocol + local_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY: let the kernel decide the active address + local_addr.sin_port = htons(mBindPort); //set local port + } - // Set socket to be reusable, this is platform dependent - int one = 1; + // Set socket to be reusable, this is platform dependent + int one = 1; #if defined ( __LINUX__ ) - ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); #endif #if defined ( __MAC_OSX__ ) - // This option is not avialable on Linux, and without it MAC OS X - // has problems rebinding a socket - ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); + // This option is not avialable on Linux, and without it MAC OS X + // has problems rebinding a socket + ::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); #endif #if defined (__WIN_32__) - //make address/port reusable - setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)); + //make address/port reusable + setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)); #endif - // Bind the Socket -#if defined ( __LINUX__ ) || ( __MAC_OSX__ ) - if ( (::bind(sock_fd, (struct sockaddr *) &local_addr, sizeof(local_addr))) < 0 ) - { throw std::runtime_error("ERROR: UDP Socket Bind Error"); } -#endif -#if defined (__WIN_32__) - //int bound; - //bound = bind(sock_fd, (SOCKADDR *) &local_addr, sizeof(local_addr)); - if ( (bind(sock_fd, (SOCKADDR *) &local_addr, sizeof(local_addr))) == SOCKET_ERROR ) - { throw std::runtime_error("ERROR: UDP Socket Bind Error"); } -#endif + // Bind the Socket + if (mIPv6) { + if ( (::bind(sock_fd, (struct sockaddr *) &local_addr6, sizeof(local_addr6))) < 0 ) + { throw std::runtime_error("ERROR: UDP Socket Bind Error"); } + } else { + if ( (::bind(sock_fd, (struct sockaddr *) &local_addr, sizeof(local_addr))) < 0 ) + { throw std::runtime_error("ERROR: UDP Socket Bind Error"); } + } - // To be able to use the two UDP sockets bound to the same port number, - // we connect the receiver and issue a SHUT_WR. - if (mRunMode == SENDER) { - // We use the sender as an unconnected UDP socket - UdpSocket.setSocketDescriptor(sock_fd, QUdpSocket::BoundState, - QUdpSocket::WriteOnly); - } - else if (mRunMode == RECEIVER) { + // To be able to use the two UDP sockets bound to the same port number, + // we connect the receiver and issue a SHUT_WR. + + // This didn't work for IPv6, so we'll instead share a full duplex socket. + /*if (mRunMode == SENDER) { + // We use the sender as an unconnected UDP socket + UdpSocket.setSocketDescriptor(sock_fd, QUdpSocket::BoundState, + QUdpSocket::WriteOnly); + }*/ + if (!mIPv6) { + // Connect only if we're using IPv4. + // (Connecting presents an issue when a host has multiple IP addresses and the peer decides to send from + // a different address. While this generally won't be a problem for IPv4, it will for IPv6.) + if ( (::connect(sock_fd, (struct sockaddr *) &mPeerAddr, sizeof(mPeerAddr))) < 0) + { throw std::runtime_error("ERROR: Could not connect UDP socket"); } #if defined (__LINUX__) || (__MAC_OSX__) - // Set peer IPv4 Address - struct sockaddr_in peer_addr; - bzero(&peer_addr, sizeof(peer_addr)); - peer_addr.sin_family = AF_INET; //AF_INET: IPv4 Protocol - peer_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY: let the kernel decide the active address - peer_addr.sin_port = htons(mPeerPort); //set local port - // Connect the socket and issue a Write shutdown (to make it a - // reader socket only) - if ( (::inet_pton(AF_INET, mPeerAddress.toString().toLatin1().constData(), - &peer_addr.sin_addr)) < 1 ) - { throw std::runtime_error("ERROR: Invalid address presentation format"); } - if ( (::connect(sock_fd, (struct sockaddr *) &peer_addr, sizeof(peer_addr))) < 0) - { throw std::runtime_error("ERROR: Could not connect UDP socket"); } - if ( (::shutdown(sock_fd,SHUT_WR)) < 0) - { throw std::runtime_error("ERROR: Could suntdown SHUT_WR UDP socket"); } + //if ( (::shutdown(sock_fd,SHUT_WR)) < 0) + //{ throw std::runtime_error("ERROR: Could shutdown SHUT_WR UDP socket"); } #endif #if defined __WIN_32__ - // Set peer IPv4 Address - SOCKADDR_IN peer_addr; - std::memset(&peer_addr, 0, sizeof(peer_addr)); // set buffer to 0 - peer_addr.sin_family = AF_INET; //AF_INET: IPv4 Protocol - peer_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY: let the kernel decide the active address - peer_addr.sin_port = htons(mPeerPort); //set local port - // Connect the socket and issue a Write shutdown (to make it a - // reader socket only) - peer_addr.sin_addr.s_addr = inet_addr(mPeerAddress.toString().toLatin1().constData()); - int con = (::connect(sock_fd, (struct sockaddr *) &peer_addr, sizeof(peer_addr))); - if ( con < 0) - { - fprintf(stderr, "ERROR: Could not connect UDP socket"); - throw std::runtime_error("ERROR: Could not connect UDP socket"); - } - //cout<<"connect returned: "<getPacketSizeInBytes(); - //cout << "full_packet_size: " << full_packet_size << endl; - mFullPacket = new int8_t[full_packet_size]; - std::memset(mFullPacket, 0, full_packet_size); // set buffer to 0 - - bool timeout = false; // Time out flag for packets that arrive too late - - // Put header in first packet - mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); - - // Redundancy Variables - // (Algorithm explained at the end of this file) - // --------------------------------------------- - int full_redundant_packet_size = full_packet_size * mUdpRedundancyFactor; - int8_t* full_redundant_packet; - full_redundant_packet = new int8_t[full_redundant_packet_size]; - std::memset(full_redundant_packet, 0, full_redundant_packet_size); // Initialize to 0 - - // Set realtime priority (function in jacktrip_globals.h) - set_crossplatform_realtime_priority(); - - switch ( mRunMode ) - { - case RECEIVER : { - // Connect signals and slots for packets arriving too late notifications - QObject::connect(this, SIGNAL(signalWatingTooLong(int)), - this, SLOT(printUdpWaitedTooLong(int)), - Qt::QueuedConnection); - //----------------------------------------------------------------------------------- - // Wait for the first packet to be ready and obtain address - // from that packet - std::cout << "Waiting for Peer..." << std::endl; - // This blocks waiting for the first packet - while ( !UdpSocket.hasPendingDatagrams() ) { - if (mStopped) { return; } - QThread::msleep(100); - } - int first_packet_size = UdpSocket.pendingDatagramSize(); - // The following line is the same as - int8_t* first_packet = new int8_t[first_packet_size]; - /// \todo fix this to avoid memory leaks - // but avoids memory leaks - //std::tr1::shared_ptr first_packet(new int8_t[first_packet_size]); - receivePacket( UdpSocket, reinterpret_cast(first_packet), first_packet_size); - // Check that peer has the same audio settings - mJackTrip->checkPeerSettings(first_packet); - mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); - std::cout << "Received Connection for Peer!" << std::endl; - emit signalReceivedConnectionFromPeer(); - - // Redundancy Variables - // -------------------- - // NOTE: These types need to be the same unsigned integer as the sequence - // number in the header. That way, they wrap around in the "same place" - 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 - - while ( !mStopped ) - { - // Timer to report packets arriving too late - // This QT method gave me a lot of trouble, so I replaced it with my own 'waitForReady' - // that uses signals and slots and can also report with packets have not - // arrive for a longer time - //timeout = UdpSocket.waitForReadyRead(30); - timeout = waitForReady(UdpSocket, 60000); //60 seconds - - // OLD CODE WITHOUT REDUNDANCY---------------------------------------------------- - /* + if (gVerboseFlag) std::cout << " UdpDataProtocol:run" << mRunMode << " before Setup Audio Packet buffer, Full Packet buffer, Redundancy Variables" << std::endl; + // Setup Audio Packet buffer + size_t audio_packet_size = getAudioPacketSizeInBites(); + //cout << "audio_packet_size: " << audio_packet_size << endl; + mAudioPacket = new int8_t[audio_packet_size]; + std::memset(mAudioPacket, 0, audio_packet_size); // set buffer to 0 + + // Setup Full Packet buffer + int full_packet_size = mJackTrip->getPacketSizeInBytes(); + //cout << "full_packet_size: " << full_packet_size << endl; + mFullPacket = new int8_t[full_packet_size]; + std::memset(mFullPacket, 0, full_packet_size); // set buffer to 0 + + // bool timeout = false; // Time out flag for packets that arrive too late + + // Put header in first packet + mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); + + // Redundancy Variables + // (Algorithm explained at the end of this file) + // --------------------------------------------- + int full_redundant_packet_size = full_packet_size * mUdpRedundancyFactor; + int8_t* full_redundant_packet; + full_redundant_packet = new int8_t[full_redundant_packet_size]; + std::memset(full_redundant_packet, 0, full_redundant_packet_size); // Initialize to 0 + + // Set realtime priority (function in jacktrip_globals.h) + if (gVerboseFlag) std::cout << " UdpDataProtocol:run" << mRunMode << " before setRealtimeProcessPriority()" << std::endl; + //std::cout << "Experimental version -- not using setRealtimeProcessPriority()" << std::endl; + //setRealtimeProcessPriority(); + + ///////////////////// + // to see thread priorities + // sudo ps -eLo pri,rtprio,cls,pid,nice,cmd | grep -E 'jackd|jacktrip|rtc|RTPRI' | sort -r + + // from David Runge + + // It seems that it tries to apply the highest available SCHED_FIFO to + // jacktrip or half of it (?) [1] (although that's not what you would want, + // as this would mean assigning a higher priority to jacktrip than e.g. to + // the audio interface and e.g. IRQs that need to be taken care of). + + // The version on github [2] (current 1.1) is actually worse off, as it + // just hardcodes RTPRIO 99 (which means jacktrip will compete with the + // Linux kernel watchdog, if the user trying to launch jacktrip is even + // allowed to use that high of a priority!). + // On most systems this will not work at all (aside from it being outright + // dangerous). On Arch (and also Ubuntu) the sane default is to allow + // rtprio 95 to a privileged user group (e.g. 'realtime' or 'audio', etc.) + + // It would be very awesome, if setting the priority would be dealt with by + // a command line flag to jacktrip (e.g. `jacktrip --priority=50`) and + // otherwise defaulting to a much much lower number (e.g. 10), so the + // application can be run out-of-the-box (even without being in a + // privileged group). + + // from Nando + + // You should actually be using the priority that jack gives you when you + // create the realtime thread, that puts your process "behind" - so to + // speak - the processing that jack does on behalf of all its clients, and + // behind (in a properly configured system) the audio interface processing + // interrupt. No need to select a priority yourself. + + // In a Fedora system I run jack with a priority of 65 (the Fedora packages + // changed the default to a much lower one which is a big no-no). The + // clients inherit 60, I think. Some clients that have their own internal + // structure of processes (jconvolver) run multiple threads and use + // priorities below 60 for them (ie: they start with what jack gave them). + + // If you need to run a thread (not the audio thread) with higher priority + // you could retrieve the priority that jack gave you and add some magic + // number to get it to be above jack itself (10 would be fine in my + // experience). + + //without setting it + // PRI RTPRIO CLS PID NI CMD + // 60 20 FF 4348 - /usr/bin/jackd -dalsa -dhw:CODEC -r48000 -p128 -n2 -Xseq + // 55 15 FF 9835 - ./jacktrip -s + // 19 - TS 9835 0 ./jacktrip -s + // 19 - TS 9835 0 ./jacktrip -s + // 19 - TS 9835 0 ./jacktrip -s + // 19 - TS 9835 0 ./jacktrip -s + // 19 - TS 9835 0 ./jacktrip -s + // 19 - TS 4348 0 /usr/bin/jackd -dalsa -dhw:CODEC -r48000 -p128 -n2 -Xseq + // 19 - TS 4348 0 /usr/bin/jackd -dalsa -dhw:CODEC -r48000 -p128 -n2 -Xseq + // 19 - TS 4348 0 /usr/bin/jackd -dalsa -dhw:CODEC -r48000 -p128 -n2 -Xseq + // 19 - TS 4348 0 /usr/bin/jackd -dalsa -dhw:CODEC -r48000 -p128 -n2 -Xseq + + // jack puts its clients in FF at 5 points below itself + + switch ( mRunMode ) + { + case RECEIVER : { + // Connect signals and slots for packets arriving too late notifications + QObject::connect(this, SIGNAL(signalWaitingTooLong(int)), + this, SLOT(printUdpWaitedTooLong(int)), + Qt::QueuedConnection); + //----------------------------------------------------------------------------------- + // Wait for the first packet to be ready and obtain address + // from that packet + if (gVerboseFlag) std::cout << " UdpDataProtocol:run" << mRunMode << " before !UdpSocket.hasPendingDatagrams()" << std::endl; + std::cout << "Waiting for Peer..." << std::endl; + // This blocks waiting for the first packet + while ( !UdpSocket.hasPendingDatagrams() ) { + if (mStopped) { return; } + QThread::msleep(100); + if (gVerboseFlag) std::cout << "100ms " << std::flush; + } + int first_packet_size = UdpSocket.pendingDatagramSize(); + // The following line is the same as + int8_t* first_packet = new int8_t[first_packet_size]; + /// \todo fix this to avoid memory leaks + // but avoids memory leaks + //std::tr1::shared_ptr first_packet(new int8_t[first_packet_size]); + receivePacket( UdpSocket, reinterpret_cast(first_packet), first_packet_size); + // Check that peer has the same audio settings + if (gVerboseFlag) std::cout << std::endl << " UdpDataProtocol:run" << mRunMode << " before mJackTrip->checkPeerSettings()" << std::endl; + mJackTrip->checkPeerSettings(first_packet); + if (gVerboseFlag) std::cout << "step 7" << std::endl; + if (gVerboseFlag) std::cout << " UdpDataProtocol:run" << mRunMode << " before mJackTrip->parseAudioPacket()" << std::endl; + mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); + std::cout << "Received Connection from Peer!" << std::endl; + emit signalReceivedConnectionFromPeer(); + + // Redundancy Variables + // -------------------- + // NOTE: These types need to be the same unsigned integer as the sequence + // number in the header. That way, they wrap around in the "same place" + 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 + + if (gVerboseFlag) std::cout << "step 8" << std::endl; + while ( !mStopped ) + { + // Timer to report packets arriving too late + // This QT method gave me a lot of trouble, so I replaced it with my own 'waitForReady' + // that uses signals and slots and can also report with packets have not + // arrive for a longer time + //timeout = UdpSocket.waitForReadyRead(30); + // timeout = cc unused! + waitForReady(UdpSocket, 60000); //60 seconds + + // OLD CODE WITHOUT REDUNDANCY---------------------------------------------------- + /* // This is blocking until we get a packet... receivePacket( UdpSocket, reinterpret_cast(mFullPacket), full_packet_size); @@ -417,23 +559,25 @@ void UdpDataProtocol::run() //mRingBuffer->insertSlotNonBlocking(mAudioPacket); mJackTrip->writeAudioBuffer(mAudioPacket); */ - //---------------------------------------------------------------------------------- - receivePacketRedundancy(UdpSocket, - full_redundant_packet, - full_redundant_packet_size, - full_packet_size, - current_seq_num, - last_seq_num, - newer_seq_num); - } - break; } - - case SENDER : { - //----------------------------------------------------------------------------------- - while ( !mStopped ) - { - // OLD CODE WITHOUT REDUNDANCY ----------------------------------------------------- - /* + //---------------------------------------------------------------------------------- + receivePacketRedundancy(UdpSocket, + full_redundant_packet, + full_redundant_packet_size, + full_packet_size, + current_seq_num, + last_seq_num, + newer_seq_num); + } + break; } + + case SENDER : { + //Make sure we don't start sending packets too soon. + QThread::msleep(100); + //----------------------------------------------------------------------------------- + while ( !mStopped ) + { + // OLD CODE WITHOUT REDUNDANCY ----------------------------------------------------- + /* // We block until there's stuff available to read mJackTrip->readAudioBuffer( mAudioPacket ); mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); @@ -441,53 +585,55 @@ void UdpDataProtocol::run() //int bytes_sent = sendPacket( reinterpret_cast(mFullPacket), full_packet_size); sendPacket( UdpSocket, PeerAddress, reinterpret_cast(mFullPacket), full_packet_size); */ - //---------------------------------------------------------------------------------- - sendPacketRedundancy(UdpSocket, - PeerAddress, - full_redundant_packet, - full_redundant_packet_size, - full_packet_size); - } - break; } - } + //---------------------------------------------------------------------------------- + sendPacketRedundancy(full_redundant_packet, + full_redundant_packet_size, + full_packet_size); + } + break; } + } } //******************************************************************************* -bool UdpDataProtocol::waitForReady(QUdpSocket& UdpSocket, int timeout_msec) +//bool +void UdpDataProtocol::waitForReady(QUdpSocket& UdpSocket, int timeout_msec) { - int loop_resolution_usec = 100; // usecs to wait on each loop - int emit_resolution_usec = 10000; // 10 milliseconds - int timeout_usec = timeout_msec * 1000; - int ellaped_time_usec = 0; // Ellapsed time in milliseconds - - while ( ( !(UdpSocket.hasPendingDatagrams()) && (ellaped_time_usec <= timeout_usec) ) - && !mStopped ){ - if (mStopped) { return false; } - QThread::usleep(loop_resolution_usec); - ellaped_time_usec += loop_resolution_usec; - - if ( !(ellaped_time_usec % emit_resolution_usec) ) { - emit signalWatingTooLong(static_cast(ellaped_time_usec/1000)); + int loop_resolution_usec = 100; // usecs to wait on each loop + int emit_resolution_usec = 10000; // 10 milliseconds + int timeout_usec = timeout_msec * 1000; + int elapsed_time_usec = 0; // Ellapsed time in milliseconds + + while ( ( !( + UdpSocket.hasPendingDatagrams() && + (UdpSocket.pendingDatagramSize() > 0) + ) && (elapsed_time_usec <= timeout_usec) ) + && !mStopped ){ + // if (mStopped) { return false; } + QThread::usleep(loop_resolution_usec); + elapsed_time_usec += loop_resolution_usec; + + if ( !(elapsed_time_usec % emit_resolution_usec) ) { + emit signalWaitingTooLong(static_cast(elapsed_time_usec/1000)); + } } - } - - if ( ellaped_time_usec >= timeout_usec ) - { - emit signalWatingTooLong(ellaped_time_usec/1000); - return false; - } - return true; + // cc under what condition? + // if ( elapsed_time_usec >= timeout_usec ) + // { + // emit signalWaitingTooLong(elapsed_time_usec/1000); + // return false; + // } + // return true; } //******************************************************************************* void UdpDataProtocol::printUdpWaitedTooLong(int wait_msec) { - int wait_time = 30; // msec - if ( !(wait_msec%wait_time) ) { - std::cerr << "UDP waiting too long (more than " << wait_time << "ms)..." << endl; - } + int wait_time = 30; // msec + if ( !(wait_msec%wait_time) ) { + std::cerr << "UDP waiting too long (more than " << wait_time << "ms)..." << endl; + } } @@ -500,90 +646,88 @@ void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket, uint16_t& last_seq_num, uint16_t& newer_seq_num) { - // This is blocking until we get a packet... - receivePacket( UdpSocket, reinterpret_cast(full_redundant_packet), - full_redundant_packet_size); - - // Get Packet Sequence Number - newer_seq_num = - mJackTrip->getPeerSequenceNumber(full_redundant_packet); - current_seq_num = newer_seq_num; - - - //cout << current_seq_num << " "; - int redun_last_index = 0; - for (unsigned int i = 1; igetPeerSequenceNumber( full_redundant_packet + (i*full_packet_size) ); + // This is blocking until we get a packet... + receivePacket( UdpSocket, reinterpret_cast(full_redundant_packet), + full_redundant_packet_size); + + // Get Packet Sequence Number + newer_seq_num = + mJackTrip->getPeerSequenceNumber(full_redundant_packet); + current_seq_num = newer_seq_num; + + //cout << current_seq_num << " "; - } - //cout << endl; + int redun_last_index = 0; + for (unsigned int i = 1; igetPeerSequenceNumber( full_redundant_packet + (i*full_packet_size) ); + //cout << current_seq_num << " "; + } + //cout << endl; - last_seq_num = newer_seq_num; // Save last read packet + last_seq_num = newer_seq_num; // Save last read packet - // Send to audio all available audio packets, in order - for (int i = redun_last_index; i>=0; i--) { - memcpy(mFullPacket, - full_redundant_packet + (i*full_packet_size), - full_packet_size); - mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); - mJackTrip->writeAudioBuffer(mAudioPacket); - } + // Send to audio all available audio packets, in order + for (int i = redun_last_index; i>=0; i--) { + memcpy(mFullPacket, + full_redundant_packet + (i*full_packet_size), + full_packet_size); + mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); + mJackTrip->writeAudioBuffer(mAudioPacket); + } } //******************************************************************************* -void UdpDataProtocol::sendPacketRedundancy(QUdpSocket& UdpSocket, - QHostAddress& PeerAddress, - int8_t* full_redundant_packet, +void UdpDataProtocol::sendPacketRedundancy(int8_t* full_redundant_packet, int full_redundant_packet_size, int full_packet_size) { - mJackTrip->readAudioBuffer( mAudioPacket ); - mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); - - // Move older packets to end of array of redundant packets - std::memmove(full_redundant_packet+full_packet_size, - full_redundant_packet, - full_packet_size*(mUdpRedundancyFactor-1)); - // Copy new packet to the begining of array - std::memcpy(full_redundant_packet, - mFullPacket, full_packet_size); - - // 10% (or other number) packet lost simulation. - // Uncomment the if to activate - //--------------------------------------------------------------------------------- - //int random_integer = rand(); - //if ( random_integer > (RAND_MAX/10) ) - //{ - sendPacket( UdpSocket, PeerAddress, reinterpret_cast(full_redundant_packet), - full_redundant_packet_size); - //} - //--------------------------------------------------------------------------------- - - mJackTrip->increaseSequenceNumber(); + mJackTrip->readAudioBuffer( mAudioPacket ); + mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); + + // Move older packets to end of array of redundant packets + std::memmove(full_redundant_packet+full_packet_size, + full_redundant_packet, + full_packet_size*(mUdpRedundancyFactor-1)); + // Copy new packet to the begining of array + std::memcpy(full_redundant_packet, + mFullPacket, full_packet_size); + + // 10% (or other number) packet lost simulation. + // Uncomment the if to activate + //--------------------------------------------------------------------------------- + //int random_integer = rand(); + //if ( random_integer > (RAND_MAX/10) ) + //{ + sendPacket( reinterpret_cast(full_redundant_packet), + full_redundant_packet_size); + //} + //--------------------------------------------------------------------------------- + + mJackTrip->increaseSequenceNumber(); } /* The Redundancy Algorythmn works as follows. We send a packet that contains - a mUdpRedundancyFactor number of packets (header+audio). This big packet looks + a mUdpRedundancyFactor number of packets (header+audio). This big packet looks as follows - + ---------- ------------ ----------------------------------- - | UDP[n] | | UDP[n-1] | ... | UDP[n-(mUdpRedundancyFactor-1)] | + | UDP[n] | | UDP[n-1] | ... | UDP[n-(mUdpRedundancyFactor-1)] | ---------- ------------ ----------------------------------- Then, for the new audio buffer, we shift everything to the right and send: - + ---------- ------------ ------------------------------------- - | UDP[n+1] | | UDP[n] | ... | UDP[n-(mUdpRedundancyFactor-1)+1] | + | UDP[n+1] | | UDP[n] | ... | UDP[n-(mUdpRedundancyFactor-1)+1] | ---------- ------------ ------------------------------------- etc... @@ -592,7 +736,7 @@ void UdpDataProtocol::sendPacketRedundancy(QUdpSocket& UdpSocket, ---------- ---------- ---------- ---------- | UDP[4] | | UDP[3] | | UDP[2] | | UDP[1] | ---------- ---------- ---------- ---------- - + ---------- ---------- ---------- ---------- | UDP[5] | | UDP[4] | | UDP[3] | | UDP[2] | ---------- ---------- ---------- ---------- diff --git a/src/UdpDataProtocol.h b/src/UdpDataProtocol.h index 4015023..2d09fe3 100644 --- a/src/UdpDataProtocol.h +++ b/src/UdpDataProtocol.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -65,31 +65,37 @@ */ class UdpDataProtocol : public DataProtocol { - Q_OBJECT; - + Q_OBJECT; + public: - - /** \brief The class constructor + + /** \brief The class constructor * \param jacktrip Pointer to the JackTrip class that connects all classes (mediator) * \param runmode Sets the run mode, use either SENDER or RECEIVER * \param bind_port Port number to bind for this socket (this is the receive or send port depending on the runmode) * \param peer_port Peer port number (this is the receive or send port depending on the runmode) * \param udp_redundancy_factor Number of redundant packets */ - UdpDataProtocol(JackTrip* jacktrip, const runModeT runmode, - int bind_port, int peer_port, - unsigned int udp_redundancy_factor = 1); - - /** \brief The class destructor + UdpDataProtocol(JackTrip* jacktrip, const runModeT runmode, + int bind_port, int peer_port, + unsigned int udp_redundancy_factor = 1); + + /** \brief The class destructor */ - virtual ~UdpDataProtocol(); + virtual ~UdpDataProtocol(); - /** \brief Set the Peer address to connect to + /** \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) throw(std::invalid_argument); + +#if defined (__WIN_32__) + void setSocket(SOCKET &socket) throw(std::runtime_error); +#else + void setSocket(int &socket) throw(std::runtime_error); +#endif - /** \brief Receives a packet. It blocks until a packet is received + /** \brief Receives a packet. It blocks until a packet is received * * This function makes sure we recieve a complete packet * of size n @@ -97,10 +103,10 @@ public: * \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(QUdpSocket& UdpSocket, char* buf, const size_t n); - - /** \brief Sends a packet + //virtual int receivePacket(char* buf, const size_t n); + virtual int receivePacket(QUdpSocket& UdpSocket, char* buf, const size_t n); + + /** \brief Sends a packet * * This function meakes sure we send a complete packet * of size n @@ -108,55 +114,58 @@ public: * \param n size of packet to receive * \return number of bytes read, -1 on error */ - virtual int sendPacket(QUdpSocket& UdpSocket, const QHostAddress& PeerAddress, - const char* buf, const size_t n); - - /** \brief Obtains the peer address from the first UDP packet received. This address + virtual int sendPacket(const char* buf, const size_t n); + + /** \brief Obtains the peer address from the first UDP packet received. This address * is used by the SERVER mode to connect back to the client. * \param peerHostAddress QHostAddress to store the peer address * \param port Receiving port */ - virtual void getPeerAddressFromFirstPacket(QUdpSocket& UdpSocket, - QHostAddress& peerHostAddress, - uint16_t& port); + virtual void getPeerAddressFromFirstPacket(QUdpSocket& UdpSocket, + QHostAddress& peerHostAddress, + uint16_t& port); - /** \brief Sets the bind port number + /** \brief Sets the bind port number */ - void setBindPort(int port) - { mBindPort = port; } + void setBindPort(int port) + { mBindPort = port; } - /** \brief Sets the peer port number + /** \brief Sets the peer port number */ - void setPeerPort(int port) - { mPeerPort = port; } + void setPeerPort(int port) + { mPeerPort = port; mPeerAddr.sin_port = htons(mPeerPort); mPeerAddr6.sin6_port = htons(mPeerPort); } - /** \brief Implements the Thread Loop. To start the thread, call start() + /** \brief Implements the Thread Loop. To start the thread, call start() * ( DO NOT CALL run() ) * * This function creats and binds all the socket and start the connection loop thread. */ - virtual void run(); + virtual void run(); private slots: - void printUdpWaitedTooLong(int wait_msec); + void printUdpWaitedTooLong(int wait_msec); signals: - /// \brief Signals when waiting every 10 milliseconds, with the total wait on wait_msec - /// \param wait_msec Total wait in milliseconds - void signalWatingTooLong(int wait_msec); + /// \brief Signals when waiting every 10 milliseconds, with the total wait on wait_msec + /// \param wait_msec Total wait in milliseconds + void signalWaitingTooLong(int wait_msec); -//private: + //private: protected: - /** \brief Binds the UDP socket to the available address and specified port + /** \brief Binds the UDP socket to the available address and specified port */ - void bindSocket(QUdpSocket& UdpSocket) throw(std::runtime_error); - - /** \brief This function blocks until data is available for reading in the +#if defined (__WIN_32__) + SOCKET bindSocket() throw(std::runtime_error); +#else + int bindSocket() throw(std::runtime_error); +#endif + + /** \brief This function blocks until data is available for reading in the * QUdpSocket. The function will timeout after timeout_msec microseconds. * * This function is intended to replace QAbstractSocket::waitForReadyRead which has @@ -165,40 +174,46 @@ protected: * \return returns true if there is data available for reading; * otherwise it returns false (if an error occurred or the operation timed out) */ - bool waitForReady(QUdpSocket& UdpSocket, int timeout_msec); + void waitForReady(QUdpSocket& UdpSocket, int timeout_msec); - /** \brief Redundancy algorythm at the receiving end + /** \brief Redundancy algorythm at the receiving end */ - virtual void receivePacketRedundancy(QUdpSocket& UdpSocket, - int8_t* full_redundant_packet, - int full_redundant_packet_size, - int full_packet_size, - uint16_t& current_seq_num, - uint16_t& last_seq_num, - uint16_t& newer_seq_num); - - /** \brief Redundancy algorythm at the sender's end + virtual void receivePacketRedundancy(QUdpSocket& UdpSocket, + int8_t* full_redundant_packet, + int full_redundant_packet_size, + int full_packet_size, + uint16_t& current_seq_num, + uint16_t& last_seq_num, + uint16_t& newer_seq_num); + + /** \brief Redundancy algorythm at the sender's end */ - virtual void sendPacketRedundancy(QUdpSocket& UdpSocket, - QHostAddress& PeerAddress, - int8_t* full_redundant_packet, - int full_redundant_packet_size, - int full_packet_size); + virtual void sendPacketRedundancy(int8_t* full_redundant_packet, + int full_redundant_packet_size, + int full_packet_size); private: - int mBindPort; ///< Local Port number to Bind - int mPeerPort; ///< Peer Port number - const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER - - QHostAddress mPeerAddress; ///< The Peer Address - - int8_t* mAudioPacket; ///< Buffer to store Audio Packets - int8_t* mFullPacket; ///< Buffer to store Full Packet (audio+header) - - unsigned int mUdpRedundancyFactor; ///< Factor of redundancy - static QMutex sUdpMutex; ///< Mutex to make thread safe the binding process + int mBindPort; ///< Local Port number to Bind + int mPeerPort; ///< Peer Port number + const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER + bool mIPv6; /// Use IPv6 + + QHostAddress mPeerAddress; ///< The Peer Address + struct sockaddr_in mPeerAddr; + struct sockaddr_in6 mPeerAddr6; +#if defined (__WIN_32__) + SOCKET mSocket; +#else + int mSocket; +#endif + + int8_t* mAudioPacket; ///< Buffer to store Audio Packets + int8_t* mFullPacket; ///< Buffer to store Full Packet (audio+header) + + unsigned int mUdpRedundancyFactor; ///< Factor of redundancy + static QMutex sUdpMutex; ///< Mutex to make thread safe the binding process }; #endif // __UDPDATAPROTOCOL_H__ diff --git a/src/UdpDataProtocolPOSIX.cpp.tmp b/src/UdpDataProtocolPOSIX.cpp.tmp index fe77daa..dbd27bf 100644 --- a/src/UdpDataProtocolPOSIX.cpp.tmp +++ b/src/UdpDataProtocolPOSIX.cpp.tmp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -50,42 +50,42 @@ //******************************************************************************* UdpDataProtocol::UdpDataProtocol(const runModeT runmode, const char* peerHostOrIP) - : DataProtocol(runmode) + : DataProtocol(runmode) { - setPeerIPv4Address(peerHostOrIP); - setBindSocket(); + setPeerIPv4Address(peerHostOrIP); + setBindSocket(); } //******************************************************************************* void UdpDataProtocol::setBindSocket() { - // UDP socket creation - mSockFd = socket(AF_INET, SOCK_DGRAM, 0); - if ( mSockFd < 0 ) { - std::cerr << "ERROR: UDP Socket Error" << std::endl; - std::exit(0); - } - - // Bind local address and port - /// \todo Bind to a different port in case this one is used by a different instance - /// of the program - struct sockaddr_in LocalIPv4Addr = getLocalIPv4AddressStruct(); - int nBind = bind(mSockFd, (struct sockaddr *) &LocalIPv4Addr, sizeof(LocalIPv4Addr)); - if ( nBind < 0 ) { - std::cerr << "ERROR: UDP Socket Bind Error" << std::endl; - std::exit(0); - } - - std::cout << "Successful socket creation and port binding" << std::endl; - - //Connected UDP - struct sockaddr_in PeerIPv4Addr = getPeerIPv4AddressStruct(); - int nCon = ::connect(mSockFd, (struct sockaddr *) &PeerIPv4Addr, sizeof(PeerIPv4Addr)); - if ( nCon < 0) { - std::cerr << "ERROR: UDP Socket Connect Error" << std::endl; - std::exit(0); - } + // UDP socket creation + mSockFd = socket(AF_INET, SOCK_DGRAM, 0); + if ( mSockFd < 0 ) { + std::cerr << "ERROR: UDP Socket Error" << std::endl; + std::exit(0); + } + + // Bind local address and port + /// \todo Bind to a different port in case this one is used by a different instance + /// of the program + struct sockaddr_in LocalIPv4Addr = getLocalIPv4AddressStruct(); + int nBind = bind(mSockFd, (struct sockaddr *) &LocalIPv4Addr, sizeof(LocalIPv4Addr)); + if ( nBind < 0 ) { + std::cerr << "ERROR: UDP Socket Bind Error" << std::endl; + std::exit(0); + } + + std::cout << "Successful socket creation and port binding" << std::endl; + + //Connected UDP + struct sockaddr_in PeerIPv4Addr = getPeerIPv4AddressStruct(); + int nCon = ::connect(mSockFd, (struct sockaddr *) &PeerIPv4Addr, sizeof(PeerIPv4Addr)); + if ( nCon < 0) { + std::cerr << "ERROR: UDP Socket Connect Error" << std::endl; + std::exit(0); + } } @@ -94,25 +94,25 @@ void UdpDataProtocol::setBindSocket() // Page 88 (readn) size_t UdpDataProtocol::receivePacket(char* buff, size_t n) { - size_t nleft; - ssize_t nread; - char* ptr; - - ptr = buff; - nleft = n; - while (nleft > 0) { - if ( (nread = ::read(mSockFd, ptr, nleft)) < 0) { - if (errno == EINTR) - nread = 0; // and call read() again - else - return(-1); - } else if (nread == 0) - break; // EOF - - nleft -= nread; - ptr += nread; - } - return(n - nleft); + size_t nleft; + ssize_t nread; + char* ptr; + + ptr = buff; + nleft = n; + while (nleft > 0) { + if ( (nread = ::read(mSockFd, ptr, nleft)) < 0) { + if (errno == EINTR) + nread = 0; // and call read() again + else + return(-1); + } else if (nread == 0) + break; // EOF + + nleft -= nread; + ptr += nread; + } + return(n - nleft); } @@ -123,23 +123,23 @@ size_t UdpDataProtocol::receivePacket(char* buff, size_t n) // Write "n" bytes to a descriptor size_t UdpDataProtocol::sendPacket(const char* buff, size_t n) { - size_t nleft; - ssize_t nwritten; - const char* ptr; - - ptr = buff; - nleft = n; - while (nleft > 0) { - if ( (nwritten = ::write(mSockFd, ptr, nleft)) <= 0) { - if (nwritten < 0 && errno == EINTR) - nwritten = 0; // and call write() again - else - return(-1); // error + size_t nleft; + ssize_t nwritten; + const char* ptr; + + ptr = buff; + nleft = n; + while (nleft > 0) { + if ( (nwritten = ::write(mSockFd, ptr, nleft)) <= 0) { + if (nwritten < 0 && errno == EINTR) + nwritten = 0; // and call write() again + else + return(-1); // error + } + + nleft -= nwritten; + ptr += nwritten; } - - nleft -= nwritten; - ptr += nwritten; - } - return(n); + return(n); } diff --git a/src/UdpDataProtocolPOSIX.h.tmp b/src/UdpDataProtocolPOSIX.h.tmp index a7fb087..49b1434 100644 --- a/src/UdpDataProtocolPOSIX.h.tmp +++ b/src/UdpDataProtocolPOSIX.h.tmp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -41,25 +41,25 @@ #include "DataProtocol.h" /** \brief UDP implementation of DataProtocol class - * - * + * + * * */ class UdpDataProtocol : public DataProtocol { public: - /** \brief The class constructor + /** \brief The class constructor * \param runmode Sets the run mode, use either SENDER or RECEIVER * \param peerHostOrIP IPv4 number or host name */ - UdpDataProtocol(const runModeT runmode, const char* peerHostOrIP); + UdpDataProtocol(const runModeT runmode, const char* peerHostOrIP); - /** \brief The class destructor + /** \brief The class destructor */ - virtual ~UdpDataProtocol() {}; + virtual ~UdpDataProtocol() {}; - /** \brief Receives a packet + /** \brief Receives a packet * * This function makes sure we recieve a complete packet * of size n @@ -67,9 +67,9 @@ public: * \param n size of packet to receive * \return number of bytes read, -1 on error */ - virtual size_t receivePacket(char* buf, size_t n); + virtual size_t receivePacket(char* buf, size_t n); - /** \brief Sends a packet + /** \brief Sends a packet * * This function meakes sure we send a complete packet * of size n @@ -77,16 +77,16 @@ public: * \param n size of packet to receive * \return number of bytes read, -1 on error */ - virtual size_t sendPacket(const char* buff, size_t n); + virtual size_t sendPacket(const char* buff, size_t n); - //virtual void run(); + //virtual void run(); private: - void setBindSocket(); + void setBindSocket(); - int mSockFd; ///< Socket file descriptor + int mSockFd; ///< Socket file descriptor }; #endif diff --git a/src/UdpMasterListener.cpp b/src/UdpMasterListener.cpp index 6650eaa..3c17676 100644 --- a/src/UdpMasterListener.cpp +++ b/src/UdpMasterListener.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -57,42 +57,49 @@ UdpMasterListener::UdpMasterListener(int server_port) : //mJTWorker(NULL), mServerPort(server_port), mStopped(false), - mTotalRunningThreads(0) + #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; - for (int i = 0; iinsert(i, NULL); - } + // Register JackTripWorker with the master listener + //mJTWorker = new JackTripWorker(this); + mJTWorkers = new QVector; + for (int i = 0; iinsert(i, NULL); + } - //mJTWorkers = new JackTripWorker(this); - mThreadPool.setExpiryTimeout(3000); // msec (-1) = forever - // Inizialize IP addresses - for (int i = 0; iat(i); - } - delete mJTWorkers; + QMutexLocker lock(&mMutex); + mThreadPool.waitForDone(); + //delete mJTWorker; + for (int i = 0; iat(i); + } + delete mJTWorkers; } @@ -103,111 +110,126 @@ UdpMasterListener::~UdpMasterListener() // 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); - } + 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; + const int tcpTimeout = 5*1000; - cout << "JackTrip MULTI-THREADED SERVER: TCP Server Listening in Port = " << TcpServer.serverPort() << endl; - while ( !mStopped ) - { - cout << "JackTrip MULTI-THREADED SERVER: Waiting for client connections..." << endl; - cout << "=======================================================" << endl; - while ( !TcpServer.waitForNewConnection(1000) ) - { if (mStopped) { return; } } // block until a new connection is received - cout << "JackTrip MULTI-THREADED 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 MULTI-THREADED 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 MULTI-THREADED 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 32-bit addresses - int id = isNewAddress(PeerAddress.toIPv4Address(), 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.toIPv4Address(), peer_udp_port); - // stop the thread - mJTWorkers->at(id_remove)->stopThread(); - // block until the thread has been removed from the pool - while ( isNewAddress(PeerAddress.toIPv4Address(), peer_udp_port) == -1 ) { - cout << "JackTrip MULTI-THREADED SERVER: Removing JackTripWorker from pool..." << endl; - QThread::msleep(10); + 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); } - // Get a new ID for this client - //id = isNewAddress(PeerAddress.toIPv4Address(), peer_udp_port); - id = getPoolID(PeerAddress.toIPv4Address(), 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 MULTI-THREADED SERVER: Client TCP Socket 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)); - // redirect port and spawn listener - cout << "---> JackTrip MULTI-THREADED SERVER: Spawning Listener..." << endl; - { - QMutexLocker lock(&mMutex); - mJTWorkers->at(id)->setJackTrip(id, mActiveAddress[id][0], - server_udp_port, mActiveAddress[id][1], - 1); /// \todo temp default to 1 channel - } - //send one thread to the pool - cout << "---> JackTrip MULTI-THREADED SERVER: Starting Thread..." << 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 MULTI-THREADED SERVER: Total Running Threads: " << mTotalRunningThreads << endl; - cout << "===============================================================" << endl; - QThread::msleep(100); } - } - /* + /* // Create objects on the stack QUdpSocket MasterUdpSocket; QHostAddress PeerAddress; @@ -263,47 +285,47 @@ void UdpMasterListener::run() // Returns 0 on error int UdpMasterListener::readClientUdpPort(QTcpSocket* clientConnection) { - // Read the size of the package - // ---------------------------- - //tcpClient.waitForReadyRead(); - cout << "Reading UDP port from Server..." << endl; - while (clientConnection->bytesAvailable() < (int)sizeof(uint16_t)) { - if (!clientConnection->waitForReadyRead()) { - std::cerr << "TCP Socket ERROR: " << clientConnection->errorString().toStdString() << endl; - return 0; + // 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; + } } - } - cout << "Ready To Read From Socket!" << 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; + 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; + // 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; + return 1; + cout << "Port sent to Client" << endl; } @@ -322,90 +344,131 @@ void UdpMasterListener::sendToPoolPrototype(int id) //******************************************************************************* 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; - } + // 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(uint32_t address, uint16_t port) +int UdpMasterListener::isNewAddress(QString address, uint16_t port) { - QMutexLocker lock(&mMutex); - bool busyAddress = false; - int id = 0; + QMutexLocker lock(&mMutex); + bool busyAddress = false; + int id = 0; - /* + /* while ( !busyAddress && (id 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 #include +#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 + * 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; + 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(); + 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; } - /// \brief Stops the execution of the Thread - void stop() { mStopped = true; } + int releaseThread(int id); - int releaseThread(int id); + void setConnectDefaultAudioPorts(bool connectDefaultAudioPorts) { m_connectDefaultAudioPorts = connectDefaultAudioPorts; } private slots: - void testReceive() - { std::cout << "========= TEST RECEIVE SLOT ===========" << std::endl; } + void testReceive() + { std::cout << "========= TEST RECEIVE SLOT ===========" << std::endl; } signals: - void Listening(); - void ClientAddressSet(); - void signalRemoveThread(int id); + void Listening(); + void ClientAddressSet(); + void signalRemoveThread(int id); private: - /** \brief Binds a QUdpSocket. It chooses the available (active) interface. + /** \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); + static void bindUdpSocket(QUdpSocket& udpsocket, int port) throw(std::runtime_error); - int readClientUdpPort(QTcpSocket* clientConnection); - int sendUdpPort(QTcpSocket* clientConnection, int udp_port); + int readClientUdpPort(QTcpSocket* clientConnection); + int sendUdpPort(QTcpSocket* clientConnection, int udp_port); - /** \brief Send the JackTripWorker to the thread pool. This will run + /** \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); + //void sendToPoolPrototype(int id); - /** \brief Check if address is already handled, if not add to array - * \param IPv4 address as a number + /** \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(uint32_t address, uint16_t port); + */ + int isNewAddress(QString address, uint16_t port); - /** \brief Returns the ID of the client in the pool. If the client + /** \brief Returns the ID of the client in the pool. If the client * is not in the pool yet, returns -1. */ - int getPoolID(uint32_t address, uint16_t port); - - //QUdpSocket mUdpMasterSocket; ///< The UDP socket - //QHostAddress mPeerAddress; ///< The Peer Address - - //JackTripWorker* mJTWorker; ///< Class that will be used as prototype - QVector* mJTWorkers; ///< Vector of JackTripWorker s - QThreadPool mThreadPool; ///< The Thread Pool - - int mServerPort; //< Server known port number - int mBasePort; - uint32_t mActiveAddress[gMaxThreads][2]; ///< Active addresses pool numbers (32 bits IPv4 numbers) - QHash mActiveAddresPortPair; - - /// Boolean stop the execution of the thread - volatile bool mStopped; - int mTotalRunningThreads; ///< Number of Threads running in the pool - QMutex mMutex; + 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* 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 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; } }; diff --git a/src/build b/src/build index 18f47fa..15011b4 100755 --- a/src/build +++ b/src/build @@ -27,7 +27,7 @@ if [[ $platform == 'linux' ]]; then QSPEC=linux-g++ elif [[ $platform == 'macosx' ]]; then QCMD=qmake - QSPEC=macx-g++ + QSPEC=macx-clang fi # Build diff --git a/src/jacktrip.pro b/src/jacktrip.pro index bd95a2b..590419f 100644 --- a/src/jacktrip.pro +++ b/src/jacktrip.pro @@ -2,22 +2,38 @@ # Created by Juan-Pablo Caceres #****************************** +CONFIG += c++11 console +CONFIG -= app_bundle + CONFIG += qt thread debug_and_release build_all CONFIG(debug, debug|release) { TARGET = jacktrip_debug } else { TARGET = jacktrip } + QT -= gui QT += network + +# rc.1.2 switch enables experimental wair build, merge some of it with WAIRTOMASTER +# DEFINES += WAIR +DEFINES += WAIRTOMASTER + # http://wiki.qtcentre.org/index.php?title=Undocumented_qmake#Custom_tools -DEFINES += __RT_AUDIO__ +#cc DEFINES += __RT_AUDIO__ # Configuration without Jack nojack { DEFINES += __NO_JACK__ } + +# for plugins +INCLUDEPATH += ../faust-src-lair/stk + !win32 { INCLUDEPATH+=/usr/local/include +# wair needs stk, can be had from linux this way +# INCLUDEPATH+=/usr/include/stk +# LIBS += -L/usr/local/lib -ljack -lstk -lm LIBS += -L/usr/local/lib -ljack -lm nojack { message(Building NONJACK) @@ -26,59 +42,83 @@ nojack { } macx { - message(MAC OS X) + message(Building on MAC OS X) QMAKE_CXXFLAGS += -D__MACOSX_CORE__ #-D__UNIX_JACK__ #RtAudio Flags QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.9 - QMAKE_MAC_SDK = macosx10.9 + #QMAKE_MAC_SDK = macosx10.9 CONFIG -= app_bundle #CONFIG += x86 #ppc #### If you have both libraries installed, you # can change between 32bits (x86) or 64bits(x86_64) Change this to go back to 32 bits (x86) LIBS += -framework CoreAudio -framework CoreFoundation DEFINES += __MAC_OSX__ } -linux-g++ { - message(Linux) - LIBS += -lasound + +linux-g++ | linux-g++-64 { +# LIBS += -lasound -lrtaudio QMAKE_CXXFLAGS += -D__LINUX_ALSA__ #-D__LINUX_OSS__ #RtAudio Flags + +FEDORA = $$system(cat /proc/version | grep -o fc) + +contains( FEDORA, fc): { + message(building on fedora) +} + +UBUNTU = $$system(cat /proc/version | grep -o Ubuntu) + +contains( UBUNTU, Ubuntu): { + message(building on Ubuntu) + +# workaround for Qt bug under ubuntu 18.04 +# gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3) +# QMake version 3.1 +# Using Qt version 5.9.5 in /usr/lib/x86_64-linux-gnu + INCLUDEPATH += /usr/include/x86_64-linux-gnu/c++/7 + +# sets differences from original fedora version + DEFINES += __UBUNTU__ +} + QMAKE_CXXFLAGS += -g -O2 DEFINES += __LINUX__ } + +linux-g++ { + message(Linux) + QMAKE_CXXFLAGS += -D__LINUX_ALSA__ #-D__LINUX_OSS__ #RtAudio Flags + } + linux-g++-64 { message(Linux 64bit) - LIBS += -lasound QMAKE_CXXFLAGS += -fPIC -D__LINUX_ALSA__ #-D__LINUX_OSS__ #RtAudio Flags - QMAKE_CXXFLAGS += -g -O2 - DEFINES += __LINUX__ } + + win32 { - message(win32) - CONFIG += x86 console - QMAKE_CXXFLAGS += -D__WINDOWS_ASIO__ #-D__UNIX_JACK__ #RtAudio Flags - LIBS += -lWs2_32 -lOle32 #needed by rtaudio/asio - LIBS += "../externals/includes/QTWindows/libjack.lib" + message(Building on win32) +#cc CONFIG += x86 console + CONFIG += c++11 console + INCLUDEPATH += "C:\Program Files (x86)\Jack\includes" + LIBS += "C:\Program Files (x86)\Jack\lib\libjack64.lib" + LIBS += "C:\Program Files (x86)\Jack\lib\libjackserver64.lib" +#cc QMAKE_CXXFLAGS += -D__WINDOWS_ASIO__ #-D__UNIX_JACK__ #RtAudio Flags + #QMAKE_LFLAGS += -static -static-libgcc -static-libstdc++ -lpthread + LIBS += -lWs2_32 #cc -lOle32 #needed by rtaudio/asio DEFINES += __WIN_32__ - DEFINES -= UNICODE #RtAudio for Qt + DEFINES += _WIN32_WINNT=0x0600 #needed for inet_pton +#cc DEFINES -= UNICODE #RtAudio for Qt } - - - DESTDIR = . QMAKE_CLEAN += -r ./jacktrip ./jacktrip_debug ./release ./debug target.path = /usr/bin INSTALLS += target -#INCLUDEPATH += ../externals/includes/rtaudio-4.0.7 -#DEPENDPATH += ../externals/includes/rtaudio-4.0.7 -win32 { - INCLUDEPATH += ../externals/includes/rtaudio-4.0.7/include - INCLUDEPATH += ../externals/includes - DEPENDPATH += ../externals/includes/rtaudio-4.0.7/include - DEPENDPATH += ../externals/includes -} +# for plugins +INCLUDEPATH += ../faust-src-lair # Input HEADERS += DataProtocol.h \ + JMess.h \ JackTrip.h \ jacktrip_globals.h \ jacktrip_types.h \ @@ -96,13 +136,13 @@ HEADERS += DataProtocol.h \ ThreadPoolTest.h \ UdpDataProtocol.h \ UdpMasterListener.h \ - AudioInterface.h \ - RtAudioInterface.h - #JamTest.h + AudioInterface.h + !nojack { -SOURCES += JackAudioInterface.h +HEADERS += JackAudioInterface.h } SOURCES += DataProtocol.cpp \ + JMess.cpp \ JackTrip.cpp \ jacktrip_globals.cpp \ jacktrip_main.cpp \ @@ -114,30 +154,22 @@ SOURCES += DataProtocol.cpp \ ProcessPlugin.cpp \ RingBuffer.cpp \ Settings.cpp \ - #tests.cpp \ UdpDataProtocol.cpp \ UdpMasterListener.cpp \ - AudioInterface.cpp \ - RtAudioInterface.cpp + AudioInterface.cpp + !nojack { SOURCES += JackAudioInterface.cpp } # RtAduio Input -HEADERS += ../externals/includes/rtaudio-4.0.7/RtAudio.h \ - ../externals/includes/rtaudio-4.0.7/RtError.h -SOURCES += ../externals/includes/rtaudio-4.0.7/RtAudio.cpp win32 { -HEADERS += asio.h \ - asiodrivers.h \ - asiolist.h \ - asiodrvr.h \ - asiosys.h \ - ginclude.h \ - iasiodrv.h \ - iasiothiscallresolver.h -SOURCES += asio.cpp \ - asiodrivers.cpp \ - asiolist.cpp \ - iasiothiscallresolver.cpp + INCLUDEPATH += ../externals/rtaudio-4.1.1/include + DEPENDPATH += ../externals/rtaudio-4.1.1/include +} +macx | win32 { +INCLUDEPATH += ../externals/rtaudio-4.1.1/ +DEPENDPATH += ../externals/rtaudio-4.1.1/ +HEADERS += +SOURCES += } diff --git a/src/jacktrip_globals.cpp b/src/jacktrip_globals.cpp index feb5191..5bd9a54 100644 --- a/src/jacktrip_globals.cpp +++ b/src/jacktrip_globals.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -36,188 +36,141 @@ */ #include -#include -#include - -#include "jacktrip_globals.h" -#include "jacktrip_types.h" #if defined ( __LINUX__ ) -#include -#include -#include + #include + #include + #include #endif //__LINUX__ #if defined ( __MAC_OSX__ ) -#include -#include - -//#include - -//#include -//#include -//#include - -//#include -//#include -//m#include -//#include -//#include - - -//#include -//#include -//#include -//#include -//#include - + #include + #include + #include +#endif //__MAC_OSX__ +#include "jacktrip_globals.h" -//#include -//#include -//#include -//#include -//#include +#if defined ( __MAC_OSX__ ) -#endif //__MAC_OSX__ +// The following function is taken from the chromium source code +// https://github.com/chromium/chromium/blob/master/base/threading/platform_thread_mac.mm +// For the following macOS implementation of the function setRealtimeProcessPriority() only: Copyright (c) 2012 The Chromium Authors. All rights reserved. + +// Enables time-contraint policy and priority suitable for low-latency, +// glitch-resistant audio. +void setRealtimeProcessPriority() { + // Increase thread priority to real-time. + + // Please note that the thread_policy_set() calls may fail in + // rare cases if the kernel decides the system is under heavy load + // and is unable to handle boosting the thread priority. + // In these cases we just return early and go on with life. + + mach_port_t mach_thread_id = mach_thread_self(); + + // Make thread fixed priority. + thread_extended_policy_data_t policy; + policy.timeshare = 0; // Set to 1 for a non-fixed thread. + kern_return_t result = + thread_policy_set(mach_thread_id, + THREAD_EXTENDED_POLICY, + reinterpret_cast(&policy), + THREAD_EXTENDED_POLICY_COUNT); + if (result != KERN_SUCCESS) { + std::cerr << "Failed to make thread fixed priority. " << result << std::endl; + return; + } + // Set to relatively high priority. + thread_precedence_policy_data_t precedence; + precedence.importance = 63; + result = thread_policy_set(mach_thread_id, + THREAD_PRECEDENCE_POLICY, + reinterpret_cast(&precedence), + THREAD_PRECEDENCE_POLICY_COUNT); + if (result != KERN_SUCCESS) { + std::cerr << "Failed to set thread priority. " << result << std::endl; + return; + } -#if defined ( __MAC_OSX__ ) -//******************************************************************************* -//http://developer.apple.com/DOCUMENTATION/Darwin/Conceptual/KernelProgramming/scheduler/chapter_8_section_4.html -//http://lists.apple.com/archives/darwin-dev/2007/Sep/msg00035.html -int set_realtime(int period, int computation, int constraint) -{ - //AbsoluteTime time; - //clock_get_uptime((uint64_t *)&time); - - //uint64_t result; - //clock_get_uptime(&result); - //clock_get_system_microtime(&result,&result); - - struct thread_time_constraint_policy ttcpolicy; - int ret; - - ttcpolicy.period=period; // HZ/160 - ttcpolicy.computation=computation; // HZ/3300; - ttcpolicy.constraint=constraint; // HZ/2200; - ttcpolicy.preemptible=1; - - if ((ret=thread_policy_set(mach_thread_self(), - THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t)&ttcpolicy, - THREAD_TIME_CONSTRAINT_POLICY_COUNT)) != KERN_SUCCESS) { - fprintf(stderr, "set_realtime() failed.\n"); - return 0; - } - return 1; + // Most important, set real-time constraints. + + // Define the guaranteed and max fraction of time for the audio thread. + // These "duty cycle" values can range from 0 to 1. A value of 0.5 + // means the scheduler would give half the time to the thread. + // These values have empirically been found to yield good behavior. + // Good means that audio performance is high and other threads won't starve. + const double kGuaranteedAudioDutyCycle = 0.75; + const double kMaxAudioDutyCycle = 0.85; + + // Define constants determining how much time the audio thread can + // use in a given time quantum. All times are in milliseconds. + + // About 128 frames @44.1KHz + const double kTimeQuantum = 2.9; + + // Time guaranteed each quantum. + const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum; + + // Maximum time each quantum. + const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum; + + // Get the conversion factor from milliseconds to absolute time + // which is what the time-constraints call needs. + mach_timebase_info_data_t tb_info; + mach_timebase_info(&tb_info); + double ms_to_abs_time = + (static_cast(tb_info.denom) / tb_info.numer) * 1000000; + + thread_time_constraint_policy_data_t time_constraints; + time_constraints.period = kTimeQuantum * ms_to_abs_time; + time_constraints.computation = kAudioTimeNeeded * ms_to_abs_time; + time_constraints.constraint = kMaxTimeAllowed * ms_to_abs_time; + time_constraints.preemptible = 0; + + result = thread_policy_set(mach_thread_id, + THREAD_TIME_CONSTRAINT_POLICY, + reinterpret_cast(&time_constraints), + THREAD_TIME_CONSTRAINT_POLICY_COUNT); + if (result != KERN_SUCCESS) + std::cerr << "Failed to set thread realtime constraints. " << result << std::endl; + + return; } -#endif //__MAC_OSX__ - -#if defined ( __LINUX__ ) -//******************************************************************************* -int get_fifo_priority (bool half) -{ - int min, max, priority; - min = sched_get_priority_min (SCHED_FIFO); - max = sched_get_priority_max (SCHED_FIFO); - if (half) { - priority = (max - (max - min) / 2); } - else { - priority = max; } - - //priority=min; - return priority; -} -#endif //__LINUX__ +#endif //__MAC_OSX__ #if defined ( __LINUX__ ) //******************************************************************************* -int set_fifo_priority (bool half) +void setRealtimeProcessPriority() { - struct sched_param p; - int priority; - // scheduling priority + int priority = sched_get_priority_max(SCHED_FIFO); // 99 is the highest possible +#ifdef __UBUNTU__ + priority = 95; // anything higher is silently ignored by Ubuntu 18.04 +#endif + struct sched_param sp = { .sched_priority = priority }; - if (true) // (!getuid () || !geteuid ()) - { - priority = get_fifo_priority (half); - p.sched_priority = priority; - - if (sched_setscheduler (0, SCHED_FIFO, &p) == -1) - { - fprintf (stderr, - "\ncould not activate scheduling with priority %d\n", - priority); - return -1; - } - seteuid (getuid ()); - //fprintf (stderr, - // "\nset scheduling priority to %d (SCHED_FIFO)\n", - // priority); - } - else - { - fprintf (stderr, - "\ninsufficient privileges to set scheduling priority\n"); - priority = 0; + if (sched_setscheduler(0, SCHED_FIFO, &sp) == -1) { + std::cerr << "Failed to set the scheduler policy and priority." << std::endl;; } - return priority; } #endif //__LINUX__ -#if defined ( __LINUX__ ) -//******************************************************************************* -int set_realtime_priority (void) +#if defined ( __WIN_32__ ) +void setRealtimeProcessPriority() { - struct sched_param schp; - - memset (&schp, 0, sizeof (schp)); - schp.sched_priority = sched_get_priority_max (SCHED_FIFO); - if (sched_setscheduler (0, SCHED_FIFO, &schp) != 0) + if (SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS) == 0) { - perror ("set_scheduler"); - return -1; + std::cerr << "Failed to set process priority class." << std::endl; + } + if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) == 0) + { + std::cerr << "Failed to set thread priority." << std::endl; } - return 0; -} -#endif //__LINUX__ - - -#if defined ( __WIN_32__ ) -int win_priority() -{ - if (SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS) == 0) - { - printf("set Priority Class failed \n"); - return -1; - } - if(SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) == 0) - { - printf("set Thread Priority failed \n"); - return -1; - } - return GetThreadPriority(GetCurrentThread()); } #endif //__WIN_32__ - - -void set_crossplatform_realtime_priority() -{ -#if defined ( __LINUX__ ) - set_fifo_priority (false); -#endif //__LINUX__ -#if defined ( __MAC_OSX__ ) - set_realtime(1250000,60000,90000); -#endif //__MAC_OSX__ -#if defined __WIN_32__ - win_priority(); -#endif - -} - - diff --git a/src/jacktrip_globals.h b/src/jacktrip_globals.h index 3f87dac..9f31e6a 100644 --- a/src/jacktrip_globals.h +++ b/src/jacktrip_globals.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -44,20 +44,48 @@ /// \todo Add this namespace //namespace JackTrip -const char* const gVersion = "1.1"; ///< JackTrip version +const char* const gVersion = "1.2beta2"; ///< JackTrip version //******************************************************************************* /// \name Default Values //@{ const int gDefaultNumInChannels = 2; const int gDefaultNumOutChannels = 2; + +#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 + +// hubpatch = 3 for TUB ensemble patching +/////////////////////////////// +// test NUC as server +//const QString gDOMAIN_TRIPLE = QString("130.149.23"); // for TUB multiclient hub +//const int gMIN_TUB = 245; // lowest client address +//const int gMAX_TUB = 245; // highest client address +/////////////////////////////// +// test Riviera as server + const QString gDOMAIN_TRIPLE = QString("192.168.0"); // for TUB multiclient hub + const int gMIN_TUB = 11; // lowest client address + const int gMAX_TUB = 20; // highest client address + +#ifdef WAIR // wair +// uses hub mode +// hard wire the number of netrev (comb filter) channels + #define NUMNETREVCHANSbecauseNOTINRECEIVEDheader 16 // for jacktripworker, jmess + const int gDefaultNumNetRevChannels = NUMNETREVCHANSbecauseNOTINRECEIVEDheader; + const int gDefaultAddCombFilterLength = 0; + const int gDefaultCombFilterFeedback = 0; +#endif // endwhere + //const JackAudioInterface::audioBitResolutionT gDefaultBitResolutionMode = // JackAudioInterface::BIT16; const AudioInterface::audioBitResolutionT gDefaultBitResolutionMode = - AudioInterface::BIT16; + AudioInterface::BIT16; const int gDefaultQueueLength = 4; const int gDefaultOutputQueueLength = 4; const uint32_t gDefaultSampleRate = 48000; +const uint32_t gDefaultDeviceID = 0; const uint32_t gDefaultBufferSizeInSamples = 128; const QString gDefaultLocalAddress = QString(); const int gDefaultRedundancy = 1; @@ -100,32 +128,8 @@ const int gJackBitResolution = 32; ///< Audio Bit Resolution of the Jack Server //******************************************************************************* /// \name Global Functions -void set_crossplatform_realtime_priority(); +void setRealtimeProcessPriority(); -//@{ -// Linux Specific Functions -#if defined ( __LINUX__ ) -/// \brief Returns fifo priority -int get_fifo_priority (bool half); -/// \brief Set fifo priority (if user has sufficient privileges). -int set_fifo_priority (bool half); -int set_realtime_priority (void); -#endif //__LINUX__ -//@} - -//@{ -// Mac OS X Specific Functions -#if defined ( __MAC_OSX__ ) -int set_realtime(int period, int computation, int constraint); -#endif //__MAC_OSX__ -//@} - -//@{ -// Windows Specific Functions -#if defined ( __WIN_32__ ) -int win_priority(); -#endif //__WIN_32__ -//@} //******************************************************************************* /// \name JackTrip Server parameters diff --git a/src/jacktrip_main.cpp b/src/jacktrip_main.cpp index 96159d2..5e172a8 100644 --- a/src/jacktrip_main.cpp +++ b/src/jacktrip_main.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -38,6 +38,8 @@ #include #include +#include +#include #include "JackAudioInterface.h" #include "UdpDataProtocol.h" @@ -48,57 +50,63 @@ #include "LoopBack.h" #include "PacketHeader.h" //#include "JackTripThread.h" +#ifdef __RT_AUDIO__ #include "RtAudioInterface.h" +#endif #include "jacktrip_tests.cpp" - #include "jacktrip_globals.h" -using std::cout; using std::endl; +void qtMessageHandler(QtMsgType /*type*/, const QMessageLogContext& /*context*/, const QString& msg) +{ + std::cerr << msg.toStdString() << std::endl; +} + int main(int argc, char** argv) { - QCoreApplication app(argc, argv); + QCoreApplication app(argc, argv); + QLoggingCategory::setFilterRules(QStringLiteral("*.debug=true")); + qInstallMessageHandler(qtMessageHandler); - bool testing = false; - if ( argc > 1 ) { - if ( !strcmp(argv[1], "test") ) { - testing = true; + bool testing = false; + if ( argc > 1 ) { + if ( !strcmp(argv[1], "test") ) { + testing = true; + } } - } - - if ( testing ) { - cout << "=========TESTING=========" << endl; - //main_tests(argc, argv); // test functions - JackTrip jacktrip; - RtAudioInterface rtaudio(&jacktrip); - //rtaudio.setup(); - rtaudio.listAllInterfaces(); - //rtaudio.printDeviceInfo(0); - //while (true) sleep(9999); - } - else { - //--------------------------------------- + if ( testing ) { + std::cout << "=========TESTING=========" << std::endl; + //main_tests(argc, argv); // test functions + JackTrip jacktrip; + //RtAudioInterface rtaudio(&jacktrip); + //rtaudio.setup(); + //rtaudio.listAllInterfaces(); + //rtaudio.printDeviceInfo(0); - // Get Settings from user - // ---------------------- - try - { - // Get Settings from user - // ---------------------- - Settings* settings = new Settings; - settings->parseInput(argc, argv); - settings->startJackTrip(); + //while (true) sleep(9999); } - catch ( const std::exception & e ) - { - std::cerr << "ERROR:" << endl; - std::cerr << e.what() << endl; - std::cerr << "Exiting JackTrip..." << endl; - std::cerr << gPrintSeparator << endl; - return -1; + else { + // catch all potential exeptions + try + { + // Get Settings from user + // ---------------------- + Settings* settings = new Settings; + settings->parseInput(argc, argv); + settings->startJackTrip(); + } + catch ( const std::exception & e ) + { + std::cerr << "ERROR:" << std::endl; + std::cerr << e.what() << std::endl; + std::cerr << "Exiting JackTrip..." << std::endl; + std::cerr << gPrintSeparator << std::endl; + return -1; + } } - } + if (gVerboseFlag) std::cout << "step 6" << std::endl; + if (gVerboseFlag) std::cout << "jacktrip_main before app.exec()" << std::endl; - return app.exec(); + return app.exec(); } diff --git a/src/jacktrip_tests.cpp b/src/jacktrip_tests.cpp index 17cddf1..e4c3d8f 100644 --- a/src/jacktrip_tests.cpp +++ b/src/jacktrip_tests.cpp @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 @@ -54,13 +54,13 @@ void test_threads_client(const char* peer_address); void main_tests(int /*argc*/, char** argv) { - if (argv[1][0] == 's' ) + if (argv[1][0] == 's' ) { - test_threads_server(); + test_threads_server(); } - else if (argv[1][0] == 'c' ) - { - test_threads_client("171.64.197.209"); + else if (argv[1][0] == 'c' ) + { + test_threads_client("171.64.197.209"); } } @@ -68,17 +68,17 @@ void main_tests(int /*argc*/, char** argv) // Test many servers running at the same time void test_threads_server() { - QVector jacktrips; - jacktrips.resize(num_jacktrips); - int port_num; - for (int i = 0; i < num_jacktrips; i++) + QVector jacktrips; + jacktrips.resize(num_jacktrips); + int port_num; + for (int i = 0; i < num_jacktrips; i++) { - port_num = base_port + i*10; - cout << "Port Number: " << port_num << endl; - jacktrips[i] = new JackTripThread(JackTrip::SERVER); - jacktrips[i]->setPort(port_num); - jacktrips[i]->start(QThread::NormalPriority); - //sleep(1); + port_num = base_port + i*10; + cout << "Port Number: " << port_num << endl; + jacktrips[i] = new JackTripThread(JackTrip::SERVER); + jacktrips[i]->setPort(port_num); + jacktrips[i]->start(QThread::NormalPriority); + //sleep(1); } } @@ -86,18 +86,18 @@ void test_threads_server() // Test many servers running at the same time void test_threads_client(const char* peer_address) { - QVector jacktrips; - jacktrips.resize(num_jacktrips); - int port_num; - for (int i = 0; i < num_jacktrips; i++) + QVector jacktrips; + jacktrips.resize(num_jacktrips); + int port_num; + for (int i = 0; i < num_jacktrips; i++) { - port_num = base_port + i*10; - cout << "Port Number: " << port_num << endl; - jacktrips[i] = new JackTripThread(JackTrip::CLIENT); - jacktrips[i]->setPort(port_num); - jacktrips[i]->setPeerAddress(peer_address); - //sleep(1); - jacktrips[i]->start(QThread::NormalPriority); - //sleep(1); + port_num = base_port + i*10; + cout << "Port Number: " << port_num << endl; + jacktrips[i] = new JackTripThread(JackTrip::CLIENT); + jacktrips[i]->setPort(port_num); + jacktrips[i]->setPeerAddress(peer_address); + //sleep(1); + jacktrips[i]->start(QThread::NormalPriority); + //sleep(1); } } diff --git a/src/jacktrip_types.h b/src/jacktrip_types.h index f9f10f0..31a6a63 100644 --- a/src/jacktrip_types.h +++ b/src/jacktrip_types.h @@ -5,7 +5,7 @@ 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 @@ -14,10 +14,10 @@ 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 -- 2.30.2