summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpieterpalmers <pieterpalmers@0c269be4-1314-0410-8aa9-9f06e86f4224>2007-05-27 21:08:35 +0000
committerpieterpalmers <pieterpalmers@0c269be4-1314-0410-8aa9-9f06e86f4224>2007-05-27 21:08:35 +0000
commitfbf63c578f150cf03cb533179ed5070329504bed (patch)
tree092d389ff8f97e36f394b1d13cf3a182c960084a
parente14a446d30f27167bed849d9199cae5a42c9a974 (diff)
downloadjack1-fbf63c578f150cf03cb533179ed5070329504bed.tar.gz
- Introduce jackmidi handling of FireWire hardware MIDI ports. This can be chosen instead of the ALSA SEQ handling of the MIDI ports. Note that this
implementation has severe issues regarding timing, since libfreebob doesn't provide/use the timestamps for the MIDI events. The ALSA SEQ backend solves this by polling to see if there are events received, and they are handled ASAP. The jackmidi approach inherently postpones processing of midi events to period boundaries. !! This is a test implementation for future (libffado) use. Don't enable for end-user use. It is not useful. You have been warned. !! git-svn-id: svn+ssh://jackaudio.org/trunk/jack@1043 0c269be4-1314-0410-8aa9-9f06e86f4224
-rw-r--r--drivers/freebob/freebob_driver.c294
-rw-r--r--drivers/freebob/freebob_driver.h92
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
+
+
};