mas01mj@567: #include "m_pd.h" mas01mj@567: #include mas01mj@567: #include mas01mj@567: mas01mj@567: #include mas01mj@567: #include mas01mj@567: #include mas01mj@567: #include mas01mj@567: mas01mj@567: mas01mj@567: #include mas01mj@567: mas01mj@567: #define ADB_MAXSTR (512U) mas01mj@567: #define MAXSTR ADB_MAXSTR mas01mj@567: mas01mj@567: // Query types mas01mj@567: #define O2_POINT_QUERY (0x4U) mas01mj@567: #define O2_SEQUENCE_QUERY (0x8U) mas01mj@567: #define O2_TRACK_QUERY (0x10U) mas01mj@567: #define O2_N_SEQUENCE_QUERY (0x20U) mas01mj@567: #define O2_ONE_TO_ONE_N_SEQUENCE_QUERY (0x40U) mas01mj@567: mas01mj@567: static t_class *adbpd_class; /* so this bit is different */ mas01mj@567: adb_ptr dbpointer={0};/*This is the audioDB pointer*/ mas01mj@567: mas01mj@567: typedef struct _adbpd { mas01mj@567: t_object x_obj; mas01mj@567: t_outlet *x_key; mas01mj@567: t_outlet *x_dist; mas01mj@567: t_outlet *x_qpos; mas01mj@567: t_outlet *x_spos; mas01mj@567: mas01mj@567: t_symbol *x_dbname; //database name mas01mj@567: mas01mj@567: t_symbol *x_dbquerytype; //sequence, track, etc. mas01mj@567: t_symbol *x_dbfeature; mas01mj@567: // t_symbol *x_dbpower; mas01mj@567: t_symbol *x_dbkey; mas01mj@567: t_float x_dbqpoint; mas01mj@567: t_float x_dbnumpoints; mas01mj@567: t_float x_dbradius; mas01mj@567: t_float x_dbresultlength; mas01mj@567: t_float x_dbsequencelength; mas01mj@567: adb_ptr db; mas01mj@567: } t_adbpd; mas01mj@567: mas01mj@567: mas01mj@567: static void adbpd_create(t_adbpd *x); mas01mj@567: static void adbpd_open(t_adbpd *x); mas01mj@567: static void adbpd_setname(t_adbpd *x, t_symbol *s, int argc, t_atom *argv); mas01mj@567: static void adbpd_status(t_adbpd *x); mas01mj@567: static void adbpd_l2norm(t_adbpd *x); mas01mj@567: static void adbpd_power(t_adbpd *x); mas01mj@567: static void adbpd_setfeatures(t_adbpd *x,t_symbol *s,int argc, t_atom *argv); mas01mj@567: static void adbpd_setquerytype(t_adbpd *x,t_symbol *s,int argc, t_atom *argv); mas01mj@567: static void adbpd_setqpoint(t_adbpd *x,t_floatarg f); mas01mj@567: static void adbpd_setresultlength(t_adbpd *x,t_floatarg f); mas01mj@567: static void adbpd_setradius(t_adbpd *x,t_floatarg f); mas01mj@567: static void adbpd_setnumpoints(t_adbpd *x,t_floatarg f); mas01mj@567: static void adbpd_setsequencelength(t_adbpd *x,t_floatarg f); mas01mj@567: static void adbpd_doquery(t_adbpd *x); mas01mj@567: static void adbpd_parameters(t_adbpd *x); mas01mj@567: static void adbpd_getname(t_adbpd *x); mas01mj@567: mas01mj@567: mas01mj@567: mas01mj@567: /* input arguments are only used for startup vals */ mas01mj@567: static void *adbpd_new(t_symbol *s) { mas01mj@567: mas01mj@567: /* some sort of standard declaration line */ mas01mj@567: t_adbpd *x = (t_adbpd *)pd_new(adbpd_class); mas01mj@567: mas01mj@567: /* setup some inlets */ mas01mj@567: floatinlet_new(&x->x_obj,&x->x_dbqpoint); /* second inlet */ mas01mj@567: floatinlet_new(&x->x_obj,&x->x_dbnumpoints); /* third inlet */ mas01mj@567: floatinlet_new(&x->x_obj,&x->x_dbradius); /* fourth inlet */ mas01mj@567: floatinlet_new(&x->x_obj,&x->x_dbresultlength); /* fifth inlet */ mas01mj@567: floatinlet_new(&x->x_obj,&x->x_dbsequencelength); /* sixth inlet */ mas01mj@567: mas01mj@567: mas01mj@567: /* outlets */ mas01mj@567: outlet_new(&x->x_obj, &s_float); mas01mj@567: mas01mj@567: x->x_key = outlet_new(&x->x_obj, &s_symbol); mas01mj@567: x->x_dist = outlet_new(&x->x_obj, &s_float); mas01mj@567: x->x_qpos = outlet_new(&x->x_obj, &s_float); mas01mj@567: x->x_spos = outlet_new(&x->x_obj, &s_float); mas01mj@567: mas01mj@567: /* adb defaults. These are the same but we need to init them */ mas01mj@567: x->x_dbname=gensym("no db has been specified yet"); mas01mj@567: x->x_dbquerytype=gensym("point"); mas01mj@567: x->x_dbfeature=gensym("No feature file yet specified"); mas01mj@567: x->x_dbqpoint=1; mas01mj@567: x->x_dbnumpoints=10; mas01mj@567: x->x_dbradius=1; mas01mj@567: x->x_dbresultlength=10; mas01mj@567: x->x_dbsequencelength=12; mas01mj@567: return(x); mas01mj@567: mas01mj@567: } mas01mj@567: mas01mj@567: mas01mj@567: mas01mj@567: /* bang just gets the status of the db */ mas01mj@567: static void adbpd_bang(t_adbpd *x) { mas01mj@567: adbpd_doquery(x); mas01mj@567: mas01mj@567: } mas01mj@567: mas01mj@567: /* create a database */ mas01mj@567: static void adbpd_create(t_adbpd *x){ mas01mj@567: mas01mj@567: post("creating db '%s'",x->x_dbname->s_name); mas01mj@567: dbpointer=audiodb_create(x->x_dbname->s_name,0,0,0); mas01mj@567: mas01mj@567: if (dbpointer){ mas01mj@567: post("Created"); mas01mj@567: x->db=dbpointer; mas01mj@567: } else { mas01mj@567: error("Could not create db. May already exist"); mas01mj@567: x->db=NULL; mas01mj@567: } mas01mj@567: mas01mj@567: } mas01mj@567: mas01mj@567: /* open a database. Need to work out the path stuff here. At present databases need to be in the root directory. Not good. */ mas01mj@567: static void adbpd_open(t_adbpd *x){ mas01mj@567: mas01mj@567: post("Opening db '%s'",x->x_dbname->s_name); mas01mj@567: dbpointer=audiodb_open(x->x_dbname->s_name,O_RDWR); mas01mj@567: mas01mj@567: if (dbpointer){ mas01mj@567: post("Opened db"); mas01mj@567: x->db=dbpointer; mas01mj@567: } else { mas01mj@567: error("failed ! Check '%s' exists, or create a new db",x->x_dbname->s_name); mas01mj@567: x->db=NULL; mas01mj@567: } mas01mj@567: mas01mj@567: } mas01mj@567: mas01mj@567: /* This is accessed via the 'set' message. It sets the name and opens the database. */ mas01mj@567: static void adbpd_setname(t_adbpd *x, t_symbol *s, int argc, t_atom *argv){ mas01mj@567: mas01mj@567: /* if we have a properly formed instruction */ mas01mj@567: if (argc == 1 && argv->a_type == A_SYMBOL){ mas01mj@567: x->x_dbname=gensym(argv->a_w.w_symbol->s_name); /* make the internal database reference name the same as the name of the database we want. mas01mj@567: This is stupid. There must be a better way of doing this. */ mas01mj@567: post("Name has been set to '%s', and can now be opened.",x->x_dbname->s_name); mas01mj@567: } mas01mj@567: mas01mj@567: /* now we can open the database as we did with the audiodb_open call above*/ mas01mj@567: mas01mj@567: post("Opening db '%s'",x->x_dbname->s_name); mas01mj@567: dbpointer=audiodb_open(x->x_dbname->s_name,O_RDWR); /* opened in read and write mode for when I get round to doing the insert code */ mas01mj@567: mas01mj@567: if (dbpointer){ /* yes we have a database*/ mas01mj@567: post("Opened db"); mas01mj@567: x->db=dbpointer; mas01mj@567: } else { mas01mj@567: error("failed ! Check '%s' exists, or create a new db",x->x_dbname->s_name); mas01mj@567: x->db=NULL; mas01mj@567: } mas01mj@567: } mas01mj@567: mas01mj@567: mas01mj@567: /* this is a status call to the audioDB API */ mas01mj@567: static void adbpd_status(t_adbpd *x){ mas01mj@567: mas01mj@567: adb_status_t mystatus; mas01mj@567: mas01mj@567: post("Getting Status"); mas01mj@567: mas01mj@567: if (x->db && !(audiodb_status(x->db,&mystatus))){ mas01mj@567: post("numFiles %d",mystatus.numFiles); mas01mj@567: post("dim %d",mystatus.dim); mas01mj@567: post("length %d",mystatus.length); mas01mj@567: post("dudCount %d",mystatus.dudCount); mas01mj@567: post("numCount %d",mystatus.nullCount); mas01mj@567: post("flags %d",mystatus.flags); mas01mj@567: post("Bytes Available %d",mystatus.data_region_size); mas01mj@567: } else { mas01mj@567: error("Can't get Status. Have you selected a db?"); mas01mj@567: } mas01mj@567: } mas01mj@567: mas01mj@567: mas01mj@567: static void adbpd_doquery(t_adbpd *x){ mas01mj@567: mas01mj@567: adb_datum_t datum = {0}; mas01mj@567: adb_query_id_t qid = {0}; mas01mj@567: adb_query_parameters_t params = {0}; mas01mj@567: adb_query_refine_t refine = {0}; mas01mj@567: adb_query_spec_t spec = {0}; mas01mj@567: mas01mj@567: qid.datum = &datum; mas01mj@567: qid.sequence_length = x->x_dbsequencelength; mas01mj@567: qid.sequence_start = x->x_dbqpoint; mas01mj@567: qid.flags = 0; mas01mj@567: mas01mj@567: refine.hopsize = 1; mas01mj@567: refine.flags |= ADB_REFINE_RADIUS; mas01mj@567: refine.radius = x->x_dbradius; mas01mj@567: mas01mj@567: spec.qid = qid; mas01mj@567: spec.params = params; mas01mj@567: spec.refine = refine; mas01mj@567: mas01mj@567: int fd; mas01mj@567: struct stat st; mas01mj@567: mas01mj@567: /* FIXME: around here there are all sorts of hideous leaks. */ mas01mj@567: fd = open(x->x_dbfeature->s_name, O_RDONLY); mas01mj@567: if(fd < 0) { mas01mj@567: error("failed to open feature file", x->x_dbfeature->s_name); mas01mj@567: return; mas01mj@567: } mas01mj@567: fstat(fd, &st); mas01mj@567: read(fd, &datum.dim, sizeof(uint32_t)); mas01mj@567: datum.nvectors = (st.st_size - sizeof(uint32_t)) / (datum.dim * sizeof(double)); mas01mj@567: datum.data = (double *) malloc(st.st_size - sizeof(uint32_t)); mas01mj@567: read(fd, datum.data, st.st_size - sizeof(uint32_t)); mas01mj@567: close(fd); mas01mj@567: mas01mj@567: int queryType; mas01mj@567: mas01mj@567: // query type mas01mj@567: if(strncmp(x->x_dbquerytype->s_name, "track", MAXSTR)==0) mas01mj@567: queryType=O2_TRACK_QUERY; mas01mj@567: else if(strncmp(x->x_dbquerytype->s_name, "point", MAXSTR)==0) mas01mj@567: queryType=O2_POINT_QUERY; mas01mj@567: else if(strncmp(x->x_dbquerytype->s_name, "sequence", MAXSTR)==0) mas01mj@567: queryType=O2_SEQUENCE_QUERY; mas01mj@567: else if(strncmp(x->x_dbquerytype->s_name, "nsequence", MAXSTR)==0) mas01mj@567: queryType=O2_N_SEQUENCE_QUERY; mas01mj@567: else if(strncmp(x->x_dbquerytype->s_name, "onetoonensequence", MAXSTR)==0) mas01mj@567: queryType=O2_ONE_TO_ONE_N_SEQUENCE_QUERY; mas01mj@567: else mas01mj@567: error("unsupported query type"); mas01mj@567: mas01mj@567: switch(queryType) mas01mj@567: { mas01mj@567: case O2_POINT_QUERY: mas01mj@567: spec.qid.sequence_length = 1; mas01mj@567: spec.params.accumulation = ADB_ACCUMULATION_DB; mas01mj@567: spec.params.distance = ADB_DISTANCE_DOT_PRODUCT; mas01mj@567: spec.params.npoints = x->x_dbnumpoints; mas01mj@567: spec.params.ntracks = x->x_dbresultlength; mas01mj@567: break; mas01mj@567: case O2_TRACK_QUERY: mas01mj@567: spec.qid.sequence_length = 1; mas01mj@567: spec.params.accumulation = ADB_ACCUMULATION_PER_TRACK; mas01mj@567: spec.params.distance = ADB_DISTANCE_DOT_PRODUCT; mas01mj@567: spec.params.npoints = x->x_dbnumpoints; mas01mj@567: spec.params.ntracks = x->x_dbresultlength; mas01mj@567: case O2_SEQUENCE_QUERY: mas01mj@567: case O2_N_SEQUENCE_QUERY: mas01mj@567: spec.params.accumulation = ADB_ACCUMULATION_PER_TRACK; mas01mj@567: // TODO : Add unit norming param. Defaults to false. mas01mj@567: // spec.params.distance = no_unit_norming ? ADB_DISTANCE_EUCLIDEAN : ADB_DISTANCE_EUCLIDEAN_NORMED; mas01mj@567: spec.params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED; mas01mj@567: spec.params.npoints = x->x_dbnumpoints; mas01mj@567: spec.params.ntracks = x->x_dbresultlength; mas01mj@567: break; mas01mj@567: case O2_ONE_TO_ONE_N_SEQUENCE_QUERY: mas01mj@567: spec.params.accumulation = ADB_ACCUMULATION_ONE_TO_ONE; mas01mj@567: // TODO : Add unit norming param. Defaults to false. mas01mj@567: // spec.params.distance = no_unit_norming ? ADB_DISTANCE_EUCLIDEAN : ADB_DISTANCE_EUCLIDEAN_NORMED; mas01mj@567: spec.params.distance =ADB_DISTANCE_EUCLIDEAN_NORMED; mas01mj@567: spec.params.npoints = 0; mas01mj@567: spec.params.ntracks = 0; mas01mj@567: break; mas01mj@567: default: mas01mj@567: post("Unsupported query type"); mas01mj@567: } mas01mj@567: mas01mj@567: adb_query_results_t *rs = audiodb_query_spec(x->db, &spec); mas01mj@567: mas01mj@567: mas01mj@567: mas01mj@567: if(datum.data) { mas01mj@567: free(datum.data); mas01mj@567: datum.data = NULL; mas01mj@567: } mas01mj@567: if(datum.power) { mas01mj@567: free(datum.data); mas01mj@567: datum.data = NULL; mas01mj@567: } mas01mj@567: if(datum.times) { mas01mj@567: free(datum.data); mas01mj@567: datum.data = NULL; mas01mj@567: } mas01mj@567: mas01mj@567: if(rs == NULL) mas01mj@567: { mas01mj@567: error("Query failed"); mas01mj@567: return; mas01mj@567: } mas01mj@567: mas01mj@567: int size = rs->nresults; mas01mj@567: post("result size:[%d]",(int)size); mas01mj@567: int i = 0; mas01mj@567: for(i=0; iresults[i]; mas01mj@567: mas01mj@567: outlet_float(x->x_dist,r.dist); mas01mj@567: outlet_float(x->x_qpos,r.qpos); mas01mj@567: outlet_float(x->x_spos,r.ipos); mas01mj@567: mas01mj@567: post("in obj key:%s",r.key); mas01mj@567: post("in obj Dist:%f", r.dist); mas01mj@567: post("in obj qpos:%d", r.qpos); mas01mj@567: post("in obj ipos:%d", r.ipos); mas01mj@567: } mas01mj@567: mas01mj@567: } mas01mj@567: mas01mj@567: /* Do I need to set a the power file for this flag to work ? Hmmm. Dunno. Also, should be on/off*/ mas01mj@567: mas01mj@567: static void adbpd_power(t_adbpd *x){ mas01mj@567: post("power"); mas01mj@567: mas01mj@567: if (x->db && !(audiodb_power(x->db))){ mas01mj@567: post("power successfully set on db"); mas01mj@567: } else { mas01mj@567: error("power flag not working"); mas01mj@567: } mas01mj@567: } mas01mj@567: mas01mj@567: /* works fine but would be better if it took an argument to switch it on and off */ mas01mj@567: mas01mj@567: static void adbpd_l2norm(t_adbpd *x){ mas01mj@567: post("l2norm"); mas01mj@567: mas01mj@567: if (x->db && !(audiodb_l2norm(x->db))){ mas01mj@567: post("l2norm successfully set on db"); mas01mj@567: } else { mas01mj@567: error("l2norm flag not working"); mas01mj@567: } mas01mj@567: } mas01mj@567: mas01mj@567: /* reports the name of the current db */ mas01mj@567: static void adbpd_getname(t_adbpd *x){ mas01mj@567: post("db name is '%s'", x->x_dbname->s_name); mas01mj@567: } mas01mj@567: mas01mj@567: /* this sets the qpoint for the current query*/ mas01mj@567: mas01mj@567: static void adbpd_setqpoint(t_adbpd *x,t_floatarg f){ mas01mj@567: post("qpoint %.3f",f); mas01mj@567: mas01mj@567: x->x_dbqpoint=f; mas01mj@567: } mas01mj@567: mas01mj@567: /* This sets the number of points for current query */ mas01mj@567: mas01mj@567: static void adbpd_setnumpoints(t_adbpd *x,t_floatarg f){ mas01mj@567: post("numpoints %.3f",f); mas01mj@567: x->x_dbnumpoints=(int)f; mas01mj@567: } mas01mj@567: mas01mj@567: /* This sets the radius */ mas01mj@567: mas01mj@567: static void adbpd_setradius(t_adbpd *x,t_floatarg f){ mas01mj@567: post("radius %.3f",f); mas01mj@567: x->x_dbradius=f; mas01mj@567: } mas01mj@567: mas01mj@567: /* This sets the result length */ mas01mj@567: mas01mj@567: static void adbpd_setresultlength(t_adbpd *x,t_floatarg f){ mas01mj@567: post("resultlength %.3f",f); mas01mj@567: x->x_dbresultlength=(int)f; mas01mj@567: } mas01mj@567: mas01mj@567: /* This sets the sequence length */ mas01mj@567: mas01mj@567: static void adbpd_setsequencelength(t_adbpd *x,t_floatarg f){ mas01mj@567: post("sequencelength %.3f",f); mas01mj@567: x->x_dbsequencelength=(int)f; mas01mj@567: } mas01mj@567: mas01mj@567: /* This sets the feature file to use for the query */ mas01mj@567: mas01mj@567: static void adbpd_setfeatures(t_adbpd *x,t_symbol *s,int argc, t_atom *argv){ mas01mj@567: mas01mj@567: if (argc == 1 && argv->a_type == A_SYMBOL){ mas01mj@567: x->x_dbfeature=gensym(argv->a_w.w_symbol->s_name); mas01mj@567: post("Features file has been set to '%s'",x->x_dbfeature->s_name); mas01mj@567: } mas01mj@567: } mas01mj@567: mas01mj@567: /* This sets the query type */ mas01mj@567: mas01mj@567: static void adbpd_setquerytype(t_adbpd *x,t_symbol *s,int argc, t_atom *argv){ mas01mj@567: mas01mj@567: if (argc == 1 && argv->a_type == A_SYMBOL){ mas01mj@567: x->x_dbquerytype=gensym(argv->a_w.w_symbol->s_name); mas01mj@567: post("Query type has been set to '%s'",x->x_dbquerytype->s_name); mas01mj@567: } mas01mj@567: } mas01mj@567: mas01mj@567: /* This lists all the parameters */ mas01mj@567: mas01mj@567: static void adbpd_parameters(t_adbpd *x){ mas01mj@567: mas01mj@567: post("dbname %s",x->x_dbname->s_name); mas01mj@567: post("querytype %s",x->x_dbquerytype->s_name); mas01mj@567: post("features %s",x->x_dbfeature->s_name); mas01mj@567: post("qpoint %.3f",x->x_dbqpoint); mas01mj@567: post("numpoints %.3f",x->x_dbnumpoints); mas01mj@567: post("radius %.3f",x->x_dbradius); mas01mj@567: post("resultlength %.3f",x->x_dbresultlength); mas01mj@567: post("sequencelength %.3f",x->x_dbsequencelength); mas01mj@567: } mas01mj@567: mas01mj@567: /* THAR SHE BLOWS. This sets up the object, takes the messages with args and maps them to methods. */ mas01mj@567: mas01mj@567: void adbpd_setup(void) { mas01mj@567: /* the arguments in this line refer to INPUT ARGUMENTS and not OUTPUTS */ mas01mj@567: adbpd_class = class_new(gensym("adbpd"),(t_newmethod)adbpd_new,0,sizeof(t_adbpd),0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); mas01mj@567: mas01mj@567: /* all methods that respond to input must be defined here */ mas01mj@567: class_addbang(adbpd_class, adbpd_bang); mas01mj@567: class_addfloat(adbpd_class, adbpd_setqpoint); mas01mj@567: class_addfloat(adbpd_class, adbpd_setnumpoints); mas01mj@567: class_addfloat(adbpd_class, adbpd_setradius); mas01mj@567: class_addfloat(adbpd_class, adbpd_setresultlength); mas01mj@567: class_addfloat(adbpd_class, adbpd_setsequencelength); mas01mj@567: class_addmethod(adbpd_class, (t_method)adbpd_setname, gensym("set"), A_GIMME, 0); mas01mj@567: class_addmethod(adbpd_class, (t_method)adbpd_getname, gensym("get"), A_NULL); mas01mj@567: class_addmethod(adbpd_class, (t_method)adbpd_open, gensym("open"), A_NULL); mas01mj@567: class_addmethod(adbpd_class, (t_method)adbpd_status, gensym("status"), A_NULL); mas01mj@567: class_addmethod(adbpd_class, (t_method)adbpd_l2norm, gensym("l2norm"), A_NULL); mas01mj@567: class_addmethod(adbpd_class, (t_method)adbpd_power, gensym("power"), A_NULL); mas01mj@567: class_addmethod(adbpd_class, (t_method)adbpd_doquery, gensym("doquery"), A_NULL); mas01mj@567: class_addmethod(adbpd_class, (t_method)adbpd_setquerytype, gensym("querytype"), A_GIMME, 0); mas01mj@567: class_addmethod(adbpd_class, (t_method)adbpd_setfeatures, gensym("features"), A_GIMME, 0); mas01mj@567: class_addmethod(adbpd_class, (t_method)adbpd_parameters, gensym("parameters"), A_NULL); mas01mj@567: mas01mj@567: mas01mj@567: } mas01mj@567: