By the way, this is what I came up with, thank you all for your help! (sorry, the video will actually come up in a few hours)
@jameslo a lop in the feedback path is something I tried but I only found it useful to shape the spectrum rather than smooth out the signal to the dac. I ended up with some crazy sets of filters and it's great fun. When I do key-tracked lops I like to put the cutoff either right on the fundamental or maybe at 2x, 3x, depending on how steep the rolloff is. Thanks for the pointer, I'll have a look!
@Booberg Yes, that occurred to me and in effect I'm doing more or less that. Filtering the feedback chain like @jameslo says is also useful in containing unstable signals. However, in this case I'm not dealing with an unstable signal per se. I think the problem really is that the excitation signal has too harsh of an attack and the delay being too long makes the repeats noticeable as almost-separate events. A partial solution at this point is to either smooth the attack of the exciter or low-pass the output signal.
In general, though, I was wondering how using
[gen~ ]manages to smooth things out without requiring filtering, and it seems the sample-level processing (as opposed to block-level) has something to do with it.
I'll admit I don't know much about KS but I understand the principle so I made this patch here (kstest.pd), initially with the standard
delread4~construct (and no
[block~ 1]). It sounded OK but not great so I set out to learn some more and I stumbled upon this Max/MSP video
which at 9:58 has about the same issue I was experiencing (gritty sound, especially at lower notes). Now, with some tweaking, I managed to make my patch sound nice enough — namely, with a certain amount of
[lop~ ]s cutting around each note's fundamental. You can see this in the
[pd ks]subpatch — EDIT scratch that, I actually removed the LPs to have a fairer comparison down the line. Carry on
However, the video carries on with using Max's
[gen~]which I don't understand very much about but it makes everything sound a lot smoother (see at about 11:50). I tried my hand at something similar with cyclone's
delay~and using a block size of 1 sample in the subpatch but to not much avail — though I'm tempted to believe I'm doing it wrong in
tabwrite~but I got told off by PD for creating a DSP loop hooking the output of
delay~to its own input via a damping factor so I figured.
As far as I can tell, there isn't very much difference at the moment between the two patches, at least on the aural side of things: they sound very much the same to my ears! So I was wondering if I'm stuck with copious amounts of filtering or if there's some kind of trickery I'm missing. There is a closed issue in PD's repo regarding this but I couldn't understand much of the theory and practicalities behind the discussion.
Any advice and/or pointers are appreciated
Right, so, to "conclude", here's what I have so far: granular_30.zip. I'm also throwing in the patch to make it polyphonic. Unfortunately, on my machine I can only run 4 voices polyphony, each composed of 8 grain clones, and only by increasing the block size to 256, which isn't even that bad.
If I try to increase the polyphony by even only one voice, or the grain counts by one, I get quite a dramatic deterioration in quality/performance and a stupid amount of latency. Have I reached the limits of the engine this easily? Not that I really need more for the moment, but it seems like a pretty dramatic limitation for a granular synth, at least one done this way.
@whale-av Aha, thanks! I think I'm moving forward a bit here! But also a bit backwards.
So, I got out of the "last pitch" issue, that's great. Here is what I achieved so far: granular_20.zip. I'm throwing in one of the samples I'm using to demonstrate why I'm going backwards.
First, though, I get clicking between notes when the
[legato-mrn]patch does its job (it's more noticeable with quieter samples and I'm sure it also happens with staccato notes but I haven't been able to hear it. Anyway, I figured I'd put another
[vline~]right after the grainSpeed bit but I'm getting some horrible zipping noise (which I suspect is just a slower version of the clicks).
Second issue, higher notes start later in the sample, so basically the higher you go, the closer to the end of the sample you start playing from. I think this is related to the way I'm modifying the output of the grainData
[vline~]. I'll play around with it a bit more but if anyone can spot an obvious solution, I'll be forever grateful at this point
(no pressure, but I'm hoping to use this one in a video that should be out in about 48 hours, so… and I'll also try to make this polyphonic, by cloning the
granularpatch, let's hope I don't kill my PD!)
EDIT: Ok, scratch the second issue, I had a day-long brain freeze that thawed only now. The first issue persists but now that I have it running polyphonically it's not even that bad.
@solipp No worries, it's late over here too so I can't quite wrap my head around your suggestions. I tried the counter solution too, decrementing by 1 on note off. If I wait long enough between notes, I get the desired result. If I play them too much back-to-back, I still get the last grain in the old pitch.
I feel like I'm fighting against bangs here -- and it turns out the second archive I attached is identical to the first one. Anyway, it'd be great if you or anybody else could have a play with the patch because at this point it's clear I'm too much of a noob to follow any explanation
@solipp No, that's one of the solutions I thought of, but I couldn't figure out how to do it…
@solipp That's what I was doing but I was still hitting the "previous pitch" issue, then I refactored it with the
[s $0-grainSpeed]because I thought it was an issue with a "leftover" float somewhere so I thought if I sent the right value to all the grains at the same time I'd avoid the problem but no.
See here: Archive 2.zip
I'm working on this (monophonic) granular patch attached (Archive.zip) and I've been having one issue in particular that really bothers me: if I play two subsequent staccato notes, the second note plays the first grain as the pitch of the previous note, and everything else is fine. Imagine if I played C4, then rest, then C3, I can hear C4 for the first grain, and then the note continues at the correct pitch.
So, I fumbled around a bit with the
grain.pdand I got something working with a bunch of triggers. However, now it's like all the grains play together at the onset of each note, and then everything goes back to being fine. The problem is that I not only have a sudden spike at the onset of each note, as if I had an attack-delay followed by a sustain at, say, 50%, but also for that first part I get some considerable clicking, which I presume is PD's way of telling me that I'm having it do too much stuff at the same time.
grain.pdpatch, there's a connection I marked as "cursed": if you leave it, you have all the clicking and spiking, but the pitch is correct. If you remove it, the audio is fine except for the first grain which plays with the previous pitch.
@seb-harmonik.ar Aha, thanks! That explains it clearly enough. Yeah, I learned to trust the compiler to do the right thing most of the time but since this is C I wouldn't expect it to select the most appropriate implementation. Chances are the compiler will only be able to safely do implicit casts, which have costs. I'll stick to 32 bit functions if this is the canon at the moment.