Following on from Part 1, this post looks at combining several of my previous projects to allow me to control the synth of a PSS-680 from a set of potentiometers.
- Part 1 – Goes into the detail of the MIDI implementation for the PSS-680 and how to handle the SysEx messages from an Arduino.
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 Arduino tutorials for the main concepts used in this project:
If you are new to Arduino, see the Getting Started pages.
Parts list
- Arduino Uno
- Simple MIDI Serial Monitor – Part 3 or similar
- Analog IO Board PCB – or similar
- Yamaha PSS-680
- MIDI leads
- Jumper wires
The Circuit
The aim is to use a bank of potentiometers to control a range of synth parameters for the PSS-680. There are several options:
- Drive them directly from an Arduino, as described here: Arduino MIDI Multi Pot Controller
- Use a multiplexer as described here: Arduino MIDI Mux Step Sequencer
- Use a port expander as described here: Arduino Mozzi FM Synthesis with MCP3008
I’ve chosen to use my Analog IO Board PCB in multiplexer mode, using a 74HC4051 or CD4051, 8-channel analog multiplexer. I’m also using my Simple MIDI Serial Monitor – Part 3.
![](https://diyelectromusic.com/wp-content/uploads/2023/08/img_7312.jpg?w=1024)
The Code
This is using the core code from part 1 but now supports the editing of more parameters and potentiometer control. The multiplexer code is explained here: Arduino MIDI Mux Step Sequencer.
Once again, a voice dump is required before editing can take place, so the workflow is as follows:
- Select the voice to use as a basis for the editing.
- Store it in Bank 1.
- Send a memory dump to the Arduino.
- Adjust the synth paramters as required.
- If any parameters are adjusted from the PSS-680’s panel then perform another memory dump back to the Arduino.
As the only communications between the Arduino and the PSS-680 is via 72-byte SysEx messages, I’ve taken particular care in the code to only update the synth when the paramters have really changed. Most of this is taken care of in the following function:
int pots[NUM_POTS]; bool updatePotReading (int pot, int reading) { if (pots[pot] == reading) { return false; } pots[pot] = reading; return true; } bool updateSynthParams(int pot, unsigned reading) { switch (pot) { case 0: if (updatePotReading(pot, reading>>8)) { setSinTbl (1, reading>>8); return true; } break; case 1: // etc } }
As can be seen in the second extract, the check is performed on a value that is scaled to be used as a synth paramter. I’ve chosen to control the following potentiometer to parameter mapping:
Potentiometer | Description | Range | Scaling code |
1 | Op 1 Sine Table | 0-3 | reading>>8 |
2 | Op 1 Frequency | 0-15 | reading>>6 |
3 | Op 1 Detune | 0-15 | reading>>6 |
4 | Feedback | 0-7 | reading>>7 |
5 | Op 2 Sine Table | 0-3 | reading>>8 |
6 | Op 2 Frequency | 0-15 | reading>>6 |
7 | Op 2 Detune | 0-15 | reading>>6 |
8 | Modulation Level | 0-99 | reading>>3 |
![](https://diyelectromusic.com/wp-content/uploads/2023/08/img_7313.jpg?w=1024)
I’ve left out the envelope generator options (AD from the UI, but others are available over MIDI) so if these are changed from the keyboard then a further memory dump will be required to resynchronise the Arduino.
Each supported parameter has a get/set function as described previously for the sine table.
The modulation parameter is a little more complex than the others. The official range of values is 0 to 99, but this maps onto a 7-bit MIDI control value 0-127. But it also maps in a slightly odd way. From the PSS-680 user manual MIDI implementation:
TL: TOTAL LEVEL 0000000 - 99 OF PANEL DATA 0000001 - 98 OF PANEL DATA 1100011 - 00 OF PANEL DATA 1111111 - 00 OF PANEL DATA
There are two TL fields – the one for the modulator (M) corresponds to the modulation level; the one for the carrier (C) corresponds to the final output level – I’m only concerned with the modulator level here.
So a MIDI value of 0 maps onto “full modulation” and a MIDI value of >99 maps onto “no modulation”.
There is also an issue with analog accuracy and drifting, so I’ve implemented a smoothing algorithm (more here) to ensure only significant changes cause the value to be updated:
#define AVGEREADINGS 16 int modpotvals[AVGEREADINGS]; int modpotidx; int modpottotal; int modpotAverage (int reading) { modpottotal = modpottotal - modpotvals[modpotidx]; modpotvals[modpotidx] = reading; modpottotal = modpottotal + modpotvals[modpotidx]; modpotidx++; if (modpotidx >= AVGEREADINGS) modpotidx = 0; return (modpottotal / AVGEREADINGS); } bool updateSynthParams(int pot, unsigned reading) { switch (pot) { // other pots case 7: modreading = modpotAverage(reading)>>3; if (updatePotReading(pot, modreading)) { setMTL (127-modreading); return true; } break; } }
All the scaling and update checking is performed on the “raw” analog reading, so that a default setting of the potentiometer to 0 doesn’t trigger a number of updates (as it would be interpreted as a value of 99). No further adjusting of the parameter from the 0..127 range is performed as the manual clearly shows what happens to values over 99 (i.e. they are all treated the same – as 0).
Closing Thoughts
The video shows the Arduino changing the frequency of both operators, the feedback level, the modulation level and then the sine tables for each operator. I’ve fiddled about with the detune options and whilst I might be able to hear a bit of a difference, I’m not totally convinced. That is one for some further experimentation in the future.
I’ve been really impressed with the capabilities of the PSS-680 and wonder what was in the original vision for the keyboard. I can’t imagine more than a small fraction of owners would have explored the full extent of its MIDI functionality, so was all this additional functionality just for advanced usage?
Or was it thought that owners would really get into the nuts and bolts and use all these features?
Kevin