Aleph Grains

Ok so this time last year (or maybe earlier than that) I was generating some hot air on the github about a half-baked idea for a new aleph module, similar to lines but with with linear-interpolating read heads and time coordinates synced to the write head. It does lo-fi pitch-shifting, feedback, echo, some kind of sample/hold & probably possible to jerry-rig some chorus/flanger-type sounds…

Finally forced myself to put other things aside and make a concerted push hacking on ‘the fusebox’. Many coffees later and I have at least something to show for all that hot air. It’s somewhat grainy-sounding, controls are still crackly & no doubt has a bunch of other stupid bugs - not to mention that there’s currently only one voice to simplify debugging (pretty sure aleph could host 4 of these)

Nonetheless I feel like it’s time to push the warty little beast out of the nest. Here goes…

http://rickvenn.com/grains-0.1.1.zip

Watch out - the module doesn’t load itself totally cleanly but I made a trivial scene (shimmers) that should get everything up and running. So to try it out:

  • copy these files to your SD card
  • do a clean aleph boot (holding button 3)
  • then load shimmers scene.

I may write brief description of the parameters/tutorial later on…

10 Likes

i am VERY VERY EXCITED about this! great work rick.
for now though I have 2 upcoming shows ( 8th in London and 19th in Brussels if anyone’s interested :slight_smile: ) and don’t want to mess with anything ‘experimental’ on my aleph before them, but really looking forward to exploring this in the dark winter nights!

what’s the London event??

I’m playing as Valid Lover at the Sonic Imperfections night - 8pm, 8th Dec - Montague Arms - SE15 2PA
or facebook info here
https://www.facebook.com/events/978453795510993/

1 Like

Really cool! Looking forward to trying this out. My Aleph’s in the shop right now, so it’ll have to be in the new year :smile:

nice, downloaded!
characters++;

1 Like

Ha - nice I’d have liked to pay a visit to the montague but moved away from london earlier this year to live in canada (partially why I didn’t touch the aleph for ages). Thinking of that stuffed zebra in the corner is nearly making me miss the uk…

Downloading, going to load it up tomorrow. Thanks!

So here is a description of all the params in grains - next release will have a README (and fix all the bugs & crackling param changes):

ADC mix params

  • fader_i
    Stereo fader from ADC i to DACS 1&2
  • pan_i
    Stereo pan from ADC i between DACS 1&2
  • effect_i
    Send from ADC i to effect bus
  • aux0_i
    Fader from ADC i to DAC3
  • aux1_i
    Fader from ADC i to DAC4

Grain Mix params

  • fader_g
    Stereo fader from grain i to DACS 1&2
  • pan_g
    Stereo pan from grain i between DACS 1&2
  • effect_g
    Send from grain i to effect bus
  • aux0_g
    Fader from grain i to DAC3
  • aux1_g
    Fader from grain i to DAC4
  • phase_g bool
    phase of grain i (+ve or -ve)

Scrub params for each grain

  • scrubPitch_g
    speed of scrubber m read tap in subsamples (1/256 sample) - bees
    displays 1.000 at speed = 1 sample/sample
  • scrubFadeLength_g
    length for scrubber m crossfades in subsamples (1/256 sample)
  • scrubLength_g
    length of scrubber m read distance from echo readhead before cross-fading
  • scrubRandomise_g
    randomising scrubLength should make phase-modulation distortion
    less disturbing.

Echo params for each grain

  • echo fade-length
    length for scrubber crossfades in ~ms (64 sample)

  • echoTime
    allows bees to force read head to a point, e.g:

    • set echo time for simple echo fx
    • trigger one-shot sample for pre-recorded audio
  • echoSpeed
    For canonical realtime FX like this guy should be 1, so the
    echo-tap stays in sync with write head. When a sample is loaded
    into the fifo this guy replays the sample. See echoEdgeBehaviour
    for playback modes. If you’re feeling crazy unglue the echo tap
    from writing head while write head runs by setting this value != 1 .
    This feature doesn’t work so well at the moment - pitches play back
    at wrong speed when echo head ‘bounces’ or goes in reverse.

  • echoEdgeBehaviour
    set this to either 0, 1 or 2 to control does echo-tap head stall,
    wrap or bounce at echo boundary?

  • echoMin
    echo boundary nearest to write-head

  • echoMax
    echo boundary furthest from write-head

  • writeEnable
    Control if the grain’s write head runs or not? echoTap & scrubtap should continue to replay sound from the underlying buffer at the same perceived pitch, whether or not the write tap runs. So e.g play a chord through the grain, with writeEnable=1 then set writeEnable=0 to sustain the chord.

1 Like

And here’s a brief description what I call a ‘grain’:

so a grain consists a buffer, a write head running at 1x, an ‘echoTap’ & a ‘scrubTap’.

  • The write head is the same as the one in lines - I set the underlying buffer to be very long.
  • The echoTap is like a lines read head but it can run at non-integer speeds, it’s time coord is relative to the write head (wrapping round the underlying buffer is hidden) but playback speed is relative to the underlying buffer (though think there’s a subtle bug in this respect with current release).
  • The scrubTap is kind of similar to an echoTap but it’s time coord is relative to the echoTap, and it’s time coord is much finer grained than the echoTap.

Well - this all makes a twisted kind of sense to me at least anyway!

1 Like

Looks amazing (i’ll find out how it sounds when i get home)

Thank you so much for sharing your work!

hmmph - so there’s a pretty major bug in the program I released. I got up early this morning to bugfix, nailed down the dodgy module load problem. But then somehow realised for some reason reading the ‘scrubTap’ is making an impact back on the ‘echoTap’ (which is completely wrong - tap reads should return a sample with no side-effects). Actually if I’d realised this I would’ve held back on posting here…

Still can be fun to make some squeaks and bleeps with this thing & get some sense of what can be done but I doubt it can be useful for anyone’s music making until that problem is fixed.

Ok so here is what should have been a first sneak preview release of this module. (The one I posted last week was probably just too bug-ridden to be worth anyone’s time - sorry about that) Well I still don’t know whether this latest offering is musically useful to anyone but posting here is providing me with some much needed focus w.r.t aleph hacking so here goes:

http://rickvenn.com/grains-0.0.2.zip

I will release again as soon as multiple grains & FM/AM modulation between grains are done (assuming I can find a way to implement that without crackling on fade boundaries or similar horridness). Either that or more fundamental refactor/rewrite - not sure which way to jump at the moment…

2 Likes

Ok so here’s an update on progress with this module, and maybe a request for suggestions/ideas from any dsp/patch gurus lurking on here!

implemented 2 grains and the FM/AM thing as a quick hack - it kind of works. You can do fun stuff like record guitar into one grain, slow it right down then use that to make a cute pitch/volume tremolo on the other grain. But kept feeling I was missing some crucial point - got curious about puredata somewhow (yes, yes - I have been living under a rock).

So got myself up and running with pd - there’s a lot in there I should learn from. Wish I’d seen it before I started trying to roll my own on an embedded dsp - aleph is 10x more exciting to me now seeing the kind of stuff that will eventually be possible! So anyway think I figured out a couple of things looking in there. If anyone in here cares to comment/discuss these points I would appreciate it…

  1. my ‘grain’ can probably be implemented better as a sampler followed by a pitchshifter, with AM/FM probably built into the sampler section.
  2. if I implement the rotating head using a phasor then overall pitchshift time-window will scale without crackling - devil’s in the detail here!
  3. higher than linear interpolation is really important for reasonable sound quality (@zebra told me so anyway but yeah hearing the difference for myself…)
  4. Really some kind of pitch estimation would be mighty handy - a single instance of some algorithm can live outside the grains with an input selector, then bees can be responsible to set the pitch shift window length to whatever.

As an aside does anyone know a simple pitch detection algorithm which will work out when used with sample-by-sample processing? (don’t think aleph currently has block processing so fft-based algorithms are out - single sample latency is kind of my preference though anyway!)

1 Like

on 3 I´m hoping to someday get my head around how to implement interpolation based on other types of fractional numbers, hopefully 32.32, but I haven´t got to a point where I can contribute how to really get there, still just trying to figure out things, happy to share ideas in the meantime. One test I need to do is to check whether pcm samples are using signed or unsigned numbers, an idea would be to use 0 as zero-crossing on an analogue mixer, but it does not seem to work that way on the bfin as default, related to this I´m also trying to find a way to detect overflow/clipping. Currently I´m using this interpolation algorithm, it’s a start that cuts some corners to increase channel count…
https://github.com/electropourlaction/aleph/blob/dev/modules/prgm/env_tcd.c#L321 multiple channels of proper higher order interpolation seems like it could be tough with only single sample latency.

1 Like

hey @test2 so I’ve chewed over this post and poking around a bit in your dev branch. Have to admit I didn’t understand your interpolation code at all really! Might help if I try to start as a user of your module and try to get a better mental picture what’s going on…

  • In response to your question are pcm samples signed or unsigned - I’m under the impression that fract32 and s32 can be used interchangeably. and both get resolved to int32_t. This stuff is defined in common/types.h I think. I stick to a convention using fract32 for audio data & s32 for all other numbers, to remind me what’s what…

  • the question of radix for fine-grained time variable is an interesting one. I finally settled on 24.8 for interpolating read heads after much pontification - at 48KHz sample rate this gives time resolution of 80ns (1/20 semitone pitch resolution) and max sample time of 3 minutes, whilst packing the time variable in a single s32. It seems to me that 32.32 interpolation is overkill - found it quite fiddly enough figuring out sensible mappings from bees’ native s16 onto the full range of a 32 bit time variable (it requires coarse & fine time coords at least). Or is there some more subtle reason to have ultra fine-grained time axis?

  • on the question of clipping - I believe mult_fr1x32x32 et. al. clip (i.e they don’t overflow) - so if you ever see 0x7FFFFFFF (== FR32_MAX == 1) or 0x80000000 (== -1) on an audio bus this indicates clipping.

  • So I think where you write #define FR32_MAX2 0xffffffff this actually corresponds to the fractional value -1, rather than the fractional value 2.

  • also thanks for the reference to Olli Niemitalo’s paper - some nice bedtime reading…

meanwhile on this module I have decided to go back to the drawing board, build a mock-up of ‘grains’ in puredata in order to really plan exactly how everything should work, only returning to the C code when I have a very clear mental model what I’m building…

1 Like

@rick_monster thanks, you might very well have saved my brain from dsp overflow!

the interpolation I use is taken straight out of that document (those code examples seem to work very well with the bfin). 1.31 gave me unexpected problems when defining those constants, but 8.24 might make it easier to try different types I presume, and then there is oversampling too, which I haven’t even tried yet.

I’m gonna need to revisit that FR32_MAX2, I think I was hoping for that an unsigned variable would give me 2 like that but it might very well be as you say.

to be honest I haven’t grasped the implications of different fractionals (though it’s getting a bit clearer now), as the saying goes, ignorance is bliss :smile:

btw I found an example of a simple pitch tracker here http://faust.grame.fr/images/faust-tutorial2.pdf

2 Likes

bear in mind my 24.8 math is not ‘hardware-accelerated’ in any sense - just kind of rolled my own in an adhoc way. It’s very naiive - if anyone knows a better way I’d love to know!

256 ‘subsamples’ == 1 sample. All sample index math trots along in units of s32 subsamples until it’s time to read. Then:

  • To get integer sample index: subsamples / 256
  • To get inter-sample distance as a fract32: (subsamples % 256) * (FR32_MAX / 256)

Also that pitch tracker algorithm looks totally do-able and probably useful for something at least! Awesome pdf thanks for the links…

sorry there was a mistake in the info I gave on fractional arithmetic - the most negative value is 0x80000000 (-1 in terms of fract32). 0xFFFFFFFF is the smallest negative value (-1 in terms of s32)