|
- /*
- 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.
-
- */
-
- #include <iostream>
- #include <unistd.h>
- #include <math.h>
- #include <stdio.h>
- #include <memory.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <stdarg.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #include <regex.h>
- #include <string.h>
-
- #include "JackFreebobDriver.h"
- #include "JackEngineControl.h"
- #include "JackClientControl.h"
- #include "JackPort.h"
- #include "JackGraphManager.h"
-
- namespace Jack
- {
-
- #define jack_get_microseconds GetMicroSeconds
-
- #define SAMPLE_MAX_24BIT 8388608.0f
- #define SAMPLE_MAX_16BIT 32768.0f
-
- int
- JackFreebobDriver::freebob_driver_read (freebob_driver_t * driver, jack_nframes_t nframes)
- {
- jack_default_audio_sample_t* buf = NULL;
- freebob_sample_t nullbuffer[nframes];
- void *addr_of_nullbuffer = (void *)nullbuffer;
-
- freebob_streaming_stream_type stream_type;
-
- printEnter();
-
- // make sure all buffers have a valid buffer if not connected
- for (unsigned int i = 0; i < driver->capture_nchannels; i++) {
- stream_type = freebob_streaming_get_playback_stream_type(driver->dev, i);
- if (stream_type == freebob_stream_type_audio) {
- freebob_streaming_set_playback_stream_buffer(driver->dev, i,
- (char *)(nullbuffer), freebob_buffer_type_float);
- } else if (stream_type == freebob_stream_type_midi) {
- // these should be read/written with the per-stream functions
- } else { // empty other buffers without doing something with them
- freebob_streaming_set_playback_stream_buffer(driver->dev, i,
- (char *)(nullbuffer), freebob_buffer_type_uint24);
- }
- }
-
- for (int i = 0; i < fCaptureChannels; i++) {
- stream_type = freebob_streaming_get_capture_stream_type(driver->dev, i);
- if (stream_type == freebob_stream_type_audio) {
-
- if (fGraphManager->GetConnectionsNum(fCapturePortList[i]) > 0) {
- buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[i], nframes);
-
- if (!buf) {
- buf = (jack_default_audio_sample_t *)addr_of_nullbuffer;
- }
-
- freebob_streaming_set_capture_stream_buffer(driver->dev, i, (char *)(buf), freebob_buffer_type_float);
- }
- } else if (stream_type == freebob_stream_type_midi) {
- // these should be read/written with the per-stream functions
- } else { // empty other buffers without doing something with them
- freebob_streaming_set_capture_stream_buffer(driver->dev, i, (char *)(nullbuffer), freebob_buffer_type_uint24);
- }
-
- }
-
- // now transfer the buffers
- freebob_streaming_transfer_capture_buffers(driver->dev);
- printExit();
- return 0;
- }
-
- int
- JackFreebobDriver::freebob_driver_write (freebob_driver_t * driver, jack_nframes_t nframes)
- {
- jack_default_audio_sample_t* buf = NULL;
-
- freebob_streaming_stream_type stream_type;
-
- freebob_sample_t nullbuffer[nframes];
- void *addr_of_nullbuffer = (void*)nullbuffer;
-
- memset(&nullbuffer, 0, nframes*sizeof(freebob_sample_t));
-
- printEnter();
- driver->process_count++;
- assert(driver->dev);
-
- // make sure all buffers output silence if not connected
- for (unsigned int i = 0; i < driver->playback_nchannels; i++) {
- stream_type = freebob_streaming_get_playback_stream_type(driver->dev, i);
- if (stream_type == freebob_stream_type_audio) {
- freebob_streaming_set_playback_stream_buffer(driver->dev, i,
- (char *)(nullbuffer), freebob_buffer_type_float);
- } else if (stream_type == freebob_stream_type_midi) {
- // these should be read/written with the per-stream functions
- } else { // empty other buffers without doing something with them
- freebob_streaming_set_playback_stream_buffer(driver->dev, i,
- (char *)(nullbuffer), freebob_buffer_type_uint24);
- }
- }
-
- for (int i = 0; i < fPlaybackChannels; i++) {
- stream_type = freebob_streaming_get_playback_stream_type(driver->dev, i);
- if (stream_type == freebob_stream_type_audio) {
- // Ouput ports
- if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) {
- buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[i], nframes);
- if (!buf) {
- buf = (jack_default_audio_sample_t *)addr_of_nullbuffer;
- }
- freebob_streaming_set_playback_stream_buffer(driver->dev, i, (char *)(buf), freebob_buffer_type_float);
- }
- }
- }
-
- freebob_streaming_transfer_playback_buffers(driver->dev);
- printExit();
- return 0;
- }
-
- jack_nframes_t
- JackFreebobDriver::freebob_driver_wait (freebob_driver_t *driver, int extra_fd, int *status,
- float *delayed_usecs)
- {
- int nframes;
- jack_time_t wait_enter;
- jack_time_t wait_ret;
-
- 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
-
- nframes = freebob_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 (nframes < 0) {
- *status = 0;
- return 0;
- }
-
- *status = 0;
- fBeginDateUst = wait_ret;
-
- // FIXME: this should do something more usefull
- *delayed_usecs = 0;
- printExit();
- return nframes - nframes % driver->period_size;
- }
-
- int
- JackFreebobDriver::freebob_driver_start (freebob_driver_t *driver)
- {
- int retval = 0;
-
- #ifdef FREEBOB_DRIVER_WITH_MIDI
- if (driver->midi_handle) {
- if ((retval = freebob_driver_midi_start(driver->midi_handle))) {
- printError("Could not start MIDI threads");
- return retval;
- }
- }
- #endif
-
- if ((retval = freebob_streaming_start(driver->dev))) {
- printError("Could not start streaming threads");
- #ifdef FREEBOB_DRIVER_WITH_MIDI
- if (driver->midi_handle) {
- freebob_driver_midi_stop(driver->midi_handle);
- }
- #endif
- return retval;
- }
-
- return 0;
- }
-
- int
- JackFreebobDriver::freebob_driver_stop (freebob_driver_t *driver)
- {
- int retval = 0;
-
- #ifdef FREEBOB_DRIVER_WITH_MIDI
- if (driver->midi_handle) {
- if ((retval = freebob_driver_midi_stop(driver->midi_handle))) {
- printError("Could not stop MIDI threads");
- return retval;
- }
- }
- #endif
- if ((retval = freebob_streaming_stop(driver->dev))) {
- printError("Could not stop streaming threads");
- return retval;
- }
-
- return 0;
- }
-
- int
- JackFreebobDriver::freebob_driver_restart (freebob_driver_t *driver)
- {
- if (Stop())
- return -1;
- return Start();
- }
-
- int
- JackFreebobDriver::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 *);
-
- freebob_driver_t *
- JackFreebobDriver::freebob_driver_new (char *name,
- freebob_jack_settings_t *params)
- {
- freebob_driver_t *driver;
-
- assert(params);
-
- if (freebob_get_api_version() != 1) {
- printMessage("Incompatible libfreebob version! (%s)", freebob_get_version());
- return NULL;
- }
-
- printMessage("Starting Freebob backend (%s)", freebob_get_version());
-
- driver = (freebob_driver_t*)calloc (1, sizeof (freebob_driver_t));
-
- /* Setup the jack interfaces */
- jack_driver_nt_init ((jack_driver_nt_t *) driver);
-
- /* driver->nt_attach = (JackDriverNTAttachFunction) freebob_driver_attach;
- driver->nt_detach = (JackDriverNTDetachFunction) freebob_driver_detach;
- driver->nt_start = (JackDriverNTStartFunction) freebob_driver_start;
- driver->nt_stop = (JackDriverNTStopFunction) freebob_driver_stop;
- driver->nt_run_cycle = (JackDriverNTRunCycleFunction) freebob_driver_run_cycle;
- driver->null_cycle = (JackDriverNullCycleFunction) freebob_driver_null_cycle;
- driver->write = (JackDriverReadFunction) freebob_driver_write;
- driver->read = (JackDriverReadFunction) freebob_driver_read;
- driver->nt_bufsize = (JackDriverNTBufSizeFunction) freebob_driver_bufsize;
- */
-
- /* copy command line parameter contents to the driver structure */
- memcpy(&driver->settings, params, sizeof(freebob_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.node_id = params->node_id;
- driver->device_options.port = params->port;
- driver->capture_frame_latency = params->capture_frame_latency;
- driver->playback_frame_latency = params->playback_frame_latency;
-
- if (!params->capture_ports) {
- driver->device_options.directions |= FREEBOB_IGNORE_CAPTURE;
- }
-
- if (!params->playback_ports) {
- driver->device_options.directions |= FREEBOB_IGNORE_PLAYBACK;
- }
-
- 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->period_size);
- debugPrint(DEBUG_LEVEL_STARTUP, " period_usecs: %d", driver->period_usecs);
- debugPrint(DEBUG_LEVEL_STARTUP, " sample rate: %d", driver->sample_rate);
-
- return (freebob_driver_t *) driver;
- }
-
- void
- JackFreebobDriver::freebob_driver_delete (freebob_driver_t *driver)
- {
- free (driver);
- }
-
- #ifdef FREEBOB_DRIVER_WITH_MIDI
- /*
- * MIDI support
- */
-
- // the thread that will queue the midi events from the seq to the stream buffers
-
- void *
- JackFreebobDriver::freebob_driver_midi_queue_thread(void *arg)
- {
- freebob_driver_midi_handle_t *m = (freebob_driver_midi_handle_t *)arg;
- assert(m);
- snd_seq_event_t *ev;
- unsigned char work_buffer[MIDI_TRANSMIT_BUFFER_SIZE];
- int bytes_to_send;
- int b;
- int i;
-
- printMessage("MIDI queue thread started");
-
- while (1) {
- // get next event, if one is present
- while ((snd_seq_event_input(m->seq_handle, &ev) > 0)) {
- // get the port this event is originated from
- freebob_midi_port_t *port = NULL;
- for (i = 0;i < m->nb_output_ports;i++) {
- if (m->output_ports[i]->seq_port_nr == ev->dest.port) {
- port = m->output_ports[i];
- break;
- }
- }
-
- if (!port) {
- printError(" Could not find target port for event: dst=%d src=%d", ev->dest.port, ev->source.port);
- break;
- }
-
- // decode it to the work buffer
- if ((bytes_to_send = snd_midi_event_decode ( port->parser,
- work_buffer,
- MIDI_TRANSMIT_BUFFER_SIZE,
- ev)) < 0) { // failed
- printError(" Error decoding event for port %d (errcode=%d)", port->seq_port_nr, bytes_to_send);
- bytes_to_send = 0;
- //return -1;
- }
-
- for (b = 0;b < bytes_to_send;b++) {
- freebob_sample_t tmp_event = work_buffer[b];
- if (freebob_streaming_write(m->dev, port->stream_nr, &tmp_event, 1) < 1) {
- printError(" Midi send buffer overrun");
- }
- }
- }
-
- // sleep for some time
- usleep(MIDI_THREAD_SLEEP_TIME_USECS);
- }
- return NULL;
- }
-
- // the dequeue thread (maybe we need one thread per stream)
- void *
- JackFreebobDriver::freebob_driver_midi_dequeue_thread (void *arg)
- {
- freebob_driver_midi_handle_t *m = (freebob_driver_midi_handle_t *)arg;
-
- int i;
- int s;
- int samples_read;
-
- assert(m);
-
- while (1) {
- // read incoming events
-
- for (i = 0;i < m->nb_input_ports;i++) {
- unsigned int buff[64];
- freebob_midi_port_t *port = m->input_ports[i];
- if (!port) {
- printError(" something went wrong when setting up the midi input port map (%d)", i);
- }
-
- do {
- samples_read = freebob_streaming_read(m->dev, port->stream_nr, buff, 64);
-
- for (s = 0;s < samples_read;s++) {
- unsigned int *byte = (buff + s) ;
- snd_seq_event_t ev;
- if ((snd_midi_event_encode_byte(port->parser, (*byte) & 0xFF, &ev)) > 0) {
- // a midi message is complete, send it out to ALSA
- snd_seq_ev_set_subs(&ev);
- snd_seq_ev_set_direct(&ev);
- snd_seq_ev_set_source(&ev, port->seq_port_nr);
- snd_seq_event_output_direct(port->seq_handle, &ev);
- }
- }
- } while (samples_read > 0);
- }
-
- // sleep for some time
- usleep(MIDI_THREAD_SLEEP_TIME_USECS);
- }
- return NULL;
- }
-
- freebob_driver_midi_handle_t *
- JackFreebobDriver::freebob_driver_midi_init(freebob_driver_t *driver)
- {
- char buf[256];
- channel_t chn;
- int nchannels;
- int i = 0;
-
- freebob_device_t *dev = driver->dev;
- assert(dev);
-
- freebob_driver_midi_handle_t *m = calloc(1, sizeof(freebob_driver_midi_handle_t));
- if (!m) {
- printError("not enough memory to create midi structure");
- return NULL;
- }
-
- if (snd_seq_open(&m->seq_handle, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK) < 0) {
- printError("Error opening ALSA sequencer.");
- free(m);
- return NULL;
- }
-
- snd_seq_set_client_name(m->seq_handle, "FreeBoB Jack MIDI");
-
- // find out the number of midi in/out ports we need to setup
- nchannels = freebob_streaming_get_nb_capture_streams(dev);
- m->nb_input_ports = 0;
-
- for (chn = 0; chn < nchannels; chn++) {
- if (freebob_streaming_get_capture_stream_type(dev, chn) == freebob_stream_type_midi) {
- m->nb_input_ports++;
- }
- }
-
- m->input_ports = calloc(m->nb_input_ports, sizeof(freebob_midi_port_t *));
- if (!m->input_ports) {
- printError("not enough memory to create midi structure");
- free(m);
- return NULL;
- }
-
- i = 0;
- for (chn = 0; chn < nchannels; chn++) {
- if (freebob_streaming_get_capture_stream_type(dev, chn) == freebob_stream_type_midi) {
- m->input_ports[i] = calloc(1, sizeof(freebob_midi_port_t));
- if (!m->input_ports[i]) {
- // fixme
- printError("Could not allocate memory for seq port");
- continue;
- }
-
- freebob_streaming_get_capture_stream_name(dev, chn, buf, sizeof(buf) - 1);
- printMessage("Register MIDI IN port %s", buf);
-
- m->input_ports[i]->seq_port_nr = snd_seq_create_simple_port(m->seq_handle, buf,
- SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ,
- SND_SEQ_PORT_TYPE_MIDI_GENERIC);
-
- if (m->input_ports[i]->seq_port_nr < 0) {
- printError("Could not create seq port");
- m->input_ports[i]->stream_nr = -1;
- m->input_ports[i]->seq_port_nr = -1;
- } else {
- m->input_ports[i]->stream_nr = chn;
- m->input_ports[i]->seq_handle = m->seq_handle;
- if (snd_midi_event_new ( ALSA_SEQ_BUFF_SIZE, &(m->input_ports[i]->parser)) < 0) {
- printError("could not init parser for MIDI IN port %d", i);
- m->input_ports[i]->stream_nr = -1;
- m->input_ports[i]->seq_port_nr = -1;
- }
- }
-
- i++;
- }
- }
-
- // playback
- nchannels = freebob_streaming_get_nb_playback_streams(dev);
-
- m->nb_output_ports = 0;
-
- for (chn = 0; chn < nchannels; chn++) {
- if (freebob_streaming_get_playback_stream_type(dev, chn) == freebob_stream_type_midi) {
- m->nb_output_ports++;
- }
- }
-
- m->output_ports = calloc(m->nb_output_ports, sizeof(freebob_midi_port_t *));
- if (!m->output_ports) {
- printError("not enough memory to create midi structure");
- for (i = 0; i < m->nb_input_ports; i++) {
- free(m->input_ports[i]);
- }
- free(m->input_ports);
- free(m);
- return NULL;
- }
-
- i = 0;
- for (chn = 0; chn < nchannels; chn++) {
- if (freebob_streaming_get_playback_stream_type(dev, chn) == freebob_stream_type_midi) {
- m->output_ports[i] = calloc(1, sizeof(freebob_midi_port_t));
- if (!m->output_ports[i]) {
- // fixme
- printError("Could not allocate memory for seq port");
- continue;
- }
-
- freebob_streaming_get_playback_stream_name(dev, chn, buf, sizeof(buf) - 1);
- printMessage("Register MIDI OUT port %s", buf);
-
- m->output_ports[i]->seq_port_nr = snd_seq_create_simple_port(m->seq_handle, buf,
- SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
- SND_SEQ_PORT_TYPE_MIDI_GENERIC);
-
- if (m->output_ports[i]->seq_port_nr < 0) {
- printError("Could not create seq port");
- m->output_ports[i]->stream_nr = -1;
- m->output_ports[i]->seq_port_nr = -1;
- } else {
- m->output_ports[i]->stream_nr = chn;
- m->output_ports[i]->seq_handle = m->seq_handle;
- if (snd_midi_event_new ( ALSA_SEQ_BUFF_SIZE, &(m->output_ports[i]->parser)) < 0) {
- printError("could not init parser for MIDI OUT port %d", i);
- m->output_ports[i]->stream_nr = -1;
- m->output_ports[i]->seq_port_nr = -1;
- }
- }
-
- i++;
- }
- }
-
- m->dev = dev;
- m->driver = driver;
- return m;
- }
-
- int
- JackFreebobDriver::freebob_driver_midi_start (freebob_driver_midi_handle_t *m)
- {
- assert(m);
- // start threads
-
- m->queue_thread_realtime = (m->driver->engine->control->real_time ? 1 : 0);
- m->queue_thread_priority =
- m->driver->engine->control->client_priority +
- FREEBOB_RT_PRIORITY_MIDI_RELATIVE;
-
- if (m->queue_thread_priority > 98) {
- m->queue_thread_priority = 98;
- }
- if (m->queue_thread_realtime) {
- printMessage("MIDI threads running with Realtime scheduling, priority %d",
- m->queue_thread_priority);
- } else {
- printMessage("MIDI threads running without Realtime scheduling");
- }
-
- if (jack_client_create_thread(NULL, &m->queue_thread, m->queue_thread_priority, m->queue_thread_realtime, freebob_driver_midi_queue_thread, (void *)m)) {
- printError(" cannot create midi queueing thread");
- return -1;
- }
-
- if (jack_client_create_thread(NULL, &m->dequeue_thread, m->queue_thread_priority, m->queue_thread_realtime, freebob_driver_midi_dequeue_thread, (void *)m)) {
- printError(" cannot create midi dequeueing thread");
- return -1;
- }
- return 0;
- }
-
- int
- JackFreebobDriver::freebob_driver_midi_stop (freebob_driver_midi_handle_t *m)
- {
- assert(m);
-
- pthread_cancel (m->queue_thread);
- pthread_join (m->queue_thread, NULL);
-
- pthread_cancel (m->dequeue_thread);
- pthread_join (m->dequeue_thread, NULL);
- return 0;
- }
-
- void
- JackFreebobDriver::freebob_driver_midi_finish (freebob_driver_midi_handle_t *m)
- {
- assert(m);
-
- int i;
- // TODO: add state info here, if not stopped then stop
-
- for (i = 0;i < m->nb_input_ports;i++) {
- free(m->input_ports[i]);
- }
- free(m->input_ports);
-
- for (i = 0;i < m->nb_output_ports;i++) {
- free(m->output_ports[i]);
- }
- free(m->output_ports);
- free(m);
- }
- #endif
-
- int JackFreebobDriver::Attach()
- {
- JackPort* port;
- int port_index;
-
- char buf[JACK_PORT_NAME_SIZE];
- char portname[JACK_PORT_NAME_SIZE];
- jack_latency_range_t range;
-
- freebob_driver_t* driver = (freebob_driver_t*)fDriver;
-
- jack_log("JackFreebobDriver::Attach fBufferSize %ld fSampleRate %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate);
-
- g_verbose = (fEngineControl->fVerbose ? 1 : 0);
- driver->device_options.verbose = (fEngineControl->fVerbose ? 1 : 0);
-
- /* packetizer thread options */
- driver->device_options.realtime = (fEngineControl->fRealTime ? 1 : 0);
-
- driver->device_options.packetizer_priority = fEngineControl->fServerPriority +
- FREEBOB_RT_PRIORITY_PACKETIZER_RELATIVE;
- if (driver->device_options.packetizer_priority > 98) {
- driver->device_options.packetizer_priority = 98;
- }
-
- // initialize the thread
- driver->dev = freebob_streaming_init(&driver->device_info, driver->device_options);
-
- if (!driver->dev) {
- printError("FREEBOB: Error creating virtual device");
- return -1;
- }
-
- #ifdef FREEBOB_DRIVER_WITH_MIDI
- driver->midi_handle = freebob_driver_midi_init(driver);
- if (!driver->midi_handle) {
- printError("-----------------------------------------------------------");
- printError("Error creating midi device!");
- printError("FreeBob will run without MIDI support.");
- printError("Consult the above error messages to solve the problem. ");
- printError("-----------------------------------------------------------\n\n");
- }
- #endif
-
- 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");
- }
-
- /* ports */
-
- // capture
- driver->capture_nchannels = freebob_streaming_get_nb_capture_streams(driver->dev);
- driver->capture_nchannels_audio = 0;
-
- for (unsigned int i = 0; i < driver->capture_nchannels; i++) {
-
- freebob_streaming_get_capture_stream_name(driver->dev, i, portname, sizeof(portname) - 1);
- snprintf(buf, sizeof(buf) - 1, "%s:%s", fClientControl.fName, portname);
-
- if (freebob_streaming_get_capture_stream_type(driver->dev, i) != freebob_stream_type_audio) {
- printMessage ("Don't register capture port %s", buf);
- } else {
- printMessage ("Registering capture port %s", buf);
-
- if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, buf,
- JACK_DEFAULT_AUDIO_TYPE,
- CaptureDriverFlags,
- fEngineControl->fBufferSize)) == NO_PORT) {
- jack_error("driver: cannot register port for %s", buf);
- return -1;
- }
- port = fGraphManager->GetPort(port_index);
- range.min = range.max = driver->period_size + driver->capture_frame_latency;
- port->SetLatencyRange(JackCaptureLatency, &range);
- fCapturePortList[i] = port_index;
- jack_log("JackFreebobDriver::Attach fCapturePortList[i] %ld ", port_index);
- driver->capture_nchannels_audio++;
- }
- }
-
- // playback
- driver->playback_nchannels = freebob_streaming_get_nb_playback_streams(driver->dev);
- driver->playback_nchannels_audio = 0;
-
- for (unsigned int i = 0; i < driver->playback_nchannels; i++) {
-
- freebob_streaming_get_playback_stream_name(driver->dev, i, portname, sizeof(portname) - 1);
- snprintf(buf, sizeof(buf) - 1, "%s:%s", fClientControl.fName, portname);
-
- if (freebob_streaming_get_playback_stream_type(driver->dev, i) != freebob_stream_type_audio) {
- printMessage ("Don't register playback port %s", buf);
- } else {
- printMessage ("Registering playback port %s", buf);
- if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, buf,
- JACK_DEFAULT_AUDIO_TYPE,
- PlaybackDriverFlags,
- fEngineControl->fBufferSize)) == NO_PORT) {
- jack_error("driver: cannot register port for %s", buf);
- return -1;
- }
- port = fGraphManager->GetPort(port_index);
- // Add one buffer more latency if "async" mode is used...
- range.min = range.max = (driver->period_size * (driver->device_options.nb_buffers - 1)) + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + driver->playback_frame_latency);
- port->SetLatencyRange(JackPlaybackLatency, &range);
- fPlaybackPortList[i] = port_index;
- jack_log("JackFreebobDriver::Attach fPlaybackPortList[i] %ld ", port_index);
- driver->playback_nchannels_audio++;
- }
- }
-
- fCaptureChannels = driver->capture_nchannels_audio;
- fPlaybackChannels = driver->playback_nchannels_audio;
-
- assert(fCaptureChannels < DRIVER_PORT_NUM);
- assert(fPlaybackChannels < DRIVER_PORT_NUM);
-
- // this makes no sense...
- assert(fCaptureChannels + fPlaybackChannels > 0);
- return 0;
- }
-
- int JackFreebobDriver::Detach()
- {
- freebob_driver_t* driver = (freebob_driver_t*)fDriver;
- jack_log("JackFreebobDriver::Detach");
-
- // finish the libfreebob streaming
- freebob_streaming_finish(driver->dev);
- driver->dev = NULL;
-
- #ifdef FREEBOB_DRIVER_WITH_MIDI
- if (driver->midi_handle) {
- freebob_driver_midi_finish(driver->midi_handle);
- }
- driver->midi_handle = NULL;
- #endif
-
- return JackAudioDriver::Detach(); // Generic JackAudioDriver Detach
- }
-
- int JackFreebobDriver::Open(freebob_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 *)freebob_driver_new ((char*)"freebob_pcm", params);
-
- if (fDriver) {
- // FreeBoB driver may have changed the in/out values
- fCaptureChannels = ((freebob_driver_t *)fDriver)->capture_nchannels_audio;
- fPlaybackChannels = ((freebob_driver_t *)fDriver)->playback_nchannels_audio;
- return 0;
- } else {
- JackAudioDriver::Close();
- return -1;
- }
- }
-
- int JackFreebobDriver::Close()
- {
- JackAudioDriver::Close();
- freebob_driver_delete((freebob_driver_t*)fDriver);
- return 0;
- }
-
- int JackFreebobDriver::Start()
- {
- JackAudioDriver::Start();
- return freebob_driver_start((freebob_driver_t *)fDriver);
- }
-
- int JackFreebobDriver::Stop()
- {
- return freebob_driver_stop((freebob_driver_t *)fDriver);
- }
-
- int JackFreebobDriver::Read()
- {
- printEnter();
-
- /* Taken from freebob_driver_run_cycle */
- freebob_driver_t* driver = (freebob_driver_t*)fDriver;
- int wait_status = 0;
- fDelayedUsecs = 0.f;
-
- retry:
-
- jack_nframes_t nframes = freebob_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("FreeBoB XRun");
- NotifyXRun(fBeginDateUst, fDelayedUsecs);
- goto retry; /* recoverable error*/
- }
-
- if (nframes != fEngineControl->fBufferSize)
- jack_log("JackFreebobDriver::Read warning nframes = %ld", nframes);
-
- // Has to be done before read
- JackDriver::CycleIncTime();
-
- printExit();
- return freebob_driver_read((freebob_driver_t *)fDriver, fEngineControl->fBufferSize);
- }
-
- int JackFreebobDriver::Write()
- {
- printEnter();
- int res = freebob_driver_write((freebob_driver_t *)fDriver, fEngineControl->fBufferSize);
- printExit();
- return res;
- }
-
- void
- JackFreebobDriver::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
- JackFreebobDriver::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
-
- 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, "freebob"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1
- strcpy(desc->desc, "Linux FreeBob 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, "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. Format is: 'hw:port[,node]'.");
- strcpy (params[i].long_desc, params[i].short_desc);
-
- 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 = '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);
-
- return desc;
- }
-
- Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) {
- unsigned int port = 0;
- unsigned int node_id = -1;
- int nbitems;
-
- const JSList * node;
- const jack_driver_param_t * param;
-
- freebob_jack_settings_t cmlparams;
-
- const char *device_name = "hw:0";
-
- cmlparams.period_size_set = 0;
- cmlparams.sample_rate_set = 0;
- cmlparams.buffer_size_set = 0;
- cmlparams.port_set = 0;
- cmlparams.node_id_set = 0;
-
- /* default values */
- cmlparams.period_size = 1024;
- cmlparams.sample_rate = 48000;
- cmlparams.buffer_size = 3;
- cmlparams.port = 0;
- cmlparams.node_id = -1;
- cmlparams.playback_ports = 0;
- cmlparams.capture_ports = 0;
- cmlparams.playback_frame_latency = 0;
- cmlparams.capture_frame_latency = 0;
-
- for (node = params; node; node = jack_slist_next (node)) {
- param = (jack_driver_param_t *) node->data;
-
- switch (param->character) {
- case 'd':
- device_name = 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 '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;
- }
- }
-
- /* duplex is the default */
- if (!cmlparams.playback_ports && !cmlparams.capture_ports) {
- cmlparams.playback_ports = TRUE;
- cmlparams.capture_ports = TRUE;
- }
-
- nbitems = sscanf(device_name, "hw:%u,%u", &port, &node_id);
- if (nbitems < 2) {
- nbitems = sscanf(device_name, "hw:%u", &port);
-
- if (nbitems < 1) {
- printError("device (-d) argument not valid\n");
- return NULL;
- } else {
- cmlparams.port = port;
- cmlparams.port_set = 1;
-
- cmlparams.node_id = -1;
- cmlparams.node_id_set = 0;
- }
- } else {
- cmlparams.port = port;
- cmlparams.port_set = 1;
-
- cmlparams.node_id = node_id;
- cmlparams.node_id_set = 1;
- }
-
- jack_error("Freebob using Firewire port %d, node %d", cmlparams.port, cmlparams.node_id);
-
- Jack::JackFreebobDriver* freebob_driver = new Jack::JackFreebobDriver("system", "freebob_pcm", engine, table);
- Jack::JackDriverClientInterface* threaded_driver = new Jack::JackThreadedDriver(freebob_driver);
- // Special open for FreeBoB driver...
- if (freebob_driver->Open(&cmlparams) == 0) {
- return threaded_driver;
- } else {
- delete threaded_driver; // Delete the decorated driver
- return NULL;
- }
- }
-
- #ifdef __cplusplus
- }
- #endif
-
|