From eb9ef05774af20edb43118182834c18a4ac70707 Mon Sep 17 00:00:00 2001 From: Davide Morelli Date: Tue, 18 Oct 2005 23:10:53 +0000 Subject: initial checkin svn path=/trunk/externals/frankenstein/; revision=3734 --- GArhythm.c | 539 ++++++++++++ Makefile | 40 + README.txt | 77 ++ SHOWME.pd | 30 + bay.orc | 418 ++++++++++ chord_melo.c | 1137 +++++++++++++++++++++++++ chord_melo_test3.pd | 630 ++++++++++++++ chords_memory.c | 1137 +++++++++++++++++++++++++ doc/LINKS.txt | 26 + doc/README.txt | 27 + doc/chords_graph.doc | Bin 0 -> 75264 bytes doc/chords_graph.sxw | Bin 0 -> 16853 bytes doc/genomes-notes.txt | 125 +++ doc/maxlibnotes.txt | 16 + doc/model.doc | Bin 0 -> 17920 bytes doc/model.sxw | Bin 0 -> 6944 bytes harmonizer.c | 629 ++++++++++++++ makefile.darwin | 37 + manager.pd | 14 + patches/SHOWME.pd | 28 + patches/bay.orc | 418 ++++++++++ patches/test-harmonizer2.pd | 185 ++++ patches/test.graph | Bin 0 -> 57604 bytes ritmo1.c | 115 +++ sglib.h | 1947 +++++++++++++++++++++++++++++++++++++++++++ test-chords_graph.pd | 46 + test-harmonizer.pd | 172 ++++ test-harmonizer2.pd | 197 +++++ test.graph | Bin 0 -> 58564 bytes 29 files changed, 7990 insertions(+) create mode 100755 GArhythm.c create mode 100755 Makefile create mode 100755 README.txt create mode 100755 SHOWME.pd create mode 100755 bay.orc create mode 100755 chord_melo.c create mode 100755 chord_melo_test3.pd create mode 100755 chords_memory.c create mode 100755 doc/LINKS.txt create mode 100755 doc/README.txt create mode 100755 doc/chords_graph.doc create mode 100755 doc/chords_graph.sxw create mode 100755 doc/genomes-notes.txt create mode 100755 doc/maxlibnotes.txt create mode 100755 doc/model.doc create mode 100755 doc/model.sxw create mode 100755 harmonizer.c create mode 100755 makefile.darwin create mode 100755 manager.pd create mode 100755 patches/SHOWME.pd create mode 100755 patches/bay.orc create mode 100755 patches/test-harmonizer2.pd create mode 100755 patches/test.graph create mode 100755 ritmo1.c create mode 100755 sglib.h create mode 100755 test-chords_graph.pd create mode 100755 test-harmonizer.pd create mode 100755 test-harmonizer2.pd create mode 100755 test.graph diff --git a/GArhythm.c b/GArhythm.c new file mode 100755 index 0000000..a41b311 --- /dev/null +++ b/GArhythm.c @@ -0,0 +1,539 @@ +/* + +*/ +#include +#include "m_pd.h" + +#define BUFFER_LENGHT 16 // lunghezza dei buffers (quanti elementi nel pattern) +#define MAX_POPULATION 100 +#define CHOIR 20 +#define NUM_STRUM 4 // quanti strumenti uso , max 8 + +#define DEF_PROB_CROSSOVER 0.9f +#define DEF_PROB_MUTATION 0.025f +#define REINSERT_SRC 1 // quanti reinserisco ad ogni ciclo usando il ritmo src +#define REINSERT_LAST 0 // quanti reinserisco ad ogni ciclo usando il ritmo src + +#define DEBUG 0 // messaggi di debug + +static t_class *GArhythm_class; + + +typedef struct _GArhythm +{ + t_object x_obj; // myself + t_symbol *x_arrayname_src_strum1; // where i read the current pattern + t_symbol *x_arrayname_src_strum2; // where i read the current pattern + t_symbol *x_arrayname_src_strum3; // where i read the current pattern + t_symbol *x_arrayname_src_strum4; // where i read the current pattern + t_symbol *x_arrayname_dest_strum1; // where i put the computed pattern + t_symbol *x_arrayname_dest_strum2; // where i put the computed pattern + t_symbol *x_arrayname_dest_strum3; // where i put the computed pattern + t_symbol *x_arrayname_dest_strum4; // where i put the computed pattern + //t_float *buf_strum1; // buffer strum1o + //t_float *buf_strum2; // buffer alto + // tutti gli indici vanno da 0 a 1; + float indice_variazione; // quanto cambio dalla battuta precedente + float indice_riempimento; // quanto voglio fitto il pattern risultante + float indice_aderenza; // quanto simile al ritmo sorgente devo essere + // la popolazione array di cromosomi + char population[MAX_POPULATION][BUFFER_LENGHT]; + float prob_crossover; + float prob_mutation; + char last[BUFFER_LENGHT]; + +} t_GArhythm; + +void GArhythm_init_pop(t_GArhythm *x) +{ + int i, j, tmp, k; + double rnd; + for (i=0; i 0.5) + { + tmp = tmp + (1<population[i][j]=tmp; + } + if (DEBUG) + post("inizializzo population[%i] = %i%i%i%i", + i, + x->population[i][0], + x->population[i][1], + x->population[i][2], + x->population[i][3] + ); + } +} + +void GArhythm_init_buf(t_float *buf) +{ + int i; + for (i=0; ibuf_strum1 = (t_float *)getbytes(BUFFER_LENGHT * sizeof(t_float)); +// x->buf_strum2 = (t_float *)getbytes(BUFFER_LENGHT * sizeof(t_float)); +// GArhythm_init_buf(x->buf_strum1); +// GArhythm_init_buf(x->buf_strum2); + +} + +void GArhythm_free(t_GArhythm *x) +{ +// freebytes(x->buf_strum1, sizeof(x->buf_strum1)); +// freebytes(x->buf_strum2, sizeof(x->buf_strum2)); +} + +// returns fitness: how similar are man and woman +static double GArhythm_evaluate_fitness1(char *woman, char *man) +{ + int res=0; + int max = BUFFER_LENGHT*2; + int i; + for (i=0; iprob_crossover) + { + split =(int) ( rnd * BUFFER_LENGHT); // da 0 a MAX_POPULATION + for (i=0; i< split; i++) + { + child[i] = woman[i]; + } + for (i=split; i 0 se è presente il battito là + rnd = rand()/((double)RAND_MAX + 1); + if (rnd < x->prob_mutation) + { + if (DEBUG) + post("mutazione al battito %i allo strumento %i", i, j); + if (tmp) + { + child[i] = child[i] & (~(0x01<x_arrayname_src_strum1, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_src_strum1->s_name); + } + else if (!garray_getfloatarray(arysrc_strum1, &vecsize, &vecsrc_strum1)) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_src_strum1->s_name); + } + else if (!(arysrc_strum2 = (t_garray *)pd_findbyclass(x->x_arrayname_src_strum2, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_src_strum2->s_name); + } + else if (!garray_getfloatarray(arysrc_strum2, &vecsize, &vecsrc_strum2)) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_src_strum2->s_name); + } + else if (!(arysrc_strum3 = (t_garray *)pd_findbyclass(x->x_arrayname_src_strum3, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_src_strum3->s_name); + } + else if (!garray_getfloatarray(arysrc_strum3, &vecsize, &vecsrc_strum3)) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_src_strum3->s_name); + } + else if (!(arysrc_strum4 = (t_garray *)pd_findbyclass(x->x_arrayname_src_strum4, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_src_strum4->s_name); + } + else if (!garray_getfloatarray(arysrc_strum4, &vecsize, &vecsrc_strum4)) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_src_strum4->s_name); + } + else if (!(arydest_strum1 = (t_garray *)pd_findbyclass(x->x_arrayname_dest_strum1, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_dest_strum1->s_name); + } + else if (!garray_getfloatarray(arydest_strum1, &vecsize, &vecdest_strum1)) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_dest_strum1->s_name); + } + else if (!(arydest_strum2 = (t_garray *)pd_findbyclass(x->x_arrayname_dest_strum2, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_dest_strum2->s_name); + } + else if (!garray_getfloatarray(arydest_strum2, &vecsize, &vecdest_strum2)) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_dest_strum2->s_name); + } + else if (!(arydest_strum3 = (t_garray *)pd_findbyclass(x->x_arrayname_dest_strum3, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_dest_strum3->s_name); + } + else if (!garray_getfloatarray(arydest_strum3, &vecsize, &vecdest_strum3)) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_dest_strum3->s_name); + } + else if (!(arydest_strum4 = (t_garray *)pd_findbyclass(x->x_arrayname_dest_strum4, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_dest_strum4->s_name); + } + else if (!garray_getfloatarray(arydest_strum4, &vecsize, &vecdest_strum4)) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_dest_strum4->s_name); + } + else // I got arrays and data + { + // vecdest_strum2 e _strum1 contengono i valori in float degli array + if (DEBUG) + post("--------- starting process"); + + // uccido a caso REINSERT_SRC elementi e inserisco il ritmo src al loro posto + for (i=0; ipopulation[me][j]=c; + } + } + // uccido a caso REINSERT_LAST elementi e inserisco il last al loro posto + for (i=0; ipopulation[me][j]=x->last[j]; + } + } + + // metà sono donne, prese a caso + for (i=0; i<(MAX_POPULATION/2); i++) + { + int winner=CHOIR; + int winner_value=0; + int men[CHOIR]; + char figlio[BUFFER_LENGHT]; + double fitness1[CHOIR]; + double fitness2[CHOIR]; + double fitness3[CHOIR]; + double fitnessTOT[CHOIR]; + rnd = rand()/((double)RAND_MAX + 1); + me =(int) ( rnd * MAX_POPULATION); // da 0 a MAX_POPULATION + // me è la donna che valuta gli uomini + + if (DEBUG) + post("woman %i = %i %i %i %i", me, x->population[me][0], x->population[me][1], x->population[me][2], x->population[me][3]); + + for (j=0; jpopulation[me], x->population[tmp]); + fitness2[j]=GArhythm_evaluate_fitness2(x->population[me], x->population[tmp]); + fitness3[j]=GArhythm_evaluate_fitness3(x->population[me], x->population[tmp]); + fitnessTOT[j]=fitness1[j] * (x->indice_aderenza) + + fitness2[j] * (x->indice_riempimento) + + (1 - fitness2[j]) * (1-(x->indice_riempimento)) + + fitness3[j] * (x->indice_variazione); + if (winner_value <= fitnessTOT[j]) + { + winner = tmp; + winner_value = fitnessTOT[j]; + } + } + // winner è il maschio migliore nel coro + if (DEBUG) + post("ho scelto il maschio %i", winner); + // genero un figlio + GArhythm_create_child(x, x->population[me], x->population[winner], figlio); + for (j=0; jpopulation[me][j] = figli[i][j]; + } + } + + // prendo il più adatto rispetto all'ultimo ritmo suonato + winner = 0; + winner_fitness = 0; + for(i=0; ilast, x->population[i]); + tmp2 = GArhythm_evaluate_fitness2(x->last, x->population[i]); + tmp3 = GArhythm_evaluate_fitness3(x->last, x->population[i]); + tmpTOT = tmp1 * (x->indice_aderenza) + + tmp2 * (x->indice_riempimento) + + (1-tmp2) * (1-(x->indice_riempimento)) + + tmp3 * (x->indice_variazione); + if (tmpTOT >= winner_fitness) + { + winner_fitness = tmpTOT; + winner = i; + } + } + + for (i=0; ilast + x->last[i] = x->population[winner][i]; + // scrivo i buffer in uscita + vecdest_strum1[i]=((x->population[winner][i] & (0x01<<0)) ? 1 : 0); + vecdest_strum2[i]=((x->population[winner][i] & (0x01<<1)) ? 1 : 0); + vecdest_strum3[i]=((x->population[winner][i] & (0x01<<2)) ? 1 : 0); + vecdest_strum4[i]=((x->population[winner][i] & (0x01<<3)) ? 1 : 0); + } + + // redraw the arrays + //garray_redraw(arysrc); + garray_redraw(arydest_strum1); + garray_redraw(arydest_strum2); + garray_redraw(arydest_strum3); + garray_redraw(arydest_strum4); + + + } +} +/* +static void GArhythm_src(t_GArhythm *x, t_symbol *s) { + x->x_arrayname_src = s; +} +*/ + +static void GArhythm_variazione_set(t_GArhythm *x, t_floatarg f) +{ + x->indice_variazione = f; + } + +static void GArhythm_aderenza_set(t_GArhythm *x, t_floatarg f) +{ + x->indice_aderenza = f; +} + +static void GArhythm_riempimento_set(t_GArhythm *x, t_floatarg f) +{ + x->indice_riempimento = f; +} + +static void GArhythm_crossover_set(t_GArhythm *x, t_floatarg f) +{ + x->prob_crossover = f; +} + +static void GArhythm_mutation_set(t_GArhythm *x, t_floatarg f) +{ + x->prob_mutation = f; +} + +static void *GArhythm_new(t_symbol *s, int argc, t_atom *argv) +{ + t_GArhythm *x = (t_GArhythm *)pd_new(GArhythm_class); + GArhythm_allocate_buffers(x); + GArhythm_init_pop(x); + // inizializzo gli indici + x->indice_variazione=0; + x->indice_riempimento=0; + x->indice_aderenza=0; + x->prob_crossover = DEF_PROB_CROSSOVER; + x->prob_mutation = DEF_PROB_MUTATION; + + + if (argc>0) + { + x->x_arrayname_src_strum1 = atom_getsymbolarg(0, argc, argv); + } + if (argc>1) + { + x->x_arrayname_src_strum2 = atom_getsymbolarg(1, argc, argv); + } + if (argc>2) + { + x->x_arrayname_src_strum3 = atom_getsymbolarg(2, argc, argv); + } + if (argc>3) + { + x->x_arrayname_src_strum4 = atom_getsymbolarg(3, argc, argv); + } + if (argc>4) + { + x->x_arrayname_dest_strum1 = atom_getsymbolarg(4, argc, argv); + } + if (argc>5) + { + x->x_arrayname_dest_strum2 = atom_getsymbolarg(5, argc, argv); + } + if (argc>6) + { + x->x_arrayname_dest_strum3 = atom_getsymbolarg(6, argc, argv); + } + if (argc>7) + { + x->x_arrayname_dest_strum4 = atom_getsymbolarg(7, argc, argv); + } + + return (x); +} + +void GArhythm_setup(void) +{ + GArhythm_class = class_new(gensym("GArhythm"), (t_newmethod)GArhythm_new, + (t_method)GArhythm_free, sizeof(t_GArhythm), CLASS_DEFAULT, A_GIMME, 0); + class_addbang(GArhythm_class, (t_method)GArhythm_bang); +// class_addmethod(GArhythm_class, (t_method)GArhythm_src, gensym("src"),A_SYMBOL, 0); + class_addmethod(GArhythm_class, (t_method)GArhythm_variazione_set, gensym("variazione"), A_DEFFLOAT, 0); + class_addmethod(GArhythm_class, (t_method)GArhythm_riempimento_set, gensym("riempimento"), A_DEFFLOAT, 0); + class_addmethod(GArhythm_class, (t_method)GArhythm_aderenza_set, gensym("aderenza"), A_DEFFLOAT, 0); +} diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..b294eaf --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +# customize here ! +#VC="C:\Programmi\Microsoft Visual Studio .NET\Vc7" +VC="C:\Programmi\Microsoft Visual Studio .NET\Vc7" +PDPATH="H:\PureData\pd-0.38-3.msw\pd" +#PDPATH="C:\Documents and Settings\Davide\Documenti\personali\pd-0.38-3.msw\pd" + + +current: pd_nt distclean + +pd_nt: chord_melo.dll chords_memory.dll harmonizer.dll GArhythm.dll ritmo1.dll + +.SUFFIXES: .dll + +PDNTCFLAGS = /W3 /WX /O2 /G6 /DNT /DPD /nologo + +PDNTINCLUDE = /I. /I$(PDPATH)\tcl\include /I$(PDPATH)\src /I$(VC)\include /Iinclude + +PDNTLDIR = $(VC)\Lib +PDNTLIB = $(PDNTLDIR)\libc.lib \ + $(PDNTLDIR)\oldnames.lib \ + $(PDNTLDIR)\kernel32.lib \ + $(PDPATH)\bin\pd.lib + +.c.dll: + cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c + link /dll /export:$*_setup $*.obj $(PDNTLIB) + + + +clean: + -del link.stamp + -del *.obj + -del *.lib + -del *.exp + -del *.dll + +distclean: + -del *.obj + -del *.lib + -del *.exp diff --git a/README.txt b/README.txt new file mode 100755 index 0000000..8ee7599 --- /dev/null +++ b/README.txt @@ -0,0 +1,77 @@ +frankenstein set of externals + +authors: +dmorelli: www.davidemorelli.it +pland: www.davidcasal.com +with the help of vatic + +last update: +14-10-2005 + +------------------- what is this? + +this is an attempt to build an intelligent system for realtime +improvisation, a system that follows a human player and proposes +rhythm, melody, chords sequences, formal structure. +it learns the style in realtime. +it uses various AI techniques: GA, searchs, ANN (maybe) + +description of files: + +chord_melo.c +a GA melody generator. it takes a melody as input and evolves it over time. +uses co-evolutionary techniques (Todd) + +chords_memory.c +it is a directional graph which implements a memory of the played +chords sequences, you can train it... +and once trained you can ask questions like: +in C major, from a D minor 7h where did I go most if the times? +or +in C major, from a D minor 7h build a chord sequence to bring me +in D major in 4 steps using the chords sequences I used most + +harmonizer.c +a GA external that build choir voicing for 5 voices: +you pass it the midi value of each voice, starting chord, next chord +and it outputs a list with the midi values of each voice. +avoids hidden 8ves and 5ths. + + +folders: + +doc/ +implementation documents, notes, logs, ideas, etc.. +patches/ +example patches, help patches, some used in performances, etc.. +aima/ +python patches not yes used but interesting for agents +ruby/ +gridflow patches not yet used +old/ +old not used code +test/ +testing code, not to be used +backup/ +previous versions + + +TODO: + +gluer/solderer +an external that takes the input of two or more chord_melody, +a chord sequence and glues the melody statemets together +to build a long complete melody. + +form_manager +an external that manages the form and structure of the piece: +decide which melody statement to use, the chord sequence to ask for, etc.. + +various objects to detect played notes + +diatonic_melody.c +same as chord_melody but without chord reference, only scale used +(for post-tonal music non based on chords) + +chromatic_melo.c +same as diatonic_melo but without any reference (for atonal music) diff --git a/SHOWME.pd b/SHOWME.pd new file mode 100755 index 0000000..f870d28 --- /dev/null +++ b/SHOWME.pd @@ -0,0 +1,30 @@ +#N canvas 63 95 785 518 12; +#X obj 513 100 tabwrite src; +#X obj 644 87 tabwrite dest; +#N canvas 0 0 450 300 graph25 0; +#X array src 16 float 1; +#A 0 53 52 55 53 52 50 48 47 48 55 57 59 60 55 52 48; +#X array dest 16 float 1; +#A 0 53 50 52 38 64 46 43 41 48 64 67 67 36 55 53 57; +#X coords 0 127 15 0 400 250 1; +#X restore 75 32 graph; +#X obj 572 38 tgl 15 0 empty empty START 0 -6 0 8 -258699 -1 -1 1 1 +; +#X floatatom 550 138 5 0 0 0 - - -; +#X floatatom 596 138 5 0 0 0 - - -; +#X floatatom 648 137 5 0 0 0 - - -; +#X obj 591 279 tgl 15 0 toggleSRC empty src 0 -6 0 8 -262144 -1 -1 +0 1; +#X obj 627 279 tgl 15 0 toggleOUT empty out 0 -6 0 8 -262144 -1 -1 +1 1; +#X obj 572 56 chord_melo_test3; +#X floatatom 613 213 5 0 0 0 current_melo - -; +#X connect 3 0 9 0; +#X connect 9 0 4 0; +#X connect 9 0 0 0; +#X connect 9 1 1 0; +#X connect 9 1 5 0; +#X connect 9 2 10 0; +#X connect 9 3 0 1; +#X connect 9 3 1 1; +#X connect 9 3 6 0; diff --git a/bay.orc b/bay.orc new file mode 100755 index 0000000..e754d31 --- /dev/null +++ b/bay.orc @@ -0,0 +1,418 @@ +; ================================================================ +; Header +; ================================================================ +sr=44100 +kr=882 +ksmps=50 +nchnls=2 + +; ================================================================ +; Globals +; ================================================================ +zakinit 10,10 + +; Global variable for the reverb unit +ga1 init 0 + +; ================================================================ +; Tables +; ================================================================ +; Waveform for the string-pad +iwave ftgen 1, 0, 4096, 10, 1, .5, .33, .25, .0, .1, .1, .1 + +; Senoid required by chorus +isine ftgen 2, 0, 4096, 10, 1 + + +; ================================================================ +; Instruments +; ================================================================ + + +instr 1 ;String pad +; String-pad borrwoed from the piece "Dorian Gray", +; http://akozar.spymac.net/music/ Modified to fit my needs + +; ihz = p4 +; idb = p5/127 * 70 ; rescale MIDI velocity to 70db +; ipos = p6 +; iamp = ampdb(idb) + +; modified by dmorelli +khz invalue "hz1" +kpos invalue "pos1" +kamp invalue "amp1" +kctrl = kamp*70 +kctrl = ampdb(kctrl) + ; Slow attack and release +; kctrl linseg 0, p3/4, iamp, p3/2, 0 + ; Slight chorus effect + afund oscil kctrl, khz, 1 ; audio oscillator + acel1 oscil kctrl, khz - .1, 1 ; audio oscillator - flat + acel2 oscil kctrl, khz + .1, 1 ; audio oscillator - sharp + asig = afund + acel1 + acel2 + + ; Cut-off high frequencies depending on midi-velocity + ; (larger velocity implies more brighter sound) + asig butterlp asig, (p5-60)*40+900 + + ; Panning + kppan = kpos*1.570796325 ; half of PI (radians of 90o angle) + kpleft = cos(kppan) ; half sign "down" + kpright = sin(kppan) ; half sign "up" + asig1 = asig*kpleft; + asig2 = asig*kpright; + ; To the chorus effect, through zak channels 1 and 2 + zawm asig1, 1 + zawm asig2, 2 +endin + + +instr 2 ;String pad +; String-pad borrwoed from the piece "Dorian Gray", +; http://akozar.spymac.net/music/ Modified to fit my needs + +; ihz = p4 +; idb = p5/127 * 70 ; rescale MIDI velocity to 70db +; ipos = p6 +; iamp = ampdb(idb) + +; modified by dmorelli +khz invalue "hz2" +kpos invalue "pos2" +kamp invalue "amp2" +kctrl = kamp*70 +kctrl = ampdb(kctrl) + ; Slow attack and release +; kctrl linseg 0, p3/4, iamp, p3/2, 0 + ; Slight chorus effect + afund oscil kctrl, khz, 1 ; audio oscillator + acel1 oscil kctrl, khz - .1, 1 ; audio oscillator - flat + acel2 oscil kctrl, khz + .1, 1 ; audio oscillator - sharp + asig = afund + acel1 + acel2 + + ; Cut-off high frequencies depending on midi-velocity + ; (larger velocity implies more brighter sound) + asig butterlp asig, (p5-60)*40+900 + + ; Panning + kppan = kpos*1.570796325 ; half of PI (radians of 90o angle) + kpleft = cos(kppan) ; half sign "down" + kpright = sin(kppan) ; half sign "up" + asig1 = asig*kpleft; + asig2 = asig*kpright; + ; To the chorus effect, through zak channels 1 and 2 + zawm asig1, 1 + zawm asig2, 2 +endin + +instr 3 ;String pad +; String-pad borrwoed from the piece "Dorian Gray", +; http://akozar.spymac.net/music/ Modified to fit my needs + + ; ihz = p4 +; idb = p5/127 * 70 ; rescale MIDI velocity to 70db +; ipos = p6 +; iamp = ampdb(idb) + +; modified by dmorelli +khz invalue "hz3" +kpos invalue "pos3" +kamp invalue "amp3" +kctrl = kamp*70 +kctrl = ampdb(kctrl) + ; Slow attack and release +; kctrl linseg 0, p3/4, iamp, p3/2, 0 + ; Slight chorus effect + afund oscil kctrl, khz, 1 ; audio oscillator + acel1 oscil kctrl, khz - .1, 1 ; audio oscillator - flat + acel2 oscil kctrl, khz + .1, 1 ; audio oscillator - sharp + asig = afund + acel1 + acel2 + + ; Cut-off high frequencies depending on midi-velocity + ; (larger velocity implies more brighter sound) + asig butterlp asig, (p5-60)*40+900 + + ; Panning + kppan = kpos*1.570796325 ; half of PI (radians of 90o angle) + kpleft = cos(kppan) ; half sign "down" + kpright = sin(kppan) ; half sign "up" + asig1 = asig*kpleft; + asig2 = asig*kpright; + ; To the chorus effect, through zak channels 1 and 2 + zawm asig1, 1 + zawm asig2, 2 +endin +instr 4 ;String pad +; String-pad borrwoed from the piece "Dorian Gray", +; http://akozar.spymac.net/music/ Modified to fit my needs + +; ihz = p4 +; idb = p5/127 * 70 ; rescale MIDI velocity to 70db +; ipos = p6 +; iamp = ampdb(idb) + +; modified by dmorelli +khz invalue "hz4" +kpos invalue "pos4" +kamp invalue "amp4" +kctrl = kamp*70 +kctrl = ampdb(kctrl) + ; Slow attack and release +; kctrl linseg 0, p3/4, iamp, p3/2, 0 + ; Slight chorus effect + afund oscil kctrl, khz, 1 ; audio oscillator + acel1 oscil kctrl, khz - .1, 1 ; audio oscillator - flat + acel2 oscil kctrl, khz + .1, 1 ; audio oscillator - sharp + asig = afund + acel1 + acel2 + + ; Cut-off high frequencies depending on midi-velocity + ; (larger velocity implies more brighter sound) + asig butterlp asig, (p5-60)*40+900 + + ; Panning + kppan = kpos*1.570796325 ; half of PI (radians of 90o angle) + kpleft = cos(kppan) ; half sign "down" + kpright = sin(kppan) ; half sign "up" + asig1 = asig*kpleft; + asig2 = asig*kpright; + ; To the chorus effect, through zak channels 1 and 2 + zawm asig1, 1 + zawm asig2, 2 +endin + + + + + +; strumento senza invalue + +instr 9 ;String pad +; String-pad borrwoed from the piece "Dorian Gray", +; http://akozar.spymac.net/music/ Modified to fit my needs + + ihz = p4 + idb = p5/127 * 70 ; rescale MIDI velocity to 70db + ipos = p6 + iamp = ampdb(idb) + +; modified by dmorelli + +;kpos invalue "pos1" +;kamp invalue "amp1" +;kctrl = kamp*70 +;kctrl = ampdb(kctrl) + ; Slow attack and release + kctrl linseg 0, p3/4, iamp, p3/2, 0 + ; Slight chorus effect + afund oscil kctrl, ihz, 1 ; audio oscillator + acel1 oscil kctrl, ihz - .1, 1 ; audio oscillator - flat + acel2 oscil kctrl, ihz + .1, 1 ; audio oscillator - sharp + asig = afund + acel1 + acel2 + + ; Cut-off high frequencies depending on midi-velocity + ; (larger velocity implies more brighter sound) + asig butterlp asig, (p5-60)*40+900 + + ; Panning + ippan = ipos*1.570796325 ; half of PI (radians of 90o angle) + ipleft = cos(ippan) ; half sign "down" + ipright = sin(ippan) ; half sign "up" + asig1 = asig*ipleft; + asig2 = asig*ipright; + ; To the chorus effect, through zak channels 1 and 2 + zawm asig1, 1 + zawm asig2, 2 +endin + +; ================================================================ +; EFFECTS +; ================================================================ + + +; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +; Chorus effect, borrowed from http://www.jlpublishing.com/Csound.htm +; I made some of its parameters accesible trhough score +instr 10 ;Chorus + ; Read input from zak + a1 zar 1 + a2 zar 2 + idlyml=p4 ;delay in milliseconds + k1 oscili idlyml/p5, 1, 2 + ar1l vdelay3 a1, idlyml/5+k1, 900 ;delayed sound 1 + ar1r vdelay3 a2, idlyml/5+k1, 900 ;delayed sound 1 + k2 oscili idlyml/p5, .995, 2 + ar2l vdelay3 a1, idlyml/5+k2, 700 ;delayed sound 2 + ar2r vdelay3 a2, idlyml/5+k2, 700 ;delayed sound 2 + k3 oscili idlyml/p5, 1.05, 2 + ar3l vdelay3 a1, idlyml/5+k3, 700 ;delayed sound 3 + ar3r vdelay3 a2, idlyml/5+k3, 700 ;delayed sound 3 + k4 oscili idlyml/p5, 1, 2 + ar4l vdelay3 a1, idlyml/5+k4, 900 ;delayed sound 4 + ar4r vdelay3 a2, idlyml/5+k4, 900 ;delayed sound 4 + aoutl = (a1+ar1l+ar2l+ar3l+ar4l)*.5 + aoutr = (a2+ar1r+ar2r+ar3r+ar4r)*.5 + + ; To the output mixer + zawm aoutl, 5 + zawm aoutr, 6 + ; and also to the reverb unit + ga1 = ga1 + (aoutl+aoutr)*.5 +endin + +; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +; Reverb +; 8 delay line FDN reverb, with feedback matrix based upon +; physical modeling scattering junction of 8 lossless waveguides +; of equal characteristic impedance. Based on Julius O. Smith III, +; "A New Approach to Digital Reverberation using Closed Waveguide +; Networks," Proceedings of the International Computer Music +; Conference 1985, p. 47-53 (also available as a seperate +; publication from CCRMA), as well as some more recent papers by +; Smith and others. +; +; Coded by Sean Costello, October 1999 +instr 25 ;Reverb + ; Note: ga1 is the global input to the reverb. + afilt1 init 0 + afilt2 init 0 + afilt3 init 0 + afilt4 init 0 + afilt5 init 0 + afilt6 init 0 + afilt7 init 0 + afilt8 init 0 + idel1 = (2473.000/sr) + idel2 = (2767.000/sr) + idel3 = (3217.000/sr) + idel4 = (3557.000/sr) + idel5 = (3907.000/sr) + idel6 = (4127.000/sr) + idel7 = (2143.000/sr) + idel8 = (1933.000/sr) + + + igain = p4 ; gain of reverb. Adjust empirically + ; for desired reverb time. .6 gives + ; a good small "live" room sound, .8 + ; a small hall, .9 a large hall, + ; .99 an enormous stone cavern. + + ipitchmod = p5 ; amount of random pitch modulation + ; for the delay lines. 1 is the "normal" + ; amount, but this may be too high for + ; held pitches such as piano tones. + ; Adjust to taste. + + itone = p6 ; Cutoff frequency of lowpass filters + ; in feedback loops of delay lines, + ; in Hz. Lower cutoff frequencies results + ; in a sound with more high-frequency + ; damping. + + ; k1-k8 are used to add random pitch modulation to the + ; delay lines. Helps eliminate metallic overtones + ; in the reverb sound. + k1 randi .001, 3.1, .06 + k2 randi .0011, 3.5, .9 + k3 randi .0017, 1.11, .7 + k4 randi .0006, 3.973, .3 + k5 randi .001, 2.341, .63 + k6 randi .0011, 1.897, .7 + k7 randi .0017, 0.891, .9 + k8 randi .0006, 3.221, .44 + ; apj is used to calculate "resultant junction pressure" for + ; the scattering junction of 8 lossless waveguides + ; of equal characteristic impedance. If you wish to + ; add more delay lines, simply add them to the following + ; equation, and replace the .25 by 2/N, where N is the + ; number of delay lines. + apj = .25 * (afilt1 + afilt2 + afilt3 + afilt4 + afilt5 + afilt6 + afilt7 + afilt8) + + + adum1 delayr 1 + adel1 deltapi idel1 + k1 * ipitchmod + delayw ga1 + apj - afilt1 + + adum2 delayr 1 + adel2 deltapi idel2 + k2 * ipitchmod + delayw ga1 + apj - afilt2 + + adum3 delayr 1 + adel3 deltapi idel3 + k3 * ipitchmod + delayw ga1 + apj - afilt3 + + adum4 delayr 1 + adel4 deltapi idel4 + k4 * ipitchmod + delayw ga1 + apj - afilt4 + + adum5 delayr 1 + adel5 deltapi idel5 + k5 * ipitchmod + delayw ga1 + apj - afilt5 + + adum6 delayr 1 + adel6 deltapi idel6 + k6 * ipitchmod + delayw ga1 + apj - afilt6 + + adum7 delayr 1 + adel7 deltapi idel7 + k7 * ipitchmod + delayw ga1 + apj - afilt7 + + adum8 delayr 1 + adel8 deltapi idel8 + k8 * ipitchmod + delayw ga1 + apj - afilt8 + + ; 1st order lowpass filters in feedback + ; loops of delay lines. + afilt1 tone adel1 * igain, itone + afilt2 tone adel2 * igain, itone + afilt3 tone adel3 * igain, itone + afilt4 tone adel4 * igain, itone + afilt5 tone adel5 * igain, itone + afilt6 tone adel6 * igain, itone + afilt7 tone adel7 * igain, itone + afilt8 tone adel8 * igain, itone + + ; The outputs of the delay lines are summed + ; and sent to the stereo outputs. This could + ; easily be modified for a 4 or 8-channel + ; sound system. + aout1 = (afilt1 + afilt3 + afilt5 + afilt7) + aout2 = (afilt2 + afilt4 + afilt6 + afilt8) + ;outs aout1, aout2 + ; To the output mixer + zawm aout1, 5 + zawm aout2, 6 + ga1 = 0 +endin + + +; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +; Output mixer +; It applies a bass enhancement, compression and fadeout +; to the whole piece, +instr 30 ;Mixer + ; Read input from zak + a1 zar 5 + a2 zar 6 + ; Bass enhancement + al1 butterlp a1, 100 + al2 butterlp a2, 100 + a1 = al1*1.5 +a1 + a2 = al2*1.5 +a2 + + ; Global amplitude shape + ; It applies a gain of p4 to the whole piece, and creates a + ; fadeout the last p5 seconds + kenv linseg p4, p3-p5, p4, p5, 0 + a1=a1*kenv + a2=a2*kenv + + ; Compression + a1 dam a1, 5000, 0.5, 1, 0.2, 0.1 + a2 dam a2, 5000, 0.5, 1, 0.2, 0.1 + + outs a1, a2 + zacl 0, 10 +endin + diff --git a/chord_melo.c b/chord_melo.c new file mode 100755 index 0000000..975b653 --- /dev/null +++ b/chord_melo.c @@ -0,0 +1,1137 @@ +/* +BUGS: +- I had to comment out arrays re-display because it crashed my pd +- sometimes dest_played gets filled with junk + +TODO: +- now rests can't be used! change chord_melo_seq_max_min +*/ +#include +#include +#include +#include "m_pd.h" + +#define BUFFER_LENGHT 16 // lunghezza dei buffers (quanti elementi nel pattern) +#define MAX_POPULATION 500 +#define CHOIR 20 +#define NOTES 128 // how many notes can I use ? MIDI ! #define MAX_OCTAVES 3 + +#define DEF_PROB_CROSSOVER 0.9f +#define DEF_PROB_MUTATION 0.03f +#define REINSERT_SRC 2 // quanti reinserisco ad ogni ciclo usando il ritmo src +#define REINSERT_LAST 0 // quanti reinserisco ad ogni ciclo usando l'ultimo ritmo scelto + +#define DEBUG 1 // messaggi di debug +#define DEBUG_VERBOSE 0 // messaggi di debug + +static t_class *chord_melo_class; + +// 1 gene of the genome +typedef struct _chord_melo_gene +{ + int chord_note; // 0-12 + int passing_note; // from -4 to +4 + int played; // 0 / 1 +} chord_melo_gene; + +// an interval between 2 genes +typedef struct _chord_melo_interval +{ + int direction; // -1/0/1 + int chord_note; // 0-12 + int passing_note; // from -4 to +4 +} chord_melo_interval; + +typedef struct _chord_melo_critic +{ + double interval[12][9]; +} chord_melo_critic; + +// return the interval between 2 genes +chord_melo_interval chord_melo_Getchord_melo_interval(chord_melo_gene *g1, chord_melo_gene *g2) +{ + int note1, note2; + chord_melo_interval res; + if (g1->chord_note < g2->chord_note) + { + // ascending + res.direction = 1; + } else if (g1->chord_note > g2->chord_note) + { + // descending + res.direction = -1; + } else if (g1->passing_note == g2->passing_note) + { + // unison + res.direction = 0; + } else + { + // passing note + if (g1->passing_note < g2->passing_note) + { + // ascending + res.direction = 1; + } else + { + // descending + res.direction = -1; + } + } + res.chord_note = g1->chord_note - g2->chord_note; + res.passing_note = g2->passing_note - g1->passing_note; + return res; +} + +// fills an array of intervals with the transition in a genome +void chord_melo_transitions(chord_melo_interval *res, chord_melo_gene *src) +{ + int i; + //chord_melo_gene *last; + //last = &src[0]; + for (i=1;i 1) + tmp = chord_melo_Getchord_melo_interval(last, &src[i]).direction; + } + //res[i-1] = chord_melo_Getchord_melo_interval(last, &src[i]).direction; + res[i-1] = tmp; + last = &src[i]; + } + if (DEBUG_VERBOSE) + post("shape: %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", + res[0], res[1],res[2],res[3],res[4],res[5],res[6],res[7],res[8],res[9], + res[10],res[11],res[12],res[13],res[14]); + +} + +// fills a 4 gene long array with a sequence +// starting - max/min - max/min - ending +void chord_melo_seq_max_min(chord_melo_gene *seq, chord_melo_gene *src) +{ + // return a 4 gene seq: + // seq[0] = starting + // seq[1] = min/max + // seq[2] = min/max + // seq[3] = end + int i; + int min_pos=0; + int max_pos=0; + int first_pos=0; + int last_pos=0; + chord_melo_interval interv; + chord_melo_gene curr_min = src[0]; + chord_melo_gene curr_max = src[0]; + for (i=1;i 0) + { + max_pos = i; + curr_max = src[i]; + } + } + seq[0] = src[0]; + seq[3] = src[BUFFER_LENGHT-1]; + if (min_pos 1) + tmp = chord_melo_Getchord_melo_interval(last, &src[i]).direction; + } + //res[i-1] = chord_melo_Getchord_melo_interval(last, &src[i]).direction; + res[i-1] = tmp; + + //res[i-1] = chord_melo_Getchord_melo_interval(last, &seq[i]).direction; + last = &seq[i]; + } + + if (DEBUG_VERBOSE) + post("higher shape: %i %i %i %i", + res[0], res[1],res[2],res[3]); +} + +typedef struct _chord_melo +{ + t_object x_obj; // myself + t_symbol *x_arrayname_src_note; // where i read the current pattern + t_symbol *x_arrayname_src_octave; // where i read the current pattern + t_symbol *x_arrayname_src_passing; // where i read the current pattern + t_symbol *x_arrayname_src_played; // where i read the current pattern + t_symbol *x_arrayname_dest_note; // where i put the computed pattern + t_symbol *x_arrayname_dest_octave; // where i put the computed pattern + t_symbol *x_arrayname_dest_passing; // where i put the computed pattern + t_symbol *x_arrayname_dest_played; + // tutti gli indici vanno da 0 a 1; + float indice_fitness1; + float indice_fitness2; + float indice_fitness3; + // la popolazione array di cromosomi + chord_melo_gene population[MAX_POPULATION][BUFFER_LENGHT]; + float prob_crossover; + float prob_mutation; + chord_melo_gene last[BUFFER_LENGHT]; + chord_melo_gene last_src[BUFFER_LENGHT]; + int init; + t_float *vecsrc_note; + t_float *vecsrc_octave; + t_float *vecsrc_passing; + t_float *vecsrc_played; + t_float *vecdest_note; + t_float *vecdest_octave; + t_float *vecdest_passing; + t_float *vecdest_played; + int tables_loaded; + +} t_chord_melo; + + +// i use pointers to return more than 1 object at a time +void chord_melo_gene2note(chord_melo_gene thisgene, + unsigned int *octave, + unsigned int *note, + int *alteration, + unsigned int *played) +{ + *octave = thisgene.chord_note / 3; + *note = thisgene.chord_note % 3; + *alteration = thisgene.passing_note; + *played = thisgene.played; +} + +// passing note as a value between 0 and 2 (with octave) +chord_melo_gene chord_melo_note2gene(unsigned int octave, + unsigned int note, + int alteration, + unsigned int played) +{ + chord_melo_gene ris; + ris.chord_note = note + 3*octave; + ris.passing_note = alteration; + ris.played = played; + return ris; +} + +// passing note as a value 0-12 (without octave) +chord_melo_gene chord_melo_note2geneB(unsigned int note, + int alteration, + unsigned int played) +{ + chord_melo_gene ris; + ris.chord_note = note; + ris.passing_note = alteration; + ris.played = played; + return ris; +} + +// returns next passing note from src in the wanted direction, both chromatic or diatonic +chord_melo_gene chord_melo_get_next_passing(chord_melo_gene *src, int direction, int chromatic) +{ + int new_note; + int new_octave; + int new_passing; + int origine_nota; + int origine_ottava; + int origine_passaggio; + chord_melo_gene res; + origine_nota = src->chord_note % 3; + origine_ottava = src->chord_note / 3; + origine_passaggio = src->passing_note; + if (chromatic) + { + if (direction < 0) + { + new_octave = origine_ottava; + new_note = origine_nota; + new_passing = origine_passaggio - 1; + } else + { + new_octave = origine_ottava; + new_note = origine_nota; + new_passing = origine_passaggio + 1; + } + } else + { + // diatonic + if (direction < 0) + { + new_octave = origine_ottava; + new_note = origine_nota; + new_passing = origine_passaggio - 2; + } else + { + new_octave = origine_ottava; + new_note = origine_nota; + new_passing = origine_passaggio + 2; + } + } + // check notes + if ((new_passing < -4 )&&(new_note==0)) + { + new_passing += 5; + new_note = 2; + new_octave -= 1; + } + if ((new_passing < -3 )&&(new_note>0)) + { + new_passing += 4; + new_note -= 1; + } + if ((new_passing > 4 )&&(new_note==2)) + { + new_passing -= 5; + new_note = 0; + new_octave += 1; + } + if ((new_passing > 3 )&&(new_note<2)) + { + new_passing -= 4; + new_note += 1; + } + if (new_octave < 0) + new_octave=0; + + if (new_octave > 2) + new_octave=2; + +// if (new_octave > MAX_OCTAVES) +// new_octave=MAX_OCTAVES; + + res = chord_melo_note2gene(new_octave, new_note, new_passing, 1); + return res; +} + +void chord_melo_fill_critic(chord_melo_critic *critic, chord_melo_gene *src) +{ + int i, j; + chord_melo_interval intervalli[BUFFER_LENGHT-1]; + for (i=0; i<12; i++) + { + for (j=0; j<9; j++) + { + critic->interval[i][j]=0; + } + } + chord_melo_transitions(intervalli, src); + for (i=0; i<(BUFFER_LENGHT-1); i++) + { + if (critic->interval[intervalli[i].chord_note][intervalli[i].passing_note] < 0) + critic->interval[intervalli[i].chord_note][intervalli[i].passing_note] = 0; + critic->interval[intervalli[i].chord_note][intervalli[i].passing_note] = + critic->interval[intervalli[i].chord_note][intervalli[i].passing_note] +1; + } +} + +double chord_melo_Todd_fitness1(chord_melo_critic *woman, chord_melo_gene *man) +{ + + int i, j, res; + chord_melo_interval intervalli[BUFFER_LENGHT-1]; + chord_melo_transitions(intervalli, man); + res = 0; + for (i=0; i<(BUFFER_LENGHT-1); i++) + { + res += woman->interval[intervalli[i].chord_note][intervalli[i].passing_note]; + } + + return res; +} + +// fitness functions over higher shape +double chord_melo_fitness1(chord_melo_gene *woman, chord_melo_gene *man) +{ + int i, res; + int hi_shape1[4]; + int hi_shape2[4]; + chord_melo_higher_shape(hi_shape1, woman); + chord_melo_higher_shape(hi_shape2, man); + res = 0; + for (i=0; i<4; i++) + { + if (hi_shape1[i] * hi_shape2[i]>=0) + { + // same direction + res += 12 - abs(hi_shape2[i] - hi_shape1[i]); + } else if (hi_shape1[i] * hi_shape2[i]==0) + { + // one voice moving, the other no + } else + { + // opposite direction + res -= 5; + } + } + + return res; +} + +// fitness functions over punctual shape +double chord_melo_fitness2(chord_melo_gene *woman, chord_melo_gene *man) +{ + int i, res; + int shape1[BUFFER_LENGHT-1]; + int shape2[BUFFER_LENGHT-1]; + chord_melo_shape(shape1, woman); + chord_melo_shape(shape2, man); + res = 0; + for (i=0; i<4; i++) + { + if (shape1[i] * shape2[i]>=0) + { + // same direction + res += 12 - abs(shape2[i] - shape1[i]); + } else if (shape1[i] * shape2[i]==0) + { + // one voice moving, the other no + } else + { + // opposite direction + res -= 5; + } + } + return res; +} + + +void chord_melo_create_child(t_chord_melo *x, chord_melo_gene *woman, chord_melo_gene *man, chord_melo_gene *child) +{ + double rnd, rnd2; + int split, i, j, tmp, direction; + // crossover + rnd = rand()/((double)RAND_MAX + 1); + if (rnd < x->prob_crossover) + { + rnd = rand()/((double)RAND_MAX + 1); + + // vertical split + //split =(int) ( rnd * BUFFER_LENGHT); // da 0 a MAX_POPULATION + split = rand() % BUFFER_LENGHT; + for (i=0; i< split; i++) + { + child[i] = woman[i]; + } + for (i=split; iprob_mutation) + { + rnd = rand()/((double)RAND_MAX + 1); + if (rnd < 0.4) + { + int prec=6; + int tmp_oct; + int tmp_note; + int tmp_alt; + int tmp_played; + int interval; + // new chord note + int nuovanota = 0; + int nuovaplayed=0; + if (i>0) + { + chord_melo_gene2note(child[i-1], &tmp_oct, &tmp_note, &tmp_alt, &tmp_played); + prec = 3*tmp_oct + tmp_note; + rnd2 = rand()/((double)RAND_MAX + 1); + if (rnd2 < 0.3) + interval = 1; + else if (rnd2 < 0.6) + interval = 2; + else if (rnd2 < 0.8) + interval = 3; + else if (rnd2 < 0.9) + interval = 4; +// else if (rnd2 < 0.95) +// interval = 5; +// else if (rnd2 < 0.99) +// interval = 6; +// else +// interval = 7; + rnd2 = rand()/((double)RAND_MAX + 1); + if (rnd2 < 0.5) + interval *= -1; + + nuovanota = prec + interval; + + } else + { + rnd2 = rand()/((double)RAND_MAX + 1); + nuovanota = rnd2 * 12; + } + child[i] = chord_melo_note2gene(nuovanota / 3, nuovanota % 3, 0, 1); + } else if (rnd < 0.8) + { + // diatonic passing note + if (i>0) + { + if (child[i-1].passing_note == 0) + { + // only diatonic passing note if i come from a chord note! + rnd2 = rand()/((double)RAND_MAX + 1); + if (rnd2 < 0.5) + direction = -1; + else + direction = 1; + child[i] = chord_melo_get_next_passing(&child[i-1], direction, 0); + // resolve passing note + if (i<(BUFFER_LENGHT-1)) + child[i+1] = chord_melo_get_next_passing(&child[i], direction, 0); + // shall I go on? + if ((i<(BUFFER_LENGHT-2)) && + ((child[i-1].chord_note==2 && direction==1)|| + (child[i-1].chord_note==0 && direction==-1))) + { + child[i+2] = chord_melo_get_next_passing(&child[i+1], direction, 0); + } + + } + } + } else + { + // chromatic passing note + if (child[i-1].passing_note == 0) + { + // only diatonic passing note if i come from a chord note! + rnd2 = rand()/((double)RAND_MAX + 1); + if (rnd2 < 0.5) + direction = -1; + else + direction = 1; + child[i] = chord_melo_get_next_passing(&child[i-1], direction, 1); + // resolve passing note + if (i<(BUFFER_LENGHT-1)) + child[i+1] = chord_melo_get_next_passing(&child[i], direction, 1); + // shall I go on? + if ((i<(BUFFER_LENGHT-2)) && + ((child[i-1].chord_note==2 && direction==1)|| + (child[i-1].chord_note==0 && direction==-1))) + { + child[i+2] = chord_melo_get_next_passing(&child[i+1], direction, 1); + } + + } + } + + } + } + + + + + +} + + + +// ----------------- normal external code ... + + +void chord_melo_init_pop(t_chord_melo *x) +{ + int i, j, tmp, tmp2, k; + double rnd; + for (i=0; ipopulation[i][j] = chord_melo_note2gene(tmp2 / 3, tmp2 % 3, 0, 1); + + } + + } +} + + +void chord_melo_init_pop2(t_chord_melo *x) +{ + int i, j, tmp, tmp2, k; + double rnd; + for (i=0; i 0.5) + if (rnd > 1) + { + for (j=0; jpopulation[i][j] = chord_melo_note2gene(tmp2 / 3, tmp2 % 3, 0, 1); + + } + } else + { + for (j=0; jpopulation[i][j] = x->last_src[j]; + } + + } + + } + + for (j=0; jlast[j] = x->last_src[j]; + if (DEBUG) + post("init last[%] chord_note=%i passing_note=%i played=%i", j, + x->last[j].chord_note, x->last[j].passing_note, x->last[j].played); + // x->last[j] = x->population[0][j]; + // x->last[i] = chord_melo_note2gene(1,1,0,1); + } + + x->init=1; + +} + + +void chord_melo_init_buf(t_float *buf) +{ + int i; + for (i=0; ibuf_strum1 = (t_float *)getbytes(BUFFER_LENGHT * sizeof(t_float)); +// x->buf_strum2 = (t_float *)getbytes(BUFFER_LENGHT * sizeof(t_float)); +// chord_melo_init_buf(x->buf_strum1); +// chord_melo_init_buf(x->buf_strum2); + +} + +void chord_melo_free(t_chord_melo *x) +{ +// freebytes(x->buf_strum1, sizeof(x->buf_strum1)); +// freebytes(x->buf_strum2, sizeof(x->buf_strum2)); +} + + +void chord_melo_get_tables(t_chord_melo *x) { + + t_garray *arysrc_note; + t_garray *arysrc_octave; + t_garray *arysrc_passing; + t_garray *arysrc_played; + t_garray *arydest_note; + t_garray *arydest_octave; + t_garray *arydest_passing; + t_garray *arydest_played; + + int vecsize; + + // load tables + + if (!(arysrc_note = (t_garray *)pd_findbyclass(x->x_arrayname_src_note, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_src_note->s_name); + } + else if (!garray_getfloatarray(arysrc_note, &vecsize, &(x->vecsrc_note))) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_src_note->s_name); + } + else if (!(arysrc_octave = (t_garray *)pd_findbyclass(x->x_arrayname_src_octave, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_src_octave->s_name); + } + else if (!garray_getfloatarray(arysrc_octave, &vecsize, &(x->vecsrc_octave))) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_src_octave->s_name); + } + else if (!(arysrc_passing = (t_garray *)pd_findbyclass(x->x_arrayname_src_passing, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_src_passing->s_name); + } + else if (!garray_getfloatarray(arysrc_passing, &vecsize, &(x->vecsrc_passing))) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_src_passing->s_name); + } + else if (!(arysrc_played = (t_garray *)pd_findbyclass(x->x_arrayname_src_played, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_src_played->s_name); + } + else if (!garray_getfloatarray(arysrc_played, &vecsize, &(x->vecsrc_played))) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_src_played->s_name); + } + else if (!(arydest_note = (t_garray *)pd_findbyclass(x->x_arrayname_dest_note, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_dest_note->s_name); + } + else if (!garray_getfloatarray(arydest_note, &vecsize, &(x->vecdest_note))) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_dest_note->s_name); + } + else if (!(arydest_octave = (t_garray *)pd_findbyclass(x->x_arrayname_dest_octave, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_dest_octave->s_name); + } + else if (!garray_getfloatarray(arydest_octave, &vecsize, &(x->vecdest_octave))) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_dest_octave->s_name); + } + else if (!(arydest_passing = (t_garray *)pd_findbyclass(x->x_arrayname_dest_passing, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_dest_passing->s_name); + } + else if (!garray_getfloatarray(arydest_passing, &vecsize, &(x->vecdest_passing))) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_dest_note->s_name); + } + else if (!(arydest_played = (t_garray *)pd_findbyclass(x->x_arrayname_dest_played, garray_class))) + { + pd_error(x, "%s: no such array", x->x_arrayname_dest_played->s_name); + } + else if (!garray_getfloatarray(arydest_played, &vecsize, &(x->vecdest_played))) + { + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname_dest_played->s_name); + } + else // I got arrays and data + { + // tutto ok + x->tables_loaded=1; + } +} + +static void chord_melo_bang(t_chord_melo *x) { + + int i, j, vecsize, ntot, tmp, me; + float prob, variatore; + + double rnd; + int winner; + double winner_fitness; + double average_fitness; + chord_melo_gene src_genome[BUFFER_LENGHT]; + chord_melo_gene figli[MAX_POPULATION][BUFFER_LENGHT]; + + //chord_melo_critic src_critic; + chord_melo_critic last_critic; + + if (x->tables_loaded == 0) + { + chord_melo_get_tables(x); + } + else // I got arrays and data + { + if (DEBUG) + post("--------- starting process"); + + if (DEBUG) + post("building genome for the src melody:"); + + srand((unsigned int)time((time_t *)NULL)); + + // get src's genome + for (i=0; ivecsrc_octave[i], + x->vecsrc_note[i], x->vecsrc_passing[i], x->vecsrc_played[i]); + x->last_src[i] = src_genome[i]; + //post("src melody: vecsrc_octave[i]=%f,vecsrc_note[i]=%f,vecsrc_passing[i]=%f", + // vecsrc_octave[i], vecsrc_note[i], vecsrc_passing[i]); + + } + //return; + + if (x->init==0) + return; + + // uccido a caso REINSERT_SRC elementi e inserisco il ritmo src al loro posto + for (i=0; ipopulation[me][j]=src_genome[j]; + } + } + + //return; + + // uccido a caso REINSERT_LAST elementi e inserisco il last al loro posto + for (i=0; ipopulation[me][j]=x->last[j]; + } + } + + // metà sono donne, prese a caso + for (i=0; i<(MAX_POPULATION/2); i++) + { + //chord_melo_critic this_chord_melo_critic; + int winner=0; + double winner_value=0; + int men[CHOIR]; + chord_melo_gene figlio[BUFFER_LENGHT]; + double fitness1[CHOIR]; + double fitness2[CHOIR]; + double fitness3[CHOIR]; + double fitnessTOT[CHOIR]; + double fitnessTodd1[CHOIR]; + chord_melo_critic woman_critic; + //rnd = rand()/((double)RAND_MAX + 1); + //me =(int) ( rnd * MAX_POPULATION); // da 0 a MAX_POPULATION + me = rand() % MAX_POPULATION; + // me è la donna che valuta gli uomini + + if (DEBUG_VERBOSE) + post("woman %i = %i %i %i %i", me, x->population[me][0], x->population[me][1], x->population[me][2], x->population[me][3]); + + chord_melo_fill_critic(&woman_critic, x->population[me]); + + for (j=0; jpopulation[me], x->population[tmp]); + fitness2[j]=chord_melo_fitness2(x->population[me], x->population[tmp]); + fitnessTodd1[j]=chord_melo_Todd_fitness1(&woman_critic, x->population[tmp]); + fitnessTOT[j]=fitness1[j] * (x->indice_fitness1) + + fitness2[j] * (x->indice_fitness2) + + fitnessTodd1[j] * (x->indice_fitness3); + if (DEBUG_VERBOSE) + post("man %i has fitness %i", tmp, fitnessTOT[j]); + if (winner_value <= fitnessTOT[j]) + { + winner = tmp; + winner_value = fitnessTOT[j]; + } + } + // winner è il maschio migliore nel coro + if (DEBUG_VERBOSE) + post("ho scelto il maschio %i con fitness %i", winner, winner_value); + // chord_melo_genero un figlio + chord_melo_create_child(x, x->population[me], x->population[winner], figlio); + for (j=0; jpopulation[me][j] = figli[i][j]; + } + } + + // prendo il più adatto rispetto all'ultimo ritmo suonato + winner = 0; + winner_fitness = 0; + average_fitness = 0; + + //chord_melo_fill_critic(&src_critic, src_genome); + chord_melo_fill_critic(&last_critic, x->last); + + for(i=0; ipopulation[i]); + //tmp2 = chord_melo_fitness2(src_genome, x->population[i]); + //fitnessTodd1=chord_melo_Todd_fitness1(&src_critic, x->population[i]); + tmp1 = chord_melo_fitness1(x->last, x->population[i]); + tmp2 = chord_melo_fitness2(x->last, x->population[i]); + fitnessTodd1=chord_melo_Todd_fitness1(&last_critic, x->population[i]); + tmpTOT = tmp1 * (x->indice_fitness1) + + tmp2 * (x->indice_fitness2) + + fitnessTodd1 * (x->indice_fitness3); + + //post("%i fitness = %i", i, tmpTOT); + + if (tmpTOT >= winner_fitness) + { + winner_fitness = tmpTOT; + winner = i; + } + average_fitness += tmpTOT; + } + average_fitness = average_fitness / MAX_POPULATION; + + if (DEBUG) + post("winner is number %i with fitness=%d, average fitness = %d", winner, winner_fitness, average_fitness); + + for (i=0; ilast + x->last[i] = x->population[winner][i]; + // scrivo i buffer in uscita + chord_melo_gene2note(x->population[winner][i], + &octave, ¬e, &passing, &played); + x->vecdest_note[i]=note; + x->vecdest_octave[i]=octave; + x->vecdest_passing[i]=passing; + x->vecdest_played[i]=(played > 0 ? 1 : 0); + if (DEBUG) + post("winner[%i] chord_note=%i, octave=%i, passing=%i, played=%i", + i, note, octave, passing, played); + } + + //return; + + // redraw the arrays +/* garray_redraw(arydest_note); + garray_redraw(arydest_octave); + garray_redraw(arydest_passing); + garray_redraw(arydest_played); +*/ + + } +} + +void chord_melo_src_note(t_chord_melo *x, t_symbol *s) { + x->x_arrayname_src_note = s; +} + +void chord_melo_src_octave(t_chord_melo *x, t_symbol *s) { + x->x_arrayname_src_octave = s; +} + +void chord_melo_src_passing(t_chord_melo *x, t_symbol *s) { + x->x_arrayname_src_passing = s; +} + +void chord_melo_src_played(t_chord_melo *x, t_symbol *s) { + x->x_arrayname_src_played = s; +} + +void chord_melo_dest_note(t_chord_melo *x, t_symbol *s) { + x->x_arrayname_dest_note = s; +} + +void chord_melo_dest_octave(t_chord_melo *x, t_symbol *s) { + x->x_arrayname_dest_octave = s; +} + +void chord_melo_dest_passing(t_chord_melo *x, t_symbol *s) { + x->x_arrayname_dest_passing = s; +} + +void chord_melo_dest_played(t_chord_melo *x, t_symbol *s) { + x->x_arrayname_dest_played = s; +} + + +void chord_melo_fitness1_set(t_chord_melo *x, t_floatarg f) +{ + x->indice_fitness1 = f; + } + +void chord_melo_fitness2_set(t_chord_melo *x, t_floatarg f) +{ + x->indice_fitness2 = f; +} + +void chord_melo_fitness3_set(t_chord_melo *x, t_floatarg f) +{ + x->indice_fitness3 = f; +} + +void chord_melo_crossover_set(t_chord_melo *x, t_floatarg f) +{ + x->prob_crossover = f; +} + +void chord_melo_mutation_set(t_chord_melo *x, t_floatarg f) +{ + x->prob_mutation = f; +} + +/* +void chord_melo_init(t_chord_melo *x, t_symbol *s) { + chord_melo_init_pop2(x); +} +*/ + +void *chord_melo_new(t_symbol *s, int argc, t_atom *argv) +{ + int i; + time_t a; + t_chord_melo *x = (t_chord_melo *)pd_new(chord_melo_class); + chord_melo_allocate_buffers(x); + chord_melo_init_pop(x); + // inizializzo gli indici + x->indice_fitness1=0; + x->indice_fitness2=0; + x->indice_fitness3=0; + x->prob_crossover = DEF_PROB_CROSSOVER; + x->prob_mutation = DEF_PROB_MUTATION; + x->init=0; + x->tables_loaded=0; + for (i=0; ilast[i] = chord_melo_note2gene(1,0,0,1); + } + srand(time(&a)); + + if (argc>0) + { + x->x_arrayname_src_note = atom_getsymbolarg(0, argc, argv); + if (DEBUG) + post("x->x_arrayname_src_note=%s",x->x_arrayname_src_note->s_name); + } + if (argc>1) + { + x->x_arrayname_src_octave = atom_getsymbolarg(1, argc, argv); + if (DEBUG) + post("x->x_arrayname_src_octave=%s",x->x_arrayname_src_octave->s_name); + } + if (argc>2) + { + x->x_arrayname_src_passing = atom_getsymbolarg(2, argc, argv); + if (DEBUG) + post("x->x_arrayname_src_passing=%s",x->x_arrayname_src_passing->s_name); + } + if (argc>3) + { + x->x_arrayname_src_played = atom_getsymbolarg(3, argc, argv); + if (DEBUG) + post("x->x_arrayname_src_played=%s",x->x_arrayname_src_played->s_name); + } + if (argc>4) + { + x->x_arrayname_dest_note = atom_getsymbolarg(4, argc, argv); + if (DEBUG) + post("x->x_arrayname_dest_note=%s",x->x_arrayname_dest_note->s_name); + } + if (argc>5) + { + x->x_arrayname_dest_octave = atom_getsymbolarg(5, argc, argv); + if (DEBUG) + post("x->x_arrayname_dest_octave=%s",x->x_arrayname_dest_octave->s_name); + } + if (argc>6) + { + x->x_arrayname_dest_passing = atom_getsymbolarg(6, argc, argv); + if (DEBUG) + post("x->x_arrayname_dest_passing=%s",x->x_arrayname_dest_passing->s_name); + } + if (argc>7) + { + x->x_arrayname_dest_played = atom_getsymbolarg(7, argc, argv); + if (DEBUG) + post("x->x_arrayname_dest_played=%s",x->x_arrayname_dest_played->s_name); + } + return (x); +} + +void chord_melo_setup(void) +{ + chord_melo_class = class_new(gensym("chord_melo"), (t_newmethod)chord_melo_new, + (t_method)chord_melo_free, sizeof(t_chord_melo), CLASS_DEFAULT, A_GIMME, 0); + class_addbang(chord_melo_class, (t_method)chord_melo_bang); + class_addmethod(chord_melo_class, (t_method)chord_melo_src_note, gensym("src_note"),A_SYMBOL, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_src_octave, gensym("src_octave"),A_SYMBOL, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_src_passing, gensym("src_passing"),A_SYMBOL, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_src_played, gensym("src_played"),A_SYMBOL, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_dest_note, gensym("dest_note"),A_SYMBOL, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_dest_octave, gensym("dest_octave"),A_SYMBOL, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_dest_passing, gensym("dest_passing"),A_SYMBOL, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_dest_played, gensym("dest_played"),A_SYMBOL, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_fitness1_set, gensym("fitness1"), A_DEFFLOAT, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_fitness2_set, gensym("fitness2"), A_DEFFLOAT, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_fitness3_set, gensym("fitness3"), A_DEFFLOAT, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_init_pop2, gensym("init"),A_SYMBOL, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_mutation_set, gensym("mutation"), A_DEFFLOAT, 0); + class_addmethod(chord_melo_class, (t_method)chord_melo_crossover_set, gensym("crossover"), A_DEFFLOAT, 0); + +} diff --git a/chord_melo_test3.pd b/chord_melo_test3.pd new file mode 100755 index 0000000..4b5185a --- /dev/null +++ b/chord_melo_test3.pd @@ -0,0 +1,630 @@ +#N canvas 204 24 1015 713 12; +#X obj 620 131 metro; +#N canvas 1 23 466 316 msecs 0; +#X obj 58 19 inlet; +#X obj 61 209 outlet; +#X obj 57 147 /; +#X obj 57 100 bang; +#X obj 73 79 t f f; +#X obj 57 174 / 4; +#X msg 57 122 60000; +#X connect 0 0 4 0; +#X connect 2 0 5 0; +#X connect 3 0 6 0; +#X connect 4 0 3 0; +#X connect 4 1 2 1; +#X connect 5 0 1 0; +#X connect 6 0 2 0; +#X restore 684 102 pd msecs; +#X obj 683 28 loadbang; +#X obj 620 108 tgl 15 0 empty empty empty 0 -6 0 8 -258699 -1 -1 0 +1; +#N canvas 0 22 498 348 graph2 0; +#X obj 122 9 inlet; +#X obj 362 255 outlet; +#X obj 171 33 int; +#X obj 204 31 + 1; +#X obj 185 57 % 4; +#X obj 168 78 sel 0; +#X obj 168 105 bng 15 250 50 0 empty empty beat 0 -6 0 8 -262144 -1 +-1; +#X obj 263 86 sel 0; +#X obj 264 108 bng 15 250 50 0 empty empty bar 0 -6 0 8 -262144 -1 +-1; +#X obj 262 63 % 16; +#X obj 69 278 hradio 10 1 0 16 empty empty empty 0 -6 0 8 -262144 -1 +-1 0; +#X obj 70 64 / 2; +#X obj 71 87 int; +#X obj 72 111 hradio 10 1 0 2 empty empty 1/8 0 -6 0 8 -262144 -1 -1 +0; +#X obj 342 78 int; +#X obj 73 220 hradio 10 1 0 4 empty empty 1/4 0 -6 0 8 -262144 -1 -1 +0; +#X obj 341 55 / 4; +#X obj 70 167 hradio 10 1 0 4 empty empty 1/16 0 -6 0 8 -262144 -1 +-1 0; +#X connect 0 0 2 0; +#X connect 2 0 3 0; +#X connect 3 0 2 1; +#X connect 3 0 4 0; +#X connect 3 0 9 0; +#X connect 4 0 5 0; +#X connect 4 0 17 0; +#X connect 4 0 11 0; +#X connect 5 0 6 0; +#X connect 7 0 8 0; +#X connect 9 0 7 0; +#X connect 9 0 10 0; +#X connect 9 0 16 0; +#X connect 9 0 1 0; +#X connect 11 0 12 0; +#X connect 12 0 13 0; +#X connect 14 0 15 0; +#X connect 16 0 14 0; +#X coords 0 1 100 -1 200 140 1; +#X restore 379 92 graph; +#X floatatom 706 127 5 0 0 0 - - -; +#X obj 632 169 delay; +#X obj 667 148 / 2; +#X text 296 172 this delay is to center the quantization; +#X obj 685 78 nbx 5 14 30 1000 0 0 empty empty empty 0 -6 0 10 -258699 +-1 -1 120 256; +#X text 29 30 test1: detect rhythmic pattern and proposes a counterpoint +; +#N canvas 0 22 458 308 valori 0; +#X obj 57 40 inlet; +#X obj 136 42 inlet; +#X obj 229 42 inlet; +#X obj 58 215 outlet; +#X msg 60 112 fitness1 \$1; +#X msg 136 74 fitness2 \$1; +#X msg 231 133 fitness3 \$1; +#X connect 0 0 4 0; +#X connect 1 0 5 0; +#X connect 2 0 6 0; +#X connect 4 0 3 0; +#X connect 5 0 3 0; +#X connect 6 0 3 0; +#X restore 713 532 pd valori; +#X obj 713 378 vsl 15 128 0 1 0 0 empty empty 1st 0 -8 0 8 -262144 +-1 -1 12700 1; +#X obj 760 380 vsl 15 128 0 1 0 0 empty empty 2nd 0 -8 0 8 -262144 +-1 -1 12700 1; +#X obj 800 383 vsl 15 128 0 1 0 0 empty empty 3rd 0 -8 0 8 -262144 +-1 -1 12700 1; +#X floatatom 700 515 5 0 0 0 - - -; +#X floatatom 750 513 5 0 0 0 - - -; +#X floatatom 800 522 5 0 0 0 - - -; +#X obj 719 49 s sampleinit; +#X obj 726 294 loadbang; +#N canvas 105 213 716 372 initMelody 0; +#X obj 79 28 loadbang; +#X msg 18 132 \; src_played const 1 \;; +#X msg 29 261 \; src_passing const 0 \;; +#X msg 260 69 \; src_note 0 0 1 2 2 1 1 0 0 0 2 2 2 0 2 1 0 \;; +#X msg 235 143 \; src_passing 0 0 0 0 -2 0 -2 0 -1 0 0 2 4 0 0 0 0 +\;; +#X msg 247 107 \; src_octave 0 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 \;; +#X connect 0 0 3 0; +#X connect 0 0 1 0; +#X connect 0 0 5 0; +#X connect 0 0 4 0; +#X restore 57 108 pd initMelody; +#X obj 153 586 noteout; +#X obj 153 562 makenote; +#X msg 238 539 60; +#X msg 270 539 100; +#X obj 244 517 loadbang; +#X msg 684 49 120; +#X msg 712 328 1; +#X obj 770 119 table src_octave; +#X obj 769 96 table src_note; +#X obj 770 141 table src_passing; +#X obj 770 165 table src_played; +#N canvas 14 41 980 629 play_melody 0; +#X obj 58 50 t f f f f; +#X obj 58 116 tabread dest_note; +#X obj 225 79 tabread dest_octave; +#X obj 186 153 tabread dest_passing; +#X obj 2 290 tabread dest_played; +#X obj 1 315 select 1; +#X obj 259 -4 inlet; +#X obj 659 8 inlet; +#X floatatom 710 68 5 0 0 0 - - -; +#X text 729 52 base midi note; +#X obj 225 103 * 12; +#X obj 57 144 select 0 1 2; +#X msg 57 186 0; +#X msg 103 184 4; +#X msg 150 185 7; +#X obj 159 266 +; +#X obj 170 303 +; +#X obj 180 344 + 36; +#X text 245 344 default C; +#X obj 275 535 outlet; +#X floatatom 204 390 5 0 0 0 - - -; +#X obj 95 451 int 0; +#X obj 2 339 bang; +#X obj 435 50 t f f f f; +#X obj 372 332 select 1; +#X obj 596 120 * 12; +#X obj 428 161 select 0 1 2; +#X msg 428 203 0; +#X msg 474 201 4; +#X msg 521 202 7; +#X obj 530 283 +; +#X obj 541 320 +; +#X obj 551 361 + 36; +#X text 616 361 default C; +#X floatatom 575 407 5 0 0 0 - - -; +#X obj 466 468 int 0; +#X obj 373 356 bang; +#X obj 373 307 tabread dest_played2; +#X obj 429 133 tabread dest_note2; +#X obj 596 96 tabread dest_octave2; +#X obj 557 170 tabread dest_passing2; +#X obj 883 242 inlet; +#X obj 235 491 spigot; +#X msg 221 455 1; +#X msg 256 456 0; +#X obj 318 492 spigot; +#X msg 297 455 1; +#X msg 332 456 0; +#X obj 258 19 t f f; +#X obj 247 430 select 0 1; +#X floatatom 373 533 5 0 0 0 - - -; +#X floatatom 195 539 5 0 0 0 - - -; +#X connect 0 0 4 0; +#X connect 0 1 1 0; +#X connect 0 2 3 0; +#X connect 0 3 2 0; +#X connect 1 0 11 0; +#X connect 2 0 10 0; +#X connect 3 0 15 1; +#X connect 4 0 5 0; +#X connect 5 0 22 0; +#X connect 6 0 48 0; +#X connect 7 0 8 0; +#X connect 7 0 17 1; +#X connect 7 0 32 1; +#X connect 10 0 16 1; +#X connect 11 0 12 0; +#X connect 11 1 13 0; +#X connect 11 2 14 0; +#X connect 12 0 15 0; +#X connect 13 0 15 0; +#X connect 14 0 15 0; +#X connect 15 0 16 0; +#X connect 16 0 17 0; +#X connect 17 0 20 0; +#X connect 17 0 21 1; +#X connect 21 0 42 0; +#X connect 22 0 21 0; +#X connect 23 0 37 0; +#X connect 23 1 38 0; +#X connect 23 2 40 0; +#X connect 23 3 39 0; +#X connect 24 0 36 0; +#X connect 25 0 31 1; +#X connect 26 0 27 0; +#X connect 26 1 28 0; +#X connect 26 2 29 0; +#X connect 27 0 30 0; +#X connect 28 0 30 0; +#X connect 29 0 30 0; +#X connect 30 0 31 0; +#X connect 31 0 32 0; +#X connect 32 0 34 0; +#X connect 32 0 35 1; +#X connect 35 0 45 0; +#X connect 36 0 35 0; +#X connect 37 0 24 0; +#X connect 38 0 26 0; +#X connect 39 0 25 0; +#X connect 40 0 30 1; +#X connect 41 0 49 0; +#X connect 42 0 19 0; +#X connect 42 0 51 0; +#X connect 43 0 42 1; +#X connect 44 0 45 1; +#X connect 45 0 19 0; +#X connect 45 0 50 0; +#X connect 46 0 45 1; +#X connect 47 0 42 1; +#X connect 48 0 0 0; +#X connect 48 1 23 0; +#X connect 49 0 43 0; +#X connect 49 0 44 0; +#X connect 49 1 46 0; +#X connect 49 1 47 0; +#X restore 153 432 pd play_melody; +#X floatatom 264 313 5 0 0 0 base_midi_note - -; +#N canvas 80 41 790 533 test_src_melody 0; +#X obj 64 33 t f f f f; +#X obj -17 299 select 1; +#X obj 67 3 inlet; +#X obj 418 160 inlet; +#X floatatom 442 209 5 0 0 0 - - -; +#X text 461 193 base midi note; +#X obj 268 125 * 12; +#X obj 57 144 select 0 1 2; +#X msg 57 186 0; +#X msg 103 184 4; +#X msg 150 185 7; +#X obj 159 266 +; +#X obj 170 303 +; +#X obj 180 344 + 36; +#X text 245 344 default C; +#X obj 120 482 outlet; +#X floatatom 204 390 5 0 0 0 - - -; +#X obj 116 450 int 0; +#X obj -16 323 bang; +#X obj -16 274 tabread src_played; +#X obj 58 116 tabread src_note; +#X obj 267 102 tabread src_octave; +#X obj 186 153 tabread src_passing; +#X connect 0 0 19 0; +#X connect 0 1 20 0; +#X connect 0 2 22 0; +#X connect 0 3 21 0; +#X connect 1 0 18 0; +#X connect 2 0 0 0; +#X connect 3 0 4 0; +#X connect 3 0 13 1; +#X connect 6 0 12 1; +#X connect 7 0 8 0; +#X connect 7 1 9 0; +#X connect 7 2 10 0; +#X connect 8 0 11 0; +#X connect 9 0 11 0; +#X connect 10 0 11 0; +#X connect 11 0 12 0; +#X connect 12 0 13 0; +#X connect 13 0 16 0; +#X connect 13 0 17 1; +#X connect 17 0 15 0; +#X connect 18 0 17 0; +#X connect 19 0 1 0; +#X connect 20 0 7 0; +#X connect 21 0 6 0; +#X connect 22 0 11 1; +#X restore 26 383 pd test_src_melody; +#N canvas 0 22 462 312 chords 0; +#X obj 146 45 select 0; +#X obj 147 17 inlet; +#X obj 150 255 outlet; +#X msg 123 193 36; +#X msg 177 193 41; +#X msg 232 191 36; +#X msg 286 191 43; +#X obj 142 96 int 0; +#X obj 144 69 bang; +#X obj 202 99 + 1; +#X obj 141 122 % 4; +#X obj 137 147 select 0 1 2 3; +#X connect 0 0 8 0; +#X connect 1 0 0 0; +#X connect 3 0 2 0; +#X connect 4 0 2 0; +#X connect 5 0 2 0; +#X connect 6 0 2 0; +#X connect 7 0 9 0; +#X connect 7 0 10 0; +#X connect 8 0 7 0; +#X connect 9 0 7 1; +#X connect 10 0 11 0; +#X connect 11 0 3 0; +#X connect 11 1 4 0; +#X connect 11 2 5 0; +#X connect 11 3 6 0; +#X restore 265 290 pd chords; +#X obj 102 619 outlet; +#X obj 14 619 outlet; +#X obj 569 246 outlet; +#X obj 379 233 t a a a a; +#X obj 607 56 inlet; +#X obj 80 181 tgl 15 0 toggleSRC empty src 0 -6 0 8 -262144 -1 -1 0 +1; +#X obj 78 215 tgl 15 0 toggleOUT empty out 0 -6 0 8 -262144 -1 -1 0 +1; +#N canvas 0 22 650 510 toggle 0; +#X obj 170 119 r toggleSRC; +#X obj 136 144 int 0; +#X obj 136 168 select 1; +#X obj 137 192 bang; +#X obj 154 20 inlet; +#X obj 197 270 outlet; +#X obj 203 231 int 0; +#X obj 86 104 bang; +#X obj 201 60 t a a; +#X connect 0 0 1 1; +#X connect 1 0 2 0; +#X connect 2 0 3 0; +#X connect 3 0 6 0; +#X connect 4 0 8 0; +#X connect 6 0 5 0; +#X connect 7 0 1 0; +#X connect 8 0 7 0; +#X connect 8 1 6 1; +#X restore 33 414 pd toggle; +#N canvas 0 22 662 522 toggle 0; +#X obj 136 144 int 0; +#X obj 136 168 select 1; +#X obj 137 192 bang; +#X obj 154 20 inlet; +#X obj 197 270 outlet; +#X obj 203 231 int 0; +#X obj 86 104 bang; +#X obj 201 60 t a a; +#X obj 170 119 r toggleOUT; +#X connect 0 0 1 0; +#X connect 1 0 2 0; +#X connect 2 0 5 0; +#X connect 3 0 7 0; +#X connect 5 0 4 0; +#X connect 6 0 0 0; +#X connect 7 0 6 0; +#X connect 7 1 5 1; +#X connect 8 0 0 1; +#X restore 155 495 pd toggle; +#N canvas 0 22 778 359 read-tables 0; +#X obj 93 110 tabread dest_note; +#X floatatom 96 147 5 0 0 0 - - -; +#X floatatom 341 44 5 0 0 0 - - -; +#X floatatom 244 147 5 0 0 0 - - -; +#X floatatom 388 146 5 0 0 0 - - -; +#X floatatom 532 143 5 0 0 0 - - -; +#X obj 243 91 tabread dest_octave; +#X obj 386 109 tabread dest_passing; +#X obj 529 89 tabread dest_played; +#X obj 514 175 print; +#X connect 0 0 1 0; +#X connect 2 0 0 0; +#X connect 2 0 6 0; +#X connect 2 0 7 0; +#X connect 2 0 8 0; +#X connect 6 0 3 0; +#X connect 7 0 4 0; +#X connect 8 0 5 0; +#X connect 8 0 9 0; +#X restore 847 310 pd read-tables; +#X msg 866 529 mutation \$1; +#X msg 883 549 crossover \$1; +#X obj 865 373 vsl 15 128 0 1 0 0 empty empty empty 0 -8 0 8 -262144 +-1 -1 0 1; +#X obj 899 371 vsl 15 128 0 1 0 0 empty empty empty 0 -8 0 8 -262144 +-1 -1 0 1; +#X floatatom 845 507 5 0 0 0 - - -; +#X floatatom 900 505 5 0 0 0 - - -; +#X msg 791 327 1; +#N canvas 0 22 923 490 chord_melo1 0; +#X obj 94 49 inlet; +#X obj 349 58 inlet; +#X obj 123 264 bang; +#X obj 140 116 select 0; +#X obj 185 256 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 191 187 int 0; +#X obj 240 188 + 1; +#X floatatom 191 211 5 0 0 0 generation - -; +#X obj 191 164 bang; +#X msg 23 270 init a; +#X obj 22 227 select 1; +#X obj 23 248 bang; +#X obj 222 344 chord_melo src_note src_octave src_passing src_played +dest_note dest_octave dest_passing dest_played; +#X obj 415 139 table dest_note; +#X obj 416 162 table dest_octave; +#X obj 415 184 table dest_passing; +#X obj 415 208 table dest_played; +#X connect 0 0 3 0; +#X connect 1 0 12 0; +#X connect 2 0 12 0; +#X connect 3 0 2 0; +#X connect 3 0 8 0; +#X connect 4 0 12 0; +#X connect 5 0 6 0; +#X connect 5 0 7 0; +#X connect 5 0 10 0; +#X connect 6 0 5 1; +#X connect 8 0 5 0; +#X connect 9 0 12 0; +#X connect 10 0 11 0; +#X connect 11 0 9 0; +#X restore 667 589 pd chord_melo1; +#N canvas 0 22 923 490 chord_melo2 0; +#X obj 94 49 inlet; +#X obj 349 58 inlet; +#X obj 123 264 bang; +#X obj 140 116 select 0; +#X obj 185 256 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 191 187 int 0; +#X obj 240 188 + 1; +#X floatatom 191 211 5 0 0 0 generation - -; +#X obj 191 164 bang; +#X msg 23 270 init a; +#X obj 22 227 select 1; +#X obj 23 248 bang; +#X obj 415 139 table dest_note2; +#X obj 416 162 table dest_octave2; +#X obj 415 184 table dest_passing2; +#X obj 415 208 table dest_played2; +#X obj 222 344 chord_melo src_note src_octave src_passing src_played +dest_note2 dest_octave2 dest_passing2 dest_played2; +#X connect 0 0 3 0; +#X connect 1 0 16 0; +#X connect 2 0 16 0; +#X connect 3 0 2 0; +#X connect 3 0 8 0; +#X connect 4 0 16 0; +#X connect 5 0 6 0; +#X connect 5 0 7 0; +#X connect 5 0 10 0; +#X connect 6 0 5 1; +#X connect 8 0 5 0; +#X connect 9 0 16 0; +#X connect 10 0 11 0; +#X connect 11 0 9 0; +#X restore 473 628 pd chord_melo2; +#X obj 339 336 manager; +#N canvas 0 22 698 589 sum 0; +#X obj 116 64 inlet; +#X obj 150 338 outlet; +#X obj 278 36 inlet; +#X obj 265 72 select 0; +#X obj 264 124 int 0; +#X obj 312 123 + 1; +#X obj 266 96 bang; +#X obj 86 202 spigot; +#X obj 147 203 int; +#X obj 104 236 + 0; +#X obj 124 99 t f f; +#X obj 211 126 bang; +#X text 383 222 not working yet; +#X text 337 254 it should join the two statements; +#X text 326 277 join means make the first interval small; +#X text 400 192 TODO; +#X obj 204 295 spigot; +#X obj 251 201 bang; +#X msg 246 223 1; +#X obj 257 247 -; +#X obj 253 177 t a a; +#X obj 254 147 % 2; +#X connect 0 0 10 0; +#X connect 2 0 3 0; +#X connect 3 0 6 0; +#X connect 3 0 11 0; +#X connect 4 0 5 0; +#X connect 4 0 21 0; +#X connect 5 0 4 1; +#X connect 6 0 4 0; +#X connect 7 0 9 0; +#X connect 8 0 9 1; +#X connect 9 0 1 0; +#X connect 10 0 8 1; +#X connect 10 0 7 0; +#X connect 10 1 16 0; +#X connect 11 0 8 0; +#X connect 16 0 1 0; +#X connect 17 0 18 0; +#X connect 18 0 19 0; +#X connect 19 0 16 1; +#X connect 20 0 17 0; +#X connect 20 1 19 1; +#X connect 21 0 20 0; +#X connect 21 0 7 1; +#X restore 224 468 pd sum; +#N canvas 0 22 458 308 valori 0; +#X obj 57 40 inlet; +#X obj 136 42 inlet; +#X obj 229 42 inlet; +#X obj 58 215 outlet; +#X msg 60 112 fitness1 \$1; +#X msg 136 74 fitness2 \$1; +#X msg 231 133 fitness3 \$1; +#X connect 0 0 4 0; +#X connect 1 0 5 0; +#X connect 2 0 6 0; +#X connect 4 0 3 0; +#X connect 5 0 3 0; +#X connect 6 0 3 0; +#X restore 511 539 pd valori; +#X obj 511 385 vsl 15 128 0 1 0 0 empty empty 1st 0 -8 0 8 -262144 +-1 -1 12700 1; +#X obj 558 387 vsl 15 128 0 1 0 0 empty empty 2nd 0 -8 0 8 -262144 +-1 -1 12700 1; +#X obj 598 390 vsl 15 128 0 1 0 0 empty empty 3rd 0 -8 0 8 -262144 +-1 -1 12700 1; +#X floatatom 498 522 5 0 0 0 - - -; +#X floatatom 548 520 5 0 0 0 - - -; +#X floatatom 598 529 5 0 0 0 - - -; +#X obj 524 301 loadbang; +#X msg 510 335 1; +#X msg 589 334 1; +#X msg 423 568 mutation \$1; +#X msg 440 588 crossover \$1; +#X obj 422 412 vsl 15 128 0 1 0 0 empty empty empty 0 -8 0 8 -262144 +-1 -1 0 1; +#X obj 456 410 vsl 15 128 0 1 0 0 empty empty empty 0 -8 0 8 -262144 +-1 -1 0 1; +#X floatatom 402 546 5 0 0 0 - - -; +#X floatatom 457 544 5 0 0 0 - - -; +#X floatatom 344 439 5 0 0 0 - - -; +#X obj 304 614 outlet; +#X connect 0 0 6 0; +#X connect 1 0 0 1; +#X connect 1 0 5 0; +#X connect 1 0 7 0; +#X connect 2 0 26 0; +#X connect 2 0 18 0; +#X connect 3 0 0 0; +#X connect 4 0 39 0; +#X connect 6 0 4 0; +#X connect 7 0 6 1; +#X connect 9 0 1 0; +#X connect 11 0 53 1; +#X connect 12 0 11 0; +#X connect 12 0 15 0; +#X connect 13 0 11 1; +#X connect 13 0 16 0; +#X connect 14 0 11 2; +#X connect 14 0 17 0; +#X connect 19 0 27 0; +#X connect 19 0 52 0; +#X connect 22 0 21 0; +#X connect 22 1 21 1; +#X connect 23 0 22 1; +#X connect 24 0 22 2; +#X connect 25 0 24 0; +#X connect 25 0 23 0; +#X connect 26 0 9 0; +#X connect 27 0 12 0; +#X connect 27 0 13 0; +#X connect 32 0 36 0; +#X connect 32 0 44 0; +#X connect 33 0 34 1; +#X connect 33 0 32 1; +#X connect 34 0 37 0; +#X connect 34 0 43 0; +#X connect 35 0 33 0; +#X connect 39 1 32 0; +#X connect 39 1 34 0; +#X connect 39 2 35 0; +#X connect 39 2 53 0; +#X connect 39 2 54 0; +#X connect 39 3 38 0; +#X connect 39 3 55 0; +#X connect 39 3 56 1; +#X connect 40 0 3 0; +#X connect 43 0 22 0; +#X connect 44 0 22 0; +#X connect 46 0 53 1; +#X connect 47 0 53 1; +#X connect 48 0 46 0; +#X connect 48 0 50 0; +#X connect 49 0 47 0; +#X connect 49 0 51 0; +#X connect 52 0 14 0; +#X connect 55 0 32 2; +#X connect 55 0 73 0; +#X connect 55 0 74 0; +#X connect 57 0 54 1; +#X connect 58 0 57 0; +#X connect 58 0 61 0; +#X connect 59 0 57 1; +#X connect 59 0 62 0; +#X connect 60 0 57 2; +#X connect 60 0 63 0; +#X connect 64 0 65 0; +#X connect 64 0 66 0; +#X connect 65 0 58 0; +#X connect 65 0 59 0; +#X connect 66 0 60 0; +#X connect 67 0 54 1; +#X connect 68 0 54 1; +#X connect 69 0 67 0; +#X connect 69 0 71 0; +#X connect 70 0 68 0; +#X connect 70 0 72 0; diff --git a/chords_memory.c b/chords_memory.c new file mode 100755 index 0000000..9695940 --- /dev/null +++ b/chords_memory.c @@ -0,0 +1,1137 @@ +/* +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 10 // 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 + } 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; + // 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; + + } +} + +// 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); + +} diff --git a/doc/LINKS.txt b/doc/LINKS.txt new file mode 100755 index 0000000..d8685c5 --- /dev/null +++ b/doc/LINKS.txt @@ -0,0 +1,26 @@ + +open source libs ready to be used... + +AthenaCL: +is a python software for algorithmic composition +we could use some code through pyext +http://www.flexatone.net/athena.html +this is specific for post tonal theory +http://www.flexatone.net/athenaDocs/www/ch-analysis.htm + +AIMA Python code, working agents, etc. +http://aima.cs.berkeley.edu/python/readme.html + +BEAGLE, implements co-evolution, open source, C++ +beagle.gel.ulaval.ca + +open source c++ genetic algorithm: +http://lancet.mit.edu/ga/ + +plain ansi C (multi platform?): +http://sourceforge.net/projects/ga-lib + +another in c++: +http://sourceforge.net/projects/ga2 + + diff --git a/doc/README.txt b/doc/README.txt new file mode 100755 index 0000000..524dfc1 --- /dev/null +++ b/doc/README.txt @@ -0,0 +1,27 @@ +***README*** + +Vatic, davide, please add notes and thoughts here for now... + +****Ruby/Gridflow instructions*** + +In order to use some of the objects in the test patches, for now just [random_choice] within [manager] in the chord_melo_test patches, you will need GridFlow and Ruby installed. + +Please find instructions here: + +http://gridflow.ca/latest/doc/install.html + +Once you have Ruby 1.8.0 or above installed, and GridFlow is happily starting in Puredata, you should see the following at PD startup: + +setting up Ruby-for-PureData... +we are using Ruby version 1.8.1 +[gf] This is GridFlow 0.8.1 within Ruby version 1.8.1 + +Or, something similar. + +Now, you will need to copy ruby/.gridflow_startup to your home directory. If it is somewhere else than /home/you/ or /Users/you, you can always load it from pd by using the Ruby console input (see pd's console window, and under IN - OUT), by doing: + +load "/path/to/.gridflow_startup + +You're now able to write puredata objects in Ruby! + + diff --git a/doc/chords_graph.doc b/doc/chords_graph.doc new file mode 100755 index 0000000..e8390c2 Binary files /dev/null and b/doc/chords_graph.doc differ diff --git a/doc/chords_graph.sxw b/doc/chords_graph.sxw new file mode 100755 index 0000000..9f901ec Binary files /dev/null and b/doc/chords_graph.sxw differ diff --git a/doc/genomes-notes.txt b/doc/genomes-notes.txt new file mode 100755 index 0000000..e39e524 --- /dev/null +++ b/doc/genomes-notes.txt @@ -0,0 +1,125 @@ + +***read README.txt before this file*** + +this ideas are related to shostakovich prelude n.2 in a minor + + +how do we map notes <--> genome ? + + + +--------------- 1st idea: whole MIDI, absolute values + +keep all the chromatic values, all midi notes + +the fitness functions and crossover functions will manage the thing +(no notes outside current chord, etc..) + +each gene is a byte, +- first 7 bits for midi note (unsigned char, 0-127) +- last bit for rest (1=play, 0=rest) + +why yes: +-not restricted to prelude n.2 musical space +-not restricted to tonal music space + +why not: +-huge transition table, intervals table +-we don't want values we don't need +-difficult to write fitness functions that check for tonality, chords, etc.. + +---------------- 2nd idea: chromatic scale, chord relative + +notes are relative to current chord but still in a chromatic space. +the critics must choose the diatonic ones and discard atonal ones + +each gene is a byte, +- first 7 bits for midi note (unsigned char, 0-127) +- last bit for rest (1=play, 0=rest) + + +why yes: +-we can use the same melody to different chords +-not restricted to prelude n.2 musical space +-not restricted to tonal music space + +why not: +-still huge tables +-difficult to write fitness functions that check for tonality, chords, etc.. + +---------------- 3rd idea: diatonic scale, chord relative + +the musical space is diatonic, no more chromatic. critics need to work much less, +they only have to know if a note is in the chord or not. + +problem: how to use chromatic passing notes this way? + +possibile notes are 7*4=28 (7 notes per octave) + passing notes +we could use : 1 byte for each gene +- 5 bits for note (0-32) +- 2 bit for cromatic passing note (0=no, 1=descending, 2=ascending, 4=not used) +- 1 bit for rests + + +why yes: +-we can use the same melody to different chords +-quick fitness functions +-we don't have notes we don't want + +why not: +-restricted to tonal music space + +---------------- 4th idea: chord notes + + +the musical space are the chord notes, not even the diatonic scale. +critics don't have to check anything! + +problem: how to use (chromatic or diatonic) passing notes? + +possibile notes are 3*4=12 (3 notes per octave) + passing notes +we should use a struct for the gene: +{ + - note (from 0 to 16) + - rest (0/1) + - passing_note (from -4 to +4) +} + +possible passing notes are 9: +no passing, from -4 semitones to +4 semitones + +the fitness functions and crossover functions will have to care sbout rules for passing notes: ++3 and +4 are allowed only for the 3rd note of the chord +-4 and -3 are allowed only for the 1st note of the chord +every passing note can exist only if the note before was next to it and proceeding in the same direction +each passing note must resolve on the nearer chord note in the right direction + +why yes: +-we can use the same melody to different chords +-quickest fitness functions +-we don't have notes we don't want + +why not: +-restricted to tonal music space +-restricted to 3-notes-chords-based harmonies (jazz is 4 notes based) + + + + + +common problems: + +i can think a way to rule passing notes (a passing note must come from a consonant note and go to a consonant note) +but what can we do if a passing not is at the very end of the melody? (i don't know what will the next note be) +...we should have to know what will be the next chord... + + + + + + + + + +Davide Morelli +17.09.05 diff --git a/doc/maxlibnotes.txt b/doc/maxlibnotes.txt new file mode 100755 index 0000000..532ddf9 --- /dev/null +++ b/doc/maxlibnotes.txt @@ -0,0 +1,16 @@ +081005 + +-the most universally useful maxlib object has to be [borax], since it not only gives us velocity, pitch, duration, but also can detect the number of voices currently playing, various delta values, can count note-ons (incrementally), amongst other things. It's like a swiss army knife of music analysis, and I suspect this is where our live input (in davide's diagram) needs to make use of maxlib in order to derive what we will feed the chord extractor, then graph. + +-[gestalt] is for monophonic melodies only, but with [tilt], will detect the onset of melodies + +-[chord] is based on Robert Rowe's Machine Musicianship algorithms for chord detection, and will successfully detect most chords, but most importantly will give us the MIDI note number of the bass note, and its -class-. + +-[history] is interesting, because while it calculates the average value fed into its first inlet over N millisecond periods, it can also tell us the general tendency (up = 1, down = -1), which may be useful somewhere in the agent, to build more musical output. Also, [edge] is interesting because it detects falling and rising 'edges' in a sequence. + +-[iso] is interesting, because it can store and play sequences of MIDI notes -and- onsets in milliseconds, but of course we're once again talking about monophonic melodies, unless we run several in parallel somehow, but they'd have to 'know' about each other. + +-interestingly, [subst] performs what I see as the bit-shifting part of our current GA construction, and may actually help in understanding how we'd build a solderer agent. + +-[match] could be useful, because it matches incoming data against up to 16 creation arguments, and outputs a list; this might help in determining how closely the live player is playing against generated output or suggestions? just a thought. + diff --git a/doc/model.doc b/doc/model.doc new file mode 100755 index 0000000..af2fcc2 Binary files /dev/null and b/doc/model.doc differ diff --git a/doc/model.sxw b/doc/model.sxw new file mode 100755 index 0000000..b3b446f Binary files /dev/null and b/doc/model.sxw differ diff --git a/harmonizer.c b/harmonizer.c new file mode 100755 index 0000000..7277723 --- /dev/null +++ b/harmonizer.c @@ -0,0 +1,629 @@ +/* +harmonizer: +this external build voicing from a chord to another. +takes n voices (midi) as input +you can set the current chord +you can ask for next note of each voice to get to a target chord + +usefull to create chorals + +voicing is built using traditional GA (not co-evolving GA) + +voicing rules are hardcoded and are: +- no parallel 8ths nor 5ths +- better no hidden 8ths nor 5ths +- better if uniform voice spacing (except for the bass) +- better if little intervals +- no all voices same direction +- no voices outside limits +- better complete chords + +TODO: +would be nice to be able so set some rule at runtime +or at least set the importance of rules in realtime.. + + +*/ +#include +#include +#include +// for string manipulation +#include +#include +#include "m_pd.h" + +// to sort arrays +#include "sglib.h" + +#define MAX_POPULATION 100 + +#define DEF_PROB_MUTATION 0.03f + +#define VOICES 5 + +#define NOTES_RANGE 80 // this should be multiple of 16 +#define LOWER_POSSIBLE_NOTE 24 // lower note possible, it should be a C +#define POSSIBLE_NOTES (NOTES_RANGE/12*4) // 4 is the max number of notes in a chord + +// testing i noticed that we don't need more than 1 generation.. +// this is because we create an initial population that is really good +// we may not need to crossover at all! +#define GENERATIONS 1 + +#define DEBUG 0 // messaggi di debug +#define DEBUG_VERBOSE 0 // messaggi di debug + +static t_class *harmonizer_class; + +typedef enum { + kMaj=0, + kMin=1, + kDim=2, + kAug=3, + kDom7=4, + kMaj7=5, + kMin7=6, + kMinMaj7=7, + kDim7=8, + kHalfDim7=9 + } mode_t; + +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 + } note_t; + + + +// this defines a chord in a tonality +typedef struct _chord +{ + mode_t mode; + note_t note; +} chord_t; + + +typedef struct _harmonizer +{ + t_object x_obj; // myself + // genotypes + int population[MAX_POPULATION][VOICES]; + int current_voices[VOICES]; + chord_t current_chord; + chord_t target_chord; + int target_notes[POSSIBLE_NOTES]; + t_outlet *l_out; + +} t_harmonizer; + +// I build a table of possible notes +// are the notes (midi)that form target_chord +void build_possible_notes_table(t_harmonizer *x) +{ + int i, octave, basenote; + int n1, n2, n3, n4; + n1=n2=n3=n4=0; // there always is the fundamental + if (DEBUG_VERBOSE) + post("build_possible_notes_table target_chord.mode=%i target_chord.note=%i", x->target_chord.mode, x->target_chord.note); + switch (x->target_chord.mode) + { + case kMaj: n2=4; n3=7; n4=0;break; + case kMin: n2=3; n3=7; n4=0;break; + case kDim: n2=3; n3=6; n4=0;break; + case kAug: n2=4; n3=8; n4=0;break; + case kMaj7: n2=4; n3=7; n4=11;break; + case kDom7: n2=4; n3=7; n4=10;break; + case kMin7: n2=3; n3=7; n4=10;break; + case kHalfDim7: n2=3; n3=6; n4=10;break; + case kDim7: n2=3; n3=6; n4=9;break; + case kMinMaj7: n2=4; n3=7; n4=11;break; + } + if (DEBUG_VERBOSE) + post("build_possible_notes_table n2=%i n3=%i n4=%i", n2, n3, n4); + + basenote=0; + switch (x->target_chord.note) + { + case C: basenote=0;break; + case Db: basenote=1;break; + case D: basenote=2;break; + case Eb: basenote=3;break; + case E: basenote=4;break; + case F: basenote=5;break; + case Gb: basenote=6;break; + case G: basenote=7;break; + case Ab: basenote=8;break; + case A: basenote=9;break; + case Bb: basenote=10;break; + case B: basenote=11;break; + } + if (DEBUG_VERBOSE) + post("build_possible_notes_table basenote=%i", basenote); + i=0; + octave=0; + while (i<(POSSIBLE_NOTES-3)) + { + x->target_notes[i++]=octave*12 + LOWER_POSSIBLE_NOTE + basenote + n1; + x->target_notes[i++]=octave*12 + LOWER_POSSIBLE_NOTE + basenote + n2; + x->target_notes[i++]=octave*12 + LOWER_POSSIBLE_NOTE + basenote + n3; + x->target_notes[i++]=octave*12 + LOWER_POSSIBLE_NOTE + basenote + n4; + octave++; + } + if (DEBUG_VERBOSE) + { + i=0; + while (i<(POSSIBLE_NOTES)) + { + post("x->target_notes[%i]=%i", i, x->target_notes[i++]); + } + } +} + +// tries to find out absolute tones names in this string +note_t string2note(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; +} + +mode_t string2mode(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; + // TODO: other chords + // beware when adding new chords + // put shorter names at end of this function! + return C; +} + +// ----------------- normal external code ... + +void harmonizer_init_pop(t_harmonizer *x) +{ + int i, j, tmp, tmp2, k, steps, note, insertpoint; + double rnd; + for (i=0; ipopulation[i][j] = x->target_notes[tmp]; + */ + + // not totally random: i start from currend chord's notes + // and randomly go up or down + insertpoint = 0; + while ((insertpoint < POSSIBLE_NOTES) && (x->target_notes[insertpoint] < x->current_voices[j])) + insertpoint++; + if (insertpoint >= POSSIBLE_NOTES) + { + // i didn't find my insert point, possible? + // i pick a random one + rnd = rand()/((double)RAND_MAX + 1); + tmp = rnd * POSSIBLE_NOTES; + x->population[i][j] = x->target_notes[tmp]; + } else + { + // insert point found + rnd = rand()/((double)RAND_MAX + 1); + if (rnd < 0.5) + { + // i go up + rnd = rand()/((double)RAND_MAX + 1); + steps = rnd * 5; // how many step (good notes) will I ignore? + note = insertpoint + steps; + if (note >= POSSIBLE_NOTES) + note = POSSIBLE_NOTES-1; + + } else + { + // i go down + rnd = rand()/((double)RAND_MAX + 1); + steps = rnd * 5; // how many step (good notes) will I ignore? + note = insertpoint - steps; + if (note < 0) + note = 0; + } + // finally assign the note + x->population[i][j] = x->target_notes[note]; + } + } + } +} + + +void harmonizer_free(t_harmonizer *x) +{ +// freebytes(x->buf_strum1, sizeof(x->buf_strum1)); +// freebytes(x->buf_strum2, sizeof(x->buf_strum2)); +} + +// here i evaluate this voicing +int fitness(t_harmonizer *x, int *candidate) +{ + int i, j, tmp, res, last, avgHI, avgLOW; + short int transitions[VOICES]; + short int directions[VOICES]; + // intervals between voices + // for parallel and hidden 5ths + // voices spacing etc.. + short int intervals[VOICES][VOICES]; + short int notes[VOICES]; + res=50; // starting fitness + + if (DEBUG_VERBOSE) + post("evaluating fitness of %i %i %i %i", candidate[0], candidate[1], candidate[2], candidate[3]); + + // shared objects + for (i=0; icurrent_voices[i]; + if (transitions[i]!=0) + directions[i] = transitions[i]/abs(transitions[i]); + else + directions[i] = 0; + if (DEBUG_VERBOSE) + post("directions[%i]=%i", i, directions[i]); + + } + for (i=0; i