Interpolation: 12-bit DAC with 8-bit tables

I'm working on an LFO which has it's waveforms stored in 8-bit arrays. What would be the best way to interpolate the values in those tables to output to a 12-bit DAC?

That probably depends on how long you have got to do the arithmetic.

Multiply them all by 1.5 before sending them?

CrossRoads:
Multiply them all by 1.5 before sending them?

If that was the case, I'd just use map(). I want as much resolution from the 12-bit DAC as possible while still using arrays with only 256 values. I need some code to fill in values in between.

surely all it would need would be something along the lines of:

byte eightBitValue = ...
int twelveBitValue = (int)eightBitValue << 4;

TCWORLD:
surely all it would need would be something along the lines of:

byte eightBitValue = ...

int twelveBitValue = (int)eightBitValue << 4;

That would still only give 256 values. I need 4096 values, filling in all the spaces left out from the 8-bit table. Need a way of interpolating between each value in the table.

Majenko??? Why 1.5?, the data was literally converted to an uint_8 value when it was 'clipped' to fit into 8 bits. The additional resolution simply isn't there anymore.
Or did I just make a BIG assumption?. Or is it just to make the data 10 bits long again? I really don't understand this at all.

Doc

bendedavis:
I'm working on an LFO which has it's waveforms stored in 8-bit arrays. What would be the best way to interpolate the values in those tables to output to a 12-bit DAC?

I'm not sure what you're trying to achieve. Do you have a waveform sampled at discrete points (using 8 bit resolution) and you're trying to output it as smoothly as possible using 12-bit values? In that case you would use an algorithm to interpolate between the sample values. For example you could use a linear interpolation as the first stab. Depending how fussed you are about the exact shape of the wave form there may be better interpolation algorithms based on curve fitting. But linear interpolation is quick and easy.

The interpolation requires some "look-ahead" capability to fill in the gaps.
If the output frequency is slow, then the code needs to know the time for sample[ x ], the value of sample[ x+1 ] at time 2. If the time between sample is always the same, then the number of values to interpolate between time1, sample[ x ] and time2, sample[ x+1 ] can be generated I think in a predictable manner.

For example if the time between was to be split into 4 groups, then the math might be:
delta = sample[ x+1 ] - sample[ x ]
sample = delta/5 ( can be positive or negative )
at time x.1, output = sample[ x ] + sample
time x.2, output = sample[ x ] + 2sample
time x.3, output = sample[ x ] + 3
sample
time x.4, output = sample[ x ] + 4sample
time2, output = sample[ x+1 ] ( or sample[ x ] + delta, or 5
sample )

Assumption:
You have an arrray "uint8_t a[10]", which contains values of a signal at t = 0ms, 100ms, 200ms, 300ms ... 900ms.
Assumed problem:
You want to know the signal value (12 bit) at u = 123ms (or any other value between 0 and 1000)

Algorithm:

  1. Search for the index values, where t1 <= u <= t2
    For x = 123 this is t1=100 (with index i1=1 and t2=200 with index i2=i1+1)

  2. do a linear interpolation between a[i1] and a[i2]
    in general this is:
    a[i1]+(a[i2]-a[i2])(u-t1)/(t2-t1)
    to get the increased 12 bit resolution, do a shift by 4 bit.. at the right places :wink:
    (a[i1]<<4)+((a[i2]-a[i2])<<4)
    (u-t1)/(t2-t1)
    Still, types are not assigned. Probably the simplest solution is to use int32_t for the multiplication:
    (((int16_t)a[i1])<<4)+(int16_t)((((int32_t)a[i2]-(int32_t)a[i2])<<4)*(int32_t)(u-t1)/(int32_t)(t2-t1))

Oliver

That would still only give 256 values. I need 4096 values, filling in all the spaces left out from the 8-bit table. Need a way of interpolating between each value in the table.

OK... I guess you want to increase the bit-depth and the sample rate. Of course, this doesn't give you more true resolution. There are still only 256 different inputs and 256 different outputs, although you are apparently trying to "smooth" the signal digitally.

So first you increase the bit depth, by multiplying by 16 (or bit-shifting 4 places).

Then after you figure-out how many more samples you want in the time-dimension, you can interpolate to get the in-between values. i.e. If you double the number of samples, then taking the average between two samples to fill-in the missing data is linear interpolation.

Docedison:
Majenko??? Why 1.5?, the data was literally converted to an uint_8 value when it was 'clipped' to fit into 8 bits. The additional resolution simply isn't there anymore.
Or did I just make a BIG assumption?. Or is it just to make the data 10 bits long again? I really don't understand this at all.

Doc

Hello? Do I hear my name being taken in vain? Why am I being dragged into this discussion?!

So yes, to add my ¢2 to the discussion, the only way you will increase "resolution" is by increasing both the bitwise values and the quantity of samples.

You cannot convert 2 numbers at 8 bit into 2 numbers at 12 bit and expect an increase in resolution - you just get an increase in step size. You need to add extra samples in between those numbers which are basically an average of the samples either side.

Say you have samples of 4, 28, 209, 48, 184.

You would first multiply them by 16 (or shift left 4 times) to give:

64, 448, 3344, 768, 2944

Then you would do the interpolation. Do as many passes as you have time (and memory) for:

64, 256, 448, 1896, 3344, 2056, 768, 1856, 2944

It's rough and ready, but it makes the wave look a bit smoother.

There's more tricky stuff you could do with vectors but that really takes a DSP to do it right.

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[256].

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?