Commit b2a27d76 authored by Miller Puckette's avatar Miller Puckette
Browse files

first try for test8.

Protection against closing dirty abstractions
closing and quitting doesn't query if everyone is clean
(but "pd perf" message turns that protection back on for performances)
ctrl-1, etc, automatically place new object under a selected one and
connect to it
return when editing an object (not a message or comment) deactivates text
parent e04a0760
#N canvas 304 143 967 599 10; #N canvas 304 143 967 599 12;
#X obj 370 524 spigot; #X obj 358 544 spigot;
#X msg 442 397 bang; #X msg 442 397 bang;
#X obj 429 488 bonk~; #X obj 428 508 bonk~;
#X msg 442 214 learn 1; #X msg 442 214 learn 1;
#X msg 442 274 learn 0; #X msg 442 274 learn 0;
#X msg 437 456 print; #X msg 446 451 print;
#X obj 390 437 adc~; #X obj 390 437 adc~;
#X msg 614 538 \; pd dsp 1; #X msg 613 558 \; pd dsp 1;
#X obj 277 524 spigot; #X obj 276 544 spigot;
#N canvas 366 126 604 404 synth 0; #N canvas 366 126 604 404 synth 0;
#X obj 112 24 r bonk-cooked; #X obj 112 24 r bonk-cooked;
#X obj 112 49 unpack; #X obj 112 49 unpack;
...@@ -59,14 +59,14 @@ ...@@ -59,14 +59,14 @@
#X connect 21 0 1 0; #X connect 21 0 1 0;
#X connect 22 0 6 0; #X connect 22 0 6 0;
#X connect 23 0 9 0; #X connect 23 0 9 0;
#X restore 846 555 pd synth; #X restore 845 575 pd synth;
#X floatatom 846 532 0 0 0 0 - - -; #X floatatom 845 552 0 0 0 0 - - -;
#X msg 846 502 0; #X msg 845 522 0;
#X msg 442 244 learn 10; #X msg 442 244 learn 10;
#X msg 442 304 forget; #X msg 442 304 forget;
#X msg 442 334 write templates.txt; #X msg 442 334 write templates.txt;
#X msg 442 364 read templates.txt; #X msg 442 364 read templates.txt;
#X msg 877 502 90; #X msg 876 522 90;
#X msg 442 81 thresh 6 50; #X msg 442 81 thresh 6 50;
#X text 8 56 The Bonk object takes an audio signal input and looks #X text 8 56 The Bonk object takes an audio signal input and looks
for "attacks" defined as sharp changes in the spectral envelope of for "attacks" defined as sharp changes in the spectral envelope of
...@@ -75,13 +75,13 @@ Bonk check the attack against a collection of stored templates to try ...@@ -75,13 +75,13 @@ Bonk check the attack against a collection of stored templates to try
to guess which of two or more instruments was hit. Bonk is described to guess which of two or more instruments was hit. Bonk is described
theoretically in the 1998 ICMC proceedings \, reprinted on http://man104nfs.ucsd.edu/~mpuckett. theoretically in the 1998 ICMC proceedings \, reprinted on http://man104nfs.ucsd.edu/~mpuckett.
; ;
#X text 602 504 click here; #X text 601 524 click here;
#X text 603 517 to start DSP; #X text 602 537 to start DSP;
#X text 8 377 In this patch \, after starting DSP \, you can print #X text 8 377 In this patch \, after starting DSP \, you can print
out the raw or cooked output using the two "spigots" or listen to a out the raw or cooked output using the two "spigots" or listen to a
synthesizer output by raising its volume.; synthesizer output by raising its volume.;
#X text 747 501 output volume; #X text 746 521 output volume;
#X text 761 519 (0-100); #X text 760 539 (0-100);
#X msg 442 150 mask 4 0.7; #X msg 442 150 mask 4 0.7;
#X msg 442 184 debounce 0; #X msg 442 184 debounce 0;
#X text 8 309 Bonk's analysis is carried out on a 256-point window #X text 8 309 Bonk's analysis is carried out on a 256-point window
...@@ -89,7 +89,7 @@ synthesizer output by raising its volume.; ...@@ -89,7 +89,7 @@ synthesizer output by raising its volume.;
The analysis period can be specified as Bonk's creation argument but The analysis period can be specified as Bonk's creation argument but
must be a multiple of 64; must be a multiple of 64;
#X text 532 185 Minimum time (msec) between attacks; #X text 532 185 Minimum time (msec) between attacks;
#X text 532 140 Describes how energy in each frequency band masks later #X text 526 136 Describes how energy in each frequency band masks later
energy in the band. Here the masking is total for 4 analysis periods energy in the band. Here the masking is total for 4 analysis periods
and then drops by 0.7 each period.; and then drops by 0.7 each period.;
#X text 530 214 Forget all templates and start learning new ones. The #X text 530 214 Forget all templates and start learning new ones. The
...@@ -101,22 +101,22 @@ to erase and record over a template.; ...@@ -101,22 +101,22 @@ to erase and record over a template.;
#X text 595 334 Write templates to a file in text-editable format. #X text 595 334 Write templates to a file in text-editable format.
; ;
#X text 596 364 Read templates from a file.; #X text 596 364 Read templates from a file.;
#X text 538 453 Print out all settings and templates.; #X text 514 450 Print out all settings and templates.;
#X msg 442 120 minvel 10; #X msg 442 120 minvel 10;
#X text 538 392 Poll the current spectrum via "raw" outlet \, You can #X text 514 392 Poll the current spectrum via "raw" outlet \, You can
set a very high threshold if you don't want attacks mixed in.; set a very high threshold if you don't want attacks mixed in.;
#X msg 437 426 debug 0; #X msg 437 426 debug 0;
#X text 538 426 turn debugging on or off.; #X text 514 426 turn debugging on or off.;
#X obj 326 525 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 #X obj 325 545 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
1; 1;
#X obj 419 525 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 #X obj 407 545 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
1; 1;
#X obj 370 554 print cooked; #X obj 358 574 print cooked;
#X obj 277 554 print raw; #X obj 276 574 print raw;
#X text 253 490 enable printout:; #X text 158 545 enable printout:;
#X text 533 121 Minimum "velocity" to output (quieter notes are ignored.) #X text 530 121 Minimum "velocity" to output (quieter notes are ignored.)
; ;
#X obj 462 513 s bonk-cooked; #X obj 461 533 s bonk-cooked;
#X text 218 12 BONK~ - an attack detector for small percussion instruments #X text 218 12 BONK~ - an attack detector for small percussion instruments
; ;
#X text 8 174 Bonk's two outputs are the raw spectrum of the attack #X text 8 174 Bonk's two outputs are the raw spectrum of the attack
...@@ -127,12 +127,14 @@ an instrument number (counting up from zero) and a "velocity". This ...@@ -127,12 +127,14 @@ an instrument number (counting up from zero) and a "velocity". This
bands \, normalized so that 100 is an attack of amplitude of about bands \, normalized so that 100 is an attack of amplitude of about
1 The instrument number is significant only if Bonk has a "template 1 The instrument number is significant only if Bonk has a "template
set" in memory.; set" in memory.;
#X text 532 52 Set low and high thresholds. Signal growth must exceed #X text 531 47 Set low and high thresholds. Signal growth must exceed
the high one and then fall to the low one to make an attack. The unit the high one and then fall to the low one to make an attack. The unit
is the sum of the proportional growth in the 11 filter bands. Proportional is the sum of the proportional growth in the 11 filter bands. Proportional
growth is essentially the logarithmic time derivative.; growth is essentially the logarithmic time derivative.;
#X text 238 27 (NOTE: this documentation does not yet describe new #X text 238 27 (NOTE: this documentation does not yet describe new
features for Pd 0.42).; features for Pd 0.42).;
#X msg 446 479 print 1;
#X text 514 481 print out filterbank settings;
#X connect 0 0 40 0; #X connect 0 0 40 0;
#X connect 1 0 2 0; #X connect 1 0 2 0;
#X connect 2 0 8 0; #X connect 2 0 8 0;
...@@ -157,3 +159,4 @@ features for Pd 0.42).; ...@@ -157,3 +159,4 @@ features for Pd 0.42).;
#X connect 36 0 2 0; #X connect 36 0 2 0;
#X connect 38 0 8 1; #X connect 38 0 8 1;
#X connect 39 0 0 1; #X connect 39 0 0 1;
#X connect 49 0 2 0;
...@@ -95,6 +95,7 @@ static t_class *bonk_class; ...@@ -95,6 +95,7 @@ static t_class *bonk_class;
#define DEFHALFTONES 6 #define DEFHALFTONES 6
#define DEFOVERLAP 1 #define DEFOVERLAP 1
#define DEFFIRSTBIN 1 #define DEFFIRSTBIN 1
#define DEFMINBANDWIDTH 1.5
#define DEFHITHRESH 5 #define DEFHITHRESH 5
#define DEFLOTHRESH 2.5 #define DEFLOTHRESH 2.5
#define DEFMASKTIME 4 #define DEFMASKTIME 4
...@@ -122,6 +123,7 @@ typedef struct _filterbank ...@@ -122,6 +123,7 @@ typedef struct _filterbank
float b_halftones; /* filter bandwidth in halftones */ float b_halftones; /* filter bandwidth in halftones */
float b_overlap; /* overlap; default 1 for 1/2-power pts */ float b_overlap; /* overlap; default 1 for 1/2-power pts */
float b_firstbin; /* freq of first filter in bins, default 1 */ float b_firstbin; /* freq of first filter in bins, default 1 */
float b_minbandwidth; /* minimum bandwidth, default 1.5 */
t_filterkernel *b_vec; /* filter kernels */ t_filterkernel *b_vec; /* filter kernels */
int b_refcount; /* number of bonk~ objects using this */ int b_refcount; /* number of bonk~ objects using this */
struct _filterbank *b_next; /* next in linked list */ struct _filterbank *b_next; /* next in linked list */
...@@ -206,7 +208,7 @@ typedef struct _bonk ...@@ -206,7 +208,7 @@ typedef struct _bonk
float x_halftones; /* nominal halftones between filters */ float x_halftones; /* nominal halftones between filters */
float x_overlap; float x_overlap;
float x_firstbin; float x_firstbin;
float x_minbandwidth;
float x_hithresh; /* threshold for total growth to trigger */ float x_hithresh; /* threshold for total growth to trigger */
float x_lothresh; /* threshold for total growth to re-arm */ float x_lothresh; /* threshold for total growth to re-arm */
float x_minvel; /* minimum velocity we output */ float x_minvel; /* minimum velocity we output */
...@@ -275,10 +277,11 @@ char *strcpy(char *s1, const char *s2); ...@@ -275,10 +277,11 @@ char *strcpy(char *s1, const char *s2);
static void bonk_tick(t_bonk *x); static void bonk_tick(t_bonk *x);
#define HALFWIDTH 0.75 /* half peak bandwidth at half power point in bins */ #define HALFWIDTH 0.75 /* half peak bandwidth at half power point in bins */
#define SLIDE 0.25 /* relative slide between filter subwindows */
static t_filterbank *bonk_newfilterbank(int npoints, int nfilters, static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
float halftones, float overlap, float firstbin) float halftones, float overlap, float firstbin, float minbandwidth)
{ {
int i, j; int i, j;
float cf, bw, h, relspace; float cf, bw, h, relspace;
...@@ -288,6 +291,7 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters, ...@@ -288,6 +291,7 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
b->b_halftones = halftones; b->b_halftones = halftones;
b->b_overlap = overlap; b->b_overlap = overlap;
b->b_firstbin = firstbin; b->b_firstbin = firstbin;
b->b_minbandwidth = minbandwidth;
b->b_refcount = 0; b->b_refcount = 0;
b->b_next = bonk_filterbanklist; b->b_next = bonk_filterbanklist;
bonk_filterbanklist = b; bonk_filterbanklist = b;
...@@ -296,17 +300,21 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters, ...@@ -296,17 +300,21 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
h = exp((log(2.)/12.)*halftones); /* specced interval between filters */ h = exp((log(2.)/12.)*halftones); /* specced interval between filters */
relspace = (h - 1)/(h + 1); /* nominal spacing-per-f for fbank */ relspace = (h - 1)/(h + 1); /* nominal spacing-per-f for fbank */
if (minbandwidth < 2*HALFWIDTH)
minbandwidth = 2*HALFWIDTH;
if (firstbin < minbandwidth/(2*HALFWIDTH))
firstbin = minbandwidth/(2*HALFWIDTH);
cf = firstbin; cf = firstbin;
bw = cf * relspace * overlap; bw = cf * relspace * overlap;
if (bw < HALFWIDTH) if (bw < (0.5*minbandwidth))
bw = HALFWIDTH; bw = (0.5*minbandwidth);
for (i = 0; i < nfilters; i++) for (i = 0; i < nfilters; i++)
{ {
float *fp, newcf, newbw; float *fp, newcf, newbw;
float normalizer = 0; float normalizer = 0;
int filterpoints, skippoints, hoppoints, nhops; int filterpoints, skippoints, hoppoints, nhops;
filterpoints = 0.5 + npoints * HALFWIDTH/bw; filterpoints = npoints * HALFWIDTH/bw;
if (cf > npoints/2) if (cf > npoints/2)
{ {
post("bonk~: only using %d filters (ran past Nyquist)", i+1); post("bonk~: only using %d filters (ran past Nyquist)", i+1);
...@@ -320,7 +328,7 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters, ...@@ -320,7 +328,7 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
else if (filterpoints > npoints) else if (filterpoints > npoints)
filterpoints = npoints; filterpoints = npoints;
hoppoints = 0.5 + 0.5 * npoints * HALFWIDTH/bw; hoppoints = SLIDE * npoints * HALFWIDTH/bw;
nhops = 1. + (npoints-filterpoints)/(float)hoppoints; nhops = 1. + (npoints-filterpoints)/(float)hoppoints;
skippoints = 0.5 * (npoints-filterpoints - (nhops-1) * hoppoints); skippoints = 0.5 * (npoints-filterpoints - (nhops-1) * hoppoints);
...@@ -343,7 +351,7 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters, ...@@ -343,7 +351,7 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
fp[1] = window * sin(phase); fp[1] = window * sin(phase);
normalizer += window; normalizer += window;
} }
normalizer = 1/(normalizer * nhops); normalizer = 1/(normalizer * sqrt(nhops));
for (fp = b->b_vec[i].k_stuff, j = 0; for (fp = b->b_vec[i].k_stuff, j = 0;
j < filterpoints; j++, fp+= 2) j < filterpoints; j++, fp+= 2)
fp[0] *= normalizer, fp[1] *= normalizer; fp[0] *= normalizer, fp[1] *= normalizer;
...@@ -353,10 +361,10 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters, ...@@ -353,10 +361,10 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
#endif #endif
newcf = (cf + bw/overlap)/(1 - relspace); newcf = (cf + bw/overlap)/(1 - relspace);
newbw = newcf * overlap * relspace; newbw = newcf * overlap * relspace;
if (newbw < HALFWIDTH) if (newbw < 0.5*minbandwidth)
{ {
newbw = HALFWIDTH; newbw = 0.5*minbandwidth;
newcf = cf + 2 * HALFWIDTH / overlap; newcf = cf + minbandwidth / overlap;
} }
cf = newcf; cf = newcf;
bw = newbw; bw = newbw;
...@@ -387,7 +395,7 @@ static void bonk_freefilterbank(t_filterbank *b) ...@@ -387,7 +395,7 @@ static void bonk_freefilterbank(t_filterbank *b)
static void bonk_donew(t_bonk *x, int npoints, int period, int nsig, static void bonk_donew(t_bonk *x, int npoints, int period, int nsig,
int nfilters, float halftones, float overlap, float firstbin, int nfilters, float halftones, float overlap, float firstbin,
float samplerate) float minbandwidth, float samplerate)
{ {
int i, j; int i, j;
t_hist *h; t_hist *h;
...@@ -440,7 +448,8 @@ static void bonk_donew(t_bonk *x, int npoints, int period, int nsig, ...@@ -440,7 +448,8 @@ static void bonk_donew(t_bonk *x, int npoints, int period, int nsig,
fb->b_halftones == x->x_halftones && fb->b_halftones == x->x_halftones &&
fb->b_firstbin == firstbin && fb->b_firstbin == firstbin &&
fb->b_overlap == overlap && fb->b_overlap == overlap &&
fb->b_npoints == x->x_npoints) fb->b_npoints == x->x_npoints &&
fb->b_minbandwidth == minbandwidth)
{ {
fb->b_refcount++; fb->b_refcount++;
x->x_filterbank = fb; x->x_filterbank = fb;
...@@ -448,7 +457,7 @@ static void bonk_donew(t_bonk *x, int npoints, int period, int nsig, ...@@ -448,7 +457,7 @@ static void bonk_donew(t_bonk *x, int npoints, int period, int nsig,
} }
if (!x->x_filterbank) if (!x->x_filterbank)
x->x_filterbank = bonk_newfilterbank(npoints, nfilters, x->x_filterbank = bonk_newfilterbank(npoints, nfilters,
halftones, overlap, firstbin), halftones, overlap, firstbin, minbandwidth),
x->x_filterbank->b_refcount++; x->x_filterbank->b_refcount++;
} }
...@@ -1035,7 +1044,7 @@ static void *bonk_new(t_symbol *s, int argc, t_atom *argv) ...@@ -1035,7 +1044,7 @@ static void *bonk_new(t_symbol *s, int argc, t_atom *argv)
int nsig = 1, period = DEFPERIOD, npts = DEFNPOINTS, int nsig = 1, period = DEFPERIOD, npts = DEFNPOINTS,
nfilters = DEFNFILTERS, j; nfilters = DEFNFILTERS, j;
float halftones = DEFHALFTONES, overlap = DEFOVERLAP, float halftones = DEFHALFTONES, overlap = DEFOVERLAP,
firstbin = DEFFIRSTBIN; firstbin = DEFFIRSTBIN, minbandwidth = DEFMINBANDWIDTH;
t_insig *g; t_insig *g;
if (argc > 0 && argv[0].a_type == A_FLOAT) if (argc > 0 && argv[0].a_type == A_FLOAT)
...@@ -1082,6 +1091,11 @@ static void *bonk_new(t_symbol *s, int argc, t_atom *argv) ...@@ -1082,6 +1091,11 @@ static void *bonk_new(t_symbol *s, int argc, t_atom *argv)
firstbin = atom_getfloatarg(1, argc, argv); firstbin = atom_getfloatarg(1, argc, argv);
argc -= 2; argv += 2; argc -= 2; argv += 2;
} }
else if (!strcmp(firstarg->s_name, "-minbandwidth") && argc > 1)
{
minbandwidth = atom_getfloatarg(1, argc, argv);
argc -= 2; argv += 2;
}
else if (!strcmp(firstarg->s_name, "-spew") && argc > 1) else if (!strcmp(firstarg->s_name, "-spew") && argc > 1)
{ {
x->x_spew = (atom_getfloatarg(1, argc, argv) != 0); x->x_spew = (atom_getfloatarg(1, argc, argv) != 0);
...@@ -1123,7 +1137,7 @@ static void *bonk_new(t_symbol *s, int argc, t_atom *argv) ...@@ -1123,7 +1137,7 @@ static void *bonk_new(t_symbol *s, int argc, t_atom *argv)
} }
x->x_cookedout = outlet_new(&x->x_obj, gensym("list")); x->x_cookedout = outlet_new(&x->x_obj, gensym("list"));
bonk_donew(x, npts, period, nsig, nfilters, halftones, overlap, bonk_donew(x, npts, period, nsig, nfilters, halftones, overlap,
firstbin, sys_getsr()); firstbin, minbandwidth, sys_getsr());
return (x); return (x);
} }
...@@ -1182,6 +1196,9 @@ int main() ...@@ -1182,6 +1196,9 @@ int main()
attr = attr_offset_new("firstbin", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_firstbin)); attr = attr_offset_new("firstbin", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_firstbin));
class_addattr(c, attr); class_addattr(c, attr);
attr = attr_offset_new("minbandwidth", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_minbandwidth));
class_addattr(c, attr);
attr = attr_offset_new("minvel", sym_float32, attrflags, (method)0L, (method)bonk_minvel_set, calcoffset(t_bonk, x_minvel)); attr = attr_offset_new("minvel", sym_float32, attrflags, (method)0L, (method)bonk_minvel_set, calcoffset(t_bonk, x_minvel));
class_addattr(c, attr); class_addattr(c, attr);
...@@ -1250,6 +1267,7 @@ static void *bonk_new(t_symbol *s, long ac, t_atom *av) ...@@ -1250,6 +1267,7 @@ static void *bonk_new(t_symbol *s, long ac, t_atom *av)
x->x_nfilters = DEFNFILTERS; x->x_nfilters = DEFNFILTERS;
x->x_halftones = DEFHALFTONES; x->x_halftones = DEFHALFTONES;
x->x_firstbin = DEFFIRSTBIN; x->x_firstbin = DEFFIRSTBIN;
x->x_minbandwidth = DEFMINBANDWIDTH;
x->x_overlap = DEFOVERLAP; x->x_overlap = DEFOVERLAP;
x->x_ninsig = 1; x->x_ninsig = 1;
...@@ -1305,7 +1323,8 @@ static void *bonk_new(t_symbol *s, long ac, t_atom *av) ...@@ -1305,7 +1323,8 @@ static void *bonk_new(t_symbol *s, long ac, t_atom *av)
x->x_clock = clock_new(x, (method)bonk_tick); x->x_clock = clock_new(x, (method)bonk_tick);
bonk_donew(x, x->x_npoints, x->x_period, x->x_ninsig, x->x_nfilters, bonk_donew(x, x->x_npoints, x->x_period, x->x_ninsig, x->x_nfilters,
x->x_halftones, x->x_overlap, x->x_firstbin, sys_getsr()); x->x_halftones, x->x_overlap, x->x_firstbin, x->x_minbandwidth,
sys_getsr());
} }
return (x); return (x);
} }
......
...@@ -602,7 +602,8 @@ void canvas_reflecttitle(t_canvas *x) ...@@ -602,7 +602,8 @@ void canvas_reflecttitle(t_canvas *x)
canvas_getdir(x)->s_name); canvas_getdir(x)->s_name);
} }
void canvas_dirty(t_canvas *x, t_int n) /* mark a glist dirty or clean */
void canvas_dirty(t_canvas *x, t_floatarg n)
{ {
t_canvas *x2 = canvas_getrootfor(x); t_canvas *x2 = canvas_getrootfor(x);
if (glist_amreloadingabstractions) if (glist_amreloadingabstractions)
...@@ -686,7 +687,7 @@ void glist_menu_open(t_glist *x) ...@@ -686,7 +687,7 @@ void glist_menu_open(t_glist *x)
{ {
t_glist *gl2 = x->gl_owner; t_glist *gl2 = x->gl_owner;
if (!gl2) if (!gl2)
bug("canvas_vis"); /* shouldn't happen but don't get too upset. */ bug("glist_menu_open"); /* shouldn't happen but not dangerous */
else else
{ {
/* erase ourself in parent window */ /* erase ourself in parent window */
...@@ -735,7 +736,8 @@ void canvas_free(t_canvas *x) ...@@ -735,7 +736,8 @@ void canvas_free(t_canvas *x)
glist_noselect(x); glist_noselect(x);
while (y = x->gl_list) while (y = x->gl_list)
glist_delete(x, y); glist_delete(x, y);
canvas_vis(x, 0); if (x == glist_getcanvas(x))
canvas_vis(x, 0);
if (strcmp(x->gl_name->s_name, "Pd")) if (strcmp(x->gl_name->s_name, "Pd"))
pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name)); pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name));
...@@ -1549,6 +1551,8 @@ void g_canvas_setup(void) ...@@ -1549,6 +1551,8 @@ void g_canvas_setup(void)
gensym("menu-open"), A_NULL); gensym("menu-open"), A_NULL);
class_addmethod(canvas_class, (t_method)canvas_map, class_addmethod(canvas_class, (t_method)canvas_map,
gensym("map"), A_FLOAT, A_NULL); gensym("map"), A_FLOAT, A_NULL);
class_addmethod(canvas_class, (t_method)canvas_dirty,
gensym("dirty"), A_FLOAT, A_NULL);
class_setpropertiesfn(canvas_class, (t_propertiesfn)canvas_properties); class_setpropertiesfn(canvas_class, (t_propertiesfn)canvas_properties);
/* ---------------------- list handling ------------------------ */ /* ---------------------- list handling ------------------------ */
......
...@@ -467,7 +467,7 @@ EXTERN void canvas_setcurrent(t_canvas *x); ...@@ -467,7 +467,7 @@ EXTERN void canvas_setcurrent(t_canvas *x);
EXTERN void canvas_unsetcurrent(t_canvas *x); EXTERN void canvas_unsetcurrent(t_canvas *x);
EXTERN t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s); EXTERN t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s);
EXTERN t_canvas *canvas_getrootfor(t_canvas *x); EXTERN t_canvas *canvas_getrootfor(t_canvas *x);
EXTERN void canvas_dirty(t_canvas *x, t_int n); EXTERN void canvas_dirty(t_canvas *x, t_floatarg n);
EXTERN int canvas_getfont(t_canvas *x); EXTERN int canvas_getfont(t_canvas *x);
typedef int (*t_canvasapply)(t_canvas *x, t_int x1, t_int x2, t_int x3); typedef int (*t_canvasapply)(t_canvas *x, t_int x1, t_int x2, t_int x3);
......
...@@ -27,6 +27,7 @@ static void canvas_clearline(t_canvas *x); ...@@ -27,6 +27,7 @@ static void canvas_clearline(t_canvas *x);
static t_binbuf *copy_binbuf; static t_binbuf *copy_binbuf;
static char *canvas_textcopybuf; static char *canvas_textcopybuf;
static int canvas_textcopybufsize; static int canvas_textcopybufsize;
static t_glist *glist_finddirty(t_glist *x);
/* ---------------- generic widget behavior ------------------------- */ /* ---------------- generic widget behavior ------------------------- */
...@@ -166,12 +167,26 @@ void glist_select(t_glist *x, t_gobj *y) ...@@ -166,12 +167,26 @@ void glist_select(t_glist *x, t_gobj *y)
} }
} }
/* recursively deselect everything in a gobj "g", if it happens to be
a glist, in preparation for deselecting g itself in glist_dselect() */
static void glist_checkanddeselectall(t_glist *gl, t_gobj *g)
{
t_glist *gl2;
t_gobj *g2;
if (pd_class(&g->g_pd) != canvas_class)
return;
gl2 = (t_glist *)g;
for (g2 = gl2->gl_list; g2; g2 = g2->g_next)
glist_checkanddeselectall(gl2, g2);
glist_noselect(gl2);
}
/* call this for selected objects only */ /* call this for selected objects only */
void glist_deselect(t_glist *x, t_gobj *y) void glist_deselect(t_glist *x, t_gobj *y)
{ {
int fixdsp = 0; int fixdsp = 0;
static int reenter = 0; static int reenter = 0;
if (reenter) return; /* if (reenter) return; */
reenter = 1; reenter = 1;
if (x->gl_editor) if (x->gl_editor)
{ {
...@@ -187,6 +202,7 @@ void glist_deselect(t_glist *x, t_gobj *y) ...@@ -187,6 +202,7 @@ void glist_deselect(t_glist *x, t_gobj *y)
{ {
z = fuddy; z = fuddy;
canvas_stowconnections(glist_getcanvas(x)); canvas_stowconnections(glist_getcanvas(x));
glist_checkanddeselectall(x, y);
} }
gobj_activate(y, x, 0); gobj_activate(y, x, 0);
} }
...@@ -1537,7 +1553,7 @@ static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit) ...@@ -1537,7 +1553,7 @@ static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)
else hiy = x->gl_editor->e_ywas, loy = ypos; else hiy = x->gl_editor->e_ywas, loy = ypos;
canvas_selectinrect(x, lox, loy, hix, hiy); canvas_selectinrect(x, lox, loy, hix, hiy);
sys_vgui(".x%lx.c delete x\n", x); sys_vgui(".x%lx.c delete x\n", x);
x->gl_editor->e_onmotion = 0; x->gl_editor->e_onmotion = MA_NONE;
} }
else sys_vgui(".x%lx.c coords x %d %d %d %d\n", else sys_vgui(".x%lx.c coords x %d %d %d %d\n",
x, x->gl_editor->e_xwas, x, x->gl_editor->e_xwas,
...@@ -1568,8 +1584,25 @@ void canvas_mouseup(t_canvas *x, ...@@ -1568,8 +1584,25 @@ void canvas_mouseup(t_canvas *x,
/* after motion, if there's only one item selected, activate it */ /* after motion, if there's only one item selected, activate it */
if (x->gl_editor->e_selection && if (x->gl_editor->e_selection &&
!(x->gl_editor->e_selection->sel_next)) !(x->gl_editor->e_selection->sel_next))
gobj_activate(x->gl_editor->e_selection->sel_what, {
x, 1); t_gobj *g = x->gl_editor->e_selection->sel_what;
t_glist *gl2;
/* first though, check we aren't an abstraction with a
dirty sub-patch that would be discarded if we edit this. */
if (pd_class(&g->g_pd) == canvas_class &&
canvas_isabstraction((t_glist *)g) &&
(gl2 = glist_finddirty((t_glist *)g)))
{
vmess(&gl2->gl_pd, gensym("menu-open"), "");
x->gl_editor->e_onmotion = MA_NONE;
sys_vgui(
"pdtk_check {Discard changes to '%s'?} {.x%lx dirty 0;\n} no\n",
canvas_getrootfor(gl2)->gl_name->s_name, gl2);
return;
}
/* OK, activate it */
gobj_activate(x->gl_editor->e_selection->sel_what, x, 1);
}
} }
if (x->gl_editor->e_onmotion != MA_NONE) if (x->gl_editor->e_onmotion != MA_NONE)
sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", x); sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", x);
...@@ -1597,7 +1630,8 @@ static void canvas_displaceselection(t_canvas *x, int dx, int dy) ...@@ -1597,7 +1630,8 @@ static void canvas_displaceselection(t_canvas *x, int dx, int dy)
} }
if (resortin) canvas_resortinlets(x); if (resortin) canvas_resortinlets(x);