Browse Source

some progress on jack-session

tags/1.9.7
Torben Hohn 16 years ago
parent
commit
3c0d95db60
15 changed files with 544 additions and 21 deletions
  1. +8
    -0
      common/JackAPI.cpp
  2. +1
    -1
      common/JackChannel.h
  3. +21
    -1
      common/JackClient.cpp
  4. +8
    -0
      common/JackClientControl.h
  5. +37
    -2
      common/JackEngine.cpp
  6. +6
    -1
      common/JackEngine.h
  7. +2
    -2
      common/JackLockedEngine.h
  8. +47
    -5
      common/JackRequest.h
  9. +7
    -2
      common/jack/types.h
  10. +183
    -0
      example-clients/session_notify.c
  11. +202
    -0
      example-clients/simple_session_client.c
  12. +2
    -0
      example-clients/wscript
  13. +18
    -3
      posix/JackSocketClientChannel.cpp
  14. +1
    -1
      posix/JackSocketClientChannel.h
  15. +1
    -3
      posix/JackSocketServerChannel.cpp

+ 8
- 0
common/JackAPI.cpp View File

@@ -1891,4 +1891,12 @@ EXPORT jack_session_command_t *jack_session_notify(jack_client_t* ext_client, co
}
}

EXPORT void jack_session_event_free(jack_session_event_t* ev)
{
free((void *)ev->session_dir);
free((void *)ev->client_uuid);
if (ev->command_line)
free(ev->command_line);
free(ev);
}


+ 1
- 1
common/JackChannel.h View File

@@ -127,7 +127,7 @@ class JackClientChannelInterface
virtual void InternalClientUnload(int refnum, int int_ref, int* status, int* result)
{}
virtual void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char *path, int *result)
virtual void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char *path, jack_session_command_t **result)
{}
};



+ 21
- 1
common/JackClient.cpp View File

@@ -272,6 +272,22 @@ int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync,
fInfoShutdown = NULL;
}
break;

case kSessionCallback:
jack_log("JackClient::kSessionCallback");
if (fSession) {
jack_session_event_t *event = (jack_session_event_t *) malloc( sizeof(jack_session_event_t) );
char uuid_buf[32];
event->type = (jack_session_event_type_t) value1;
event->session_dir = strdup( message );
event->command_line = NULL;
event->flags = (jack_session_flags_t) 0;
snprintf( uuid_buf, sizeof(uuid_buf), "%d", GetClientControl()->fSessionID );
event->client_uuid = strdup( uuid_buf );

fSession(event, fSessionArg);
}
break;
}
}

@@ -1057,7 +1073,11 @@ void JackClient::InternalClientUnload(int ref, jack_status_t* status)

jack_session_command_t *JackClient::SessionNotify( const char* target, jack_session_event_type_t type, const char* path )
{
return NULL;
printf( "yo man\n" );
sleep(1);
jack_session_command_t *res;
fChannel->SessionNotify( GetClientControl()->fRefNum, target, type, path, &res );
return res;
}




+ 8
- 0
common/JackClientControl.h View File

@@ -26,6 +26,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "JackSynchro.h"
#include "JackNotification.h"

#include "jack/session.h"

namespace Jack
{

@@ -44,6 +46,10 @@ struct JackClientControl : public JackShmMemAble
int fPID;
bool fActive;

int fSessionID;
char fSessionCommand[256 + 1];
jack_session_flags_t fSessionFlags;

JackClientControl(const char* name, int pid, int refnum)
{
Init(name, pid, refnum);
@@ -77,6 +83,8 @@ struct JackClientControl : public JackShmMemAble
fTransportSync = false;
fTransportTimebase = false;
fActive = false;

fSessionID = 0;
}

} POST_PACKED_STRUCTURE;


+ 37
- 2
common/JackEngine.cpp View File

@@ -863,10 +863,45 @@ int JackEngine::PortRename(int refnum, jack_port_id_t port, const char* name)
return 0;
}

void JackEngine::SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, int *result )
void JackEngine::SessionNotify(int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket )
{
if (fSessionPendingReplies != 0) {
JackSessionNotifyResult res(-1);
res.Write(socket);
return;
}

fSessionResult = new JackSessionNotifyResult();

for (int i = 0; i < CLIENT_NUM; i++) {
JackClientInterface* client = fClientTable[i];
if (client && client->GetClientControl()->fCallback[kSessionCallback]) {

// check if this is a notification to a specific client.
if (target!=NULL && strlen(target)!=0) {
if (strcmp(target, client->GetClientControl()->fName)) {
continue;
}
}

int result = client->ClientNotify(i, client->GetClientControl()->fName, kSessionCallback, true, path, (int) type, 0);
if (result == 2) {
fSessionPendingReplies += 1;
} else if (result == 1) {
char uuid_buf[32];
snprintf( uuid_buf, sizeof(uuid_buf), "%d", client->GetClientControl()->fSessionID );
fSessionResult->fCommandList.push_back( JackSessionCommand( uuid_buf,
client->GetClientControl()->fName,
client->GetClientControl()->fSessionCommand,
client->GetClientControl()->fSessionFlags ));
}



}
}
// yay... :(
*result = 0;
//*result = 0;
}




+ 6
- 1
common/JackEngine.h View File

@@ -52,6 +52,11 @@ class SERVER_EXPORT JackEngine : public JackLockAble
JackProcessSync fSignal;
jack_time_t fLastSwitchUsecs;

int fSessionPendingReplies;
JackChannelTransaction *fSessionTransaction;
JackSessionNotifyResult *fSessionResult;


int ClientCloseAux(int refnum, JackClientInterface* client, bool wait);
void CheckXRun(jack_time_t callback_usecs);

@@ -132,7 +137,7 @@ class SERVER_EXPORT JackEngine : public JackLockAble
void NotifyFreewheel(bool onoff);
void NotifyQuit();

void SessionNotify( int refnum, const char *target, jack_session_event_type_t type, const char *path, int *result );
void SessionNotify( int refnum, const char *target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket );
};




+ 2
- 2
common/JackLockedEngine.h View File

@@ -307,11 +307,11 @@ class SERVER_EXPORT JackLockedEngine
CATCH_EXCEPTION
}

void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char *path, int *result)
void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char *path, JackChannelTransaction *socket)
{
TRY_CALL
JackLock lock(&fEngine);
return fEngine.SessionNotify( refnum, target, type, path, result );
fEngine.SessionNotify( refnum, target, type, path, socket );
CATCH_EXCEPTION
}


+ 47
- 5
common/JackRequest.h View File

@@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "types.h"
#include <string.h>
#include <stdio.h>
#include <list>

namespace Jack
{
@@ -1071,28 +1072,69 @@ struct JackClientNotificationRequest : public JackRequest

} POST_PACKED_STRUCTURE;

struct JackSessionCommand
{
char fUUID[32];
char fClientName[JACK_CLIENT_NAME_SIZE+1];
char fCommand[MAX_PATH+1];
jack_session_flags_t fFlags;

JackSessionCommand()
{}

JackSessionCommand( const char *uuid, const char *clientname, const char *command, jack_session_flags_t flags )
{
strncpy( fUUID, uuid, sizeof(fUUID));
strncpy( fClientName, clientname, sizeof(fClientName));
strncpy( fCommand, command, sizeof(fCommand));
fFlags = flags;
}
};


struct JackSessionNotifyResult : public JackResult
{

int fStatus;
std::list<JackSessionCommand> fCommandList;

JackSessionNotifyResult(): JackResult()
{}
JackSessionNotifyResult(int32_t result, int status)
: JackResult(result), fStatus(status)
JackSessionNotifyResult(int32_t result)
: JackResult(result)
{}

int Read(JackChannelTransaction* trans)
{
CheckRes(JackResult::Read(trans));
CheckRes(trans->Read(&fStatus, sizeof(int)));
while(1) {
JackSessionCommand buffer;

CheckRes(trans->Read(buffer.fUUID, sizeof(buffer.fUUID)));
if (buffer.fUUID[0] == '\0')
break;

CheckRes(trans->Read(buffer.fClientName, sizeof(buffer.fClientName)));
CheckRes(trans->Read(buffer.fCommand, sizeof(buffer.fCommand)));
CheckRes(trans->Read(&(buffer.fFlags), sizeof(buffer.fFlags)));

fCommandList.push_back(buffer);
}
return 0;
}

int Write(JackChannelTransaction* trans)
{
char terminator[32];
terminator[0] = '\0';

CheckRes(JackResult::Write(trans));
CheckRes(trans->Write(&fStatus, sizeof(int)));
for (std::list<JackSessionCommand>::iterator i=fCommandList.begin(); i!=fCommandList.end(); i++) {
CheckRes(trans->Write(i->fUUID, sizeof(i->fUUID)));
CheckRes(trans->Write(i->fClientName, sizeof(i->fClientName)));
CheckRes(trans->Write(i->fCommand, sizeof(i->fCommand)));
CheckRes(trans->Write(&(i->fFlags), sizeof(i->fFlags)));
}
CheckRes(trans->Write(terminator, sizeof(terminator)));
return 0;
}


+ 7
- 2
common/jack/types.h View File

@@ -345,11 +345,16 @@ enum JackOptions {
* Pass optional <em>(char *) load_init</em> string to the
* jack_initialize() entry point of an internal client.
*/
JackLoadInit = 0x10
JackLoadInit = 0x10,

/**
* pass a SessionID Token this allows the sessionmanager to identify the client again.
*/
JackSessionID = 0x20
};

/** Valid options for opening an external client. */
#define JackOpenOptions (JackServerName|JackNoStartServer|JackUseExactName)
#define JackOpenOptions (JackSessionID|JackServerName|JackNoStartServer|JackUseExactName)

/** Valid options for loading an internal client. */
#define JackLoadOptions (JackLoadInit|JackLoadName|JackUseExactName)


+ 183
- 0
example-clients/session_notify.c View File

@@ -0,0 +1,183 @@
/*
* session_notify.c -- ultra minimal session manager
*
* Copyright (C) 2010 Torben Hohn.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <jack/jack.h>
#include <jack/types.h>
#include <jack/jslist.h>
#include <jack/transport.h>
#include <jack/session.h>

char *package; /* program name */
jack_client_t *client;

jack_session_event_type_t notify_type;
char *save_path = NULL;

void jack_shutdown(void *arg)
{
fprintf(stderr, "JACK shut down, exiting ...\n");
exit(1);
}

void signal_handler(int sig)
{
jack_client_close(client);
fprintf(stderr, "signal received, exiting ...\n");
exit(0);
}

void parse_arguments(int argc, char *argv[])
{

/* basename $0 */
package = strrchr(argv[0], '/');
if (package == 0)
package = argv[0];
else
package++;

if (argc==2) {
if( !strcmp( argv[1], "quit" ) ) {
notify_type = JackSessionSaveAndQuit;
return;
}
}
if (argc==3) {
if( !strcmp( argv[1], "save" ) ) {
notify_type = JackSessionSave;
save_path = argv[2];
return;
}

}
fprintf(stderr, "usage: %s quit|save [path]\n", package);
exit(9);
}

typedef struct {
char name[32];
char uuid[16];
} uuid_map_t;

JSList *uuid_map = NULL;

void add_uuid_mapping( const char *uuid ) {
char *clientname = jack_get_client_name_by_uuid( client, uuid );
if( !clientname ) {
printf( "error... cant find client for uuid" );
return;
}

uuid_map_t *mapping = malloc( sizeof(uuid_map_t) );
snprintf( mapping->uuid, sizeof(mapping->uuid), "%s", uuid );
snprintf( mapping->name, sizeof(mapping->name), "%s", clientname );
uuid_map = jack_slist_append( uuid_map, mapping );
}

char *map_port_name_to_uuid_port( const char *port_name )
{
JSList *node;
char retval[300];
char *port_component = strchr( port_name,':' );
char *client_component = strdup( port_name );
strchr( client_component, ':' )[0] = '\0';

sprintf( retval, "%s", port_name );

for( node=uuid_map; node; node=jack_slist_next(node) ) {
uuid_map_t *mapping = node->data;
if( !strcmp( mapping->name, client_component ) ) {
sprintf( retval, "%s%s", mapping->uuid, port_component );
break;
}
}

return strdup(retval);
}

int main(int argc, char *argv[])
{
parse_arguments(argc, argv);
jack_session_command_t *retval;
int k,i,j;


/* become a JACK client */
if ((client = jack_client_open(package, JackNullOption, NULL)) == 0) {
fprintf(stderr, "JACK server not running?\n");
exit(1);
}

signal(SIGQUIT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGHUP, signal_handler);
signal(SIGINT, signal_handler);

jack_on_shutdown(client, jack_shutdown, 0);

jack_activate(client);


retval = jack_session_notify( client, NULL, notify_type, save_path );
printf( "retval = %p\n", retval );
for(i=0; retval[i].uuid; i++ ) {
printf( "export SESSION_DIR=\"%s%s/\"\n", save_path, retval[i].client_name );
printf( "%s &\n", retval[i].command );
add_uuid_mapping(retval[i].uuid);
}

printf( "sleep 10\n" );

for(k=0; retval[k].uuid; k++ ) {

char* port_regexp = alloca( jack_client_name_size()+3 );
char* client_name = jack_get_client_name_by_uuid( client, retval[k].uuid );
snprintf( port_regexp, jack_client_name_size()+3, "%s:.*", client_name );
jack_free(client_name);
const char **ports = jack_get_ports( client, port_regexp, NULL, 0 );
if( !ports ) {
continue;
}
for (i = 0; ports[i]; ++i) {
const char **connections;
if ((connections = jack_port_get_all_connections (client, jack_port_by_name(client, ports[i]))) != 0) {
for (j = 0; connections[j]; j++) {
char *src = map_port_name_to_uuid_port( ports[i] );
char *dst = map_port_name_to_uuid_port( connections[j] );
printf( "jack_connect -u \"%s\" \"%s\"\n", src, dst );
}
jack_free (connections);
}
}
jack_free(ports);

}
jack_session_commands_free(retval);

jack_client_close(client);

return 0;
}

+ 202
- 0
example-clients/simple_session_client.c View File

@@ -0,0 +1,202 @@
/** @file simple_session_client.c
*
* @brief This simple client demonstrates the most basic features of JACK
* as they would be used by many applications.
* this version also adds session manager functionality.
*/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <jack/jack.h>
#include <jack/types.h>
#include <jack/session.h>

jack_port_t *input_port;
jack_port_t *output_port;
jack_client_t *client;

int simple_quit = 0;

/**
* The process callback for this JACK application is called in a
* special realtime thread once for each audio cycle.
*
* This client does nothing more than copy data from its input
* port to its output port. It will exit when stopped by
* the user (e.g. using Ctrl-C on a unix-ish operating system)
*/
int
process (jack_nframes_t nframes, void *arg)
{
jack_default_audio_sample_t *in, *out;

in = jack_port_get_buffer (input_port, nframes);
out = jack_port_get_buffer (output_port, nframes);
memcpy (out, in,
sizeof (jack_default_audio_sample_t) * nframes);

return 0;
}

void
session_callback (jack_session_event_t *event, void *arg)
{
char retval[100];
printf ("session notification\n");
printf ("path %s, uuid %s, type: %s\n", event->session_dir, event->client_uuid, event->type == JackSessionSave ? "save" : "quit");


snprintf (retval, 100, "jack_simple_session_client %s", event->client_uuid);
event->command_line = strdup (retval);

jack_session_reply( client, event );

if (event->type == JackSessionSaveAndQuit) {
simple_quit = 1;
}

jack_session_event_free (event);
}

/**
* JACK calls this shutdown_callback if the server ever shuts down or
* decides to disconnect the client.
*/
void
jack_shutdown (void *arg)
{
exit (1);
}

int
main (int argc, char *argv[])
{
const char **ports;
const char *client_name = "simple";
jack_status_t status;

/* open a client connection to the JACK server */

if( argc == 1 )
client = jack_client_open (client_name, JackNullOption, &status );
else if( argc == 2 )
client = jack_client_open (client_name, JackSessionID, &status, argv[1] );

if (client == NULL) {
fprintf (stderr, "jack_client_open() failed, "
"status = 0x%2.0x\n", status);
if (status & JackServerFailed) {
fprintf (stderr, "Unable to connect to JACK server\n");
}
exit (1);
}
if (status & JackServerStarted) {
fprintf (stderr, "JACK server started\n");
}
if (status & JackNameNotUnique) {
client_name = jack_get_client_name(client);
fprintf (stderr, "unique name `%s' assigned\n", client_name);
}

/* tell the JACK server to call `process()' whenever
there is work to be done.
*/

jack_set_process_callback (client, process, 0);

/* tell the JACK server to call `jack_shutdown()' if
it ever shuts down, either entirely, or if it
just decides to stop calling us.
*/

jack_on_shutdown (client, jack_shutdown, 0);

/* tell the JACK server to call `session_callback()' if
the session is saved.
*/

jack_set_session_callback (client, session_callback, NULL);

/* display the current sample rate.
*/

printf ("engine sample rate: %" PRIu32 "\n",
jack_get_sample_rate (client));

/* create two ports */

input_port = jack_port_register (client, "input",
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsInput, 0);
output_port = jack_port_register (client, "output",
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);

if ((input_port == NULL) || (output_port == NULL)) {
fprintf(stderr, "no more JACK ports available\n");
exit (1);
}

/* Tell the JACK server that we are ready to roll. Our
* process() callback will start running now. */

if (jack_activate (client)) {
fprintf (stderr, "cannot activate client");
exit (1);
}

/* Connect the ports. You can't do this before the client is
* activated, because we can't make connections to clients
* that aren't running. Note the confusing (but necessary)
* orientation of the driver backend ports: playback ports are
* "input" to the backend, and capture ports are "output" from
* it.
*/


/* only do the autoconnect when not reloading from a session.
* in case of a session reload, the SM will restore our connections
*/

if (argc==1) {

ports = jack_get_ports (client, NULL, NULL,
JackPortIsPhysical|JackPortIsOutput);
if (ports == NULL) {
fprintf(stderr, "no physical capture ports\n");
exit (1);
}

if (jack_connect (client, ports[0], jack_port_name (input_port))) {
fprintf (stderr, "cannot connect input ports\n");
}

free (ports);

ports = jack_get_ports (client, NULL, NULL,
JackPortIsPhysical|JackPortIsInput);
if (ports == NULL) {
fprintf(stderr, "no physical playback ports\n");
exit (1);
}

if (jack_connect (client, jack_port_name (output_port), ports[0])) {
fprintf (stderr, "cannot connect output ports\n");
}

free (ports);
}

/* keep running until until we get a quit event */

while (!simple_quit)
sleep(1);

jack_client_close (client);
exit (0);
}

+ 2
- 0
example-clients/wscript View File

@@ -22,6 +22,8 @@ example_programs = {
'jack_monitor_client' : 'monitor_client.c',
'jack_thru' : 'thru_client.c',
'jack_cpu_load' : 'cpu_load.c',
'jack_simple_session_client' : 'simple_session_client.c',
'jack_session_notify' : 'session_notify.c',
'jack_server_control' : 'server_control.cpp',
}



+ 18
- 3
posix/JackSocketClientChannel.cpp View File

@@ -246,14 +246,29 @@ void JackSocketClientChannel::SetFreewheel(int onoff, int* result)
ServerSyncCall(&req, &res, result);
}

void JackSocketClientChannel::SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char* path, int* result)
void JackSocketClientChannel::SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char* path, jack_session_command_t ** result)
{
JackSessionNotifyRequest req(refnum, target, type, path);
JackResult res;
JackSessionNotifyResult res;
int intresult;
ServerSyncCall(&req, &res, &intresult);

*result = 0;
jack_session_command_t *session_command = (jack_session_command_t *)malloc( sizeof(jack_session_command_t) * (res.fCommandList.size()+1) );
int i=0;
for (std::list<JackSessionCommand>::iterator ci=res.fCommandList.begin(); ci!=res.fCommandList.end(); ci++) {
session_command[i].uuid = strdup( ci->fUUID );
session_command[i].client_name = strdup( ci->fClientName );
session_command[i].command = strdup( ci->fCommand );
session_command[i].flags = ci->fFlags;

i+=1;
}
session_command[i].uuid = NULL;


*result = session_command;
}

void JackSocketClientChannel::ReleaseTimebase(int refnum, int* result)


+ 1
- 1
posix/JackSocketClientChannel.h View File

@@ -95,7 +95,7 @@ class JackSocketClientChannel : public detail::JackClientChannelInterface, publi
bool Init();
bool Execute();

void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char* path, int* result);
void SessionNotify(int refnum, const char* target, jack_session_event_type_t type, const char* path, jack_session_command_t** result);
};

} // end of namespace


+ 1
- 3
posix/JackSocketServerChannel.cpp View File

@@ -405,10 +405,8 @@ bool JackSocketServerChannel::HandleRequest(int fd)
JackSessionNotifyRequest req;
JackSessionNotifyResult res;
if (req.Read(socket) == 0) {
fServer->GetEngine()->SessionNotify(req.fRefNum, req.fDst, req.fEventType, req.fPath, &res.fStatus);
fServer->GetEngine()->SessionNotify(req.fRefNum, req.fDst, req.fEventType, req.fPath, socket);
}
if (res.Write(socket) < 0)
jack_error("JackRequest::SessionNotify write error ref = %d", req.fRefNum);
break;
}



Loading…
Cancel
Save