I think your intent is to reduce quantization noise in the DAC output, so your output low-pass filter can have a higher cutoff frequency, or disappear entirely. I also think you want to calculate your interpolated DAC output values on the fly, rather than store them in memory, because you have more spare processor cycles than memory.
Everybody's talking about linear interpolation, and I will, too. It might be that it's not satisfactory to you, and you want something smoother, but you didn't say so, and the Arduino doesn't lend itself to the complicated floating-point operations that more complex algorithms require. If you're wondering about linear interpolation, the Wikipedia article is straightforward and short.
Somewhere in your program is a variable that points to which of 4096 phantom values you want to output to the DAC. That might be a straightforward twelve bit number, or it might be something more complicated, like bits 18 through 29 of a 32-bit accumulator. You'll want to derive a pointer into your array from that value, and then do linear interpolation.
Here's some pseudocode:
Pointer12Bit = Derive12BitPointer(Pointer)
Pointer8Bit = Derive8BitPointer(Pointer12Bit)
DeltaMagnitude = Array[(Pointer8Bit+1) & 0xFF] - Array[Pointer8Bit]
DeltaPhase = Pointer12Bit & 0xF
DACOutputValue = (Array[Pointer8Bit] << 4) + DeltaMagnitude * DeltaPhase
Obviously, you don't need to write explicit "DeriveXBitPointer()" functions, and you might be able to get by without ever calculating the 12 bit pointer, as long as you can isolate its top 8 bits to index into your array, and its bottom 4 bits for interpolation. You can grab the values you need with shift and bitwise-and operations. Also, "DeltaMagnitude" can take on negative values as big as -255, so it needs to be a signed type bigger than 1 byte, probably int
. "DeltaPhase" is always positive, and less that 16, so it can be unsigned char
. You might be able to do without the "&& 0xFF" in the third line; I add it because I'm never confident that the compiler won't do all the intermediate results in integer, and eventually try to access Array.
If your arrays don't change during execution, consider putting them in program memory, if you haven't already. There's a lot more flash in an Arduino than SRAM. You might be able to make them bigger, or have more of them.
So, what are you cooking up, that needs multiple arrays of output samples?