diff --git a/ChangeLog b/ChangeLog index 57564e88..26e32041 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,13 @@ Jackdmp changes log --------------------------- +2007-02-05 Stephane Letz + + * Add Pieter Palmers FreeBob driver. + +2007-01-31 Stephane Letz + + * Use pthread_attr_setstacksize in JackPosixThread class. 2007-01-30 Stephane Letz diff --git a/common/JackClient.cpp b/common/JackClient.cpp index 9e975b87..bda9c966 100644 --- a/common/JackClient.cpp +++ b/common/JackClient.cpp @@ -327,7 +327,7 @@ bool JackClient::Execute() // Process call if (IsActive()) { CallSyncCallback(); - bool res = CallProcessCallback(); + bool res = CallProcessCallback(); CallTimebaseCallback(); if (!res) goto end; diff --git a/linux/Makefile b/linux/Makefile index a51f3ab8..27fd804c 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -3,7 +3,7 @@ prefix := /usr/local prefix := ${DESTDIR}${prefix} -subprojects := ../common ../tests ../example-clients alsa +subprojects := ../common ../tests ../example-clients alsa freebob sources := $(wildcard *.cpp) $(wildcard */*.cpp) $(wildcard ../common/*.cpp) $(wildcard ../tests/*.cpp) @@ -17,11 +17,13 @@ TARGET_LINUX_WRAPPER_LIB := libjackwrapper.so TARGET_LINUX_ALSA := jack_alsa.so +TARGET_LINUX_FREEBOB := jack_freebob.so + TARGET_LINUX_DUMMY := jack_dummy.so VPATH := $(subprojects) -LIB_LINUX := -lpthread -lrt -lasound +LIB_LINUX := -lpthread -lrt -lasound objects_common_server_lib := JackActivationCount.o JackAPI.o JackAudioDriver.o JackClient.o JackConnectionManager.o \ @@ -39,6 +41,8 @@ objects_linux_server := Jackdmp.o objects_linux_alsa := JackAlsaDriver.o memops.o generic_hw.o hdsp.o hammerfall.o ice1712.o +objects_linux_freebob := JackFreebobDriver.o + objects_linux_dummy := JackDummyDriver.o CFLAGS := -g -O3 -fPIC -DUSE_POSIX_SHM $(addprefix -I, $(subprojects)) $(CFLAGS) @@ -53,10 +57,12 @@ CXXFLAGS := -g -O3 -fPIC -DSOCKET_RPC_FIFO_SEMA -D__SMP__ -DADDON_DIR=\"$(prefix # Add the -D__CLIENTDEBUG__ flag to activate client validation -linux : $(TARGET_LINUX_SERVER_LIB) $(TARGET_LINUX_CLIENT_LIB) $(TARGET_LINUX_WRAPPER_LIB) $(TARGET_LINUX_SERVER) $(TARGET_LINUX_ALSA) $(TARGET_LINUX_DUMMY) \ - synchroClient synchroServer synchroServerClient testSem jack_test +linux : $(TARGET_LINUX_SERVER_LIB) $(TARGET_LINUX_CLIENT_LIB) $(TARGET_LINUX_WRAPPER_LIB) \ + $(TARGET_LINUX_SERVER) $(TARGET_LINUX_ALSA) $(TARGET_LINUX_FREEBOB) $(TARGET_LINUX_DUMMY) \ + synchroClient synchroServer synchroServerClient testSem jack_test -all : $(TARGET_LINUX_SERVER) $(TARGET_LINUX_SERVER_LIB) $(TARGET_LINUX_ALSA) $(TARGET_LINUX_DUMMY) +all : $(TARGET_LINUX_SERVER) $(TARGET_LINUX_SERVER_LIB) $(TARGET_LINUX_ALSA) \ + $(TARGET_LINUX_FREEBOB) $(TARGET_LINUX_DUMMY) synchroClient: JackPosixSemaphore.o testSynchroClient.o JackPosixThread.o JackError.o JackFifo.o $(CXX) $(CXXFLAGS) JackPosixSemaphore.o testSynchroClient.o JackPosixThread.o JackError.o JackFifo.o $(LIB_LINUX) -o synchroClient @@ -89,6 +95,9 @@ $(TARGET_LINUX_SERVER) : $(objects_linux_server) $(TARGET_LINUX_ALSA) : $(objects_linux_alsa) $(CXX) $(CXXFLAGS) -shared $(objects_linux_alsa) $(LIB_LINUX) libjackdmp.so -o $(TARGET_LINUX_ALSA) +$(TARGET_LINUX_FREEBOB) : $(objects_linux_freebob) + $(CXX) $(CXXFLAGS) -shared $(objects_linux_freebob) $(LIB_LINUX) -lfreebob libjackdmp.so -o $(TARGET_LINUX_FREEBOB) + $(TARGET_LINUX_DUMMY) : $(objects_linux_dummy) $(CXX) $(CXXFLAGS) -shared $(objects_linux_dummy) $(LIB_LINUX) libjackdmp.so -o $(TARGET_LINUX_DUMMY) @@ -100,6 +109,7 @@ install: cp libjackdmp.so $(prefix)/lib install -d $(prefix)/lib/jackmp/ cp jack_alsa.so $(prefix)/lib/jackmp + cp jack_freebob.so $(prefix)/lib/jackmp cp jack_dummy.so $(prefix)/lib/jackmp cd $(prefix)/lib && [ -f libjack.so.0.0.23 ] && mv -f libjack.so.0.0.23 tmp_libjack.so.0.0.23 || echo "Jack not found, continue..." cd $(prefix)/lib && rm -f libjack.so* @@ -108,6 +118,8 @@ install: /sbin/ldconfig # Remove jackdmp and tries to restore jack +uninstall: remove + remove: rm $(prefix)/bin/jackdmp rm $(prefix)/lib/libjackmp.so @@ -121,7 +133,7 @@ remove: clean : rm -f *.o - rm -f $(TARGET_LINUX_SERVER) $(TARGET_LINUX_SERVER_LIB) $(TARGET_LINUX_CLIENT_LIB) $(TARGET_LINUX_WRAPPER_LIB) $(TARGET_LINUX_ALSA) $(TARGET_LINUX_DUMMY) \ + rm -f $(TARGET_LINUX_SERVER) $(TARGET_LINUX_SERVER_LIB) $(TARGET_LINUX_CLIENT_LIB) $(TARGET_LINUX_WRAPPER_LIB) $(TARGET_LINUX_ALSA) $(TARGET_LINUX_FREEBOB) $(TARGET_LINUX_DUMMY) \ synchroClient synchroServer synchroServerClient testSem jack_test depend : diff --git a/linux/freebob/JackFreebobDriver.cpp b/linux/freebob/JackFreebobDriver.cpp new file mode 100644 index 00000000..dc8549b1 --- /dev/null +++ b/linux/freebob/JackFreebobDriver.cpp @@ -0,0 +1,1254 @@ +/* +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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +// channel_t chn; +// JSList *node; +// jack_port_t* port; + + 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 (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) +{ + channel_t chn; + JSList *node; + jack_default_audio_sample_t* buf = NULL; + + jack_port_t *port; + + 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 (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; + fLastWaitUst = 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; + fLastWaitUst = 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) +{ + if (driver->dev) { + Detach(); + } + + jack_driver_nt_finish ((jack_driver_nt_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) +{ +// int err; + + 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; + unsigned long port_flags; + + char buf[JACK_PORT_NAME_SIZE]; + char portname[JACK_PORT_NAME_SIZE]; + + freebob_driver_t* driver = (freebob_driver_t*)fDriver; + + JackLog("JackFreebobDriver::Attach fBufferSize %ld fSampleRate %ld\n", 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->fPriority + + 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 + port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; + + 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, (JackPortFlags)port_flags)) == NO_PORT) { + jack_error("driver: cannot register port for %s", buf); + return -1; + } + port = fGraphManager->GetPort(port_index); + port->SetLatency(driver->period_size + driver->capture_frame_latency); + fCapturePortList[i] = port_index; + JackLog("JackAudioDriver::Attach fCapturePortList[i] %ld \n", port_index); + driver->capture_nchannels_audio++; + } + } + + // playback + port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; + + driver->playback_nchannels = freebob_streaming_get_nb_playback_streams(driver->dev); + driver->playback_nchannels_audio = 0; + + for (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, (JackPortFlags)port_flags)) == NO_PORT) { + jack_error("driver: cannot register port for %s", buf); + return -1; + } + port = fGraphManager->GetPort(port_index); + port->SetLatency((driver->period_size * (driver->device_options.nb_buffers - 1)) + driver->playback_frame_latency); + fPlaybackPortList[i] = port_index; + JackLog("JackAudioDriver::Attach fPlaybackPortList[i] %ld \n", port_index); + driver->playback_nchannels_audio++; + } + } + + fCaptureChannels = driver->capture_nchannels_audio; + fPlaybackChannels = driver->playback_nchannels_audio; + + assert(fCaptureChannels < PORT_NUM); + assert(fPlaybackChannels < PORT_NUM); + + // this makes no sense... + assert(fCaptureChannels + fPlaybackChannels > 0); + return 0; +} + +int JackFreebobDriver::Detach() +{ + freebob_driver_t* driver = (freebob_driver_t*)fDriver; + + JackLog("JackFreebobDriver::Detach\n"); + + // 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 + + // unregister the ports + for (int i = 0; i < fCaptureChannels; i++) { + fGraphManager->RemovePort(fClientControl->fRefNum, fCapturePortList[i]); + } + + for (int i = 0; i < fPlaybackChannels; i++) { + fGraphManager->RemovePort(fClientControl->fRefNum, fPlaybackPortList[i]); + if (fWithMonitorPorts) + fGraphManager->RemovePort(fClientControl->fRefNum, fMonitorPortList[i]); + } + + return 0; +} + +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 ("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 { + return -1; + } +} + +int JackFreebobDriver::Close() +{ + JackAudioDriver::Close(); + freebob_driver_delete((freebob_driver_t*)fDriver); + return 0; +} + +int JackFreebobDriver::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; + float delayed_usecs = 0.0; + + jack_nframes_t nframes = freebob_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. + */ + //engine->delay (engine, delayed_usecs); + JackLog("FreeBoB XRun \n"); + //NotifyXRun(jack_get_microseconds()); + NotifyXRun(fLastWaitUst); + //return 0; + return -1; + } + + //fLastWaitUst = GetMicroSeconds(); // Take callback date here + if (nframes != fEngineControl->fBufferSize) + JackLog("JackFreebobDriver::Read nframes = %ld\n", nframes); + + //return engine->run_cycle (engine, nframes, delayed_usecs); + fDelayedUst = (jack_time_t)delayed_usecs; + printExit(); + return freebob_driver_read((freebob_driver_t *)fDriver, fEngineControl->fBufferSize); +} + +int JackFreebobDriver::Write() +{ + printEnter(); + + //JackLog("write\n"); + int res = freebob_driver_write((freebob_driver_t *)fDriver, fEngineControl->fBufferSize); + jack_time_t write_time = GetMicroSeconds(); + + /* + if (write_time > (fLastWaitUst - fDelayedUst) + fEngineControl->fPeriodUsecs) { + JackLog("FreeBoB write XRun \n"); + NotifyXRun(write_time); + } + */ + 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; + + pthread_mutex_init (&driver->nt_run_lock, NULL); +} + +void +JackFreebobDriver::jack_driver_nt_finish(jack_driver_nt_t * driver) +{ + pthread_mutex_destroy (&driver->nt_run_lock); +} + +void JackFreebobDriver::PrintState() +{ + std::cout << "JackFreebobDriver State" << std::endl; + + int port_index; + + std::cout << "Input ports" << std::endl; + + for (int i = 0; i < fPlaybackChannels; i++) { + port_index = fCapturePortList[i]; + JackPort* port = fGraphManager->GetPort(port_index); + std::cout << port->GetName() << std::endl; + //if (fGraphManager->IsConnected(port_index)) {} + if (fGraphManager->GetConnectionsNum(port_index)) {} + } + + std::cout << "Output ports" << std::endl; + + for (int i = 0; i < fCaptureChannels; i++) { + port_index = fPlaybackPortList[i]; + JackPort* port = fGraphManager->GetPort(port_index); + std::cout << port->GetName() << std::endl; + //if (fGraphManager->IsConnected(port_index)) {} + if (fGraphManager->GetConnectionsNum(port_index)) {} + } +} + +/* +JackDriver* DriverInit(JackGraphManager* manager) +{ + return new JackFreebobDriver("ALSA", manager); +} +*/ +} // 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"); + 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::JackEngine* engine, Jack::JackSynchro** table, const JSList* params) { + jack_driver_t *driver; + + 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; + + 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 = 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 '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) { + free(device_name); + 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("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 + + diff --git a/linux/freebob/JackFreebobDriver.h b/linux/freebob/JackFreebobDriver.h new file mode 100644 index 00000000..52d2aaab --- /dev/null +++ b/linux/freebob/JackFreebobDriver.h @@ -0,0 +1,98 @@ +/* +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. + +*/ + +#ifndef __JackFreebobDriver__ +#define __JackFreebobDriver__ + +#include "JackAudioDriver.h" +#include "JackThreadedDriver.h" +#include "JackTime.h" + +#include "freebob_driver.h" + +namespace Jack +{ + +/*! +\brief The FreeBoB driver. +*/ + +class JackFreebobDriver : public JackAudioDriver +{ + + private: + + // enable verbose messages + int g_verbose; + + jack_driver_t* fDriver; + jack_time_t fDelayedUst; + int freebob_driver_attach (freebob_driver_t *driver); + int freebob_driver_detach (freebob_driver_t *driver); + int freebob_driver_read (freebob_driver_t * driver, jack_nframes_t nframes); + int freebob_driver_write (freebob_driver_t * driver, jack_nframes_t nframes); + jack_nframes_t freebob_driver_wait (freebob_driver_t *driver, + int extra_fd, int *status, + float *delayed_usecs); + int freebob_driver_start (freebob_driver_t *driver); + int freebob_driver_stop (freebob_driver_t *driver); + int freebob_driver_restart (freebob_driver_t *driver); + freebob_driver_t *freebob_driver_new (char *name, freebob_jack_settings_t *params); + void freebob_driver_delete (freebob_driver_t *driver); + +#ifdef FREEBOB_DRIVER_WITH_MIDI + freebob_driver_midi_handle_t *freebob_driver_midi_init(freebob_driver_t *driver); + void freebob_driver_midi_finish (freebob_driver_midi_handle_t *m); + int freebob_driver_midi_start (freebob_driver_midi_handle_t *m); + int freebob_driver_midi_stop (freebob_driver_midi_handle_t *m); +#endif + + void jack_driver_init (jack_driver_t *driver); + void jack_driver_nt_init (jack_driver_nt_t * driver); + void jack_driver_nt_finish(jack_driver_nt_t * driver); + + public: + + JackFreebobDriver(const char* name, JackEngine* engine, JackSynchro** table): JackAudioDriver(name, engine, table) + {} + virtual ~JackFreebobDriver() + {} + + int Open(freebob_jack_settings_t *cmlparams); + + int Close(); + int Attach(); + int Detach(); + + int Start(); + int Stop(); + + int Read(); + int Write(); + + int SetBufferSize(jack_nframes_t nframes); + + void PrintState(); +}; + +} // end of namespace + +#endif diff --git a/linux/freebob/freebob_driver.h b/linux/freebob/freebob_driver.h new file mode 100644 index 00000000..8cada7f3 --- /dev/null +++ b/linux/freebob/freebob_driver.h @@ -0,0 +1,245 @@ +/* freebob_driver.h + * + * FreeBob Backend for Jack + * FreeBob = Firewire (pro-)audio for linux + * + * adapted for jackmp + * + * http://freebob.sf.net + * http://jackit.sf.net + * + * Copyright (C) 2005,2006,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. + */ + +/* + * Main Jack driver entry routines + * + */ + +#ifndef __JACK_FREEBOB_DRIVER_H__ +#define __JACK_FREEBOB_DRIVER_H__ + +// #define FREEBOB_DRIVER_WITH_MIDI +// #define DEBUG_ENABLED + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef FREEBOB_DRIVER_WITH_MIDI +#include +#include +#endif + +// debug print control flags +#define DEBUG_LEVEL_BUFFERS (1<<0) +#define DEBUG_LEVEL_HANDLERS (1<<1) +#define DEBUG_LEVEL_XRUN_RECOVERY (1<<2) +#define DEBUG_LEVEL_WAIT (1<<3) + +#define DEBUG_LEVEL_RUN_CYCLE (1<<8) + +#define DEBUG_LEVEL_PACKETCOUNTER (1<<16) +#define DEBUG_LEVEL_STARTUP (1<<17) +#define DEBUG_LEVEL_THREADS (1<<18) + +#ifdef DEBUG_ENABLED + +// default debug level +#define DEBUG_LEVEL ( DEBUG_LEVEL_RUN_CYCLE | \ + (DEBUG_LEVEL_XRUN_RECOVERY)| DEBUG_LEVEL_STARTUP | DEBUG_LEVEL_WAIT | DEBUG_LEVEL_PACKETCOUNTER) + +#warning Building debug build! + +#define printMessage(format, args...) jack_error( "FreeBoB MSG: %s:%d (%s): " format, __FILE__, __LINE__, __FUNCTION__, ##args ) +#define printError(format, args...) jack_error( "FreeBoB ERR: %s:%d (%s): " format, __FILE__, __LINE__, __FUNCTION__, ##args ) + +/* #define printEnter() jack_error( "FBDRV ENTERS: %s (%s)", __FUNCTION__, __FILE__) + #define printExit() jack_error( "FBDRV EXITS: %s (%s)", __FUNCTION__, __FILE__)*/ +#define printEnter() +#define printExit() + +#define debugError(format, args...) jack_error( "FREEBOB ERR: %s:%d (%s): " format, __FILE__, __LINE__, __FUNCTION__, ##args ) +#define debugPrint(Level, format, args...) if(DEBUG_LEVEL & (Level)) jack_error("DEBUG %s:%d (%s) :" format, __FILE__, __LINE__, __FUNCTION__, ##args ); +#define debugPrintShort(Level, format, args...) if(DEBUG_LEVEL & (Level)) jack_error( format,##args ); +#define debugPrintWithTimeStamp(Level, format, args...) if(DEBUG_LEVEL & (Level)) jack_error( "%16lu: "format, debugGetCurrentUTime(),##args ); +#define SEGFAULT int *test=NULL; *test=1; +#else +#define DEBUG_LEVEL + +#define printMessage(format, args...) if(g_verbose) \ + jack_error("FreeBoB MSG: " format, ##args ) +#define printError(format, args...) jack_error("FreeBoB ERR: " format, ##args ) + +#define printEnter() +#define printExit() + +#define debugError(format, args...) +#define debugPrint(Level, format, args...) +#define debugPrintShort(Level, format, args...) +#define debugPrintWithTimeStamp(Level, format, args...) +#endif + +// thread priority setup +#define FREEBOB_RT_PRIORITY_PACKETIZER_RELATIVE 5 + +#ifdef FREEBOB_DRIVER_WITH_MIDI + +#define ALSA_SEQ_BUFF_SIZE 1024 +#define MIDI_TRANSMIT_BUFFER_SIZE 1024 +#define MIDI_THREAD_SLEEP_TIME_USECS 100 +// midi priority should be higher than the audio priority in order to +// make sure events are not only delivered on period boundarys +// but I think it should be smaller than the packetizer thread in order not +// to lose any packets +#define FREEBOB_RT_PRIORITY_MIDI_RELATIVE 4 + +#endif + +typedef struct _freebob_driver freebob_driver_t; + +/* + * Jack Driver command line parameters + */ + +typedef struct _freebob_jack_settings freebob_jack_settings_t; +struct _freebob_jack_settings +{ + int period_size_set; + jack_nframes_t period_size; + + int sample_rate_set; + int sample_rate; + + int buffer_size_set; + jack_nframes_t buffer_size; + + int port_set; + int port; + + int node_id_set; + int node_id; + + int playback_ports; + int capture_ports; + + jack_nframes_t capture_frame_latency; + jack_nframes_t playback_frame_latency; + + freebob_handle_t fb_handle; +}; + +#ifdef FREEBOB_DRIVER_WITH_MIDI + +typedef struct +{ + int stream_nr; + int seq_port_nr; + snd_midi_event_t *parser; + snd_seq_t *seq_handle; +} +freebob_midi_port_t; + +typedef struct _freebob_driver_midi_handle +{ + freebob_device_t *dev; + freebob_driver_t *driver; + + snd_seq_t *seq_handle; + + pthread_t queue_thread; + pthread_t dequeue_thread; + int queue_thread_realtime; + int queue_thread_priority; + + int nb_input_ports; + int nb_output_ports; + + freebob_midi_port_t **input_ports; + freebob_midi_port_t **output_ports; + + freebob_midi_port_t **input_stream_port_map; + int *output_port_stream_map; +} +freebob_driver_midi_handle_t; + +#endif +/* + * JACK driver structure + */ + +struct _freebob_driver +{ + JACK_DRIVER_NT_DECL + + jack_nframes_t sample_rate; + jack_nframes_t period_size; + unsigned long wait_time; + + jack_time_t wait_last; + jack_time_t wait_next; + int wait_late; + + jack_client_t *client; + + int xrun_detected; + int xrun_count; + + int process_count; + + /* settings from the command line */ + freebob_jack_settings_t settings; + + /* the freebob virtual device */ + freebob_device_t *dev; + + JSList *capture_ports; + JSList *playback_ports; + JSList *monitor_ports; + unsigned long playback_nchannels; + unsigned long capture_nchannels; + unsigned long playback_nchannels_audio; + unsigned long capture_nchannels_audio; + + jack_nframes_t playback_frame_latency; + jack_nframes_t capture_frame_latency; + + freebob_device_info_t device_info; + freebob_options_t device_options; + +#ifdef FREEBOB_DRIVER_WITH_MIDI + freebob_driver_midi_handle_t *midi_handle; +#endif +}; + +#endif /* __JACK_FREEBOB_DRIVER_H__ */ + +