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.
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
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?
ReplyDeleteAndy
Hi,
DeleteThe 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
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.
ReplyDelete//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;
}