JackTrip.cpp 35 KB
Newer Older
jcaceres's avatar
jcaceres committed
1
2
//*****************************************************************
/*
jcaceres's avatar
jcaceres committed
3
  JackTrip: A System for High-Quality Audio Network Performance
jcaceres's avatar
jcaceres committed
4
5
6
7
  over the Internet

  Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe.
  SoundWIRE group at CCRMA, Stanford University.
8

jcaceres's avatar
jcaceres committed
9
10
11
12
13
14
15
16
  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:
17

jcaceres's avatar
jcaceres committed
18
19
  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.
20

jcaceres's avatar
jcaceres committed
21
22
23
24
25
26
27
28
29
30
31
32
  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.
*/
//*****************************************************************

/**
jcaceres's avatar
jcaceres committed
33
 * \file JackTrip.cpp
jcaceres's avatar
jcaceres committed
34
35
36
37
 * \author Juan-Pablo Caceres
 * \date July 2008
 */

jcaceres's avatar
jcaceres committed
38
#include "JackTrip.h"
jcaceres's avatar
jcaceres committed
39
#include "UdpDataProtocol.h"
40
#include "RingBufferWavetable.h"
41
#include "jacktrip_globals.h"
42
#include "JackAudioInterface.h"
43
#ifdef __RT_AUDIO__
44
#include "RtAudioInterface.h"
45
#endif
jcaceres's avatar
jcaceres committed
46
47

#include <iostream>
jcacerec's avatar
jcacerec committed
48
#include <cstdlib>
49
#include <stdexcept>
50
51

#include <QHostAddress>
Aaron Wyatt's avatar
Aaron Wyatt committed
52
#include <QHostInfo>
53
#include <QThread>
54
#include <QTcpSocket>
55
56
#include <QTimer>
#include <QDateTime>
jcaceres's avatar
jcaceres committed
57

jcaceres's avatar
jcaceres committed
58
using std::cout; using std::endl;
jcaceres's avatar
jcaceres committed
59

60
61
62
63
//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)
64
65
66
{
    exit(0);
}
67
#endif
jcaceres's avatar
jcaceres committed
68
69
70

//*******************************************************************************
JackTrip::JackTrip(jacktripModeT JacktripMode,
71
72
                   dataProtocolT DataProtocolType,
                   int NumChans,
Chris Chafe's avatar
src/  
Chris Chafe committed
73
74
75
                   #ifdef WAIR // WAIR
                   int NumNetRevChans,
                   #endif // endwhere
76
77
78
79
80
81
82
83
84
85
86
87
                   int BufferQueueLength,
                   unsigned int redundancy,
                   AudioInterface::audioBitResolutionT AudioBitResolution,
                   DataProtocol::packetHeaderTypeT PacketHeaderType,
                   underrunModeT UnderRunMode,
                   int receiver_bind_port, int sender_bind_port,
                   int receiver_peer_port, int sender_peer_port) :
    mJackTripMode(JacktripMode),
    mDataProtocol(DataProtocolType),
    mPacketHeaderType(PacketHeaderType),
    mAudiointerfaceMode(JackTrip::JACK),
    mNumChans(NumChans),
Chris Chafe's avatar
src/  
Chris Chafe committed
88
89
90
    #ifdef WAIR // WAIR
    mNumNetRevChans(NumNetRevChans),
    #endif // endwhere
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
    mBufferQueueLength(BufferQueueLength),
    mSampleRate(gDefaultSampleRate),
    mDeviceID(gDefaultDeviceID),
    mAudioBufferSize(gDefaultBufferSizeInSamples),
    mAudioBitResolution(AudioBitResolution),
    mDataProtocolSender(NULL),
    mDataProtocolReceiver(NULL),
    mAudioInterface(NULL),
    mPacketHeader(NULL),
    mUnderRunMode(UnderRunMode),
    mSendRingBuffer(NULL),
    mReceiveRingBuffer(NULL),
    mReceiverBindPort(receiver_bind_port),
    mSenderPeerPort(sender_peer_port),
    mSenderBindPort(sender_bind_port),
    mReceiverPeerPort(receiver_peer_port),
    mTcpServerPort(4464),
    mRedundancy(redundancy),
Aaron Wyatt's avatar
Aaron Wyatt committed
109
    mJackClientName(gJackDefaultClientName),
110
111
112
113
    mConnectionMode(JackTrip::NORMAL),
    mReceivedConnection(false),
    mTcpConnectionError(false),
    mStopped(false),
114
115
    mConnectDefaultAudioPorts(true),
    mIOStatLogStream(std::cout.rdbuf())
116
{
117
    createHeader(mPacketHeaderType);
118
}
jcaceres's avatar
jcaceres committed
119
120
121
122
123


//*******************************************************************************
JackTrip::~JackTrip()
{
124
125
126
127
128
129
130
    wait();
    delete mDataProtocolSender;
    delete mDataProtocolReceiver;
    delete mAudioInterface;
    delete mPacketHeader;
    delete mSendRingBuffer;
    delete mReceiveRingBuffer;
jcaceres's avatar
jcaceres committed
131
132
133
134
}


//*******************************************************************************
Chris Chafe's avatar
src/  
Chris Chafe committed
135
void JackTrip::setupAudio(
Chris Chafe's avatar
Chris Chafe committed
136
        #ifdef WAIRTOMASTER // WAIR
Chris Chafe's avatar
src/  
Chris Chafe committed
137
138
139
        int ID
        #endif // endwhere
        )
jcaceres's avatar
jcaceres committed
140
{
141
142
143
144
145
146
147
    // 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();
    }
148

149
150
    // Create AudioInterface Client Object
    if ( mAudiointerfaceMode == JackTrip::JACK ) {
151
#ifndef __NO_JACK__
Chris Chafe's avatar
src/  
Chris Chafe committed
152
153
154
155
156
157
158
        if (gVerboseFlag) std::cout << "  JackTrip:setupAudio before new JackAudioInterface" << std::endl;
        mAudioInterface = new JackAudioInterface(this, mNumChans, mNumChans,
                                         #ifdef WAIR // wair
                                                 mNumNetRevChans,
                                         #endif // endwhere
                                                 mAudioBitResolution);

159
#ifdef WAIRTOMASTER // WAIR
160
161
        qDebug() << "mPeerAddress" << mPeerAddress << mPeerAddress.contains(gDOMAIN_TRIPLE);
        QString VARIABLE_AUDIO_NAME = WAIR_AUDIO_NAME; // legacy for WAIR
Aaron Wyatt's avatar
Aaron Wyatt committed
162
163
164
165
        //Set our Jack client name if we're a hub server or a custom name hasn't been set
	if(mPeerAddress.toStdString()!="" && (mJackClientName == gJackDefaultClientName || mJackTripMode == SERVERPINGSERVER)) {
            mJackClientName = QString(mPeerAddress).replace(":", ".").toLatin1().constData();
        }
166
167
        std::cout  << "WAIR ID " << ID << " jacktrip client name set to=" <<
                      mJackClientName << std::endl;
Chris Chafe's avatar
Chris Chafe committed
168

Chris Chafe's avatar
src/  
Chris Chafe committed
169
170
#endif // endwhere

171
        mAudioInterface->setClientName(mJackClientName);
Chris Chafe's avatar
src/  
Chris Chafe committed
172
173

        if (gVerboseFlag) std::cout << "  JackTrip:setupAudio before mAudioInterface->setup" << std::endl;
174
175
176
177
        mAudioInterface->setup();
        mSampleRate = mAudioInterface->getSampleRate();
        mDeviceID = mAudioInterface->getDeviceID();
        mAudioBufferSize = mAudioInterface->getBufferSizeInSamples();
178
179
#endif //__NON_JACK__
#ifdef __NO_JACK__ /// \todo FIX THIS REPETITION OF CODE
180
#ifdef __RT_AUDIO__
181
182
183
184
185
186
        cout << "Warning: using non jack version, RtAudio will be used instead" << endl;
        mAudioInterface = new RtAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution);
        mAudioInterface->setSampleRate(mSampleRate);
        mAudioInterface->setDeviceID(mDeviceID);
        mAudioInterface->setBufferSizeInSamples(mAudioBufferSize);
        mAudioInterface->setup();
187
#endif
188
#endif
189
190
    }
    else if ( mAudiointerfaceMode == JackTrip::RTAUDIO ) {
191
#ifdef __RT_AUDIO__
192
193
194
195
196
        mAudioInterface = new RtAudioInterface(this, mNumChans, mNumChans, mAudioBitResolution);
        mAudioInterface->setSampleRate(mSampleRate);
        mAudioInterface->setDeviceID(mDeviceID);
        mAudioInterface->setBufferSizeInSamples(mAudioBufferSize);
        mAudioInterface->setup();
197
#endif
198
    }
199

200
201
202
203
204
205
206
207
208
209
210
211
    std::cout << "The Sampling Rate is: " << mSampleRate << std::endl;
    std::cout << gPrintSeparator << std::endl;
    int AudioBufferSizeInBytes = mAudioBufferSize*sizeof(sample_t);
    std::cout << "The Audio Buffer Size is: " << mAudioBufferSize << " samples" << std::endl;
    std::cout << "                      or: " << AudioBufferSizeInBytes
              << " bytes" << std::endl;
    std::cout << gPrintSeparator << std::endl;
    cout << "The Number of Channels is: " << mAudioInterface->getNumInputChannels() << endl;
    std::cout << gPrintSeparator << std::endl;
    cout << "The RTAudio device ID is: " << mAudioInterface->getDeviceID() << endl;
    std::cout << gPrintSeparator << std::endl;
    QThread::usleep(100);
jcaceres's avatar
jcaceres committed
212
213
214
}


215
//*******************************************************************************
216
void JackTrip::closeAudio()
217
{
218
219
220
221
222
223
    //mAudioInterface->close();
    if ( mAudioInterface != NULL ) {
        mAudioInterface->stopProcess();
        delete mAudioInterface;
        mAudioInterface = NULL;
    }
224
225
226
}


jcaceres's avatar
jcaceres committed
227
228
229
//*******************************************************************************
void JackTrip::setupDataProtocol()
{
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
    // Create DataProtocol Objects
    switch (mDataProtocol) {
    case UDP:
        std::cout << "Using UDP Protocol" << std::endl;
        std::cout << gPrintSeparator << std::endl;
        QThread::usleep(100);
        mDataProtocolSender = new UdpDataProtocol(this, DataProtocol::SENDER,
                                                  //mSenderPeerPort, mSenderBindPort,
                                                  mSenderBindPort, mSenderPeerPort,
                                                  mRedundancy);
        mDataProtocolReceiver =  new UdpDataProtocol(this, DataProtocol::RECEIVER,
                                                     mReceiverBindPort, mReceiverPeerPort,
                                                     mRedundancy);
        break;
    case TCP:
        throw std::invalid_argument("TCP Protocol is not implemented");
        break;
    case SCTP:
        throw std::invalid_argument("SCTP Protocol is not implemented");
        break;
    default:
        throw std::invalid_argument("Protocol not defined or unimplemented");
        break;
    }

    // Set Audio Packet Size
    //mDataProtocolSender->setAudioPacketSize
    //  (mAudioInterface->getSizeInBytesPerChannel() * mNumChans);
    //mDataProtocolReceiver->setAudioPacketSize
    //  (mAudioInterface->getSizeInBytesPerChannel() * mNumChans);
    mDataProtocolSender->setAudioPacketSize(getTotalAudioPacketSizeInBytes());
    mDataProtocolReceiver->setAudioPacketSize(getTotalAudioPacketSizeInBytes());
jcaceres's avatar
jcaceres committed
262
263
264
265
266
267
}


//*******************************************************************************
void JackTrip::setupRingBuffers()
{
268
269
270
271
272
273
274
275
276
277
278
279
    // Create RingBuffers with the apprioprate size
    /// \todo Make all this operations cleaner
    //int total_audio_packet_size = getTotalAudioPacketSizeInBytes();
    int slot_size = getRingBuffersSlotSize();

    switch (mUnderRunMode) {
    case WAVETABLE:
        mSendRingBuffer = new RingBufferWavetable(slot_size,
                                                  gDefaultOutputQueueLength);
        mReceiveRingBuffer = new RingBufferWavetable(slot_size,
                                                     mBufferQueueLength);
        /*
jcacerec's avatar
jcacerec committed
280
    mSendRingBuffer = new RingBufferWavetable(mAudioInterface->getSizeInBytesPerChannel() * mNumChans,
281
                gDefaultOutputQueueLength);
jcacerec's avatar
jcacerec committed
282
    mReceiveRingBuffer = new RingBufferWavetable(mAudioInterface->getSizeInBytesPerChannel() * mNumChans,
283
284
             mBufferQueueLength);
             */
285
286
287
288
289
290
291
292

        break;
    case ZEROS:
        mSendRingBuffer = new RingBuffer(slot_size,
                                         gDefaultOutputQueueLength);
        mReceiveRingBuffer = new RingBuffer(slot_size,
                                            mBufferQueueLength);
        /*
jcacerec's avatar
jcacerec committed
293
    mSendRingBuffer = new RingBuffer(mAudioInterface->getSizeInBytesPerChannel() * mNumChans,
294
             gDefaultOutputQueueLength);
jcacerec's avatar
jcacerec committed
295
    mReceiveRingBuffer = new RingBuffer(mAudioInterface->getSizeInBytesPerChannel() * mNumChans,
296
297
          mBufferQueueLength);
          */
298
299
300
301
302
        break;
    default:
        throw std::invalid_argument("Underrun Mode undefined");
        break;
    }
jcaceres's avatar
jcaceres committed
303
304
305
306
}


//*******************************************************************************
jcacerec's avatar
jcacerec committed
307
void JackTrip::setPeerAddress(const char* PeerHostOrIP)
jcaceres's avatar
jcaceres committed
308
{
309
    mPeerAddress = PeerHostOrIP;
jcaceres's avatar
jcaceres committed
310
311
312
313
}


//*******************************************************************************
jcacerec's avatar
jcacerec committed
314
void JackTrip::appendProcessPlugin(ProcessPlugin* plugin)
jcaceres's avatar
jcaceres committed
315
{
316
317
    mProcessPlugins.append(plugin);
    //mAudioInterface->appendProcessPlugin(plugin);
jcaceres's avatar
jcaceres committed
318
319
320
321
}


//*******************************************************************************
Chris Chafe's avatar
src/  
Chris Chafe committed
322
void JackTrip::startProcess(
Chris Chafe's avatar
Chris Chafe committed
323
        #ifdef WAIRTOMASTER // WAIR
Chris Chafe's avatar
src/  
Chris Chafe committed
324
325
        int ID
        #endif // endwhere
326
        )
327
328
329
{ //signal that catches ctrl c in rtaudio-asio mode
#if defined (__WIN_32__)
    if (signal(SIGINT, sigint_handler) == SIG_ERR) {
330
331
        perror("signal");
        exit(1);
332
    }
333
334
335
#endif
    // Check if ports are already binded by another process on this machine
    // ------------------------------------------------------------------
Chris Chafe's avatar
src/  
Chris Chafe committed
336
337
338
339
340
341
342
343
344
    if (gVerboseFlag) std::cout << "step 1" << std::endl;

    if (gVerboseFlag) std::cout << "  JackTrip:startProcess before checkIfPortIsBinded(mReceiverBindPort)" << std::endl;
#if defined __WIN_32__
    //cc fixed windows crash with this print statement!
    qDebug() << "before  mJackTrip->startProcess"
             << mReceiverBindPort<< mSenderBindPort;
    //        msleep(2000);
#endif
345
    checkIfPortIsBinded(mReceiverBindPort);
Chris Chafe's avatar
src/  
Chris Chafe committed
346
    if (gVerboseFlag) std::cout << "  JackTrip:startProcess before checkIfPortIsBinded(mSenderBindPort)" << std::endl;
347
348
349
    checkIfPortIsBinded(mSenderBindPort);
    // Set all classes and parameters
    // ------------------------------
Chris Chafe's avatar
src/  
Chris Chafe committed
350
351
    if (gVerboseFlag) std::cout << "  JackTrip:startProcess before setupAudio" << std::endl;
    setupAudio(
Chris Chafe's avatar
Chris Chafe committed
352
            #ifdef WAIRTOMASTER // wair
Chris Chafe's avatar
src/  
Chris Chafe committed
353
354
355
356
                ID
            #endif // endwhere
                );
    //cc redundant with instance creator  createHeader(mPacketHeaderType); next line fixme
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
    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 :
Chris Chafe's avatar
src/  
Chris Chafe committed
380
381
        if (gVerboseFlag) std::cout << "step 2c client only" << std::endl;
        if (gVerboseFlag) std::cout << "  JackTrip:startProcess case CLIENT before clientStart" << std::endl;
382
383
384
        clientStart();
        break;
    case SERVER :
Chris Chafe's avatar
src/  
Chris Chafe committed
385
386
        if (gVerboseFlag) std::cout << "step 2s server only" << std::endl;
        if (gVerboseFlag) std::cout << "  JackTrip:startProcess case SERVER before serverStart" << std::endl;
387
388
389
        serverStart();
        break;
    case CLIENTTOPINGSERVER :
Chris Chafe's avatar
src/  
Chris Chafe committed
390
391
        if (gVerboseFlag) std::cout << "step 2C client only" << std::endl;
        if (gVerboseFlag) std::cout << "  JackTrip:startProcess case CLIENTTOPINGSERVER before clientPingToServerStart" << std::endl;
392
393
394
395
396
397
398
        if ( clientPingToServerStart() == -1 ) { // if error on server start (-1) we return inmediatly
            mTcpConnectionError = true;
            slotStopProcesses();
            return;
        }
        break;
    case SERVERPINGSERVER :
Chris Chafe's avatar
src/  
Chris Chafe committed
399
400
        if (gVerboseFlag) std::cout << "step 2S server only (same as 2s)" << std::endl;
        if (gVerboseFlag) std::cout << "  JackTrip:startProcess case SERVERPINGSERVER before serverStart" << std::endl;
401
402
403
404
405
406
407
408
        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;
409
    }
410

Aaron Wyatt's avatar
Aaron Wyatt committed
411
412
413
414
415
416
417
418
    // Have the threads share a single socket that operates at full duplex.
#if defined (__WIN_32__)
    SOCKET sock_fd = INVALID_SOCKET;
#else
    int sock_fd = -1;
#endif
    mDataProtocolReceiver->setSocket(sock_fd);
    mDataProtocolSender->setSocket(sock_fd);
Aaron Wyatt's avatar
Aaron Wyatt committed
419

420
    // Start Threads
Chris Chafe's avatar
src/  
Chris Chafe committed
421
422
423
424
425
426
    if (gVerboseFlag) std::cout << "  JackTrip:startProcess before mDataProtocolReceiver->start" << std::endl;
    mDataProtocolReceiver->start();
    QThread::msleep(1);
    if (gVerboseFlag) std::cout << "  JackTrip:startProcess before mDataProtocolSender->start" << std::endl;
    mDataProtocolSender->start();
    /*
427
     * changed order so that audio starts after receiver and sender
428
     * because UdpDataProtocol:run0 before setRealtimeProcessPriority()
429
430
431
432
     * causes an audio hiccup from jack JackPosixSemaphore::TimedWait err = Interrupted system call
     * new QThread::msleep(1);
     * to allow sender to start
     */
Chris Chafe's avatar
src/  
Chris Chafe committed
433
434
435
    QThread::msleep(1);
    if (gVerboseFlag) std::cout << "step 5" << std::endl;
    if (gVerboseFlag) std::cout << "  JackTrip:startProcess before mAudioInterface->startProcess" << std::endl;
436
    mAudioInterface->startProcess();
437

438
439
440
441
    for (int i = 0; i < mProcessPlugins.size(); ++i) {
        mAudioInterface->appendProcessPlugin(mProcessPlugins[i]);
    }
    if (mConnectDefaultAudioPorts) {  mAudioInterface->connectDefaultPorts(); }
442
443
}

444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
//*******************************************************************************
void JackTrip::startIOStatTimer(int timeout_sec, const std::ostream& log_stream)
{
    mIOStatLogStream.rdbuf(log_stream.rdbuf());
    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(onStatTimer()));
    timer->start(timeout_sec*1000);
}

//*******************************************************************************
void JackTrip::onStatTimer()
{
    DataProtocol::PktStat pkt_stat;
    if (!mDataProtocolReceiver->getStats(&pkt_stat)) {
        return;
    }
    bool reset = (0 == pkt_stat.statCount);
    RingBuffer::IOStat recv_io_stat;
    if (!mReceiveRingBuffer->getStats(&recv_io_stat, reset)) {
        return;
    }
    RingBuffer::IOStat send_io_stat;
    if (!mSendRingBuffer->getStats(&send_io_stat, reset)) {
        return;
    }
    QString now = QDateTime::currentDateTime().toString(Qt::ISODate);
    int32_t skew = recv_io_stat.underruns - recv_io_stat.overflows
                - pkt_stat.lost + pkt_stat.revived;

    static QMutex mutex;
    QMutexLocker locker(&mutex);
    mIOStatLogStream << now.toLocal8Bit().constData()
      << " " << getPeerAddress().toLocal8Bit().constData()
      << " send: "
      << send_io_stat.underruns
      << "/" << send_io_stat.overflows
      << " recv: "
      << recv_io_stat.underruns
      << "/" << recv_io_stat.overflows
      << " prot: "
      << pkt_stat.lost
      << "/" << pkt_stat.outOfOrder
      << "/" << pkt_stat.revived
      << " tot: "
      << pkt_stat.tot
      << " skew: " << skew
      << endl;
}
492
493
494
495

//*******************************************************************************
void JackTrip::stop()
{
496
497
498
    // Stop The Sender
    mDataProtocolSender->stop();
    mDataProtocolSender->wait();
499

500
501
502
    // Stop The Receiver
    mDataProtocolReceiver->stop();
    mDataProtocolReceiver->wait();
503

504
505
506
    // Stop the audio processes
    //mAudioInterface->stopProcess();
    closeAudio();
507

508
509
    cout << "JackTrip Processes STOPPED!" << endl;
    cout << gPrintSeparator << endl;
510

511
512
    // Emit the jack stopped signal
    emit signalProcessesStopped();
jcaceres's avatar
jcaceres committed
513
514
}

515

jcacerec's avatar
jcacerec committed
516
//*******************************************************************************
517
void JackTrip::waitThreads()
jcacerec's avatar
jcacerec committed
518
{
519
520
    mDataProtocolSender->wait();
    mDataProtocolReceiver->wait();
jcacerec's avatar
jcacerec committed
521
}
jcaceres's avatar
jcaceres committed
522

523

jcaceres's avatar
jcaceres committed
524
//*******************************************************************************
525
void JackTrip::clientStart()
jcaceres's avatar
jcaceres committed
526
{
527
528
529
530
531
532
533
534
535
536
    // For the Client mode, the peer (or server) address has to be specified by the user
    if ( mPeerAddress.isEmpty() ) {
        throw std::invalid_argument("Peer Address has to be set if you run in CLIENT mode");
    }
    else {
        mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() );
        mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() );
        cout << "Peer Address set to: " << mPeerAddress.toStdString() << std::endl;
        cout << gPrintSeparator << endl;
    }
jcaceres's avatar
jcaceres committed
537
538
539
}


jcaceres's avatar
jcaceres committed
540
//*******************************************************************************
Chris Chafe's avatar
src/  
Chris Chafe committed
541
int JackTrip::serverStart(bool timeout, int udpTimeout) // udpTimeout unused
jcaceres's avatar
jcaceres committed
542
{
543
544
    // Set the peer address
    if ( !mPeerAddress.isEmpty() ) {
Chris Chafe's avatar
src/  
Chris Chafe committed
545
        if (gVerboseFlag) std::cout << "WARNING: SERVER mode: Peer Address was set but will be deleted." << endl;
546
547
548
549
        //throw std::invalid_argument("Peer Address has to be set if you run in CLIENT mode");
        mPeerAddress.clear();
        //return;
    }
550

551
552
553
    // Get the client address when it connects
    QHostAddress peerHostAddress;
    uint16_t peer_port;
Chris Chafe's avatar
src/  
Chris Chafe committed
554
    if (gVerboseFlag) std::cout << "JackTrip:serverStart before QUdpSocket UdpSockTemp" << std::endl;
555
    QUdpSocket UdpSockTemp;// Create socket to wait for client
jcacerec's avatar
jcacerec committed
556

Aaron Wyatt's avatar
Aaron Wyatt committed
557
    if (gVerboseFlag) std::cout << "JackTrip:serverStart before UdpSockTemp.bind(Any)" << std::endl;
558
    // Bind the socket
Aaron Wyatt's avatar
Aaron Wyatt committed
559
    if ( !UdpSockTemp.bind(QHostAddress::Any, mReceiverBindPort,
560
561
562
563
                           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.");
564
    }
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
    // Listen to client
    int sleepTime = 100; // ms
    int elapsedTime = 0;
    if (timeout) {
        while ( (!UdpSockTemp.hasPendingDatagrams()) && (elapsedTime <= udpTimeout) ) {
            if (mStopped == true) { emit signalUdpTimeOut(); UdpSockTemp.close(); return -1; }
            QThread::msleep(sleepTime);
            elapsedTime += sleepTime;
        }
        if (!UdpSockTemp.hasPendingDatagrams()) {
            emit signalUdpTimeOut();
            cout << "JackTrip Server Timed Out!" << endl;
            return -1;
        }
    } else {
Chris Chafe's avatar
src/  
Chris Chafe committed
580
581
        if (gVerboseFlag) std::cout << "JackTrip:serverStart before !UdpSockTemp.hasPendingDatagrams()" << std::endl;
        cout << "Waiting for Connection From a Client..." << endl;
582
583
        while ( !UdpSockTemp.hasPendingDatagrams() ) {
            if (mStopped == true) { emit signalUdpTimeOut(); return -1; }
Chris Chafe's avatar
src/  
Chris Chafe committed
584
            if (gVerboseFlag) std::cout << sleepTime << "ms  " << std::flush;
585
586
            QThread::msleep(sleepTime);
        }
587
    }
Chris Chafe's avatar
src/  
Chris Chafe committed
588
589
590
591
592
593
594
595
596
    //    char buf[1];
    //    // set client address
    //    UdpSockTemp.readDatagram(buf, 1, &peerHostAddress, &peer_port);
    //    UdpSockTemp.close(); // close the socket

    // IPv6 addition from fyfe
    // Get the datagram size to avoid problems with IPv6
    qint64 datagramSize = UdpSockTemp.pendingDatagramSize();
    char buf[datagramSize];
597
    // set client address
Chris Chafe's avatar
src/  
Chris Chafe committed
598
    UdpSockTemp.readDatagram(buf, datagramSize, &peerHostAddress, &peer_port);
599
    UdpSockTemp.close(); // close the socket
jcacerec's avatar
jcacerec committed
600

Chris Chafe's avatar
src/  
Chris Chafe committed
601
602
603
604
605
606
607
608
    // Check for mapped IPv4->IPv6 addresses that look like ::ffff:x.x.x.x
    if (peerHostAddress.protocol() == QAbstractSocket::IPv6Protocol) {
        bool mappedIPv4;
        uint32_t address = peerHostAddress.toIPv4Address(&mappedIPv4);
        // If the IPv4 address is mapped to IPv6, convert it to IPv4
        if (mappedIPv4) {
            QHostAddress ipv4Address = QHostAddress(address);
            mPeerAddress = ipv4Address.toString();
Aaron Wyatt's avatar
Aaron Wyatt committed
609
610
        } else {
            mPeerAddress = peerHostAddress.toString();
Chris Chafe's avatar
src/  
Chris Chafe committed
611
612
613
614
615
        }
    }
    else {
        mPeerAddress = peerHostAddress.toString();
    }
616

617
    // Set the peer address to send packets (in the protocol sender)
Chris Chafe's avatar
src/  
Chris Chafe committed
618
    if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolSender->setPeerAddress()" << std::endl;
619
    mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().constData() );
Chris Chafe's avatar
src/  
Chris Chafe committed
620
    if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolReceiver->setPeerAddress()" << std::endl;
621
    mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().constData() );
Chris Chafe's avatar
src/  
Chris Chafe committed
622
623
624
625
626
627
628
629
    //     We reply to the same port the peer sent the packets from
    //     This way we can go through NAT
    //     Because of the NAT traversal scheme, the portn need to be
    //     "symetric", e.g.:
    //     from Client to Server : src = 4474, dest = 4464
    //     from Server to Client : src = 4464, dest = 4474
    // no -- all are the same -- 4464
    if (gVerboseFlag) std::cout << "JackTrip:serverStart before setting all peer_port instances to " << peer_port << std::endl;
630
631
632
633
    mDataProtocolSender->setPeerPort(peer_port);
    mDataProtocolReceiver->setPeerPort(peer_port);
    setPeerPorts(peer_port);
    return 0;
634
}
jcacerec's avatar
jcacerec committed
635

636

jcacerec's avatar
jcacerec committed
637
//*******************************************************************************
638
int JackTrip::clientPingToServerStart()
jcacerec's avatar
jcacerec committed
639
{
640
641
642
643
644
645
646
647
648
649
    //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;
    }
650

Chris Chafe's avatar
src/  
Chris Chafe committed
651
    // Create Socket Objects
652
653
654
    // --------------------
    QTcpSocket tcpClient;
    QHostAddress serverHostAddress;
Aaron Wyatt's avatar
Aaron Wyatt committed
655
656
657
658
659
660
    if (!serverHostAddress.setAddress(mPeerAddress)) {
        QHostInfo info = QHostInfo::fromName(mPeerAddress);
        if (!info.addresses().isEmpty()) {
            // use the first IP address
            serverHostAddress = info.addresses().first();
        }
Aaron Wyatt's avatar
Aaron Wyatt committed
661
    }
662
663
664
665

    // Connect Socket to Server and wait for response
    // ----------------------------------------------
    tcpClient.connectToHost(serverHostAddress, mTcpServerPort);
Chris Chafe's avatar
src/  
Chris Chafe committed
666
    if (gVerboseFlag) cout << "Connecting to TCP Server..." << endl;
667
668
669
670
671
    if (!tcpClient.waitForConnected()) {
        std::cerr << "TCP Socket ERROR: " << tcpClient.errorString().toStdString() <<  endl;
        //std::exit(1);
        return -1;
    }
Chris Chafe's avatar
src/  
Chris Chafe committed
672
    if (gVerboseFlag) cout << "TCP Socket Connected to Server!" << endl;
673
    emit signalTcpClientConnected();
jcacerec's avatar
jcacerec committed
674

675
676
677
678
    // Send Client Port Number to Server
    // ---------------------------------
    char port_buf[sizeof(mReceiverBindPort)];
    std::memcpy(port_buf, &mReceiverBindPort, sizeof(mReceiverBindPort));
jcacerec's avatar
jcacerec committed
679

680
681
682
    tcpClient.write(port_buf, sizeof(mReceiverBindPort));
    while ( tcpClient.bytesToWrite() > 0 ) {
        tcpClient.waitForBytesWritten(-1);
683
    }
Chris Chafe's avatar
src/  
Chris Chafe committed
684
    if (gVerboseFlag) cout << "Port sent to Server" << endl;
685
686
687

    // Read the size of the package
    // ----------------------------
Chris Chafe's avatar
src/  
Chris Chafe committed
688
    if (gVerboseFlag) cout << "Reading UDP port from Server..." << endl;
689
690
691
692
693
694
695
    while (tcpClient.bytesAvailable() < (int)sizeof(uint16_t)) {
        if (!tcpClient.waitForReadyRead()) {
            std::cerr << "TCP Socket ERROR: " << tcpClient.errorString().toStdString() <<  endl;
            //std::exit(1);
            return -1;
        }
    }
Chris Chafe's avatar
src/  
Chris Chafe committed
696
    if (gVerboseFlag) cout << "Ready To Read From Socket!" << endl;
697
698
699
700
701
702
703
704
705
706
707
708
709
710

    // 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;
Chris Chafe's avatar
src/  
Chris Chafe committed
711
    if (gVerboseFlag) cout << "Connection Succesfull!" << endl;
712
713
714
715
716
717
718
719
720
721
722

    // 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;
723

724
    /*
jcacerec's avatar
jcacerec committed
725
726
727
728
729
  else {
    // Set the peer address
    mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() );
  }

730
731
  // Start the Sender Threads
  // ------------------------
jcacerec's avatar
jcacerec committed
732
  mAudioInterface->startProcess();
jcacerec's avatar
jcacerec committed
733
  mDataProtocolSender->start();
734
  // block until mDataProtocolSender thread starts
jcacerec's avatar
jcacerec committed
735
  while ( !mDataProtocolSender->isRunning() ) { QThread::msleep(100); }
jcacerec's avatar
jcacerec committed
736

737
738
  // Create a Socket to listen to Server's answer
  // --------------------------------------------
jcacerec's avatar
jcacerec committed
739
740
741
742
743
  QHostAddress serverHostAddress;
  QUdpSocket UdpSockTemp;// Create socket to wait for server answer
  uint16_t server_port;

  // Bind the socket
744
745
  //bindReceiveSocket(UdpSockTemp, mReceiverBindPort,
  //                  mPeerAddress, peer_port);
jcacerec's avatar
jcacerec committed
746
  if ( !UdpSockTemp.bind(QHostAddress::Any,
747
                         mReceiverBindPort,
748
                         QUdpSocket::ShareAddress) ) {
jcacerec's avatar
jcacerec committed
749
    //throw std::runtime_error("Could not bind PingToServer UDP socket. It may be already binded.");
jcacerec's avatar
jcacerec committed
750
  }
751

jcacerec's avatar
jcacerec committed
752
753
  // Listen to server response
  cout << "Waiting for server response..." << endl;
754
  while ( !UdpSockTemp.hasPendingDatagrams() ) { QThread::msleep(100); }
jcacerec's avatar
jcacerec committed
755
756
757
758
759
760
761
762
763
  cout << "Received response from server!" << endl;
  char buf[1];
  // set client address
  UdpSockTemp.readDatagram(buf, 1, &serverHostAddress, &server_port);
  UdpSockTemp.close(); // close the socket

  // Stop the sender thread to change server port
  mDataProtocolSender->stop();
  mDataProtocolSender->wait(); // Wait for the thread to terminate
764
765

  cout << "Server port now set to: " << server_port << endl;
jcacerec's avatar
jcacerec committed
766
  cout << gPrintSeparator << endl;
767
  mDataProtocolSender->setPeerPort(server_port);
768

jcacerec's avatar
jcacerec committed
769
  // Start Threads
jcacerec's avatar
jcacerec committed
770
  //mAudioInterface->connectDefaultPorts();
jcacerec's avatar
jcacerec committed
771
772
  mDataProtocolSender->start();
  mDataProtocolReceiver->start();
773
774
775
776
777
  */
}


//*******************************************************************************
jcacerec's avatar
jcacerec committed
778
/*
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
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;
jcacerec's avatar
jcacerec committed
830
}
jcacerec's avatar
jcacerec committed
831
*/
jcacerec's avatar
jcacerec committed
832

jcacerec's avatar
jcacerec committed
833

834
//*******************************************************************************
jcacerec's avatar
jcacerec committed
835
836
void JackTrip::createHeader(const DataProtocol::packetHeaderTypeT headertype)
{
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
    delete mPacketHeader; //Just in case it has already been allocated
    switch (headertype) {
    case DataProtocol::DEFAULT :
        mPacketHeader = new DefaultHeader(this);
        break;
    case DataProtocol::JAMLINK :
        mPacketHeader = new JamLinkHeader(this);
        break;
    case DataProtocol::EMPTY :
        mPacketHeader = new EmptyHeader(this);
        break;
    default :
        throw std::invalid_argument("Undefined Header Type");
        break;
    }
jcacerec's avatar
jcacerec committed
852
853
854
}


855
856
//*******************************************************************************
void JackTrip::putHeaderInPacket(int8_t* full_packet, int8_t* audio_packet)
jcacerec's avatar
jcacerec committed
857
{
858
859
860
861
862
863
864
865
    mPacketHeader->fillHeaderCommonFromAudio();
    mPacketHeader->putHeaderInPacket(full_packet);

    int8_t* audio_part;
    audio_part = full_packet + mPacketHeader->getHeaderSizeInBytes();
    //std::memcpy(audio_part, audio_packet, mAudioInterface->getBufferSizeInBytes());
    //std::memcpy(audio_part, audio_packet, mAudioInterface->getSizeInBytesPerChannel() * mNumChans);
    std::memcpy(audio_part, audio_packet, getTotalAudioPacketSizeInBytes());
866
867
868
869
}


//*******************************************************************************
jcacerec's avatar
jcacerec committed
870
int JackTrip::getPacketSizeInBytes()
871
{
872
873
874
875
876
    //return (mAudioInterface->getBufferSizeInBytes() + mPacketHeader->getHeaderSizeInBytes());
    //return (mAudioInterface->getSizeInBytesPerChannel() * mNumChans  +
    //mPacketHeader->getHeaderSizeInBytes());
    return (getTotalAudioPacketSizeInBytes()  +
            mPacketHeader->getHeaderSizeInBytes());
jcacerec's avatar
jcacerec committed
877
878
}

879
880
881
882

//*******************************************************************************
void JackTrip::parseAudioPacket(int8_t* full_packet, int8_t* audio_packet)
{
883
884
885
886
887
    int8_t* audio_part;
    audio_part = full_packet + mPacketHeader->getHeaderSizeInBytes();
    //std::memcpy(audio_packet, audio_part, mAudioInterface->getBufferSizeInBytes());
    //std::memcpy(audio_packet, audio_part, mAudioInterface->getSizeInBytesPerChannel() * mNumChans);
    std::memcpy(audio_packet, audio_part, getTotalAudioPacketSizeInBytes());
888
}
jcacerec's avatar
jcacerec committed
889
890
891
892
893


//*******************************************************************************
void JackTrip::checkPeerSettings(int8_t* full_packet)
{
894
    mPacketHeader->checkPeerSettings(full_packet);
jcacerec's avatar
jcacerec committed
895
}
896
897
898
899
900


//*******************************************************************************
void JackTrip::checkIfPortIsBinded(int port)
{
901
902
    QUdpSocket UdpSockTemp;// Create socket to wait for client
    // Bind the socket
Chris Chafe's avatar
src/  
Chris Chafe committed
903
904
905
    //cc        if ( !UdpSockTemp.bind(QHostAddress::AnyIPv4, port, QUdpSocket::DontShareAddress) )
    if ( !UdpSockTemp.bind(QHostAddress::Any, port,
                           QUdpSocket::DontShareAddress) )
906
907
908
909
910
    {
        UdpSockTemp.close(); // close the socket
        throw std::runtime_error(
                    "Could not bind UDP socket. It may already be binded by another process on your machine. Try using a different port number");
    }
911
912
    UdpSockTemp.close(); // close the socket
}