diff options
Diffstat (limited to 'common/JackServer.cpp')
-rw-r--r-- | common/JackServer.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/common/JackServer.cpp b/common/JackServer.cpp new file mode 100644 index 00000000..269b41fd --- /dev/null +++ b/common/JackServer.cpp @@ -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 + |