From 4d84d14ac1aa13958eaa2971b03f7f929a519105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 8 Feb 2008 13:00:32 +0000 Subject: reorganized svn path=/trunk/; revision=9400 --- desiredata/src/s_midi.c | 619 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 619 insertions(+) create mode 100644 desiredata/src/s_midi.c (limited to 'desiredata/src/s_midi.c') diff --git a/desiredata/src/s_midi.c b/desiredata/src/s_midi.c new file mode 100644 index 00000000..f8ee0fd9 --- /dev/null +++ b/desiredata/src/s_midi.c @@ -0,0 +1,619 @@ +/* Copyright (c) 1997-1999 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* Clock functions (which should move, but where?) and MIDI queueing */ + +#define PD_PLUSPLUS_FACE +#include "desire.h" +#include "s_stuff.h" +#ifdef UNISTD +#include +#include +#ifdef HAVE_BSTRING_H +#include +#endif +#endif +#ifdef MSW +#include +#include +#include +#include +#endif +#include +#include +#include + +struct t_midiqelem { + double time; + int portno; + unsigned char onebyte; + unsigned char byte1; + unsigned char byte2; + unsigned char byte3; +}; + +#define MIDIQSIZE 1024 + +t_midiqelem midi_outqueue[MIDIQSIZE]; +int midi_outhead, midi_outtail; +t_midiqelem midi_inqueue[MIDIQSIZE]; +int midi_inhead, midi_intail; +static double sys_midiinittime; + +#ifndef API_DEFAULT +#define API_DEFAULT 0 +#endif +int sys_midiapi = API_DEFAULT; + +/* this is our current estimate for at what "system" real time the + current logical time's output should occur. */ +static double sys_dactimeminusrealtime; +/* same for input, should be schduler advance earlier. */ +static double sys_adctimeminusrealtime; + +static double sys_newdactimeminusrealtime = -1e20; +static double sys_newadctimeminusrealtime = -1e20; +static double sys_whenupdate; + +void sys_initmidiqueue() { + sys_midiinittime = clock_getlogicaltime(); + sys_dactimeminusrealtime = sys_adctimeminusrealtime = 0; +} + +/* this is called from the OS dependent code from time to time when we + think we know the delay (outbuftime) in seconds, at which the last-output + audio sample will go out the door. */ +void sys_setmiditimediff(double inbuftime, double outbuftime) { + double dactimeminusrealtime = .001 * clock_gettimesince(sys_midiinittime) - outbuftime - sys_getrealtime(); + double adctimeminusrealtime = .001 * clock_gettimesince(sys_midiinittime) + inbuftime - sys_getrealtime(); + if (dactimeminusrealtime > sys_newdactimeminusrealtime) sys_newdactimeminusrealtime = dactimeminusrealtime; + if (adctimeminusrealtime > sys_newadctimeminusrealtime) sys_newadctimeminusrealtime = adctimeminusrealtime; + if (sys_getrealtime() > sys_whenupdate) { + sys_dactimeminusrealtime = sys_newdactimeminusrealtime; + sys_adctimeminusrealtime = sys_newadctimeminusrealtime; + sys_newdactimeminusrealtime = -1e20; + sys_newadctimeminusrealtime = -1e20; + sys_whenupdate = sys_getrealtime() + 1; + } +} + +/* return the logical time of the DAC sample we believe is currently going out, based on how much "system time" + has elapsed since the last time sys_setmiditimediff got called. */ +static double sys_getmidioutrealtime() {return sys_getrealtime() + sys_dactimeminusrealtime;} +static double sys_getmidiinrealtime () {return sys_getrealtime() + sys_adctimeminusrealtime;} + +static void sys_putnext () { + t_midiqelem &m = midi_outqueue[midi_outtail]; + int portno = m.portno; +#ifdef USEAPI_ALSA + if (sys_midiapi == API_ALSA) { + if (m.onebyte) sys_alsa_putmidibyte(portno, m.byte1); + else sys_alsa_putmidimess(portno, m.byte1, m.byte2, m.byte3); + } else +#endif /* ALSA */ + { + if (m.onebyte) sys_putmidibyte(portno, m.byte1); + else sys_putmidimess(portno, m.byte1, m.byte2, m.byte3); + } + midi_outtail = (midi_outtail + 1 == MIDIQSIZE ? 0 : midi_outtail + 1); +} + +/* #define TEST_DEJITTER */ + +void sys_pollmidioutqueue() { +#ifdef TEST_DEJITTER + static int db = 0; +#endif + double midirealtime = sys_getmidioutrealtime(); +#ifdef TEST_DEJITTER + if (midi_outhead == midi_outtail) db = 0; +#endif + while (midi_outhead != midi_outtail) { +#ifdef TEST_DEJITTER + if (!db) { + post("out: del %f, midiRT %f logicaltime %f, RT %f dacminusRT %f", + (midi_outqueue[midi_outtail].time - midirealtime), + midirealtime, .001 * clock_gettimesince(sys_midiinittime), + sys_getrealtime(), sys_dactimeminusrealtime); + db = 1; + } +#endif + if (midi_outqueue[midi_outtail].time <= midirealtime) + sys_putnext(); + else break; + } +} + +static void sys_queuemidimess(int portno, int onebyte, int a, int b, int c) { + int newhead = midi_outhead +1; + if (newhead == MIDIQSIZE) newhead = 0; + /* if FIFO is full flush an element to make room */ + if (newhead == midi_outtail) sys_putnext(); + t_midiqelem m = midi_outqueue[midi_outhead]; + m.portno = portno; + m.onebyte = onebyte; + m.byte1 = a; + m.byte2 = b; + m.byte3 = c; + m.time = .001 * clock_gettimesince(sys_midiinittime); + midi_outhead = newhead; + sys_pollmidioutqueue(); +} + +#define MIDI_NOTEON 144 +#define MIDI_POLYAFTERTOUCH 160 +#define MIDI_CONTROLCHANGE 176 +#define MIDI_PROGRAMCHANGE 192 +#define MIDI_AFTERTOUCH 208 +#define MIDI_PITCHBEND 224 + +void outmidi_noteon(int portno, int channel, int pitch, int velo) { + if (pitch < 0) pitch = 0; else if (pitch > 127) pitch = 127; + if (velo < 0) velo = 0; else if (velo > 127) velo = 127; + sys_queuemidimess(portno, 0, MIDI_NOTEON + (channel & 0xf), pitch, velo); +} + +void outmidi_controlchange(int portno, int channel, int ctl, int value) { + if (ctl < 0) ctl = 0; else if (ctl > 127) ctl = 127; + if (value < 0) value = 0; else if (value > 127) value = 127; + sys_queuemidimess(portno, 0, MIDI_CONTROLCHANGE + (channel & 0xf), + ctl, value); +} + +void outmidi_programchange(int portno, int channel, int value) { + if (value < 0) value = 0; else if (value > 127) value = 127; + sys_queuemidimess(portno, 0, MIDI_PROGRAMCHANGE + (channel & 0xf), value, 0); +} + +void outmidi_pitchbend(int portno, int channel, int value) { + if (value < 0) value = 0; else if (value > 16383) value = 16383; + sys_queuemidimess(portno, 0, MIDI_PITCHBEND + (channel & 0xf), (value & 127), ((value>>7) & 127)); +} + +void outmidi_aftertouch(int portno, int channel, int value) { + if (value < 0) value = 0; else if (value > 127) value = 127; + sys_queuemidimess(portno, 0, MIDI_AFTERTOUCH + (channel & 0xf), value, 0); +} + +void outmidi_polyaftertouch(int portno, int channel, int pitch, int value) { + if (pitch < 0) pitch = 0; else if (pitch > 127) pitch = 127; + if (value < 0) value = 0; else if (value > 127) value = 127; + sys_queuemidimess(portno, 0, MIDI_POLYAFTERTOUCH + (channel & 0xf), pitch, value); +} + +void outmidi_byte(int portno, int value) { +#ifdef USEAPI_ALSA + if (sys_midiapi == API_ALSA) sys_alsa_putmidibyte(portno, value); else +#endif + sys_putmidibyte(portno, value); +} + +void outmidi_mclk(int portno) {sys_queuemidimess(portno, 1, 0xf8, 0,0);} + +/* ------------------------- MIDI input queue handling ------------------ */ +typedef struct midiparser { + int mp_status; + int mp_gotbyte1; + int mp_byte1; +} t_midiparser; + +#define MIDINOTEOFF 0x80 /* 2 following 'data bytes' */ +#define MIDINOTEON 0x90 /* 2 */ +#define MIDIPOLYTOUCH 0xa0 /* 2 */ +#define MIDICONTROLCHANGE 0xb0 /* 2 */ +#define MIDIPROGRAMCHANGE 0xc0 /* 1 */ +#define MIDICHANNELTOUCH 0xd0 /* 1 */ +#define MIDIPITCHBEND 0xe0 /* 2 */ +#define MIDISTARTSYSEX 0xf0 /* (until F7) */ +#define MIDITIMECODE 0xf1 /* 1 */ +#define MIDISONGPOS 0xf2 /* 2 */ +#define MIDISONGSELECT 0xf3 /* 1 */ +#define MIDIRESERVED1 0xf4 /* ? */ +#define MIDIRESERVED2 0xf5 /* ? */ +#define MIDITUNEREQUEST 0xf6 /* 0 */ +#define MIDIENDSYSEX 0xf7 /* 0 */ +#define MIDICLOCK 0xf8 /* 0 */ +#define MIDITICK 0xf9 /* 0 */ +#define MIDISTART 0xfa /* 0 */ +#define MIDICONT 0xfb /* 0 */ +#define MIDISTOP 0xfc /* 0 */ +#define MIDIACTIVESENSE 0xfe /* 0 */ +#define MIDIRESET 0xff /* 0 */ + +static void sys_dispatchnextmidiin() { + static t_midiparser parser[MAXMIDIINDEV], *parserp; + int portno = midi_inqueue[midi_intail].portno, byte = midi_inqueue[midi_intail].byte1; + if (!midi_inqueue[midi_intail].onebyte) bug("sys_dispatchnextmidiin"); + if (portno < 0 || portno >= MAXMIDIINDEV) bug("sys_dispatchnextmidiin 2"); + parserp = parser + portno; + outlet_setstacklim(); + if (byte >= 0xf8) { + inmidi_realtimein(portno, byte); + } else { + inmidi_byte(portno, byte); + if (byte & 0x80) { + if (byte == MIDITUNEREQUEST || byte == MIDIRESERVED1 || byte == MIDIRESERVED2) { + parserp->mp_status = 0; + } else if (byte == MIDISTARTSYSEX) { + inmidi_sysex(portno, byte); + parserp->mp_status = byte; + } else if (byte == MIDIENDSYSEX) { + inmidi_sysex(portno, byte); + parserp->mp_status = 0; + } else { + parserp->mp_status = byte; + } + parserp->mp_gotbyte1 = 0; + } else { + int cmd = (parserp->mp_status >= 0xf0 ? parserp->mp_status : + (parserp->mp_status & 0xf0)); + int chan = (parserp->mp_status & 0xf); + int byte1 = parserp->mp_byte1, gotbyte1 = parserp->mp_gotbyte1; + switch (cmd) { + case MIDINOTEOFF: + if (gotbyte1) inmidi_noteon(portno, chan, byte1, 0), parserp->mp_gotbyte1 = 0; + else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1; + break; + case MIDINOTEON: + if (gotbyte1) inmidi_noteon(portno, chan, byte1, byte), parserp->mp_gotbyte1 = 0; + else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1; + break; + case MIDIPOLYTOUCH: + if (gotbyte1) inmidi_polyaftertouch(portno, chan, byte1, byte), parserp->mp_gotbyte1 = 0; + else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1; + break; + case MIDICONTROLCHANGE: + if (gotbyte1) inmidi_controlchange(portno, chan, byte1, byte), parserp->mp_gotbyte1 = 0; + else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1; + break; + case MIDIPROGRAMCHANGE: + inmidi_programchange(portno, chan, byte); + break; + case MIDICHANNELTOUCH: + inmidi_aftertouch(portno, chan, byte); + break; + case MIDIPITCHBEND: + if (gotbyte1) inmidi_pitchbend(portno, chan, ((byte << 7) + byte1)), parserp->mp_gotbyte1 = 0; + else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1; + break; + case MIDISTARTSYSEX: + inmidi_sysex(portno, byte); + break; + /* other kinds of messages are just dropped here. We'll + need another status byte before we start letting MIDI in + again (no running status across "system" messages). */ + case MIDITIMECODE: break; /* 1 data byte*/ + case MIDISONGPOS: break; /* 2 */ + case MIDISONGSELECT: break; /* 1 */ + } + } + } + midi_intail = (midi_intail + 1 == MIDIQSIZE ? 0 : midi_intail + 1); +} + +void sys_pollmidiinqueue() { +#ifdef TEST_DEJITTER + static int db = 0; +#endif + double logicaltime = .001 * clock_gettimesince(sys_midiinittime); +#ifdef TEST_DEJITTER + if (midi_inhead == midi_intail) + db = 0; +#endif + while (midi_inhead != midi_intail) { +#ifdef TEST_DEJITTER + if (!db) { + post("in del %f, logicaltime %f, RT %f adcminusRT %f", + (midi_inqueue[midi_intail].time - logicaltime), + logicaltime, sys_getrealtime(), sys_adctimeminusrealtime); + db = 1; + } +#endif +#if 0 + if (midi_inqueue[midi_intail].time <= logicaltime - 0.007) + post("late %f", 1000 * (logicaltime - midi_inqueue[midi_intail].time)); +#endif + if (midi_inqueue[midi_intail].time <= logicaltime) { +#if 0 + post("diff %f", 1000* (logicaltime - midi_inqueue[midi_intail].time)); +#endif + sys_dispatchnextmidiin(); + } + else break; + } +} + + /* this should be called from the system dependent MIDI code when a byte + comes in, as a result of our calling sys_poll_midi. We stick it on a + timetag queue and dispatch it at the appropriate logical time. */ + + +void sys_midibytein(int portno, int byte) { + static int warned = 0; + int newhead = midi_inhead+1; + if (newhead == MIDIQSIZE) newhead = 0; + /* if FIFO is full flush an element to make room */ + if (newhead == midi_intail) { + if (!warned) { + post("warning: MIDI timing FIFO overflowed"); + warned = 1; + } + sys_dispatchnextmidiin(); + } + midi_inqueue[midi_inhead].portno = portno; + midi_inqueue[midi_inhead].onebyte = 1; + midi_inqueue[midi_inhead].byte1 = byte; + midi_inqueue[midi_inhead].time = sys_getmidiinrealtime(); + midi_inhead = newhead; + sys_pollmidiinqueue(); +} + +void sys_pollmidiqueue() { +#if 0 + static double lasttime; + double newtime = sys_getrealtime(); + if (newtime - lasttime > 0.007) + post("delay %d", (int)(1000 * (newtime - lasttime))); + lasttime = newtime; +#endif +#ifdef USEAPI_ALSA + if (sys_midiapi == API_ALSA) sys_alsa_poll_midi(); + else +#endif /* ALSA */ + sys_poll_midi(); /* OS dependent poll for MIDI input */ + sys_pollmidioutqueue(); + sys_pollmidiinqueue(); +} + +/******************** dialog window and device listing ********************/ + +#ifdef USEAPI_ALSA +void midi_alsa_init(); +#endif +#ifndef USEAPI_PORTMIDI +#ifdef USEAPI_OSS +void midi_oss_init(); +#endif +#endif + +/* last requested parameters */ +static int midi_nmidiindev; +static int midi_midiindev[MAXMIDIINDEV]; +static int midi_nmidioutdev; +static int midi_midioutdev[MAXMIDIOUTDEV]; + +void sys_get_midi_apis(char *buf) { + int n = 0; + strcpy(buf, "{ "); + sprintf(buf + strlen(buf), "{default-MIDI %d} ", API_DEFAULT); n++; +#ifdef USEAPI_ALSA + sprintf(buf + strlen(buf), "{ALSA-MIDI %d} ", API_ALSA); n++; +#endif + strcat(buf, "}"); +} +void sys_get_midi_params(int *pnmidiindev, int *pmidiindev, int *pnmidioutdev, int *pmidioutdev) { + *pnmidiindev = midi_nmidiindev; for (int i=0; i 1 || noutdev > 1) flongform = 1; + *(strptr = midiinstr) = 0; + for(i = 0; i < 16; ++i) { + sprintf(strptr,"%3d ",nindev > i && midiindev[i]>= 0 ? midiindev[i] : -1); + strptr += strlen(strptr); + } + *(strptr = midioutstr) = 0; + for(i = 0; i < 16; ++i) { + sprintf(strptr,"%3d ",noutdev > i && midioutdev[i]>= 0 ? midioutdev[i] : -1); + strptr += strlen(strptr); + } + sys_vgui("pdtk_midi_dialog %%s %s %s %s %s %d\n", + indevliststring,midiinstr,outdevliststring,midioutstr,!!flongform); +} + +/* new values from dialog window */ +void glob_midi_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) { + int i, nindev, noutdev; + int newmidiindev[16], newmidioutdev[16]; + int alsadevin, alsadevout; + for (i = 0; i < 16; i++) { + newmidiindev[i] = atom_getintarg(i, argc, argv); + newmidioutdev[i] = atom_getintarg(i+16, argc, argv); + } + for (i = 0, nindev = 0; i < 16; i++) { + if (newmidiindev[i] >= 0) {newmidiindev[nindev] = newmidiindev[i]; nindev++;} + } + for (i = 0, noutdev = 0; i < 16; i++) { + if (newmidioutdev[i] >= 0) {newmidioutdev[noutdev] = newmidioutdev[i]; noutdev++;} + } + alsadevin = atom_getintarg(32, argc, argv); + alsadevout = atom_getintarg(33, argc, argv); +#ifdef USEAPI_ALSA + if (sys_midiapi == API_ALSA) { + sys_alsa_close_midi(); + sys_open_midi(alsadevin, newmidiindev, alsadevout, newmidioutdev, 1); + } else +#endif + { + sys_close_midi(); + sys_open_midi(nindev, newmidiindev, noutdev, newmidioutdev, 1); + } +} + +/* tb { */ + +void glob_midi_getindevs(t_pd *dummy, t_symbol *s, int ac, t_atom *av) { + /* these are all the devices on your system: */ + char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; + int nindevs = 0, noutdevs = 0; + t_atom argv[MAXNDEV]; + int f = ac ? (int)atom_getfloatarg(0,ac,av) : -1; + t_symbol *selector = gensym("midiindev"); + t_symbol *pd = gensym("pd"); + midi_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, MAXNDEV, DEVDESCSIZE); + if (f < 0) { + for (int i=0; is_thing, selector, nindevs, argv); + } else if (f < nindevs) { + SETSYMBOL(argv, gensym(indevlist + f * DEVDESCSIZE)); + typedmess(pd->s_thing, selector, 1, argv); + } +} + +void glob_midi_getoutdevs(t_pd *dummy, t_symbol *s, int ac, t_atom *av) { + /* these are all the devices on your system: */ + char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; + int nindevs = 0, noutdevs = 0; + t_atom argv[MAXNDEV]; + int f = ac ? (int)atom_getfloatarg(0,ac,av) : -1; + t_symbol *selector = gensym("midioutdev"); + t_symbol *pd = gensym("pd"); + midi_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, MAXNDEV, DEVDESCSIZE); + if (f < 0) { + for (int i=0; is_thing, selector, noutdevs, argv); + } else if (f < noutdevs) { + SETSYMBOL(argv, gensym(outdevlist + f*DEVDESCSIZE)); + typedmess(pd->s_thing, selector, 1, argv); + } +} + +void glob_midi_getcurrentindevs(t_pd *dummy) { + /* these are the devices you're using: */ + int nindev, midiindev[MAXMIDIINDEV]; + int noutdev, midioutdev[MAXMIDIOUTDEV]; + t_atom argv[MAXNDEV]; + sys_get_midi_params(&nindev, midiindev, &noutdev, midioutdev); + for (int i=0; is_thing, gensym("midicurrentindev"), nindev, argv); +} + +void glob_midi_getcurrentoutdevs(t_pd *dummy) { + /* these are the devices you're using: */ + int nindev, midiindev[MAXMIDIINDEV]; + int noutdev, midioutdev[MAXMIDIOUTDEV]; + t_atom argv[MAXNDEV]; + sys_get_midi_params(&nindev, midiindev, &noutdev, midioutdev); + for (int i=0; is_thing, gensym("midicurrentoutdev"), noutdev, argv); +} -- cgit v1.2.1