@porres the user's explanation for the basis of that patch doesn't seem to fit what is described in this paper: https://www.researchgate.net/publication/260701331_A_Perceptual_Study_on_Velvet_Noise_and_Its_Variants_at_Different_Pulse_Densities
but it seems like there are a few different types of velvet noise. The original seems to be 1 impulse (chosen to be randomly either 1 or -1) randomly placed in time within a regular period. (which is not what the above patch does)
that's not to say it might not be a different type though, I only looked at the original
@porres oh yeah I always forget about
[expr~]for some reason.. maybe bc of the license it used to have.
There's no real reason to have the phase reset inlet, just more control.
From what I can tell, velvet noise is outputting a 1-sample impulse of value that's either 1 or -1, chosen randomly, at a random point in a regular period.
So I have a
[phasor~]to keep track of the period, and the
[samphold~]samples a random value from 0-1 when the
[phasor~]resets. This will be the point in the phase of that period that the impulse will occur. When the
[phasor~]gets to above or equal to this value, the
[<~ ]will go from 1 to 0. That goes into
[rzero~ 1], which will put out a -1 on the sample that occurs. That goes into the
[==~ -1], which acts (together with
[*~ ]) as a gate for the noise value that has been manipulated to either be 1 or -1, depending on if the output of
[noise~]is positive or not.
The issue with this original implementation was that when the
[phasor~]wraps around to start a new phase, it can wrap to a value greater than the new
[samphold~]value from the noise. That means that if the noise value is very small, there will be no impulse for that period since the
[phasor~]value will never be less than the sampled noise value for that period (and therefore the
[<~ ]won't go from 1 to 0, which means the
[rzero~ 1]below it won't output a -1, and so on). So, the
[<~ 0]put out a 1-sample value of 1 when the
[phasor~]wraps around, which is combined with the other signal in
[min~ 1]to take care of this. That way the
[rzero~ 1]below the
[min~ 1]will get a 1 even if the wrapped-around
[phasor~]value is less than the new sampled noise value, and there will be an impulse for that period.
(that is what "make sure there is at least 1 1 on wraparound" means)
edit: after writing all of this it occurred to me that the sampled noise value could also be greater than the value that the
[phasor~]will get to before it wraps around.. perhaps the solution is to constrain the sampled noise value depending on the frequency and phase of the
I gave it a shot using zexy relationals, might be more efficient using other externals (hence "pure")
edit: realized I didn't need one of the
edit2: now I realize there is also the possibility of missed periods in the case that the
[phasor~]wraps around to a value higher than the
edit3: 3rd time's the charm? used another
[rzero~]to put a 1 in there when the
@alexesc a workaround for using externals in camomile through the pd~ object was recently mentioned.. not sure on the specifics but here is the info https://github.com/pierreguillot/Camomile/discussions/233
another option is to compile Camomile or mobmuplat and include the source files/setup() functions from the externals you want to include.
anyways, I would say one should rely on externals where possible since they are usually faster than the vanilla implementation.
edit: the exception to this might be when making a library and you want to cut down on too many dependencies.
if you do want to use pure vanilla, you should take a look at the
[bag]object. Whenever you receive a noteoff when the pedal is held then add the note number to the bag. Then when the pedal is released send the note numbers back out w/ 0 velocity.
edit: suspedal might need to be modified some to handle repeated notes while the pedal is held (in order to send "0 velocity" for notes that are already held down if the same note is received, if that behavior is desired.)
I think this way takes 1/2 as many operations and 1/2 as much space as the extended way (bc you do it in-place instead of with a temp array):
you just have a pointer at the beginning that you increment and a pointer at the end you decrement. You swap the elements they point to until they meet in the middle.