465 lines
13 KiB
C++
465 lines
13 KiB
C++
/*
|
|
Copyright (C) 2001 Paul Davis
|
|
Copyright (C) 2004-2008 Grame
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the 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 "JackSystemDeps.h"
|
|
#include "JackServerGlobals.h"
|
|
#include "JackTime.h"
|
|
#include "JackFreewheelDriver.h"
|
|
#include "JackThreadedDriver.h"
|
|
#include "JackGlobals.h"
|
|
#include "JackLockedEngine.h"
|
|
#include "JackAudioDriver.h"
|
|
#include "JackChannel.h"
|
|
#include "JackClientControl.h"
|
|
#include "JackEngineControl.h"
|
|
#include "JackGraphManager.h"
|
|
#include "JackInternalClient.h"
|
|
#include "JackError.h"
|
|
#include "JackMessageBuffer.h"
|
|
|
|
const char * jack_get_self_connect_mode_description(char mode);
|
|
|
|
namespace Jack
|
|
{
|
|
|
|
//----------------
|
|
// Server control
|
|
//----------------
|
|
JackServer::JackServer(bool sync, bool temporary, int timeout, bool rt, int priority, int port_max, bool verbose, jack_timer_type_t clock, char self_connect_mode, const char* server_name)
|
|
{
|
|
if (rt) {
|
|
jack_info("JACK server starting in realtime mode with priority %ld", priority);
|
|
} else {
|
|
jack_info("JACK server starting in non-realtime mode");
|
|
}
|
|
|
|
jack_info("self-connect-mode is \"%s\"", jack_get_self_connect_mode_description(self_connect_mode));
|
|
|
|
fGraphManager = JackGraphManager::Allocate(port_max);
|
|
fMetadata = new JackMetadata(true);
|
|
fEngineControl = new JackEngineControl(sync, temporary, timeout, rt, priority, verbose, clock, server_name);
|
|
fEngine = new JackLockedEngine(fGraphManager, GetSynchroTable(), fEngineControl, self_connect_mode);
|
|
|
|
// A distinction is made between the threaded freewheel driver and the
|
|
// regular freewheel driver because the freewheel driver needs to run in
|
|
// threaded mode when freewheel mode is active and needs to run as a slave
|
|
// when freewheel mode isn't active.
|
|
JackFreewheelDriver* freewheelDriver = new JackFreewheelDriver(fEngine, GetSynchroTable());
|
|
fThreadedFreewheelDriver = new JackThreadedDriver(freewheelDriver);
|
|
|
|
fFreewheelDriver = freewheelDriver;
|
|
fDriverInfo = new JackDriverInfo();
|
|
fAudioDriver = NULL;
|
|
fFreewheel = false;
|
|
JackServerGlobals::fInstance = this; // Unique instance
|
|
JackServerGlobals::fUserCount = 1; // One user
|
|
JackGlobals::fVerbose = verbose;
|
|
}
|
|
|
|
JackServer::~JackServer()
|
|
{
|
|
JackGraphManager::Destroy(fGraphManager);
|
|
delete fMetadata;
|
|
delete fDriverInfo;
|
|
delete fThreadedFreewheelDriver;
|
|
delete fEngine;
|
|
delete fEngineControl;
|
|
}
|
|
|
|
int JackServer::Open(jack_driver_desc_t* driver_desc, JSList* driver_params)
|
|
{
|
|
// TODO: move that in reworked JackServerGlobals::Init()
|
|
if (!JackMessageBuffer::Create()) {
|
|
jack_error("Cannot create message buffer");
|
|
}
|
|
|
|
if ((fAudioDriver = fDriverInfo->Open(driver_desc, fEngine, GetSynchroTable(), driver_params)) == NULL) {
|
|
jack_error("Cannot initialize driver");
|
|
goto fail_close1;
|
|
}
|
|
|
|
if (fRequestChannel.Open(fEngineControl->fServerName, this) < 0) {
|
|
jack_error("Server channel open error");
|
|
goto fail_close2;
|
|
}
|
|
|
|
if (fEngine->Open() < 0) {
|
|
jack_error("Cannot open engine");
|
|
goto fail_close3;
|
|
}
|
|
|
|
if (fFreewheelDriver->Open() < 0) {
|
|
jack_error("Cannot open freewheel driver");
|
|
goto fail_close4;
|
|
}
|
|
|
|
if (fAudioDriver->Attach() < 0) {
|
|
jack_error("Cannot attach audio driver");
|
|
goto fail_close5;
|
|
}
|
|
|
|
fFreewheelDriver->SetMaster(false);
|
|
fAudioDriver->SetMaster(true);
|
|
fAudioDriver->AddSlave(fFreewheelDriver);
|
|
InitTime();
|
|
SetClockSource(fEngineControl->fClockSource);
|
|
return 0;
|
|
|
|
fail_close5:
|
|
fFreewheelDriver->Close();
|
|
|
|
fail_close4:
|
|
fEngine->Close();
|
|
|
|
fail_close3:
|
|
fRequestChannel.Close();
|
|
|
|
fail_close2:
|
|
fAudioDriver->Close();
|
|
|
|
fail_close1:
|
|
JackMessageBuffer::Destroy();
|
|
return -1;
|
|
}
|
|
|
|
int JackServer::Close()
|
|
{
|
|
jack_log("JackServer::Close");
|
|
fRequestChannel.Close();
|
|
fAudioDriver->Detach();
|
|
fAudioDriver->Close();
|
|
fFreewheelDriver->Close();
|
|
fEngine->Close();
|
|
// TODO: move that in reworked JackServerGlobals::Destroy()
|
|
JackMessageBuffer::Destroy();
|
|
EndTime();
|
|
return 0;
|
|
}
|
|
|
|
int JackServer::Start()
|
|
{
|
|
jack_log("JackServer::Start");
|
|
if (fAudioDriver->Start() < 0) {
|
|
return -1;
|
|
}
|
|
return fRequestChannel.Start();
|
|
}
|
|
|
|
int JackServer::Stop()
|
|
{
|
|
jack_log("JackServer::Stop");
|
|
int res = -1;
|
|
|
|
if (fFreewheel) {
|
|
if (fThreadedFreewheelDriver) {
|
|
res = fThreadedFreewheelDriver->Stop();
|
|
}
|
|
} else {
|
|
if (fAudioDriver) {
|
|
res = fAudioDriver->Stop();
|
|
}
|
|
}
|
|
|
|
fEngine->NotifyQuit();
|
|
fRequestChannel.Stop();
|
|
fEngine->NotifyFailure(JackFailure | JackServerError, JACK_SERVER_FAILURE);
|
|
|
|
return res;
|
|
}
|
|
|
|
bool JackServer::IsRunning()
|
|
{
|
|
jack_log("JackServer::IsRunning");
|
|
assert(fAudioDriver);
|
|
return fAudioDriver->IsRunning();
|
|
}
|
|
|
|
//------------------
|
|
// Internal clients
|
|
//------------------
|
|
|
|
int JackServer::InternalClientLoad1(const char* client_name, const char* so_name, const char* objet_data, int options, int* int_ref, jack_uuid_t uuid, int* status)
|
|
{
|
|
JackLoadableInternalClient* client = new JackLoadableInternalClient1(JackServerGlobals::fInstance, GetSynchroTable(), objet_data);
|
|
assert(client);
|
|
return InternalClientLoadAux(client, so_name, client_name, options, int_ref, uuid, status);
|
|
}
|
|
|
|
int JackServer::InternalClientLoad2(const char* client_name, const char* so_name, const JSList * parameters, int options, int* int_ref, jack_uuid_t uuid, int* status)
|
|
{
|
|
JackLoadableInternalClient* client = new JackLoadableInternalClient2(JackServerGlobals::fInstance, GetSynchroTable(), parameters);
|
|
assert(client);
|
|
return InternalClientLoadAux(client, so_name, client_name, options, int_ref, uuid, status);
|
|
}
|
|
|
|
int JackServer::InternalClientLoadAux(JackLoadableInternalClient* client, const char* so_name, const char* client_name, int options, int* int_ref, jack_uuid_t uuid, int* status)
|
|
{
|
|
// Clear status
|
|
*status = 0;
|
|
|
|
// Client object is internally kept in JackEngine
|
|
if ((client->Init(so_name) < 0) || (client->Open(JackTools::DefaultServerName(), client_name, uuid, (jack_options_t)options, (jack_status_t*)status) < 0)) {
|
|
delete client;
|
|
int my_status1 = *status | JackFailure;
|
|
*status = (jack_status_t)my_status1;
|
|
*int_ref = 0;
|
|
return -1;
|
|
} else {
|
|
*int_ref = client->GetClientControl()->fRefNum;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//---------------------------
|
|
// From request thread : API
|
|
//---------------------------
|
|
|
|
int JackServer::SetBufferSize(jack_nframes_t buffer_size)
|
|
{
|
|
jack_log("JackServer::SetBufferSize nframes = %ld", buffer_size);
|
|
jack_nframes_t current_buffer_size = fEngineControl->fBufferSize;
|
|
|
|
if (current_buffer_size == buffer_size) {
|
|
jack_log("SetBufferSize: requirement for new buffer size equals current value");
|
|
return 0;
|
|
}
|
|
|
|
if (fAudioDriver->IsFixedBufferSize()) {
|
|
jack_log("SetBufferSize: driver only supports a fixed buffer size");
|
|
return -1;
|
|
}
|
|
|
|
if (fAudioDriver->Stop() != 0) {
|
|
jack_error("Cannot stop audio driver");
|
|
return -1;
|
|
}
|
|
|
|
if (fAudioDriver->SetBufferSize(buffer_size) == 0) {
|
|
fEngine->NotifyBufferSize(buffer_size);
|
|
return fAudioDriver->Start();
|
|
} else { // Failure: try to restore current value
|
|
jack_error("Cannot SetBufferSize for audio driver, restore current value %ld", current_buffer_size);
|
|
fAudioDriver->SetBufferSize(current_buffer_size);
|
|
fAudioDriver->Start();
|
|
// SetBufferSize actually failed, so return an error...
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Freewheel mode is implemented by switching from the (audio [slaves] + freewheel) driver to the freewheel driver only:
|
|
|
|
- "global" connection state is saved
|
|
- all audio driver and slaves ports are deconnected, thus there is no more dependencies with the audio driver and slaves
|
|
- 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)
|
|
{
|
|
jack_log("JackServer::SetFreewheel is = %ld want = %ld", fFreewheel, onoff);
|
|
|
|
if (fFreewheel) {
|
|
if (onoff) {
|
|
return -1;
|
|
} else {
|
|
fFreewheel = false;
|
|
fThreadedFreewheelDriver->Stop();
|
|
fGraphManager->Restore(&fConnectionState); // Restore connection state
|
|
fEngine->NotifyFreewheel(onoff);
|
|
fFreewheelDriver->SetMaster(false);
|
|
fAudioDriver->SetMaster(true);
|
|
return fAudioDriver->Start();
|
|
}
|
|
} else {
|
|
if (onoff) {
|
|
fFreewheel = true;
|
|
fAudioDriver->Stop();
|
|
fGraphManager->Save(&fConnectionState); // Save connection state
|
|
// Disconnect all slaves
|
|
std::list<JackDriverInterface*> slave_list = fAudioDriver->GetSlaves();
|
|
std::list<JackDriverInterface*>::const_iterator it;
|
|
for (it = slave_list.begin(); it != slave_list.end(); it++) {
|
|
JackDriver* slave = dynamic_cast<JackDriver*>(*it);
|
|
assert(slave);
|
|
fGraphManager->DisconnectAllPorts(slave->GetClientControl()->fRefNum);
|
|
}
|
|
// Disconnect master
|
|
fGraphManager->DisconnectAllPorts(fAudioDriver->GetClientControl()->fRefNum);
|
|
fEngine->NotifyFreewheel(onoff);
|
|
fAudioDriver->SetMaster(false);
|
|
fFreewheelDriver->SetMaster(true);
|
|
return fThreadedFreewheelDriver->Start();
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------
|
|
// Coming from the RT thread
|
|
//---------------------------
|
|
|
|
void JackServer::Notify(int refnum, int notify, int value)
|
|
{
|
|
switch (notify) {
|
|
|
|
case kGraphOrderCallback:
|
|
fEngine->NotifyGraphReorder();
|
|
break;
|
|
|
|
case kXRunCallback:
|
|
fEngine->NotifyClientXRun(refnum);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//--------------------
|
|
// Backend management
|
|
//--------------------
|
|
|
|
JackDriverInfo* JackServer::AddSlave(jack_driver_desc_t* driver_desc, JSList* driver_params)
|
|
{
|
|
JackDriverInfo* info = new JackDriverInfo();
|
|
JackDriverClientInterface* slave = info->Open(driver_desc, fEngine, GetSynchroTable(), driver_params);
|
|
|
|
if (!slave) {
|
|
goto error1;
|
|
}
|
|
if (slave->Attach() < 0) {
|
|
goto error2;
|
|
}
|
|
|
|
slave->SetMaster(false);
|
|
fAudioDriver->AddSlave(slave);
|
|
return info;
|
|
|
|
error2:
|
|
slave->Close();
|
|
|
|
error1:
|
|
delete info;
|
|
return NULL;
|
|
}
|
|
|
|
void JackServer::RemoveSlave(JackDriverInfo* info)
|
|
{
|
|
JackDriverClientInterface* slave = info->GetBackend();
|
|
fAudioDriver->RemoveSlave(slave);
|
|
slave->Detach();
|
|
slave->Close();
|
|
}
|
|
|
|
int JackServer::SwitchMaster(jack_driver_desc_t* driver_desc, JSList* driver_params)
|
|
{
|
|
std::list<JackDriverInterface*> slave_list;
|
|
std::list<JackDriverInterface*>::const_iterator it;
|
|
|
|
// Remove current master
|
|
fAudioDriver->Stop();
|
|
fAudioDriver->Detach();
|
|
fAudioDriver->Close();
|
|
|
|
// Open new master
|
|
JackDriverInfo* info = new JackDriverInfo();
|
|
JackDriverClientInterface* master = info->Open(driver_desc, fEngine, GetSynchroTable(), driver_params);
|
|
|
|
if (!master) {
|
|
goto error;
|
|
}
|
|
|
|
// Get slaves list
|
|
slave_list = fAudioDriver->GetSlaves();
|
|
|
|
// Move slaves in new master
|
|
for (it = slave_list.begin(); it != slave_list.end(); it++) {
|
|
JackDriverInterface* slave = *it;
|
|
master->AddSlave(slave);
|
|
}
|
|
|
|
// Delete old master
|
|
delete fDriverInfo;
|
|
|
|
// Activate master
|
|
fAudioDriver = master;
|
|
fDriverInfo = info;
|
|
|
|
if (fAudioDriver->Attach() < 0) {
|
|
goto error;
|
|
}
|
|
|
|
// Notify clients of new values
|
|
fEngine->NotifyBufferSize(fEngineControl->fBufferSize);
|
|
fEngine->NotifySampleRate(fEngineControl->fSampleRate);
|
|
|
|
// And finally start
|
|
fAudioDriver->SetMaster(true);
|
|
return fAudioDriver->Start();
|
|
|
|
error:
|
|
delete info;
|
|
return -1;
|
|
}
|
|
|
|
//----------------------
|
|
// Transport management
|
|
//----------------------
|
|
|
|
int JackServer::ReleaseTimebase(int refnum)
|
|
{
|
|
return fEngineControl->fTransport.ResetTimebase(refnum);
|
|
}
|
|
|
|
int JackServer::SetTimebaseCallback(int refnum, int conditional)
|
|
{
|
|
return fEngineControl->fTransport.SetTimebaseMaster(refnum, conditional);
|
|
}
|
|
|
|
JackLockedEngine* JackServer::GetEngine()
|
|
{
|
|
return fEngine;
|
|
}
|
|
|
|
JackSynchro* JackServer::GetSynchroTable()
|
|
{
|
|
return fSynchroTable;
|
|
}
|
|
|
|
JackEngineControl* JackServer::GetEngineControl()
|
|
{
|
|
return fEngineControl;
|
|
}
|
|
|
|
JackGraphManager* JackServer::GetGraphManager()
|
|
{
|
|
return fGraphManager;
|
|
}
|
|
|
|
JackMetadata* JackServer::GetMetadata()
|
|
{
|
|
return fMetadata;
|
|
}
|
|
|
|
} // end of namespace
|
|
|