I want to recreate the timing accuracy of vline~ in a c++ project, and there's something that i can't figure out exactly - how does it act, seemingly outside of the block construct, to get its accuracy?
in the source code, there is a calculation for elapsed logical time, and i assume that has something to do with it?
if i make a vline~ from 0 to 1, over 10ms, does this mean that it actually doesn't START right on 0, but rather starts at a slightly offset value to compensate for the block boundary?
any help would be appreciated!
Very quickly:
(the end point of the linear movement) using the t_vseg struct; events are scheduled in the vline_tilde_float routine.
see how the x_referencetime member works because it's used to measure time everywhere else. There are lots of
time of the next event(s) have elapsed (it's possible to have two events at the same logical time, where one leaps to a value and the other ramps from that value). It calculates the increment based on a linear interpolation from the start time to the target time, and from there it can increment until the target time has elapsed. The interpolation is how it gets subsample accuracy. Checking every sample to see if a new event should start is more expensive than checking every block (obviously).
others in the system interface file s_inter.c
Hopefully this is all correct. If not, someone else please chime in.
On Fri, Sep 25, 2015 at 3:21 AM, i go bananas hard.off@gmail.com wrote:
I want to recreate the timing accuracy of vline~ in a c++ project, and there's something that i can't figure out exactly - how does it act, seemingly outside of the block construct, to get its accuracy?
in the source code, there is a calculation for elapsed logical time, and i assume that has something to do with it?
if i make a vline~ from 0 to 1, over 10ms, does this mean that it actually doesn't START right on 0, but rather starts at a slightly offset value to compensate for the block boundary?
any help would be appreciated!
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Thanks,
I guess here is my question put better:
If i make a line and a vline object, and feed them both a [1, 0 50( message, they perform differently. The line object jumps around, presumably cos it is tied to block boundaries. But the vline always triggers exactly the same.
It's as if somehow the vline~ works outside of the block structure. Does it actually do that? Say you're at sample 47 of a 64 sample block, does vline jump the queue and trigger right then?
Or i guess, even more succinctly, i was assuming that everything in pd got triggered in blocks. Is that the case? Does vline also have to wait for a block boundary when first triggered?
Sorry, there's obviously some 'aha' moment i'm failing to have here.
Does vline also have to wait for a block boundary when first triggered?
this is probably cleared out and not sure if this is the question, but vline~ will respect the schedule timing of events with a delay of one block, meaning that it'll convert the events to audio rate but only for the next block.
now I wonder what happens for blocks that are smaller than 64
cheers
2015-09-26 9:24 GMT-03:00 i go bananas hard.off@gmail.com:
Thanks,
I guess here is my question put better:
If i make a line and a vline object, and feed them both a [1, 0 50( message, they perform differently. The line object jumps around, presumably cos it is tied to block boundaries. But the vline always triggers exactly the same.
It's as if somehow the vline~ works outside of the block structure. Does it actually do that? Say you're at sample 47 of a 64 sample block, does vline jump the queue and trigger right then?
Or i guess, even more succinctly, i was assuming that everything in pd got triggered in blocks. Is that the case? Does vline also have to wait for a block boundary when first triggered?
Sorry, there's obviously some 'aha' moment i'm failing to have here.
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Hi Alexandre, The [vline~] object can compute ramps with subsample accuracy, regardless of the block size. Though I might not understand the question.
-Jonathan
On Friday, October 16, 2015 4:42 PM, Alexandre Torres Porres porres@gmail.com wrote:
Does vline also have to wait for a block boundary when first triggered?
this is probably cleared out and not sure if this is the question, but vline~ will respect the schedule timing of events with a delay of one block, meaning that it'll convert the events to audio rate but only for the next block. now I wonder what happens for blocks that are smaller than 64 cheers 2015-09-26 9:24 GMT-03:00 i go bananas hard.off@gmail.com:
Thanks, I guess here is my question put better: If i make a line and a vline object, and feed them both a [1, 0 50( message, they perform differently. The line object jumps around, presumably cos it is tied to block boundaries. But the vline always triggers exactly the same. It's as if somehow the vline~ works outside of the block structure. Does it actually do that? Say you're at sample 47 of a 64 sample block, does vline jump the queue and trigger right then? Or i guess, even more succinctly, i was assuming that everything in pd got triggered in blocks. Is that the case? Does vline also have to wait for a block boundary when first triggered? Sorry, there's obviously some 'aha' moment i'm failing to have here. _______________________________________________ Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
i was wondering if it worked well with block sizes less than 64, I find sometimes some object don't act well for such block sizes
but I tested it and vline is fine ;)
2015-10-16 19:51 GMT-03:00 Jonathan Wilkes jancsika@yahoo.com:
Hi Alexandre, The [vline~] object can compute ramps with subsample accuracy, regardless of the block size.
Though I might not understand the question.
-Jonathan
On Friday, October 16, 2015 4:42 PM, Alexandre Torres Porres < porres@gmail.com> wrote:
Does vline also have to wait for a block boundary when first triggered?
this is probably cleared out and not sure if this is the question, but vline~ will respect the schedule timing of events with a delay of one block, meaning that it'll convert the events to audio rate but only for the next block.
now I wonder what happens for blocks that are smaller than 64
cheers
2015-09-26 9:24 GMT-03:00 i go bananas hard.off@gmail.com:
Thanks,
I guess here is my question put better:
If i make a line and a vline object, and feed them both a [1, 0 50( message, they perform differently. The line object jumps around, presumably cos it is tied to block boundaries. But the vline always triggers exactly the same.
It's as if somehow the vline~ works outside of the block structure. Does it actually do that? Say you're at sample 47 of a 64 sample block, does vline jump the queue and trigger right then?
Or i guess, even more succinctly, i was assuming that everything in pd got triggered in blocks. Is that the case? Does vline also have to wait for a block boundary when first triggered?
Sorry, there's obviously some 'aha' moment i'm failing to have here.
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
One helpful reminder is that you're still triggering control events on blockboundaries. For example: imagine you set the block size to such a large value that eachblock lasts one day long. In that case if you sent a message to vline~ at noonon Monday to jump to zero then ramp up to one in three seconds, then at noonon Tuesday it would jump to zero then ramp up to one in three seconds.
On Saturday, September 26, 2015 1:22 AM, Matt Barber brbrofsvl@gmail.com wrote:
Very quickly:
vline~ stores a linked list of events with start times and target times (the end point of the linear movement) using the t_vseg struct; events are scheduled in the vline_tilde_float routine.
vline_tilde_new sets all of the relevant struct members. You'll want to see how the x_referencetime member works because it's used to measure time everywhere else. There are lots of
As vline_tilde_perform runs, it checks every sample to see if the start time of the next event(s) have elapsed (it's possible to have two events at the same logical time, where one leaps to a value and the other ramps from that value). It calculates the increment based on a linear interpolation from the start time to the target time, and from there it can increment until the target time has elapsed. The interpolation is how it gets subsample accuracy. Checking every sample to see if a new event should start is more expensive than checking every block (obviously).
A lot of the clock objects and functions are in m_sched.c; there are others in the system interface file s_inter.c
Hopefully this is all correct. If not, someone else please chime in. On Fri, Sep 25, 2015 at 3:21 AM, i go bananas hard.off@gmail.com wrote:
I want to recreate the timing accuracy of vline~ in a c++ project, and there's something that i can't figure out exactly - how does it act, seemingly outside of the block construct, to get its accuracy? in the source code, there is a calculation for elapsed logical time, and i assume that has something to do with it? if i make a vline~ from 0 to 1, over 10ms, does this mean that it actually doesn't START right on 0, but rather starts at a slightly offset value to compensate for the block boundary? any help would be appreciated! _______________________________________________ Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
In that case, maybe an even simpler question:
What is the difference between sending a [1, 0 50( message to vline as opposed to line ?
Why does line exhibit jitter, if both only trigger on block boundaries?
What I mean is that both [line~] and [vline~] receive their messages on block boundaries.But unlike [line~], [vline~] can start/end ramps and jump to the values you give it without beinglimited by block boundaries. Another example with my day-long block sizes: At noon on Monday you send a bang to [metro 150]--[tgl]--[vline~]. You'll haveto wait until noon Tuesday to hear the result, but you _will_ hear that same pattern of ones andzeros spaced 150ms apart that you were sending on Monday, even though the block size lasts aday. That's the strength of [vline~].
On the other hand, the [line~] object would just take the last [tgl] value it received on Monday(before it begins computing Tuesday's block), and it would just repeat that value the entire day of Tuesday. If you had sent it a ramp time, you would get your ramp Tuesday, but it would necessarilystretch across the entire day of Tuesday because that is the block size. Essentially-- you can't send a message that would interrupt the [vline~] object's perform routineand feed it new values. But because block sizes are usually small, I can't think of asituation where you'd need to do that. It occurs to me I could be wrong about any or all of this. If so I'm certain Matt or Miller can setme straight. -Jonathan
-Jonathan
On Saturday, September 26, 2015 10:24 AM, i go bananas <hard.off@gmail.com> wrote:
In that case, maybe an even simpler question: What is the difference between sending a [1, 0 50( message to vline as opposed to line ? Why does line exhibit jitter, if both only trigger on block boundaries?
It depends on what you mean by "trigger". Triggered by the mouse, I think you're right. But see the attached patch. Since [del] (among other objects, but I used [del] here for clarity) can schedule bangs between boundaries, you can trigger [vline~] in the middle of blocks. Not so with [line~]: if a bang is scheduled in that block, it starts the ramp at the beginning of that block and ends it at the end of a future block such that the total ms of the ramp does not exceed the requested ms.
On Sat, Sep 26, 2015 at 12:01 PM, Jonathan Wilkes jancsika@yahoo.com wrote:
What I mean is that both [line~] and [vline~] receive their messages on block boundaries. But unlike [line~], [vline~] can start/end ramps and jump to the values you give it without being limited by block boundaries.
Another example with my day-long block sizes: At noon on Monday you send a bang to [metro 150]--[tgl]--[vline~]. You'll have to wait until noon Tuesday to hear the result, but you _will_ hear that same pattern of ones and zeros spaced 150ms apart that you were sending on Monday, even though the block size lasts a day. That's the strength of [vline~].
On the other hand, the [line~] object would just take the last [tgl] value it received on Monday (before it begins computing Tuesday's block), and it would just repeat that value the entire day of Tuesday. If you had sent it a ramp time, you would get your ramp Tuesday, but it would necessarily stretch across the entire day of Tuesday because that is the block size.
Essentially-- you can't send a message that would interrupt the [vline~] object's perform routine and feed it new values. But because block sizes are usually small, I can't think of a situation where you'd need to do that.
It occurs to me I could be wrong about any or all of this. If so I'm certain Matt or Miller can set me straight.
-Jonathan
-Jonathan
On Saturday, September 26, 2015 10:24 AM, i go bananas hard.off@gmail.com wrote:
In that case, maybe an even simpler question:
What is the difference between sending a [1, 0 50( message to vline as opposed to line ?
Why does line exhibit jitter, if both only trigger on block boundaries?
Hi Matt,In my day-long block example, the [metro 150] is "triggering" the [tgl] to [vline~]. I am just usingday-long blocks to show that once a block has been computed you can't use [vline~] to travel back in time and change it. You have to wait till Tuesday-- the next block.
Of course you could have sent a bang to a [delay] object on Sunday that triggers the [metro 150] tostart on noon, Monday. But the point is you have to start somewhere-- whether it's GUI events orloading a patch.
This isn't a big deal for Pd users because-- again-- I can't think of a case where this ends up gettingin their way. But when people start inspecting the code, I think they have to understand this. It'sway too tantalizing to just assume that sub-block accuracy means you have to be able to interrupta perform routine in the middle of calculations. That isn't true, but a) it's not obvious why it isn'ttrue, and b) externals that check for clock events in a perform routine are almost certainly doingit wrong. But again, check my logic because this stuff is difficult to understand and harder to explain. :)
-Jonathan
On Saturday, September 26, 2015 12:51 PM, Matt Barber <brbrofsvl@gmail.com> wrote:
It depends on what you mean by "trigger". Triggered by the mouse, I think you're right. But see the attached patch. Since [del] (among other objects, but I used [del] here for clarity) can schedule bangs between boundaries, you can trigger [vline~] in the middle of blocks. Not so with [line~]: if a bang is scheduled in that block, it starts the ramp at the beginning of that block and ends it at the end of a future block such that the total ms of the ramp does not exceed the requested ms. On Sat, Sep 26, 2015 at 12:01 PM, Jonathan Wilkes jancsika@yahoo.com wrote:
What I mean is that both [line~] and [vline~] receive their messages on block boundaries.But unlike [line~], [vline~] can start/end ramps and jump to the values you give it without beinglimited by block boundaries. Another example with my day-long block sizes: At noon on Monday you send a bang to [metro 150]--[tgl]--[vline~]. You'll haveto wait until noon Tuesday to hear the result, but you _will_ hear that same pattern of ones andzeros spaced 150ms apart that you were sending on Monday, even though the block size lasts aday. That's the strength of [vline~].
On the other hand, the [line~] object would just take the last [tgl] value it received on Monday(before it begins computing Tuesday's block), and it would just repeat that value the entire day of Tuesday. If you had sent it a ramp time, you would get your ramp Tuesday, but it would necessarilystretch across the entire day of Tuesday because that is the block size. Essentially-- you can't send a message that would interrupt the [vline~] object's perform routineand feed it new values. But because block sizes are usually small, I can't think of asituation where you'd need to do that. It occurs to me I could be wrong about any or all of this. If so I'm certain Matt or Miller can setme straight. -Jonathan
-Jonathan
On Saturday, September 26, 2015 10:24 AM, i go bananas <hard.off@gmail.com> wrote:
In that case, maybe an even simpler question: What is the difference between sending a [1, 0 50( message to vline as opposed to line ? Why does line exhibit jitter, if both only trigger on block boundaries?
Hi,
On Sat, Sep 26, 2015 at 11:24:40PM +0900, i go bananas wrote:
In that case, maybe an even simpler question:
What is the difference between sending a [1, 0 50( message to vline as opposed to line ?
There will only be a difference in how line~ and vline~ react to this message when the message was triggered by something with a "clock" inside. These kinds of messages are called clock-delayed messages.
Clock-delayed messages originate in objects like [metro] or [delay] or [qlist]. Messages that originate for example in mouse clicks are not clock-delayed (i.e. if you click a [bng] or move a slider).
vline~ evaluates clock delayed message with high, sub-sample timing accuracy. line~ quantizes even clock delayed messages to block-boundaries or to 64 samples, I'm not sure ATM which it is. But line~ quantizes.
So depending on when the message was issued any by what, the ramp generators may act the same or different.
Try delaying the message:
[bang( | [delay 0.3] | [1. 0 50( | [line~ or vline~] | [print~]
Frank Barknecht _ ______footils.org__
Hi Frank,The [1, 0 50( message will almost always trigger differentoutput when fed to [line~] and [vline~]. The only exceptionis when the ramp ends exactly on a block boundary--otherwise [line~] will stretch the final part of the ramp tothe block boundary.
In fact, I'm willing to bet that if visualized this [line~]quantization to new users at the outset they'd almostalways use [vline~]. After all, who wants imperfect lines? :) -Jonathan
On Saturday, September 26, 2015 12:57 PM, Frank Barknecht <fbar@footils.org> wrote:
Hi,
On Sat, Sep 26, 2015 at 11:24:40PM +0900, i go bananas wrote:
In that case, maybe an even simpler question:
What is the difference between sending a [1, 0 50( message to vline as opposed to line ?
There will only be a difference in how line~ and vline~ react to this message when the message was triggered by something with a "clock" inside. These kinds of messages are called clock-delayed messages.
Clock-delayed messages originate in objects like [metro] or [delay] or [qlist]. Messages that originate for example in mouse clicks are not clock-delayed (i.e. if you click a [bng] or move a slider).
vline~ evaluates clock delayed message with high, sub-sample timing accuracy. line~ quantizes even clock delayed messages to block-boundaries or to 64 samples, I'm not sure ATM which it is. But line~ quantizes.
So depending on when the message was issued any by what, the ramp generators may act the same or different.
Try delaying the message:
[bang( | [delay 0.3] | [1. 0 50( | [line~ or vline~] | [print~]
Frank Barknecht _ ______footils.org__
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Well, obviously it depends on what you want to use the lines for. If it's just to fade something in or out over 10-50 ms to avoid a discontinuity, it's not that big a deal. Moreover sometimes it's great to have the ends and beginnings of ramps happen at block boundaries; e.g. when [switch~]ing off a subpatch or abstraction in a synth bank at the end of a quick fade, it's nice to know that you can target things to boundaries without having to try too hard.
On Sat, Sep 26, 2015 at 6:08 PM, Jonathan Wilkes via Pd-list < pd-list@lists.iem.at> wrote:
Hi Frank, The [1, 0 50( message will almost always trigger different output when fed to [line~] and [vline~]. The only exception is when the ramp ends exactly on a block boundary-- otherwise [line~] will stretch the final part of the ramp to the block boundary.
In fact, I'm willing to bet that if visualized this [line~] quantization to new users at the outset they'd almost always use [vline~]. After all, who wants imperfect lines? :)
-Jonathan
On Saturday, September 26, 2015 12:57 PM, Frank Barknecht < fbar@footils.org> wrote:
Hi,
On Sat, Sep 26, 2015 at 11:24:40PM +0900, i go bananas wrote:
In that case, maybe an even simpler question:
What is the difference between sending a [1, 0 50( message to vline as opposed to line ?
There will only be a difference in how line~ and vline~ react to this message when the message was triggered by something with a "clock" inside. These kinds of messages are called clock-delayed messages.
Clock-delayed messages originate in objects like [metro] or [delay] or [qlist]. Messages that originate for example in mouse clicks are not clock-delayed (i.e. if you click a [bng] or move a slider).
vline~ evaluates clock delayed message with high, sub-sample timing accuracy. line~ quantizes even clock delayed messages to block-boundaries or to 64 samples, I'm not sure ATM which it is. But line~ quantizes.
So depending on when the message was issued any by what, the ramp generators may act the same or different.
Try delaying the message:
[bang( | [delay 0.3]
| [1. 0 50(
| [line~ or vline~] | [print~]
Ciao
Frank Barknecht _ ______footils.org__
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
I tend to use [vline~] in those cases because-- to my ears-- it sounds warmer.
(I so wanted to click send, but I have to come clean and say I'm just kidding.) -Jonathan
On Saturday, September 26, 2015 7:46 PM, Matt Barber <brbrofsvl@gmail.com> wrote:
Well, obviously it depends on what you want to use the lines for. If it's just to fade something in or out over 10-50 ms to avoid a discontinuity, it's not that big a deal. Moreover sometimes it's great to have the ends and beginnings of ramps happen at block boundaries; e.g. when [switch~]ing off a subpatch or abstraction in a synth bank at the end of a quick fade, it's nice to know that you can target things to boundaries without having to try too hard. On Sat, Sep 26, 2015 at 6:08 PM, Jonathan Wilkes via Pd-list pd-list@lists.iem.at wrote:
Hi Frank,The [1, 0 50( message will almost always trigger differentoutput when fed to [line~] and [vline~]. The only exceptionis when the ramp ends exactly on a block boundary--otherwise [line~] will stretch the final part of the ramp tothe block boundary.
In fact, I'm willing to bet that if visualized this [line~]quantization to new users at the outset they'd almostalways use [vline~]. After all, who wants imperfect lines? :) -Jonathan
On Saturday, September 26, 2015 12:57 PM, Frank Barknecht <fbar@footils.org> wrote:
Hi,
On Sat, Sep 26, 2015 at 11:24:40PM +0900, i go bananas wrote:
In that case, maybe an even simpler question:
What is the difference between sending a [1, 0 50( message to vline as opposed to line ?
There will only be a difference in how line~ and vline~ react to this message when the message was triggered by something with a "clock" inside. These kinds of messages are called clock-delayed messages.
Clock-delayed messages originate in objects like [metro] or [delay] or [qlist]. Messages that originate for example in mouse clicks are not clock-delayed (i.e. if you click a [bng] or move a slider).
vline~ evaluates clock delayed message with high, sub-sample timing accuracy. line~ quantizes even clock delayed messages to block-boundaries or to 64 samples, I'm not sure ATM which it is. But line~ quantizes.
So depending on when the message was issued any by what, the ramp generators may act the same or different.
Try delaying the message:
[bang( | [delay 0.3] | [1. 0 50( | [line~ or vline~] | [print~]
Frank Barknecht _ ______footils.org__
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
It's because of the linear interpolation, which always sounds warmer. :)
On Sat, Sep 26, 2015 at 8:23 PM, Jonathan Wilkes jancsika@yahoo.com wrote:
I tend to use [vline~] in those cases because-- to my ears-- it sounds warmer.
(I so wanted to click send, but I have to come clean and say I'm just kidding.)
-Jonathan
On Saturday, September 26, 2015 7:46 PM, Matt Barber brbrofsvl@gmail.com wrote:
Well, obviously it depends on what you want to use the lines for. If it's just to fade something in or out over 10-50 ms to avoid a discontinuity, it's not that big a deal. Moreover sometimes it's great to have the ends and beginnings of ramps happen at block boundaries; e.g. when [switch~]ing off a subpatch or abstraction in a synth bank at the end of a quick fade, it's nice to know that you can target things to boundaries without having to try too hard.
On Sat, Sep 26, 2015 at 6:08 PM, Jonathan Wilkes via Pd-list < pd-list@lists.iem.at> wrote:
Hi Frank, The [1, 0 50( message will almost always trigger different output when fed to [line~] and [vline~]. The only exception is when the ramp ends exactly on a block boundary-- otherwise [line~] will stretch the final part of the ramp to the block boundary.
In fact, I'm willing to bet that if visualized this [line~] quantization to new users at the outset they'd almost always use [vline~]. After all, who wants imperfect lines? :)
-Jonathan
On Saturday, September 26, 2015 12:57 PM, Frank Barknecht < fbar@footils.org> wrote:
Hi,
On Sat, Sep 26, 2015 at 11:24:40PM +0900, i go bananas wrote:
In that case, maybe an even simpler question:
What is the difference between sending a [1, 0 50( message to vline as opposed to line ?
There will only be a difference in how line~ and vline~ react to this message when the message was triggered by something with a "clock" inside. These kinds of messages are called clock-delayed messages.
Clock-delayed messages originate in objects like [metro] or [delay] or [qlist]. Messages that originate for example in mouse clicks are not clock-delayed (i.e. if you click a [bng] or move a slider).
vline~ evaluates clock delayed message with high, sub-sample timing accuracy. line~ quantizes even clock delayed messages to block-boundaries or to 64 samples, I'm not sure ATM which it is. But line~ quantizes.
So depending on when the message was issued any by what, the ramp generators may act the same or different.
Try delaying the message:
[bang( | [delay 0.3]
| [1. 0 50(
| [line~ or vline~] | [print~]
Ciao
Frank Barknecht _ ______footils.org__
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
I actually think Frank might have the my answer here.
What i'm noticing, is not an inconsistency in the length of the ramps. It's an inconsisency in when they are first triggered. This is for stuff like the initial click of a bassdrum, so you can even hear the difference quite clearly. The line object jumps around, as it is being triggered on block boundaries, but the vline object is faithfully consistent in starting at the exact TIME i ask it to - regardless of blocksize, etc.
The only logical answer i can see here, is the one Frank has given. I'm scheduling drums using a clock controlled by metro.
So what i'm guessing that Frank is suggesting here, is that the scheduler is keeping a list of all the metro and delay bangs that should arrive in the upcoming block, and sceduling them to be hit while the block plays. And what vline does, is it reads that list, and then schedules itself to be hit after the correct amount of logical time has elapsed.
And i would guess that the normal line object is not capable of that. It just waits until block boundaries and starts its simple incrementation.
So, going back to Jonathan's day-long block example, i can see now how that works. the line can only be triggered once, at the start of the block (i still have doubts that it would actually stretch to the length of the block though. I reckon a 50ms line is still gonna take 50ms. ) But the vline has that linked list inbuilt, and it reads from the scheduler to see when it should be triggered. So if you had the day-long block, then vline's linked list is gonna end up with a full day's worth of messages in it!
Anyway, i'll go back to the source code tomorrow and have another look. But pretty sure that i now see how it is the ability to look ahead at metro scheduled events that gives vline the accuracy i need.
Thanks Matt for suggesting i look at the clock, and scheduler code, thanks Jonothan for the great example of the day long block - taking things to extremes like that is a good way to see how they work. And thanks Frank for finally turning on that lightbulb and suggesting the role that metro was playing here.
As always, it's good to include a demo patch when you have a question like this. I wasn't sure what you meant by "jitter" initially, but now I understand it because [line~] isn't translating your [metro] faithfully. You can see that jitter in the patch I attached above. It's important to see how [line~] starts its ramps *before* the logical time of the clock-delayed triggers -- those have to be scheduled before the target block starts executing, and [line~] quantizes them to the beginning of the target block.
On Sat, Sep 26, 2015 at 10:55 PM, i go bananas hard.off@gmail.com wrote:
I actually think Frank might have the my answer here.
What i'm noticing, is not an inconsistency in the length of the ramps. It's an inconsisency in when they are first triggered. This is for stuff like the initial click of a bassdrum, so you can even hear the difference quite clearly. The line object jumps around, as it is being triggered on block boundaries, but the vline object is faithfully consistent in starting at the exact TIME i ask it to - regardless of blocksize, etc.
The only logical answer i can see here, is the one Frank has given. I'm scheduling drums using a clock controlled by metro.
So what i'm guessing that Frank is suggesting here, is that the scheduler is keeping a list of all the metro and delay bangs that should arrive in the upcoming block, and sceduling them to be hit while the block plays. And what vline does, is it reads that list, and then schedules itself to be hit after the correct amount of logical time has elapsed.
And i would guess that the normal line object is not capable of that. It just waits until block boundaries and starts its simple incrementation.
So, going back to Jonathan's day-long block example, i can see now how that works. the line can only be triggered once, at the start of the block (i still have doubts that it would actually stretch to the length of the block though. I reckon a 50ms line is still gonna take 50ms. ) But the vline has that linked list inbuilt, and it reads from the scheduler to see when it should be triggered. So if you had the day-long block, then vline's linked list is gonna end up with a full day's worth of messages in it!
Anyway, i'll go back to the source code tomorrow and have another look. But pretty sure that i now see how it is the ability to look ahead at metro scheduled events that gives vline the accuracy i need.
Thanks Matt for suggesting i look at the clock, and scheduler code, thanks Jonothan for the great example of the day long block - taking things to extremes like that is a good way to see how they work. And thanks Frank for finally turning on that lightbulb and suggesting the role that metro was playing here.
And what vline does, is it reads that list, and then schedules itself to be hit after the correct amount of logical time has elapsed.
I'm not exactly sure what "schedules itself" means here. So I'll juststart explaining how it works until I understand it myself... :)
The [vline~]object processes blocks of samples at a time (in vline_tilde_perform). Butany of those clock events Frank mentioned that happen to be "ripe" forthe upcoming block get sent to the "float" method of line (vline_tilde_float),which adds them to the linked list x_xlist. For example,suppose you have the following: [0 0.1, 1 0.2(|[pipe]|[vline~] |[print~] When you click the message box, [pipe] does the proper clock delay foreach message: the "0" is scheduled for 0.1ms in the future, and the "1" isscheduled for 0.2ms. But these delay times take less time to fire than ittakes to compute a block. So if [vline~] is calculating its output in blocks of 64 samples, when does it actually receive the output from [pipe~]? The answer is that Pd actually sends the "0" and "1" messages to [vline~]one after the other, before it calculates the next block. By "send" I mean itcalls the function vline_tilde_float with the float argument "0", and then it callsvline_tilde_float with the float argument "1". But before each of those calls, it updates the "sys_time" to be the time when that clock was set to go off. So if the "0" was set to go off at "now + 0.1", Pd sets "now+0.1" to be the new system time, then it sends the "0" message to vline_tilde_float. And before it sends "1", it sets the sys_time to "now + 0.2".
Let's go back to vline_tilde_float-- notice in the first line we're fetching thesys_time. And what is it going to be for our "0" message? As stated above, weset it to "now + 0.1". Great! And when we process "1" message, we get "now + 0.2". Thus, [vline~] adds those messages and their associated timings to the linked list. Finally, we do [vline~] perform routine. It starts with the time equal to what itwas at the beginning of the block. Then it loops through our block's samplesuntil it reaches a sample that corresponds to the time that an event in ourlinked list needs to be triggered. When we reach that sample, we jump tothe new value.
It's the same process for ramps, except that vline~ calculates an incrementvalue for the ramp.
After looking, I'm kind of curious what is most responsible for [vline~]overhead. I'd guess it's the math needed to set each ramp segment, but I'm notcertain.
-Jonathan
On Saturday, September 26, 2015 10:55 PM, i go bananas <hard.off@gmail.com> wrote:
I actually think Frank might have the my answer here. What i'm noticing, is not an inconsistency in the length of the ramps. It's an inconsisency in when they are first triggered. This is for stuff like the initial click of a bassdrum, so you can even hear the difference quite clearly. The line object jumps around, as it is being triggered on block boundaries, but the vline object is faithfully consistent in starting at the exact TIME i ask it to - regardless of blocksize, etc. The only logical answer i can see here, is the one Frank has given. I'm scheduling drums using a clock controlled by metro. So what i'm guessing that Frank is suggesting here, is that the scheduler is keeping a list of all the metro and delay bangs that should arrive in the upcoming block, and sceduling them to be hit while the block plays. And what vline does, is it reads that list, and then schedules itself to be hit after the correct amount of logical time has elapsed. And i would guess that the normal line object is not capable of that. It just waits until block boundaries and starts its simple incrementation. So, going back to Jonathan's day-long block example, i can see now how that works. the line can only be triggered once, at the start of the block (i still have doubts that it would actually stretch to the length of the block though. I reckon a 50ms line is still gonna take 50ms. ) But the vline has that linked list inbuilt, and it reads from the scheduler to see when it should be triggered. So if you had the day-long block, then vline's linked list is gonna end up with a full day's worth of messages in it! Anyway, i'll go back to the source code tomorrow and have another look. But pretty sure that i now see how it is the ability to look ahead at metro scheduled events that gives vline the accuracy i need. Thanks Matt for suggesting i look at the clock, and scheduler code, thanks Jonothan for the great example of the day long block - taking things to extremes like that is a good way to see how they work. And thanks Frank for finally turning on that lightbulb and suggesting the role that metro was playing here.
The ramp segments themselves are pretty simple once they get going -- the entire thing is calculated at once, and then it's just a matter of adding the resulting constant increment until the target time has elapsed, or a new event supersedes the current ramp. This is less than the interpolation formula in [tabread4~], and not much more than what [line~] itself has to do (although all the time stuff is done using doubles so there may be some overhead there depending on architecture). But it does have to run one or more nested conditionals every sample. Most of the time it's checking to see if there's something in the linked list, so there isn't much to do, but if you sent it a bunch of events at once, it's got a lot of things it has to do. Here's the per-sample for-loop:
for (i = 0; i < n; i++)
{
double timenext = timenow + msecpersamp;
checknext:
if (s)
{
/* has starttime elapsed? If so update value and increment */
if (s->s_starttime < timenext)
{
if (x->x_targettime <= timenext)
f = x->x_target, inc = 0;
/* if zero-length segment bash output value */
if (s->s_targettime <= s->s_starttime)
{
f = s->s_target;
inc = 0;
}
else
{
double incpermsec = (s->s_target - f)/
(s->s_targettime - s->s_starttime);
f = f + incpermsec * (timenext - s->s_starttime);
inc = incpermsec * msecpersamp;
}
x->x_inc = inc;
x->x_target = s->s_target;
x->x_targettime = s->s_targettime;
x->x_list = s->s_next;
t_freebytes(s, sizeof(*s));
s = x->x_list;
goto checknext;
}
}
if (x->x_targettime <= timenext)
f = x->x_target, inc = x->x_inc = 0, x->x_targettime = 1e20;
*out++ = f;
f = f + inc;
timenow = timenext;
}
That's a lot of conditionals. Luckily all of the clock function calls and the can take place outside this loop because sample rate is constant, and the knotty boolean algebra for structuring the event list is handled in the vline_tilde_float method. The *goto checknext *here adds one extra pass if there are two events scheduled for the same time (I think the vline_tilde_float method ensures that the only way this can happen is in the "leap, then ramp" scenario). Allocating new events (in the vline_tilde_float method) and deallocating past events from the linked list — t_freebytes(s, sizeof(*s)); here — also has some overhead not encountered in [line~].
In general, [vline~] has more to do per event scheduled, and also more to do per sample, than [line~].
On Sun, Sep 27, 2015 at 1:03 AM, Jonathan Wilkes jancsika@yahoo.com wrote:
And what vline does, is it reads that list, and then schedules itself to
be hit after the correct amount of logical time has elapsed.
I'm not exactly sure what "schedules itself" means here. So I'll just start explaining how it works until I understand it myself... :)
The [vline~] object processes blocks of samples at a time (in vline_tilde_perform). But any of those clock events Frank mentioned that happen to be "ripe" for the upcoming block get sent to the "float" method of line (vline_tilde_float), which adds them to the linked list x_xlist.
For example, suppose you have the following:
[0 0.1, 1 0.2( | [pipe] | [vline~] | [print~]
When you click the message box, [pipe] does the proper clock delay for each message: the "0" is scheduled for 0.1ms in the future, and the "1" is scheduled for 0.2ms. But these delay times take less time to fire than it takes to compute a block. So if [vline~] is calculating its output in blocks of 64 samples, when does it actually receive the output from [pipe~]?
The answer is that Pd actually sends the "0" and "1" messages to [vline~] one after the other, before it calculates the next block. By "send" I mean it calls the function vline_tilde_float with the float argument "0", and then it calls vline_tilde_float with the float argument "1". But before each of those calls, it updates the "sys_time" to be the time when that clock was set to go off. So if the "0" was set to go off at "now + 0.1", Pd sets "now+0.1" to be the new system time, then it sends the "0" message to vline_tilde_float. And before it sends "1", it sets the sys_time to "now + 0.2".
Let's go back to vline_tilde_float-- notice in the first line we're fetching the sys_time. And what is it going to be for our "0" message? As stated above, we set it to "now + 0.1". Great! And when we process "1" message, we get "now + 0.2". Thus, [vline~] adds those messages and their associated timings to the linked list.
Finally, we do [vline~] perform routine. It starts with the time equal to what it was at the beginning of the block. Then it loops through our block's samples until it reaches a sample that corresponds to the time that an event in our linked list needs to be triggered. When we reach that sample, we jump to the new value.
It's the same process for ramps, except that vline~ calculates an increment value for the ramp.
After looking, I'm kind of curious what is most responsible for [vline~] overhead. I'd guess it's the math needed to set each ramp segment, but I'm not certain.
-Jonathan
On Saturday, September 26, 2015 10:55 PM, i go bananas hard.off@gmail.com wrote:
I actually think Frank might have the my answer here.
What i'm noticing, is not an inconsistency in the length of the ramps. It's an inconsisency in when they are first triggered. This is for stuff like the initial click of a bassdrum, so you can even hear the difference quite clearly. The line object jumps around, as it is being triggered on block boundaries, but the vline object is faithfully consistent in starting at the exact TIME i ask it to - regardless of blocksize, etc.
The only logical answer i can see here, is the one Frank has given. I'm scheduling drums using a clock controlled by metro.
So what i'm guessing that Frank is suggesting here, is that the scheduler is keeping a list of all the metro and delay bangs that should arrive in the upcoming block, and sceduling them to be hit while the block plays. And what vline does, is it reads that list, and then schedules itself to be hit after the correct amount of logical time has elapsed.
And i would guess that the normal line object is not capable of that. It just waits until block boundaries and starts its simple incrementation.
So, going back to Jonathan's day-long block example, i can see now how that works. the line can only be triggered once, at the start of the block (i still have doubts that it would actually stretch to the length of the block though. I reckon a 50ms line is still gonna take 50ms. ) But the vline has that linked list inbuilt, and it reads from the scheduler to see when it should be triggered. So if you had the day-long block, then vline's linked list is gonna end up with a full day's worth of messages in it!
Anyway, i'll go back to the source code tomorrow and have another look. But pretty sure that i now see how it is the ability to look ahead at metro scheduled events that gives vline the accuracy i need.
Thanks Matt for suggesting i look at the clock, and scheduler code, thanks Jonothan for the great example of the day long block - taking things to extremes like that is a good way to see how they work. And thanks Frank for finally turning on that lightbulb and suggesting the role that metro was playing here.
Hi,
On Sat, Sep 26, 2015 at 10:08:16PM +0000, Jonathan Wilkes wrote:
Hi Frank,The [1, 0 50( message will almost always trigger different output when fed to [line~] and [vline~]. The only exceptionis when the ramp ends exactly on a block boundary--otherwise [line~] will stretch
... or compress ...
the final part of the ramp tothe block boundary.
You're right, I wasn't thinking of the line-endpoints in my explanation. In fact, until now I believed, that these wouldn't be quantized with line~, but indeed they are (see attachement). And it's pretty much what Miller writes in the book: http://msp.ucsd.edu/techniques/latest/book-html/node43.html#fig03.05
Still in my view the quantization of endpoints is a bit counter-intuitive. Whereas the actual firing of a message from the GUI is inherently vague and shaky, the duration of the ramp (50 ms in the example) is written explicitely in the control message. That [line~] changes it, "feels" like a bug, although it isn't, and in the end it's the same way, [line~] deals with clock-delayed messages.
For many applications the correct timing of start points musically is more important (drums!), but one should be aware of possible quantization at endpoints as well.
Frank Barknecht _ ______footils.org__
My apologies for doubting you Jonathan. The line object does indeed stretch to fill the whole block.
No problem. This is a difficult part of the software to understand. -Jonathan
On Sunday, September 27, 2015 9:16 PM, i go bananas <hard.off@gmail.com> wrote:
My apologies for doubting you Jonathan. The line object does indeed stretch to fill the whole block.
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Ok, looked again at the code: If you try to do a ramp in less time than it takes to compute a block, [line~] will stretch the ramp to fill one block. If the ramp takes longer than a block, the ramp will only extend to the last full block. So if you specify a ramp timethat would normally take up 3.5 blocks, [line~] will quantize the ramp to happen in 3 blocks. -Jonathan
On Sunday, September 27, 2015 11:20 PM, Jonathan Wilkes via Pd-list <pd-list@lists.iem.at> wrote:
No problem. This is a difficult part of the software to understand. -Jonathan
On Sunday, September 27, 2015 9:16 PM, i go bananas <hard.off@gmail.com> wrote:
My apologies for doubting you Jonathan. The line object does indeed stretch to fill the whole block.
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Hi, me again.
Thanks for the discussion. It has really opened my eyes.
So, i got my naive c++ implementation of line~ basically working.
And of course, just running a for loop incrementing by ticks, i run into the exact precision error that this block quantizing seems to avoid. My line from 0 to 100 over 44100 samples only gets to 99.93
So, i also need to add something like pd's block quantization to make sure my line goes all the way to the specified value.
My questions then are 2:
Is pd's method the way i should do it? Or is there a better alternative?
And, if i do it the pd way, how does that work? Does the increment get updated every block? Or is it just the last block that is stretched?
Hey i go bananas,
I can't say I'm totally familiar with Pd's specific implementation but I did this recently and a simple implementation is to store the target value and calculate how many samples it takes to reach it based on the delay time. In your loop decrement the number of samples to target and output the slope value, when numSamplesToTarget is 0 just output the stored target value.
Cheers, Joe
On 2 October 2015 at 16:36, i go bananas hard.off@gmail.com wrote:
Hi, me again.
Thanks for the discussion. It has really opened my eyes.
So, i got my naive c++ implementation of line~ basically working.
And of course, just running a for loop incrementing by ticks, i run into the exact precision error that this block quantizing seems to avoid. My line from 0 to 100 over 44100 samples only gets to 99.93
So, i also need to add something like pd's block quantization to make sure my line goes all the way to the specified value.
My questions then are 2:
Is pd's method the way i should do it? Or is there a better alternative?
And, if i do it the pd way, how does that work? Does the increment get updated every block? Or is it just the last block that is stretched?
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Post some code. :)
In both [line~] and [vline~] I believe the increment is calculated, set, and unset set once per event, since all the info you need for the ramp is given by the event definition. Unsetting it is the fun part -- it doesn't unset once it's reached its target, but rather once the number of blocks or samples has passed. Check out these lines of code, though:
from [line~]: if (x->x_ticksleft) { t_sample f = x->x_value; while (n--) *out++ = f, f += x->x_inc; x->x_value += x->x_biginc; x->x_ticksleft--; } else { t_sample g = x->x_value = x->x_target; while (n--) *out++ = g; }
from [vline~]: if (x->x_targettime <= timenext) f = x->x_target, inc = x->x_inc = 0, x->x_targettime = 1e20; *out++ = f; f = f + inc;
In [line~]: if there are still blocks to ramp over, get the value set from the last block, increment all the samples in the new block by the increment, store what will be the value for the next block, and decrement the number of blocks left. If there are no blocks left, then manually set a variable and the next block value to the target, and write that to all the points in the block.
In [vline~]: if the target time has elapsed, set the variable f to the target, the increment to zero, and put the next target time WAY off in the future. Then continue to increment successive values in the block by 0. Since it's checking every sample here instead of every block, a conditional here would be more expensive than just adding 0 every time.
Anyway, manually setting the output value to the target at the end of the ramp ensures that you'll make your target. That way you don't have to worry about trying to time exactly when to unset the increment right when it's finally incremented to the target, which is a great way to get an off-by-one error.
Matt
On Fri, Oct 2, 2015 at 11:36 AM, i go bananas hard.off@gmail.com wrote:
Hi, me again.
Thanks for the discussion. It has really opened my eyes.
So, i got my naive c++ implementation of line~ basically working.
And of course, just running a for loop incrementing by ticks, i run into the exact precision error that this block quantizing seems to avoid. My line from 0 to 100 over 44100 samples only gets to 99.93
So, i also need to add something like pd's block quantization to make sure my line goes all the way to the specified value.
My questions then are 2:
Is pd's method the way i should do it? Or is there a better alternative?
And, if i do it the pd way, how does that work? Does the increment get updated every block? Or is it just the last block that is stretched?
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
yeah, am considering the option of just manually setting once the ramp finishes. But considering that i'm off by about 0.1% every 44100 samples, it's a bit worrying. Would be ok for synth envelopes, etc...but i'll probably want to use this to lookup audio file tables, etc too, in the future, so kinda want to get it spot on.
right now, the performDSP function is literally a single conditional and then += increment, which seems nice and lightweight. i'd like to aim to keep it as close to that as possible...
Pd does the increment and time stuff in double precision. You're likely losing precision by going with float for everything; if you divide 100/44100 and get a float, and then increment 0 with it 44100 times, you're not necessarily going to end up with exactly 100 at the end.
See attached.
M
On Fri, Oct 2, 2015 at 12:44 PM, i go bananas hard.off@gmail.com wrote:
yeah, am considering the option of just manually setting once the ramp finishes. But considering that i'm off by about 0.1% every 44100 samples, it's a bit worrying. Would be ok for synth envelopes, etc...but i'll probably want to use this to lookup audio file tables, etc too, in the future, so kinda want to get it spot on.
right now, the performDSP function is literally a single conditional and then += increment, which seems nice and lightweight. i'd like to aim to keep it as close to that as possible...
There are probably a lot more ways to implement ramps. For example, youcould increment only at block boundaries and just repeat that value for therest of the block. That would looks a lot like Supercollider's "kr" ugens. (Iactually thought that's how [line~] worked until I looked at the code.) Btw-- does it cost anything significant to dereference x->x_inc inside thewhile loop of line_tilde_perform? Or is the compiler able to somehowoptimize that? -Jonathan
On Friday, October 2, 2015 11:36 AM, i go bananas <hard.off@gmail.com> wrote:
Hi, me again. Thanks for the discussion. It has really opened my eyes. So, i got my naive c++ implementation of line~ basically working. And of course, just running a for loop incrementing by ticks, i run into the exact precision error that this block quantizing seems to avoid. My line from 0 to 100 over 44100 samples only gets to 99.93 So, i also need to add something like pd's block quantization to make sure my line goes all the way to the specified value. My questions then are 2: Is pd's method the way i should do it? Or is there a better alternative? And, if i do it the pd way, how does that work? Does the increment get updated every block? Or is it just the last block that is stretched?
You'll get zipper noise with the samphold per block approach.
Cost to dereference a struct member is probably a little more than just using or getting a value. It's possible it'll be cached, though. On Oct 2, 2015 5:26 PM, "Jonathan Wilkes via Pd-list" pd-list@lists.iem.at wrote:
There are probably a lot more ways to implement ramps. For example, you could increment only at block boundaries and just repeat that value for the rest of the block. That would looks a lot like Supercollider's "kr" ugens. (I actually thought that's how [line~] worked until I looked at the code.)
Btw-- does it cost anything significant to dereference x->x_inc inside the while loop of line_tilde_perform? Or is the compiler able to somehow optimize that?
-Jonathan
On Friday, October 2, 2015 11:36 AM, i go bananas hard.off@gmail.com wrote:
Hi, me again.
Thanks for the discussion. It has really opened my eyes.
So, i got my naive c++ implementation of line~ basically working.
And of course, just running a for loop incrementing by ticks, i run into the exact precision error that this block quantizing seems to avoid. My line from 0 to 100 over 44100 samples only gets to 99.93
So, i also need to add something like pd's block quantization to make sure my line goes all the way to the specified value.
My questions then are 2:
Is pd's method the way i should do it? Or is there a better alternative?
And, if i do it the pd way, how does that work? Does the increment get updated every block? Or is it just the last block that is stretched?
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Doesn't the audibility of zipper noise depend on the duration of the ramp? I seem to remember some Supercollider tutorials that used Line.kr witha duration of a second or so, and I don't remember hearing zipper noise. (Alsomade a little Pd demo using [bang~] and a counter, but I don't know what Idid with it.)
-Jonathan
On Friday, October 2, 2015 7:41 PM, Matt Barber <brbrofsvl@gmail.com> wrote:
You'll get zipper noise with the samphold per block approach.Cost to dereference a struct member is probably a little more than just using or getting a value. It's possible it'll be cached, though.On Oct 2, 2015 5:26 PM, "Jonathan Wilkes via Pd-list" pd-list@lists.iem.at wrote:
There are probably a lot more ways to implement ramps. For example, youcould increment only at block boundaries and just repeat that value for therest of the block. That would looks a lot like Supercollider's "kr" ugens. (Iactually thought that's how [line~] worked until I looked at the code.) Btw-- does it cost anything significant to dereference x->x_inc inside thewhile loop of line_tilde_perform? Or is the compiler able to somehowoptimize that? -Jonathan
On Friday, October 2, 2015 11:36 AM, i go bananas <hard.off@gmail.com> wrote:
Hi, me again. Thanks for the discussion. It has really opened my eyes. So, i got my naive c++ implementation of line~ basically working. And of course, just running a for loop incrementing by ticks, i run into the exact precision error that this block quantizing seems to avoid. My line from 0 to 100 over 44100 samples only gets to 99.93 So, i also need to add something like pd's block quantization to make sure my line goes all the way to the specified value. My questions then are 2: Is pd's method the way i should do it? Or is there a better alternative? And, if i do it the pd way, how does that work? Does the increment get updated every block? Or is it just the last block that is stretched?
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
It'll have more to do with how large the increment is, and what an increment means psychoacoustically. If you ramp over the range of a piano in pitch over a second, incrementing every 64 samples, each increment will be about 12.5¢, which is likely to be audible. Or if you ramped from 20hz to 20khz in frequency, you'd be adding some 28hz per increment, which is a lot of pitch at the low end. Doesn't the audibility of zipper noise depend on the duration of the ramp?
I seem to remember some Supercollider tutorials that used Line.kr with a duration of a second or so, and I don't remember hearing zipper noise. (Also made a little Pd demo using [bang~] and a counter, but I don't know what I did with it.)
-Jonathan
On Friday, October 2, 2015 7:41 PM, Matt Barber brbrofsvl@gmail.com wrote:
You'll get zipper noise with the samphold per block approach. Cost to dereference a struct member is probably a little more than just using or getting a value. It's possible it'll be cached, though. On Oct 2, 2015 5:26 PM, "Jonathan Wilkes via Pd-list" pd-list@lists.iem.at wrote:
There are probably a lot more ways to implement ramps. For example, you could increment only at block boundaries and just repeat that value for the rest of the block. That would looks a lot like Supercollider's "kr" ugens. (I actually thought that's how [line~] worked until I looked at the code.)
Btw-- does it cost anything significant to dereference x->x_inc inside the while loop of line_tilde_perform? Or is the compiler able to somehow optimize that?
-Jonathan
On Friday, October 2, 2015 11:36 AM, i go bananas hard.off@gmail.com wrote:
Hi, me again.
Thanks for the discussion. It has really opened my eyes.
So, i got my naive c++ implementation of line~ basically working.
And of course, just running a for loop incrementing by ticks, i run into the exact precision error that this block quantizing seems to avoid. My line from 0 to 100 over 44100 samples only gets to 99.93
So, i also need to add something like pd's block quantization to make sure my line goes all the way to the specified value.
My questions then are 2:
Is pd's method the way i should do it? Or is there a better alternative?
And, if i do it the pd way, how does that work? Does the increment get updated every block? Or is it just the last block that is stretched?
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
I think I saw it used just to fade in and out on a sine tone.
On Friday, October 2, 2015 9:52 PM, Matt Barber <brbrofsvl@gmail.com> wrote:
It'll have more to do with how large the increment is, and what an increment means psychoacoustically. If you ramp over the range of a piano in pitch over a second, incrementing every 64 samples, each increment will be about 12.5¢, which is likely to be audible. Or if you ramped from 20hz to 20khz in frequency, you'd be adding some 28hz per increment, which is a lot of pitch at the low end.Doesn't the audibility of zipper noise depend on the duration of the ramp? I seem to remember some Supercollider tutorials that used Line.kr witha duration of a second or so, and I don't remember hearing zipper noise. (Alsomade a little Pd demo using [bang~] and a counter, but I don't know what Idid with it.)
-Jonathan
On Friday, October 2, 2015 7:41 PM, Matt Barber <brbrofsvl@gmail.com> wrote:
You'll get zipper noise with the samphold per block approach.Cost to dereference a struct member is probably a little more than just using or getting a value. It's possible it'll be cached, though.On Oct 2, 2015 5:26 PM, "Jonathan Wilkes via Pd-list" pd-list@lists.iem.at wrote:
There are probably a lot more ways to implement ramps. For example, youcould increment only at block boundaries and just repeat that value for therest of the block. That would looks a lot like Supercollider's "kr" ugens. (Iactually thought that's how [line~] worked until I looked at the code.) Btw-- does it cost anything significant to dereference x->x_inc inside thewhile loop of line_tilde_perform? Or is the compiler able to somehowoptimize that? -Jonathan
On Friday, October 2, 2015 11:36 AM, i go bananas <hard.off@gmail.com> wrote:
Hi, me again. Thanks for the discussion. It has really opened my eyes. So, i got my naive c++ implementation of line~ basically working. And of course, just running a for loop incrementing by ticks, i run into the exact precision error that this block quantizing seems to avoid. My line from 0 to 100 over 44100 samples only gets to 99.93 So, i also need to add something like pd's block quantization to make sure my line goes all the way to the specified value. My questions then are 2: Is pd's method the way i should do it? Or is there a better alternative? And, if i do it the pd way, how does that work? Does the increment get updated every block? Or is it just the last block that is stretched?
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list
Audibility of zipper noise would depend on the ramp length (shorter ramp would have bigger zip jumps) , but would also depend heavily on what is being done with the ramp. Ramping the volume of a long bass tone would be much more audible than ramping the volume of a noisy wind sample.
Matt, yep, should have been doing my processing with doubles too. That's really made it better.
For any purpose i will use, incrementing by double, and then setting the final tick to target value looks like it will be more than good enough.
Actually, guess i'll probably just throw in a second performDSP method, without using the conditional to set that final tick. For audio envelopes, i don't care too much if it only goes to 0.9993 instead of 1
Actually, yeah it does matter if line doesn't hit its target. When ramping 1 to 0, really wanna make sure it goes all the way to 0 to fully switch volume off, etc.
Sorry, just thinking aloud.