Reply To: New Firmware for NS1 Nanosynth

Home Forums Products NS1nanosynth New Firmware for NS1 Nanosynth Reply To: New Firmware for NS1 Nanosynth

#1546

rssutcliffe
Participant

Hi Liam,

I was a bit confused with the dual euclid sketch (what are you using for step and pulse inputs?)

I modified this single euclid pattern generator (below) I found here:

http://facproductions.net/temp/fac_euclid.ino

Thanks to fac for the original.

I’m not sure what is meant by:

// Clock divisors and multipliers range from 1 to 16
// Outputs are organized in divider/multiplier pairs
// First pair is D0 and D1, then OX outputs 00 to 07.

Can you shed any light on that? : ) I’m using the DAC and 1st 2 digi outs, works well.

// ============================================================
//
// Program: ArdCore Euclidean Pattern Generator
//
// Description:
//
//
// Clock divisors and multipliers range from 1 to 16.
//
// Outputs are organized in divider/multiplier pairs
// First pair is D0 and D1, then OX outputs 00 to 07.
//
// I/O Usage:
// Knob A0: Pattern length (1 to 16)
// Knob A1: Density – proportion of triggers (0 to 1)
// Analog In A2: Shift – reset to step (0 to length-1)
// Analog In A3: Reset trigger
// Digital Out D0: Pattern output
// Digital Out D1: Inverted output or Gate output (can be user-selected)
// Clock In: Clock input
// Analog Out: Extra CV out (e.g., for pitch, cutoff, etc)
//
// This sketch was programmed by Alfonso Alba (fac)
// E-mail: shadowfac@hotmail.com
//
// Created: Feb 2013
// Modified: Feb 2013
// Modified: Apr 2016 for use with NS1 Nanosynth by RSS
// ============================================================
//
// License:
//
// This software is licensed under the Creative Commons
// “Attribution-NonCommercial license. This license allows you
// to tweak and build upon the code for non-commercial purposes,
// without the requirement to license derivative works on the
// same terms. If you wish to use this (or derived) work for
// commercial work, please contact 20 Objects LLC at our website
// (www.20objects.com).
//
// For more information on the Creative Commons CC BY-NC license,
// visit http://creativecommons.org/licenses/
//
// ================= start of global section ==================

#include <SPI.h> // Remember this line!
#include <DAC_MCP49xx.h>
DAC_MCP49xx dac(DAC_MCP49xx::MCP4922, 4, -1);

// User definable options

#define VERBOSE 1 // Set to nonzero to print parameters in the console window
#define MAXLENGTH 16 // Set maximum pattern length (16 is recommended, but can also be higher)
#define TRIG_TIME 50 // Trigger length in milliseconds (default is 50, decrease for faster clocks)
#define CV_QUANTIZE 1 // Set to nonzero to quantize the CV OUT voltage in 1/12 volt increments
#define CV_PER_CLOCK 1 // 0 = change CV OUT at each clock, 1 = change CV OUT at each trigger in D0
#define D1_IS_GATE 1 // 0 = D1 is an inverted version of D0, 1 = D1 is a gated version of D0

// constants related to the Arduino Nano pin use
#define clkIn 2 // the digital (clock) input
#define digPin0 3 // the digital output pin D0
#define digPin1 4 // the digital output pin D1
#define pinOffset 5 // the first DAC pin (from 5-12)

// variables for interrupt handling of the clock input
volatile char clkState = LOW;
char resetState = LOW;

unsigned char pattern[MAXLENGTH];
unsigned char cv[MAXLENGTH];
unsigned char pos;
unsigned char length;
unsigned char density;
unsigned char reset_position;
unsigned char reset_state;

unsigned long oldMillis;
unsigned char trig_on;

// ==================== start of setup() ======================

// This setup routine should be used in any ArdCore sketch that
// you choose to write; it sets up the pin usage, and sets up
// initial state. Failure to properly set up the pin usage may
// lead to damaging the Arduino hardware, or at the very least
// cause your program to be unstable.

void setup()
{
// if you need to send data back to your computer, you need
// to open the serial device. Otherwise, comment this line out.
Serial.begin(9600);

// set up the digital (clock) input
pinMode(clkIn, INPUT);

// set up the digital outputs
pinMode(digPin0, OUTPUT);
digitalWrite(digPin0, LOW);
pinMode(digPin1, OUTPUT);
digitalWrite(digPin1, LOW);

dac.setGain(2);

// set up an interrupt handler for the clock in. If you
// aren’t going to use clock input, you should probably
// comment out this call.
// Note: Interrupt 2 is for pin 2 (clkIn)
attachInterrupt(2, isr, RISING);

update();
reset_state = reset_position;
oldMillis = millis();
trig_on = 0;
}

// This is the main loop

void update() {
int l, d, s;
l = ((analogRead(0) * MAXLENGTH) >> 10) + 1;
d = ((analogRead(1) * (l + 1)) >> 10);
reset_position = ((analogRead(2) * l) >> 10);

if ((l != length) || (d != density)) {
length = l;
density = d;
euclid(pattern, cv, length, density);

if (VERBOSE) {
Serial.print(“Length = “);
Serial.print(length);
Serial.print(” Density = “);
Serial.print(density);
Serial.print(” Shift = “);
Serial.println(reset_position);
}
}
}

void loop() {
unsigned long curMillis;

// Turn
if (trig_on && ((millis() – curMillis) > TRIG_TIME)) {
trig_on = 0;
digitalWrite(digPin0, 0);
if (!D1_IS_GATE) digitalWrite(digPin1, 0);
}

if (clkState == HIGH) {
clkState = LOW;

// Process reset
if (analogRead(3) > 512) {
if (resetState == LOW) {
resetState = HIGH;
pos = reset_position;
}
}
else resetState = LOW;

digitalWrite(digPin0, pattern[pos]);
digitalWrite(digPin1, D1_IS_GATE ? pattern[pos] : (1 – pattern[pos]));
if (CV_PER_CLOCK || pattern[pos]) {
dac.outputA(CV_QUANTIZE ? cv[pos] : quantize(cv[pos]));
}

trig_on = 1;

pos++;
if (pos >= length) pos = 0;

update();
}

}

// =================== convenience routines ===================

// These routines are some things you will need to use for
// various functions of the hardware. Explanations are provided
// to help you know when to use them.

// isr() – quickly handle interrupts from the clock input
// ——————————————————
void isr()
{
// Note: you don’t want to spend a lot of time here, because
// it interrupts the activity of the rest of your program.
// In most cases, you just want to set a variable and get
// out.
clkState = HIGH;
}

// euclidean pattern generator

void add(unsigned char *v, int n, unsigned char delta) {
while (n–) *v++ += delta;
}

void euclid(unsigned char *p, unsigned char *v, int length, int pulses) {
char lp[MAXLENGTH];
char sp[MAXLENGTH];
unsigned char vlp[MAXLENGTH];
unsigned char vsp[MAXLENGTH];

int p_len, lp_len, sp_len, q, r, i, n, k;

if (pulses == 0) {
memset(p, 0, length);
return;
}

lp[0] = 1; lp_len = 1; vlp[0] = (length << 4) | pulses;
sp[0] = 0; sp_len = 1; vsp[1] = (pulses << 4) | length;
n = pulses;
k = length – pulses;

while (k > 1) {
if (k >= n) {
q = k / n;
r = k % n;
for (i = 0; i < q; i++) {
memcpy(lp+lp_len, sp, sp_len);
memcpy(vlp+lp_len, vsp, sp_len);
add(vsp, sp_len, (q << 4) | r);
lp_len += sp_len;
}
k = r;
}
else {
memcpy(p, lp, lp_len);
memcpy(lp + lp_len, sp, sp_len);
memcpy(sp, p, lp_len);

memcpy(v, vlp, lp_len);
memcpy(vlp + lp_len, vsp, sp_len);
memcpy(vsp, v, lp_len);

i = lp_len;
lp_len += sp_len;
sp_len = i;

r = n – k;
n = k;
k = r;

add(vsp, sp_len, (r << 4));
}
}

p_len = 0;
for (i = 0; i < n; i++) {
memcpy(p+p_len, lp, lp_len);
memcpy(v+p_len, vlp, lp_len);
add(vlp, lp_len, (pulses << 4) | length);
p_len += lp_len;
}
for (i = 0; i < k; i++) {
memcpy(p+p_len, sp, sp_len);
memcpy(v+p_len, vsp, sp_len);
add(vsp, sp_len, (length << 4) | pulses);
p_len += sp_len;
}
}

// quantizer

unsigned char quantize(unsigned char in) {
// Output goes from 0 to 5 volts, which covers a 5 octave range; that is, 60 semitones.
// Since the output values go from 0 to 255, then each semitone should increases the output in 4.25
// Therefore, we divide the output by 4.25, round the result, and multiply by 4.25 to obtain the
// quantized value.

return (unsigned char)(4.25 * (float)((int)((float)in / 4.25 + 0.5)));
}