Hacking the SX-150. Building an Arduino-based sequencer - Part 2
[Update: This article has been continued with Part 3]
Back again with the next phase in the Arduino-based sequencer for the SX-150 project. Boy, I need a catchier name for that. I'll work on that.
If you recall, at the end of Part 1 I had used a digital potentiometer to replace the stylus-and-strip control for the Gakken SX-150 Analog Synthesizer.
In this next stage of the project, I'm going to replace the digipot with one that has a wider range, then extend my circuit and my source code to let the arduino directly play individual notes without having to worry about resistance values.
Follow up:
Getting the chip right
As I said at the end of the last part, I selected the wrong digipot for my purposes. With only 10K ohm range, the AD5206-BN10 provided less than an octave range through the synth. They do make 50K & 100K versions, but for some reason they don't seem to be available anywhere, at least not in the DIP form factor I needed for breadboarding. So, first things first, I had to swap out that digipot for one that was available and provided an expanded range.
I originally chose the AD5206 because there was already an article about how to use it -- I had heard that the SPI interface used to control the resistance level could vary in significant ways from chip-to-chip, and since the article included working code, there was no senses messing with something that already worked.
Since the AD5206 was out of the question, however, I did some research and found that a few people had used the MCP42xxx series of ICs with the Ardunio. And, even more luckily, Digikey had the MCP42100 digipot -- which provides 100K of range -- available in the DIP form factor. And it's only $2.40! It only has 2 circuits (not 6 like the AD5206), but honestly I only needed one circuit for what I'm doing so far; so I sent off an order for 3 of them (plus about 20 other things I suddenly decided I needed). I am going to write a blog post sometime about the electronics providers I use, but until then I have to say that I'm very pleased with DigiKey. They have a great selection, good prices, and they ship things out very quickly -- I usually have stuff within 3-4 working days.
While I waited for the hardware to arrive, I checked out the datasheet for the MCP42100 to see how the pinouts and SPI interface changed.
In fact, I needed to make very few changes to my configuration. It took a bit of figuring because they call the pinouts something different (SCK rather than CLK, for instance), but the physical hookup really only required reversing two pins on the Arduino. In the code, it was just a matter of a different command byte.
The new code for controlling the pot now looks like this:
//--- MCP42100 code
byte SetPot(int address, int value)
{
// Slave Select set low to allow commands
digitalWrite(SLAVESELECT, LOW);
// 2 byte command
SPITransfer(0x10 + address); // 0x10 = 'set pot' command
SPITransfer(value); // Value to set pot
// Release chip, signal end transfer
digitalWrite(SLAVESELECT, HIGH);
}
From Resistance to Pitch
As I alluded to at the end of Part 1, the next step was to take the 256 possible settings for the digipot and find out what pitch it played.
This was pretty much a manual proceedure. I had picked up an inexpensive tuner that would show me what pitch was playing and how far out of tune it was, so I sat that near to the little SX-150 speaker and changed my code to hold each resistance value for 3 seconds at a time and output the resistance value to the serial monitor.
Then I just watched. As each pitch played, I watched the tuner. When it homed in on the right pitch, I made a note of it. A low 'A' was given at value 11, B-flat at 18, B at 25, and so on. As the resistance level climbed, so did the pitch, and by the time I was up around 240, I had registed 3 full octaves of range.
It turns out that there was about 7 steps between each semitone. Unfortunately, it wasn't exactly 7 steps -- which means that I don't really get a perfectly pure pitch out of it...some notes are a little more out of tune than others. I'd love to find a digipot that allowed me maybe 250K ohm range with 1024 possible settings, I think I'd get more range and precision out of it -- but for a cheap little analog synth this is actually pretty good. If anyone knows a chip with characteristics like that, I'd love to hear about it.
(Another thought: my SX-150 seems to have arrived with a really glitchy external output...I think I would have had better results if I could have tested against a better speaker, not the little one attached to the SX, but so be it).
From Pitch To Notes
From that list that I made while I was testing the various resistance levels, I was able to make a simple array in my code that held the various pitch values. I changed the code a bit to loop not through the 255 different resistance levels, but instead through the 36 pitch values I noted. Playing each one for a second or so, it was quite clearly playing scales for me! Now we're starting to get somewhere.
But one part is still missing. The pitch changes nicely, but we are in effect playing a single long continuous note as far as the synth is concerned. We aren't firing off the envelope follower as the pitch changes -- it's the equivalent of holding the stylus against the little ribbon controller area and moving back and forth. The movement is much more precise, but we're still just moving back and forth -- not picking the stylus up and putting it back down.
We need the effect of 'pressing a new key' on a synthesizer. To do that we have to actually break the connection altogether and then connect it again. I was hopeful when I saw that the MCP42100 had a 'shutdown' mode, but that just sets the resistance to the lowest level, it doesn't break the connection altogether.
So I needed a switch to gate the connection between the digipot and the SX-150, and although I haven't actually worked with them that much since starting to hack at this stuff, I knew a transistor was the way to go. I won't try to recap what transistors do in any detail -- there are a ton of sites that do that (most of which, it seems, I visited while figuring stuff out) -- but suffice it to say that there are 3 pins on the transistor. If you apply a voltage to the middle one (the base), it allows a different signal to pass from one of the other pins (the collector) to the third (the emitter). Take away the voltage from the base and the signal can't pass -- so that's how I'm going to switch between 'note on' and 'note off' mode.
I used another arduino pin (Pin 9 in my set up) to act as the 'note on'/'note off' director and hooked it up to the base of the transmitter. Then I fed the output from the MPC42100 into the transmitter at the collector, and connected the emitter to the SX-150. Here's the schematic I came up with:

And here's what it looks like in real life:


And, voila, that's it.
Well, not quite it, we do need a bit of code to get it all working. As you saw in Part 1, I sort of layered my code. I had the layer that was responsible for dealing with the SPI commands (SPITransmit), then a layer for dealing with the digipot (SetPot), and now I'm going to add a layer for working with notes directly.
//--- Sequencer code
// A Bb B C C# D Eb E F Fb G G# A B Bb C C# D Eb E F F# G G# A Bb B C C# D Eb E F F# G G#
byte noteValues[] = { 11, 19, 26, 33, 40, 47, 54, 61, 68, 76, 83, 90, 98, 105, 112, 119, 126, 132, 139, 146, 153, 160, 167, 173, 180, 186, 193, 199, 206, 212, 218, 224, 231, 237, 243, 249 };
void NoteOn( int noteNum )
{
SetPot(1, noteValues[noteNum]); // Set the resistance for the given note
digitalWrite(NOTEON, HIGH); // Then turn on the note
}
void NoteOff()
{
digitalWrite(NOTEON, LOW); // Turn off the noite
}
Yes, you could collapse those three layers together if you wanted, it would save a tiny bit of memory and speed things up a very very tiny bit, but I like having the reusability.
Now let's change our main loop to play those notes in a rising scale:
void loop()
{
// run through each of the notes in ascending pitch
for (int noteNum = 0; noteNum < 35; noteNum++)
{
// play the note for 650 ms
NoteOn(noteNum);
delay(650);
// turn off the note and wait 100 ms to rest
NoteOff();
delay(100);
}
}
And now that's it. We've abstracted away the whole notion of the stylus, or the potentiometer being there, and allowed ourselves to think just in terms of the notes we want to play on the SX, even though that synth doesn't have that concept to start with. Right now we just call the notes 0-35 rather than "A", "F#", etc., but we could add those concepts into our code if we wanted -- but since we're not going to interact with them that way, we can just leave it as is for now.
You can see that we're pretty close to having what we need to really get into building the sequencer now.
I've been changing the code around quite a bit, so here's a link to the full source as it stands at this point.
Looking Forward
The code that I have right now just plays through the notes in a row: 0, 1, 2, 3, 4, etc., and at a fixed rate. If we wanted, we could certainly tell it to play a sequence of specific notes, that would be pretty trivial in fact. And if we wanted to change the tempo, we just have to change the delay() calls used to hold the note and provide a little break between them.
So that's what I did. I grabbed a spare potentiometer and hooked the output up to a spare analog input pin on the arduino, generating a number between 0 & 1023. Then on each pass through the loop, I check the analog value it is inputting and use that value to dictate the length of the note. Slow it down and you get about one note per second. Turn it the other way and the tempo increases until it all rushes together. I added an LED so I could see it flash as the notes played.
And that's the end of Part 2. I hope people are enjoying this -- I certain am!
01/09/10 11:29:44 am,