# Apply decay envelope to waveform without multiplication

I'm trying to apply a decay envelope to a sinusoidal waveform using Verilog. Hardware constraints prevent use of multiplication to simply multiply by the envelope. The sine values and envelope values, for which I'm simply using $1-e^{-x}$, are stored in ROMs to speed everything up.

Right now I'm simply calculating the output sample by subtracting the exponential value from the sine value if the sine value is larger. If the exponential value is larger than the sine value, the output sample is zero. This works pretty well, but the current exponential value to subtract needs to be modulated by the current location on the sine wave so that the full value is subtracted when the sine is maximum and zero is subtracted when the sine is zero.

I'd appreciate any advice that anyone can give and I can provide more details if required.

## migrated from math.stackexchange.comNov 28 '11 at 10:29

This question came from our site for people studying math at any level and professionals in related fields.

• I thought $1-e^{-x}$ would be an envelope saturating to a maximum of $1$ rather than a decay? But, how many samples per period do you have of your sinusoidal waveform and is the sampling rate an integer multiple of the frequency of the sinusoid? If the answer to the latter question is Yes, I may have several simple things to suggest. – Dilip Sarwate Nov 28 '11 at 14:01
• With regard to the first question: 1-exp(-x) does saturate to 1 but the final sample is calculated by subtracting this value from the sine sample so the overall result is one of decay. Both the sine and exponential functions are scaled by 32767 in each ROM (i.e. maximum value of both functions is 32767 rather than 1) so that everything is integer based. – user1068795 Nov 29 '11 at 2:07
• The clock is 100MHz and the audio sampling rate is only 48kHz so there are a lot of clock cycles to spare. At 48kHz, a sine sample is read from the ROM and an exponential sample is read from the other ROM and the exponential is subtracted from the sine. Different sine frequencies and different decay rates are achieved by stepping through the ROM addresses at different rates, so the sine frequency is not constant. – user1068795 Nov 29 '11 at 2:07

There are other methods if you can't support direct hardware multiplication, but you're going to have to sacrifice something. For instance, if you have multiple clock cycles to spare, you could use an iterative approach. C-like pseudocode:

accum = 0
for (i = 0; i < LENGTH(envelope); ++i)
{
if (((envelope >> i) & 0x1) == 0) accum += signal >> (i + 1)
}
// "accum" now holds the multiplication result


Translating the above functionality to Verilog is an exercise left to the interested reader (it's been a while since I've written in that language). This scheme essentially performs long base-2 multiplication using additions and shifts. For each bit that is set in the envelope scaling factor, an appropriately-right-shifted version of the signal sample is added to the accumulator. The result of the multiplication is held in the accumulator after the loop is complete.

As I said, this isn't the most efficient approach in the world; it's probable that a good synthesis tool would come up with something better even on a device without hardware multipliers. This method trades resource usage for latency in the form of extra clock cycles required to perform the iterations of the unrolled multiply. I'm left to wonder what drives the no-multiplication constraint in your application. Could you add more details on that?

Note: You may need to tweak the amount of extra shift that you apply to the signal when adding to the accumulator based on the numeric format of accum and signal. If they are both of the same fixed-point format (like Q15), then the above approach should work properly.

• Multiplication is technically valid, but FPGA restrictions make it undesirable. I've gotten it to work by pipelining the multiplication but I'm going to look into the addition+shifting idea to see if it will work better. – user1068795 Nov 29 '11 at 2:07
• I implemented the multiplication with a series of shifts and adds and now everything is incredibly fast. Thank you for your help! – user1068795 Nov 29 '11 at 2:07