Tuesday, April 2, 2013

Polysix - Re-Using the Arp Clock for the Aftertouch LFO

In a previous post, I demonstrated aftertouch in my Korg Polysix.  The aftertouch is used add vibrato -- an oscillating modulation of the pitch of the current note -- which is very similar to the effect of the mod wheel that is already built-in to the Polysix.  In fact, I originally planned on having the aftertouch utilize the same modulation generator (LFO) as used by the mod wheel.  But then I thought that it might be better to have the vibrato clocked by its own LFO thereby leaving the Polysix's built-in modulation generator free to do other effects like filter sweeps or tremolo.  Doing a vibrato-dedicated LFO in software in the Arduino is trivial, but how do I control its speed?  Is there a control on the front of the Polysix that I can re-purpose for controlling the vibrato LFO's speed?  Well, there is.  I chose to re-purposed the Arpeggiator's "Speed" knob.  Here's the story of how I did it.  (Note that I go into some gory details here, so you might just want to browse the pictures below and then go watch some funny cat videos.  I recommend this one).


The key limitation in this hack is that I do not (yet) want to add any more knobs or switches to my Polysix.  I would like to re-purpose one of the built-in knobs or switches.  This is made more challenging in that, because I have so far only replaced the Key Assigner microprocessor, I only have access to the knobs and switches that are serviced by the Key Assigner.  Most of the knobs and switches are actually serviced by the main processor in the Polysix, not the Key Assigner.  The only controls that serviced by the Key Assigner are (as shown in the picture below) the Arpeggiator controls and the Key Assign Mode button.  You know, that Arpeggiator "Speed" knob looks like it could be pretty useful...


One convenient property of the Arpeggiator controls is that they serve no purpose when the Arpeggiator is off.  Conversely, when the Arpeggiator is on, one is not likely to care about applying aftertouch vibrato.  So, we have a nice opportunity here to use the Arpeggiator controls for the Arpeggiator when in Arp mode, and an opportunity to re-use the Arpeggiator controls for another purpose when in normal mode.  Sounds good!

In the Arpeggiator controls, we see that we have a nice knob named "Speed".  It even has a blinky LED to indicate its current speed.   This seems like a perfect control for me to use to control my aftertouch vibrato.  How do we go about doing it?  Well, first we need to learn about what that knob is attached to, which means that we need to learn about the Polysix's Arpeggiator clock...

The Arp's "Speed" knob is wired into the circuit shown in the schematic below.  It is a circuit for generating clock pulses.  These pulses are used to drive the Arpeggiator when the synth is in Arpeggiator mode.  The "Speed" knob is wired into this circuit such that it changes the rate of these pulses so that the Arpeggiator goes faster or slower as desired.  It looks like the best way for me to re-use the Arp's "Speed" knob is to simply listen to the "ACKI" line and respond to changes in the rate of the pulses.


Looking at other parts of the schematic (KLM-366), the ACKI line is brought back to the Key Assigner's 8049 microprocessor.  Since I just replaced this microprocessor with my Arduino, it means that I have easy access to this signal.  I just need to figure out the best way to get the Arduino to respond to the arrival of these short pulses.  Using one of the Arduino's interrupt pins is clearly the best solution. If you're not familiar with interrupt pins, here's the idea:

Some of the Arduino's pins are special -- they can be configured so that they are always being monitored for changes, even while the Arduino is doing its other work.  Whenever a change is detected on one of these pins, the Arduino interrupts what it was doing and executes a special piece of code that you write called an "interrupt service routine" (ISR).  In the ISR, I would write the logic to measuring the timing of received pulses so that, back in the main part of the code, the vibrato LFO can adjust its speed.  There are three steps to implementing this:  (1) connect the ACKI line  to a one of the interrupt-enabled pins on my Arduino, (2) write an ISR, and (3) "attach" the ISR to the interrupt pin.  Let's go!

Step 1, Connect ACKI to an Interrupt Pin:  The Arduino's interrupt pins are discussed on this page.  I'm using an Arduino Mega and you'll see that there are six pins that are capable of being interrupt pins.  For no particular reason, I chose to go with Interrupt 4, which means that I should use pin 19.  So I brought a wire from the ACKI line in my Polysix (from the now-empty 8049 socket) out to pin 19 on my Arduino Mega.

Step 2, Write the ISR:.  The ISR is a software routine will be called whenever a pulse is received from the Arp clock via the ACKI line.  After thinking about how to handle the timing of the vibrato's software LFO, I've decided that the ISR only needs to measure the time that has passed between the previous Arp clock pulse and the newly-receive Arp clock pulse.  Once I know the time between pulses, I can adjust my software LFO in the Arduino so that it'll cycle at the same rate.  Below is my ISR code for measuring the time between pulses.  The Arduino executes it whenever a new pulse is detected on my interrupt pin.

typedef unsigned long micros_t;         //data type returned by micros();
volatile micros_t previous_ARP_micros = 0;     //don't forget "volatile"!
volatile micros_t ARP_period_micros = 1000000; //don't forget "volatile"!

void measureInterruptTiming(void)
{
  //get the current time
  micros_t current_ARP_micros = micros();  

  //compute how much time has passed since the last pulse

  ARP_period_micros = current_ARP_micros - previous_ARP_micros;

  //save the current time for use when the next pulse arrives

  previous_ARP_micros= current_ARP_micros;
}

Step 3, Attach the ISR to the Interrupt Pin:  With the ISR written, now I need to tell the Arduino to execute this code whenever it detects a change on my particular interrupt pin.  This is called "attaching" the interrupt.  Following the documentation in the Arduino link above, I add this command to my void setup() routine:

attachInterrupt(4, measureInterruptTiming, RISING);

And we're done!  The result of this process is that the user (me!) can turn the "Speed" knob on the front of my keyboard, which changes the rate of pulses from the Arp clock, which is noticed by the Arduino via Interrupt #4 (aka, Pin 19), which causes it to run my ISR routine, which writes the time between pulses to a global variable, which is then acted upon in the main part of the Arduino software (not in the ISR) to adjust the rate of the vibrato.  Stated more succinctly, turning the "Speed" knob will now change the speed of my aftertouch vibrato.  And I did it without needing to cut any new holes in my Polysix for new knobs or buttons.  Mission accomplished.  Thank you Arduino!  Thank you Polysix!  Thank you reader!

Next Step: Mounting the Arduino in my Polysix
Next Step: With this success, I also use the Arp Speed knob to control detuning in my Polysix

3 comments:

  1. Hey there, I'm wondering if you had a seperated schem for just the lfo or mg section? Or could you point me to that part of the main schem please?

    Andy

    ReplyDelete
    Replies
    1. Hi,

      The built-in MG/LFO for the Polysix is on the schematic sheet for KLM-367. It's the circuitry in the bottom-right of the page. Coming out of IC19, you'll see three pins: X3, X4, and X5. These are the control signals from the CPU that set the intensity, delay, and and speed of the MG/LFO effect.

      Note that X5 and X3 are both labeled "MG SP", which is an error. I believe that X5 is the intensity and X3 is the speed.

      This circuit generates two outputs: "A" and "J". I think that "A" goes off to the mod wheel. "J" actually implements the modulation on the VCO/VCF/VCA based on the knob on the front of the Polysix.

      Hope this helps!

      Chip

      Delete
  2. Thank you so much for this code. I have been working on a simple BPM counter for a new module for WEEKS! There is so much crap code out there and a lot of misinformation about how to use Interrupts. This helped me out. Here's what I came up with to do the BPM calc. My BPMS were off by 1, so added 1 to the BPM, don't know why, but it works.

    //https://synthhacker.blogspot.com/search?q=clock+in

    typedef unsigned long micros_t; //data type returned by micros();
    volatile micros_t previous_ARP_micros = 0; //don't forget "volatile"!
    volatile micros_t ARP_period_micros = 1000000; //don't forget "volatile"!
    int bpm;

    void setup() {
    Serial.begin(9600);
    attachInterrupt(digitalPinToInterrupt (2), measureInterruptTiming, RISING);
    }

    void loop() {
    bpm = ((60000000/ARP_period_micros)/4)+1;
    Serial.print(ARP_period_micros); Serial.print(" : "); Serial.println(bpm);
    }

    void measureInterruptTiming(void)
    {
    //get the current time
    micros_t current_ARP_micros = micros();

    //compute how much time has passed since the last pulse
    ARP_period_micros = current_ARP_micros - previous_ARP_micros;

    //save the current time for use when the next pulse arrives
    previous_ARP_micros = current_ARP_micros;
    }

    ReplyDelete