/* * FireWire Backend for Jack * using FFADO * FFADO = Firewire (pro-)audio for linux * * http://www.ffado.org * http://www.jackaudio.org * * Copyright (C) 2005-2007 Pieter Palmers * Copyright (C) 2012 Jonathan Woithe * * 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. */ /* * Main Jack driver entry routines * */ #include #include #include #include #include #include #include #include #include #include "internal.h" #include "engine.h" #include "libjack/local.h" #include #include #include "ffado_driver.h" #define SAMPLE_MAX_24BIT 8388608.0f #define SAMPLE_MAX_16BIT 32768.0f static int ffado_driver_stop(ffado_driver_t *driver); // Basic functionality requires API version 8. If version 9 or later // is present the buffers can be resized at runtime. #define FIREWIRE_REQUIRED_FFADO_API_VERSION 8 #define FIREWIRE_REQUIRED_FFADO_API_VERSION_FOR_SETBUFSIZE 9 /* FFADO_API_VERSION was first defined with API_VERSION 9, so all previous * headers do not provide this define. */ #ifndef FFADO_API_VERSION extern int ffado_streaming_set_period_size(ffado_device_t *dev, unsigned int period) __attribute__((__weak__)); #endif // enable verbose messages static int g_verbose = 0; static void ffado_latency_callback (jack_latency_callback_mode_t mode, void* arg) { ffado_driver_t* driver = (ffado_driver_t*)arg; jack_client_t* client = driver->client; jack_latency_range_t range; JSList* node; if (mode == JackPlaybackLatency) { range.min = range.max = (driver->period_size * (driver->device_options.nb_buffers - 1)); } else { range.min = range.max = driver->period_size + driver->capture_frame_latency; } for (node = client->ports; node; node = jack_slist_next (node)) jack_port_set_latency_range ((jack_port_t*)node->data, mode, &range); } static int ffado_driver_attach (ffado_driver_t *driver) { char buf[64]; char buf2[64]; channel_t chn; jack_port_t *port = NULL; int port_flags; g_verbose = driver->engine->verbose; if (driver->engine->set_buffer_size (driver->engine, driver->period_size)) { jack_error ("FFADO: cannot set engine buffer size to %d (check MIDI)", driver->period_size); return -1; } driver->engine->set_sample_rate (driver->engine, driver->sample_rate); /* preallocate some buffers such that they don't have to be allocated in RT context (or from the stack) */ /* the null buffer is a buffer that contains one period of silence */ driver->nullbuffer = calloc (driver->period_size, sizeof(ffado_sample_t)); if (driver->nullbuffer == NULL) { printError ("could not allocate memory for null buffer"); return -1; } /* calloc should do this, but it can't hurt to be sure */ memset (driver->nullbuffer, 0, driver->period_size * sizeof(ffado_sample_t)); /* the scratch buffer is a buffer of one period that can be used as dummy memory */ driver->scratchbuffer = calloc (driver->period_size, sizeof(ffado_sample_t)); if (driver->scratchbuffer == NULL) { printError ("could not allocate memory for scratch buffer"); return -1; } /* packetizer thread options */ driver->device_options.realtime = (driver->engine->control->real_time ? 1 : 0); driver->device_options.packetizer_priority = driver->engine->rtpriority; if (driver->device_options.packetizer_priority > 98) { driver->device_options.packetizer_priority = 98; } if (driver->device_options.packetizer_priority < 1) { driver->device_options.packetizer_priority = 1; } driver->dev = ffado_streaming_init (driver->device_info, driver->device_options); if (!driver->dev) { printError ("Error creating FFADO streaming device"); return -1; } if (driver->device_options.realtime) { printMessage ("Streaming thread running with Realtime scheduling, priority %d", driver->device_options.packetizer_priority); } else { printMessage ("Streaming thread running without Realtime scheduling"); } ffado_streaming_set_audio_datatype (driver->dev, ffado_audio_datatype_float); /* ports */ port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; driver->capture_nchannels = ffado_streaming_get_nb_capture_streams (driver->dev); driver->capture_channels = calloc (driver->capture_nchannels, sizeof(ffado_capture_channel_t)); if (driver->capture_channels == NULL) { printError ("could not allocate memory for capture channel list"); return -1; } for (chn = 0; chn < driver->capture_nchannels; chn++) { ffado_streaming_get_capture_stream_name (driver->dev, chn, buf, sizeof(buf) - 1); driver->capture_channels[chn].stream_type = ffado_streaming_get_capture_stream_type (driver->dev, chn); if (driver->capture_channels[chn].stream_type == ffado_stream_type_audio) { snprintf (buf2, 64, "C%d_%s", (int)chn, buf); // needed to avoid duplicate names printMessage ("Registering audio capture port %s", buf2); if ((port = jack_port_register (driver->client, buf2, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0)) == NULL) { printError (" cannot register port for %s", buf2); break; } driver->capture_ports = jack_slist_append (driver->capture_ports, port); // setup port parameters if (ffado_streaming_set_capture_stream_buffer (driver->dev, chn, NULL)) { printError (" cannot configure initial port buffer for %s", buf2); } if (ffado_streaming_capture_stream_onoff (driver->dev, chn, 1)) { printError (" cannot enable port %s", buf2); } } else if (driver->capture_channels[chn].stream_type == ffado_stream_type_midi) { snprintf (buf2, 64, "C%d_%s", (int)chn, buf); // needed to avoid duplicate names printMessage ("Registering midi capture port %s", buf2); if ((port = jack_port_register (driver->client, buf2, JACK_DEFAULT_MIDI_TYPE, port_flags, 0)) == NULL) { printError (" cannot register port for %s", buf2); break; } driver->capture_ports = jack_slist_append (driver->capture_ports, port); // setup port parameters if (ffado_streaming_set_capture_stream_buffer (driver->dev, chn, NULL)) { printError (" cannot configure initial port buffer for %s", buf2); } if (ffado_streaming_capture_stream_onoff (driver->dev, chn, 1)) { printError (" cannot enable port %s", buf2); } // setup midi unpacker midi_unpack_init (&driver->capture_channels[chn].midi_unpack); midi_unpack_reset (&driver->capture_channels[chn].midi_unpack); // setup the midi buffer driver->capture_channels[chn].midi_buffer = calloc (driver->period_size, sizeof(uint32_t)); } else { printMessage ("Don't register capture port %s", buf); // we have to add a NULL entry in the list to be able to loop over the channels in the read/write routines driver->capture_ports = jack_slist_append (driver->capture_ports, NULL); } } port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; driver->playback_nchannels = ffado_streaming_get_nb_playback_streams (driver->dev); driver->playback_channels = calloc (driver->playback_nchannels, sizeof(ffado_playback_channel_t)); if (driver->playback_channels == NULL) { printError ("could not allocate memory for playback channel list"); return -1; } for (chn = 0; chn < driver->playback_nchannels; chn++) { ffado_streaming_get_playback_stream_name (driver->dev, chn, buf, sizeof(buf) - 1); driver->playback_channels[chn].stream_type = ffado_streaming_get_playback_stream_type (driver->dev, chn); if (driver->playback_channels[chn].stream_type == ffado_stream_type_audio) { snprintf (buf2, 64, "P%d_%s", (int)chn, buf); // needed to avoid duplicate names printMessage ("Registering audio playback port %s", buf2); if ((port = jack_port_register (driver->client, buf2, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0)) == NULL) { printError (" cannot register port for %s", buf2); break; } driver->playback_ports = jack_slist_append (driver->playback_ports, port); // setup port parameters if (ffado_streaming_set_playback_stream_buffer (driver->dev, chn, NULL)) { printError (" cannot configure initial port buffer for %s", buf2); } if (ffado_streaming_playback_stream_onoff (driver->dev, chn, 1)) { printError (" cannot enable port %s", buf2); } } else if (driver->playback_channels[chn].stream_type == ffado_stream_type_midi) { snprintf (buf2, 64, "P%d_%s", (int)chn, buf); // needed to avoid duplicate names printMessage ("Registering midi playback port %s", buf2); if ((port = jack_port_register (driver->client, buf2, JACK_DEFAULT_MIDI_TYPE, port_flags, 0)) == NULL) { printError (" cannot register port for %s", buf2); break; } driver->playback_ports = jack_slist_append (driver->playback_ports, port); // setup port parameters if (ffado_streaming_set_playback_stream_buffer (driver->dev, chn, NULL)) { printError (" cannot configure initial port buffer for %s", buf2); } if (ffado_streaming_playback_stream_onoff (driver->dev, chn, 1)) { printError (" cannot enable port %s", buf2); } // setup midi packer midi_pack_reset (&driver->playback_channels[chn].midi_pack); // setup the midi buffer driver->playback_channels[chn].midi_buffer = calloc (driver->period_size, sizeof(uint32_t)); } else { printMessage ("Don't register playback port %s", buf); // we have to add a NULL entry in the list to be able to loop over the channels in the read/write routines driver->playback_ports = jack_slist_append (driver->playback_ports, NULL); } } if (ffado_streaming_prepare (driver->dev)) { printError ("Could not prepare streaming device!"); return -1; } return jack_activate (driver->client); } static int ffado_driver_detach (ffado_driver_t *driver) { JSList *node; unsigned int chn; if (driver->engine == NULL) { return 0; } for (node = driver->capture_ports; node; node = jack_slist_next (node)) { // Don't try to unregister NULL entries added for non-audio // ffado ports by ffado_driver_attach(). if (node->data != NULL) { jack_port_unregister (driver->client, ((jack_port_t*)node->data)); } } jack_slist_free (driver->capture_ports); driver->capture_ports = 0; for (node = driver->playback_ports; node; node = jack_slist_next (node)) { if (node->data != NULL) { jack_port_unregister (driver->client, ((jack_port_t*)node->data)); } } jack_slist_free (driver->playback_ports); driver->playback_ports = 0; ffado_streaming_finish (driver->dev); driver->dev = NULL; for (chn = 0; chn < driver->capture_nchannels; chn++) if (driver->capture_channels[chn].midi_buffer) { free (driver->capture_channels[chn].midi_buffer); } free (driver->capture_channels); for (chn = 0; chn < driver->playback_nchannels; chn++) if (driver->playback_channels[chn].midi_buffer) { free (driver->playback_channels[chn].midi_buffer); } free (driver->playback_channels); free (driver->nullbuffer); free (driver->scratchbuffer); return 0; } static int ffado_driver_read (ffado_driver_t * driver, jack_nframes_t nframes) { channel_t chn; int nb_connections; JSList *node; jack_port_t* port; printEnter (); for (chn = 0, node = driver->capture_ports; node; node = jack_slist_next (node), chn++) { if (driver->capture_channels[chn].stream_type == ffado_stream_type_audio) { port = (jack_port_t*)node->data; nb_connections = jack_port_connected (port); /* if there are no connections, use the dummy buffer and disable the stream */ if (nb_connections) { ffado_streaming_capture_stream_onoff (driver->dev, chn, 1); ffado_streaming_set_capture_stream_buffer (driver->dev, chn, (char*)(jack_port_get_buffer (port, nframes))); } else { ffado_streaming_capture_stream_onoff (driver->dev, chn, 0); ffado_streaming_set_capture_stream_buffer (driver->dev, chn, (char*)(driver->scratchbuffer)); } } else if (driver->capture_channels[chn].stream_type == ffado_stream_type_midi) { port = (jack_port_t*)node->data; nb_connections = jack_port_connected (port); if (nb_connections) { ffado_streaming_capture_stream_onoff (driver->dev, chn, 1); } else { ffado_streaming_capture_stream_onoff (driver->dev, chn, 0); } /* always set a buffer */ ffado_streaming_set_capture_stream_buffer (driver->dev, chn, (char*)(driver->capture_channels[chn].midi_buffer)); } else { /* ensure a valid buffer */ ffado_streaming_set_capture_stream_buffer (driver->dev, chn, (char*)(driver->scratchbuffer)); ffado_streaming_capture_stream_onoff (driver->dev, chn, 0); } } /* now transfer the buffers */ ffado_streaming_transfer_capture_buffers (driver->dev); /* process the midi data */ for (chn = 0, node = driver->capture_ports; node; node = jack_slist_next (node), chn++) { if (driver->capture_channels[chn].stream_type == ffado_stream_type_midi) { jack_default_audio_sample_t* buf; int i; int done; uint32_t *midi_buffer = driver->capture_channels[chn].midi_buffer; midi_unpack_t *midi_unpack = &driver->capture_channels[chn].midi_unpack; port = (jack_port_t*)node->data; nb_connections = jack_port_connected (port); buf = jack_port_get_buffer (port, nframes); /* if the returned buffer is invalid, discard the midi data */ jack_midi_clear_buffer (buf); /* no connections means no processing */ if (nb_connections == 0) { continue; } /* else unpack note that libffado guarantees that midi bytes are on 8-byte aligned indexes */ for (i = 0; i < nframes; i += 8) { if (midi_buffer[i] & 0xFF000000) { done = midi_unpack_buf (midi_unpack, (unsigned char*)(midi_buffer + i), 1, buf, i); if (done != 1) { printError ("MIDI buffer overflow in channel %d\n", chn); break; } } } } } printExit (); return 0; } static int ffado_driver_write (ffado_driver_t * driver, jack_nframes_t nframes) { channel_t chn; int nb_connections; JSList *node; jack_port_t *port; printEnter (); driver->process_count++; if (driver->engine->freewheeling) { return 0; } for (chn = 0, node = driver->playback_ports; node; node = jack_slist_next (node), chn++) { if (driver->playback_channels[chn].stream_type == ffado_stream_type_audio) { port = (jack_port_t*)node->data; nb_connections = jack_port_connected (port); /* use the silent buffer + disable if there are no connections */ if (nb_connections) { ffado_streaming_playback_stream_onoff (driver->dev, chn, 1); ffado_streaming_set_playback_stream_buffer (driver->dev, chn, (char*)(jack_port_get_buffer (port, nframes))); } else { ffado_streaming_playback_stream_onoff (driver->dev, chn, 0); ffado_streaming_set_playback_stream_buffer (driver->dev, chn, (char*)(driver->nullbuffer)); } } else if (driver->playback_channels[chn].stream_type == ffado_stream_type_midi) { jack_default_audio_sample_t* buf; int nevents; int i; midi_pack_t *midi_pack = &driver->playback_channels[chn].midi_pack; uint32_t *midi_buffer = driver->playback_channels[chn].midi_buffer; int min_next_pos = 0; port = (jack_port_t*)node->data; nb_connections = jack_port_connected (port); /* skip if no connections */ if (nb_connections == 0) { ffado_streaming_set_playback_stream_buffer (driver->dev, chn, (char*)(driver->nullbuffer)); ffado_streaming_playback_stream_onoff (driver->dev, chn, 0); continue; } memset (midi_buffer, 0, nframes * sizeof(uint32_t)); ffado_streaming_set_playback_stream_buffer (driver->dev, chn, (char*)(midi_buffer)); ffado_streaming_playback_stream_onoff (driver->dev, chn, 1); /* check if we still have to process bytes from the previous period */ /* if(driver->playback_channels[chn].nb_overflow_bytes) { printMessage("have to process %d bytes from previous period", driver->playback_channels[chn].nb_overflow_bytes); } */ for (i = 0; i < driver->playback_channels[chn].nb_overflow_bytes; ++i) { midi_buffer[min_next_pos] = 0x01000000 | (driver->playback_channels[chn].overflow_buffer[i] & 0xFF); min_next_pos += 8; } driver->playback_channels[chn].nb_overflow_bytes = 0; /* process the events in this period */ buf = jack_port_get_buffer (port, nframes); nevents = jack_midi_get_event_count (buf); for (i = 0; i < nevents; ++i) { int j; jack_midi_event_t event; jack_midi_event_get (&event, buf, i); midi_pack_event (midi_pack, &event); /* floor the initial position to be a multiple of 8 */ int pos = event.time & 0xFFFFFFF8; for (j = 0; j < event.size; j++) { /* make sure we don't overwrite a previous byte */ while (pos < min_next_pos && pos < nframes) /* printMessage("have to correct pos to %d", pos); */ pos += 8; if (pos >= nframes) { int f; /* printMessage("midi message crosses period boundary"); */ driver->playback_channels[chn].nb_overflow_bytes = event.size - j; if (driver->playback_channels[chn].nb_overflow_bytes > MIDI_OVERFLOW_BUFFER_SIZE) { printError ("too much midi bytes cross period boundary"); driver->playback_channels[chn].nb_overflow_bytes = MIDI_OVERFLOW_BUFFER_SIZE; } /* save the bytes that still have to be transmitted in the next period */ for (f = 0; f < driver->playback_channels[chn].nb_overflow_bytes; f++) driver->playback_channels[chn].overflow_buffer[f] = event.buffer[j + f]; /* exit since we can't transmit anything anymore. the rate should be controlled */ if (i < nevents - 1) { printError ("%d midi events lost due to period crossing", nevents - i - 1); } break; } else { midi_buffer[pos] = 0x01000000 | (event.buffer[j] & 0xFF); pos += 8; min_next_pos = pos; } } } } else { /* ensure a valid buffer */ ffado_streaming_set_playback_stream_buffer (driver->dev, chn, (char*)(driver->nullbuffer)); ffado_streaming_playback_stream_onoff (driver->dev, chn, 0); } } ffado_streaming_transfer_playback_buffers (driver->dev); printExit (); return 0; } //static inline jack_nframes_t static jack_nframes_t ffado_driver_wait (ffado_driver_t *driver, int extra_fd, int *status, float *delayed_usecs) { jack_time_t wait_enter; jack_time_t wait_ret; ffado_wait_response response; printEnter (); wait_enter = driver->engine->get_microseconds (); if (wait_enter > driver->wait_next) { /* * This processing cycle was delayed past the * next due interrupt! Do not account this as * a wakeup delay: */ driver->wait_next = 0; driver->wait_late++; } // *status = -2; interrupt // *status = -3; timeout // *status = -4; extra FD response = ffado_streaming_wait (driver->dev); wait_ret = driver->engine->get_microseconds (); if (driver->wait_next && wait_ret > driver->wait_next) { *delayed_usecs = wait_ret - driver->wait_next; } driver->wait_last = wait_ret; driver->wait_next = wait_ret + driver->period_usecs; driver->engine->transport_cycle_start (driver->engine, wait_ret); if (response == ffado_wait_ok) { // all good *status = 0; } else if (response == ffado_wait_xrun) { // xrun happened, but it's handled *status = 0; return 0; } else if (response == ffado_wait_error) { // an error happened (unhandled xrun) // this should be fatal *status = -1; return 0; } else if (response == ffado_wait_shutdown) { // we are to shutdown the ffado system // this should be fatal *status = -1; return 0; } else { // we don't know about this response code printError ("unknown wait response (%d) from ffado", response); // this should be fatal *status = -1; return 0; } driver->last_wait_ust = wait_ret; // FIXME: this should do something more useful *delayed_usecs = 0; printExit (); return driver->period_size; } static int ffado_driver_run_cycle (ffado_driver_t *driver) { jack_engine_t *engine = driver->engine; int wait_status = 0; float delayed_usecs = 0.0; jack_nframes_t nframes = ffado_driver_wait (driver, -1, &wait_status, &delayed_usecs); if ((wait_status < 0)) { printError ( "wait status < 0! (= %d)", wait_status); return -1; } if ((nframes == 0)) { /* we detected an xrun and restarted: notify * clients about the delay. */ printMessage ("xrun detected"); engine->delay (engine, delayed_usecs); return 0; } return engine->run_cycle (engine, nframes, delayed_usecs); } /* * in a null cycle we should discard the input and write silence to the outputs */ static int ffado_driver_null_cycle (ffado_driver_t* driver, jack_nframes_t nframes) { channel_t chn; JSList *node; ffado_streaming_stream_type stream_type; printEnter (); if (driver->engine->freewheeling) { return 0; } // write silence to buffer for (chn = 0, node = driver->playback_ports; node; node = jack_slist_next (node), chn++) { stream_type = ffado_streaming_get_playback_stream_type (driver->dev, chn); if (stream_type == ffado_stream_type_audio) { ffado_streaming_set_playback_stream_buffer (driver->dev, chn, (char*)(driver->nullbuffer)); } } ffado_streaming_transfer_playback_buffers (driver->dev); // read & discard from input ports for (chn = 0, node = driver->capture_ports; node; node = jack_slist_next (node), chn++) { stream_type = ffado_streaming_get_capture_stream_type (driver->dev, chn); if (stream_type == ffado_stream_type_audio) { ffado_streaming_set_capture_stream_buffer (driver->dev, chn, (char*)(driver->scratchbuffer)); } } ffado_streaming_transfer_capture_buffers (driver->dev); printExit (); return 0; } static int ffado_driver_start (ffado_driver_t *driver) { int retval = 0; if ((retval = ffado_streaming_start (driver->dev))) { printError ("Could not start streaming threads: %d", retval); return retval; } return 0; } static int ffado_driver_stop (ffado_driver_t *driver) { int retval = 0; if ((retval = ffado_streaming_stop (driver->dev))) { printError ("Could not stop streaming threads"); return retval; } return 0; } static int ffado_driver_bufsize (ffado_driver_t* driver, jack_nframes_t nframes) { signed int chn; // The speed of this function isn't critical; we can afford the // time to check the FFADO API version. if (ffado_get_api_version () < FIREWIRE_REQUIRED_FFADO_API_VERSION_FOR_SETBUFSIZE || ffado_streaming_set_period_size == NULL) { printError ("unsupported on current version of FFADO; please upgrade FFADO"); return -1; } driver->period_size = nframes; driver->period_usecs = (jack_time_t)floor ((((float)nframes) / driver->sample_rate) * 1000000.0f); // Reallocate the null and scratch buffers. driver->nullbuffer = calloc (driver->period_size, sizeof(ffado_sample_t)); if (driver->nullbuffer == NULL) { printError ("could not allocate memory for null buffer"); return -1; } driver->scratchbuffer = calloc (driver->period_size, sizeof(ffado_sample_t)); if (driver->scratchbuffer == NULL) { printError ("could not allocate memory for scratch buffer"); return -1; } // MIDI buffers need reallocating for (chn = 0; chn < driver->capture_nchannels; chn++) { if (driver->capture_channels[chn].stream_type == ffado_stream_type_midi) { // setup the midi buffer if (driver->capture_channels[chn].midi_buffer != NULL) { free (driver->capture_channels[chn].midi_buffer); } driver->capture_channels[chn].midi_buffer = calloc (driver->period_size, sizeof(uint32_t)); } } for (chn = 0; chn < driver->playback_nchannels; chn++) { if (driver->playback_channels[chn].stream_type == ffado_stream_type_midi) { if (driver->playback_channels[chn].midi_buffer != NULL) { free (driver->playback_channels[chn].midi_buffer); } driver->playback_channels[chn].midi_buffer = calloc (driver->period_size, sizeof(uint32_t)); } } // Notify FFADO of the period size change if (ffado_streaming_set_period_size (driver->dev, nframes) != 0) { printError ("could not alter FFADO device period size"); return -1; } // This is needed to give the shadow variables a chance to // properly update to the changes. sleep (1); /* tell the engine to change its buffer size */ if (driver->engine->set_buffer_size (driver->engine, nframes)) { jack_error ("FFADO: cannot set engine buffer size to %d (check MIDI)", nframes); return -1; } return 0; } typedef void (*JackDriverFinishFunction)(jack_driver_t *); ffado_driver_t * ffado_driver_new (jack_client_t * client, char *name, ffado_jack_settings_t *params) { ffado_driver_t *driver; if (ffado_get_api_version () < FIREWIRE_REQUIRED_FFADO_API_VERSION) { printError ("Incompatible libffado version! (%s)", ffado_get_version ()); return NULL; } printMessage ("Starting firewire backend (%s)", ffado_get_version ()); driver = calloc (1, sizeof(ffado_driver_t)); /* Setup the jack interfaces */ jack_driver_nt_init ((jack_driver_nt_t*)driver); driver->nt_attach = (JackDriverNTAttachFunction)ffado_driver_attach; driver->nt_detach = (JackDriverNTDetachFunction)ffado_driver_detach; driver->nt_start = (JackDriverNTStartFunction)ffado_driver_start; driver->nt_stop = (JackDriverNTStopFunction)ffado_driver_stop; driver->nt_run_cycle = (JackDriverNTRunCycleFunction)ffado_driver_run_cycle; driver->null_cycle = (JackDriverNullCycleFunction)ffado_driver_null_cycle; driver->write = (JackDriverReadFunction)ffado_driver_write; driver->read = (JackDriverReadFunction)ffado_driver_read; driver->nt_bufsize = (JackDriverNTBufSizeFunction)ffado_driver_bufsize; /* copy command line parameter contents to the driver structure */ memcpy (&driver->settings, params, sizeof(ffado_jack_settings_t)); /* prepare all parameters */ driver->sample_rate = params->sample_rate; driver->period_size = params->period_size; driver->last_wait_ust = 0; driver->period_usecs = (jack_time_t)floor ((((float)driver->period_size) * 1000000.0f) / driver->sample_rate); driver->client = client; driver->engine = NULL; jack_set_latency_callback (client, ffado_latency_callback, driver); memset (&driver->device_options, 0, sizeof(driver->device_options)); driver->device_options.sample_rate = params->sample_rate; driver->device_options.period_size = params->period_size; driver->device_options.nb_buffers = params->buffer_size; driver->capture_frame_latency = params->capture_frame_latency; driver->playback_frame_latency = params->playback_frame_latency; driver->device_options.slave_mode = params->slave_mode; driver->device_options.snoop_mode = params->snoop_mode; driver->device_options.verbose = params->verbose_level; driver->device_info.nb_device_spec_strings = 1; driver->device_info.device_spec_strings = calloc (1, sizeof(char *)); driver->device_info.device_spec_strings[0] = strdup (params->device_info); debugPrint (DEBUG_LEVEL_STARTUP, " Driver compiled on %s %s for FFADO %s (API version %d)", __DATE__, __TIME__, ffado_get_version (), ffado_get_api_version ()); debugPrint (DEBUG_LEVEL_STARTUP, " Created driver %s", name); debugPrint (DEBUG_LEVEL_STARTUP, " period_size: %d", driver->period_size); debugPrint (DEBUG_LEVEL_STARTUP, " period_usecs: %d", driver->period_usecs); debugPrint (DEBUG_LEVEL_STARTUP, " sample rate: %d", driver->sample_rate); return (ffado_driver_t*)driver; } static void ffado_driver_delete (ffado_driver_t *driver) { unsigned int i; jack_driver_nt_finish ((jack_driver_nt_t*)driver); for (i = 0; i < driver->device_info.nb_device_spec_strings; i++) free (driver->device_info.device_spec_strings[i]); free (driver->device_info.device_spec_strings); free (driver); } /* * dlopen plugin stuff */ const char driver_client_name[] = "firewire_pcm"; const jack_driver_desc_t * driver_get_descriptor () { jack_driver_desc_t * desc; jack_driver_param_desc_t * params; unsigned int i; desc = calloc (1, sizeof(jack_driver_desc_t)); strcpy (desc->name, "firewire"); desc->nparams = 11; params = calloc (desc->nparams, sizeof(jack_driver_param_desc_t)); desc->params = params; i = 0; strcpy (params[i].name, "device"); params[i].character = 'd'; params[i].type = JackDriverParamString; strcpy (params[i].value.str, "hw:0"); strcpy (params[i].short_desc, "The FireWire device to use."); strcpy (params[i].long_desc, "The FireWire device to use. Please consult the FFADO documentation for more info."); i++; strcpy (params[i].name, "period"); params[i].character = 'p'; params[i].type = JackDriverParamUInt; params[i].value.ui = 1024; strcpy (params[i].short_desc, "Frames per period"); strcpy (params[i].long_desc, params[i].short_desc); i++; strcpy (params[i].name, "nperiods"); params[i].character = 'n'; params[i].type = JackDriverParamUInt; params[i].value.ui = 3; strcpy (params[i].short_desc, "Number of periods of playback latency"); strcpy (params[i].long_desc, params[i].short_desc); i++; strcpy (params[i].name, "rate"); params[i].character = 'r'; params[i].type = JackDriverParamUInt; params[i].value.ui = 48000U; strcpy (params[i].short_desc, "Sample rate"); strcpy (params[i].long_desc, params[i].short_desc); i++; strcpy (params[i].name, "capture"); params[i].character = 'i'; params[i].type = JackDriverParamUInt; params[i].value.ui = 1U; strcpy (params[i].short_desc, "Provide capture ports."); strcpy (params[i].long_desc, params[i].short_desc); i++; strcpy (params[i].name, "playback"); params[i].character = 'o'; params[i].type = JackDriverParamUInt; params[i].value.ui = 1U; strcpy (params[i].short_desc, "Provide playback ports."); strcpy (params[i].long_desc, params[i].short_desc); i++; strcpy (params[i].name, "input-latency"); params[i].character = 'I'; params[i].type = JackDriverParamUInt; params[i].value.ui = 0; strcpy (params[i].short_desc, "Extra input latency (frames)"); strcpy (params[i].long_desc, params[i].short_desc); i++; strcpy (params[i].name, "output-latency"); params[i].character = 'O'; params[i].type = JackDriverParamUInt; params[i].value.ui = 0; strcpy (params[i].short_desc, "Extra output latency (frames)"); strcpy (params[i].long_desc, params[i].short_desc); i++; strcpy (params[i].name, "slave"); params[i].character = 'x'; params[i].type = JackDriverParamUInt; params[i].value.ui = 0U; strcpy (params[i].short_desc, "Act as a BounceDevice slave"); strcpy (params[i].long_desc, params[i].short_desc); i++; strcpy (params[i].name, "slave"); params[i].character = 'X'; params[i].type = JackDriverParamUInt; params[i].value.ui = 0U; strcpy (params[i].short_desc, "Operate in snoop mode"); strcpy (params[i].long_desc, params[i].short_desc); i++; strcpy (params[i].name, "verbose"); params[i].character = 'v'; params[i].type = JackDriverParamUInt; params[i].value.ui = 0U; strcpy (params[i].short_desc, "Verbose level for the firewire backend"); strcpy (params[i].long_desc, params[i].short_desc); return desc; } jack_driver_t * driver_initialize (jack_client_t *client, JSList * params) { jack_driver_t *driver; const JSList * node; const jack_driver_param_t * param; ffado_jack_settings_t cmlparams; char *device_name = "hw:0"; cmlparams.period_size_set = 0; cmlparams.sample_rate_set = 0; cmlparams.buffer_size_set = 0; /* default values */ cmlparams.period_size = 1024; cmlparams.sample_rate = 48000; cmlparams.buffer_size = 3; cmlparams.playback_ports = 1; cmlparams.capture_ports = 1; cmlparams.playback_frame_latency = 0; cmlparams.capture_frame_latency = 0; cmlparams.slave_mode = 0; cmlparams.snoop_mode = 0; cmlparams.verbose_level = 0; for (node = params; node; node = jack_slist_next (node)) { param = (jack_driver_param_t*)node->data; switch (param->character) { case 'd': device_name = strdup (param->value.str); break; case 'p': cmlparams.period_size = param->value.ui; cmlparams.period_size_set = 1; break; case 'n': cmlparams.buffer_size = param->value.ui; cmlparams.buffer_size_set = 1; break; case 'r': cmlparams.sample_rate = param->value.ui; cmlparams.sample_rate_set = 1; break; case 'i': cmlparams.capture_ports = param->value.ui; break; case 'o': cmlparams.playback_ports = param->value.ui; break; case 'I': cmlparams.capture_frame_latency = param->value.ui; break; case 'O': cmlparams.playback_frame_latency = param->value.ui; break; case 'x': cmlparams.slave_mode = param->value.ui; break; case 'X': cmlparams.snoop_mode = param->value.ui; break; case 'v': cmlparams.verbose_level = param->value.ui; break; } } // temporary cmlparams.device_info = device_name; driver = (jack_driver_t*)ffado_driver_new (client, "ffado_pcm", &cmlparams); return driver; } void driver_finish (jack_driver_t *driver) { ffado_driver_t *drv = (ffado_driver_t*)driver; // If jack hasn't called the detach method, do it now. As of jack 0.101.1 // the detach method was not being called explicitly on closedown, and // we need it to at least deallocate the iso resources. if (drv->dev != NULL) { ffado_driver_detach (drv); } ffado_driver_delete (drv); }