as the controls are logarithmic, it won't go to complete zero.
though the way it is set up should be able to be modded such that a zero-rate playback is possible (though this would need a "rate" control multiplying the frequencies of the readout-phases. Perhaps... one more control wouldn't hurt? XD ...... ok,added it, done haha).
SirSickSik Contributions
Ahh yes, this might the the problem I had also. My approach was also using logarithmic scaling, so might be the same issue. I just didnt know how to put it in words. Glad you did
Mine works steadily untill 32X stretch, and then goes bunkers from there.
Let us know when you decided name and if you upload it to com.lib
with mine, with lots of down-pitchshifting it does impose quantification of the signal and both the pitchshift as well as the timestretch quality depend on the window size, which can be the total buffer. So it's more like a universal granular time manipulator.
I've just uploaded the module.
I've called it "Factotum" (capital F!!), which means in dutch "manusje-van-alles", someone who can fix all kinds of things. Though then again, it would be more "mashing up" than "fixing"... XD
@SirSickSik that looks pretty promising!
You think it would be possible to have this split into several objects? Like one that records and one that plays back and does the stretching / granular?
I personally have built a sophisticated looping system using @rbrt blocks and would love to have timestretched playback but still use my own recording mechanisms...
Anyways I'll try your object later, looks really nice
not sure, it might be possible (not looked yet) to read out external buffers, but the module needs to know the exact size of the buffer, which is depending on the internally saved recording time.
Differently said, I'm not sure whether you could just put another module in the middle for apllying functions.
Though you could put it in "overwrite" mode, use the timestretch control and listen to the effect.
Play with the window/length knobs to adjust characteristics of windowsize.
Also, the way I've done this shouldn't be to hard to add to that looping system of rbrt.
I measure the bufferlength of recordigns in samples, so you I could feed this into an external inlet.
The object is not a sampler, right? It is more like a live sampling device or something like that, that you need to feed a signal through?
it's more intended for live sampling/delay/pitchshifting/granulazing of incoming audio like a synth, drums or guitar.
It's definitely working now, and I swear this is one of the best compressors I've used, cheers Remco
Works especially well as a 'creative' compressor, excellent for working on rhythmic material, drums etc. Makes a nice bus compressor as well in my opinion, although those who prefer surgical compressors would probably disagree with me. I generally put compressors into two categories, surgical and creative. Generally not a fan of surgical compressors (or pansy compressors as I like to call them). But this falls well and truly under "creative" compression - love it!
Was supposed to be playing with the sampler today, but your compressor has taken over - hehe
Dude Factotum is f*ing killer. Simply the best. I put four of them together cuz i liked your four track looper and trying to keep to my four track roots, plus I work with Ehx 2880 and Electrix Repeater as my main loopers/heart of my rig.. I added a couple fx, a delay, that reverb gizmo from mutable elements. Simple Monotribe drums sound crazy huge. little melodies become glitchy flutes fluttering. it is insane. Finally ordering another axo to use this full time. got to midi it up and build a ipad template, add some lfos and oscs to modulate..., well, everything... Can't thank you enough! You are legend.
slowly I'm uploading some modules from the past month
for today:
NEW MODULES
FILTER
"bppBIQ" and "bppSVF"
two filters containing 3 filters each (LP, HP and BP), one fully biquad, the other SVF.
The LP and HP work together to form a bandpass filter (serially connected), the BP filter is able to sweep between high/low cutoff frequencies and is connected in parallel, adding it's output to the LP/HP band pair..
The biquad version mostly removes lots of frequencies as the resonance is more of a slope-control of the filter instead of adding a resonance-peak. Instead, the SVF version adds lots of resonance, which is btw internally soft-distorted. Also, to also saveguard the output, I had to divide the input volume by 2. So the svf-version is the more "dirty/creative" one and the biquad version the surgical one.
As they're set up using object-oriënted coding, more filters can easily be added, creating your own fat filter.
The SVF type sounds like they'd be ideal for making the sort of parametric EQ's I want to build, but I don't get it, why does everone keep missing-out Band Reject (or band Eliminate) filters from their filter sets?
Mate, this would likely be the ultimate filter set if it had band elimiate as well, so would that be possible?
I mean in both versions, BIQ and SVF.
hey @SirSickSik ,
I've just looked into 'Factotum' to see if I could somehow adapt all the nice timestretching/pitchshifting,
looks to me like it would be possible to merge this with 'ldrive' , because internally this one is working with samples-units and it 'knows' about the precise length of the recording...
it just scales up to 'fraction of table' because I never got to the point that I understood
'table/read interp'
can U maybe send my just the playback-algorhythm 'stripped down' in a way that I could just connect there with a playhead-position in samples?
does this make sense?
for parametric EQ's you can better use biquad filters. But the problem is that the code used for the biquad filters doesn't feature the highshelf/lowshelf/allpass coefficients calculations..
I had given it a try a year ago, writing my own biquad module, but back then I needed to do it in floats as I still got a bit confused of all the bit shifting, making the module way too cpu heavy.. Perhaps I should give it another try..
As for the notch filter, basically it's often just the bandpass subtracted from the original filter.
That's why in lots of hardware filters, you only got the LP,HP and BP. To get another filter, you just subtract/add it from/to the original signal.
eg. a lowshelf filter could be made by adding/subtracting a lowpass filtered signal to it's original.
an allpass could be made by subtracting the original from the lowpass filter to obtain an inversed highpassed signal. Add these together and you'll have a simple allpass where the high freq part is inverted and the low freq part is normal.
Ah right, I actually thought it might have someting to do with that some time back, cause I knew that using HP and LP gives BP. The bit that confuses me is how to actually add/subtract a whole filter from another one. I tried some time back pretty much after I first got my Axoloti, so I'll try again now that I know it can be done.
Thanks!
First you start with a couple of facts:
-you know the length of your phase-generator (32bits)
-you know the length of your recording (in samples)
-you know the rate of the recording: 1 sample position every sample-moment. Thus for a recording of 64 samples, the end of the phase, used for playback, should be reached in 64 samples.
As you know the length of the phase (32bit->(1<(1< is the rate needed to drive the phase generator for normal playback if the phase will be taken as the full length of the recording.
now to scale the phase to the delayline length:
-add a (int64_t) in the function to enable the code to give 64-bit anwsers instead of wrapping when going above 32bit. Multiply the phase with the length of the recording and remove the size of the phase itself by left-bitshifting 32bits: Let's call the anwser "p1". (length is the recording length is samples)
int32_t p1 = ( int64_t ) phase * length >> 32;
p1 will be used to select which array-position will be read. But as you can see, we've thrown away lots of bits, as the length doesn't even come close to 32bit. We can get these bits back and use these to mix between the position we've just calculated and one sample next to that. This will get a linear interpolation between the two sample points.
To get the "mix" crossfade value, we get the p1 value back to it's original strength by doing the same calculation, but the other way (back). But as we've thrown away bits, it will be quantized and different from the original value.
So, lets call this value p2 (note the brackets and the int64_t, though not sure if you really need the first (int64_t)):
int32_t p2 = ( int64_t ) ( ( int64_t ) p1 << 32 ) / length;
To get back the bits that we've thrown away, we subtract the quantized (p2) from the original signal (phase) and put it in "mix".
int32_t mix = ( int64_t ) ( phase-p2 ) * length >> 2;
As you can see I also multiply the difference with "length" and then left-bitshift by 2 ( divide by 4).
Multiplying it by the "length" scales the difference-left-over back to 32bit (full phase range). To make the value easily usable in the crossfade, I convert it to a range of 30bit using the ">>2"
To read out the buffers using p1, we need to make a copy that is 1 sample ahead for the second readout. Note though, that when the phase restarts at 0, the 1 sample ahead should already have done that, so just adding 1 to the phase isn't really helping as it would just try to read 1 sample further then the lenght of the recording.
To wrap the signal, we use the same kind of calculation. We can reuse the name "p2" as we don't need the value in that one any longer. The first line adds +1 to p1. p2/length*length will first make p2 smaller, losing bits and thus quantising the signal and then get it back to original size. As soon as p2 reaches length "p2/length" will go to 1, multiplying it with length, makes it.... padumta.. the length... subtracting it from p2 makes it reset back to 0 one sample ahead of p1.
p2 = p1 + 1;
p2=p2-p2/length*length;
now to use these readout positions, we put them into the outcome "out":
int32_t out = array[p1] + ___SMMUL( array[p2] - array[p1] <<2 , mix );
so, array[p1] is the "base" value to be send to "out". But as soon as array[p2]-array[p1] gets mixed in, array[p1] gets mixed out at the same rate as array[p2] gets mixed in, as the negative addition to the "base" value "array[p1]" effectively neutralises it.
so the base function for converting phase to readposition is:
int32_t p1 = ( int64_t ) phase * length >> 32;
int32_t p2 = ( int64_t ) ( ( int64_t ) p1 << 32 ) / length;
int32_t mix = ( int64_t ) ( phase-p2 ) * length >> 2;
p2 = p1 + 1;
p2=p2-p2/length*length;
int32_t out = array[p1] + ___SMMUL( array[p2] - array[p1] <<2 , mix );
Though, this will still cause clicks when the readposition loops.
So what I did was that while I record, a counter (count) is running and as soon the recording stops, the counter stops and it saves this value as the length value.
But, after it stops, it starts a second counter (COUNT) to keep the recording going ( if((REC>0)||COUNT>0)) for another 1024/2048/4096/8192 samples
(fadeout setting: attr_fade ).
in krate code:
if ( ( param_rec > 0 ) && !rec ) {
rec = 1;
REC = 1;
count=0;
else if ( ( param_rec == 0 ) && rec ){length=count;rec=0;COUNT=(1<<attr_fade);}
in audio rate code:
if ( ( REC > 0 ) || ( COUNT>0 ) )
{
if ( COUNT > 0 )
{
COUNT - = 1 ;
}
array[count] = inlet_in;
count + = 1;
}
So even though REC goes to zero, COUNT is set to the length set by (1<<attr_fade), for example (1<<10)=1024, and starts counting down. When it reaches zero, the recording stops.
So the total length of the recorded buffer is length+1024 (=length+(1<<attr_fade))
Now to remove the clicks!
Whenever the phase wraps we want it to fade out from the position before it wrapped (and would have played on) and fade in the part after the wrap (where it restarts).
We can easily do this by using the readposition p1. We make a "crossfade/mix" value, subtracting p1 from the length and clipping it so it cannot go below zero (the 31 is not really important, but shouldn't be smaller then the buffer length of course) :
int32_t fade = ( (int64_t) __USAT( 1024 - p1 , 31 ) <<30 -10) ;
Now we have to fade in the start of the buffer while fading out the end of the buffer (leaving from the last sample it played before the wrap)
here we double the array[....] readouts to enable an overlap, so that's why I put it into an object-oriented code ( int32_t READ(..bla bla bla..){..bla bla bla..} -> see the module.
Anyways, while crossfading, we just use the normal p1/p2 phases.
The looping readout will just start where it has to start (eg. you could use offsets for start position).
The fade-out readout will add p1/p2 to the last position it was before the wrap + 1 sample. (recording length or perhaps a manually set loop-point)
This way, while the loop fades in at the start of the buffer, the fade-out part fades out the piece of the recording that would have come after the wrap.
Ok, this last part isn't really clear, but I want to do other things and I've been typing far too long XD
But this should get you on the way I think
12 characters needed... hmm... yes.. haha
Hey SSS!
Some thoughts and wishes on your Factotum. If you don't mind, I was wondering if you could elaborate on how it works, it would be much appreciated!! The Length, Playback Speed, Pitchshift, Rate, and Stretch make sense but I was hoping for a description of the Rate Divisor, Quantize, and the 2 Delay Line parts and how they relate to the ShiftSize or Window and Pitchshift/Stretch controls.
The description popup of the play direction is the same on both the inlet and on board control, I think you meant to describe it on one, so what is that third direction? I would love a one shot option as well, would be cool to use with the sync function.
The rate doesn't work as I expected, regular speed is at 64 ( or -64 in reverse.) So it only slows down. I guess I wish 0 (12:00) was stopped playing, 64/-64 = x2/-x2, 32/-32 = x1/-x1 (regular speed.) What's the difference between Playback speed and Rate?
Another request is instead of a length control I wish it had separate start and end point controls, so you could focus on a section of your sample, a custom window if you will. With Start all the way counterclockwise, and End all the way clockwise would be full the sample. If they cross each other, then it plays that part in reverse. This is taken from the functionality of the amazing "Where's the Party At? 2" diy lofi sampler. It's a lot of fun to zero in on different parts or tiny grains, anywhere in your sample.
Anyhow, brilliant work as usual, as all your creations are. You've been invaluable to this device and we're all indebted to you and your hard work. Thank you!
Side note: I couldn't find your newest filters, the bpp's? Maybe i'm not looking in the right place?!?
the rate divisor is just a linear "gain control" of the frequency of the phase, so if you bitshift that part of the code by eg "<<3", then 1x rate would be at 8. (remember, you can always embed a module and update it to the needs of the patch).
But currently if it's to 32, it should just play half the speed of whatever the pitch/stretch controls tell it to play.
Quantize divides the recording length (based on a power of 2) and uses this length to quantize the shift, so it only shifts to equal distances. So if the knob is totally to the right, it only offsets play position by two sizes (0 phase and 180 phase). So if you've recorded for a length of 2 beats, the shift will shift to another position that's on the beat. If set to 4, it can also shift to a half beat into the recording (2/4).
The shiftposition offsets the readout position between the two buffers being read and which are used to window the recording to avoid clicking. Normally this isn't done as it would screw up a good pitching effect, but well, that is exactly why it's there XD. Each time the window fades to the other buffer, so it can "reset" the one previously in use, it also adds the shiftsize to it's play position, making it start from another point then it normally should do.
So if you record 4 beats, window full, use a quantification of 4, set shift to 4 and set length to 16 , it will:
-play only one beat each window (as the length is set to a quarter)
-will repeat this beat 4 times, before reading out the next beat. (as the shift is set a quarter of the length. As it thus shifts in 1/16th and quantification is set to 4 steps, 0/16 to 3/16 will all be rounded to 0/4 and 4/16 to 7/16 will be rounded to 1/4)
ps. I admit a "quantification of 4" is a bit difficult as there is no readout for this and should be done by ear...
about the playdirection:
0=normal
1=reversed
2=input controls the playdirection by sending a 0 or a 1.
about the bbp filters, I guess I forgot to hit "sync libraries"
Really enjoy using your modules, but am having issues getting pllOSC to work. Above I attached the error messages. Any insight?
hmmm yes.... that's indeed not going to work.. how did I came up with that?!? XD
it's kinda fixed. though I must say, although it has some very interesting agressive sound, I'm not sure whether this really falls under PLL... XD