git-svn-id: svn+ssh://jackaudio.org/trunk/jack@666 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
| @@ -15,7 +15,7 @@ dnl changes are made | |||
| dnl --- | |||
| JACK_MAJOR_VERSION=0 | |||
| JACK_MINOR_VERSION=95 | |||
| JACK_MICRO_VERSION=9 | |||
| JACK_MICRO_VERSION=10 | |||
| dnl --- | |||
| dnl HOWTO: updating the jack protocol version | |||
| @@ -4,6 +4,10 @@ MAINTAINERCLEANFILES=Makefile.in | |||
| CLEANFILES=doxygen-build.stamp | |||
| DOX=reference.doxygen | |||
| DOXSOURCES=mainpage.dox transport.dox fsm.png \ | |||
| ../jack/jack.h ../jack/types.h ../jack/transport.h \ | |||
| ../jack/ringbuffer.h ../jack/port.h \ | |||
| ../example-clients/simple_client.c ../example-clients/inprocess.c | |||
| EXTRA_DIST=mainpage.dox transport.dox fsm.png | |||
| @@ -15,9 +19,7 @@ DOC_DIR=$(HTML_DIR) | |||
| all-local: doxygen-build.stamp | |||
| doxygen-build.stamp: $(DOX) mainpage.dox transport.dox fsm.png \ | |||
| ../jack/jack.h ../jack/types.h ../jack/transport.h ../jack/ringbuffer.h ../jack/port.h \ | |||
| ../example-clients/simple_client.c | |||
| doxygen-build.stamp: $(DOX) $(DOXSOURCES) | |||
| @echo '*** Running doxygen ***' | |||
| doxygen $(DOX) | |||
| touch doxygen-build.stamp | |||
| @@ -72,9 +72,11 @@ of just: | |||
| data. | |||
| There is a lot more that you can do with JACK's interfaces, but for | |||
| many applications, this is all that is needed. This <simple_client.c> | |||
| demonstrates a complete (simple!) JACK application that just copies | |||
| the signal arriving at its input port to its output port. | |||
| many applications, this is all that is needed. The @ref | |||
| simple_client.c example demonstrates a complete (simple!) JACK | |||
| application that just copies the signal arriving at its input port to | |||
| its output port. Similarly, @ref inprocess.c shows how to write an | |||
| internal client "plugin" that runs within the JACK server process. | |||
| @section reference Reference | |||
| @@ -90,7 +92,7 @@ The JACK programming interfaces are described in several header files: | |||
| media software. It is critical for use in applications that do disk | |||
| I/O such as audio file players and recording software. | |||
| In addition, the example_clients directory provides numerous examples | |||
| In addition, the example-clients directory provides numerous examples | |||
| of simple JACK clients that nevertheless use the API to do something | |||
| useful. It includes | |||
| @@ -368,7 +368,8 @@ INPUT = @top_srcdir@/doc/mainpage.dox \ | |||
| @top_srcdir@/jack/types.h \ | |||
| @top_srcdir@/jack/transport.h \ | |||
| @top_srcdir@/jack/ringbuffer.h \ | |||
| @top_srcdir@/example-clients/simple_client.c | |||
| @top_srcdir@/example-clients/simple_client.c \ | |||
| @top_srcdir@/example-clients/inprocess.c | |||
| # If the value of the INPUT tag contains directories, you can use the | |||
| # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp | |||
| @@ -1,52 +1,108 @@ | |||
| /** @file inprocess.c | |||
| * | |||
| * @brief This demonstrates the basic concepts for writing a client | |||
| * that runs within the JACK server process. | |||
| * | |||
| * For the sake of example, a port_pair_t is allocated in | |||
| * jack_initialize(), passed to process() as an argument, then freed | |||
| * in jack_finish(). | |||
| */ | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| #include <memory.h> | |||
| #include <jack/jack.h> | |||
| jack_port_t *input_port; | |||
| jack_port_t *output_port; | |||
| /** | |||
| * For the sake of example, an instance of this struct is allocated in | |||
| * jack_initialize(), passed to process() as an argument, then freed | |||
| * in jack_finish(). | |||
| */ | |||
| typedef struct { | |||
| jack_port_t *input_port; | |||
| jack_port_t *output_port; | |||
| } port_pair_t; | |||
| static int | |||
| /** | |||
| * Called in the realtime thread on every process cycle. The entry | |||
| * point name was passed to jack_set_process_callback() by | |||
| * jack_initialize(). | |||
| * | |||
| * @return 0 if successful; otherwise jack_finish() will be called and | |||
| * the client terminated immediately. | |||
| */ | |||
| int | |||
| process (jack_nframes_t nframes, void *arg) | |||
| { | |||
| jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port, nframes); | |||
| jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) jack_port_get_buffer (input_port, nframes); | |||
| port_pair_t *pp = arg; | |||
| jack_default_audio_sample_t *out = | |||
| jack_port_get_buffer (pp->output_port, nframes); | |||
| jack_default_audio_sample_t *in = | |||
| jack_port_get_buffer (pp->input_port, nframes); | |||
| memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes); | |||
| return 0; | |||
| return 0; /* continue */ | |||
| } | |||
| /** | |||
| * This required entry point is called after the client is loaded by | |||
| * jack_internal_client_new(). | |||
| * | |||
| * @param client pointer to JACK client structure. | |||
| * @param so_data character string passed from jack_internal_client_new(). | |||
| * | |||
| * @return 0 if successful; otherwise jack_finish() will be called and | |||
| * the client terminated immediately. | |||
| */ | |||
| int | |||
| jack_initialize (jack_client_t *client, const char *data) | |||
| jack_initialize (jack_client_t *client, const char *so_data) | |||
| { | |||
| jack_set_process_callback (client, process, 0); | |||
| port_pair_t *pp = malloc (sizeof (port_pair_t)); | |||
| /* create two ports */ | |||
| if (pp == NULL) | |||
| return 1; /* heap exhausted */ | |||
| 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); | |||
| jack_set_process_callback (client, process, pp); | |||
| /* start the client */ | |||
| /* create a pair of ports */ | |||
| pp->input_port = jack_port_register (client, "input", | |||
| JACK_DEFAULT_AUDIO_TYPE, | |||
| JackPortIsInput, 0); | |||
| pp->output_port = jack_port_register (client, "output", | |||
| JACK_DEFAULT_AUDIO_TYPE, | |||
| JackPortIsOutput, 0); | |||
| /* join the process() cycle */ | |||
| jack_activate (client); | |||
| /* connect the ports */ | |||
| if (jack_connect (client, "alsa_pcm:capture_1", jack_port_name (input_port))) { | |||
| if (jack_connect (client, "alsa_pcm:capture_1", | |||
| jack_port_name (pp->input_port))) { | |||
| fprintf (stderr, "cannot connect input port\n"); | |||
| return 1; /* terminate client */ | |||
| } | |||
| if (jack_connect (client, jack_port_name (output_port), "alsa_pcm:playback_1")) { | |||
| if (jack_connect (client, jack_port_name (pp->output_port), | |||
| "alsa_pcm:playback_1")) { | |||
| fprintf (stderr, "cannot connect output port\n"); | |||
| return 1; /* terminate client */ | |||
| } | |||
| /* our client is running. we're happy */ | |||
| return 0; | |||
| return 0; /* success */ | |||
| } | |||
| /** | |||
| * This required entry point is called immediately before the client | |||
| * is unloaded, which could happen due to a call to | |||
| * jack_internal_client_close(), or a nonzero return from either | |||
| * jack_initialize() or process(). | |||
| * | |||
| * @param arg the same parameter provided to process(). | |||
| */ | |||
| void | |||
| jack_finish (void) | |||
| jack_finish (void *arg) | |||
| { | |||
| /* relax */ | |||
| if (arg) | |||
| free ((port_pair_t *) arg); | |||
| } | |||
| @@ -142,17 +142,17 @@ jack_initialize (jack_client_t *client, const char *arg) | |||
| if (jack_set_timebase_callback(client, 0, callback, NULL) != 0) { | |||
| fprintf (stderr, "Unable to take over timebase.\n"); | |||
| return 1; | |||
| return 1; /* terminate */ | |||
| } | |||
| fprintf (stderr, "Internal timebase master defined.\n"); | |||
| jack_activate (client); | |||
| return 0; | |||
| return 0; /* success */ | |||
| } | |||
| /* before unloading */ | |||
| void | |||
| jack_finish (void) | |||
| jack_finish (void *arg) | |||
| { | |||
| fprintf (stderr, "Internal timebase client exiting.\n"); | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| /** @file simple_client.c | |||
| * | |||
| * @brief This is very simple client that demonstrates the basic | |||
| * features of JACK as they would be used by many applications. | |||
| * @brief This simple client demonstrates the basic features of JACK | |||
| * as they would be used by many applications. | |||
| */ | |||
| #include <stdio.h> | |||
| @@ -64,11 +64,17 @@ int jack_client_name_size(void); | |||
| /** | |||
| * Attempt to load an internal client into the Jack server. | |||
| * | |||
| * Internal clients run within the JACK server process. They can use | |||
| * of the same functions as external clients. Each internal client | |||
| * must declare jack_initialize() and jack_finish() entry points, | |||
| * called at load and unload times. See inprocess.c for an example of | |||
| * how to write an internal client. | |||
| * | |||
| * @param client_name of at most jack_client_name_size() characters. | |||
| * @param so_name A path to a shared object file containing the code | |||
| * for the new client | |||
| * for the new client. | |||
| * @param so_data An arbitary string containing information to be | |||
| * passed to the init() routine of the new client | |||
| * passed to the jack_initialize() routine of the new client. | |||
| */ | |||
| int jack_internal_client_new (const char *client_name, const char *so_name, | |||
| const char *so_data); | |||
| @@ -88,8 +88,8 @@ static void jack_remove_client (jack_engine_t *engine, | |||
| jack_client_internal_t *client); | |||
| static void jack_client_delete (jack_engine_t *, jack_client_internal_t *); | |||
| static jack_client_internal_t | |||
| *jack_client_internal_new (jack_engine_t *engine, int fd, | |||
| jack_client_connect_request_t *); | |||
| *jack_setup_client_control (jack_engine_t *engine, int fd, | |||
| jack_client_connect_request_t *); | |||
| static void jack_sort_graph (jack_engine_t *engine); | |||
| static int jack_rechain_graph (jack_engine_t *engine); | |||
| static int jack_get_fifo_fd (jack_engine_t *engine, unsigned int which_fifo); | |||
| @@ -1006,7 +1006,7 @@ setup_client (jack_engine_t *engine, int client_fd, | |||
| } | |||
| } | |||
| if ((client = jack_client_internal_new (engine, client_fd, req)) | |||
| if ((client = jack_setup_client_control (engine, client_fd, req)) | |||
| == NULL) { | |||
| jack_error ("cannot create new client object"); | |||
| return 0; | |||
| @@ -1030,16 +1030,15 @@ setup_client (jack_engine_t *engine, int client_fd, | |||
| #endif | |||
| if (jack_client_is_internal(client)) { | |||
| /* set up the pointers necessary for the request system | |||
| to work. | |||
| */ | |||
| /* set up the pointers necessary for the request | |||
| * system to work. */ | |||
| client->control->deliver_request = internal_client_request; | |||
| client->control->deliver_arg = engine; | |||
| /* the client is in the same address space */ | |||
| res->client_control = client->control; | |||
| res->engine_control = engine->control; | |||
| @@ -1047,10 +1046,9 @@ setup_client (jack_engine_t *engine, int client_fd, | |||
| strcpy (res->fifo_prefix, engine->fifo_prefix); | |||
| } | |||
| /* add new client to the clients list */ | |||
| jack_lock_graph (engine); | |||
| engine->clients = jack_slist_prepend (engine->clients, client); | |||
| engine->clients = jack_slist_prepend (engine->clients, client); | |||
| jack_engine_reset_rolling_usecs (engine); | |||
| switch (client->control->type) { | |||
| @@ -1072,8 +1070,15 @@ setup_client (jack_engine_t *engine, int client_fd, | |||
| if (client->initialize (client->control->private_client, | |||
| req->object_data)) { | |||
| jack_client_delete (engine, client); | |||
| return 0; | |||
| /* failed: clean up client data */ | |||
| VERBOSE (engine, | |||
| "%s jack_initialize() failed!\n", | |||
| client->control->name); | |||
| jack_lock_graph (engine); | |||
| jack_remove_client (engine, client); | |||
| jack_unlock_graph (engine); | |||
| return NULL; | |||
| } | |||
| } | |||
| @@ -2482,9 +2487,11 @@ jack_engine_delete (jack_engine_t *engine) | |||
| return 0; | |||
| } | |||
| /* Set up the engine's client internal and control structures for both | |||
| * internal and external clients. */ | |||
| static jack_client_internal_t * | |||
| jack_client_internal_new (jack_engine_t *engine, int fd, | |||
| jack_client_connect_request_t *req) | |||
| jack_setup_client_control (jack_engine_t *engine, int fd, | |||
| jack_client_connect_request_t *req) | |||
| { | |||
| jack_client_internal_t *client; | |||
| @@ -2522,14 +2529,15 @@ jack_client_internal_new (jack_engine_t *engine, int fd, | |||
| } | |||
| if (jack_attach_shm (&client->control_shm)) { | |||
| jack_error ("cannot attach to client control block for %s (%s)", | |||
| req->name, strerror (errno)); | |||
| jack_error ("cannot attach to client control block " | |||
| "for %s (%s)", req->name, strerror (errno)); | |||
| jack_destroy_shm (&client->control_shm); | |||
| free (client); | |||
| return 0; | |||
| } | |||
| client->control = (jack_client_control_t *) jack_shm_addr (&client->control_shm); | |||
| client->control = (jack_client_control_t *) | |||
| jack_shm_addr (&client->control_shm); | |||
| } | |||
| client->control->type = req->type; | |||
| @@ -2567,7 +2575,7 @@ jack_client_internal_new (jack_engine_t *engine, int fd, | |||
| jack_error ("cannot dynamically load client from" | |||
| " \"%s\"", req->object_path); | |||
| jack_client_delete (engine, client); | |||
| return 0; | |||
| return NULL; | |||
| } | |||
| } | |||
| @@ -2628,6 +2636,7 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client) | |||
| } | |||
| /* try to force the server thread to return from poll */ | |||
| /* JOQ: why do this for internal clients? */ | |||
| close (client->event_fd); | |||
| close (client->request_fd); | |||
| @@ -2677,7 +2686,8 @@ jack_client_delete (jack_engine_t *engine, jack_client_internal_t *client) | |||
| if (jack_client_is_internal (client)) { | |||
| jack_client_unload (client); | |||
| free ((char *) client->control); | |||
| free (client->control->private_client); | |||
| free ((void *) client->control); | |||
| } else { | |||
| @@ -176,6 +176,9 @@ jack_client_alloc () | |||
| return client; | |||
| } | |||
| /* | |||
| * Build the jack_client_t structure for an internal client. | |||
| */ | |||
| jack_client_t * | |||
| jack_client_alloc_internal (jack_client_control_t *cc, jack_engine_t* engine) | |||
| { | |||