JackTrip.cpp 34.8 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
109
110
111
112
113
    mBufferQueueLength(BufferQueueLength),
    mSampleRate(gDefaultSampleRate),
    mDeviceID(gDefaultDeviceID),
    mAudioBufferSize(gDefaultBufferSizeInSamples),
    mAudioBitResolution(AudioBitResolution),
    mDataProtocolSender(NULL),
    mDataProtocolReceiver(NULL),
    mAudioInterface(NULL),
    mPacketHeader(NULL),
    mUnderRunMode(UnderRunMode),
    mSendRingBuffer(NULL),
    mReceiveRingBuffer(NULL),
    mReceiverBindPort(receiver_bind_port),
    mSenderPeerPort(sender_peer_port),
    mSenderBindPort(sender_bind_port),
    mReceiverPeerPort(receiver_peer_port),
    mTcpServerPort(4464),
    mRedundancy(redundancy),
    mJackClientName("JackTrip"),
    mConnectionMode(JackTrip::NORMAL),
    mReceivedConnection(false),
    mTcpConnectionError(false),
    mStopped(false),
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
        QByteArray tmp = QString(mPeerAddress).replace(":", ".").toLatin1();
163
164
        if(mPeerAddress.toStdString()!="")
            mJackClientName = tmp.constData();
165
166
        std::cout  << "WAIR ID " << ID << " jacktrip client name set to=" <<
                      mJackClientName << std::endl;
Chris Chafe's avatar
Chris Chafe committed
167

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

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

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

199
200
201
202
203
204
205
206
207
208
209
210
    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
211
212
213
}


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


jcaceres's avatar
jcaceres committed
226
227
228
//*******************************************************************************
void JackTrip::setupDataProtocol()
{
229
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
    // 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
261
262
263
264
265
266
}


//*******************************************************************************
void JackTrip::setupRingBuffers()
{
267
268
269
270
271
272
273
274
275
276
277
278
    // 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
279
    mSendRingBuffer = new RingBufferWavetable(mAudioInterface->getSizeInBytesPerChannel() * mNumChans,
280
                gDefaultOutputQueueLength);
jcacerec's avatar
jcacerec committed
281
    mReceiveRingBuffer = new RingBufferWavetable(mAudioInterface->getSizeInBytesPerChannel() * mNumChans,
282
283
             mBufferQueueLength);
             */
284
285
286
287
288
289
290
291

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


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


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


//*******************************************************************************
Chris Chafe's avatar
src/  
Chris Chafe committed
321
void JackTrip::startProcess(
Chris Chafe's avatar
Chris Chafe committed
322
        #ifdef WAIRTOMASTER // WAIR
Chris Chafe's avatar
src/  
Chris Chafe committed
323
324
        int ID
        #endif // endwhere
325
        )
326
327
328
{ //signal that catches ctrl c in rtaudio-asio mode
#if defined (__WIN_32__)
    if (signal(SIGINT, sigint_handler) == SIG_ERR) {
329
330
        perror("signal");
        exit(1);
331
    }
332
333
334
#endif
    // Check if ports are already binded by another process on this machine
    // ------------------------------------------------------------------
Chris Chafe's avatar
src/  
Chris Chafe committed
335
336
337
338
339
340
341
342
343
    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
344
    checkIfPortIsBinded(mReceiverBindPort);
Chris Chafe's avatar
src/  
Chris Chafe committed
345
    if (gVerboseFlag) std::cout << "  JackTrip:startProcess before checkIfPortIsBinded(mSenderBindPort)" << std::endl;
346
347
348
    checkIfPortIsBinded(mSenderBindPort);
    // Set all classes and parameters
    // ------------------------------
Chris Chafe's avatar
src/  
Chris Chafe committed
349
350
    if (gVerboseFlag) std::cout << "  JackTrip:startProcess before setupAudio" << std::endl;
    setupAudio(
Chris Chafe's avatar
Chris Chafe committed
351
            #ifdef WAIRTOMASTER // wair
Chris Chafe's avatar
src/  
Chris Chafe committed
352
353
354
355
                ID
            #endif // endwhere
                );
    //cc redundant with instance creator  createHeader(mPacketHeaderType); next line fixme
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
    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
379
380
        if (gVerboseFlag) std::cout << "step 2c client only" << std::endl;
        if (gVerboseFlag) std::cout << "  JackTrip:startProcess case CLIENT before clientStart" << std::endl;
381
382
383
        clientStart();
        break;
    case SERVER :
Chris Chafe's avatar
src/  
Chris Chafe committed
384
385
        if (gVerboseFlag) std::cout << "step 2s server only" << std::endl;
        if (gVerboseFlag) std::cout << "  JackTrip:startProcess case SERVER before serverStart" << std::endl;
386
387
388
        serverStart();
        break;
    case CLIENTTOPINGSERVER :
Chris Chafe's avatar
src/  
Chris Chafe committed
389
390
        if (gVerboseFlag) std::cout << "step 2C client only" << std::endl;
        if (gVerboseFlag) std::cout << "  JackTrip:startProcess case CLIENTTOPINGSERVER before clientPingToServerStart" << std::endl;
391
392
393
394
395
396
397
        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
398
399
        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;
400
401
402
403
404
405
406
407
        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;
408
    }
409

Aaron Wyatt's avatar
Aaron Wyatt committed
410
411
412
413
414
415
416
417
    // 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
418

419
    // Start Threads
Chris Chafe's avatar
src/  
Chris Chafe committed
420
421
422
423
424
425
    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();
    /*
426
     * changed order so that audio starts after receiver and sender
427
     * because UdpDataProtocol:run0 before setRealtimeProcessPriority()
428
429
430
431
     * 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
432
433
434
    QThread::msleep(1);
    if (gVerboseFlag) std::cout << "step 5" << std::endl;
    if (gVerboseFlag) std::cout << "  JackTrip:startProcess before mAudioInterface->startProcess" << std::endl;
435
    mAudioInterface->startProcess();
436

437
438
439
440
    for (int i = 0; i < mProcessPlugins.size(); ++i) {
        mAudioInterface->appendProcessPlugin(mProcessPlugins[i]);
    }
    if (mConnectDefaultAudioPorts) {  mAudioInterface->connectDefaultPorts(); }
441
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
//*******************************************************************************
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;
}
491
492
493
494

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

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

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

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

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

514

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

522

jcaceres's avatar
jcaceres committed
523
//*******************************************************************************
524
void JackTrip::clientStart()
jcaceres's avatar
jcaceres committed
525
{
526
527
528
529
530
531
532
533
534
535
    // 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
536
537
538
}


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

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

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

Chris Chafe's avatar
src/  
Chris Chafe committed
600
601
602
603
604
605
606
607
    // 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
608
609
        } else {
            mPeerAddress = peerHostAddress.toString();
Chris Chafe's avatar
src/  
Chris Chafe committed
610
611
612
613
614
        }
    }
    else {
        mPeerAddress = peerHostAddress.toString();
    }
615

616
    // Set the peer address to send packets (in the protocol sender)
Chris Chafe's avatar
src/  
Chris Chafe committed
617
    if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolSender->setPeerAddress()" << std::endl;
618
    mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().constData() );
Chris Chafe's avatar
src/  
Chris Chafe committed
619
    if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolReceiver->setPeerAddress()" << std::endl;
620
    mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().constData() );
Chris Chafe's avatar
src/  
Chris Chafe committed
621
622
623
624
625
626
627
628
    //     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;
629
630
631
632
    mDataProtocolSender->setPeerPort(peer_port);
    mDataProtocolReceiver->setPeerPort(peer_port);
    setPeerPorts(peer_port);
    return 0;
633
}
jcacerec's avatar
jcacerec committed
634

635

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

Chris Chafe's avatar
src/  
Chris Chafe committed
650
    // Create Socket Objects
651
652
653
    // --------------------
    QTcpSocket tcpClient;
    QHostAddress serverHostAddress;
Aaron Wyatt's avatar
Aaron Wyatt committed
654
655
656
657
658
659
    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
660
    }
661
662
663
664

    // Connect Socket to Server and wait for response
    // ----------------------------------------------
    tcpClient.connectToHost(serverHostAddress, mTcpServerPort);
Chris Chafe's avatar
src/  
Chris Chafe committed
665
    if (gVerboseFlag) cout << "Connecting to TCP Server..." << endl;
666
667
668
669
670
    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
671
    if (gVerboseFlag) cout << "TCP Socket Connected to Server!" << endl;
672
    emit signalTcpClientConnected();
jcacerec's avatar
jcacerec committed
673

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

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

    // Read the size of the package
    // ----------------------------
Chris Chafe's avatar
src/  
Chris Chafe committed
687
    if (gVerboseFlag) cout << "Reading UDP port from Server..." << endl;
688
689
690
691
692
693
694
    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
695
    if (gVerboseFlag) cout << "Ready To Read From Socket!" << endl;
696
697
698
699
700
701
702
703
704
705
706
707
708
709

    // 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
710
    if (gVerboseFlag) cout << "Connection Succesfull!" << endl;
711
712
713
714
715
716
717
718
719
720
721

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

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

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

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

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

jcacerec's avatar
jcacerec committed
751
752
  // Listen to server response
  cout << "Waiting for server response..." << endl;
753
  while ( !UdpSockTemp.hasPendingDatagrams() ) { QThread::msleep(100); }
jcacerec's avatar
jcacerec committed
754
755
756
757
758
759
760
761
762
  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
763
764

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

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


//*******************************************************************************
jcacerec's avatar
jcacerec committed
777
/*
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
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
829
}
jcacerec's avatar
jcacerec committed
830
*/
jcacerec's avatar
jcacerec committed
831

jcacerec's avatar
jcacerec committed
832

833
//*******************************************************************************
jcacerec's avatar
jcacerec committed
834
835
void JackTrip::createHeader(const DataProtocol::packetHeaderTypeT headertype)
{
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
    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
851
852
853
}


854
855
//*******************************************************************************
void JackTrip::putHeaderInPacket(int8_t* full_packet, int8_t* audio_packet)
jcacerec's avatar
jcacerec committed
856
{
857
858
859
860
861
862
863
864
    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());
865
866
867
868
}


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

878
879
880
881

//*******************************************************************************
void JackTrip::parseAudioPacket(int8_t* full_packet, int8_t* audio_packet)
{
882
883
884
885
886
    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());
887
}
jcacerec's avatar
jcacerec committed
888
889
890
891
892


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


//*******************************************************************************
void JackTrip::checkIfPortIsBinded(int port)
{
900
901
    QUdpSocket UdpSockTemp;// Create socket to wait for client
    // Bind the socket
Chris Chafe's avatar
src/  
Chris Chafe committed
902
903
904
    //cc        if ( !UdpSockTemp.bind(QHostAddress::AnyIPv4, port, QUdpSocket::DontShareAddress) )
    if ( !UdpSockTemp.bind(QHostAddress::Any, port,
                           QUdpSocket::DontShareAddress) )
905
906
907
908
909
    {
        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");
    }
910
911
    UdpSockTemp.close(); // close the socket
}