port granulator from max/msp to pd
I used this tool https://github.com/tmhglnd/max-pd-converter to try to convert the .maxpat
then I put a [declare -lib cyclone] object and replaced [tapin~]/[tapout~] with [delwrite~]/[vd~]
replaced the [if ] with a simple [< ]
changed the [inlet] to [inlet~] and [outlet] to [outlet~]
grain.pd
seems like the result is similar to what you already had though.. maybe the issue is your [clone] usage..
edit: forgot the comma for the message box..
summer: an additive synthesizer with per-partial ADSR amplitude envelopes & LFOs
I took a shot at optimizing a bit. the main patch looks good but in addosc there are a few little things (which may add up bc you have 224 of them):
[tabread~] is taking a control-rate input. So, it isn't doing anything that [tabread] couldn't do, and have it translated to a signal later down the graph instead. The difference is that [tabread~] will still process the last input every sample whereas [tabread] will only output when it gets a new message. (so you'll be doing about samplerate times fewer computations if you have a new input every second)
similarly [+~ ] and [*~ ] are more efficient if you use the versions with a control-rate right inlet bc the loops can be vectorized and maybe predicted better. (to do this supply them with a float argument).
generally the arithmetic objects are more efficient than the [expr~] family. And, [expr~ 1 - $v1] is also another case of not needing the signal version bc the input is control-rate (after changing it to [tabread]). Instead you can change it to [1 $1( going into a [- ]. (tho in this case idk if it is actually better than [expr 1 - $f1] bc we also have to process/send the list from the message box.. the main thing is making it control-rate tho)
The final little thing is replacing the division [/ 127] in the adsr subpatch by multiplication with the reciprocal, bc processors are better at multiplying than dividing. You can do this any time you're dividing by a value that won't change.
hope it helps
addosc.pd
as for locality I think the only way is to use $0 in your arrays like you say.. you might consider making addvoice an abstraction as well. Then you could supply $0 as the argument, and inside supply it as the 2nd argument of [addosc] with $1, which could then use it as $2 in the [tabread]s to refer to the grandparent's $0
that way you'd only have to edit 28 [addosc]s instead of 224
Is there a scheduler-queue external?
@ddw_music you could also use a combination of external and internal. keep track of the current beat #. If the next item in the queue is >= the beat # for the next tick then wait for the next tick. Otherwise, [delay] by the difference to the next beat value in the queue and then output the message (and increment the current beat #).
for example if you get 16th note (0.25 beat) ticks and are on beat # 0.5 with
elements in queue: 0.6 "something", 0.7 "something else", 0.8 "third thing":
-> check if beat value for next element ("something") >= (0.5 + 0.25) = 0.75 -> no
-> delay by (0.6 - 0.5) = 0.1 beats and then pop "something"
-> increment beat # to 0.6
-> check if beat value for next element ("something else") >= 0.75 again -> no
-> delay by (0.7 - 0.6) = 0.1 beats and then pop "something else"
-> increment beat # to 0.7
-> check if beat value for next element ("third thing") >= 0.75 -> yes
-> wait for next tick and then increment beat # to 0.75
-> check if beat value for that element ("third thing") >= 1 -> no
-> delay by (0.8 - 0.75) = 0.05 beats and then pop "third thing"
etc.
planifolia (0.1) [release]
Hello!
I have just released in github and deken the abstraction pack 'planifolia' (v.0.1).
Planifolia (from the second name of the orchid 'Vanilla planifolia') is a set of Pure Data abstractions designed to work without any compiled externals.
The abstractions implement a series of useful operations like arbitrary unary and binary operations in lists and arrays, quicksort, logical operators to check equality of arbitrary datatypes, easy OSC formatting/routing, etc. It also comes with toggle-based matrix GUI (designed to work be compatible with iemmatrix objects) and a matrix based step sequencer with some non usual features (independent tempo, beat and duration patterns, col/row/colrow modes, nice colors, etc).
Some bugs may still be there... 
abstractions
| abstraction | description |
|---|---|
[any.==] |
compares datatypes |
[array.binop] |
binary operations in arrays |
[array.rand] |
random populates an arrays |
[array.unop] |
unary operations in arrays |
[ls.binop] |
binary operations in list |
[ls.butlast] |
list but the last element |
[ls.choose] |
random choose an element of the list |
[ls.getRotate] |
N rotated element of a list |
[ls.group] |
groups elements into a list |
[ls.iter] |
list iteration |
[ls.last] |
last element of the list |
[ls.max] |
greatest element of a list |
[ls.mean] |
mean of the floats in a list |
[ls.min] |
minimum element in a list |
[ls.mode] |
mode of a list |
[ls.quicksort] |
quicksort algorithm implemented as a vanilla abstraction |
[ls.removeAt] |
removes element at index |
[ls.rotate] |
N rotation of a list |
[ls.scramble] |
randomize elements in a list |
[ls.ser.arithm] |
build an artithmetic series list |
[ls.splice] |
replaces element in a list with anothe list |
[ls.unop] |
perform unary operation in elements of a list |
[mtxgui] |
vanilla matrix interface |
[mtxstep] |
vanilla step sequencer with some non usual features |
[slash.oscformat] |
tool to format OSC messages |
[slash.oscroute] |
tool to route OSC messages |
[symbol.==] |
test if two symbols are equal |
[symbol.split] |
splits a symbol according to a char |
Brazilian percussion samples were gently provided by Chico Correa youtube: /c/ChicoCorrea
List Comparison
@buzzelljason Yes. Well. Everything sent by a control rate object is a message.
The messages can be floats or symbols or lists.
Lists are a list comprising floats or symbols or a mixture of the two.
Items (atoms) in a list are separated by spaces.
It gets complicated.
If the first atom in the list is a float then the "list" tag is automatically applied, and then is silently ignored and dropped as it passes through the next object (except for [route list symbol float].......)
If the first atom in the list is a symbol then that symbol becomes the tag.... the list header..... and the list can be routed by that tag. But if the list passes through a [list] object then "list" is added (prepended) as the header. That is why [list trim] is used almost everywhere after a [list] object...... to remove the header and then be able to [route] by the original tag (if it was a symbol).
HINT..
If you install the Zexy library then [rawprint] shows you whether an atom is a "tag", 'symbol' or float....
Trying to work with vanilla OSC objects can be just guesswork without [rawprint].
mmm.pd

The messages arrive from [audiosettings] as a list which is a mixture of floats and symbols........ but the first atom is a symbol and there is no "list" tag.
[listdevices( returns a series of messages..... they are lists...... with the first atom "device"
That is why "device"....... a symbol.... and "0" or "1" can be routed. "1" could not be routed by [route 0 1] unless it is a float and "device" could not be routed by [route device] unless it is a symbol.
The device name is a symbol........ it can only be sent on in one hit because it is a symbol...... if it were a list then each part would be sent separately as a space normally "means" "next item".
But once it has passed through [route 0 1] it has lost its "symbol" tag. That can be put back by passing it through [symbol].
So the problem for [list-compare] is to create a symbol that is identical. Not easy as a message with component symbols will be treated as a list. Fortunately @ingox gave us a vanilla solution.... [concat].
And then of course to turn them both into lists for the comparison.
This will work........ this.zip
It can probably be simplified (and certainly @ingox will shortly post a more elegant solution....
).
David.

Pd FLOSS Manual, what to do with it?
@porres So... and you seem up for it..... let's have a better Pure Data Vanilla manual...... start with that and leave the rest for later.
Terminology...?
Will you adopt "manual" in preference to "Documentation"?
Will you stress (emphasise) "VANILLA" so that it is recognised and understood by new users?...
...... "Pure Data Vanilla Manual"....?..... "Pure Data - The Core - Vanilla"..... "The beating heart of `Pure Data - Vanilla"........."Pure Data Vanilla - The Matrix"
......:
"Pure Data Vanilla is the core.... the "operating system" of Pure Data".
And then introduce externals and Deken at the very start of the manual (before the current Chapters 1 and 2) so that new users stay concentrated on Vanilla as they progress......knowing that the other stuff is "extra" and exactly what that means.
No details will be required at that point... just one sentence and a link to the workings of Deken in a later chapter.
Once that is achieved the same few sentences from the start of the new Vanilla manual need to be added to the start of every other manual and tutorial that exists on the web......... if that is possible.
Would "Core" or some other word actually be better than "Vanilla"?
It has a meaning that is slightly obscure in modern English.
Google is not enthusiastic...... and brown paper envelopes are a rarity nowadays.......
"Synonyms & Antonyms of vanilla · beige, · characterless, · faceless, · featureless, · indistinctive, · neutral, · noncommittal, · nondescript."
David.
Signal-rate circular buffer?
@ddw_music I think whichever's easiest depends on if you're (over)writing the circular buffer continuously or not. If you are then [vd~]/[delread4~] with some kind of [samphold~] & [phasor~] combo to keep playing the same grain might be easier than managing your own but maybe the case is specific enough that it's worth making your own delay line that can pause writing.
but then how does it wrap around if you don't pause before the end? AFAICS there is no reliable way to make it do that.
you would have to make sure the array is a multiple of 64 samples and schedule the [tabwrite~] to start the next pass using message-rate (probably [delay]). granted, it might be far messier than the [poke~] object solution. But that would be the vanilla way to do it, and it may be more efficient since you don't have to provide your own [phasor~] (and things may be vectorized better by the compiler).
going that route might be more complicated in coordinating the exact delay time when stopping recording anyways though. (bc signal/message rate interaction).
But the thing about pausing writing is that when you move off of that grain the next time frame won't be available yet, the stuff in the array will be behind by however long you were playing that grain for, so you would have to wait for the entire array to fill up again in order to guarantee consistency in delay time.
What we really need is an object like max/msp's [stutter~] (which copies from a delay-line to another play buffer) with interpolation. I've been meaning to get to making a near-clone..
it may be possible to somehow do the same thing that [stutter~] does with complicated logic, but keeping track of what needs to be played from the delay line vs. what's already been copied to the play buffer (and therefore which should be read from for any given sample) would be even more of a headache in pd than it would be in c imo.
clicks management
@Jess use [line~] instead of [sig~], and anywhere a message-rate input goes into a signal inlet. I usually use [pack f 5] or [pack f 10] into [line~]
also delete the arguments to your [*~ ] boxes and use [line~] for the right inlet of those as well. If you need defaults, just send them to [line~] when the patch loads with [loadbang]
the clicks are a result of changing parameters too quickly. control-rate going into the [osc~] probably isn't too noticeable but if you suddenly multiply by a different value than the previous sample you'll get a click due to the sudden discontinuity.
edit: also, if you decide not to use [line~] to go into [osc~] (bc frequency discontinuities might not create a noticeable click) I would add an argument of 0 to the [+~ ] above the 2nd [osc~] and swap the stuff going into its inlets. Then the 2nd inlet will be message rate which will be a little more efficient.
edit2: changing delay times without artifacts can be tricky generally. With [line~] you might get a zipper/pitch-change effect, but just jumping to a different spot in the buffer will create a click.
Does using inlet~ create latency?
@jameslo i haven't tested it but it might be the case that if you make sure to create [receive~] after [send~] that there will be no latency, (unless the [receive~] is in part of the graph that leads to [send~]). You might have to create the entire graph the [recieve~] is part of after creating the entire graph the [send~] is part of, I'm not sure..
edit: after testing I was not able to get that to work 
edit2: after further testing it seems like if I create the graph with the [receive~] first it gets sorted last, but not if I delete the [receive~] and recreate it afterwards
so there isn't always latency, it depends on how the dsp graph is sorted. If the [recieve~] is sorted earlier in the graph before the [send~], as in the case of feedback, then there will be a block delay since [recieve~] will process the shared buffer's samples before [send~] has had a chance to write to the shared buffer. (so [recieve~] will only get the samples written in the current block by [send~] in the next block, creating a block delay.)
[SOLVED]video still remains after disconnecting [ofelia d] script
I've modified some abstractions created by @60hz for prototyping fast with ofelia (BTW, thanks a million for your abstractions they're great! And of course a million thanks to @cuinjune for creating Ofelia!). Things seem to work OK, but there's this happening. I have the following patch (I've changed the names from [gl.draw] to [ofelia.draw] for this and other abstractions)
[ofelia.draw]
|
[ofelia.cube]
and I see a white cube in the Ofelia window. Then I make the following connections:
[ofelia.draw]
|
[ofelia.movie]
|
[ofelia.cube]
and I get a video playing in the same cube. Till now everything is fine. But if I go back to the first patch, I don't get a white cube anymore but a still from the video file from [ofelia.movie]. Here are the scripts of the abstractions (almost identical to @60hz abstractions):
[ofelia.draw] (I'm returning ofColor because I want to have all abstractions output pointers and not bangs, maybe there's a better solution for this):
ofelia d draw$0;
local c = ofCanvas(this);
local args = c:getArgs();
local depth = true;
local screenwidth;
local screenheigh;
local widthHeigh;
;
function M.new();
if args[2] == nil then depth = true;
else;
M.depth(args[2]);
end;
if ofWindow.exists then;
screenwidth = ofGetWidth();
screenheight = ofGetHeight();
end;
end;
;
function M.screensize(list);
screenwidth, screenheight = list[1], list[2];
end;
;
function M.depth(string);
if string == "3d" then;
depth = true;
else;
depth = false;
end;
end;
function M.bang();
ofSetDepthTest(depth);
ofTranslate(screenwidth*0.5, screenheight*0.5);
return ofColor(255, 255, 255);
end;
[ofelia.movie]:
ofelia d -c17 videoplayer-$0;
local canvas = ofCanvas(this);
local args = canvas:getArgs();
local videoplayer = ofVideoPlayer();
local filename, start, loop = args[1], args[2], args[3];
local loaded = 0;
;
function M.new();
ofWindow.addListener("setup", this);
if args[1] == nil then print("No file found");
else M.open(filename);
end;
if args[2] == 1 then M.play();
end;
if args[3] == nil then loop = 0;
end;
end;
;
function M.free();
ofWindow.removeListener("setup", this);
end;
;
function M.setup();
M.open(filename);
end;
;
function M.open(string);
if ofWindow.exists then;
videoplayer:close();
videoplayer:load(string);
if (videoplayer:isLoaded()) then;
print("loaded " .. string);
videoplayer:update();
end;
end;
end;
function M.url(string);
if ofWindow.exists then;
videoplayer:close();
videoplayer:load(string);
if (videoplayer:isLoaded()) then;
print("loaded " .. string);
videoplayer:update();
end;
end;
end;
function M.play() videoplayer:play() end;
function M.stop() videoplayer:stop() end;
function M.pause() videoplayer:setPaused(true) end;
function M.speed(float) videoplayer:setSpeed(float) end;
function M.frame(float) videoplayer:setFrame(float) end;
function M.volume(float) videoplayer:setVolume(float) end;
function M.loop(float);
if float == 0 then videoplayer:setLoopState(OF_LOOP_NONE);
elseif float == 1 then videoplayer:setLoopState(OF_LOOP_NORMAL);
elseif float == 2 then videoplayer:setLoopState(OF_LOOP_PALINDROME);
end;
end;
function M.get()
return ofTable (videoplayer, videoplayer:isLoaded(), videoplayer:isPlaying(), videoplayer:getCurrentFrame(), videoplayer:getTotalNumFrames(), videoplayer:getWidth(), videoplayer:getHeight());
end;
;
function M.pointer(p);
videoplayer:update();
videoplayer:bind();
return videoplayer;
end;
function M.bang();
videoplayer:update();
videoplayer:bind();
return videoplayer;
end;
Inside [ofelia.movie] there's this patch:
[ofelia d movie_script] <- this is the ofelia object that loads the script above
|
[t a a]
| |
| [outlet]
|
[ofelia d videoplayer_unbind;] <- this is one object
[function M.pointer(p); ]
[p:unbind; ]
[end; ]
and this is [ofelia.cube]:
ofelia d $0-box;
local c = ofCanvas(this);
local args = c:getArgs();
local width, height, depth, resw, resh, resd, drawmode, strokeweight = args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8];
local position, orientation, scale = ofVec3f(0, 0, 0), ofVec3f(0, 0, 0), ofVec3f(1, 1, 1);
;
function M.new();
ofWindow.addListener("setup", this);
if args[1] == nil then width = 100 end;
if args[2] == nil then height = 100 end;
if args[3] == nil then depth = 100 end;
if args[4] == nil then resw = 5 end;
if args[5] == nil then resh = 5 end;
if args[6] == nil then resd = 5 end;
if args[7] == nil then drawmode = "fill" end;
if args[8] == nil then strokeweight = 1 end;
M.setup();
end;
;
function M.free();
ofWindow.removeListener("setup", this)
end;
;
function M.setup();
box$0 = ofBoxPrimitive();
end;
;
function M.resw(float) resw = float end;
function M.resh(float) resh = float end;
function M.resd(float) resd = float end;
function M.width(float) width = float end;
function M.height(float) height = float end;
function M.depth(float) depth = float end;
function M.draw(string) drawmode = string end;
function M.stroke(float) strokeweight = float end;
function M.position(list) position = ofVec3f(list[1], list[2], list[3]) end;
function M.orientation(list) orientation = ofVec3f(list[1], list[2], list[3]) end;
function M.scale(list) scale = ofVec3f(list[1], list[2], list[3]) end;
;
function M.pointer(p);
ofSetLineWidth(strokeweight);
box$0:setPosition (position:vec3());
box$0:setOrientation (orientation:vec3());
box$0:setScale (scale:vec3());
box$0:set(width, height, depth, math.abs(resw), math.abs(resh), math.abs(resd));
if drawmode == "fill" then box$0:drawFaces() end;
if drawmode == "point" then box$0:drawVertices() end;
if drawmode == "line" then box$0:drawWireframe() end;
return p;
end;
function M.bang();
ofSetLineWidth(strokeweight);
box$0:setPosition (position:vec3());
box$0:setOrientation (orientation:vec3());
box$0:setScale (scale:vec3());
box$0:set(width, height, depth, math.abs(resw), math.abs(resh), math.abs(resd));
if drawmode == "fill" then box$0:drawFaces() end;
if drawmode == "point" then box$0:drawVertices() end;
if drawmode == "line" then box$0:drawWireframe() end;
return anything;
end;
This is a bit too much information but I think it's necessary if anyone can help. There's probably stuff I'm ignorant of. @cuinjune any hints?


