Browse Source

Merge pull request #78 from sdrik/pull/proxydriver-v1

Add a Proxy driver
tags/v1.9.11-RC1
Stéphane Letz 10 years ago
parent
commit
aed15eb971
13 changed files with 945 additions and 10 deletions
  1. +608
    -0
      common/JackProxyDriver.cpp
  2. +181
    -0
      common/JackProxyDriver.h
  3. +14
    -0
      common/JackTimedDriver.cpp
  4. +26
    -0
      common/JackTimedDriver.h
  5. +39
    -0
      common/JackWaitCallbackDriver.cpp
  6. +50
    -0
      common/JackWaitCallbackDriver.h
  7. +13
    -8
      common/JackWaitThreadedDriver.cpp
  8. +6
    -2
      common/JackWaitThreadedDriver.h
  9. +1
    -0
      common/wscript
  10. +1
    -0
      linux/wscript
  11. +2
    -0
      macosx/wscript
  12. +2
    -0
      solaris/wscript
  13. +2
    -0
      windows/wscript

+ 608
- 0
common/JackProxyDriver.cpp View File

@@ -0,0 +1,608 @@
/*
Copyright (C) 2014 Cédric Schieli

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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

*/

#include "JackCompilerDeps.h"
#include "driver_interface.h"
#include "JackEngineControl.h"
#include "JackLockedEngine.h"
#include "JackWaitCallbackDriver.h"
#include "JackProxyDriver.h"

using namespace std;

namespace Jack
{
JackProxyDriver::JackProxyDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table,
const char* upstream, const char* promiscuous,
char* client_name, bool auto_connect, bool auto_save)
: JackRestarterDriver(name, alias, engine, table)
{
jack_log("JackProxyDriver::JackProxyDriver upstream %s", upstream);

assert(strlen(upstream) < JACK_CLIENT_NAME_SIZE);
strcpy(fUpstream, upstream);

assert(strlen(client_name) < JACK_CLIENT_NAME_SIZE);
strcpy(fClientName, client_name);

if (promiscuous) {
fPromiscuous = strdup(promiscuous);
}

fAutoConnect = auto_connect;
fAutoSave = auto_save;
}

JackProxyDriver::~JackProxyDriver()
{
if (fHandle) {
UnloadJackModule(fHandle);
}
}

int JackProxyDriver::LoadClientLib()
{
// Already loaded
if (fHandle) {
return 0;
}
fHandle = LoadJackModule(JACK_PROXY_CLIENT_LIB);
if (!fHandle) {
return -1;
}
LoadSymbols();
return 0;
}

//open, close, attach and detach------------------------------------------------------

int JackProxyDriver::Open(jack_nframes_t buffer_size,
jack_nframes_t samplerate,
bool capturing,
bool 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)
{
fDetectPlaybackChannels = (outchannels == -1);
fDetectCaptureChannels = (inchannels == -1);

if (LoadClientLib() != 0) {
jack_error("Cannot dynamically load client library !");
return -1;
}

return JackWaiterDriver::Open(buffer_size, samplerate,
capturing, playing,
inchannels, outchannels,
monitor,
capture_driver_name, playback_driver_name,
capture_latency, playback_latency);
}

int JackProxyDriver::Close()
{
FreePorts();
return JackWaiterDriver::Close();
}

// Attach and Detach are defined as empty methods: port allocation is done when driver actually start (that is in Init)
int JackProxyDriver::Attach()
{
return 0;
}

int JackProxyDriver::Detach()
{
return 0;
}

//init and restart--------------------------------------------------------------------

/*
JackProxyDriver is wrapped in a JackWaitCallbackDriver decorator that behaves
as a "dummy driver, until Initialize method returns.
*/
bool JackProxyDriver::Initialize()
{
jack_log("JackProxyDriver::Initialize");

// save existing local connections if needed
if (fAutoSave) {
SaveConnections(0);
}

// new loading, but existing client, restart the driver
if (fClient) {
jack_info("JackProxyDriver restarting...");
jack_client_close(fClient);
}
FreePorts();

// display some additional infos
jack_info("JackProxyDriver started in %s mode.",
(fEngineControl->fSyncMode) ? "sync" : "async");

do {
jack_status_t status;
char *old = NULL;

if (fPromiscuous) {
// as we are fiddling with the environment variable content, save it
const char* tmp = getenv("JACK_PROMISCUOUS_SERVER");
if (tmp) {
old = strdup(tmp);
}
// temporary enable promiscuous mode
if (setenv("JACK_PROMISCUOUS_SERVER", fPromiscuous, 1) < 0) {
free(old);
jack_error("Error allocating memory.");
return false;
}
}

jack_info("JackProxyDriver connecting to %s", fUpstream);
fClient = jack_client_open(fClientName, static_cast<jack_options_t>(JackNoStartServer|JackServerName), &status, fUpstream);

if (fPromiscuous) {
// restore previous environment variable content
if (old) {
if (setenv("JACK_PROMISCUOUS_SERVER", old, 1) < 0) {
free(old);
jack_error("Error allocating memory.");
return false;
}
free(old);
} else {
unsetenv("JACK_PROMISCUOUS_SERVER");
}
}

// the connection failed, try again later
if (!fClient) {
JackSleep(1000000);
}

} while (!fClient);
jack_info("JackProxyDriver connected to %s", fUpstream);

// we are connected, let's register some callbacks

jack_on_shutdown(fClient, shutdown_callback, this);

if (jack_set_process_callback(fClient, process_callback, this) != 0) {
jack_error("Cannot set process callback.");
return false;
}

if (jack_set_buffer_size_callback(fClient, bufsize_callback, this) != 0) {
jack_error("Cannot set buffer size callback.");
return false;
}

if (jack_set_sample_rate_callback(fClient, srate_callback, this) != 0) {
jack_error("Cannot set sample rate callback.");
return false;
}

if (jack_set_port_connect_callback(fClient, connect_callback, this) != 0) {
jack_error("Cannot set port connect callback.");
return false;
}

// detect upstream physical playback ports if needed
if (fDetectPlaybackChannels) {
fPlaybackChannels = CountIO(JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsOutput);
}

// detect upstream physical capture ports if needed
if (fDetectCaptureChannels) {
fCaptureChannels = CountIO(JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsInput);
}

if (AllocPorts() != 0) {
jack_error("Can't allocate ports.");
return false;
}

bufsize_callback(jack_get_buffer_size(fClient));
srate_callback(jack_get_sample_rate(fClient));

// restore local connections if needed
if (fAutoSave) {
LoadConnections(0);
}

// everything is ready, start upstream processing
if (jack_activate(fClient) != 0) {
jack_error("Cannot activate jack client.");
return false;
}

// connect upstream ports if needed
if (fAutoConnect) {
ConnectPorts();
}

return true;
}

int JackProxyDriver::Stop()
{
if (fClient && (jack_deactivate(fClient) != 0)) {
jack_error("Cannot deactivate jack client.");
return -1;
}
return 0;
}

//client callbacks---------------------------------------------------------------------------

int JackProxyDriver::process_callback(jack_nframes_t nframes, void* arg)
{
assert(static_cast<JackProxyDriver*>(arg));
return static_cast<JackProxyDriver*>(arg)->Process();
}

int JackProxyDriver::bufsize_callback(jack_nframes_t nframes, void* arg)
{
assert(static_cast<JackProxyDriver*>(arg));
return static_cast<JackProxyDriver*>(arg)->bufsize_callback(nframes);
}
int JackProxyDriver::bufsize_callback(jack_nframes_t nframes)
{
if (JackTimedDriver::SetBufferSize(nframes) == 0) {
return -1;
}
JackDriver::NotifyBufferSize(nframes);
return 0;
}

int JackProxyDriver::srate_callback(jack_nframes_t nframes, void* arg)
{
assert(static_cast<JackProxyDriver*>(arg));
return static_cast<JackProxyDriver*>(arg)->srate_callback(nframes);
}
int JackProxyDriver::srate_callback(jack_nframes_t nframes)
{
if (JackTimedDriver::SetSampleRate(nframes) == 0) {
return -1;
}
JackDriver::NotifySampleRate(nframes);
return 0;
}

void JackProxyDriver::connect_callback(jack_port_id_t a, jack_port_id_t b, int connect, void* arg)
{
assert(static_cast<JackProxyDriver*>(arg));
static_cast<JackProxyDriver*>(arg)->connect_callback(a, b, connect);
}
void JackProxyDriver::connect_callback(jack_port_id_t a, jack_port_id_t b, int connect)
{
jack_port_t* port;
int i;

// skip port if not our own
port = jack_port_by_id(fClient, a);
if (!jack_port_is_mine(fClient, port)) {
port = jack_port_by_id(fClient, b);
if (!jack_port_is_mine(fClient, port)) {
return;
}
}

for (i = 0; i < fCaptureChannels; i++) {
if (fUpstreamPlaybackPorts[i] == port) {
fUpstreamPlaybackPortConnected[i] = connect;
}
}

for (i = 0; i < fPlaybackChannels; i++) {
if (fUpstreamCapturePorts[i] == port) {
fUpstreamCapturePortConnected[i] = connect;
}
}
}

void JackProxyDriver::shutdown_callback(void* arg)
{
assert(static_cast<JackProxyDriver*>(arg));
static_cast<JackProxyDriver*>(arg)->RestartWait();
}

//jack ports and buffers--------------------------------------------------------------

int JackProxyDriver::CountIO(const char* type, int flags)
{
int count = 0;
const char** ports = jack_get_ports(fClient, NULL, type, flags);
if (ports != NULL) {
while (ports[count]) { count++; }
jack_free(ports);
}
return count;
}

int JackProxyDriver::AllocPorts()
{
jack_log("JackProxyDriver::AllocPorts fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate);

char proxy[REAL_JACK_PORT_NAME_SIZE];
int i;

fUpstreamPlaybackPorts = new jack_port_t* [fCaptureChannels];
fUpstreamPlaybackPortConnected = new int [fCaptureChannels];
for (i = 0; i < fCaptureChannels; i++) {
snprintf(proxy, sizeof(proxy), "%s:to_client_%d", fClientName, i + 1);
fUpstreamPlaybackPorts[i] = jack_port_register(fClient, proxy, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0);
if (fUpstreamPlaybackPorts[i] == NULL) {
jack_error("driver: cannot register upstream port %s", proxy);
return -1;
}
fUpstreamPlaybackPortConnected[i] = 0;
}

fUpstreamCapturePorts = new jack_port_t* [fPlaybackChannels];
fUpstreamCapturePortConnected = new int [fPlaybackChannels];
for (i = 0; i < fPlaybackChannels; i++) {
snprintf(proxy, sizeof(proxy), "%s:from_client_%d", fClientName, i + 1);
fUpstreamCapturePorts[i] = jack_port_register(fClient, proxy, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0);
if (fUpstreamCapturePorts[i] == NULL) {
jack_error("driver: cannot register upstream port %s", proxy);
return -1;
}
fUpstreamCapturePortConnected[i] = 0;
}

// local ports are registered here
return JackAudioDriver::Attach();
}

int JackProxyDriver::FreePorts()
{
jack_log("JackProxyDriver::FreePorts");

int i;

for (i = 0; i < fCaptureChannels; i++) {
if (fCapturePortList[i] > 0) {
fEngine->PortUnRegister(fClientControl.fRefNum, fCapturePortList[i]);
fCapturePortList[i] = 0;
}
if (fUpstreamPlaybackPorts && fUpstreamPlaybackPorts[i]) {
fUpstreamPlaybackPorts[i] = NULL;
}
}

for (i = 0; i < fPlaybackChannels; i++) {
if (fPlaybackPortList[i] > 0) {
fEngine->PortUnRegister(fClientControl.fRefNum, fPlaybackPortList[i]);
fPlaybackPortList[i] = 0;
}
if (fUpstreamCapturePorts && fUpstreamCapturePorts[i]) {
fUpstreamCapturePorts[i] = NULL;
}
}

delete[] fUpstreamPlaybackPorts;
delete[] fUpstreamPlaybackPortConnected;
delete[] fUpstreamCapturePorts;
delete[] fUpstreamCapturePortConnected;

fUpstreamPlaybackPorts = NULL;
fUpstreamPlaybackPortConnected = NULL;
fUpstreamCapturePorts = NULL;
fUpstreamCapturePortConnected = NULL;

return 0;
}

void JackProxyDriver::ConnectPorts()
{
jack_log("JackProxyDriver::ConnectPorts");
const char** ports = jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsOutput);
if (ports != NULL) {
for (int i = 0; i < fCaptureChannels && ports[i]; i++) {
jack_connect(fClient, ports[i], jack_port_name(fUpstreamPlaybackPorts[i]));
}
jack_free(ports);
}

ports = jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsInput);
if (ports != NULL) {
for (int i = 0; i < fPlaybackChannels && ports[i]; i++) {
jack_connect(fClient, jack_port_name(fUpstreamCapturePorts[i]), ports[i]);
}
jack_free(ports);
}
}

//driver processes--------------------------------------------------------------------

int JackProxyDriver::Read()
{
// take the time at the beginning of the cycle
JackDriver::CycleTakeBeginTime();

int i;
void *from, *to;
size_t buflen = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize;

for (i = 0; i < fCaptureChannels; i++) {
if (fUpstreamPlaybackPortConnected[i]) {
from = jack_port_get_buffer(fUpstreamPlaybackPorts[i], fEngineControl->fBufferSize);
to = GetInputBuffer(i);
memcpy(to, from, buflen);
}
}

return 0;
}

int JackProxyDriver::Write()
{
int i;
void *from, *to;
size_t buflen = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize;

for (i = 0; i < fPlaybackChannels; i++) {
if (fUpstreamCapturePortConnected[i]) {
to = jack_port_get_buffer(fUpstreamCapturePorts[i], fEngineControl->fBufferSize);
from = GetOutputBuffer(i);
memcpy(to, from, buflen);
}
}

return 0;
}

//driver loader-----------------------------------------------------------------------

#ifdef __cplusplus
extern "C"
{
#endif

SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor()
{
jack_driver_desc_t * desc;
jack_driver_desc_filler_t filler;
jack_driver_param_value_t value;

desc = jack_driver_descriptor_construct("proxy", JackDriverMaster, "proxy backend", &filler);

strcpy(value.str, DEFAULT_UPSTREAM);
jack_driver_descriptor_add_parameter(desc, &filler, "upstream", 'u', JackDriverParamString, &value, NULL, "Name of the upstream jack server", NULL);

strcpy(value.str, "");
jack_driver_descriptor_add_parameter(desc, &filler, "promiscuous", 'p', JackDriverParamString, &value, NULL, "Promiscuous group", NULL);

value.i = -1;
jack_driver_descriptor_add_parameter(desc, &filler, "input-ports", 'C', JackDriverParamInt, &value, NULL, "Number of audio input ports", "Number of audio input ports. If -1, audio physical input from the master");
jack_driver_descriptor_add_parameter(desc, &filler, "output-ports", 'P', JackDriverParamInt, &value, NULL, "Number of audio output ports", "Number of audio output ports. If -1, audio physical output from the master");

strcpy(value.str, "proxy");
jack_driver_descriptor_add_parameter(desc, &filler, "client-name", 'n', JackDriverParamString, &value, NULL, "Name of the jack client", NULL);

value.i = false;
jack_driver_descriptor_add_parameter(desc, &filler, "use-username", 'U', JackDriverParamBool, &value, NULL, "Use current username as client name", NULL);

value.i = false;
jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect proxy to upstream system ports", NULL);

value.i = false;
jack_driver_descriptor_add_parameter(desc, &filler, "auto-save", 's', JackDriverParamBool, &value, NULL, "Save/restore connection state when restarting", NULL);

return desc;
}

SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params)
{
char upstream[JACK_CLIENT_NAME_SIZE + 1];
char promiscuous[JACK_CLIENT_NAME_SIZE + 1] = {0};
char client_name[JACK_CLIENT_NAME_SIZE + 1];
jack_nframes_t period_size = 1024; // to be used while waiting for master period_size
jack_nframes_t sample_rate = 48000; // to be used while waiting for master sample_rate
int capture_ports = -1;
int playback_ports = -1;
const JSList* node;
const jack_driver_param_t* param;
bool auto_connect = false;
bool auto_save = false;
bool use_promiscuous = false;

// Possibly use env variable for upstream name
const char* default_upstream = getenv("JACK_PROXY_UPSTREAM");
strcpy(upstream, (default_upstream) ? default_upstream : DEFAULT_UPSTREAM);

// Possibly use env variable for upstream promiscuous
const char* default_promiscuous = getenv("JACK_PROXY_PROMISCUOUS");
strcpy(promiscuous, (default_promiscuous) ? default_promiscuous : "");

// Possibly use env variable for client name
const char* default_client_name = getenv("JACK_PROXY_CLIENT_NAME");
strcpy(client_name, (default_client_name) ? default_client_name : DEFAULT_CLIENT_NAME);

#ifdef WIN32
const char* username = getenv("USERNAME");
#else
const char* username = getenv("LOGNAME");
#endif

for (node = params; node; node = jack_slist_next(node)) {
param = (const jack_driver_param_t*) node->data;
switch (param->character)
{
case 'u' :
assert(strlen(param->value.str) < JACK_CLIENT_NAME_SIZE);
strcpy(upstream, param->value.str);
break;
case 'p':
assert(strlen(param->value.str) < JACK_CLIENT_NAME_SIZE);
use_promiscuous = true;
strcpy(promiscuous, param->value.str);
break;
case 'C':
capture_ports = param->value.i;
break;
case 'P':
playback_ports = param->value.i;
break;
case 'n' :
assert(strlen(param->value.str) < JACK_CLIENT_NAME_SIZE);
strncpy(client_name, param->value.str, JACK_CLIENT_NAME_SIZE);
break;
case 'U' :
if (username && *username) {
assert(strlen(username) < JACK_CLIENT_NAME_SIZE);
strncpy(client_name, username, JACK_CLIENT_NAME_SIZE);
}
case 'c':
auto_connect = true;
break;
case 's':
auto_save = true;
break;
}
}

try {

Jack::JackDriverClientInterface* driver = new Jack::JackWaitCallbackDriver(
new Jack::JackProxyDriver("system", "proxy_pcm", engine, table, upstream, use_promiscuous ? promiscuous : NULL, client_name, auto_connect, auto_save));
if (driver->Open(period_size, sample_rate, 1, 1, capture_ports, playback_ports, false, "capture_", "playback_", 0, 0) == 0) {
return driver;
} else {
delete driver;
return NULL;
}

} catch (...) {
return NULL;
}
}

#ifdef __cplusplus
}
#endif
}

+ 181
- 0
common/JackProxyDriver.h View File

@@ -0,0 +1,181 @@
/*
Copyright (C) 2014 Cédric Schieli

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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

*/

#ifndef __JackProxyDriver__
#define __JackProxyDriver__

#include "JackTimedDriver.h"

#define DEFAULT_UPSTREAM "default" /*!< Default upstream Jack server to connect to */
#define DEFAULT_CLIENT_NAME "proxy" /*!< Default client name to use when connecting to upstream Jack server */

#ifdef __APPLE__
#define JACK_PROXY_CLIENT_LIB "libjack.0.dylib"
#elif defined(WIN32)
#ifdef _WIN64
#define JACK_PROXY_CLIENT_LIB "libjack64.dll"
#else
#define JACK_PROXY_CLIENT_LIB "libjack.dll"
#endif
#else
#define JACK_PROXY_CLIENT_LIB "libjack.so.0"
#endif

#define PROXY_DEF_SYMBOL(ret,name,...) ret (*name) (__VA_ARGS__)
#define PROXY_LOAD_SYMBOL(ret,name,...) name = (ret (*) (__VA_ARGS__)) GetJackProc(fHandle, #name); assert(name)

namespace Jack
{
/*! \Brief This class describes the Proxy Backend

It uses plain Jack API to connect to an upstream server. The latter is
either running as the same user, or is running in promiscuous mode.

The main use case is the multi-user, multi-session, shared workstation:

- a classic server with hw driver is launched system-wide at boot time, in
promiscuous mode, optionaly restricted to the audio group
- in each user session, a jackdbus server is automatically started with
JackProxyDriver as master driver, automatically connected to the
system-wide one
- optionaly, each user run PulseAudio with a pulse-jack bridge
*/

class JackProxyDriver : public JackRestarterDriver
{

private:

char fUpstream[JACK_CLIENT_NAME_SIZE]; /*<! the upstream server name */
char fClientName[JACK_CLIENT_NAME_SIZE]; /*<! client name to use when connecting */
const char* fPromiscuous; /*<! if not null, group or gid to use for promiscuous mode */

//jack data
jack_client_t* fClient; /*<! client handle */
jack_port_t** fUpstreamCapturePorts; /*<! ports registered for capture in the upstream server */
jack_port_t** fUpstreamPlaybackPorts; /*<! ports registered for playback in the upstream server */
int* fUpstreamCapturePortConnected; /*<! map of capture ports connected upstream, for optimization purpose */
int* fUpstreamPlaybackPortConnected; /*<! map of playback ports connected upstream, for optimization purpose */

bool fAutoSave; /*<! wether the local connections should be saved/restored when upstream connection is restarted */
bool fAutoConnect; /*<! wether the upstream ports should be automatically connected to upstream physical ports */
bool fDetectPlaybackChannels; /*<! wether the number of playback ports registered should match the number of upstream physical playback ports */
bool fDetectCaptureChannels; /*<! wether the number of capture ports registered should match the number of upstream physical capture ports */

bool Initialize(); /*<! establish upstream connection and register the client callbacks */

int AllocPorts(); /*<! register local and upstream ports */
int FreePorts(); /*<! unregister local ports */
void ConnectPorts(); /*<! connect upstream ports to physical ones */

int CountIO(const char*, int); /*<! get the number of upstream ports of a specific type */

// client callbacks
static int process_callback(jack_nframes_t, void*);
static int bufsize_callback(jack_nframes_t, void*);
static int srate_callback(jack_nframes_t, void*);
static void connect_callback(jack_port_id_t, jack_port_id_t, int, void*);
static void shutdown_callback(void*);

// indirect member callbacks
int bufsize_callback(jack_nframes_t);
int srate_callback(jack_nframes_t);
void connect_callback(jack_port_id_t, jack_port_id_t, int);

JACK_HANDLE fHandle; /*<! handle to the jack client library */

// map needed client library symbols as members to override those from the jackserver library
PROXY_DEF_SYMBOL(jack_client_t*, jack_client_open, const char*, jack_options_t, jack_status_t*, ...);
PROXY_DEF_SYMBOL(int, jack_set_process_callback, jack_client_t*, JackProcessCallback, void*);
PROXY_DEF_SYMBOL(int, jack_set_buffer_size_callback, jack_client_t*, JackBufferSizeCallback, void*);
PROXY_DEF_SYMBOL(int, jack_set_sample_rate_callback, jack_client_t*, JackSampleRateCallback, void*);
PROXY_DEF_SYMBOL(int, jack_set_port_connect_callback, jack_client_t*, JackPortConnectCallback, void*);
PROXY_DEF_SYMBOL(void, jack_on_shutdown, jack_client_t*, JackShutdownCallback, void*);
PROXY_DEF_SYMBOL(jack_nframes_t, jack_get_buffer_size, jack_client_t*);
PROXY_DEF_SYMBOL(jack_nframes_t, jack_get_sample_rate, jack_client_t*);
PROXY_DEF_SYMBOL(int, jack_activate, jack_client_t*);
PROXY_DEF_SYMBOL(int, jack_deactivate, jack_client_t*);
PROXY_DEF_SYMBOL(jack_port_t*, jack_port_by_id, jack_client_t*, jack_port_id_t);
PROXY_DEF_SYMBOL(int, jack_port_is_mine, const jack_client_t*, const jack_port_t*);
PROXY_DEF_SYMBOL(const char**, jack_get_ports, jack_client_t*, const char*, const char*, unsigned long);
PROXY_DEF_SYMBOL(void, jack_free, void*);
PROXY_DEF_SYMBOL(jack_port_t*, jack_port_register, jack_client_t*, const char*, const char*, unsigned long, unsigned long);
PROXY_DEF_SYMBOL(int, jack_port_unregister, jack_client_t*, jack_port_t*);
PROXY_DEF_SYMBOL(void*, jack_port_get_buffer, jack_port_t*, jack_nframes_t);
PROXY_DEF_SYMBOL(int, jack_connect, jack_client_t*, const char*, const char*);
PROXY_DEF_SYMBOL(const char*, jack_port_name, const jack_port_t*);
PROXY_DEF_SYMBOL(int, jack_client_close, jack_client_t*);

/*! load the needed library symbols */
void LoadSymbols()
{
PROXY_LOAD_SYMBOL(jack_client_t*, jack_client_open, const char*, jack_options_t, jack_status_t*, ...);
PROXY_LOAD_SYMBOL(int, jack_set_process_callback, jack_client_t*, JackProcessCallback, void*);
PROXY_LOAD_SYMBOL(int, jack_set_buffer_size_callback, jack_client_t*, JackBufferSizeCallback, void*);
PROXY_LOAD_SYMBOL(int, jack_set_sample_rate_callback, jack_client_t*, JackSampleRateCallback, void*);
PROXY_LOAD_SYMBOL(int, jack_set_port_connect_callback, jack_client_t*, JackPortConnectCallback, void*);
PROXY_LOAD_SYMBOL(void, jack_on_shutdown, jack_client_t*, JackShutdownCallback, void*);
PROXY_LOAD_SYMBOL(jack_nframes_t, jack_get_buffer_size, jack_client_t*);
PROXY_LOAD_SYMBOL(jack_nframes_t, jack_get_sample_rate, jack_client_t*);
PROXY_LOAD_SYMBOL(int, jack_activate, jack_client_t*);
PROXY_LOAD_SYMBOL(int, jack_deactivate, jack_client_t*);
PROXY_LOAD_SYMBOL(jack_port_t*, jack_port_by_id, jack_client_t*, jack_port_id_t);
PROXY_LOAD_SYMBOL(int, jack_port_is_mine, const jack_client_t*, const jack_port_t*);
PROXY_LOAD_SYMBOL(const char**, jack_get_ports, jack_client_t*, const char*, const char*, unsigned long);
PROXY_LOAD_SYMBOL(void, jack_free, void*);
PROXY_LOAD_SYMBOL(jack_port_t*, jack_port_register, jack_client_t*, const char*, const char*, unsigned long, unsigned long);
PROXY_LOAD_SYMBOL(int, jack_port_unregister, jack_client_t*, jack_port_t*);
PROXY_LOAD_SYMBOL(void*, jack_port_get_buffer, jack_port_t*, jack_nframes_t);
PROXY_LOAD_SYMBOL(int, jack_connect, jack_client_t*, const char*, const char*);
PROXY_LOAD_SYMBOL(const char*, jack_port_name, const jack_port_t*);
PROXY_LOAD_SYMBOL(int, jack_client_close, jack_client_t*);
}
int LoadClientLib(); /*!< load the client library */

public:

JackProxyDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table,
const char* upstream, const char* promiscuous, char* client_name, bool auto_connect, bool auto_save);
virtual ~JackProxyDriver();

int Open(jack_nframes_t buffer_size,
jack_nframes_t samplerate,
bool capturing,
bool 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 Close();

int Stop();

int Attach();
int Detach();

int Read();
int Write();

};
}

#endif

+ 14
- 0
common/JackTimedDriver.cpp View File

@@ -86,4 +86,18 @@ int JackWaiterDriver::ProcessNull()
return 0;
}

void JackRestarterDriver::SetRestartDriver(JackDriver* driver)
{
fRestartDriver = driver;
}

int JackRestarterDriver::RestartWait()
{
if (!fRestartDriver) {
jack_error("JackRestartedDriver::RestartWait driver not set");
return -1;
}
return fRestartDriver->Start();
}

} // end of namespace

+ 26
- 0
common/JackTimedDriver.h View File

@@ -75,6 +75,32 @@ class SERVER_EXPORT JackWaiterDriver : public JackTimedDriver

};

/*!
\brief A restartable driver.

When wrapped into a JackWaitCallbackDriver, this driver can restart the
wrapper thread which will wait again for the Initialize method to return.
*/
class SERVER_EXPORT JackRestarterDriver : public JackWaiterDriver
{

private:

JackDriver* fRestartDriver; /*!< The wrapper driver */

public:

JackRestarterDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table)
: JackWaiterDriver(name, alias, engine, table), fRestartDriver(NULL)
{}
virtual ~JackRestarterDriver()
{}

void SetRestartDriver(JackDriver* driver); /*!< Let the wrapper register itself */
int RestartWait(); /*!< Restart the wrapper thread */

};

} // end of namespace

#endif

+ 39
- 0
common/JackWaitCallbackDriver.cpp View File

@@ -0,0 +1,39 @@
/*
Copyright (C) 2014 Cédric Schieli

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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

*/

#include "JackWaitCallbackDriver.h"

namespace Jack
{

JackWaitCallbackDriver::JackWaitCallbackDriver(JackRestarterDriver* driver)
: JackWaitThreadedDriver(driver)
{
// Self register with the decorated driver so it can restart us
assert(driver);
driver->SetRestartDriver((JackDriver*)this);
}

bool JackWaitCallbackDriver::ExecuteReal()
{
// End the thread and let the callback driver do its job
return false;
}

} // end of namespace

+ 50
- 0
common/JackWaitCallbackDriver.h View File

@@ -0,0 +1,50 @@
/*
Copyright (C) 2014 Cédric Schieli

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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

*/

#ifndef __JackWaitCallbackDriver__
#define __JackWaitCallbackDriver__

#include "JackWaitThreadedDriver.h"

namespace Jack
{

/*!
\brief Wrapper for a restartable non-threaded driver (e.g. JackProxyDriver).

Simply ends its thread when the decorated driver Initialize method returns.
Self register with the supplied JackRestarterDriver so it can restart the thread.
*/

class SERVER_EXPORT JackWaitCallbackDriver : public JackWaitThreadedDriver
{
public:

JackWaitCallbackDriver(JackRestarterDriver* driver);

protected:

bool ExecuteReal();
};


} // end of namespace


#endif

+ 13
- 8
common/JackWaitThreadedDriver.cpp View File

@@ -37,16 +37,21 @@ bool JackWaitThreadedDriver::Init()

bool JackWaitThreadedDriver::Execute()
{
try {
SetRealTime();

SetRealTime();
// Process a null cycle until NetDriver has started
while (!fStarter.fRunning && fThread.GetStatus() == JackThread::kRunning) {
// Use base class method
assert(static_cast<JackWaiterDriver*>(fDriver));
static_cast<JackWaiterDriver*>(fDriver)->ProcessNull();
}

// Process a null cycle until NetDriver has started
while (!fStarter.fRunning && fThread.GetStatus() == JackThread::kRunning) {
// Use base class method
assert(static_cast<JackWaiterDriver*>(fDriver));
static_cast<JackWaiterDriver*>(fDriver)->ProcessNull();
}
return ExecuteReal();
}
bool JackWaitThreadedDriver::ExecuteReal()
{
try {

// Switch to keep running even in case of error
while (fThread.GetStatus() == JackThread::kRunning) {


+ 6
- 2
common/JackWaitThreadedDriver.h View File

@@ -28,10 +28,10 @@ namespace Jack
{

/*!
\brief To be used as a wrapper of JackNetDriver.
\brief Wrapper for a restartable threaded driver (e.g. JackNetDriver).

The idea is to behave as the "dummy" driver, until the network connection is really started and processing starts.
The Execute method will call the Process method from the base JackTimedDriver, until the decorated driver Init method returns.
The Execute method will call the ProcessNull method from the base JackWaiterDriver, until the decorated driver Initialize method returns.
A helper JackDriverStarter thread is used for that purpose.
*/

@@ -89,6 +89,10 @@ class SERVER_EXPORT JackWaitThreadedDriver : public JackThreadedDriver
// JackRunnableInterface interface
bool Init();
bool Execute();

protected:

virtual bool ExecuteReal(); /*!< Real work to be done when the decorated driver has finish initializing */
};




+ 1
- 0
common/wscript View File

@@ -223,6 +223,7 @@ def build(bld):
'JackThreadedDriver.cpp',
'JackRestartThreadedDriver.cpp',
'JackWaitThreadedDriver.cpp',
'JackWaitCallbackDriver.cpp',
'JackServerAPI.cpp',
'JackDriverLoader.cpp',
'JackServerGlobals.cpp',


+ 1
- 0
linux/wscript View File

@@ -91,3 +91,4 @@ def build(bld):
'../common/netjack.c',
'../common/netjack_packet.c' ], ["SAMPLERATE", "CELT"])

create_jack_driver_obj(bld, 'proxy', '../common/JackProxyDriver.cpp')

+ 2
- 0
macosx/wscript View File

@@ -87,3 +87,5 @@ def build(bld):
create_jack_driver_obj(bld, 'netone', [ '../common/JackNetOneDriver.cpp',
'../common/netjack.c',
'../common/netjack_packet.c' ], "SAMPLERATE CELT" )

create_jack_driver_obj(bld, 'proxy', '../common/JackProxyDriver.cpp')

+ 2
- 0
solaris/wscript View File

@@ -35,3 +35,5 @@ def build(bld):
create_jack_driver_obj(bld, 'net', '../common/JackNetDriver.cpp')

create_jack_driver_obj(bld, 'loopback', '../common/JackLoopbackDriver.cpp')

create_jack_driver_obj(bld, 'proxy', '../common/JackProxyDriver.cpp')

+ 2
- 0
windows/wscript View File

@@ -65,3 +65,5 @@ def build(bld):
create_jack_driver_obj(bld, 'netone', [ '../common/JackNetOneDriver.cpp',
'../common/netjack.c',
'../common/netjack_packet.c' ], ["SAMPLERATE", "CELT"] )

create_jack_driver_obj(bld, 'proxy', '../common/JackProxyDriver.cpp')

Loading…
Cancel
Save