Log inRegister an accountBrowse CSDbHelp & documentationFacts & StatisticsThe forumsAvailable RSS-feeds on CSDbSupport CSDb Commodore 64 Scene Database
You are not logged in - nap
CSDb User Forums


Forums > C64 Coding > Neat way to implement single/double hard-restart
2016-03-17 06:29
lft

Registered: Jul 2007
Posts: 369
Neat way to implement single/double hard-restart

In SID envelope rate counter phase alignment, Laurent6581 off-topicly wrote:

Quoting Laurent6581
I have seen many players that seemed to voluntarily triggered a second delay bug before attack restart.


Thanks a lot for this! I was unaware of the technique, but it is very useful. Here's how I implemented it in my current playroutine:

Two frames before a hard-restart instrument is about to be triggered, clear SR and Gate. On the target frame---again, only for hard-restart instruments---first write 0f to SR. The counter escapes from the 9-cycle bottle. Now do other stuff (including setting up AD), counting the CPU cycles. When the envelope counter is known to be in the range 33..61, set Gate. Finally write the correct SR.

In this way, hard-restart instruments with attack rate 0 or 1 will have the extra 30 ms delay, for snappy percussion sounds. Attack rate 2 or higher will behave normally. And the bug where the decay rate is used for one cycle is circumvented automatically.

The total attack rate becomes:

Rate   Normal   Extra   Total
0        2 ms   30 ms   32 ms
1        8 ms   30 ms   38 ms
2       16 ms           16 ms
3       24 ms           24 ms
4       38 ms           38 ms
...


So, for instance, instruments with attack rate 4 will line up with (hard-restart) instruments having attack rate 1.

Since the extra "delay" is just the envelope being stuck at zero, the first frame of wavetable/filtertable has to contain a dummy value. A corresponding delay of one frame could be programmed into the playroutine, or the dummy value could be hidden in the tracker UI.
2016-03-17 09:39
ChristopherJam

Registered: Aug 2004
Posts: 1409
Sweet!

Not sure about your characterisation of such notes having a 30ms longer attack time though; it's more that because the envelope start is delayed 30ms, all the wave/frequency/etc updates happen 1.5 frames earlier relative to the actual note start.

I think ideally I'd want to move a frame of that into the end of the previous note, so the delay's only 10ms. As it is, the player I'm working on places the two frames for HR into the tail end of the previous note (iff HR is required). Might have to try to incorporate something similar for this..
2016-03-17 10:13
lft

Registered: Jul 2007
Posts: 369
To clarify: It is the peaks of the envelopes that will line up for rates 4 and 1. The onsets of the notes will differ by 30 ms.

If I understood your suggestion correctly, that would involve starting the entire procedure three frames ahead of time when a hard-restart instrument with attack rate 0 or 1 is due. In my player right now, that information is not available at that time, although with a different architecture it could certainly be done. It's the usual tradeoff between rastertime, code size and features.
2016-03-17 11:20
ChristopherJam

Registered: Aug 2004
Posts: 1409
Yes, fair point about the peak alignment.

Quote:
If I understood your suggestion correctly, that would involve starting the entire procedure three frames ahead of time when a hard-restart instrument with attack rate 0 or 1 is due.

Not necessarily. If we use your suggestion from Avoiding the ADSR bug in the decay phase. to hide the bug in the sustain phase, then one can use the second last frame of the previous note to initiate a 6ms release, then in the final frame one can use your suggestion above to kick off a delayed attack :D
2016-03-17 11:47
cadaver

Registered: Feb 2002
Posts: 1160
Requiring a semi-precise window of timing practically means you'll have to run the player in the border, otherwise the variable time of execution on other channels may lead the time-critical execution of e.g. the last channel to happen during a badline.
2016-03-17 13:29
lft

Registered: Jul 2007
Posts: 369
Quoting ChristopherJam
If we use your suggestion from Avoiding the ADSR bug in the decay phase. to hide the bug in the sustain phase, then one can use the second last frame of the previous note to initiate a 6ms release, then in the final frame one can use your suggestion above to kick off a delayed attack :D


Ah, I see. But then you'd rely on the composer manually ensuring that the first hard-restart won't be necessary, so that it's possible to perform only the second. To me, that defeats the purpose of having hard-restart in the first place.
2016-03-17 13:41
lft

Registered: Jul 2007
Posts: 369
Quoting cadaver
Requiring a semi-precise window of timing practically means you'll have to run the player in the border, otherwise the variable time of execution on other channels may lead the time-critical execution of e.g. the last channel to happen during a badline.


Correct!

For what it's worth, I believe that sprite DMA is safe. Nine cycles of phase uncertainty plus 0..19 stolen cycles, that's a jitter of 9 + 20 - 1 = 28 cycles, and we need to be within a 31-cycle range. So ghost sprites in the border shouldn't be a problem as long as the playroutine is tightly coded.
2016-03-17 13:44
ChristopherJam

Registered: Aug 2004
Posts: 1409
My plan is to have the editor software (rather than the composer) ensure every note ends with env=0, and the rate counter safely trapped in the 0-8 loop. But this is getting a fair way off topic now.

Nice cycle counting on the sprite DMA uncertainty, btw :)
2016-03-18 03:01
Agemixer

Registered: Dec 2002
Posts: 39
Does this all assume that sustain value should be dropped completely down to $00 before restart? Or by (level-1)..0?

Now this is why i never understood why a hard restart should be done in the way you expect:

What if i wanted to leave at previous sustain level (read: reverb) until just the next tone Attack cycle starts?
..Without any delay, how would you deal with that?

I'd say the release would be only used for HR then. Decay is identical to HR, so why not simulate the release with lowering Sustain level instead. But how would you do a HR then, let's say when the remaining amplitude would be anything between $00..$ff? (And i don't mean the $d406 sustain setting)
2016-03-18 08:00
Jammer

Registered: Nov 2002
Posts: 1335
To add my three cents, I've practically stopped using Sustain/Release in instruments at 2x/4x speed if instrument is supposed to fade out at any time - I set SR to 0F and operate on AD entirely. It allowed me to use hardrestart time in Goat Tracker reduced from 8 player calls to 1 or 2 at 4x speed:

http://f.jammerstudio.pl/RealClap_4x.sid

Besides it's definitely more stable that my previous unstable Attack set to 1 that imposed earlier sound and I can use really quick retriggers ;)
2016-03-18 08:14
lft

Registered: Jul 2007
Posts: 369
Agemixer, I think there are two motivations for the traditional approach: Simplicity (and thus code size), and consistency of note starts. It's important to have precise control over the attack of notes, and that becomes difficult if the initial volume is varying.

When I want to do legato notes, I make some copies of the current instrument, disable hard-restart and fix the ADSR values to prevent the bug. Or I just change the pitch without retriggering the envelope.

But it's certainly possible to do what you suggest under the right circumstances, and I think that it overlaps with what ChristopherJam is attempting as well. If a note is currently being sustained (gate = on), we can do the hard-restart by writing 00 to AD two frames before the next note. To trigger the new note, we clear SR and the control register, set up the new AD, turn gate on, and finally set up SR. On the other hand, if no note is playing, we can do hard-restart the normal way. Or, like you suggest, we always stop notes by dropping S to 0 and leaving gate on.

The problem is when there's a note playing in the attack or decay phases. Clearing AD would produce an audible blip that would be out of time with the music. The only way to be safe would be to ensure, while composing, that there's enough time for every note to reach the sustain phase.
2016-03-18 15:46
ChristopherJam

Registered: Aug 2004
Posts: 1409
Quoting Agemixer
Does this all assume that sustain value should be dropped completely down to $00 before restart? Or by (level-1)..0?

Level (amplitude), not sustain.

Quote:
What if i wanted to leave at previous sustain level (read: reverb) until just the next tone Attack cycle starts?
..Without any delay, how would you deal with that?

Force a bug at start of second last frame of previous note, clear gate, and set S=R=0. This way the sustain continues until halfway through the last frame (as the envelope is frozen), then the level drops to zero in at most 6ms, leaving less than 10ms silence before the start of the next note.

Quote:
why not simulate the release with lowering Sustain level instead.

Internally, that's what I'll probably do, whenever there's a rest between that note and the next. But that's just an implementation detail; the editor should show a standard ADSR waveform.

Quote:
But how would you do a HR then, let's say when the remaining amplitude would be anything between $00..$ff? (And i don't mean the $d406 sustain setting)

See above - freeze env for 30ms, at the end of which it'll take at most 6ms to drop down from any amplitude. (edited now I read the question properly)
2016-03-18 20:24
lft

Registered: Jul 2007
Posts: 369
Quoting ChristopherJam
Force a bug at start of second last frame of previous note, clear gate, and set S=R=0. This way the sustain continues until halfway through the last frame (as the envelope is frozen), then the level drops to zero in at most 6ms, leaving less than 10ms silence before the start of the next note.


How would you force the bug at that precise moment unless you know the value of the counter? For long notes, you could rely on a hard-restart hidden in the sustain phase, but again, what if the new note is interrupting an ongoing attack or decay?
2016-03-18 20:33
lft

Registered: Jul 2007
Posts: 369
Quoting lft
Quoting cadaver
Requiring a semi-precise window of timing practically means you'll have to run the player in the border, otherwise the variable time of execution on other channels may lead the time-critical execution of e.g. the last channel to happen during a badline.


Correct!

For what it's worth, I believe that sprite DMA is safe. Nine cycles of phase uncertainty plus 0..19 stolen cycles, that's a jitter of 9 + 20 - 1 = 28 cycles, and we need to be within a 31-cycle range. So ghost sprites in the border shouldn't be a problem as long as the playroutine is tightly coded.


Some further thoughts: The worst that could happen is eight sprites, badline, eight sprites. That's going to push the counter beyond 63, but it's still well below 149. For the musician, this means that hard-restart instruments with attack rate 2 or 3 are unsafe (i.e. the note start could randomly be delayed by 30 ms) if the playroutine executes in the normal display area. Any other attack rate is going to behave properly. Rates 0 and 1 will always have the extra delay, and rates 4 and above will never have it. So this technique would still be usable in e.g. CIA-tempo tunes if you just avoid those two attack rates.
2016-03-18 22:13
ChristopherJam

Registered: Aug 2004
Posts: 1409
Quote: Quoting ChristopherJam
Force a bug at start of second last frame of previous note, clear gate, and set S=R=0. This way the sustain continues until halfway through the last frame (as the envelope is frozen), then the level drops to zero in at most 6ms, leaving less than 10ms silence before the start of the next note.


How would you force the bug at that precise moment unless you know the value of the counter? For long notes, you could rely on a hard-restart hidden in the sustain phase, but again, what if the new note is interrupting an ongoing attack or decay?


Editor should know what the rate limit is at that point, from the duration of the note. If the rate limit index is less than $f,then increase to $f for at least nine cycles, then drop rate limit index to $0

If rate limit index is equal to $f then it's a bit messier; easy way out would be to lower that for any notes short enough to be interrupted..

Of course, if it's a fairly fast attack or decay, then freezing the envelope level for 30ms might not be appropriate..
2016-03-19 20:24
lft

Registered: Jul 2007
Posts: 369
Quoting ChristopherJam
Editor should know what the rate limit is at that point, from the duration of the note. If the rate limit index is less than $f,then increase to $f for at least nine cycles, then drop rate limit index to $0


Suppose that rate $d is in use, perhaps during a note release. When it is time for hard-restart, the player switches to $f, then to $0. This will indeed trigger a wraparound. If the counter happened to be at a low value, you'll get the 30 ms delay, and "less than 10ms silence before the start of the next note". But if the counter was at 10000, you'll only get a 20 ms delay, and thus 20 ms of silence.
2016-03-20 03:55
ChristopherJam

Registered: Aug 2004
Posts: 1409
Quote: Quoting ChristopherJam
Editor should know what the rate limit is at that point, from the duration of the note. If the rate limit index is less than $f,then increase to $f for at least nine cycles, then drop rate limit index to $0


Suppose that rate $d is in use, perhaps during a note release. When it is time for hard-restart, the player switches to $f, then to $0. This will indeed trigger a wraparound. If the counter happened to be at a low value, you'll get the 30 ms delay, and "less than 10ms silence before the start of the next note". But if the counter was at 10000, you'll only get a 20 ms delay, and thus 20 ms of silence.


Good point. I guess anything from $d to $f would need dropping down to $c a frame or two in advance; if the decay is that slow then halting the fall for a few frames should be inaudible.
2016-03-20 03:57
ChristopherJam

Registered: Aug 2004
Posts: 1409
Jammer, those claps are excellent! I'm going to have to take a closer look at your register bashing..
2016-08-07 21:18
lft

Registered: Jul 2007
Posts: 369
Quoting lft
Quoting lft
Quoting cadaver
Requiring a semi-precise window of timing practically means you'll have to run the player in the border, otherwise the variable time of execution on other channels may lead the time-critical execution of e.g. the last channel to happen during a badline.


Correct!

For what it's worth, I believe that sprite DMA is safe. Nine cycles of phase uncertainty plus 0..19 stolen cycles, that's a jitter of 9 + 20 - 1 = 28 cycles, and we need to be within a 31-cycle range. So ghost sprites in the border shouldn't be a problem as long as the playroutine is tightly coded.


Some further thoughts: The worst that could happen is eight sprites, badline, eight sprites. That's going to push the counter beyond 63, but it's still well below 149. For the musician, this means that hard-restart instruments with attack rate 2 or 3 are unsafe (i.e. the note start could randomly be delayed by 30 ms) if the playroutine executes in the normal display area. Any other attack rate is going to behave properly. Rates 0 and 1 will always have the extra delay, and rates 4 and above will never have it. So this technique would still be usable in e.g. CIA-tempo tunes if you just avoid those two attack rates.


Based on a true story: If you use this technique, and you have sprite DMA interfering with the playroutine, then you should avoid decay rate 2 in double-hard-restart instruments (e.g. with attack 0 or 1).

Normally, my routine above will nicely avoid the bug where the decay rate is used for the first cycle of attack. However, at least in my current playroutine, if eight sprites are active and the envelope counter is at 8 when the bottle is opened, the counter will be at 62 just when the gate bit is set. Then the counter will wrap around to zero, preventing the second hard-restart and messing up the note attack. This only happens on real hardware, and it goes away if I change the decay rate to 1.
RefreshSubscribe to this thread:

You need to be logged in to post in the forum.

Search the forum:
Search   for   in  
All times are CET.
Search CSDb
Advanced
Users Online
WVL/Xenon
Unlock/Padua/Albion
Freeze/Blazon
MWR/Visdom
Steffan/BOOM!
Didi/Laxity
sln.pixelrat
csabanw
Trap/Bonzai
Guests online: 110
Top Demos
1 Next Level  (9.7)
2 13:37  (9.7)
3 Mojo  (9.7)
4 Coma Light 13  (9.6)
5 Edge of Disgrace  (9.6)
6 What Is The Matrix 2  (9.6)
7 The Demo Coder  (9.6)
8 Uncensored  (9.6)
9 Comaland 100%  (9.6)
10 Wonderland XIV  (9.6)
Top onefile Demos
1 Layers  (9.6)
2 No Listen  (9.6)
3 Cubic Dream  (9.6)
4 Party Elk 2  (9.6)
5 Copper Booze  (9.6)
6 Dawnfall V1.1  (9.5)
7 Rainbow Connection  (9.5)
8 Onscreen 5k  (9.5)
9 Morph  (9.5)
10 Libertongo  (9.5)
Top Groups
1 Performers  (9.3)
2 Booze Design  (9.3)
3 Oxyron  (9.3)
4 Triad  (9.3)
5 Censor Design  (9.3)
Top Swappers
1 Derbyshire Ram  (10)
2 Jerry  (9.8)
3 Violator  (9.7)
4 Acidchild  (9.7)
5 Cash  (9.6)

Home - Disclaimer
Copyright © No Name 2001-2024
Page generated in: 0.088 sec.