Browse Source

First import

git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@1195 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.58
sletz 18 years ago
parent
commit
73e2d7a6d3
100 changed files with 19631 additions and 0 deletions
  1. +1114
    -0
      common/JackAPI.cpp
  2. +43
    -0
      common/JackActivationCount.cpp
  3. +83
    -0
      common/JackActivationCount.h
  4. +197
    -0
      common/JackAtomic.h
  5. +235
    -0
      common/JackAtomicArrayState.h
  6. +240
    -0
      common/JackAtomicState.h
  7. +229
    -0
      common/JackAudioDriver.cpp
  8. +88
    -0
      common/JackAudioDriver.h
  9. +210
    -0
      common/JackChannel.h
  10. +48
    -0
      common/JackChannelTransaction.h
  11. +767
    -0
      common/JackClient.cpp
  12. +162
    -0
      common/JackClient.h
  13. +76
    -0
      common/JackClientControl.h
  14. +56
    -0
      common/JackClientInterface.h
  15. +452
    -0
      common/JackConnectionManager.cpp
  16. +468
    -0
      common/JackConnectionManager.h
  17. +64
    -0
      common/JackConstants.h
  18. +457
    -0
      common/JackDebugClient.cpp
  19. +127
    -0
      common/JackDebugClient.h
  20. +204
    -0
      common/JackDriver.cpp
  21. +215
    -0
      common/JackDriver.h
  22. +498
    -0
      common/JackDriverLoader.cpp
  23. +66
    -0
      common/JackDriverLoader.h
  24. +224
    -0
      common/JackDummyDriver.cpp
  25. +68
    -0
      common/JackDummyDriver.h
  26. +644
    -0
      common/JackEngine.cpp
  27. +119
    -0
      common/JackEngine.h
  28. +58
    -0
      common/JackEngineControl.h
  29. +229
    -0
      common/JackEngineTiming.cpp
  30. +105
    -0
      common/JackEngineTiming.h
  31. +48
    -0
      common/JackError.c
  32. +48
    -0
      common/JackError.h
  33. +30
    -0
      common/JackExports.h
  34. +83
    -0
      common/JackExternalClient.cpp
  35. +60
    -0
      common/JackExternalClient.h
  36. +209
    -0
      common/JackFifo.cpp
  37. +73
    -0
      common/JackFifo.h
  38. +109
    -0
      common/JackFrameTimer.cpp
  39. +81
    -0
      common/JackFrameTimer.h
  40. +47
    -0
      common/JackFreewheelDriver.cpp
  41. +54
    -0
      common/JackFreewheelDriver.h
  42. +28
    -0
      common/JackGlobals.cpp
  43. +282
    -0
      common/JackGlobals.h
  44. +460
    -0
      common/JackGlobalsClient.cpp
  45. +460
    -0
      common/JackGlobalsServer.cpp
  46. +777
    -0
      common/JackGraphManager.cpp
  47. +121
    -0
      common/JackGraphManager.h
  48. +105
    -0
      common/JackInternalClient.cpp
  49. +60
    -0
      common/JackInternalClient.h
  50. +116
    -0
      common/JackInternalClientChannel.h
  51. +133
    -0
      common/JackLibAPI.cpp
  52. +162
    -0
      common/JackLibClient.cpp
  53. +60
    -0
      common/JackLibClient.h
  54. +97
    -0
      common/JackLibGlobals.h
  55. +209
    -0
      common/JackLoopbackDriver.cpp
  56. +63
    -0
      common/JackLoopbackDriver.h
  57. +229
    -0
      common/JackPort.cpp
  58. +98
    -0
      common/JackPort.h
  59. +205
    -0
      common/JackPosixSemaphore.cpp
  60. +71
    -0
      common/JackPosixSemaphore.h
  61. +209
    -0
      common/JackPosixThread.cpp
  62. +73
    -0
      common/JackPosixThread.h
  63. +180
    -0
      common/JackProcessSync.h
  64. +207
    -0
      common/JackPthreadCond.cpp
  65. +133
    -0
      common/JackPthreadCond.h
  66. +612
    -0
      common/JackRequest.h
  67. +339
    -0
      common/JackServer.cpp
  68. +96
    -0
      common/JackServer.h
  69. +174
    -0
      common/JackServerAPI.cpp
  70. +389
    -0
      common/JackServerGlobals.cpp
  71. +58
    -0
      common/JackServerGlobals.h
  72. +92
    -0
      common/JackShmMem.cpp
  73. +308
    -0
      common/JackShmMem.h
  74. +312
    -0
      common/JackSocket.cpp
  75. +104
    -0
      common/JackSocket.h
  76. +264
    -0
      common/JackSocketClientChannel.cpp
  77. +89
    -0
      common/JackSocketClientChannel.h
  78. +79
    -0
      common/JackSocketNotifyChannel.cpp
  79. +56
    -0
      common/JackSocketNotifyChannel.h
  80. +375
    -0
      common/JackSocketServerChannel.cpp
  81. +70
    -0
      common/JackSocketServerChannel.h
  82. +59
    -0
      common/JackSocketServerNotifyChannel.cpp
  83. +55
    -0
      common/JackSocketServerNotifyChannel.h
  84. +51
    -0
      common/JackSyncInterface.h
  85. +102
    -0
      common/JackSynchro.h
  86. +97
    -0
      common/JackThread.h
  87. +89
    -0
      common/JackThreadedDriver.cpp
  88. +154
    -0
      common/JackThreadedDriver.h
  89. +121
    -0
      common/JackTime.c
  90. +111
    -0
      common/JackTime.h
  91. +259
    -0
      common/JackTransportEngine.cpp
  92. +137
    -0
      common/JackTransportEngine.h
  93. +36
    -0
      common/JackTypes.h
  94. +569
    -0
      common/Jackdmp.cpp
  95. +94
    -0
      common/cycles.h
  96. +97
    -0
      common/driver_interface.h
  97. +33
    -0
      common/driver_parse.h
  98. +132
    -0
      common/intclient.h
  99. +805
    -0
      common/jack.h
  100. +287
    -0
      common/jslist.h

+ 1114
- 0
common/JackAPI.cpp
File diff suppressed because it is too large
View File


+ 43
- 0
common/JackActivationCount.cpp View File

@@ -0,0 +1,43 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackAtomic.h"
#include "JackActivationCount.h"
#include "JackConstants.h"
#include "JackClientControl.h"
#include "JackError.h"

namespace Jack
{

bool JackActivationCount::Signal(JackSynchro* synchro, JackClientControl* control)
{
if (fValue == 0) {
// Transfer activation to next clients
jack_error("JackActivationCount::Signal value = 0 ref = %ld", control->fRefNum);
return synchro->Signal();
} else if (DEC_ATOMIC(&fValue) == 1) {
return synchro->Signal();
} else {
return true;
}
}

} // end of namespace


+ 83
- 0
common/JackActivationCount.h View File

@@ -0,0 +1,83 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackActivationCount__
#define __JackActivationCount__

#include "JackSynchro.h"
#include "JackTime.h"

namespace Jack
{

struct JackClientControl;

/*!
\brief Client activation counter.
*/

class JackActivationCount
{

private:

long fValue;
long fCount;

public:

JackActivationCount(): fValue(0), fCount(0)
{}
virtual ~JackActivationCount()
{}

bool Signal(JackSynchro* synchro, JackClientControl* control);

inline void Reset()
{
fValue = fCount;
}

inline void SetValue(int val)
{
fCount = val;
}

inline void IncValue()
{
fCount++;
}

inline void DecValue()
{
fCount--;
}

inline int GetValue() const
{
return fValue;
}

};

} // end of namespace


#endif


+ 197
- 0
common/JackAtomic.h View File

@@ -0,0 +1,197 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackAtomic__
#define __JackAtomic__

typedef unsigned short UInt16;
typedef unsigned long UInt32;
typedef long SInt32;
#ifdef WIN32
#include <windows.h>

typedef ULONGLONG UInt64;
#else

typedef unsigned long long UInt64;
#endif

#if defined(__APPLE__)

#if defined(__ppc__)

static inline int CAS(register UInt32 value, register UInt32 newvalue, register volatile void* addr)
{
register int result;
asm volatile (
"# CAS \n"
" lwarx r0, 0, %1 \n" // creates a reservation on addr
" cmpw r0, %2 \n" // test value at addr
" bne- 1f \n"
" sync \n" // synchronize instructions
" stwcx. %3, 0, %1 \n" // if the reservation is not altered
// stores the new value at addr
" bne- 1f \n"
" li %0, 1 \n"
" b 2f \n"
"1: \n"
" li %0, 0 \n"
"2: \n"
: "=r" (result)
: "r" (addr), "r" (value), "r" (newvalue)
: "r0"
);
return result;
}

#endif

#if defined(__i386__) || defined(__x86_64__)

#ifdef __SMP__
# define LOCK "lock ; "
#else
# define LOCK ""
#endif

static inline char CAS(volatile UInt32 value, UInt32 newvalue, volatile void* addr)
{
register char ret;
__asm__ __volatile__ (
"# CAS \n\t"
LOCK "cmpxchg %2, (%1) \n\t"
"sete %0 \n\t"
: "=a" (ret)
: "c" (addr), "d" (newvalue), "a" (value)
);
return ret;
}

#endif

#endif

#ifdef __linux__

#ifdef __PPC__

static inline int CAS(register UInt32 value, register UInt32 newvalue, register volatile void* addr)
{
register int result;
register UInt32 tmp;
asm volatile (
"# CAS \n"
" lwarx %4, 0, %1 \n" // creates a reservation on addr
" cmpw %4, %2 \n" // test value at addr
" bne- 1f \n"
" sync \n" // synchronize instructions
" stwcx. %3, 0, %1 \n" // if the reservation is not altered
// stores the new value at addr
" bne- 1f \n"
" li %0, 1 \n"
" b 2f \n"
"1: \n"
" li %0, 0 \n"
"2: \n"
: "=r" (result)
: "r" (addr), "r" (value), "r" (newvalue), "r" (tmp)
);
return result;
}

#endif

#if defined(__i386__) || defined(__x86_64__)

#ifdef __SMP__
# define LOCK "lock ; "
#else
# define LOCK ""
#endif

static inline char CAS(volatile UInt32 value, UInt32 newvalue, volatile void* addr)
{
register char ret;
__asm__ __volatile__ (
"# CAS \n\t"
LOCK "cmpxchg %2, (%1) \n\t"
"sete %0 \n\t"
: "=a" (ret)
: "c" (addr), "d" (newvalue), "a" (value)
);
return ret;
}

#endif

#endif

#ifdef WIN32

#ifdef __SMP__
# define LOCK lock
#else
# define LOCK
#endif

#define inline __inline

//----------------------------------------------------------------
// CAS functions
//----------------------------------------------------------------
inline char CAS (volatile UInt32 value, UInt32 newvalue, volatile void * addr)
{
register char c;
__asm {
push ebx
push esi
mov esi, addr
mov eax, value
mov ebx, newvalue
LOCK cmpxchg dword ptr [esi], ebx
sete c
pop esi
pop ebx
}
return c;
}

#endif

static inline long INC_ATOMIC(volatile SInt32* val)
{
SInt32 actual;
do {
actual = *val;
} while (!CAS(actual, actual + 1, val));
return actual;
}

static inline long DEC_ATOMIC(volatile SInt32* val)
{
SInt32 actual;
do {
actual = *val;
} while (!CAS(actual, actual - 1, val));
return actual;
}

#endif



+ 235
- 0
common/JackAtomicArrayState.h View File

@@ -0,0 +1,235 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackAtomicArrayState__
#define __JackAtomicArrayState__

#include "JackAtomic.h"
#include "JackError.h"
#include <string.h> // for memcpy

namespace Jack
{

/*!
\brief Counter for CAS
*/

struct AtomicArrayCounter
{
union {
struct {
unsigned char fByteVal[4];
}
scounter;
UInt32 fLongVal;
}info;

AtomicArrayCounter& operator=(volatile AtomicArrayCounter& obj)
{
info.fLongVal = obj.info.fLongVal;
return *this;
}
};

#define Counter1(e) (e).info.fLongVal
#define GetIndex1(e, state) ((e).info.scounter.fByteVal[state])
#define SetIndex1(e, state, val) ((e).info.scounter.fByteVal[state] = val)
#define IncIndex1(e, state) ((e).info.scounter.fByteVal[state]++)
#define SwapIndex1(e, state) (((e).info.scounter.fByteVal[0] == state) ? 0 : state)

/*!
\brief A class to handle serveral states in a lock-free manner
Requirement:
- a "current" state
- several possible "pending" state
- an TrySwitchState(int state) operation to atomically switch a "pending" to the "current" state (the pending becomes the current).
The TrySwitchState operation returns a "current" state (either the same if switch fails or the new one; one can know if the switch has succeeded)
- a WriteStartState(int state) returns a "pending" state to be written into
- a WriteStartStop(int state) make the written "pending" state become "switchable"
Different pending states can be written independantly and concurently.
GetCurrentIndex() *must* return an increasing value to be able to check reading current state coherency
The fCounter is an array of indexes to access te current and 3 different "pending" states.
ÂĄ WriteNextStateStart(int index) must return a valid state to be written into, and must invalidate state "index" ==> cur state switch.
ÂĄ WriteNextStateStop(int index) makes the "index" state become "switchable" with the current state.
ÂĄ TrySwitchState(int index) must detect that pending state is a new state, and does the switch
ÂĄ ReadCurrentState() must return the state
ÂĄ GetCurrentIndex() must return an index increased each new switch.
ÂĄ WriteNextStateStart(int index1) and WriteNextStateStart(int index2) can be interleaved
[switch counter][index state][index state][cur index]
*/

// CHECK livelock

template <class T>
class JackAtomicArrayState
{

protected:

// fState[0] ==> current
// fState[1] ==> pending
// fState[2] ==> request

T fState[3];
volatile AtomicArrayCounter fCounter;

UInt32 WriteNextStateStartAux(int state, bool* result)
{
AtomicArrayCounter old_val;
AtomicArrayCounter new_val;
UInt32 cur_index;
UInt32 next_index;
bool need_copy;
do {
old_val = fCounter;
new_val = old_val;
*result = GetIndex1(new_val, state);
cur_index = GetIndex1(new_val, 0);
next_index = SwapIndex1(fCounter, state);
need_copy = (GetIndex1(new_val, state) == 0); // Written = false, switch just occured
SetIndex1(new_val, state, 0); // Written = false, invalidate state
} while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
if (need_copy)
memcpy(&fState[next_index], &fState[cur_index], sizeof(T));
return next_index;
}

void WriteNextStateStopAux(int state)
{
AtomicArrayCounter old_val;
AtomicArrayCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
SetIndex1(new_val, state, 1); // Written = true, state becomes "switchable"
} while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
}

public:

JackAtomicArrayState()
{
JackLog("JackAtomicArrayState constructor\n");
Counter1(fCounter) = 0;
}

~JackAtomicArrayState() // Not virtual ??
{}

/*!
\brief Returns the current state : only valid in the RT reader thread
*/

T* ReadCurrentState()
{
return &fState[GetIndex1(fCounter, 0)];
}

/*!
\brief Returns the current switch counter
*/

UInt16 GetCurrentIndex()
{
return GetIndex1(fCounter, 3);
}

/*!
\brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one)
*/

T* TrySwitchState(int state)
{
AtomicArrayCounter old_val;
AtomicArrayCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
if (GetIndex1(new_val, state)) { // If state has been written
SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch
SetIndex1(new_val, state, 0); // Invalidate the state "state"
IncIndex1(new_val, 3); // Inc switch
}
} while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
return &fState[GetIndex1(fCounter, 0)]; // Read the counter again
}

/*!
\brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one)
*/

T* TrySwitchState(int state, bool* result)
{
AtomicArrayCounter old_val;
AtomicArrayCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
if ((*result = GetIndex1(new_val, state))) { // If state has been written
SetIndex1(new_val, 0, SwapIndex1(new_val, state)); // Prepare switch
SetIndex1(new_val, state, 0); // Invalidate the state "state"
IncIndex1(new_val, 3); // Inc switch
}
} while (!CAS(Counter1(old_val), Counter1(new_val), (UInt32*)&fCounter));
return &fState[GetIndex1(fCounter, 0)]; // Read the counter again
}

/*!
\brief Start write operation : setup and returns the next state to update, check for recursive write calls.
*/

T* WriteNextStateStart(int state)
{
bool tmp;
UInt32 index = WriteNextStateStartAux(state, &tmp);
return &fState[index];
}

T* WriteNextStateStart(int state, bool* result)
{
UInt32 index = WriteNextStateStartAux(state, result);
return &fState[index];
}

/*!
\brief Stop write operation : make the next state ready to be used by the RT thread
*/
void WriteNextStateStop(int state)
{
WriteNextStateStopAux(state);
}

};

} // end of namespace


#endif


+ 240
- 0
common/JackAtomicState.h View File

@@ -0,0 +1,240 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackAtomicState__
#define __JackAtomicState__

#include "JackAtomic.h"
#include <string.h> // for memcpy

namespace Jack
{

/*!
\brief Counter for CAS
*/

struct AtomicCounter
{
union {
struct {
UInt16 fShortVal1; // Cur
UInt16 fShortVal2; // Next
}
scounter;
UInt32 fLongVal;
}info;

AtomicCounter& operator=(volatile AtomicCounter& obj)
{
info.fLongVal = obj.info.fLongVal;
return *this;
}

};


#define Counter(e) (e).info.fLongVal
#define CurIndex(e) (e).info.scounter.fShortVal1
#define NextIndex(e) (e).info.scounter.fShortVal2

#define CurArrayIndex(e) (CurIndex(e) & 0x0001)
#define NextArrayIndex(e) ((CurIndex(e) + 1) & 0x0001)

/*!
\brief A class to handle two states (switching from one to the other) in a lock-free manner
*/

// CHECK livelock

template <class T>
class JackAtomicState
{

protected:

T fState[2];
volatile AtomicCounter fCounter;
SInt32 fCallWriteCounter;

UInt32 WriteNextStateStartAux()
{
AtomicCounter old_val;
AtomicCounter new_val;
UInt32 cur_index;
UInt32 next_index;
bool need_copy;
do {
old_val = fCounter;
new_val = old_val;
cur_index = CurArrayIndex(new_val);
next_index = NextArrayIndex(new_val);
need_copy = (CurIndex(new_val) == NextIndex(new_val));
NextIndex(new_val) = CurIndex(new_val); // Invalidate next index
} while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
if (need_copy)
memcpy(&fState[next_index], &fState[cur_index], sizeof(T));
return next_index;
}

void WriteNextStateStopAux()
{
AtomicCounter old_val;
AtomicCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
NextIndex(new_val)++; // Set next index
} while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
}

public:

JackAtomicState()
{
Counter(fCounter) = 0;
fCallWriteCounter = 0;
}

~JackAtomicState() // Not virtual ??
{}

/*!
\brief Returns the current state : only valid in the RT reader thread
*/
T* ReadCurrentState()
{
return &fState[CurArrayIndex(fCounter)];
}

/*!
\brief Returns the current state index
*/
UInt16 GetCurrentIndex()
{
return CurIndex(fCounter);
}

/*!
\brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one)
*/
T* TrySwitchState()
{
AtomicCounter old_val;
AtomicCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
CurIndex(new_val) = NextIndex(new_val); // Prepare switch
} while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
return &fState[CurArrayIndex(fCounter)]; // Read the counter again
}

/*!
\brief Tries to switch to the next state and returns the new current state (either the same as before if case of switch failure or the new one)
*/
T* TrySwitchState(bool* result)
{
AtomicCounter old_val;
AtomicCounter new_val;
do {
old_val = fCounter;
new_val = old_val;
*result = (CurIndex(new_val) != NextIndex(new_val));
CurIndex(new_val) = NextIndex(new_val); // Prepare switch
} while (!CAS(Counter(old_val), Counter(new_val), (UInt32*)&fCounter));
return &fState[CurArrayIndex(fCounter)]; // Read the counter again
}

/*!
\brief Start write operation : setup and returns the next state to update, check for recursive write calls.
*/
T* WriteNextStateStart()
{
UInt32 next_index = (fCallWriteCounter++ == 0)
? WriteNextStateStartAux()
: NextArrayIndex(fCounter); // We are inside a wrapping WriteNextStateStart call, NextArrayIndex can be read safely
return &fState[next_index];
}

/*!
\brief Stop write operation : make the next state ready to be used by the RT thread
*/
void WriteNextStateStop()
{
if (--fCallWriteCounter == 0)
WriteNextStateStopAux();
}

bool IsPendingChange()
{
return CurIndex(fCounter) != NextIndex(fCounter);
}

/*
// Single writer : write methods get the *next* state to be updated
void TestWriteMethod()
{
T* state = WriteNextStateStart();
......
......
WriteNextStateStop();
}

// First RT call possibly switch state
void TestReadRTMethod1()
{
T* state = TrySwitchState();
......
......
}

// Other RT methods can safely use the current state during the *same* RT cycle
void TestReadRTMethod2()
{
T* state = ReadCurrentState();
......
......
}
// Non RT read methods : must check state coherency
void TestReadMethod()
{
T* state;
UInt16 cur_index;
UInt16 next_index = GetCurrentIndex();
do {
cur_index = next_index;
state = ReadCurrentState();
......
......
next_index = GetCurrentIndex();
} while (cur_index != next_index);
}
*/
};


} // end of namespace


#endif


+ 229
- 0
common/JackAudioDriver.cpp View File

@@ -0,0 +1,229 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackAudioDriver.h"
#include "JackTime.h"
#include "JackError.h"
#include "JackEngineControl.h"
#include "JackClientControl.h"
#include "JackPort.h"
#include "JackGraphManager.h"
#include "JackEngine.h"
#include <assert.h>

namespace Jack
{


JackAudioDriver::JackAudioDriver(const char* name, JackEngine* engine, JackSynchro** table)
: JackDriver(name, engine, table),
fCaptureChannels(0),
fPlaybackChannels(0),
fWithMonitorPorts(false)
{}

JackAudioDriver::~JackAudioDriver()
{}

int JackAudioDriver::Open(jack_nframes_t nframes,
jack_nframes_t samplerate,
int capturing,
int playing,
int inchannels,
int outchannels,
bool monitor,
const char* capture_driver_name,
const char* playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency)
{
fCaptureChannels = inchannels;
fPlaybackChannels = outchannels;
fWithMonitorPorts = monitor;
return JackDriver::Open(nframes, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency);
}

int JackAudioDriver::Attach()
{
JackPort* port;
jack_port_id_t port_index;
char buf[JACK_PORT_NAME_SIZE];
unsigned long port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal;
int i;

JackLog("JackAudioDriver::Attach fBufferSize %ld fSampleRate %ld\n", fEngineControl->fBufferSize, fEngineControl->fSampleRate);

for (i = 0; i < fCaptureChannels; i++) {
snprintf(buf, sizeof(buf) - 1, "%s:%s:out%d", fClientControl->fName, fCaptureDriverName, i + 1);
if ((port_index = fGraphManager->AllocatePort(fClientControl->fRefNum, buf, (JackPortFlags)port_flags)) == NO_PORT) {
jack_error("driver: cannot register port for %s", buf);
return -1;
}
port = fGraphManager->GetPort(port_index);
port->SetLatency(fEngineControl->fBufferSize + fCaptureLatency);
fCapturePortList[i] = port_index;
JackLog("JackAudioDriver::Attach fCapturePortList[i] %ld \n", port_index);
}

port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal;

for (i = 0; i < fPlaybackChannels; i++) {
snprintf(buf, sizeof(buf) - 1, "%s:%s:in%d", fClientControl->fName, fPlaybackDriverName, i + 1);
if ((port_index = fGraphManager->AllocatePort(fClientControl->fRefNum, buf, (JackPortFlags)port_flags)) == NO_PORT) {
jack_error("driver: cannot register port for %s", buf);
return -1;
}
port = fGraphManager->GetPort(port_index);
port->SetLatency(fEngineControl->fBufferSize + fPlaybackLatency);
fPlaybackPortList[i] = port_index;
JackLog("JackAudioDriver::Attach fPlaybackPortList[i] %ld \n", port_index);

// Monitor ports
if (fWithMonitorPorts) {
JackLog("Create monitor port \n");
snprintf(buf, sizeof(buf) - 1, "%s:%s:monitor_%u", fClientControl->fName, fPlaybackDriverName, i + 1);
if ((port_index = fGraphManager->AllocatePort(fClientControl->fRefNum, buf, JackPortIsOutput)) == NO_PORT) {
jack_error("Cannot register monitor port for %s", buf);
return -1;
} else {
port = fGraphManager->GetPort(port_index);
port->SetLatency(fEngineControl->fBufferSize);
fMonitorPortList[i] = port_index;
}
}
}

return 0;
}

int JackAudioDriver::Detach()
{
int i;
JackLog("JackAudioDriver::Detach\n");

for (i = 0; i < fCaptureChannels; i++) {
fGraphManager->RemovePort(fClientControl->fRefNum, fCapturePortList[i]);
}

for (i = 0; i < fPlaybackChannels; i++) {
fGraphManager->RemovePort(fClientControl->fRefNum, fPlaybackPortList[i]);
if (fWithMonitorPorts)
fGraphManager->RemovePort(fClientControl->fRefNum, fMonitorPortList[i]);
}

return 0;
}

int JackAudioDriver::Write()
{
for (int i = 0; i < fPlaybackChannels; i++) {
if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) {
float* buffer = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[i], fEngineControl->fBufferSize);
int size = sizeof(float) * fEngineControl->fBufferSize;
// Monitor ports
if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[i]) > 0)
memcpy((jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[i], fEngineControl->fBufferSize), buffer, size);
}
}
return 0;
}

int JackAudioDriver::Process()
{
return (fEngineControl->fSyncMode) ? ProcessSync() : ProcessAsync();
}

/*
The driver ASYNC mode: output buffers computed at the *previous cycle* are used, the server does not
synchronize to the end of client graph execution.
*/

int JackAudioDriver::ProcessAsync()
{
if (Read() < 0) { // Read input buffers for the current cycle
jack_error("ProcessAsync: read error");
return 0;
}

if (Write() < 0) { // Write output buffers from the previous cycle
jack_error("ProcessAsync: write error");
return 0;
}

if (fIsMaster) {
fEngine->Process(fLastWaitUst); // fLastWaitUst is set in the "low level" layer
fGraphManager->ResumeRefNum(fClientControl, fSynchroTable);
ProcessSlaves();
} else {
fGraphManager->ResumeRefNum(fClientControl, fSynchroTable);
}
return 0;
}

/*
The driver SYNC mode: the server does synchronize to the end of client graph execution,
output buffers computed at the *current cycle* are used.
*/

int JackAudioDriver::ProcessSync()
{
if (Read() < 0) { // Read input buffers for the current cycle
jack_error("ProcessSync: read error");
return 0;
}

if (fIsMaster) {
fEngine->Process(fLastWaitUst); // fLastWaitUst is set in the "low level" layer
fGraphManager->ResumeRefNum(fClientControl, fSynchroTable);
ProcessSlaves();
if (fGraphManager->SuspendRefNum(fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs) < 0)
jack_error("JackAudioDriver::ProcessSync SuspendRefNum error");
if (Write() < 0) // Write output buffers for the current cycle
jack_error("Process: write error");
} else {
fGraphManager->ResumeRefNum(fClientControl, fSynchroTable);
}
return 0;
}

void JackAudioDriver::NotifyXRun(jack_time_t callback_usecs)
{
fEngine->NotifyXRun(callback_usecs);
}

jack_default_audio_sample_t* JackAudioDriver::GetInputBuffer(int port_index)
{
assert(fCapturePortList[port_index]);
return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[port_index], fEngineControl->fBufferSize);
}

jack_default_audio_sample_t* JackAudioDriver::GetOutputBuffer(int port_index)
{
assert(fPlaybackPortList[port_index]);
return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[port_index], fEngineControl->fBufferSize);
}

jack_default_audio_sample_t* JackAudioDriver::GetMonitorBuffer(int port_index)
{
assert(fPlaybackPortList[port_index]);
return (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[port_index], fEngineControl->fBufferSize);
}

} // end of namespace

+ 88
- 0
common/JackAudioDriver.h View File

@@ -0,0 +1,88 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackAudioDriver__
#define __JackAudioDriver__

#include "JackDriver.h"


namespace Jack
{

/*!
\brief The base class for audio drivers: drivers with audio ports.
*/

class EXPORT JackAudioDriver : public JackDriver
{

protected:

int fCaptureChannels;
int fPlaybackChannels;

// static tables since the actual number of ports may be changed by the real driver
// thus dynamic allocation is more difficult to handle
jack_port_id_t fCapturePortList[PORT_NUM];
jack_port_id_t fPlaybackPortList[PORT_NUM];
jack_port_id_t fMonitorPortList[PORT_NUM];

bool fWithMonitorPorts;

jack_default_audio_sample_t* GetInputBuffer(int port_index);
jack_default_audio_sample_t* GetOutputBuffer(int port_index);
jack_default_audio_sample_t* GetMonitorBuffer(int port_index);

private:

int ProcessAsync();
int ProcessSync();

public:

JackAudioDriver(const char* name, JackEngine* engine, JackSynchro** table);
virtual ~JackAudioDriver();

virtual int Process();

virtual int Open(jack_nframes_t nframes,
jack_nframes_t samplerate,
int capturing,
int playing,
int inchannels,
int outchannels,
bool monitor,
const char* capture_driver_name,
const char* playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency);

virtual int Attach();
virtual int Detach();
virtual int Write();

virtual void NotifyXRun(jack_time_t callback_usecs); // XRun notification sent by the driver

};

} // end of namespace

#endif

+ 210
- 0
common/JackChannel.h View File

@@ -0,0 +1,210 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackChannel__
#define __JackChannel__

#include "types.h"
#include "JackError.h"
#include "JackTransportEngine.h"

namespace Jack
{

class JackClientInterface;
class JackClient;
class JackServer;
struct JackEngineControl;
class JackGraphManager;

/*!
\brief Inter process channel for server/client bidirectionnal communication : request and (receiving) notifications.
*/

class JackClientChannelInterface
{

public:

JackClientChannelInterface()
{}
virtual ~JackClientChannelInterface()
{}

// Open the Server/Client connection
virtual int Open(const char* name, JackClient* obj)
{
return 0;
}

// Close the Server/Client connection
virtual void Close()
{}

// Start listening for messages from the server
virtual int Start()
{
return 0;
}

// Stop listening for messages from the server
virtual void Stop()
{}

virtual void ClientNew(const char* name, int* shared_engine, int* shared_client, int* shared_ports, int* result)
{}
virtual void ClientNew(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, int* result)
{}
virtual void ClientClose(int refnum, int* result)
{}

virtual void ClientActivate(int refnum, int* result)
{}
virtual void ClientDeactivate(int refnum, int* result)
{}

virtual void PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result)
{}
virtual void PortUnRegister(int refnum, jack_port_id_t port_index, int* result)
{}

virtual void PortConnect(int refnum, const char* src, const char* dst, int* result)
{}
virtual void PortDisconnect(int refnum, const char* src, const char* dst, int* result)
{}
virtual void PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result)
{}
virtual void PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result)
{}

virtual void SetBufferSize(jack_nframes_t nframes, int* result)
{}
virtual void SetFreewheel(int onoff, int* result)
{}

virtual void ReleaseTimebase(int refnum, int* result)
{}

virtual void SetTimebaseCallback(int refnum, int conditional, int* result)
{}

};

/*!
\brief Inter process channel for server to client notifications.
*/

class JackNotifyChannelInterface
{

public:

JackNotifyChannelInterface()
{}
virtual ~JackNotifyChannelInterface()
{}

// Open the Server/Client connection
virtual int Open(const char* name)
{
return 0;
}
// Close the Server/Client connection
virtual void Close()
{}

/*
The "sync" parameter allows to choose between "synchronous" and "asynchronous" notification
*/
virtual void ClientNotify(int refnum, const char* name, int notify, int sync, int value, int* result)
{}

typedef enum {
kAddClient = 0,
kRemoveClient = 1,
kXRunCallback = 2,
kGraphOrderCallback = 3,
kBufferSizeCallback = 4,
kStartFreewheel = 5,
kStopFreewheel = 6,
kPortRegistrationOn = 7,
kPortRegistrationOff = 8,
kZombifyClient = 9,
kDeadClient = 10
} NotificationType;

};

/*!
\brief Entry point channel for client/server communication.
*/

class JackServerChannelInterface
{

public:

JackServerChannelInterface()
{}
virtual ~JackServerChannelInterface()
{}

// Open the Server/Client connection
virtual int Open(JackServer* server)
{
return 0;
}
// Close the Server/Client connection
virtual void Close()
{}
};

/*!
\brief Channel for server RT thread to request server thread communication.
*/

class JackServerNotifyChannelInterface
{

public:

JackServerNotifyChannelInterface()
{}
virtual ~JackServerNotifyChannelInterface()
{}

// Open the Server RT/Server connection
virtual int Open()
{
return 0;
}
// Close the Server RT/Server connection
virtual void Close()
{}

virtual void ClientNotify(int refnum, int notify, int value)
{}

};


} // end of namespace

#endif


+ 48
- 0
common/JackChannelTransaction.h View File

@@ -0,0 +1,48 @@
/*
Copyright (C) 2004-2006 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#ifndef __JackChannelTransaction__
#define __JackChannelTransaction__


namespace Jack
{

/*!
\brief Channel input/output communication.
*/

class JackChannelTransaction
{

public:

JackChannelTransaction()
{}
virtual ~JackChannelTransaction()
{}

virtual int Read(void* data, int len) = 0;
virtual int Write(void* data, int len) = 0;
};

} // end of namespace

#endif


+ 767
- 0
common/JackClient.cpp View File

@@ -0,0 +1,767 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/


#include "JackClient.h"
#include "JackGraphManager.h"
#include "JackClientControl.h"
#include "JackEngineControl.h"
#include "JackGlobals.h"
#include "JackChannel.h"
#include "JackTransportEngine.h"
#include <math.h>
#include <string>

int verbose = 0;

using namespace std;

namespace Jack
{

JackClient::JackClient()
{}

JackClient::JackClient(JackSynchro** table)
{
fThread = JackGlobals::MakeThread(this);
fSynchroTable = table;
fProcess = NULL;
fGraphOrder = NULL;
fXrun = NULL;
fShutdown = NULL;
fInit = NULL;
fBufferSize = NULL;
fFreewheel = NULL;
fPortRegistration = NULL;
fSync = NULL;
fProcessArg = NULL;
fGraphOrderArg = NULL;
fXrunArg = NULL;
fShutdownArg = NULL;
fInitArg = NULL;
fBufferSizeArg = NULL;
fFreewheelArg = NULL;
fPortRegistrationArg = NULL;
fSyncArg = NULL;
fConditionnal = 0; // Temporary??
}

JackClient::~JackClient()
{
delete fThread;
}

int JackClient::Close()
{
JackLog("JackClient::Close ref = %ld\n", GetClientControl()->fRefNum);
Deactivate();
int result = -1;
fChannel->ClientClose(GetClientControl()->fRefNum, &result);
fChannel->Stop();
fChannel->Close();
fSynchroTable[GetClientControl()->fRefNum]->Disconnect();
return result;
}

bool JackClient::IsActive()
{
return (GetClientControl()) ? GetClientControl()->fActive : false;
}

pthread_t JackClient::GetThreadID()
{
return fThread->GetThreadID();
}

/*!
\brief
In ASYNC mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations.
The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations.
Drivers synchro are setup in "flush" mode if server is ASYNC and NOT freewheel.
*/
void JackClient::SetupDriverSync(bool freewheel)
{
if (!freewheel && !GetEngineControl()->fSyncMode) {
JackLog("JackClient::SetupDriverSync driver sem in flush mode\n");
fSynchroTable[AUDIO_DRIVER_REFNUM]->SetFlush(true);
fSynchroTable[FREEWHEEL_DRIVER_REFNUM]->SetFlush(true);
fSynchroTable[LOOPBACK_DRIVER_REFNUM]->SetFlush(true);
} else {
JackLog("JackClient::SetupDriverSync driver sem in normal mode\n");
fSynchroTable[AUDIO_DRIVER_REFNUM]->SetFlush(false);
fSynchroTable[FREEWHEEL_DRIVER_REFNUM]->SetFlush(false);
fSynchroTable[LOOPBACK_DRIVER_REFNUM]->SetFlush(false);
}
}

/*!
\brief Notification received from the server.
*/

int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, int value)
{
return 0;
}

int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, int value)
{
int res = 0;

// Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
switch (notify) {

case JackNotifyChannelInterface::kAddClient:
case JackNotifyChannelInterface::kRemoveClient:
res = ClientNotifyImp(refnum, name, notify, sync, value);
break;
}

/*
The current semantic is that notifications can only be received when the client has been activated,
although is this implementation, one could imagine calling notifications as soon as the client has be opened.
*/
if (IsActive()) {

switch (notify) {

case JackNotifyChannelInterface::kBufferSizeCallback:
JackLog("JackClient::kBufferSizeCallback buffer_size = %ld\n", value);
if (fBufferSize)
res = fBufferSize(value, fBufferSizeArg);
break;

case JackNotifyChannelInterface::kGraphOrderCallback:
JackLog("JackClient::kGraphOrderCallback\n");
if (fGraphOrder)
res = fGraphOrder(fGraphOrderArg);
break;

case JackNotifyChannelInterface::kStartFreewheel:
JackLog("JackClient::kStartFreewheel\n");
SetupDriverSync(true);
fThread->DropRealTime();
if (fFreewheel)
fFreewheel(1, fFreewheelArg);
break;

case JackNotifyChannelInterface::kStopFreewheel:
JackLog("JackClient::kStopFreewheel\n");
SetupDriverSync(false);
if (fFreewheel)
fFreewheel(0, fFreewheelArg);
fThread->AcquireRealTime();
break;

case JackNotifyChannelInterface::kPortRegistrationOn:
JackLog("JackClient::kPortRegistrationOn port_index = %ld\n", value);
if (fPortRegistration)
fPortRegistration(value, 1, fPortRegistrationArg);
break;

case JackNotifyChannelInterface::kPortRegistrationOff:
JackLog("JackClient::kPortRegistrationOff port_index = %ld \n", value);
if (fPortRegistration)
fPortRegistration(value, 0, fPortRegistrationArg);
break;

case JackNotifyChannelInterface::kXRunCallback:
JackLog("JackClient::kXRunCallback\n");
if (fXrun)
res = fXrun(fXrunArg);
break;

case JackNotifyChannelInterface::kZombifyClient:
res = fThread->Kill();
JackLog("JackClient::kZombifyClient name = %s ref = %ld \n", name, refnum);
ShutDown();
break;
}
}

return res;
}

/*!
\brief We need to start thread before activating in the server, otherwise the FW driver
connected to the client may not be activated.
*/
int JackClient::Activate()
{
JackLog("JackClient::Activate \n");
if (IsActive())
return 0;

if (StartThread() < 0)
return -1;

int result = -1;
fChannel->ClientActivate(GetClientControl()->fRefNum, &result);
if (result < 0)
return result;

if (fSync != NULL) /* If a SyncCallback is pending... */
SetSyncCallback(fSync, fSyncArg);

if (fTimebase != NULL) /* If a TimebaseCallback is pending... */
SetTimebaseCallback(fConditionnal, fTimebase, fTimebaseArg);

GetClientControl()->fActive = true;
return 0;
}

/*!
\brief Need to stop thread after deactivating in the server.
*/
int JackClient::Deactivate()
{
JackLog("JackClient::Deactivate \n");
if (!IsActive())
return 0;

GetClientControl()->fActive = false;
int result = -1;
fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);

JackLog("JackClient::Deactivate res = %ld \n", result);
// We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
fThread->Kill();
return result;
}

//----------------------
// RT thread management
//----------------------

bool JackClient::CallProcessCallback()
{
return (fProcess == NULL) ? true : (fProcess(GetEngineControl()->fBufferSize, fProcessArg) == 0);
}

/*!
\brief Called once when the thread starts.
*/
bool JackClient::Init()
{
if (fInit) {
JackLog("JackClient::Init calling client thread init callback\n");
fInit(fInitArg);
}
return true;
}

int JackClient::StartThread()
{
JackLog("JackClient::StartThread : period = %ld computation = %ld constraint = %ld\n",
long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));

// Will do "something" on OSX only...
fThread->SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);

if (fThread->Start() < 0) {
jack_error("Start thread error");
return -1;
}

if (GetEngineControl()->fRealTime) {
if (fThread->AcquireRealTime(GetEngineControl()->fPriority - 1) < 0) {
jack_error("AcquireRealTime error");
}
}

return 0;
}

/*!
\brief RT thread.
*/
bool JackClient::Execute()
{
// Suspend itself: wait on the input synchro
if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
jack_error("SuspendRefNum error");
goto error;
}

// Process call
if (IsActive()) {
CallSyncCallback();
bool res = CallProcessCallback();
CallTimebaseCallback();
if (!res)
goto end;
} else {
JackLog("Process called for an inactive client\n");
// Happens if client is still not activated (connected to the FW)
// or still runs while being desactivated by the server
}

// Resume: signal output clients connected to the running client
if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
jack_error("ResumeRefNum error");
}

return true;

end:
JackLog("JackClient::Execute end name = %s\n", GetClientControl()->fName);

// Continue graph execution for this cycle
if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
jack_error("ResumeRefNum error");
}

// Hum... not sure about this, the following "close" code is called in the RT thread...
int result;
fThread->DropRealTime();
fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
Close(); // Not sure...
return false;

error:
jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
// Hum... not sure about this, the following "close" code is called in the RT thread...
fThread->DropRealTime();
ShutDown();
return false;
}

//-----------------
// Port management
//-----------------

int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
{
// Check port name length
string port_name_str = string(port_name);
if (port_name_str.size() == 0) {
jack_error("port_name is empty.");
return 0; // Means failure here...
}

string name = string(GetClientControl()->fName) + string(":") + port_name_str;
if (name.size() >= JACK_PORT_NAME_SIZE) {
jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
"Please use %lu characters or less.",
GetClientControl()->fName,
port_name,
JACK_PORT_NAME_SIZE - 1);
return 0; // Means failure here...
}

// Check if port name already exists
if (GetGraphManager()->GetPort(name.c_str()) != NO_PORT) {
jack_error("port_name \"%s\" already exists.", port_name);
return 0; // Means failure here...
}

JackLog("JackClient::PortRegister ref = %ld name = %s \n", GetClientControl()->fRefNum, name.c_str());

int result = -1;
jack_port_id_t port_index = NO_PORT;
fChannel->PortRegister(GetClientControl()->fRefNum, name.c_str(), flags, buffer_size, &port_index, &result);
JackLog("JackClient::PortRegister port_index = %ld \n", port_index);

if (result == 0) {
fPortList.push_back(port_index);
return port_index;
} else {
return 0;
}
}

int JackClient::PortUnRegister(jack_port_id_t port_index)
{
JackLog("JackClient::PortUnRegister port_index = %ld\n", port_index);
list<jack_port_id_t>::iterator it;
for (it = fPortList.begin(); it != fPortList.end() && *it != port_index; it++)
;

if (it != fPortList.end()) {
fPortList.erase(it);
int result = -1;
fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
return result;
} else {
jack_error("unregistering a port %ld that is not own by the client", port_index);
return -1;
}
}

int JackClient::PortConnect(const char* src, const char* dst)
{
JackLog("JackClient::Connect src = %s dst = %s\n", src, dst);
int result = -1;
fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
return result;
}

int JackClient::PortDisconnect(const char* src, const char* dst)
{
JackLog("JackClient::Disconnect src = %s dst = %s\n", src, dst);
int result = -1;
fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
return result;
}

int JackClient::PortConnect(jack_port_id_t src, jack_port_id_t dst)
{
JackLog("JackClient::PortConnect src = %ld dst = %ld\n", src, dst);
int result = -1;
fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
return result;
}

int JackClient::PortDisconnect(jack_port_id_t src)
{
JackLog("JackClient::PortDisconnect src = %ld\n", src);
int result = -1;
fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
return result;
}

int JackClient::PortIsMine(jack_port_id_t port_index)
{
JackPort* port = GetGraphManager()->GetPort(port_index);
return GetClientControl()->fRefNum == port->GetRefNum();
}

//--------------------
// Context management
//--------------------

int JackClient::SetBufferSize(jack_nframes_t nframes)
{
int result = -1;
fChannel->SetBufferSize(nframes, &result);
return result;
}

int JackClient::SetFreeWheel(int onoff)
{
int result = -1;
fChannel->SetFreewheel(onoff, &result);
return result;
}

/*
ShutDown is called:
- from the RT thread when Execute method fails
- possibly from a "closed" notification channel
(Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown))
*/

void JackClient::ShutDown()
{
JackLog("ShutDown\n");
if (fShutdown) {
GetClientControl()->fActive = false;
fShutdown(fShutdownArg);
fShutdown = NULL;
}
}

//----------------------
// Transport management
//----------------------

int JackClient::ReleaseTimebase()
{
int result = -1;
fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
if (result == 0) {
fTimebase = NULL;
fTimebaseArg = NULL;
}
return result;
}

/* Call the server if the client is active, otherwise keeps the arguments */
int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
{
if (IsActive())
GetClientControl()->fTransportState = (sync_callback == NULL) ? JackTransportStopped : JackTransportSynching;
fSync = sync_callback;
fSyncArg = arg;
return 0;
}

int JackClient::SetSyncTimeout(jack_time_t timeout)
{
GetEngineControl()->fTransport.SetSyncTimeout(timeout);
return 0;
}

/* Call the server if the client is active, otherwise keeps the arguments */
int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
{
if (IsActive()) {
int result = -1;
fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
JackLog("SetTimebaseCallback result = %ld\n", result);
if (result == 0) {
fTimebase = timebase_callback;
fTimebaseArg = arg;
} else {
fTimebase = NULL;
fTimebaseArg = NULL;
}
JackLog("SetTimebaseCallback OK result = %ld\n", result);
return result;
} else {
fTimebase = timebase_callback;
fTimebaseArg = arg;
fConditionnal = conditional;
return 0;
}
}

// Must be RT safe
int JackClient::RequestNewPos(jack_position_t* pos)
{
JackTransportEngine& transport = GetEngineControl()->fTransport;
jack_position_t* request = transport.WriteNextStateStart(2);
pos->unique_1 = pos->unique_2 = transport.GenerateUniqueID();
JackTransportEngine::TransportCopyPosition(pos, request);
JackLog("RequestNewPos pos = %ld\n", pos->frame);
transport.WriteNextStateStop(2);
return 0;
}

int JackClient::TransportLocate(jack_nframes_t frame)
{
jack_position_t pos;
pos.frame = frame;
pos.valid = (jack_position_bits_t)0;
JackLog("TransportLocate pos = %ld\n", pos.frame);
return RequestNewPos(&pos);
}

int JackClient::TransportReposition(jack_position_t* pos)
{
jack_position_t tmp = *pos;
JackLog("TransportReposition pos = %ld\n", pos->frame);
return (tmp.valid & ~JACK_POSITION_MASK) ? EINVAL : RequestNewPos(&tmp);
}

jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
{
if (pos)
GetEngineControl()->fTransport.ReadCurrentPos(pos);
return GetEngineControl()->fTransport.GetState();
}

jack_nframes_t JackClient::GetCurrentTransportFrame()
{
jack_position_t pos;
jack_transport_state_t state = TransportQuery(&pos);

if (state == JackTransportRolling) {
float usecs = GetMicroSeconds() - pos.usecs;
jack_nframes_t elapsed = (jack_nframes_t)floor((((float) pos.frame_rate) / 1000000.0f) * usecs);
return pos.frame + elapsed;
} else {
return pos.frame;
}
}

// Must be RT safe: directly write in the transport shared mem
void JackClient::TransportStart()
{
GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
}

// Must be RT safe: directly write in the transport shared mem
void JackClient::TransportStop()
{
GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
}

// Never called concurently with the server
// TODO check concurency with SetSyncCallback

void JackClient::CallSyncCallback()
{
JackTransportEngine& transport = GetEngineControl()->fTransport;
jack_position_t* cur_pos = transport.ReadCurrentState();
jack_transport_state_t transport_state = transport.GetState();

switch (transport_state) {

case JackTransportStarting: // Starting...
if (fSync == NULL) {
GetClientControl()->fTransportState = JackTransportRolling;
} else if (GetClientControl()->fTransportState == JackTransportStarting) {
if (fSync(transport_state, cur_pos, fSyncArg))
GetClientControl()->fTransportState = JackTransportRolling;
}
break;

case JackTransportRolling:
if (fSync != NULL && GetClientControl()->fTransportState == JackTransportStarting) { // Client still not ready
if (fSync(transport_state, cur_pos, fSyncArg))
GetClientControl()->fTransportState = JackTransportRolling;
}
break;

case JackTransportSynching:
// New pos when transport engine is stopped...
if (fSync != NULL) {
fSync(JackTransportStopped, cur_pos, fSyncArg);
GetClientControl()->fTransportState = JackTransportStopped;
}
break;

default:
break;
}
}

void JackClient::CallTimebaseCallback()
{
JackTransportEngine& transport = GetEngineControl()->fTransport;

if (fTimebase != NULL && fTimebaseArg != NULL && GetClientControl()->fRefNum == transport.GetTimebaseMaster()) {

jack_transport_state_t transport_state = transport.GetState();
jack_position_t* cur_pos = transport.WriteNextStateStart(1);

switch (transport_state) {

case JackTransportRolling:
fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
break;

case JackTransportSynching:
fTimebase(JackTransportStopped, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
break;

default:
break;
}

transport.WriteNextStateStop(1);
}
}

//---------------------
// Callback management
//---------------------

void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
{
if (IsActive()) {
jack_error("You cannot set callbacks on an active client");
} else {
fShutdownArg = arg;
fShutdown = callback;
}
}

int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
{
if (IsActive()) {
jack_error("You cannot set callbacks on an active client");
return -1;
} else {
fProcessArg = arg;
fProcess = callback;
return 0;
}
}

int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
{
if (IsActive()) {
jack_error("You cannot set callbacks on an active client");
return -1;
} else {
fXrunArg = arg;
fXrun = callback;
return 0;
}
}

int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
{
if (IsActive()) {
jack_error("You cannot set callbacks on an active client");
return -1;
} else {
fInitArg = arg;
fInit = callback;
return 0;
}
}

int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
{
JackLog("SetGraphOrderCallback \n");

if (IsActive()) {
jack_error("You cannot set callbacks on an active client");
return -1;
} else {
fGraphOrder = callback;
fGraphOrderArg = arg;
return 0;
}
}

int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
{
if (IsActive()) {
jack_error("You cannot set callbacks on an active client");
return -1;
} else {
fBufferSizeArg = arg;
fBufferSize = callback;
return 0;
}
}

int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
{
if (IsActive()) {
jack_error("You cannot set callbacks on an active client");
return -1;
} else {
fFreewheelArg = arg;
fFreewheel = callback;
return 0;
}
}

int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
{
if (IsActive()) {
jack_error("You cannot set callbacks on an active client");
return -1;
} else {
fPortRegistrationArg = arg;
fPortRegistration = callback;
return 0;
}
}

} // end of namespace


+ 162
- 0
common/JackClient.h View File

@@ -0,0 +1,162 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackClient__
#define __JackClient__

#include "JackClientInterface.h"
#include "JackThread.h"
#include "JackConstants.h"
#include "JackSynchro.h"
#include "types.h"
#include "transport_types.h"
#include <list>

namespace Jack
{

class JackClientChannelInterface;
class JackGraphManager;
class JackServer;
class JackEngine;
class JackSynchro;
struct JackClientControl;
struct JackEngineControl;
class JackSyncInterface;

typedef void (*JackShutdownCallback)(void *arg);

/*!
\brief The base class for clients: share part of the implementation for JackInternalClient and JackLibClient.
*/

class JackClient : public JackClientInterface, public JackRunnableInterface
{

protected:

JackProcessCallback fProcess;
JackGraphOrderCallback fGraphOrder;
JackXRunCallback fXrun;
JackShutdownCallback fShutdown;
JackThreadInitCallback fInit;
JackBufferSizeCallback fBufferSize;
JackFreewheelCallback fFreewheel;
JackPortRegistrationCallback fPortRegistration;
JackTimebaseCallback fTimebase;
JackSyncCallback fSync;
void* fProcessArg;
void* fGraphOrderArg;
void* fXrunArg;
void* fShutdownArg;
void* fInitArg;
void* fBufferSizeArg;
void* fFreewheelArg;
void* fPortRegistrationArg;
void* fTimebaseArg;
void* fSyncArg;
int fConditionnal;

JackThread* fThread; /*! Thread to execute the Process function */
JackClientChannelInterface* fChannel;
JackSynchro** fSynchroTable;
std::list<jack_port_id_t> fPortList;

int StartThread();
void SetupDriverSync(bool freewheel);
bool IsActive();

bool CallProcessCallback();
void CallSyncCallback();
void CallTimebaseCallback();
int RequestNewPos(jack_position_t* pos);

virtual int ClientNotifyImp(int refnum, const char* name, int notify, int sync, int value);

public:

JackClient();
JackClient(JackSynchro** table);
virtual ~JackClient();

virtual int Open(const char* name) = 0;
virtual int Close();

virtual JackGraphManager* GetGraphManager() const = 0;
virtual JackEngineControl* GetEngineControl() const = 0;

// Notifications
virtual int ClientNotify(int refnum, const char* name, int notify, int sync, int value);

virtual int Activate();
virtual int Deactivate();

// Context
virtual int SetBufferSize(jack_nframes_t nframes);
virtual int SetFreeWheel(int onoff);
virtual void ShutDown();
virtual pthread_t GetThreadID();

// Port management
virtual int PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size);
virtual int PortUnRegister(jack_port_id_t port);

virtual int PortConnect(const char* src, const char* dst);
virtual int PortDisconnect(const char* src, const char* dst);
virtual int PortConnect(jack_port_id_t src, jack_port_id_t dst);
virtual int PortDisconnect(jack_port_id_t src);

int PortIsMine(jack_port_id_t port_index);

// Transport
virtual int ReleaseTimebase();
virtual int SetSyncCallback(JackSyncCallback sync_callback, void* arg);
virtual int SetSyncTimeout(jack_time_t timeout);
virtual int SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg);
virtual int TransportLocate(jack_nframes_t frame);
virtual jack_transport_state_t TransportQuery(jack_position_t* pos);
virtual jack_nframes_t GetCurrentTransportFrame();
virtual int TransportReposition(jack_position_t* pos);
virtual void TransportStart();
virtual void TransportStop();

// Callbacks
virtual void OnShutdown(JackShutdownCallback callback, void *arg);
virtual int SetProcessCallback(JackProcessCallback callback, void* arg);
virtual int SetXRunCallback(JackXRunCallback callback, void* arg);
virtual int SetInitCallback(JackThreadInitCallback callback, void* arg);
virtual int SetGraphOrderCallback(JackGraphOrderCallback callback, void* arg);
virtual int SetBufferSizeCallback(JackBufferSizeCallback callback, void* arg);
virtual int SetFreewheelCallback(JackFreewheelCallback callback, void* arg);
virtual int SetPortRegistrationCallback(JackPortRegistrationCallback callback, void* arg);

// JackRunnableInterface interface
bool Init();
bool Execute();
};

// Each "side" server and client will implement this to get the shared graph manager, engine control and inter-process synchro table.
extern JackGraphManager* GetGraphManager();
extern JackEngineControl* GetEngineControl();
extern JackSynchro** GetSynchroTable();

} // end of namespace

#endif

+ 76
- 0
common/JackClientControl.h View File

@@ -0,0 +1,76 @@
/*
Copyright (C) 2003 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackClientControl__
#define __JackClientControl__

#include "JackShmMem.h"
#include "JackPort.h"
#include "JackSynchro.h"
#include "transport_types.h"

namespace Jack
{

/*!
\brief Client control in shared memory.
*/

struct JackClientControl : public JackShmMem
{
char fName[JACK_CLIENT_NAME_SIZE + 1];
volatile jack_transport_state_t fTransportState;
int fRefNum;
bool fZombie;
bool fActive;

JackClientControl(const char* name, int refnum)
{
Init(name, refnum);
}

JackClientControl(const char* name)
{
Init(name, -1);
}

JackClientControl()
{
Init("", -1);
}

virtual ~JackClientControl()
{}

void Init(const char* name, int refnum)
{
strcpy(fName, name);
fRefNum = refnum;
fTransportState = JackTransportStopped;
fZombie = false;
fActive = false;
}

};

} // end of namespace


#endif

+ 56
- 0
common/JackClientInterface.h View File

@@ -0,0 +1,56 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackClientInterface__
#define __JackClientInterface__

#include "JackExports.h"


namespace Jack
{

struct JackClientControl;

/*!
\brief Client interface.
*/

class EXPORT JackClientInterface
{

public:

JackClientInterface()
{}
virtual ~JackClientInterface()
{}

virtual int Close() = 0;

virtual int ClientNotify(int refnum, const char* name, int notify, int sync, int value) = 0;

virtual JackClientControl* GetClientControl() const = 0;
};


} // end of namespace

#endif

+ 452
- 0
common/JackConnectionManager.cpp View File

@@ -0,0 +1,452 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <iostream>
#include <assert.h>
#include "JackConnectionManager.h"
#include "JackClientControl.h"
#include "JackError.h"

namespace Jack
{

JackConnectionManager::JackConnectionManager()
{
int i;
JackLog("JackConnectionManager::InitConnections size = %ld \n", sizeof(JackConnectionManager));

for (i = 0; i < PORT_NUM; i++) {
fConnection[i].Init();
}

fLoopFeedback.Init();

JackLog("JackConnectionManager::InitClients\n");
for (i = 0; i < CLIENT_NUM; i++) {
InitClient(i);
}
}

JackConnectionManager::~JackConnectionManager()
{}

//--------------
// Internal API
//--------------

bool JackConnectionManager::IsLoopPathAux(int ref1, int ref2) const
{
JackLog("JackConnectionManager::IsLoopPathAux ref1 = %ld ref2 = %ld\n", ref1, ref2);

if (ref1 == AUDIO_DRIVER_REFNUM // Driver is reached
|| ref2 == AUDIO_DRIVER_REFNUM
|| ref1 == FREEWHEEL_DRIVER_REFNUM
|| ref2 == FREEWHEEL_DRIVER_REFNUM
|| ref1 == LOOPBACK_DRIVER_REFNUM
|| ref2 == LOOPBACK_DRIVER_REFNUM) {
return false;
} else if (ref1 == ref2) { // Same refnum
return true;
} else {
jack_int_t output[CLIENT_NUM];
fConnectionRef.GetOutputTable(ref1, output);

if (fConnectionRef.IsInsideTable(ref2, output)) { // If ref2 is contained in the outputs of ref1
return true;
} else {
for (int i = 0; i < CLIENT_NUM && output[i] != EMPTY; i++) { // Otherwise recurse for all ref1 outputs
if (IsLoopPathAux(output[i], ref2))
return true; // Stop when a path is found
}
return false;
}
}
}

void JackConnectionManager::InitClient(int refnum)
{
fInputPort[refnum].Init();
fOutputPort[refnum].Init();
fConnectionRef.Init(refnum);
fInputCounter[refnum].SetValue(0);
}

//--------------
// External API
//--------------

int JackConnectionManager::GetActivation(int refnum) const
{
return fInputCounter[refnum].GetValue();
}

/*!
\brief Connect port_src to port_dst.
*/
int JackConnectionManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst)
{
JackLog("JackConnectionManager::Connect port_src = %ld port_dst = %ld\n", port_src, port_dst);

if (fConnection[port_src].AddItem(port_dst)) {
return 0;
} else {
jack_error("Connection table is full !!");
return -1;
}
}

/*!
\brief Disconnect port_src from port_dst.
*/
int JackConnectionManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst)
{
JackLog("JackConnectionManager::Disconnect port_src = %ld port_dst = %ld\n", port_src, port_dst);

if (fConnection[port_src].RemoveItem(port_dst)) {
return 0;
} else {
jack_error("Connection not found !!");
return -1;
}
}

/*!
\brief Check if port_src and port_dst are connected.
*/
bool JackConnectionManager::IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const
{
return fConnection[port_src].CheckItem(port_dst);
}

/*!
\brief Get the connection number of a given port.
*/
jack_int_t JackConnectionManager::Connections(jack_port_id_t port_index) const
{
return fConnection[port_index].GetItemCount();
}

jack_port_id_t JackConnectionManager::GetPort(jack_port_id_t port_index, int connection) const
{
assert(connection < CONNECTION_NUM);
return (jack_port_id_t)fConnection[port_index].GetItem(connection);
}

/*!
\brief Get the connection port array.
*/
const jack_int_t* JackConnectionManager::GetConnections(jack_port_id_t port_index) const
{
return fConnection[port_index].GetItems();
}

//------------------------
// Client port management
//------------------------

/*!
\brief Add an input port to a client.
*/
int JackConnectionManager::AddInputPort(int refnum, jack_port_id_t port_index)
{
if (fInputPort[refnum].AddItem(port_index)) {
JackLog("JackConnectionManager::AddInputPort ref = %ld port = %ld\n", refnum, port_index);
return 0;
} else {
jack_error("Maximum number of input ports is reached for application ref = %ld", refnum);
return -1;
}
}

/*!
\brief Add an output port to a client.
*/
int JackConnectionManager::AddOutputPort(int refnum, jack_port_id_t port_index)
{
if (fOutputPort[refnum].AddItem(port_index)) {
JackLog("JackConnectionManager::AddOutputPort ref = %ld port = %ld\n", refnum, port_index);
return 0;
} else {
jack_error("Maximum number of output ports is reached for application ref = %ld", refnum);
return -1;
}
}

/*!
\brief Remove an input port from a client.
*/
int JackConnectionManager::RemoveInputPort(int refnum, jack_port_id_t port_index)
{
JackLog("JackConnectionManager::RemoveInputPort ref = %ld port_index = %ld \n", refnum, port_index);

if (fInputPort[refnum].RemoveItem(port_index)) {
return 0;
} else {
jack_error("Input port index = %ld not found for application ref = %ld", port_index, refnum);
return -1;
}
}

/*!
\brief Remove an output port from a client.
*/
int JackConnectionManager::RemoveOutputPort(int refnum, jack_port_id_t port_index)
{
JackLog("JackConnectionManager::RemoveOutputPort ref = %ld port_index = %ld \n", refnum, port_index);

if (fOutputPort[refnum].RemoveItem(port_index)) {
return 0;
} else {
jack_error("Output port index = %ld not found for application ref = %ld", port_index, refnum);
return -1;
}
}

/*!
\brief Get the input port array of a given refnum.
*/
const jack_int_t* JackConnectionManager::GetInputPorts(int refnum)
{
return fInputPort[refnum].GetItems();
}

/*!
\brief Get the output port array of a given refnum.
*/
const jack_int_t* JackConnectionManager::GetOutputPorts(int refnum)
{
return fOutputPort[refnum].GetItems();
}

/*!
\brief Return the first available refnum.
*/
int JackConnectionManager::AllocateRefNum()
{
for (int i = 0; i < CLIENT_NUM; i++) {
if (fInputPort[i].IsAvailable()) {
JackLog("JackConnectionManager::AllocateRefNum ref = %ld\n", i);
return i;
}
}

return -1;
}

/*!
\brief Release the refnum.
*/
void JackConnectionManager::ReleaseRefNum(int refnum)
{
JackLog("JackConnectionManager::ReleaseRefNum ref = %ld\n", refnum);
InitClient(refnum);
}

/*!
\brief Reset all clients activation.
*/
void JackConnectionManager::ResetGraph(JackClientTiming* timing)
{
// Reset activation counter : must be done *before* starting to resume clients
for (int i = 0; i < CLIENT_NUM; i++) {
fInputCounter[i].Reset();
timing[i].fStatus = NotTriggered;
}
}

/*!
\brief Wait on the input synchro.
*/
int JackConnectionManager::SuspendRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing, long time_out_usec)
{
int res;
if ((res = table[control->fRefNum]->TimedWait(time_out_usec))) {
timing[control->fRefNum].fStatus = Running;
timing[control->fRefNum].fAwakeAt = GetMicroSeconds();
}
return (res) ? 0 : -1;
}

/*!
\brief Signal clients connected to the given client.
*/
int JackConnectionManager::ResumeRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing)
{
jack_time_t current_date = GetMicroSeconds();
const jack_int_t* outputRef = fConnectionRef.GetItems(control->fRefNum);
int res = 0;

// Update state and timestamp of current client
timing[control->fRefNum].fStatus = Finished;
timing[control->fRefNum].fFinishedAt = current_date;

for (int i = 0; i < CLIENT_NUM; i++) {
// Signal connected clients or drivers
if (outputRef[i] > 0) {
// Update state and timestamp of destination clients
timing[i].fStatus = Triggered;
timing[i].fSignaledAt = current_date;

if (!fInputCounter[i].Signal(table[i], control)) {
JackLog("JackConnectionManager::ResumeRefNum error: ref = %ld output = %ld \n", control->fRefNum, i);
res = -1;
}
}
}

return res;
}

/*!
\brief Increment the number of ports between 2 clients, if the 2 clients become connected, then the Activation counter is updated.
*/
void JackConnectionManager::IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
int ref1 = GetOutputRefNum(port_src);
int ref2 = GetInputRefNum(port_dst);

assert(ref1 >= 0 && ref2 >= 0);

DirectConnect(ref1, ref2);
JackLog("JackConnectionManager::IncConnectionRef: ref1 = %ld ref2 = %ld\n", ref1, ref2);
}

/*!
\brief Decrement the number of ports between 2 clients, if the 2 clients become disconnected, then the Activation counter is updated.
*/
void JackConnectionManager::DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
int ref1 = GetOutputRefNum(port_src);
int ref2 = GetInputRefNum(port_dst);

assert(ref1 >= 0 && ref2 >= 0);

DirectDisconnect(ref1, ref2);
JackLog("JackConnectionManager::DecConnectionRef: ref1 = %ld ref2 = %ld\n", ref1, ref2);
}

/*!
\brief Directly connect 2 reference numbers.
*/
void JackConnectionManager::DirectConnect(int ref1, int ref2)
{
assert(ref1 >= 0 && ref2 >= 0);

if (fConnectionRef.IncItem(ref1, ref2) == 1) { // First connection between client ref1 and client ref2
JackLog("JackConnectionManager::DirectConnect first: ref1 = %ld ref2 = %ld\n", ref1, ref2);
fInputCounter[ref2].IncValue();
}
}

/*!
\brief Directly disconnect 2 reference numbers.
*/
void JackConnectionManager::DirectDisconnect(int ref1, int ref2)
{
assert(ref1 >= 0 && ref2 >= 0);

if (fConnectionRef.DecItem(ref1, ref2) == 0) { // Last connection between client ref1 and client ref2
JackLog("JackConnectionManager::DirectDisconnect last: ref1 = %ld ref2 = %ld\n", ref1, ref2);
fInputCounter[ref2].DecValue();
}
}

/*!
\brief Returns the connections state between 2 refnum.
*/
bool JackConnectionManager::IsDirectConnection(int ref1, int ref2) const
{
assert(ref1 >= 0 && ref2 >= 0);
return fConnectionRef.GetItemCount(ref1, ref2);
}

/*!
\brief Get the client refnum of a given input port.
*/
int JackConnectionManager::GetInputRefNum(jack_port_id_t port_index) const
{
for (int i = 0; i < CLIENT_NUM; i++) {
if (fInputPort[i].CheckItem(port_index))
return i;
}

return -1;
}

/*!
\brief Get the client refnum of a given ouput port.
*/
int JackConnectionManager::GetOutputRefNum(jack_port_id_t port_index) const
{
for (int i = 0; i < CLIENT_NUM; i++) {
if (fOutputPort[i].CheckItem(port_index))
return i;
}

return -1;
}

/*!
\brief Test is a connection path exists between port_src and port_dst.
*/
bool JackConnectionManager::IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const
{
return IsLoopPathAux(GetInputRefNum(port_dst), GetOutputRefNum(port_src));
}

bool JackConnectionManager::IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const
{
return (fLoopFeedback.GetConnectionIndex(GetOutputRefNum(port_src), GetInputRefNum(port_dst)) >= 0);
}

bool JackConnectionManager::IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
int ref1 = GetOutputRefNum(port_src);
int ref2 = GetInputRefNum(port_dst);

// Add an activation connection in the other direction
JackLog("JackConnectionManager::IncFeedbackConnection ref1 = %ld ref2 = %ld\n", ref1, ref2);
assert(ref1 >= 0 && ref2 >= 0);

if (ref1 != ref2)
DirectConnect(ref2, ref1);

return fLoopFeedback.IncConnection(ref1, ref2); // Add the feedback connection
}

bool JackConnectionManager::DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
int ref1 = GetOutputRefNum(port_src);
int ref2 = GetInputRefNum(port_dst);

// Remove an activation connection in the other direction
JackLog("JackConnectionManager::DecFeedbackConnection ref1 = %ld ref2 = %ld\n", ref1, ref2);
assert(ref1 >= 0 && ref2 >= 0);

if (ref1 != ref2)
DirectDisconnect(ref2, ref1);

return fLoopFeedback.DecConnection(ref1, ref2); // Remove the feedback connection
}

} // end of namespace



+ 468
- 0
common/JackConnectionManager.h View File

@@ -0,0 +1,468 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackConnectionManager__
#define __JackConnectionManager__

#include "JackConstants.h"
#include "JackActivationCount.h"
#include <assert.h>

namespace Jack
{

#define NO_PORT 0xFFFE

#define EMPTY 0xFFFD
#define FREE 0xFFFC

typedef uint16_t jack_int_t; // Internal type for ports and refnum

struct JackClientControl;

typedef enum {
NotTriggered,
Triggered,
Running,
Finished,
} jack_client_state_t;

/*!
\brief Utility class.
*/

template <int SIZE>
class JackFixedArray
{

private:

jack_int_t fTable[SIZE];
uint32_t fCounter;

public:

JackFixedArray()
{
Init();
}

virtual ~JackFixedArray()
{}

void Init()
{
for (int i = 0; i < SIZE; i++)
fTable[i] = EMPTY;
fCounter = 0;
}

bool AddItem(jack_int_t index)
{
for (int i = 0; i < SIZE; i++) {
if (fTable[i] == EMPTY) {
fTable[i] = index;
fCounter++;
return true;
}
}
return false;
}

bool RemoveItem(jack_int_t index)
{
for (int i = 0; i < SIZE; i++) {
if (fTable[i] == index) {
fCounter--;
// Shift all indexes
if (i == SIZE - 1) {
fTable[i] = EMPTY;
} else {
int j;
for (j = i; j <= SIZE - 2 && fTable[j] != EMPTY; j++) {
fTable[j] = fTable[j + 1];
}
fTable[j] = EMPTY;
}
return true;
}
}
return false;
}

jack_int_t GetItem(jack_int_t index) const
{
return (index < SIZE) ? fTable[index] : EMPTY;
}

const jack_int_t* GetItems() const
{
return fTable;
}

bool CheckItem(jack_int_t index) const
{
for (int i = 0; i < SIZE && fTable[i] != EMPTY; i++) {
if (fTable[i] == index)
return true;
}
return false;
}

uint32_t GetItemCount() const
{
return fCounter;
}

};

/*!
\brief Utility class.
*/

template <int SIZE>
class JackFixedArray1 : public JackFixedArray<SIZE>
{
private:

bool fUsed;

public:

JackFixedArray1()
{
Init();
}

virtual ~JackFixedArray1()
{}

void Init()
{
JackFixedArray<SIZE>::Init();
fUsed = false;
}

bool IsAvailable()
{
if (fUsed) {
return false;
} else {
fUsed = true;
return true;
}
}
};

/*!
\brief Utility class.
*/

template <int SIZE>
class JackFixedMatrix
{
private:

jack_int_t fTable[SIZE][SIZE];

public:

JackFixedMatrix()
{}

virtual ~JackFixedMatrix()
{}

void Init(jack_int_t index)
{
for (int i = 0; i < SIZE; i++) {
fTable[index][i] = 0;
fTable[i][index] = 0;
}
}

const jack_int_t* GetItems(jack_int_t index) const
{
return fTable[index];
}

jack_int_t IncItem(jack_int_t index1, jack_int_t index2)
{
fTable[index1][index2]++;
return fTable[index1][index2];
}

jack_int_t DecItem(jack_int_t index1, jack_int_t index2)
{
fTable[index1][index2]--;
return fTable[index1][index2];
}

jack_int_t GetItemCount(jack_int_t index1, jack_int_t index2) const
{
return fTable[index1][index2];
}

/*!
\brief Get the output indexes of a given index.
*/
void GetOutputTable(jack_int_t index, jack_int_t* output) const
{
int i, j;

for (i = 0; i < SIZE; i++)
output[i] = EMPTY;

for (i = 0, j = 0; i < SIZE; i++) {
if (fTable[index][i] > 0) {
output[j] = i;
j++;
}
}
}

bool IsInsideTable(jack_int_t index, jack_int_t* output) const
{
for (int i = 0; i < SIZE && output[i] != EMPTY; i++) {
if (output[i] == index)
return true;
}
return false;
}

};

/*!
\brief Utility class.
*/

template <int SIZE>
class JackLoopFeedback
{
private:

int fTable[SIZE][3];

/*!
\brief Add a feedback connection between 2 refnum.
*/
bool AddConnectionAux(int ref1, int ref2)
{
for (int i = 0; i < SIZE; i++) {
if (fTable[i][0] == EMPTY) {
fTable[i][0] = ref1;
fTable[i][1] = ref2;
fTable[i][2] = 1;
JackLog("JackLoopFeedback::AddConnectionAux ref1 = %ld ref2 = %ld\n", ref1, ref2);
return true;
}
}
jack_error("Feedback table is full !!\n");
return false;
}

/*!
\brief Remove a feedback connection between 2 refnum.
*/
bool RemoveConnectionAux(int ref1, int ref2)
{
for (int i = 0; i < SIZE; i++) {
if (fTable[i][0] == ref1 && fTable[i][1] == ref2) {
fTable[i][0] = EMPTY;
fTable[i][1] = EMPTY;
fTable[i][2] = 0;
JackLog("JackLoopFeedback::RemoveConnectionAux ref1 = %ld ref2 = %ld\n", ref1, ref2);
return true;
}
}
jack_error("Feedback connection not found\n");
return false;
}

int IncConnection(int index)
{
fTable[index][2]++;
return fTable[index][2];
}

int DecConnection(int index)
{
fTable[index][2]--;
return fTable[index][2];
}

public:

JackLoopFeedback()
{
Init();
}
virtual ~JackLoopFeedback()
{}

void Init()
{
for (int i = 0; i < SIZE; i++) {
fTable[i][0] = EMPTY;
fTable[i][1] = EMPTY;
fTable[i][2] = 0;
}
}

bool IncConnection(int ref1, int ref2)
{
int index = GetConnectionIndex(ref1, ref2);

if (index >= 0) { // Feedback connection is already added, increment counter
IncConnection(index);
return true;
} else {
return AddConnectionAux(ref1, ref2); // Add the feedback connection
}
}

bool DecConnection(int ref1, int ref2)
{
int index = GetConnectionIndex(ref1, ref2);

if (index >= 0) {
JackLog("JackLoopFeedback::DecConnection ref1 = %ld ref2 = %ld index = %ld\n", ref1, ref2, index);
return (DecConnection(index) == 0) ? RemoveConnectionAux(ref1, ref2) : true;
} else {
return false;
}
}

/*!
\brief Test if a connection between 2 refnum is a feedback connection.
*/
int GetConnectionIndex(int ref1, int ref2) const
{
for (int i = 0; i < SIZE; i++) {
if (fTable[i][0] == ref1 && fTable[i][1] == ref2)
return i;
}
return -1;
}

};

/*!
\brief For client timing measurements.
*/

struct JackClientTiming
{
jack_time_t fSignaledAt;
jack_time_t fAwakeAt;
jack_time_t fFinishedAt;
jack_client_state_t fStatus;
JackClientTiming():fSignaledAt(0), fAwakeAt(0), fFinishedAt(0), fStatus(NotTriggered)
{}
~JackClientTiming()
{}
};

/*!
\brief Connection manager.
<UL>
<LI>The <B>fConnection</B> array contains the list (array line) of connected ports for a given port.
<LI>The <B>fConnectionCount</B> array contains the number of connected ports to a given port.
<LI>The <B>fInputPort</B> array contains the list (array line) of input connected ports for a given client.
<LI>The <B>fOutputPort</B> array contains the list (array line) of ouput connected ports for a given client.
<LI>The <B>fConnectionRef</B> array contains the number of ports connected between two clients.
<LI>The <B>fInputRef</B> array contains the number of input clients connected to a given client.
<LI>The <B>fInputCounter</B> array contains the number of input clients connected to a given for activation purpose.
</UL>
*/

class JackConnectionManager
{

private:

JackFixedArray<CONNECTION_NUM> fConnection[PORT_NUM]; /*! Connection matrix: list of connected ports for a given port: needed to compute Mix buffer */
JackFixedArray1<PORT_NUM_FOR_CLIENT> fInputPort[CLIENT_NUM]; /*! Table of input port per refnum : to find a refnum for a given port */
JackFixedArray<PORT_NUM_FOR_CLIENT> fOutputPort[CLIENT_NUM]; /*! Table of output port per refnum : to find a refnum for a given port */
JackFixedMatrix<CLIENT_NUM> fConnectionRef; /*! Table of port connections by (refnum , refnum) */
JackActivationCount fInputCounter[CLIENT_NUM]; /*! Activation counter per refnum */
JackLoopFeedback<CONNECTION_NUM> fLoopFeedback; /*! Loop feedback connections */

bool IsLoopPathAux(int ref1, int ref2) const;
void InitClient(int refnum);

public:

JackConnectionManager();
virtual ~JackConnectionManager();

// Connections management
int Connect(jack_port_id_t port_src, jack_port_id_t port_dst);
int Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst);
bool IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const;

jack_int_t Connections(jack_port_id_t port_index) const;
jack_port_id_t GetPort(jack_port_id_t port_index, int connection) const;
const jack_int_t* GetConnections(jack_port_id_t port_index) const;

bool IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst);
bool DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst);
bool IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const;

bool IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const;
void IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst);
void DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst);

// Ports management
int AddInputPort(int refnum, jack_port_id_t port_index);
int AddOutputPort(int refnum, jack_port_id_t port_index);

int RemoveInputPort(int refnum, jack_port_id_t port_index);
int RemoveOutputPort(int refnum, jack_port_id_t port_index);

const jack_int_t* GetInputPorts(int refnum);
const jack_int_t* GetOutputPorts(int refnum);

// Client management
int AllocateRefNum();
void ReleaseRefNum(int refnum);

int GetInputRefNum(jack_port_id_t port_index) const;
int GetOutputRefNum(jack_port_id_t port_index) const;

// Connect/Disconnect 2 refnum "directly"
bool IsDirectConnection(int ref1, int ref2) const;
void DirectConnect(int ref1, int ref2);
void DirectDisconnect(int ref1, int ref2);

int GetActivation(int refnum) const;

// Graph
void ResetGraph(JackClientTiming* timing);
int ResumeRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing);
int SuspendRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing, long time_out_usec);

};

} // end of namespace

#endif


+ 64
- 0
common/JackConstants.h View File

@@ -0,0 +1,64 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#define PRINTDEBUG

#define VERSION "0.58"

#define FORK_SERVER 1

#define BUFFER_SIZE_MAX 8192
#define SAMPLE_RATE 44100

#define JACK_PORT_NAME_SIZE 256
#define JACK_PORT_TYPE_SIZE 32

#define JACK_CLIENT_NAME_SIZE 64

#define PORT_NUM 512
#define PORT_NUM_FOR_CLIENT 256

#define CONNECTION_NUM 256

#define CLIENT_NUM 64

#define AUDIO_DRIVER_REFNUM 0 // Audio driver is initialized first, it will get the refnum 0
#define FREEWHEEL_DRIVER_REFNUM 1 // Freewheel driver is initialized second, it will get the refnum 1
#define LOOPBACK_DRIVER_REFNUM 2 // Loopback driver is initialized third, it will get the refnum 2
#define REAL_REFNUM LOOPBACK_DRIVER_REFNUM + 1 // Real clients start at LOOPBACK_DRIVER_REFNUM + 1

#define SOCKET_TIME_OUT 5

#ifdef WIN32
#define jack_server_dir "server"
#define jack_client_dir "client"
#else
#define jack_server_dir "/dev/shm"
#define jack_client_dir "/dev/shm"
#endif

/*
#define jack_server_dir "/mnt/ramfs"
#define jack_client_dir "/mnt/ramfs"
*/

#define jack_server_entry "jackdmp_entry"
#define jack_client_entry "jack_client"

#define ALL_CLIENTS -1 // for notification

+ 457
- 0
common/JackDebugClient.cpp View File

@@ -0,0 +1,457 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackDebugClient.h"
#include "JackError.h"
#include <iostream>
#include <iomanip>
#include <sstream>
#include <fstream>
#include <string>
#include <time.h>

using namespace std;

namespace Jack
{

JackDebugClient::JackDebugClient(JackClient * client)
{
fTotalPortNumber = 1; // The total number of port opened and maybe closed. Historical view.
fOpenPortNumber = 0; // The current number of opened port.
fIsActivated = 0;
fIsDeactivated = 0;
fIsClosed = 0;
fClient = client;
}

JackDebugClient::~JackDebugClient()
{
fTotalPortNumber--; // fTotalPortNumber start at 1
*fStream << endl << endl << "----------------------------------- JackDebugClient summary ------------------------------- " << endl << endl;
*fStream << "Client flags ( 1:yes / 0:no ) :" << endl;
*fStream << setw(5) << "- Client call activated : " << fIsActivated << endl;
*fStream << setw(5) << "- Client call deactivated : " << fIsDeactivated << endl;
*fStream << setw(5) << "- Client call closed : " << fIsClosed << endl;
*fStream << setw(5) << "- Total number of instantiated port : " << fTotalPortNumber << endl;
*fStream << setw(5) << "- Number of port remaining open when exiting client : " << fOpenPortNumber << endl;
if (fOpenPortNumber != 0)
*fStream << "!!! WARNING !!! Some ports have not been unregistrated ! Incorrect exiting !" << endl;
if (fIsDeactivated != fIsActivated)
*fStream << "!!! ERROR !!! Client seem do not perform symetric activation-deactivation ! (not the same number of activate and deactivate)" << endl;
if (fIsClosed == 0)
*fStream << "!!! ERROR !!! Client have not been closed with jack_client_close() !" << endl;

*fStream << endl << endl << "---------------------------- JackDebugClient detailed port summary ------------------------ " << endl << endl;
//for (int i = 0; i < fTotalPortNumber ; i++) {
for (int i = 1; i <= fTotalPortNumber ; i++) {
*fStream << endl << "Port index (internal debug test value) : " << i << endl;
*fStream << setw(5) << "- Name : " << fPortList[i].name << endl;
*fStream << setw(5) << "- idport : " << fPortList[i].idport << endl;
*fStream << setw(5) << "- IsConnected : " << fPortList[i].IsConnected << endl;
*fStream << setw(5) << "- IsUnregistrated : " << fPortList[i].IsUnregistrated << endl;
if (fPortList[i].IsUnregistrated == 0)
*fStream << "!!! WARNING !!! Port have not been unregistrated ! Incorrect exiting !" << endl;
}
*fStream << "delete object JackDebugClient : end of tracing" << endl;
delete fStream;
delete fClient;
}

int JackDebugClient::Open(const char* name)
{
int Tidport;
Tidport = fClient->Open(name);
char provstr[256];
char buffer[256];
time_t curtime;
struct tm *loctime;
/* Get the current time. */
curtime = time (NULL);
/* Convert it to local time representation. */
loctime = localtime (&curtime);
strftime (buffer, 256, "%I-%M", loctime);
sprintf(provstr, "JackClientDebug-%s-%s.log", name, buffer);
fStream = new ofstream(provstr, ios_base::ate);
if (fStream->is_open()) {
if (Tidport == -1) {
*fStream << "Trying to Open Client with name '" << name << "' with bad result (client not opened)." << Tidport << endl;
} else {
*fStream << "Open Client with name '" << name << "'." << endl;
}
} else {
JackLog("JackDebugClient::Open : cannot open log file\n");
}
strcpy(fClientName, name);
return Tidport;
}

int JackDebugClient::Close()
{
fIsClosed++;
*fStream << "Client '" << fClientName << "' was Closed" << endl;
return fClient->Close();
}

pthread_t JackDebugClient::GetThreadID()
{
return fClient->GetThreadID();
}

JackGraphManager* JackDebugClient::GetGraphManager() const
{
return fClient->GetGraphManager();
}
JackEngineControl* JackDebugClient::GetEngineControl() const
{
return fClient->GetEngineControl();
}
/*!
\brief Notification received from the server.
*/

int JackDebugClient::ClientNotify(int refnum, const char* name, int notify, int sync, int value)
{
return fClient->ClientNotify( refnum, name, notify, sync, value);
}

int JackDebugClient::Activate()
{
int Tidport;
Tidport = fClient->Activate();
fIsActivated++;
if (fIsDeactivated)
*fStream << "Client '" << fClientName << "' call activate a new time (it already call 'activate' previously)." << endl;
*fStream << "Client '" << fClientName << "' Activated" << endl;
if (Tidport != 0)
*fStream << "Client '" << fClientName << "' try to activate but server return " << Tidport << " ." << endl;
return Tidport;
}

int JackDebugClient::Deactivate()
{
int Tidport;
Tidport = fClient->Deactivate();
fIsDeactivated++;
if (fIsActivated == 0)
*fStream << "Client '" << fClientName << "' deactivate while it hasn't been previoulsy activated !" << endl;
*fStream << "Client '" << fClientName << "' Deactivated" << endl;
if (Tidport != 0)
*fStream << "Client '" << fClientName << "' try to deactivate but server return " << Tidport << " ." << endl;
return Tidport;
}

//-----------------
// Port management
//-----------------

int JackDebugClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
{
int Tidport;
Tidport = fClient->PortRegister(port_name, port_type, flags, buffer_size);
if (Tidport <= 0) {
*fStream << "Client '" << fClientName << "' try port Register ('" << port_name << "') and server return error " << Tidport << " ." << endl;
} else {
if (fTotalPortNumber < MAX_PORT_HISTORY) {
fPortList[fTotalPortNumber].idport = Tidport;
strcpy(fPortList[fTotalPortNumber].name, port_name);
fPortList[fTotalPortNumber].IsConnected = 0;
fPortList[fTotalPortNumber].IsUnregistrated = 0;
} else {
*fStream << "!!! WARNING !!! History is full : no more port history will be recorded." << endl;
}
fTotalPortNumber++;
fOpenPortNumber++;
*fStream << "Client '" << fClientName << "' port Register with portname '" << port_name << " port " << Tidport << "' ." << endl;
}
return Tidport;
}

int JackDebugClient::PortUnRegister(jack_port_id_t port_index)
{
int Tidport;
Tidport = fClient->PortUnRegister(port_index);
fOpenPortNumber--;
int i;
for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history
if (fPortList[i].idport == port_index) { // We found the last record
if (fPortList[i].IsUnregistrated != 0)
*fStream << "!!! ERROR !!! : '" << fClientName << "' id deregistering port '" << fPortList[i].name << "' that have already been unregistered !" << endl;
fPortList[i].IsUnregistrated++;
break;
}
}
if (i == 0) // Port is not found
*fStream << "JackClientDebug : PortUnregister : port " << port_index << " was not previously registered !" << endl;
if (Tidport != 0)
*fStream << "Client '" << fClientName << "' try to do PortUnregister and server return " << Tidport << " )." << endl;
*fStream << "Client '" << fClientName << "' unregister port '" << port_index << "'." << endl;
return Tidport;
}

int JackDebugClient::PortConnect(const char* src, const char* dst)
{
if (!(fIsActivated))
*fStream << "!!! ERROR !!! Trying to connect a port ( " << src << " to " << dst << ") while the client has not been activated !" << endl;
int Tidport;
int i;
Tidport = fClient->PortConnect( src, dst);
for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history
if (strcmp(fPortList[i].name, src) == 0) { // We found the last record in sources
if (fPortList[i].IsUnregistrated != 0)
*fStream << "!!! ERROR !!! Connecting port " << src << " previoulsy unregistered !" << endl;
fPortList[i].IsConnected++;
*fStream << "Connecting port " << src << " to " << dst << ". ";
break;
} else if (strcmp(fPortList[i].name, dst) == 0 ) { // We found the record in dest
if (fPortList[i].IsUnregistrated != 0)
*fStream << "!!! ERROR !!! Connecting port " << dst << " previoulsy unregistered !" << endl;
fPortList[i].IsConnected++;
*fStream << "Connecting port " << src << " to " << dst << ". ";
break;
}
}
if (i == 0) // Port is not found
*fStream << "JackClientDebug : PortConnect : port was not found in debug database !" << endl;
if (Tidport != 0)
*fStream << "Client '" << fClientName << "' try to do PortConnect but server return " << Tidport << " ." << endl;
//*fStream << "Client Port Connect done with names" << endl;
return Tidport;
}

int JackDebugClient::PortDisconnect(const char* src, const char* dst)
{
if (!(fIsActivated))
*fStream << "!!! ERROR !!! Trying to disconnect a port ( " << src << " to " << dst << ") while the client has not been activated !" << endl;
int Tidport;
Tidport = fClient->PortDisconnect( src, dst);
int i;
for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history
if (strcmp(fPortList[i].name, src) == 0) { // We found the record in sources
if (fPortList[i].IsUnregistrated != 0)
*fStream << "!!! ERROR !!! : Disconnecting port " << src << " previoulsy unregistered !" << endl;
fPortList[i].IsConnected--;
*fStream << "disconnecting port " << src << ". ";
break;
} else if (strcmp(fPortList[i].name, dst) == 0 ) { // We found the record in dest
if (fPortList[i].IsUnregistrated != 0)
*fStream << "!!! ERROR !!! : Disonnecting port " << dst << " previoulsy unregistered !" << endl;
fPortList[i].IsConnected--;
*fStream << "disconnecting port " << dst << ". ";
break;
}
}
if (i == 0) // Port is not found
*fStream << "JackClientDebug : PortDisConnect : port was not found in debug database !" << endl;
if (Tidport != 0)
*fStream << "Client '" << fClientName << "' try to do PortDisconnect but server return " << Tidport << " ." << endl;
//*fStream << "Client Port Disconnect done." << endl;
return Tidport;
}

int JackDebugClient::PortConnect(jack_port_id_t src, jack_port_id_t dst)
{
if (!(fIsActivated))
*fStream << "!!! ERROR !!! : Trying to connect port " << src << " to " << dst << " while the client has not been activated !" << endl;
int Tidport;
Tidport = fClient->PortConnect(src, dst);
int i;
for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history
if (fPortList[i].idport == src) { // We found the record in sources
if (fPortList[i].IsUnregistrated != 0)
*fStream << "!!! ERROR !!! : Connecting port " << src << " previoulsy unregistered !" << endl;
fPortList[i].IsConnected++;
*fStream << "Connecting port " << src << ". ";
break;
} else if (fPortList[i].idport == dst) { // We found the record in dest
if (fPortList[i].IsUnregistrated != 0)
*fStream << "!!! ERROR !!! : Connecting port " << dst << " previoulsy unregistered !" << endl;
fPortList[i].IsConnected++;
*fStream << "Connecting port " << dst << ". ";
break;
}
}
if (i == 0) // Port is not found
*fStream << "JackClientDebug : PortConnect : port was not found in debug database !" << endl;
if (Tidport == -1)
*fStream << "Client '" << fClientName << "' try to do Portconnect but server return " << Tidport << " ." << endl;
//*fStream << "Client Port Connect with ID done." << endl;
return Tidport;
}

int JackDebugClient::PortDisconnect(jack_port_id_t src)
{
if (!(fIsActivated))
*fStream << "!!! ERROR !!! : Trying to disconnect port " << src << " while that client has not been activated !" << endl;
int Tidport;
Tidport = fClient->PortDisconnect(src);
int i;
for (i = (fTotalPortNumber - 1); i >= 0; i--) { // We search the record into the history
if (fPortList[i].idport == src) { // We found the record in sources
if (fPortList[i].IsUnregistrated != 0)
*fStream << "!!! ERROR !!! : Disconnecting port " << src << " previoulsy unregistered !" << endl;
fPortList[i].IsConnected--;
*fStream << "Disconnecting port " << src << ". " << endl;
break;
}
}
if (i == 0) // Port is not found
*fStream << "JackClientDebug : PortDisconnect : port was not found in debug database !" << endl;
if (Tidport != 0)
*fStream << "Client '" << fClientName << "' try to do PortDisconnect but server return " << Tidport << " ." << endl;
//*fStream << "Client Port Disconnect with ID done." << endl;
return Tidport;
}

int JackDebugClient::PortIsMine(jack_port_id_t port_index)
{
return fClient->PortIsMine(port_index);
}

//--------------------
// Context management
//--------------------

int JackDebugClient::SetBufferSize(jack_nframes_t nframes)
{
return fClient->SetBufferSize(nframes);
}

int JackDebugClient::SetFreeWheel(int onoff)
{
return fClient->SetFreeWheel(onoff);
}

/*
ShutDown is called:
- from the RT thread when Execute method fails
- possibly from a "closed" notification channel
(Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown))
*/

void JackDebugClient::ShutDown()
{
fClient->ShutDown();
}

//---------------------
// Transport management
//---------------------

int JackDebugClient::ReleaseTimebase()
{
return fClient->ReleaseTimebase();
}

int JackDebugClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
{
return fClient->SetSyncCallback(sync_callback, arg);
}

int JackDebugClient::SetSyncTimeout(jack_time_t timeout)
{
return fClient->SetSyncTimeout(timeout);
}

int JackDebugClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
{
return fClient->SetTimebaseCallback( conditional, timebase_callback, arg);
}

int JackDebugClient::TransportLocate(jack_nframes_t frame)
{
return fClient->TransportLocate(frame);
}

jack_transport_state_t JackDebugClient::TransportQuery(jack_position_t* pos)
{
return fClient->TransportQuery(pos);
}

jack_nframes_t JackDebugClient::GetCurrentTransportFrame()
{
return fClient->GetCurrentTransportFrame();
}

int JackDebugClient::TransportReposition(jack_position_t* pos)
{
return fClient->TransportReposition(pos);
}

void JackDebugClient::TransportStart()
{
fClient->TransportStart();
}

void JackDebugClient::TransportStop()
{
fClient->TransportStop();
}

//---------------------
// Callback management
//---------------------

void JackDebugClient::OnShutdown(JackShutdownCallback callback, void *arg)
{
fClient->OnShutdown(callback, arg);
}

int JackDebugClient::SetProcessCallback(JackProcessCallback callback, void *arg)
{
return fClient->SetProcessCallback( callback, arg);
}

int JackDebugClient::SetXRunCallback(JackXRunCallback callback, void *arg)
{
return fClient->SetXRunCallback(callback, arg);
}

int JackDebugClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
{
return fClient->SetInitCallback(callback, arg);
}

int JackDebugClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
{
return fClient->SetGraphOrderCallback(callback, arg);
}

int JackDebugClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
{
return fClient->SetBufferSizeCallback(callback, arg);
}

int JackDebugClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
{
return fClient->SetFreewheelCallback(callback, arg);
}

int JackDebugClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
{
return fClient->SetPortRegistrationCallback(callback, arg);
}

JackClientControl* JackDebugClient::GetClientControl() const
{
return fClient->GetClientControl();
}

} // end of namespace


+ 127
- 0
common/JackDebugClient.h View File

@@ -0,0 +1,127 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackDebugClient__
#define __JackDebugClient__

#define MAX_PORT_HISTORY 2048

#include "JackClient.h"
#include <list>

namespace Jack
{

/*!
\brief Follow a single port.
*/

typedef struct
{
jack_port_id_t idport;
char name[JACK_PORT_NAME_SIZE]; //portname
int IsConnected;
int IsUnregistrated;
}
PortFollower;

/*!
\brief A "decorator" debug client to validate API use.
*/

class JackDebugClient : public JackClient
{
private:

JackClient* fClient;
std::ofstream* fStream;

protected:
PortFollower fPortList[MAX_PORT_HISTORY]; // Arbitrary value... To be tuned...
int fTotalPortNumber; // The total number of port opened and maybe closed. Historical view.
int fOpenPortNumber; // The current number of opened port.
int fIsActivated;
int fIsDeactivated;
int fIsClosed;
char fClientName[JACK_CLIENT_NAME_SIZE];

public:

JackDebugClient(JackClient *fTheClient);

virtual ~JackDebugClient();

virtual int Open(const char* name);
int Close();

virtual JackGraphManager* GetGraphManager() const;
virtual JackEngineControl* GetEngineControl() const;

// Notifications
int ClientNotify(int refnum, const char* name, int notify, int sync, int value);

int Activate();
int Deactivate();

// Context
int SetBufferSize(jack_nframes_t nframes);
int SetFreeWheel(int onoff);
void ShutDown();
pthread_t GetThreadID();

// Port management
int PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size);
int PortUnRegister(jack_port_id_t port);

int PortConnect(const char* src, const char* dst);
int PortDisconnect(const char* src, const char* dst);
int PortConnect(jack_port_id_t src, jack_port_id_t dst);
int PortDisconnect(jack_port_id_t src);

int PortIsMine(jack_port_id_t port_index);

// Transport
int ReleaseTimebase();
int SetSyncCallback(JackSyncCallback sync_callback, void* arg);
int SetSyncTimeout(jack_time_t timeout);
int SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg);
int TransportLocate(jack_nframes_t frame);
jack_transport_state_t TransportQuery(jack_position_t* pos);
jack_nframes_t GetCurrentTransportFrame();
int TransportReposition(jack_position_t* pos);
void TransportStart();
void TransportStop();

// Callbacks
void OnShutdown(JackShutdownCallback callback, void *arg);
int SetProcessCallback(JackProcessCallback callback, void* arg);
int SetXRunCallback(JackXRunCallback callback, void* arg);
int SetInitCallback(JackThreadInitCallback callback, void* arg);
int SetGraphOrderCallback(JackGraphOrderCallback callback, void* arg);
int SetBufferSizeCallback(JackBufferSizeCallback callback, void* arg);
int SetFreewheelCallback(JackFreewheelCallback callback, void* arg);
int SetPortRegistrationCallback(JackPortRegistrationCallback callback, void* arg);
JackClientControl* GetClientControl() const;

};


} // end of namespace

#endif

+ 204
- 0
common/JackDriver.cpp View File

@@ -0,0 +1,204 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackDriver.h"
#include "JackTime.h"
#include "JackError.h"
#include "JackPort.h"
#include "JackGraphManager.h"
#include "JackGlobals.h"
#include "JackEngineControl.h"
#include "JackClientControl.h"
#include "JackEngine.h"
#include <math.h>
#include <assert.h>

using namespace std;

namespace Jack
{

JackDriver::JackDriver(const char* name, JackEngine* engine, JackSynchro** table)
{
assert(strlen(name) < JACK_CLIENT_NAME_SIZE);
fSynchroTable = table;
fClientControl = new JackClientControl(name);
fEngine = engine;
fGraphManager = NULL;
fLastWaitUst = 0;
fIsMaster = true;
}

JackDriver::JackDriver()
{
fSynchroTable = NULL;
fClientControl = NULL;
fEngine = NULL;
fGraphManager = NULL;
fLastWaitUst = 0;
fIsMaster = true;
}

JackDriver::~JackDriver()
{
JackLog("~JackDriver\n");
delete fClientControl;
}

int JackDriver::Open()
{
int refnum = -1;

if (fEngine->ClientCheckName(fClientControl->fName)) {
jack_error("client %s already registered", fClientControl->fName);
return -1;
}

if (fEngine->ClientInternalNew(fClientControl->fName, &refnum, &fEngineControl, &fGraphManager, this) != 0) {
jack_error("Cannot allocate internal client for audio driver");
return -1;
}

fClientControl->fRefNum = refnum;
fClientControl->fActive = true;
fGraphManager->DirectConnect(fClientControl->fRefNum, fClientControl->fRefNum); // Connect driver to itself for sync

/*
In ASYNC mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations.
The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations.
*/
if (!fEngineControl->fSyncMode) {
fSynchroTable[AUDIO_DRIVER_REFNUM]->SetFlush(true);
fSynchroTable[FREEWHEEL_DRIVER_REFNUM]->SetFlush(true);
fSynchroTable[LOOPBACK_DRIVER_REFNUM]->SetFlush(true);
}
return 0;
}

int JackDriver::Open(jack_nframes_t nframes,
jack_nframes_t samplerate,
int capturing,
int playing,
int inchannels,
int outchannels,
bool monitor,
const char* capture_driver_name,
const char* playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency)
{
JackLog("JackDriver::Open capture_driver_name = %s\n", capture_driver_name);
JackLog("JackDriver::Open playback_driver_name = %s\n", playback_driver_name);
int refnum = -1;

if (fEngine->ClientCheckName(fClientControl->fName)) {
jack_error("client %s already registered", fClientControl->fName);
return -1;
}

if (fEngine->ClientInternalNew(fClientControl->fName, &refnum, &fEngineControl, &fGraphManager, this) != 0) {
jack_error("Cannot allocate internal client for audio driver");
return -1;
}

fClientControl->fRefNum = refnum;
fClientControl->fActive = true;
fEngineControl->fBufferSize = nframes;
fEngineControl->fSampleRate = samplerate;
fCaptureLatency = capture_latency;
fPlaybackLatency = playback_latency;

assert(strlen(capture_driver_name) < JACK_CLIENT_NAME_SIZE);
assert(strlen(playback_driver_name) < JACK_CLIENT_NAME_SIZE);

strcpy(fCaptureDriverName, capture_driver_name);
strcpy(fPlaybackDriverName, playback_driver_name);

fEngineControl->fPeriodUsecs = (jack_time_t)floor((((float)nframes) / (float)samplerate) * 1000000.0f);
if (fEngineControl->fTimeOutUsecs == 0) /* usecs; if zero, use 2 period size. */
fEngineControl->fTimeOutUsecs = (jack_time_t)(2.f * fEngineControl->fPeriodUsecs);

fGraphManager->DirectConnect(fClientControl->fRefNum, fClientControl->fRefNum); // Connect driver to itself for sync

/*
In ASYNC mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations.
The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations.
*/
if (!fEngineControl->fSyncMode) {
fSynchroTable[AUDIO_DRIVER_REFNUM]->SetFlush(true);
fSynchroTable[FREEWHEEL_DRIVER_REFNUM]->SetFlush(true);
fSynchroTable[LOOPBACK_DRIVER_REFNUM]->SetFlush(true);
}
return 0;
}

int JackDriver::Close()
{
JackLog("JackDriver::Close\n");
fGraphManager->DirectDisconnect(fClientControl->fRefNum, fClientControl->fRefNum); // Disconnect driver from itself for sync
fClientControl->fActive = false;
return fEngine->ClientInternalCloseIm(fClientControl->fRefNum);
}

bool JackDriver::IsRealTime()
{
return fEngineControl->fRealTime;
}

JackClientControl* JackDriver::GetClientControl() const
{
return fClientControl;
}

void JackDriver::NotifyXRun(jack_time_t callback_usecs)
{
fEngine->NotifyXRun(callback_usecs);
}

void JackDriverClient::SetMaster(bool onoff)
{
fIsMaster = onoff;
}

bool JackDriverClient::GetMaster()
{
return fIsMaster;
}

void JackDriverClient::AddSlave(JackDriverInterface* slave)
{
fSlaveList.push_back(slave);
}

void JackDriverClient::RemoveSlave(JackDriverInterface* slave)
{
fSlaveList.remove(slave);
}

void JackDriverClient::ProcessSlaves()
{
list<JackDriverInterface*>::const_iterator it;
for (it = fSlaveList.begin(); it != fSlaveList.end(); it++) {
JackDriverInterface* slave = *it;
slave->Process();
}
}

} // end of namespace

+ 215
- 0
common/JackDriver.h View File

@@ -0,0 +1,215 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackDriver__
#define __JackDriver__

#include "types.h"
#include "JackClientInterface.h"
#include "JackConstants.h"
#include <list>

namespace Jack
{

class JackEngine;
class JackGraphManager;
class JackSynchro;
struct JackEngineControl;
struct JackClientControl;

/*!
\brief The base interface for drivers.
*/

class EXPORT JackDriverInterface
{

public:

JackDriverInterface()
{}
virtual ~JackDriverInterface()
{}

virtual int Open() = 0;

virtual int Open(jack_nframes_t nframes,
jack_nframes_t samplerate,
int capturing,
int playing,
int inchannels,
int outchannels,
bool monitor,
const char* capture_driver_name,
const char* playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency) = 0;

virtual int Attach() = 0;
virtual int Detach() = 0;

virtual int Read() = 0;
virtual int Write() = 0;
virtual int Start() = 0;
virtual int Stop() = 0;
virtual int SetBufferSize(jack_nframes_t nframes) = 0;

virtual int Process() = 0;

virtual void SetMaster(bool onoff) = 0;
virtual bool GetMaster() = 0;
virtual void AddSlave(JackDriverInterface* slave) = 0;
virtual void RemoveSlave(JackDriverInterface* slave) = 0;
virtual void ProcessSlaves() = 0;

virtual bool IsRealTime() = 0;

virtual void PrintState() = 0;
};

/*!
\brief The base interface for drivers clients.
*/

class EXPORT JackDriverClientInterface : public JackDriverInterface, public JackClientInterface
{};

/*!
\brief The base class for drivers clients.
*/

class EXPORT JackDriverClient : public JackDriverClientInterface
{
private:

std::list<JackDriverInterface*> fSlaveList;

protected:

bool fIsMaster;

public:

virtual void SetMaster(bool onoff);
virtual bool GetMaster();
virtual void AddSlave(JackDriverInterface* slave);
virtual void RemoveSlave(JackDriverInterface* slave);
virtual void ProcessSlaves();
};

/*!
\brief The base class for drivers.
*/

class EXPORT JackDriver : public JackDriverClient
{

protected:

char fCaptureDriverName[JACK_CLIENT_NAME_SIZE];
char fPlaybackDriverName[JACK_CLIENT_NAME_SIZE];
jack_nframes_t fCaptureLatency;
jack_nframes_t fPlaybackLatency;
jack_time_t fLastWaitUst;
JackEngine* fEngine;
JackGraphManager* fGraphManager;
JackSynchro** fSynchroTable;
JackEngineControl* fEngineControl;
JackClientControl* fClientControl;

JackClientControl* GetClientControl() const;

public:

JackDriver(const char* name, JackEngine* engine, JackSynchro** table);
JackDriver();
virtual ~JackDriver();

virtual int Open();

virtual int Open(jack_nframes_t nframes,
jack_nframes_t samplerate,
int capturing,
int playing,
int inchannels,
int outchannels,
bool monitor,
const char* capture_driver_name,
const char* playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency);

virtual int Close();

virtual int Process()
{
return 0;
}

virtual int Attach()
{
return 0;
}
virtual int Detach()
{
return 0;
}

virtual int Read()
{
return 0;
}
virtual int Write()
{
return 0;
}

virtual int Start()
{
return 0;
}
virtual int Stop()
{
return 0;
}

virtual int SetBufferSize(jack_nframes_t nframes)
{
return 0;
}

void NotifyXRun(jack_time_t callback_usecs); // XRun notification sent by the driver

virtual bool IsRealTime();

virtual void PrintState()
{}

int ClientNotify(int refnum, const char* name, int notify, int sync, int value)
{
return 0;
}

};

} // end of namespace

#endif

+ 498
- 0
common/JackDriverLoader.cpp View File

@@ -0,0 +1,498 @@
/*
Copyright (C) 2001-2005 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackDriverLoader.h"
#include "JackError.h"
#include <getopt.h>

#ifdef WIN32
#define ADDON_DIR "jackmp" // TO IMPROVE
#else
#include <dirent.h>
#define ADDON_DIR "/usr/local/lib/jackmp" // TO IMPROVE
#endif

static void
jack_print_driver_options (jack_driver_desc_t * desc, FILE *file)
{
unsigned long i;
char arg_default[JACK_DRIVER_PARAM_STRING_MAX + 1];

for (i = 0; i < desc->nparams; i++) {
switch (desc->params[i].type) {
case JackDriverParamInt:
//sprintf (arg_default, "%" PRIi32, desc->params[i].value.i);
sprintf (arg_default, "%" "i", desc->params[i].value.i);
break;
case JackDriverParamUInt:
//sprintf (arg_default, "%" PRIu32, desc->params[i].value.ui);
sprintf (arg_default, "%" "u", desc->params[i].value.ui);
break;
case JackDriverParamChar:
sprintf (arg_default, "%c", desc->params[i].value.c);
break;
case JackDriverParamString:
if (desc->params[i].value.str &&
strcmp (desc->params[i].value.str, "") != 0)
sprintf (arg_default, "%s", desc->params[i].value.str);
else
sprintf (arg_default, "none");
break;
case JackDriverParamBool:
sprintf (arg_default, "%s", desc->params[i].value.i ? "true" : "false");
break;
}

fprintf (file, "\t-%c, --%s \t%s (default: %s)\n",
desc->params[i].character,
desc->params[i].name,
desc->params[i].short_desc,
arg_default);
}
}

static void
jack_print_driver_param_usage (jack_driver_desc_t * desc, unsigned long param, FILE *file)
{
fprintf (file, "Usage information for the '%s' parameter for driver '%s':\n",
desc->params[param].name, desc->name);

fprintf (file, "%s\n", desc->params[param].long_desc);
}

EXPORT int
jack_parse_driver_params (jack_driver_desc_t * desc, int argc, char* argv[], JSList ** param_ptr)
{
struct option * long_options;
char * options, * options_ptr;
unsigned long i;
int opt;
unsigned int param_index;
JSList * params = NULL;
jack_driver_param_t * driver_param;

if (argc <= 1) {
*param_ptr = NULL;
return 0;
}

/* check for help */
if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) {
if (argc > 2) {
for (i = 0; i < desc->nparams; i++) {
if (strcmp (desc->params[i].name, argv[2]) == 0) {
jack_print_driver_param_usage (desc, i, stdout);
return 1;
}
}

fprintf (stderr, "jackd: unknown option '%s' "
"for driver '%s'\n", argv[2],
desc->name);
}

printf ("Parameters for driver '%s' (all parameters are optional):\n", desc->name);
jack_print_driver_options (desc, stdout);
return 1;
}

/* set up the stuff for getopt */
options = (char*)calloc (desc->nparams * 3 + 1, sizeof (char));
long_options = (option*)calloc (desc->nparams + 1, sizeof (struct option));

options_ptr = options;
for (i = 0; i < desc->nparams; i++) {
sprintf (options_ptr, "%c::", desc->params[i].character);
options_ptr += 3;

long_options[i].name = desc->params[i].name;
long_options[i].flag = NULL;
long_options[i].val = desc->params[i].character;
long_options[i].has_arg = optional_argument;
}

/* create the params */
optind = 0;
opterr = 0;
while ((opt = getopt_long(argc, argv, options, long_options, NULL)) != -1) {

if (opt == ':' || opt == '?') {
if (opt == ':') {
fprintf (stderr, "Missing option to argument '%c'\n", optopt);
} else {
fprintf (stderr, "Unknownage with option '%c'\n", optopt);
}

fprintf (stderr, "Options for driver '%s':\n", desc->name);
jack_print_driver_options (desc, stderr);
exit (1);
}

for (param_index = 0; param_index < desc->nparams; param_index++) {
if (opt == desc->params[param_index].character) {
break;
}
}

driver_param = (jack_driver_param_t*)calloc (1, sizeof (jack_driver_param_t));
driver_param->character = desc->params[param_index].character;

if (!optarg && optind < argc &&
strlen(argv[optind]) &&
argv[optind][0] != '-') {
optarg = argv[optind];
}

if (optarg) {
switch (desc->params[param_index].type) {
case JackDriverParamInt:
driver_param->value.i = atoi (optarg);
break;
case JackDriverParamUInt:
driver_param->value.ui = strtoul (optarg, NULL, 10);
break;
case JackDriverParamChar:
driver_param->value.c = optarg[0];
break;
case JackDriverParamString:
strncpy (driver_param->value.str, optarg, JACK_DRIVER_PARAM_STRING_MAX);
break;
case JackDriverParamBool:

/*
if (strcasecmp ("false", optarg) == 0 ||
strcasecmp ("off", optarg) == 0 ||
strcasecmp ("no", optarg) == 0 ||
strcasecmp ("0", optarg) == 0 ||
strcasecmp ("(null)", optarg) == 0 ) {
*/
// steph
if (strcmp ("false", optarg) == 0 ||
strcmp ("off", optarg) == 0 ||
strcmp ("no", optarg) == 0 ||
strcmp ("0", optarg) == 0 ||
strcmp ("(null)", optarg) == 0 ) {
driver_param->value.i = false;

} else {

driver_param->value.i = true;

}
break;
}
} else {
if (desc->params[param_index].type == JackDriverParamBool) {
driver_param->value.i = true;
} else {
driver_param->value = desc->params[param_index].value;
}
}

params = jack_slist_append (params, driver_param);
}

free (options);
free (long_options);

if (param_ptr)
*param_ptr = params;

return 0;
}

EXPORT jack_driver_desc_t *
jack_find_driver_descriptor (JSList * drivers, const char * name)
{
jack_driver_desc_t * desc = 0;
JSList * node;

for (node = drivers; node; node = jack_slist_next (node)) {
desc = (jack_driver_desc_t *) node->data;

if (strcmp (desc->name, name) != 0) {
desc = NULL;
} else {
break;
}
}

return desc;
}

jack_driver_desc_t *
jack_drivers_get_descriptor (JSList * drivers, const char * sofile)
{
jack_driver_desc_t * descriptor, * other_descriptor;
JackDriverDescFunction so_get_descriptor = NULL;
JSList * node;
void * dlhandle;
char * filename;
#ifdef WIN32
int dlerr;
#else
const char * dlerr;
#endif

int err;
char* driver_dir;

if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) {
driver_dir = ADDON_DIR;
}

#ifdef WIN32
if (strcmp(ADDON_DIR, "") == 0) {
char temp_driver_dir1[512];
char temp_driver_dir2[512];
GetCurrentDirectory(512, temp_driver_dir1);
sprintf (temp_driver_dir2, "%s/%s", temp_driver_dir1, ADDON_DIR);
driver_dir = temp_driver_dir2;
}
#endif

filename = (char *)malloc (strlen (driver_dir) + 1 + strlen (sofile) + 1);
sprintf (filename, "%s/%s", driver_dir, sofile);

if ((dlhandle = LoadDriverModule (filename)) == NULL) {
#ifdef WIN32
jack_error ("could not open driver .dll '%s': %ld\n", filename, GetLastError());
#else
jack_error ("could not open driver .so '%s': %s\n", filename, dlerror ());
#endif

free (filename);
return NULL;
}

so_get_descriptor = (JackDriverDescFunction)
GetProc (dlhandle, "driver_get_descriptor");

#ifdef WIN32
if ((so_get_descriptor == NULL) && (dlerr = GetLastError()) != 0) {
fprintf(stderr, "%ld\n", dlerr);
#else
if ((so_get_descriptor == NULL) && (dlerr = dlerror ()) != NULL) {
fprintf(stderr, "%s\n", dlerr);
#endif

UnloadDriverModule (dlhandle);
free (filename);
return NULL;
}

if ((descriptor = so_get_descriptor ()) == NULL) {
jack_error ("driver from '%s' returned NULL descriptor\n", filename);
UnloadDriverModule (dlhandle);
free (filename);
return NULL;
}

#ifdef WIN32
if ((err = UnloadDriverModule (dlhandle)) == 0) {
jack_error ("error closing driver .so '%s': %ld\n", filename, GetLastError ());
}
#else
if ((err = UnloadDriverModule (dlhandle)) != 0) {
jack_error ("error closing driver .so '%s': %s\n", filename, dlerror ());
}
#endif

/* check it doesn't exist already */
for (node = drivers; node; node = jack_slist_next (node)) {
other_descriptor = (jack_driver_desc_t *) node->data;

if (strcmp (descriptor->name, other_descriptor->name) == 0) {
jack_error ("the drivers in '%s' and '%s' both have the name '%s'; using the first\n",
other_descriptor->file, filename, other_descriptor->name);
/* FIXME: delete the descriptor */
free (filename);
return NULL;
}
}

strncpy (descriptor->file, filename, PATH_MAX);
free (filename);
return descriptor;
}

#ifdef WIN32

EXPORT JSList *
jack_drivers_load (JSList * drivers) {
char driver_dir[512];
char dll_filename[512];
WIN32_FIND_DATA filedata;
HANDLE file;
const char * ptr = NULL;
JSList * driver_list = NULL;
jack_driver_desc_t * desc;

GetCurrentDirectory(512, driver_dir);

sprintf (dll_filename, "%s/%s", ADDON_DIR, "*.dll");
file = (HANDLE )FindFirstFile(dll_filename, &filedata);

if (file == INVALID_HANDLE_VALUE) {
printf("error\n");
return NULL;
}

do {
ptr = strrchr (filedata.cFileName, '.');
if (!ptr) {
continue;
}
ptr++;
if (strncmp ("dll", ptr, 3) != 0) {
continue;
}

desc = jack_drivers_get_descriptor (drivers, filedata.cFileName);
if (desc) {
driver_list = jack_slist_append (driver_list, desc);
}

} while ((file = (HANDLE )FindNextFile(file, &filedata)) != 0);

if (!driver_list) {
jack_error ("could not find any drivers in %s!\n", driver_dir);
return NULL;
}

return driver_list;
}

#else

JSList *
jack_drivers_load (JSList * drivers) {
struct dirent * dir_entry;
DIR * dir_stream;
const char * ptr;
int err;
JSList * driver_list = NULL;
jack_driver_desc_t * desc;
char* driver_dir;

if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) {
driver_dir = ADDON_DIR;
}

/* search through the driver_dir and add get descriptors
from the .so files in it */
dir_stream = opendir (driver_dir);
if (!dir_stream) {
jack_error ("could not open driver directory %s: %s\n",
driver_dir, strerror (errno));
return NULL;
}

while ((dir_entry = readdir(dir_stream))) {

/* check the filename is of the right format */
if (strncmp ("jack_", dir_entry->d_name, 5) != 0) {
continue;
}

ptr = strrchr (dir_entry->d_name, '.');
if (!ptr) {
continue;
}
ptr++;
if (strncmp ("so", ptr, 2) != 0) {
continue;
}

desc = jack_drivers_get_descriptor (drivers, dir_entry->d_name);
if (desc) {
driver_list = jack_slist_append (driver_list, desc);
}
}

err = closedir (dir_stream);
if (err) {
jack_error ("error closing driver directory %s: %s\n",
driver_dir, strerror (errno));
}

if (!driver_list) {
jack_error ("could not find any drivers in %s!\n", driver_dir);
return NULL;
}

return driver_list;
}

#endif

jack_driver_info_t *
jack_load_driver (jack_driver_desc_t * driver_desc) {
#ifdef WIN32
int errstr;
#else
const char * errstr;
#endif

jack_driver_info_t *info;

info = (jack_driver_info_t *) calloc (1, sizeof (*info));
info->handle = LoadDriverModule (driver_desc->file);

if (info->handle == NULL) {
#ifdef WIN32
if ((errstr = GetLastError ()) != 0) {
jack_error ("can't load \"%s\": %ld", driver_desc->file,
errstr);
#else
if ((errstr = dlerror ()) != 0) {
jack_error ("can't load \"%s\": %s", driver_desc->file,
errstr);
#endif

} else {
jack_error ("bizarre error loading driver shared "
"object %s", driver_desc->file);
}
goto fail;
}

info->initialize = (initialize)GetProc(info->handle, "driver_initialize");

#ifdef WIN32
if ((info->initialize == NULL) && (errstr = GetLastError ()) != 0) {
#else
if ((info->initialize == NULL) && (errstr = dlerror ()) != 0) {
#endif
jack_error ("no initialize function in shared object %s\n",
driver_desc->file);
goto fail;
}

return info;

fail:
if (info->handle) {
UnloadDriverModule(info->handle);
}
free (info);
return NULL;
}


+ 66
- 0
common/JackDriverLoader.h View File

@@ -0,0 +1,66 @@
/*
Copyright (C) 2001-2005 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackDriverLoader__
#define __JackDriverLoader__

#include "jslist.h"
#include "driver_interface.h"
#include "JackDriver.h"


#ifdef WIN32

#include <windows.h>
#define DRIVER_HANDLE HINSTANCE
#define LoadDriverModule(name) LoadLibrary((name))
#define UnloadDriverModule(handle) (FreeLibrary(((HMODULE)handle)))
#define GetProc(handle, name) GetProcAddress(((HMODULE)handle),(name))

#else

#include <dlfcn.h>
#define DRIVER_HANDLE void*
#define LoadDriverModule(name) dlopen((name), RTLD_NOW | RTLD_GLOBAL)
#define UnloadDriverModule(handle) dlclose((handle))
#define GetProc(handle, name) dlsym((handle), (name))

#endif

typedef jack_driver_desc_t * (*JackDriverDescFunction) ();
typedef Jack::JackDriverClientInterface* (*initialize) (Jack::JackEngine*, Jack::JackSynchro**, const JSList *);

typedef struct _jack_driver_info
{
Jack::JackDriverClientInterface* (*initialize)(Jack::JackEngine*, Jack::JackSynchro**, const JSList *);
DRIVER_HANDLE handle;
}
jack_driver_info_t;

EXPORT jack_driver_desc_t * jack_find_driver_descriptor (JSList * drivers, const char * name);

jack_driver_desc_t * jack_drivers_get_descriptor (JSList * drivers, const char * sofile);

EXPORT JSList * jack_drivers_load (JSList * drivers);

jack_driver_info_t * jack_load_driver (jack_driver_desc_t * driver_desc);

#endif


+ 224
- 0
common/JackDummyDriver.cpp View File

@@ -0,0 +1,224 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackDummyDriver.h"
#include "JackEngineControl.h"
#include "JackGraphManager.h"
#include "driver_interface.h"
#include "JackDriverLoader.h"
#include <iostream>
#include <unistd.h>

namespace Jack
{

int JackDummyDriver::Open(jack_nframes_t nframes,
jack_nframes_t samplerate,
int capturing,
int playing,
int inchannels,
int outchannels,
bool monitor,
const char* capture_driver_name,
const char* playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency)
{
int res = JackAudioDriver::Open(nframes,
samplerate,
capturing,
playing,
inchannels,
outchannels,
monitor,
capture_driver_name,
playback_driver_name,
capture_latency,
playback_latency);
fEngineControl->fPeriod = 0;
fEngineControl->fComputation = 500 * 1000;
fEngineControl->fConstraint = 500 * 1000;
return res;
}

int JackDummyDriver::Process()
{
fLastWaitUst = GetMicroSeconds(); // Take callback date here
JackAudioDriver::Process();
usleep(std::max(0L, long(fWaitTime - (GetMicroSeconds() - fLastWaitUst))));
return 0;
}

int JackDummyDriver::SetBufferSize(jack_nframes_t nframes)
{
fEngineControl->fBufferSize = nframes;
fEngineControl->fPeriodUsecs = jack_time_t(1000000.f / fEngineControl->fSampleRate * fEngineControl->fBufferSize); // In microsec
return 0;
}

void JackDummyDriver::PrintState()
{
std::cout << "JackDummyDriver state" << std::endl;

jack_port_id_t port_index;

std::cout << "Input ports" << std::endl;

for (int i = 0; i < fPlaybackChannels; i++) {
port_index = fCapturePortList[i];
JackPort* port = fGraphManager->GetPort(port_index);
std::cout << port->GetName() << std::endl;
if (fGraphManager->GetConnectionsNum(port_index)) {}
}

std::cout << "Output ports" << std::endl;

for (int i = 0; i < fCaptureChannels; i++) {
port_index = fPlaybackPortList[i];
JackPort* port = fGraphManager->GetPort(port_index);
std::cout << port->GetName() << std::endl;
if (fGraphManager->GetConnectionsNum(port_index)) {}
}
}

} // end of namespace

#ifdef __cplusplus
extern "C"
{
#endif

jack_driver_desc_t * driver_get_descriptor () {
jack_driver_desc_t * desc;
unsigned int i;

desc = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t));
strcpy(desc->name, "dummy");
desc->nparams = 6;
desc->params = (jack_driver_param_desc_t*)calloc (desc->nparams, sizeof (jack_driver_param_desc_t));

i = 0;
strcpy(desc->params[i].name, "capture");
desc->params[i].character = 'C';
desc->params[i].type = JackDriverParamUInt;
desc->params[i].value.ui = 2U;
strcpy(desc->params[i].short_desc, "Number of capture ports");
strcpy(desc->params[i].long_desc, desc->params[i].short_desc);

i++;
strcpy(desc->params[i].name, "playback");
desc->params[i].character = 'P';
desc->params[i].type = JackDriverParamUInt;
desc->params[1].value.ui = 2U;
strcpy(desc->params[i].short_desc, "Number of playback ports");
strcpy(desc->params[i].long_desc, desc->params[i].short_desc);

i++;
strcpy(desc->params[i].name, "rate");
desc->params[i].character = 'r';
desc->params[i].type = JackDriverParamUInt;
desc->params[i].value.ui = 48000U;
strcpy(desc->params[i].short_desc, "Sample rate");
strcpy(desc->params[i].long_desc, desc->params[i].short_desc);

i++;
strcpy(desc->params[i].name, "monitor");
desc->params[i].character = 'm';
desc->params[i].type = JackDriverParamBool;
desc->params[i].value.i = 0;
strcpy(desc->params[i].short_desc, "Provide monitor ports for the output");
strcpy(desc->params[i].long_desc, desc->params[i].short_desc);

i++;
strcpy(desc->params[i].name, "period");
desc->params[i].character = 'p';
desc->params[i].type = JackDriverParamUInt;
desc->params[i].value.ui = 1024U;
strcpy(desc->params[i].short_desc, "Frames per period");
strcpy(desc->params[i].long_desc, desc->params[i].short_desc);

i++;
strcpy(desc->params[i].name, "wait");
desc->params[i].character = 'w';
desc->params[i].type = JackDriverParamUInt;
desc->params[i].value.ui = 21333U;
strcpy(desc->params[i].short_desc,
"Number of usecs to wait between engine processes");
strcpy(desc->params[i].long_desc, desc->params[i].short_desc);

return desc;
}

Jack::JackDriverClientInterface* driver_initialize(Jack::JackEngine* engine, Jack::JackSynchro** table, const JSList* params) {
jack_nframes_t sample_rate = 48000;
jack_nframes_t period_size = 1024;
unsigned int capture_ports = 2;
unsigned int playback_ports = 2;
unsigned long wait_time = 0;
const JSList * node;
const jack_driver_param_t * param;
bool monitor = false;

for (node = params; node; node = jack_slist_next (node)) {
param = (const jack_driver_param_t *) node->data;

switch (param->character) {

case 'C':
capture_ports = param->value.ui;
break;

case 'P':
playback_ports = param->value.ui;
break;

case 'r':
sample_rate = param->value.ui;
break;

case 'p':
period_size = param->value.ui;
break;

case 'w':
wait_time = param->value.ui;
break;

case 'm':
monitor = param->value.i;
break;
}
}

if (wait_time == 0) // Not set
wait_time = (unsigned long)((((float)period_size) / ((float)sample_rate)) * 1000000.0f);

Jack::JackDriverClientInterface* driver = new Jack::JackThreadedDriver(new Jack::JackDummyDriver("dummy_pcm", engine, table, wait_time));
if (driver->Open(period_size, sample_rate, 1, 1, capture_ports, playback_ports, monitor, "dummy", "dummy", 0, 0) == 0) {
return driver;
} else {
delete driver;
return NULL;
}
}

#ifdef __cplusplus
}
#endif

+ 68
- 0
common/JackDummyDriver.h View File

@@ -0,0 +1,68 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackDummyDriver__
#define __JackDummyDriver__

#include "JackAudioDriver.h"
#include "JackThreadedDriver.h"
#include "JackTime.h"

namespace Jack
{

/*!
\brief The dummy driver.
*/

class JackDummyDriver : public JackAudioDriver
{
private:

long fWaitTime;

public:

JackDummyDriver(const char* name, JackEngine* engine, JackSynchro** table, unsigned long wait_time)
: JackAudioDriver(name, engine, table), fWaitTime(wait_time)
{}
virtual ~JackDummyDriver()
{}

int Open(jack_nframes_t frames_per_cycle,
jack_nframes_t rate,
int capturing,
int playing,
int chan_in,
int chan_out,
bool monitor,
const char* capture_driver_name,
const char* playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency);

int Process();
int SetBufferSize(jack_nframes_t nframe);
void PrintState();
};

} // end of namespace

#endif

+ 644
- 0
common/JackEngine.cpp View File

@@ -0,0 +1,644 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <iostream>
#include <fstream>
#include <assert.h>

#include "JackEngine.h"
#include "JackExternalClient.h"
#include "JackEngineControl.h"
#include "JackClientControl.h"
#include "JackEngineTiming.h"
#include "JackGlobals.h"
#include "JackChannel.h"
#include "JackSyncInterface.h"

namespace Jack
{

JackEngine::JackEngine(JackGraphManager* manager, JackSynchro** table, JackEngineControl* control, JackSyncInterface* signal, bool sync, long time_out_ms, bool rt, long priority, bool ve)
{
fGraphManager = manager;
fSynchroTable = table;
fEngineControl = control;
fEngineControl->fSyncMode = sync;
fEngineControl->fTimeOutUsecs = time_out_ms * 1000;
fEngineControl->fRealTime = rt;
fEngineControl->fPriority = priority;
fEngineControl->fVerbose = ve;
verbose = ve;
fChannel = JackGlobals::MakeServerNotifyChannel();
fTiming = new JackEngineTiming(fClientTable, fGraphManager, fEngineControl);
fSignal = signal;
for (int i = 0; i < CLIENT_NUM; i++)
fClientTable[i] = 0;
fTiming->ClearTimeMeasures();
fTiming->ResetRollingUsecs();
}

JackEngine::~JackEngine()
{
delete fChannel;
delete fTiming;
}

//-------------------
// Client management
//-------------------

int JackEngine::Open()
{
JackLog("JackEngine::Open\n");

// Open audio thread => request thread communication channel
if (fChannel->Open() < 0) {
jack_error("Cannot connect to server");
return -1;
} else {
return 0;
}
}

int JackEngine::Close()
{
JackLog("JackEngine::Close\n");
fChannel->Close();

// Close (possibly) remaining clients (RT is stopped)
for (int i = 0; i < CLIENT_NUM; i++) {
JackClientInterface* client = fClientTable[i];
if (client) {
JackLog("JackEngine::Close remaining client %ld\n", i);
ClientCloseAux(i, client, false);
client->Close();
delete client;
}
}

return 0;
}

//------------------
// Graph management
//------------------

void JackEngine::Process(jack_time_t callback_usecs)
{
// Transport
fEngineControl->fTransport.CycleBegin(fEngineControl->fSampleRate, callback_usecs);

//JackLog("Process: callback_usecs %lld\n",callback_usecs/1000);

// Timing
fEngineControl->fFrameTimer.IncFrameTime(fEngineControl->fBufferSize, callback_usecs, fEngineControl->fPeriodUsecs);
fTiming->UpdateTiming(callback_usecs);

// Graph
if (fGraphManager->IsFinishedGraph()) {
fLastSwitchUsecs = callback_usecs;
if (fGraphManager->RunNextGraph()) // True if the graph actually switched to a new state
fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kGraphOrderCallback, 0);
fSignal->SignalAll(); // Signal for threads waiting for next cycle
} else {
JackLog("Process: graph not finished!\n");
if (callback_usecs > fLastSwitchUsecs + fEngineControl->fTimeOutUsecs) {
JackLog("Process: switch to next state %ld\n", long(callback_usecs - fLastSwitchUsecs));
//RemoveZombifiedClients(callback_usecs); TODO
fLastSwitchUsecs = callback_usecs;
if (fGraphManager->RunNextGraph())
fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kGraphOrderCallback, 0);
fSignal->SignalAll(); // Signal for threads waiting for next cycle
} else {
JackLog("Process: waiting to switch %ld\n", long(callback_usecs - fLastSwitchUsecs));
if (callback_usecs < fLastSwitchUsecs + 2 * fEngineControl->fPeriodUsecs) // Signal XRun only for the first failling cycle
CheckXRun(callback_usecs);
fGraphManager->RunCurrentGraph();
}
}

// Transport
fEngineControl->fTransport.CycleEnd(fClientTable, fEngineControl->fSampleRate, fEngineControl->fBufferSize);
}

/*
Client that finish *after* the callback date are considered late even if their output buffers may have been
correctly mixed in the time window: callbackUsecs <==> Read <==> Write.
*/

void JackEngine::CheckXRun(jack_time_t callback_usecs) // REVOIR les conditions de fin
{
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++)
{
JackClientInterface* client = fClientTable[i];
if (client && client->GetClientControl()->fActive) {
JackClientTiming* timing = fGraphManager->GetClientTiming(i);
jack_client_state_t status = timing->fStatus;
jack_time_t finished_date = timing->fFinishedAt;
if (status != NotTriggered && status != Finished) {
jack_error("JackEngine::XRun: client = %s was not runned: state = %ld", client->GetClientControl()->fName, status);
//fChannel->ClientNotify(i, kXRunCallback, 0); // Notify the failing client
fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0); // Notify all clients
}
if (status == Finished && (long)(finished_date - callback_usecs) > 0) {
jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName);
//fChannel->ClientNotify(i, kXRunCallback, 0); // Notify the failing client
fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0); // Notify all clients
}
}
}
}

//---------------
// Zombification
//---------------

bool JackEngine::IsZombie(JackClientInterface* client, jack_time_t current_time)
{
return ((current_time - fGraphManager->GetClientTiming(client->GetClientControl()->fRefNum)->fFinishedAt) > 2 * fEngineControl->fTimeOutUsecs); // A VERIFIER
}

// TODO : check what happens with looped sub-graph....
void JackEngine::GetZombifiedClients(bool zombi_clients[CLIENT_NUM], jack_time_t current_time)
{
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
JackClientInterface* client1 = fClientTable[i];
if (client1 && IsZombie(client1, current_time)) {
JackLog("JackEngine::GetZombifiedClients: %s\n", client1->GetClientControl()->fName);
zombi_clients[i] = true; // Assume client is dead
// If another dead client is connected to the scanned one, then the scanned one is not the first of the dead subgraph
for (int j = REAL_REFNUM; j < CLIENT_NUM; j++) {
JackClientInterface* client2 = fClientTable[j];
if (client2 && IsZombie(client2, current_time) && fGraphManager->IsDirectConnection(j, i)) {
zombi_clients[i] = false;
break;
}
}
} else {
zombi_clients[i] = false;
}
}
}

void JackEngine::RemoveZombifiedClients(jack_time_t current_time)
{
bool zombi_clients[CLIENT_NUM];
GetZombifiedClients(zombi_clients, current_time);

for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
if (zombi_clients[i] && !fClientTable[i]->GetClientControl()->fZombie) {
fClientTable[i]->GetClientControl()->fZombie = true;
JackLog("RemoveZombifiedCients: name = %s\n", fClientTable[i]->GetClientControl()->fName);
fGraphManager->DirectDisconnect(FREEWHEEL_DRIVER_REFNUM, i);
fGraphManager->DirectDisconnect(i, FREEWHEEL_DRIVER_REFNUM);
fGraphManager->DisconnectAllPorts(i);
fChannel->ClientNotify(i, JackNotifyChannelInterface::kZombifyClient, 0); // Signal engine
}
}
}

void JackEngine::ZombifyClient(int refnum)
{
NotifyClient(refnum, JackNotifyChannelInterface::kZombifyClient, false, 0);
}

//---------------
// Notifications
//---------------

void JackEngine::NotifyClient(int refnum, int event, int sync, int value)
{
JackClientInterface* client = fClientTable[refnum];
// The client may be notified by the RT thread while closing
if (client && (client->ClientNotify(refnum, client->GetClientControl()->fName, event, sync, value) < 0)) {
jack_error("NotifyClient fails name = %s event = %ld = val = %ld", client->GetClientControl()->fName, event, value);
} else {
JackLog("JackEngine::NotifyClient: client not available anymore\n");
}
}

void JackEngine::NotifyClients(int event, int sync, int value)
{
for (int i = 0; i < CLIENT_NUM; i++) {
JackClientInterface* client = fClientTable[i];
if (client && (client->ClientNotify(i, client->GetClientControl()->fName, event, sync, value) < 0)) {
jack_error("NotifyClient fails name = %s event = %ld = val = %ld", client->GetClientControl()->fName, event, value);
}
}
}

int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum)
{
// Notify existing clients of the new client and new client of existing clients.
for (int i = 0; i < CLIENT_NUM; i++) {
JackClientInterface* old_client = fClientTable[i];
if (old_client) {
if (old_client->ClientNotify(refnum, name, JackNotifyChannelInterface::kAddClient, true, 0) < 0)
return -1;
if (new_client->ClientNotify(i, old_client->GetClientControl()->fName, JackNotifyChannelInterface::kAddClient, true, 0) < 0)
return -1;
}
}

return 0;
}

void JackEngine::NotifyRemoveClient(const char* name, int refnum)
{
// Notify existing clients (including the one beeing suppressed) of the removed client
for (int i = 0; i < CLIENT_NUM; i++) {
JackClientInterface* client = fClientTable[i];
if (client) {
client->ClientNotify(refnum, name, JackNotifyChannelInterface::kRemoveClient, true, 0);
}
}
}

// Coming from the driver
void JackEngine::NotifyXRun(jack_time_t callback_usecs)
{
// Use the audio thread => request thread communication channel
fEngineControl->fFrameTimer.ResetFrameTime(fEngineControl->fSampleRate, callback_usecs, fEngineControl->fPeriodUsecs);
fChannel->ClientNotify(ALL_CLIENTS, JackNotifyChannelInterface::kXRunCallback, 0);
}

void JackEngine::NotifyXRun(int refnum)
{
if (refnum == ALL_CLIENTS) {
NotifyClients(JackNotifyChannelInterface::kXRunCallback, false, 0);
} else {
NotifyClient(refnum, JackNotifyChannelInterface::kXRunCallback, false, 0);
}
}

void JackEngine::NotifyGraphReorder()
{
NotifyClients(JackNotifyChannelInterface::kGraphOrderCallback, false, 0);
}

void JackEngine::NotifyBufferSize(jack_nframes_t nframes)
{
NotifyClients(JackNotifyChannelInterface::kBufferSizeCallback, true, nframes);
}

void JackEngine::NotifyFreewheel(bool onoff)
{
fEngineControl->fRealTime = !onoff;
NotifyClients((onoff ? JackNotifyChannelInterface::kStartFreewheel : JackNotifyChannelInterface::kStopFreewheel), true, 0);
}

void JackEngine::NotifyPortRegistation(jack_port_id_t port_index, bool onoff)
{
NotifyClients((onoff ? JackNotifyChannelInterface::kPortRegistrationOn : JackNotifyChannelInterface::kPortRegistrationOff), false, port_index);
}

//-------------------
// Client management
//-------------------

bool JackEngine::ClientCheckName(const char* name)
{
for (int i = 0; i < CLIENT_NUM; i++) {
JackClientInterface* client = fClientTable[i];
if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
return true;
}

return false;
}

// Used for external clients
int JackEngine::ClientNew(const char* name, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager)
{
if (ClientCheckName(name)) {
jack_error("client %s already registered", name);
return -1;
}

JackExternalClient* client = new JackExternalClient();
if (ClientExternalNew(name, ref, shared_engine, shared_client, shared_graph_manager, client) < 0) {
delete client;
return -1;
}

return 0;
}

// Used for external clients
int JackEngine::ClientExternalNew(const char* name, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager, JackExternalClient* client)
{
JackLog("JackEngine::ClientNew: name %s \n", name);
int refnum = fGraphManager->AllocateRefNum();

if (refnum < 0) {
jack_error("No more refnum available");
return -1;
}

if (!fSynchroTable[refnum]->Allocate(name, 0)) {
jack_error("Cannot allocate synchro");
goto error;
}

if (client->Open(name, refnum, shared_client) < 0) {
jack_error("Cannot open client");
goto error;
}

if (!fSignal->TimedWait(5 * 1000000)) {
// Failure if RT thread is not running (problem with the driver...)
jack_error("Driver is not running");
goto error;
}

if (NotifyAddClient(client, name, refnum) < 0) {
jack_error("Cannot notify add client");
goto error;
}

fClientTable[refnum] = client;
fTiming->ResetRollingUsecs();
*shared_engine = fEngineControl->GetShmIndex();
*shared_graph_manager = fGraphManager->GetShmIndex();
*ref = refnum;
return 0;

error:
fGraphManager->ReleaseRefNum(refnum);
ClientCloseAux(refnum, client, false);
client->Close();
return -1;
}

// Used for server driver clients
int JackEngine::ClientInternalNew(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client)
{
JackLog("JackEngine::ClientInternalNew: name %s\n", name);
int refnum = fGraphManager->AllocateRefNum();

if (refnum < 0) {
jack_error("No more refnum available");
return -1;
}

if (!fSynchroTable[refnum]->Allocate(name, 0)) {
jack_error("Cannot allocate synchro");
goto error;
}

if (NotifyAddClient(client, name, refnum) < 0) {
jack_error("Cannot notify add client");
goto error;
}

fClientTable[refnum] = client;
fTiming->ResetRollingUsecs();
*shared_engine = fEngineControl;
*shared_manager = fGraphManager;
*ref = refnum;
return 0;

error:
fGraphManager->ReleaseRefNum(refnum);
return -1;
}

// Used for externall clients
int JackEngine::ClientClose(int refnum)
{
JackClientInterface* client = fClientTable[refnum];
if (client) {
fEngineControl->fTransport.ResetTimebase(refnum);
int res = ClientCloseAux(refnum, client, true);
client->Close();
delete client;
return res;
} else {
return -1;
}
}

// Used for server internal clients
int JackEngine::ClientInternalClose(int refnum)
{
JackClientInterface* client = fClientTable[refnum];
return (client) ? ClientCloseAux(refnum, client, true) : -1;
}

// Used for drivers that close when the RT thread is stopped
int JackEngine::ClientInternalCloseIm(int refnum)
{
JackClientInterface* client = fClientTable[refnum];
return (client) ? ClientCloseAux(refnum, client, false) : -1;
}

int JackEngine::ClientCloseAux(int refnum, JackClientInterface* client, bool wait)
{
JackLog("JackEngine::ClientCloseAux ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);

// Remove the client from the table
fClientTable[refnum] = NULL;

// Remove ports
fGraphManager->RemoveAllPorts(refnum);

// Wait until next cycle to be sure client is not used anymore
if (wait) {
if (!fSignal->TimedWait(fEngineControl->fTimeOutUsecs * 2)) { // Must wait at least until a switch occurs in Process, even in case of graph end failure
JackLog("JackEngine::ClientCloseAux wait error ref = %ld \n", refnum);
}
}

// Notify running clients
NotifyRemoveClient(client->GetClientControl()->fName, client->GetClientControl()->fRefNum);

// Cleanup...
fSynchroTable[refnum]->Destroy();
fGraphManager->ReleaseRefNum(refnum);
fTiming->ResetRollingUsecs();
return 0;
}

int JackEngine::ClientActivate(int refnum)
{
JackClientInterface* client = fClientTable[refnum];
assert(fClientTable[refnum]);
JackLog("JackEngine::ClientActivate ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
// Wait for graph state change to be effective
if (!fSignal->TimedWait(fEngineControl->fPeriodUsecs * 10)) {
JackLog("JackEngine::ClientActivate wait error ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
return -1;
} else {
return 0;
}
}

// May be called without client
int JackEngine::ClientDeactivate(int refnum)
{
JackClientInterface* client = fClientTable[refnum];
if (client == NULL)
return -1;

JackLog("JackEngine::ClientDeactivate ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
fGraphManager->DisconnectAllPorts(refnum);
// Wait for graph state change to be effective
if (!fSignal->TimedWait(fEngineControl->fPeriodUsecs * 10)) {
JackLog("JackEngine::ClientDeactivate wait error ref = %ld name = %s\n", refnum, client->GetClientControl()->fName);
return -1;
} else {
return 0;
}
}

//-----------------
// Port management
//-----------------

int JackEngine::PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index)
{
JackLog("JackEngine::PortRegister ref = %ld name = %s flags = %d buffer_size = %d\n", refnum, name, flags, buffer_size);
assert(fClientTable[refnum]);

*port_index = fGraphManager->AllocatePort(refnum, name, (JackPortFlags)flags);
if (*port_index != NO_PORT) {
NotifyPortRegistation(*port_index, true);
return 0;
} else {
return -1;
}
}

int JackEngine::PortUnRegister(int refnum, jack_port_id_t port_index)
{
JackLog("JackEngine::PortUnRegister ref = %ld port_index = %ld\n", refnum, port_index);
assert(fClientTable[refnum]);

if (fGraphManager->RemovePort(refnum, port_index) == 0) {
fGraphManager->ReleasePort(port_index);
NotifyPortRegistation(port_index, false);
return 0;
} else {
return -1;
}
}

int JackEngine::PortConnect(int refnum, const char* src, const char* dst)
{
JackLog("JackEngine::PortConnect src = %s dst = %s\n", src, dst);
jack_port_id_t port_src, port_dst;

return (fGraphManager->CheckPorts(src, dst, &port_src, &port_dst) < 0)
? -1
: PortConnect(refnum, port_src, port_dst);
}

int JackEngine::PortDisconnect(int refnum, const char* src, const char* dst)
{
JackLog("JackEngine::PortDisconnect src = %s dst = %s\n", src, dst);
jack_port_id_t port_src, port_dst;

return (fGraphManager->CheckPorts(src, dst, &port_src, &port_dst) < 0)
? -1
: fGraphManager->Disconnect(port_src, port_dst);
}

int JackEngine::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
{
JackLog("JackEngine::PortConnect src = %d dst = %d\n", src, dst);
JackClientInterface* client;
int ref;

if (fGraphManager->CheckPorts(src, dst) < 0)
return -1;

ref = fGraphManager->GetOutputRefNum(src);
assert(ref >= 0);
client = fClientTable[ref];
assert(client);
if (!client->GetClientControl()->fActive) {
jack_error("Cannot connect ports owned by inactive clients:"
" \"%s\" is not active", client->GetClientControl()->fName);
return -1;
}

ref = fGraphManager->GetInputRefNum(dst);
assert(ref >= 0);
client = fClientTable[ref];
assert(client);
if (!client->GetClientControl()->fActive) {
jack_error("Cannot connect ports owned by inactive clients:"
" \"%s\" is not active", client->GetClientControl()->fName);
return -1;
}

return fGraphManager->Connect(src, dst);
}

int JackEngine::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
{
JackLog("JackEngine::PortDisconnect src = %d dst = %d\n", src, dst);

if (dst == ALL_PORTS) {
return (fGraphManager->CheckPort(src) < 0)
? -1
: fGraphManager->DisconnectAll(src);
} else {
return (fGraphManager->CheckPorts(src, dst) < 0)
? -1
: fGraphManager->Disconnect(src, dst);
}
}

//----------------------
// Transport management
//----------------------

int JackEngine::ReleaseTimebase(int refnum)
{
return fEngineControl->fTransport.ResetTimebase(refnum);
}

int JackEngine::SetTimebaseCallback(int refnum, int conditional)
{
return fEngineControl->fTransport.SetTimebase(refnum, conditional);
}

//-----------
// Debugging
//-----------

void JackEngine::PrintState()
{
std::cout << "Engine State" << std::endl;

for (int i = 0; i < CLIENT_NUM; i++) {
JackClientInterface* client = fClientTable[i];
if (client)
std::cout << "Client : " << client->GetClientControl()->fName << " : " << i << std::endl;
}

//fGraphManager->PrintState();
fTiming->PrintState();
}

} // end of namespace


+ 119
- 0
common/JackEngine.h View File

@@ -0,0 +1,119 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackEngine__
#define __JackEngine__

#include "JackConstants.h"
#include "JackGraphManager.h"
#include "JackSynchro.h"
#include "JackTransportEngine.h"

namespace Jack
{

class JackClientInterface;
struct JackEngineControl;
class JackServerNotifyChannelInterface;
class JackEngineTiming;
class JackExternalClient;
class JackSyncInterface;

/*!
\brief Engine description.
*/

class JackEngine
{
private:

JackGraphManager* fGraphManager;
JackEngineControl* fEngineControl;
JackClientInterface* fClientTable[CLIENT_NUM];
JackSynchro** fSynchroTable;
JackServerNotifyChannelInterface* fChannel; /*! To communicate between the RT thread and server */
JackEngineTiming* fTiming;
JackSyncInterface* fSignal;
jack_time_t fLastSwitchUsecs;

int ClientCloseAux(int refnum, JackClientInterface* client, bool wait);
void CheckXRun(jack_time_t callback_usecs);
int NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum);
void NotifyRemoveClient(const char* name, int refnum);
bool IsZombie(JackClientInterface* client, jack_time_t current_time);
void RemoveZombifiedClients(jack_time_t current_time);
void GetZombifiedClients(bool clients[CLIENT_NUM], jack_time_t current_time);

public:

JackEngine(JackGraphManager* manager, JackSynchro** table, JackEngineControl* control, JackSyncInterface* signal, bool sync, long time_out_ms, bool rt, long priority, bool verbose);
virtual ~JackEngine();

int Open();
int Close();

// Client management
bool ClientCheckName(const char* name);
int ClientNew(const char* name, int* refnum, int* shared_engine, int* shared_client, int* shared_graph_manager);
int ClientExternalNew(const char* name, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager, JackExternalClient* client);
int ClientInternalNew(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client);

int ClientClose(int refnum);
int ClientInternalClose(int refnum);
int ClientInternalCloseIm(int refnum);

int ClientActivate(int refnum);
int ClientDeactivate(int refnum);

// Port management
int PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index);
int PortUnRegister(int refnum, jack_port_id_t port);

int PortConnect(int refnum, const char* src, const char* dst);
int PortDisconnect(int refnum, const char* src, const char* dst);

int PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst);
int PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst);

// Transport management
int ReleaseTimebase(int refnum);
int SetTimebaseCallback(int refnum, int conditional);

// Graph
void Process(jack_time_t callback_usecs);
void ZombifyClient(int refnum);

// Notifications
void NotifyClient(int refnum, int event, int sync, int value);
void NotifyClients(int event, int sync, int value);
void NotifyXRun(jack_time_t callback_usecs);
void NotifyXRun(int refnum);
void NotifyGraphReorder();
void NotifyBufferSize(jack_nframes_t nframes);
void NotifyFreewheel(bool onoff);
void NotifyPortRegistation(jack_port_id_t port_index, bool onoff);

void PrintState();
};


} // end of namespace

#endif


+ 58
- 0
common/JackEngineControl.h View File

@@ -0,0 +1,58 @@
/*
Copyright (C) 2003 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackEngineControl__
#define __JackEngineControl__

#include "JackShmMem.h"
#include "JackFrameTimer.h"
#include "JackTransportEngine.h"
#include "types.h"

namespace Jack
{

/*!
\brief Engine control in shared memory.
*/

struct JackEngineControl : public JackShmMem
{
jack_nframes_t fBufferSize;
jack_nframes_t fSampleRate;
bool fRealTime;
int32_t fPriority;
float fCPULoad;
jack_time_t fPeriodUsecs;
jack_time_t fTimeOutUsecs;
UInt64 fPeriod;
UInt64 fComputation;
UInt64 fConstraint;
JackFrameTimer fFrameTimer;
JackTransportEngine fTransport;
bool fSyncMode;
bool fVerbose;
};


} // end of namespace


#endif

+ 229
- 0
common/JackEngineTiming.cpp View File

@@ -0,0 +1,229 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackEngineTiming.h"
#include "JackClientInterface.h"
#include "JackEngineControl.h"
#include "JackClientControl.h"
#include <math.h>
#include <algorithm>
#include <iostream>
//#include <fstream>
#include <assert.h>

namespace Jack
{

inline jack_time_t MAX(jack_time_t a, jack_time_t b)
{
return (a < b) ? b : a;
}

JackEngineTiming::JackEngineTiming(JackClientInterface** table, JackGraphManager* manager, JackEngineControl* control)
{
fClientTable = table;
fGraphManager = manager;
fEngineControl = control;
fLastTime = 0;
fCurTime = 0;
fProcessTime = 0;
fLastProcessTime = 0;
fSpareUsecs = 0;
fMaxUsecs = 0;
fAudioCycle = 0;
}

void JackEngineTiming::UpdateTiming(jack_time_t callback_usecs)
{
GetTimeMeasure(callback_usecs);
CalcCPULoad();
}

void JackEngineTiming::CalcCPULoad()
{
jack_time_t lastCycleEnd = fLastProcessTime;

for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
JackClientInterface* client = fClientTable[i];
JackClientTiming* timing = fGraphManager->GetClientTiming(i);
if (client && client->GetClientControl()->fActive && timing->fStatus == Finished) {
lastCycleEnd = MAX(lastCycleEnd, timing->fFinishedAt);
}
}

/* store the execution time for later averaging */
fRollingClientUsecs[fRollingClientUsecsIndex++] = lastCycleEnd - fLastTime;

if (fRollingClientUsecsIndex >= JACK_ENGINE_ROLLING_COUNT) {
fRollingClientUsecsIndex = 0;
}

/* every so often, recompute the current maximum use over the
last JACK_ENGINE_ROLLING_COUNT client iterations.
*/

if (++fRollingClientUsecsCnt % fRollingInterval == 0) {

jack_time_t maxUsecs = 0;
for (int i = 0; i < JACK_ENGINE_ROLLING_COUNT; i++) {
maxUsecs = MAX(fRollingClientUsecs[i], maxUsecs);
}

fMaxUsecs = MAX(fMaxUsecs, maxUsecs);
fSpareUsecs = jack_time_t((maxUsecs < fEngineControl->fPeriodUsecs) ? fEngineControl->fPeriodUsecs - maxUsecs : 0);
fEngineControl->fCPULoad
= ((1.0f - (float(fSpareUsecs) / float(fEngineControl->fPeriodUsecs))) * 50.0f + (fEngineControl->fCPULoad * 0.5f));
}
}

void JackEngineTiming::ResetRollingUsecs()
{
memset(fRollingClientUsecs, 0, sizeof(fRollingClientUsecs));
fRollingClientUsecsIndex = 0;
fRollingClientUsecsCnt = 0;
fSpareUsecs = 0;
fRollingInterval = (int)floor((JACK_ENGINE_ROLLING_INTERVAL * 1000.0f) / fEngineControl->fPeriodUsecs);
}

void JackEngineTiming::GetTimeMeasure(jack_time_t callbackUsecs)
{
int pos = (++fAudioCycle) % TIME_POINTS;

fLastTime = fCurTime;
fCurTime = callbackUsecs;

fLastProcessTime = fProcessTime;
fProcessTime = GetMicroSeconds();

if (fLastTime > 0) {
fMeasure[pos].fEngineTime = fLastTime;
fMeasure[pos].fAudioCycle = fAudioCycle;

for (int i = 0; i < CLIENT_NUM; i++) {
JackClientInterface* client = fClientTable[i];
JackClientTiming* timing = fGraphManager->GetClientTiming(i);
if (client && client->GetClientControl()->fActive) {
fMeasure[pos].fClientTable[i].fRefNum = i;
fMeasure[pos].fClientTable[i].fSignaledAt = timing->fSignaledAt;
fMeasure[pos].fClientTable[i].fAwakeAt = timing->fAwakeAt;
fMeasure[pos].fClientTable[i].fFinishedAt = timing->fFinishedAt;
fMeasure[pos].fClientTable[i].fStatus = timing->fStatus;
}
}
}
}

void JackEngineTiming::PrintState()
{
jack_time_t prevtime, time;
prevtime = time = fMeasure[0].fEngineTime;
int cycle, prevcycle = fMeasure[0].fAudioCycle;
/*

std::ofstream f("measure.txt");

if (f.is_open()) {

//std::cout << "---------------------------------------------" << std::endl;

for (int i = 0; i < CLIENT_NUM; i++) { // client
JackClientInterface* client = fClientTable[i];
if (client && client->GetClientControl()->fActive) {
// f << "Client : " << i << std::endl;
long maxsignalledat = 0;
long maxawakedat = 0;
long maxfinisheddat = 0;
bool max = false;
prevtime = fMeasure[0].fEngineTime;
prevcycle = fMeasure[0].fAudioCycle;

// TODO
for (int j = 0; j < TIME_POINTS; j++) { // measure
time = fMeasure[j].fEngineTime;
cycle = fMeasure[j].fAudioCycle;

if (fMeasure[j].fClientTable[i].fRefNum > 0) {

if (fMeasure[j].fClientTable[i].fStatus != Finished) {
f << "error status " << '\t'
<< prevtime << '\t'
<< time << '\t'
<< fMeasure[j + 1].fEngineTime << '\t'
<< prevcycle << '\t'
<< cycle << '\t'
<< fMeasure[j].fClientTable[i].fSignaledAt << '\t'
<< fMeasure[j].fClientTable[i].fAwakeAt << '\t'
<< fMeasure[j].fClientTable[i].fFinishedAt
<< std::endl;
}

if (long(time - prevtime) > 0) {

f << long(time - prevtime) << '\t'
<< fMeasure[j].fClientTable[i].fSignaledAt - time << '\t'
<< fMeasure[j].fClientTable[i].fAwakeAt - time << '\t'
<< fMeasure[j].fClientTable[i].fFinishedAt - time << '\t'
<< fMeasure[j].fClientTable[i].fStatus
<< std::endl;
} else {
f << "error time : " << j << " " << long(time - prevtime) << std::endl;
}

maxsignalledat = MAX(maxsignalledat, long(fMeasure[j].fClientTable[i].fSignaledAt - time));
maxawakedat = MAX(maxawakedat, long(fMeasure[j].fClientTable[i].fAwakeAt - time));
maxfinisheddat = MAX(maxfinisheddat, long(fMeasure[j].fClientTable[i].fFinishedAt - time));
max = true;
}
prevtime = time;
prevcycle = cycle;
}

f << std::endl;
if (max) {
f << "maxsignalledat: " << maxsignalledat
<< '\t' << "maxawakedat: " << maxawakedat
<< '\t' << "maxfinisheddat: " << maxfinisheddat
<< '\t' << std::endl;
}
}
}

f.close();
}
*/
}

void JackEngineTiming::ClearTimeMeasures()
{
for (int i = 0; i < TIME_POINTS; i++) {
for (int j = 0; j < CLIENT_NUM; j++) {
fMeasure[i].fClientTable[j].fRefNum = 0;
fMeasure[i].fClientTable[j].fSignaledAt = 0;
fMeasure[i].fClientTable[j].fAwakeAt = 0;
fMeasure[i].fClientTable[j].fFinishedAt = 0;
}
}
fLastTime = fCurTime = 0;
}


} // end of namespace


+ 105
- 0
common/JackEngineTiming.h View File

@@ -0,0 +1,105 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackEngineTiming__
#define __JackEngineTiming__

#include "types.h"
#include "JackGraphManager.h"

namespace Jack
{

#define TIME_POINTS 1000
#define JACK_ENGINE_ROLLING_COUNT 32
#define JACK_ENGINE_ROLLING_INTERVAL 1024

class JackClientInterface;
struct JackEngineControl;

/*!
\brief Timing stucture for a client.
*/

struct JackTimingMeasureClient
{
int fRefNum;
jack_time_t fSignaledAt;
jack_time_t fAwakeAt;
jack_time_t fFinishedAt;
jack_client_state_t fStatus;
};

/*!
\brief Timing stucture for a table of clients.
*/

struct JackTimingMeasure
{
long fAudioCycle;
jack_time_t fEngineTime;
JackTimingMeasureClient fClientTable[CLIENT_NUM];
};

/*!
\brief Engine timing management.
*/

class JackEngineTiming
{
private:

JackClientInterface** fClientTable;
JackGraphManager* fGraphManager;
JackEngineControl* fEngineControl;

JackTimingMeasure fMeasure[TIME_POINTS];
jack_time_t fLastTime;
jack_time_t fCurTime;
jack_time_t fProcessTime;
jack_time_t fLastProcessTime;
jack_time_t fSpareUsecs;
jack_time_t fMaxUsecs;
uint32_t fAudioCycle;

jack_time_t fRollingClientUsecs[JACK_ENGINE_ROLLING_COUNT];
int fRollingClientUsecsCnt;
int fRollingClientUsecsIndex;
int fRollingInterval;

void CalcCPULoad();
void GetTimeMeasure(jack_time_t callback_usecs);

public:

JackEngineTiming(JackClientInterface** table, JackGraphManager* manager, JackEngineControl* control);
virtual ~JackEngineTiming()
{}

void UpdateTiming(jack_time_t callback_usecs);
void ResetRollingUsecs();

void ClearTimeMeasures();
void PrintState();
};

} // end of namespace

#endif


+ 48
- 0
common/JackError.c View File

@@ -0,0 +1,48 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <stdarg.h>
#include <stdio.h>
#include "JackError.h"

EXPORT void jack_error (const char *fmt, ...)
{
va_list ap;
char buffer[300];
va_start(ap, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, ap);
//jack_error_callback(buffer);
fprintf(stderr, "%s\n", buffer);
va_end(ap);
}

EXPORT void JackLog(char *fmt,...)
{
#ifdef PRINTDEBUG
if (verbose) {
va_list ap;
va_start(ap, fmt);
fprintf(stderr,"Jack: ");
vfprintf(stderr, fmt, ap);
va_end(ap);
}
#endif
}


+ 48
- 0
common/JackError.h View File

@@ -0,0 +1,48 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


*/

#include <string.h>
#include <errno.h>
#include <stdio.h>

#include "JackConstants.h"
#include "JackExports.h"


#ifdef __cplusplus
extern "C"
{
#endif

#ifdef WIN32
#define vsnprintf _vsnprintf
#define snprintf _snprintf
#endif

EXPORT void jack_error(const char *fmt, ...);

EXPORT void JackLog(char *fmt, ...);

extern int verbose;

#ifdef __cplusplus
}
#endif

+ 30
- 0
common/JackExports.h View File

@@ -0,0 +1,30 @@
/*
Copyright (C) 2004-2005 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackExports__
#define __JackExports__

#ifdef WIN32
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif

#endif


+ 83
- 0
common/JackExternalClient.cpp View File

@@ -0,0 +1,83 @@
/*
Copyright (C) 2001-2003 Paul Davis
Copyright (C) 2004-2006 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackExternalClient.h"
#include "JackClientControl.h"
#include "JackGlobals.h"
#include "JackChannel.h"

namespace Jack
{

JackExternalClient::JackExternalClient(): fClientControl(NULL)
{
fChannel = JackGlobals::MakeNotifyChannel();
}

JackExternalClient::~JackExternalClient()
{
delete fChannel;
}

int JackExternalClient::ClientNotify(int refnum, const char* name, int notify, int sync, int value)
{
int result = -1;
JackLog("JackExternalClient::ClientNotify ref = %ld name = %s notify = %ld\n", refnum, name, notify);
fChannel->ClientNotify(refnum, name, notify, sync, value, &result);
return result;
}

int JackExternalClient::Open(const char* name, int refnum, int* shared_client)
{
try {

if (fChannel->Open(name) < 0) {
jack_error("Cannot connect to client name = %s port\n", name);
return -1;
}

fClientControl = new JackClientControl(name, refnum);
if (!fClientControl) {
jack_error("Cannot allocate client shared memory segment");
return -1;
}

*shared_client = fClientControl->GetShmIndex();
JackLog("JackExternalClient::Open name = %s index = %ld base = %x\n", name, fClientControl->GetShmIndex(), fClientControl->GetShmAddress());
return 0;

} catch (std::exception e) {
return -1;
}
}

int JackExternalClient::Close()
{
fChannel->Close();
delete fClientControl;
return 0;
}

JackClientControl* JackExternalClient::GetClientControl() const
{
return fClientControl;
}

} // end of namespace

+ 60
- 0
common/JackExternalClient.h View File

@@ -0,0 +1,60 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackExternalClient__
#define __JackExternalClient__

#include "JackClientInterface.h"

namespace Jack
{

class JackNotifyChannelInterface;
struct JackClientControl;

/*!
\brief Server side implementation of library clients.
*/

class JackExternalClient : public JackClientInterface
{

private:

JackNotifyChannelInterface* fChannel; /*! Server/client communication channel */
JackClientControl* fClientControl; /*! Client control in shared memory */

public:

JackExternalClient();
virtual ~JackExternalClient();

int Open(const char* name, int refnum, int* shared_client);
int Close();

int ClientNotify(int refnum, const char* name, int notify, int sync, int value);

JackClientControl* GetClientControl() const;
};


} // end of namespace

#endif

+ 209
- 0
common/JackFifo.cpp View File

@@ -0,0 +1,209 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackFifo.h"
#include "JackError.h"
#include "JackChannel.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>

namespace Jack
{

void JackFifo::BuildName(const char* name, char* res)
{
sprintf(res, "%s/jack_fifo.%s", jack_client_dir, name);
}

bool JackFifo::Signal()
{
bool res;
char c = 0;
assert(fFifo >= 0);

if (fFlush)
return true;

if ((res = (write(fFifo, &c, sizeof(c)) != sizeof(c)))) {
jack_error("JackFifo::Signal name = %s err = %s", fName, strerror(errno));
}
return !res;
}

bool JackFifo::SignalAll()
{
bool res;
char c = 0;
assert(fFifo >= 0);

if (fFlush)
return true;

if ((res = (write(fFifo, &c, sizeof(c)) != sizeof(c)))) {
jack_error("JackFifo::SignalAll name = %s err = %s", fName, strerror(errno));
}
return !res;
}

bool JackFifo::Wait()
{
bool res;
char c;
assert(fFifo >= 0);

if ((res = (read(fFifo, &c, sizeof(c)) != sizeof(c)))) {
jack_error("JackFifo::Wait name = %s err = %s", fName, strerror(errno));
}
return !res;
}

#ifdef __APPLE__
#warning JackFifo::TimedWait not available : synchronous mode may not work correctly if FIFO are used
bool JackFifo::TimedWait(long usec)
{
return Wait();
}
#else
// Does not work on OSX ??
bool JackFifo::TimedWait(long usec)
{
assert(fFifo >= 0);

if ((poll(&fPoll, 1, usec / 1000) < 0) && (errno != EINTR)) {
jack_error("JackFifo::TimedWait name = %s err = %s", fName, strerror(errno));
return false;
}

if (fPoll.revents & POLLIN) {
return Wait();
} else {
jack_error("JackFifo::TimedWait fails name = %s revents %ld ", fName, fPoll.revents);
return false;
}
}
#endif

// Server side
bool JackFifo::Allocate(const char* name, int value)
{
struct stat statbuf;
BuildName(name, fName);

JackLog("JackFifo::Allocate name = %s\n", fName);

if (stat(fName, &statbuf)) {
if (errno == ENOENT) {
if (mkfifo(fName, 0666) < 0) {
jack_error("Cannot create inter-client FIFO [%s] (%s)\n", name, strerror(errno));
return false;
}
} else {
jack_error("Cannot check on FIFO %s\n", name);
return false;
}
} else {
if (!S_ISFIFO(statbuf.st_mode)) {
jack_error("FIFO (%s) already exists, but is not a FIFO!\n", name);
return false;
}
}

if ((fFifo = open(fName, O_RDWR | O_CREAT, 0666)) < 0) {
jack_error("Cannot open fifo [%s] (%s)", name, strerror(errno));
return false;
} else {
fPoll.fd = fFifo;
fPoll.events = POLLERR | POLLIN | POLLHUP | POLLNVAL;
return true;
}
}

// Client side
bool JackFifo::ConnectAux(const char* name, int access)
{
BuildName(name, fName);
JackLog("JackFifo::ConnectAux name = %s\n", fName);

// Temporary...
if (fFifo >= 0) {
JackLog("Already connected name = %s\n", name);
return true;
}

if ((fFifo = open(fName, access)) < 0) {
jack_error("Connect: can't connect named fifo name = %s err = %s", fName, strerror(errno));
return false;
} else {
fPoll.fd = fFifo;
fPoll.events = POLLERR | POLLIN | POLLHUP | POLLNVAL;
return true;
}
}

bool JackFifo::Connect(const char* name)
{
return ConnectAux(name, O_RDWR);
}

bool JackFifo::ConnectOutput(const char* name)
{
return ConnectAux(name, O_WRONLY | O_NONBLOCK);
}

bool JackFifo::ConnectInput(const char* name)
{
return ConnectAux(name, O_RDONLY);
}

bool JackFifo::Disconnect()
{
if (fFifo >= 0) {
JackLog("JackFifo::Disconnect %s\n", fName);
if (close(fFifo) != 0) {
jack_error("Disconnect: can't disconnect named fifo name = %s err = %s", fName, strerror(errno));
return false;
} else {
fFifo = -1;
return true;
}
} else {
return true;
}
}

// Server side : destroy the fifo
void JackFifo::Destroy()
{
if (fFifo > 0) {
JackLog("JackFifo::Destroy name = %s\n", fName);
unlink(fName);
if (close(fFifo) != 0) {
jack_error("Destroy: can't destroy fifo name = %s err = %s", fName, strerror(errno));
}
fFifo = -1;
} else {
jack_error("JackFifo::Destroy fifo < 0");
}
}

} // end of namespace


+ 73
- 0
common/JackFifo.h View File

@@ -0,0 +1,73 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackFifo__
#define __JackFifo__

#include "JackSynchro.h"
#include <assert.h>
#include <stdio.h>
#include <poll.h>

namespace Jack
{

/*!
\brief Inter process synchronization using Fifo.
*/

class JackFifo : public JackSynchro
{

private:

int fFifo;
pollfd fPoll;

bool ConnectAux(const char* name, int access);

protected:

void BuildName(const char* name, char* res);

public:

JackFifo(): JackSynchro(), fFifo( -1)
{}
virtual ~JackFifo()
{}

bool Signal();
bool SignalAll();
bool Wait();
bool TimedWait(long usec);

bool Allocate(const char* name, int value);
bool Connect(const char* name);
bool ConnectInput(const char* name);
bool ConnectOutput(const char* name);
bool Disconnect();
void Destroy();
};

} // end of namespace


#endif


+ 109
- 0
common/JackFrameTimer.cpp View File

@@ -0,0 +1,109 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackFrameTimer.h"
#include "JackError.h"
#include <math.h>

namespace Jack
{

JackTimer::JackTimer()
{
fInitialized = false;
fFrames = 0;
fCurrentWakeup = 0;
fCurrentCallback = 0;
fNextWakeUp = 0;
fFilterCoefficient = 0.01f;
fSecondOrderIntegrator = 0.0f;
}

void JackFrameTimer::Init()
{
fFirstWakeUp = true;
}

void JackFrameTimer::IncFrameTime(jack_nframes_t nframes, jack_time_t callback_usecs, jack_time_t period_usecs)
{
if (fFirstWakeUp) {
InitFrameTime(callback_usecs, period_usecs);
fFirstWakeUp = false;
} else {
IncFrameTimeAux(nframes, callback_usecs, period_usecs);
}
}

void JackFrameTimer::InitFrameTime(jack_time_t callback_usecs, jack_time_t period_usecs)
{
JackTimer* timer = WriteNextStateStart();
timer->fSecondOrderIntegrator = 0.0f;
timer->fCurrentCallback = callback_usecs;
timer->fNextWakeUp = callback_usecs + period_usecs;
WriteNextStateStop();
TrySwitchState();
}

void JackFrameTimer::ResetFrameTime(jack_nframes_t frames_rate, jack_time_t callback_usecs, jack_time_t period_usecs)
{
if (!fFirstWakeUp) { // ResetFrameTime may be called by a xrun/delayed wakeup on the first cycle
JackTimer* timer = WriteNextStateStart();
jack_nframes_t period_size_guess = (jack_nframes_t)(frames_rate * ((timer->fNextWakeUp - timer->fCurrentWakeup) / 1000000.0));
timer->fFrames += ((callback_usecs - timer->fNextWakeUp) / period_size_guess) * period_size_guess;
timer->fCurrentWakeup = callback_usecs;
timer->fCurrentCallback = callback_usecs;
timer->fNextWakeUp = callback_usecs + period_usecs;
WriteNextStateStop();
TrySwitchState();
}
}

void JackFrameTimer::IncFrameTimeAux(jack_nframes_t nframes, jack_time_t callback_usecs, jack_time_t period_usecs)
{
JackTimer* timer = WriteNextStateStart();
float delta = (int64_t)callback_usecs - (int64_t)timer->fNextWakeUp;
timer->fCurrentWakeup = timer->fNextWakeUp;
timer->fCurrentCallback = callback_usecs;
timer->fFrames += nframes;
timer->fSecondOrderIntegrator += 0.5f * timer->fFilterCoefficient * delta;
timer->fNextWakeUp = timer->fCurrentWakeup + period_usecs + (int64_t) floorf((timer->fFilterCoefficient * (delta + timer->fSecondOrderIntegrator)));
timer->fInitialized = true;
WriteNextStateStop();
TrySwitchState();
}

/*
Use the state returned by ReadCurrentState and check that the state was not changed during the read operation.
The operation is lock-free since there is no intermediate state in the write operation that could cause the
read to loop forever.
*/
void JackFrameTimer::ReadFrameTime(JackTimer* timer)
{
UInt16 next_index = GetCurrentIndex();
UInt16 cur_index;
do {
cur_index = next_index;
memcpy(timer, ReadCurrentState(), sizeof(JackTimer));
next_index = GetCurrentIndex();
} while (cur_index != next_index); // Until a coherent state has been read
}

} // end of namespace


+ 81
- 0
common/JackFrameTimer.h View File

@@ -0,0 +1,81 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackFrameTimer__
#define __JackFrameTimer__

#include "JackAtomicState.h"
#include "types.h"

namespace Jack
{

/*!
\brief A structure used for time management.
*/

struct JackTimer
{
jack_nframes_t fFrames;
jack_time_t fCurrentWakeup;
jack_time_t fCurrentCallback;
jack_time_t fNextWakeUp;
float fSecondOrderIntegrator;
bool fInitialized;

/* not accessed by clients */

float fFilterCoefficient; /* set once, never altered */

JackTimer();
~JackTimer()
{}
};

/*!
\brief A class using the JackAtomicState to manage jack time.
*/

class JackFrameTimer : public JackAtomicState<JackTimer>
{
private:

bool fFirstWakeUp;
void IncFrameTimeAux(jack_nframes_t nframes, jack_time_t callback_usecs, jack_time_t period_usecs);

public:

JackFrameTimer(): fFirstWakeUp(true)
{}
~JackFrameTimer()
{}

void Init();
void InitFrameTime(jack_time_t callback_usecs, jack_time_t period_usecs);
void ResetFrameTime(jack_nframes_t frames_rate, jack_time_t callback_usecs, jack_time_t period_usecs);
void IncFrameTime(jack_nframes_t nframes, jack_time_t callback_usecs, jack_time_t period_usecs);
void ReadFrameTime(JackTimer* timer);
};


} // end of namespace

#endif

+ 47
- 0
common/JackFreewheelDriver.cpp View File

@@ -0,0 +1,47 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackFreewheelDriver.h"
#include "JackEngineControl.h"
#include "JackEngine.h"

namespace Jack
{

int JackFreewheelDriver::Process()
{
if (fIsMaster) {
JackLog("JackFreewheelDriver::Process master %lld\n", fEngineControl->fTimeOutUsecs);
fLastWaitUst = GetMicroSeconds();
fEngine->Process(fLastWaitUst);
fGraphManager->ResumeRefNum(fClientControl, fSynchroTable); // Signal all clients
if (fGraphManager->SuspendRefNum(fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs * 20) < 0) // Wait for all clients to finish
jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error");
} else {
fGraphManager->ResumeRefNum(fClientControl, fSynchroTable); // Signal all clients
if (fEngineControl->fSyncMode) {
if (fGraphManager->SuspendRefNum(fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs) < 0)
jack_error("JackFreewheelDriver::ProcessSync SuspendRefNum error");
}
}
return 0;
}

} // end of namespace

+ 54
- 0
common/JackFreewheelDriver.h View File

@@ -0,0 +1,54 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackFreewheelDriver__
#define __JackFreewheelDriver__

#include "JackDriver.h"

namespace Jack
{

/*!
\brief The FreeWheel driver : run Jack engine at full speed.
*/

class JackFreewheelDriver : public JackDriver
{

public:

JackFreewheelDriver(const char* name, JackEngine* engine, JackSynchro** table): JackDriver(name, engine, table)
{}
virtual ~JackFreewheelDriver()
{}

bool IsRealTime() const
{
return false;
}

int Process();
};

} // end of namespace


#endif

+ 28
- 0
common/JackGlobals.cpp View File

@@ -0,0 +1,28 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/


#include "JackGlobals.h"

namespace Jack
{

JackFactoryImpl* JackGlobals::fInstance;

} // end of namespace

+ 282
- 0
common/JackGlobals.h View File

@@ -0,0 +1,282 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackGlobals__
#define __JackGlobals__

#include "JackError.h"

namespace Jack
{

class JackSynchro;
class JackServerNotifyChannelInterface;
class JackClientChannelInterface;
class JackNotifyChannelInterface;
class JackServerChannelInterface;
class JackSyncInterface;
class JackThread;
class JackDriverClientInterface;
class JackRunnableInterface;
class JackEngine;

/*!
\brief Factory description
\totdo possibly use in a dynamic way to test different communication/synchro implementations.
*/

class JackFactoryImpl
{
public:

JackFactoryImpl()
{}
virtual ~JackFactoryImpl()
{}

virtual JackSynchro* MakeSynchro() = 0;
virtual JackServerNotifyChannelInterface* MakeServerNotifyChannel() = 0;
virtual JackClientChannelInterface* MakeClientChannel() = 0;
virtual JackNotifyChannelInterface* MakeNotifyChannel() = 0;
virtual JackServerChannelInterface* MakeServerChannel() = 0;
virtual JackSyncInterface* MakeInterProcessSync() = 0;
virtual JackThread* MakeThread(JackRunnableInterface* runnable) = 0;
};

#ifdef __linux__

class JackFactoryLinuxServer : public JackFactoryImpl
{
public:

JackFactoryLinuxServer()
{}
virtual ~JackFactoryLinuxServer()
{}

JackSynchro* MakeSynchro();
JackServerNotifyChannelInterface* MakeServerNotifyChannel();
JackClientChannelInterface* MakeClientChannel();
JackNotifyChannelInterface* MakeNotifyChannel();
JackServerChannelInterface* MakeServerChannel();
JackSyncInterface* MakeInterProcessSync();
JackThread* MakeThread(JackRunnableInterface* runnable);
};

class JackFactoryLinuxClient : public JackFactoryImpl
{
public:

JackFactoryLinuxClient()
{}
virtual ~JackFactoryLinuxClient()
{}

JackSynchro* MakeSynchro();
JackServerNotifyChannelInterface* MakeServerNotifyChannel();
JackClientChannelInterface* MakeClientChannel();
JackNotifyChannelInterface* MakeNotifyChannel();
JackServerChannelInterface* MakeServerChannel();
JackSyncInterface* MakeInterProcessSync();
JackThread* MakeThread(JackRunnableInterface* runnable);
};

#endif

#ifdef WIN32

class JackFactoryWindowsServer : public JackFactoryImpl
{
public:

JackFactoryWindowsServer()
{}
virtual ~JackFactoryWindowsServer()
{}

JackSynchro* MakeSynchro();
JackServerNotifyChannelInterface* MakeServerNotifyChannel();
JackClientChannelInterface* MakeClientChannel();
JackNotifyChannelInterface* MakeNotifyChannel();
JackServerChannelInterface* MakeServerChannel();
JackSyncInterface* MakeInterProcessSync();
JackThread* MakeThread(JackRunnableInterface* runnable);
};

class JackFactoryWindowsClient : public JackFactoryImpl
{
public:

JackFactoryWindowsClient()
{}
virtual ~JackFactoryWindowsClient()
{}

JackSynchro* MakeSynchro();
JackServerNotifyChannelInterface* MakeServerNotifyChannel();
JackClientChannelInterface* MakeClientChannel();
JackNotifyChannelInterface* MakeNotifyChannel();
JackServerChannelInterface* MakeServerChannel();
JackSyncInterface* MakeInterProcessSync();
JackThread* MakeThread(JackRunnableInterface* runnable);
};

#endif

#if defined(__APPLE__)

class JackFactoryOSXServer : public JackFactoryImpl
{
public:

JackFactoryOSXServer()
{}
virtual ~JackFactoryOSXServer()
{}

JackSynchro* MakeSynchro();
JackServerNotifyChannelInterface* MakeServerNotifyChannel();
JackClientChannelInterface* MakeClientChannel();
JackNotifyChannelInterface* MakeNotifyChannel();
JackServerChannelInterface* MakeServerChannel();
JackSyncInterface* MakeInterProcessSync();
JackThread* MakeThread(JackRunnableInterface* runnable);
};

class JackFactoryOSXClient : public JackFactoryImpl
{
public:

JackFactoryOSXClient()
{}
virtual ~JackFactoryOSXClient()
{}

JackSynchro* MakeSynchro();
JackServerNotifyChannelInterface* MakeServerNotifyChannel();
JackClientChannelInterface* MakeClientChannel();
JackNotifyChannelInterface* MakeNotifyChannel();
JackServerChannelInterface* MakeServerChannel();
JackSyncInterface* MakeInterProcessSync();
JackThread* MakeThread(JackRunnableInterface* runnable);
};

#endif

/*!
\brief Factory for OS specific ressources.
*/

class JackGlobals
{
private:

static JackFactoryImpl* fInstance;

public:

JackGlobals()
{}
virtual ~JackGlobals()
{}

static JackSynchro* MakeSynchro()
{
return fInstance->MakeSynchro();
}
static JackServerNotifyChannelInterface* MakeServerNotifyChannel()
{
return fInstance->MakeServerNotifyChannel();
}
static JackClientChannelInterface* MakeClientChannel()
{
return fInstance->MakeClientChannel();
}
static JackNotifyChannelInterface* MakeNotifyChannel()
{
return fInstance->MakeNotifyChannel();
}
static JackServerChannelInterface* MakeServerChannel()
{
return fInstance->MakeServerChannel();
}
static JackSyncInterface* MakeInterProcessSync()
{
return fInstance->MakeInterProcessSync();
}
static JackThread* MakeThread(JackRunnableInterface* runnable)
{
return fInstance->MakeThread(runnable);
}

static void InitServer()
{
JackLog("JackGlobals InitServer\n");
if (!fInstance) {
#ifdef __APPLE__
fInstance = new JackFactoryOSXServer();
#endif

#ifdef WIN32
fInstance = new JackFactoryWindowsServer();
#endif

#ifdef __linux__
fInstance = new JackFactoryLinuxServer();
#endif

}
}

static void InitClient()
{
JackLog("JackGlobals InitClient\n");
if (!fInstance) {
#ifdef __APPLE__
fInstance = new JackFactoryOSXClient();
#endif

#ifdef WIN32
fInstance = new JackFactoryWindowsClient();
#endif

#ifdef __linux__
fInstance = new JackFactoryLinuxClient();
#endif

}
}

static void Destroy()
{
JackLog("JackGlobals Destroy\n");
if (fInstance) {
delete fInstance;
fInstance = NULL;
}
}

};

} // end of namespace

#endif

+ 460
- 0
common/JackGlobalsClient.cpp View File

@@ -0,0 +1,460 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackGlobals.h"

// OSX
#if defined(__APPLE__)
#include "JackCoreAudioDriver.h"
#include "JackMachServerNotifyChannel.h"
#include "JackMachNotifyChannel.h"
#include "JackMachServerChannel.h"
#include "JackMachClientChannel.h"
#include "JackMachThread.h"
#include "JackMachSemaphore.h"
#include "JackProcessSync.h"
#include "JackSocketServerNotifyChannel.h"
#include "JackSocketNotifyChannel.h"
#include "JackSocketServerChannel.h"
#include "JackSocketClientChannel.h"
#include "JackPosixThread.h"
#include "JackPosixSemaphore.h"
#include "JackFifo.h"
#endif

// WINDOWS
#ifdef WIN32
#include "JackWinProcessSync.h"
#include "JackWinNamedPipeClientChannel.h"
#include "JackWinEvent.h"
#include "JackWinThread.h"
#endif

// LINUX
#ifdef __linux__
#include "JackAlsaDriver.h"
#include "JackProcessSync.h"
#include "JackSocketServerNotifyChannel.h"
#include "JackSocketNotifyChannel.h"
#include "JackSocketServerChannel.h"
#include "JackSocketClientChannel.h"
#include "JackPosixThread.h"
#include "JackPosixSemaphore.h"
#include "JackFifo.h"
#endif


using namespace std;

namespace Jack
{

#ifdef WIN32

JackSynchro* JackFactoryWindowsClient::MakeSynchro()
{
return new JackWinEvent();
}

JackServerNotifyChannelInterface* JackFactoryWindowsClient::MakeServerNotifyChannel()
{
return NULL;
}
// Not used
JackClientChannelInterface* JackFactoryWindowsClient::MakeClientChannel()
{
return new JackWinNamedPipeClientChannel();
}

JackNotifyChannelInterface* JackFactoryWindowsClient::MakeNotifyChannel()
{
return NULL;
}
// Not used
JackServerChannelInterface* JackFactoryWindowsClient::MakeServerChannel()
{
return NULL;
}
// Not used
JackSyncInterface* JackFactoryWindowsClient::MakeInterProcessSync()
{
return new JackWinProcessSync();
}

JackThread* JackFactoryWindowsClient::MakeThread(JackRunnableInterface* runnable)
{
return new JackWinThread(runnable);
}
#endif

#ifdef __linux__

#if defined(SOCKET_RPC_POSIX_SEMA)
JackSynchro* JackFactoryLinuxClient::MakeSynchro()
{
return new JackPosixSemaphore();
}

JackServerNotifyChannelInterface* JackFactoryLinuxClient::MakeServerNotifyChannel()
{
return NULL;
}
// Not used
JackClientChannelInterface* JackFactoryLinuxClient::MakeClientChannel()
{
return new JackSocketClientChannel();
}

JackNotifyChannelInterface* JackFactoryLinuxClient::MakeNotifyChannel()
{
return NULL;
}
// Not used
JackServerChannelInterface* JackFactoryLinuxClient::MakeServerChannel()
{
return NULL;
}
// Not used
JackSyncInterface* JackFactoryLinuxClient::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryLinuxClient::MakeThread(JackRunnableInterface* runnable)
{
return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(SOCKET_RPC_FIFO_SEMA)
JackSynchro* JackFactoryLinuxClient::MakeSynchro()
{
return new JackFifo();
}

JackServerNotifyChannelInterface* JackFactoryLinuxClient::MakeServerNotifyChannel()
{
return NULL;
}
// Not used
JackClientChannelInterface* JackFactoryLinuxClient::MakeClientChannel()
{
return new JackSocketClientChannel();
}

JackNotifyChannelInterface* JackFactoryLinuxClient::MakeNotifyChannel()
{
return NULL;
}
// Not used
JackServerChannelInterface* JackFactoryLinuxClient::MakeServerChannel()
{
return NULL;
}
// Not used
JackSyncInterface* JackFactoryLinuxClient::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryLinuxClient::MakeThread(JackRunnableInterface* runnable)
{
return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(SOCKET_RPC_FIFO_SEMA_DUMMY)
JackSynchro* JackFactoryLinuxClient::MakeSynchro()
{
return new JackFifo();
}

JackServerNotifyChannelInterface* JackFactoryLinuxClient::MakeServerNotifyChannel()
{
return NULL;
}
// Not used
JackClientChannelInterface* JackFactoryLinuxClient::MakeClientChannel()
{
return new JackSocketClientChannel();
}

JackNotifyChannelInterface* JackFactoryLinuxClient::MakeNotifyChannel()
{
return NULL;
}
// Not used
JackServerChannelInterface* JackFactoryLinuxClient::MakeServerChannel()
{
return NULL;
}
// Not used
JackSyncInterface* JackFactoryLinuxClient::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryLinuxClient::MakeThread(JackRunnableInterface* runnable)
{
return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#endif

#if defined(__APPLE__)

#if defined(MACH_RPC_MACH_SEMA)
// Mach RPC + Mach Semaphore
JackSynchro* JackFactoryOSXClient::MakeSynchro()
{
return new JackMachSemaphore();
}

JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel()
{
return NULL;
}
// Not used
JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel()
{
return new JackMachClientChannel();
}

JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel()
{
return NULL;
}
// Not used
JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel()
{
return NULL;
} // Not used

JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(SOCKET_RPC_POSIX_SEMA)
// Socket RPC + Posix Semaphore
JackSynchro* JackFactoryOSXClient::MakeSynchro()
{
return new JackPosixSemaphore();
}

JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel()
{
return NULL;
}
// Not used
JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel()
{
return new JackSocketClientChannel();
}

JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel()
{
return NULL;
}
// Not used
JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel()
{
return NULL;
}
// Not used
JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(SOCKET_RPC_FIFO_SEMA)
// Socket RPC + Fifo Semaphore
JackSynchro* JackFactoryOSXClient::MakeSynchro()
{
return new JackFifo();
}

JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel()
{
return NULL;
}
// Not used
JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel()
{
return new JackSocketClientChannel();
}

JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel()
{
return NULL;
}
// Not used
JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel()
{
return NULL;
}
// Not used
JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}

JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable, int priority)
{
return new JackMachThread(runnable, false, priority, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(MACH_RPC_FIFO_SEMA)
// Mach RPC + Fifo Semaphore
JackSynchro* JackFactoryOSXClient::MakeSynchro()
{
return new JackFifo();
}

JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel()
{
return NULL;
}
// Not used
JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel()
{
return new JackMachClientChannel();
}

JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel()
{
return NULL;
}
// Not used
JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel()
{
return NULL;
}
// Not used
JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(MACH_RPC_POSIX_SEMA)
// Mach RPC + Posix Semaphore
JackSynchro* JackFactoryOSXClient::MakeSynchro()
{
return new JackPosixSemaphore();
}

JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel()
{
return NULL;
}
// Not used
JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel()
{
return new JackMachClientChannel();
}

JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel()
{
return NULL;
}
// Not used
JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel()
{
return NULL;
}
// Not used
JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined SOCKET_RPC_MACH_SEMA
// Socket RPC + Mach Semaphore
JackSynchro* JackFactoryOSXClient::MakeSynchro()
{
return new JackMachSemaphore();
}

JackServerNotifyChannelInterface* JackFactoryOSXClient::MakeServerNotifyChannel()
{
return NULL;
}
// Not used
JackClientChannelInterface* JackFactoryOSXClient::MakeClientChannel()
{
return new JackSocketClientChannel();
}

JackNotifyChannelInterface* JackFactoryOSXClient::MakeNotifyChannel()
{
return NULL;
}
// Not used
JackServerChannelInterface* JackFactoryOSXClient::MakeServerChannel()
{
return NULL;
}
// Not used
JackSyncInterface* JackFactoryOSXClient::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXClient::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#endif

} // end of namespace



+ 460
- 0
common/JackGlobalsServer.cpp View File

@@ -0,0 +1,460 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackGlobals.h"

// OSX
#if defined(__APPLE__)
#include "JackCoreAudioDriver.h"
#include "JackMachServerNotifyChannel.h"
#include "JackMachNotifyChannel.h"
#include "JackMachServerChannel.h"
#include "JackMachClientChannel.h"
#include "JackMachThread.h"
#include "JackMachSemaphore.h"
#include "JackProcessSync.h"

#include "JackSocketServerNotifyChannel.h"
#include "JackSocketNotifyChannel.h"
#include "JackSocketServerChannel.h"
#include "JackSocketClientChannel.h"
#include "JackPosixThread.h"
#include "JackPosixSemaphore.h"
#include "JackFifo.h"
#endif

// WINDOWS
#ifdef WIN32
#include "JackWinProcessSync.h"
#include "JackWinNamedPipeServerNotifyChannel.h"
#include "JackWinNamedPipeNotifyChannel.h"
#include "JackWinNamedPipeServerChannel.h"
#include "JackWinNamedPipeClientChannel.h"
#include "JackWinEvent.h"
#include "JackWinThread.h"
#endif

// LINUX
#ifdef __linux__
#include "JackAlsaDriver.h"
#include "JackProcessSync.h"
#include "JackSocketServerNotifyChannel.h"
#include "JackSocketNotifyChannel.h"
#include "JackSocketServerChannel.h"
#include "JackSocketClientChannel.h"
#include "JackPosixThread.h"
#include "JackPosixSemaphore.h"
#include "JackFifo.h"
#endif

// COMMON
#include "JackDummyDriver.h"
#include "JackAudioDriver.h"


using namespace std;

namespace Jack
{

#ifdef WIN32
JackSynchro* JackFactoryWindowsServer::MakeSynchro()
{
return new JackWinEvent();
}

JackServerNotifyChannelInterface* JackFactoryWindowsServer::MakeServerNotifyChannel()
{
return new JackWinNamedPipeServerNotifyChannel();
}

JackClientChannelInterface* JackFactoryWindowsServer::MakeClientChannel()
{
return NULL;
} // Not used

JackNotifyChannelInterface* JackFactoryWindowsServer::MakeNotifyChannel()
{
return new JackWinNamedPipeNotifyChannel();
}

JackServerChannelInterface* JackFactoryWindowsServer::MakeServerChannel()
{
return new JackWinNamedPipeServerChannel();
}

JackSyncInterface* JackFactoryWindowsServer::MakeInterProcessSync()
{
return new JackWinProcessSync();
}

JackThread* JackFactoryWindowsServer::MakeThread(JackRunnableInterface* runnable)
{
return new JackWinThread(runnable);
}
#endif

#ifdef __linux__

#if defined(SOCKET_RPC_POSIX_SEMA)
JackSynchro* JackFactoryLinuxServer::MakeSynchro()
{
return new JackPosixSemaphore();
}

JackServerNotifyChannelInterface* JackFactoryLinuxServer::MakeServerNotifyChannel()
{
return new JackSocketServerNotifyChannel();
}

JackClientChannelInterface* JackFactoryLinuxServer::MakeClientChannel()
{
return NULL;
} // Not used

JackNotifyChannelInterface* JackFactoryLinuxServer::MakeNotifyChannel()
{
return new JackSocketNotifyChannel();
}

JackServerChannelInterface* JackFactoryLinuxServer::MakeServerChannel()
{
return new JackSocketServerChannel();
}

JackSyncInterface* JackFactoryLinuxServer::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryLinuxServer::MakeThread(JackRunnableInterface* runnable)
{
return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(SOCKET_RPC_FIFO_SEMA)
JackSynchro* JackFactoryLinuxServer::MakeSynchro()
{
return new JackFifo();
}

JackServerNotifyChannelInterface* JackFactoryLinuxServer::MakeServerNotifyChannel()
{
return new JackSocketServerNotifyChannel();
}

JackClientChannelInterface* JackFactoryLinuxServer::MakeClientChannel()
{
return NULL;
}
// Not used
JackNotifyChannelInterface* JackFactoryLinuxServer::MakeNotifyChannel()
{
return new JackSocketNotifyChannel();
}

JackServerChannelInterface* JackFactoryLinuxServer::MakeServerChannel()
{
return new JackSocketServerChannel();
}

JackSyncInterface* JackFactoryLinuxServer::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryLinuxServer::MakeThread(JackRunnableInterface* runnable)
{
return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(SOCKET_RPC_FIFO_SEMA_DUMMY)
JackSynchro* JackFactoryLinuxServer::MakeSynchro()
{
return new JackFifo();
}

JackServerNotifyChannelInterface* JackFactoryLinuxServer::MakeServerNotifyChannel()
{
return new JackSocketServerNotifyChannel();
}

JackClientChannelInterface* JackFactoryLinuxServer::MakeClientChannel()
{
return NULL;
}
// Not used
JackNotifyChannelInterface* JackFactoryLinuxServer::MakeNotifyChannel()
{
return new JackSocketNotifyChannel();
}

JackServerChannelInterface* JackFactoryLinuxServer::MakeServerChannel()
{
return new JackSocketServerChannel();
}

JackSyncInterface* JackFactoryLinuxServer::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryLinuxServer::MakeThread(JackRunnableInterface* runnable)
{
return new JackPosixThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#endif

#if defined(__APPLE__)

#if defined(MACH_RPC_MACH_SEMA)
// Mach RPC + Mach Semaphore
JackSynchro* JackFactoryOSXServer::MakeSynchro()
{
return new JackMachSemaphore();
}

JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel()
{
return new JackMachServerNotifyChannel();
}

JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel()
{
return NULL;
} // Not used

JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel()
{
return new JackMachNotifyChannel();
}

JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel()
{
return new JackMachServerChannel();
}

JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(SOCKET_RPC_POSIX_SEMA)
// Socket RPC + Posix Semaphore
JackSynchro* JackFactoryOSXServer::MakeSynchro()
{
return new JackPosixSemaphore();
}

JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel()
{
return new JackSocketServerNotifyChannel();
}

JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel()
{
return NULL;
}
// Not used
JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel()
{
return new JackSocketNotifyChannel();
}

JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel()
{
return new JackSocketServerChannel();
}

JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(SOCKET_RPC_FIFO_SEMA)
// Socket RPC + Fifo Semaphore
JackSynchro* JackFactoryOSXServer::MakeSynchro()
{
return new JackFifo();
}

JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel()
{
return new JackSocketServerNotifyChannel();
}

JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel()
{
return NULL;
}
// Not used
JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel()
{
return new JackSocketNotifyChannel();
}

JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel()
{
return new JackSocketServerChannel();
}

JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(MACH_RPC_FIFO_SEMA)
// Mach RPC + Fifo Semaphore
JackSynchro* JackFactoryOSXServer::MakeSynchro()
{
return new JackFifo();
}

JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel()
{
return new JackMachServerNotifyChannel();
}

JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel()
{
return NULL;
}
// Not used
JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel()
{
return new JackMachNotifyChannel();
}

JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel()
{
return new JackMachServerChannel();
}

JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(MACH_RPC_POSIX_SEMA)
// Mach RPC + Posix Semaphore
JackSynchro* JackFactoryOSXServer::MakeSynchro()
{
return new JackPosixSemaphore();
}

JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel()
{
return new JackMachServerNotifyChannel();
}

JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel()
{
return NULL;
}
// Not used
JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel()
{
return new JackMachNotifyChannel();
}

JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel()
{
return new JackMachServerChannel();
}

JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#if defined(SOCKET_RPC_MACH_SEMA)
// Socket RPC + Mac Semaphore
JackSynchro* JackFactoryOSXServer::MakeSynchro()
{
return new JackMachSemaphore();
}

JackServerNotifyChannelInterface* JackFactoryOSXServer::MakeServerNotifyChannel()
{
return new JackSocketServerNotifyChannel();
}

JackClientChannelInterface* JackFactoryOSXServer::MakeClientChannel()
{
return NULL;
}
// Not used
JackNotifyChannelInterface* JackFactoryOSXServer::MakeNotifyChannel()
{
return new JackSocketNotifyChannel();
}

JackServerChannelInterface* JackFactoryOSXServer::MakeServerChannel()
{
return new JackSocketServerChannel();
}

JackSyncInterface* JackFactoryOSXServer::MakeInterProcessSync()
{
return new JackProcessSync();
}

JackThread* JackFactoryOSXServer::MakeThread(JackRunnableInterface* runnable)
{
return new JackMachThread(runnable, PTHREAD_CANCEL_ASYNCHRONOUS);
}
#endif

#endif

} // end of namespace

+ 777
- 0
common/JackGraphManager.cpp View File

@@ -0,0 +1,777 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "JackGraphManager.h"
#include "JackConstants.h"
#include <assert.h>
#include <stdlib.h>
#include <algorithm>
#include <regex.h>

namespace Jack
{

static inline jack_nframes_t MAX(jack_nframes_t a, jack_nframes_t b)
{
return (a < b) ? b : a;
}

static void AssertPort(jack_port_id_t port_index)
{
if (port_index >= PORT_NUM) {
JackLog("JackGraphManager::AssertPort port_index = %ld\n", port_index);
assert(port_index < PORT_NUM);
}
}

static void AssertBufferSize(jack_nframes_t frames)
{
if (frames > BUFFER_SIZE_MAX) {
JackLog("JackGraphManager::AssertBufferSize frames = %ld\n", frames);
assert(frames <= BUFFER_SIZE_MAX);
}
}

JackPort* JackGraphManager::GetPort(jack_port_id_t port_index)
{
AssertPort(port_index);
return &fPortArray[port_index];
}

float* JackGraphManager::GetBuffer(jack_port_id_t port_index)
{
return fPortArray[port_index].GetBuffer();
}

// RT, client
int JackGraphManager::GetConnectionsNum(jack_port_id_t port_index)
{
JackConnectionManager* manager = ReadCurrentState();
return manager->Connections(port_index);
}

// Server
int JackGraphManager::AllocateRefNum()
{
JackConnectionManager* manager = WriteNextStateStart();
int res = manager->AllocateRefNum();
WriteNextStateStop();
return res;
}

// Server
void JackGraphManager::ReleaseRefNum(int refnum)
{
JackConnectionManager* manager = WriteNextStateStart();
manager->ReleaseRefNum(refnum);
WriteNextStateStop();
}

// RT
void JackGraphManager::RunCurrentGraph()
{
JackConnectionManager* manager = ReadCurrentState();
manager->ResetGraph(fClientTiming);
}

// RT
bool JackGraphManager::RunNextGraph()
{
bool res;
JackConnectionManager* manager = TrySwitchState(&res);
manager->ResetGraph(fClientTiming);
return res;
}

// RT
bool JackGraphManager::IsFinishedGraph()
{
JackConnectionManager* manager = ReadCurrentState();
return (manager->GetActivation(FREEWHEEL_DRIVER_REFNUM) == 0);
}

// RT
int JackGraphManager::ResumeRefNum(JackClientControl* control, JackSynchro** table)
{
JackConnectionManager* manager = ReadCurrentState();
return manager->ResumeRefNum(control, table, fClientTiming);
}

// RT
int JackGraphManager::SuspendRefNum(JackClientControl* control, JackSynchro** table, long usec)
{
JackConnectionManager* manager = ReadCurrentState();
return manager->SuspendRefNum(control, table, fClientTiming, usec);
}

JackClientTiming* JackGraphManager::GetClientTiming(int ref)
{
return &fClientTiming[ref];
}

// Server
void JackGraphManager::DirectConnect(int ref1, int ref2)
{
JackConnectionManager* manager = WriteNextStateStart();
manager->DirectConnect(ref1, ref2);
JackLog("JackGraphManager::ConnectRefNum cur_index = %ld ref1 = %ld ref2 = %ld\n", CurIndex(fCounter), ref1, ref2);
WriteNextStateStop();
}

// Server
void JackGraphManager::DirectDisconnect(int ref1, int ref2)
{
JackConnectionManager* manager = WriteNextStateStart();
manager->DirectDisconnect(ref1, ref2);
JackLog("JackGraphManager::DisconnectRefNum cur_index = %ld ref1 = %ld ref2 = %ld\n", CurIndex(fCounter), ref1, ref2);
WriteNextStateStop();
}

// Server
bool JackGraphManager::IsDirectConnection(int ref1, int ref2)
{
JackConnectionManager* manager = ReadCurrentState();
return manager->IsDirectConnection(ref1, ref2);
}

// RT
void* JackGraphManager::GetBuffer(jack_port_id_t port_index, jack_nframes_t frames)
{
AssertPort(port_index);
AssertBufferSize(frames);

JackConnectionManager* manager = ReadCurrentState();
JackPort* port = GetPort(port_index);

if (!port->IsUsed()) {
// This happens when a port has just been unregistered and is still used by the RT code.
JackLog("JackGraphManager::GetBuffer : port = %ld is released state\n", port_index);
return GetBuffer(0); // port_index 0 is not used
}

// Output port
if (port->fFlags & JackPortIsOutput) {
return (port->fTied != NO_PORT) ? GetBuffer(port->fTied, frames) : GetBuffer(port_index);
}

// Input port
jack_int_t len = manager->Connections(port_index);

if (len == 0) { // No connections: return a zero-filled buffer
float* buffer = GetBuffer(port_index);
memset(buffer, 0, frames * sizeof(float)); // Clear buffer
return buffer;
} else if (len == 1) { // One connection: use zero-copy mode - just pass the buffer of the connected (output) port.
assert(manager->GetPort(port_index, 0) != port_index); // Check recursion
return GetBuffer(manager->GetPort(port_index, 0), frames);
} else { // Multiple connections
const jack_int_t* connections = manager->GetConnections(port_index);
float* mixbuffer = GetBuffer(port_index);
jack_port_id_t src_index;
float* buffer;

// Copy first buffer
src_index = connections[0];
AssertPort(src_index);
buffer = (float*)GetBuffer(src_index, frames);
memcpy(mixbuffer, buffer, frames * sizeof(float));

// Mix remaining buffers
for (int i = 1; (i < CONNECTION_NUM) && ((src_index = connections[i]) != EMPTY); i++) {
AssertPort(src_index);
buffer = (float*)GetBuffer(src_index, frames);
JackPort::MixBuffer(mixbuffer, buffer, frames);
}
return mixbuffer;
}
}

int JackGraphManager::RequestMonitor(jack_port_id_t port_index, bool onoff) // Client
{
AssertPort(port_index);
JackPort* port = GetPort(port_index);

/**
jackd.h
* If @ref JackPortCanMonitor is set for this @a port, turn input
* monitoring on or off. Otherwise, do nothing.
if (!(fFlags & JackPortCanMonitor))
return -1;
*/

port->RequestMonitor(onoff);

const jack_int_t* connections = ReadCurrentState()->GetConnections(port_index);
if ((port->fFlags & JackPortIsOutput) == 0) { // ?? Taken from jack, why not (port->fFlags & JackPortIsInput) ?
jack_port_id_t src_index;
for (int i = 0; (i < CONNECTION_NUM) && ((src_index = connections[i]) != EMPTY); i++) {
// XXX much worse things will happen if there is a feedback loop !!!
RequestMonitor(src_index, onoff);
}
}

return 0;
}

jack_nframes_t JackGraphManager::GetTotalLatencyAux(jack_port_id_t port_index, jack_port_id_t src_port_index, JackConnectionManager* manager, int hop_count)
{
const jack_int_t* connections = manager->GetConnections(port_index);
jack_nframes_t latency = GetPort(port_index)->GetLatency();
jack_nframes_t max_latency = 0;
jack_port_id_t dst_index;

if (hop_count > 8)
return latency;

for (int i = 0; (i < CONNECTION_NUM) && ((dst_index = connections[i]) != EMPTY); i++) {
if (src_port_index != dst_index) {
AssertPort(dst_index);
JackPort* dst_port = GetPort(dst_index);
jack_nframes_t this_latency = (dst_port->fFlags & JackPortIsTerminal)
? dst_port->GetLatency()
: GetTotalLatencyAux(dst_index, port_index, manager, hop_count + 1);
max_latency = MAX(max_latency, this_latency);
}
}

return max_latency + latency;
}

jack_nframes_t JackGraphManager::GetTotalLatency(jack_port_id_t port_index)
{
UInt16 cur_index;
UInt16 next_index;
jack_nframes_t total_latency;
AssertPort(port_index);
JackLog("JackGraphManager::GetTotalLatency port_index = %ld\n", port_index);

do {
cur_index = GetCurrentIndex();
total_latency = GetTotalLatencyAux(port_index, port_index, ReadCurrentState(), 0);
next_index = GetCurrentIndex();
} while (cur_index != next_index); // Until a coherent state has been read

return total_latency;
}

// Server
jack_port_id_t JackGraphManager::AllocatePortAux(int refnum, const char* port_name, JackPortFlags flags)
{
jack_port_id_t port_index;

// Look for first available port Port index start at 1, otherwise a port_index of 0 is "seen" as a NULL port by the external API...
for (port_index = 1; port_index < PORT_NUM; port_index++) {
JackPort* port = GetPort(port_index);
if (!port->IsUsed()) {
JackLog("JackGraphManager::AllocatePortAux port_index = %ld name = %s\n", port_index, port_name);
port->Allocate(refnum, port_name, flags);
break;
}
}

return (port_index < PORT_NUM) ? port_index : NO_PORT;
}

// Server
jack_port_id_t JackGraphManager::AllocatePort(int refnum, const char* port_name, JackPortFlags flags)
{
JackConnectionManager* manager = WriteNextStateStart();
jack_port_id_t port_index = AllocatePortAux(refnum, port_name, flags);

if (port_index != NO_PORT) {
int res;
if (flags & JackPortIsOutput) {
res = manager->AddOutputPort(refnum, port_index);
} else {
res = manager->AddInputPort(refnum, port_index);
}
if (res < 0) {
JackPort* port = GetPort(port_index);
assert(port);
port->Release();
port_index = NO_PORT;
}
}

WriteNextStateStop();
return port_index;
}

// Server
void JackGraphManager::ReleasePort(jack_port_id_t port_index)
{
JackPort* port = GetPort(port_index);
port->Release();
}

// Server
int JackGraphManager::RemovePort(int refnum, jack_port_id_t port_index)
{
JackConnectionManager* manager = WriteNextStateStart();
JackPort* port = GetPort(port_index);
int res;

if (port->fFlags & JackPortIsOutput) {
DisconnectAllOutput(port_index);
res = manager->RemoveOutputPort(refnum, port_index);
} else {
DisconnectAllInput(port_index);
res = manager->RemoveInputPort(refnum, port_index);
}

WriteNextStateStop();
return res;
}

// Server
void JackGraphManager::RemoveAllPorts(int refnum)
{
JackLog("JackGraphManager::RemoveAllPorts ref = %ld\n", refnum);
JackConnectionManager* manager = WriteNextStateStart();
jack_port_id_t port_index;

// Warning : RemovePort shift port to left, thus we always remove the first port until the "input" table is empty
const jack_int_t* input = manager->GetInputPorts(refnum);
while ((port_index = input[0]) != EMPTY) {
RemovePort(refnum, port_index);
ReleasePort(port_index);
}

// Warning : RemovePort shift port to left, thus we always remove the first port until the "output" table is empty
const jack_int_t* output = manager->GetOutputPorts(refnum);
while ((port_index = output[0]) != EMPTY) {
RemovePort(refnum, port_index);
ReleasePort(port_index);
}

WriteNextStateStop();
}

// Server
void JackGraphManager::DisconnectAllPorts(int refnum)
{
int i;
JackLog("JackGraphManager::DisconnectAllPorts ref = %ld\n", refnum);
JackConnectionManager* manager = WriteNextStateStart();

const jack_int_t* input = manager->GetInputPorts(refnum);
for (i = 0; i < PORT_NUM_FOR_CLIENT && input[i] != EMPTY ; i++) {
DisconnectAllInput(input[i]);
}

const jack_int_t* output = manager->GetOutputPorts(refnum);
for (i = 0; i < PORT_NUM_FOR_CLIENT && output[i] != EMPTY; i++) {
DisconnectAllOutput(output[i]);
}

WriteNextStateStop();
}

// Server
void JackGraphManager::DisconnectAllInput(jack_port_id_t port_index)
{
JackLog("JackGraphManager::DisconnectAllInput port_index = %ld \n", port_index);
JackConnectionManager* manager = WriteNextStateStart();

for (int i = 0; i < PORT_NUM; i++) {
if (manager->IsConnected(i, port_index)) {
JackLog("JackGraphManager::Disconnect i = %ld port_index = %ld\n", i, port_index);
Disconnect(i, port_index);
}
}
WriteNextStateStop();
}

// Server
void JackGraphManager::DisconnectAllOutput(jack_port_id_t port_index)
{
JackLog("JackGraphManager::DisconnectAllOutput port_index = %ld \n", port_index);
JackConnectionManager* manager = WriteNextStateStart();

const jack_int_t* connections = manager->GetConnections(port_index);
while (connections[0] != EMPTY) {
Disconnect(port_index, connections[0]); // Warning : Disconnect shift port to left
}
WriteNextStateStop();
}

// Server
int JackGraphManager::DisconnectAll(jack_port_id_t port_index)
{
AssertPort(port_index);

JackPort* port = GetPort(port_index);
if (port->fFlags & JackPortIsOutput) {
DisconnectAllOutput(port_index);
} else {
DisconnectAllInput(port_index);
}
return 0;
}

// Server
int JackGraphManager::GetInputRefNum(jack_port_id_t port_index)
{
AssertPort(port_index);
JackConnectionManager* manager = WriteNextStateStart();
int res = manager->GetInputRefNum(port_index);
WriteNextStateStop();
return res;
}

// Server
int JackGraphManager::GetOutputRefNum(jack_port_id_t port_index)
{
AssertPort(port_index);
JackConnectionManager* manager = WriteNextStateStart();
int res = manager->GetOutputRefNum(port_index);
WriteNextStateStop();
return res;
}

int JackGraphManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst)
{
JackConnectionManager* manager = WriteNextStateStart();
JackLog("JackGraphManager::Connect port_src = %ld port_dst = %ld\n", port_src, port_dst);
bool in_use_src = GetPort(port_src)->fInUse;
bool in_use_dst = GetPort(port_src)->fInUse;
int res = 0;

if (!in_use_src || !in_use_dst) {
if (!in_use_src)
jack_error("JackGraphManager::Connect: port_src not %ld used name = %s", port_src, GetPort(port_src)->fName);
if (!in_use_dst)
jack_error("JackGraphManager::Connect: port_dst not %ld used name = %s", port_dst, GetPort(port_dst)->fName);
res = -1;
goto end;
}
if (manager->IsConnected(port_src, port_dst)) {
jack_error("JackGraphManager::Connect already connected port_src = %ld port_dst = %ld", port_src, port_dst);
res = EEXIST;
goto end;
}

res = manager->Connect(port_src, port_dst);
if (res < 0) {
jack_error("JackGraphManager::Connect failed port_src = %ld port_dst = %ld", port_src, port_dst);
goto end;
}
manager->Connect(port_dst, port_src);
if (res < 0) {
jack_error("JackGraphManager::Connect failed port_src = %ld port_dst = %ld", port_dst, port_src);
goto end;
}

if (manager->IsLoopPath(port_src, port_dst)) {
JackLog("JackGraphManager::Connect: LOOP detected\n");
manager->IncFeedbackConnection(port_src, port_dst);
} else {
manager->IncDirectConnection(port_src, port_dst);
}

end:
WriteNextStateStop();
if (res < 0)
jack_error("JackGraphManager::Connect failed port_src = %ld port_dst = %ld", port_dst, port_src);
return res;
}

// Server
int JackGraphManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst)
{
JackConnectionManager* manager = WriteNextStateStart();
JackLog("JackGraphManager::Disconnect port_src = %ld port_dst = %ld\n", port_src, port_dst);
bool in_use_src = GetPort(port_src)->fInUse;
bool in_use_dst = GetPort(port_src)->fInUse;
int res = 0;

if (!in_use_src || !in_use_dst) {
if (!in_use_src)
jack_error("JackGraphManager::Disconnect: port_src not %ld used name = %s", port_src, GetPort(port_src)->fName);
if (!in_use_dst)
jack_error("JackGraphManager::Disconnect: port_src not %ld used name = %s", port_dst, GetPort(port_dst)->fName);
res = -1;
goto end;
}
if (!manager->IsConnected(port_src, port_dst)) {
jack_error("JackGraphManager::Disconnect not connected port_src = %ld port_dst = %ld", port_src, port_dst);
res = -1;
goto end;
}

manager->Disconnect(port_src, port_dst);
if (res < 0) {
jack_error("JackGraphManager::Disconnect failed port_src = %ld port_dst = %ld", port_src, port_dst);
goto end;
}
manager->Disconnect(port_dst, port_src);
if (res < 0) {
jack_error("JackGraphManager::Disconnect failed port_src = %ld port_dst = %ld", port_dst, port_src);
goto end;
}

if (manager->IsFeedbackConnection(port_src, port_dst)) {
JackLog("JackGraphManager::Disconnect: FEEDBACK removed\n");
manager->DecFeedbackConnection(port_src, port_dst);
} else {
manager->DecDirectConnection(port_src, port_dst);
}

end:
WriteNextStateStop();
return res;
}

// Client
int JackGraphManager::ConnectedTo(jack_port_id_t port_src, const char* port_name)
{
JackLog("JackGraphManager::ConnectedTo port_src = %ld port_name = %s\n", port_src, port_name);
JackConnectionManager* manager = ReadCurrentState();
jack_port_id_t port_dst;

if ((port_dst = GetPort(port_name)) == NO_PORT) {
jack_error("Unknown destination port port_name = %s", port_name);
return 0;
} else {
return manager->IsConnected(port_src, port_dst);
}
}

// Server
int JackGraphManager::CheckPort(jack_port_id_t port_index)
{
JackPort* port = GetPort(port_index);

if (port->fLocked) {
jack_error("Port %s is locked against connection changes", port->fName);
return -1;
} else {
return 0;
}
}

int JackGraphManager::CheckPorts(jack_port_id_t port_src, jack_port_id_t port_dst)
{
JackPort* src = GetPort(port_src);
JackPort* dst = GetPort(port_dst);

if ((dst->Flags() & JackPortIsInput) == 0) {
jack_error("Destination port in attempted (dis)connection of %s and %s is not an input port", src->fName, dst->fName);
return -1;
}

if ((src->Flags() & JackPortIsOutput) == 0) {
jack_error("Source port in attempted (dis)connection of %s and %s is not an output port", src->fName, dst->fName);
return -1;
}

if (src->fLocked) {
jack_error("Source port %s is locked against connection changes", src->fName);
return -1;
}

if (dst->fLocked) {
jack_error("Destination port %s is locked against connection changes", dst->fName);
return -1;
}

return 0;
}

int JackGraphManager::CheckPorts(const char* src_name, const char* dst_name, jack_port_id_t* port_src, jack_port_id_t* port_dst)
{
JackLog("JackGraphManager::CheckConnect src_name = %s dst_name = %s\n", src_name, dst_name);

if ((*port_src = GetPort(src_name)) == NO_PORT) {
jack_error("Unknown source port in attempted (dis)connection src_name [%s] dst_name [%s]", src_name, dst_name);
return -1;
}

if ((*port_dst = GetPort(dst_name)) == NO_PORT) {
jack_error("Unknown destination port in attempted (dis)connection src_name [%s] dst_name [%s]", src_name, dst_name);
return -1;
}

return CheckPorts(*port_src, *port_dst);
}

// Client : port array
jack_port_id_t JackGraphManager::GetPort(const char* name)
{
for (int i = 0; i < PORT_NUM; i++) {
JackPort* port = GetPort(i);
if (port->IsUsed() && strcmp(port->fName, name) == 0)
return i;
}
return NO_PORT;
}

/*!
\brief Get the connection port name array.
*/

// Client
void JackGraphManager::GetConnectionsAux(JackConnectionManager* manager, const char** res, jack_port_id_t port_index)
{
const jack_int_t* connections = manager->GetConnections(port_index);
jack_int_t index;
int i;

for (i = 0; (i < CONNECTION_NUM) && ((index = connections[i]) != EMPTY) ; i++) {
JackPort* port = GetPort(index);
res[i] = port->fName;
}

res[i] = NULL;
}

// Client
/*
Use the state returned by ReadCurrentState and check that the state was not changed during the read operation.
The operation is lock-free since there is no intermediate state in the write operation that could cause the
read to loop forever.
*/
const char** JackGraphManager::GetConnections(jack_port_id_t port_index)
{
const char** res = (const char**)malloc(sizeof(char*) * (CONNECTION_NUM + 1));
UInt16 cur_index;
UInt16 next_index;
AssertPort(port_index);

do {
cur_index = GetCurrentIndex();
GetConnectionsAux(ReadCurrentState(), res, port_index);
next_index = GetCurrentIndex();
} while (cur_index != next_index); // Until a coherent state has been read

return res;
}

// Client
const char** JackGraphManager::GetPortsAux(const char* port_name_pattern, const char* type_name_pattern, unsigned long flags)
{
const char** matching_ports;
unsigned long match_cnt = 0;
regex_t port_regex;
regex_t type_regex;
bool matching;

if (port_name_pattern && port_name_pattern[0]) {
regcomp(&port_regex, port_name_pattern, REG_EXTENDED | REG_NOSUB);
}
if (type_name_pattern && type_name_pattern[0]) {
regcomp(&type_regex, type_name_pattern, REG_EXTENDED | REG_NOSUB);
}

matching_ports = (const char**)malloc(sizeof(char*) * PORT_NUM);

for (int i = 0; i < PORT_NUM; i++) {
matching = true;

JackPort* port = GetPort(i);

if (port->IsUsed()) {

if (flags) {
if ((port->fFlags & flags) != flags) {
matching = false;
}
}

if (matching && port_name_pattern && port_name_pattern[0]) {
if (regexec(&port_regex, port->fName, 0, NULL, 0)) {
matching = false;
}
}

/*
if (matching && type_name_pattern && type_name_pattern[0]) {
jack_port_type_id_t ptid = psp[i].ptype_id;
if (regexec (&type_regex,engine->port_types[ptid].type_name,0, NULL, 0)) {
matching = false;
}
}
*/

// TO BE IMPROVED
if (matching && type_name_pattern && type_name_pattern[0]) {
if (regexec(&type_regex, JACK_DEFAULT_AUDIO_TYPE, 0, NULL, 0)) {
matching = false;
}
}

if (matching) {
matching_ports[match_cnt++] = port->fName;
}
}
}

matching_ports[match_cnt] = 0;

if (match_cnt == 0) {
free(matching_ports);
matching_ports = NULL;
}

return matching_ports;
}

// Client
/*
Check that the state was not changed during the read operation.
The operation is lock-free since there is no intermediate state in the write operation that could cause the
read to loop forever.
*/
const char** JackGraphManager::GetPorts(const char* port_name_pattern, const char* type_name_pattern, unsigned long flags)
{
const char** matching_ports = NULL;
UInt16 cur_index;
UInt16 next_index;

do {
cur_index = GetCurrentIndex();
if (matching_ports)
free(matching_ports);
matching_ports = GetPortsAux(port_name_pattern, type_name_pattern, flags);
next_index = GetCurrentIndex();
} while (cur_index != next_index); // Until a coherent state has been read

return matching_ports;
}

// Server
void JackGraphManager::Save(JackConnectionManager* dst)
{
JackConnectionManager* manager = WriteNextStateStart();
memcpy(dst, manager, sizeof(JackConnectionManager));
WriteNextStateStop();
}

// Server
void JackGraphManager::Restore(JackConnectionManager* src)
{
JackConnectionManager* manager = WriteNextStateStart();
memcpy(manager, src, sizeof(JackConnectionManager));
WriteNextStateStop();
}

} // end of namespace



+ 121
- 0
common/JackGraphManager.h View File

@@ -0,0 +1,121 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackGraphManager__
#define __JackGraphManager__

#include "JackShmMem.h"
#include "JackPort.h"
#include "JackConstants.h"
#include "JackConnectionManager.h"
#include "JackAtomicState.h"

namespace Jack
{

/*!
\brief Graph manager: contains the connection manager and the port array.
*/

class JackGraphManager : public JackShmMem, public JackAtomicState<JackConnectionManager>
{

private:

JackPort fPortArray[PORT_NUM];
JackClientTiming fClientTiming[CLIENT_NUM];

jack_port_id_t AllocatePortAux(int refnum, const char* port_name, JackPortFlags flags);
void GetConnectionsAux(JackConnectionManager* manager, const char** res, jack_port_id_t port_index);
const char** GetPortsAux(const char* port_name_pattern, const char* type_name_pattern, unsigned long flags);
float* GetBuffer(jack_port_id_t port_index);
void* GetBufferAux(JackConnectionManager* manager, jack_port_id_t port_index, jack_nframes_t frames);
jack_nframes_t GetTotalLatencyAux(jack_port_id_t port_index, jack_port_id_t src_port_index, JackConnectionManager* manager, int hop_count);

public:

JackGraphManager()
{}
virtual ~JackGraphManager()
{}

// Ports management
jack_port_id_t AllocatePort(int refnum, const char* port_name, JackPortFlags flags);
void ReleasePort(jack_port_id_t port_index);
JackPort* GetPort(jack_port_id_t index);
jack_port_id_t GetPort(const char* name);
jack_nframes_t GetTotalLatency(jack_port_id_t port_index);
int RequestMonitor(jack_port_id_t port_index, bool onoff);

// Connections management
int Connect(jack_port_id_t src_index, jack_port_id_t dst_index);
int Disconnect(jack_port_id_t src_index, jack_port_id_t dst_index);
int GetConnectionsNum(jack_port_id_t port_index);

int ConnectedTo(jack_port_id_t port_src, const char* port_name);
const char** GetConnections(jack_port_id_t port_index);
const char** GetPorts(const char* port_name_pattern, const char* type_name_pattern, unsigned long flags);

int CheckPorts(const char* src, const char* dst, jack_port_id_t* src_index, jack_port_id_t* dst_index);
int CheckPorts(jack_port_id_t port_src, jack_port_id_t port_dst);
int CheckPort(jack_port_id_t port_index);

void DisconnectAllInput(jack_port_id_t port_index);
void DisconnectAllOutput(jack_port_id_t port_index);
int DisconnectAll(jack_port_id_t port_index);

// Client management
int AllocateRefNum();
void ReleaseRefNum(int refnum);

bool IsDirectConnection(int ref1, int ref2);
void DirectConnect(int ref1, int ref2);
void DirectDisconnect(int ref1, int ref2);

int RemovePort(int refnum, jack_port_id_t port_index);
void RemoveAllPorts(int refnum);
void DisconnectAllPorts(int refnum);

int GetInputRefNum(jack_port_id_t port_index);
int GetOutputRefNum(jack_port_id_t port_index);

// Buffer management
void* GetBuffer(jack_port_id_t port_index, jack_nframes_t frames);

// Activation management
void RunCurrentGraph();
bool RunNextGraph();
bool IsFinishedGraph();

int ResumeRefNum(JackClientControl* control, JackSynchro** table);
int SuspendRefNum(JackClientControl* control, JackSynchro** table, long usecs);
JackClientTiming* GetClientTiming(int ref);
void Save(JackConnectionManager* dst);
void Restore(JackConnectionManager* src);

};


} // end of namespace

#endif


+ 105
- 0
common/JackInternalClient.cpp View File

@@ -0,0 +1,105 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackInternalClient.h"
#include "JackEngine.h"
#include "JackServer.h"
#include "JackGraphManager.h"
#include "JackEngineControl.h"
#include "JackClientControl.h"
#include "JackInternalClientChannel.h"
#include <assert.h>


namespace Jack
{

JackGraphManager* JackInternalClient::fGraphManager = NULL;
JackEngineControl* JackInternalClient::fEngineControl = NULL;

// Used for external C API (JackAPI.cpp)
JackGraphManager* GetGraphManager()
{
return JackServer::fInstance->GetGraphManager();
}

JackEngineControl* GetEngineControl()
{
return JackServer::fInstance->GetEngineControl();
}

JackSynchro** GetSynchroTable()
{
return JackServer::fInstance->GetSynchroTable();
}

JackInternalClient::JackInternalClient(JackServer* server, JackSynchro** table): JackClient(table)
{
fClientControl = new JackClientControl();
fChannel = new JackInternalClientChannel(server);
}

JackInternalClient::~JackInternalClient()
{
delete fClientControl;
delete fChannel;
}

int JackInternalClient::Open(const char* name)
{
int result;
JackLog("JackInternalClient::Open name = %s\n", name);
strcpy(fClientControl->fName, name);

// Require new client
fChannel->ClientNew(name, &fClientControl->fRefNum, &fEngineControl, &fGraphManager, this, &result);
if (result < 0) {
jack_error("Cannot open client name = %s", name);
goto error;
}

SetupDriverSync(false);
return 0;

error:
fChannel->Stop();
fChannel->Close();
return -1;
}

JackGraphManager* JackInternalClient::GetGraphManager() const
{
assert(fGraphManager);
return fGraphManager;
}

JackEngineControl* JackInternalClient::GetEngineControl() const
{
assert(fEngineControl);
return fEngineControl;
}

JackClientControl* JackInternalClient::GetClientControl() const
{
return fClientControl;
}

} // end of namespace


+ 60
- 0
common/JackInternalClient.h View File

@@ -0,0 +1,60 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackInternalClient__
#define __JackInternalClient__

#include "JackClient.h"

namespace Jack
{

struct JackEngineControl;
class JackClientChannelInterface;

/*!
\brief Internal clients in the server.
*/

class JackInternalClient : public JackClient
{

private:

JackClientControl* fClientControl; /*! Client control */

public:

JackInternalClient(JackServer* server, JackSynchro** table);
virtual ~JackInternalClient();

int Open(const char* name);

JackGraphManager* GetGraphManager() const;
JackEngineControl* GetEngineControl() const;
JackClientControl* GetClientControl() const;

static JackGraphManager* fGraphManager; /*! Shared memory Port manager */
static JackEngineControl* fEngineControl; /*! Shared engine cotrol */
};

} // end of namespace

#endif

+ 116
- 0
common/JackInternalClientChannel.h View File

@@ -0,0 +1,116 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackInternalClientChannel__
#define __JackInternalClientChannel__

#include "JackChannel.h"

namespace Jack
{

/*!
\brief JackClientChannel for server internal clients.
*/

class JackInternalClientChannel : public JackClientChannelInterface
{

private:

JackServer* fServer;
JackEngine* fEngine;

public:

JackInternalClientChannel(JackServer* server): fServer(server), fEngine(server->GetEngine())
{}
virtual ~JackInternalClientChannel()
{}

void ClientNew(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, int* result)
{
*result = fEngine->ClientInternalNew(name, ref, shared_engine, shared_manager, client);
}
void ClientClose(int refnum, int* result)
{
*result = fEngine->ClientInternalClose(refnum);
}
void ClientActivate(int refnum, int* result)
{
*result = fServer->Activate(refnum);
}
void ClientDeactivate(int refnum, int* result)
{
*result = fServer->Deactivate(refnum);
}

void PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result)
{
*result = fEngine->PortRegister(refnum, name, flags, buffer_size, port_index);
}
void PortUnRegister(int refnum, jack_port_id_t port_index, int* result)
{
*result = fEngine->PortUnRegister(refnum, port_index);
}

void PortConnect(int refnum, const char* src, const char* dst, int* result)
{
*result = fEngine->PortConnect(refnum, src, dst);
}
void PortDisconnect(int refnum, const char* src, const char* dst, int* result)
{
*result = fEngine->PortDisconnect(refnum, src, dst);
}

void PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result)
{
*result = fEngine->PortConnect(refnum, src, dst);
}
void PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result)
{
*result = fEngine->PortDisconnect(refnum, src, dst);
}

void SetBufferSize(jack_nframes_t nframes, int* result)
{
*result = fServer->SetBufferSize(nframes);
}
void SetFreewheel(int onoff, int* result)
{
*result = fServer->SetFreewheel(onoff);
}

// A FINIR
virtual void ReleaseTimebase(int refnum, int* result)
{
*result = fEngine->ReleaseTimebase(refnum);
}

virtual void SetTimebaseCallback(int refnum, int conditional, int* result)
{
*result = fEngine->SetTimebaseCallback(refnum, conditional);
}

};

} // end of namespace

#endif


+ 133
- 0
common/JackLibAPI.cpp View File

@@ -0,0 +1,133 @@
/*
Copyright (C) 2001-2003 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#include "JackDebugClient.h"
#include "JackLibClient.h"
#include "JackChannel.h"
#include "JackGraphManager.h"
#include "JackLibGlobals.h"
#include "JackGlobals.h"
#include "varargs.h"

using namespace Jack;

#ifdef WIN32
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif

#ifdef __cplusplus
extern "C"
{
#endif

EXPORT jack_client_t * jack_client_open (const char *client_name,
jack_options_t options,
jack_status_t *status, ...);
EXPORT jack_client_t * jack_client_new (const char *client_name);
EXPORT int jack_client_close (jack_client_t *client);

#ifdef __cplusplus
}
#endif

JackLibGlobals* JackLibGlobals::fGlobals = NULL;
long JackLibGlobals::fClientCount = 0;

static inline bool CheckPort(jack_port_id_t port_index)
{
return (port_index < PORT_NUM);
}

EXPORT jack_client_t* jack_client_new(const char* client_name)
{
int options = JackUseExactName;
if (getenv("JACK_START_SERVER") == NULL)
options |= JackNoStartServer;

return jack_client_open(client_name, (jack_options_t)options, NULL);
}

// TO BE IMPLEMENTED PROPERLY
EXPORT jack_client_t* jack_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...)
{
va_list ap; /* variable argument pointer */
jack_varargs_t va; /* variable arguments */
jack_status_t my_status;

if (status == NULL) /* no status from caller? */
status = &my_status; /* use local status word */
*status = (jack_status_t)0;

/* validate parameters */
if ((options & ~JackOpenOptions)) {
int my_status1 = *status | (JackFailure | JackInvalidOption);
*status = (jack_status_t)my_status1;
return NULL;
}

/* parse variable arguments */
va_start(ap, status);
jack_varargs_parse(options, ap, &va);
va_end(ap);

JackLog("jack_client_open %s\n", client_name);
if (client_name == NULL) {
jack_error("jack_client_new called with a NULL client_name");
return NULL;
}

JackLibGlobals::Init(); // jack library initialisation

#ifdef __CLIENTDEBUG__
JackClient* client = new JackDebugClient(new JackLibClient(GetSynchroTable())); // Debug mode
#else
JackClient* client = new JackLibClient(GetSynchroTable());
#endif

int res = client->Open(client_name);
if (res < 0) {
delete client;
JackLibGlobals::Destroy(); // jack library destruction
return NULL;
} else {
*status = (jack_status_t)0;
return (jack_client_t*)client;
}
return NULL;
}

EXPORT int jack_client_close(jack_client_t* ext_client)
{
JackLog("jack_client_close\n");
JackClient* client = (JackClient*)ext_client;
if (client == NULL) {
jack_error("jack_client_close called with a NULL client");
return -1;
}
int res = client->Close();
delete client;
JackLog("jack_client_close OK\n");
JackLibGlobals::Destroy(); // jack library destruction
return res;
}



+ 162
- 0
common/JackLibClient.cpp View File

@@ -0,0 +1,162 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackLibClient.h"
#include "JackTime.h"
#include "JackLibGlobals.h"
#include "JackGlobals.h"
#include "JackChannel.h"

namespace Jack
{

// Used for external C API (JackAPI.cpp)
JackGraphManager* GetGraphManager()
{
assert(JackLibGlobals::fGlobals->fGraphManager);
return JackLibGlobals::fGlobals->fGraphManager;
}

JackEngineControl* GetEngineControl()
{
assert(JackLibGlobals::fGlobals->fEngineControl);
return JackLibGlobals::fGlobals->fEngineControl;
}

JackSynchro** GetSynchroTable()
{
assert(JackLibGlobals::fGlobals);
return JackLibGlobals::fGlobals->fSynchroTable;
}

//-------------------
// Client management
//-------------------

JackLibClient::JackLibClient(JackSynchro** table): JackClient(table)
{
JackLog("JackLibClient::JackLibClient table = %x\n", table);
fChannel = JackGlobals::MakeClientChannel();
}

JackLibClient::~JackLibClient()
{
JackLog("JackLibClient::~JackLibClient\n");
delete fChannel;
}

int JackLibClient::Open(const char* name)
{
int shared_engine, shared_client, shared_ports, result;
JackLog("JackLibClient::Open %s\n", name);

// Open server/client channel
if (fChannel->Open(name, this) < 0) {
jack_error("Cannot connect to the server");
goto error;
}

// Start receiving notifications
if (fChannel->Start() < 0) {
jack_error("Cannot start channel");
goto error;
}

// Require new client
fChannel->ClientNew(name, &shared_engine, &shared_client, &shared_ports, &result);
if (result < 0) {
jack_error("Cannot open %s client", name);
goto error;
}

try {
// Map shared memory segments
JackLibGlobals::fGlobals->fEngineControl = shared_engine;
JackLibGlobals::fGlobals->fGraphManager = shared_ports;
fClientControl = shared_client;
verbose = GetEngineControl()->fVerbose;
} catch (int n) {
jack_error("Map shared memory segments exception %d", n);
goto error;
}

SetupDriverSync(false);

// Connect shared synchro : the synchro must be usable in I/O mode when several clients live in the same process
if (!fSynchroTable[fClientControl->fRefNum]->Connect(name)) {
jack_error("Cannot ConnectSemaphore %s client", name);
goto error;
}

JackLog("JackLibClient::Open: name, refnum %s %ld\n", name, fClientControl->fRefNum);
return 0;

error:
fChannel->Stop();
fChannel->Close();
return -1;
}

// Notifications received from the server
// TODO this should be done once for all clients in the process, when a shared notification channel
// will be shared by all clients...
int JackLibClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, int value)
{
int res = 0;

// Done all time
switch (notify) {

case JackNotifyChannelInterface::kAddClient:
JackLog("JackClient::AddClient name = %s, ref = %ld \n", name, refnum);
// the synchro must be usable in I/O mode when several clients live in the same process
res = fSynchroTable[refnum]->Connect(name) ? 0 : -1;
break;

case JackNotifyChannelInterface::kRemoveClient:
JackLog("JackClient::RemoveClient name = %s, ref = %ld \n", name, refnum);
if (strcmp(GetClientControl()->fName, name) != 0)
res = fSynchroTable[refnum]->Disconnect() ? 0 : -1;
break;
}

return res;
}

JackGraphManager* JackLibClient::GetGraphManager() const
{
assert(JackLibGlobals::fGlobals->fGraphManager);
return JackLibGlobals::fGlobals->fGraphManager;
}

JackEngineControl* JackLibClient::GetEngineControl() const
{
assert(JackLibGlobals::fGlobals->fEngineControl);
return JackLibGlobals::fGlobals->fEngineControl;
}

JackClientControl* JackLibClient::GetClientControl() const
{
return fClientControl;
}

} // end of namespace




+ 60
- 0
common/JackLibClient.h View File

@@ -0,0 +1,60 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackLibClient__
#define __JackLibClient__

#include "JackClient.h"
#include "JackShmMem.h"
#include "JackClientControl.h"
#include "JackEngineControl.h"

namespace Jack
{

/*!
\brief Client on the library side.
*/

class JackLibClient : public JackClient
{

private:

JackShmReadWritePtr1<JackClientControl> fClientControl; /*! Shared client control */

public:

JackLibClient(JackSynchro** table);
virtual ~JackLibClient();

int Open(const char* name);

int ClientNotifyImp(int refnum, const char* name, int notify, int sync, int value);

JackGraphManager* GetGraphManager() const;
JackEngineControl* GetEngineControl() const;
JackClientControl* GetClientControl() const;
};


} // end of namespace

#endif


+ 97
- 0
common/JackLibGlobals.h View File

@@ -0,0 +1,97 @@
/*
Copyright (C) 2005 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackLibGlobals__
#define __JackLibGlobals__

#include "JackShmMem.h"
#include "JackEngineControl.h"
#ifdef __APPLE__
#include "JackMachPort.h"
#include <map>
#endif
#include "JackGlobals.h"
#include "JackTime.h"
#include <assert.h>

namespace Jack
{

class JackClient;

/*!
\brief Global library static structure: singleton kind of pattern.
*/

struct JackLibGlobals
{
JackShmReadWritePtr<JackGraphManager> fGraphManager; /*! Shared memory Port manager */
JackShmReadWritePtr<JackEngineControl> fEngineControl; /*! Shared engine control */ // transport engine has to be writable
JackSynchro* fSynchroTable[CLIENT_NUM]; /*! Shared synchro table */
#ifdef __APPLE__
std::map<mach_port_t, JackClient*> fClientTable; /*! Client table */
#endif

static long fClientCount;
static JackLibGlobals* fGlobals;

JackLibGlobals()
{
JackLog("JackLibGlobals\n");
for (int i = 0; i < CLIENT_NUM; i++)
fSynchroTable[i] = JackGlobals::MakeSynchro();
fGraphManager = -1;
fEngineControl = -1;
}

virtual ~JackLibGlobals()
{
JackLog("~JackLibGlobals\n");
for (int i = 0; i < CLIENT_NUM; i++) {
fSynchroTable[i]->Disconnect();
delete fSynchroTable[i];
}
}

static void Init()
{
if (fClientCount++ == 0 && !fGlobals) {
JackLog("JackLibGlobals Init %x\n", fGlobals);
JackGlobals::InitClient();
InitTime();
fGlobals = new JackLibGlobals();
}
}

static void Destroy()
{
if (--fClientCount == 0 && fGlobals) {
JackLog("JackLibGlobals Destroy %x\n", fGlobals);
delete fGlobals;
fGlobals = NULL;
JackGlobals::Destroy();
}
}

};

} // end of namespace

#endif


+ 209
- 0
common/JackLoopbackDriver.cpp View File

@@ -0,0 +1,209 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackLoopbackDriver.h"
#include "JackEngineControl.h"
#include "JackGraphManager.h"
#include <iostream>
#include <assert.h>

namespace Jack
{

int JackLoopbackDriver::Open(jack_nframes_t nframes,
jack_nframes_t samplerate,
int capturing,
int playing,
int inchannels,
int outchannels,
bool monitor,
const char* capture_driver_name,
const char* playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency)
{
return JackAudioDriver::Open(nframes,
samplerate,
capturing,
playing,
inchannels,
outchannels,
monitor,
capture_driver_name,
playback_driver_name,
capture_latency,
playback_latency);
}

int JackLoopbackDriver::Process()
{
assert(fCaptureChannels == fPlaybackChannels);

// Loopback copy
for (int i = 0; i < fCaptureChannels; i++) {
memcpy(fGraphManager->GetBuffer(fCapturePortList[i], fEngineControl->fBufferSize),
fGraphManager->GetBuffer(fPlaybackPortList[i], fEngineControl->fBufferSize),
sizeof(float) * fEngineControl->fBufferSize);
}

fGraphManager->ResumeRefNum(fClientControl, fSynchroTable); // Signal all clients
if (fEngineControl->fSyncMode) {
if (fGraphManager->SuspendRefNum(fClientControl, fSynchroTable, fEngineControl->fTimeOutUsecs) < 0)
jack_error("JackLoopbackDriver::ProcessSync SuspendRefNum error");
}
return 0;
}

void JackLoopbackDriver::PrintState()
{
int i;
jack_port_id_t port_index;

std::cout << "JackLoopbackDriver state" << std::endl;
std::cout << "Input ports" << std::endl;

for (i = 0; i < fPlaybackChannels; i++) {
port_index = fCapturePortList[i];
JackPort* port = fGraphManager->GetPort(port_index);
std::cout << port->GetName() << std::endl;
if (fGraphManager->GetConnectionsNum(port_index)) {}
}

std::cout << "Output ports" << std::endl;

for (i = 0; i < fCaptureChannels; i++) {
port_index = fPlaybackPortList[i];
JackPort* port = fGraphManager->GetPort(port_index);
std::cout << port->GetName() << std::endl;
if (fGraphManager->GetConnectionsNum(port_index)) {}
}
}

} // end of namespace

/*
#ifdef __cplusplus
extern "C" {
#endif
jack_driver_desc_t * driver_get_descriptor ()
{
jack_driver_desc_t * desc;
jack_driver_param_desc_t * params;
unsigned int i;
desc = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t));
strcpy (desc->name, "dummy");
desc->nparams = 5;
params = (jack_driver_param_desc_t*)calloc (desc->nparams, sizeof (jack_driver_param_desc_t));
i = 0;
strcpy (params[i].name, "capture");
params[i].character = 'C';
params[i].type = JackDriverParamUInt;
params[i].value.ui = 2U;
strcpy (params[i].short_desc, "Number of capture ports");
strcpy (params[i].long_desc, params[i].short_desc);
i++;
strcpy (params[i].name, "playback");
params[i].character = 'P';
params[i].type = JackDriverParamUInt;
params[1].value.ui = 2U;
strcpy (params[i].short_desc, "Number of playback ports");
strcpy (params[i].long_desc, params[i].short_desc);
i++;
strcpy (params[i].name, "rate");
params[i].character = 'r';
params[i].type = JackDriverParamUInt;
params[i].value.ui = 48000U;
strcpy (params[i].short_desc, "Sample rate");
strcpy (params[i].long_desc, params[i].short_desc);
i++;
strcpy (params[i].name, "period");
params[i].character = 'p';
params[i].type = JackDriverParamUInt;
params[i].value.ui = 1024U;
strcpy (params[i].short_desc, "Frames per period");
strcpy (params[i].long_desc, params[i].short_desc);
i++;
strcpy (params[i].name, "wait");
params[i].character = 'w';
params[i].type = JackDriverParamUInt;
params[i].value.ui = 21333U;
strcpy (params[i].short_desc,
"Number of usecs to wait between engine processes");
strcpy (params[i].long_desc, params[i].short_desc);
desc->params = params;
return desc;
}
Jack::JackDriverClientInterface* driver_initialize(Jack::JackEngine* engine, Jack::JackSynchro** table, const JSList* params)
{
jack_nframes_t sample_rate = 48000;
jack_nframes_t period_size = 1024;
unsigned int capture_ports = 2;
unsigned int playback_ports = 2;
const JSList * node;
const jack_driver_param_t * param;
for (node = params; node; node = jack_slist_next (node)) {
param = (const jack_driver_param_t *) node->data;
switch (param->character) {
case 'C':
capture_ports = param->value.ui;
break;
case 'P':
playback_ports = param->value.ui;
break;
case 'r':
sample_rate = param->value.ui;
break;
case 'p':
period_size = param->value.ui;
break;
}
}
Jack::JackDriverClientInterface* driver = new Jack::JackLoopbackDriver("loopback", engine, table);
if (driver->Open(period_size, sample_rate, 1, 1, capture_ports, playback_ports, "loopback") == 0) {
return driver;
} else {
delete driver;
return NULL;
}
}
#ifdef __cplusplus
}
#endif
*/


+ 63
- 0
common/JackLoopbackDriver.h View File

@@ -0,0 +1,63 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackLoopbackDriver__
#define __JackLoopbackDriver__

#include "JackAudioDriver.h"
#include "JackTime.h"

namespace Jack
{

/*!
\brief The loopback driver : to be used to "pipeline" applications connected in sequence.
*/

class JackLoopbackDriver : public JackAudioDriver
{

public:

JackLoopbackDriver(const char* name, JackEngine* engine, JackSynchro** table)
: JackAudioDriver(name, engine, table)
{}
virtual ~JackLoopbackDriver()
{}

int Open(jack_nframes_t frames_per_cycle,
jack_nframes_t rate,
int capturing,
int playing,
int chan_in,
int chan_out,
bool monitor,
const char* capture_driver_name,
const char* playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency);

int Process();
void PrintState();
};

} // end of namespace

#endif

+ 229
- 0
common/JackPort.cpp View File

@@ -0,0 +1,229 @@
/*
Copyright (C) 2001-2003 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackPort.h"
#include "JackError.h"
#include <stdio.h>

namespace Jack
{

JackPort::JackPort()
: fFlags(JackPortIsInput), fRefNum( -1), fLatency(0), fMonitorRequests(0), fInUse(false), fLocked(false), fTied(NO_PORT)
{}

JackPort::~JackPort()
{}

void JackPort::Allocate(int refnum, const char* port_name, JackPortFlags flags)
{
fFlags = flags;
fRefNum = refnum;
strcpy(fName, port_name);
memset(fBuffer, 0, BUFFER_SIZE_MAX * sizeof(float));
fInUse = true;
fLocked = false;
fLatency = 0;
fTied = NO_PORT;
}

void JackPort::Release()
{
fFlags = JackPortIsInput;
fRefNum = -1;
fInUse = false;
fLocked = false;
fLatency = 0;
fTied = NO_PORT;
}

bool JackPort::IsUsed() const
{
return fInUse;
}

float* JackPort::GetBuffer()
{
return fBuffer;
}

int JackPort::GetRefNum() const
{
return fRefNum;
}

int JackPort::Lock()
{
fLocked = true;
return 0;
}

int JackPort::Unlock()
{
fLocked = false;
return 0;
}

jack_nframes_t JackPort::GetLatency() const
{
return fLatency;
}

void JackPort::SetLatency(jack_nframes_t nframes)
{
fLatency = nframes;
}

int JackPort::Tie(jack_port_id_t port_index)
{
fTied = port_index;
return 0;
}

int JackPort::UnTie()
{
fTied = NO_PORT;
return 0;
}

int JackPort::RequestMonitor(bool onoff)
{
/**
jackd.h
* If @ref JackPortCanMonitor is set for this @a port, turn input
* monitoring on or off. Otherwise, do nothing.
if (!(fFlags & JackPortCanMonitor))
return -1;
*/

if (onoff) {
fMonitorRequests++;
} else if (fMonitorRequests) {
fMonitorRequests--;
}

return 0;
}

int JackPort::EnsureMonitor(bool onoff)
{
/**
jackd.h
* If @ref JackPortCanMonitor is set for this @a port, turn input
* monitoring on or off. Otherwise, do nothing.
if (!(fFlags & JackPortCanMonitor))
return -1;
*/

if (onoff) {
if (fMonitorRequests == 0) {
fMonitorRequests++;
}
} else {
if (fMonitorRequests > 0) {
fMonitorRequests = 0;
}
}

return 0;
}

bool JackPort::MonitoringInput()
{
return (fMonitorRequests > 0);
}

const char* JackPort::GetName() const
{
return fName;
}

const char* JackPort::GetShortName() const
{
/* we know there is always a colon, because we put
it there ...
*/
return strchr(fName, ':') + 1;
}

int JackPort::Flags() const
{
return fFlags;
}

const char* JackPort::Type() const
{
// TO IMPROVE
return "Audio";
}

int JackPort::SetName(const char* new_name)
{
char* colon = strchr(fName, ':');
int len = sizeof(fName) - ((int) (colon - fName)) - 2;
snprintf(colon + 1, len, "%s", new_name);
return 0;
}

void JackPort::MixBuffer(float* mixbuffer, float* buffer, jack_nframes_t frames)
{
jack_nframes_t frames_group = frames / 4;
frames = frames % 4;

while (frames_group > 0) {
register float mixFloat1 = *mixbuffer;
register float sourceFloat1 = *buffer;
register float mixFloat2 = *(mixbuffer + 1);
register float sourceFloat2 = *(buffer + 1);
register float mixFloat3 = *(mixbuffer + 2);
register float sourceFloat3 = *(buffer + 2);
register float mixFloat4 = *(mixbuffer + 3);
register float sourceFloat4 = *(buffer + 3);

buffer += 4;
frames_group--;

mixFloat1 += sourceFloat1;
mixFloat2 += sourceFloat2;
mixFloat3 += sourceFloat3;
mixFloat4 += sourceFloat4;

*mixbuffer = mixFloat1;
*(mixbuffer + 1) = mixFloat2;
*(mixbuffer + 2) = mixFloat3;
*(mixbuffer + 3) = mixFloat4;

mixbuffer += 4;
}

while (frames > 0) {
register float mixFloat1 = *mixbuffer;
register float sourceFloat1 = *buffer;
buffer++;
frames--;
mixFloat1 += sourceFloat1;
*mixbuffer = mixFloat1;
mixbuffer++;
}
}

} // end of namespace

+ 98
- 0
common/JackPort.h View File

@@ -0,0 +1,98 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackPort__
#define __JackPort__

#include "types.h"
#include "JackConstants.h"

namespace Jack
{

#define ALL_PORTS 0xFFFF
#define NO_PORT 0xFFFE

/*!
\brief Base class for port.
*/

class JackPort
{

friend class JackGraphManager;

private:

enum JackPortFlags fFlags;
char fName[JACK_PORT_NAME_SIZE + 2];
int fRefNum;

jack_nframes_t fLatency;
uint8_t fMonitorRequests;

bool fInUse;
bool fLocked;
jack_port_id_t fTied; // Locally tied source port

float fBuffer[BUFFER_SIZE_MAX];

bool IsUsed() const;
static void MixBuffer(float* mixbuffer, float* buffer, jack_nframes_t frames);

public:

JackPort();
virtual ~JackPort();

void Allocate(int refnum, const char* port_name, JackPortFlags flags);
void Release();
const char* GetName() const;
const char* GetShortName() const;
int SetName(const char * name);

int Flags() const;
const char* Type() const;

int Lock();
int Unlock();

int Tie(jack_port_id_t port_index);
int UnTie();

jack_nframes_t GetLatency() const;
void SetLatency(jack_nframes_t latency);

int RequestMonitor(bool onoff);
int EnsureMonitor(bool onoff);
bool MonitoringInput();

float* GetBuffer();
int GetRefNum() const;
};


} // end of namespace


#endif


+ 205
- 0
common/JackPosixSemaphore.cpp View File

@@ -0,0 +1,205 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackPosixSemaphore.h"
#include "JackChannel.h"
#include "JackError.h"
#include <fcntl.h>
#include <sys/time.h>

namespace Jack
{

void JackPosixSemaphore::BuildName(const char* name, char* res)
{
sprintf(res, "%s/jack_sem.%s", jack_client_dir, name);
}

bool JackPosixSemaphore::Signal()
{
int res;
assert(fSemaphore);

if (fFlush)
return true;

if ((res = sem_post(fSemaphore)) != 0) {
jack_error("JackPosixSemaphore::Signal name = %s err = %s", fName, strerror(errno));
}
return (res == 0);
}

bool JackPosixSemaphore::SignalAll()
{
int res;
assert(fSemaphore);

if (fFlush)
return true;

if ((res = sem_post(fSemaphore)) != 0) {
jack_error("JackPosixSemaphore::SignalAll name = %s err = %s", fName, strerror(errno));
}
return (res == 0);
}

/*
bool JackPosixSemaphore::Wait()
{
int res;
assert(fSemaphore);
if ((res = sem_wait(fSemaphore)) != 0) {
jack_error("JackPosixSemaphore::Wait name = %s err = %s", fName, strerror(errno));
}
return (res == 0);
}
*/

bool JackPosixSemaphore::Wait()
{
int res;

while ((res = sem_wait(fSemaphore) < 0)) {
jack_error("JackPosixSemaphore::Wait name = %s err = %s", fName, strerror(errno));
if (errno != EINTR)
break;
}
return (res == 0);
}


/*
#ifdef __linux__
bool JackPosixSemaphore::TimedWait(long usec) // unusable semantic !!
{
int res;
struct timeval now;
timespec time;
assert(fSemaphore);
gettimeofday(&now, 0);
time.tv_sec = now.tv_sec + usec / 1000000;
time.tv_nsec = (now.tv_usec + (usec % 1000000)) * 1000;
if ((res = sem_timedwait(fSemaphore, &time)) != 0) {
jack_error("JackPosixSemaphore::TimedWait err = %s", strerror(errno));
JackLog("now %ld %ld \n", now.tv_sec, now.tv_usec);
JackLog("next %ld %ld \n", time.tv_sec, time.tv_nsec/1000);
}
return (res == 0);
}
#else
#warning "JackPosixSemaphore::TimedWait is not supported: Jack in SYNC mode with JackPosixSemaphore will not run properly !!"
bool JackPosixSemaphore::TimedWait(long usec)
{
return Wait();
}
#endif
*/

#warning JackPosixSemaphore::TimedWait not available : synchronous mode may not work correctly if POSIX semaphore are used

bool JackPosixSemaphore::TimedWait(long usec)
{
return Wait();
}

// Server side : publish the semaphore in the global namespace
bool JackPosixSemaphore::Allocate(const char* name, int value)
{
BuildName(name, fName);
JackLog("JackPosixSemaphore::Allocate name = %s val = %ld\n", fName, value);

if ((fSemaphore = sem_open(fName, O_CREAT, 0777, value)) == (sem_t*)SEM_FAILED) {
jack_error("Allocate: can't check in named semaphore name = %s err = %s", fName, strerror(errno));
return false;
} else {
return true;
}
}

// Client side : get the published semaphore from server
bool JackPosixSemaphore::ConnectInput(const char* name)
{
BuildName(name, fName);
JackLog("JackPosixSemaphore::Connect %s\n", fName);

// Temporary...
if (fSemaphore) {
JackLog("Already connected name = %s\n", name);
return true;
}

if ((fSemaphore = sem_open(fName, O_CREAT)) == (sem_t*)SEM_FAILED) {
jack_error("Connect: can't connect named semaphore name = %s err = %s", fName, strerror(errno));
return false;
} else {
int val = 0;
sem_getvalue(fSemaphore, &val);
JackLog("JackPosixSemaphore::Connect sem_getvalue %ld\n", val);
return true;
}
}

bool JackPosixSemaphore::Connect(const char* name)
{
return ConnectInput(name);
}

bool JackPosixSemaphore::ConnectOutput(const char* name)
{
return ConnectInput(name);
}

bool JackPosixSemaphore::Disconnect()
{
JackLog("JackPosixSemaphore::Disconnect %s\n", fName);

if (fSemaphore) {
if (sem_close(fSemaphore) != 0) {
jack_error("Disconnect: can't disconnect named semaphore name = %s err = %s", fName, strerror(errno));
return false;
} else {
fSemaphore = NULL;
return true;
}
} else {
return true;
}
}

// Server side : destroy the semaphore
void JackPosixSemaphore::Destroy()
{
if (fSemaphore != NULL) {
JackLog("JackPosixSemaphore::Destroy\n");
sem_unlink(fName);
if (sem_close(fSemaphore) != 0) {
jack_error("Destroy: can't destroy semaphore name = %s err = %s", fName, strerror(errno));
}
fSemaphore = NULL;
} else {
jack_error("JackPosixSemaphore::Destroy semaphore == NULL");
}
}

} // end of namespace


+ 71
- 0
common/JackPosixSemaphore.h View File

@@ -0,0 +1,71 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackPosixSemaphore__
#define __JackPosixSemaphore__

#include "JackSynchro.h"
#include <semaphore.h>
#include <time.h>
#include <stdio.h>
#include <assert.h>

namespace Jack
{

/*!
\brief Inter process synchronization using POSIX semaphore.
*/

class JackPosixSemaphore : public JackSynchro
{

private:

sem_t* fSemaphore;

protected:

void BuildName(const char* name, char* res);

public:

JackPosixSemaphore(): JackSynchro(), fSemaphore(NULL)
{}
virtual ~JackPosixSemaphore()
{}

bool Signal();
bool SignalAll();
bool Wait();
bool TimedWait(long usec);

bool Allocate(const char* name, int value);
bool Connect(const char* name);
bool ConnectInput(const char* name);
bool ConnectOutput(const char* name);
bool Disconnect();
void Destroy();
};

} // end of namespace


#endif


+ 209
- 0
common/JackPosixThread.cpp View File

@@ -0,0 +1,209 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackPosixThread.h"
#include "JackError.h"
#include <string.h> // for memset

namespace Jack
{

void* JackPosixThread::ThreadHandler(void* arg)
{
JackPosixThread* obj = (JackPosixThread*)arg;
JackRunnableInterface* runnable = obj->fRunnable;
int err;

if ((err = pthread_setcanceltype(obj->fCancellation, NULL)) != 0) {
jack_error("pthread_setcanceltype err = %s", strerror(err));
}

// Call Init method
if (!runnable->Init()) {
jack_error("Thread init fails: thread quits");
return 0;
}

JackLog("ThreadHandler: start\n");

// If Init succeed start the thread loop
bool res = true;
while (obj->fRunning && res) {
res = runnable->Execute();
//pthread_testcancel();
}

JackLog("ThreadHandler: exit\n");
return 0;
}

int JackPosixThread::Start()
{
int res;
fRunning = true;

if (fRealTime) {

JackLog("Create RT thread\n");

/* Get the client thread to run as an RT-FIFO
scheduled thread of appropriate priority.
*/
pthread_attr_t attributes;
struct sched_param rt_param;
pthread_attr_init(&attributes);
if ((res = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED))) {
jack_error("Cannot request explicit scheduling for RT thread %d %s", res, strerror(errno));
return -1;
}
if ((res = pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_JOINABLE))) {
jack_error("Cannot request joinable thread creation for RT thread %d %s", res, strerror(errno));
return -1;
}
if ((res = pthread_attr_setscope(&attributes, PTHREAD_SCOPE_SYSTEM))) {
jack_error("Cannot set scheduling scope for RT thread %d %s", res, strerror(errno));
return -1;
}

//if ((res = pthread_attr_setschedpolicy(&attributes, SCHED_FIFO))) {

if ((res = pthread_attr_setschedpolicy(&attributes, SCHED_RR))) {
jack_error("Cannot set FIFO scheduling class for RT thread %d %s", res, strerror(errno));
return -1;
}

if ((res = pthread_attr_setscope(&attributes, PTHREAD_SCOPE_SYSTEM))) {
jack_error("Cannot set scheduling scope for RT thread %d %s", res, strerror(errno));
return -1;
}

memset(&rt_param, 0, sizeof(rt_param));
rt_param.sched_priority = fPriority;

if ((res = pthread_attr_setschedparam(&attributes, &rt_param))) {
jack_error("Cannot set scheduling priority for RT thread %d %s", res, strerror(errno));
return -1;
}

if ((res = pthread_create(&fThread, &attributes, ThreadHandler, this))) {
jack_error("Cannot set create thread %d %s", res, strerror(errno));
return -1;
}

return 0;
} else {
JackLog("Create non RT thread\n");

if ((res = pthread_create(&fThread, 0, ThreadHandler, this))) {
jack_error("Cannot set create thread %d %s", res, strerror(errno));
return -1;
}
return 0;
}
}

int JackPosixThread::StartSync()
{
jack_error("Not implemented yet");
return -1;
}

int JackPosixThread::Kill()
{
if (fThread) { // If thread has been started
JackLog("JackPosixThread::Kill\n");
void* status;
pthread_cancel(fThread);
pthread_join(fThread, &status);
return 0;
} else {
return -1;
}
}

int JackPosixThread::Stop()
{
if (fThread) { // If thread has been started
JackLog("JackPosixThread::Stop\n");
void* status;
fRunning = false; // Request for the thread to stop
pthread_join(fThread, &status);
return 0;
} else {
return -1;
}
}

int JackPosixThread::AcquireRealTime()
{
struct sched_param rtparam;
int res;

if (!fThread)
return -1;

memset(&rtparam, 0, sizeof(rtparam));
rtparam.sched_priority = fPriority;

//if ((res = pthread_setschedparam(fThread, SCHED_FIFO, &rtparam)) != 0) {

if ((res = pthread_setschedparam(fThread, SCHED_RR, &rtparam)) != 0) {
jack_error("Cannot use real-time scheduling (FIFO/%d) "
"(%d: %s)", rtparam.sched_priority, res,
strerror(res));
return -1;
}
return 0;
}

int JackPosixThread::AcquireRealTime(int priority)
{
fPriority = priority;
return AcquireRealTime();
}

int JackPosixThread::DropRealTime()
{
struct sched_param rtparam;
int res;

if (!fThread)
return -1;

memset(&rtparam, 0, sizeof(rtparam));
rtparam.sched_priority = 0;

if ((res = pthread_setschedparam(fThread, SCHED_OTHER, &rtparam)) != 0) {
jack_error("Cannot switch to normal scheduling priority(%s)\n", strerror(errno));
return -1;
}
return 0;
}

pthread_t JackPosixThread::GetThreadID()
{
return fThread;
}

} // end of namespace


+ 73
- 0
common/JackPosixThread.h View File

@@ -0,0 +1,73 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackPosixThread__
#define __JackPosixThread__

#include "JackThread.h"
#include <pthread.h>

namespace Jack
{

/*!
\brief The POSIX thread base class.
*/

class JackPosixThread : public JackThread
{

protected:

pthread_t fThread;

static void* ThreadHandler(void* arg);

public:

JackPosixThread(JackRunnableInterface* runnable, bool real_time, int priority, int cancellation)
: JackThread(runnable, priority, real_time, cancellation), fThread((pthread_t)NULL)
{}
JackPosixThread(JackRunnableInterface* runnable)
: JackThread(runnable, 0, false, PTHREAD_CANCEL_DEFERRED), fThread((pthread_t)NULL)
{}
JackPosixThread(JackRunnableInterface* runnable, int cancellation)
: JackThread(runnable, 0, false, cancellation), fThread((pthread_t)NULL)
{}

virtual ~JackPosixThread()
{}

virtual int Start();
virtual int StartSync();
virtual int Kill();
virtual int Stop();

virtual int AcquireRealTime();
virtual int AcquireRealTime(int priority);
virtual int DropRealTime();

pthread_t GetThreadID();
};

} // end of namespace


#endif

+ 180
- 0
common/JackProcessSync.h View File

@@ -0,0 +1,180 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackProcessSync__
#define __JackProcessSync__

#include "JackSyncInterface.h"
#include "JackSynchro.h"
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>

namespace Jack
{

/*!
\brief A synchronization primitive built using a condition variable.
*/

class JackProcessSync : public JackSyncInterface
{

private:

pthread_mutex_t fLock; // Mutex
pthread_cond_t fCond; // Condition variable

public:

JackProcessSync(): JackSyncInterface()
{
pthread_mutex_init(&fLock, NULL);
pthread_cond_init(&fCond, NULL);
}
virtual ~JackProcessSync()
{
pthread_mutex_destroy(&fLock);
pthread_cond_destroy(&fCond);
}

bool Allocate(const char* name)
{
return true;
}

bool Connect(const char* name)
{
return true;
}

void Destroy()
{}

bool TimedWait(long usec)
{
struct timeval T0, T1;
timespec time;
struct timeval now;
int res;

pthread_mutex_lock(&fLock);
JackLog("JackProcessSync::Wait time out = %ld\n", usec);
gettimeofday(&T0, 0);

static const UInt64 kNanosPerSec = 1000000000ULL;
static const UInt64 kNanosPerUsec = 1000ULL;
gettimeofday(&now, 0);
UInt64 nextDateNanos = now.tv_sec * kNanosPerSec + (now.tv_usec + usec) * kNanosPerUsec;
time.tv_sec = nextDateNanos / kNanosPerSec;
time.tv_nsec = nextDateNanos % kNanosPerSec;
res = pthread_cond_timedwait(&fCond, &fLock, &time);
if (res != 0)
jack_error("pthread_cond_timedwait error usec = %ld err = %s", usec, strerror(res));

gettimeofday(&T1, 0);
pthread_mutex_unlock(&fLock);
JackLog("JackProcessSync::Wait finished delta = %5.1lf\n",
(1e6 * T1.tv_sec - 1e6 * T0.tv_sec + T1.tv_usec - T0.tv_usec));
return (res == 0);
}

void Wait()
{
int res;
pthread_mutex_lock(&fLock);
JackLog("JackProcessSync::Wait...\n");
if ((res = pthread_cond_wait(&fCond, &fLock)) != 0)
jack_error("pthread_cond_wait error err = %s", strerror(errno));
pthread_mutex_unlock(&fLock);
JackLog("JackProcessSync::Wait finished\n");
}

void SignalAll()
{
//pthread_mutex_lock(&fLock);
pthread_cond_broadcast(&fCond);
//pthread_mutex_unlock(&fLock);
}

};

/*!
\brief A synchronization primitive built using an inter-process synchronization object.
*/

class JackInterProcessSync : public JackSyncInterface
{

private:

JackSynchro* fSynchro;

public:

JackInterProcessSync(JackSynchro* synchro): fSynchro(synchro)
{}
virtual ~JackInterProcessSync()
{
delete fSynchro;
}

bool Allocate(const char* name)
{
return fSynchro->Allocate(name, 0);
}

void Destroy()
{
fSynchro->Destroy();
}

bool Connect(const char* name)
{
return fSynchro->Connect(name);
}

bool TimedWait(long usec)
{
struct timeval T0, T1;
JackLog("JackInterProcessSync::Wait...\n");
gettimeofday(&T0, 0);
bool res = fSynchro->TimedWait(usec);
gettimeofday(&T1, 0);
JackLog("JackInterProcessSync::Wait finished delta = %5.1lf\n",
(1e6 * T1.tv_sec - 1e6 * T0.tv_sec + T1.tv_usec - T0.tv_usec));
return res;
}

void Wait()
{
fSynchro->Wait();
}

void SignalAll()
{
fSynchro->SignalAll();
}
};


} // end of namespace

#endif


+ 207
- 0
common/JackPthreadCond.cpp View File

@@ -0,0 +1,207 @@
/*
Copyright (C) 2004-2005 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackPthreadCond.h"
#include "JackError.h"

namespace Jack
{

JackPthreadCondArray* JackPthreadCondServer::fTable = NULL;
long JackPthreadCondServer::fCount = 0;

JackShmReadWritePtr1<JackPthreadCondArray> JackPthreadCondClient::fTable;
long JackPthreadCondClient::fCount = 0;

JackPthreadCondArray::JackPthreadCondArray()
{
for (int i = 0; i < MAX_ITEM; i++) {
strcpy(fTable[i].fName, "");
}
}

void JackPthreadCond::BuildName(const char* name, char* res)
{
sprintf(res, "%s/jack_sem.%s", jack_client_dir, name);
}

bool JackPthreadCond::Signal()
{
//pthread_mutex_lock(&fSynchro->fLock);
//JackLog("JackPthreadCond::Signal...\n");
pthread_cond_signal(&fSynchro->fCond);
//pthread_mutex_unlock(&fSynchro->fLock);
return true;
}

bool JackPthreadCond::SignalAll()
{
pthread_cond_broadcast(&fSynchro->fCond);
return true;
}

bool JackPthreadCond::Wait()
{
pthread_mutex_lock(&fSynchro->fLock);
//JackLog("JackPthreadCond::Wait...\n");
pthread_cond_wait(&fSynchro->fCond, &fSynchro->fLock);
pthread_mutex_unlock(&fSynchro->fLock);
//JackLog("JackProcessSync::Wait finished\n");
return true;
}

bool JackPthreadCond::TimedWait(long usec)
{
timespec time;
struct timeval now;
gettimeofday(&now, 0);
time.tv_sec = now.tv_sec + usec / 1000000;
time.tv_nsec = (now.tv_usec + (usec % 1000000)) * 1000;
pthread_mutex_lock(&fSynchro->fLock);
JackLog("JackProcessSync::Wait...\n");
pthread_cond_timedwait(&fSynchro->fCond, &fSynchro->fLock, &time);
pthread_mutex_unlock(&fSynchro->fLock);
JackLog("JackProcessSync::Wait finished\n");
return true;
}

// Client side : get the published semaphore from server
bool JackPthreadCond::ConnectInput(const char* name)
{
BuildName(name, fName);
JackLog("JackPthreadCond::Connect %s\n", fName);

// Temporary...
if (fSynchro) {
JackLog("Already connected name = %s\n", name);
return true;
}

for (int i = 0; i < MAX_ITEM; i++) {
JackPthreadCondItem* synchro = &(GetTable()->fTable[i]);
if (strcmp(fName, synchro->fName) == 0) {
fSynchro = synchro;
return true;
}
}

return false;
}

bool JackPthreadCond::Connect(const char* name)
{
return ConnectInput(name);
}

bool JackPthreadCond::ConnectOutput(const char* name)
{
return ConnectInput(name);
}

bool JackPthreadCond::Disconnect()
{
JackLog("JackPthreadCond::Disconnect %s\n", fName);

if (fSynchro) {
strcpy(fSynchro->fName, "");
fSynchro = NULL;
return true;
} else {
return false;
}
}

JackPthreadCondServer::JackPthreadCondServer(): JackPthreadCond()
{
if (fCount++ == 0 && !fTable) {
fTable = new JackPthreadCondArray();
}
if (fCount == MAX_ITEM)
throw new std::bad_alloc;
}

JackPthreadCondServer::~JackPthreadCondServer()
{
if (--fCount == 0 && fTable) {
delete fTable;
fTable = NULL;
}
}

bool JackPthreadCondServer::Allocate(const char* name, int value)
{
BuildName(name, fName);
JackLog("JackPthreadCond::Allocate name = %s val = %ld\n", fName, value);

pthread_mutexattr_t mutex_attr;
pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_NORMAL);

pthread_condattr_t cond_attr;
pthread_condattr_init(&cond_attr);
pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);

for (int i = 0; i < MAX_ITEM; i++) {
if (strcmp(fTable->fTable[i].fName, "") == 0) { // first empty place
fSynchro = &fTable->fTable[i];
if (pthread_mutex_init(&fSynchro->fLock, &mutex_attr) != 0) {
jack_error("Allocate: can't check in named semaphore name = %s err = %s", fName, strerror(errno));
return false;
}
if (pthread_cond_init(&fSynchro->fCond, &cond_attr) != 0) {
jack_error("Allocate: can't check in named semaphore name = %s err = %s", fName, strerror(errno));
return false;
}
strcpy(fSynchro->fName, fName);
return true;
}
}

return false;
}

void JackPthreadCondServer::Destroy()
{
if (fSynchro != NULL) {
pthread_mutex_destroy(&fSynchro->fLock);
pthread_cond_destroy(&fSynchro->fCond);
strcpy(fSynchro->fName, "");
fSynchro = NULL;
} else {
jack_error("JackPthreadCond::Destroy semaphore == NULL");
}
}

JackPthreadCondClient::JackPthreadCondClient(int shared_index): JackPthreadCond()
{
if (fCount++ == 0 && !fTable) {
fTable = shared_index;
}
if (fCount == MAX_ITEM)
throw new std::bad_alloc;
}

JackPthreadCondClient::~JackPthreadCondClient()
{
if (--fCount == 0 && fTable)
delete fTable;
}

} // end of namespace


+ 133
- 0
common/JackPthreadCond.h View File

@@ -0,0 +1,133 @@
/*
Copyright (C) 2004-2005 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackPthreadCond__
#define __JackPthreadCond__

#include "JackSynchro.h"
#include "JackShmMem.h"
#include <pthread.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <assert.h>

#define MAX_ITEM 8

namespace Jack
{

struct JackPthreadCondItem
{
char fName[SYNC_MAX_NAME_SIZE];
pthread_mutex_t fLock;
pthread_cond_t fCond;
};

struct JackPthreadCondArray : public JackShmMem
{
JackPthreadCondItem fTable[MAX_ITEM];

JackPthreadCondArray();
virtual ~JackPthreadCondArray()
{}
}
;

/*!
\brief Inter process synchronization using pthread condition variables.
*/

class JackPthreadCond : public JackSynchro
{

protected:

JackPthreadCondItem* fSynchro;
void BuildName(const char* name, char* res);
virtual JackPthreadCondArray* GetTable() = 0;

public:

JackPthreadCond(): fSynchro(NULL)
{}
virtual ~JackPthreadCond()
{}

bool Signal();
bool SignalAll();
bool Wait();
bool TimedWait(long usec);

bool Connect(const char* name);
bool ConnectInput(const char* name);
bool ConnectOutput(const char* name);
bool Disconnect();

};

class JackPthreadCondServer : public JackPthreadCond
{

private:

static JackPthreadCondArray* fTable;
static long fCount;

protected:

JackPthreadCondArray* GetTable()
{
return fTable;
}

public:

JackPthreadCondServer();
virtual ~JackPthreadCondServer();

bool Allocate(const char* name, int value);
void Destroy();
};

class JackPthreadCondClient : public JackPthreadCond
{

private:

static JackShmReadWritePtr1<JackPthreadCondArray> fTable;
static long fCount;

protected:

JackPthreadCondArray* GetTable()
{
return fTable;
}

public:

JackPthreadCondClient(int shared_index);
virtual ~JackPthreadCondClient();
};

} // end of namespace

#endif


+ 612
- 0
common/JackRequest.h View File

@@ -0,0 +1,612 @@

/*
Copyright (C) 2001 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef __JackRequest__
#define __JackRequest__

#include "JackPort.h"
#include "JackChannelTransaction.h"
#include "JackError.h"
#include <stdio.h>

namespace Jack
{

/*!
\brief Request from client to server.
*/

struct JackRequest
{

public:

typedef enum {
kRegisterPort = 1,
kUnRegisterPort = 2,
kConnectPorts = 3,
kDisconnectPorts = 4,
kSetTimeBaseClient = 5,
kActivateClient = 6,
kDeactivateClient = 7,
kDisconnectPort = 8,
kSetClientCapabilities = 9,
kGetPortConnections = 10,
kGetPortNConnections = 11,

kReleaseTimebase = 12,
kSetTimebaseCallback = 13,

kSetBufferSize = 20,
kSetFreeWheel = 21,
kClientNew = 22,
kClientClose = 23,
kConnectNamePorts = 24,
kDisconnectNamePorts = 25,

kNotification = 26
} RequestType;

RequestType fType;

JackRequest()
{}

JackRequest(RequestType type): fType(type)
{}

virtual ~JackRequest()
{}

virtual int Read(JackChannelTransaction* trans)
{
return trans->Read(this, sizeof(JackRequest));
}

virtual int Write(JackChannelTransaction* trans)
{
return -1;
}

};

/*!
\brief Result from the server.
*/

struct JackResult
{

int fResult;

JackResult(): fResult( -1)
{}
JackResult(int status): fResult(status)
{}
virtual ~JackResult()
{}

virtual int Read(JackChannelTransaction* trans)
{
return trans->Read(this, sizeof(JackResult));
}

virtual int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackResult));
}
};

/*!
\brief NewClient request.
*/

struct JackClientNewRequest : public JackRequest
{

char fName[JACK_CLIENT_NAME_SIZE + 1];

JackClientNewRequest()
{}
JackClientNewRequest(const char* name): JackRequest(kClientNew)
{
snprintf(fName, sizeof(fName), "%s", name);
}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fName, sizeof(JackClientNewRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackClientNewRequest));
}
};

/*!
\brief NewClient result.
*/

struct JackClientNewResult : public JackResult
{

int fSharedEngine;
int fSharedClient;
int fSharedPorts;
uint32_t fProtocolVersion;

JackClientNewResult()
{}
JackClientNewResult(int32_t status, int index1, int index2, int index3)
: JackResult(status), fSharedEngine(index1), fSharedClient(index2), fSharedPorts(index3), fProtocolVersion(0)
{}

virtual int Read(JackChannelTransaction* trans)
{
return trans->Read(this, sizeof(JackClientNewResult));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackClientNewResult));
}
};

/*!
\brief CloseClient request.
*/

struct JackClientCloseRequest : public JackRequest
{

int fRefNum;

JackClientCloseRequest()
{}
JackClientCloseRequest(int refnum): JackRequest(kClientClose), fRefNum(refnum)
{}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackClientCloseRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackClientCloseRequest));
}
};

/*!
\brief Activate request.
*/

struct JackActivateRequest : public JackRequest
{

int fRefNum;

JackActivateRequest()
{}
JackActivateRequest(int refnum): JackRequest(kActivateClient), fRefNum(refnum)
{}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackActivateRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackActivateRequest));
}

};

/*!
\brief Deactivate request.
*/

struct JackDeactivateRequest : public JackRequest
{

int fRefNum;

JackDeactivateRequest()
{}
JackDeactivateRequest(int refnum): JackRequest(kDeactivateClient), fRefNum(refnum)
{}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackDeactivateRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackDeactivateRequest));
}

};

/*!
\brief PortRegister request.
*/

struct JackPortRegisterRequest : public JackRequest
{

int fRefNum;
char fName[JACK_PORT_NAME_SIZE + 1];
char fPortType[JACK_PORT_TYPE_SIZE + 1];
unsigned int fFlags;
unsigned int fBufferSize;

JackPortRegisterRequest()
{}
JackPortRegisterRequest(int refnum, const char* name, const char* port_type, unsigned int flags, unsigned int buffer_size)
: JackRequest(kRegisterPort), fRefNum(refnum), fFlags(flags), fBufferSize(buffer_size)
{
strcpy(fName, name);
strcpy(fPortType, port_type);
}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackPortRegisterRequest) - sizeof(JackRequest)) ;
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackPortRegisterRequest));
}
};

/*!
\brief PortRegister result.
*/

struct JackPortRegisterResult : public JackResult
{

jack_port_id_t fPortIndex;

JackPortRegisterResult(): fPortIndex(NO_PORT)
{}

virtual int Read(JackChannelTransaction* trans)
{
return trans->Read(this, sizeof(JackPortRegisterResult));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackPortRegisterResult));
}
};

/*!
\brief PortUnregister request.
*/

struct JackPortUnRegisterRequest : public JackRequest
{

int fRefNum;
int fPortIndex;

JackPortUnRegisterRequest()
{}
JackPortUnRegisterRequest(int refnum, int index): JackRequest(kUnRegisterPort), fRefNum(refnum), fPortIndex(index)
{}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackPortUnRegisterRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackPortUnRegisterRequest));
}
};

/*!
\brief PortConnectName request.
*/

struct JackPortConnectNameRequest : public JackRequest
{

int fRefNum;
char fSrc[JACK_PORT_NAME_SIZE + 1];
char fDst[JACK_PORT_NAME_SIZE + 1];

JackPortConnectNameRequest()
{}
JackPortConnectNameRequest(int refnum, const char* src_name, const char* dst_name): JackRequest(kConnectNamePorts), fRefNum(refnum)
{
strcpy(fSrc, src_name);
strcpy(fDst, dst_name);
}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackPortConnectNameRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackPortConnectNameRequest));
}
};

/*!
\brief PortDisconnectName request.
*/

struct JackPortDisconnectNameRequest : public JackRequest
{

int fRefNum;
char fSrc[JACK_PORT_NAME_SIZE + 1];
char fDst[JACK_PORT_NAME_SIZE + 1];

JackPortDisconnectNameRequest()
{}
JackPortDisconnectNameRequest(int refnum, const char* src_name, const char* dst_name): JackRequest(kDisconnectNamePorts), fRefNum(refnum)
{
strcpy(fSrc, src_name);
strcpy(fDst, dst_name);
}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackPortDisconnectNameRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackPortDisconnectNameRequest));
}
};

/*!
\brief PortConnect request.
*/

struct JackPortConnectRequest : public JackRequest
{

int fRefNum;
jack_port_id_t fSrc;
jack_port_id_t fDst;

JackPortConnectRequest()
{}
JackPortConnectRequest(int refnum, jack_port_id_t src, jack_port_id_t dst): JackRequest(kConnectPorts), fRefNum(refnum), fSrc(src), fDst(dst)
{}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackPortConnectRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackPortConnectRequest));
}
};


/*!
\brief PortDisconnect request.
*/

struct JackPortDisconnectRequest : public JackRequest
{

int fRefNum;
jack_port_id_t fSrc;
jack_port_id_t fDst;

JackPortDisconnectRequest()
{}
JackPortDisconnectRequest(int refnum, jack_port_id_t src, jack_port_id_t dst): JackRequest(kDisconnectPorts), fRefNum(refnum), fSrc(src), fDst(dst)
{}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackPortDisconnectRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackPortDisconnectRequest));
}
};

/*!
\brief SetBufferSize request.
*/

struct JackSetBufferSizeRequest : public JackRequest
{

jack_nframes_t fBufferSize;

JackSetBufferSizeRequest()
{}
JackSetBufferSizeRequest(jack_nframes_t buffer_size): JackRequest(kSetBufferSize), fBufferSize(buffer_size)
{}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fBufferSize, sizeof(JackSetBufferSizeRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackSetBufferSizeRequest));
}
};

/*!
\brief SetFreeWheel request.
*/

struct JackSetFreeWheelRequest : public JackRequest
{

int fOnOff;

JackSetFreeWheelRequest()
{}
JackSetFreeWheelRequest(int onoff): JackRequest(kSetFreeWheel), fOnOff(onoff)
{}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fOnOff, sizeof(JackSetFreeWheelRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackSetFreeWheelRequest));
}
};

/*!
\brief ReleaseTimebase request.
*/

struct JackReleaseTimebaseRequest : public JackRequest
{

int fRefNum;

JackReleaseTimebaseRequest()
{}
JackReleaseTimebaseRequest(int refnum): JackRequest(kReleaseTimebase), fRefNum(refnum)
{}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackReleaseTimebaseRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackReleaseTimebaseRequest));
}

};

/*!
\brief SetTimebaseCallback request.
*/

struct JackSetTimebaseCallbackRequest : public JackRequest
{

int fRefNum;
int fConditionnal;

JackSetTimebaseCallbackRequest()
{}
JackSetTimebaseCallbackRequest(int refnum, int conditional): JackRequest(kSetTimebaseCallback), fRefNum(refnum), fConditionnal(conditional)
{}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackSetTimebaseCallbackRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackSetTimebaseCallbackRequest));
}
};

/*!
\brief ClientNotification request.
*/

struct JackClientNotificationRequest : public JackRequest
{

int fRefNum;
int fNotify;
int fValue;

JackClientNotificationRequest()
{}
JackClientNotificationRequest(int refnum, int notify, int value)
: JackRequest(kNotification), fRefNum(refnum), fNotify(notify), fValue(value)
{}

int Read(JackChannelTransaction* trans)
{
return trans->Read(&fRefNum, sizeof(JackClientNotificationRequest) - sizeof(JackRequest));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackClientNotificationRequest));
}

};

/*!
\brief ClientNotification.
*/

struct JackClientNotification
{
char fName[JACK_CLIENT_NAME_SIZE + 1];
int fRefNum;
int fNotify;
int fValue;
int fSync;

JackClientNotification(): fNotify( -1), fValue( -1)
{}
JackClientNotification(const char* name, int refnum, int notify, int sync, int value)
: fRefNum(refnum), fNotify(notify), fValue(value), fSync(sync)
{
snprintf(fName, sizeof(fName), "%s", name);
}

int Read(JackChannelTransaction* trans)
{
return trans->Read(this, sizeof(JackClientNotification));
}

int Write(JackChannelTransaction* trans)
{
return trans->Write(this, sizeof(JackClientNotification));
}

};

} // end of namespace

#endif

+ 339
- 0
common/JackServer.cpp View File

@@ -0,0 +1,339 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackServer.h"
#include "JackTime.h"
#include "JackFreewheelDriver.h"
#include "JackLoopbackDriver.h"
#include "JackThreadedDriver.h"
#include "JackGlobals.h"
#include "JackEngine.h"
#include "JackAudioDriver.h"
#include "JackChannel.h"
#include "JackClientControl.h"
#include "JackEngineControl.h"
#include "JackSyncInterface.h"
#include "JackGraphManager.h"

#ifdef __APPLE_
#include <CoreFoundation/CFNotificationCenter.h>
#endif

namespace Jack
{

JackServer* JackServer::fInstance = NULL;

JackServer::JackServer(bool sync, long timeout, bool rt, long priority, long loopback, bool verbose)
{
JackGlobals::InitServer();
for (int i = 0; i < CLIENT_NUM; i++)
fSynchroTable[i] = JackGlobals::MakeSynchro();
fGraphManager = new JackGraphManager();
fEngineControl = new JackEngineControl();
fSignal = JackGlobals::MakeInterProcessSync();
fEngine = new JackEngine(fGraphManager, fSynchroTable, fEngineControl, fSignal, sync, timeout, rt, priority, verbose);
fFreewheelDriver = new JackThreadedDriver(new JackFreewheelDriver("freewheel", fEngine, fSynchroTable));
fLoopbackDriver = new JackLoopbackDriver("loopback", fEngine, fSynchroTable);
fChannel = JackGlobals::MakeServerChannel();
fState = new JackConnectionManager();
fFreewheel = false;
fSyncMode = sync;
fLoopback = loopback;
fDriverInfo = NULL;
fAudioDriver = NULL;
fInstance = this; // Unique instance
}

JackServer::~JackServer()
{
for (int i = 0; i < CLIENT_NUM; i++)
delete fSynchroTable[i];
delete fGraphManager;
delete fAudioDriver;
delete fFreewheelDriver;
delete fLoopbackDriver;
delete fEngine;
delete fChannel;
delete fEngineControl;
delete fSignal;
delete fState;
if (fDriverInfo) {
UnloadDriverModule(fDriverInfo->handle);
free(fDriverInfo);
}
JackGlobals::Destroy();
}

// TODO : better handling of intermediate failing cases...

int JackServer::Open(jack_driver_desc_t* driver_desc, JSList* driver_params)
{
if (fChannel->Open(this) < 0) {
jack_error("Server channel open error");
return -1;
}

if (fEngine->Open() != 0) {
jack_error("Cannot open engine");
return -1;
}

if ((fDriverInfo = jack_load_driver(driver_desc)) == NULL) {
return -1;
}

if ((fAudioDriver = fDriverInfo->initialize(fEngine, fSynchroTable, driver_params)) == NULL) {
jack_error("Cannot initialize driver");
return -1;
}

if (fFreewheelDriver->Open() != 0) { // before engine open
jack_error("Cannot open driver");
return -1;
}

// Before engine open
if (fLoopbackDriver->Open(fEngineControl->fBufferSize, fEngineControl->fSampleRate, 1, 1, fLoopback, fLoopback, false, "loopback", "loopback", 0, 0) != 0) {
jack_error("Cannot open driver");
return -1;
}

if (fAudioDriver->Attach() != 0) {
jack_error("Cannot attach audio driver");
return -1;
}

if (fLoopback > 0 && fLoopbackDriver->Attach() != 0) {
jack_error("Cannot attach loopback driver");
return -1;
}

fFreewheelDriver->SetMaster(false);
fAudioDriver->SetMaster(true);
if (fLoopback > 0)
fAudioDriver->AddSlave(fLoopbackDriver);
fAudioDriver->AddSlave(fFreewheelDriver); // After ???
InitTime();

#ifdef __APPLE__
// Send notification to be used in the Jack Router
CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(),
CFSTR("com.grame.jackserver.start"),
CFSTR("com.grame.jackserver"),
NULL,
true);
#endif

return 0;
}

int JackServer::Close()
{
JackLog("JackServer::Close\n");
fChannel->Close();
fSignal->Destroy(); // A REVOIR
fAudioDriver->Detach();
if (fLoopback > 0)
fLoopbackDriver->Detach();
fAudioDriver->Close();
fFreewheelDriver->Close();
fLoopbackDriver->Close();
fEngine->Close();

#ifdef __APPLE__
// Send notification to be used in the Jack Router
CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(),
CFSTR("com.grame.jackserver.stop"),
CFSTR("com.grame.jackserver"),
NULL,
true);
#endif

return 0;
}

int JackServer::Start()
{
JackLog("JackServer::Start\n");
fEngineControl->fFrameTimer.Init();
return fAudioDriver->Start();
}

int JackServer::Stop()
{
JackLog("JackServer::Stop\n");
return fAudioDriver->Stop();
}

int JackServer::Activate(int refnum)
{
fGraphManager->DirectConnect(fFreewheelDriver->GetClientControl()->fRefNum, refnum);
fGraphManager->DirectConnect(refnum, fFreewheelDriver->GetClientControl()->fRefNum);
return fEngine->ClientActivate(refnum);
}

// Disconnection from the FW must be done in last otherwise an intermediate "unconnected"
// (thus unactivated) state may happen where the client is still checked for its end.
int JackServer::Deactivate(int refnum)
{
int res = fEngine->ClientDeactivate(refnum);

// Disconnect only when needed
if (fGraphManager->IsDirectConnection(fFreewheelDriver->GetClientControl()->fRefNum, refnum)) {
fGraphManager->DirectDisconnect(fFreewheelDriver->GetClientControl()->fRefNum, refnum);
} else {
JackLog("JackServer::Deactivate: client = %ld was not activated \n", refnum);
}

// Disconnect only when needed
if (fGraphManager->IsDirectConnection(refnum, fFreewheelDriver->GetClientControl()->fRefNum)) {
fGraphManager->DirectDisconnect(refnum, fFreewheelDriver->GetClientControl()->fRefNum);
} else {
JackLog("JackServer::Deactivate: client = %ld was not activated \n", refnum);
}

return res;
}

int JackServer::SetBufferSize(jack_nframes_t nframes)
{
JackLog("JackServer::SetBufferSize nframes = %ld\n", nframes);

if (nframes > BUFFER_SIZE_MAX)
return -1;

if (fAudioDriver->Stop() != 0) {
jack_error("Cannot stop audio driver");
return -1;
}

if (fAudioDriver->SetBufferSize(nframes) != 0) {
jack_error("Cannot SetBufferSize for audio driver");
return -1;
}

if (fFreewheelDriver->SetBufferSize(nframes) != 0) {
jack_error("Cannot SetBufferSize for freewheel driver");
return -1;
}

fEngine->NotifyBufferSize(nframes);

fEngineControl->fFrameTimer.Init();
return fAudioDriver->Start();
}

/*
Freewheel mode is implemented by switching from the (audio + freewheel) driver to the freewheel driver only:
- "global" connection state is saved
- all audio driver ports are deconnected, thus there is no more dependancies with the audio driver
- the freewheel driver will be synchronized with the end of graph execution : all clients are connected to the freewheel driver
- the freewheel driver becomes the "master"
Normal mode is restored with the connections state valid before freewheel mode was done. Thus one consider that
no graph state change can be done during freewheel mode.
*/

int JackServer::SetFreewheel(bool onoff)
{
JackLog("JackServer::SetFreewheel state = %ld\n", onoff);

if (fFreewheel) {
if (onoff) {
return -1;
} else {
fFreewheel = false;
fFreewheelDriver->Stop();
fGraphManager->Restore(fState); // Restore previous connection state
fEngine->NotifyFreewheel(onoff);
fFreewheelDriver->SetMaster(false);
fEngineControl->fFrameTimer.Init();
return fAudioDriver->Start();
}
} else {
if (onoff) {
fFreewheel = true;
fAudioDriver->Stop();
fGraphManager->Save(fState); // Save connection state
fGraphManager->DisconnectAllPorts(fAudioDriver->GetClientControl()->fRefNum);
fEngine->NotifyFreewheel(onoff);
fFreewheelDriver->SetMaster(true);
return fFreewheelDriver->Start();
} else {
return -1;
}
}
}

// Coming from the RT thread or server channel
void JackServer::Notify(int refnum, int notify, int value)
{
switch (notify) {

case JackNotifyChannelInterface::kGraphOrderCallback:
fEngine->NotifyGraphReorder();
break;

case JackNotifyChannelInterface::kXRunCallback:
fEngine->NotifyXRun(refnum);
break;

case JackNotifyChannelInterface::kZombifyClient:
fEngine->ZombifyClient(refnum);
break;

case JackNotifyChannelInterface::kDeadClient:
JackLog("JackServer: kDeadClient ref = %ld\n", refnum);
Deactivate(refnum);
fEngine->ClientClose(refnum);
break;
}
}

JackEngine* JackServer::GetEngine()
{
return fEngine;
}

JackSynchro** JackServer::GetSynchroTable()
{
return fSynchroTable;
}

JackEngineControl* JackServer::GetEngineControl()
{
return fEngineControl;
}

JackGraphManager* JackServer::GetGraphManager()
{
return fGraphManager;
}

void JackServer::PrintState()
{
fAudioDriver->PrintState();
fEngine->PrintState();
}

} // end of namespace


+ 96
- 0
common/JackServer.h View File

@@ -0,0 +1,96 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackServer__
#define __JackServer__

#include "JackExports.h"
#include "driver_interface.h"
#include "JackDriverLoader.h"
#include "jslist.h"

namespace Jack
{

class JackGraphManager;
class JackConnectionManager;
class JackDriverClientInterface;
class JackServerChannelInterface;
class JackSyncInterface;
struct JackEngineControl;
class JackEngine;

/*!
\brief The Jack server.
*/

class EXPORT JackServer
{

private:

jack_driver_info_t* fDriverInfo;
JackDriverClientInterface* fAudioDriver;
JackDriverClientInterface* fFreewheelDriver;
JackDriverClientInterface* fLoopbackDriver;
JackEngine* fEngine;
JackEngineControl* fEngineControl;
JackGraphManager* fGraphManager;
JackServerChannelInterface* fChannel;
JackConnectionManager* fState;
JackSynchro* fSynchroTable[CLIENT_NUM];
JackSyncInterface* fSignal;
bool fSyncMode;
bool fFreewheel;
long fLoopback;

public:

JackServer(bool sync, long timeout, bool rt, long priority, long loopback, bool verbose);
virtual ~JackServer();

int Open(jack_driver_desc_t* driver_desc, JSList* driver_params);

int Close();

int Start();
int Stop();

int Activate(int refnum);
int Deactivate(int refnum);

int SetBufferSize(jack_nframes_t nframes);
int SetFreewheel(bool onoff);
void Notify(int refnum, int notify, int value);

JackEngine* GetEngine();
JackEngineControl* GetEngineControl();
JackSynchro** GetSynchroTable();
JackGraphManager* GetGraphManager();

void PrintState();

static JackServer* fInstance; // Unique instance
};

} // end of namespace


#endif

+ 174
- 0
common/JackServerAPI.cpp View File

@@ -0,0 +1,174 @@
/*
Copyright (C) 2001-2003 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackInternalClient.h"
#include "JackGraphManager.h"
#include "JackServer.h"
#include "JackDebugClient.h"
#include "JackServerGlobals.h"
#include "JackError.h"
#include "varargs.h"

/*
TODO:
- implement the "jack_client_new", "jack_client_open", "jack_client_close" API so that we can provide a libjackdmp shared library
to be used by clients for direct access.
- automatic launch of the jack server with the first client open, automatic close when the last client exit. Use of a jackd.rsc configuration file.
*/

#ifdef WIN32
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif

#ifdef __cplusplus
extern "C"
{
#endif

EXPORT jack_client_t* my_jack_internal_client_new(const char* client_name);
EXPORT void my_jack_internal_client_close(jack_client_t* ext_client);

EXPORT jack_client_t * jack_client_open (const char *client_name,
jack_options_t options,
jack_status_t *status, ...);
EXPORT jack_client_t * jack_client_new (const char *client_name);
EXPORT int jack_client_close (jack_client_t *client);

#ifdef __cplusplus
}
#endif

using namespace Jack;

EXPORT jack_client_t* my_jack_internal_client_new(const char* client_name)
{
JackLog("jack_internal_client_new %s", client_name);
if (client_name == NULL) {
jack_error("jack_internal_client_new called with a NULL client_name");
return NULL;
}
#ifdef __CLIENTDEBUG__
JackClient* client = new JackDebugClient(new JackInternalClient(JackServer::fInstance, GetSynchroTable())); // Debug mode
#else
JackClient* client = new JackInternalClient(JackServer::fInstance, GetSynchroTable()); // To improve...
#endif

int res = client->Open(client_name);
if (res < 0) {
delete client;
return NULL;
} else {
return (jack_client_t*)client;
}
}

EXPORT void my_jack_internal_client_close(jack_client_t* ext_client)
{
JackLog("jack_internal_client_close");
JackClient* client = (JackClient*)ext_client;
if (client == NULL) {
jack_error("jack_internal_client_close called with a NULL client");
} else {
int res = client->Deactivate();
JackLog("jack_internal_client_close Deactivate %ld", res);
res = client->Close();
delete client;
JackLog("jack_internal_client_close OK");
}
}

EXPORT jack_client_t* jack_client_new(const char* client_name)
{
int options = JackUseExactName;
if (getenv("JACK_START_SERVER") == NULL)
options |= JackNoStartServer;

return jack_client_open(client_name, (jack_options_t)options, NULL);
}

// TO BE IMPLEMENTED PROPERLY
EXPORT jack_client_t* jack_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...)
{
va_list ap; /* variable argument pointer */
jack_varargs_t va; /* variable arguments */
jack_status_t my_status;

if (status == NULL) /* no status from caller? */
status = &my_status; /* use local status word */
*status = (jack_status_t)0;

/* validate parameters */
if ((options & ~JackOpenOptions)) {
int my_status1 = *status | (JackFailure | JackInvalidOption);
*status = (jack_status_t)my_status1;
return NULL;
}

/* parse variable arguments */
va_start(ap, status);
jack_varargs_parse(options, ap, &va);
va_end(ap);

JackLog("jack_client_open %s\n", client_name);
if (client_name == NULL) {
jack_error("jack_client_new called with a NULL client_name");
return NULL;
}

JackServerGlobals::Init(); // jack server initialisation

#ifdef __CLIENTDEBUG__
JackClient* client = new JackDebugClient(new JackInternalClient(JackServer::fInstance, GetSynchroTable())); // Debug mode
#else
JackClient* client = new JackInternalClient(JackServer::fInstance, GetSynchroTable()); // To improve...
#endif

int res = client->Open(client_name);
if (res < 0) {
delete client;
JackServerGlobals::Destroy(); // jack server destruction
return NULL;
} else {
*status = (jack_status_t)0;
return (jack_client_t*)client;
}
return NULL;
}

EXPORT int jack_client_close(jack_client_t* ext_client)
{
JackLog("jack_client_close\n");
JackClient* client = (JackClient*)ext_client;
if (client == NULL) {
jack_error("jack_client_close called with a NULL client");
return -1;
}
int res = client->Close();
delete client;
JackLog("jack_client_close OK\n");
JackServerGlobals::Destroy(); // jack library destruction
return res;
}


+ 389
- 0
common/JackServerGlobals.cpp View File

@@ -0,0 +1,389 @@
/*
Copyright (C) 2005 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackServerGlobals.h"
#include "JackError.h"
#include "shm.h"
#include <getopt.h>

#ifndef WIN32
#include <dirent.h>
#endif

#define DEFAULT_TMP_DIR "/tmp"
char* jack_tmpdir = DEFAULT_TMP_DIR;
static char* server_name = "default";
static int realtime = 0;
static int client_timeout = 0; /* msecs; if zero, use period size. */
static int realtime_priority = 10;
static int verbose_aux = 0;
static int do_mlock = 1;
static unsigned int port_max = 128;
static int loopback = 0;
static int do_unlock = 0;
static int temporary = 0;

namespace Jack
{

JackServerGlobals* JackServerGlobals::fGlobals = NULL;
long JackServerGlobals::fClientCount = 0;
JackServer* JackServerGlobals::fServer = NULL;

#ifndef WIN32

static char* jack_default_server_name(void)
{
char *server_name;
if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL)
server_name = "default";
return server_name;
}

/* returns the name of the per-user subdirectory of jack_tmpdir */
static char* jack_user_dir(void)
{
static char user_dir[PATH_MAX] = "";

/* format the path name on the first call */
if (user_dir[0] == '\0') {
snprintf (user_dir, sizeof (user_dir), "%s/jack-%d",
jack_tmpdir, getuid ());
}

return user_dir;
}

/* returns the name of the per-server subdirectory of jack_user_dir() */

static char* get_jack_server_dir(const char* toto)
{
static char server_dir[PATH_MAX] = "";

// format the path name on the first call
if (server_dir[0] == '\0') {
snprintf (server_dir, sizeof (server_dir), "%s/%s",
jack_user_dir (), server_name);
}

return server_dir;
}

static void
jack_cleanup_files (const char *server_name)
{
DIR *dir;
struct dirent *dirent;
char *dir_name = get_jack_server_dir (server_name);

/* On termination, we remove all files that jackd creates so
* subsequent attempts to start jackd will not believe that an
* instance is already running. If the server crashes or is
* terminated with SIGKILL, this is not possible. So, cleanup
* is also attempted when jackd starts.
*
* There are several tricky issues. First, the previous JACK
* server may have run for a different user ID, so its files
* may be inaccessible. This is handled by using a separate
* JACK_TMP_DIR subdirectory for each user. Second, there may
* be other servers running with different names. Each gets
* its own subdirectory within the per-user directory. The
* current process has already registered as `server_name', so
* we know there is no other server actively using that name.
*/

/* nothing to do if the server directory does not exist */
if ((dir = opendir (dir_name)) == NULL) {
return ;
}

/* unlink all the files in this directory, they are mine */
while ((dirent = readdir (dir)) != NULL) {

char fullpath[PATH_MAX];

if ((strcmp (dirent->d_name, ".") == 0)
|| (strcmp (dirent->d_name, "..") == 0)) {
continue;
}

snprintf (fullpath, sizeof (fullpath), "%s/%s",
dir_name, dirent->d_name);

if (unlink (fullpath)) {
jack_error ("cannot unlink `%s' (%s)", fullpath,
strerror (errno));
}
}

closedir (dir);

/* now, delete the per-server subdirectory, itself */
if (rmdir (dir_name)) {
jack_error ("cannot remove `%s' (%s)", dir_name,
strerror (errno));
}

/* finally, delete the per-user subdirectory, if empty */
if (rmdir (jack_user_dir ())) {
if (errno != ENOTEMPTY) {
jack_error ("cannot remove `%s' (%s)",
jack_user_dir (), strerror (errno));
}
}
}

#endif

int JackServerGlobals::JackStart(jack_driver_desc_t* driver_desc, JSList* driver_params, int sync, int time_out_ms, int rt, int priority, int loopback, int verbose)
{
JackLog("Jackdmp: sync = %ld timeout = %ld rt = %ld priority = %ld verbose = %ld \n", sync, time_out_ms, rt, priority, verbose);
fServer = new JackServer(sync, time_out_ms, rt, priority, loopback, verbose);
int res = fServer->Open(driver_desc, driver_params);
return (res < 0) ? res : fServer->Start();
}

int JackServerGlobals::JackStop()
{
fServer->Stop();
fServer->Close();
JackLog("Jackdmp: server close\n");
delete fServer;
JackLog("Jackdmp: delete server\n");
return 0;
}

int JackServerGlobals::JackDelete()
{
delete fServer;
JackLog("Jackdmp: delete server\n");
return 0;
}

// Temporary : to test

JackServerGlobals::JackServerGlobals()
{
jack_driver_desc_t* driver_desc;
const char *options = "-ad:P:uvshVRL:STFl:t:mn:p:";
struct option long_options[] = {
{ "driver", 1, 0, 'd'
},
{ "verbose", 0, 0, 'v' },
{ "help", 0, 0, 'h' },
{ "port-max", 1, 0, 'p' },
{ "no-mlock", 0, 0, 'm' },
{ "name", 0, 0, 'n' },
{ "unlock", 0, 0, 'u' },
{ "realtime", 0, 0, 'R' },
{ "loopback", 0, 0, 'L' },
{ "realtime-priority", 1, 0, 'P' },
{ "timeout", 1, 0, 't' },
{ "temporary", 0, 0, 'T' },
{ "version", 0, 0, 'V' },
{ "silent", 0, 0, 's' },
{ "sync", 0, 0, 'S' },
{ 0, 0, 0, 0 }
};
int opt = 0;
int option_index = 0;
int seen_driver = 0;
char *driver_name = NULL;
char **driver_args = NULL;
JSList* driver_params;
int driver_nargs = 1;
JSList* drivers = NULL;
char* server_name = NULL;
int show_version = 0;
int sync = 0;
int rc, i;

int argc = 8;
//char* argv[] = {"jackdmp", "-R", "-v", "-d", "coreaudio", "-p", "512"};
char* argv[] = {"jackdmp", "-R", "-S", "-v", "-d", "portaudio", "-p", "512"};

for (i = 0; i < argc; i++) {
printf("arg %i %s\n", i, argv[i]);
}

opterr = 0;
while (!seen_driver &&
(opt = getopt_long(argc, argv, options,
long_options, &option_index)) != EOF) {
switch (opt) {

case 'd':
seen_driver = 1;
driver_name = optarg;
break;

case 'v':
verbose_aux = 1;
break;

case 's':
// jack_set_error_function(silent_jack_error_callback);
break;

case 'S':
sync = 1;
break;

case 'n':
server_name = optarg;
break;

case 'm':
do_mlock = 0;
break;

case 'p':
port_max = (unsigned int)atol(optarg);
break;

case 'P':
realtime_priority = atoi(optarg);
break;

case 'R':
realtime = 1;
break;

case 'L':
loopback = atoi(optarg);
break;

case 'T':
temporary = 1;
break;

case 't':
client_timeout = atoi(optarg);
break;

case 'u':
do_unlock = 1;
break;

case 'V':
show_version = 1;
break;

default:
fprintf(stderr, "unknown option character %c\n",
optopt);
/*fallthru*/
case 'h':
//usage(stdout);
return ;
}
}

drivers = jack_drivers_load (drivers);
if (!drivers) {
fprintf (stderr, "jackdmp: no drivers found; exiting\n");
exit (1);
}

driver_desc = jack_find_driver_descriptor (drivers, driver_name);
if (!driver_desc) {
fprintf (stderr, "jackdmp: unknown driver '%s'\n", driver_name);
exit (1);
}

if (optind < argc) {
driver_nargs = 1 + argc - optind;
} else {
driver_nargs = 1;
}

if (driver_nargs == 0) {
fprintf (stderr, "No driver specified ... hmm. JACK won't do"
" anything when run like this.\n");
return ;
}

driver_args = (char **) malloc (sizeof (char *) * driver_nargs);
driver_args[0] = driver_name;

for (i = 1; i < driver_nargs; i++) {
driver_args[i] = argv[optind++];
}

if (jack_parse_driver_params (driver_desc, driver_nargs,
driver_args, &driver_params)) {
return ;
}

#ifndef WIN32
if (server_name == NULL)
server_name = jack_default_server_name ();
#endif

rc = jack_register_server (server_name);

/* clean up shared memory and files from any previous
* instance of this server name */
jack_cleanup_shm();
#ifndef WIN32
jack_cleanup_files(server_name);
#endif

if (!realtime && client_timeout == 0)
client_timeout = 500; /* 0.5 sec; usable when non realtime. */

int res = JackStart(driver_desc, driver_params, sync, client_timeout, realtime, realtime_priority, loopback, verbose_aux);
if (res < 0) {
jack_error("Cannot start server... exit");
JackDelete();
return ;
}
}

JackServerGlobals::~JackServerGlobals()
{
JackLog("~JackServerGlobals\n");
JackStop();
jack_cleanup_shm();
#ifndef WIN32
jack_cleanup_files(server_name);
#endif
jack_unregister_server(server_name);
}

void JackServerGlobals::Init()
{
if (fClientCount++ == 0 && !fGlobals) {
JackLog("JackServerGlobals Init %x\n", fGlobals);
fGlobals = new JackServerGlobals();
}
}

void JackServerGlobals::Destroy()
{
if (--fClientCount == 0 && fGlobals) {
JackLog("JackServerGlobals Destroy %x\n", fGlobals);
delete fGlobals;
fGlobals = NULL;
}
}

} // end of namespace



+ 58
- 0
common/JackServerGlobals.h View File

@@ -0,0 +1,58 @@
/*
Copyright (C) 2005 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackServerGlobals__
#define __JackServerGlobals__

#include "driver_interface.h"
#include "driver_parse.h"
#include "JackDriverLoader.h"
#include "JackServer.h"

#include <assert.h>

namespace Jack
{

class JackClient;

/*!
\brief Global server static structure: singleton kind of pattern.
*/

struct JackServerGlobals
{
static long fClientCount;
static JackServerGlobals* fGlobals;
static JackServer* fServer;

JackServerGlobals();
virtual ~JackServerGlobals();

static void Init();
static void Destroy();
static int JackStart(jack_driver_desc_t* driver_desc, JSList* driver_params, int sync, int time_out_ms, int rt, int priority, int loopback, int verbose);
static int JackStop();
static int JackDelete();
};

} // end of namespace

#endif


+ 92
- 0
common/JackShmMem.cpp View File

@@ -0,0 +1,92 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackShmMem.h"
#include "JackError.h"
#include <stdio.h>

namespace Jack
{

unsigned long JackShmMem::fSegmentNum = 0;
unsigned long JackShmMem::fSegmentCount = 0;

jack_shm_info_t JackShmMem::gInfo;

void* JackShmMem::operator new(size_t size)
{
jack_shm_info_t info;
JackShmMem* obj;
char name[64];

snprintf(name, sizeof(name), "/jack_shared%ld", JackShmMem::fSegmentNum++);

if (JackShmMem::fSegmentCount++ == 0) {
JackLog("jack_initialize_shm\n");
if (jack_initialize_shm_server() < 0) {
jack_error("cannot initialize shm", strerror(errno));
goto error;
}
}

if (jack_shmalloc(name, size, &info)) {
jack_error("cannot create shared memory segment of size = %d", size, strerror(errno));
goto error;
}

if (jack_attach_shm(&info)) {
jack_error("cannot attach shared memory segment name = %s err = %s", name, strerror(errno));
jack_destroy_shm(&info);
goto error;
}

obj = (JackShmMem*)jack_shm_addr(&info);
// It is unsafe to set object fields directly (may be overwritten during object initialization),
// so use an intermediate global data
gInfo.index = info.index;
gInfo.attached_at = info.attached_at;
JackLog("JackShmMem::new index = %ld attached = %x size = %ld \n", info.index, info.attached_at, size);
return obj;

error:
jack_error("JackShmMem::new bad alloc", size);
throw new std::bad_alloc;
}

void JackShmMem::operator delete(void* p, size_t size)
{
jack_shm_info_t info;
JackShmMem* obj = (JackShmMem*)p;
info.index = obj->fInfo.index;
info.attached_at = obj->fInfo.attached_at;

JackLog("JackShmMem::delete size = %ld index = %ld\n", size, info.index);

jack_release_shm(&info);
jack_destroy_shm(&info);

if (--JackShmMem::fSegmentCount == 0) {
JackLog("jack_cleanup_shm\n");
jack_cleanup_shm();
}
}

} // end of namespace


+ 308
- 0
common/JackShmMem.h View File

@@ -0,0 +1,308 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackShmMem__
#define __JackShmMem__

#include "shm.h"
#include <new> // GCC 4.0
#include "JackError.h"
#include <errno.h>

namespace Jack
{

/*!
\brief The base class for shared memory management.
A class which objects need to be allocated in shared memory derives from this class.
*/

class JackShmMem
{

private:

jack_shm_info_t fInfo;
static unsigned long fSegmentNum;
static unsigned long fSegmentCount;
static jack_shm_info_t gInfo;

public:

void* operator new(size_t size);
void operator delete(void* p, size_t size);

JackShmMem()
{
fInfo.index = gInfo.index;
fInfo.attached_at = gInfo.attached_at;
}

virtual ~JackShmMem()
{}

int GetShmIndex()
{
return fInfo.index;
}

char* GetShmAddress()
{
return (char*)fInfo.attached_at;
}

};

/*!
\brief Pointer on shared memory segment in the client side.
*/

template <class T>
class JackShmReadWritePtr
{

private:

jack_shm_info_t fInfo;

void Init(int index)
{
if (fInfo.index < 0 && index >= 0) {
JackLog("JackShmReadWritePtr::Init %ld %ld\n", index, fInfo.index);
if (jack_initialize_shm_client() < 0)
throw - 1;
fInfo.index = index;
if (jack_attach_shm(&fInfo)) {
//jack_error("cannot attach shared memory segment", strerror(errno));
throw - 2;
}
}
}

public:

JackShmReadWritePtr()
{
fInfo.index = -1;
fInfo.attached_at = NULL;
}

JackShmReadWritePtr(int index)
{
Init(index);
}

virtual ~JackShmReadWritePtr()
{
if (fInfo.index >= 0) {
JackLog("JackShmReadWritePtr::~JackShmReadWritePtr %ld\n", fInfo.index);
jack_release_shm(&fInfo);
fInfo.index = -1;
}
}

T* operator->() const
{
return (T*)fInfo.attached_at;
}

operator T*() const
{
return (T*)fInfo.attached_at;
}

JackShmReadWritePtr& operator=(int index)
{
Init(index);
return *this;
}

int GetShmIndex()
{
return fInfo.index;
}

T* GetShmAddress()
{
return (T*)fInfo.attached_at;
}
};

/*!
\brief Pointer on shared memory segment in the client side: destroy the segment (used client control)
*/

template <class T>
class JackShmReadWritePtr1
{

private:

jack_shm_info_t fInfo;

void Init(int index)
{
if (fInfo.index < 0 && index >= 0) {
JackLog("JackShmReadWritePtr1::Init %ld %ld\n", index, fInfo.index);
if (jack_initialize_shm_client() < 0)
throw - 1;
fInfo.index = index;
if (jack_attach_shm(&fInfo)) {
//jack_error("cannot attach shared memory segment", strerror(errno));
throw - 2;
}
/*
nobody else needs to access this shared memory any more, so
destroy it. because we have our own attachment to it, it won't
vanish till we exit (and release it).
*/
jack_destroy_shm(&fInfo);
}
}

public:

JackShmReadWritePtr1()
{
fInfo.index = -1;
fInfo.attached_at = NULL;
}

JackShmReadWritePtr1(int index)
{
Init(index);
}

virtual ~JackShmReadWritePtr1()
{
if (fInfo.index >= 0) {
JackLog("JackShmReadWritePtr1::~JackShmReadWritePtr1 %ld\n", fInfo.index);
jack_release_shm(&fInfo);
fInfo.index = -1;
}
}

T* operator->() const
{
return (T*)fInfo.attached_at;
}

operator T*() const
{
return (T*)fInfo.attached_at;
}

JackShmReadWritePtr1& operator=(int index)
{
Init(index);
return *this;
}

int GetShmIndex()
{
return fInfo.index;
}

T* GetShmAddress()
{
return (T*)fInfo.attached_at;
}
};

/*!
\brief Pointer on shared memory segment in the client side.
*/

template <class T>
class JackShmReadPtr
{

private:

jack_shm_info_t fInfo;

void Init(int index)
{
if (fInfo.index < 0 && index >= 0) {
JackLog("JackShmPtrRead::Init %ld %ld\n", index, fInfo.index);
if (jack_initialize_shm_client() < 0)
throw - 1;
fInfo.index = index;
if (jack_attach_shm_read(&fInfo)) {
//jack_error("cannot attach shared memory segment", strerror(errno));
throw - 2;
}
}
}

public:

JackShmReadPtr()
{
fInfo.index = -1;
fInfo.attached_at = NULL;
}

JackShmReadPtr(int index)
{
Init(index);
}

virtual ~JackShmReadPtr()
{
if (fInfo.index >= 0) {
JackLog("JackShmPtrRead::~JackShmPtrRead %ld\n", fInfo.index);
jack_release_shm(&fInfo);
fInfo.index = -1;
}
}

T* operator->() const
{
return (T*)fInfo.attached_at;
}

operator T*() const
{
return (T*)fInfo.attached_at;
}

JackShmReadPtr& operator=(int index)
{
Init(index);
return *this;
}

int GetShmIndex()
{
return fInfo.index;
}

T* GetShmAddress()
{
return (T*)fInfo.attached_at;
}

};

} // end of namespace

#endif

+ 312
- 0
common/JackSocket.cpp View File

@@ -0,0 +1,312 @@
/*
Copyright (C) 2004-2006 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#include "JackSocket.h"
#include "JackError.h"
#include <string.h>

namespace Jack
{

JackClientSocket::JackClientSocket(int socket): fSocket(socket)
{}

void JackClientSocket::SetReadTimeOut(long sec)
{
struct timeval timout;
timout.tv_sec = sec;
timout.tv_usec = 0;
if (setsockopt(fSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timout, sizeof(timeval)) < 0) {
JackLog("setsockopt SO_RCVTIMEO fd = %ld err = (%s)\n", fSocket, strerror(errno));
}
}

void JackClientSocket::SetWriteTimeOut(long sec)
{
struct timeval timout;
timout.tv_sec = sec ;
timout.tv_usec = 0;
if (setsockopt(fSocket, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timout, sizeof(timeval)) < 0) {
JackLog("setsockopt SO_SNDTIMEO fd = %ld err = (%s)\n", fSocket, strerror(errno));
}
}

int JackClientSocket::Connect(const char* dir, const char* name, int which) // A revoir : utilisation de "which"
{
struct sockaddr_un addr;

if ((fSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
jack_error("Cannot create socket (%s)", strerror(errno));
return -1;
}

addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "%s/jack_%s", dir, name);

JackLog("Connect: addr.sun_path %s\n", addr.sun_path);

if (connect(fSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
jack_error("Cannot connect to server socket (%s)", strerror(errno));
close(fSocket);
return -1;
}

#ifdef __APPLE__
int on = 1 ;
if (setsockopt(fSocket, SOL_SOCKET, SO_NOSIGPIPE, (const char*)&on, sizeof(on)) < 0) {
JackLog("setsockopt SO_NOSIGPIPE fd = %ld err = %s\n", fSocket, strerror(errno));
}
#endif

return 0;
}

int JackClientSocket::Connect(const char* dir, int which)
{
struct sockaddr_un addr;

if ((fSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
jack_error("Cannot create socket (%s)", strerror(errno));
return -1;
}

addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "%s/jack_%d", dir, which);

JackLog("Connect: addr.sun_path %s\n", addr.sun_path);

if (connect(fSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
jack_error("Cannot connect to server socket (%s)", strerror(errno));
close(fSocket);
return -1;
}

#ifdef __APPLE__
int on = 1 ;
if (setsockopt(fSocket, SOL_SOCKET, SO_NOSIGPIPE, (const char*)&on, sizeof(on)) < 0) {
JackLog("setsockopt SO_NOSIGPIPE fd = %ld err = %s\n", fSocket, strerror(errno));
}
#endif

return 0;
}

int JackClientSocket::Close()
{
JackLog("JackClientSocket::Close\n");
//shutdown(fSocket, SHUT_RDWR);
if (fSocket > 0) {
close(fSocket);
fSocket = -1;
return 0;
} else {
return -1;
}
}

int JackClientSocket::Read(void* data, int len)
{
int len1;
JackLog("JackClientSocket::Read len = %ld\n", len);

if ((len1 = read(fSocket, data, len)) != len) {
jack_error("Cannot read socket %d %d (%s)", len, len1, strerror(errno));
if (errno == EWOULDBLOCK) {
JackLog("JackClientSocket::Read time out\n");
return 0;
} else {
return -1;
}
} else {
return 0;
}
}

int JackClientSocket::Write(void* data, int len)
{
if (write(fSocket, data, len) != len) {
jack_error("Cannot write socket fd %ld (%s)", fSocket, strerror(errno));
return -1;
} else {
return 0;
}
}

/*
void
jack_cleanup_files ()
{
DIR *dir;
struct dirent *dirent;
// its important that we remove all files that jackd creates
// because otherwise subsequent attempts to start jackd will
// believe that an instance is already running.
if ((dir = opendir (jack_server_dir)) == NULL) {
fprintf (stderr, "jack(%d): cannot open jack FIFO directory "
"(%s)\n", getpid(), strerror (errno));
return;
}
while ((dirent = readdir (dir)) != NULL) {
if (strncmp (dirent->d_name, "jack-", 5) == 0 ||
strncmp (dirent->d_name, "jack_", 5) == 0) {
char fullpath[PATH_MAX+1];
snprintf (fullpath, sizeof (fullpath), "%s/%s",
jack_server_dir, dirent->d_name);
unlink (fullpath);
}
}
closedir (dir);
}
*/

int JackServerSocket::Bind(const char* dir, const char* name, int which) // A revoir : utilisation de "which"
{
struct sockaddr_un addr;

if ((fSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
jack_error("Cannot create server socket (%s)", strerror(errno));
return -1;
}

addr.sun_family = AF_UNIX;

// TO CORRECT: always reuse the same name for now...
snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "%s/jack_%s", dir, name);
snprintf(fName, sizeof(addr.sun_path) - 1, "%s/jack_%s", dir, name);
/*
if (access(addr.sun_path, F_OK) == 0) {
goto error;
}
*/

JackLog("Bind: addr.sun_path %s\n", addr.sun_path);
unlink(fName); // Security...

JackLog("Bind: addr.sun_path %s\n", addr.sun_path);
unlink(fName); // Security...

if (bind(fSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
jack_error("Cannot bind server to socket (%s)", strerror(errno));
goto error;
}

if (listen(fSocket, 1) < 0) {
jack_error("Cannot enable listen on server socket (%s)", strerror(errno));
goto error;
}

return 0;

error:
unlink(fName);
close(fSocket);
return -1;
}

int JackServerSocket::Bind(const char* dir, int which) // A revoir : utilisation de "which"
{
struct sockaddr_un addr;

if ((fSocket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
jack_error ("Cannot create server socket (%s)", strerror(errno));
return -1;
}

addr.sun_family = AF_UNIX;

/*
for (int i = 0; i < 999; i++) {
snprintf(addr.sun_path, sizeof(addr.sun_path) - 1,"%s/jack_%d", dir, i);
snprintf(fName, sizeof(addr.sun_path) - 1,"%s/jack_%d", dir, i);
if (access(addr.sun_path, F_OK) != 0) {
break;
}
}
*/

// TO CORRECT: always reuse the same name for now...
snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "%s/jack_%d", dir, which);
snprintf(fName, sizeof(addr.sun_path) - 1, "%s/jack_%d", dir, which);
/*
if (access(addr.sun_path, F_OK) == 0) {
goto error;
}
*/

JackLog("Bind: addr.sun_path %s\n", addr.sun_path);
unlink(fName); // Security...

if (bind(fSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
jack_error("Cannot bind server to socket (%s)", strerror(errno));
goto error;
}

if (listen(fSocket, 1) < 0) {
jack_error("Cannot enable listen on server socket (%s)", strerror(errno));
goto error;
}

return 0;

error:
unlink(fName);
close(fSocket);
return -1;
}

JackClientSocket* JackServerSocket::Accept()
{
struct sockaddr_un client_addr;
socklen_t client_addrlen;

memset(&client_addr, 0, sizeof(client_addr));
client_addrlen = sizeof(client_addr);

int fd = accept(fSocket, (struct sockaddr*) & client_addr, &client_addrlen);
if (fd < 0) {
jack_error("Cannot accept new connection (%s)", strerror(errno));
return 0;
} else {
return new JackClientSocket(fd);
}
}

int JackServerSocket::Close()
{
JackLog("JackServerSocket::Close %s\n", fName);
//shutdown(fSocket, SHUT_RDWR);
if (fSocket > 0) {
//shutdown(fSocket, SHUT_RDWR);
close(fSocket);
unlink(fName);
fSocket = -1;
return 0;
} else {
return -1;
}
}

} // end of namespace



+ 104
- 0
common/JackSocket.h View File

@@ -0,0 +1,104 @@
/*
Copyright (C) 2004-2006 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#ifndef __JackSocket__
#define __JackSocket__

#include "JackChannelTransaction.h"

#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

namespace Jack
{

/*!
\brief Client socket.
*/

class JackClientSocket : public JackChannelTransaction
{

private:

int fSocket;

public:

JackClientSocket(): fSocket( -1)
{}
JackClientSocket(int socket);
virtual ~JackClientSocket()
{}

int Connect(const char* dir, int which);
int Connect(const char* dir, const char* name, int which);
int Close();
int Read(void* data, int len);
int Write(void* data, int len);
int GetFd()
{
return fSocket;
}
void SetReadTimeOut(long sec);
void SetWriteTimeOut(long sec);
};

/*!
\brief Server socket.
*/

class JackServerSocket
{

private:

int fSocket;
char fName[256];

public:

JackServerSocket(): fSocket( -1)
{}
virtual ~JackServerSocket()
{}

int Bind(const char* dir, int which);
int Bind(const char* dir, const char* name, int which);
JackClientSocket* Accept();
int Close();
int GetFd()
{
return fSocket;
}
};

} // end of namespace

#endif


+ 264
- 0
common/JackSocketClientChannel.cpp View File

@@ -0,0 +1,264 @@
/*
Copyright (C) 2004-2006 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#include "JackSocketClientChannel.h"
#include "JackRequest.h"
#include "JackClient.h"
#include "JackGlobals.h"

namespace Jack
{

JackSocketClientChannel::JackSocketClientChannel()
{
fThread = JackGlobals::MakeThread(this);
fNotificationSocket = NULL;
fClient = NULL;
}

JackSocketClientChannel::~JackSocketClientChannel()
{
delete fThread;
delete fNotificationSocket;
}

int JackSocketClientChannel::Open(const char* name, JackClient* obj)
{
JackLog("JackSocketClientChannel::Open name = %s\n", name);

if (fRequestSocket.Connect(jack_server_dir, 0) < 0) {
jack_error("Cannot connect to server socket");
goto error;
}

if (fNotificationListenSocket.Bind(jack_client_dir, name, 0) < 0) {
jack_error("Cannot bind socket");
goto error;
}

fClient = obj;
return 0;

error:
fRequestSocket.Close();
fNotificationListenSocket.Close();
return -1;
}

void JackSocketClientChannel::Close()
{
fRequestSocket.Close();
fNotificationListenSocket.Close();
if (fNotificationSocket)
fNotificationSocket->Close();
}

int JackSocketClientChannel::Start()
{
JackLog("JackSocketClientChannel::Start\n");
if (fThread->Start() != 0) {
jack_error("Cannot start Jack client listener");
return -1;
} else {
return 0;
}
}

void JackSocketClientChannel::Stop()
{
JackLog("JackSocketClientChannel::Stop\n");
fThread->Kill();
}

void JackSocketClientChannel::ServerSyncCall(JackRequest* req, JackResult* res, int* result)
{
if (req->Write(&fRequestSocket) < 0) {
jack_error("Could not write request type = %ld", req->fType);
*result = -1;
return ;
}

if (res->Read(&fRequestSocket) < 0) {
jack_error("Could not read result type = %ld", req->fType);
*result = -1;
return ;
}

*result = res->fResult;
}

void JackSocketClientChannel::ServerAsyncCall(JackRequest* req, JackResult* res, int* result)
{
if (req->Write(&fRequestSocket) < 0) {
jack_error("Could not write request type = %ld", req->fType);
*result = -1;
} else {
*result = 0;
}
}

void JackSocketClientChannel::ClientNew(const char* name, int* shared_engine, int* shared_client, int* shared_ports, int* result)
{
JackClientNewRequest req(name);
JackClientNewResult res;
ServerSyncCall(&req, &res, result);
*shared_engine = res.fSharedEngine;
*shared_client = res.fSharedClient;
*shared_ports = res.fSharedPorts;
}

void JackSocketClientChannel::ClientClose(int refnum, int* result)
{
JackClientCloseRequest req(refnum);
JackResult res;
ServerAsyncCall(&req, &res, result);
}

void JackSocketClientChannel::ClientActivate(int refnum, int* result)
{
JackActivateRequest req(refnum);
JackResult res;
ServerSyncCall(&req, &res, result);
}

void JackSocketClientChannel::ClientDeactivate(int refnum, int* result)
{
JackDeactivateRequest req(refnum);
JackResult res;
ServerSyncCall(&req, &res, result);
}

void JackSocketClientChannel::PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result)
{
JackPortRegisterRequest req(refnum, name, "audio", flags, buffer_size);
JackPortRegisterResult res;
ServerSyncCall(&req, &res, result);
*port_index = res.fPortIndex;
}

void JackSocketClientChannel::PortUnRegister(int refnum, jack_port_id_t port_index, int* result)
{
JackPortUnRegisterRequest req(refnum, port_index);
JackResult res;
ServerSyncCall(&req, &res, result);
}

void JackSocketClientChannel::PortConnect(int refnum, const char* src, const char* dst, int* result)
{
JackPortConnectNameRequest req(refnum, src, dst);
JackResult res;
ServerSyncCall(&req, &res, result);
}

void JackSocketClientChannel::PortDisconnect(int refnum, const char* src, const char* dst, int* result)
{
JackPortDisconnectNameRequest req(refnum, src, dst);
JackResult res;
ServerSyncCall(&req, &res, result);
}

void JackSocketClientChannel::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result)
{
JackPortConnectRequest req(refnum, src, dst);
JackResult res;
ServerSyncCall(&req, &res, result);
}

void JackSocketClientChannel::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result)
{
JackPortDisconnectRequest req(refnum, src, dst);
JackResult res;
ServerSyncCall(&req, &res, result);
}

void JackSocketClientChannel::SetBufferSize(jack_nframes_t nframes, int* result)
{
JackSetBufferSizeRequest req(nframes);
JackResult res;
ServerSyncCall(&req, &res, result);
}

void JackSocketClientChannel::SetFreewheel(int onoff, int* result)
{
JackSetFreeWheelRequest req(onoff);
JackResult res;
ServerSyncCall(&req, &res, result);
}

void JackSocketClientChannel::ReleaseTimebase(int refnum, int* result)
{
JackReleaseTimebaseRequest req(refnum);
JackResult res;
ServerSyncCall(&req, &res, result);
}

void JackSocketClientChannel::SetTimebaseCallback(int refnum, int conditional, int* result)
{
JackSetTimebaseCallbackRequest req(refnum, conditional);
JackResult res;
ServerSyncCall(&req, &res, result);
}

bool JackSocketClientChannel::Init()
{
JackLog("JackSocketClientChannel::Init \n");
fNotificationSocket = fNotificationListenSocket.Accept();
// No more needed
fNotificationListenSocket.Close();

if (!fNotificationSocket) {
jack_error("JackSocketClientChannel: cannot establish notication socket");
return false;
} else {
return true;
}
}

bool JackSocketClientChannel::Execute()
{
JackClientNotification event;
JackResult res;

//fClient->Init(); // To be checked

if (event.Read(fNotificationSocket) < 0) {
fNotificationSocket->Close();
jack_error("JackSocketClientChannel read fail");
goto error;
}

res.fResult = fClient->ClientNotify(event.fRefNum, event.fName, event.fNotify, event.fSync, event.fValue);

if (event.fSync) {
if (res.Write(fNotificationSocket) < 0) {
fNotificationSocket->Close();
jack_error("JackSocketClientChannel write fail");
goto error;
}
}
return true;

error:
fClient->ShutDown();
return false;
}

} // end of namespace



+ 89
- 0
common/JackSocketClientChannel.h View File

@@ -0,0 +1,89 @@
/*
Copyright (C) 2004-2006 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#ifndef __JackSocketClientChannel__
#define __JackSocketClientChannel__

#include "JackChannel.h"
#include "JackSocket.h"
#include "JackThread.h"
#include "JackRequest.h"

namespace Jack
{

/*!
\brief JackClientChannel using sockets.
*/

class JackSocketClientChannel : public JackClientChannelInterface, public JackRunnableInterface
{

private:

JackClientSocket fRequestSocket; // Socket to communicate with the server
JackServerSocket fNotificationListenSocket; // Socket listener for server notification
JackClientSocket* fNotificationSocket; // Socket for server notification
JackThread* fThread; // Thread to execute the event loop
JackClient* fClient;

void ServerSyncCall(JackRequest* req, JackResult* res, int* result);
void ServerAsyncCall(JackRequest* req, JackResult* res, int* result);

public:

JackSocketClientChannel();
virtual ~JackSocketClientChannel();

int Open(const char* name, JackClient* obj);
void Close();

int Start();
void Stop();

void ClientNew(const char* name, int* shared_engine, int* shared_client, int* shared_ports, int* result);
void ClientClose(int refnum, int* result);

void ClientActivate(int refnum, int* result);
void ClientDeactivate(int refnum, int* result);

void PortRegister(int refnum, const char* name, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result);
void PortUnRegister(int refnum, jack_port_id_t port_index, int* result);

void PortConnect(int refnum, const char* src, const char* dst, int* result);
void PortDisconnect(int refnum, const char* src, const char* dst, int* result);

void PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result);
void PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst, int* result);

void SetBufferSize(jack_nframes_t nframes, int* result);
void SetFreewheel(int onoff, int* result);

void ReleaseTimebase(int refnum, int* result);
void SetTimebaseCallback(int refnum, int conditional, int* result);

// JackRunnableInterface interface
bool Init();
bool Execute();
};

} // end of namespace

#endif


+ 79
- 0
common/JackSocketNotifyChannel.cpp View File

@@ -0,0 +1,79 @@
/*
Copyright (C) 2004-2006 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#include "JackRequest.h"
#include "JackSocketNotifyChannel.h"
#include "JackError.h"
#include "JackConstants.h"

namespace Jack
{

// Server to client
int JackSocketNotifyChannel::Open(const char* name)
{
JackLog("JackSocketNotifyChannel::Open name = %s\n", name);

// Connect to client listen socket
if (fNotifySocket.Connect(jack_client_dir, name, 0) < 0) {
jack_error("Cannot connect client socket");
return -1;
}
// Use a time out for notifications
fNotifySocket.SetReadTimeOut(SOCKET_TIME_OUT);
return 0;
}

void JackSocketNotifyChannel::Close()
{
JackLog("JackSocketNotifyChannel::Close\n");
fNotifySocket.Close();
}

void JackSocketNotifyChannel::ClientNotify(int refnum, const char* name, int notify, int sync, int value, int* result)
{
JackClientNotification event(name, refnum, notify, sync, value);
JackResult res;

// Send notification
if (event.Write(&fNotifySocket) < 0) {
jack_error("Could not write notification");
fNotifySocket.Close();
*result = -1;
return ;
}

// Read the result in "synchronous" mode only
if (sync) {
// Get result : use a time out
if (res.Read(&fNotifySocket) < 0) {
jack_error("Could not read result");
fNotifySocket.Close();
*result = -1;
} else {
*result = res.fResult;
}
} else {
*result = 0;
}
}

} // end of namespace



+ 56
- 0
common/JackSocketNotifyChannel.h View File

@@ -0,0 +1,56 @@
/*
Copyright (C) 2004-2006 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#ifndef __JackSocketNotifyChannel__
#define __JackSocketNotifyChannel__

#include "JackChannel.h"
#include "JackSocket.h"

namespace Jack
{

/*!
\brief JackNotifyChannel using sockets.
*/

class JackSocketNotifyChannel : public JackNotifyChannelInterface
{

private:

JackClientSocket fNotifySocket; // Socket to communicate with the server : from server to client

public:

JackSocketNotifyChannel()
{}
virtual ~JackSocketNotifyChannel()
{}

int Open(const char* name); // Open the Server/Client connection
void Close(); // Close the Server/Client connection

void ClientNotify(int refnum, const char* name, int notify, int sync, int value, int* result);
};

} // end of namespace

#endif


+ 375
- 0
common/JackSocketServerChannel.cpp View File

@@ -0,0 +1,375 @@
/*
Copyright (C) 2004-2006 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#include "JackSocketServerChannel.h"
#include "JackRequest.h"
#include "JackServer.h"
#include "JackEngine.h"
#include "JackGlobals.h"
#include "JackClient.h"
#include <assert.h>

using namespace std;

namespace Jack
{

JackSocketServerChannel::JackSocketServerChannel()
{
fThread = JackGlobals::MakeThread(this);
fPollTable = NULL;
fRebuild = true;
}

JackSocketServerChannel::~JackSocketServerChannel()
{
delete fThread;
delete[] fPollTable;
}

int JackSocketServerChannel::Open(JackServer* server)
{
JackLog("JackSocketServerChannel::Open \n");
fServer = server;

// Prepare request socket
if (fRequestListenSocket.Bind(jack_server_dir, 0) < 0) {
JackLog("JackSocketServerChannel::Open : cannot create result listen socket\n");
return -1;
}

// Prepare for poll
BuildPoolTable();

// Start listening
if (fThread->Start() != 0) {
jack_error("Cannot start Jack server listener");
goto error;
}

return 0;

error:
fRequestListenSocket.Close();
return -1;
}

void JackSocketServerChannel::Close()
{
fThread->Kill();
fRequestListenSocket.Close();
}

void JackSocketServerChannel::CreateClient()
{
JackLog("JackSocketServerChannel::CreateClient socket\n");
JackClientSocket* socket = fRequestListenSocket.Accept();
if (socket) {
fSocketTable[socket->GetFd()] = make_pair( -1, socket);
fRebuild = true;
} else {
jack_error("Client socket cannot be created");
}
}

void JackSocketServerChannel::AddClient(int fd, char* name, int* shared_engine, int* shared_client, int* shared_ports, int* result)
{
JackLog("JackSocketServerChannel::AddClient\n");
int refnum = -1;
*result = fServer->GetEngine()->ClientNew(name, &refnum, shared_engine, shared_client, shared_ports);
if (*result == 0) {
fSocketTable[fd].first = refnum;
fRebuild = true;
} else {
jack_error("Cannot create new client");
}
}

void JackSocketServerChannel::RemoveClient(int fd, int refnum)
{
pair<int, JackClientSocket*> elem = fSocketTable[fd];
JackClientSocket* socket = elem.second;
assert(socket);
JackLog("JackSocketServerChannel::RemoveClient ref = %d\n", refnum);
fSocketTable.erase(fd);
socket->Close();
delete socket;
fRebuild = true;
}

void JackSocketServerChannel::KillClient(int fd)
{
pair<int, JackClientSocket*> elem = fSocketTable[fd];
JackClientSocket* socket = elem.second;
int refnum = elem.first;

assert(socket);
JackLog("JackSocketServerChannel::KillClient ref = %d\n", refnum);

if (refnum == -1) { // Should never happen... correspond to a client that started the socket but never opened...
jack_error("Client not opened");
} else {
fServer->Notify(refnum, JackNotifyChannelInterface::kDeadClient, 0);
}

fSocketTable.erase(fd);
socket->Close();
delete socket;
fRebuild = true;
}

int JackSocketServerChannel::HandleRequest(int fd)
{
pair<int, JackClientSocket*> elem = fSocketTable[fd];
JackClientSocket* socket = elem.second;
assert(socket);

// Read header
JackRequest header;
if (header.Read(socket) < 0) {
jack_error("HandleRequest: cannot read header");
return -1;
}

// Read data
switch (header.fType) {

case JackRequest::kClientNew: {
JackLog("JackRequest::ClientNew\n");
JackClientNewRequest req;
JackClientNewResult res;
req.Read(socket);
AddClient(fd, req.fName, &res.fSharedEngine, &res.fSharedClient, &res.fSharedPorts, &res.fResult);
res.Write(socket);
break;
}

case JackRequest::kClientClose: {
JackLog("JackRequest::ClientClose\n");
JackClientCloseRequest req;
JackResult res;
req.Read(socket);
res.fResult = fServer->GetEngine()->ClientClose(req.fRefNum);
res.Write(socket);
RemoveClient(fd, req.fRefNum);
break;
}

case JackRequest::kActivateClient: {
JackActivateRequest req;
JackResult res;
JackLog("JackRequest::ActivateClient\n");
req.Read(socket);
res.fResult = fServer->Activate(req.fRefNum);
res.Write(socket);
break;
}

case JackRequest::kDeactivateClient: {
JackLog("JackRequest::DeactivateClient\n");
JackDeactivateRequest req;
JackResult res;
req.Read(socket);
res.fResult = fServer->Deactivate(req.fRefNum);
res.Write(socket);
break;
}

case JackRequest::kRegisterPort: {
JackLog("JackRequest::RegisterPort\n");
JackPortRegisterRequest req;
JackPortRegisterResult res;
req.Read(socket);
res.fResult = fServer->GetEngine()->PortRegister(req.fRefNum, req.fName, req.fFlags, req.fBufferSize, &res.fPortIndex);
res.Write(socket);
break;
}

case JackRequest::kUnRegisterPort: {
JackLog("JackRequest::UnRegisterPort\n");
JackPortUnRegisterRequest req;
JackResult res;
req.Read(socket);
res.fResult = fServer->GetEngine()->PortUnRegister(req.fRefNum, req.fPortIndex);
res.Write(socket);
break;
}

case JackRequest::kConnectNamePorts: {
JackLog("JackRequest::ConnectPorts\n");
JackPortConnectNameRequest req;
JackResult res;
req.Read(socket);
res.fResult = fServer->GetEngine()->PortConnect(req.fRefNum, req.fSrc, req.fDst);
res.Write(socket);
break;
}

case JackRequest::kDisconnectNamePorts: {
JackLog("JackRequest::DisconnectPorts\n");
JackPortDisconnectNameRequest req;
JackResult res;
req.Read(socket);
res.fResult = fServer->GetEngine()->PortDisconnect(req.fRefNum, req.fSrc, req.fDst);
res.Write(socket);
break;
}

case JackRequest::kConnectPorts: {
JackLog("JackRequest::ConnectPorts\n");
JackPortConnectRequest req;
JackResult res;
req.Read(socket);
res.fResult = fServer->GetEngine()->PortConnect(req.fRefNum, req.fSrc, req.fDst);
res.Write(socket);
break;
}

case JackRequest::kDisconnectPorts: {
JackLog("JackRequest::DisconnectPorts\n");
JackPortDisconnectRequest req;
JackResult res;
req.Read(socket);
res.fResult = fServer->GetEngine()->PortDisconnect(req.fRefNum, req.fSrc, req.fDst);
res.Write(socket);
break;
}

case JackRequest::kSetBufferSize: {
JackLog("JackRequest::SetBufferSize\n");
JackSetBufferSizeRequest req;
JackResult res;
req.Read(socket);
res.fResult = fServer->SetBufferSize(req.fBufferSize);
res.Write(socket);
break;
}

case JackRequest::kSetFreeWheel: {
JackLog("JackRequest::SetFreeWheel\n");
JackSetFreeWheelRequest req;
JackResult res;
req.Read(socket);
res.fResult = fServer->SetFreewheel(req.fOnOff);
res.Write(socket);
break;
}

case JackRequest::kReleaseTimebase: {
JackLog("JackRequest::kReleaseTimebase\n");
JackReleaseTimebaseRequest req;
JackResult res;
req.Read(socket);
res.fResult = fServer->GetEngine()->ReleaseTimebase(req.fRefNum);
res.Write(socket);
break;
}

case JackRequest::kSetTimebaseCallback: {
JackLog("JackRequest::kSetTimebaseCallback\n");
JackSetTimebaseCallbackRequest req;
JackResult res;
req.Read(socket);
res.fResult = fServer->GetEngine()->SetTimebaseCallback(req.fRefNum, req.fConditionnal);
res.Write(socket);
break;
}

case JackRequest::kNotification: {
JackLog("JackRequest::Notification\n");
JackClientNotificationRequest req;
req.Read(socket);
fServer->Notify(req.fRefNum, req.fNotify, req.fValue);
break;
}

default:
JackLog("Unknown request %ld\n", header.fType);
break;
}

return 0;
}

void JackSocketServerChannel::BuildPoolTable()
{
if (fRebuild) {
fRebuild = false;
delete[] fPollTable;
fPollTable = new pollfd[fSocketTable.size() + 1];

JackLog("JackSocketServerChannel::BuildPoolTable size = %d\n", fSocketTable.size() + 1);

// First fd is the server request socket
fPollTable[0].fd = fRequestListenSocket.GetFd();
fPollTable[0].events = POLLIN | POLLERR;

// Next fd for clients
map<int, pair<int, JackClientSocket*> >::iterator it;
int i;

for (i = 1, it = fSocketTable.begin(); it != fSocketTable.end(); it++, i++) {
JackLog("fSocketTable i = %ld fd = %ld\n", i, it->first);
fPollTable[i].fd = it->first;
fPollTable[i].events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL;
}
}
}

bool JackSocketServerChannel::Execute()
{
// Global poll
if ((poll(fPollTable, fSocketTable.size() + 1, 10000) < 0) && (errno != EINTR)) {
jack_error("Engine poll failed err = %s request thread quits...", strerror(errno));
return false;
} else {

// Poll all clients
for (unsigned int i = 1; i < fSocketTable.size() + 1; i++) {
int fd = fPollTable[i].fd;
JackLog("fPollTable i = %ld fd = %ld\n", i, fd);
if (fPollTable[i].revents & ~POLLIN) {
jack_error("Poll client error err = %s", strerror(errno));
KillClient(fd);
} else if (fPollTable[i].revents & POLLIN) {
if (HandleRequest(fd) < 0) {
jack_error("Could not handle external client request");
//RemoveClient(fd); TO CHECK
}
}
}

// Check the server request socket */
if (fPollTable[0].revents & POLLERR) {
jack_error("Error on server request socket err = %s", strerror(errno));
//return false; TO CHECK
}

if (fPollTable[0].revents & POLLIN) {
CreateClient();
}
}

BuildPoolTable();
return true;
}

} // end of namespace



+ 70
- 0
common/JackSocketServerChannel.h View File

@@ -0,0 +1,70 @@
/*
Copyright (C) 2004-2006 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#ifndef __JackSocketServerChannel__
#define __JackSocketServerChannel__

#include "JackChannel.h"
#include "JackSocket.h"
#include "JackThread.h"
#include <poll.h>
#include <map>

namespace Jack
{

/*!
\brief JackServerChannel using sockets.
*/

class JackSocketServerChannel : public JackServerChannelInterface, public JackRunnableInterface
{

private:

JackServerSocket fRequestListenSocket; // Socket to create request socket for the client
JackThread* fThread; // Thread to execute the event loop
JackServer* fServer;
pollfd* fPollTable;
bool fRebuild;
std::map<int, std::pair<int, JackClientSocket*> > fSocketTable;

int HandleRequest(int fd);
void CreateClient();
void AddClient(int fd, char* name, int* shared_engine, int* shared_client, int* shared_ports, int* result);
void RemoveClient(int fd, int refnum);
void KillClient(int fd);
void BuildPoolTable();

public:

JackSocketServerChannel();
virtual ~JackSocketServerChannel();

int Open(JackServer* server); // Open the Server/Client connection
void Close(); // Close the Server/Client connection

// JackRunnableInterface interface
bool Execute();
};

} // end of namespace

#endif


+ 59
- 0
common/JackSocketServerNotifyChannel.cpp View File

@@ -0,0 +1,59 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackSocketServerNotifyChannel.h"
#include "JackError.h"
#include "JackRequest.h"
#include "JackConstants.h"

namespace Jack
{

int JackSocketServerNotifyChannel::Open()
{
if (fRequestSocket.Connect(jack_server_dir, 0) < 0) {
jack_error("Cannot connect to server socket");
return -1;
} else {
return 0;
}
}

void JackSocketServerNotifyChannel::Close()
{
fRequestSocket.Close();
}

/*
The requirement is that the Notification from RT thread can be delivered... not sure using a socket is adequate here...
Can the write operation block?
A non blocking write operation shoud be used : check if write can succeed, and ignore the notification otherwise
(since its mainly used for XRun, ignoring a notification is OK, successive XRun will come...)
*/
void JackSocketServerNotifyChannel::ClientNotify(int refnum, int notify, int value)
{
JackClientNotificationRequest req(refnum, notify, value);
if (req.Write(&fRequestSocket) < 0) {
jack_error("Could not write request type = %ld", req.fType);
}
}

} // end of namespace



+ 55
- 0
common/JackSocketServerNotifyChannel.h View File

@@ -0,0 +1,55 @@
/*
Copyright (C) 2004-2006 Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#ifndef __JackSocketServerNotifyChannel__
#define __JackSocketServerNotifyChannel__

#include "JackChannel.h"
#include "JackSocket.h"

namespace Jack
{

/*!
\brief JackServerNotifyChannel using sockets.
*/

class JackSocketServerNotifyChannel : public JackServerNotifyChannelInterface
{
private:

JackClientSocket fRequestSocket;

public:

JackSocketServerNotifyChannel()
{}
virtual ~JackSocketServerNotifyChannel()
{}

int Open();
void Close();

void ClientNotify(int refnum, int notify, int value);
};

} // end of namespace

#endif


+ 51
- 0
common/JackSyncInterface.h View File

@@ -0,0 +1,51 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackSyncInterface__
#define __JackSyncInterface__

namespace Jack
{

/*!
\brief A synchronization primitive interface.
*/

class JackSyncInterface
{

public:

JackSyncInterface()
{}
virtual ~JackSyncInterface()
{}

virtual bool Allocate(const char* name) = 0;
virtual bool Connect(const char* name) = 0;
virtual bool TimedWait(long usec) = 0;
virtual void Wait() = 0;
virtual void SignalAll() = 0;
virtual void Destroy() = 0;
};

} // end of namespace

#endif


+ 102
- 0
common/JackSynchro.h View File

@@ -0,0 +1,102 @@
/*
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackSynchro__
#define __JackSynchro__

#include "JackError.h"

#define SYNC_MAX_NAME_SIZE 256

namespace Jack
{

/*!
\brief An inter process synchronization primitive.
*/

class JackSynchro
{

protected:

char fName[SYNC_MAX_NAME_SIZE];
bool fFlush; // If true, signal are "flushed" : used for drivers that do no consume the signal

virtual void BuildName(const char* name, char* res)
{}

public:

JackSynchro(): fFlush(false)
{}
virtual ~JackSynchro()
{}

virtual bool Signal()
{
return true;
}
virtual bool SignalAll()
{
return true;
}
virtual bool Wait()
{
return true;
}
virtual bool TimedWait(long usec)
{
return true;
}
virtual bool Allocate(const char* name, int value)
{
return true;
}
virtual bool Connect(const char* name)
{
return true;
}
virtual bool ConnectInput(const char* name)
{
return true;
}
virtual bool ConnectOutput(const char* name)
{
return true;
}
virtual bool Disconnect()
{
return true;
}
virtual void Destroy()
{}

void SetFlush(bool mode)
{
fFlush = mode;
}

};


} // end of namespace

#endif


+ 97
- 0
common/JackThread.h View File

@@ -0,0 +1,97 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackThread__
#define __JackThread__

#ifdef WIN32
#include <windows.h>
typedef HANDLE pthread_t;
typedef ULONGLONG UInt64;
#else
#include <pthread.h>
typedef unsigned long long UInt64;
#endif

namespace Jack
{

/*!
\brief The base class for runnable objects, that have an <B> Init </B> and <B> Execute </B> method to be called in a thread.
*/

class JackRunnableInterface
{

public:

JackRunnableInterface()
{}
virtual ~JackRunnableInterface()
{}

virtual bool Init() /*! Called once when the thread is started */
{
return true;
}
virtual bool Execute() = 0; /*! Must be implemented by subclasses */
};

/*!
\brief The thread base class.
*/

class JackThread
{

protected:

JackRunnableInterface* fRunnable;
int fPriority;
bool fRealTime;
volatile bool fRunning;
int fCancellation;

public:

JackThread(JackRunnableInterface* runnable, int priority, bool real_time, int cancellation):
fRunnable(runnable), fPriority(priority), fRealTime(real_time), fRunning(false), fCancellation(cancellation)
{}
virtual ~JackThread()
{}

virtual int Start() = 0;
virtual int StartSync() = 0;
virtual int Kill() = 0;
virtual int Stop() = 0;

virtual int AcquireRealTime() = 0;
virtual int AcquireRealTime(int priority) = 0;
virtual int DropRealTime() = 0;

virtual void SetParams(UInt64 period, UInt64 computation, UInt64 constraint) // Empty implementation, will only make sense on OSX...
{}

virtual pthread_t GetThreadID() = 0;
};

} // end of namespace

#endif

+ 89
- 0
common/JackThreadedDriver.cpp View File

@@ -0,0 +1,89 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackThreadedDriver.h"
#include "JackError.h"
#include "JackGlobals.h"
#include "JackClient.h"
#include "JackEngineControl.h"

namespace Jack
{

JackThreadedDriver::JackThreadedDriver(JackDriverClient* driver)
{
fThread = JackGlobals::MakeThread(this);
fDriver = driver;
}

JackThreadedDriver::~JackThreadedDriver()
{
delete fThread;
delete fDriver;
}

int JackThreadedDriver::Start()
{
JackLog("JackThreadedDriver::Start\n");
int res;

if ((res = fDriver->Start()) < 0) {
jack_error("Cannot start driver");
return res;
}
if ((res = fThread->Start()) < 0) {
jack_error("Cannot start thread");
return res;
}

if (fDriver->IsRealTime()) {
JackLog("JackThreadedDriver::Start IsRealTime\n");
// Will do "something" on OSX only...
// fThread->SetParams(fDriver->fEngineControl->fPeriod, fDriver->fEngineControl->fComputation, fDriver->fEngineControl->fConstraint);
if (fThread->AcquireRealTime(GetEngineControl()->fPriority) < 0) {
jack_error("AcquireRealTime error");
}
}

return 0;
}

int JackThreadedDriver::Stop()
{
JackLog("JackThreadedDriver::Stop\n");
int res;

if ((res = fThread->Stop()) < 0) { // Stop when the thread cycle is finished
jack_error("Cannot stop thread");
return res;
}
if ((res = fDriver->Stop()) < 0) {
jack_error("Cannot stop driver");
return res;
}
return 0;
}

bool JackThreadedDriver::Execute()
{
return (Process() == 0);
}

} // end of namespace

+ 154
- 0
common/JackThreadedDriver.h View File

@@ -0,0 +1,154 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackThreadedDriver__
#define __JackThreadedDriver__

#include "JackDriver.h"
#include "JackThread.h"

namespace Jack
{

/*!
\brief The base class for threaded drivers. Threaded drivers are used with blocking devices.
*/

class JackThreadedDriver : public JackDriverClientInterface, public JackRunnableInterface
{

private:

JackThread* fThread;
JackDriverClient* fDriver;

public:

JackThreadedDriver(JackDriverClient* driver);
virtual ~JackThreadedDriver();

virtual int Open()
{
return fDriver->Open();
}

virtual int Open(jack_nframes_t nframes,
jack_nframes_t samplerate,
int capturing,
int playing,
int inchannels,
int outchannels,
bool monitor,
const char* capture_driver_name,
const char* playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency)
{
return fDriver->Open(nframes, samplerate, capturing, playing, inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency);
}

virtual int Close()
{
return fDriver->Close();
}

virtual int Process()
{
return fDriver->Process();
}

virtual int Attach()
{
return fDriver->Attach();
}
virtual int Detach()
{
return fDriver->Detach();
}

virtual int Read()
{
return fDriver->Read();
}
virtual int Write()
{
return fDriver->Write();
}

virtual int Start();
virtual int Stop();

virtual int SetBufferSize(jack_nframes_t nframes)
{
return fDriver->SetBufferSize(nframes);
}

virtual void SetMaster(bool onoff)
{
fDriver->SetMaster(onoff);
}
virtual bool GetMaster()
{
return fDriver->GetMaster();
}

virtual void AddSlave(JackDriverInterface* slave)
{
fDriver->AddSlave(slave);
}
virtual void RemoveSlave(JackDriverInterface* slave)
{
fDriver->RemoveSlave(slave);
}
virtual void ProcessSlaves()
{
fDriver->ProcessSlaves();
}

virtual void PrintState()
{
fDriver->PrintState();
}

virtual int ClientNotify(int refnum, const char* name, int notify, int sync, int value)
{
return fDriver->ClientNotify(refnum, name, notify, sync, value);
}

virtual JackClientControl* GetClientControl() const
{
return fDriver->GetClientControl();
}

virtual bool IsRealTime()
{
return fDriver->IsRealTime();
}

// JackRunnableInterface interface

virtual bool Execute();

};

} // end of namespace


#endif

+ 121
- 0
common/JackTime.c View File

@@ -0,0 +1,121 @@
/*
Copyright (C) 2001-2003 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackTime.h"
#include "JackError.h"

#ifdef __APPLE__

double __jack_time_ratio;

/* This should only be called ONCE per process. */
void InitTime()
{
JackLog("InitTime\n");
mach_timebase_info_data_t info;
mach_timebase_info(&info);
__jack_time_ratio = ((float)info.numer / info.denom) / 1000;
}

#endif

#ifdef WIN32

EXPORT LARGE_INTEGER _jack_freq;

void InitTime()
{
QueryPerformanceFrequency(&_jack_freq);
JackLog("InitTime freq = %ld %ld\n", _jack_freq.HighPart, _jack_freq.LowPart);
_jack_freq.QuadPart = _jack_freq.QuadPart / 1000000; // by usec
}

jack_time_t GetMicroSeconds(void)
{
LARGE_INTEGER t1;
QueryPerformanceCounter (&t1);
return (jack_time_t)(((double)t1.QuadPart)/((double)_jack_freq.QuadPart));
}

// TODO
#endif

#ifdef linux

#ifdef GETCYCLE_TIME

#include <stdio.h>
jack_time_t GetMhz(void)
{
FILE *f = fopen("/proc/cpuinfo", "r");
if (f == 0)
{
perror("can't open /proc/cpuinfo\n");
exit(1);
}

for ( ; ; )
{
jack_time_t mhz;
int ret;
char buf[1000];

if (fgets(buf, sizeof(buf), f) == NULL) {
jack_error ("FATAL: cannot locate cpu MHz in "
"/proc/cpuinfo\n");
exit(1);
}

#if defined(__powerpc__)
ret = sscanf(buf, "clock\t: %" SCNu64 "MHz", &mhz);
#elif defined( __i386__ ) || defined (__hppa__) || defined (__ia64__) || \
defined(__x86_64__)
ret = sscanf(buf, "cpu MHz : %" SCNu64, &mhz);
#elif defined( __sparc__ )
ret = sscanf(buf, "Cpu0Bogo : %" SCNu64, &mhz);
#elif defined( __mc68000__ )
ret = sscanf(buf, "Clocking: %" SCNu64, &mhz);
#elif defined( __s390__ )
ret = sscanf(buf, "bogomips per cpu: %" SCNu64, &mhz);
#else /* MIPS, ARM, alpha */
ret = sscanf(buf, "BogoMIPS : %" SCNu64, &mhz);
#endif
if (ret == 1)
{
fclose(f);
return (jack_time_t)mhz;
}
}
}

jack_time_t __jack_cpu_mhz;

void InitTime()
{
__jack_cpu_mhz = GetMhz();
}

#else
void InitTime()
{}

#endif

#endif

+ 111
- 0
common/JackTime.h View File

@@ -0,0 +1,111 @@
/*
Copyright (C) 2001-2003 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#ifndef __JackTime__
#define __JackTime__

#include "types.h"
#include "JackExports.h"
#include <stdio.h>

#ifdef __cplusplus
extern "C"
{
#endif

#if defined(__APPLE__)

#include <mach/mach_time.h>
#include <unistd.h>

extern double __jack_time_ratio;

static inline jack_time_t GetMicroSeconds(void) {
return (jack_time_t) (mach_absolute_time () * __jack_time_ratio);
}

/* This should only be called ONCE per process. */
extern void InitTime();

static inline void JackSleep(long usec) {
usleep(usec);
}

#endif

#ifdef WIN32

extern EXPORT LARGE_INTEGER _jack_freq;

/*
static jack_time_t GetMicroSeconds(void) {
LARGE_INTEGER t1;
QueryPerformanceCounter (&t1);
return (jack_time_t)(((double)t1.QuadPart)/((double)_jack_freq.QuadPart));
}
*/

extern EXPORT jack_time_t GetMicroSeconds(void) ;

extern void InitTime();

static void JackSleep(long usec) {
Sleep(usec / 1000);
}

#endif

#ifdef linux

#include <unistd.h>

static inline void JackSleep(long usec) {
usleep(usec);
}

#ifdef GETCYCLE_TIME
#include "cycles.h"
extern jack_time_t __jack_cpu_mhz;
extern jack_time_t GetMhz();
extern void InitTime();
static inline jack_time_t GetMicroSeconds (void) {
return get_cycles() / __jack_cpu_mhz;
}
#else
#include <time.h>
extern void InitTime();
static inline jack_time_t GetMicroSeconds (void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (jack_time_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
}
#endif

#endif

#ifdef __cplusplus
}
#endif


#endif




+ 259
- 0
common/JackTransportEngine.cpp View File

@@ -0,0 +1,259 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "JackTransportEngine.h"
#include "JackClientControl.h"
#include "JackError.h"
#include "JackTime.h"
#include <assert.h>
#include <stdlib.h>

using namespace std;

namespace Jack
{

JackTransportEngine::JackTransportEngine(): JackAtomicArrayState<jack_position_t>()
{
fTransportState = JackTransportStopped;
fTransportCmd = fPreviousCmd = TransportCommandStop;
fSyncTimeout = 2000000; /* 2 second default */
fSyncTimeLeft = 0;
fTimeBaseMaster = -1;
fWriteCounter = 0;
fPendingPos = false;
}

// compute the number of cycle for timeout
void JackTransportEngine::SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size)
{
long buf_usecs = (long)((buffer_size * (jack_time_t) 1000000) / frame_rate);
fSyncTimeLeft = fSyncTimeout / buf_usecs;
JackLog("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld\n", (long)fSyncTimeout, (long)fSyncTimeLeft);
}

int JackTransportEngine::ResetTimebase(int refnum)
{
if (fTimeBaseMaster == refnum) {
jack_position_t* request = WriteNextStateStart(2); // To check
request->valid = (jack_position_bits_t)0;
WriteNextStateStop(2);
fTimeBaseMaster = -1;
return 0;
} else {
return EINVAL;
}
}

int JackTransportEngine::SetTimebase(int refnum, bool conditionnal)
{
if (conditionnal && fTimeBaseMaster > 0) {
if (refnum != fTimeBaseMaster) {
JackLog("conditional timebase for ref = %ld failed: %ld is already the master\n", refnum, fTimeBaseMaster);
return EBUSY;
} else {
JackLog("ref = %ld was already timebase master\n", refnum);
return 0;
}
} else {
fTimeBaseMaster = refnum;
JackLog("new timebase master: ref = %ld\n", refnum);
return 0;
}
}

bool JackTransportEngine::CheckOneSynching(JackClientInterface** table)
{
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
JackClientInterface* client = table[i];
if (client && client->GetClientControl()->fTransportState == JackTransportSynching) {
JackLog("CheckOneSynching\n");
return true;
}
}
return false;
}

bool JackTransportEngine::CheckAllRolling(JackClientInterface** table)
{
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
JackClientInterface* client = table[i];
if (client && client->GetClientControl()->fTransportState != JackTransportRolling) {
JackLog("CheckAllRolling refnum = %ld is not rolling\n", i);
return false;
}
}
JackLog("CheckAllRolling\n");
return true;
}

void JackTransportEngine::MakeAllStarting(JackClientInterface** table)
{
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
JackClientInterface* client = table[i];
if (client) {
// Unactive clients don't have their process function called at all, they appear as already "rolling" for the transport....
client->GetClientControl()->fTransportState = (client->GetClientControl()->fActive) ? JackTransportStarting : JackTransportRolling;
JackLog("MakeAllStarting refnum = %ld \n", i);
}
}
JackLog("MakeAllStarting\n");
}

void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time) // really needed?? (woule be done in CycleEnd...)
{
jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
pending->usecs = time;
pending->frame_rate = frame_rate;
WriteNextStateStop(1);
}

void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
{
TrySwitchState(1); // Switch from "pending" to "current", it always works since there is always a pending state

/* Handle any new transport command from the last cycle. */
transport_command_t cmd = fTransportCmd;
if (cmd != fPreviousCmd) {
fPreviousCmd = cmd;
JackLog("transport command: %s\n", (cmd == TransportCommandStart ? "START" : "STOP"));
} else {
cmd = TransportCommandNone;
}

/* state transition switch */
switch (fTransportState) {

case JackTransportSynching:
if (cmd == TransportCommandStart) {
fTransportState = JackTransportStarting;
MakeAllStarting(table);
SyncTimeout(frame_rate, buffer_size);
JackLog("transport locate ==> starting....\n");
} else if (fPendingPos) {
fTransportState = JackTransportSynching;
JackLog("transport locate ==> locate....\n");
} else {
fTransportState = JackTransportStopped;
JackLog("transport locate ==> stopped....\n");
}
break;

case JackTransportStopped:
// Set a JackTransportStarting for the current cycle, if all clients are ready (now slow_sync) ==> JackTransportRolling next state
if (cmd == TransportCommandStart) {
fTransportState = JackTransportStarting;
MakeAllStarting(table);
SyncTimeout(frame_rate, buffer_size);
JackLog("transport stopped ==> starting....\n");
} else if (fPendingPos || CheckOneSynching(table)) {
fTransportState = JackTransportSynching;
JackLog("transport stopped ==> locate....\n");
}
break;

case JackTransportStarting:
JackLog("transport starting fSyncTimeLeft %ld\n", fSyncTimeLeft);

if (cmd == TransportCommandStop) {
fTransportState = JackTransportStopped;
JackLog("transport starting ==> stopped\n");
} else if (fPendingPos) {
fTransportState = JackTransportStarting;
MakeAllStarting(table);
SyncTimeout(frame_rate, buffer_size);
} else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) {
fTransportState = JackTransportRolling;
JackLog("transport starting ==> rolling.... fSyncTimeLeft %ld\n", fSyncTimeLeft);
}
break;

case JackTransportRolling:
if (cmd == TransportCommandStop) {
fTransportState = JackTransportStopped;
JackLog("transport rolling ==> stopped\n");
} else if (fPendingPos || CheckOneSynching(table)) {
fTransportState = JackTransportStarting;
MakeAllStarting(table);
SyncTimeout(frame_rate, buffer_size);
JackLog("transport rolling ==> starting....\n");
}
break;

default:
jack_error("Invalid JACK transport state: %d", fTransportState);
}

/* Update timebase, if needed. */
if (fTransportState == JackTransportRolling) {
jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
pending->frame += buffer_size;
WriteNextStateStop(1);
}

/* See if an asynchronous position request arrived during the last cycle. */
jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
if (fPendingPos) {
JackLog("New pos = %ld\n", request->frame);
jack_position_t* pending = WriteNextStateStart(1);
TransportCopyPosition(request, pending);
WriteNextStateStop(1);
}
}

void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
{
UInt16 next_index = GetCurrentIndex();
UInt16 cur_index;
do {
cur_index = next_index;
memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
next_index = GetCurrentIndex();
} while (cur_index != next_index); // Until a coherent state has been read
}

void JackTransportEngine::TransportCopyPosition(jack_position_t* from, jack_position_t* to)
{
int tries = 0;
long timeout = 1000;

do {
/* throttle the busy wait if we don't get the answer
* very quickly. See comment above about this
* design.
*/
if (tries > 10) {
JackSleep(20);
tries = 0;

/* debug code to avoid system hangs... */
if (--timeout == 0) {
jack_error("hung in loop copying position B");
abort();
}
}
*to = *from;
tries++;

} while (to->unique_1 != to->unique_2);
}


} // end of namespace

+ 137
- 0
common/JackTransportEngine.h View File

@@ -0,0 +1,137 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackTransportEngine__
#define __JackTransportEngine__

#include "transport_types.h"
#include "JackClientInterface.h"
#include "JackConstants.h"
#include "JackAtomicArrayState.h"

namespace Jack
{

typedef enum {
TransportCommandNone = 0,
TransportCommandStart = 1,
TransportCommandStop = 2,
} transport_command_t;

/*!
\brief The client transport structure.
We have:
- a "current" position
- a "pending" position prepared by the server at each cycle
- a "request" position wanted by a client
At the beginning of a cycle the server needs to select a new current position. When a request and a pending position are available,
the resquest takes precedence on the pending one. The server atomically switches to the new position.
The current position can be read by clients.
We use a JackAtomicArrayState pattern that allows to manage several "next" states independantly.
*/

class JackTransportEngine : public JackAtomicArrayState<jack_position_t>
{

private:

jack_transport_state_t fTransportState;
volatile transport_command_t fTransportCmd;
transport_command_t fPreviousCmd; /* previous transport_cmd */
jack_time_t fSyncTimeout;
int fSyncTimeLeft;
int fTimeBaseMaster;
bool fPendingPos;
volatile SInt32 fWriteCounter;

bool CheckOneSynching(JackClientInterface** table);
bool CheckAllRolling(JackClientInterface** table);
void MakeAllStarting(JackClientInterface** table);
void SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size);

public:

JackTransportEngine();

~JackTransportEngine()
{}

void SetCommand(transport_command_t state)
{
fTransportCmd = state;
}

jack_transport_state_t GetState() const
{
return fTransportState;
}

int GetTimebaseMaster() const
{
return fTimeBaseMaster;
}

/*
\brief
*/
int ResetTimebase(int refnum);

/*
\brief
*/
int SetTimebase(int refnum, bool conditionnal);

/*
\brief
*/
void CycleBegin(jack_nframes_t frame_rate, jack_time_t time);

/*
\brief
*/
void CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size);

/*
\brief
*/
void SetSyncTimeout(jack_time_t timeout)
{
fSyncTimeout = timeout;
}

void ReadCurrentPos(jack_position_t* pos);

jack_unique_t GenerateUniqueID()
{
return (jack_unique_t)INC_ATOMIC(&fWriteCounter);
}

static void TransportCopyPosition(jack_position_t* from, jack_position_t* to);

};


} // end of namespace

#endif

+ 36
- 0
common/JackTypes.h View File

@@ -0,0 +1,36 @@
/*
Copyright (C) 2001 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

$Id: JackTypes.h,v 1.2.2.1 2006/06/20 14:44:00 letz Exp $
*/

#ifndef __JackTypes__
#define __JackTypes__

namespace Jack
{

typedef enum {
NotTriggered,
Triggered,
Running,
Finished,
} jack_client_state_t;

}

#endif

+ 569
- 0
common/Jackdmp.cpp View File

@@ -0,0 +1,569 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004-2006 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <iostream>
#include <assert.h>
#include <signal.h>
#include <pwd.h>
#include <sys/types.h>
#include <dirent.h>
#include <getopt.h>

#include "JackServer.h"
#include "JackConstants.h"
#include "driver_interface.h"
#include "driver_parse.h"
#include "JackDriverLoader.h"
#include "jslist.h"
#include "JackError.h"
#include "shm.h"
#include "jack.h"

using namespace Jack;

static JackServer* fServer;
static char* server_name = NULL;
static int realtime_priority = 10;
static int do_mlock = 1;
static unsigned int port_max = 128;
static int realtime = 0;
static int loopback = 0;
static int temporary = 0;
static int client_timeout = 0; /* msecs; if zero, use period size. */
static int do_unlock = 0;
static JSList* drivers = NULL;

static sigset_t signals;

#define DEFAULT_TMP_DIR "/tmp"
char* jack_tmpdir = DEFAULT_TMP_DIR;

static void silent_jack_error_callback (const char *desc)
{}

static void copyright(FILE* file)
{
fprintf (file, "jackdmp " VERSION "\n"
"Copyright 2001-2005 Paul Davis and others.\n"
"Copyright 2004-2006 Grame.\n"
"jackdmp comes with ABSOLUTELY NO WARRANTY\n"
"This is free software, and you are welcome to redistribute it\n"
"under certain conditions; see the file COPYING for details\n");
}

static void usage (FILE* file)
{
copyright (file);
fprintf (file, "\n"
"usage: jackdmp [ --realtime OR -R [ --realtime-priority OR -P priority ] ]\n"
" [ --name OR -n server-name ]\n"
// " [ --no-mlock OR -m ]\n"
// " [ --unlock OR -u ]\n"
" [ --timeout OR -t client-timeout-in-msecs ]\n"
" [ --loopback OR -L loopback-port-number ]\n"
// " [ --port-max OR -p maximum-number-of-ports]\n"
" [ --verbose OR -v ]\n"
" [ --silent OR -s ]\n"
" [ --sync OR -S ]\n"
" [ --version OR -V ]\n"
" -d driver [ ... driver args ... ]\n"
" where driver can be `alsa', `coreaudio' or `dummy'\n"
" jackdmp -d driver --help\n"
" to display options for each driver\n\n");
}


static void DoNothingHandler(int sig)
{
/* this is used by the child (active) process, but it never
gets called unless we are already shutting down after
another signal.
*/
char buf[64];
snprintf(buf, sizeof(buf), "received signal %d during shutdown (ignored)\n", sig);
write(1, buf, strlen(buf));
}

static int JackStart(jack_driver_desc_t* driver_desc, JSList* driver_params, int sync, int time_out_ms, int rt, int priority, int loopback, int verbose)
{
JackLog("Jackdmp: sync = %ld timeout = %ld rt = %ld priority = %ld verbose = %ld \n", sync, time_out_ms, rt, priority, verbose);
fServer = new JackServer(sync, time_out_ms, rt, priority, loopback, verbose);
int res = fServer->Open(driver_desc, driver_params);
return (res < 0) ? res : fServer->Start();
}

static int JackStop()
{
fServer->Stop();
fServer->Close();
JackLog("Jackdmp: server close\n");
delete fServer;
JackLog("Jackdmp: delete server\n");
return 0;
}

static int JackDelete()
{
delete fServer;
JackLog("Jackdmp: delete server\n");
return 0;
}

static void FilterSIGPIPE()
{
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGPIPE);
//sigprocmask(SIG_BLOCK, &set, 0);
pthread_sigmask(SIG_BLOCK, &set, 0);
}

static char* jack_default_server_name(void)
{
char *server_name;
if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL)
server_name = "default";
return server_name;
}

/* returns the name of the per-user subdirectory of jack_tmpdir */
static char* jack_user_dir(void)
{
static char user_dir[PATH_MAX] = "";

/* format the path name on the first call */
if (user_dir[0] == '\0') {
snprintf (user_dir, sizeof (user_dir), "%s/jack-%d",
jack_tmpdir, getuid ());
}

return user_dir;
}

/* returns the name of the per-server subdirectory of jack_user_dir() */

static char* get_jack_server_dir(const char* toto)
{
static char server_dir[PATH_MAX] = "";

// format the path name on the first call
if (server_dir[0] == '\0') {
snprintf (server_dir, sizeof (server_dir), "%s/%s",
jack_user_dir (), server_name);
}

return server_dir;
}

static void
jack_cleanup_files (const char *server_name)
{
DIR *dir;
struct dirent *dirent;
char *dir_name = get_jack_server_dir (server_name);

/* On termination, we remove all files that jackd creates so
* subsequent attempts to start jackd will not believe that an
* instance is already running. If the server crashes or is
* terminated with SIGKILL, this is not possible. So, cleanup
* is also attempted when jackd starts.
*
* There are several tricky issues. First, the previous JACK
* server may have run for a different user ID, so its files
* may be inaccessible. This is handled by using a separate
* JACK_TMP_DIR subdirectory for each user. Second, there may
* be other servers running with different names. Each gets
* its own subdirectory within the per-user directory. The
* current process has already registered as `server_name', so
* we know there is no other server actively using that name.
*/

/* nothing to do if the server directory does not exist */
if ((dir = opendir (dir_name)) == NULL) {
return ;
}

/* unlink all the files in this directory, they are mine */
while ((dirent = readdir (dir)) != NULL) {

char fullpath[PATH_MAX];

if ((strcmp (dirent->d_name, ".") == 0)
|| (strcmp (dirent->d_name, "..") == 0)) {
continue;
}

snprintf (fullpath, sizeof (fullpath), "%s/%s",
dir_name, dirent->d_name);

if (unlink (fullpath)) {
jack_error ("cannot unlink `%s' (%s)", fullpath,
strerror (errno));
}
}

closedir (dir);

/* now, delete the per-server subdirectory, itself */
if (rmdir (dir_name)) {
jack_error ("cannot remove `%s' (%s)", dir_name,
strerror (errno));
}

/* finally, delete the per-user subdirectory, if empty */
if (rmdir (jack_user_dir ())) {
if (errno != ENOTEMPTY) {
jack_error ("cannot remove `%s' (%s)",
jack_user_dir (), strerror (errno));
}
}
}

#ifdef FORK_SERVER

int main(int argc, char* argv[])
{
int sig;
sigset_t allsignals;
struct sigaction action;
int waiting;

jack_driver_desc_t* driver_desc;
const char *options = "-ad:P:uvshVRL:STFl:t:mn:p:";
struct option long_options[] = {
{ "driver", 1, 0, 'd'
},
{ "verbose", 0, 0, 'v' },
{ "help", 0, 0, 'h' },
{ "port-max", 1, 0, 'p' },
{ "no-mlock", 0, 0, 'm' },
{ "name", 0, 0, 'n' },
{ "unlock", 0, 0, 'u' },
{ "realtime", 0, 0, 'R' },
{ "loopback", 0, 0, 'L' },
{ "realtime-priority", 1, 0, 'P' },
{ "timeout", 1, 0, 't' },
{ "temporary", 0, 0, 'T' },
{ "version", 0, 0, 'V' },
{ "silent", 0, 0, 's' },
{ "sync", 0, 0, 'S' },
{ 0, 0, 0, 0 }
};
int opt = 0;
int option_index = 0;
int seen_driver = 0;
char *driver_name = NULL;
char **driver_args = NULL;
JSList* driver_params;
int driver_nargs = 1;
int show_version = 0;
int sync = 0;
int rc, i;

opterr = 0;
while (!seen_driver &&
(opt = getopt_long(argc, argv, options,
long_options, &option_index)) != EOF) {
switch (opt) {

case 'd':
seen_driver = 1;
driver_name = optarg;
break;

case 'v':
verbose = 1;
break;

case 's':
jack_set_error_function(silent_jack_error_callback);
break;

case 'S':
sync = 1;
break;

case 'n':
server_name = optarg;
break;

case 'm':
do_mlock = 0;
break;

case 'p':
port_max = (unsigned int)atol(optarg);
break;

case 'P':
realtime_priority = atoi(optarg);
break;

case 'R':
realtime = 1;
break;

case 'L':
loopback = atoi(optarg);
break;

case 'T':
temporary = 1;
break;

case 't':
client_timeout = atoi(optarg);
break;

case 'u':
do_unlock = 1;
break;

case 'V':
show_version = 1;
break;

default:
fprintf(stderr, "unknown option character %c\n",
optopt);
/*fallthru*/
case 'h':
usage(stdout);
return -1;
}
}

/*
if (show_version) {
printf ( "jackd version " VERSION
" tmpdir " DEFAULT_TMP_DIR
" protocol " PROTOCOL_VERSION
"\n");
return -1;
}
*/

if (!seen_driver) {
usage (stderr);
exit (1);
}

drivers = jack_drivers_load (drivers);
if (!drivers) {
fprintf (stderr, "jackdmp: no drivers found; exiting\n");
exit (1);
}

driver_desc = jack_find_driver_descriptor (drivers, driver_name);
if (!driver_desc) {
fprintf (stderr, "jackdmp: unknown driver '%s'\n", driver_name);
exit (1);
}

if (optind < argc) {
driver_nargs = 1 + argc - optind;
} else {
driver_nargs = 1;
}

if (driver_nargs == 0) {
fprintf (stderr, "No driver specified ... hmm. JACK won't do"
" anything when run like this.\n");
return -1;
}

driver_args = (char **) malloc (sizeof (char *) * driver_nargs);
driver_args[0] = driver_name;

for (i = 1; i < driver_nargs; i++) {
driver_args[i] = argv[optind++];
}

if (jack_parse_driver_params (driver_desc, driver_nargs,
driver_args, &driver_params)) {
exit (0);
}

if (server_name == NULL)
server_name = jack_default_server_name ();

copyright (stdout);

rc = jack_register_server (server_name);
switch (rc) {
case EEXIST:
fprintf (stderr, "`%s' server already active\n", server_name);
exit (1);
case ENOSPC:
fprintf (stderr, "too many servers already active\n");
exit (2);
case ENOMEM:
fprintf (stderr, "no access to shm registry\n");
exit (3);
default:
if (verbose)
fprintf (stderr, "server `%s' registered\n",
server_name);
}

/* clean up shared memory and files from any previous
* instance of this server name */
jack_cleanup_shm();
jack_cleanup_files(server_name);

pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

sigemptyset(&signals);
sigaddset(&signals, SIGHUP);
sigaddset(&signals, SIGINT);
sigaddset(&signals, SIGQUIT);
sigaddset(&signals, SIGPIPE);
sigaddset(&signals, SIGTERM);
sigaddset(&signals, SIGUSR1);
sigaddset(&signals, SIGUSR2);

// all child threads will inherit this mask unless they
// explicitly reset it

FilterSIGPIPE();
pthread_sigmask(SIG_BLOCK, &signals, 0);

if (!realtime && client_timeout == 0)
client_timeout = 500; /* 0.5 sec; usable when non realtime. */

int res = JackStart(driver_desc, driver_params, sync, client_timeout, realtime, realtime_priority, loopback, verbose);
if (res < 0) {
jack_error("Cannot start server... exit");
JackDelete();
return 0;
}

/*
For testing purpose...
InternalMetro* client1 = new InternalMetro(1200, 0.4, 20, 80, "metro1");
InternalMetro* client2 = new InternalMetro(600, 0.4, 20, 150, "metro2");
InternalMetro* client3 = new InternalMetro(1000, 0.4, 20, 110, "metro3");
InternalMetro* client4 = new InternalMetro(1200, 0.4, 20, 80, "metro4");
InternalMetro* client5 = new InternalMetro(1500, 0.4, 20, 60, "metro5");
InternalMetro* client6 = new InternalMetro(1200, 0.4, 20, 84, "metro6");
InternalMetro* client7 = new InternalMetro(600, 0.4, 20, 160, "metro7");
InternalMetro* client8 = new InternalMetro(1000, 0.4, 20, 113, "metro8");
InternalMetro* client9 = new InternalMetro(1200, 0.4, 20, 84, "metro9");
InternalMetro* client10 = new InternalMetro(1500, 0.4, 20, 70, "metro10");
*/

// install a do-nothing handler because otherwise pthreads
// behaviour is undefined when we enter sigwait.

sigfillset(&allsignals);
action.sa_handler = DoNothingHandler;
action.sa_mask = allsignals;
action.sa_flags = SA_RESTART | SA_RESETHAND;

for (i = 1; i < NSIG; i++) {
if (sigismember(&signals, i)) {
sigaction(i, &action, 0);
}
}

waiting = TRUE;

while (waiting) {
sigwait(&signals, &sig);

fprintf(stderr, "jack main caught signal %d\n", sig);

switch (sig) {
case SIGUSR1:
//jack_dump_configuration(engine, 1);
break;
case SIGUSR2:
// driver exit
waiting = FALSE;
break;
default:
waiting = FALSE;
break;
}
}

if (sig != SIGSEGV) {
// unblock signals so we can see them during shutdown.
// this will help prod developers not to lose sight of
// bugs that cause segfaults etc. during shutdown.
sigprocmask(SIG_UNBLOCK, &signals, 0);
}

JackStop();

jack_cleanup_shm();
jack_cleanup_files(server_name);
jack_unregister_server(server_name);

return 1;
}

#else

int main(int argc, char* argv[])
{
char c;
long sample_sate = lopt(argv, "-r", 44100);
long buffer_size = lopt(argv, "-p", 512);
long chan_in = lopt(argv, "-i", 2);
long chan_out = lopt(argv, "-o", 2);
long audiodevice = lopt(argv, "-I", -1);
long sync = lopt(argv, "-s", 0);
long timeout = lopt(argv, "-t", 100 * 1000);
const char* name = flag(argv, "-n", "Built-in Audio");
long rt = lopt(argv, "-R", 0);
verbose = lopt(argv, "-v", 0);

copyright(stdout);
usage(stdout);

FilterSIGPIPE();

printf("jackdmp: sample_sate = %ld buffer_size = %ld chan_in = %ld chan_out = %ld name = %s sync-mode = %ld\n",
sample_sate, buffer_size, chan_in, chan_out, name, sync);
assert(buffer_size <= BUFFER_SIZE_MAX);

int res = JackStart(sample_sate, buffer_size, chan_in, chan_out, name, audiodevice, sync, timeout, rt);
if (res < 0) {
jack_error("Cannot start server... exit\n");
JackDelete();
return 0;
}

while (((c = getchar()) != 'q')) {

switch (c) {

case 's':
fServer->PrintState();
break;
}
}

JackStop();
return 0;
}

#endif

+ 94
- 0
common/cycles.h View File

@@ -0,0 +1,94 @@
/*
Copyright (C) 2001 Paul Davis
Code derived from various headers from the Linux kernel
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

$Id: cycles.h,v 1.4.2.1 2006/06/20 14:44:00 letz Exp $
*/

#ifndef __jack_cycles_h__
#define __jack_cycles_h__

/*
* Standard way to access the cycle counter on i586+ CPUs.
* Currently only used on SMP.
*
* If you really have a SMP machine with i486 chips or older,
* compile for that, and this will just always return zero.
* That's ok, it just means that the nicer scheduling heuristics
* won't work for you.
*
* We only use the low 32 bits, and we'd simply better make sure
* that we reschedule before that wraps. Scheduling at least every
* four billion cycles just basically sounds like a good idea,
* regardless of how fast the machine is.
*/

#ifdef __linux__

#ifdef __PPC__

/* PowerPC */

#define CPU_FTR_601 0x00000100

typedef unsigned long cycles_t;

/* For the "cycle" counter we use the timebase lower half. */

extern cycles_t cacheflush_time;

static inline cycles_t get_cycles(void)
{
cycles_t ret = 0;

__asm__ __volatile__(
"98: mftb %0\n"
"99:\n"
".section __ftr_fixup,\"a\"\n"
" .long %1\n"
" .long 0\n"
" .long 98b\n"
" .long 99b\n"
".previous"
: "=r" (ret) : "i" (CPU_FTR_601));
return ret;
}

#endif

#ifdef __i386__

typedef unsigned long long cycles_t;

extern cycles_t cacheflush_time;

#define rdtscll(val) \
__asm__ __volatile__("rdtsc" : "=A" (val))

static inline cycles_t get_cycles (void)
{
unsigned long long ret;

rdtscll(ret);
return ret;
}

#endif

#endif

#endif /* __jack_cycles_h__ */

+ 97
- 0
common/driver_interface.h View File

@@ -0,0 +1,97 @@
/*
Copyright (C) 2003 Bob Ham <rah@bash.sh>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#ifndef __jack_driver_interface_h__
#define __jack_driver_interface_h__

#ifdef __cplusplus
extern "C"
{
#endif

#include <limits.h>

#ifdef WIN32
#include "types.h"
#define PATH_MAX 1024
#else
#include <inttypes.h>
#endif


#define JACK_DRIVER_NAME_MAX 15
#define JACK_DRIVER_PARAM_NAME_MAX 15
#define JACK_DRIVER_PARAM_STRING_MAX 63

/** Driver parameter types */
typedef enum
{
JackDriverParamInt = 1,
JackDriverParamUInt,
JackDriverParamChar,
JackDriverParamString,
JackDriverParamBool
} jack_driver_param_type_t;

/** Driver parameter value */
typedef union
{
uint32_t ui;
int32_t i;
char c;
char str[JACK_DRIVER_PARAM_STRING_MAX + 1];
} jack_driver_param_value_t;


/** A driver parameter descriptor */
typedef struct {
char name[JACK_DRIVER_NAME_MAX + 1]; /**< The parameter's name */
char character; /**< The parameter's character (for getopt, etc) */
jack_driver_param_type_t type; /**< The parameter's type */
jack_driver_param_value_t value; /**< The parameter's (default) value */
char short_desc[64]; /**< A short (~30 chars) description for the user */
char long_desc[1024]; /**< A longer description for the user */
}
jack_driver_param_desc_t;

/** A driver parameter */
typedef struct {
char character;
jack_driver_param_value_t value;
}
jack_driver_param_t;


/** A struct for describing a jack driver */
typedef struct {
char name[JACK_DRIVER_NAME_MAX + 1]; /**< The driver's canonical name */
char file[PATH_MAX + 1]; /**< The filename of the driver's shared object file */
uint32_t nparams; /**< The number of parameters the driver has */
jack_driver_param_desc_t * params; /**< An array of parameter descriptors */
}
jack_driver_desc_t;


#ifdef __cplusplus
}
#endif

#endif /* __jack_driver_interface_h__ */



+ 33
- 0
common/driver_parse.h View File

@@ -0,0 +1,33 @@
/*
Copyright (C) 2003 Bob Ham <rah@bash.sh
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __jack_driver_parse_h__
#define __jack_driver_parse_h__

#include "jslist.h"
#include "driver_interface.h"
#include "JackExports.h"

EXPORT int
jack_parse_driver_params (jack_driver_desc_t * desc, int argc, char* argv[], JSList ** param_ptr);


#endif /* __jack_driver_parse_h__ */



+ 132
- 0
common/intclient.h View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2004 Jack O'Quin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Id: intclient.h,v 1.2.2.1 2006/06/20 14:44:00 letz Exp $
*/

#ifndef __jack_intclient_h__
#define __jack_intclient_h__

#ifdef __cplusplus
extern "C"
{
#endif

//#include <jack/types.h>
#include "types.h"

/**
* Get an internal client's name. This is useful when @ref
* JackUseExactName was not specified on jack_internal_client_load()
* and @ref JackNameNotUnique status was returned. In that case, the
* actual name will differ from the @a client_name requested.
*
* @param client requesting JACK client's handle.
*
* @param intclient handle returned from jack_internal_client_load()
* or jack_internal_client_handle().
*
* @return NULL if unsuccessful, otherwise pointer to the internal
* client name obtained from the heap via malloc(). The caller should
* free() this storage when no longer needed.
*/
char *jack_get_internal_client_name (jack_client_t *client,
jack_intclient_t intclient);

/**
* Return the @ref jack_intclient_t handle for an internal client
* running in the JACK server.
*
* @param client requesting JACK client's handle.
*
* @param client_name for the internal client of no more than
* jack_client_name_size() characters. The name scope is local to the
* current server.
*
* @param status (if non-NULL) an address for JACK to return
* information from this operation. This status word is formed by
* OR-ing together the relevant @ref JackStatus bits.
*
* @return Opaque internal client handle if successful. If 0, the
* internal client was not found, and @a *status includes the @ref
* JackNoSuchClient and @ref JackFailure bits.
*/
jack_intclient_t jack_internal_client_handle (jack_client_t *client,
const char *client_name,
jack_status_t *status);

/**
* Load an internal client into the JACK server.
*
* Internal clients run inside the JACK server process. They can use
* most of the same functions as external clients. Each internal
* client is built as a shared object module, which must declare
* jack_initialize() and jack_finish() entry points called at load and
* unload times. See @ref inprocess.c for an example.
*
* @param client loading JACK client's handle.
*
* @param client_name of at most jack_client_name_size() characters
* for the internal client to load. The name scope is local to the
* current server.
*
* @param options formed by OR-ing together @ref JackOptions bits.
* Only the @ref JackLoadOptions bits are valid.
*
* @param status (if non-NULL) an address for JACK to return
* information from the load operation. This status word is formed by
* OR-ing together the relevant @ref JackStatus bits.
*
* <b>Optional parameters:</b> depending on corresponding [@a options
* bits] additional parameters may follow @a status (in this order).
*
* @arg [@ref JackLoadName] <em>(char *) load_name</em> is the shared
* object file from which to load the new internal client (otherwise
* use the @a client_name).
*
* @arg [@ref JackLoadInit] <em>(char *) load_init</em> an arbitary
* string passed to the internal client's jack_initialize() routine
* (otherwise NULL), of no more than @ref JACK_LOAD_INIT_LIMIT bytes.
*
* @return Opaque internal client handle if successful. If this is 0,
* the load operation failed, the internal client was not loaded, and
* @a *status includes the @ref JackFailure bit.
*/
jack_intclient_t jack_internal_client_load (jack_client_t *client,
const char *client_name,
jack_options_t options,
jack_status_t *status, ...);
/**
* Unload an internal client from a JACK server. This calls the
* intclient's jack_finish() entry point then removes it. See @ref
* inprocess.c for an example.
*
* @param client unloading JACK client's handle.
*
* @param intclient handle returned from jack_internal_client_load() or
* jack_internal_client_handle().
*
* @return 0 if successful, otherwise @ref JackStatus bits.
*/
jack_status_t jack_internal_client_unload (jack_client_t *client,
jack_intclient_t intclient);

#ifdef __cplusplus
}
#endif

#endif /* __jack_intclient_h__ */

+ 805
- 0
common/jack.h View File

@@ -0,0 +1,805 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004 Jack O'Quin
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

$Id: jack.h,v 1.5.2.6 2006/06/20 14:44:00 letz Exp $
*/

#ifndef __jack_h__
#define __jack_h__

#ifdef __cplusplus
extern "C"
{
#endif

#ifdef WIN32
#include <windows.h>
typedef HANDLE pthread_t;
#else
#include <pthread.h>
#endif

/*
#include <jack/types.h>
#include <jack/transport.h>
*/

#include "types.h"
#include "transport.h"

/**
* Note: More documentation can be found in jack/types.h.
*/

/**
* Open an external client session with a JACK server. This interface
* is more complex but more powerful than jack_client_new(). With it,
* clients may choose which of several servers to connect, and control
* whether and how to start the server automatically, if it was not
* already running. There is also an option for JACK to generate a
* unique client name, when necessary.
*
* @param client_name of at most jack_client_name_size() characters.
* The name scope is local to each server. Unless forbidden by the
* @ref JackUseExactName option, the server will modify this name to
* create a unique variant, if needed.
*
* @param options formed by OR-ing together @ref JackOptions bits.
* Only the @ref JackOpenOptions bits are allowed.
*
* @param status (if non-NULL) an address for JACK to return
* information from the open operation. This status word is formed by
* OR-ing together the relevant @ref JackStatus bits.
*
*
* <b>Optional parameters:</b> depending on corresponding [@a options
* bits] additional parameters may follow @a status (in this order).
*
* @arg [@ref JackServerName] <em>(char *) server_name</em> selects
* from among several possible concurrent server instances. Server
* names are unique to each user. If unspecified, use "default"
* unless \$JACK_DEFAULT_SERVER is defined in the process environment.
*
* @return Opaque client handle if successful. If this is NULL, the
* open operation failed, @a *status includes @ref JackFailure and the
* caller is not a JACK client.
*/
jack_client_t * jack_client_open (const char *client_name,
jack_options_t options,
jack_status_t *status, ...);

/**
* Attempt to become an external client of the Jack server.
*
* JACK is evolving a mechanism for automatically starting the server
* when needed. As a transition, jack_client_new() only does this
* when \$JACK_START_SERVER is defined in the environment of the
* calling process. In the future this will become normal behavior.
* For full control of this feature, use jack_client_open(), instead.
* In either case, defining \$JACK_NO_START_SERVER disables this
* feature.
*
* @param client_name of at most jack_client_name_size() characters.
* If this name is already in use, the request fails.
*
* @return Opaque client handle if successful, otherwise NULL.
*
* @note Failure generally means that the JACK server is not running.
* If there was some other problem, it will be reported via the @ref
* jack_error_callback mechanism. Use jack_client_open() and check
* the @a status parameter for more detailed information.
*/
jack_client_t * jack_client_new (const char *client_name);

/**
* Disconnects an external client from a JACK server.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_client_close (jack_client_t *client);

/**
* @return the maximum number of characters in a JACK client name
* including the final NULL character. This value is a constant.
*/
int jack_client_name_size (void);

/**
* @return pointer to actual client name. This is useful when @ref
* JackUseExactName is not specified on open and @ref
* JackNameNotUnique status was returned. In that case, the actual
* name will differ from the @a client_name requested.
*/
char * jack_get_client_name (jack_client_t *client);

/**
* Load an internal client into the Jack server.
*
* Internal clients run inside the JACK server process. They can use
* most of the same functions as external clients. Each internal
* client must declare jack_initialize() and jack_finish() entry
* points, called at load and unload times. See inprocess.c for an
* example of how to write an internal client.
*
* @deprecated Please use jack_internal_client_load().
*
* @param client_name of at most jack_client_name_size() characters.
*
* @param load_name of a shared object file containing the code for
* the new client.
*
* @param load_init an arbitary string passed to the jack_initialize()
* routine of the new client (may be NULL).
*
* @return 0 if successful.
*/
int jack_internal_client_new (const char *client_name,
const char *load_name,
const char *load_init);

jack_client_t* my_jack_internal_client_new(const char* client_name);

/**
* Remove an internal client from a JACK server.
*
* @deprecated Please use jack_internal_client_load().
*/
void jack_internal_client_close (const char *client_name);

void my_jack_internal_client_close (jack_client_t* client);

/**
* @param client pointer to JACK client structure.
*
* Check if the JACK subsystem is running with -R (--realtime).
*
* @return 1 if JACK is running realtime, 0 otherwise
*/
int jack_is_realtime (jack_client_t *client);

/**
* @param client pointer to JACK client structure.
* @param function The jack_shutdown function pointer.
* @param arg The arguments for the jack_shutdown function.
*
* Register a function (and argument) to be called if and when the
* JACK server shuts down the client thread. The function must
* be written as if it were an asynchonrous POSIX signal
* handler --- use only async-safe functions, and remember that it
* is executed from another thread. A typical function might
* set a flag or write to a pipe so that the rest of the
* application knows that the JACK client thread has shut
* down.
*
* NOTE: clients do not need to call this. It exists only
* to help more complex clients understand what is going
* on. It should be called before jack_client_activate().
*/
void jack_on_shutdown (jack_client_t *client,
void (*function)(void *arg), void *arg);

/**
* Tell the Jack server to call @a process_callback whenever there is
* work be done, passing @a arg as the second argument.
*
* The code in the supplied function must be suitable for real-time
* execution. That means that it cannot call functions that might
* block for a long time.  This includes malloc, free, printf,
* pthread_mutex_lock, sleep, wait, poll, select, pthread_join,
* pthread_cond_wait, etc, etc.  See
* http://jackit.sourceforge.net/docs/design/design.html#SECTION00411000000000000000
* for more information.
*
* @return 0 on success, otherwise a non-zero error code, causing JACK
* to remove that client from the process() graph.
*/
int jack_set_process_callback (jack_client_t *client,
JackProcessCallback process_callback,
void *arg);

/**
* Tell JACK to call @a thread_init_callback once just after
* the creation of the thread in which all other callbacks
* will be handled.
*
* The code in the supplied function does not need to be
* suitable for real-time execution.
*
* @return 0 on success, otherwise a non-zero error code, causing JACK
* to remove that client from the process() graph.
*/
int jack_set_thread_init_callback (jack_client_t *client,
JackThreadInitCallback thread_init_callback,
void *arg);

/**
* Tell the Jack server to call @a freewheel_callback
* whenever we enter or leave "freewheel" mode, passing @a
* arg as the second argument. The first argument to the
* callback will be non-zero if JACK is entering freewheel
* mode, and zero otherwise.
*
* @return 0 on success, otherwise a non-zero error code.
*/
int jack_set_freewheel_callback (jack_client_t *client,
JackFreewheelCallback freewheel_callback,
void *arg);

/**
* Start/Stop JACK's "freewheel" mode.
*
* When in "freewheel" mode, JACK no longer waits for
* any external event to begin the start of the next process
* cycle.
*
* As a result, freewheel mode causes "faster than realtime"
* execution of a JACK graph. If possessed, real-time
* scheduling is dropped when entering freewheel mode, and
* if appropriate it is reacquired when stopping.
*
* IMPORTANT: on systems using capabilities to provide real-time
* scheduling (i.e. Linux kernel 2.4), if onoff is zero, this function
* must be called from the thread that originally called jack_activate().
* This restriction does not apply to other systems (e.g. Linux kernel 2.6
* or OS X).
*
* @param client pointer to JACK client structure
* @param onoff if non-zero, freewheel mode starts. Otherwise
* freewheel mode ends.
*
* @return 0 on success, otherwise a non-zero error code.
*/
int jack_set_freewheel(jack_client_t* client, int onoff);

/**
* Change the buffer size passed to the @a process_callback.
*
* This operation stops the JACK engine process cycle, then calls all
* registered @a bufsize_callback functions before restarting the
* process cycle. This will cause a gap in the audio flow, so it
* should only be done at appropriate stopping points.
*
* @see jack_set_buffer_size_callback()
*
* @param client pointer to JACK client structure.
* @param nframes new buffer size. Must be a power of two.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes);

/**
* Tell JACK to call @a bufsize_callback whenever the size of the the
* buffer that will be passed to the @a process_callback is about to
* change. Clients that depend on knowing the buffer size must supply
* a @a bufsize_callback before activating themselves.
*
* @param client pointer to JACK client structure.
* @param bufsize_callback function to call when the buffer size changes.
* @param arg argument for @a bufsize_callback.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_buffer_size_callback (jack_client_t *client,
JackBufferSizeCallback bufsize_callback,
void *arg);

/**
* Tell the Jack server to call @a srate_callback whenever the system
* sample rate changes.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_sample_rate_callback (jack_client_t *client,
JackSampleRateCallback srate_callback,
void *arg);

/**
* Tell the JACK server to call @a registration_callback whenever a
* port is registered or unregistered, passing @a arg as a parameter.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_port_registration_callback (jack_client_t *,
JackPortRegistrationCallback
registration_callback, void *arg);

/**
* Tell the JACK server to call @a graph_callback whenever the
* processing graph is reordered, passing @a arg as a parameter.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_graph_order_callback (jack_client_t *,
JackGraphOrderCallback graph_callback,
void *);

/**
* Tell the JACK server to call @a xrun_callback whenever there is a
* xrun, passing @a arg as a parameter.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_xrun_callback (jack_client_t *,
JackXRunCallback xrun_callback, void *arg);

/**
* Tell the Jack server that the program is ready to start processing
* audio.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_activate (jack_client_t *client);

/**
* Tell the Jack server to remove this @a client from the process
* graph. Also, disconnect all ports belonging to it, since inactive
* clients have no port connections.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_deactivate (jack_client_t *client);

/**
* Create a new port for the client. This is an object used for moving
* data of any type in or out of the client. Ports may be connected
* in various ways.
*
* Each port has a short name. The port's full name contains the name
* of the client concatenated with a colon (:) followed by its short
* name. The jack_port_name_size() is the maximum length of this full
* name. Exceeding that will cause the port registration to fail and
* return NULL.
*
* All ports have a type, which may be any non-NULL and non-zero
* length string, passed as an argument. Some port types are built
* into the JACK API, currently only JACK_DEFAULT_AUDIO_TYPE.
*
* @param client pointer to JACK client structure.
* @param port_name non-empty short name for the new port (not
* including the leading @a "client_name:").
* @param port_type port type name. If longer than
* jack_port_type_size(), only that many characters are significant.
* @param flags @ref JackPortFlags bit mask.
* @param buffer_size must be non-zero if this is not a built-in @a
* port_type. Otherwise, it is ignored.
*
* @return jack_port_t pointer on success, otherwise NULL.
*/
jack_port_t * jack_port_register (jack_client_t *client,
const char *port_name,
const char *port_type,
unsigned long flags,
unsigned long buffer_size);

/**
* Remove the port from the client, disconnecting any existing
* connections.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_port_unregister (jack_client_t *, jack_port_t *);

/**
* This returns a pointer to the memory area associated with the
* specified port. For an output port, it will be a memory area
* that can be written to; for an input port, it will be an area
* containing the data from the port's connection(s), or
* zero-filled. if there are multiple inbound connections, the data
* will be mixed appropriately.
*
* FOR OUTPUT PORTS ONLY
* ---------------------
* You may cache the value returned, but only between calls to
* your "blocksize" callback. For this reason alone, you should
* either never cache the return value or ensure you have
* a "blocksize" callback and be sure to invalidate the cached
* address from there.
*/
void * jack_port_get_buffer (jack_port_t *, jack_nframes_t);

/**
* @return the full name of the jack_port_t (including the @a
* "client_name:" prefix).
*
* @see jack_port_name_size().
*/
const char * jack_port_name (const jack_port_t *port);

/**
* @return the short name of the jack_port_t (not including the @a
* "client_name:" prefix).
*
* @see jack_port_name_size().
*/
const char * jack_port_short_name (const jack_port_t *port);

/**
* @return the @ref JackPortFlags of the jack_port_t.
*/
int jack_port_flags (const jack_port_t *port);

/**
* @return the @a port type, at most jack_port_type_size() characters
* including a final NULL.
*/
const char * jack_port_type (const jack_port_t *port);

/**
* @return TRUE if the jack_port_t belongs to the jack_client_t.
*/
int jack_port_is_mine (const jack_client_t *, const jack_port_t *port);

/**
* @return number of connections to or from @a port.
*
* @pre The calling client must own @a port.
*/
int jack_port_connected (const jack_port_t *port);

/**
* @return TRUE if the locally-owned @a port is @b directly connected
* to the @a port_name.
*
* @see jack_port_name_size()
*/
int jack_port_connected_to (const jack_port_t *port,
const char *port_name);

/**
* @return a null-terminated array of full port names to which the @a
* port is connected. If none, returns NULL.
*
* The caller is responsible for calling free(3) on any non-NULL
* returned value.
*
* @param port locally owned jack_port_t pointer.
*
* @see jack_port_name_size(), jack_port_get_all_connections()
*/
const char ** jack_port_get_connections (const jack_port_t *port);

/**
* @return a null-terminated array of full port names to which the @a
* port is connected. If none, returns NULL.
*
* The caller is responsible for calling free(3) on any non-NULL
* returned value.
*
* This differs from jack_port_get_connections() in two important
* respects:
*
* 1) You may not call this function from code that is
* executed in response to a JACK event. For example,
* you cannot use it in a GraphReordered handler.
*
* 2) You need not be the owner of the port to get information
* about its connections.
*
* @see jack_port_name_size()
*/
const char ** jack_port_get_all_connections (const jack_client_t *client,
const jack_port_t *port);

/**
* A client may call this on a pair of its own ports to
* semi-permanently wire them together. This means that
* a client that wants to direct-wire an input port to
* an output port can call this and then no longer
* have to worry about moving data between them. Any data
* arriving at the input port will appear automatically
* at the output port.
*
* The 'destination' port must be an output port. The 'source'
* port must be an input port. Both ports must belong to
* the same client. You cannot use this to tie ports between
* clients. That is what a connection is for.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_port_tie (jack_port_t *src, jack_port_t *dst);

/**
* This undoes the effect of jack_port_tie(). The port
* should be same as the 'destination' port passed to
* jack_port_tie().
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_port_untie (jack_port_t *port);

/**
* A client may call this function to prevent other objects
* from changing the connection status of a port. The port
* must be owned by the calling client.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_port_lock (jack_client_t *, jack_port_t *);

/**
* This allows other objects to change the connection status of a port.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_port_unlock (jack_client_t *, jack_port_t *);

/**
* @return the time (in frames) between data being available or
* delivered at/to a port, and the time at which it arrived at or is
* delivered to the "other side" of the port. E.g. for a physical
* audio output port, this is the time between writing to the port and
* when the signal will leave the connector. For a physical audio
* input port, this is the time between the sound arriving at the
* connector and the corresponding frames being readable from the
* port.
*/
jack_nframes_t jack_port_get_latency (jack_port_t *port);

/**
* The maximum of the sum of the latencies in every
* connection path that can be drawn between the port and other
* ports with the @ref JackPortIsTerminal flag set.
*/
jack_nframes_t jack_port_get_total_latency (jack_client_t *,
jack_port_t *port);

/**
* The port latency is zero by default. Clients that control
* physical hardware with non-zero latency should call this
* to set the latency to its correct value. Note that the value
* should include any systemic latency present "outside" the
* physical hardware controlled by the client. For example,
* for a client controlling a digital audio interface connected
* to an external digital converter, the latency setting should
* include both buffering by the audio interface *and* the converter.
*/
void jack_port_set_latency (jack_port_t *, jack_nframes_t);

/**
*
*/
int jack_recompute_total_latencies (jack_client_t*);

/**
* Modify a port's short name. May be called at any time. If the
* resulting full name (including the @a "client_name:" prefix) is
* longer than jack_port_name_size(), it will be truncated.
*
* @return 0 on success, otherwise a non-zero error code.
*/
int jack_port_set_name (jack_port_t *port, const char *port_name);

/**
* If @ref JackPortCanMonitor is set for this @a port, turn input
* monitoring on or off. Otherwise, do nothing.
*/
int jack_port_request_monitor (jack_port_t *port, int onoff);

/**
* If @ref JackPortCanMonitor is set for this @a port_name, turn input
* monitoring on or off. Otherwise, do nothing.
*
* @return 0 on success, otherwise a non-zero error code.
*
* @see jack_port_name_size()
*/
int jack_port_request_monitor_by_name (jack_client_t *client,
const char *port_name, int onoff);

/**
* If @ref JackPortCanMonitor is set for a port, this function turns
* on input monitoring if it was off, and turns it off if only one
* request has been made to turn it on. Otherwise it does nothing.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_port_ensure_monitor (jack_port_t *port, int onoff);

/**
* @return TRUE if input monitoring has been requested for @a port.
*/
int jack_port_monitoring_input (jack_port_t *port);

/**
* Establish a connection between two ports.
*
* When a connection exists, data written to the source port will
* be available to be read at the destination port.
*
* @pre The port types must be identical.
*
* @pre The @ref JackPortFlags of the @a source_port must include @ref
* JackPortIsOutput.
*
* @pre The @ref JackPortFlags of the @a destination_port must include
* @ref JackPortIsInput.
*
* @return 0 on success, EEXIST if the connection is already made,
* otherwise a non-zero error code
*/
int jack_connect (jack_client_t *,
const char *source_port,
const char *destination_port);

/**
* Remove a connection between two ports.
*
* @pre The port types must be identical.
*
* @pre The @ref JackPortFlags of the @a source_port must include @ref
* JackPortIsOutput.
*
* @pre The @ref JackPortFlags of the @a destination_port must include
* @ref JackPortIsInput.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_disconnect (jack_client_t *,
const char *source_port,
const char *destination_port);

/**
* Perform the same function as jack_disconnect() using port handles
* rather than names. This avoids the name lookup inherent in the
* name-based version.
*
* Clients connecting their own ports are likely to use this function,
* while generic connection clients (e.g. patchbays) would use
* jack_disconnect().
*/
int jack_port_disconnect (jack_client_t *, jack_port_t *);

/**
* @return the maximum number of characters in a full JACK port name
* including the final NULL character. This value is a constant.
*
* A port's full name contains the owning client name concatenated
* with a colon (:) followed by its short name and a NULL
* character.
*/
int jack_port_name_size(void);

/**
* @return the maximum number of characters in a JACK port type name
* including the final NULL character. This value is a constant.
*/
int jack_port_type_size(void);

/**
* @return the sample rate of the jack system, as set by the user when
* jackd was started.
*/
jack_nframes_t jack_get_sample_rate (jack_client_t *);

/**
* @return the current maximum size that will ever be passed to the @a
* process_callback. It should only be used *before* the client has
* been activated. This size may change, clients that depend on it
* must register a @a bufsize_callback so they will be notified if it
* does.
*
* @see jack_set_buffer_size_callback()
*/
jack_nframes_t jack_get_buffer_size (jack_client_t *);

/**
* @param port_name_pattern A regular expression used to select
* ports by name. If NULL or of zero length, no selection based
* on name will be carried out.
* @param type_name_pattern A regular expression used to select
* ports by type. If NULL or of zero length, no selection based
* on type will be carried out.
* @param flags A value used to select ports by their flags.
* If zero, no selection based on flags will be carried out.
*
* @return a NULL-terminated array of ports that match the specified
* arguments. The caller is responsible for calling free(3) any
* non-NULL returned value.
*
* @see jack_port_name_size(), jack_port_type_size()
*/
const char ** jack_get_ports (jack_client_t *,
const char *port_name_pattern,
const char *type_name_pattern,
unsigned long flags);

/**
* @return address of the jack_port_t named @a port_name.
*
* @see jack_port_name_size()
*/
jack_port_t * jack_port_by_name (jack_client_t *, const char *port_name);

/**
* @return address of the jack_port_t of a @a port_id.
*/
jack_port_t * jack_port_by_id (jack_client_t *client,
jack_port_id_t port_id);

/**
* Old-style interface to become the timebase for the entire JACK
* subsystem.
*
* @deprecated This function still exists for compatibility with the
* earlier transport interface, but it does nothing. Instead, see
* transport.h and use jack_set_timebase_callback().
*
* @return ENOSYS, function not implemented.
*/
int jack_engine_takeover_timebase (jack_client_t *);

/**
* @return the time in frames that has passed since the JACK server
* began the current process cycle.
*/
jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *);

/**
* @return an estimate of the current time in frames. This is a
* running counter, no significance should be attached to its value,
* but it can be compared to a previously returned value.
*/
jack_nframes_t jack_frame_time (const jack_client_t *);

/**
* @return the frame_time after the last processing of the graph
* this is only to be used from the process callback.
*
* This function can be used to put timestamps generated by
* jack_frame_time() in correlation to the current process cycle.
*/
jack_nframes_t jack_last_frame_time (const jack_client_t *client);

/**
* @return the current CPU load estimated by JACK. This is a running
* average of the time it takes to execute a full process cycle for
* all clients as a percentage of the real time available per cycle
* determined by the buffer size and sample rate.
*/
float jack_cpu_load (jack_client_t *client);

/**
* @return the pthread ID of the thread running the JACK client side
* code.
*/
pthread_t jack_client_thread_id (jack_client_t *);

/**
* Display JACK error message.
*
* Set via jack_set_error_function(), otherwise a JACK-provided
* default will print @a msg (plus a newline) to stderr.
*
* @param msg error message text (no newline at end).
*/
extern void (*jack_error_callback)(const char *msg);

/**
* Set the @ref jack_error_callback for error message display.
*
* The JACK library provides two built-in callbacks for this purpose:
* default_jack_error_callback() and silent_jack_error_callback().
*/
void jack_set_error_function (void (*func)(const char *));

#ifdef __cplusplus
}
#endif

#endif /* __jack_h__ */

+ 287
- 0
common/jslist.h View File

@@ -0,0 +1,287 @@
/*
Based on gslist.c from glib-1.2.9 (LGPL).

Adaption to JACK, Copyright (C) 2002 Kai Vehmanen.
- replaced use of gtypes with normal ANSI C types
- glib's memory allocation routines replaced with
malloc/free calls

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#ifndef __jack_jslist_h__
#define __jack_jslist_h__

#include <stdlib.h>

#ifdef WIN32
#define __inline__ inline
#endif

typedef struct _JSList JSList;

typedef int (*JCompareFunc) (void* a, void* b);
struct _JSList
{
void *data;
JSList *next;
};

static __inline__
JSList*
jack_slist_alloc (void)
{
JSList *new_list;

new_list = (JSList*)malloc(sizeof(JSList));
new_list->data = NULL;
new_list->next = NULL;

return new_list;
}

static __inline__
JSList*
jack_slist_prepend (JSList* list, void* data)
{
JSList *new_list;

new_list = (JSList*)malloc(sizeof(JSList));
new_list->data = data;
new_list->next = list;

return new_list;
}

#define jack_slist_next(slist) ((slist) ? (((JSList *)(slist))->next) : NULL)
static __inline__
JSList*
jack_slist_last (JSList *list)
{
if (list) {
while (list->next)
list = list->next;
}

return list;
}

static __inline__
JSList*
jack_slist_remove_link (JSList *list,
JSList *link)
{
JSList *tmp;
JSList *prev;

prev = NULL;
tmp = list;

while (tmp) {
if (tmp == link) {
if (prev)
prev->next = tmp->next;
if (list == tmp)
list = list->next;

tmp->next = NULL;
break;
}

prev = tmp;
tmp = tmp->next;
}

return list;
}

static __inline__
void
jack_slist_free (JSList *list)
{
while (list) {
JSList *next = list->next;
free(list);
list = next;
}
}

static __inline__
void
jack_slist_free_1 (JSList *list)
{
if (list) {
free(list);
}
}

static __inline__
JSList*
jack_slist_remove (JSList *list,
void *data)
{
JSList *tmp;
JSList *prev;

prev = NULL;
tmp = list;

while (tmp) {
if (tmp->data == data) {
if (prev)
prev->next = tmp->next;
if (list == tmp)
list = list->next;

tmp->next = NULL;
jack_slist_free (tmp);

break;
}

prev = tmp;
tmp = tmp->next;
}

return list;
}

static __inline__
unsigned int
jack_slist_length (JSList *list)
{
unsigned int length;

length = 0;
while (list) {
length++;
list = list->next;
}

return length;
}

static __inline__
JSList*
jack_slist_find (JSList *list,
void *data)
{
while (list) {
if (list->data == data)
break;
list = list->next;
}

return list;
}

static __inline__
JSList*
jack_slist_copy (JSList *list)
{
JSList *new_list = NULL;

if (list) {
JSList *last;

new_list = jack_slist_alloc ();
new_list->data = list->data;
last = new_list;
list = list->next;
while (list) {
last->next = jack_slist_alloc ();
last = last->next;
last->data = list->data;
list = list->next;
}
}

return new_list;
}

static __inline__
JSList*
jack_slist_append (JSList *list,
void *data)
{
JSList *new_list;
JSList *last;

new_list = jack_slist_alloc ();
new_list->data = data;

if (list) {
last = jack_slist_last (list);
last->next = new_list;

return list;
} else
return new_list;
}

static __inline__
JSList*
jack_slist_sort_merge (JSList *l1,
JSList *l2,
JCompareFunc compare_func)
{
JSList list, *l;

l = &list;

while (l1 && l2) {
if (compare_func(l1->data, l2->data) < 0) {
l = l->next = l1;
l1 = l1->next;
} else {
l = l->next = l2;
l2 = l2->next;
}
}
l->next = l1 ? l1 : l2;

return list.next;
}

static __inline__
JSList*
jack_slist_sort (JSList *list,
JCompareFunc compare_func)
{
JSList *l1, *l2;

if (!list)
return NULL;
if (!list->next)
return list;

l1 = list;
l2 = list->next;

while ((l2 = l2->next) != NULL) {
if ((l2 = l2->next) == NULL)
break;
l1 = l1->next;
}
l2 = l1->next;
l1->next = NULL;

return jack_slist_sort_merge (jack_slist_sort (list, compare_func),
jack_slist_sort (l2, compare_func),
compare_func);
}

#endif /* __jack_jslist_h__ */

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save