I HATE sample accurate coding! (audio-rate incompetence and help needed inside)

Rodrigo's icon

So my patching has grown by leaps and bounds in the last few months and I've gotten some amazing stuff working. Where I often fall short on things is with audio rate stuff. Specifically audiorate triggering/enveloping/etc... Things that I'm used to doing with control objects, but haven't quite wrapped my head around how to do the equivalent in MSP objects.

Here are three problems I've run into (several times, but specifically with the patch I'm currently working on (a virtual CD skipping machine)).

1. I've defined a small loop within a buffer using position([+~]) and window([%~]) controls (rather than min/max in groove~) so it can wrap around 'zero'. That's fine and dandy, but I get clicks when it repeats. I've cobbled together an envelope but it's half max objects and half msp objects. I'm using msp objects to figure out where to do it, but then I'm using edge~, a message box, and line~ at the end. I can kind of 'tune' it so it's declicking, but it's sloppy. So trying to figure out how to declick a loop in the middle of a phasor~ (from 0.15 to 0.3 for example).

2. When moving the position/window I'm crossfading to a granular synth so I don't get that gritty/zippery stuff that comes with moving a live play~ head. I'm using line~ and slide~ being triggered by a message box. It works but it still lets through little clicks sometimes. It could be because my tuning numbers are off, but again, I suspect it's because I'm trying to do MSP things with Max objects.

3. So I'm using a master phasor~ that represents the whole buffer, which is followed by [%~] & [+~] to define a window, and playback position. This is pretty easy and works perfectly, EXCEPT every time the master phasor resets to 0, it breaks the wrapping of [%~] and jumps it back to 0., and I get an hiccup in the audio (as well as a click).

Here is these three bits isolated and annotated.
Can someone have mercy on my audiorate fuckery?

Max Patch
Copy patch and select New From Clipboard in Max.

Roman Thilenius's icon

the topic says you´d need incompetence among other things. i have plenty of that to give.

triggering something at audio rate is in many situations just not possible, at least
you the way you are currently trying it. it is a real trap, whenever you think you found
a way, somewhere wlese in your patch there is a leap where you can no longer come
from audio to audio.

there are third party objects which can be of help, but i would recommend you to
find yourr own way.

the main trick is to accept that everything audio MUST be started at one point from
data.
for example, you can sychrnonize different obejcts by puttin them into a poly
and the stop and restart audio on that poly - everythign will start at the 1st sample
of the same vector.
and when you rethink/rebuild you patch starting from a bang or 0/1 command,
you will also find that the one or other object will simply not work under this government.
index~ or count~ are usually the preferred objects as "master timeline", and objects like
adsr~ might be incompatible with such a patch.

AudioLemon's icon

I presume I misunderstood the question as usual :) but for me repeatedly watching Delicious's YouTube Pitch Shifting Tutorial - http://www.youtube.com/watch?v=uyzY_ZP54pA&feature=related and studying Nobuyasu Sakonda Sugar Synth - https://cycling74.com/forums/new-granular-synth-patch-sugarsynth help as I am trying to understand audio rate control.

From the above I learned to do audio rate enveloping for clicks with a phasor~ driven cosine controlling an amp. Sugar synth shows how to use sample and hold to force changes to the audio stream to happen only when the cosine controlling the amp is below 0.1 or very very quiet. This reduces all the garbage sounds. Again I most likely completely misunderstand.

Rodrigo's icon

Heh, I'll take incompetence where I can get it!

I'm not patching anything like a sequencer where I have a 'master clock' as such, nor am I a stickler for 'audio rate everywhere'. It's only the places where I can hear it that I'm concerned with (clicks/jumps).

That makes sense but where I flub it is that I'm using control objects in the middle of an audiorate objects. Like my windowing for example one. I have this:

[sig~]
[>=~]
[edge~]
(1, 0 20 1 20)
[line~ 1]

So I'm getting a trigger at a reasonable place, but I'm converting that into a bang, and then generating an envelope, all in control domain.

I need to avoid edge~ there.

Rodrigo's icon

@grizzle

A simplified version of Sugarsynth is what I'm using when I crossfade to a granular synth for scrubbing. It sounds great and is easy to use, but I don't really understand the sah~/cosine stuff in there.

brendan mccloskey's icon

Hi Rodrigo
I may well have sent this to you before; all it really demonstrates is how little I understand about your specific problem......and my version of audio-rate central-phasor-windowing-type stuff (f*ckery LOL)

Max Patch
Copy patch and select New From Clipboard in Max.

Brendan

Rodrigo's icon

@n00b

Man, that patch is confusing as shit!

It almost fixes all of my problems too!

I can ditch the granular part of the patch since this lets you adjust things without noise, and I don't need to declick on jump as this is declicked from jump.

A couple of things, the windowing is really extreme. On longer loops, you can hear a really massive fade out before it starts again (rather than just windowing the 'click'). Looking at the [p simpleWin] I don't what I can adjust in terms of the window. I'm assuming from the sound that the window is relative in terms of loop length, rather than absolute (and tiny).

The other funky thing is that it plays past the maximum of the file without wrapping (which was a big part of me moving to phasor~ rather than my trusty groove~). What's nice about the phasor~/%~/+~ thing is that you wrap around the 'zero' if your window is bigger than the maximum it wraps back around.
Like playing from 80% of the file, to 10% of the file, it goes 80-90-0-10-20, and loops that.

Roman Thilenius's icon

"I need to avoid edge~ there."

no, you would need to create a replacement for line~, which does the same but takes either
a spike or a jump from 0 to 1 as trigger.

and as easy as that sounds, it might be that it is impossible to recreate it that way. :)

Peter McCulloch's icon

For my two cents, avoid play~ unless you absolutely have to access the contents of the entire buffer at any instant, though if you want the wraparound, it may be easier to do with play, since you wouldn't have to do two loops. (I like to use wave~ for granular and use sah~ on its right inlets to control the start/stop points)

Also, it sounds like you're looking for trapezoid~ for your windowing. You can use scale~ to automatically adjust the fade-in/out points dependent on grain length.

brendan mccloskey's icon

Hi
you can replace the [p simpleWin] with a [trapezoid 0.05 0.95] for longer loops - but for granulation-size loops (say, < 100ms) one should avoid windows with hard-knees (according to Roads et al) as this introduces or reinforces amplitude modulation sidebands.

I didn't annotate the above patch as I assumed you would 'get it', but it's fairly straightforward - the only real mystery might be the use of feedback send/receive. This is because the central phasor is situated AFTER the loopsize input. I gleaned all this from an old thread of mine: "make play work like groove", and with massive input from Tim Lloyd.

Brendan

Andrew Benson's icon

For a click-triggering line~ replacement, try zigzag~. I built a couple of convenience objects for doing this stuff awhile ago:
https://cycling74.com/tools/benson-signal-rate-utility/

These haven't been updated to support 64-bit audio processing in Max6 yet, but they might help.

Peter McCulloch's icon

@Brendan: good point re: amp modulation. Fortunately, because trapezoid~ is always outputting in a 0-1 range, you can use a lookup table on its output (e.g. use half a Hanning function) to transform it into softer shape.

Also, this is slightly perverse, but you could also use adsr~ with the decay, sustain and release set to 0. That'll give you a 0-1 ramp of arbitrary duration that is scaled by the input to the attack time.

Andrew Benson's icon

That's a nice trick Peter!

pid's icon
Max Patch
Copy patch and select New From Clipboard in Max.

i lazily always use trapezoid~. it just so beautiful and easy.

and i know i am 'that guy', but really, build a phasor~ in gen~ and you are away, so easy... and talking of gen~, i agree with peter about wave~ but, unfortunately, it sounds like a dog's arse, but we have gen~, and it so easy to make [wave @index phasor @interp spline] these days it solves all these problems.

pid's icon

oh and by the way, yes, andrews objects are awesome (although it would be great to get a max6 update one day!) and don't listen to me, listen to anything peter mcculloch says, he is a msp genius.

Rodrigo's icon

I started a post but then refreshed in a different page and saw pid's great example.

I've not taken a look at the gen~ phasor yet (nor do I understand how that would improve things, but I will check it shortly. (it's just a drop-in replacement for phasor~?)

The trapezoid thing looks elegant as shit, but with longer files (12sec sample) it's super audible. I could change it to 0.001, but it would still be relative. Is there an 'absolute' equivalent for trapezoid? (clicks don't get any longer because a sample is longer).

Another issue which is still kicking my butt is the [%~] thing. If I set that to something like 0.30, after 3 repetitions of my window, the master phasor will hit 0, which means the fourth repetition of [%~] only makes it to part of the way through before resetting. So how do you get rid of the 'cycle within a cycle' when you use a phasor followed by a modulo.

Now with the sah~ stuff I tried setting it up for only portions of a phasor (like after [%~] and [+~]) so I'm changing parameters where appropriate to that section of the timeline (as opposed to the master phasor). I set this up which seems to be working, but I'm having a hard time figuring out how it's going to work with both parameters moving. I guess it will also impact the way the thing sounds/acts if nothing is changing while I'm making changes.

Max Patch
Copy patch and select New From Clipboard in Max.

With the crossfade subpatch I think you misunderstood what I was doing in the initial patch. I was using the number box to 'bang' the envelopes, rather than as a pan between the two. So while the number box was moving, I would hear the granular synth, and when it was stationary, it was the dry audio. This might be a moot point now though since I'll likely be jumping with sah~ instead.

So issues still dangling:

trapezoid-like solution, but with an absolute envelope
phasor+modulo creating 'cycle within a cycle' timing

Floating Point's icon

Rodrigo, I am not going to suggest any msp/max solutions,but in the good old days when I'd have to prepare samples for hardware synths which could loop smoothly, this is what I did:

1: cut sample in half (call 1st half=A, 2nd half=B)
2: put second half in front of first half (B, A)
3: cross-fade the discontinuity between the end of B and start of A-- this Xfade can be long (500ms) or short (5 ms) depending on your needs
4: load into synth buffer- perfect looping, no need to crossfade the end with the start

Obviously this wont necessarily work for samples with a definitive attack, or a prescribed order of internal events (ie human utterance), but it is good for sustained, homogenous sounds etc

In max this procedure could be automated somehow if you need to adapt it to real-time sampling in a performance situation; ie record into 2 buffers, window, then concatenate the two halves somehow into a third buffer-- probably easier to do using the ftm library

hth

Roman Thilenius's icon

the best windowing of all is consine anway; there are no egdes but it is symetric.

in my granular effects cosine windwoing is the default and triangle or hanning
more somethign like a special effect.

Peter McCulloch's icon

Here's an example of an absolute trapezoid~ type solution. It's a little messy, but the gist is there. I didn't fade in/out at the max time, but you could easily extend this patch to do that as well. It's even easier, since it's relatively static.

I'm pretty sure you could do the same thing with pid's solution and a bit of math. You really just need to figure out what % of your grain length is your fade time. (so you could even use trapezoid~; I figured I'd do it the hard way)

Max Patch
Copy patch and select New From Clipboard in Max.

To summarize:
Find the distance between half your grain length and your position (minus the start time so it's expressed in a scale that's 0 to grainlength). The difference where you care about it is going to be grainlength/2 - fadetime. Let's call that x. You'll now use clip~ to clamp the value between x and grainlength/2. Scale that value into a 0-1 range and you're there. I added some extra fun stuff such as a Hann curve.

Rodrigo's icon

Man that's an amazing patch!

Everyone one your patches I've run onto here are super helpful/useful.

This sounds amazing and eliminates the 'cycle in cycle' thing too.

One thing I noticed is that if the position is near zero, you can't get below 200ms for window size as it goes silent around the sample start/end. Anywhere else, position wise, lets you go as small as you want. I tried tweaking the ramp duration, and tried the straight trapezoid instead of the wave~ version. The lowest I could get is about 130ms, which is probably as small as I want to go, but I'm curious as to why that is.

Lastly, and more sililly, I can't figure out where to stop/start it. Doing it at transposition and banging click~ again works well, but it only stops when the phasor runs its course, as opposed to stopping instantly. I can gate the signal going into the phasor and send it a 0 to stop it fully, but making sure I'm not missing something obvious somewhere.

Peter McCulloch's icon

Thanks. I'd recommend gating the output of play~ rather than the input of phasor~. If you stop phasor~, it stops at some position in play~, which will cause DC offset (since you're stopping playback at an arbitrary position in a wave; unlikely that it'll be at a 0) You can use sah~ in conjunction with this so you stop playback at a grain border. Is your stoppage happening at control or signal rate?

The 200 ms thing: I think that's because there's some silence at the opening of the cherokee.aif file. I swapped it with drumloop.aif and it works fine. Short grains also shouldn't be a problem. I can do 10 ms grains here with no problem. (note that if the ramp time is > grainlength/2 it just uses grainlength/2)

Rodrigo's icon

Hah, I didn't even think that it was because the sound file, ridiculous of me.

That makes sense with the gating play too. I'm driving some GUI stuff with that, but it's easy enough to gate and then send a 0 to wherever needs it.

This sah~ stuff opens up such a new world of possibilities in general, particularly in a coding 'blindspot' for me.

brendan mccloskey's icon

Must. Resist. But. Must. Buy. Max. 6.

Okay, I give in

Rodrigo's icon

It's definitely nice. So many little subtle things.

Wetterberg's icon

Lovely patch as always, Peter.

I've done what I often do with these; sit around and in a fit of near-OCD tidy up cabling and add tiny features - in this case a hamming window which in some cases sounds so much better, and a [slide~] on the segment start, so you can scrub around in the sound without glitches.

Max Patch
Copy patch and select New From Clipboard in Max.

But primarily I did it so that I could read the patch a bit better, even though it's at its core quite a simple one.
I don't know, I thought it might be interesting:

Now all I need is to be able to real-time record into this buffer without glitches. Then I'd be a really happy stutterer!

Rodrigo's icon

I just tidied it up as well, about to take a look at yours.

This is what I'm using to record to the buffer (different buffer name), and sending 'cd1max' to everywhere the info~ was connected to.

(I feel so legit having that "1 project" under my name now!)

Max Patch
Copy patch and select New From Clipboard in Max.

Rodrigo's icon

Wouldn't [slide] get broken by the sah anyways? So when you drag the thing, nothing is changing except on phasor cycles (most of which I changed to 0.001), but with slide, it will move where you tell it on the first phasor cycle, then move again after its done sliding (on the next phasor cycle), unless I'm misunderstanding slide there.

edit:

Here's my tidied up patch with a few trimmings. I removed the pitch control stuff as I'm only every playing 100%, added buffer record (dynamic), and a stop/play, which resets the playhead, and changed the position/window controls to 0-1, rather than absolute ms.

Max Patch
Copy patch and select New From Clipboard in Max.

pid's icon

[slide~] is a cool feature / idea, but it should go downstream of [sah~], not before it - then there is a point to it.

also, the ugly (sorry!) old-skool
[tapin~ 0]
|
[tapout~ 0]
hack can be replaced by 1 sample accuracy with gen~.

you could put much of this in gen~, especially the [poke~] recording, and further get rid of some of the signal domain to ctrl domain sections. worth looking into for the future maybe?

my 2c.
it is a very cool patch / project.

Wetterberg's icon

did you listen to my patch? The slide works, but it help even more after the [sah 0.001] and before the +~ - it can be a lot shorter there, too. :)

@pid: Care to take a stab at a gen~ version? I'd love you forever if you did.

Rodrigo's icon

I did listen to it and didn't notice any improvement (or much difference).

Also wouldn't having it after the sah~, again, defeat the purpose of the sah~? I thought the point was to chance on a cycle, whereas if you have a slide, you're sliding around. Again, I could be (and likely am) mistaken.

I only have the gen~ runtime, not to mention, don't know low level DSP stuff.

I tried putting the gen~ phasor from your earlier post in, but couldn't figure out how to get it to be the same playback speed (I didn't try very hard as I was sorting out other parts of the patch at the same time).

I will throw in some love if it's going around....

Wetterberg's icon

I think it's because we use it differently - I scrub the position of very short loops ala wavescanning a lot - your mod seems geared towards sort of longer loops?

One lovely thing I realized (duh) is that since the loop being played back is dictated by the phasor, doing stutters here is super easy - feed its phase (0. to 7./8.) and you can do the ol' mlr chop on it. So I added a little qwertyui input thing.
I also reintroduced un-sah'd pitchchanges to be able to reverse with a "-1."

Oh, since we're trimming you can safely remove the [*~ 1] in the upper left :)

I love this.
much love,
Andreas

Max Patch
Copy patch and select New From Clipboard in Max.

-oh, the qwertyui thing; connect to rightmost input of phasor~:

Wetterberg's icon
Max Patch
Copy patch and select New From Clipboard in Max.

I accidentally made a presentation window and made the patch stereo. Oops.

pid's icon
Max Patch
Copy patch and select New From Clipboard in Max.

@rodrigo, apologies, that was hastilly cribbed from another project. i hope this explains better:

having said that, thinking about it, not sure you need this in this scenario now - i was possibly confusing myself.

@wetterberg i not got the time. my projects all a bit different. you and rodrigo should do it. you might like the three example patches in the Cycling '74 > examples > gen, folder:
"gen~.livelooper.maxpat"
"gen~.chopper.maxpat"
"gen~.chopper_repeat.maxpat"

hth.

Wetterberg's icon

It does help, cheers.

Rodrigo's icon

Yeah that makes more sense.

Indeed, a nice thing about the phasor setup is sending it random values between 0-1.

It's funny, my intended application with this patch is a virtual, skipping CD player, so it's all this meticulous work to get the clicks out, to then replace them with synthetic and sampled CD player hiccup/blips.

The chopper is a pretty awesome effect. I've incorporated it as is into one of my patches.

pid's icon

you know the nic collins (senior) piece for trumpet and skipping cd player (i think) ? it is completely fantastic. it uses a real skipping cd player, 'skipping' controlled by max i think (no msp back then). from about 15 years ago or so. i seen it performed live, too. just struck me, i should mention it. indeed skipping cd's is one of my all time favourite sounds. i prefer it to needle dust / vinyl scratch any day.

i rather love this idea of getting all the clicks out so you can... make it click. as someone once said, "if you wanna fuck it up, you gotta perfect it first".

let us know when you finish the project.

Rodrigo's icon

Yeah I love that stuff. I've done tons of stuff with skipping CD in the past, but always hardware based, which obviously sounds amazing.

Here's a picee I did where I took a Bach prelude, learned it 'straight', then filled a couple of different CDs with it, and beat them up, and improvised to them. Amazing stuff.
http://rodrigoconstanzo.bandcamp.com/track/bach-prelude-in-f-minor-12-ii

This is before I had modded a CD player Nic Collins style (he has it so when you pause, you still hear that audio), so it jumps around and acts very erratically. It would die a lot too. Challenging to record.

I love the hardwareness of it, but the main impetus for doing a max version was so I could have the buffer filled dynamically. I want to model the skipping timing/rhythm a bit better (at the moment it's somewhat weighted/probabalistic but it's not ideal).

The bigger plan is to have a virtual media player kind of thing, so there will be models. A skipping CD, then a dusty/wobbly turntable, then some gritty/hissy tape etc...

brendan mccloskey's icon

Hey Rodrigo
just wanted to say, amazing sounds!

Really really inspirational - good work.

Brendan

Rodrigo's icon

Thanks!
It was really fun to make, though I had to record a ton of material as the CD player(s) I used would keep stopping, or being uncooperative.

Peter McCulloch's icon

At the risk of giving more mess to clean up...

Here's some mods to @Rodrigo's patch. Sah~ is definitely powerful once you grok it. I altered it so that it records the amp envelope of the sound while you're recording. The volume info is then used for gating and picking new positions, so you can say "don't change to this position unless it's greater than n% in volume".

In general, anytime you can make these processes slightly smarter, it's a big win. I also added some comments.

Max Patch
Copy patch and select New From Clipboard in Max.

@Wetterberg: I used a Hanning window because the Hamming doesn't go to 0 at the ends. It's a small difference but in some applications it really makes a difference. You could always do some math on the Hamming to fix the offset, but I'm lazy and just load a pre-compensated aif of it.

P.S. Glad to see folks running with this. Thanks.

Rodrigo's icon

I finally got a nice CD skipping patch going. It needs a bit more work before I post it (not to mention it uses a bunch of actual CD skip/glitch samples for it's authentic mojo).

Nice one Peter. I need to have a sift through that and make sense of it. And see what to add to the existing patch.

One thing that I've noticed, particularly now that I have a waveform display setup and I can see it happening, is that a static sah~ doesn't work too well when you have a moving position/window thing.

Meaning, I don't get clicks or anything, but if I'm only looking at the start of the phasor (sah~ 0.001) then I'm only jumping on those spots. So if I'm moving my position/window forward a lot, I'm not playing 'inside' the window ever. Same goes for moving the position/window backwards. If I move that, I can easily play out the other side of the window too.

So how do you setup a dynamic sah~ like that, or rather, how do I stay INSIDE the designated window at all times?

Here's a section of my patch with a waveform display and all that. (the 'glitches' part will only playback the 'synthetic' clicks and not the real ones since the audio files aren't there), but with this you can see what I mean when you drag the position around while a file is playing. The playback head goes by what the window was at back when it started (as opposed to where it is now).

Max Patch
Copy patch and select New From Clipboard in Max.

xidance's icon
Peter McCulloch's icon

It sounds then like you might want to jump either at the end of the phasor, or whenever you tell it to. The good thing is that this is pretty much already in the architecture via the click~ that starts everything (in the phasor~ loop). You just need to fade the output down before you jump.

So the sequence is:
Fade out -> Jump -> Fade in

Max Patch
Copy patch and select New From Clipboard in Max.

Here's an example.

Rodrigo's icon

That works for jumping to arbitrary positions, but it doesn't work for smooth stuff.

In the actual patch I have a phasor moving position around, sort of like an inertial movement. So you're kind of still 'playing' the CD but it's skipping back and forth in a given window size.

With position triggering the envelope/position change on every movement, it sounds super granular.

Ideally it would be something where I can move the position/window around, but it would only jump when the playhead hits wherever the min/max of the window happen to be at that moment. So if I'm moving the position/window forward slowly, nothing will ever happen as the playback head will sit squarely in the middle of the window, but moving faster/slower would force it to jump.

I guess it would require not using sah~ for the position triggers? Or dynamically updating the sah~ triggers?

Here's the patch with a phasor driving the movement of position

Max Patch
Copy patch and select New From Clipboard in Max.

Peter McCulloch's icon

Okay, I think I understand a bit better now what you're looking for. I'd say that sah~ is still what you want, but it's just that you may need a better test for it. You're not just interested in whether the phasor is done with its cycle, but you also want to know if the position has exceeded the current bounds. (and then there is a question of "exceeded its current bounds right now" or "exceeded its bounds ever during the cycle"...)

Max Patch
Copy patch and select New From Clipboard in Max.

You now need to do some comparison with the previous value to decide whether to sample. There's a good chance you may need multiple sah~ objects. It's not that hard, but it's a little weird at first. (sampling-and-holding the sampling-and-hold...) This reminds me a bit of the triple Sample and Hold unit on Buchla synths. Here's a Max implementation that may be of interest.

Peter McCulloch's icon
Max Patch
Copy patch and select New From Clipboard in Max.

Try this out. This is a general version, but I think it looks like what you're going for. It makes the jump at the boundaries of the slow phasor. You'll need to update the boundaries, but that's more of the same with sah~.

Rodrigo's icon

Just when I thought I was starting to understand sah~ I stepped into a scene in sah-ception....

I kind of understand that last example, until I tried to put it into practice. So the pong just before play is the top level phasor (in the example) that I'm going to be testing, by sah~ against the position/window controls.
That makes sense.

After that it falls apart a little bit. I'm replacing two of the existing sah~'s with this new high/low sah~, and sending the new position/window info based on that, but in practice, it's not as clear.

Here's as far as I managed to get it:
(it's all inside the [p play] subpatcher)

Max Patch
Copy patch and select New From Clipboard in Max.

@raja. That's a great patch! I wrestled around with it for a bit, but then realized that it's functionally different from what I'm doing (you're advancing by a specific interval, and a specific rate). There's plenty in there to look at and learn from (I'm slowly pulling it apart to understand what's left, as the wiring makes it very difficult to 'see' anything in it).

Rodrigo's icon

Ok, I've been going over this for a couple days (had a busy work week) and I'm still stuck. Not for lack of trying mind you!

When I connect what makes sense to me I either lock up the patch (the phasor stops) or the sah~ doesn't trigger like I thought it would and one parameter doesn't work.

I tried controlling one single parameter with this "is it there now" kind of sah~ (the window high point), and got no joy.

So the goal is for the playhead to jump back to the start (of the position offset) ONLY when it hits the point set by 'window'. So if window parameter is moving forward at the same rate as the playhead, it will never move back (though I would obviously run out of room quickly). Or if I move the window setting down, it would jump as soon as that threshold is met.

With my understanding that means no 'sah~ within an sah~' as I'm only testing for where the parameter is NOW (as opposed to where it is within the cycle of the phasor). I am however sah~ing the 'position' parameter since I will jump to that only when a new cycle starts (when the playhead touches the peak of the window).

So I tried using the [>~] with the window position, but it never triggers (as it never reaches the max number), so I added [- 0.1] and that gets sah~ to trigger. So I'm trying to use the position of window, as the trigger in an sah~ to force a jump in the 'position' parameter. Is that right? It doesn't seem to work as this locks up the clock.

Here's a tidied up version of the patch (removed all the osc and non-specific to this problem stuff).

Max Patch
Copy patch and select New From Clipboard in Max.

Peter McCulloch's icon

Hi Rodrigo, I think you may need to come up with an exact list of requirements, because the solutions will be different depending on those requirements. It sounds like:

- Normal playback is the default.
- When glitching is turned on, it should set the start point to some time in the past (before the current position) and set the end position of the glitch equal to the current position (plus some short fade out?).

What's not clear:
- Can the glitch be changed while it's glitching? And if so, which parameters?
- Are there bounds for how often it can switch between glitch and non-glitch? Does this need to be able to happen at signal rate?
- Are there minimum / maximum values for the glitch parameters? (e.g. duration)

Rodrigo's icon

Howdie Peter.

Yeah this stuff is definitely hard to communicate via text.

So in answer to your question(s). (And stating everything as fully/clearly as I can too, so some is redundant)

- It will never play back 'normally' (as in playing fully from beginning to end). This buffer is specifically for this window/glitch playback.

- So the main playback mechanism is via 'position' (offset from start) and 'window' (distance from 'position'). It loops back and forth between these two points (including wrapping around 'zero' (from 0.9 to 0.1))

So that's the basic 'gist' of it. Now all the sah~ and actual windowing (envelope) stuff has been to clean the above two things up, but has introduced other little problems.

- So the glitch CAN be changed while glitching but only with regards to the position/window parameters. The playhead would (ideally) continue doing what it's doing. So if I'm looping between 0.4 and 0.6, and then I start moving the 'position' parameter forward, at the same rate as the playhead, the jump will not happen.

- Playback will always be forward, so the end point (window) parameter should always be 'live'. So if that comes in contact with the playhead, it will jump to wherever the 'position' parameter is. This can happen as fast as possible (though realistically I can only move that parameter so fast). So this doesn't have to happen at signal rate, although it will need audio enveloping, which I'm guessing HAS to be audio rate.

- The low point of the loop window ('position') presents a different problem. I messed with a few different ways of handling the low point moving faster than the playhead before having a big epiphany. Since I'm emulating a skipping CD player, I can just use that style of 'cd player fast forward'. Which I measured to be between 9-12hz depending on CD player (at least the old ones I have sitting around). So that would translate to only being able to move the 'position' point every 100ms.

- So only when the playhead meets the low point, that can only update every 100ms, but when the playhead hits the high point, it updates (jumps to the low point) immediately. If that makes sense.

- The only minimum is the window size (0.01 of total file size).

It should be noted that everything wraps around 'zero' using [%], so the min/max of position is 0/1 but that it can wrap around.
ie a window from 0.4 to 0.6 can turn into 0.9 to 0.1 when moved forward.

Lastly, the actual 'position' parameter is itself being controlled by a slow phasor, so it's moving automatically (forwards or backwards) pretty much the whole time.

Peter McCulloch's icon

I meant normal on a more local basis, as in playing forward at normal speed with no envelope or skipping.

It looks like this may be a better model for your problem (divide and conquer):

normal playback ....... glitch (granular) ........ normal playback...... glitch (granular) ...... glitch (granular) ...... glitch (granular)......normal playback.

The reason this may make sense is that it removes a substantial amount of complexity from your problem, and it means that you can then solve it in parts. The first part is very easy, and you could use groove~. The only trick is to make sure that it starts up where the glitched one starts (so the "last time through the loop" is actually just starting the original copy at the beginning of the loop and letting it play)

Once that's dealt with, it's a question of building the glitch system and the switching mechanism.

Rodrigo's icon

Not following you here.

Is that normal/glitch/normal/glitch a sequence of events or different modules?

I was using groove~ originally, but I couldn't wrap around zero with loop points (0.9 to 0.1) so I moved to play~ instead.

Peter McCulloch's icon

Hi Rodrigo, yes and yes.

Here's some example code using gen~. I tried it in normal MSP, but the signal vector delay was too much. There's still room for improvement--I'd probably move the groove~ code into gen~ somehow. (have to think on how all that works, but it's not awful in this case since you're not doing transposition)

Max Patch
Copy patch and select New From Clipboard in Max.

The crossfade isn't dead on, but it's reasonably good. Also, it's got parameters for repeats as well as a freeze, and you can set the min and max glitch times.

Rodrigo's icon

I see what you mean about different modules now. That would offset/distribute the problems.

I need to start getting on the gen~ thing. I only have the runtime at the moment.

So in looking at this and messing it a bit, I managed to drive absolute position using a sig~ going into [p phasor], which is handy. And controlling min/max_duration gives me that window control, though the controls (unless I'm missing something) seem to take still work in a similar way as far as sah~ goes. (Changing only when it reaches the max point WITHIN the cycle, as opposed to NOW).

So if I have a frozen position (or even with it moving, though it's easier to with it frozen), and my min/max_duration are set to 2000. If I type in 1000 a little bit after the cycle has started moving, it doesn't do that until it's hit the 2000 point.

A more general question about complex playback (programming in general I guess). Is it good practice to break complex singular functions up like this? Or is this more about learning how to tackle each problem and then incorporating it into one solution in the end. (ie multiple playback objects with crossfades vs a single playback object with lots of bits hanging off of it)

Peter McCulloch's icon

Sah~ can definitely handle the change, you just have to remember that it affects the system. If the frequency of the phasor is dependent on the length of the segment... This also looks like your envelope function may also get sucked into gen~, since you're going to need to control the envelope when you update this value--you'll have to ramp down the envelope, then do the jump at the end. So, in a sense, it's still a "jump request", as it is now, albeit a little more responsive. Currently, the jump requests are only handled at the end of the cycle because that's where we have our envelope. You can have it somewhere else, but it means changing how the envelope is handled.

Is there an absolute minimum duration for your glitches? Because there's a little latency in fading in/out when jumping, there will probably need to be a time-out mechanism for changing jump points. Otherwise, continuous input gets pretty messy (line~ for example...).

For the last question, I would say you definitely want to break it down into as many small, manageable components as possible. Abstractions and subpatchers are a big help. I teach my students that if a chunk of code does one thing, and one thing only, make it a subpatcher/abstraction (depending on whether you want to reuse it).

When you break it down, there's several ways of structuring it. You could do this like the example I posted, but you could also do it as a poly~ patch where each voice is one glitch or one glitch loop, and you have a synthesizer that's playing the glitches. Your timing probably won't be quite as dead on, but it gets you thinking about things like adsr~ and then you realize that midinotes with poly~ are actually pretty good with indeterminate length. IIRC there's someone who's made a signal-rate poly~ library, but I can't remember who at the moment.

I'm attaching a picture of the gen~ patch. (can you see it with the runtime?)

Peter McCulloch's icon

Whoops. Here it is:

3279.Screenshot20120131at9.15.45AM.png
png
Rodrigo's icon

I can see the gen thing (it opens up, it just isn't unlockable).

I had a rehearsal earlier today with Alex Harker and afterwards I was chatting to him about this. He suggested either doing it in control domain or in gen. He gave me a gen patch that does everything I wanted though there's still a snag with enveloping it. I'll post it once it's working right.

Rather than using sah~ (or sah in gen) it uses >/< to test every sample to see if it's hit the high point. If so, jump to the low point, immediately, and to where the low point is at that instant.
It also avoids using sah~/sah to eliminate the zipper/scrubbing when moving the low point by using delay (in gen) to 'lockout' changes in position for a certain amount of time (about 100ms to simulate the 'fast forwarding CD player' sound).

My overall plan/goal is quite complex so I've been building it module by module (this skipping thing being a module). For most things I've also worked on paper a lot (particularly when it comes to things like state machines).

I've avoided using/making abstractions (and bpatchers) to keep everything local/within the patch. When I reuse bits I generally just copy the [p patchers] across. Probably not the most efficient thing but it's easy for me to wrap my head around (and easily edit) things.

Peter McCulloch's icon

Sounds like you're on your way, and I'm interested to see it.

One thing I was curious about: if you just jump from low to high, then how do you avoid the discontinuity? It seems like you would need to maybe set your "high point" a bit before the actual high point so that you can do a ramp down before jumping. (unless the discontinuity is idiomatic, then everything gets a lot simpler...)

Rodrigo's icon

It would only jump from high to low ever, or did you mean that discontinuity?

Here's the 'almost working' version for the sake of sharing while its coming along.

The reset doesn't work in this version (it's not connected inside gen, and I can't edit it).

And it looks like [history stall 0] breaks the looping. The first version of the gen had no history where the stall is and it looped perfectly, so I think it's there to fix something else (though it broke something else).

Enveloping looks like it's also going to be a problem since the 'grain' size is now constantly changing (I tried your absolute envelope window with this, but it looked like it was only fading up (but jumping down) and only worked at certain parameter settings.
I'm not sure what the enveloping solution is now with the way the looping engine is setup. I thought about maybe just doing a sah so it only changes parameters on very quiet amplitude, but gen only contains a counting engine, with the actual audio playback being outside of gen.

But yeah, here's the patch at the moment.

Max Patch
Copy patch and select New From Clipboard in Max.

Rodrigo's icon

It looks like its delay (in gen) that's the source of the new problems.

Incidentally here is the original somewhat working version of the patch).

It can't reset manually and sometimes has some zipper noise issues when jumping backwards quickly, but the looping aspect of it acts correctly.

Max Patch
Copy patch and select New From Clipboard in Max.

Peter McCulloch's icon

You need to incorporate the fade into the gen code, since the fade has to happen before the jump. You can't jump instantly without a discontinuity, so you have to do a fade out, then jump, then fade back in.

It's a fun and nasty problem, but I think this may be a better solution. Note that there are sample and hold objects, but the condition for triggering them is different. (in this case, if you make a change in values, and the timeout hasn't locked you out, and the current position exceeds the jump point for the fade out (not the loop end, since that'd be too late!)

Max Patch
Copy patch and select New From Clipboard in Max.

You can change the loop times and it will keep playing if the playhead isn't near them. You'll also see some standard recipes in the gen code, such as > into delta into > 0. This just takes a continuous test and only sends a 1 for one-sample when it becomes true.

AlexHarker's icon

Hi,

Thought I'd chip in as I wrote the gen code above...

When I was speaking to rod about this he didn't initially mention enveloping. Of course not enveloping will create a discontinuity, but then it's for a CD skipping emulation so.....

The problem with my previous approach was trying to skip a few more objects by using delay to do the timeout, so I really needed to roll my own system that is reliable.

The below is a working gen patch minus enveloping. As far as I am concerned enveloping is not an easily solvable problem for this scenario, as the loop points can change at sample rate. Until we hit the end point we can't be sure where the envelope is. Of course if we fix the points until the next jump then the problem is a lot easier, but that wasn't part of the original spec that I got (I haven't followed this thread) so I'm not sure what has been discussed above. If the window is large then that could make any real-time control/UI very unresponsive. Anyway, to solve the enveloping problem there needs to be a clearer spec about how the system should work.

@PM - I haven't totally checked out your patch in detail, but it seems the timeout duration only updates on manual reset. In my version it is constantly updateable. Also if I play around long enough with timeout and reset I can get your gen to spin off into infinity. I was having the same problem with my code due to the suspect use of delay, but I'm 99.9% sure the below code cannot suffer this issue.

Max Patch
Copy patch and select New From Clipboard in Max.

Akex

Rodrigo's icon

Howdie Alex.

I was going to post that code just now (long day!) I've only played with it for a couple of minutes but this latest version seems bulletproof.

Enveloping isn't terribly critical for this patch given how the end patch is going to function (I'm triggering a glitch/click sample at every reset anyways). There has been a lot of envelope chat/patching during the thread as it was the main reason for going audio rate with the control stuff.

I'd also happily have the patch click over it being unresponsive (it's going to be used dynamically/instrumentally/quickly) so my priority was with that (which is why enveloping never came up when we were talking about it). If the windowing is only 20ms total (10out 10in) then that wouldn't impact the playability much.

AlexHarker's icon

Yeah. One approach to take is to delay the audio output of play by 10ms or less and detect the jumps off the control signal, starting a ramp immediately, but applying it to the delayed version. You could probably do this sample-accurately without gen~ (for instance start a ramp using my voicedrive~ object, or andrew bension's similar objects for non-interrupting ramps) that covers the fadein and out (or rather out and in) and then manipulate the single line produced using basic MSP objects / pong~ to create a down and up ramp.

This gives a fixed latency that you probably can't feel if if controlling in realtime, and removes the difficulties of seeing in to the future and guessing what might / will happen or locking points ahead of time.

One other nice thing about this is that it would work for manual resets too, although you could also code it so it ignores them (don't trigger a ramp if the jump coincides with a reset click in...)

Alex

AlexHarker's icon

Also I'm pretty sure there could be two or three less objects in the gen patch... (minimal patching is my new obsession) If I get bored sometime I'll try to prove it...

A.

Rodrigo's icon

Here's a version using this 'delayed windowing' thing (using voicedrive~ and wave/hanning).

Max Patch
Copy patch and select New From Clipboard in Max.

Rodrigo's icon

And here is a functionally perfect one. It has windowing on jumps and play/stop, a minimum~ to override the lockout when doing short grains, a jitter control, and a nice waveform display with wraparound.

I tried using wave~ on the stop/play windowing but settled for line/slide when I couldn't get it working right.

With the real click/glitch samples it sounds amazing! (Particularly with a lockout of 100ms when moving around quickly).

Does anyone happen to know what makes up the CD 'glitch' sound in a real CD player? It only happens when audio is present (doesn't happen on quiet parts or silent tracks) so I'm guessing it has something to do with the audio content. I'm using 11 samples along with some pink~ * noise~ as subtle attack modifiers but it would be more dynamic/interesting if I was actually generating 'real' glitches by doing whatever it is that the CD player is doing.

Here are the samples for my patch as an example of what I'm talking about:

Max Patch
Copy patch and select New From Clipboard in Max.

Rodrigo's icon

Here's the completed patch (multiples of each module and some GUI/midi learn etc...). (not posted straight on the forum as it requires a poly~ and audio samples)

Thanks for all the help throughout the thread as I definitely understand audio rate/enveloping much more now!

pid's icon

hi rodrigo, fantastic stuff, congratulations. and thanks for the crazy thread, too.

in your download you have forgotten to include (or mention they are required):

zeroconf.browser, zeroconf.resolve, randomvals, voicedrive~

best.

p.s. - i'm not a 'controller' person, but your video has made me want an arc now damn you.

Rodrigo's icon

randomvals/voicedrive are from the Alex Harker externals, the other two are generic monome externals (not needed if you don't have a monome and if you have one, you'd already have them).

I could've sworn I mentioned the Harker thing. I'll edit that now. They can be downloaded here:
http://www.alexanderjharker.co.uk/Software.html

Rodrigo's icon

Yeah I didn't mention it. My other patches have dedicated instructions documents that say what externals are used with links and all that. This one, being relatively straight forward, is just the patch in the download.

The arc is pretty damn amazing. It's such a weird paradigm to program for (which is also why I went with a simple app like this as my first attempt at it).

Since I often use my electronics stuff along with an acoustic instrument I figured an unusual controller type like these (monome+arc) would make me be more creative with the code as I don't have the time (or limbs) to twiddle a hundred knobs/faders.
As a result there's lots of 'behind the scenes' stuff going on with buffer analysis (in The Party Van) and subtle randomization (Chocolate Grinder).