/****************************************************** * * audiosettings - get/set audio preferences from within Pd-patches * Copyright (C) 2010-2012 IOhannes m zmölnig * * forum::für::umläute * * institute of electronic music and acoustics (iem) * university of music and dramatic arts, graz (kug) * * ****************************************************** * * license: GNU General Public License v.3 or later * ******************************************************/ #include "mediasettings.h" #define MAXAUDIOINDEV 4 #define MAXAUDIOOUTDEV 4 static void as_get_audio_params( int *pnaudioindev, int *paudioindev, int *pchindev, int *pnaudiooutdev, int *paudiooutdev, int *pchoutdev, int *prate, int *padvance, int *pcallback, int *pblocksize) { #if (defined PD_MINOR_VERSION) && (PD_MINOR_VERSION >= 43) sys_get_audio_params(pnaudioindev , paudioindev , pchindev, pnaudiooutdev, paudiooutdev, pchoutdev, prate, padvance, pcallback, pblocksize); #else if(pblocksize) *pblocksize=-1; sys_get_audio_params(pnaudioindev , paudioindev , pchindev, pnaudiooutdev, paudiooutdev, pchoutdev, prate, padvance, pcallback); #endif } static t_class *audiosettings_class; t_symbol*s_pdsym=NULL; typedef struct _as_drivers { t_symbol*name; int id; struct _as_drivers *next; } t_as_drivers; typedef struct _as_params { int naudioindev, audioindev[MAXAUDIOINDEV], chindev[MAXAUDIOINDEV]; int naudiooutdev, audiooutdev[MAXAUDIOOUTDEV], choutdev[MAXAUDIOOUTDEV]; int rate, advance, callback; int blocksize; } t_as_params; t_as_drivers*as_finddriver(t_as_drivers*drivers, const t_symbol*name) { while(drivers) { if(name==drivers->name)return drivers; drivers=drivers->next; } return NULL; } t_as_drivers*as_finddriverid(t_as_drivers*drivers, const int id) { while(drivers) { if(id==drivers->id)return drivers; drivers=drivers->next; } return NULL; } t_as_drivers*as_adddriver(t_as_drivers*drivers, t_symbol*name, int id, int overwrite) { t_as_drivers*driver=as_finddriver(drivers, name); if(driver) { if(overwrite) { driver->name=name; driver->id =id; } return drivers; } driver=(t_as_drivers*)getbytes(sizeof(t_as_drivers)); driver->name=name; driver->id=id; driver->next=drivers; return driver; } t_as_drivers*as_driverparse(t_as_drivers*drivers, const char*buf) { int start=-1; int stop =-1; unsigned int index=0; int depth=0; const char*s; char substring[MAXPDSTRING]; for(index=0, s=buf; 0!=*s; s++, index++) { if('{'==*s) { start=index; depth++; } if('}'==*s) { depth--; stop=index; if(start>=0 && start=MAXPDSTRING)length=MAXPDSTRING-1; snprintf(substring, length, "%s", buf+start+1); if(common_parsedriver(substring, length, drivername, MAXPDSTRING, &driverid)) { drivers=as_adddriver(drivers, gensym(drivername), driverid, 0); } else { if((start+1)!=(stop)) post("unparseable: '%s' (%d-%d)", substring, start, stop); } } start=-1; stop=-1; } } return drivers; } static t_as_drivers*DRIVERS=NULL; static t_symbol*as_getdrivername(const int id) { t_as_drivers*driver=as_finddriverid(DRIVERS, id); if(driver) { return driver->name; } else { return gensym(""); } } static int as_getdriverid(const t_symbol*id) { t_as_drivers*driver=as_finddriver(DRIVERS, id); if(driver) { return driver->id; } return -1; /* unknown */ } static void as_params_print(t_as_params*parms) { int i=0; post("\n=================================<"); post("indevs: %d", parms->naudioindev); for(i=0; iaudioindev[i], parms->chindev[i]); } post("outdevs: %d", parms->naudiooutdev); for(i=0; iaudiooutdev[i], parms->choutdev[i]); } post("rate=%d", parms->rate); post("advance=%d", parms->advance); post("callback=%d", parms->callback); post(">=================================\n"); } static void as_params_get(t_as_params*parms) { int i=0; memset(parms, 0, sizeof(t_as_params)); parms->callback=-1; as_get_audio_params( &parms->naudioindev, parms->audioindev, parms->chindev, &parms->naudiooutdev, parms->audiooutdev, parms->choutdev, &parms->rate, &parms->advance, &parms->callback, &parms->blocksize); for(i=parms->naudioindev; iaudioindev[i]=0; parms->chindev[i]=0; } for(i=parms->naudiooutdev; iaudiooutdev[i]=0; parms->choutdev[i]=0; } // as_params_print(parms); } typedef struct _audiosettings { t_object x_obj; t_outlet*x_info; t_as_params x_params; } t_audiosettings; static void audiosettings_listparams(t_audiosettings *x); static void audiosettings_listdevices(t_audiosettings *x) { int i; char indevlist[MAXNDEV][DEVDESCSIZE], outdevlist[MAXNDEV][DEVDESCSIZE]; int indevs = 0, outdevs = 0, canmulti = 0, cancallback = 0; t_atom atoms[3]; sys_get_audio_devs((char*)indevlist, &indevs, (char*)outdevlist, &outdevs, &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE); SETSYMBOL (atoms+0, gensym("driver")); SETSYMBOL (atoms+1, as_getdrivername(sys_audioapi)); outlet_anything(x->x_info, gensym("device"), 2, atoms); SETSYMBOL (atoms+0, gensym("multi")); SETFLOAT (atoms+1, (t_float)canmulti); outlet_anything(x->x_info, gensym("device"), 2, atoms); SETSYMBOL (atoms+0, gensym("callback")); SETFLOAT (atoms+1, (t_float)cancallback); outlet_anything(x->x_info, gensym("device"), 2, atoms); SETSYMBOL(atoms+0, gensym("in")); SETSYMBOL(atoms+1, gensym("devices")); SETFLOAT (atoms+2, (t_float)indevs); outlet_anything(x->x_info, gensym("device"), 3, atoms); for(i=0; ix_info, gensym("device"), 3, atoms); } SETSYMBOL(atoms+0, gensym("out")); SETSYMBOL(atoms+1, gensym("devices")); SETFLOAT (atoms+2, (t_float)outdevs); outlet_anything(x->x_info, gensym("device"), 3, atoms); for(i=0; ix_info, gensym("device"), 3, atoms); } } /* this is the actual settings used * */ static void audiosettings_listparams(t_audiosettings *x) { int i; t_atom atoms[4]; t_as_params params; as_params_get(¶ms); SETSYMBOL (atoms+0, gensym("rate")); SETFLOAT (atoms+1, (t_float)params.rate); outlet_anything(x->x_info, gensym("params"), 2, atoms); SETSYMBOL (atoms+0, gensym("advance")); SETFLOAT (atoms+1, (t_float)params.advance); outlet_anything(x->x_info, gensym("params"), 2, atoms); SETSYMBOL (atoms+0, gensym("callback")); SETFLOAT (atoms+1, (t_float)params.callback); outlet_anything(x->x_info, gensym("params"), 2, atoms); SETSYMBOL(atoms+0, gensym("in")); SETSYMBOL(atoms+1, gensym("devices")); SETFLOAT (atoms+2, (t_float)params.naudioindev); outlet_anything(x->x_info, gensym("params"), 3, atoms); for(i=0; ix_info, gensym("params"), 3, atoms); } SETSYMBOL(atoms+0, gensym("out")); SETSYMBOL(atoms+1, gensym("devices")); SETFLOAT (atoms+2, (t_float)params.naudiooutdev); outlet_anything(x->x_info, gensym("params"), 3, atoms); for(i=0; ix_info, gensym("params"), 3, atoms); } } static void audiosettings_params_init(t_audiosettings*x) { as_params_get(&x->x_params); } static void audiosettings_params_apply(t_audiosettings*x) { /* "pd audio-dialog ..." #00: indev[0] #01: indev[1] #02: indev[2] #03: indev[3] #04: inchan[0] #05: inchan[1] #06: inchan[2] #07: inchan[3] #08: outdev[0] #09: outdev[1] #10: outdev[2] #11: outdev[3] #12: outchan[0] #13: outchan[1] #14: outchan[2] #15: outchan[3] #16: rate #17: advance #18: callback */ t_atom argv [2*MAXAUDIOINDEV+2*MAXAUDIOOUTDEV+3]; int argc=2*MAXAUDIOINDEV+2*MAXAUDIOOUTDEV+3; int i=0; // as_params_print(&x->x_params); for(i=0; ix_params.audioindev[i])); SETFLOAT(argv+i+1*MAXAUDIOINDEV, (t_float)(x->x_params.chindev [i])); } for(i=0; ix_params.audiooutdev[i])); SETFLOAT(argv+i+2*MAXAUDIOINDEV+1*MAXAUDIOOUTDEV,(t_float)(x->x_params.choutdev [i])); } SETFLOAT(argv+2*MAXAUDIOINDEV+2*MAXAUDIOOUTDEV+0,(t_float)(x->x_params.rate)); SETFLOAT(argv+2*MAXAUDIOINDEV+2*MAXAUDIOOUTDEV+1,(t_float)(x->x_params.advance)); SETFLOAT(argv+2*MAXAUDIOINDEV+2*MAXAUDIOOUTDEV+2,(t_float)(x->x_params.callback)); if (s_pdsym->s_thing) typedmess(s_pdsym->s_thing, gensym("audio-dialog"), argc, argv); } /* find the beginning of the next parameter in the list */ typedef enum { PARAM_RATE, PARAM_ADVANCE, PARAM_CALLBACK, PARAM_INPUT, PARAM_OUTPUT, PARAM_INVALID } t_paramtype; static t_paramtype audiosettings_setparams_id(t_symbol*s) { if(gensym("@rate")==s) { return PARAM_RATE; } else if(gensym("@samplerate")==s) { return PARAM_RATE; } else if(gensym("@advance")==s) { return PARAM_ADVANCE; } else if(gensym("@buffersize")==s) { return PARAM_ADVANCE; } else if(gensym("@callback")==s) { return PARAM_CALLBACK; } else if(gensym("@input")==s) { return PARAM_INPUT; } else if(gensym("@output")==s) { return PARAM_OUTPUT; } return PARAM_INVALID; } /* find the beginning of the next parameter in the list */ static int audiosettings_setparams_next(int argc, t_atom*argv) { int i=0; for(i=0; is_name[0]) return i; } } return i; } /* ... */ static int audiosettings_setparams_rate(t_audiosettings*x, int argc, t_atom*argv) { if(argc<=0)return 1; t_int rate=atom_getint(argv); if(rate>0) x->x_params.rate=rate; return 1; } /* ... */ static int audiosettings_setparams_advance(t_audiosettings*x, int argc, t_atom*argv) { if(argc<=0)return 1; t_int advance=atom_getint(argv); if(advance>0) x->x_params.advance=advance; return 1; } /* ... */ static int audiosettings_setparams_callback(t_audiosettings*x, int argc, t_atom*argv) { if(argc<=0)return 1; t_int callback=atom_getint(argv); x->x_params.callback=callback; return 1; } /* [ ]* ... */ static int audiosettings_setparams_input(t_audiosettings*x, int argc, t_atom*argv) { int length=audiosettings_setparams_next(argc, argv); int i; int numpairs=length/2; if(length%2)return length; if(numpairs>MAXAUDIOINDEV) numpairs=MAXAUDIOINDEV; for(i=0; ix_params.audioindev[i]=dev; x->x_params.chindev[i]=ch; } return length; } static int audiosettings_setparams_output(t_audiosettings*x, int argc, t_atom*argv) { int length=audiosettings_setparams_next(argc, argv); int i; int numpairs=length/2; if(length%2)return length; if(numpairs>MAXAUDIOOUTDEV) numpairs=MAXAUDIOOUTDEV; for(i=0; ix_params.audiooutdev[i]=dev; x->x_params.choutdev[i]=ch; } return length; } static void audiosettings_setparams(t_audiosettings *x, t_symbol*s, int argc, t_atom*argv) { /* PLAN: several messages that accumulate to a certain settings, and then "apply" them */ int apply=1; int advance=0; t_paramtype param=PARAM_INVALID; audiosettings_params_init (x); /* re-initialize to what we got */ advance=audiosettings_setparams_next(argc, argv); while((argc-=advance)>0) { argv+=advance; s=atom_getsymbol(argv); param=audiosettings_setparams_id(s); argv++; argc--; switch(param) { case PARAM_RATE: advance=audiosettings_setparams_rate(x, argc, argv); break; case PARAM_ADVANCE: advance=audiosettings_setparams_advance(x, argc, argv); break; case PARAM_CALLBACK: advance=audiosettings_setparams_callback(x, argc, argv); break; case PARAM_INPUT: advance=audiosettings_setparams_input(x, argc, argv); break; case PARAM_OUTPUT: advance=audiosettings_setparams_output(x, argc, argv); break; default: pd_error(x, "unknown parameter"); postatom(1, argv);endpost(); break; } argc-=advance; argv+=advance; advance=audiosettings_setparams_next(argc, argv); } if(apply) { audiosettings_params_apply(x); } } static void audiosettings_testdevices(t_audiosettings *x); /* */ static void audiosettings_listdrivers(t_audiosettings *x) { t_as_drivers*driver=NULL; t_atom ap[2]; for(driver=DRIVERS; driver; driver=driver->next) { SETSYMBOL(ap+0, driver->name); SETFLOAT (ap+1, (t_float)(driver->id)); outlet_anything(x->x_info, gensym("driver"), 2, ap); } } static void audiosettings_setdriver(t_audiosettings *x, t_symbol*s, int argc, t_atom*argv) { int id=-1; s=gensym(""); /* just re-use the argument, which is not needed anyhow */ switch(argc) { case 0: audiosettings_listdrivers(x); return; case 1: if(A_FLOAT==argv->a_type) { s=as_getdrivername(atom_getint(argv)); break; } else if (A_SYMBOL==argv->a_type) { s=atom_getsymbol(argv); break; } } id=as_getdriverid(s); if(id<0) { pd_error(x, "invalid driver '%s'", s->s_name); return; } verbose(1, "setting driver '%s' (=%d)", s->s_name, id); #ifdef HAVE_SYS_CLOSE_AUDIO sys_close_audio(); sys_set_audio_api(id); sys_reopen_audio(); #else if (s_pdsym->s_thing) { t_atom ap[1]; SETFLOAT(ap, id); typedmess(s_pdsym->s_thing, gensym("audio-setapi"), 1, ap); } #endif } static void audiosettings_bang(t_audiosettings *x) { audiosettings_listdrivers(x); audiosettings_listdevices(x); audiosettings_listparams(x); } static void audiosettings_free(t_audiosettings *x){ } static void *audiosettings_new(void) { t_audiosettings *x = (t_audiosettings *)pd_new(audiosettings_class); x->x_info=outlet_new(&x->x_obj, 0); char buf[MAXPDSTRING]; sys_get_audio_apis(buf); DRIVERS=as_driverparse(DRIVERS, buf); audiosettings_params_init (x); return (x); } void audiosettings_setup(void) { s_pdsym=gensym("pd"); post("audiosettings: audio settings manager"); post(" Copyright (C) 2010 IOhannes m zmoelnig"); post(" for the IntegraLive project"); post(" institute of electronic music and acoustics (iem)"); post(" published under the GNU General Public License version 3 or later"); #ifdef AUDIOSETTINGS_VERSION startpost(" version:"AUDIOSETTINGS_VERSION); #endif post("\tcompiled: "__DATE__""); #if (defined PD_MINOR_VERSION) && (PD_MINOR_VERSION < 43) if(1) { int major, minor, bugfix; sys_getversion(&major, &minor, &bugfix); if(0==major && minor>=43) { error("[audiosettings] have been compiled against an old version of Pd"); error(" that is incompatible with the one you are using!"); error(" recompile [audiosettings]"); } return; } #endif audiosettings_class = class_new(gensym("audiosettings"), (t_newmethod)audiosettings_new, (t_method)audiosettings_free, sizeof(t_audiosettings), 0, 0); class_addbang(audiosettings_class, (t_method)audiosettings_bang); class_addmethod(audiosettings_class, (t_method)audiosettings_listdrivers, gensym("listdrivers"), A_NULL); class_addmethod(audiosettings_class, (t_method)audiosettings_listdevices, gensym("listdevices"), A_NULL); class_addmethod(audiosettings_class, (t_method)audiosettings_listparams, gensym("listparams"), A_NULL); class_addmethod(audiosettings_class, (t_method)audiosettings_setdriver, gensym("driver"), A_GIMME, A_NULL); class_addmethod(audiosettings_class, (t_method)audiosettings_setparams, gensym("params"), A_GIMME, A_NULL); class_addmethod(audiosettings_class, (t_method)audiosettings_testdevices, gensym("testdevices"), A_NULL); } static void audiosettings_testdevices(t_audiosettings *x) { int i; char indevlist[MAXNDEV][DEVDESCSIZE], outdevlist[MAXNDEV][DEVDESCSIZE]; int indevs = 0, outdevs = 0, canmulti = 0, cancallback = 0; if(0) { pd_error(x, "this should never happen"); } sys_get_audio_devs((char*)indevlist, &indevs, (char*)outdevlist, &outdevs, &canmulti, &cancallback, MAXNDEV, DEVDESCSIZE); post("%d indevs", indevs); for(i=0; i