/* Copyright (C) 2001 Paul Davis Copyright (C) 2004 Grame Copyright (C) 2007 Pieter Palmers 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. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "JackFFADODriver.h" #include "JackEngineControl.h" #include "JackClientControl.h" #include "JackPort.h" #include "JackGraphManager.h" #include "JackCompilerDeps.h" namespace Jack { #define FIREWIRE_REQUIRED_FFADO_API_VERSION 8 #define jack_get_microseconds GetMicroSeconds #define SAMPLE_MAX_24BIT 8388608.0f #define SAMPLE_MAX_16BIT 32768.0f int JackFFADODriver::ffado_driver_read (ffado_driver_t * driver, jack_nframes_t nframes) { channel_t chn; jack_default_audio_sample_t* buf = NULL; printEnter(); for (chn = 0; chn < driver->capture_nchannels; chn++) { // if nothing connected, don't process if (fGraphManager->GetConnectionsNum(fCapturePortList[chn]) == 0) { buf = (jack_default_audio_sample_t*)driver->scratchbuffer; // we always have to specify a valid buffer ffado_streaming_set_capture_stream_buffer(driver->dev, chn, (char *)(buf)); // notify the streaming system that it can (but doesn't have to) skip // this channel ffado_streaming_capture_stream_onoff(driver->dev, chn, 0); } else { if (driver->capture_channels[chn].stream_type == ffado_stream_type_audio) { buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[chn], nframes); /* if the returned buffer is invalid, use the dummy buffer */ if (!buf) buf = (jack_default_audio_sample_t*)driver->scratchbuffer; ffado_streaming_set_capture_stream_buffer(driver->dev, chn, (char *)(buf)); ffado_streaming_capture_stream_onoff(driver->dev, chn, 1); } else if (driver->capture_channels[chn].stream_type == ffado_stream_type_midi) { ffado_streaming_set_capture_stream_buffer(driver->dev, chn, (char *)(driver->capture_channels[chn].midi_buffer)); ffado_streaming_capture_stream_onoff(driver->dev, chn, 1); } else { // always have a valid buffer ffado_streaming_set_capture_stream_buffer(driver->dev, chn, (char *)(driver->scratchbuffer)); // don't process what we don't use 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; chn < driver->capture_nchannels; chn++) { if (driver->capture_channels[chn].stream_type == ffado_stream_type_midi) { jack_nframes_t i; int done; uint32_t *midi_buffer = driver->capture_channels[chn].midi_buffer; midi_unpack_t *midi_unpack = &driver->capture_channels[chn].midi_unpack; buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[chn], nframes); jack_midi_clear_buffer(buf); /* if the returned buffer is invalid, discard the midi data */ if (!buf) 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("buffer overflow in channel %d\n", chn); break; } printMessage("MIDI IN: %08X (i=%d)", midi_buffer[i], i); } } } } printExit(); return 0; } int JackFFADODriver::ffado_driver_write (ffado_driver_t * driver, jack_nframes_t nframes) { channel_t chn; jack_default_audio_sample_t* buf; printEnter(); driver->process_count++; for (chn = 0; chn < driver->playback_nchannels; chn++) { if (fGraphManager->GetConnectionsNum(fPlaybackPortList[chn]) == 0) { buf = (jack_default_audio_sample_t*)driver->nullbuffer; // we always have to specify a valid buffer ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(buf)); // notify the streaming system that it can (but doesn't have to) skip // this channel ffado_streaming_playback_stream_onoff(driver->dev, chn, 0); } else { if (driver->playback_channels[chn].stream_type == ffado_stream_type_audio) { buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[chn], nframes); /* use the silent buffer if there is no valid jack buffer */ if (!buf) buf = (jack_default_audio_sample_t*)driver->nullbuffer; ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(buf)); } else if (driver->playback_channels[chn].stream_type == ffado_stream_type_midi) { jack_nframes_t nevents; jack_nframes_t i; midi_pack_t *midi_pack = &driver->playback_channels[chn].midi_pack; uint32_t *midi_buffer = driver->playback_channels[chn].midi_buffer; buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[chn], nframes); jack_nframes_t min_next_pos = 0; memset(midi_buffer, 0, nframes * sizeof(uint32_t)); ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(midi_buffer)); /* if the returned buffer is invalid, continue */ if (!buf) { ffado_streaming_playback_stream_onoff(driver->dev, chn, 0); continue; } 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 nevents = jack_midi_get_event_count(buf); //if (nevents) // printMessage("MIDI: %d events in ch %d", nevents, chn); for (i = 0; i < nevents; ++i) { size_t 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 jack_nframes_t 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) { pos += 8; printMessage("have to correct pos to %d", pos); } if (pos >= nframes) { unsigned 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; } } //printMessage("MIDI: sent %d-byte event at %ld", (int)event.size, (long)event.time); } } else { // always have 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; } jack_nframes_t JackFFADODriver::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 = jack_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 = jack_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) { // ffado requested shutdown (e.g. device unplugged) // this should be fatal *status = -1; return 0; } else { // unknown response code. should be fatal // this should be fatal *status = -1; return 0; } fBeginDateUst = wait_ret; printExit(); return driver->period_size; } int JackFFADODriver::ffado_driver_start (ffado_driver_t *driver) { int retval = 0; if ((retval = ffado_streaming_start(driver->dev))) { printError("Could not start streaming threads"); return retval; } return 0; } int JackFFADODriver::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; } int JackFFADODriver::ffado_driver_restart (ffado_driver_t *driver) { if (Stop()) return -1; return Start(); } int JackFFADODriver::SetBufferSize (jack_nframes_t nframes) { printError("Buffer size change requested but not supported!!!"); /* driver->period_size = nframes; driver->period_usecs = (jack_time_t) floor ((((float) nframes) / driver->sample_rate) * 1000000.0f); */ /* tell the engine to change its buffer size */ //driver->engine->set_buffer_size (driver->engine, nframes); return -1; // unsupported } typedef void (*JackDriverFinishFunction) (jack_driver_t *); ffado_driver_t * JackFFADODriver::ffado_driver_new (const char *name, ffado_jack_settings_t *params) { ffado_driver_t *driver; assert(params); if (ffado_get_api_version() != FIREWIRE_REQUIRED_FFADO_API_VERSION) { printError("Incompatible libffado version! (%s)", ffado_get_version()); return NULL; } printMessage("Starting FFADO backend (%s)", ffado_get_version()); driver = (ffado_driver_t*)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; fBeginDateUst = 0; driver->period_usecs = (jack_time_t) floor ((((float) driver->period_size) * 1000000.0f) / driver->sample_rate); // driver->client = client; driver->engine = NULL; 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->device_options.verbose = params->verbose_level; driver->capture_frame_latency = params->capture_frame_latency; driver->playback_frame_latency = params->playback_frame_latency; debugPrint(DEBUG_LEVEL_STARTUP, " Driver compiled on %s %s", __DATE__, __TIME__); debugPrint(DEBUG_LEVEL_STARTUP, " Created driver %s", name); debugPrint(DEBUG_LEVEL_STARTUP, " period_size: %d", driver->device_options.period_size); debugPrint(DEBUG_LEVEL_STARTUP, " period_usecs: %d", driver->period_usecs); debugPrint(DEBUG_LEVEL_STARTUP, " sample rate: %d", driver->device_options.sample_rate); debugPrint(DEBUG_LEVEL_STARTUP, " verbose level: %d", driver->device_options.verbose); return (ffado_driver_t *) driver; } void JackFFADODriver::ffado_driver_delete (ffado_driver_t *driver) { free (driver); } int JackFFADODriver::Attach() { JackPort* port; int port_index; unsigned long port_flags; char buf[JACK_PORT_NAME_SIZE]; char portname[JACK_PORT_NAME_SIZE]; ffado_driver_t* driver = (ffado_driver_t*)fDriver; jack_log("JackFFADODriver::Attach fBufferSize %ld fSampleRate %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); g_verbose = (fEngineControl->fVerbose ? 1 : 0); /* 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 = (ffado_sample_t *)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 = (ffado_sample_t *)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 = (fEngineControl->fRealTime ? 1 : 0); driver->device_options.packetizer_priority = fEngineControl->fPriority + FFADO_RT_PRIORITY_PACKETIZER_RELATIVE; if (driver->device_options.packetizer_priority > 98) { driver->device_options.packetizer_priority = 98; } // initialize the thread driver->dev = ffado_streaming_init(driver->device_info, driver->device_options); if (!driver->dev) { printError("FFADO: Error creating virtual 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 */ // capture port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; driver->capture_nchannels = ffado_streaming_get_nb_capture_streams(driver->dev); driver->capture_channels = (ffado_capture_channel_t *)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; } fCaptureChannels = 0; for (channel_t chn = 0; chn < driver->capture_nchannels; chn++) { ffado_streaming_get_capture_stream_name(driver->dev, chn, portname, sizeof(portname) - 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(buf, sizeof(buf) - 1, "%s:AC%d_%s", fClientControl.fName, (int)chn, portname); printMessage ("Registering audio capture port %s", buf); if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, buf, JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize)) == NO_PORT) { jack_error("driver: cannot register port for %s", buf); return -1; } // setup port parameters if (ffado_streaming_set_capture_stream_buffer(driver->dev, chn, NULL)) { printError(" cannot configure initial port buffer for %s", buf); } ffado_streaming_capture_stream_onoff(driver->dev, chn, 0); port = fGraphManager->GetPort(port_index); port->SetLatency(driver->period_size + driver->capture_frame_latency); fCapturePortList[chn] = port_index; jack_log("JackFFADODriver::Attach fCapturePortList[i] %ld ", port_index); fCaptureChannels++; } else if (driver->capture_channels[chn].stream_type == ffado_stream_type_midi) { snprintf(buf, sizeof(buf) - 1, "%s:MC%d_%s", fClientControl.fName, (int)chn, portname); printMessage ("Registering midi capture port %s", buf); if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, buf, JACK_DEFAULT_MIDI_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize)) == NO_PORT) { jack_error("driver: cannot register port for %s", buf); return -1; } // setup port parameters if (ffado_streaming_set_capture_stream_buffer(driver->dev, chn, NULL)) { printError(" cannot configure initial port buffer for %s", buf); } if (ffado_streaming_capture_stream_onoff(driver->dev, chn, 0)) { printError(" cannot enable port %s", buf); } // 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 = (uint32_t *)calloc(driver->period_size, sizeof(uint32_t)); port = fGraphManager->GetPort(port_index); port->SetLatency(driver->period_size + driver->capture_frame_latency); fCapturePortList[chn] = port_index; jack_log("JackFFADODriver::Attach fCapturePortList[i] %ld ", port_index); fCaptureChannels++; } else { printMessage ("Don't register capture port %s", portname); } } // playback port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; driver->playback_nchannels = ffado_streaming_get_nb_playback_streams(driver->dev); driver->playback_channels = (ffado_playback_channel_t *)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; } fPlaybackChannels = 0; for (channel_t chn = 0; chn < driver->playback_nchannels; chn++) { ffado_streaming_get_playback_stream_name(driver->dev, chn, portname, sizeof(portname) - 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(buf, sizeof(buf) - 1, "%s:AP%d_%s", fClientControl.fName, (int)chn, portname); printMessage ("Registering audio playback port %s", buf); if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, buf, JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize)) == NO_PORT) { jack_error("driver: cannot register port for %s", buf); return -1; } // setup port parameters if (ffado_streaming_set_playback_stream_buffer(driver->dev, chn, NULL)) { printError(" cannot configure initial port buffer for %s", buf); } if (ffado_streaming_playback_stream_onoff(driver->dev, chn, 0)) { printError(" cannot enable port %s", buf); } port = fGraphManager->GetPort(port_index); // Add one buffer more latency if "async" mode is used... port->SetLatency((driver->period_size * (driver->device_options.nb_buffers - 1)) + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + driver->playback_frame_latency); fPlaybackPortList[chn] = port_index; jack_log("JackFFADODriver::Attach fPlaybackPortList[i] %ld ", port_index); fPlaybackChannels++; } else if (driver->playback_channels[chn].stream_type == ffado_stream_type_midi) { snprintf(buf, sizeof(buf) - 1, "%s:MP%d_%s", fClientControl.fName, (int)chn, portname); printMessage ("Registering midi playback port %s", buf); if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, buf, JACK_DEFAULT_MIDI_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize)) == NO_PORT) { jack_error("driver: cannot register port for %s", buf); return -1; } // setup port parameters if (ffado_streaming_set_playback_stream_buffer(driver->dev, chn, NULL)) { printError(" cannot configure initial port buffer for %s", buf); } if (ffado_streaming_playback_stream_onoff(driver->dev, chn, 0)) { printError(" cannot enable port %s", buf); } // setup midi packer midi_pack_reset(&driver->playback_channels[chn].midi_pack); // setup the midi buffer driver->playback_channels[chn].midi_buffer = (uint32_t *)calloc(driver->period_size, sizeof(uint32_t)); port = fGraphManager->GetPort(port_index); port->SetLatency((driver->period_size * (driver->device_options.nb_buffers - 1)) + driver->playback_frame_latency); fPlaybackPortList[chn] = port_index; jack_log("JackFFADODriver::Attach fPlaybackPortList[i] %ld ", port_index); fPlaybackChannels++; } else { printMessage ("Don't register playback port %s", portname); } } assert(fCaptureChannels < DRIVER_PORT_NUM); assert(fPlaybackChannels < DRIVER_PORT_NUM); if (ffado_streaming_prepare(driver->dev)) { printError("Could not prepare streaming device!"); return -1; } // this makes no sense... assert(fCaptureChannels + fPlaybackChannels > 0); return 0; } int JackFFADODriver::Detach() { channel_t chn; ffado_driver_t* driver = (ffado_driver_t*)fDriver; jack_log("JackFFADODriver::Detach"); // finish the libfreebob streaming ffado_streaming_finish(driver->dev); driver->dev = NULL; // free all internal buffers 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 JackAudioDriver::Detach(); // Generic JackAudioDriver Detach } int JackFFADODriver::Open(ffado_jack_settings_t *params) { // Generic JackAudioDriver Open if (JackAudioDriver::Open( params->period_size, params->sample_rate, params->playback_ports, params->playback_ports, 0, 0, 0, "", "", params->capture_frame_latency, params->playback_frame_latency) != 0) { return -1; } fDriver = (jack_driver_t *)ffado_driver_new ("ffado_pcm", params); if (fDriver) { // FFADO driver may have changed the in/out values //fCaptureChannels = ((ffado_driver_t *)fDriver)->capture_nchannels_audio; //fPlaybackChannels = ((ffado_driver_t *)fDriver)->playback_nchannels_audio; return 0; } else { return -1; } } int JackFFADODriver::Close() { JackAudioDriver::Close(); ffado_driver_delete((ffado_driver_t*)fDriver); return 0; } int JackFFADODriver::Start() { return ffado_driver_start((ffado_driver_t *)fDriver); } int JackFFADODriver::Stop() { return ffado_driver_stop((ffado_driver_t *)fDriver); } int JackFFADODriver::Read() { printEnter(); /* Taken from ffado_driver_run_cycle */ ffado_driver_t* driver = (ffado_driver_t*)fDriver; int wait_status = 0; fDelayedUsecs = 0.f; jack_nframes_t nframes = ffado_driver_wait(driver, -1, &wait_status, &fDelayedUsecs); 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. */ jack_log("FFADO XRun"); NotifyXRun(fBeginDateUst, fDelayedUsecs); return -1; } if (nframes != fEngineControl->fBufferSize) jack_log("JackFFADODriver::Read nframes = %ld", nframes); // Has to be done before read JackDriver::CycleIncTime(); printExit(); return ffado_driver_read((ffado_driver_t *)fDriver, fEngineControl->fBufferSize); } int JackFFADODriver::Write() { printEnter(); int res = ffado_driver_write((ffado_driver_t *)fDriver, fEngineControl->fBufferSize); printExit(); return res; } void JackFFADODriver::jack_driver_init (jack_driver_t *driver) { memset (driver, 0, sizeof (*driver)); driver->attach = 0; driver->detach = 0; driver->write = 0; driver->read = 0; driver->null_cycle = 0; driver->bufsize = 0; driver->start = 0; driver->stop = 0; } void JackFFADODriver::jack_driver_nt_init (jack_driver_nt_t * driver) { memset (driver, 0, sizeof (*driver)); jack_driver_init ((jack_driver_t *) driver); driver->attach = 0; driver->detach = 0; driver->bufsize = 0; driver->stop = 0; driver->start = 0; driver->nt_bufsize = 0; driver->nt_start = 0; driver->nt_stop = 0; driver->nt_attach = 0; driver->nt_detach = 0; driver->nt_run_cycle = 0; } } // end of namespace #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT const jack_driver_desc_t * driver_get_descriptor () { jack_driver_desc_t * desc; jack_driver_param_desc_t * params; unsigned int i; desc = (jack_driver_desc_t *)calloc (1, sizeof (jack_driver_desc_t)); strcpy (desc->name, "firewire"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 strcpy(desc->desc, "Linux FFADO API based audio backend"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 desc->nparams = 11; params = (jack_driver_param_desc_t *)calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); desc->params = params; i = 0; 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 = 'C'; params[i].type = JackDriverParamBool; params[i].value.i = 0; 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 = 'P'; params[i].type = JackDriverParamBool; params[i].value.i = 0; strcpy (params[i].short_desc, "Provide playback ports."); strcpy (params[i].long_desc, params[i].short_desc); i++; strcpy (params[i].name, "duplex"); params[i].character = 'D'; params[i].type = JackDriverParamBool; params[i].value.i = 1; strcpy (params[i].short_desc, "Provide both capture and 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, "inchannels"); params[i].character = 'i'; params[i].type = JackDriverParamUInt; params[i].value.ui = 0; strcpy (params[i].short_desc, "Number of input channels to provide (note: currently ignored)"); strcpy (params[i].long_desc, params[i].short_desc); i++; strcpy (params[i].name, "outchannels"); params[i].character = 'o'; params[i].type = JackDriverParamUInt; params[i].value.ui = 0; strcpy (params[i].short_desc, "Number of output channels to provide (note: currently ignored)"); 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 = 3; strcpy (params[i].short_desc, "libffado verbose level"); strcpy (params[i].long_desc, params[i].short_desc); return desc; } SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { const JSList * node; const jack_driver_param_t * param; ffado_jack_settings_t cmlparams; 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 = 0; cmlparams.capture_ports = 0; cmlparams.playback_frame_latency = 0; cmlparams.capture_frame_latency = 0; cmlparams.verbose_level = 0; cmlparams.slave_mode = 0; cmlparams.snoop_mode = 0; cmlparams.device_info = NULL; for (node = params; node; node = jack_slist_next (node)) { param = (jack_driver_param_t *) node->data; switch (param->character) { 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 'C': cmlparams.capture_ports = 1; break; case 'P': cmlparams.playback_ports = 1; break; case 'D': cmlparams.capture_ports = 1; cmlparams.playback_ports = 1; break; case 'I': cmlparams.capture_frame_latency = param->value.ui; break; case 'O': cmlparams.playback_frame_latency = param->value.ui; break; // ignore these for now case 'i': break; case 'o': break; case 'v': cmlparams.verbose_level = param->value.ui; } } /* duplex is the default */ if (!cmlparams.playback_ports && !cmlparams.capture_ports) { cmlparams.playback_ports = TRUE; cmlparams.capture_ports = TRUE; } Jack::JackFFADODriver* ffado_driver = new Jack::JackFFADODriver("system", "firewire_pcm", engine, table); Jack::JackDriverClientInterface* threaded_driver = new Jack::JackThreadedDriver(ffado_driver); // Special open for FFADO driver... if (ffado_driver->Open(&cmlparams) == 0) { return threaded_driver; } else { delete threaded_driver; // Delete the decorated driver return NULL; } } #ifdef __cplusplus } #endif