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