git-svn-id: http://subversion.jackaudio.org/jack/jack2/branches/libjacknet@3529 0c269be4-1314-0410-8aa9-9f06e86f4224tags/1.9.8
@@ -18,50 +18,71 @@ Fernando Lopez-Lezcano | |||
Romain Moret | |||
Florian Faber | |||
Michael Voigt | |||
Torben Hohn | |||
Torben Hohn | |||
Paul Davis | |||
--------------------------- | |||
Jackdmp changes log | |||
--------------------------- | |||
2009-04-08 Stephane Letz <letz@grame.fr> | |||
--------------------------- | |||
* Native MIDI backend (JackCoreMidiDriver, JackWinMMEDriver) in progress. | |||
2009-05-06 Stephane Letz <letz@grame.fr> | |||
* Fix transport callback (timebase master, sync) issue when used after jack_activate (RT thread was not running). | |||
2009-04-03 Stephane Letz <letz@grame.fr> | |||
2009-05-05 Stephane Letz <letz@grame.fr> | |||
* First working version of native MIDI backend (JackCoreMidiDriver, JackWinMMEDriver). | |||
* Simplify JackClient RT code, jack_thread_wait API marked deprecated." | |||
2009-04-22 Stephane Letz <letz@grame.fr> | |||
* jackctl_server_load_master renamed to jackctl_server_switch_master, jackctl_server_unload_master is removed. | |||
2009-04-21 Stephane Letz <letz@grame.fr> | |||
* Add jackctl_server_load_master/jackctl_server_unload_master API. | |||
2009-04-20 Stephane Letz <letz@grame.fr> | |||
* In ALSA audio card reservation code, tries to open the card even if reservation fails. | |||
* Clock source setting on Linux. | |||
2009-04-08 Stephane Letz <letz@grame.fr> | |||
* Native MIDI backend (JackCoreMidiDriver, JackWinMMEDriver) in progress. | |||
2009-04-03 Stephane Letz <letz@grame.fr> | |||
* Simplify JackClient RT code, jack_thread_wait API marked deprecated." | |||
2009-03-29 Stephane Letz <letz@grame.fr> | |||
* Cleanup JackInternalClient code. | |||
* Cleanup JackInternalClient code. | |||
2009-03-27 Stephane Letz <letz@grame.fr> | |||
* Add a buffer size callback for netmaster that just remove the client (it will be recreated with the new parameters). | |||
* Add a buffer size callback for netmaster that just remove the client (it will be recreated with the new parameters). | |||
2009-03-26 Stephane Letz <letz@grame.fr> | |||
* First working JackBoomerDriver two threads version. | |||
* First working JackBoomerDriver two threads version. | |||
2009-03-24 Stephane Letz <letz@grame.fr> | |||
* New JackBoomerDriver class for Boomer driver on Solaris. | |||
* New JackBoomerDriver class for Boomer driver on Solaris. | |||
* Add mixed 32/64 bits mode (off by default). | |||
2009-03-23 Stephane Letz <letz@grame.fr> | |||
* Version 1.9.3 started. | |||
2009-03-19 Stephane Letz <letz@grame.fr> | |||
* Tim Blechmann optimization patch (inlining some heavy used methods). | |||
2009-03-12 Stephane Letz <letz@grame.fr> | |||
* Virtualize and allow overriding of thread creation function, to allow Wine support (from JACK1). | |||
2009-03-12 Stephane Letz <letz@grame.fr> | |||
* Try automatic adaptative mode in adapters. | |||
@@ -843,7 +843,7 @@ EXPORT jack_nframes_t jack_thread_wait(jack_client_t* ext_client, int status) | |||
return 0; | |||
} else { | |||
jack_error("jack_thread_wait: deprecated, use jack_cycle_wait/jack_cycle_signal"); | |||
return -1; | |||
return 0; | |||
} | |||
} | |||
@@ -118,14 +118,13 @@ void JackClient::SetupDriverSync(bool freewheel) | |||
{ | |||
if (!freewheel && !GetEngineControl()->fSyncMode) { | |||
jack_log("JackClient::SetupDriverSync driver sem in flush mode"); | |||
fSynchroTable[AUDIO_DRIVER_REFNUM].SetFlush(true); | |||
fSynchroTable[FREEWHEEL_DRIVER_REFNUM].SetFlush(true); | |||
fSynchroTable[MIDI_DRIVER_REFNUM].SetFlush(true); | |||
for (int i = 0; i < GetEngineControl()->fDriverNum; i++) { | |||
fSynchroTable[i].SetFlush(true); | |||
} | |||
} else { | |||
jack_log("JackClient::SetupDriverSync driver sem in normal mode"); | |||
fSynchroTable[AUDIO_DRIVER_REFNUM].SetFlush(false); | |||
fSynchroTable[FREEWHEEL_DRIVER_REFNUM].SetFlush(false); | |||
fSynchroTable[MIDI_DRIVER_REFNUM].SetFlush(false); | |||
for (int i = 0; i < GetEngineControl()->fDriverNum; i++) | |||
fSynchroTable[i].SetFlush(false); | |||
} | |||
} | |||
@@ -338,7 +337,7 @@ int JackClient::StartThread() | |||
// Will do "something" on OSX only... | |||
fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint); | |||
if (fThread.Start() < 0) { | |||
if (fThread.StartSync() < 0) { | |||
jack_error("Start thread error"); | |||
return -1; | |||
} | |||
@@ -593,6 +592,27 @@ void JackClient::ShutDown() | |||
// Transport management | |||
//---------------------- | |||
inline int JackClient::ActivateAux() | |||
{ | |||
// If activated without RT thread... | |||
if (IsActive() && fThread.GetStatus() != JackThread::kRunning) { | |||
jack_log("ActivateAux"); | |||
// RT thread is started | |||
if (StartThread() < 0) | |||
return -1; | |||
int result = -1; | |||
GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime(); | |||
fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result); | |||
return result; | |||
} else { | |||
return 0; | |||
} | |||
} | |||
int JackClient::ReleaseTimebase() | |||
{ | |||
int result = -1; | |||
@@ -611,29 +631,30 @@ int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg) | |||
GetClientControl()->fTransportSync = (fSync != NULL); | |||
fSyncArg = arg; | |||
fSync = sync_callback; | |||
return 0; | |||
} | |||
int JackClient::SetSyncTimeout(jack_time_t timeout) | |||
{ | |||
GetEngineControl()->fTransport.SetSyncTimeout(timeout); | |||
return 0; | |||
return ActivateAux(); | |||
} | |||
int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg) | |||
{ | |||
int result = -1; | |||
fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result); | |||
jack_log("SetTimebaseCallback result = %ld", result); | |||
if (result == 0) { | |||
GetClientControl()->fTransportTimebase = true; | |||
fTimebase = timebase_callback; | |||
fTimebaseArg = arg; | |||
return ActivateAux(); | |||
} else { | |||
fTimebase = NULL; | |||
fTimebaseArg = NULL; | |||
return -1; | |||
} | |||
return result; | |||
} | |||
int JackClient::SetSyncTimeout(jack_time_t timeout) | |||
{ | |||
GetEngineControl()->fTransport.SetSyncTimeout(timeout); | |||
return 0; | |||
} | |||
// Must be RT safe | |||
@@ -108,6 +108,7 @@ class JackClient : public JackClientInterface, public JackRunnableInterface | |||
inline void CycleSignalAux(int status); | |||
inline void CallSyncCallbackAux(); | |||
inline void CallTimebaseCallbackAux(); | |||
inline int ActivateAux(); | |||
public: | |||
@@ -17,11 +17,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#include <iostream> | |||
#include <assert.h> | |||
#include "JackConnectionManager.h" | |||
#include "JackClientControl.h" | |||
#include "JackEngineControl.h" | |||
#include "JackGlobals.h" | |||
#include "JackError.h" | |||
#include <iostream> | |||
#include <assert.h> | |||
namespace Jack | |||
{ | |||
@@ -54,12 +56,7 @@ bool JackConnectionManager::IsLoopPathAux(int ref1, int ref2) const | |||
{ | |||
jack_log("JackConnectionManager::IsLoopPathAux ref1 = %ld ref2 = %ld", ref1, ref2); | |||
if (ref1 == AUDIO_DRIVER_REFNUM // Driver is reached | |||
|| ref2 == AUDIO_DRIVER_REFNUM | |||
|| ref1 == FREEWHEEL_DRIVER_REFNUM | |||
|| ref2 == FREEWHEEL_DRIVER_REFNUM | |||
|| ref1 == MIDI_DRIVER_REFNUM | |||
|| ref2 == MIDI_DRIVER_REFNUM) { | |||
if (ref1 < GetEngineControl()->fDriverNum || ref2 < GetEngineControl()->fDriverNum) { | |||
return false; | |||
} else if (ref1 == ref2) { // Same refnum | |||
return true; | |||
@@ -49,8 +49,6 @@ | |||
#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 MIDI_DRIVER_REFNUM 2 // Loopback driver is initialized third, it will get the refnum 2 | |||
#define REAL_REFNUM MIDI_DRIVER_REFNUM + 1 // Real clients start at MIDI_DRIVER_REFNUM + 1 | |||
#define JACK_DEFAULT_SERVER_NAME "default" | |||
@@ -81,6 +81,10 @@ struct jackctl_server | |||
/* uint32_t, ports of the loopback driver */ | |||
union jackctl_parameter_value loopback_ports; | |||
union jackctl_parameter_value default_loopback_ports; | |||
/* uint32_t, clock source type */ | |||
union jackctl_parameter_value clock_source; | |||
union jackctl_parameter_value default_clock_source; | |||
/* bool */ | |||
union jackctl_parameter_value replace_registry; | |||
@@ -733,6 +737,20 @@ EXPORT jackctl_server_t * jackctl_server_create() | |||
{ | |||
goto fail_free_parameters; | |||
} | |||
value.ui = 0; | |||
if (jackctl_add_parameter( | |||
&server_ptr->parameters, | |||
"clock-source", | |||
"Clocksource type : c(ycle) | h(pet) | s(ystem)", | |||
"", | |||
JackParamUInt, | |||
&server_ptr->clock_source, | |||
&server_ptr->default_clock_source, | |||
value) == NULL) | |||
{ | |||
goto fail_free_parameters; | |||
} | |||
value.b = false; | |||
if (jackctl_add_parameter( | |||
@@ -864,6 +882,7 @@ jackctl_server_start( | |||
server_ptr->realtime_priority.i, | |||
server_ptr->loopback_ports.ui, | |||
server_ptr->verbose.b, | |||
(jack_timer_type_t)server_ptr->clock_source.ui, | |||
server_ptr->name.str); | |||
if (server_ptr->engine == NULL) | |||
{ | |||
@@ -1163,7 +1182,7 @@ EXPORT bool jackctl_server_unload_internal( | |||
} | |||
} | |||
EXPORT bool jackctl_server_load_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) | |||
EXPORT bool jackctl_server_add_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) | |||
{ | |||
if (server_ptr->engine != NULL) { | |||
driver_ptr->info = server_ptr->engine->AddSlave(driver_ptr->desc_ptr, driver_ptr->set_parameters); | |||
@@ -1173,13 +1192,23 @@ EXPORT bool jackctl_server_load_slave(jackctl_server * server_ptr, jackctl_drive | |||
} | |||
} | |||
EXPORT bool jackctl_server_unload_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) | |||
EXPORT bool jackctl_server_remove_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) | |||
{ | |||
if (server_ptr->engine != NULL) { | |||
server_ptr->engine->RemoveSlave(driver_ptr->info); | |||
delete driver_ptr->info; | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
EXPORT bool jackctl_server_switch_master(jackctl_server * server_ptr, jackctl_driver * driver_ptr) | |||
{ | |||
if (server_ptr->engine != NULL) { | |||
return (server_ptr->engine->SwitchMaster(driver_ptr->desc_ptr, driver_ptr->set_parameters) == 0); | |||
} else { | |||
return false; | |||
} | |||
} | |||
@@ -220,12 +220,15 @@ EXPORT bool jackctl_server_unload_internal( | |||
jackctl_server * server, | |||
jackctl_internal * internal); | |||
EXPORT bool jackctl_server_load_slave(jackctl_server_t * server, | |||
EXPORT bool jackctl_server_add_slave(jackctl_server_t * server, | |||
jackctl_driver_t * driver); | |||
EXPORT bool jackctl_server_unload_slave(jackctl_server_t * server, | |||
EXPORT bool jackctl_server_remove_slave(jackctl_server_t * server, | |||
jackctl_driver_t * driver); | |||
EXPORT bool | |||
jackctl_server_switch_master(jackctl_server_t * server, | |||
jackctl_driver_t * driver); | |||
#if 0 | |||
{ /* Adjust editor indent */ | |||
@@ -47,7 +47,7 @@ JackDriver::JackDriver(const char* name, const char* alias, JackLockedEngine* en | |||
fBeginDateUst = 0; | |||
fDelayedUsecs = 0.f; | |||
fIsMaster = true; | |||
} | |||
} | |||
JackDriver::JackDriver() | |||
{ | |||
@@ -74,6 +74,7 @@ int JackDriver::Open() | |||
fClientControl.fRefNum = refnum; | |||
fClientControl.fActive = true; | |||
fEngineControl->fDriverNum++; | |||
fGraphManager->DirectConnect(fClientControl.fRefNum, fClientControl.fRefNum); // Connect driver to itself for "sync" mode | |||
SetupDriverSync(fClientControl.fRefNum, false); | |||
return 0; | |||
@@ -100,6 +101,7 @@ int JackDriver::Open (bool capturing, | |||
fClientControl.fRefNum = refnum; | |||
fClientControl.fActive = true; | |||
fEngineControl->fDriverNum++; | |||
fCaptureLatency = capture_latency; | |||
fPlaybackLatency = playback_latency; | |||
@@ -113,7 +115,6 @@ int JackDriver::Open (bool capturing, | |||
if (!fEngineControl->fTimeOut) | |||
fEngineControl->fTimeOutUsecs = jack_time_t(2.f * fEngineControl->fPeriodUsecs); | |||
//fGraphManager->SetBufferSize(fEngineControl->fBufferSize); | |||
fGraphManager->DirectConnect(fClientControl.fRefNum, fClientControl.fRefNum); // Connect driver to itself for "sync" mode | |||
SetupDriverSync(fClientControl.fRefNum, false); | |||
return 0; | |||
@@ -142,6 +143,7 @@ int JackDriver::Open(jack_nframes_t buffer_size, | |||
fClientControl.fRefNum = refnum; | |||
fClientControl.fActive = true; | |||
fEngineControl->fDriverNum++; | |||
fEngineControl->fBufferSize = buffer_size; | |||
fEngineControl->fSampleRate = samplerate; | |||
fCaptureLatency = capture_latency; | |||
@@ -168,6 +170,7 @@ int JackDriver::Close() | |||
jack_log("JackDriver::Close"); | |||
fGraphManager->DirectDisconnect(fClientControl.fRefNum, fClientControl.fRefNum); // Disconnect driver from itself for sync | |||
fClientControl.fActive = false; | |||
fEngineControl->fDriverNum--; | |||
return fEngine->ClientInternalClose(fClientControl.fRefNum, false); | |||
} | |||
@@ -93,6 +93,7 @@ class SERVER_EXPORT JackDriverInterface | |||
virtual bool GetMaster() = 0; | |||
virtual void AddSlave(JackDriverInterface* slave) = 0; | |||
virtual void RemoveSlave(JackDriverInterface* slave) = 0; | |||
virtual std::list<JackDriverInterface*> GetSlaves() = 0; | |||
virtual int ProcessSlaves() = 0; | |||
virtual bool IsRealTime() const = 0; | |||
@@ -148,8 +149,13 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface | |||
void SetMaster(bool onoff); | |||
bool GetMaster(); | |||
void AddSlave(JackDriverInterface* slave); | |||
void RemoveSlave(JackDriverInterface* slave); | |||
std::list<JackDriverInterface*> GetSlaves() | |||
{ | |||
return fSlaveList; | |||
} | |||
int ProcessSlaves(); | |||
virtual int Open(); | |||
@@ -27,6 +27,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
#include "JackInternalClient.h" | |||
#include "JackEngineControl.h" | |||
#include "JackClientControl.h" | |||
#include "JackServerGlobals.h" | |||
#include "JackGlobals.h" | |||
#include "JackChannel.h" | |||
#include "JackError.h" | |||
@@ -71,7 +72,7 @@ int JackEngine::Close() | |||
fChannel.Close(); | |||
// Close remaining clients (RT is stopped) | |||
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { | |||
for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) { | |||
if (JackLoadableInternalClient* loadable_client = dynamic_cast<JackLoadableInternalClient*>(fClientTable[i])) { | |||
jack_log("JackEngine::Close loadable client = %s", loadable_client->GetClientControl()->fName); | |||
loadable_client->Close(); | |||
@@ -110,7 +111,7 @@ void JackEngine::ReleaseRefnum(int ref) | |||
if (fEngineControl->fTemporary) { | |||
int i; | |||
for (i = REAL_REFNUM; i < CLIENT_NUM; i++) { | |||
for (i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) { | |||
if (fClientTable[i]) | |||
break; | |||
} | |||
@@ -180,7 +181,7 @@ 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++) { | |||
for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) { | |||
JackClientInterface* client = fClientTable[i]; | |||
if (client && client->GetClientControl()->fActive) { | |||
JackClientTiming* timing = fGraphManager->GetClientTiming(i); | |||
@@ -236,6 +237,7 @@ void JackEngine::NotifyClients(int event, int sync, int value1, int value2) | |||
int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum) | |||
{ | |||
jack_log("JackEngine::NotifyAddClient: name = %s", name); | |||
// 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]; | |||
@@ -44,7 +44,7 @@ void JackEngineControl::CalcCPULoad(JackClientInterface** table, | |||
// In Asynchronous mode, last cycle end is the max of client end dates | |||
if (!fSyncMode) { | |||
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { | |||
for (int i = fDriverNum; i < CLIENT_NUM; i++) { | |||
JackClientInterface* client = table[i]; | |||
JackClientTiming* timing = manager->GetClientTiming(i); | |||
if (client && client->GetClientControl()->fActive && timing->fStatus == Finished) | |||
@@ -63,6 +63,8 @@ struct SERVER_EXPORT JackEngineControl : public JackShmMem | |||
int fMaxClientPriority; | |||
char fServerName[64]; | |||
JackTransportEngine fTransport; | |||
jack_timer_type_t fClockSource; | |||
int fDriverNum; | |||
bool fVerbose; | |||
// CPU Load | |||
@@ -88,7 +90,7 @@ struct SERVER_EXPORT JackEngineControl : public JackShmMem | |||
JackEngineProfiling fProfiler; | |||
#endif | |||
JackEngineControl(bool sync, bool temporary, long timeout, bool rt, long priority, bool verbose, const char* server_name) | |||
JackEngineControl(bool sync, bool temporary, long timeout, bool rt, long priority, bool verbose, jack_timer_type_t clock, const char* server_name) | |||
{ | |||
fBufferSize = 512; | |||
fSampleRate = 48000; | |||
@@ -113,7 +115,9 @@ struct SERVER_EXPORT JackEngineControl : public JackShmMem | |||
fConstraint = 0; | |||
fMaxDelayedUsecs = 0.f; | |||
fXrunDelayedUsecs = 0.f; | |||
} | |||
fClockSource = clock; | |||
fDriverNum = 0; | |||
} | |||
~JackEngineControl() | |||
{} | |||
@@ -20,7 +20,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
#include "JackEngineProfiling.h" | |||
#include "JackGraphManager.h" | |||
#include "JackClientControl.h" | |||
#include "JackEngineControl.h" | |||
#include "JackClientInterface.h" | |||
#include "JackGlobals.h" | |||
#include "JackTime.h" | |||
namespace Jack | |||
@@ -312,7 +314,7 @@ void JackEngineProfiling::Profile(JackClientInterface** table, | |||
fProfileTable[fAudioCycle].fPrevCycleEnd = prev_cycle_end; | |||
fProfileTable[fAudioCycle].fAudioCycle = fAudioCycle; | |||
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { | |||
for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) { | |||
JackClientInterface* client = table[i]; | |||
JackClientTiming* timing = manager->GetClientTiming(i); | |||
if (client && client->GetClientControl()->fActive && client->GetClientControl()->fCallback[kRealTimeCallback]) { | |||
@@ -115,6 +115,7 @@ int JackLibClient::Open(const char* server_name, const char* name, jack_options_ | |||
JackGlobals::fClientTable[GetClientControl()->fRefNum] = this; | |||
JackGlobals::fServerRunning = true; | |||
SetClockSource(GetEngineControl()->fClockSource); | |||
jack_log("JackLibClient::Open name = %s refnum = %ld", name_res, GetClientControl()->fRefNum); | |||
return 0; | |||
@@ -22,6 +22,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
#include "JackServerGlobals.h" | |||
#include "JackTime.h" | |||
#include "JackFreewheelDriver.h" | |||
#include "JackLoopbackDriver.h" | |||
#include "JackDummyDriver.h" | |||
#include "JackThreadedDriver.h" | |||
#include "JackGlobals.h" | |||
#include "JackLockedEngine.h" | |||
@@ -37,7 +39,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
namespace Jack | |||
{ | |||
JackServer::JackServer(bool sync, bool temporary, long timeout, bool rt, long priority, long loopback, bool verbose, const char* server_name) | |||
JackServer::JackServer(bool sync, bool temporary, long timeout, bool rt, long priority, long loopback, bool verbose, jack_timer_type_t clock, const char* server_name) | |||
{ | |||
if (rt) { | |||
jack_info("JACK server starting in realtime mode with priority %ld", priority); | |||
@@ -46,11 +48,13 @@ JackServer::JackServer(bool sync, bool temporary, long timeout, bool rt, long pr | |||
} | |||
fGraphManager = new JackGraphManager(); | |||
fEngineControl = new JackEngineControl(sync, temporary, timeout, rt, priority, verbose, server_name); | |||
fEngineControl = new JackEngineControl(sync, temporary, timeout, rt, priority, verbose, clock, server_name); | |||
fEngine = new JackLockedEngine(fGraphManager, GetSynchroTable(), fEngineControl); | |||
fFreewheelDriver = new JackThreadedDriver(new JackFreewheelDriver(fEngine, GetSynchroTable())); | |||
fDriverInfo = new JackDriverInfo(); | |||
fAudioDriver = NULL; | |||
fFreewheel = false; | |||
fLoopback = loopback; | |||
JackServerGlobals::fInstance = this; // Unique instance | |||
JackServerGlobals::fUserCount = 1; // One user | |||
jack_verbose = verbose; | |||
@@ -60,6 +64,7 @@ JackServer::~JackServer() | |||
{ | |||
delete fGraphManager; | |||
delete fAudioDriver; | |||
delete fDriverInfo; | |||
delete fFreewheelDriver; | |||
delete fEngine; | |||
delete fEngineControl; | |||
@@ -80,7 +85,7 @@ int JackServer::Open(jack_driver_desc_t* driver_desc, JSList* driver_params) | |||
goto fail_close2; | |||
} | |||
if ((fAudioDriver = fDriverInfo.Open(driver_desc, fEngine, GetSynchroTable(), driver_params)) == NULL) { | |||
if ((fAudioDriver = fDriverInfo->Open(driver_desc, fEngine, GetSynchroTable(), driver_params)) == NULL) { | |||
jack_error("Cannot initialize driver"); | |||
goto fail_close3; | |||
} | |||
@@ -89,18 +94,19 @@ int JackServer::Open(jack_driver_desc_t* driver_desc, JSList* driver_params) | |||
jack_error("Cannot open 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); // After ??? | |||
InitTime(); | |||
SetClockSource(fEngineControl->fClockSource); | |||
return 0; | |||
fail_close5: | |||
fFreewheelDriver->Close(); | |||
@@ -287,23 +293,64 @@ void JackServer::ClientKill(int refnum) | |||
JackDriverInfo* JackServer::AddSlave(jack_driver_desc_t* driver_desc, JSList* driver_params) | |||
{ | |||
JackDriverInfo* info = new JackDriverInfo(); | |||
JackDriverClientInterface* backend = info->Open(driver_desc, fEngine, GetSynchroTable(), driver_params); | |||
if (backend == NULL) { | |||
JackDriverClientInterface* slave = info->Open(driver_desc, fEngine, GetSynchroTable(), driver_params); | |||
if (slave == NULL) { | |||
delete info; | |||
return NULL; | |||
} else { | |||
backend->Attach(); | |||
fAudioDriver->AddSlave(backend); | |||
slave->Attach(); | |||
fAudioDriver->AddSlave(slave); | |||
return info; | |||
} | |||
} | |||
void JackServer::RemoveSlave(JackDriverInfo* info) | |||
{ | |||
JackDriverClientInterface* backend = info->GetBackend(); | |||
fAudioDriver->RemoveSlave(info->GetBackend()); | |||
backend->Detach(); | |||
backend->Close(); | |||
JackDriverClientInterface* slave = info->GetBackend(); | |||
fAudioDriver->RemoveSlave(slave); | |||
slave->Detach(); | |||
slave->Close(); | |||
} | |||
int JackServer::SwitchMaster(jack_driver_desc_t* driver_desc, JSList* driver_params) | |||
{ | |||
/// 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 == NULL || info == NULL) { | |||
delete info; | |||
delete master; | |||
return -1; | |||
} else { | |||
// Get slaves list | |||
std::list<JackDriverInterface*> slave_list = fAudioDriver->GetSlaves(); | |||
std::list<JackDriverInterface*>::const_iterator it; | |||
// 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 fAudioDriver; | |||
delete fDriverInfo; | |||
// Activate master | |||
fAudioDriver = master; | |||
fDriverInfo = info; | |||
fEngineControl->InitFrameTime(); | |||
fAudioDriver->Attach(); | |||
fAudioDriver->SetMaster(true); | |||
return fAudioDriver->Start(); | |||
} | |||
} | |||
//---------------------- | |||
@@ -47,7 +47,7 @@ class SERVER_EXPORT JackServer | |||
private: | |||
JackDriverInfo fDriverInfo; | |||
JackDriverInfo* fDriverInfo; | |||
JackDriverClientInterface* fAudioDriver; | |||
JackDriverClientInterface* fFreewheelDriver; | |||
JackLockedEngine* fEngine; | |||
@@ -57,12 +57,13 @@ class SERVER_EXPORT JackServer | |||
JackConnectionManager fConnectionState; | |||
JackSynchro fSynchroTable[CLIENT_NUM]; | |||
bool fFreewheel; | |||
long fLoopback; | |||
int InternalClientLoadAux(JackLoadableInternalClient* client, const char* so_name, const char* client_name, int options, int* int_ref, int* status); | |||
public: | |||
JackServer(bool sync, bool temporary, long timeout, bool rt, long priority, long loopback, bool verbose, const char* server_name); | |||
JackServer(bool sync, bool temporary, long timeout, bool rt, long priority, long loopback, bool verbose, jack_timer_type_t clock, const char* server_name); | |||
~JackServer(); | |||
int Open(jack_driver_desc_t* driver_desc, JSList* driver_params); | |||
@@ -88,7 +89,8 @@ class SERVER_EXPORT JackServer | |||
// Backend management | |||
JackDriverInfo* AddSlave(jack_driver_desc_t* driver_desc, JSList* driver_params); | |||
void RemoveSlave(JackDriverInfo* info); | |||
int SwitchMaster(jack_driver_desc_t* driver_desc, JSList* driver_params); | |||
// Object access | |||
JackLockedEngine* GetEngine(); | |||
JackEngineControl* GetEngineControl(); | |||
@@ -40,10 +40,11 @@ int JackServerGlobals::Start(const char* server_name, | |||
int rt, | |||
int priority, | |||
int loopback, | |||
int verbose) | |||
int verbose, | |||
jack_timer_type_t clock) | |||
{ | |||
jack_log("Jackdmp: sync = %ld timeout = %ld rt = %ld priority = %ld verbose = %ld ", sync, time_out_ms, rt, priority, verbose); | |||
new JackServer(sync, temporary, time_out_ms, rt, priority, loopback, verbose, server_name); // Will setup fInstance and fUserCount globals | |||
new JackServer(sync, temporary, time_out_ms, rt, priority, loopback, verbose, clock, server_name); // Will setup fInstance and fUserCount globals | |||
int res = fInstance->Open(driver_desc, driver_params); | |||
return (res < 0) ? res : fInstance->Start(); | |||
} | |||
@@ -92,6 +93,7 @@ bool JackServerGlobals::Init() | |||
char buffer[255]; | |||
int argc = 0; | |||
char* argv[32]; | |||
jack_timer_type_t clock_source = JACK_TIMER_SYSTEM_CLOCK; | |||
// First user starts the server | |||
if (fUserCount++ == 0) { | |||
@@ -99,8 +101,9 @@ bool JackServerGlobals::Init() | |||
jack_log("JackServerGlobals Init"); | |||
jack_driver_desc_t* driver_desc; | |||
const char *options = "-ad:P:uvshVRL:STFl:t:mn:p:"; | |||
const char *options = "-ad:P:uvshVRL:STFl:t:mn:p:c:"; | |||
static struct option long_options[] = { | |||
{ "clock-source", 1, 0, 'c' }, | |||
{ "driver", 1, 0, 'd' }, | |||
{ "verbose", 0, 0, 'v' }, | |||
{ "help", 0, 0, 'h' }, | |||
@@ -155,6 +158,18 @@ bool JackServerGlobals::Init() | |||
(opt = getopt_long(argc, argv, options, long_options, &option_index)) != EOF) { | |||
switch (opt) { | |||
case 'c': | |||
if (tolower (optarg[0]) == 'h') { | |||
clock_source = JACK_TIMER_HPET; | |||
} else if (tolower (optarg[0]) == 'c') { | |||
clock_source = JACK_TIMER_CYCLE_COUNTER; | |||
} else if (tolower (optarg[0]) == 's') { | |||
clock_source = JACK_TIMER_SYSTEM_CLOCK; | |||
} else { | |||
jack_error("unknown option character %c", optopt); | |||
} | |||
break; | |||
case 'd': | |||
seen_driver = 1; | |||
@@ -281,7 +296,7 @@ bool JackServerGlobals::Init() | |||
free(argv[i]); | |||
} | |||
int res = Start(server_name, driver_desc, driver_params, sync, temporary, client_timeout, realtime, realtime_priority, loopback, verbose_aux); | |||
int res = Start(server_name, driver_desc, driver_params, sync, temporary, client_timeout, realtime, realtime_priority, loopback, verbose_aux, clock_source); | |||
if (res < 0) { | |||
jack_error("Cannot start server... exit"); | |||
Delete(); | |||
@@ -22,8 +22,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
#include "driver_interface.h" | |||
#include "JackDriverLoader.h" | |||
#include "JackCompilerDeps.h" | |||
#include "JackServer.h" | |||
#include <assert.h> | |||
namespace Jack | |||
{ | |||
@@ -44,7 +44,7 @@ struct SERVER_EXPORT JackServerGlobals | |||
static bool Init(); | |||
static void Destroy(); | |||
static int Start(const char* server_name, | |||
static int Start (const char* server_name, | |||
jack_driver_desc_t* driver_desc, | |||
JSList* driver_params, | |||
int sync, | |||
@@ -53,7 +53,8 @@ struct SERVER_EXPORT JackServerGlobals | |||
int rt, | |||
int priority, | |||
int loopback, | |||
int verbose); | |||
int verbose, | |||
jack_timer_type_t clock); | |||
static void Stop(); | |||
static void Delete(); | |||
}; | |||
@@ -132,6 +132,11 @@ int JackThreadedDriver::ProcessSlaves() | |||
return fDriver->ProcessSlaves(); | |||
} | |||
std::list<JackDriverInterface*> JackThreadedDriver::GetSlaves() | |||
{ | |||
return fDriver->GetSlaves(); | |||
} | |||
int JackThreadedDriver::ClientNotify(int refnum, const char* name, int notify, int sync, int value1, int value2) | |||
{ | |||
return fDriver->ClientNotify(refnum, name, notify, sync, value1, value2); | |||
@@ -91,6 +91,7 @@ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, publi | |||
virtual bool GetMaster(); | |||
virtual void AddSlave(JackDriverInterface* slave); | |||
virtual void RemoveSlave(JackDriverInterface* slave); | |||
virtual std::list<JackDriverInterface*> GetSlaves(); | |||
virtual int ProcessSlaves(); | |||
virtual int ClientNotify(int refnum, const char* name, int notify, int sync, int value1, int value2); | |||
@@ -23,15 +23,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
#include "types.h" | |||
#include "JackCompilerDeps.h" | |||
#include "JackTypes.h" | |||
#ifdef __cplusplus | |||
extern "C" | |||
{ | |||
#endif | |||
SERVER_EXPORT void InitTime(); | |||
SERVER_EXPORT jack_time_t GetMicroSeconds(); | |||
SERVER_EXPORT void JackSleep(long usec); | |||
SERVER_EXPORT void InitTime(); | |||
SERVER_EXPORT jack_time_t GetMicroSeconds(void); | |||
SERVER_EXPORT void JackSleep(long usec); | |||
SERVER_EXPORT void SetClockSource(jack_timer_type_t source); | |||
SERVER_EXPORT const char* ClockSourceName(jack_timer_type_t source); | |||
#ifdef __cplusplus | |||
} | |||
@@ -21,6 +21,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
#include "JackTransportEngine.h" | |||
#include "JackClientInterface.h" | |||
#include "JackClientControl.h" | |||
#include "JackEngineControl.h" | |||
#include "JackGlobals.h" | |||
#include "JackError.h" | |||
#include "JackTime.h" | |||
#include <assert.h> | |||
@@ -89,7 +91,7 @@ int JackTransportEngine::SetTimebaseMaster(int refnum, bool conditionnal) | |||
// RT | |||
bool JackTransportEngine::CheckAllRolling(JackClientInterface** table) | |||
{ | |||
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { | |||
for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) { | |||
JackClientInterface* client = table[i]; | |||
if (client && client->GetClientControl()->fTransportState != JackTransportRolling) { | |||
jack_log("CheckAllRolling ref = %ld is not rolling", i); | |||
@@ -103,7 +105,7 @@ bool JackTransportEngine::CheckAllRolling(JackClientInterface** table) | |||
// RT | |||
void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table) | |||
{ | |||
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { | |||
for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) { | |||
JackClientInterface* client = table[i]; | |||
if (client) { | |||
JackClientControl* control = client->GetClientControl(); | |||
@@ -119,7 +121,7 @@ void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table) | |||
// RT | |||
void JackTransportEngine::MakeAllStopping(JackClientInterface** table) | |||
{ | |||
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { | |||
for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) { | |||
JackClientInterface* client = table[i]; | |||
if (client) { | |||
JackClientControl* control = client->GetClientControl(); | |||
@@ -134,7 +136,7 @@ void JackTransportEngine::MakeAllStopping(JackClientInterface** table) | |||
// RT | |||
void JackTransportEngine::MakeAllLocating(JackClientInterface** table) | |||
{ | |||
for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) { | |||
for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) { | |||
JackClientInterface* client = table[i]; | |||
if (client) { | |||
JackClientControl* control = client->GetClientControl(); | |||
@@ -21,6 +21,8 @@ | |||
#ifndef __JackTypes__ | |||
#define __JackTypes__ | |||
#include "JackCompilerDeps.h" | |||
typedef unsigned short UInt16; | |||
#if __LP64__ | |||
typedef unsigned int UInt32; | |||
@@ -34,8 +36,11 @@ typedef signed long SInt32; | |||
typedef uint16_t jack_int_t; // Internal type for ports and refnum | |||
namespace Jack | |||
{ | |||
typedef enum { | |||
JACK_TIMER_SYSTEM_CLOCK, | |||
JACK_TIMER_CYCLE_COUNTER, | |||
JACK_TIMER_HPET, | |||
} jack_timer_type_t; | |||
typedef enum { | |||
NotTriggered, | |||
@@ -44,6 +49,4 @@ typedef enum { | |||
Finished, | |||
} jack_client_state_t; | |||
} | |||
#endif |
@@ -97,6 +97,9 @@ static void usage(FILE* file) | |||
" [ --timeout OR -t client-timeout-in-msecs ]\n" | |||
" [ --midi OR -X midi-driver ]\n" | |||
" [ --verbose OR -v ]\n" | |||
#ifdef __linux__ | |||
" [ --clocksource OR -c [ c(ycle) | h(pet) | s(ystem) ]\n" | |||
#endif | |||
" [ --replace-registry OR -r ]\n" | |||
" [ --silent OR -s ]\n" | |||
" [ --sync OR -S ]\n" | |||
@@ -156,8 +159,17 @@ int main(int argc, char* argv[]) | |||
const char* server_name = "default"; | |||
jackctl_driver_t * audio_driver_ctl; | |||
jackctl_driver_t * midi_driver_ctl; | |||
#ifdef __linux__ | |||
const char *options = "-ad:X:P:uvrshVRL:STFl:t:mn:p:c:"; | |||
#else | |||
const char *options = "-ad:X:P:uvrshVRL:STFl:t:mn:p:"; | |||
#endif | |||
struct option long_options[] = { | |||
#ifdef __linux__ | |||
{ "clock-source", 1, 0, 'c' }, | |||
#endif | |||
{ "audio-driver", 1, 0, 'd' }, | |||
{ "midi-driver", 1, 0, 'X' }, | |||
{ "verbose", 0, 0, 'v' }, | |||
@@ -177,6 +189,7 @@ int main(int argc, char* argv[]) | |||
{ "sync", 0, 0, 'S' }, | |||
{ 0, 0, 0, 0 } | |||
}; | |||
int i,opt = 0; | |||
int option_index = 0; | |||
bool seen_audio_driver = false; | |||
@@ -204,13 +217,33 @@ int main(int argc, char* argv[]) | |||
} | |||
server_parameters = jackctl_server_get_parameters(server_ctl); | |||
opterr = 0; | |||
while (!seen_audio_driver && | |||
(opt = getopt_long(argc, argv, options, | |||
long_options, &option_index)) != EOF) { | |||
switch (opt) { | |||
#ifdef __linux__ | |||
case 'c': | |||
param = jackctl_get_parameter(server_parameters, "clock-source"); | |||
if (param != NULL) { | |||
if (tolower (optarg[0]) == 'h') { | |||
value.ui = JACK_TIMER_HPET; | |||
jackctl_parameter_set_value(param, &value); | |||
} else if (tolower (optarg[0]) == 'c') { | |||
value.ui = JACK_TIMER_CYCLE_COUNTER; | |||
jackctl_parameter_set_value(param, &value); | |||
} else if (tolower (optarg[0]) == 's') { | |||
value.ui = JACK_TIMER_SYSTEM_CLOCK; | |||
jackctl_parameter_set_value(param, &value); | |||
} else { | |||
usage(stdout); | |||
goto fail_free; | |||
} | |||
} | |||
break; | |||
#endif | |||
case 'd': | |||
seen_audio_driver = true; | |||
audio_driver_name = optarg; | |||
@@ -382,7 +415,7 @@ int main(int argc, char* argv[]) | |||
goto fail_free; | |||
} | |||
jackctl_server_load_slave(server_ctl, midi_driver_ctl); | |||
jackctl_server_add_slave(server_ctl, midi_driver_ctl); | |||
} | |||
notify_server_start(server_name); | |||
@@ -510,13 +510,42 @@ jack_log( | |||
/* @} */ | |||
/** | |||
* Call this function to add a slave in the driver slave list. | |||
* | |||
* @param server server object handle | |||
* @param driver driver to add in the driver slave list. | |||
* | |||
* @return success status: true - success, false - fail | |||
*/ | |||
bool | |||
jackctl_server_add_slave(jackctl_server_t * server, | |||
jackctl_driver_t * driver); | |||
/** | |||
* Call this function to remove a slave from the driver slave list. | |||
* | |||
* @param server server object handle | |||
* @param driver driver to remove from the driver slave list. | |||
* | |||
* @return success status: true - success, false - fail | |||
*/ | |||
bool | |||
jackctl_server_load_slave(jackctl_server_t * server, | |||
jackctl_server_remove_slave(jackctl_server_t * server, | |||
jackctl_driver_t * driver); | |||
/** | |||
* Call this function to switch master driver. | |||
* | |||
* @param server server object handle | |||
* @param driver driver to switch to | |||
* | |||
* @return success status: true - success, false - fail | |||
*/ | |||
bool | |||
jackctl_server_unload_slave(jackctl_server_t * server, | |||
jackctl_server_switch_master(jackctl_server_t * server, | |||
jackctl_driver_t * driver); | |||
#if 0 | |||
{ /* Adjust editor indent */ | |||
@@ -131,6 +131,7 @@ def build(bld): | |||
'JackNetTool.cpp', | |||
'JackNetInterface.cpp', | |||
'JackArgParser.cpp', | |||
'JackDummyDriver.cpp', | |||
] | |||
if bld.env['IS_LINUX']: | |||
@@ -67,7 +67,7 @@ SERVER_EXPORT void* audio_acquire(int num) | |||
NULL, | |||
&error)) < 0) { | |||
jack_error ("Failed to acquire device: %s\n", error.message ? error.message : strerror(-e)); | |||
jack_error("Failed to acquire device name : %s error : %s", audio_name, (error.message ? error.message : strerror(-e))); | |||
return NULL; | |||
} | |||
@@ -89,5 +89,7 @@ SERVER_EXPORT void audio_release(void* dev) | |||
if (device) { | |||
jack_info("Release audio card"); | |||
rd_release(device); | |||
} else { | |||
jack_info("No audio card to release..."); | |||
} | |||
} |
@@ -263,6 +263,22 @@ jack_controller_stop_server( | |||
return TRUE; | |||
} | |||
bool | |||
jack_controller_switch_master( | |||
struct jack_controller * controller_ptr, | |||
void *dbus_call_context_ptr) | |||
{ | |||
if (!jackctl_server_switch_master( | |||
controller_ptr->server, | |||
controller_ptr->driver)) | |||
{ | |||
jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Failed to switch master"); | |||
return FALSE; | |||
} | |||
return TRUE; | |||
} | |||
void * | |||
jack_controller_create( | |||
DBusConnection *connection) | |||
@@ -105,6 +105,17 @@ jack_control_run_method( | |||
jack_controller_control_send_signal_server_stopped(); | |||
} | |||
} | |||
else if (strcmp (call->method_name, "SwitchMaster") == 0) | |||
{ | |||
if (!jack_controller_switch_master(controller_ptr, call)) | |||
{ | |||
jack_error ("Failed to switch master"); | |||
} | |||
else | |||
{ | |||
jack_controller_control_send_signal_server_stopped(); | |||
} | |||
} | |||
else if (strcmp (call->method_name, "GetLoad") == 0) | |||
{ | |||
if (!controller_ptr->started) | |||
@@ -252,6 +263,9 @@ JACK_DBUS_METHOD_ARGUMENTS_END | |||
JACK_DBUS_METHOD_ARGUMENTS_BEGIN(StopServer) | |||
JACK_DBUS_METHOD_ARGUMENTS_END | |||
JACK_DBUS_METHOD_ARGUMENTS_BEGIN(SwitchMaster) | |||
JACK_DBUS_METHOD_ARGUMENTS_END | |||
JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetLoad) | |||
JACK_DBUS_METHOD_ARGUMENT("load", "d", true) | |||
JACK_DBUS_METHOD_ARGUMENTS_END | |||
@@ -295,6 +309,7 @@ JACK_DBUS_METHODS_BEGIN | |||
JACK_DBUS_METHOD_DESCRIBE(IsStarted, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(StartServer, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(StopServer, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(SwitchMaster, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(GetLoad, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(GetXruns, NULL) | |||
JACK_DBUS_METHOD_DESCRIBE(GetSampleRate, NULL) | |||
@@ -81,6 +81,11 @@ jack_controller_stop_server( | |||
struct jack_controller *controller_ptr, | |||
void *dbus_call_context_ptr); | |||
bool | |||
jack_controller_switch_master( | |||
struct jack_controller *controller_ptr, | |||
void *dbus_call_context_ptr); | |||
bool | |||
jack_controller_select_driver( | |||
struct jack_controller *controller_ptr, | |||
@@ -109,6 +109,7 @@ def main(): | |||
print " status - check whether jack server is started, return value is 0 if runing and 1 otherwise" | |||
print " start - start jack server if not currently started" | |||
print " stop - stop jack server if currenly started" | |||
print " sm - switch master to currently selected driver" | |||
print " dl - get list of available drivers" | |||
print " dg - get currently selected driver" | |||
print " ds <driver> - select driver" | |||
@@ -155,6 +156,9 @@ def main(): | |||
elif arg == 'stop': | |||
print "--- stop" | |||
control_iface.StopServer() | |||
elif arg == 'sm': | |||
print "--- switch master driver" | |||
control_iface.SwitchMaster() | |||
elif arg == 'ism': | |||
if control_iface.IsManuallyActivated(): | |||
print "Manually activated" | |||
@@ -53,6 +53,24 @@ static jackctl_internal_t * jackctl_server_get_internal(jackctl_server_t *server | |||
return NULL; | |||
} | |||
static jackctl_parameter_t * | |||
jackctl_get_parameter( | |||
const JSList * parameters_list, | |||
const char * parameter_name) | |||
{ | |||
while (parameters_list) | |||
{ | |||
if (strcmp(jackctl_parameter_get_name((jackctl_parameter_t *)parameters_list->data), parameter_name) == 0) | |||
{ | |||
return (jackctl_parameter_t *)parameters_list->data; | |||
} | |||
parameters_list = jack_slist_next(parameters_list); | |||
} | |||
return NULL; | |||
} | |||
static void print_value(union jackctl_parameter_value value, jackctl_param_type_t type) | |||
{ | |||
switch (type) { | |||
@@ -152,6 +170,16 @@ int main(int argc, char *argv[]) | |||
server = jackctl_server_create(); | |||
parameters = jackctl_server_get_parameters(server); | |||
/* | |||
jackctl_parameter_t* param; | |||
union jackctl_parameter_value value; | |||
param = jackctl_get_parameter(parameters, "verbose"); | |||
if (param != NULL) { | |||
value.b = true; | |||
jackctl_parameter_set_value(param, &value); | |||
} | |||
*/ | |||
printf("\n========================== \n"); | |||
printf("List of server parameters \n"); | |||
printf("========================== \n"); | |||
@@ -182,7 +210,24 @@ int main(int argc, char *argv[]) | |||
jackctl_server_start(server, jackctl_server_get_driver(server, driver_name)); | |||
jackctl_server_load_internal(server, jackctl_server_get_internal(server, client_name)); | |||
/* | |||
// Switch master test | |||
jackctl_driver_t* master; | |||
usleep(5000000); | |||
printf("jackctl_server_load_master\n"); | |||
master = jackctl_server_get_driver(server, "coreaudio"); | |||
jackctl_server_switch_master(server, master); | |||
usleep(5000000); | |||
printf("jackctl_server_load_master\n"); | |||
master = jackctl_server_get_driver(server, "dummy"); | |||
jackctl_server_switch_master(server, master); | |||
*/ | |||
signals = jackctl_setup_signals(0); | |||
jackctl_wait_signals(signals); | |||
@@ -18,84 +18,249 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
*/ | |||
#if defined(HAVE_CONFIG_H) | |||
#include "config.h" | |||
#endif | |||
#include "JackConstants.h" | |||
#include "JackTime.h" | |||
#include "JackTypes.h" | |||
#include "JackError.h" | |||
#include "cycles.h" | |||
#include <stdint.h> | |||
#include <stdio.h> | |||
#include <sys/mman.h> | |||
#include <sys/time.h> | |||
#include <sys/types.h> | |||
#include <sys/stat.h> | |||
#include <fcntl.h> | |||
#include <errno.h> | |||
#include <string.h> | |||
#include <unistd.h> | |||
#include <stdlib.h> | |||
SERVER_EXPORT void JackSleep(long usec) | |||
static jack_time_t __jack_cpu_mhz = 0; | |||
jack_time_t (*_jack_get_microseconds)(void) = 0; | |||
#if defined(__gnu_linux__) && (defined(__i386__) || defined(__x86_64__)) | |||
#define HPET_SUPPORT | |||
#define HPET_MMAP_SIZE 1024 | |||
#define HPET_CAPS 0x000 | |||
#define HPET_PERIOD 0x004 | |||
#define HPET_COUNTER 0x0f0 | |||
#define HPET_CAPS_COUNTER_64BIT (1 << 13) | |||
#if defined(__x86_64__) | |||
typedef uint64_t hpet_counter_t; | |||
#else | |||
typedef uint32_t hpet_counter_t; | |||
#endif | |||
static int hpet_fd; | |||
static unsigned char *hpet_ptr; | |||
static uint32_t hpet_period; /* period length in femto secs */ | |||
static uint64_t hpet_offset = 0; | |||
static uint64_t hpet_wrap; | |||
static hpet_counter_t hpet_previous = 0; | |||
#endif /* defined(__gnu_linux__) && (__i386__ || __x86_64__) */ | |||
#ifdef HPET_SUPPORT | |||
static int jack_hpet_init () | |||
{ | |||
usleep(usec); | |||
uint32_t hpet_caps; | |||
hpet_fd = open("/dev/hpet", O_RDONLY); | |||
if (hpet_fd < 0) { | |||
jack_error ("This system has no accessible HPET device (%s)", strerror (errno)); | |||
return -1; | |||
} | |||
hpet_ptr = (unsigned char *) mmap(NULL, HPET_MMAP_SIZE, | |||
PROT_READ, MAP_SHARED, hpet_fd, 0); | |||
if (hpet_ptr == MAP_FAILED) { | |||
jack_error ("This system has no mappable HPET device (%s)", strerror (errno)); | |||
close (hpet_fd); | |||
return -1; | |||
} | |||
/* this assumes period to be constant. if needed, | |||
it can be moved to the clock access function | |||
*/ | |||
hpet_period = *((uint32_t *) (hpet_ptr + HPET_PERIOD)); | |||
hpet_caps = *((uint32_t *) (hpet_ptr + HPET_CAPS)); | |||
hpet_wrap = ((hpet_caps & HPET_CAPS_COUNTER_64BIT) && | |||
(sizeof(hpet_counter_t) == sizeof(uint64_t))) ? | |||
0 : ((uint64_t) 1 << 32); | |||
return 0; | |||
} | |||
static jack_time_t jack_get_microseconds_from_hpet (void) | |||
{ | |||
hpet_counter_t hpet_counter; | |||
long double hpet_time; | |||
hpet_counter = *((hpet_counter_t *) (hpet_ptr + HPET_COUNTER)); | |||
if (hpet_counter < hpet_previous) | |||
hpet_offset += hpet_wrap; | |||
hpet_previous = hpet_counter; | |||
hpet_time = (long double) (hpet_offset + hpet_counter) * | |||
(long double) hpet_period * (long double) 1e-9; | |||
return ((jack_time_t) (hpet_time + 0.5)); | |||
} | |||
#ifdef GETCYCLE_TIME | |||
#else | |||
static int jack_hpet_init () | |||
{ | |||
jack_error ("This version of JACK or this computer does not have HPET support.\n" | |||
"Please choose a different clock source."); | |||
return -1; | |||
} | |||
static jack_time_t jack_get_microseconds_from_hpet (void) | |||
{ | |||
/* never called */ | |||
return 0; | |||
} | |||
#endif /* HPET_SUPPORT */ | |||
static jack_time_t jack_get_microseconds_from_cycles (void) { | |||
return get_cycles() / __jack_cpu_mhz; | |||
} | |||
#include <stdio.h> | |||
#include "cycles.h" | |||
/* | |||
* This is another kludge. It looks CPU-dependent, but actually it | |||
* reflects the lack of standards for the Linux kernel formatting of | |||
* /proc/cpuinfo. | |||
*/ | |||
static jack_time_t __jack_cpu_mhz; | |||
static jack_time_t jack_get_mhz (void) | |||
{ | |||
FILE *f = fopen("/proc/cpuinfo", "r"); | |||
if (f == 0) | |||
{ | |||
perror("can't open /proc/cpuinfo\n"); | |||
exit(1); | |||
} | |||
static inline jack_time_t GetMhz(void) | |||
for ( ; ; ) | |||
{ | |||
FILE *f = fopen("/proc/cpuinfo", "r"); | |||
if (f == 0) { | |||
perror("can't open /proc/cpuinfo\n"); | |||
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); | |||
} | |||
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; | |||
} | |||
#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; | |||
} | |||
} | |||
} | |||
#define HAVE_CLOCK_GETTIME 1 | |||
#ifndef HAVE_CLOCK_GETTIME | |||
static jack_time_t jack_get_microseconds_from_system (void) | |||
{ | |||
jack_time_t jackTime; | |||
struct timeval tv; | |||
gettimeofday (&tv, NULL); | |||
jackTime = (jack_time_t) tv.tv_sec * 1000000 + (jack_time_t) tv.tv_usec; | |||
return jackTime; | |||
} | |||
SERVER_EXPORT void InitTime() | |||
{ | |||
__jack_cpu_mhz = GetMhz(); | |||
} | |||
SERVER_EXPORT jack_time_t GetMicroSeconds(void) | |||
{ | |||
return get_cycles() / __jack_cpu_mhz; | |||
} | |||
#else | |||
#include <time.h> | |||
SERVER_EXPORT void InitTime() | |||
{} | |||
SERVER_EXPORT jack_time_t GetMicroSeconds(void) | |||
static jack_time_t jack_get_microseconds_from_system (void) | |||
{ | |||
jack_time_t jackTime; | |||
struct timespec time; | |||
clock_gettime(CLOCK_MONOTONIC, &time); | |||
jackTime = (jack_time_t) time.tv_sec * 1e6 + | |||
(jack_time_t) time.tv_nsec / 1e3; | |||
return jackTime; | |||
} | |||
#endif /* HAVE_CLOCK_GETTIME */ | |||
SERVER_EXPORT void JackSleep(long usec) | |||
{ | |||
usleep(usec); | |||
} | |||
SERVER_EXPORT void InitTime() | |||
{ | |||
__jack_cpu_mhz = jack_get_mhz (); | |||
} | |||
SERVER_EXPORT void SetClockSource(jack_timer_type_t source) | |||
{ | |||
jack_log("Clock source : %s", ClockSourceName(source)); | |||
switch (source) | |||
{ | |||
struct timespec ts; | |||
clock_gettime(CLOCK_MONOTONIC, &ts); | |||
return (jack_time_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000; | |||
case JACK_TIMER_CYCLE_COUNTER: | |||
_jack_get_microseconds = jack_get_microseconds_from_cycles; | |||
break; | |||
case JACK_TIMER_HPET: | |||
if (jack_hpet_init () == 0) { | |||
_jack_get_microseconds = jack_get_microseconds_from_hpet; | |||
} else { | |||
_jack_get_microseconds = jack_get_microseconds_from_system; | |||
} | |||
break; | |||
case JACK_TIMER_SYSTEM_CLOCK: | |||
default: | |||
_jack_get_microseconds = jack_get_microseconds_from_system; | |||
break; | |||
} | |||
#endif | |||
} | |||
SERVER_EXPORT const char* ClockSourceName(jack_timer_type_t source) | |||
{ | |||
switch (source) { | |||
case JACK_TIMER_CYCLE_COUNTER: | |||
return "cycle counter"; | |||
case JACK_TIMER_HPET: | |||
return "hpet"; | |||
case JACK_TIMER_SYSTEM_CLOCK: | |||
#ifdef HAVE_CLOCK_GETTIME | |||
return "system clock via clock_gettime"; | |||
#else | |||
return "system clock via gettimeofday"; | |||
#endif | |||
} | |||
/* what is wrong with gcc ? */ | |||
return "unknown"; | |||
} | |||
SERVER_EXPORT jack_time_t GetMicroSeconds() | |||
{ | |||
return _jack_get_microseconds(); | |||
} |
@@ -51,6 +51,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
#include "audio_reserve.h" | |||
//#define DEBUG_WAKEUP 1 | |||
namespace Jack | |||
{ | |||
@@ -1346,6 +1348,9 @@ JackAlsaDriver::alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *stat | |||
/* if POLLIN was the only bit set, we're OK */ | |||
*status = 0; | |||
if (driver->pfd[nfds-1].revents == POLLIN) { | |||
jack_error("driver->pfd[nfds-1].revents == POLLIN"); | |||
} | |||
return (driver->pfd[nfds-1].revents == POLLIN) ? 0 : -1; | |||
} | |||
@@ -2191,21 +2196,18 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, | |||
if (audio_reservation_init() < 0) { | |||
jack_error("Audio device reservation service not available...."); | |||
} else if (strcmp(capture_driver_name, playback_driver_name) == 0) { // Same device for input and output | |||
fReservedCaptureDevice = audio_acquire(card_to_num(capture_driver_name)); | |||
if (fReservedCaptureDevice == NULL) { | |||
jack_error("Error audio device %s not available...", capture_driver_name); | |||
return -1; | |||
fReservedCaptureDevice = audio_acquire(card_to_num(capture_driver_name)); | |||
if (fReservedCaptureDevice == NULL) { | |||
jack_error("Error audio device %s cannot be acquired, trying to open it anyway...", capture_driver_name); | |||
} | |||
} else { | |||
fReservedCaptureDevice = audio_acquire(card_to_num(capture_driver_name)); | |||
if (fReservedCaptureDevice == NULL) { | |||
jack_error("Error capture audio device %s not available...", capture_driver_name); | |||
return -1; | |||
} | |||
jack_error("Error capture audio device %s cannot be acquired, trying to open it anyway...", capture_driver_name); | |||
} | |||
fReservedPlaybackDevice = audio_acquire(card_to_num(playback_driver_name)); | |||
if (fReservedPlaybackDevice == NULL) { | |||
jack_error("Error playback audio device %s not available...", playback_driver_name); | |||
return -1; | |||
jack_error("Error playback audio device %s cannot be acquired, trying to open it anyway...", playback_driver_name); | |||
} | |||
} | |||
#endif | |||
@@ -2277,7 +2279,7 @@ int JackAlsaDriver::Read() | |||
/* we detected an xrun and restarted: notify | |||
* clients about the delay. | |||
*/ | |||
jack_log("ALSA XRun"); | |||
jack_log("ALSA XRun wait_status = %d", wait_status); | |||
NotifyXRun(fBeginDateUst, fDelayedUsecs); | |||
return -1; | |||
} | |||
@@ -39,6 +39,20 @@ | |||
#ifdef __linux__ | |||
#ifdef __x86_64__ | |||
typedef unsigned long cycles_t; | |||
extern cycles_t cacheflush_time; | |||
static inline unsigned long get_cycles(void) | |||
{ | |||
unsigned int hi, lo; | |||
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); | |||
return (((unsigned long)hi)<<32) | ((unsigned long)lo); | |||
} | |||
#endif | |||
#ifdef __PPC__ | |||
/* PowerPC */ | |||
@@ -42,7 +42,7 @@ rpc_type server_rpc_jack_client_check(mach_port_t private_port, client_name_t na | |||
rpc_type server_rpc_jack_client_open(mach_port_t server_port, client_name_t name, int pid, mach_port_t* private_port, int* shared_engine, int* shared_client, int* shared_graph, int* result) | |||
{ | |||
jack_log("rpc_jack_client_opne name = %s", name); | |||
jack_log("rpc_jack_client_open name = %s", name); | |||
JackMachServerChannel* channel = JackMachServerChannel::fPortTable[server_port]; | |||
assert(channel); | |||
channel->ClientOpen((char*)name, pid, private_port, shared_engine, shared_client, shared_graph, result); | |||
@@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
#include "JackTime.h" | |||
#include "JackError.h" | |||
#include "JackTypes.h" | |||
#include <mach/mach_time.h> | |||
#include <unistd.h> | |||
@@ -42,3 +43,11 @@ SERVER_EXPORT jack_time_t GetMicroSeconds(void) | |||
{ | |||
return (jack_time_t) (mach_absolute_time () * __jack_time_ratio); | |||
} | |||
SERVER_EXPORT void SetClockSource(jack_timer_type_t source) | |||
{} | |||
SERVER_EXPORT const char* ClockSourceName(jack_timer_type_t source) | |||
{ | |||
return ""; | |||
} |
@@ -577,6 +577,8 @@ | |||
4BF284190F31B4BC00B05BE3 /* JackArgParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BF284170F31B4BC00B05BE3 /* JackArgParser.h */; }; | |||
4BF2841A0F31B4BC00B05BE3 /* JackArgParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF284160F31B4BC00B05BE3 /* JackArgParser.cpp */; }; | |||
4BF2841B0F31B4BC00B05BE3 /* JackArgParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BF284170F31B4BC00B05BE3 /* JackArgParser.h */; }; | |||
4BF2F4210F9F4DA300B3FFAD /* JackDummyDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3988908B3CF6C00B6F371 /* JackDummyDriver.cpp */; }; | |||
4BF2F4220F9F4DA700B3FFAD /* JackDummyDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3988908B3CF6C00B6F371 /* JackDummyDriver.cpp */; }; | |||
4BF339160F8B86DC0080FB5B /* JackCoreMidiDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BF339140F8B86DC0080FB5B /* JackCoreMidiDriver.h */; }; | |||
4BF339170F8B86DC0080FB5B /* JackCoreMidiDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF339150F8B86DC0080FB5B /* JackCoreMidiDriver.cpp */; }; | |||
4BF339180F8B86DC0080FB5B /* JackCoreMidiDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BF339140F8B86DC0080FB5B /* JackCoreMidiDriver.h */; }; | |||
@@ -5475,6 +5477,7 @@ | |||
4BECB2FB0F4451C10091B70A /* JackProcessSync.cpp in Sources */, | |||
4BF339190F8B86DC0080FB5B /* JackCoreMidiDriver.cpp in Sources */, | |||
4BF339210F8B873E0080FB5B /* JackMidiDriver.cpp in Sources */, | |||
4BF2F4220F9F4DA700B3FFAD /* JackDummyDriver.cpp in Sources */, | |||
); | |||
runOnlyForDeploymentPostprocessing = 0; | |||
}; | |||
@@ -5855,6 +5858,7 @@ | |||
4BBAE4110F42FA6100B8BD3F /* JackEngineProfiling.cpp in Sources */, | |||
4BECB2F50F4451C10091B70A /* JackProcessSync.cpp in Sources */, | |||
4BF339230F8B873E0080FB5B /* JackMidiDriver.cpp in Sources */, | |||
4BF2F4210F9F4DA300B3FFAD /* JackDummyDriver.cpp in Sources */, | |||
); | |||
runOnlyForDeploymentPostprocessing = 0; | |||
}; | |||
@@ -157,6 +157,14 @@ error: | |||
return err; | |||
} | |||
static CFStringRef GetDeviceName(AudioDeviceID id) | |||
{ | |||
UInt32 size = sizeof(CFStringRef); | |||
CFStringRef UIname; | |||
OSStatus err = AudioDeviceGetProperty(id, 0, false, kAudioDevicePropertyDeviceUID, &size, &UIname); | |||
return (err == noErr) ? UIname : NULL; | |||
} | |||
OSStatus JackCoreAudioDriver::Render(void *inRefCon, | |||
AudioUnitRenderActionFlags *ioActionFlags, | |||
const AudioTimeStamp *inTimeStamp, | |||
@@ -434,6 +442,136 @@ JackCoreAudioDriver::JackCoreAudioDriver(const char* name, const char* alias, Ja | |||
JackCoreAudioDriver::~JackCoreAudioDriver() | |||
{} | |||
OSStatus JackCoreAudioDriver::CreateAggregateDevice(AudioDeviceID captureDeviceID, AudioDeviceID playbackDeviceID, AudioDeviceID* outAggregateDevice) | |||
{ | |||
OSStatus osErr = noErr; | |||
UInt32 outSize; | |||
Boolean outWritable; | |||
//----------------------- | |||
// Start to create a new aggregate by getting the base audio hardware plugin | |||
//----------------------- | |||
osErr = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyPlugInForBundleID, &outSize, &outWritable); | |||
if (osErr != noErr) | |||
return osErr; | |||
AudioValueTranslation pluginAVT; | |||
CFStringRef inBundleRef = CFSTR("com.apple.audio.CoreAudio"); | |||
AudioObjectID pluginID; | |||
pluginAVT.mInputData = &inBundleRef; | |||
pluginAVT.mInputDataSize = sizeof(inBundleRef); | |||
pluginAVT.mOutputData = &pluginID; | |||
pluginAVT.mOutputDataSize = sizeof(pluginID); | |||
osErr = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID, &outSize, &pluginAVT); | |||
if (osErr != noErr) | |||
return osErr; | |||
//----------------------- | |||
// Create a CFDictionary for our aggregate device | |||
//----------------------- | |||
CFMutableDictionaryRef aggDeviceDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |||
CFStringRef AggregateDeviceNameRef = CFSTR("JackDuplex"); | |||
CFStringRef AggregateDeviceUIDRef = CFSTR("com.grame.JackDuplex"); | |||
// add the name of the device to the dictionary | |||
CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceNameKey), AggregateDeviceNameRef); | |||
// add our choice of UID for the aggregate device to the dictionary | |||
CFDictionaryAddValue(aggDeviceDict, CFSTR(kAudioAggregateDeviceUIDKey), AggregateDeviceUIDRef); | |||
//------------------------------------------------- | |||
// Create a CFMutableArray for our sub-device list | |||
//------------------------------------------------- | |||
CFStringRef captureDeviceUID = GetDeviceName(captureDeviceID); | |||
CFStringRef playbackDeviceUID = GetDeviceName(playbackDeviceID); | |||
if (captureDeviceUID == NULL || playbackDeviceUID == NULL) | |||
return -1; | |||
// we need to append the UID for each device to a CFMutableArray, so create one here | |||
CFMutableArrayRef subDevicesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |||
// two sub-devices in this example, so append the sub-device's UID to the CFArray | |||
CFArrayAppendValue(subDevicesArray, captureDeviceUID); | |||
CFArrayAppendValue(subDevicesArray, playbackDeviceUID); | |||
//----------------------------------------------------------------------- | |||
// Feed the dictionary to the plugin, to create a blank aggregate device | |||
//----------------------------------------------------------------------- | |||
AudioObjectPropertyAddress pluginAOPA; | |||
pluginAOPA.mSelector = kAudioPlugInCreateAggregateDevice; | |||
pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; | |||
pluginAOPA.mElement = kAudioObjectPropertyElementMaster; | |||
UInt32 outDataSize; | |||
osErr = AudioObjectGetPropertyDataSize(pluginID, &pluginAOPA, 0, NULL, &outDataSize); | |||
if (osErr != noErr) | |||
return osErr; | |||
osErr = AudioObjectGetPropertyData(pluginID, &pluginAOPA, sizeof(aggDeviceDict), &aggDeviceDict, &outDataSize, outAggregateDevice); | |||
if (osErr != noErr) | |||
return osErr; | |||
// pause for a bit to make sure that everything completed correctly | |||
// this is to work around a bug in the HAL where a new aggregate device seems to disappear briefly after it is created | |||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); | |||
//------------------------- | |||
// Set the sub-device list | |||
//------------------------- | |||
pluginAOPA.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList; | |||
pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; | |||
pluginAOPA.mElement = kAudioObjectPropertyElementMaster; | |||
outDataSize = sizeof(CFMutableArrayRef); | |||
osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &subDevicesArray); | |||
if (osErr != noErr) | |||
return osErr; | |||
// pause again to give the changes time to take effect | |||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); | |||
//----------------------- | |||
// Set the master device | |||
//----------------------- | |||
// set the master device manually (this is the device which will act as the master clock for the aggregate device) | |||
// pass in the UID of the device you want to use | |||
pluginAOPA.mSelector = kAudioAggregateDevicePropertyMasterSubDevice; | |||
pluginAOPA.mScope = kAudioObjectPropertyScopeGlobal; | |||
pluginAOPA.mElement = kAudioObjectPropertyElementMaster; | |||
outDataSize = sizeof(CFStringRef); | |||
osErr = AudioObjectSetPropertyData(*outAggregateDevice, &pluginAOPA, 0, NULL, outDataSize, &captureDeviceUID); // capture is master... | |||
if (osErr != noErr) | |||
return osErr; | |||
// pause again to give the changes time to take effect | |||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); | |||
//---------- | |||
// Clean up | |||
//---------- | |||
// release the CF objects we have created - we don't need them any more | |||
CFRelease(aggDeviceDict); | |||
CFRelease(subDevicesArray); | |||
// release the device UID | |||
CFRelease(captureDeviceUID); | |||
CFRelease(playbackDeviceUID); | |||
jack_log("New aggregate device %ld", *outAggregateDevice); | |||
return noErr; | |||
} | |||
int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, const char* playback_driver_uid, char* capture_driver_name, char* playback_driver_name) | |||
{ | |||
capture_driver_name[0] = 0; | |||
@@ -442,16 +580,42 @@ int JackCoreAudioDriver::SetupDevices(const char* capture_driver_uid, const char | |||
// Duplex | |||
if (strcmp(capture_driver_uid, "") != 0 && strcmp(playback_driver_uid, "") != 0) { | |||
jack_log("JackCoreAudioDriver::Open duplex"); | |||
if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { | |||
jack_log("Will take default in/out"); | |||
if (GetDefaultDevice(&fDeviceID) != noErr) { | |||
jack_error("Cannot open default device"); | |||
/* | |||
AudioDeviceID captureID, playbackID; | |||
if (GetDeviceIDFromUID(capture_driver_uid, &captureID) != noErr) | |||
return -1; | |||
if (GetDeviceIDFromUID(playback_driver_uid, &playbackID) != noErr) | |||
return -1; | |||
if (CreateAggregateDevice(captureID, playbackID, &fDeviceID) != noErr) | |||
return -1; | |||
*/ | |||
// Same device for capture and playback... | |||
if (strcmp(capture_driver_uid, playback_driver_uid) == 0) { | |||
if (GetDeviceIDFromUID(playback_driver_uid, &fDeviceID) != noErr) { | |||
jack_log("Will take default in/out"); | |||