Cubic or spline ramp generator?
@ddw_music said:
That's the "bad" one from tabread4~
So that is 'lagrange' interpolation, and it seems different than the one I use, but ok. For reference, here it is
double interp_lagrange(double frac, double a, double b, double c, double d){
double cminusb = c-b;
return((t_float)(b + frac * (cminusb - (1. - frac)ONE_SIXTH *
((d - a - 3.0cminusb) * frac + d + 2.0*a - 3.0 * b ))));
}
Now, what is bad about it?
where, in my abstraction, I used the "good" one from tabread4c~
Where is the reference to this formula and what is "good" about it?
Guard points for tabosc4~
Each time a tabosc4~ reads a value from its table, it uses a combination of the four values closest to the given index. This allows it to produce a suitable value through interpolation even if the index isn't a whole number.
Let's imagine an object that does this without using guard values.
When the object is reading from the start of the table (any index less than 1), one of the four values it needs to combine lies before the start of the table. Because the waveform repeats, this is the same as the value at the end of the table. So the value can be read from there instead.
Similarly, when the object is reading from the end of the table (any index within 2 of the last index), one or two of the values it needs to combine lie beyond the end of the table. Because the waveform repeats, these are the same as the values at the start of the table. So the values can be read from there instead.
So our imaginary object without guard values works - what's the problem? The problem is that checking for those special cases increases the cost of looking up every value. It's more efficient to make copies of the extra values we need - one at the start and two at the end - when setting up the table. This extra work only needs to be done once, and then the lookup code that runs thousands of times per second can be as efficient as possible.
To answer the question about the discrepancy in the documentation about whether we should have three guard points at the end, or one at the start and two at the end: either way will work. Both approaches will produce waveforms that repeat cleanly, but the start point of the waveform (and thus its phase) will differ by one sample depending on which approach we choose. So if it's important to preserve the phase of your original waveform, add one guard point to the start and two to the end.
ELSE 1.0-0 RC13 with Live Electronics Tutorial Released
Ok, the cat is out of the bag --> https://github.com/porres/pd-else/releases/tag/1.0-rc13 I'm officialy announcing the update and uploaded binaries to deken for mac (intel/arm), Win and Linux. It all looks ok but tell me if you see something funny please. There's also a raspberry pi binary but not working 100%yet and we'll still look into that. Hopefully someone could help me/us with it. I might make another upload just for the pi later on if/when we figure it out. Find release notes and changelog below.
RELEASE NOTES:
Please support me on Patreon https://www.patreon.com/porres I'll now try to add special content for subscribers. You can follow me on instagram as well if you like... I'm always posting Pd development stuff over there https://www.instagram.com/alexandre.torres.porres/
It's been a little bit over 7 months since the last update and I almost broke the record for taking too long to release an update (which had happened in my previous update). So yeah, there's just too much to talk about! I guess the delays in releasing updates is because it's been a little tricky and hard to sync the release cycles of ELSE with PlugData, which includes ELSE in its download.
Plugdata 0.9.2 should come out soon with ELSE RC13 and it's supposedly the last update before 1.0.0, so I've heard. And the plans was to get to that still in 2025! This means ELSE could be at its last "Release Candidate" phase as I'm aiming to sync the final release with PlugData. Until then, I'll still make breaking changes and I can't wait until I can't do that anymore as I really feel bad. On the other hand, it's kind of inevitable when I'm always adding new stuff and redesigning and reconfiguring objects to include more functionalities. And I always got a lot of new stuff! So I'm thinking that I will eventually try some mechanism like Pd's compatibility flag or something. I'll try to come up with something like that in the next update.
This update has 22 new objects for a total of 573 and 26 new examples in my tutorial for a total of 554 examples. Let's dive into the highlights (see full changelog below after the release notes).
-
Multichannel Support: Last release had 92 MC aware objects, now it's 139! Over a 50% increase that include old and new objects (all the new ones have been coming with MC support). Virtually all oscillators and envelope generators now have MC support, plus some other random ones. Let me highlight the new [lace~]/[delace~] objects that are 'MC' tools that perform interleave/deinterleave in Multichannel connections. My bare minimum number of objects "to start with" would be at least a bit over half the number of signal objects. That was my target for 1.0! ELSE right now has 319 signal objects, so that'd be at least 160. I will definitely pass this milestone in the next update. I guess a good number of MC objects would be around 75% of the signal objects. I will aim for that as soon as I can. Some objects simply can't be MC at all, so 100% will never be the case, but maybe an ideal 90% eventually? We'll see... I am just proud and happy that ELSE is taking such a big jump on MC awareness in less than a couple years.
-
Envelope generators ([adsr~]/[asr~]/[envgen~]/[function~]) now have more curve options. For [adsr~]/[asr~] the default is now a new log curve that you can set the curve parameter (and was 'stolen' from SuperCollider). A new [smooth~] family of objects perform the same kind of curved smoothening for alternating inputs - [envgen~] and [function~] also have that but also '1-pole' filtering, 'sine' and 'hann' curves. You can now trigger [adsr~] and [asr~] with impulses.
-
The [play.file~] object now supports even more file formats besides MP3 and stuff. Hey, you can even stream the supported formats from weblinks! The [sfload] object (which loads files into arrays) also gained support for more formats and can download from weblinks as well! It also has a new threaded mode, so loading big files won't choke Pd. It now also outputs the file information, which is a way to tell you when loading finished in threaded mode. The [sample~], [player~], [gran.player~] and [pvoc.player~] objects are now also based on [sfload], so they support all these file formats!!! Now [sample~] and [tabplayer~] are integrated in a way that [tabplayer~] is always aware of the sample rate of the file loaded in [sample~] (so it reads in the "correct speed"). A new [sfinfo] object is able to extract looping regions and instrument metadata information from AIFF files (which is something I wanted for ages) - it should do more stuff in the future.
-
[knob] has become the ultimate featured bloated creep GUI I always feared and avoided. MAX is envy! but I'm happy with this structure and I want to replicate in other GUIs in the future (yeah, I got plans to offer alternatives to all iemguis). I wanna highlight a new 'param' symbol I added that allows you to remotely set a particular method in an object, so you don't to connect to a "method $1" message and you can even do this wirelessly with a send symbol. [knob] now also acts like a number box, where you can type in the value, which may also be displayed in different ways or the value can be sent elsewhere via another send symbol so you can temper with it using [makefilename] or [else/format]. I've been using this for the MERDA modules and it's really cool.
-
We finally have a [popmenu] GUI object! This was in my to do list forever and was crucial to improve the MERDA modules to set waveforms, instruments and whatnot.
-
Let's about MERDA, the "Modular Euroracks Dancing Along" subset of abstractions in ELSE. It was first released in the last update and it's been driving lots of the development in ELSE as you can see. I now added a MIDI Learn feature for all knobs that feels great and quite handy! There are many fixes and improvements in general and some new modules. I wanna highlight the new [sfont.m~] module, which loads "sound font" banks and you can just click on a [popmenu] to choose the instrument you want. The default bank has numerous (hundreds) options and also comes with PlugData. The sequencer module [seq8.m~] was rather worthless but it's now a whole new cool thingie. It allows you to set pitches with symbols and even has quarter tone resolution. I added a right outlet to send impulses to trigger envelopes and stuff (there's still more stuff of course, see full changelog below).
-
There are newly designed/renamed/recreated [resonbank~]/[resonbank2~] objects that are well suited for Modal Synthesis.
-
What actually drives my development is my Live Electronics tutorial, which got a fair upgrade with a new chapter on Modal Synthesis amongst other things, such as new subtractive synthesis examples and a revision of envelope generators with examples on AHDSR and DAHDSR - by the way, there are new gaterelease~/gatedelay~ objects for handling envelopes (and other processes).
-
I have to thank some people. Tim added 'zoom' to the [pic] object, as well as an image offset. Tim also implemented a new and better technique for bandlimited oscillators. Ben Wesh gave me a new [scope3d~] GUI object, pretty cool, that plots an oscilloscope in 3 dimensions, which is coded in LUA - and ELSE has been carrying a modified version of [pdlua] because it now depends on it for a couple of GUIs. Tim and Ben made many improvements to [pdlua] (as well as Albert Graef, of course).
-
For more new objects, let me also tell you about the simple and cool [float2imp~], that is based on [vline~] and can convert floats to impulses with sample accuracy (don't know why I didn't think of that earlier). A new [tanh~] object has Multichannel support. A bit earlier I made an update to Cyclone that actually "borrows" and includes this one from ELSE instead of its original one (which does not have Multichannel support). PlugData users will load the one from ELSE. This is another tiny step that sort of integrates ELSE and Cyclone, specially for PlugData users.
happy patching.
CHANGELOG:
LIBRARY:
Breaking changes:
- [adsr~]/[asr~]: now a gate off before reaching the sustain point does not start the release right away (this allows you to trigger it with impulses). There's a new mode just for immediate release. There's a new exponential setting for curve factors, the old 'log' mode is renamed to 'lag' as it's the same as used in the [lag~] object. For [adsr~], a bang now is not "retrigger", but an impulse at control rate, there's a new 'retrigger' message for control rate retriggering (and now it only retriggers if the gate is on). For [asr~] a bang now also works like an impulse.
- [sample~]: no more 'load' message, args to 'open' message changed, size is now only in 'ms'.
- [format]: outputs are now always symbols, before you could get float outputs. Also, we just have a simplified symbol output, no more lists or anythings. Hopefully I'll be able to get the 'list' output back, but it involved some bugs that I couldn't fix so I just removed it. You cannot use bangs and lists in secondary inlets no more (this is cylone/max crappy paradigm we don't want here). Bang method was actually removed as well.
- [pack2]: no more support for anythings, also no more support for lists in secondary inlets and output has a list selector (I wanna make this more Pd like and not a silly clone from MAX's [pak], cause fuck MAX).
- [merge]/[unmerge]/[group]: no more '-trim' flag (again, respecting pd's usual list paradigm), in [merge] now there's no more 'hot' argument and a bang now represents an empty list and inlets initialized with empty lists
- [mono]: 1st argument is now 'glide' in ms.
- [sfont~] now uses 'mma' for bank selection (this alters how CC messages set the bank number).
- [player~]/[play.file~]: 'open' message does not play files right away anymore.
- [tabplayer~]/[player~]: play message without args now play at the default settings (whole file at regular speed).
- [envgen~]: removed the 'maxsustain' parameter, use the new [gaterelease~] or [gaterelease] objects instead. Removed the rightmost inlet just to set envelopes, now a list input only sets the envelope and doesn't trigger it. The 'set' message is then removed.
- [envgen~]/[function~]: simplified and got rid of '-exp' flag and message, also deleted 'expl' and 'expi' messages. A new 'curve' and cimpler message sets exponential factors for all or individual segments, and includes more curve formats.
- [knob]: 'esc' key now deactivates the object. The 'ticks' message is renamed to 'steps' and there is a new 'ticks' message that toggles showing ticks on and off. The 'start' message has been renamed to 'arcstart'. The 'outline' message has been renamed to 'square' for better clarity. Design changed a bit to make it like it is in PlugData (they won), so we now fill the whole background color when in 'square mode' and the knob circle has an 85% proportion in this case inside the full 100% square size (so it grows bigger when not in 'square' mode). Now, by default, the GUI is in a new 'loadbang' mode (I don't think this will influence old patches). I'm afraid some old patches might behave really weird since I added a lot of new stuff. I changed the 'load' message behaviour to not update the object (this can arguably be considered a bug fix).
- [wavetable~], [bl.wavetable~] and [wt2d~]: 'set' message now sets frequencies because of the MC support in [wt~] and [wt2d~], while there's a new 'table' method to set the table name.
- [gbman~]/[cusp~] list method is now for MC, old list method is now renamed back to an old 'coeffs' method.
- [f2s~]/[float2sig~] default value is now 10 ms.
- [op] now behaves like [*~] where the smaller list wraps til reaching the size of the longer one.
- [list.seq] does not loop anymore by default.
- [impseq~] list input removed, use the new [float2imp~] object to convert floats to impulses.
- [resonant~] now has 'q' as the default.
- [resonant2~] has been removed.
- [decay2~] has also been removed ([asr~] much better).
- [vcf2~] has been renamed to [resonator2~].
- [resonbank~]/[resonbank2~] have basically been deleted and replaced by new objects with the same name... [resonator~] is based on a new [resonator~] object which is similar to [resonant~] and [resonbank2~] is now based on [resonator2~] (old [vcf2~] instead of [resonant2~] that got deleted). These are well suited objects for Modal Synthesis.
- [oscbank~] now uses a 'partial' list and not a frequency list. The freq input now defaults to '1' and this makes [oscbank2~] completely obsolete.
- [oscbank2~] has been deleted since it became completely obsolete.
- [sfload] load message changed the behaviour a bit.
Enhancements/fixes/other changes:
- [adsr~]: We have now a new mode for immediate release (see breaking changes above, I'm not repeating it). Fixed ADSR signal inputs (it was simply not really working, specially for linear). Fixed status output for MC signals. There's a new curve parameter that allows you to set the curvature.
- [asr~] I actually just made the new [adsr~] code into a new [asr~] code as a simplified version (as it was before)... so it's got the same impromevents/fixes.
- [play.file~]: added support for more file formats and even weblinks for online streaming!
- [sfload]: added an outlet to output information, added threaded mode, added support for more file formats and even weblinks for downloading.
- [sample~], [player~], [gran.player~] and [pvoc.player~] are now also based on [sfload], so they support more file formats!
- [sample~]: improved extension management with [file splitext].
- [sample~] and [tabplayer~] now are automatically integrated in a way that [tabplayer~] is always aware of the sample rate of the file loaded in [sample~], so it automatically adjusts the reading speed if it is different than the one Pd is running with.
- [numbox~]'s number display is not preceded by "~" anymore (that was just kinda stupid to have).
- [format]: fixed issues where empty symbols and symbols with escaped spaces didn't work. Added support '%a' and '%A' type. Added support for an escaped 'space' flag. Improved and added support for length modifiers. Improved syntax check which prevents a crash. Improved documentation.
- [knob]: added new 'param', 'var', 'savestate', 'read only', 'loadbang', "active", "reset" and 'ticks' methods. Added the possibility to type in number values and also modes on how to display these number values, plus new send symbols for 'activity', 'typing', 'tab' and 'enter'. New design more like plugdata. Changed some shortcuts to make it simpler. If you have the yet unreleased Pd 0.56-0 you can also use 'double clicking' in the same way that works in PlugData. Properties were also significantly improved (I'm finally starting to learn how to deal with this tcl/tk thingie). Yup, a lot of shit here...
- [autofade2~]/[autofade2.mc~]: fixed immediate jump up for 0 ramp up.
- [synth~]: fixed polyphony bug.
- [metronome~]: fixed bug with 'set' message.
- [midi2note]: fixed range (octaves 0-8).
- [pulsecount~]: fixed reset count to not output immediately, added bang to reset counter at control rate
- [click]: fixed regression bug where it stopped working.
- [else]: new 'dir' method to output ELSE's binary directory in a new rightmost outlet. The print information also includes the directory.
- [pic]: added zoom capability finally (thanks to tim schoen) and added offset message (also thanks to tim).
- [store]: added 'sort' functionality.
- [scales]: fixed octave number argument. Added functionality to allow octave number as part of the note symbol.
- [mono]: added 'glide' parameter, as in [mono~].
- [pluck~]: fixed list input.
- [rescale]/[rescale~]: added a "reverse log" mode.
- [limit]: added a new second ignore mode.
- [graph~]: added an external source input for plotting the graph and a 'clear' message.
- [canvas.setname]: added a new argument for "abstraction mode" and methods to set name, depth (and mode).
- [midi.learn]: added a new argument for "abstraction mode", fixed 'dirty' message sent to parent.
- [brickwall~]: fixed initialization.
- [list.seq]: added a loop mode and a 2nd outlet to send a bang when the sequence is done.
- [delete]: fixed index for positive numbers.
- [dust~]: added 'list', 'set' and '-mc' flag for managing the already existing Multichannel capabilities.
- Thanks to Tim we have many fixes and a whole new technique for band limited oscillators. Now [bl.saw~], [bl.saw2~], [bl.vsaw~], [bl.square~], [bl.tri~], [bl.imp~] and [bl.imp2~] have been redesigned to implement elliptic blep, which should provide better anti-aliasing.
- [parabolic~] now uses and internal wavetable for more efficiency.
- [resonant~]: added 'bw' resonance mode.
- [lowpass~]/[highpass~]: added 't60' resonance mode.
- [quantizer~]/[quantizer]: added a new mode, which combines floor (for negative) and ceil (for positive) values.
- [crusher~]: now uses the new [quantizer~] mode from above (arguably a breaking change).
- [envgen~]: fixed a bug (actually a misconception) where ramps started one sample earlier. Fixed 0-length lines. Added a possibility to set time in samples instead of ms. Maximum number of lines is now 1024. Added loop mode. Added many curve options (sin/hann/log curve/lag).
- [function~]: Added many curve options (sin/hann/log curve/lag).
- [The out~] family of abstractions now use [bitnormal~] so you won't blow your speakers beyond repair in edge cases.
- [trig.delay~]/[trig.delay2~]: fixed bug where impulse values different than '1' didn't work.
- Added MC support to: [trig.delay~], [trig.delay2~], [gatehold~], [vca.m~], [gain2~], [decay~], [asr~], [envgen~], [function~], [bl.osc~], [bl.saw~], [bl.saw2~], [bl.vsaw~], [bl.square~], [bl.tri~], [bl.imp~], [bl.imp2~], [imp2~], [tri~], [saw~], [saw2~], [vsaw~], [square~], [pulse~], [parabolic~], [gaussian~], [wavetable~], [wt2d~], [randpulse~], [randpulse2~], [stepnoise~], [rampnoise~] [pink~], [gbamn~], [cusp~], [gray~] and [white~].
- Also added MIDI input and soft sync to [imp2~], [tri~], [saw~], [saw2~], [vsaw~], [square~], [pulse~], [gaussian~] and [parabolic~].
- [wavetable~] and [wt2d~] gained args to set xfading.
- Updated pdlua to 0.12.23.
- M.E.R.D.A: Added MIDI-LEARN for all modules (this is only for the knobs). Replaced some number boxes that were attached to knobs by an internal number display mechanism (new feature from knob). Improved interface of [gendyn.m~]. Preset/symbol name fixes to [flanger.m~]. Now we have automatic MIDI mode detection for [plaits.m~] and [pluck.m~] when no signals are connected (still trying to get plaits right, huh? Yup! And bow MIDI input with monophony and trigger mode has been fixed in [plaits.m~]). Added MC support to [vca.m~]. Increased range of [drive.m~] down to 0.1. Changed some objects to include the new [popmenu] GUI. [vco.m~] now uses the new MC functionalities of oscillators and doesn't need to load abstractions into [clone], I hope it makes this more efficient and clean. The [seq8.m~] module was worthless and got a decent upgrade, it's practically a new module. Added new modules (see below). Note that MERDA is still at alpha development phase, much experimental. Expect changes as it evolves.
- 22 new objects: [float2imp~], [lace], [delace], [lace~], [delace~], [gatehold], [gatedelay],[gatedelay~], [gaterelease~], [gaterelease], [popmenu], [scope3d~], [tanh~], [resonator~], [sfinfo], [smooth], [smooth2], [smooth~], [smooth2~], [dbgain~], [level~] plus [crusher.m~], [sfont.m~] and [level.m~] MERDA Modules.
Objects count: total of 573 (319 signal objects [139 of which are MC aware] and 254 control objects)!
- 323 coded objects (210 signal objects / 113 control objects)
- 227 abstractions objects (87 signal objects / 140 control objects)
- 23 MERDA modular abstractions (22 audio / 1 control)
TUTORIAL:
- New examples and revisions to add the new objects, features and breaking changes in ELSE.
- Added the MERDA modules into the examples for reference.
- Revised section on envelopes.
- New subtractive synthesis examples.
- New chapter on Modal Synthesis.
- Total number of examples is now 554! (26 new ones)
looking for velvet noise generator
As I was imagining, as a C code, this is all hassle free, very simple, and never misses a period. And the code is pretty simple. And Multichannel capable
So far this is what I have....
// Porres 2024
#include "m_pd.h"
#include <stdlib.h>
#include "random.h"
#define MAXLEN 1024
typedef struct _velvet{
t_object x_obj;
double *x_phase;
double *x_lastphase;
t_random_state x_rstate;
int x_id;
t_float *x_rand;
int x_nchans;
t_float x_hz;
t_int x_n;
t_int x_ch2;
t_int x_ch3;
t_inlet *x_inlet_reg;
t_inlet *x_inlet_bias;
t_outlet *x_outlet;
double x_sr_rec;
}t_velvet;
static t_class *velvet_class;
static void velvet_seed(t_velvet *x, t_symbol *s, int ac, t_atom *av){
random_init(&x->x_rstate, get_seed(s, ac, av, x->x_id));
}
static t_int *velvet_perform(t_int *w){
t_velvet *x = (t_velvet *)(w[1]);
t_float *in1 = (t_float *)(w[2]);
// t_float *in2 = (t_float *)(w[3]); // bias placeholder
// t_float *in3 = (t_float *)(w[4]); // regularity placeholder
t_float *out = (t_float *)(w[5]);
double phase = x->x_phase;
double lastphase = x->x_lastphase;
for(int j = 0; j < x->x_nchans; j++){
for(int i = 0, n = x->x_n; i < n; i++){
double hz = in1[jn + i];
double step = hz * x->x_sr_rec; // phase step
step = step > 1 ? 1 : step < 0 ? 0 : step; // clipped phase_step
out[jn + i] = ((phase[j] + x->x_rand[j]) >= 1.) && ((lastphase[j] + x->x_rand[j]) < 1.);
if(phase[j] >= 1.){
uint32_t *s1 = &x->x_rstate.s1;
uint32_t *s2 = &x->x_rstate.s2;
uint32_t *s3 = &x->x_rstate.s3;
x->x_rand[j] = (t_float)(random_frand(s1, s2, s3)) * 0.5 + 0.5;
post("phase = %f", phase[j]);
post("random = %f", x->x_rand[j]);
phase[j] -= 1; // wrapped phase
}
lastphase[j] = phase[j];
phase[j] += step;
}
}
x->x_phase = phase;
x->x_lastphase = lastphase;
return(w+6);
}
static void velvet_dsp(t_velvet *x, t_signal **sp){
x->x_n = sp[0]->s_n, x->x_sr_rec = 1.0 / (double)sp[0]->s_sr;
int chs = sp[0]->s_nchans;
x->x_ch2 = sp[1]->s_nchans, x->x_ch3 = sp[2]->s_nchans;
if(x->x_nchans != chs){
x->x_lastphase = (double *)resizebytes(x->x_lastphase,
x->x_nchans * sizeof(double), chs * sizeof(double));
x->x_phase = (double *)resizebytes(x->x_phase,
x->x_nchans * sizeof(double), chs * sizeof(double));
x->x_rand = (t_float )resizebytes(x->x_rand,
x->x_nchans * sizeof(t_float), chs * sizeof(t_float));
x->x_nchans = chs;
}
signal_setmultiout(&sp[3], x->x_nchans);
if((x->x_ch2 > 1 && x->x_ch2 != x->x_nchans)
|| (x->x_ch3 > 1 && x->x_ch3 != x->x_nchans)){
dsp_add_zero(sp[3]->s_vec, x->x_nchansx->x_n);
pd_error(x, "[velvet~]: channel sizes mismatch");
return;
}
dsp_add(velvet_perform, 5, x, sp[0]->s_vec,
sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec);
}
static void *velvet_free(t_velvet *x){
inlet_free(x->x_inlet_bias);
inlet_free(x->x_inlet_reg);
outlet_free(x->x_outlet);
freebytes(x->x_phase, x->x_nchans * sizeof(*x->x_phase));
freebytes(x->x_lastphase, x->x_nchans * sizeof(*x->x_lastphase));
freebytes(x->x_rand, x->x_nchans * sizeof(*x->x_rand));
return(void *)x;
}
static void *velvet_new(t_symbol *s, int ac, t_atom *av){
s = NULL;
t_velvet *x = (t_velvet *)pd_new(velvet_class);
x->x_id = random_get_id();
x->x_phase = (double *)getbytes(sizeof(*x->x_phase));
x->x_lastphase = (double *)getbytes(sizeof(*x->x_lastphase));
x->x_rand = (t_float *)getbytes(sizeof(*x->x_rand));
x->x_hz = x->x_phase[0] = x->x_lastphase[0] = x->x_rand[0] = 0;
velvet_seed(x, s, 0, NULL);
if(ac){
while(av->a_type == A_SYMBOL){
if(ac >= 2 && atom_getsymbol(av) == gensym("-seed")){
t_atom at[1];
SETFLOAT(at, atom_getfloat(av+1));
ac-=2, av+=2;
velvet_seed(x, s, 1, at);
}
else
goto errstate;
}
if(ac && av->a_type == A_FLOAT){
x->x_hz = av->a_w.w_float;
ac--, av++;
}
}
x->x_inlet_bias = inlet_new((t_object *)x, (t_pd *)x, &s_signal, &s_signal);
pd_float((t_pd *)x->x_inlet_bias, 0);
x->x_inlet_reg = inlet_new((t_object *)x, (t_pd *)x, &s_signal, &s_signal);
pd_float((t_pd *)x->x_inlet_reg, x->x_phase[0]);
x->x_outlet = outlet_new(&x->x_obj, &s_signal);
return(x);
errstate:
post("[velvet~]: improper args");
return(NULL);
}
void velvet_tilde_setup(void){
velvet_class = class_new(gensym("velvet~"), (t_newmethod)velvet_new, (t_method)velvet_free,
sizeof(t_velvet), CLASS_MULTICHANNEL, A_GIMME, 0);
CLASS_MAINSIGNALIN(velvet_class, t_velvet, x_hz);
class_addmethod(velvet_class, (t_method)velvet_dsp, gensym("dsp"), A_CANT, 0);
class_addmethod(velvet_class, (t_method)velvet_seed, gensym("seed"), A_GIMME, 0);
}
Pd compiled for double-precision floats and Windows
@jameslo said:
@ddw_music I love that story but am scratching my head over the 1/10 example you gave. Here's a test I made in Arduino c++: ... I went out 40 digits and didn't see anything unexpected. Was that example you gave just a metaphor for the issue, or is my test naive?
Not a metaphor at all:
[16, 17, 40].do { |prec|
"% digits: %\n".postf(
prec,
0.1.asStringPrec(prec)
)
};
16 digits: 0.1
17 digits: 0.10000000000000001
40 digits: 0.1000000000000000055511151231257827021182
As for Arduino, the float datatype reference says "Unlike other platforms, where you can get more precision by using a double (e.g. up to 15 digits), on the Arduino, double is the same size as float" -- so my guess here is that Serial places a limit on the number of digits it will try to render, and then fills the rest with zeros.
I went out 40 digits and didn't see anything unexpected.
Seeing zeros all the way out to 40 digits is unexpected! Arduino's output here is more comforting to end-users (which might be why they did that), but it isn't accurate.
Considering that Arduino calculates "double" using single precision, the output should deviate from the mathematically true value even earlier:
// 0.1.as32Bits = single precision but as an integer
// Float.from32Bits = double precision but based on the 32 bit float
Float.from32Bits(0.1.as32Bits).asStringPrec(40)
-> 0.100000001490116119384765625
The most reasonable conclusion I can draw is that Arduino is gussying up the output to reduce the number of "what the xxx is it printing" questions on their forum. That should not be taken as a standard against which other software libraries may be judged.
Edit: hmm, but here's Pd64.
Pd64 is doing it right, and Arduino is not.
Getting back to how Pd seems to differ from other programming languages, I'm going to hazard a guess and say that Pd hasn't separated the value of a float from its display/storage format.
The value must be stored in standard single/double precision format. You need the CPU to be able to take advantage of floating point instructions.
It's rather that Pd has to render the arguments as text, and this part isn't syncing up with the "double" compiler switch.
PS I hope my attempt at humor didn't discourage @porres from responding to @oid's question. I'm sure he would have something more meaningful to contribute.
Of patchers as programming languages... well, I got a lot of opinions about that. Another time. For now, just to say, classical algorithms are much harder to express in patchers because patchers are missing a few key features of programming languages.
hjh
Order of ops confusion (troubleshooting vanilla demux abstraction)
@lacuna said:
Still a mystery why the send-method works? Anyone knows?
You mean mine with the symbols in the list? As I understand it the only time symbols go into the symbol hash table is when they are lone symbols not attached to any other data structure, they are stored in the heap and the hash table has pointers to the memory location. If the symbol is part of an object which allocates its own place in memory (list, text) it never goes to the hash table. In this case pd just passes around the pointer to the position of the symbol in the list's allocated memory so never needs to search the hash table.
Drowning in symbols is referring to how pd gets slow at finding symbols in the hash table if the number of symbols gets high, big hash table more time spent trying to find the right symbol. My above assumptions regarding symbols and list/text not going into the hash table are based off of pd not choking on symbols when you load large text files into a [text]. I mostly posted that patch to see if anyone could clarify on how pd really works here. On modern devices the symbol count can get very high before issues arrive, can't remember the last time I hit the wall here and the only times I have hit the wall was way back when before I knew about the hash table stuff.
I think [send] will be the most efficient as long as symbol count does not cause issues. I believe you could also speed up your spigot version a tiny amount by changing the three [t a b] to [t a a] to avoid the type conversion, messages don't care what triggers them so no need to do the conversion.
Math detail problem of numbers larger than six or so decimals
A numeric representation with a finite number of digits is always an approximation, unless it's a rational number and the prime factors of the denominator are all prime factors of the base.
2022.122 can be represented accurately in decimal because the denominator is 1000, with prime factors 2 and 5 (same as base 10).
1/3 cannot be represented accurately in decimal because base 10 does not include 3 as a prime factor.
In binary floating point, base 2 has only one prime factor, so the only numbers that can be represented accurately are those whose denominator is a power of 2. 0.25 = 1/4, ok. 0.2 = 1/5, not ok.
Many people get confused because they wrote an exact decimal value and assume that it will be exact in binary. It may very well not be. 2022.122 is one of those (the denominator has three factors of 5, which binary cannot handle).
One takeaway is that == tests are often invalid for floating point. You shouldn't expect it to work, actually. It isn't weird. It's in the nature of the number system.
Similarly, there's no "right pi" and "wrong pi." Every representation of pi in a computer is wrong. The expr example merely generates a wrong pi whose wrongness matches the later operations.
Double precision pushes these issues further away so they are less visible -- but 2022.122 is a repeating binary in double just as much as in single precision. It would be good for Pd to move up to doubles for control messages though.
hjh
ways of exponentiation ( / range mapping)
More generally, this is range mapping, absolutely central to any type of interactive work. I usually teach it like this:
You have a base value. This could be one end of the range, or the center of the range.
You have some input data -- could be LFO, envelope, or external data.
Normalize the input -- if the base value is the end of the output range, then your normalized input would be 0 .. 1. If it's the center, then the normalized input is -1 .. +1.
Then linear mapping is base + ((range_end - base) * normalized_value)
. I write it in this order because... then...
Exponential ("log") mapping only promotes the operators up one level: base * ((range_end / base) ** normalized_value)
where **
is "pow" in Pd. (Note that range_end / base needs to be positive and nonzero.)
You can translate from any input range to any output range this way.
For variable-curvature mapping, I translated the formula from SuperCollider in my abstraction library, the [lincurve] and [lincurve~] objects.
hjh
math.h, float or double?
@morpheu5 the canonical supported pd only uses 32-bit floats in messages (and signals). the question of how to write/format externals that support both floats and doubles is an ongoing discussion afaik. Right now almost everyone uses "normal" pd w/32-bit floats, and externals generally aren't available for pd-double.
most architectures support doubles now, so t_float isn't based on the machine. (but I presume the reason supported pd uses 32-bit floats is so it can run on an abbacus)
However, there's nothing to restrict external developers from using doubles in their externals internally. It's just that when the message/signal goes back into pd it will be 32-bit.
I think functions like fmod are fine to use for both doubles and floats (I wouldn't be surprised if the compiler will just use fmodf instead if the inputs are floats)
But I don't know how far off pd-double is from being supported or used anyways, so right now
I'd say right now you don't need to worry about t_floats or t_samples being doubles at all. No externals support pd double, because I don't think the loading mechanism/convention has been completely fleshed-out yet. (and barely anyone uses pd-double)
Need help implementing "sinesum" oscillator with variable list (
@tomatoKetchup The procedure for array and table is the same. It's just that a table is not actually drawn unless you open its window so it is much faster. Hiding the array in a sub-patch might be as fast.... I am not sure about that..... but the same objects can be used to read/write a table as an array using its name.
However you are probably better off reading your data table with [tabread] .... (no tilde) ..... banged at audio rate...... where you just want the data without interpolation.....and read read your sinesum table with [tabread4~] where you do want interpolation.
You will need a cyclic counter for [tabread] counting 0-99 and [list] to collect the data before sending it onwards. If you want to go faster with the read you will need to put [tabread] in a sub-patch with [bang~] and use [block 1] to read one "bit" of the table every sample. You will get the complete data table every 100 samples like that.
As I said....... not my subject and hoping someone comes to correct me.......
It's a shame you need to use sinesum as you need the whole message at once.
Maybe there is another way.
David.