diff options
-rw-r--r-- | drivers/freebob/freebob_driver.c | 294 | ||||
-rw-r--r-- | drivers/freebob/freebob_driver.h | 92 |
2 files changed, 262 insertions, 124 deletions
diff --git a/drivers/freebob/freebob_driver.c b/drivers/freebob/freebob_driver.c index 871c6b8..86a0870 100644 --- a/drivers/freebob/freebob_driver.c +++ b/drivers/freebob/freebob_driver.c @@ -5,7 +5,7 @@ * http://freebob.sf.net * http://jackit.sf.net * - * Copyright (C) 2005 Pieter Palmers <pieterpalmers@users.sourceforge.net> + * Copyright (C) 2005-2007 Pieter Palmers <pieter.palmers@ffado.org> * * 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 @@ -50,7 +50,7 @@ static int freebob_driver_stop (freebob_driver_t *driver); -#ifdef FREEBOB_DRIVER_WITH_MIDI +#ifdef FREEBOB_DRIVER_WITH_ALSA_MIDI static freebob_driver_midi_handle_t *freebob_driver_midi_init(freebob_driver_t *driver); static void freebob_driver_midi_finish (freebob_driver_midi_handle_t *m); static int freebob_driver_midi_start (freebob_driver_midi_handle_t *m); @@ -64,9 +64,13 @@ static int freebob_driver_attach (freebob_driver_t *driver) { char buf[64]; +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + char buf2[64]; +#endif channel_t chn; jack_port_t *port=NULL; int port_flags; + int error=0; g_verbose=driver->engine->verbose; driver->device_options.verbose=g_verbose; @@ -90,7 +94,7 @@ freebob_driver_attach (freebob_driver_t *driver) return -1; } -#ifdef FREEBOB_DRIVER_WITH_MIDI +#ifdef FREEBOB_DRIVER_WITH_ALSA_MIDI driver->midi_handle=freebob_driver_midi_init(driver); if(!driver->midi_handle) { printError("-----------------------------------------------------------"); @@ -113,59 +117,117 @@ freebob_driver_attach (freebob_driver_t *driver) driver->capture_nchannels=freebob_streaming_get_nb_capture_streams(driver->dev); +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + // allocate midi structures + driver->midi_in_ports=calloc(driver->capture_nchannels, sizeof(freebob_midi_input_port_t)); + if (driver->midi_in_ports == NULL) return -ENOMEM; +#endif + for (chn = 0; chn < driver->capture_nchannels; chn++) { - + error=0; + freebob_streaming_get_capture_stream_name(driver->dev, chn, buf, sizeof(buf) - 1); - - if(freebob_streaming_get_capture_stream_type(driver->dev, chn) != freebob_stream_type_audio) { - printMessage ("Don't register capture port %s", buf); -// continue; - // we have to add a NULL entry in the list to be able to loop over the channels in the read/write routines - driver->capture_ports = - jack_slist_append (driver->capture_ports, NULL); - } else { - printMessage ("Registering capture port %s", buf); + switch(freebob_streaming_get_capture_stream_type(driver->dev, chn)) { + case freebob_stream_type_audio: + printMessage ("Registering audio capture port %s", buf); if ((port = jack_port_register (driver->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0)) == NULL) { printError (" cannot register port for %s", buf); + error=1; break; } driver->capture_ports = jack_slist_append (driver->capture_ports, port); + break; + +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + case freebob_stream_type_midi: + snprintf(buf2, 64, "midiin%d_%s",(int)chn,buf); // needed to avoid duplicate names + printMessage ("Registering midi capture port %s", buf2); + if ((port = jack_port_register (driver->client, buf2, + JACK_DEFAULT_MIDI_TYPE, + port_flags, 0)) == NULL) { + printError (" cannot register port for %s", buf2); + error=1; + break; + } + + // init the midi unpacker for this port + midi_unpack_init(&driver->midi_in_ports[chn].unpack); + + driver->capture_ports = + jack_slist_append (driver->capture_ports, port); + break; +#endif + + default: + printMessage ("Don't register capture port for %s", buf); + // we have to add a NULL entry in the list to be able to loop over the channels in the read/write routines + driver->capture_ports = + jack_slist_append (driver->capture_ports, NULL); + break; } - jack_port_set_latency (port, driver->period_size + driver->capture_frame_latency); + if(error) break; + jack_port_set_latency (port, driver->period_size + driver->capture_frame_latency); } port_flags = JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal; driver->playback_nchannels=freebob_streaming_get_nb_playback_streams(driver->dev); +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + // allocate midi structures + driver->midi_out_ports=calloc(driver->playback_nchannels, sizeof(freebob_midi_output_port_t)); + if (driver->midi_out_ports == NULL) return -ENOMEM; +#endif + for (chn = 0; chn < driver->playback_nchannels; chn++) { + error=0; freebob_streaming_get_playback_stream_name(driver->dev, chn, buf, sizeof(buf) - 1); - if(freebob_streaming_get_playback_stream_type(driver->dev, chn) != freebob_stream_type_audio) { - printMessage ("Don't register playback port %s", buf); -// continue; - // we have to add a NULL entry in the list to be able to loop over the channels in the read/write routines - driver->playback_ports = - jack_slist_append (driver->playback_ports, NULL); - } else { - printMessage ("Registering playback port %s", buf); + switch(freebob_streaming_get_playback_stream_type(driver->dev, chn)){ + case freebob_stream_type_audio: + printMessage ("Registering playback audio port %s", buf); if ((port = jack_port_register (driver->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0)) == NULL) { printError(" cannot register port for %s", buf); + error=1; break; } driver->playback_ports = jack_slist_append (driver->playback_ports, port); + break; +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + case freebob_stream_type_midi: + snprintf(buf2, 64, "midiout%d_%s",(int)chn,buf); // needed to avoid duplicate names + printMessage ("Registering playback midi port %s", buf2); + if ((port = jack_port_register (driver->client, buf2, + JACK_DEFAULT_MIDI_TYPE, + port_flags, 0)) == NULL) { + printError(" cannot register port for %s", buf2); + error=1; + break; + } + + driver->playback_ports = + jack_slist_append (driver->playback_ports, port); + break; +#endif + default: + printMessage ("Don't register playback port %s", buf); + // we have to add a NULL entry in the list to be able to loop over the channels in the read/write routines + driver->playback_ports = + jack_slist_append (driver->playback_ports, NULL); } - jack_port_set_latency (port, (driver->period_size * (driver->device_options.nb_buffers - 1)) + driver->playback_frame_latency); + if(error) break; + + jack_port_set_latency (port, (driver->period_size * (driver->device_options.nb_buffers - 1)) + driver->playback_frame_latency); } return jack_activate (driver->client); @@ -205,44 +267,18 @@ freebob_driver_detach (freebob_driver_t *driver) freebob_streaming_finish(driver->dev); driver->dev=NULL; -#ifdef FREEBOB_DRIVER_WITH_MIDI +#ifdef FREEBOB_DRIVER_WITH_ALSA_MIDI if(driver->midi_handle) { freebob_driver_midi_finish(driver->midi_handle); } driver->midi_handle=NULL; #endif - return 0; - -} - -static inline void -freebob_driver_read_from_channel (freebob_driver_t *driver, - channel_t channel, - jack_default_audio_sample_t *dst, - jack_nframes_t nsamples) -{ - - freebob_sample_t buffer[nsamples]; - char *src=(char *)buffer; - - freebob_streaming_read(driver->dev, channel, buffer, nsamples); - - /* ALERT: signed sign-extension portability !!! */ - - while (nsamples--) { - int x; -#if __BYTE_ORDER == __LITTLE_ENDIAN - memcpy((char*)&x + 1, src, 3); -#elif __BYTE_ORDER == __BIG_ENDIAN - memcpy(&x, src, 3); +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + free(driver->midi_in_ports); + free(driver->midi_out_ports); #endif - x >>= 8; - *dst = x / SAMPLE_MAX_24BIT; - dst++; - src += sizeof(freebob_sample_t); - } - + return 0; } static int @@ -264,14 +300,44 @@ freebob_driver_read (freebob_driver_t * driver, jack_nframes_t nframes) stream_type = freebob_streaming_get_capture_stream_type(driver->dev, chn); if(stream_type == freebob_stream_type_audio) { port = (jack_port_t *) node->data; - buf = jack_port_get_buffer (port, nframes); + if(!buf) buf=(jack_default_audio_sample_t *)addr_of_nullbuffer; - freebob_streaming_set_capture_stream_buffer(driver->dev, chn, (char *)(buf), freebob_buffer_type_float); + freebob_streaming_set_capture_stream_buffer( + driver->dev, chn, (char *)(buf), freebob_buffer_type_float); + +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI } else if(stream_type == freebob_stream_type_midi) { - // these should be read/written with the per-stream functions + unsigned int midibuff[64]; + unsigned char midibuff2[64]; + int samples_read; + port = (jack_port_t *) node->data; + buf = jack_port_get_buffer (port, nframes); + + jack_midi_clear_buffer(buf); + + samples_read=freebob_streaming_read( + driver->dev, chn, midibuff, 64); + + while(samples_read) { + int idx; + int done; + //printMessage("MIDI: "); + for (idx=0;idx<samples_read;idx++) { + midibuff2[idx]=(unsigned char)(midibuff[idx] & 0xFF); + //printMessage(" %02X", midibuff2[idx]); + } + + done = midi_unpack_buf( + &driver->midi_in_ports[chn].unpack, + midibuff2, samples_read, buf, 0 /* time */); + + samples_read=freebob_streaming_read( + driver->dev, chn, midibuff, 64); + } +#endif } else { // empty other buffers without doing something with them freebob_streaming_set_capture_stream_buffer(driver->dev, chn, (char *)(nullbuffer), freebob_buffer_type_uint24); } @@ -283,41 +349,6 @@ freebob_driver_read (freebob_driver_t * driver, jack_nframes_t nframes) printExit(); return 0; - -} - -static inline void -freebob_driver_write_to_channel (freebob_driver_t *driver, - channel_t channel, - jack_default_audio_sample_t *buf, - jack_nframes_t nsamples) -{ - long long y; - freebob_sample_t buffer[nsamples]; - unsigned int i=0; - char *dst=(char *)buffer; - - // convert from float to integer - for(;i<nsamples;i++) { - y = (long long)(*buf * SAMPLE_MAX_24BIT); - - if (y > (INT_MAX >> 8 )) { - y = (INT_MAX >> 8); - } else if (y < (INT_MIN >> 8 )) { - y = (INT_MIN >> 8 ); - } -#if __BYTE_ORDER == __LITTLE_ENDIAN - memcpy (dst, &y, 3); -#elif __BYTE_ORDER == __BIG_ENDIAN - memcpy (dst, (char *)&y + 5, 3); -#endif - dst += sizeof(freebob_sample_t); - buf++; - } - - // write to the freebob streaming device - freebob_streaming_write(driver->dev, channel, buffer, nsamples); - } static int @@ -356,9 +387,41 @@ freebob_driver_write (freebob_driver_t * driver, jack_nframes_t nframes) freebob_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(buf), freebob_buffer_type_float); +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI } else if(stream_type == freebob_stream_type_midi) { - // these should be read/written with the per-stream functions + int i,idx; + int samples_written; + + port = (jack_port_t *) node->data; + buf = jack_port_get_buffer (port, nframes); + + int nevents = jack_midi_get_event_count(buf); + + if (nevents) + printMessage("jack_out: %d events\n", nevents); + for (i=0; i<nevents; ++i) { + jack_midi_event_t event; + + jack_midi_event_get(&event, buf, i); + + midi_pack_event(&driver->midi_out_ports[chn].packer, &event); + + freebob_sample_t midibuff[event.size]; + for (idx=0;idx<event.size;idx++) { + midibuff[idx]=(freebob_sample_t)(event.buffer[idx]); + } + + samples_written=freebob_streaming_write( + driver->dev, chn, midibuff, event.size); + if (samples_written!=event.size) { + printMessage("midi out: buffer overrun\n"); + break; + } else { + printMessage("midi out: sent %d-byte event at %ld\n", (int)event.size, (long)event.time); + } + } +#endif } else { // empty other buffers without doing something with them freebob_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(nullbuffer), freebob_buffer_type_uint24); } @@ -446,10 +509,27 @@ freebob_driver_run_cycle (freebob_driver_t *driver) } if ((nframes == 0)) { + int chn; + chn=0; // avoid unused variable warning when compiling without jack midi + /* we detected an xrun and restarted: notify * clients about the delay. */ printMessage("xrun detected"); engine->delay (engine, delayed_usecs); + + // reset the midi stuff + #ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + for (chn = 0; chn < driver->capture_nchannels; chn++) { + // init the midi unpacker for this port + midi_unpack_reset(&driver->midi_in_ports[chn].unpack); + } + + for (chn = 0; chn < driver->playback_nchannels; chn++) { + // init the midi unpacker for this port + midi_pack_reset(&driver->midi_out_ports[chn].packer); + } + #endif + return 0; } @@ -523,8 +603,10 @@ static int freebob_driver_start (freebob_driver_t *driver) { int retval=0; + int chn; + chn=0; // avoid unused variable warning when compiling without jack midi -#ifdef FREEBOB_DRIVER_WITH_MIDI +#ifdef FREEBOB_DRIVER_WITH_ALSA_MIDI if(driver->midi_handle) { if((retval=freebob_driver_midi_start(driver->midi_handle))) { printError("Could not start MIDI threads"); @@ -533,13 +615,26 @@ freebob_driver_start (freebob_driver_t *driver) } #endif + // reset the midi stuff +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + for (chn = 0; chn < driver->capture_nchannels; chn++) { + // init the midi unpacker for this port + midi_unpack_reset(&driver->midi_in_ports[chn].unpack); + } + + for (chn = 0; chn < driver->playback_nchannels; chn++) { + // init the midi unpacker for this port + midi_pack_reset(&driver->midi_out_ports[chn].packer); + } +#endif + if((retval=freebob_streaming_start(driver->dev))) { printError("Could not start streaming threads"); -#ifdef FREEBOB_DRIVER_WITH_MIDI +#ifdef FREEBOB_DRIVER_WITH_ALSA_MIDI if(driver->midi_handle) { freebob_driver_midi_stop(driver->midi_handle); } -#endif +#endif return retval; } @@ -552,7 +647,7 @@ freebob_driver_stop (freebob_driver_t *driver) { int retval=0; -#ifdef FREEBOB_DRIVER_WITH_MIDI +#ifdef FREEBOB_DRIVER_WITH_ALSA_MIDI if(driver->midi_handle) { if((retval=freebob_driver_midi_stop(driver->midi_handle))) { printError("Could not stop MIDI threads"); @@ -672,7 +767,7 @@ freebob_driver_delete (freebob_driver_t *driver) free (driver); } -#ifdef FREEBOB_DRIVER_WITH_MIDI +#ifdef FREEBOB_DRIVER_WITH_ALSA_MIDI /* * MIDI support */ @@ -694,6 +789,9 @@ void * freebob_driver_midi_queue_thread(void *arg) while(1) { // get next event, if one is present while ((snd_seq_event_input(m->seq_handle, &ev) > 0)) { + if (ev->source.client == SND_SEQ_CLIENT_SYSTEM) + continue; + // get the port this event is originated from freebob_midi_port_t *port=NULL; for (i=0;i<m->nb_output_ports;i++) { diff --git a/drivers/freebob/freebob_driver.h b/drivers/freebob/freebob_driver.h index 00848ee..fcaf374 100644 --- a/drivers/freebob/freebob_driver.h +++ b/drivers/freebob/freebob_driver.h @@ -6,7 +6,7 @@ * http://freebob.sf.net * http://jackit.sf.net * - * Copyright (C) 2005 Pieter Palmers <pieterpalmers@users.sourceforge.net> + * Copyright (C) 2005-2007 Pieter Palmers <pieter.palmers@ffado.org> * * 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 @@ -31,7 +31,19 @@ #ifndef __JACK_FREEBOB_DRIVER_H__ #define __JACK_FREEBOB_DRIVER_H__ -#define FREEBOB_DRIVER_WITH_MIDI +// Only one of these ! +#define FREEBOB_DRIVER_WITH_ALSA_MIDI +//#define FREEBOB_DRIVER_WITH_JACK_MIDI + +#ifdef FREEBOB_DRIVER_WITH_ALSA_MIDI + #ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + #error "Can't have both ALSA midi and JACK midi defined for the FreeBoB backend" + #endif +#endif + +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + #warning "JACK midi for FreeBoB is experimental and has severe jitter issues." +#endif #include <libfreebob/freebob.h> #include <libfreebob/freebob_streaming.h> @@ -52,11 +64,6 @@ #include <jack/engine.h> #include <jack/types.h> -#ifdef FREEBOB_DRIVER_WITH_MIDI -#include <jack/thread.h> -#include <alsa/asoundlib.h> -#endif - // debug print control flags #define DEBUG_LEVEL_BUFFERS (1<<0) #define DEBUG_LEVEL_HANDLERS (1<<1) @@ -110,18 +117,45 @@ // thread priority setup #define FREEBOB_RT_PRIORITY_PACKETIZER_RELATIVE 5 -#ifdef FREEBOB_DRIVER_WITH_MIDI +// 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 +#ifdef FREEBOB_DRIVER_WITH_ALSA_MIDI + +#include <jack/thread.h> +#include <alsa/asoundlib.h> + +#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 // FREEBOB_DRIVER_WITH_ALSA_MIDI + +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + +#include "../alsa-midi/midi_pack.h" +#include "../alsa-midi/midi_unpack.h" +#include <jack/midiport.h> + +typedef struct freebob_midi_input_port_t { + // jack + midi_unpack_t unpack; + + // midi + int overruns; +} freebob_midi_input_port_t; + +typedef struct freebob_midi_output_port_t { + // jack + midi_pack_t packer; +} freebob_midi_output_port_t; + +#endif // FREEBOB_DRIVER_WITH_JACK_MIDI -#endif typedef struct _freebob_driver freebob_driver_t; @@ -155,7 +189,7 @@ struct _freebob_jack_settings { freebob_handle_t fb_handle; }; -#ifdef FREEBOB_DRIVER_WITH_MIDI +#ifdef FREEBOB_DRIVER_WITH_ALSA_MIDI typedef struct { @@ -202,8 +236,8 @@ struct _freebob_driver jack_nframes_t period_size; unsigned long wait_time; - jack_time_t wait_last; - jack_time_t wait_next; + jack_time_t wait_last; + jack_time_t wait_next; int wait_late; jack_client_t *client; @@ -219,11 +253,11 @@ struct _freebob_driver /* the freebob virtual device */ freebob_device_t *dev; - JSList *capture_ports; - JSList *playback_ports; - JSList *monitor_ports; - channel_t playback_nchannels; - channel_t capture_nchannels; + JSList *capture_ports; + JSList *playback_ports; + JSList *monitor_ports; + channel_t playback_nchannels; + channel_t capture_nchannels; jack_nframes_t playback_frame_latency; jack_nframes_t capture_frame_latency; @@ -231,10 +265,16 @@ struct _freebob_driver freebob_device_info_t device_info; freebob_options_t device_options; -#ifdef FREEBOB_DRIVER_WITH_MIDI +#ifdef FREEBOB_DRIVER_WITH_ALSA_MIDI freebob_driver_midi_handle_t *midi_handle; #endif +#ifdef FREEBOB_DRIVER_WITH_JACK_MIDI + freebob_midi_input_port_t *midi_in_ports; + freebob_midi_output_port_t *midi_out_ports; +#endif + + }; |