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__ */ |