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