I was looking at Concerto:
I like that approach to envelope management: envelopes are configurations that any voice can use.
So I've been thinking about a sound API.
I've thought about a full-fledged API, but I think just an envelope manager with an interrupt is the right way.
VOICE ENVELOPES: $0200 thru $020F. Which envelope each voice is using, if any.
VOICE STATES: $0210 thru $021F. (Internal) state variable. Current state ('A','D','S','R', or 0 for 'done') of voice. Used by interrupt.
VOICE TARGETS: $0220 thru $022F. (Internal) state variable. Target value for voice. Used by interrupt.
ENVELOPE DATA: $0230 and up. Envelope configurations. Four bytes per envelope: one each for Attack, Decay, Sustain, and Release, in jiffies or something.
Theoretically there can be a whole bunch of envelope configs. Concerto (above) has three. Performance testing can determine what a reasonable number is (somewhere between 1 and 16+).
...and then an interrupt which modifies voice volume based on the envelope it's set at.
So each cycle, the interrupt checks each voice state and the voice's current volume:
If the current state is 0, then skip.
If the current state is 255, then set the current state to 'A' and set up the temporary variables for this voice.
If the current state is 'A':
increase volume. steepness is based on attack value.
if current volume == target, update state to 'D'.
If the current state is 'D':
decrease volume. steepness is based on decay value.
if current volume == target, update state to 'S'.
If the current state is 'S':
decrease target. steepness is based on sustain value.
if target == 0, update state to 'R'.
If the current state is 'R':
decrease volume. steepness is based on release value.
if current volume == 0, update status to 0.