Index: jack/doc/Makefile.am =================================================================== RCS file: /cvsroot/jackit/jack/doc/Makefile.am,v retrieving revision 1.16 diff -u -r1.16 Makefile.am --- jack/doc/Makefile.am 28 Nov 2004 00:29:44 -0000 1.16 +++ jack/doc/Makefile.am 5 Dec 2005 02:36:55 -0000 @@ -13,7 +13,8 @@ ../jack/ringbuffer.h \ ../jack/thread.h \ ../jack/transport.h \ - ../jack/types.h + ../jack/types.h \ + ../jack/midiport.h EXTRA_DIST=mainpage.dox transport.dox fsm.png fsm.eps porting.dox Index: jack/doc/mainpage.dox =================================================================== RCS file: /cvsroot/jackit/jack/doc/mainpage.dox,v retrieving revision 1.10 diff -u -r1.10 mainpage.dox --- jack/doc/mainpage.dox 31 Dec 2004 23:55:56 -0000 1.10 +++ jack/doc/mainpage.dox 5 Dec 2005 02:36:55 -0000 @@ -97,6 +97,8 @@ - @ref types.h "" defines the main JACK data types. - @ref thread.h "" functions standardize thread creation for JACK and its clients. + - @ref midiport.h "" functions to handle writing + of midi data to a port In addition, the example-clients directory provides numerous examples of simple JACK clients that nevertheless use the API to do something Index: jack/doc/reference.doxygen.in =================================================================== RCS file: /cvsroot/jackit/jack/doc/reference.doxygen.in,v retrieving revision 1.15 diff -u -r1.15 reference.doxygen.in --- jack/doc/reference.doxygen.in 31 Dec 2004 23:55:56 -0000 1.15 +++ jack/doc/reference.doxygen.in 5 Dec 2005 02:36:55 -0000 @@ -373,7 +373,8 @@ @top_srcdir@/jack/statistics.h \ @top_srcdir@/jack/thread.h \ @top_srcdir@/jack/transport.h \ - @top_srcdir@/jack/types.h + @top_srcdir@/jack/types.h \ + @top_srcdir@/jack/midiport.h # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp Index: jack/example-clients/Makefile.am =================================================================== RCS file: /cvsroot/jackit/jack/example-clients/Makefile.am,v retrieving revision 1.21 diff -u -r1.21 Makefile.am --- jack/example-clients/Makefile.am 7 Apr 2004 04:52:58 -0000 1.21 +++ jack/example-clients/Makefile.am 5 Dec 2005 02:36:55 -0000 @@ -37,7 +37,9 @@ jack_lsp \ jack_freewheel \ $(JACKREC) \ - $(JACK_TRANSPORT) + $(JACK_TRANSPORT) \ + jack_midisine \ + jack_midiseq if HAVE_SNDFILE # note! jackrec_CFLAGS syntax not supported by automake-1.4 @@ -111,6 +113,14 @@ jack_unload_LDFLAGS = @OS_LDFLAGS@ jack_unload_LDADD = ../libjack/libjack.la +jack_midiseq_SOURCES = midiseq.c +jack_midiseq_LDFLAGS = -lrt -ldl -lpthread +jack_midiseq_LDADD = ../libjack/libjack.la + +jack_midisine_SOURCES = midisine.c +jack_midisine_LDFLAGS = -lrt -ldl -lpthread -lm +jack_midisine_LDADD = ../libjack/libjack.la + # # sample in-process client(s) # Index: jack/example-clients/midiseq.c =================================================================== RCS file: jack/example-clients/midiseq.c diff -N jack/example-clients/midiseq.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ jack/example-clients/midiseq.c 5 Dec 2005 02:36:55 -0000 @@ -0,0 +1,117 @@ +/* + Copyright (C) 2004 Ian Esten + + 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 + +jack_client_t *client; +jack_port_t *output_port; + +unsigned char* note_frqs; +jack_nframes_t* note_starts; +jack_nframes_t* note_lengths; +jack_nframes_t num_notes; +jack_nframes_t loop_nsamp; +jack_nframes_t loop_index; + +void usage() +{ + fprintf(stderr, "usage: jack_midiseq name nsamp [startindex note nsamp] ...... [startindex note nsamp]\n"); + fprintf(stderr, "eg: jack_midiseq 24000 0 60 8000 12000 63 8000\n"); + fprintf(stderr, "will play a 1/2 sec loop (if srate is 48khz) with a c4 note at the start of the loop\n"); + fprintf(stderr, "that lasts for 12000 samples, then a d4# that starts at 1/4 sec that lasts for 800 samples\n"); +} + +int process(jack_nframes_t nframes, void *arg) +{ + int i,j; + void* port_buf = jack_port_get_buffer(output_port, nframes); + unsigned char* buffer; + jack_midi_clear_buffer(port_buf, nframes); + /*memset(buffer, 0, nframes*sizeof(jack_default_audio_sample_t));*/ + + for(i=0; i= loop_nsamp ? 0 : loop_index+1; + } + return 0; +} + +int main(int narg, char **args) +{ + int i; + jack_nframes_t nframes; + if((narg<6) || ((narg-3)%3 !=0)) + { + usage(); + exit(1); + } + if((client = jack_client_new (args[1])) == 0) + { + fprintf (stderr, "jack server not running?\n"); + return 1; + } + jack_set_process_callback (client, process, 0); + output_port = jack_port_register (client, "out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); + nframes = jack_get_buffer_size(client); + jack_midi_reset_new_port(jack_port_get_buffer(output_port, nframes), nframes); + loop_index = 0; + num_notes = (narg - 3)/3; + note_frqs = malloc(num_notes*sizeof(unsigned char)); + note_starts = malloc(num_notes*sizeof(unsigned char)); + note_lengths = malloc(num_notes*sizeof(jack_nframes_t)); + loop_nsamp = atoi(args[2]); + for(i=0; i +#include +#include +#include +#include +#include + +#include +#include + +jack_port_t *input_port; +jack_port_t *output_port; +jack_default_audio_sample_t ramp=0.0; +jack_default_audio_sample_t note_on; +unsigned char note = 0; +jack_default_audio_sample_t note_frqs[128]; + +void calc_note_frqs(jack_default_audio_sample_t srate) +{ + int i; + for(i=0; i<128; i++) + { + note_frqs[i] = (2.0 * 440.0 / 32.0) * pow(2, (((jack_default_audio_sample_t)i - 9.0) / 12.0)) / srate; + } +} + +int process(jack_nframes_t nframes, void *arg) +{ + int i; + void* port_buf = jack_port_get_buffer(input_port, nframes); + jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port, nframes); + jack_midi_event_t in_event; + jack_nframes_t event_index = 0; + jack_nframes_t event_count = jack_midi_port_get_info(port_buf, nframes)->event_count; + if(event_count > 1) + { + printf(" midisine: have %d events\n", event_count); + for(i=0; i 1.0) ? ramp - 2.0 : ramp; + out[i] = note_on*sin(2*M_PI*ramp); + } + return 0; +} + +int srate(jack_nframes_t nframes, void *arg) +{ + printf("the sample rate is now %" PRIu32 "/sec\n", nframes); + calc_note_frqs((jack_default_audio_sample_t)nframes); + return 0; +} + +void jack_shutdown(void *arg) +{ + exit(1); +} + +int main(int narg, char **args) +{ + jack_client_t *client; + + if ((client = jack_client_new("midisine")) == 0) + { + fprintf(stderr, "jack server not running?\n"); + return 1; + } + + calc_note_frqs(jack_get_sample_rate (client)); + + jack_set_process_callback (client, process, 0); + + jack_set_sample_rate_callback (client, srate, 0); + + jack_on_shutdown (client, jack_shutdown, 0); + + input_port = jack_port_register (client, "midi_in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); + output_port = jack_port_register (client, "audio_out", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + + if (jack_activate (client)) + { + fprintf(stderr, "cannot activate client"); + return 1; + } + + /* run until interrupted */ + while(1) + { + sleep(1); + } + jack_client_close(client); + exit (0); +} + Index: jack/jack/Makefile.am =================================================================== RCS file: /cvsroot/jackit/jack/jack/Makefile.am,v retrieving revision 1.27 diff -u -r1.27 Makefile.am --- jack/jack/Makefile.am 30 Oct 2005 11:01:31 -0000 1.27 +++ jack/jack/Makefile.am 5 Dec 2005 02:36:55 -0000 @@ -10,7 +10,8 @@ thread.h \ timestamps.h \ transport.h \ - types.h + types.h \ + midiport.h noinst_HEADERS = \ atomicity.h \ Index: jack/jack/midiport.h =================================================================== RCS file: jack/jack/midiport.h diff -N jack/jack/midiport.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ jack/jack/midiport.h 5 Dec 2005 02:36:55 -0000 @@ -0,0 +1,168 @@ +/* + Copyright (C) 2004 Ian Esten + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + + +#ifndef __JACK_MIDIPORT_H +#define __JACK_MIDIPORT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** Type for raw event data contained in @ref jack_midi_event_t. */ +typedef unsigned char jack_midi_data_t; + +/* buffer is a pointer to the midi data. time is the sample index at which this + * event is valid. size_t is how many bytes of data are in 'buffer'. the events + * in 'buffer' are standard midi messages. note that there is no event type field + * anymore. that is now just byte 0 of the buffer. + */ +typedef struct _jack_midi_event +{ + jack_nframes_t time; + size_t size; + jack_midi_data_t* buffer; +} jack_midi_event_t; + +typedef struct _jack_midi_port_info +{ + jack_nframes_t event_count; +} jack_midi_port_info_t; + +/* returns an info struct. only info in there at the moment is how many + * events are in the buffer + */ +jack_midi_port_info_t* jack_midi_port_get_info(void* port_buffer, jack_nframes_t nframes); + + +/** Get a MIDI event from an event port buffer. + * + * The MIDI event returned is guaranteed to be a complete MIDI + * event (i.e. clients do not have to deal with running status + * as the status byte of the event will always be present). + * + * @param event Event structure to store retrieved event in. + * @param port_buffer Port buffer from which to retrieve event. + * @param event_index Index of event to retrieve. + * @param nframes Number of valid frames this cycle. + * @return 0 on success, ENODATA if buffer is empty. + */ +int jack_midi_event_get(jack_midi_event_t* event, void* port_buffer, jack_nframes_t event_idx, jack_nframes_t nframes); + + +/** Initialise the port state. This must be used after port_buffer is allocated */ +void jack_midi_reset_new_port(void* port_buffer, jack_nframes_t nframes); + + +/** Clear an event buffer. + * + * This should be called at the beginning of each process cycle before calling + * @ref jack_midi_event_reserve or @ref jack_midi_event_write. This + * function may not be called on an input port's buffer. + * + * @param port_buffer Port buffer to clear (must be an output port buffer). + * @param nframes Number of valid frames this cycle. + */ +void jack_midi_clear_buffer(void* port_buffer, jack_nframes_t nframes); + + +/** Allocate space for an event to be written to an event port buffer. + * + * Clients are to write the actual event data to be written starting at the + * pointer returned by this function. Clients must not write more than + * @a data_size bytes into this buffer. + * + * @param port_buffer Buffer to write event to. + * @param time Sample offset of event. + * @param data_size Length of event's raw data in bytes. + * @param nframes Number of valid frames this event. + * @return Pointer to the beginning of the reserved event's data buffer, or + * NULL on error (ie not enough space). + */ +jack_midi_data_t* jack_midi_event_reserve(void* port_buffer, jack_nframes_t time, + size_t data_size, jack_nframes_t nframes); + + +/** Write an event into an event port buffer. + * + * This function is simply a wrapper for @ref jack_midi_event_reserve + * which writes the event data into the space reserved in the buffer. + * + * @param port_buffer Buffer to write event to. + * @param time Sample offset of event. + * @param data Message data to be written. + * @param data_size Length of @ref data in bytes. + * @param nframes Number of valid frames this event. + * @return 0 on success, ENOBUFS if there's not enough space in buffer for event. + */ +int jack_midi_event_write(void* port_buffer, jack_nframes_t time, jack_midi_data_t* data, size_t data_size, jack_nframes_t nframes); + + + +/* function to make dealing with running status easier. calls to write update the + * running status byte stored in a port, and this function returns it. note that + * when a port is initialised, last_status_byte is set to 0, which is not a valid + * status byte. valid status bytes are in the range 0x80 <= valid < 0xf8 */ +/* this function cannot be supported because it cannot be guaranteed that the + * last status byte stored will be correct. it was intended that + * jack_midi_write_next_event would cache the status byte from the previous write. + * this assumes that users are going to alternate calls to + * jack_midi_write_next_event with writing of actual data. it is a pity that this + * can't be forced as it makes things more efficient. it would have be made clear + * in the api doc that users must write data after calling + * jack_midi_write_next_event for this function to be implementable. either that + * or jack_midi_write_next_event must be removed from the api, and only + * jack_midi_write_next_event2 would be supported. jack_midi_write_next_event is + * heavily used in the hardware i/o client, and removing it would make the input + * thread more complex and use more memory. + * the other way of making this function possible to implement is to force users + * to pass in the first byte of the message (which they should know, as the + * message size they request depends on it). this is probably the best way to do + * things as now that i think about it, the first method suggested in this + * comment might result in erroneous status byte reporting as last_status_byte + * updated with a delay of one message */ +/*jack_midi_data_t jack_midi_get_last_status_byte(void* port_buffer, jack_nframes_t nframes);*/ + + +/** return the last status byte in the stream before event n */ +jack_midi_data_t jack_midi_get_status_before_event_n(void* port_buffer, jack_nframes_t event_index, jack_nframes_t nframes); + +/** Get the number of events that could not be written to @a port_buffer. + * + * This function returning a non-zero value implies @a port_buffer is full. + * Currently the only way this can happen is if events are lost on port mixdown. + * + * @param port_buffer Port to receive count for. + * @param nframes Number of valid frames this cycle. + * @returns Number of events that could not be written to @a port_buffer. + */ +jack_nframes_t jack_midi_get_lost_event_count(void* port_buffer, jack_nframes_t nframes); + + +#ifdef __cplusplus +} +#endif + + +#endif /* __JACK_MIDIPORT_H */ + + Index: jack/jack/port.h =================================================================== RCS file: /cvsroot/jackit/jack/jack/port.h,v retrieving revision 1.20 diff -u -r1.20 port.h --- jack/jack/port.h 20 Feb 2004 04:31:22 -0000 1.20 +++ jack/jack/port.h 5 Dec 2005 02:36:55 -0000 @@ -42,6 +42,9 @@ */ #define JACK_MAX_PORT_TYPES 4 #define JACK_AUDIO_PORT_TYPE 0 +#define JACK_MIDI_PORT_TYPE 1 + +/* these should probably go somewhere else, but not in */ #define JACK_CLIENT_NAME_SIZE 33 typedef uint32_t jack_client_id_t; Index: jack/jack/types.h =================================================================== RCS file: /cvsroot/jackit/jack/jack/types.h,v retrieving revision 1.34 diff -u -r1.34 types.h --- jack/jack/types.h 25 Dec 2004 18:39:44 -0000 1.34 +++ jack/jack/types.h 5 Dec 2005 02:36:55 -0000 @@ -170,9 +170,10 @@ /** * Used for the type argument of jack_port_register() for default - * audio ports. + * audio and midi ports. */ #define JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" +#define JACK_DEFAULT_MIDI_TYPE "8 bit raw midi" /** * For convenience, use this typedef if you want to be able to change Index: jack/libjack/Makefile.am =================================================================== RCS file: /cvsroot/jackit/jack/libjack/Makefile.am,v retrieving revision 1.24 diff -u -r1.24 Makefile.am --- jack/libjack/Makefile.am 19 Dec 2004 18:41:29 -0000 1.24 +++ jack/libjack/Makefile.am 5 Dec 2005 02:36:55 -0000 @@ -16,6 +16,7 @@ messagebuffer.c \ pool.c \ port.c \ + midiport.c \ ringbuffer.c \ shm.c \ thread.c \ Index: jack/libjack/midiport.c =================================================================== RCS file: jack/libjack/midiport.c diff -N jack/libjack/midiport.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ jack/libjack/midiport.c 5 Dec 2005 02:36:56 -0000 @@ -0,0 +1,313 @@ +/* + Copyright (C) 2004 Ian Esten + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + + +#include +#include +#include +#include +#include + +/* even though the new implementation with byte offsets being stored instead of + * pointers, last_write_loc can't be removed as it gets used in the mixdown + * function. */ +typedef struct _jack_midi_port_info_private +{ + jack_midi_port_info_t info; + jack_nframes_t last_write_loc; + char last_status; /* status byte for last event in buffer */ + jack_nframes_t events_lost; /* number of events lost in this buffer. + * mixdown is the only place that sets + * this for now */ +} jack_midi_port_info_private_t; + +typedef struct _jack_midi_port_internal_event +{ + jack_nframes_t time; + size_t size; + size_t byte_offset; +} jack_midi_port_internal_event_t; + + +jack_midi_port_info_t* +jack_midi_port_get_info(void* port_buffer, jack_nframes_t nframes) +{ + return (jack_midi_port_info_t*)port_buffer; +} + + +int +jack_midi_event_get(jack_midi_event_t* event, void* port_buffer, jack_nframes_t event_idx, jack_nframes_t nframes) +{ + jack_midi_port_internal_event_t* port_event; + jack_midi_port_info_private_t* info = (jack_midi_port_info_private_t*)port_buffer; + if(event_idx >= info->info.event_count) + return ENODATA; + port_event = (jack_midi_port_internal_event_t*)(info+1); + port_event += event_idx; + event->time = port_event->time; + event->size = port_event->size; + event->buffer = ((jack_midi_data_t*)port_buffer) + port_event->byte_offset; + return 0; +} + + +jack_midi_data_t* +jack_midi_event_reserve(void* port_buffer, jack_nframes_t time, size_t data_size, jack_nframes_t nframes) +{ + jack_midi_data_t* retbuf = (jack_midi_data_t*)port_buffer; + /* bad: below line needs to know about buffer scale factor */ + jack_nframes_t buffer_size = nframes*sizeof(jack_default_audio_sample_t)/sizeof(unsigned char); + jack_midi_port_info_private_t* info = (jack_midi_port_info_private_t*)port_buffer; + jack_midi_port_internal_event_t* event_buffer = (jack_midi_port_internal_event_t*)(info + 1); + /* check there is enough space in the buffer for the event. */ + if(info->last_write_loc + sizeof(jack_midi_port_info_private_t) + + (info->info.event_count + 1) * sizeof(jack_midi_port_internal_event_t) + + data_size > buffer_size) + return NULL; + else + { + info->last_write_loc += data_size; + retbuf = &retbuf[buffer_size - 1 - info->last_write_loc]; + event_buffer[info->info.event_count].time = time; + event_buffer[info->info.event_count].size = data_size; + event_buffer[info->info.event_count].byte_offset = buffer_size - 1 - info->last_write_loc; + info->info.event_count += 1; + return retbuf; + } +} + + +int +jack_midi_event_write(void* port_buffer, jack_nframes_t time, jack_midi_data_t* data, size_t data_size, jack_nframes_t nframes) +{ + jack_midi_data_t* retbuf = jack_midi_event_reserve(port_buffer, time, data_size, nframes); + if(retbuf) + { + memcpy(retbuf, data, data_size); + return 0; + } + else + return ENOBUFS; +} + + +/*jack_midi_data_t jack_midi_get_last_status_byte(void* port_buffer, jack_nframes_t nframes) +{ + return ((jack_midi_port_info_private_t*)(port_buffer))->last_status; +}*/ + + +jack_midi_data_t jack_midi_get_status_before_event_n(void* port_buffer, jack_nframes_t event_index, jack_nframes_t nframes) +{ + jack_midi_data_t* event_buf = (jack_midi_data_t*)port_buffer; + jack_midi_port_info_private_t* info = (jack_midi_port_info_private_t*)port_buffer; + jack_midi_port_internal_event_t* event_buffer = (jack_midi_port_internal_event_t*)(info + 1); + + /* safeguard against unitialised ports */ + if(info->info.event_count * sizeof(jack_midi_port_internal_event_t) + sizeof(jack_midi_port_info_private_t) + > nframes * sizeof(jack_default_audio_sample_t)) + return 0; + /* don't know what to do here, so we return 0 */ + if(event_index >= info->info.event_count) + return 0; + if(info->info.event_count > 0) + { + event_index -= 1; + while( (event_index >= 0) + && (event_buf[event_buffer[event_index].byte_offset] < 0x80) + && (event_buf[event_buffer[event_index].byte_offset] > 0xf7) ) + event_index--; + if(event_index >= 0) + return event_buf[event_buffer[event_index].byte_offset]; + } + /* all the events in the port are running status, so we return the previous buffers last + * status byte */ + return info->last_status; +} + + +/* this function can be got rid of now, as the above function replaces it and is more general */ +static jack_midi_data_t get_last_status_byte(void* port_buffer, jack_nframes_t nframes) +{ + int event_index = 0; + jack_midi_data_t* event_buf = (jack_midi_data_t*)port_buffer; + jack_midi_port_info_private_t* info = (jack_midi_port_info_private_t*)port_buffer; + jack_midi_port_internal_event_t* event_buffer = (jack_midi_port_internal_event_t*)(info + 1); + + /* safeguard against unitialised ports */ + if(info->info.event_count * sizeof(jack_midi_port_internal_event_t) + sizeof(jack_midi_port_info_private_t) + > nframes * sizeof(jack_default_audio_sample_t)) + return 0; + if(info->info.event_count > 0) + { + event_index = info->info.event_count - 1; + while( (event_index >= 0) + && (event_buf[event_buffer[event_index].byte_offset] < 0x80) + && (event_buf[event_buffer[event_index].byte_offset] > 0xf7) ) + event_index--; + if(event_index >= 0) + return event_buf[event_buffer[event_index].byte_offset]; + } + return info->last_status; +} + + +/* can't check to make sure this port is an output anymore. if this gets + * called on an input port, all clients after the client that calls it + * will think there are no events in the buffer as the event count has + * been reset + * TODO: this function must take note of system realtime messages and + * skip them when updating last_status. done. */ +void jack_midi_clear_buffer(void* port_buffer, jack_nframes_t nframes) +{ + jack_midi_port_info_private_t* info = (jack_midi_port_info_private_t*)port_buffer; + if(port_buffer == NULL) + { +/* fprintf(stderr, "Error: port_buffer in jack_midi_clear_buffer is NULL!\n");*/ + return; + } + /* find the last status byte in the buffer for use in the mixdown function */ + /* this code is dangerous when used on a freshly allocated buffer that has + * not has jack_midi_reset_new_port called on it, because the event_count + * may be some huge number... */ + info->last_status = get_last_status_byte(port_buffer, nframes); + info->info.event_count = 0; + info->last_write_loc = 0; + info->events_lost = 0; +} + + +void jack_midi_reset_new_port(void* port_buffer, jack_nframes_t nframes) +{ + jack_midi_port_info_private_t* info = (jack_midi_port_info_private_t*)port_buffer; + info->info.event_count = 0; + info->last_write_loc = 0; + info->last_status = 0; + info->events_lost = 0; +} + + +void jack_midi_port_mixdown (jack_port_t *port, jack_nframes_t nframes) +{ + JSList *node; + jack_port_t *input; + jack_nframes_t num_events = 0, i = 0; + int err = 0; + /* smallest_info is for the data buffer holding the next event */ + jack_midi_port_info_private_t *in_info, *out_info, *smallest_info; + /* smallest_event is for the event buffer holding the next event */ + jack_midi_port_internal_event_t *in_events, *smallest_event, *out_events; + /* for assembling new events if running status is interrupted */ + jack_midi_data_t new_event_buf[3], *smallest_buffer; + + out_info = (jack_midi_port_info_private_t*)port->mix_buffer; + out_events = (jack_midi_port_internal_event_t*)(out_info + 1); + /* cache last status byte */ + out_info->last_status = get_last_status_byte(port->mix_buffer, nframes); + + out_info->info.event_count = 0; + + /* initialise smallest_info to point to the first port buffer */ + smallest_info = (jack_midi_port_info_private_t*)jack_output_port_buffer((jack_port_t*)(port->connections->data)); + smallest_event = (jack_midi_port_internal_event_t*)(smallest_info + 1); + + /* in this loop we can use last_write_loc in jack_midi_port_info_private_t + * to store indexes of the last event read from that buffer. this is ok + * because last_write_loc is used when writing events to a buffer, which + * is already complete. + */ + for(node = port->connections; node; node = jack_slist_next (node)) + { + input = (jack_port_t *) node->data; + in_info = (jack_midi_port_info_private_t*)jack_output_port_buffer (input); + num_events += in_info->info.event_count; + in_info->last_write_loc = 0; + /* look to see if first event in each buffer is running status */ + } + + printf(" jack_midi_port_mixdown got %d events\n", num_events); + + while(iconnections; + /* had something else to do here, to do with smallest_info/buf? */ + while(node) + { + in_info = (jack_midi_port_info_private_t*)jack_output_port_buffer (((jack_port_t *) node->data)); + in_events = (jack_midi_port_internal_event_t*)(in_info + 1); + /* make sure there are events left in this port */ + if(in_info->info.event_count > in_info->last_write_loc) + { + /* (first_event_in_buffer_timestamp < smallest_timestamp_event) */ + if(in_events[in_info->last_write_loc].time <= smallest_event[smallest_info->last_write_loc].time) + { + smallest_info = in_info; + smallest_event = (jack_midi_port_internal_event_t*)(&in_events[in_info->last_write_loc]); + smallest_buffer = (jack_midi_data_t*)in_info; + } + } + /* else buffer has no events remaining in it */ + node = jack_slist_next(node); + } + /* write event to output port. + * things to consider: + * if event is running status, make sure previous event in its port is of the same + * type as the last event in the output port. + */ + + + + /* test to see if this event is running status, or 1 byte realtime msg */ + if( (smallest_buffer[smallest_event[smallest_info->last_write_loc].byte_offset] < 0x80) + && ((*new_event_buf = get_last_status_byte((void*)smallest_info, nframes)) + != get_last_status_byte((void*)port->mix_buffer, nframes)) ) + { + memcpy(new_event_buf + 1, + &smallest_buffer[smallest_event[smallest_info->last_write_loc].byte_offset], + smallest_event[smallest_info->last_write_loc].size); + err = jack_midi_event_write(jack_output_port_buffer((jack_port_t*)node->data), + smallest_event[smallest_info->last_write_loc].time, + new_event_buf, + smallest_event[smallest_info->last_write_loc].size + 1, + nframes); + } + else + { + err = jack_midi_event_write(jack_output_port_buffer((jack_port_t*)node->data), + smallest_event[smallest_info->last_write_loc].time, + &smallest_buffer[smallest_event[smallest_info->last_write_loc].byte_offset], + smallest_event[smallest_info->last_write_loc].size, + nframes); + } + if(err < 0) + { + out_info->events_lost = num_events - i - 1; + break; + } + else + i++; + } +} + + +jack_nframes_t jack_midi_get_lost_event_count(void* port_buffer, jack_nframes_t nframes) +{ + return ((jack_midi_port_info_private_t*)port_buffer)->events_lost; +} Index: jack/libjack/port.c =================================================================== RCS file: /cvsroot/jackit/jack/libjack/port.c,v retrieving revision 1.19 diff -u -r1.19 port.c --- jack/libjack/port.c 4 Sep 2005 19:50:57 -0000 1.19 +++ jack/libjack/port.c 5 Dec 2005 02:36:56 -0000 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,9 @@ static void jack_audio_port_mixdown (jack_port_t *port, jack_nframes_t nframes); +extern void jack_midi_port_mixdown (jack_port_t *port, + jack_nframes_t nframes); + /* These function pointers are local to each address space. For * internal clients they reside within jackd; for external clients in * the application process. */ @@ -46,15 +50,22 @@ .mixdown = jack_audio_port_mixdown, }; +jack_port_functions_t jack_builtin_midi_functions = { + .mixdown = jack_midi_port_mixdown, +}; + jack_port_functions_t jack_builtin_NULL_functions = { .mixdown = NULL, }; -/* Only the Audio port type is currently built in. */ +/* Only the Audio and MIDI port types are currently built in. */ jack_port_type_info_t jack_builtin_port_types[] = { { .type_name = JACK_DEFAULT_AUDIO_TYPE, .buffer_scale_factor = 1, }, + { .type_name = JACK_DEFAULT_MIDI_TYPE, + .buffer_scale_factor = 1, + }, { .type_name = "", } }; @@ -336,6 +347,11 @@ port->fptr = jack_builtin_audio_functions; port->shared->has_mixdown = TRUE; + } else if (ptid == JACK_MIDI_PORT_TYPE) { + + port->fptr = jack_builtin_midi_functions; + port->shared->has_mixdown = TRUE; + } else { /* no other builtin functions */ port->fptr = jack_builtin_NULL_functions; @@ -719,7 +735,7 @@ * nframes); } port->fptr.mixdown (port, nframes); - return (jack_default_audio_sample_t *) port->mix_buffer; + return (void *) port->mix_buffer; } int @@ -949,3 +965,8 @@ #endif /* ARCH_X86 */ } } + + + + +