How to plot minblep/hard sync?

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

Hi,

I'm currently plotting my wave shape (on a wavetable synth) easily iterating each sample, such as:

Code: Select all

for (size_t i = 0; i <= wavetable.mWaveLen; i += iSkip) {
	// get wave value
	float wave;
	float wave0 = wavetable.at(pos0, i % wavetable.mWaveLen);
	if (posF > 0.0f) {
		float wave1 = wavetable.at(pos0 + 1, i % wavetable.mWaveLen);
		wave = crossfade(wave0, wave1, posF);
	} else {
		wave = wave0;
	}

	// add point to line
	Vec p;
	p.x = float(i) / (float)wavetable.mWaveLen;
	p.y = 0.5f - 0.5f * wave;
	p = scopeRect.pos + scopeRect.size * p;
	if (i == 0) {
		nvgMoveTo(args.vg, VEC_ARGS(p));
	} else {
		nvgLineTo(args.vg, VEC_ARGS(p));
	}
}
Now, I'd like to add an internal hard sync knob, which will hard reset the signal using a min blep generator.

But than, how would I plot it? Not sure I can just iterating each sample...

Thanks for any tips.

Post

I'd probably just change the wavetable reads to read at mod(i*syncMult,waveLen) instead of i%waveLen (and then do interpolated read), since this way you'll be drawing the same number of points no matter what. Downside is that if the waveform is wiggly, we'll skip more samples when syncMult gets bigger, so perhaps you could read from mipmaps in the drawing code too?

The other alternative is to just change the loop limit from wavetable.mWaveLen to (size_t)ceil(wavetable.mWaveLen*syncMult) and divide p.x by syncMult. This will multiply the number of points plotted by syncMult though, so could have a performance impact depending on how many points are there to begin with and how fast nvg can stroke those (no idea how well it scales, 'cos I haven't used it). If performance isn't a problem, then this might look a bit better though.

Post

mystran wrote: Wed Oct 11, 2023 8:47 am I'd probably just change the wavetable reads to read at mod(i*syncMult,waveLen) instead of i%waveLen (and then do interpolated read), since this way you'll be drawing the same number of points no matter what. Downside is that if the waveform is wiggly, we'll skip more samples when syncMult gets bigger, so perhaps you could read from mipmaps in the drawing code too?

The other alternative is to just change the loop limit from wavetable.mWaveLen to (size_t)ceil(wavetable.mWaveLen*syncMult) and divide p.x by syncMult. This will multiply the number of points plotted by syncMult though, so could have a performance impact depending on how many points are there to begin with and how fast nvg can stroke those (no idea how well it scales, 'cos I haven't used it). If performance isn't a problem, then this might look a bit better though.
Thanks for the reply. In your suggestion, syncMult refers to the delta sync span?
Heres the starting point I'm using: https://github.com/VCVRack/Fundamental/ ... O.cpp#L209

Basically, I'll trigger the Sync with an interal LFO (not external signal as for the Rack module), so would be nice to also plot it.

The table will be 1024 samples.

Post

Note: of course, while increasing hard sync knob, the freq of signal increase as well, so It needs to increase the plot as well (thats pretty fancy anyway, just multiply by rate).

still not sure what you mean with syncMult @mystran...

Post

Q8FuelDSP wrote: Mon Oct 16, 2023 5:13 pm still not sure what you mean with syncMult @mystran...
Oh.. the most type of "internal hard-sync" is one where you have the "master" stay at the nominal pitch and the "slave" is at some multiple of that frequency... so "syncMult" would be that multiple.

Post

mystran wrote: Mon Oct 16, 2023 10:46 pm
Q8FuelDSP wrote: Mon Oct 16, 2023 5:13 pm still not sure what you mean with syncMult @mystran...
Oh.. the most type of "internal hard-sync" is one where you have the "master" stay at the nominal pitch and the "slave" is at some multiple of that frequency... so "syncMult" would be that multiple.
Clear, make sense. I'll try your suggestion, many thanks.

What confuse me is the fact that most synth display the waveform twice, instead of a single cycle, not sure why...

Post

Q8FuelDSP wrote: Thu Oct 19, 2023 3:37 pm What confuse me is the fact that most synth display the waveform twice, instead of a single cycle, not sure why...
I'd imagine that's what we call "artistic choices" .. perhaps it looks nicer to whoever designed it.

Post

Q8FuelDSP wrote: Thu Oct 19, 2023 3:37 pm What confuse me is the fact that most synth display the waveform twice, instead of a single cycle, not sure why...
On the synth I'm developing there's a view mode where you see three waveforms. I think the Waldorf Iridium shows this too.

The reason, aside from aesthetic, is so that you can easily see discontinuities at either side of the waveform. It gives you a visual on the wave over more than one cycle.

Post

JustinJ wrote: Fri Oct 20, 2023 6:48 am
Q8FuelDSP wrote: Thu Oct 19, 2023 3:37 pm What confuse me is the fact that most synth display the waveform twice, instead of a single cycle, not sure why...
On the synth I'm developing there's a view mode where you see three waveforms. I think the Waldorf Iridium shows this too.

The reason, aside from aesthetic, is so that you can easily see discontinuities at either side of the waveform. It gives you a visual on the wave over more than one cycle.
But every cycle will be the same, so I just find it redundant, isn't?

Post

Q8FuelDSP wrote: Fri Oct 20, 2023 7:24 am
JustinJ wrote: Fri Oct 20, 2023 6:48 am The reason, aside from aesthetic, is so that you can easily see discontinuities at either side of the waveform. It gives you a visual on the wave over more than one cycle.
But every cycle will be the same, so I just find it redundant, isn't?
Yes, it is redundant, but sometimes that's what you want. On the other hand, there are also plenty of synths that just plot a single cycle and even for basic waveforms there is variation on they are aligned (eg. is the discontinuity of a saw at the edges or center of the display, do you align triangles at peaks or zero crossings, perhaps make them even or odd symmetric on the display, etc).

Also since your waveform displays are part of some GUI, consider what style is the rest of the GUI following and what sort of plots should be stylistically a good match for the rest of the GUI. Do you stroke the waveform, perhaps fill between zero-line and waveform value, perhaps approximate how it would look on an analog scope?

It's not like you even need an integer number of cycles. You could say draw one cycle of the waveform between say x=15% and x=85% and then fill the edges with wrap around data, perhaps in a faded out color to show how it extend to previous/next cycles (so making discontinuities at edges visible) without having to give up resolution to fit two full cycles.

Ultimately though, you need to figure out what works best for your plugin, because UX isn't just hard science where there's one correct answer, rather it exists in the unique space between technology and human psychology. Try different things, see what feels best.

Post

mystran wrote: Fri Oct 20, 2023 8:12 am It's not like you even need an integer number of cycles. You could say draw one cycle of the waveform between say x=15% and x=85% and then fill the edges with wrap around data, perhaps in a faded out color to show how it extend to previous/next cycles (so making discontinuities at edges visible) without having to give up resolution to fit two full cycles.
That's something I'd considered, and a good idea IMHO. Plotting three entire waves is a little wasteful on space when it's only the discontinuities at the edges that you want to have visible and without a visual cue (such as the faded out colour) a user might think that it represents the whole wave instead of a periodic replication.

Post

Hi,

I hope I may ask a related question about this topic (with the code I'm currently learn/using).

Not sure about the meaning of syncCrossing: why should I need to calculate the delta between the amount of “cross” in respect of the prev value in regard of external signal?

For what I understand, an external signal (master) should reset the phase (to 0) of the slave signal (i.e. the main osc) at that sample: why the amount of the external signal passing from last value to the new one (crossing 0 axis) is considered?

I mean: if the external signal pass from -5V to 2V, it should reset the slave osc signal at the same phase of an hypotetic master signal that pass from -3V to 2V, in that exact sample.
And for catch when it cross, I believe its sufficient to check when prev value is negative, and the next is positive (so the trigger => phase 0 on master phase).

The delta, paradoxically, could make sense on slave signal (i.e. the audio one), considering its prev value and the next one at phase 0. Thats the “two points” I need to “link” in a bandlimited way; not the external one :O

Instead, when syncCrossing/external signal cross differently, it will reset the phase (and the resulting transient) differently, which I don't get.

Can you explain to me this in simple words? (if possible)

Thanks

Post

Q8FuelDSP wrote: Thu Nov 09, 2023 8:01 am I mean: if the external signal pass from -5V to 2V, it should reset the slave osc signal at the same phase of an hypotetic master signal that pass from -3V to 2V, in that exact sample.
And for catch when it cross, I believe its sufficient to check when prev value is negative, and the next is positive (so the trigger => phase 0 on master phase).
This is sufficient to show that there is a zero crossing between those two sampling points, however it does not tell use where within the sampling interval said zero crossing would be. Rounding the reset point to a full sample results in more aliasing, basically, so even if there was no other anti-aliasing strategy used, you get slightly cleaner results if you estimate a more accurate reset point.

Post

mystran wrote: Thu Nov 09, 2023 8:27 am
Q8FuelDSP wrote: Thu Nov 09, 2023 8:01 am I mean: if the external signal pass from -5V to 2V, it should reset the slave osc signal at the same phase of an hypotetic master signal that pass from -3V to 2V, in that exact sample.
And for catch when it cross, I believe its sufficient to check when prev value is negative, and the next is positive (so the trigger => phase 0 on master phase).
This is sufficient to show that there is a zero crossing between those two sampling points, however it does not tell use where within the sampling interval said zero crossing would be. Rounding the reset point to a full sample results in more aliasing, basically, so even if there was no other anti-aliasing strategy used, you get slightly cleaner results if you estimate a more accurate reset point.
Not yet sure about the benefit in terms of aliasing.

This approch looks to me more realistic in regard of exactly when the cross happens (i.e. from -5v to 5v exactly at the half path between the distance PP, delta = 0.5), i.e. evaluate when the trigger happens in "continuous time".

But if I discard this precision, and I just "suppose" the trigger really happens at full sample (p or p+1), why the slave signal would suffer more alias? It doesn't know (:P) when the trigger happens, so it should just add discontinuity at every single position (even integer ones), in the same way it could happens in the middle of two points :?: Ok, it will "false" the exact starting point, but alias I believe would be compensated in any case?

Edge case for an example: feed an external signal which goes from -5v to 0.001v, delta become 1.0, so it will add discontinuity at full sample; are you saying that this will get more aliasing?

Post

Q8FuelDSP wrote: Thu Nov 09, 2023 12:23 pm But if I discard this precision, and I just "suppose" the trigger really happens at full sample (p or p+1), why the slave signal would suffer more alias? It doesn't know (:P) when the trigger happens, so it should just add discontinuity at every single position (even integer ones), in the same way it could happens in the middle of two points :?: Ok, it will "false" the exact starting point, but alias I believe would be compensated in any case?
Another way to think of aliasing is as a sort of "false periodicity" and if the true period was say 654.3 samples and sometimes we sync at 654 samples and sometimes 655 samples, then you get various unwanted harmonics depending on how the 654 and 655 periods interleave. Solving for a more accurate approximation of the true period and then handling the hardsync so that the slave phase is allowed to accumulate for the partial sampling interval after the estimated sync point can help mitigate this somewhat.

Post Reply

Return to “DSP and Plugin Development”