Page 1 of 1

Drawing ADSR envelopes to screen.

Posted: Thu Oct 09, 2014 1:46 pm
by Count SymphoniC
What the hell! How is this possible? I see this as a great challenge considering the tile based graphics system. Lots of sprites maybe? What would be the most feasible option?

Posted: Thu Oct 09, 2014 2:02 pm
by r57shell
You just need to implement bitmap drawing based on tiles, nothing difficut.
For me, vizualize sensible ADSR envelope itself much harder. I mean, if you can draw curve, which one to draw?

Posted: Thu Oct 09, 2014 2:03 pm
by BigEvilCorporation
If you can manipulate individual pixels, you can draw anything ;)

A basic line rasteriser shouldn't be too tricky to knock up with some basic maths. You'll need enough spare VDP memory for one tile per cell (for the area you want to draw on).

Posted: Thu Oct 09, 2014 2:35 pm
by Count SymphoniC
Thanks guys... I was thinking too much of working with the nametables and not the tiles' bytes themselves. r57shell you're probably right about that being the hard part. I guess I'll just have to break down each part of the envelope until each part works right and BigEvilCorp, as usual you're an inspiration. Bresenham's line algorithm looks useful for the logic part.

I can't wait to share my ADSR envelopes when they're done. They're going to be amazing.

Posted: Thu Oct 09, 2014 4:25 pm
by BigEvilCorporation
My next article is on ADSR, it's been in draft form for a long time though due to work commitments :(

Just in case it's of any use to you:



Welcome to the second article in the Megadrive Sound series! I've been hard at work on my PSG sequencer, making a few improvements, adding new features, and messing with different sounds. The first addition is software envelopes, which allow for individual notes to fade in and out over time. The second is white noise, which allows for some basic percussion sounds. As usual, I'll be starting off with a little theory behind the things I've implemented.

ADSR Envelopes

An envelope is a predefined volume curve applied to each note, which can be tweaked to provide softer sounding notes compared to just hammering them on or off at full volume. With a little creativity, they can be adjusted to create different sounding instruments, or can even be used to create very short notes to simulate percussion, especially when applied to white noise rather than a wave. I've been implementing a specific kind of envelope - ADSR. These envelopes comprise four parts - Attack, Decay, Sustain and Release. Attack is a curve from full attenuation up to an initial "on" volume. The volume then dips down a Decay curve, settling on the Sustain volume. When the sustain time has expired, the volume fades down over the Release curve.

Image

If you're not familiar with the concept, it's easier to imagine how a piano note behaves - it begins with the player pressing down on the key which hits the string with the mallet and immediately recoils, creating the attack sound. This very quickly (over a couple of milliseconds) decays down to a softer sustain volume. Once the player lifts their finger off they key, a dampener covers the string to force the sound to die down sharply, which is the release. Technically, there's a lot more to it than that, but this is all we need to know in order to approximate the sound electronically.

Software ADSR on the PSG

Many dedicated audio processors implement ADSR envelopes on the hardware, but the PSG is very simple and does no such thing. We need to manually ramp the volume up and down to approximate the curves. In the image above, I've demonstrated linear changes, but in the real world these would likely be curves. The PSG's attenuation values from 0xF to 0x0 step up the volume exponentially, so by incrementing the attenuation register linearly we get a curve for free. Unfortunately, with only 16 possible values, the difference between the top few values are quite steep, which can result in some undesired artefacts. It seems the PSG applies all register changes immediately, rather than waiting for the next square wave polarity flip, which means we can potentially alter the attenuation in the middle of a zero crossover point causing pops and clicks. There's no real solution to this, and after some discussions with a few wiser brains on the SpritesMind forum, it seems the best thing to do is avoid these top attenuation values completely. I've also been told the PSG's volume is far louder than the FM chip, so losing the ability to play the PSG at full blast is no real loss. With this in mind, I've chosen a minimum attenuation value of 0x5 to avoid these large, quick jumps in volume. So, how do we define the envelope? We need the following information: attack time, attack height, decay time, sustain height, and release time. We already have the sustain time, that's defined in the data for each note.

Posted: Thu Oct 09, 2014 4:56 pm
by Count SymphoniC
Yes I'm sure some of that information will be useful. I'm pretty familiar with ADSR envelopes and how they affect the volume of an instrument just not the programming side of it yet, but the issue with artifacts in the PSG is something interesting. Maybe that's why Deflemask doesn't support PSG envelopes... last I checked anyways.

Hey, whenever you get started on the YM2612 let me know. I can pass along the YMDJ .asm source file for all the register defines (there are many of them and typing them all out is a bit more involved than the other chips, enough for me to justify defining the YM2612 globals in a separate file) I try to be organized anyways.

Posted: Tue Jan 27, 2015 2:32 pm
by db-electronics
Image

With respect to the specifics of creating a line equation from register values; does anyone have specific information concerning the linearity of values?

For instance, in the sound manual it states that each increment in TL results in roughly a 0.75dB increase in perceived volume.

As an example, Attack Rate is stored as a 5bit number (0 to 31), does this map linearly from 0° to 90°?

Posted: Tue Jan 27, 2015 2:59 pm
by TmEE co.(TM)
Attack is exponential, rest are linear.

Attack : EnvOut += (NOT(EnvOut) * AttInc) SHR 4
Others : EnvOut += AttInc

AttInc value is 0,1,2,4 or 8, depending on some input parameters. I should turn them to shift values instead and lose the multiplication from my emulation core.