git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@1195 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.58
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 |
@@ -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 |
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 | |||||
@@ -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 | |||||
@@ -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 |
@@ -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 | |||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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; | |||||
} | |||||
@@ -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 | |||||
@@ -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 |
@@ -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 |
@@ -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 | |||||
@@ -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 | |||||
@@ -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 |
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
} | |||||
@@ -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 |
@@ -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 | |||||
@@ -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 |
@@ -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 |
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 | |||||
@@ -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 |
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 |
@@ -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 | |||||
@@ -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; | |||||
} | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
*/ | |||||
@@ -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 |
@@ -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 |
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 |
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 |
@@ -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 | |||||
@@ -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 |
@@ -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; | |||||
} | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 |
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 | |||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 | |||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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__ */ |
@@ -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__ */ | |||||
@@ -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__ */ | |||||
@@ -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__ */ |
@@ -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__ */ |
@@ -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__ */ |