From: IOhannes m zmölnig Date: Sun, 28 Jun 2015 19:44:36 +0000 (+0200) Subject: Imported Upstream version 1.1~repack X-Git-Tag: archive/raspbian/2.5.1+ds-1+rpi1~1^2~9^2~33 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=c666435c5848ef17cabca8bd7f7ad5642171aef8;p=jacktrip.git Imported Upstream version 1.1~repack --- diff --git a/CHANGESLOG.txt b/CHANGESLOG.txt index 1849861..446e7e5 100644 --- a/CHANGESLOG.txt +++ b/CHANGESLOG.txt @@ -1,3 +1,10 @@ +--- +1.1 +- (added) Support for RtAudio. Jacktrip can now be used without Jack +- (added) DNS Look-up support, now one machine can have a private IP (but still needs to have UDP ports open) +- (added) New port to Windows XP and Windows Vista (experimental and not tested for a long time, only when using jacktrip as a library) +- (added) Multiclient Server (experimental and not exposed in the executable) + --- 1.0.5 - (added) Compatibility with JamLink boxes (restricted at the moment to 48KHz, 64 buffer size and 1 channel) diff --git a/INSTALL.txt b/INSTALL.txt index cc95c0f..81f3d67 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1,69 +1,116 @@ -Jacktrip : Build Instructions for Linux and MacOS X - -JackTrip: A System for High-Quality Audio Network Performance over the Internet. - ---- -MacOS X (10.3.9 or higher) Installation: - -If you are installing on MacOS X, a universal binary is provided. You just need to have JackOSX installed on your machine -http://www.jackosx.com/ - -To install (using Terminal): go to bin/ directory and type: - - 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) - - ---- -Dependencies: - -You need to have installed the libraries in your system: -qt4-devel -jack-audio-connection-kit-devel - -If you are using yum (in Fedora 8 or later) you can just install them (as root) with: - yum install qt4-devel jack-audio-connection-kit-devel - -If you want to build on MacOS X, you need JackOSX -http://www.jackosx.com/ -and Qt 4.5 or higher. -http://trolltech.com/products/qt/ - - ---- -Build: - -If you're on Mac OS X or Fedora Linux and have all the dependencies installed, -you can build by simply going to the /src directory and typing the following: - ./build - - -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 - - -If you want to install install (using Terminal): on the /src directory type: - - 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) - - ---- -Post Configuration -Detailed instructions at -http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/ - - ---- -Using JackTrip -Detailed instructions at -http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/ +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: + +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/ + +To install (using Terminal): go to bin/ directory and type: + + 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) + + +--- +Dependencies: + +You need to have installed the libraries in your system: +Qt 5.3 or higher +jack-audio-connection-kit-devel + +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 +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. + +It is also possible to build without jack, see below. + +--- +Build: + +If you're on Mac OS X or Fedora Linux and have all the dependencies installed, +you can build by simply going to the /src directory and typing the following: + ./build + +If you want to build without Jack support (meaning that you won't need jack installed +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 + +Or without Jack support: + qmake -config nojack jacktrip.pro + make release + + +If you want to install install (using Terminal): on the /src directory type: + + 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) + +----------------------------- +WINDOWS (XP and later) + +Dependencies: + +- ASIO4all audio driver is required even with Audio interfaces that support ASIO: www.asio4all.com + +Installation: + +Simply Run the setup file Jacktrip-1.1.exe, which will unpack Jacktrip in the directory of your choice, +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 +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/ + +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. + +Building is not supported with Microsoft Visual Studio Compilers. + +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 + +Always keep the /src and /externals directories under the same directory. + +--- +Post Configuration +Detailed instructions at +http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/ + + +--- +Using JackTrip +Detailed instructions at +http://ccrma.stanford.edu/groups/soundwire/software/jacktrip/ diff --git a/documentation/documentation.cpp b/documentation/documentation.cpp index 54908c2..4783844 100644 --- a/documentation/documentation.cpp +++ b/documentation/documentation.cpp @@ -61,7 +61,7 @@ It is currently being developed and actively tested at CCRMA by the SoundWIRE gr \section install_sec Installation Download the latest release: -Download +Download Please read the documentation inside the packet to install. diff --git a/jacktrip_doxygen b/jacktrip_doxygen index 61e6740..8682067 100644 --- a/jacktrip_doxygen +++ b/jacktrip_doxygen @@ -282,7 +282,7 @@ EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. -EXTRACT_PRIVATE = YES +EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. diff --git a/src/AudioInterface.cpp b/src/AudioInterface.cpp new file mode 100644 index 0000000..dfea25f --- /dev/null +++ b/src/AudioInterface.cpp @@ -0,0 +1,397 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file AudioInterface.cpp + * \author Juan-Pablo Caceres + * \date July 2009 + */ + +#include "AudioInterface.h" +#include "JackTrip.h" +#include +#include + +using std::cout; using std::endl; + +//******************************************************************************* +AudioInterface::AudioInterface(JackTrip* jacktrip, + int NumInChans, int NumOutChans, + audioBitResolutionT AudioBitResolution) : +mJackTrip(jacktrip), +mNumInChans(NumInChans), mNumOutChans(NumOutChans), +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; + } +} + + +//******************************************************************************* +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]; + } +} + + +//******************************************************************************* +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); + } +} + + +//******************************************************************************* +size_t AudioInterface::getSizeInBytesPerChannel() const +{ + return (getBufferSizeInSamples() * getAudioBitResolution()/8); +} + + +//******************************************************************************* +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************************** + ///******************************************************** + /* + 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, ); + + ///******************************************************** + ///******************************************************** + +} + + +//******************************************************************************* +// Before sending and reading to Jack, we have to round to the sample resolution +// that the program is using. Jack uses 32 bits (gJackBitResolution in globals.h) +// by default +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 ); + } + } +} + + +//******************************************************************************* +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 ); +} + + +//******************************************************************************* +// 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) +{ + 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; + 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; + 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; + case BIT32 : + std::memcpy(output, input, 4); // 32bit = 4 bytes + break; + } +} + + +//******************************************************************************* +void AudioInterface::fromBitToSampleConversion + (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) + { + case BIT8 : + 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; + 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; + case BIT32 : + std::memcpy(output, input, 4); // 4 bytes + break; + } +} + + +//******************************************************************************* +void AudioInterface::appendProcessPlugin(ProcessPlugin* 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; +} + +//******************************************************************************* +int AudioInterface::getSampleRateFromType(samplingRateT rate_type) +{ + int sample_rate = 0; + switch (rate_type) + { + case SR22 : + sample_rate = 22050; + return sample_rate; + break; + case SR32 : + sample_rate = 32000; + return sample_rate; + break; + case SR44 : + sample_rate = 44100; + return sample_rate; + break; + case SR48 : + sample_rate = 48000; + return sample_rate; + break; + case SR88 : + sample_rate = 88200; + return sample_rate; + break; + case SR96 : + sample_rate = 96000; + return sample_rate; + break; + case SR192 : + sample_rate = 192000; + return sample_rate; + break; + default: + return sample_rate; + break; + } + + return sample_rate; +} + + diff --git a/src/AudioInterface.h b/src/AudioInterface.h new file mode 100644 index 0000000..b35b5cd --- /dev/null +++ b/src/AudioInterface.h @@ -0,0 +1,212 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file AudioInterface.h + * \author Juan-Pablo Caceres + * \date July 2009 + */ + +#ifndef __AUDIOINTERFACE_H__ +#define __AUDIOINTERFACE_H__ + +#include "ProcessPlugin.h" +#include "jacktrip_types.h" + +#include +#include +//#include "jacktrip_globals.h" + +// Forward declarations +class JackTrip; + +//using namespace JackTripNamespace; + + +/** \brief Base Class that provides an interface with audio + */ +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 + * \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 + * + * 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 + 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 + * 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 + * 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 + * 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 + * + * 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 + * JackAudioInterface::samplingRateT + * \param rate_type JackAudioInterface::samplingRateT enum type + * \return Sample Rate in Hz + */ + static int getSampleRateFromType(samplingRateT rate_type); + //------------------------------------------------------------------ + + +private: + + /// \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); + + 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 +}; + +#endif // __AUDIOINTERFACE_H__ diff --git a/src/DataProtocol.h b/src/DataProtocol.h index 9f9d2bc..53f2790 100644 --- a/src/DataProtocol.h +++ b/src/DataProtocol.h @@ -38,14 +38,23 @@ #ifndef __DATAPROTOCOL_H__ #define __DATAPROTOCOL_H__ -//#include //basic socket definitions +#ifdef __WIN_32__ +#include +#endif + +#ifndef __WIN_32__ #include //sockaddr_in{} and other Internet defns #include //inet(3) functions #include -#include //for shared_ptr +//#include //for shared_ptr +#endif + +#include #include #include +#include +#include class JackTrip; // forward declaration @@ -82,6 +91,8 @@ class JackTrip; // forward declaration */ class DataProtocol : public QThread { + Q_OBJECT; + public: //----------ENUMS------------------------------------------ @@ -123,17 +134,20 @@ public: virtual void run() = 0; /// \brief Stops the execution of the Thread - virtual void stop() { mStopped = true; }; + virtual void stop() { + QMutexLocker lock(&mMutex); + mStopped = true; + } /** \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 * \return size_bytes Size in bytes */ - size_t getAudioPacketSizeInBites() { return(mAudioPacketSize); }; + size_t getAudioPacketSizeInBites() { return(mAudioPacketSize); } /** \brief Set the peer address * \param peerHostOrIP IPv4 number or host name @@ -150,12 +164,19 @@ public: //virtual void getPeerAddressFromFirstPacket(QHostAddress& peerHostAddress, // uint16_t& port) = 0; + +signals: + + void signalError(const char* error_message); + void signalReceivedConnectionFromPeer(); + + protected: /** \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; @@ -163,6 +184,7 @@ protected: volatile bool mHasPeerAddress; /// Boolean that indicates if a packet was received volatile bool mHasPacketsToReceive; + QMutex mMutex; private: diff --git a/src/JackAudioInterface.cpp b/src/JackAudioInterface.cpp index 5ca3ba3..3c8c0ca 100644 --- a/src/JackAudioInterface.cpp +++ b/src/JackAudioInterface.cpp @@ -62,47 +62,38 @@ QMutex JackAudioInterface::sJackMutex; //******************************************************************************* JackAudioInterface::JackAudioInterface(JackTrip* jacktrip, - int NumInChans, int NumOutChans, - audioBitResolutionT AudioBitResolution, - const char* ClienName) : - mNumInChans(NumInChans), mNumOutChans(NumOutChans), - mAudioBitResolution(AudioBitResolution*8), mBitResolutionMode(AudioBitResolution), - mClient(NULL), - mClientName(ClienName), - mJackTrip(jacktrip) -{ - //setupClient(); - //setProcessCallback(); -} + int NumInChans, int NumOutChans, + 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) +{} //******************************************************************************* JackAudioInterface::~JackAudioInterface() -{ - delete[] mInputPacket; - delete[] mOutputPacket; - - for (int i = 0; i < mNumInChans; i++) { - delete[] mInProcessBuffer[i]; - } - - for (int i = 0; i < mNumOutChans; i++) { - delete[] mOutProcessBuffer[i]; - } -} +{} //******************************************************************************* void JackAudioInterface::setup() { setupClient(); + AudioInterface::setup(); setProcessCallback(); } //******************************************************************************* void JackAudioInterface::setupClient() -{ +{ const char* client_name = mClientName; const char* server_name = NULL; jack_options_t options = JackNoStartServer; @@ -141,35 +132,12 @@ void JackAudioInterface::setupClient() // Create input and output channels createChannels(); - // 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]; - // Buffer size member mNumFrames = getBufferSizeInSamples(); // Initialize Buffer array to read and write audio mInBuffer.resize(mNumInChans); mOutBuffer.resize(mNumOutChans); - - // 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); - } } @@ -207,72 +175,6 @@ uint32_t JackAudioInterface::getSampleRate() const } -//******************************************************************************* -JackAudioInterface::samplingRateT JackAudioInterface::getSampleRateType() const -{ - uint32_t rate = jack_get_sample_rate(mClient); - - if ( rate == 22050 ) { - return JackAudioInterface::SR22; } - else if ( rate == 32000 ) { - return JackAudioInterface::SR32; } - else if ( rate == 44100 ) { - return JackAudioInterface::SR44; } - else if ( rate == 48000 ) { - return JackAudioInterface::SR48; } - else if ( rate == 88200 ) { - return JackAudioInterface::SR88; } - else if ( rate == 96000 ) { - return JackAudioInterface::SR96; } - else if ( rate == 19200 ) { - return JackAudioInterface::SR192; } - - return JackAudioInterface::UNDEF; -} - - -//******************************************************************************* -int JackAudioInterface::getSampleRateFromType(samplingRateT rate_type) -{ - int sample_rate = 0; - switch (rate_type) - { - case SR22 : - sample_rate = 22050; - return sample_rate; - break; - case SR32 : - sample_rate = 32000; - return sample_rate; - break; - case SR44 : - sample_rate = 44100; - return sample_rate; - break; - case SR48 : - sample_rate = 48000; - return sample_rate; - break; - case SR88 : - sample_rate = 88200; - return sample_rate; - break; - case SR96 : - sample_rate = 96000; - return sample_rate; - break; - case SR192 : - sample_rate = 192000; - return sample_rate; - break; - default: - return sample_rate; - break; - } - - return sample_rate; -} - //******************************************************************************* uint32_t JackAudioInterface::getBufferSizeInSamples() const { @@ -280,27 +182,6 @@ uint32_t JackAudioInterface::getBufferSizeInSamples() const } -//******************************************************************************* -int JackAudioInterface::getAudioBitResolution() const -{ - return mAudioBitResolution; -} - - -//******************************************************************************* -int JackAudioInterface::getNumInputChannels() const -{ - return mNumInChans; -} - - -//******************************************************************************* -int JackAudioInterface::getNumOutputChannels() const -{ - return mNumOutChans; -} - - //******************************************************************************* size_t JackAudioInterface::getSizeInBytesPerChannel() const { @@ -345,7 +226,8 @@ int JackAudioInterface::startProcess() const int JackAudioInterface::stopProcess() const { QMutexLocker locker(&sJackMutex); - if ( int code = (jack_client_close(mClient)) ) + int code = (jack_client_close(mClient)); + if ( code != 0 ) { std::cerr << "Cannot disconnect client" << std::endl; return(code); @@ -364,87 +246,109 @@ void JackAudioInterface::jackShutdown (void*) } + //******************************************************************************* -/* -void JackAudioInterface::setRingBuffers -(const std::tr1::shared_ptr InRingBuffer, - const std::tr1::shared_ptr OutRingBuffer) +int JackAudioInterface::processCallback(jack_nframes_t nframes) { - mInRingBuffer = InRingBuffer; - mOutRingBuffer = OutRingBuffer; + // 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; } -*/ //******************************************************************************* -// Before sending and reading to Jack, we have to round to the sample resolution -// that the program is using. Jack uses 32 bits (gJackBitResolution in globals.h) -// by default -void JackAudioInterface::computeNetworkProcessFromNetwork() +int JackAudioInterface::wrapperProcessCallback(jack_nframes_t nframes, void *arg) { - /// \todo cast *mInBuffer[i] to the bit resolution - //cout << mNumFrames << endl; - // Output Process (from NETWORK to JACK) - // ---------------------------------------------------------------- - // Read Audio buffer from RingBuffer (read from incoming packets) - //mOutRingBuffer->readSlotNonBlocking( mOutputPacket ); - 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 = mOutBuffer[i]; //sample buffer for channel i - for (int j = 0; j < mNumFrames; j++) { - //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); - // Change the bit resolution on each sample - //cout << tmp_sample[j] << endl; - fromBitToSampleConversion(&mOutputPacket[(i*mSizeInBytesPerChannel) - + (j*mBitResolutionMode)], - &tmp_sample[j], - mBitResolutionMode); - } - } + return static_cast(arg)->processCallback(nframes); } //******************************************************************************* -void JackAudioInterface::computeNetworkProcessToNetwork() +void JackAudioInterface::connectDefaultPorts() { - // 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 = mInBuffer[i]; //sample buffer for channel i - sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process - sample_t tmp_result; - for (int j = 0; j < mNumFrames; j++) { - //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); - // Change the bit resolution on each sample + const char** ports; - // 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); + // 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++) + { + // 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 << "WARING: 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); } - // Send Audio buffer to RingBuffer (these goes out as outgoing packets) - //mInRingBuffer->insertSlotNonBlocking( mInputPacket ); - mJackTrip->sendNetworkPacket( mInputPacket ); } + + + + + + + + + + + + + + + + + +// OLD CODE +// ============================================================================== + //******************************************************************************* +/* int JackAudioInterface::processCallback(jack_nframes_t nframes) { // Get input and output buffers from JACK @@ -495,8 +399,7 @@ int JackAudioInterface::processCallback(jack_nframes_t nframes) // 3) Finally, send packets to peer // -------------------------------- computeNetworkProcessToNetwork(); - - +*/ ///************PROTORYPE FOR CELT************************** ///******************************************************** /* @@ -508,116 +411,99 @@ int JackAudioInterface::processCallback(jack_nframes_t nframes) //unsigned char* compressed; //CELTEncoder* celtEncoder; //celt_encode_float(celtEncoder, mInBuffer, NULL, compressed, ); - + ///******************************************************** ///******************************************************** +// return 0; +//} - return 0; -} - - //******************************************************************************* -int JackAudioInterface::wrapperProcessCallback(jack_nframes_t nframes, void *arg) +/* +void JackAudioInterface::setRingBuffers +(const std::tr1::shared_ptr InRingBuffer, + const std::tr1::shared_ptr OutRingBuffer) { - return static_cast(arg)->processCallback(nframes); + mInRingBuffer = InRingBuffer; + mOutRingBuffer = OutRingBuffer; } +*/ //******************************************************************************* -// This function quantize from 32 bit to a lower bit resolution -// 24 bit is not working yet -void JackAudioInterface::fromSampleToBitConversion(const sample_t* const input, - int8_t* output, - const audioBitResolutionT targetBitResolution) +// Before sending and reading to Jack, we have to round to the sample resolution +// that the program is using. Jack uses 32 bits (gJackBitResolution in globals.h) +// by default +/* +void JackAudioInterface::computeNetworkProcessFromNetwork() { - 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; - 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; - 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; - case BIT32 : - std::memcpy(output, input, 4); // 32bit = 4 bytes - break; + /// \todo cast *mInBuffer[i] to the bit resolution + //cout << mNumFrames << endl; + // Output Process (from NETWORK to JACK) + // ---------------------------------------------------------------- + // Read Audio buffer from RingBuffer (read from incoming packets) + //mOutRingBuffer->readSlotNonBlocking( mOutputPacket ); + 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 = mOutBuffer[i]; //sample buffer for channel i + for (int j = 0; j < mNumFrames; j++) { + //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); + // Change the bit resolution on each sample + //cout << tmp_sample[j] << endl; + fromBitToSampleConversion(&mOutputPacket[(i*mSizeInBytesPerChannel) + + (j*mBitResolutionMode)], + &tmp_sample[j], + mBitResolutionMode); } + } } - +*/ //******************************************************************************* -void JackAudioInterface::fromBitToSampleConversion(const int8_t* const input, - sample_t* output, - const audioBitResolutionT sourceBitResolution) +/* +void JackAudioInterface::computeNetworkProcessToNetwork() { - 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; - 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; - 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; - case BIT32 : - std::memcpy(output, input, 4); // 4 bytes - break; + // 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 = mInBuffer[i]; //sample buffer for channel i + sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process + sample_t tmp_result; + for (int j = 0; j < mNumFrames; j++) { + //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); + // 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 RingBuffer (these goes out as outgoing packets) + //mInRingBuffer->insertSlotNonBlocking( mInputPacket ); + mJackTrip->sendNetworkPacket( mInputPacket ); } +*/ //******************************************************************************* +/* //void JackAudioInterface::appendProcessPlugin(const std::tr1::shared_ptr plugin) void JackAudioInterface::appendProcessPlugin(ProcessPlugin* plugin) { @@ -625,51 +511,4 @@ void JackAudioInterface::appendProcessPlugin(ProcessPlugin* plugin) if ( plugin->getNumInputs() ) {} mProcessPlugins.append(plugin); } - - - -//******************************************************************************* -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++) - { - // 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 << "WARING: 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); - } -} +*/ diff --git a/src/JackAudioInterface.h b/src/JackAudioInterface.h index 2b01ff2..39ddd0c 100644 --- a/src/JackAudioInterface.h +++ b/src/JackAudioInterface.h @@ -40,7 +40,7 @@ #define __JACKAUDIOINTERFACE_H__ #include -#include //for shared_ptr +//#include //for shared_ptr #include //for mem_fun_ref #include @@ -51,8 +51,9 @@ #include "jacktrip_types.h" #include "ProcessPlugin.h" +#include "AudioInterface.h" -class JackTrip; //forward declaration +//class JackTrip; //forward declaration /** \brief Class that provides an interface with the Jack Audio Server @@ -60,31 +61,10 @@ class JackTrip; //forward declaration * \todo implement srate_callback * \todo automatically starts jack with buffer and sample rate settings specified by the user */ -class JackAudioInterface +class JackAudioInterface : public AudioInterface { public: - /// \brief Enum for Audio Resolution in bits - /// \todo implement this into the class, now it's using jack default of 32 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 @@ -93,123 +73,47 @@ public: * \param ClientName Client name in Jack */ JackAudioInterface(JackTrip* jacktrip, - int NumInChans, int NumOutChans, - audioBitResolutionT AudioBitResolution = BIT16, - const char* ClientName = "JackTrip"); - - /** \brief The class destructor - */ + int NumInChans, int NumOutChans, + AudioInterface::audioBitResolutionT AudioBitResolution = AudioInterface::BIT16, + const char* ClientName = "JackTrip"); + /// \brief The class destructor virtual ~JackAudioInterface(); - /** \brief Setup the client - */ - void setup(); - - /** \brief Get the Jack Server Sampling Rate, in samples/second - */ - uint32_t getSampleRate() const; - - /** \brief Get the Jack Server Sampling Rate Enum Type samplingRateT - * \return JackAudioInterface::samplingRateT enum type - */ - samplingRateT getSampleRateType() const; - - /** \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); - - /** \brief Get the Jack Server Buffer Size, in samples - */ - uint32_t getBufferSizeInSamples() const; - - /** \brief Get the Jack Server Buffer Size, in bytes - */ - uint32_t getBufferSizeInBytes() const - { - return (getBufferSizeInSamples() * getAudioBitResolution()/8); - } - - /** \brief Get the Audio Bit Resolution, in bits - * - * This is one of the audioBitResolutionT set in construction - */ - int getAudioBitResolution() const; - - /// \brief Get Number of Input Channels - int getNumInputChannels() const; - - /// \brief Get Number of Output Channels - int getNumOutputChannels() const; - - /// \brief Get size of each audio per channel, in bytes - size_t getSizeInBytesPerChannel() const; - + /// \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 */ - int startProcess() const; - + virtual int startProcess() const; /** \brief Stops the process-callback thread * \return 0 on success, otherwise a non-zero error code */ - int stopProcess() const; - - /** \brief Set the pointer to the Input and Output RingBuffer - * that'll be use to read and write audio - * - * These RingBuffers are used to read and write audio samples on - * each JACK callback. - * \todo If the RingBuffer is blocked, the callback should stay - * on the last buffer, as in JackTrip (wavetable synth) - * \param InRingBuffer RingBuffer to read samples from - * \param OutRingBuffer RingBuffer to write samples to - */ - /* - void setRingBuffers(const std::tr1::shared_ptr InRingBuffer, - const std::tr1::shared_ptr OutRingBuffer); - */ - - /** \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); - */ - //void appendProcessPlugin(const std::tr1::shared_ptr plugin); - void appendProcessPlugin(ProcessPlugin* plugin); - - /** \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 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 audioBitResolutionT sourceBitResolution); - + 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) - void setClientName(const char* ClientName) + 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; + //------------------------------------------------------------------ private: @@ -222,30 +126,16 @@ private: * - Creates the appropriate number of input and output channels */ void setupClient(); - - /** \brief Creates input and output channels in the Jack client - */ + /// \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 Sets the part of the process callback that sends and receive packets - //void computeNetworkProcess(); - - /// \brief Compute the process to receive packets to JACK - void computeNetworkProcessFromNetwork(); - - /// \brief Compute the process from JACK to send packets - void computeNetworkProcessToNetwork(); - /** \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 @@ -257,7 +147,6 @@ private: * for more details */ 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 * @@ -272,32 +161,21 @@ private: // 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 - audioBitResolutionT mBitResolutionMode; ///< Bit resolution (audioBitResolutionT) mode + //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) - //jack_port_t** mInPorts; ///< Vector of Input Ports (Channels) - //jack_port_t** 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 - - 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 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 6384504..0ae9f39 100644 --- a/src/JackTrip.cpp +++ b/src/JackTrip.cpp @@ -39,6 +39,10 @@ #include "UdpDataProtocol.h" #include "RingBufferWavetable.h" #include "jacktrip_globals.h" +#include "JackAudioInterface.h" +#ifdef __RT_AUDIO__ +#include "RtAudioInterface.h" +#endif #include //#include // for usleep, sleep @@ -47,10 +51,18 @@ #include #include +#include using std::cout; using std::endl; - +//the following function has to remain outside the Jacktrip class definition +//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); + } +#endif //******************************************************************************* JackTrip::JackTrip(jacktripModeT JacktripMode, @@ -58,7 +70,7 @@ JackTrip::JackTrip(jacktripModeT JacktripMode, int NumChans, int BufferQueueLength, unsigned int redundancy, - JackAudioInterface::audioBitResolutionT AudioBitResolution, + AudioInterface::audioBitResolutionT AudioBitResolution, DataProtocol::packetHeaderTypeT PacketHeaderType, underrunModeT UnderRunMode, int receiver_bind_port, int sender_bind_port, @@ -66,14 +78,15 @@ JackTrip::JackTrip(jacktripModeT JacktripMode, mJackTripMode(JacktripMode), mDataProtocol(DataProtocolType), mPacketHeaderType(PacketHeaderType), + mAudiointerfaceMode(JackTrip::JACK), mNumChans(NumChans), mBufferQueueLength(BufferQueueLength), - mSampleRate(0), - mAudioBufferSize(0), + mSampleRate(gDefaultSampleRate), + mAudioBufferSize(gDefaultBufferSizeInSamples), mAudioBitResolution(AudioBitResolution), mDataProtocolSender(NULL), mDataProtocolReceiver(NULL), - mJackAudio(NULL), + mAudioInterface(NULL), mPacketHeader(NULL), mUnderRunMode(UnderRunMode), mSendRingBuffer(NULL), @@ -82,17 +95,25 @@ JackTrip::JackTrip(jacktripModeT JacktripMode, mSenderPeerPort(sender_peer_port), mSenderBindPort(sender_bind_port), mReceiverPeerPort(receiver_peer_port), + mTcpServerPort(4464), mRedundancy(redundancy), - mJackClientName("JackTrip") -{} + mJackClientName("JackTrip"), + mConnectionMode(JackTrip::NORMAL), + mReceivedConnection(false), + mTcpConnectionError(false), + mStopped(false) +{ + createHeader(mPacketHeaderType); +} //******************************************************************************* JackTrip::~JackTrip() { + wait(); delete mDataProtocolSender; delete mDataProtocolReceiver; - delete mJackAudio; + delete mAudioInterface; delete mPacketHeader; delete mSendRingBuffer; delete mReceiveRingBuffer; @@ -100,27 +121,69 @@ JackTrip::~JackTrip() //******************************************************************************* -void JackTrip::setupJackAudio() +void JackTrip::setupAudio() { - // Create JackAudioInterface Client Object - mJackAudio = new JackAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); - mJackAudio->setClientName(mJackClientName); - mJackAudio->setup(); - mSampleRate = mJackAudio->getSampleRate(); + // 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 ) { +#ifndef __NO_JACK__ + mAudioInterface = new JackAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); + mAudioInterface->setClientName(mJackClientName); + mAudioInterface->setup(); + mSampleRate = mAudioInterface->getSampleRate(); + 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(); +#endif +#endif + } + else if ( mAudiointerfaceMode == JackTrip::RTAUDIO ) { +#ifdef __RT_AUDIO__ + mAudioInterface = new RtAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution); + mAudioInterface->setSampleRate(mSampleRate); + mAudioInterface->setBufferSizeInSamples(mAudioBufferSize); + mAudioInterface->setup(); +#endif + } + std::cout << "The Sampling Rate is: " << mSampleRate << std::endl; std::cout << gPrintSeparator << std::endl; - mAudioBufferSize = mJackAudio->getBufferSizeInSamples(); 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 << " or: " << AudioBufferSizeInBytes + << " bytes" << std::endl; std::cout << gPrintSeparator << std::endl; - cout << "The Number of Channels is: " << mJackAudio->getNumInputChannels() << endl; + cout << "The Number of Channels is: " << mAudioInterface->getNumInputChannels() << endl; std::cout << gPrintSeparator << std::endl; QThread::usleep(100); } +//******************************************************************************* +void JackTrip::closeAudio() +{ + //mAudioInterface->close(); + if ( mAudioInterface != NULL ) { + mAudioInterface->stopProcess(); + delete mAudioInterface; + mAudioInterface = NULL; + } +} + + //******************************************************************************* void JackTrip::setupDataProtocol() { @@ -150,10 +213,12 @@ void JackTrip::setupDataProtocol() } // Set Audio Packet Size - mDataProtocolSender->setAudioPacketSize - (mJackAudio->getSizeInBytesPerChannel() * mNumChans); - mDataProtocolReceiver->setAudioPacketSize - (mJackAudio->getSizeInBytesPerChannel() * mNumChans); + //mDataProtocolSender->setAudioPacketSize + // (mAudioInterface->getSizeInBytesPerChannel() * mNumChans); + //mDataProtocolReceiver->setAudioPacketSize + // (mAudioInterface->getSizeInBytesPerChannel() * mNumChans); + mDataProtocolSender->setAudioPacketSize(getTotalAudioPacketSizeInBytes()); + mDataProtocolReceiver->setAudioPacketSize(getTotalAudioPacketSizeInBytes()); } @@ -162,19 +227,34 @@ 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(mJackAudio->getSizeInBytesPerChannel() * mNumChans, - gDefaultOutputQueueLength); - mReceiveRingBuffer = new RingBufferWavetable(mJackAudio->getSizeInBytesPerChannel() * mNumChans, - mBufferQueueLength); + 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(mJackAudio->getSizeInBytesPerChannel() * mNumChans, - gDefaultOutputQueueLength); - mReceiveRingBuffer = new RingBuffer(mJackAudio->getSizeInBytesPerChannel() * mNumChans, - mBufferQueueLength); + 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"); @@ -194,48 +274,82 @@ void JackTrip::setPeerAddress(const char* PeerHostOrIP) void JackTrip::appendProcessPlugin(ProcessPlugin* plugin) { mProcessPlugins.append(plugin); - //mJackAudio->appendProcessPlugin(plugin); + //mAudioInterface->appendProcessPlugin(plugin); } //******************************************************************************* -void JackTrip::start() -{ +void JackTrip::startProcess() 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 - setupJackAudio(); + // ------------------------------ + 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 : - clientPingToServerStart(); - break; - default: - throw std::invalid_argument("Jacktrip Mode undefined"); - break; + { + 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; + } + break; + default: + throw std::invalid_argument("Jacktrip Mode undefined"); + break; + } + // Start Threads - mJackAudio->startProcess(); + mAudioInterface->startProcess(); + for (int i = 0; i < mProcessPlugins.size(); ++i) { - mJackAudio->appendProcessPlugin(mProcessPlugins[i]); + mAudioInterface->appendProcessPlugin(mProcessPlugins[i]); } - mJackAudio->connectDefaultPorts(); - mDataProtocolSender->start(); + mAudioInterface->connectDefaultPorts(); mDataProtocolReceiver->start(); + QThread::msleep(1); + mDataProtocolSender->start(); } @@ -250,8 +364,9 @@ void JackTrip::stop() mDataProtocolReceiver->stop(); mDataProtocolReceiver->wait(); - // Stop the jack process callback - mJackAudio->stopProcess(); + // Stop the audio processes + //mAudioInterface->stopProcess(); + closeAudio(); cout << "JackTrip Processes STOPPED!" << endl; cout << gPrintSeparator << endl; @@ -260,8 +375,9 @@ void JackTrip::stop() emit signalProcessesStopped(); } + //******************************************************************************* -void JackTrip::wait() +void JackTrip::waitThreads() { mDataProtocolSender->wait(); mDataProtocolReceiver->wait(); @@ -269,14 +385,13 @@ void JackTrip::wait() //******************************************************************************* -void JackTrip::clientStart() +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 { - // Set the peer address mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() ); cout << "Peer Address set to: " << mPeerAddress.toStdString() << std::endl; @@ -286,12 +401,15 @@ void JackTrip::clientStart() //******************************************************************************* -void JackTrip::serverStart() +int JackTrip::serverStart(bool timeout, int udpTimeout) + 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; } // Get the client address when it connects @@ -304,10 +422,29 @@ void JackTrip::serverStart() 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 - while ( !UdpSockTemp.hasPendingDatagrams() ) { QThread::usleep(100000); } + 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 { + while ( !UdpSockTemp.hasPendingDatagrams() ) { + if (mStopped == true) { emit signalUdpTimeOut(); return -1; } + QThread::msleep(sleepTime); + } + } char buf[1]; // set client address UdpSockTemp.readDatagram(buf, 1, &peerHostAddress, &peer_port); @@ -321,7 +458,7 @@ void JackTrip::serverStart() // 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 + // 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.: @@ -330,40 +467,122 @@ void JackTrip::serverStart() mDataProtocolSender->setPeerPort(peer_port); mDataProtocolReceiver->setPeerPort(peer_port); setPeerPorts(peer_port); + return 0; } + //******************************************************************************* -void JackTrip::clientPingToServerStart() +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; + } + + // 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(); + + // 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 << "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; + } + } + 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; + + /* else { // Set the peer address mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); } - // Start Threads - mJackAudio->startProcess(); - //mJackAudio->connectDefaultPorts(); + // Start the Sender Threads + // ------------------------ + mAudioInterface->startProcess(); mDataProtocolSender->start(); - //cout << "STARTED DATA PROTOCOL SENDER-----------------------------" << endl; - //mDataProtocolReceiver->start(); + // block until mDataProtocolSender thread starts + while ( !mDataProtocolSender->isRunning() ) { QThread::msleep(100); } + // Create a Socket to listen to Server's answer + // -------------------------------------------- QHostAddress serverHostAddress; QUdpSocket UdpSockTemp;// Create socket to wait for server answer uint16_t server_port; // Bind the socket + //bindReceiveSocket(UdpSockTemp, mReceiverBindPort, + // mPeerAddress, peer_port); if ( !UdpSockTemp.bind(QHostAddress::Any, - mReceiverBindPort, - QUdpSocket::DefaultForPlatform) ) { - throw std::runtime_error("Could not bind UDP socket. It may be already binded."); + mReceiverBindPort, + QUdpSocket::ShareAddress) ) { + //throw std::runtime_error("Could not bind PingToServer UDP socket. It may be already binded."); } + // Listen to server response cout << "Waiting for server response..." << endl; - while ( !UdpSockTemp.hasPendingDatagrams() ) { QThread::usleep(100000); } + while ( !UdpSockTemp.hasPendingDatagrams() ) { QThread::msleep(100); } cout << "Received response from server!" << endl; char buf[1]; // set client address @@ -373,27 +592,80 @@ void JackTrip::clientPingToServerStart() // Stop the sender thread to change server port mDataProtocolSender->stop(); mDataProtocolSender->wait(); // Wait for the thread to terminate - /* - while ( mDataProtocolSender->isRunning() ) - { - cout << "IS RUNNING!" << endl; - QThread::usleep(100000); - } - */ - cout << "Server port now set to: " << server_port-1 << endl; + + cout << "Server port now set to: " << server_port << endl; cout << gPrintSeparator << endl; - mDataProtocolSender->setPeerPort(server_port-1); + mDataProtocolSender->setPeerPort(server_port); // Start Threads - //mJackAudio->connectDefaultPorts(); + //mAudioInterface->connectDefaultPorts(); mDataProtocolSender->start(); mDataProtocolReceiver->start(); + */ } +//******************************************************************************* +/* +void JackTrip::bindReceiveSocket(QUdpSocket& UdpSocket, int bind_port, + QHostAddress PeerHostAddress, int peer_port) +throw(std::runtime_error) +{ + // Creat socket descriptor + int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + + // Set local IPv4 Address + struct sockaddr_in local_addr; + ::bzero(&local_addr, sizeof(local_addr)); + 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(bind_port); //set bind port + + // 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)); +#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)); +#endif + + // Bind the Socket + 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. + // 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(peer_port); //set local port + // Connect the socket and issue a Write shutdown (to make it a + // reader socket only) + if ( (::inet_pton(AF_INET, PeerHostAddress.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"); } + + UdpSocket.setSocketDescriptor(sock_fd, QUdpSocket::ConnectedState, + QUdpSocket::ReadOnly); + cout << "UDP Socket Receiving in Port: " << bind_port << endl; + cout << gPrintSeparator << endl; +} +*/ + + //******************************************************************************* 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); @@ -419,17 +691,20 @@ void JackTrip::putHeaderInPacket(int8_t* full_packet, int8_t* audio_packet) int8_t* audio_part; audio_part = full_packet + mPacketHeader->getHeaderSizeInBytes(); - //std::memcpy(audio_part, audio_packet, mJackAudio->getBufferSizeInBytes()); - std::memcpy(audio_part, audio_packet, mJackAudio->getSizeInBytesPerChannel() * mNumChans); + //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() const +int JackTrip::getPacketSizeInBytes() { - //return (mJackAudio->getBufferSizeInBytes() + mPacketHeader->getHeaderSizeInBytes()); - return (mJackAudio->getSizeInBytesPerChannel() * mNumChans + - mPacketHeader->getHeaderSizeInBytes()); + //return (mAudioInterface->getBufferSizeInBytes() + mPacketHeader->getHeaderSizeInBytes()); + //return (mAudioInterface->getSizeInBytesPerChannel() * mNumChans + + //mPacketHeader->getHeaderSizeInBytes()); + return (getTotalAudioPacketSizeInBytes() + + mPacketHeader->getHeaderSizeInBytes()); } @@ -438,8 +713,9 @@ 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, mJackAudio->getBufferSizeInBytes()); - std::memcpy(audio_packet, audio_part, mJackAudio->getSizeInBytesPerChannel() * mNumChans); + //std::memcpy(audio_packet, audio_part, mAudioInterface->getBufferSizeInBytes()); + //std::memcpy(audio_packet, audio_part, mAudioInterface->getSizeInBytesPerChannel() * mNumChans); + std::memcpy(audio_packet, audio_part, getTotalAudioPacketSizeInBytes()); } diff --git a/src/JackTrip.h b/src/JackTrip.h index 69576f2..9084290 100644 --- a/src/JackTrip.h +++ b/src/JackTrip.h @@ -38,17 +38,24 @@ #ifndef __JACKTRIP_H__ #define __JACKTRIP_H__ -#include //for shared_ptr +//#include //for shared_ptr +#include #include #include +#include #include "DataProtocol.h" +#include "AudioInterface.h" + +#ifndef __NO_JACK__ #include "JackAudioInterface.h" +#endif //__NO_JACK__ + #include "PacketHeader.h" #include "RingBuffer.h" - +#include /** \brief Main class to creates a SERVER (to listen) or a CLIENT (to connect * to a listening server) to send audio streams in the network. * @@ -56,6 +63,7 @@ * This class also acts as a Mediator between all the other class. * Classes that uses JackTrip methods need to register with it. */ + class JackTrip : public QThread { Q_OBJECT; @@ -74,7 +82,8 @@ public: enum jacktripModeT { SERVER, ///< Run in Server Mode CLIENT, ///< Run in Client Mode - CLIENTTOPINGSERVER ///< Client of the Ping Server Mode + CLIENTTOPINGSERVER, ///< Client of the Ping Server Mode + SERVERPINGSERVER ///< Server of the MultiThreaded JackTrip }; /// \brief Enum for the JackTrip Underrun Mode, when packets @@ -82,6 +91,19 @@ public: 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 + }; //--------------------------------------------------------- @@ -95,11 +117,11 @@ public: */ JackTrip(jacktripModeT JacktripMode = CLIENT, dataProtocolT DataProtocolType = UDP, - int NumChans = 2, - int BufferQueueLength = 8, - unsigned int redundancy = 1, - JackAudioInterface::audioBitResolutionT AudioBitResolution = - JackAudioInterface::BIT16, + int NumChans = gDefaultNumInChannels, + int BufferQueueLength = gDefaultQueueLength, + unsigned int redundancy = gDefaultRedundancy, + AudioInterface::audioBitResolutionT AudioBitResolution = + AudioInterface::BIT16, DataProtocol::packetHeaderTypeT PacketHeaderType = DataProtocol::DEFAULT, underrunModeT UnderRunMode = WAVETABLE, @@ -111,63 +133,62 @@ public: /// \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 - void setPeerAddress(const char* PeerHostOrIP); + 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); - void appendProcessPlugin(ProcessPlugin* plugin); + virtual void appendProcessPlugin(ProcessPlugin* plugin); /// \brief Start the processing threads - void start(); + virtual void startProcess() throw(std::invalid_argument); /// \brief Stop the processing threads - void stop(); + virtual void stop(); /// \brief Wait for all the threads to finish. This functions is used when JackTrip is /// run as a thread - void wait(); + virtual void waitThreads(); /// \brief Check if UDP port is already binded /// \param port Port number - void checkIfPortIsBinded(int port); + virtual void checkIfPortIsBinded(int port); //------------------------------------------------------------------------------------ - /// \name Methods to change parameters after construction + /// \name Getters and Setters Methods to change parameters after construction //@{ // /// \brief Sets (override) JackTrip Mode after construction - void setJackTripMode(jacktripModeT JacktripMode) + virtual void setJackTripMode(jacktripModeT JacktripMode) { mJackTripMode = JacktripMode; } /// \brief Sets (override) DataProtocol Type after construction - void setDataProtocoType(dataProtocolT DataProtocolType) + virtual void setDataProtocoType(dataProtocolT DataProtocolType) { mDataProtocol = DataProtocolType; } /// \brief Sets the Packet header type - void setPacketHeaderType(DataProtocol::packetHeaderTypeT PacketHeaderType) + virtual void setPacketHeaderType(DataProtocol::packetHeaderTypeT PacketHeaderType) { mPacketHeaderType = PacketHeaderType; delete mPacketHeader; mPacketHeader = NULL; createHeader(mPacketHeaderType); } - /// \brief Sets (override) Number of Channels after construction - /// \todo implement this, not working right now because channels cannot be changed after construction - //void setNumChannels(int NumChans) - //{ mNumChans=NumChans; } /// \brief Sets (override) Buffer Queue Length Mode after construction - void setBufferQueueLength(int BufferQueueLength) + virtual void setBufferQueueLength(int BufferQueueLength) { mBufferQueueLength = BufferQueueLength; } /// \brief Sets (override) Audio Bit Resolution after construction - void setAudioBitResolution(JackAudioInterface::audioBitResolutionT AudioBitResolution) + virtual void setAudioBitResolution(AudioInterface::audioBitResolutionT AudioBitResolution) { mAudioBitResolution = AudioBitResolution; } /// \brief Sets (override) Underrun Mode - void setUnderRunMode(underrunModeT UnderRunMode) + virtual void setUnderRunMode(underrunModeT UnderRunMode) { mUnderRunMode = UnderRunMode; } /// \brief Sets port numbers for the local and peer machine. /// Receive port is port - void setAllPorts(int port) + virtual void setAllPorts(int port) { mReceiverBindPort = port; mSenderPeerPort = port; @@ -175,20 +196,84 @@ public: mReceiverPeerPort = port; } /// \brief Sets port numbers to bind in RECEIVER and SENDER sockets. - void setBindPorts(int port) + virtual void setBindPorts(int port) { mReceiverBindPort = port; mSenderBindPort = port; } /// \brief Sets port numbers for the peer (remote) machine. - void setPeerPorts(int port) + virtual void setPeerPorts(int port) { mSenderPeerPort = port; mReceiverPeerPort = port; } /// \brief Set Client Name to something different that the default (JackTrip) - void setClientName(char* ClientName) + 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; } //@} //------------------------------------------------------------------------------------ @@ -197,49 +282,88 @@ public: /// \name Mediator Functions //@{ /// \todo Document all these functions - void createHeader(const DataProtocol::packetHeaderTypeT headertype); + virtual void createHeader(const DataProtocol::packetHeaderTypeT headertype); void putHeaderInPacket(int8_t* full_packet, int8_t* audio_packet); - int getPacketSizeInBytes() const; + virtual int getPacketSizeInBytes(); void parseAudioPacket(int8_t* full_packet, int8_t* audio_packet); - void sendNetworkPacket(const int8_t* ptrToSlot) + virtual void sendNetworkPacket(const int8_t* ptrToSlot) { mSendRingBuffer->insertSlotNonBlocking(ptrToSlot); } - void receiveNetworkPacket(int8_t* ptrToReadSlot) + virtual void receiveNetworkPacket(int8_t* ptrToReadSlot) { mReceiveRingBuffer->readSlotNonBlocking(ptrToReadSlot); } - void readAudioBuffer(int8_t* ptrToReadSlot) + virtual void readAudioBuffer(int8_t* ptrToReadSlot) { mSendRingBuffer->readSlotBlocking(ptrToReadSlot); } - void writeAudioBuffer(const int8_t* ptrToSlot) + virtual void writeAudioBuffer(const int8_t* ptrToSlot) { mReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot); } uint32_t getBufferSizeInSamples() const - { return mJackAudio->getBufferSizeInSamples(); } - JackAudioInterface::samplingRateT getSampleRateType() const - { return mJackAudio->getSampleRateType(); } + { 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 mJackAudio->getAudioBitResolution(); } - int getNumInputChannels() const - { return mJackAudio->getNumInputChannels(); } - int getNumOutputChannels() const - {return mJackAudio->getNumOutputChannels(); } - void checkPeerSettings(int8_t* full_packet); + { 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(); } - int getPeerSequenceNumber(int8_t* full_packet) const + + 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;} public slots: /// \brief Slot to stop all the processes and threads - void slotStopProcesses() + virtual void slotStopProcesses() { std::cout << "Stopping JackTrip..." << std::endl; - stop(); - }; + mStopped = true; + this->stop(); + } /** \brief This slot emits in turn the signal signalNoUdpPacketsForSeconds - * when UDP is waited for more than 30 seconds. + * when UDP has waited for more than 30 seconds. * * It is used to remove the thread from the server. */ @@ -251,46 +375,67 @@ public slots: emit signalNoUdpPacketsForSeconds(); } } - + void slotPrintTest() + { std::cout << "=== TESTING ===" << std::endl; } + void slotReceivedConnectionFromPeer() + { mReceivedConnection = true; } + signals: - /// \brieg Signal emitted when all the processes and threads are stopped + + void signalUdpTimeOut(); + /// \brief Signal emitted when all the processes and threads are stopped void signalProcessesStopped(); - /// \brieg Signal emitted when no UDP Packets have been received for a while + /// \brief Signal emitted when no UDP Packets have been received for a while void signalNoUdpPacketsForSeconds(); + void signalTcpClientConnected(); -private: +public: - /// \brief Set the JackAudioInteface object - void setupJackAudio(); + /// \brief Set the AudioInteface object + virtual void setupAudio(); + /// \brief Close the JackAudioInteface and disconnects it from JACK + void closeAudio(); /// \brief Set the DataProtocol objects - void setupDataProtocol(); + virtual void setupDataProtocol(); /// \brief Set the RingBuffer objects void setupRingBuffers(); /// \brief Starts for the CLIENT mode - void clientStart(); + void clientStart() throw(std::invalid_argument); /// \brief Starts for the SERVER mode - void serverStart(); + /// \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 - void clientPingToServerStart(); + /// \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 - JackAudioInterface::audioBitResolutionT mAudioBitResolution; ///< Audio Bit Resolutions + 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 + /// Pointer to Abstract Type DataProtocol that receives packets DataProtocol* mDataProtocolReceiver; - JackAudioInterface* mJackAudio; ///< Interface to Jack Client + AudioInterface* mAudioInterface; ///< Interface to Jack Client PacketHeader* mPacketHeader; ///< Pointer to Packet Header underrunModeT mUnderRunMode; ///< underrunModeT Mode @@ -303,11 +448,20 @@ private: 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; }; #endif + + diff --git a/src/JackTripThread.h b/src/JackTripThread.h index 6825386..06a85c7 100644 --- a/src/JackTripThread.h +++ b/src/JackTripThread.h @@ -47,11 +47,11 @@ class JackTripThread : public QThread { public: - JackTripThread(JackTrip::jacktripModeT JacktripMode) : mJackTripMode(JacktripMode) {}; - virtual ~JackTripThread(){}; + JackTripThread(JackTrip::jacktripModeT JacktripMode) : mJackTripMode(JacktripMode) {} + virtual ~JackTripThread(){} void run(); - void setPort(int port_num) { mPortNum = port_num; } ; + void setPort(int port_num) { mPortNum = port_num; } void setPeerAddress(const char* PeerHostOrIP) { mPeerAddress = PeerHostOrIP; } private: diff --git a/src/JackTripWorker.cpp b/src/JackTripWorker.cpp index e20c4ba..b83b5c7 100644 --- a/src/JackTripWorker.cpp +++ b/src/JackTripWorker.cpp @@ -40,12 +40,16 @@ #include #include +#include #include "JackTripWorker.h" #include "JackTrip.h" #include "UdpMasterListener.h" #include "NetKS.h" #include "LoopBack.h" +#ifdef __JAMTEST__ +#include "JamTest.h" +#endif using std::cout; using std::endl; @@ -73,8 +77,7 @@ JackTripWorker::JackTripWorker(UdpMasterListener* udpmasterlistener) : //******************************************************************************* JackTripWorker::~JackTripWorker() { - delete mUdpMasterListener; - + //delete mUdpMasterListener; } @@ -89,7 +92,8 @@ void JackTripWorker::setJackTrip(int id, uint32_t client_address, } mID = id; // Set the jacktrip address and ports - mClientAddress.setAddress(client_address); + //mClientAddress.setAddress(client_address); + mClientAddress = client_address; mServerPort = server_port; mClientPort = client_port; mNumChans = num_channels; @@ -99,74 +103,160 @@ void JackTripWorker::setJackTrip(int id, uint32_t client_address, //******************************************************************************* 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.' - */ - + caught before control returns to Qt Concurrent.'*/ + + { QMutexLocker locker(&mMutex); mSpawning = true; } + + 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::CLIENT, JackTrip::UDP, mNumChans, 2); - jacktrip.setPeerAddress( mClientAddress.toString().toLatin1().data() ); - jacktrip.setBindPorts(mServerPort); - jacktrip.setPeerPorts(mClientPort-1); - - // Connect signals and slots - // ------------------------- - 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); - - // Karplus Strong String - NetKS netks; - jacktrip.appendProcessPlugin(&netks); - // Play the String - QTimer timer; - QObject::connect(&timer, SIGNAL(timeout()), &netks, SLOT(exciteString()), - Qt::QueuedConnection); - timer.start(300); - - // Start Threads and event loop - jacktrip.start(); - - { // Thread is already spawning, so release the lock - QMutexLocker locker(&mMutex); - mSpawning = false; - } - - event_loop.exec(); // Excecution will block here until exit() the QEventLoop - //-------------------------------------------------------------------------- - - // wait for jacktrip to be done before exiting the Worker Thread - jacktrip.wait(); - + { + // 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); + cout << "---> JackTripWorker: Creating jacktip objects..." << endl; +#ifndef __JAMTEST__ + JackTrip jacktrip(JackTrip::SERVERPINGSERVER, JackTrip::UDP, mNumChans, 2); +#endif +#ifdef __JAMTEST__ + 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 + //-------------------------------------------------------------------------- + + { QMutexLocker locker(&mMutex); mSpawning = true; } + + // 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; - } + { + 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; + } - mUdpMasterListener->releasePort(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; + +//******************************************************************************* +// 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; + } + } + // 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; } @@ -176,3 +266,11 @@ bool JackTripWorker::isSpawning() QMutexLocker locker(&mMutex); return mSpawning; } + + +//******************************************************************************* +void JackTripWorker::stopThread() +{ + QMutexLocker locker(&mMutex); + emit signalRemoveThread(); +} diff --git a/src/JackTripWorker.h b/src/JackTripWorker.h index 8df371e..adf1602 100644 --- a/src/JackTripWorker.h +++ b/src/JackTripWorker.h @@ -46,9 +46,9 @@ #include #include -#include "jacktrip_types.h" +#include "JackTrip.h" -class JackTrip; // forward declaration +//class JackTrip; // forward declaration class UdpMasterListener; // forward declaration @@ -70,40 +70,45 @@ class JackTripWorker : public QObject, public QRunnable 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() ). - */ + /// \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 - */ + /// \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 - */ + /// \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; + } private slots: void slotTest() - { - std::cout << "--- JackTripWorker TEST SLOT ---" << std::endl; - } + { std::cout << "--- JackTripWorker TEST SLOT ---" << std::endl; } + + +signals: + void signalRemoveThread(); private: + int setJackTripFromClientHeader(JackTrip& jacktrip); + JackTrip::connectionModeT getConnectionModeFromHeader(); UdpMasterListener* mUdpMasterListener; ///< Master Listener Socket - QHostAddress mClientAddress; ///< Client Address + //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 diff --git a/src/LoopBack.cpp b/src/LoopBack.cpp index f22197b..b8a698e 100644 --- a/src/LoopBack.cpp +++ b/src/LoopBack.cpp @@ -40,6 +40,10 @@ #include "jacktrip_types.h" #include // for memcpy +#include + +using std::cout; using std::endl; +//using namespace JackTripNamespace; //******************************************************************************* diff --git a/src/NetKS.h b/src/NetKS.h index 2d09c4b..864be52 100644 --- a/src/NetKS.h +++ b/src/NetKS.h @@ -39,7 +39,7 @@ #define __NETKS_H__ #include -#include +//#include #include @@ -73,7 +73,7 @@ private slots: std::cout << "========= EXTICING STRING ===========" << std::endl; fbutton0 = 1.0; //std::cout << fbutton0 << std::endl; - usleep(280000); /// \todo Define this number based on the sampling rate and buffer size + QThread::usleep(280000); /// \todo Define this number based on the sampling rate and buffer size fbutton0 = 0.0; //std::cout << fbutton0 << std::endl; } diff --git a/src/PacketHeader.cpp b/src/PacketHeader.cpp index 1a37556..41e41dc 100644 --- a/src/PacketHeader.cpp +++ b/src/PacketHeader.cpp @@ -46,6 +46,29 @@ using std::cout; using std::endl; +// below is the gettimeofday definition for windows: this function is not defined in sys/time.h as it is in unix +// for more info check: http://www.halcode.com/archives/2008/08/26/retrieving-system-time-gettimeofday/ +#if defined __WIN_32__ +#ifdef __cplusplus +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; +} +#else +/* Must be defined somewhere else */ +int gettimeofday(struct timeval* p, void* tz /* IGNORED */); +#endif +#endif + + //####################################################################### //####################### PacketHeader ################################## //####################################################################### @@ -79,23 +102,22 @@ DefaultHeader::DefaultHeader(JackTrip* jacktrip) : mHeader.BufferSize = 0; mHeader.SamplingRate = 0; mHeader.BitResolution = 0; - mHeader.NumInChannels = 0; - mHeader.NumOutChannels = 0; - //mHeader.Dummy = 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.NumInChannels = mJackTrip->getNumInputChannels(); mHeader.BitResolution = mJackTrip->getAudioBitResolution(); - mHeader.NumOutChannels = mJackTrip->getNumOutputChannels(); - //mHeader.SeqNumber = 0; - mHeader.TimeStamp = PacketHeader::usecTime(); - //cout << mHeader.TimeStamp << endl; + mHeader.NumChannels = mJackTrip->getNumChannels(); + mHeader.ConnectionMode = static_cast(mJackTrip->getConnectionMode()); //printHeader(); } @@ -120,17 +142,17 @@ void DefaultHeader::checkPeerSettings(int8_t* full_packet) // Check Sampling Rate if ( peer_header->SamplingRate != mHeader.SamplingRate ) - { - std::cerr << "ERROR: Peer Sampling Rate is : " << - JackAudioInterface::getSampleRateFromType - ( static_cast(peer_header->SamplingRate) ) << endl; - std::cerr << " Local Sampling Rate is : " << - JackAudioInterface::getSampleRateFromType - ( static_cast(mHeader.SamplingRate) ) << endl; - std::cerr << "Make sure both machines use the same Sampling Rate" << 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 Audio Bit Resolution if ( peer_header->BitResolution != mHeader.BitResolution ) @@ -149,7 +171,8 @@ void DefaultHeader::checkPeerSettings(int8_t* full_packet) { //std::cerr << "Exiting program..." << endl; //std::exit(1); - throw std::logic_error("Local and Peer Settings don't match"); + //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 } @@ -160,18 +183,30 @@ 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 JackAudioInterface::samplingRateT + // Get the sample rate in Hz form the AudioInterface::samplingRateT int sample_rate = - JackAudioInterface::getSampleRateFromType - ( static_cast(mHeader.SamplingRate) ); + 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 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 << sizeof(mHeader) << endl; +} + + + +//*********************************************************************** +uint64_t DefaultHeader::getPeerTimeStamp(int8_t* full_packet) const +{ + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + return peer_header->TimeStamp; } @@ -184,6 +219,49 @@ uint16_t DefaultHeader::getPeerSequenceNumber(int8_t* full_packet) const } +//*********************************************************************** +uint16_t DefaultHeader::getPeerBufferSize(int8_t* full_packet) const +{ + 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; +} + + +//*********************************************************************** +uint8_t DefaultHeader::getPeerBitResolution(int8_t* full_packet) const +{ + 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; +} + + +//*********************************************************************** +uint8_t DefaultHeader::getPeerConnectionMode(int8_t* full_packet) const +{ + DefaultHeaderStruct* peer_header; + peer_header = reinterpret_cast(full_packet); + return static_cast(peer_header->ConnectionMode); +} @@ -212,41 +290,48 @@ void JamLinkHeader::fillHeaderCommonFromAudio() //std::cerr << "ERROR: JamLink only support ONE channel. Run JackTrip using only one channel" // << endl; //std::exit(1); - throw std::logic_error("JamLink only support ONE channel. Run JackTrip using only one channel"); + //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 != JackAudioInterface::SR48 ) { - throw std::logic_error("ERROR: JamLink only support 48kHz for communication with JackTrip at the moment."); + 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 ) - { - throw std::logic_error("ERROR: JamLink only support 64 buffer size for communication with JackTrip at the moment."); - } + 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 JackAudioInterface::SR48 : + case AudioInterface::SR48 : mHeader.Common = (mHeader.Common | ETX_48KHZ); break; - case JackAudioInterface::SR44 : + case AudioInterface::SR44 : mHeader.Common = (mHeader.Common | ETX_44KHZ); break; - case JackAudioInterface::SR32 : + case AudioInterface::SR32 : mHeader.Common = (mHeader.Common | ETX_32KHZ); break; - case JackAudioInterface::SR22 : + case AudioInterface::SR22 : 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"); + //throw std::out_of_range("Sample rate not supported by JamLink"); + emit signalError("Sample rate not supported by JamLink."); break; } } diff --git a/src/PacketHeader.h b/src/PacketHeader.h index 73cf979..3988039 100644 --- a/src/PacketHeader.h +++ b/src/PacketHeader.h @@ -39,9 +39,12 @@ #define __PACKETHEADER_H__ #include -#include // for shared_ptr +//#include // for shared_ptr #include +#include +#include + #include "jacktrip_types.h" #include "jacktrip_globals.h" class JackTrip; // Forward Declaration @@ -60,9 +63,10 @@ public: 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 Dummy; ///< Dummy value to byte padding alignment + //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; }; //--------------------------------------------------------- @@ -103,70 +107,67 @@ struct JamLinkHeaderStuct : public HeaderStruct }; + //####################################################################### //####################### PacketHeader ################################## //####################################################################### /** \brief Base class for header type. Subclass this struct to * create a new header. */ -class PacketHeader +class PacketHeader : public QObject { + Q_OBJECT; + public: /// \brief The class Constructor PacketHeader(JackTrip* jacktrip); /// \brief The class Destructor - virtual ~PacketHeader() {}; + virtual ~PacketHeader() {} - /** \brief Return a time stamp in microseconds - * \return Time stamp: microseconds since midnight (0 hour), January 1, 1970 - */ + /// \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) - */ + /// \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 - */ + /// \brief Increase sequence number for counter, a 16bit number virtual void increaseSequenceNumber() - { - mSeqNumber++; - }; - - /* \brief Returns the current sequence number - * \return 16bit Sequence number - */ + { 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 - */ + { 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) - */ + } + /// \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); + + private: uint16_t mSeqNumber; JackTrip* mJackTrip; ///< JackTrip mediator class @@ -183,48 +184,37 @@ private: class DefaultHeader : public PacketHeader { public: - /* - //----------STRUCT----------------------------------------- - /// \brief Default Header Struct - struct DefaultHeaderStruct - { - // 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 NumInChannels; ///< Number of Input Channels - uint8_t NumOutChannels; ///< Number of Output Channels - // uint8_t BitResolution; ///< \todo implement this part - }; - //--------------------------------------------------------- - */ + DefaultHeader(JackTrip* jacktrip); - virtual ~DefaultHeader() {}; + virtual ~DefaultHeader() {} + virtual void fillHeaderCommonFromAudio(); - virtual void parseHeader() {}; + virtual void parseHeader() {} virtual void checkPeerSettings(int8_t* full_packet); virtual void increaseSequenceNumber() - { - mHeader.SeqNumber++; - //std::cout << "Sequence Number = " << static_cast(mHeader.SeqNumber) << std::endl; - }; + { mHeader.SeqNumber++; } virtual uint16_t getSequenceNumber() const - { - return mHeader.SeqNumber; - } - virtual uint16_t getPeerSequenceNumber(int8_t* full_packet) const; - virtual int getHeaderSizeInBytes() const { return sizeof(mHeader); }; + { return mHeader.SeqNumber; } + virtual int getHeaderSizeInBytes() const { return sizeof(mHeader); } virtual void putHeaderInPacket(int8_t* full_packet) - { - putHeaderInPacketBaseClass(full_packet, mHeader); - //std::memcpy(full_packet, reinterpret_cast(&mHeader), - // getHeaderSizeInBytes() ); - }; + { 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; ///< Header Struct DefaultHeaderStruct mHeader;///< Default Header Struct JackTrip* mJackTrip; ///< JackTrip mediator class }; @@ -243,18 +233,24 @@ class JamLinkHeader : public PacketHeader public: JamLinkHeader(JackTrip* jacktrip); - virtual ~JamLinkHeader() {}; + virtual ~JamLinkHeader() {} virtual void fillHeaderCommonFromAudio(); - virtual void parseHeader() {}; + virtual void parseHeader() {} virtual void checkPeerSettings(int8_t* /*full_packet*/) {} - virtual uint16_t getPeerSequenceNumber(int8_t* /*full_packet*/) const { /*\todo IMPLEMENT*/ return 0; } - virtual void increaseSequenceNumber() {}; - virtual int getHeaderSizeInBytes() const { return sizeof(mHeader); }; + + 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); - }; + { putHeaderInPacketBaseClass(full_packet, mHeader); } private: JamLinkHeaderStuct mHeader; ///< JamLink Header Struct @@ -274,15 +270,23 @@ class EmptyHeader : public PacketHeader public: EmptyHeader(JackTrip* jacktrip); - virtual ~EmptyHeader() {}; + virtual ~EmptyHeader() {} - virtual void fillHeaderCommonFromAudio() {}; - virtual void parseHeader() {}; + virtual void fillHeaderCommonFromAudio() {} + virtual void parseHeader() {} virtual void checkPeerSettings(int8_t* /*full_packet*/) {} - virtual uint16_t getPeerSequenceNumber(int8_t* /*full_packet*/) const { return 0; /*\todo IMPLEMENT*/} - virtual void increaseSequenceNumber() {}; - virtual int getHeaderSizeInBytes() const { return 0; }; - virtual void putHeaderInPacket(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 diff --git a/src/ProcessPlugin.h b/src/ProcessPlugin.h index 70533c8..4c0cae8 100644 --- a/src/ProcessPlugin.h +++ b/src/ProcessPlugin.h @@ -39,7 +39,7 @@ #define __PROCESSPLUGIN_H__ #include -#include +#include /** \brief Interface for the process plugins to add to the JACK callback process in * JackAudioInterface @@ -49,7 +49,7 @@ * except init, which is optional for processing that are sampling rate dependent or * that need specific initialization. */ -class ProcessPlugin : public QObject +class ProcessPlugin : public QThread { public: diff --git a/src/RingBuffer.h b/src/RingBuffer.h index e3cf986..9a5cab6 100644 --- a/src/RingBuffer.h +++ b/src/RingBuffer.h @@ -44,6 +44,8 @@ #include "jacktrip_types.h" +//using namespace JackTripNamespace; + /** \brief Provides a ring-buffer (or circular-buffer) that can be written to and read from * asynchronously (blocking) or synchronously (non-blocking). diff --git a/src/RtAudioInterface.cpp b/src/RtAudioInterface.cpp new file mode 100644 index 0000000..3add762 --- /dev/null +++ b/src/RtAudioInterface.cpp @@ -0,0 +1,452 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file RtAudioInterface.cpp + * \author Juan-Pablo Caceres + * \date July 2009 + */ + +#include "RtAudioInterface.h" +#include "JackTrip.h" +#include "jacktrip_globals.h" + +#include + + +using std::cout; using std::endl; + + +//******************************************************************************* +RtAudioInterface::RtAudioInterface(JackTrip* jacktrip, + int NumInChans, int NumOutChans, + audioBitResolutionT AudioBitResolution) : +AudioInterface(jacktrip, + NumInChans, NumOutChans, + AudioBitResolution), +mJackTrip(jacktrip), +mRtAudio(NULL) +{} + + +//******************************************************************************* +RtAudioInterface::~RtAudioInterface() +{ + delete mRtAudio; +} + + +//******************************************************************************* +void RtAudioInterface::setup() +{ + // 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; +#ifdef __WIN_32__ + 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 ); + } + + // 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; + } + } +} + + +//******************************************************************************* +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(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); +} + + +//******************************************************************************* +int RtAudioInterface::stopProcess() const +{ + try { mRtAudio->closeStream(); } + catch ( RtError& e ) { + std::cout << '\n' << e.getMessage() << '\n' << std::endl; + return(-1); + } + return 0; +} + + + + + + + + + + + + + + + + + + + + + + +// OLD CODE +// ============================================================================= + +/* +int RtAudioInterface::processCallback(jack_nframes_t nframes) +{ + mJackTrip->printTextTest(); + return JackAudioInterface::processCallback(nframes); +} +*/ + + +//******************************************************************************* +//int RtAudioInterface::RtAudioCallback(void *outputBuffer, void *inputBuffer, +// unsigned int nFrames, +// double /*streamTime*/, RtAudioStreamStatus /*status*/) +//{ +/* + mInBuffer[0] = (sample_t*) inputBuffer; + mOutBuffer[0] = (sample_t*) outputBuffer; + //AudioInterface::callback(mInBuffer, mOutBuffer, mInputPacket, mOutputPacket, + // nFrames, mInProcessBuffer, mOutProcessBuffer); + + + // Output Process (from NETWORK to JACK) + // ---------------------------------------------------------------- + // Read Audio buffer from RingBuffer (read from incoming packets) + //mOutRingBuffer->readSlotNonBlocking( mOutputPacket ); + mJackTrip->receiveNetworkPacket( mOutputPacket ); + + + // Extract separate channels to send to Jack + for (int i = 0; i < getNumOutputChannels(); i++) { + //-------- + // This should be faster for 32 bits + //std::memcpy(mOutBuffer[i], &mOutputPacket[i*mSizeInBytesPerChannel], + // mSizeInBytesPerChannel); + //-------- + sample_t* tmp_sample = mOutBuffer[i]; //sample buffer for channel i + for (unsigned int j = 0; j < nFrames; j++) { + //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); + // Change the bit resolution on each sample + //cout << tmp_sample[j] << endl; + AudioInterface::fromBitToSampleConversion(&mOutputPacket[(i*getSizeInBytesPerChannel()) + + (j*BIT16)], + &tmp_sample[j], + BIT16); + } + } + + + + // Input Process (from JACK to NETWORK) + // ---------------------------------------------------------------- + // Concatenate all the channels from jack to form packet + for (int i = 0; i < getNumInputChannels(); i++) { + //-------- + // This should be faster for 32 bits + //std::memcpy(&mInputPacket[i*getSizeInBytesPerChannel()], mInBuffer[i], + // mSizeInBytesPerChannel); + //-------- + sample_t* tmp_sample = mInBuffer[i]; //sample buffer for channel i + sample_t tmp_result; + for (unsigned int j = 0; j < nFrames; j++) { + // Add the input jack buffer to the buffer resulting from the output process + tmp_result = tmp_sample[j]; + AudioInterface::fromSampleToBitConversion(&tmp_result, + &mInputPacket[(i*getSizeInBytesPerChannel()) + + (j*BIT16)], + BIT16); + } + } + // Send Audio buffer to RingBuffer (these goes out as outgoing packets) + //mInRingBuffer->insertSlotNonBlocking( mInputPacket ); + mJackTrip->sendNetworkPacket( mInputPacket ); + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + //mTestJackTrip->printTextTest(); + + //if (mJackTrip != NULL) + // cout << "(mJackTrip != NULL)" << endl; + + + //if (mJackTrip == NULL) { cout << " === JACKTRIPNULL === " << endl; } + + //const int8_t* caca; + //mJackTrip->sendNetworkPacket( mInputPacket ); + + //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); + + + //cout << "nFrames = ==================== = = = = = = = ======== " << this->getBufferSizeInSamples() << endl; + //int8_t* input_packet = new int8_t[nFrames*2]; + + //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; + + + //mOutputPacket = static_cast(inputBuffer); + //mInputPacket = static_cast(outputBuffer); + + // 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++) { + sample_t* tmp_sample = mOutBuffer[i]; //sample buffer for channel i + for (int j = 0; j < mBufferSize; j++) { + fromBitToSampleConversion(&mOutputPacket[(i*getSizeInBytesPerChannel()) + (j*BIT16)], + &tmp_sample[j], + BIT16); + } + } + */ + + + // 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 + //std::memcpy(&mInputPacket[i*mSizeInBytesPerChannel], mInBuffer[i], + // mSizeInBytesPerChannel); + //-------- + float* tmp_sample = in_buffer; //sample buffer for channel i + //sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process + sample_t tmp_result; + for (int j = 0; j < mBufferSize; j++) { + //std::memcpy(&tmp_sample[j], &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*4)], 4); + // 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_sample, + &mInputPacket[(i*getSizeInBytesPerChannel()) + + (j*BIT16)], + BIT16); + + + + } + } + // Send Audio buffer to RingBuffer (these goes out as outgoing packets) + //mInRingBuffer->insertSlotNonBlocking( mInputPacket ); + mJackTrip->sendNetworkPacket( mInputPacket ); + */ + //return 0; +//} + diff --git a/src/RtAudioInterface.h b/src/RtAudioInterface.h new file mode 100644 index 0000000..a176e78 --- /dev/null +++ b/src/RtAudioInterface.h @@ -0,0 +1,97 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file RtAudioInterface.h + * \author Juan-Pablo Caceres + * \date July 2009 + */ + +#ifndef __RTAUDIOINTERFACE_H__ +#define __RTAUDIOINTERFACE_H__ + +#include "RtAudio.h" + +#include "AudioInterface.h" +#include "jacktrip_globals.h" +class JackTrip; // Forward declaration + +/// \brief Base Class that provides an interface with RtAudio +class RtAudioInterface : public AudioInterface +{ +public: + + /** \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--------------------------------------------- + //------------------------------------------------------------------ + + +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 +}; + +#endif // __RTAUDIOINTERFACE_H__ diff --git a/src/Settings.cpp b/src/Settings.cpp index 5b764da..21d9601 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -55,220 +55,275 @@ int gVerboseFlag = 0; //******************************************************************************* Settings::Settings() : - mJackTrip(NULL), - mJackTripMode(JackTrip::SERVER), - mDataProtocol(JackTrip::UDP), - mNumChans(2), - mBufferQueueLength(gDefaultQueueLength), - mAudioBitResolution(JackAudioInterface::BIT16), - mPortNum(gDefaultPort), - mClientName(NULL), - mUnderrrunZero(false), - mLoopBack(false), - mJamLink(false), - mEmptyHeader(false), - mJackTripServer(false), - mRedundancy(1) + mJackTrip(NULL), + mJackTripMode(JackTrip::SERVER), + mDataProtocol(JackTrip::UDP), + mNumChans(2), + mBufferQueueLength(gDefaultQueueLength), + mAudioBitResolution(AudioInterface::BIT16), + mBindPortNum(gDefaultPort), mPeerPortNum(gDefaultPort), + mClientName(NULL), + mUnderrrunZero(false), + mLoopBack(false), + mJamLink(false), + mEmptyHeader(false), + mJackTripServer(false), + mLocalAddress(gDefaultLocalAddress), + mRedundancy(1), + mUseJack(true), + mChanfeDefaultSR(false), + mChanfeDefaultBS(false) {} //******************************************************************************* Settings::~Settings() { - stopJackTrip(); - delete mJackTrip; + stopJackTrip(); + delete mJackTrip; } //******************************************************************************* void Settings::parseInput(int argc, char** argv) { - // If no command arguments are given, print instructions - if(argc == 1) { - printUsage(); - std::exit(0); - } - - // Usage example at: - // http://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html#Getopt-Long-Option-Example - // 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 - { "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 - { "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 - { "version", no_argument, NULL, 'v' }, // Version Number - { "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:q:r:b:zljeJ:vh", longopts, NULL)) != -1 ) - switch (ch) { - - case 'n': // Number of input and output channels - //------------------------------------------------------- - mNumChans = atoi(optarg); - break; - case 's': // Run in server mode - //------------------------------------------------------- - mJackTripMode = JackTrip::SERVER; - break; - case 'S': // Run in jacktripserver mode - //------------------------------------------------------- - mJackTripServer = true; - break; - case 'c': // Client mode - //------------------------------------------------------- - mJackTripMode = JackTrip::CLIENT; - mPeerAddress = optarg; - break; - case 'C': // Ping to server - //------------------------------------------------------- - mJackTripMode = JackTrip::CLIENTTOPINGSERVER; - mPeerAddress = optarg; - break; - case 'o': // Port Offset - //------------------------------------------------------- - mPortNum += atoi(optarg); - break; - case 'b': - //------------------------------------------------------- - if ( atoi(optarg) == 8 ) { - mAudioBitResolution = JackAudioInterface::BIT8; } - else if ( atoi(optarg) == 16 ) { - mAudioBitResolution = JackAudioInterface::BIT16; } - else if ( atoi(optarg) == 24 ) { - mAudioBitResolution = JackAudioInterface::BIT24; } - else if ( atoi(optarg) == 32 ) { - mAudioBitResolution = JackAudioInterface::BIT32; } - else { - std::cerr << "--bitres ERROR: Wrong bit resolutions: " - << atoi(optarg) << " is not supported." << endl; - printUsage(); - std::exit(1); } - break; - case 'q': - //------------------------------------------------------- - if ( atoi(optarg) <= 0 ) { - std::cerr << "--queue ERROR: The queue has to be a positive integer" << endl; - printUsage(); - std::exit(1); } - else { - mBufferQueueLength = atoi(optarg); - } - break; - case 'r': - //------------------------------------------------------- - if ( atoi(optarg) <= 0 ) { - std::cerr << "--queue ERROR: The queue has to be a positive integer" << endl; - printUsage(); - std::exit(1); } - else { - mRedundancy = atoi(optarg); - } - break; - case 'z': // underrun to zero - //------------------------------------------------------- - mUnderrrunZero = true; - break; - case 'l': // loopback - //------------------------------------------------------- - mLoopBack = true; - break; - case 'e': // jamlink - //------------------------------------------------------- - mEmptyHeader = true; - break; - case 'j': // jamlink - //------------------------------------------------------- - mJamLink = true; - break; - case 'J': - //------------------------------------------------------- - mClientName = optarg; - break; - case 'v': - //------------------------------------------------------- - cout << "JackTrip VERSION: " << gVersion << endl; - cout << "Copyright (c) 2008-2009 Juan-Pablo Caceres, Chris Chafe." << endl; - cout << "SoundWIRE group at CCRMA, Stanford University" << endl; - cout << "" << endl; - std::exit(0); - break; - case 'h': - //------------------------------------------------------- - printUsage(); - std::exit(0); - break; - default: - //------------------------------------------------------- - printUsage(); - std::exit(0); - break; + // If no command arguments are given, print instructions + if(argc == 1) { + printUsage(); + std::exit(0); } - // Warn user if undefined options where entered - //---------------------------------------------------------------------------- - if (optind < argc) { - cout << gPrintSeparator << endl; - cout << "WARINING: The following entered options have no effect" << endl; - cout << " They will be ignored!" << endl; - cout << " Type jacktrip to see options." << endl; - for( ; optind < argc; optind++) { - printf("argument: %s\n", argv[optind]); + // Usage example at: + // http://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html#Getopt-Long-Option-Example + // 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 } + }; + + // 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 ) + switch (ch) { + + case 'n': // Number of input and output channels + //------------------------------------------------------- + mNumChans = atoi(optarg); + break; + case 's': // Run in server mode + //------------------------------------------------------- + mJackTripMode = JackTrip::SERVER; + break; + case 'S': // Run in jacktripserver mode + //------------------------------------------------------- + mJackTripServer = true; + break; + case 'c': // Client mode + //------------------------------------------------------- + mJackTripMode = JackTrip::CLIENT; + mPeerAddress = optarg; + break; + case 'L': // set optional local host address + //------------------------------------------------------- + mLocalAddress = optarg; + break; + case 'C': // Ping to server + //------------------------------------------------------- + mJackTripMode = JackTrip::CLIENTTOPINGSERVER; + mPeerAddress = optarg; + break; + case 'o': // Port Offset + //------------------------------------------------------- + mBindPortNum += atoi(optarg); + mPeerPortNum += atoi(optarg); + break; + case 'B': // Bind Port + //------------------------------------------------------- + mBindPortNum = atoi(optarg); + break; + case 'P': // Peer Port + //------------------------------------------------------- + mPeerPortNum = atoi(optarg); + break; + case 'b': + //------------------------------------------------------- + if ( atoi(optarg) == 8 ) { + mAudioBitResolution = AudioInterface::BIT8; } + else if ( atoi(optarg) == 16 ) { + mAudioBitResolution = AudioInterface::BIT16; } + else if ( atoi(optarg) == 24 ) { + mAudioBitResolution = AudioInterface::BIT24; } + else if ( atoi(optarg) == 32 ) { + mAudioBitResolution = AudioInterface::BIT32; } + else { + std::cerr << "--bitres ERROR: Wrong bit resolutions: " + << atoi(optarg) << " is not supported." << endl; + printUsage(); + std::exit(1); } + break; + case 'q': + //------------------------------------------------------- + if ( atoi(optarg) <= 0 ) { + std::cerr << "--queue ERROR: The queue has to be equal or greater that 2" << endl; + printUsage(); + std::exit(1); } + else { + mBufferQueueLength = atoi(optarg); + } + break; + case 'r': + //------------------------------------------------------- + if ( atoi(optarg) <= 0 ) { + std::cerr << "--redundancy ERROR: The reduncancy has to be a positive integer" << endl; + printUsage(); + std::exit(1); } + else { + mRedundancy = atoi(optarg); + } + break; + case 'z': // underrun to zero + //------------------------------------------------------- + mUnderrrunZero = true; + break; + case 'l': // loopback + //------------------------------------------------------- + mLoopBack = true; + break; + case 'e': // jamlink + //------------------------------------------------------- + mEmptyHeader = true; + break; + case 'j': // jamlink + //------------------------------------------------------- + mJamLink = true; + break; + case 'J': // Set client Name + //------------------------------------------------------- + mClientName = optarg; + break; + case 'R': // RtAudio + //------------------------------------------------------- + mUseJack = false; + break; + case 'T': // Sampling Rate + //------------------------------------------------------- + mChanfeDefaultSR = true; + mSampleRate = atoi(optarg); + break; + case 'F': // Buffer Size + //------------------------------------------------------- + mChanfeDefaultBS = true; + mAudioBufferSize = atoi(optarg); + break; + case 'v': + //------------------------------------------------------- + cout << "JackTrip VERSION: " << gVersion << endl; + cout << "Copyright (c) 2008-2009 Juan-Pablo Caceres, Chris Chafe." << endl; + cout << "SoundWIRE group at CCRMA, Stanford University" << endl; + cout << "" << endl; + std::exit(0); + break; + case 'h': + //------------------------------------------------------- + printUsage(); + std::exit(0); + break; + default: + //------------------------------------------------------- + printUsage(); + std::exit(0); + break; + } + + // Warn user if undefined options where entered + //---------------------------------------------------------------------------- + if (optind < argc) { + cout << gPrintSeparator << endl; + cout << "WARINING: The following entered options have no effect" << endl; + cout << " They will be ignored!" << endl; + cout << " Type jacktrip to see options." << endl; + for( ; optind < argc; optind++) { + printf("argument: %s\n", argv[optind]); + } + cout << gPrintSeparator << endl; } - cout << gPrintSeparator << endl; - } } //******************************************************************************* 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-2009 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 << " -s, --server Run in Server Mode" << endl; - cout << " -c, --client Run in Client Mode" << endl; - cout << " -n, --numchannels # Number of Input and Output Channels (default " - << 2 << ")" << endl; - cout << " -q, --queue # (1 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)" - << endl; - cout << " -o, --portoffset # Receiving port offset from base port " << gDefaultPort << 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 << " -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 << " -v, --version Prints Version Number" << endl; - cout << " -h, --help Prints this Help" << endl; - cout << "" << endl; + 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 << "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 << endl; + cout << "OPTIONAL ARGUMENTS: " << endl; + cout << "===================" << endl; + 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 " + << gDefaultQueueLength << ")" << endl; + cout << " -r, --redundancy # (1 or more) Packet Redundancy to avoid glitches with packet losses (defaul 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 << " -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 << 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 << endl; + cout << "HELP ARGUMENTS: " << endl; + cout << "===============" << endl; + cout << " -v, --version Prints Version Number" << endl; + cout << " -h, --help Prints this Help" << endl; + cout << "" << endl; } @@ -276,13 +331,13 @@ void Settings::printUsage() void Settings::startJackTrip() { - ///\todo Change this, just here to test - if ( mJackTripServer ) { - UdpMasterListener* udpmaster = new UdpMasterListener; - udpmaster->start(); - - //---Thread Pool Test-------------------------------------------- - /* + /// \todo Change this, just here to test + if ( mJackTripServer ) { + UdpMasterListener* udpmaster = new UdpMasterListener; + udpmaster->start(); + + //---Thread Pool Test-------------------------------------------- + /* cout << "BEFORE START" << endl; ThreadPoolTest* thtest = new ThreadPoolTest(); // QThreadPool takes ownership and deletes 'hello' automatically @@ -293,84 +348,112 @@ void Settings::startJackTrip() thtest->stop(); QThreadPool::globalInstance()->waitForDone(); */ - //--------------------------------------------------------------- - } - - else { - - //JackTrip jacktrip(mJackTripMode, mDataProtocol, mNumChans, - // mBufferQueueLength, mAudioBitResolution); - mJackTrip = new JackTrip(mJackTripMode, mDataProtocol, mNumChans, - mBufferQueueLength, mRedundancy, mAudioBitResolution); - - // Change client name if different from default - if (mClientName != NULL) { - mJackTrip->setClientName(mClientName); + //--------------------------------------------------------------- } - // Set buffers to zero when underrun - if ( mUnderrrunZero ) { - cout << "Setting buffers to zero when underrun..." << endl; - cout << gPrintSeparator << std::endl; - mJackTrip->setUnderRunMode(JackTrip::ZEROS); - } - - // Set peer address in server mode - if ( mJackTripMode == JackTrip::CLIENT || mJackTripMode == JackTrip::CLIENTTOPINGSERVER ) { - mJackTrip->setPeerAddress(mPeerAddress.toLatin1().data()); } - - // Set Ports - cout << "SETTING ALL PORTS" << endl; - mJackTrip->setAllPorts(mPortNum); - - // Set in JamLink Mode - if ( mJamLink ) { - cout << "Running in JamLink Mode..." << endl; - cout << gPrintSeparator << std::endl; - mJackTrip->setPacketHeaderType(DataProtocol::JAMLINK); - } + else { - // Set in EmptyHeader Mode - if ( mEmptyHeader ) { - cout << "Running in EmptyHeader Mode..." << endl; - cout << gPrintSeparator << std::endl; - mJackTrip->setPacketHeaderType(DataProtocol::EMPTY); - } + //JackTrip jacktrip(mJackTripMode, mDataProtocol, mNumChans, + // mBufferQueueLength, mAudioBitResolution); + mJackTrip = new JackTrip(mJackTripMode, mDataProtocol, mNumChans, + mBufferQueueLength, mRedundancy, mAudioBitResolution); - // Add Plugins - if ( mLoopBack ) { - cout << "Running in Loop-Back Mode..." << endl; - cout << gPrintSeparator << std::endl; - //std::tr1::shared_ptr loopback(new LoopBack(mNumChans)); - //mJackTrip->appendProcessPlugin(loopback.get()); - - LoopBack* loopback = new LoopBack(mNumChans); - mJackTrip->appendProcessPlugin(loopback); - - // ----- Test Karplus Strong ----------------------------------- - //std::tr1::shared_ptr loopback(new NetKS()); - //mJackTrip->appendProcessPlugin(loopback); - //loopback->play(); - //NetKS* netks = new NetKS; - //mJackTrip->appendProcessPlugin(netks); - //netks->play(); - // ------------------------------------------------------------- - } - - // Start JackTrip - mJackTrip->start(); - - /* + // Connect Signals and Slots + QObject::connect(mJackTrip, SIGNAL( signalProcessesStopped() ), + this, SLOT( slotExitProgram() )); + + // Change client name if different from default + if (mClientName != NULL) { + mJackTrip->setClientName(mClientName); + } + + // Set buffers to zero when underrun + if ( mUnderrrunZero ) { + cout << "Setting buffers to zero when underrun..." << endl; + cout << gPrintSeparator << std::endl; + mJackTrip->setUnderRunMode(JackTrip::ZEROS); + } + + // Set peer address in server mode + 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); + + // Set Ports + //cout << "SETTING ALL PORTS" << endl; + mJackTrip->setBindPorts(mBindPortNum); + mJackTrip->setPeerPorts(mPeerPortNum); + + // Set in JamLink Mode + if ( mJamLink ) { + cout << "Running in JamLink Mode..." << endl; + cout << gPrintSeparator << std::endl; + mJackTrip->setPacketHeaderType(DataProtocol::JAMLINK); + } + + // Set in EmptyHeader Mode + if ( mEmptyHeader ) { + cout << "Running in EmptyHeader Mode..." << endl; + cout << gPrintSeparator << std::endl; + mJackTrip->setPacketHeaderType(DataProtocol::EMPTY); + } + + // Set RtAudio +#ifdef __RT_AUDIO__ + if (!mUseJack) { + mJackTrip->setAudiointerfaceMode(JackTrip::RTAUDIO); + } +#endif + + // Chanfe default Sampling Rate + if (mChanfeDefaultSR) { + mJackTrip->setSampleRate(mSampleRate); + } + + // Chanfe default Buffer Size + if (mChanfeDefaultBS) { + mJackTrip->setAudioBufferSizeInSamples(mAudioBufferSize); + } + + // Add Plugins + if ( mLoopBack ) { + cout << "Running in Loop-Back Mode..." << endl; + cout << gPrintSeparator << std::endl; + //std::tr1::shared_ptr loopback(new LoopBack(mNumChans)); + //mJackTrip->appendProcessPlugin(loopback.get()); + + LoopBack* loopback = new LoopBack(mNumChans); + mJackTrip->appendProcessPlugin(loopback); + + // ----- Test Karplus Strong ----------------------------------- + //std::tr1::shared_ptr loopback(new NetKS()); + //mJackTrip->appendProcessPlugin(loopback); + //loopback->play(); + //NetKS* netks = new NetKS; + //mJackTrip->appendProcessPlugin(netks); + //netks->play(); + // ------------------------------------------------------------- + } + + // Start JackTrip + mJackTrip->startProcess(); + mJackTrip->start(); + + /* sleep(10); cout << "Stoping JackTrip..." << endl; mJackTrip->stop(); */ - } + } } //******************************************************************************* void Settings::stopJackTrip() { - mJackTrip->stop(); + mJackTrip->stop(); } diff --git a/src/Settings.h b/src/Settings.h index 5c08808..90eea26 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -39,14 +39,22 @@ #ifndef __SETTINGS_H__ #define __SETTINGS_H__ +#include + #include "DataProtocol.h" + +#ifndef __NO_JACK__ #include "JackAudioInterface.h" +#endif //__NO_JACK__ + #include "JackTrip.h" /** \brief Class to set usage options and parse settings from input */ -class Settings +class Settings : public QThread { + Q_OBJECT; + public: Settings(); virtual ~Settings(); @@ -60,7 +68,15 @@ public: /// \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); + } private: JackTrip* mJackTrip; ///< JackTrip class @@ -68,17 +84,24 @@ private: JackTrip::dataProtocolT mDataProtocol; ///< Data Protocol int mNumChans; ///< Number of Channels (inputs = outputs) int mBufferQueueLength; ///< Audio Buffer from network queue length - JackAudioInterface::audioBitResolutionT mAudioBitResolution; + AudioInterface::audioBitResolutionT mAudioBitResolution; QString mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode - int mPortNum; ///< Port Number - char* mClientName; + 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; }; #endif diff --git a/src/UdpDataProtocol.cpp b/src/UdpDataProtocol.cpp index 9e2275f..0cd1369 100644 --- a/src/UdpDataProtocol.cpp +++ b/src/UdpDataProtocol.cpp @@ -39,12 +39,19 @@ #include "jacktrip_globals.h" #include "JackTrip.h" +#include + #include #include #include #include #include +#ifdef __WIN_32__ +#include +#endif +#if defined (__LINUX__) || (__MAC__OSX__) #include // for POSIX Sockets +#endif using std::cout; using std::endl; @@ -65,6 +72,7 @@ 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); @@ -82,14 +90,26 @@ UdpDataProtocol::~UdpDataProtocol() //******************************************************************************* -void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP) +void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP) throw(std::invalid_argument) { - mPeerAddress.setAddress(peerHostOrIP); + // 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; + } + // check if the ip address is valid if ( mPeerAddress.isNull() ) { - 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(""); + 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 { @@ -103,16 +123,51 @@ void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP) //******************************************************************************* -void UdpDataProtocol::bindSocket(QUdpSocket& UdpSocket) +void UdpDataProtocol::bindSocket(QUdpSocket& UdpSocket) throw(std::runtime_error) { QMutexLocker locker(&sUdpMutex); +#if defined __WIN_32__ + WORD wVersionRequested; + WSADATA wsaData; + int err; + + wVersionRequested = MAKEWORD( 1, 1 ); + + err = WSAStartup( wVersionRequested, &wsaData ); + if ( err != 0 ) { + // Tell the user that we couldn't find a useable + // winsock.dll. + + return; + } + + // 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; + } + // Creat socket descriptor - int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + SOCKET sock_fd; + SOCKADDR_IN local_addr; +#endif - // Set local IPv4 Address +#if defined ( __LINUX__ ) || (__MAC_OSX__) + int sock_fd; + //Set local IPv4 Address struct sockaddr_in local_addr; - ::bzero(&local_addr, sizeof(local_addr)); +#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 @@ -127,10 +182,22 @@ void UdpDataProtocol::bindSocket(QUdpSocket& UdpSocket) // 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)); +#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 // To be able to use the two UDP sockets bound to the same port number, // we connect the receiver and issue a SHUT_WR. @@ -140,6 +207,7 @@ void UdpDataProtocol::bindSocket(QUdpSocket& UdpSocket) QUdpSocket::WriteOnly); } else if (mRunMode == RECEIVER) { +#if defined (__LINUX__) || (__MAC_OSX__) // Set peer IPv4 Address struct sockaddr_in peer_addr; bzero(&peer_addr, sizeof(peer_addr)); @@ -155,6 +223,31 @@ void UdpDataProtocol::bindSocket(QUdpSocket& UdpSocket) { 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"); } +#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: "< first_packet(new int8_t[first_packet_size]); - receivePacket( UdpSocket, reinterpret_cast(first_packet.get()), first_packet_size); + //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.get()); + mJackTrip->checkPeerSettings(first_packet); mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); std::cout << "Received Connection for Peer!" << std::endl; + emit signalReceivedConnectionFromPeer(); // Redundancy Variables // -------------------- @@ -357,7 +463,7 @@ bool UdpDataProtocol::waitForReady(QUdpSocket& UdpSocket, int timeout_msec) while ( ( !(UdpSocket.hasPendingDatagrams()) && (ellaped_time_usec <= timeout_usec) ) && !mStopped ){ - //cout << mStopped << endl; + if (mStopped) { return false; } QThread::usleep(loop_resolution_usec); ellaped_time_usec += loop_resolution_usec; @@ -380,7 +486,7 @@ void UdpDataProtocol::printUdpWaitedTooLong(int wait_msec) { int wait_time = 30; // msec if ( !(wait_msec%wait_time) ) { - std::cerr << "UDP is waited too long (more than " << wait_time << "ms)..." << endl; + std::cerr << "UDP waiting too long (more than " << wait_time << "ms)..." << endl; } } @@ -409,7 +515,7 @@ void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket, for (unsigned int i = 1; i + #include #include #include +#include #include "DataProtocol.h" #include "jacktrip_types.h" @@ -84,7 +87,7 @@ public: /** \brief Set the Peer address to connect to * \param peerHostOrIP IPv4 number or host name */ - void setPeerAddress(const char* peerHostOrIP); + void setPeerAddress(const char* peerHostOrIP) throw(std::invalid_argument); /** \brief Receives a packet. It blocks until a packet is received * @@ -146,11 +149,12 @@ signals: void signalWatingTooLong(int wait_msec); -private: +//private: +protected: /** \brief Binds the UDP socket to the available address and specified port */ - void bindSocket(QUdpSocket& UdpSocket); + void bindSocket(QUdpSocket& UdpSocket) throw(std::runtime_error); /** \brief This function blocks until data is available for reading in the * QUdpSocket. The function will timeout after timeout_msec microseconds. @@ -165,21 +169,24 @@ private: /** \brief Redundancy algorythm at the receiving end */ - 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 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 */ - void sendPacketRedundancy(QUdpSocket& UdpSocket, - QHostAddress& PeerAddress, - int8_t* full_redundant_packet, - int full_redundant_packet_size, - int full_packet_size); + virtual void sendPacketRedundancy(QUdpSocket& UdpSocket, + QHostAddress& PeerAddress, + 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 diff --git a/src/UdpMasterListener.cpp b/src/UdpMasterListener.cpp index 874eda4..6650eaa 100644 --- a/src/UdpMasterListener.cpp +++ b/src/UdpMasterListener.cpp @@ -38,6 +38,12 @@ #include #include #include +#include + +#include +#include +#include +#include #include "UdpMasterListener.h" #include "JackTripWorker.h" @@ -48,13 +54,20 @@ using std::cout; using std::endl; //******************************************************************************* UdpMasterListener::UdpMasterListener(int server_port) : - mJTWorker(NULL), + //mJTWorker(NULL), mServerPort(server_port), mStopped(false), mTotalRunningThreads(0) { // Register JackTripWorker with the master listener - mJTWorker = new JackTripWorker(this); + //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; } //******************************************************************************* +// Now that the first handshake is with TCP server, if the addreess/peer port of +// the client is already on the thread pool, it means that a new connection is +// requested (the old was desconnected). So we have to remove that thread from +// the pool and then connect again. void UdpMasterListener::run() { mStopped = false; + QHostAddress PeerAddress; // Object to store peer address + int peer_udp_port; // Peer listening port + int server_udp_port; // Server assigned udp port + + // Create and bind the TCP server + // ------------------------------ + QTcpServer TcpServer; + if ( !TcpServer.listen(QHostAddress::Any, mServerPort) ) { + std::cerr << "TCP Socket Server ERROR: " << TcpServer.errorString().toStdString() << endl; + std::exit(1); + } + + const int tcpTimeout = 5*1000; + + + cout << "JackTrip 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); + } + // 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; @@ -93,59 +221,110 @@ void UdpMasterListener::run() cout << "Waiting for client..." << endl; cout << "=======================================================" << endl; while ( !mStopped ) + { + //cout << "WAITING........................." << endl; + while ( MasterUdpSocket.hasPendingDatagrams() ) { - //cout << "WAITING........................." << endl; - while ( MasterUdpSocket.hasPendingDatagrams() ) - { - //cout << "Received request from Client!" << endl; - // Get Client IP Address and outgoing port from packet - int rv = MasterUdpSocket.readDatagram(buf, 1, &PeerAddress, &peer_port); - //cout << "Peer Port in Server ==== " << peer_port << endl; - if (rv < 0) { std::cerr << "ERROR: Bad UDP packet read..." << endl; } - - /// \todo Get number of channels in the client from header - - // check by comparing 32-bit addresses - /// \todo Add the port number in the comparison - int id = isNewAddress(PeerAddress.toIPv4Address(), peer_port); - - //cout << "IDIDIDIDIDDID === " << id << endl; - - // If the address is new, create a new thread in the pool - if (id >= 0) // old address is -1 - { - // redirect port and spawn listener - sendToPoolPrototype(id); - // wait until one is complete before another spawns - while (mJTWorker->isSpawning()) { QThread::msleep(1); } - mTotalRunningThreads++; - cout << "Total Running Threads: " << mTotalRunningThreads << endl; - cout << "=======================================================" << endl; - } - //cout << "ENDDDDDDDDDDDDDDDDDd === " << id << endl; - } - QThread::msleep(100); + cout << "Received request from Client!" << endl; + // Get Client IP Address and outgoing port from packet + int rv = MasterUdpSocket.readDatagram(buf, 1, &PeerAddress, &peer_port); + cout << "Peer Port in Server ==== " << peer_port << endl; + if (rv < 0) { std::cerr << "ERROR: Bad UDP packet read..." << endl; } + + /// \todo Get number of channels in the client from header + + // check by comparing 32-bit addresses + /// \todo Add the port number in the comparison + cout << "peer_portpeer_portpeer_port === " << peer_port << endl; + int id = isNewAddress(PeerAddress.toIPv4Address(), peer_port); + + //cout << "IDIDIDIDIDDID === " << id << endl; + + // If the address is new, create a new thread in the pool + if (id >= 0) // old address is -1 + { + // redirect port and spawn listener + sendToPoolPrototype(id); + // wait until one is complete before another spawns + while (mJTWorker->isSpawning()) { QThread::msleep(10); } + mTotalRunningThreads++; + cout << "Total Running Threads: " << mTotalRunningThreads << endl; + cout << "=======================================================" << endl; + } + //cout << "ENDDDDDDDDDDDDDDDDDd === " << id << endl; + } + QThread::msleep(100); + } + */ +} + + +//******************************************************************************* +// Returns 0 on error +int UdpMasterListener::readClientUdpPort(QTcpSocket* clientConnection) +{ + // Read the size of the package + // ---------------------------- + //tcpClient.waitForReadyRead(); + cout << "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; + } + } + + 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; +} + + +//******************************************************************************* +int UdpMasterListener::sendUdpPort(QTcpSocket* clientConnection, int udp_port) +{ + // Send Port Number to Client + // -------------------------- + char port_buf[sizeof(udp_port)]; + std::memcpy(port_buf, &udp_port, sizeof(udp_port)); + clientConnection->write(port_buf, sizeof(udp_port)); + while ( clientConnection->bytesToWrite() > 0 ) { + if ( clientConnection->state() == QAbstractSocket::ConnectedState ) { + clientConnection->waitForBytesWritten(-1); } + else { + return 0; + } + } + return 1; + cout << "Port sent to Client" << endl; } //******************************************************************************* +/* void UdpMasterListener::sendToPoolPrototype(int id) { - cout << "id ID **********@@@@@@@@@@@@@@@@@@@@@************** " << id << endl; mJTWorker->setJackTrip(id, mActiveAddress[id][0], - mBasePort+(2*id), mActiveAddress[id][1], - 1); /// \todo temp default to 1 channel + mBasePort+(2*id), mActiveAddress[id][1], + 1); /// \todo temp default to 1 channel mThreadPool.start(mJTWorker, QThread::TimeCriticalPriority); //send one thread to the pool } +*/ //******************************************************************************* -void UdpMasterListener::bindUdpSocket(QUdpSocket& udpsocket, int port) +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) ) { + 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."); @@ -160,27 +339,73 @@ void UdpMasterListener::bindUdpSocket(QUdpSocket& udpsocket, int port) // check by comparing 32-bit addresses int UdpMasterListener::isNewAddress(uint32_t address, uint16_t port) { - /// \todo Add the port number in the comparison, i.e., compart IP/port pair - + QMutexLocker lock(&mMutex); bool busyAddress = false; int id = 0; + + /* while ( !busyAddress && (id +#include #include #include #include #include +#include +#include +#include #include "jacktrip_types.h" #include "jacktrip_globals.h" @@ -63,25 +67,23 @@ public: UdpMasterListener(int server_port = gServerUdpPort); virtual ~UdpMasterListener(); - /** \brief Implements the Thread Loop. To start the thread, call start() - * ( DO NOT CALL run() ) - */ + /// \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; }; + void stop() { mStopped = true; } - int releasePort(int id); + int releaseThread(int id); private slots: - void testRecieve() - { - 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); private: @@ -89,13 +91,17 @@ private: * \param udpsocket a QUdpSocket * \param port Port number */ - static void bindUdpSocket(QUdpSocket& udpsocket, int port); + static void bindUdpSocket(QUdpSocket& udpsocket, int port) throw(std::runtime_error); + + int readClientUdpPort(QTcpSocket* clientConnection); + int sendUdpPort(QTcpSocket* clientConnection, int udp_port); - /* \brief Send the JackTripWorker to the thread pool. This will run + + /** \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 @@ -103,10 +109,16 @@ private: */ int isNewAddress(uint32_t address, uint16_t port); - QUdpSocket mUdpMasterSocket; ///< The UDP socket - QHostAddress mPeerAddress; ///< The Peer Address + /** \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 + //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 @@ -117,6 +129,7 @@ private: /// Boolean stop the execution of the thread volatile bool mStopped; int mTotalRunningThreads; ///< Number of Threads running in the pool + QMutex mMutex; }; diff --git a/src/build b/src/build index 546567d..18f47fa 100755 --- a/src/build +++ b/src/build @@ -14,12 +14,32 @@ fi # Set qmake command name if [[ $platform == 'linux' ]]; then - QCMD=qmake-qt4 + if hash qmake-qt5 2>/dev/null; then + echo "Using qmake-qt5" + QCMD=qmake-qt5 + elif hash qmake-qt4 2>/dev/null; then + echo "Using qmake-qt4" + QCMD=qmake-qt4 + elif hash qmake 2>/dev/null; then #in case qt was compiled by user + echo "Using qmake" + QCMD=qmake + fi + QSPEC=linux-g++ elif [[ $platform == 'macosx' ]]; then QCMD=qmake + QSPEC=macx-g++ fi # Build -$QCMD jacktrip.pro -make clean -make release +if [[ $1 == 'nojack' ]]; then + echo "Building without Jack" + $QCMD -spec $QSPEC -config nojack jacktrip.pro + make clean + $QCMD -spec $QSPEC -config nojack jacktrip.pro + make release +else + $QCMD -spec $QSPEC jacktrip.pro + make clean + $QCMD -spec $QSPEC jacktrip.pro + make release +fi diff --git a/src/jacktrip-1.0.5.diff b/src/jacktrip-1.0.5.diff deleted file mode 100644 index 30292b9..0000000 --- a/src/jacktrip-1.0.5.diff +++ /dev/null @@ -1,26 +0,0 @@ -Index: src/JackTripWorker.h -=================================================================== ---- src/JackTripWorker.h (revision 495) -+++ src/JackTripWorker.h (working copy) -@@ -46,6 +46,8 @@ - #include - #include - -+#include "jacktrip_types.h" -+ - class JackTrip; // forward declaration - class UdpMasterListener; // forward declaration - -Index: src/jacktrip_globals.cpp -=================================================================== ---- src/jacktrip_globals.cpp (revision 495) -+++ src/jacktrip_globals.cpp (working copy) -@@ -38,6 +38,8 @@ - #include "jacktrip_globals.h" - #include "jacktrip_types.h" - -+#include -+ - #if defined ( __LINUX__ ) - #include - #endif //__LINUX__ diff --git a/src/jacktrip.pro b/src/jacktrip.pro index b92e69d..bd95a2b 100644 --- a/src/jacktrip.pro +++ b/src/jacktrip.pro @@ -10,28 +10,75 @@ CONFIG(debug, debug|release) { } QT -= gui QT += network -INCLUDEPATH+=/usr/local/include -LIBS += -ljack -lm +# http://wiki.qtcentre.org/index.php?title=Undocumented_qmake#Custom_tools +DEFINES += __RT_AUDIO__ +# Configuration without Jack +nojack { + DEFINES += __NO_JACK__ +} +!win32 { + INCLUDEPATH+=/usr/local/include + LIBS += -L/usr/local/lib -ljack -lm + nojack { + message(Building NONJACK) + LIBS -= -ljack + } +} + macx { message(MAC OS X) + QMAKE_CXXFLAGS += -D__MACOSX_CORE__ #-D__UNIX_JACK__ #RtAudio Flags + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.9 + QMAKE_MAC_SDK = macosx10.9 CONFIG -= app_bundle - CONFIG += x86 #ppc - LIBS += -framework CoreAudio + #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 + QMAKE_CXXFLAGS += -D__LINUX_ALSA__ #-D__LINUX_OSS__ #RtAudio Flags + QMAKE_CXXFLAGS += -g -O2 + DEFINES += __LINUX__ + } +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" + DEFINES += __WIN_32__ + DEFINES -= UNICODE #RtAudio for Qt +} + + + + DESTDIR = . -QMAKE_CLEAN += ./jacktrip ./jacktrip_debug +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 +} + # Input HEADERS += DataProtocol.h \ - JackAudioInterface.h \ JackTrip.h \ jacktrip_globals.h \ jacktrip_types.h \ @@ -49,9 +96,13 @@ HEADERS += DataProtocol.h \ ThreadPoolTest.h \ UdpDataProtocol.h \ UdpMasterListener.h \ - jacktrip_tests.cpp + AudioInterface.h \ + RtAudioInterface.h + #JamTest.h +!nojack { +SOURCES += JackAudioInterface.h +} SOURCES += DataProtocol.cpp \ - JackAudioInterface.cpp \ JackTrip.cpp \ jacktrip_globals.cpp \ jacktrip_main.cpp \ @@ -63,6 +114,30 @@ SOURCES += DataProtocol.cpp \ ProcessPlugin.cpp \ RingBuffer.cpp \ Settings.cpp \ - tests.cpp \ + #tests.cpp \ UdpDataProtocol.cpp \ - UdpMasterListener.cpp + UdpMasterListener.cpp \ + AudioInterface.cpp \ + RtAudioInterface.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 +} diff --git a/src/jacktrip_globals.cpp b/src/jacktrip_globals.cpp index df5c24d..feb5191 100644 --- a/src/jacktrip_globals.cpp +++ b/src/jacktrip_globals.cpp @@ -35,13 +35,17 @@ * \date August 2008 */ +#include +#include +#include + #include "jacktrip_globals.h" #include "jacktrip_types.h" -#include - #if defined ( __LINUX__ ) #include +#include +#include #endif //__LINUX__ #if defined ( __MAC_OSX__ ) @@ -183,6 +187,25 @@ int set_realtime_priority (void) } #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__ ) @@ -191,6 +214,10 @@ void set_crossplatform_realtime_priority() #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 f9d2d76..3f87dac 100644 --- a/src/jacktrip_globals.h +++ b/src/jacktrip_globals.h @@ -38,22 +38,31 @@ #ifndef __JACKTRIP_GLOBALS_H__ #define __JACKTRIP_GLOBALS_H__ -#include "JackAudioInterface.h" +#include "AudioInterface.h" +//#include "JackAudioInterface.h" +/// \todo Add this namespace +//namespace JackTrip -//namespace JackTrip/// \todo Add this namespace - -const char* const gVersion = "1.0.5"; ///< JackTrip version +const char* const gVersion = "1.1"; ///< JackTrip version //******************************************************************************* /// \name Default Values //@{ const int gDefaultNumInChannels = 2; const int gDefaultNumOutChannels = 2; -const JackAudioInterface::audioBitResolutionT gDefaultBitResolutionMode = - JackAudioInterface::BIT16; +//const JackAudioInterface::audioBitResolutionT gDefaultBitResolutionMode = +// JackAudioInterface::BIT16; +const AudioInterface::audioBitResolutionT gDefaultBitResolutionMode = + AudioInterface::BIT16; const int gDefaultQueueLength = 4; const int gDefaultOutputQueueLength = 4; +const uint32_t gDefaultSampleRate = 48000; +const uint32_t gDefaultBufferSizeInSamples = 128; +const QString gDefaultLocalAddress = QString(); +const int gDefaultRedundancy = 1; +const int gTimeOutMultiThreadedServer = 5000; // seconds +const int gWaitCounter = 60; //@} @@ -110,7 +119,13 @@ int set_realtime_priority (void); 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 e0d4203..96159d2 100644 --- a/src/jacktrip_main.cpp +++ b/src/jacktrip_main.cpp @@ -47,11 +47,11 @@ //#include "TestRingBuffer.h" #include "LoopBack.h" #include "PacketHeader.h" -#include "JackTripThread.h" +//#include "JackTripThread.h" +#include "RtAudioInterface.h" #include "jacktrip_tests.cpp" #include "jacktrip_globals.h" - using std::cout; using std::endl; @@ -59,14 +59,30 @@ int main(int argc, char** argv) { QCoreApplication app(argc, argv); - //--------TESTS-------------------------- - //main_tests(argc, argv); // test functions - //while (true) sleep(9999); - //--------------------------------------- + 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); - // Get Settings from user - // ---------------------- - try + //while (true) sleep(9999); + } + else { + //--------------------------------------- + + // Get Settings from user + // ---------------------- + try { // Get Settings from user // ---------------------- @@ -74,7 +90,7 @@ int main(int argc, char** argv) settings->parseInput(argc, argv); settings->startJackTrip(); } - catch ( const std::exception & e ) + catch ( const std::exception & e ) { std::cerr << "ERROR:" << endl; std::cerr << e.what() << endl; @@ -82,5 +98,7 @@ int main(int argc, char** argv) std::cerr << gPrintSeparator << endl; return -1; } + } + return app.exec(); } diff --git a/src/jacktrip_types.h b/src/jacktrip_types.h index 08bfe87..f9f10f0 100644 --- a/src/jacktrip_types.h +++ b/src/jacktrip_types.h @@ -39,47 +39,51 @@ #ifndef __JACKTRIP_TYPES_H__ #define __JACKTRIP_TYPES_H__ -#include +//#include #include //For QT4 types -//------------------------------------------------------------------------------- -/** \name Audio typedefs +//namespace JackTripNamespace +//{ + + //------------------------------------------------------------------------------- + /** \name Audio typedefs * */ -//------------------------------------------------------------------------------- -//@{ -/// Audio sample type -typedef jack_default_audio_sample_t sample_t; -//@} + //------------------------------------------------------------------------------- + //@{ + /// Audio sample type + //typedef jack_default_audio_sample_t sample_t; + typedef float sample_t; + //@} -//------------------------------------------------------------------------------- -/** \name Typedefs that guaranty some specific bit length + //------------------------------------------------------------------------------- + /** \name Typedefs that guaranty some specific bit length * * It uses the QT4 types. This can be changed in the future, keeping * compatibility for the rest of the code. */ -//------------------------------------------------------------------------------- -//@{ -/// Typedef for unsigned char. This type is guaranteed to be 8-bit. -typedef quint8 uint8_t; -/// Typedef for unsigned short. This type is guaranteed to be 16-bit. -typedef quint16 uint16_t; -/// Typedef for unsigned int. This type is guaranteed to be 32-bit. -typedef quint32 uint32_t; -/// \brief Typedef for unsigned long long int. This type is guaranteed to -/// be 64-bit. -//typedef quint64 uint64_t; -/// Typedef for signed char. This type is guaranteed to be 8-bit. -typedef qint8 int8_t; -/// Typedef for signed short. This type is guaranteed to be 16-bit. -typedef qint16 int16_t; -/// Typedef for signed int. This type is guaranteed to be 32-bit. -typedef qint32 int32_t; -/// \brief Typedef for long long int. This type is guaranteed to -/// be 64-bit. -//typedef qint64 int64_t; -//@} - + //------------------------------------------------------------------------------- + //@{ + /// Typedef for unsigned char. This type is guaranteed to be 8-bit. + typedef quint8 uint8_t; + /// Typedef for unsigned short. This type is guaranteed to be 16-bit. + typedef quint16 uint16_t; + /// Typedef for unsigned int. This type is guaranteed to be 32-bit. + typedef quint32 uint32_t; + /// \brief Typedef for unsigned long long int. This type is guaranteed to + /// be 64-bit. + //typedef quint64 uint64_t; + /// Typedef for signed char. This type is guaranteed to be 8-bit. + typedef qint8 int8_t; + /// Typedef for signed short. This type is guaranteed to be 16-bit. + typedef qint16 int16_t; + /// Typedef for signed int. This type is guaranteed to be 32-bit. + typedef qint32 int32_t; + /// \brief Typedef for long long int. This type is guaranteed to + /// be 64-bit. + //typedef qint64 int64_t; + //@} +//} // end of namespace JackTripNamespace #endif diff --git a/src/tests.cpp b/src/tests.cpp deleted file mode 100644 index 6582563..0000000 --- a/src/tests.cpp +++ /dev/null @@ -1,122 +0,0 @@ -//***************************************************************** -/* - JackTrip: A System for High-Quality Audio Network Performance - over the Internet - - Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe. - SoundWIRE group at CCRMA, Stanford University. - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. -*/ -//***************************************************************** - -/** - * \file tests.cpp - * \author Juan-Pablo Caceres - * \date June 2008 - */ -#include -#include -#include - - -#include "JackAudioInterface.h" -#include "UdpDataProtocol.h" -#include "RingBuffer.h" -#include "JackTrip.h" -#include "Settings.h" -#include "TestRingBuffer.h" -#include "jacktrip_globals.h" - -using namespace std; - -void tests() -{ - // Test JackTrip - //================================================================ - //JackTrip jacktrip1; - //jacktrip1.startThreads(); - - //JackTrip jacktrip2; - //jacktrip2.startThreads(); - - /* - // TestRingBuffer - //================================================================ - TestRingBufferWrite tw; - TestRingBufferRead tr; - tr.start(); - tw.start(); - */ - - /* - // Test RingBuffer - //================================================================ - RingBuffer rb(2,2); - - int8_t* writeSlot; - writeSlot = new int8_t[2]; - writeSlot[0] = *"a"; - writeSlot[1] = *"b"; - std::cout << *writeSlot << std::endl; - std::cout << writeSlot[0] << std::endl; - std::cout << writeSlot[1] << std::endl; - std::cout << *(writeSlot+1) << std::endl; - rb.writeSlot(writeSlot); - - int8_t* readSlot; - readSlot = new int8_t[2]; - rb.readSlot(readSlot); - std::cout << *(readSlot) << std::endl; - std::cout << *(readSlot+1) << std::endl; - */ - - - /* - // Test UDP Socket - //================================================================ - UdpDataProtocol udp_rec(RECEIVER, "192.168.1.4"); - UdpDataProtocol udp_send(SENDER, "192.168.1.4"); - udp_rec.start(); - udp_send.start(); - */ - - /* - // Test JackAudioInterface - //================================================================ - JackAudioInterface jack_test(4); - cout << "SR: " << jack_test.getSampleRate() << endl; - cout << "Buffer Size: " << jack_test.getBufferSize() << endl; - jack_test.setProcessCallback(process); - jack_test.startProcess(); - */ - - - while (true) - { - //cout << "SR: " << test.getSampleRate() << endl; - //cout << "Buffer Size: " << test.getBufferSize() << endl; - usleep(1000000); - //usleep(1); - } - -}