/* chords_memory: an external that using graph learns the played style and reproduces chords sequences see http://en.wikipedia.org/wiki/Graph_%28mathematics%29 for an idea of what graphs are (but we'll use weights also) idea: ------- NODES each node of the graph is a chord each node has a name like "Imin Imaj ... IImin IImaj .... etc" We'll cover each possible grade and form, each grade can be maj min dim aug maj7maj maj7min min7maj min7min dim7dim dim7min dim7maj aug7min aug7maj (tot 13 forms) we have 12 grades: I I# II II# III IV IV# V V# VI VI# VII for a total of 12x13=156 nodes ------- ARCS each node is connected to any other node each node has a weight the weight is augmented each time the human plays this chord sequence so probably I V will have high weight and I VI#7 will be very light this will be a table of 156x156 int (24336 elements) what can i do with the graph? i can do questions like: simple questions like: starting from IIImin tell me a chord so i have a high weight (no novelty) starting from IIImin tell me a chord so I have hight novelty (low weight) (the graph simply must select the arc starting from IIImin with the desired weight) i can build walks giving starting chord: as before but more than 1 passage i can build walks giving target chord: - "build a walk from current chord to target chord with high novelty" - "build the shortest walk from current chord to target chord with novelty less than ..." - "build a 4 chords long walk from here to there with ... average novelty" all these questions are usefull if there is a "form manager" that decides the piece structure,for example it wants the 2nd theme to start in 4 measures and asks for a chord sequence with medium novelty ending on the dominant (V), when the piece is ending it can ask for a coming back to the first tone.. once we have all the arcs building walks is simply a matter of apply search methods like http://en.wikipedia.org/wiki/A%2A_search_algorithm there are plenty of such algos, we must just copy them down. */ #include #include // for random, we may want to use it when building walks #include // for file io #include // for string manipulation #include #include #include "m_pd.h" #define DEBUG 1 // messaggi di debug #define DEBUG_VERBOSE 0 // messaggi di debug // is this system Little Endian? (thanks to Mathieu Bouchard) // TODO: use this code in file writing/reading // instead of forcing little endian! //static inline int is_le() {int x=1; return ((char *)&x)[0];} // ------------------------------- declaration of used types static t_class *chords_memory_class; // how can a chord be? #define TYPES_NUM 11 // keep me updated typedef enum { kMaj=0, kMin=1, kDim=2, kAug=3, kDom7=4, kMaj7=5, kMin7=6, kMinMaj7=7, kDim7=8, kHalfDim7=9, //pland adding 9ths 30.11.12 kDomb9=10 } chord_type_t; // how many tones do we have in our octave? #define TONES_NUM 12 // keep me updated typedef enum { I=0, Id=1, II=2, IId=3, III=4, IV=5, IVd=6, V=7, Vd=8, VI=9, VId=10, VII=11 } chord_tone_t; // how many nodes does this graph have? // for now TYPES_NUM*TONES_NUM // when we introduce modulation // we'll have more #define NODES_NUM TYPES_NUM*TONES_NUM // this defines a chord in a tonality typedef struct _chord { chord_type_t mode; chord_tone_t note; } chord_t; // enumeration of absolute notes // i'll need this when parsing strings like "C major" typedef enum { C=0, Db=1, D=2, Eb=3, E=4, F=5, Gb=6, G=7, Ab=8, A=9, Bb=10, B=11 } abs_note_t; // enumeration of modes // i'll start with minor and major only // but we could add phrigian, doric, misolidian ,e tc... #define MODES_NUM 2 typedef enum { MAJOR=0, MINOR=1 } modes_t; #define MODULATIONS_NUM MODES_NUM*TONES_NUM // data type for the steps of a walk typedef struct _step { // 1 if i must modulate to tonality before computing chord int modulate; // this chord chord_t chord; // new tonality to be applied before chord int tonality_note; int tonality_mode; } step_t; // struct defining this external's memory space typedef struct _chords_memory { t_object x_obj; // myself t_outlet *x_outchordname; /* chord name, e.g. "Cmajor7" */ t_outlet *x_outtonalityname; /* chord name, e.g. "Cmajor7" */ t_outlet *x_outchordssequence; /* sequence of chords,a walk */ // the matrix : arcs of the graph // each tonality mode has his matrix // each matrix is in this form: // from which chord to which chord short int arcs[MODES_NUM][NODES_NUM][NODES_NUM]; // modulations matrix // same as above // but for modulations short int modulations[MODES_NUM][NODES_NUM][MODULATIONS_NUM]; // I use this to normalize weights 0-1 short int maxweight[MODES_NUM]; // to convert from absolute tones (C, D, E, ..) to relative ones (I, II, III) and back abs_note_t fundamental_note; // describes current tonality modes_t fundamental_mode; // describes current tonality // to save and load my status t_symbol *filename; // to normalize weights to 0-1 chord_t last_chord; int last_chord_set; // to use for walks step_t *walk; int steps; int using_walk; int current_step; } t_chords_memory; //static inline int mod(int a, int b) {int c=a%b; c+=b&-(c&&(a<0)^(b<0)); return c;} // bring this number in 0 12 range int clean_note(int src) { while (src>12) src-=12; while (src<0) src+=12; return src; } // or use this one: #define MOD(x,y) (((x%y)+y)%y) // ------------------------------- init functions // initializes the graphs to 0 (everything is new) void chords_memory_init_graph(t_chords_memory *x) { int i, j, m; for (m=0; mmaxweight[m]=1; for (i=0; iarcs[m][i][j]=0; } } } x->last_chord_set=0; } void chords_memory_init(t_chords_memory *x, t_floatarg f) { chords_memory_init_graph(x); } // ------------- function for string manipulation (from string to chords) // tries to find out absolute tones names in this string abs_note_t from_string_to_abs_tone(const char *substr) { if (strstr(substr, "C")) return C; if (strstr(substr, "Db")) return Db; if (strstr(substr, "D")) return D; if (strstr(substr, "Eb")) return Eb; if (strstr(substr, "E")) return E; if (strstr(substr, "F")) return F; if (strstr(substr, "Gb")) return Gb; if (strstr(substr, "G")) return G; if (strstr(substr, "Ab")) return Ab; if (strstr(substr, "A")) return A; if (strstr(substr, "Bb")) return Bb; if (strstr(substr, "B")) return B; return C; } chord_type_t from_string_to_type(const char *substr) { if (strstr(substr, "minor/major 7th")) return kMinMaj7; if (strstr(substr, "major 7th")) return kMaj7; if (strstr(substr, "major")) return kMaj; if (strstr(substr, "minor 7th")) return kMin7; if (strstr(substr, "minor")) return kMin; if (strstr(substr, "half diminished 7th")) return kHalfDim7; if (strstr(substr, "diminished 7th")) return kDim7; if (strstr(substr, "diminished")) return kDim; if (strstr(substr, "augmented")) return kAug; if (strstr(substr, "dominant 7th")) return kDom7; // pland adding chords 30.11.05 if (strstr(substr, "dominant b9")) return kDomb9; // TODO: other chords // beware when adding new chords // put shorter names at end of this function! return C; } // find the tonality mode in this string modes_t from_string_to_mode(const char *substr) { if (strstr(substr, "major")) return MAJOR; if (strstr(substr, "minor")) return MINOR; // TODO: other modes (doric, misolidian , custom, etc.. return C; } // builds a string for this chord // the string is in maxlib's chord format void chords_memory_chord2string(t_chords_memory *x, char *string, chord_t chord) { abs_note_t newnote; memset( string, '\0', sizeof(string) ); newnote = clean_note(chord.note + x->fundamental_note); switch (newnote) { case C: strcat(string, "C "); break; case Db: strcat(string, "Db "); break; case D: strcat(string, "D "); break; case Eb: strcat(string, "Eb "); break; case E: strcat(string, "E "); break; case F: strcat(string, "F "); break; case Gb: strcat(string, "Gb "); break; case G: strcat(string, "G "); break; case Ab: strcat(string, "Ab "); break; case A: strcat(string, "A "); break; case Bb: strcat(string, "Bb "); break; case B: strcat(string, "B "); break; } switch (chord.mode) { case kMaj: strcat(string, "major"); break; case kMin: strcat(string, "minor"); break; case kDim: strcat(string, "diminished"); break; case kAug: strcat(string, "augmented"); break; case kMaj7: strcat(string, "major 7th"); break; case kDom7: strcat(string, "dominant 7th"); break; case kMin7: strcat(string, "minor 7th"); break; case kHalfDim7: strcat(string, "half diminished 7th"); break; case kDim7: strcat(string, "diminished 7th"); break; case kMinMaj7: strcat(string, "minor/major 7th"); break; case kDomb9: strcat(string, "dominant b9"); break; } } // helper function that returns a substring of str // starting from start and ending in end char* substring_r(char* buffer, char* str, int start, int end) { int i, x = 0; for(i = start ; i <= end; i++) buffer[x++] = str[i]; buffer[x] = '\0'; return buffer; } // TODO: function to translate a string to chord // used both from inlet and from textfile chord_t chords_memory_string2chord(t_chords_memory *x, char *string) { chord_t chord; int index1; int interval; abs_note_t absnote; char substr[32]; // is 32 ok? if (DEBUG) post("chords_memory_string2chord: string='%s'",string); // c strings ends with \0 // so I set the substring to \0 memset( substr, '\0', sizeof(substr) ); // I assume the input is from maxlib's [chord] // i don't need the notes before ":" index1 = strcspn( string, ":"); if (index1 == strlen(string)) { // : not found // then the input was not from maxlib's [chord] // i hope they passed me the right string... strncpy( substr, string, strlen(string)); } else { // I will work on the right substring split by ":" substring_r(substr, string, index1+1, strlen(string)-1); if (isspace(substr[0])) { // substring inizia con uno spazio, lo tolgo index1 = strlen(substr)-1; memmove(substr, substr+1, index1); substr[index1] = '\0'; } } // now in substr i *should* have a string like this // "C dominant 7th" if (DEBUG) post("chords_memory_string2chord: substr='%s'",substr); // now I need to understand how many semitones there are // between x->current_fundamental and this chord absnote = from_string_to_abs_tone(substr); interval = clean_note(absnote - x->fundamental_note); chord.note = interval; chord.mode=from_string_to_type(substr); if (DEBUG) post("chords_memory_string2chord: chord.note=%i chord.mode=%i",chord.note, chord.mode); return chord; } // ------------------------------- search functions // internal function // find the better chord starting from chord1 // using the desired weight which is a value between 0 and 1 // so i have to normalize weights to 0-1 interval // i use maxweight to do that // TODO: add random, don't simply select the best but make a list with candidates // and select randomly chord_t chords_memory_find_better(t_chords_memory *x, chord_t chord1, float desired_weight) { // chords are integers // to know what this integer means do that: // int tone = chord1 / TONES_NUM // int type = chord1 % TYPES_NUM // the use a switch(tone) and a switch(type) // to know what kind of chord is this int chord1int, chord2int, i, best_index; float best_value; float tmp; double rnd; chord_t chord2; chord1int = chord1.note*TYPES_NUM + chord1.mode; //rnd = rand()/((double)RAND_MAX + 1); //best_index = rnd * NODES_NUM; tmp = 0; //best_value = fabs(((float) x->arcs[chord1int][best_index]) / ((float) x->maxweight) - desired_weight); //if (DEBUG_VERBOSE) // post("chords_memory_find_better: initial %i best value = %f",best_index, best_value); best_index = x->fundamental_mode; // fallback is I best_value = 2; // higher than everyone for (i=0; iarcs[x->fundamental_mode][chord1int][i]>0) { tmp = fabs(((float)x->arcs[x->fundamental_mode][chord1int][i]) / ((float)x->maxweight[x->fundamental_mode]) - desired_weight); if (DEBUG_VERBOSE) post("chords_memory_find_better: x->arcs[%i][%i][%i]=%i x->maxweight[%i]=%i desired_weight=%f tmp=%f",x->fundamental_mode,chord1int,i, x->arcs[x->fundamental_mode][chord1int][i], x->fundamental_mode, x->maxweight[x->fundamental_mode], desired_weight, tmp); if (tmp < best_value) { if (DEBUG_VERBOSE) { post("chords_memory_find_better: new best with value = %f", tmp); post("chords_memory_find_better: x->arcs[%i][chord1int][%i]=%i x->maxweight[%i]=%i desired_weight=%f",x->fundamental_mode,i, x->arcs[x->fundamental_mode][chord1int][i], x->fundamental_mode, x->maxweight[x->fundamental_mode], desired_weight); } best_value = tmp; best_index = i; } if (tmp == best_value) { rnd = rand()/((double)RAND_MAX + 1); if (rnd < 0.5) { best_value = tmp; best_index = i; if (DEBUG_VERBOSE) { post("chords_memory_find_better: new best with value = %f", tmp); post("chords_memory_find_better: x->arcs[%i][chord1int][%i]=%i x->maxweight[%i]=%i desired_weight=%f",x->fundamental_mode,i, x->arcs[x->fundamental_mode][chord1int][i],x->fundamental_mode,x->maxweight[x->fundamental_mode], desired_weight); } } } } } // now in best_index I have the best chord // i build the chord back from the integer chord2.mode = best_index % TYPES_NUM; chord2.note = best_index / TYPES_NUM; if (DEBUG) post("chords_memory_find_better: chord.note=%i chord.mode=%i",chord2.note, chord2.mode); return chord2; } // data structures to be used by build_walk // to sort arrays #include "sglib.h" // the data set we will work on it a matrix of candidates steps // this define each possibile chord and/or modulation // that can be chosen at each step typedef struct _possible_step { int chordInt; int cost; // NB integers! int modulation; // 1 if needs modulations before new chord int tonalityInt; } possible_step_t; typedef struct _possible_step2 { int index; int cost; // NB integers! } possible_step2_t; // a row of our data set // each row is a step in the walk typedef struct _step_row { possible_step_t cell[NODES_NUM+MODULATIONS_NUM]; int curr_cell; } step_row_t; // this is the real searching function // implementing a modified version of the // depth limited search // the difference is that we don't accept solutions // in less than the wanted number of steps // sglib comparator for an array of possible_step_t #define POSSIBLE_STEP_COMPARATOR(e1, e2) (e1.cost - e2.cost) #define MY_ARRAY_ELEMENTS_EXCHANGER(type, a, i, j) {type tmp;tmp=a[i];a[i]=a[j];a[j]=tmp;} // recursive function // returns 0 if solutions was not found // 1 if a solution was found // the way i implement this changes the search function // actually is a greedy one: // i always select the lower cost // and take the first solution // this algo is: // COMPLETE: YES // OPTIMAL: NO // complexity (where s=steps, n=nodes: // best case: n*s // worst case: n^s // this will surely need threads! int chords_memory_build_walk_recursive(t_chords_memory *x, int chord_from_int, int tonality_from_int, int chord_to_int, int tonality_to_int, int this_step, int wanted_steps, float desired_weight, step_row_t *step_matrix) { int i, cost_tmp, ret; int this_tonality_note; int this_tonality_mode; float cost_float; //test possible_step2_t *ordered_list; ordered_list = malloc(sizeof(possible_step2_t)*(NODES_NUM+MODULATIONS_NUM)); this_tonality_note = tonality_from_int / MODES_NUM; this_tonality_mode = tonality_from_int % MODES_NUM; if (DEBUG) post("chords_memory_build_walk_recursive: recursive function called, this_step=%i,this_tonality_mode=%i", this_step,this_tonality_mode); // first of all, I write all costs in step_matrix[this_step]; // chords first for (i=0; iarcs[this_tonality_mode][chord_from_int][i]>0) { cost_float = fabs(((float)x->arcs[this_tonality_mode][chord_from_int][i]) / ((float)x->maxweight[this_tonality_mode]) - desired_weight); } else { cost_float = 2; // never used this chord, so very costly } cost_tmp = cost_float * 1000; step_matrix[this_step].cell[i].cost = cost_tmp; //test ordered_list[i].index=i; ordered_list[i].cost=cost_tmp; if (DEBUG_VERBOSE) post("%i: cost_float=%f cost_tmp=%i chordInt=%i", i,cost_float,step_matrix[this_step].cell[i].cost, step_matrix[this_step].cell[i].chordInt); } // then modulations for (i=NODES_NUM; iusing_walk) free(x->walk); x->walk = malloc(sizeof(step_t)*steps); step_matrix = malloc(sizeof(step_row_t)*steps); chords_sequence = malloc(sizeof(t_atom)*steps); // for each step: // fill this step with costs // order the list // take the first // use it to fill the next level until you get to the desired step // if you don't find a solution then step back // select the second alternative and again chord_from_int = starting_chord.note * TYPES_NUM + starting_chord.mode ; tonality_from_int = x->fundamental_note * MODES_NUM + x->fundamental_mode; chord_to_int = ending_chord.note * TYPES_NUM + ending_chord.mode ; tonality_to_int = tonality_from_int; // TODO !!!!! if (DEBUG) post("chords_memory_build_walk: calling recursive function"); ret = chords_memory_build_walk_recursive(x, chord_from_int, tonality_from_int, chord_to_int, tonality_to_int, 0, steps, desired_weight, step_matrix); if (ret==0) { // no solution found // what do i do now?!?! if (DEBUG) post("no solution found"); return; } // copy the solution to the x->walk // and set all needed vars for (i=0; iwalk[i].modulate = modulationTMP; x->walk[i].chord.note = chordIntTMP / TYPES_NUM; x->walk[i].chord.mode = chordIntTMP % TYPES_NUM; x->walk[i].tonality_note = tonalityIntTMP / MODULATIONS_NUM; x->walk[i].tonality_mode = tonalityIntTMP % MODULATIONS_NUM; // build a list to send out to the // right outlet chords_memory_chord2string(x,stringTMP,x->walk[i].chord); if (DEBUG) post("chord: %s", stringTMP); //outlet_symbol(x->x_outtonalityname, gensym(stringTMP)); SETSYMBOL(chords_sequence+1, gensym(stringTMP)); //SETSYMBOL(chords_sequence+1, stringTMP); } // send the solution list to outlet x_outchordssequence outlet_list(x->x_outchordssequence, gensym("list") , steps, chords_sequence); // TODO: set other vars for walk // free arrays free(step_matrix); free(chords_sequence); // don't free walk, will be freed by someone else } // ------------------------------- from now on there are functions directly called by pd // when you send a |next x( message... // function actually invoked when sending a "next x" message to the external void chords_memory_next(t_chords_memory *x, t_floatarg f) { float desired_weight; chord_t best; char string[32]; desired_weight = f; best = chords_memory_find_better(x, x->last_chord, desired_weight); chords_memory_chord2string(x, string, best); outlet_symbol(x->x_outchordname, gensym(string)); } // when you send a search message void chords_memory_search(t_chords_memory *x, t_symbol *sl, int argc, t_atom *argv) { int steps; chord_t chord2; float desired_weight; // parse the list // you need chord_dest, steps and desired_weight // (later desired tonality also) if (argc<4) { error("usage: search chord-note chord-type steps weight"); return; } chord2.note = atom_getint(argv); chord2.mode= atom_getint(argv+1); steps = atom_getint(argv+2); desired_weight = atom_getfloat(argv+3); chords_memory_build_walk(x, x->last_chord, chord2, desired_weight, steps); } // ------------------------------- functions to set and add chords/tonality // sets the current chord void chords_memory_set_chord(t_chords_memory *x, t_symbol *s) { if (! x->last_chord_set) { x->last_chord_set=1; } x->last_chord = chords_memory_string2chord(x, s->s_name); if (DEBUG) post("chords_memory_set_chord: chord.note=%i chord.mode=%i",x->last_chord.note, x->last_chord.mode); } // add a chord sequence to the graph void chords_memory_add(t_chords_memory *x, chord_t chord1, chord_t chord2) { // chords are integers // to know what this integer means do that: // int tone = chord1 / TONES_NUM // int type = chord1 % TYPES_NUM // the use a switch(tone) and a switch(type) // to know what kind of chord is this int chord1int, chord2int; chord1int = chord1.note*TYPES_NUM + chord1.mode; chord2int = chord2.note*TYPES_NUM + chord2.mode; // now that i've translated chords in integers i can add // 1 to its wheight (a bit less new) x->arcs[x->fundamental_mode][chord1int][chord2int] = x->arcs[x->fundamental_mode][chord1int][chord2int] + 1; // is this the new maxweight? if (x->arcs[x->fundamental_mode][chord1int][chord2int] > x->maxweight[x->fundamental_mode]) { x->maxweight[x->fundamental_mode] = x->arcs[x->fundamental_mode][chord1int][chord2int]; if (DEBUG) post("x->maxweight[%i] = %i",x->fundamental_mode, x->maxweight[x->fundamental_mode]); } } // function invoked when a new chord is added at the graph // the external remembers the previous played chord void chords_memory_add_chord(t_chords_memory *x, t_symbol *s) { chord_t chord1; chord1 = chords_memory_string2chord(x, s->s_name); if (x->last_chord_set) { chords_memory_add(x, x->last_chord, chord1); } else { x->last_chord_set=1; } x->last_chord = chord1; if (DEBUG) post("chord added: %s", s->s_name); } // sets the current tonality void chords_memory_set_tonality(t_chords_memory *x, t_symbol *s) { chord_t c; int old_tonality; int interval; old_tonality = x->fundamental_note; //x->fundamental_note = (x->fundamental_note + from_string_to_abs_tone(s->s_name)) % 12; x->fundamental_note = from_string_to_abs_tone(s->s_name); x->fundamental_mode = from_string_to_mode(s->s_name); // when i set the tonality i always // go on the I grade if (! x->last_chord_set) { x->last_chord_set=1; } interval = x->fundamental_note - old_tonality; x->last_chord.note = clean_note(x->last_chord.note - interval); outlet_symbol(x->x_outtonalityname, gensym(s->s_name)); if (DEBUG) { post("chords_memory_set_tonality: new tonality note=%i mode=%i",x->fundamental_note,x->fundamental_mode); post("chords_memory_set_tonality: chord.note=%i chord.mode=%i",x->last_chord.note, x->last_chord.mode); } } // adds this modulation to memory // code similar to chords_memory_add // but adds a modulation instead of a chord void chords_memory_add_modulation(t_chords_memory *x, t_symbol *s) { chord_t c; int old_tonality; int newnote,newmode,newabsnote,chord1int; short int modulationInt; old_tonality = x->fundamental_note; //x->fundamental_note = (x->fundamental_note + from_string_to_abs_tone(s->s_name)) % 12; newabsnote = from_string_to_abs_tone(s->s_name); newmode = from_string_to_mode(s->s_name); // when i set the tonality i always // go on the I grade if (! x->last_chord_set) { x->last_chord_set=1; } newnote = clean_note(newabsnote - old_tonality); modulationInt = newnote*MODES_NUM + newmode; chord1int = x->last_chord.note*TYPES_NUM + x->last_chord.mode; x->modulations[x->fundamental_mode][chord1int][modulationInt] = x->modulations[x->fundamental_mode][chord1int][modulationInt] + 1; // is this the new maxweight? if (x->modulations[x->fundamental_mode][chord1int][modulationInt] > x->maxweight[x->fundamental_mode]) { x->maxweight[x->fundamental_mode] = x->modulations[x->fundamental_mode][chord1int][modulationInt]; } if (DEBUG) { post("chords_memory_add_modulation: new tonality note=%i mode=%i",newnote,newmode); } chords_memory_set_tonality(x, gensym(s->s_name)); } // ---------------------- file I/O // function to read graph from a file void chords_memory_read_from_file(t_chords_memory *x, t_symbol *s) { FILE *fp; int i, j, m; unsigned char tmp[2]; if ((fp=fopen(s->s_name, "r+b"))==NULL) { post("error: can't open file %s", s->s_name); return; } for (m=0; mmaxweight[m] = (fgetc(fp)<<16)|(fgetc(fp)); // i read the matrix for (i=0; iarcs[m][i][j] = (tmp[0]<<8)|(tmp[1]); if (DEBUG_VERBOSE) { if (x->arcs[m][i][j]>0) { post("x->arcs[%i][%i][%i] = %i",m,i,j,x->arcs[m][i][j]); } } } for (j=0; jmodulations[m][i][j] = (tmp[0]<<8)|(tmp[1]); if (DEBUG_VERBOSE) { if (x->modulations[m][i][j]>0) { post("x->modulations[%i][%i][%i] = %i",m,i,j,x->modulations[m][i][j]); } } } } if (DEBUG) post("x->maxweight[%i] = %i",m, x->maxweight[m]); } if (DEBUG) post("graph read from file %s",s->s_name); fclose(fp); } // function to write graph to a file (for later loading) void chords_memory_write_to_file(t_chords_memory *x, t_symbol *s) { FILE *fp; int i, j, m, tmp; fp=fopen(s->s_name, "w+b"); for (m=0; mmaxweight[m]>>8, fp); tmp = x->maxweight[m]<<8; tmp = tmp >> 8; fputc(tmp, fp); // i write down the matrix for (i=0; iarcs[m][i][j]>>8, fp); tmp = x->arcs[m][i][j]<<8; tmp = tmp >> 8; fputc(tmp, fp); } for (j=0; jmodulations[m][i][j]>>8, fp); tmp = x->modulations[m][i][j]<<8; tmp = tmp >> 8; fputc(tmp, fp); } } } if (DEBUG) post("graph wrote to file %s",s->s_name); fclose(fp); } // TODO: function that reads chords from a textfile and trains the graph // TODO: recursive function that builds a walk from chord1 to chord2 using desired novelty // set filename void chords_memory_set_filename(t_chords_memory *x, t_symbol *s) { x->filename = s; } void *chords_memory_new(t_symbol *s, int argc, t_atom *argv) { int i; time_t a; t_chords_memory *x = (t_chords_memory *)pd_new(chords_memory_class); x->x_outchordname = outlet_new(&x->x_obj, gensym("symbol")); x->x_outtonalityname = outlet_new(&x->x_obj, gensym("symbol")); x->x_outchordssequence = outlet_new(&x->x_obj, &s_list); srand(time(&a)); chords_memory_init_graph(x); x->fundamental_note = C; x->fundamental_mode = MAJOR; // example parameter if (argc>0) { x->filename = atom_getsymbolarg(0, argc, argv); chords_memory_read_from_file(x, x->filename); } return (x); } // here I free allocated memory if any void chords_memory_free(t_chords_memory *x) { // free(x->current_fundamental); } // TEST typedef struct _bla { int value; int index; } bla_t; typedef struct _bla2 { bla_t array[10]; } bla2_t; #define BLA_COMPARATOR(e1, e2) (e1.value - e2.value) //TEST void chords_memory_bang(t_chords_memory *x) { int i; bla2_t test[2]; for (i=0; i<10; i++) { test[1].array[i].index = i; test[1].array[i].value = 10-i; } SGLIB_ARRAY_QUICK_SORT (bla_t, test[1].array, 10, BLA_COMPARATOR, SGLIB_ARRAY_ELEMENTS_EXCHANGER); for (i=0; i<10; i++) { post("array[%i].value = %i, array[%i].index = %i",i, test[1].array[i].value,i, test[1].array[i].index ); } } void chords_memory_setup(void) { chords_memory_class = class_new(gensym("chords_memory"), (t_newmethod)chords_memory_new, (t_method)chords_memory_free, sizeof(t_chords_memory), CLASS_DEFAULT, A_GIMME, 0); class_addbang(chords_memory_class, (t_method)chords_memory_bang); // file I/O class_addmethod(chords_memory_class, (t_method)chords_memory_write_to_file, gensym("write"),A_SYMBOL, 0); class_addmethod(chords_memory_class, (t_method)chords_memory_read_from_file, gensym("read"),A_SYMBOL, 0); // ask for the best choice form here class_addmethod(chords_memory_class, (t_method)chords_memory_next, gensym("next"), A_DEFFLOAT, 0); // ask for a path from here to desired destination class_addmethod(chords_memory_class, (t_method)chords_memory_search, gensym("search"), A_GIMME, 0); // add or set chord class_addmethod(chords_memory_class, (t_method)chords_memory_add_chord, gensym("add"),A_SYMBOL, 0); class_addmethod(chords_memory_class, (t_method)chords_memory_set_chord, gensym("set"),A_SYMBOL, 0); // change current tonality class_addmethod(chords_memory_class, (t_method)chords_memory_set_tonality, gensym("tonality"),A_SYMBOL, 0); class_addmethod(chords_memory_class, (t_method)chords_memory_add_modulation, gensym("modulation"),A_SYMBOL, 0); // reinit memory class_addmethod(chords_memory_class, (t_method)chords_memory_init, gensym("init"), A_DEFFLOAT, 0); }