Arduino Slider PWM Waveform Generator

Having finally been through the details of PWM audio output, I can now link this up with my 16 slider potentiometers and start creating more complex waveforms by actually “drawing” out the wave with the pots.

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

These are the key projects for the main concepts used in this project:

If you are new to Arduino, see the Getting Started pages.

Parts list

The Circuit

SliderPotMuxPWM_bb

I’ve split up my two fader boards and joined them via long 10-way “dupont” headers.  There is a slight quirk however – this means that the faders are no longer in order, so the code will need a way to define which fader is which part of the wave, and which faders are going “low to high” and which are now going “high to low”.

I’ve also used the Arduino A5-A2 pins as the address lines for the multiplexer.  D9 and GND provides the PWM output, which I’ve put through my Arduino PWM Output Filter Circuit.

IMG_5338

The Code

The basic idea is to use the code from Arduino PWM Sound Output but populate the wavetable with values derived from the 16 slider potentiometers, which are read using the code from Arduino Multi-Slider MIDI Controller – Part 3.  You may already be anticipating a slight problem – I want 256 entries in the table but there are only 16 potentiometers.

The answer is to interpolate, or basically mathematically guess, what the intermediate values ought to be.  There are a number of sophisticated “curve matching” algorithms that could be used to get nice smooth curves between the two points, but I’ve gone for a simple linear interpolation.  Basically, I mathematically draw a line between two points derived from the potentiometer readings and then calculate all the intermediate values.  There is a really good discussion of how to do this here.

This could be done “on the fly” which would then only require 16 values in the wavetable, but I’ve opted to calculated it “up front” in slower time to create a full 256 value table which can then be read efficiently in the sample playing code in the same as for previous projects.

There are a few other considerations too (all of which are commented in the code):

  • I build the wavetable from the initial values of the pots in setup() before it starts trying to play it.
  • I’ve used the Serial Plotter again to continually output (once a second) the current waveform, so you can “see” the changes as they happen.
  • As already mentioned, the code has to allow for the pots to be in any order and for some to be read “backwards”.  I’ve done this by creating two lists – one for the index for each pot (not pin numbers – these are the “addresses” used with the multiplexer), and one to signify it is a “mirror” pot (or not).
#define NUMPOTS 16
int pots[NUMPOTS] = {7,6,5,4,3,2,1,0,8,9,10,11,12,13,14,15};
int mirrorpots[NUMPOTS] = {1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0};

// later on in loop():
  int algval;
  if (mirrorpots[pot]) {
    algval = 1023-muxAnalogRead(pots[pot]);
  } else {
    algval = muxAnalogRead(pots[pot]);
  }
  • When it comes to building the wavetable, I need to account for the fact that we want the start and end to “join up”.  This means that the “previous” point for point 0 is the last point in the table.
  • There is a function – updateWavetable() – that re-calculates a new portion of the wavetable.  It will be given one of the pot indexes that has been updated and will have to work out the previous 16 values, based on the (pot-1) and the next 16 values based on the (pot+1) using linear interpolation.  This has to be able to cope with a negative line gradient in the calculations.  To maintain accuracy, I’m using a signed 32-bit value (a long).
  • In order to keep the time for disruption in the wave playing to a minimum, I use a temporary section of wavetable for the calculation, then write back all 32 values in one go.
  • In order to maintain accuracy, I’m using “n.8” fixed-point arithmetic, keeping all values 256 times bigger than they will eventually be when used.  This means my temporary wavetable is actually a set of 33 uint16_t values.  This is enough for two sets of 16 values either side of the pot of interest in addition to our starting reference point.
  • In order to derive the new point from the analog pot reading, I turn the 10-bit (0 to 1023) value from the pot into a “8.2” fixed point value, but it is decimal point aligned to the other “n.8” values.  This gives me a fractional component that I can use in the calculations again to maintain higher resolution for the calculations.
  • The code plays a constant note – MIDI note 69 which is A4 or “concert A” at 440Hz.

Finally it is worth noting that the functionality described in this post is all part of the TEST mode of the code, which is actually ready to be driven by MIDI.  Or at least it will be once I’ve tested it a bit more, but I’ll leave that for a future post.

Find it on GitHub here.

Closing Thoughts

As soon as I saw cheap linear “slider” pots, I wanted to get a set of them together to allow me a “draw” a waveshape. It’s take a bit of effort to get to this point, but I can now finally do it!

The next step is to properly test and use the MIDI functionality.

In terms of future enhancements, I quite like the serial plotter output, but I can’t have that when MIDI is enabled, so it might be nice to add in one of those small OLED displays to allow the Arduino to present a simple drawing of the waveform itself.

Kevin

Leave a comment