/****************************************************************************** * recordMyDesktop * ******************************************************************************* * * * Copyright (C) 2006,2007,2008 John Varouhakis * * * * * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * * * * * * For further information contact me at johnvarouhakis@gmail.com * ******************************************************************************/ #include "config.h" #include "rmd_jack.h" #include "rmd_types.h" #include #include #ifdef HAVE_LIBJACK /** * Callback for capture through jack * * \param nframes Number of frames captured * * \param jdata_t Pointer to JackData struct containing port * and client information * * \returns Zero always */ static int rmdJackCapture(jack_nframes_t nframes, void *jdata_t) { JackData *jdata = (JackData *)jdata_t; if (!jdata->pdata->running || jdata->pdata->paused || !jdata->capture_started) return 0; for(int i = 0; i < jdata->nports; i++) jdata->portbuf[i] = jack_port_get_buffer(jdata->ports[i], nframes); pthread_mutex_lock(jdata->sound_buffer_mutex); //vorbis analysis buffer wants uninterleaved data //so we are simply placing the buffers for every channel //sequentially on the ringbuffer for(int i = 0; i < jdata->nports; i++) jack_ringbuffer_write( jdata->sound_buffer, (void *)(jdata->portbuf[i]), nframes * sizeof(jack_default_audio_sample_t)); pthread_cond_signal(jdata->sound_data_read); pthread_mutex_unlock(jdata->sound_buffer_mutex); return 0; } /** * Register and Activate specified ports * * \param jdata_t Pointer to JackData struct containing port * and client information * * \returns 0 on Success, 1 on failure */ static int rmdSetupPorts(JackData *jdata) { jdata->ports = malloc(sizeof(jack_port_t *) * jdata->nports); jdata->portbuf = malloc(sizeof(jack_default_audio_sample_t *) * jdata->nports); memset(jdata->portbuf, 0, sizeof(jack_default_audio_sample_t *) * jdata->nports); for(int i = 0; i < jdata->nports; i++) { char name[64];//recordMyDesktop:input_n<64 is enough for full name char num[8]; strcpy(name, "input_"); snprintf(num, 8, "%d", i + 1); strcat(name, num); jdata->ports[i] = jack_port_register( jdata->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); if (!jdata->ports[i]) { fprintf(stderr, "Cannot register input port \"%s\"!\n", name); return 1; } if (jack_connect(jdata->client, jdata->port_names[i], jack_port_name(jdata->ports[i]))) { fprintf(stderr, "Cannot connect input port %s to %s\n", jack_port_name(jdata->ports[i]), jdata->port_names[i]); return 1; } } return 0; } //in case the jack server shuts down //the program should stop recording, //encode the result(if not on the fly) //an exit cleanly. static void rmdJackShutdown(void *jdata_t) { JackData *jdata = (JackData *)jdata_t; jdata->pdata->running = FALSE; fprintf (stderr, "JACK shutdown\n"); } int rmdStartJackClient(JackData *jdata) { float ring_buffer_size = 0.0; int pid; char pidbuf[8]; char rmd_client_name[32]; //construct the jack client name //which is recordMyDesktop-pid //in order to allow multiple //instances of recordMyDesktop //to connetc to a Jack Server strcpy(rmd_client_name, "recordMyDesktop-"); pid = getpid(); snprintf( pidbuf, 8, "%d", pid ); strcat(rmd_client_name, pidbuf); if ((jdata->client = jack_client_new(rmd_client_name)) == 0) { fprintf(stderr, "Could not create new client!\n" "Make sure that Jack server is running!\n"); return 15; } //in contrast to ALSA and OSS, Jack dictates frequency //and buffersize to the values it was launched with. //Supposedly jack_set_buffer_size can set the buffersize //but that causes some kind of halt and keeps giving me //zero buffers. //recordMyDesktop cannot handle buffer size changes. //FIXME //There is a callback for buffer size changes that I should use. //It will provide clean exits, instead of ?segfaults? . //Continuing though is not possible, with the current layout //(it might be in some cases, but it will certainly be the cause //of unpredicted problems). A clean exit is preferable //and any recording up to that point will be encoded and saved. jdata->frequency = jack_get_sample_rate(jdata->client); jdata->buffersize = jack_get_buffer_size(jdata->client); ring_buffer_size = ( jdata->ringbuffer_secs* jdata->frequency* sizeof(jack_default_audio_sample_t)* jdata->nports); jdata->sound_buffer = jack_ringbuffer_create((int)(ring_buffer_size+0.5));//round up jack_set_process_callback(jdata->client, rmdJackCapture, jdata); jack_on_shutdown(jdata->client, rmdJackShutdown, jdata); if (jack_activate(jdata->client)) { fprintf(stderr, "cannot activate client!\n"); return 16; } if (rmdSetupPorts(jdata)) { jack_client_close(jdata->client); return 17; } return 0; } int rmdStopJackClient(JackData *jdata) { int ret = 0; jack_ringbuffer_free(jdata->sound_buffer); if (jack_client_close(jdata->client)) { fprintf(stderr, "Cannot close Jack client!\n"); ret = 1; } /*TODO*/ //I need to make some kind of program/thread //flow diagram to see where it's safe to dlclose //because here it causes a segfault. // if (dlclose(jdata->jack_lib_handle)) { // fprintf(stderr, "Cannot unload Jack library!\n"); // ret=1; // } // else fprintf(stderr, "Unloaded Jack library.\n"); return ret; } #endif