Commit 3c0d95db authored by Torben Hohn's avatar Torben Hohn
Browse files

some progress on jack-session

parent cffa5dfd
......@@ -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);
}
......@@ -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)
{}
};
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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 );
};
......
......@@ -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
}
......
......@@ -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;
}
......
......@@ -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)
......
/*
* 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;
}
/** @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);
}