Hi Jamie,
Nice to hear from you. I've been having fun with a_grain, and I'm beginning to understand it a little better.
I've attached what I've done with it so far, which uses the no-wire/message method I described below. I don't think this is always the best way to go about this (see discussion on this thread), but I'm finding it useful in this case, because it makes a complex patch with many inputs easier to read. Also included is the test runner (derived from yours), which shows how the message sending objects (number boxes and toggles) send to prefix-tagged destinations in the a_grain abstraction (the prefix being passed to a_grain as its third argument).
I've only begun to "untangle" a_grain, as you can see. But at least I
can see what the various parts are doing at this point. Very cool.
I've only included the two files I changed, by the way. Anyone else
wanting to actually try these changes needs to get the original folder from
http://www.puredata.org/Members/jb/a_grain%7E/view
then copy a_grain~.pd over its namesake, and add a_grainTest.pd to the folder and run it.
I'm enjoying a_grain and it's helping me understand granular synthesis à la PD; thank you for making it and putting it out there.
Phil Stone UC Davis
Jamie Bullock wrote:
On Thu, 2006-10-12 at 11:24 -0700, Phil Stone wrote:
I've been playing with Jamie Bullock's 'a_grain' lately (see http://www.puredata.org/Members/jb/a_grain%7E/view ), and in order to understand it better, I've been refactoring it.
Great news!
A_grain has 14 inputs to control various parameters; my first approach to cleaning it up was to put all the inlets, in the correct order, at the top of the patch -- I then connected those inlets to 'send' objects with $0 variables, placing matching 'receive's close by where they are needed. This really cleaned up the wiring quite a bit, and made it easier to "read".
Looking back at it I'm really glad I provided some documentation. The patch is a complete mess!
Now it occurs to me that I could eliminate the inlets entirely, and just write to send/receive pairs directly (perhaps also passing in a "prefix" as an argument that is prepended to all receives inside the abstraction, which would allow multiple instantiations of the abstraction, with independent control of each). At the UI-level patch, I could use named senders (from number boxes, sliders, whatever) just hovering nearby the a_grain abstraction; no wires, no mess.
The approach I am starting to use for all of my abstractions is to have the first inlet, and first outlet for all control-rate data, and use messages containing key/value tuples passed to [route] objects inside the abstraction to pass data in. What I like about this that once everything is connected up it is easy to see what parameters the data coming into the abstraction correspond to. It is also possible to implement a kind of pseudo-inheritance by nesting abstractions that implement this approach.
Anyhow, I look forward to seeing the results of your refactoring. I think it will inspire me to add some more features to [a_grain~] as per the TODO...
best,
Jamie
I'm wondering what experienced PD architects consider the best practice here; if the second approach is better, I begin to question the advisability of wired inlets for more than two or three arguments. The left-to-right ordering of them, along with the rats-nest wiring caused by high numbers of inputs, seem to argue against them. The only downside I can see to the second method is that if it's not done neatly, i.e., the senders are placed indiscriminately and not necessarily near the abstraction they're sending to -- it could become very hard to understand/maintain the patch.
I'll be interested to hear other PD user's thoughts on this.
Phil Stone UC Davis
PD-list@iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
#N canvas 571 140 520 531 10; #X floatatom 141 313 10 0 0 0 - - -; #X obj 274 6 nbx 5 14 -10 1000 0 1 ag1-ptrhopsize empty ptrhopsize(s) 60 5 0 10 -225271 -1 -1 -5 256; #X obj 274 23 nbx 5 14 0 100 0 1 ag1-ptrdeviation empty ptrdeviation() 60 5 0 10 -225271 -1 -1 5 256; #X obj 274 40 nbx 5 14 0 100 0 1 ag1-ptrdeviationsmear empty ptrdeviationsmear(ms) 60 5 0 10 -225271 -1 -1 7 256; #X obj 274 57 nbx 5 14 0 1000 0 1 ag1-ptrdrunkinc empty ptrdrunkinc(ms) 60 5 0 10 -225271 -1 -1 0 256; #X obj 274 80 nbx 5 14 0 4000 0 1 ag1-grainreadrate empty grainreadrate(Hz) 60 5 0 10 -225271 -1 -1 157 256; #X obj 274 97 nbx 5 14 0 1000 0 1 ag1-grainsize empty grainsize(ms) 60 5 0 10 -225271 -1 -1 11 256; #X obj 274 114 nbx 5 14 0 100 0 1 ag1-grainsizedeviation empty grainsizedeviation() 60 5 0 10 -225271 -1 -1 0 256; #X obj 274 131 nbx 5 14 0 1000 0 1 ag1-grdevsmear empty graindeviationsmear(ms) 60 5 0 10 -225271 -1 -1 0 256; #X obj 274 148 nbx 5 14 0 1000 0 1 ag1-grdrunkinc empty graindrunkinc(ms) 60 5 0 10 -225271 -1 -1 0 256; #X obj 274 165 nbx 5 14 0 100 0 1 ag1-grainamp empty grainamp(0-100) 60 5 0 10 -225271 -1 -1 52 256; #X obj 274 194 nbx 5 14 0 100 0 1 ag1-tabstrtoff empty tabstartoffset(+%) 60 5 0 10 -225271 -1 -1 4 256; #X obj 274 211 nbx 5 14 -100 0 0 1 ag1-tabendoff empty tabendoffset(-%) 60 5 0 10 -225271 -1 -1 0 256; #X obj 58 260 tgl 15 0 empty empty ON/OFF 0 -6 0 8 -225271 -1 -1 0 1; #N canvas 37 82 520 561 tables 0; #N canvas 0 22 450 300 (subpatch) 0; #X array env 100 float 1; #A 0 0 0.000986636 0.00394267 0.00885636 0.0157084 0.0244717 0.0351118 0.0475865 0.0618467 0.0778361 0.0954915 0.114743 0.135516 0.157727 0.181288 0.206107 0.232087 0.259123 0.287111 0.315938 0.345492 0.375655 0.406309 0.437333 0.468605 0.5 0.531395 0.562667 0.593691 0.624345 0.654509 0.684062 0.71289 0.740877 0.767913 0.793893 0.818712 0.842274 0.864484 0.885257 0.904509 0.922164 0.938153 0.952414 0.964888 0.975528 0.984292 0.991144 0.996057 0.999013 1 0.999013 0.996057 0.991144 0.984292 0.975528 0.964888 0.952414 0.938153 0.922164 0.904508 0.885257 0.864484 0.842274 0.818712 0.793893 0.767913 0.740877 0.71289 0.684062 0.654508 0.624345 0.593691 0.562667 0.531395 0.5 0.468605 0.437333 0.406309 0.375655 0.345491 0.315938 0.28711 0.259123 0.232087 0.206107 0.181288 0.157726 0.135516 0.114743 0.0954915 0.077836 0.0618466 0.0475864 0.0351117 0.0244717 0.0157084 0.00885636 0.00394264 0.000986636; #X coords 0 1 99 0 150 100 1; #X restore 79 63 graph; #N canvas 0 22 450 300 (subpatch) 0; #X array samp 97088 float 0; #X coords 0 1 97087 -1 150 100 1; #X restore 79 189 graph; #N canvas 0 22 450 300 (subpatch) 0; #X array samp2 105536 float 0; #X coords 0 1 105535 -1 150 100 1; #X restore 79 322 graph; #X obj 238 138 soundfiler; #X obj 238 53 select 0 1; #X obj 238 30 inlet; #X msg 269 106 read -resize 2.wav samp2; #X obj 238 167 s $0-tabsize; #X msg 238 79 read -resize 1.wav samp; #X connect 3 0 7 0; #X connect 4 0 8 0; #X connect 4 1 6 0; #X connect 5 0 4 0; #X connect 6 0 3 0; #X connect 8 0 3 0; #X restore 73 82 pd tables; #N canvas 0 22 450 428 README 0; #X text 5 11 The two arguments to the a_grain~ abstraction give the enveloping function table name and the sample data table name respectively. The first outlet is th audio output , the second outputs the current read position. The abstraction works by reading audio 'grains' of grainsize from a selection delimited by 0 + tabstartoffset and tabsize + tabendoffset (where tabendoffset should be negative). A table lookup for each grain is performed by a phasor that runs at grainreadrate. After each successive grain has been read , the starting point for the table lookup is incremented by ptrhopsize. A windowing function is applied to each grain , and some control over the steepness of this function is given by grainamp. Random offsets can be introduced for the point from which the grain is read (ptrdeviation) , and the grainsize (grainsizedeviation). These are expressed as a percentage of the original value. Both offsets have a smear time , which introduces a linear ramp towards the new value and 'smooths' the sound. The random offsets can be random values between -deviation and +deviation , or change using a 'random walk' with a step size of ptrdrunkinc or graindrunkinc. This will be the behaviour if rand-drunk is set to 1 This abstraction requires various output~ by Miller Puckette , and a_rand , a_drunk and a_tgl as included with the patch.; #X restore 389 286 pd README; #X msg 100 14 0; #X msg 100 42 1; #X msg 57 14 samp; #X msg 55 42 samp2; #X obj 31 15 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 -1; #X obj 31 43 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 -1; #X text 218 501 EXPERIMENTAL VERSION; #X obj 22 260 vsl 15 120 0.0001 20 1 1 empty empty gain -2 -6 0 8 -225271 -1 -1 8800 1; #X obj 127 195 s ag1-grantabsize; #X obj 274 244 tgl 15 1 ag1-rd empty rand-drunk 17 7 0 10 -225271 -1 -1 0 1; #X obj 37 119 s ag1-samptab; #X obj 126 162 r $0-tabsize; #X floatatom 66 197 7 0 0 0 - - -; #X obj 59 285 a_grain~ env samp ag1; #X obj 267 347 loadbang; #X msg 268 374 ; pd dsp 1; #X msg 268 417 ; pd dsp 0; #X obj 61 457 dac~; #X obj 61 434 *~; #X obj 77 410 line~; #X obj 76 334 moses 0.0002; #X msg 76 359 0; #X obj 77 385 pack 0 50; #X connect 13 0 29 0; #X connect 16 0 14 0; #X connect 17 0 14 0; #X connect 18 0 26 0; #X connect 19 0 26 0; #X connect 20 0 18 0; #X connect 20 0 16 0; #X connect 21 0 19 0; #X connect 21 0 17 0; #X connect 23 0 36 0; #X connect 27 0 24 0; #X connect 27 0 28 0; #X connect 29 0 34 0; #X connect 29 1 0 0; #X connect 30 0 31 0; #X connect 34 0 33 0; #X connect 34 0 33 1; #X connect 35 0 34 1; #X connect 36 0 37 0; #X connect 36 1 38 0; #X connect 37 0 38 0; #X connect 38 0 35 0;
#N canvas 5 24 1172 702 10; #X obj 367 126 phasor~; #X floatatom 380 106 5 0 0 1 - - -; #X obj 389 163 *~; #X obj 527 466 cos~; #X obj 358 234 +~; #X obj 145 219 +; #X obj 98 219 float; #X obj 107 77 bang~; #X obj 107 97 a_counter; #X msg 69 72 0; #X obj 107 157 t b b; #X obj 107 116 >=; #X msg 374 213 0; #X obj 160 64 expr (44100 / 64) / $f1; #X obj 408 356 snapshot~; #X obj 107 135 sel 1; #X obj 365 546 *~; #X obj 527 444 expr~ ($v1 * .5) - 0.25; #X obj 308 183 samphold~; #X obj 595 158 a_rand; #X obj 462 179 * 44.1; #X obj 461 158 +; #X obj 500 158 t b f; #X obj 463 248 line~; #X obj 527 492 *~ 50; #X obj 463 225 pack f f; #X obj 180 148 line~; #X obj 180 125 pack f f; #X obj 202 412 +; #X obj 189 374 t b f; #X obj 11 377 * -1; #X obj 23 349 expr 0.5 * ($f1 / 100) * $f2; #X obj 618 129 expr 0.5 * ($f1 / 100) * $f2; #X obj 655 158 * -1; #X obj 408 413 sel 1; #X obj 926 99 switch~; #X msg 926 76 $1 20; #X obj 925 51 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1 ; #X obj 438 433 s $0-cycle; #X obj 527 514 tabread4~ $1; #X obj 4 97 s $0-blockbang; #X obj 365 566 outlet~; #X obj 365 521 tabread4~ $2; #X obj 477 356 r $3-grantabsize; #X obj 205 234 * 44.1; #X obj 669 236 a_drunk; #X msg 721 204 1; #X msg 755 204 50; #X msg 691 204 0; #X obj 811 26 * 0.001; #X obj 634 182 * 1000; #X obj 622 203 * 1000; #X msg 167 507 1; #X msg 201 507 50; #X msg 137 506 0; #X obj 151 619 * 0.001; #X obj 24 454 * 1000; #X obj 12 504 * 1000; #X obj 50 430 a_tgl; #X obj 161 480 loadbang; #X obj 722 181 loadbang; #X obj 795 66 a_tgl; #X floatatom 24 476 5 0 0 0 - - -; #X obj 925 31 inlet on/off; #X obj 824 44 r $3-rd; #X obj 88 376 r $3-rd; #X obj 202 435 s $0-ptrhop; #X msg 98 195 0; #X obj 407 380 expr if($f1 >= ($f2 + $f5) && ($f3 >= 0) , $f4 , if(($f1 <= $f4) && ($f3 < 0) , ($f2 - $f5) , 1)); #X text 731 424 Ensures table size limit is respected , and that the offsets never overlap; #X obj 910 486 t b f; #X obj 931 535 t b f; #X obj 779 601 expr $f1 / 100 * $f2; #X obj 914 582 r $3-grantabsize; #X obj 751 646 expr $f1 / 100 * $f2; #X obj 886 627 r $3-grantabsize; #X obj 751 513 expr if(($f1 <= 0) || ($f1 - $f2) >= 99.9 , 0 , $f1) ; #X obj 778 561 expr if(($f1 >= 0) || ($f2 - $f1) >=99.9 , 0 , $f1) ; #X text 186 82 Smear time; #X obj 751 667 s $0-segstart; #X obj 779 622 s $0-segend; #X obj 484 562 outlet; #X text 533 562 Current pointer position; #X obj 377 456 r $3-samptab; #X msg 376 500 set $1; #X obj 376 478 symbol; #X obj 151 595 a_drunk; #X obj 50 409 a_rand; #X text 36 0 Granular synthesis using incremental table lookup + pulse train enveloping. Table name for enveloping function is given by the first arg , table for sample data is given by second arg (or changed via the first inlet).; #X text 526 275 If the pointer position is greater than or equal to the segment size and moving forwards move the pointer to the start of the segment otherwise if the pointer position is less than or equal to the segment start , and moving backwards , move the pointer to the end of the segment , otherwise do nothing.; #X obj 202 312 r $3-ptrhopsize; #X obj 23 299 r $3-ptrdeviation; #X obj 179 98 r $3-ptrdeviationsmear; #X obj 195 563 r $3-ptrdrunkinc; #X obj 366 79 r $3-grainreadrate; #X obj 808 106 r $3-grainsize; #X obj 460 111 r $3-grainsize; #X obj 619 53 r $3-grainsizedeviation; #X obj 472 200 r $3-grdevsmear; #X obj 730 156 r $3-grdrunkinc; #X obj 561 467 r $3-grainamp; #X obj 750 463 r $3-tabstrtoff; #X obj 911 463 r $3-tabendoff; #X text 215 297 Pointer rate; #X obj 594 104 r $0-blockbang; #X obj 206 210 r $0-ptrhop; #X obj 128 195 r $0-cycle; #X obj 675 356 r $0-segstart; #X obj 770 356 r $0-segend; #X obj 593 356 r $0-ptrhop; #X obj 416 335 r $0-blockbang; #X obj 49 323 r $0-blockbang; #X connect 0 0 2 0; #X connect 0 0 17 0; #X connect 0 0 18 1; #X connect 1 0 0 0; #X connect 1 0 13 0; #X connect 2 0 4 0; #X connect 3 0 24 0; #X connect 4 0 14 0; #X connect 4 0 42 0; #X connect 5 0 6 1; #X connect 6 0 5 0; #X connect 6 0 27 0; #X connect 7 0 8 0; #X connect 7 0 40 0; #X connect 8 0 11 0; #X connect 9 0 8 1; #X connect 10 0 9 0; #X connect 10 1 6 0; #X connect 11 0 15 0; #X connect 12 0 4 1; #X connect 13 0 11 1; #X connect 14 0 68 0; #X connect 14 0 81 0; #X connect 15 0 10 0; #X connect 16 0 41 0; #X connect 17 0 3 0; #X connect 18 0 4 1; #X connect 19 0 61 0; #X connect 20 0 25 0; #X connect 21 0 20 0; #X connect 22 0 21 0; #X connect 22 1 21 1; #X connect 23 0 2 1; #X connect 24 0 39 0; #X connect 25 0 23 0; #X connect 26 0 18 0; #X connect 27 0 26 0; #X connect 28 0 66 0; #X connect 29 0 28 0; #X connect 29 1 28 1; #X connect 30 0 57 0; #X connect 30 0 87 1; #X connect 31 0 30 0; #X connect 31 0 56 0; #X connect 31 0 87 2; #X connect 32 0 19 2; #X connect 32 0 33 0; #X connect 32 0 50 0; #X connect 33 0 19 1; #X connect 33 0 51 0; #X connect 34 1 38 0; #X connect 36 0 35 0; #X connect 37 0 36 0; #X connect 39 0 16 1; #X connect 42 0 16 0; #X connect 43 0 68 1; #X connect 44 0 5 1; #X connect 45 0 49 0; #X connect 46 0 45 2; #X connect 47 0 45 3; #X connect 49 0 61 1; #X connect 50 0 45 5; #X connect 51 0 45 4; #X connect 52 0 86 2; #X connect 53 0 86 3; #X connect 54 0 86 1; #X connect 55 0 58 1; #X connect 56 0 62 0; #X connect 56 0 86 5; #X connect 57 0 86 4; #X connect 58 0 29 0; #X connect 59 0 54 0; #X connect 59 0 52 0; #X connect 59 0 53 0; #X connect 60 0 48 0; #X connect 60 0 46 0; #X connect 60 0 47 0; #X connect 61 0 22 0; #X connect 63 0 37 0; #X connect 64 0 61 2; #X connect 65 0 58 2; #X connect 67 0 6 0; #X connect 68 0 34 0; #X connect 70 0 76 0; #X connect 70 1 76 1; #X connect 71 0 77 0; #X connect 71 1 77 1; #X connect 72 0 80 0; #X connect 73 0 72 1; #X connect 74 0 79 0; #X connect 75 0 74 1; #X connect 76 0 74 0; #X connect 77 0 72 0; #X connect 83 0 85 0; #X connect 84 0 42 0; #X connect 85 0 84 0; #X connect 86 0 55 0; #X connect 87 0 58 0; #X connect 90 0 28 0; #X connect 90 0 31 1; #X connect 91 0 31 0; #X connect 92 0 27 1; #X connect 93 0 86 2; #X connect 94 0 0 0; #X connect 95 0 32 1; #X connect 96 0 21 0; #X connect 97 0 32 0; #X connect 98 0 25 1; #X connect 99 0 45 2; #X connect 100 0 24 1; #X connect 101 0 76 0; #X connect 101 0 71 0; #X connect 102 0 70 0; #X connect 102 0 77 0; #X connect 104 0 19 0; #X connect 105 0 44 0; #X connect 106 0 6 1; #X connect 107 0 68 3; #X connect 108 0 68 4; #X connect 109 0 68 2; #X connect 110 0 14 0; #X connect 111 0 86 0; #X connect 111 0 87 0;