New Firmware for NS1 Nanosynth

Home Forums Products NS1nanosynth New Firmware for NS1 Nanosynth

This topic contains 34 replies, has 8 voices, and was last updated by  Niklas 1 year, 5 months ago.

Viewing 15 posts - 16 through 30 (of 35 total)
  • Author
    Posts
  • #1522

    liamfpower
    Participant

    Hi Rssutcliffe, have plenty of other hacks which I have made. At the moment I have converted quite a bit of the Ardcore code found here to work with the nanosynth DAC. Let me know if any of those sketches interest you and I can post some. I am using the shapeable LFO patch at the moment, but have had heaps of fun with the EG patches and the sequencer patch as well with the Nanosynth. These are all CV manipulation modules, and I have converted most of them for use with the nanosynth.
    Try this one out!

    //  ============================================================
    //
    //  Program: ArdCore Sequencer
    //
    //  Description: A basic 8 step sequencer, where the input
    //               is done using A0 - A1
    //
    //  I/O Usage:
    //    Analog 0: Pitch to be recorded at the current step - try the ribbon!
    //    Analog 1: Step Selection - Connect C1 Nanosynth!
    //    Analog In 1: unused
    //    Analog In 2: unused
    //    Digital Out 1: Trigger on step output - Gate for ADSR
    //    Digital Out 2: Trigger on currently selected step
    //    Clock In: External clock input (pin 2)
    //    Analog Out: DAC0 - MCP4922
    //
    //
    //  Created:  10 Dec 2010
    //  Modified: 13 Feb 2011 - rework to use knobs only
    //            12 Mar 2011 - Added pause function for step selection
    //                        - Added digital out firing on current step
    //                        - Fixed gate output turnoff
    //            17 Apr 2012  ddg Updated for Arduino 1.0
    //						18 Apr 2012	 ddg Changed dacOutput routine to Alba version
    //            04 Feb 2016  L.P modified for use with NS1 Nanosynth.
    //  ============================================================
    //
    //  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 ==================
    
    //  constants related to the Arduino pin use
    // Nanosynth DAC Code
    #include <SPI.h>         // Remember this line!
    #include <DAC_MCP49xx.h>
    DAC_MCP49xx dac(DAC_MCP49xx::MCP4922, 4, -1);
    
    const int clkIn = 2;           // the digital (clock) input
    const int digPin[2] = {5, 6};  // the digital output pins
          // the first DAC pin (from 5-12)
    const int trigTime = 25;       // the standard trigger time
    const int pauseTime = 250;     // require a pause
    
    //  variables for interrupt handling of the clock input
    volatile int clkState = LOW;
    
    //  variables used to control the current DIO output states
    int digState[2] = {LOW, LOW};  // start with both set low
    unsigned long digTime[2] = {0, 0};      // the times of the last HIGH
    
    //  recording and playback variables
    int seqValue[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    int maxSeqValue = 8;
    int currSeqPlay = 0;
    
    //  limit changes to the sequencer to require a 100ms pause
    int lastPos = -1;
    unsigned long lastPosMillis = 0;
    
    //  ==================== start of setup() ======================
    
    void setup() {
      // set up the digital (clock) input
      pinMode(clkIn, INPUT);
      //Set Nanosynth DAC gain
      dac.setGain(2);
      // set up the digital outputs
      for (int i=0; i<2; i++) {
        pinMode(digPin[i], OUTPUT);
        digitalWrite(digPin[i], LOW);
      }
    
      
      attachInterrupt(2, isr, RISING);
    }
    
    //  ==================== start of loop() =======================
    
    void loop()
    {
      int tempPos = analogRead(1) >> 7;
    
      // on clock tick, move forward and play.
      if (clkState == HIGH) {
        clkState = LOW;
        
        currSeqPlay = (++currSeqPlay % maxSeqValue);
        //Send out value, bitshifted from 8 to 12 bits for DAC
        dac.outputA(seqValue[currSeqPlay] << 4);
        
        digState[0] = HIGH;
        digTime[0] = millis();
        digitalWrite(digPin[0], HIGH);
        
        if (currSeqPlay == tempPos) {
          digState[1] = HIGH;
          digTime[1] = millis();
          digitalWrite(digPin[1], HIGH);
        }
      }
    
      // record the current knob position values
      if (tempPos != lastPos) {
        lastPos = tempPos;
        lastPosMillis = millis();
      }
      
      // if we've been pause long enough, read the value
      if (millis() - lastPosMillis > pauseTime) {
        int tempVal = quantNote(analogRead(0));
        seqValue[lastPos] = tempVal;
      }
      
      // turn off the gate when required
      for (int i=0; i<2; i++) {
        if ((millis() - digTime[i] > trigTime) && (digState[i] == HIGH)) {
          digState[i] = LOW;
          digitalWrite(digPin[i], LOW);
        }
      }
    }
    
    //  =================== convenience routines ===================
    
    //  isr() - quickly handle interrupts from the clock input
    //  ------------------------------------------------------
    void isr()
    {
      clkState = HIGH;
    }
    
    //  quantNote(int) - drop an incoming value to a note value
    //  -------------------------------------------------------
    int quantNote(int v)
    {
      // feed this routine the input from one of the analog inputs
      // and it will return the value in a 0-64 range.
      return (v >> 4) << 2;
    }
    
    //  ===================== end of program =======================
    #1523

    liamfpower
    Participant

    Basically you input a clock (square wave LFO) to pin D2 on the Nanosynth. Then you connect a voltage to record – Ribbon HLD input is fun – to A0. Then connect C1 to A1 to choose a step to edit. Connect DAC0 to V/oct input and voila, a sequencer onboard the Nano!

    #1524

    jonncgiles
    Participant

    rssutcliffe and liamfpower:

    Thanks for the help. Couldn’t find the the analog output pin, but now that I know it is pin 9 I can really test the sketch out. I didn’t see the output pin listed in the code at the top, although other pins are described. Maybe I have an older version of it. Regardless, thanks for all the help.

    jg

    #1525

    rssutcliffe
    Participant

    @ liam, nice 1! thanks very much, I love sequencers : ) I’ll deffo look at these very soon, thanks again : )

    @ Jonn, Pin 9 is actually unlisted in the above, I went to the Mozzi homepage to find which one was the sound output

    #1526

    rssutcliffe
    Participant

    Hi Liam,

    Wow the sequencer is well good! Made my day with that : ) SEEN
    I’d like to try all the stuff you’ve translated please! The shaped LFO and Eucledian seqs sound interesting (they all sound great though!)

    Do you still have problems uploading to github?
    Would be great to have a repo for all stuff
    Maybe you could zip them in another google drive link?

    Cheers dude!

    #1527

    rssutcliffe
    Participant

    Sounds great with the 2 LFOs into the AND Gate, tempo a go go : )

    #1528

    liamfpower
    Participant

    Hi Rssutcliffe,
    Alright, here is a link to what I have so far on the Ardcore Code conversion for the NS1.
    Keep in mind these are all pretty roughly translated for the Nanosynth. Here is a guide on how to convert them for yourself if you find a patch that needs to the be converted.

    Basically most of the trigger/gate based patches should work perfectly for the Nanosynth without any modifications.
    If you find a patch which takes a clock in, you need to change
    attachInterrupt(0, isr, RISING); to attachInterrupt(2, isr, RISING);

    you also need to change the DAC output routine, so first you put this line of code at the top.
    #include <SPI.h> // Remember this line!
    #include <DAC_MCP49xx.h>
    DAC_MCP49xx dac(DAC_MCP49xx::MCP4922, 4, -1);

    then, in setup, you delete this:
    for (int i=0; i<8; i++) {
    pinMode(dacOffset + i, OUTPUT);
    digitalWrite(dacOffset + i, LOW);
    }
    and add this:
    dac.setGain(2);

    then you find any code which says dacOutput(number) — ardcore dac output routine
    and change it to: dac.outputA(number <<4) — Nanosynth dac output
    This is because the Ardcore outputs an 8 bit value, and we want a 12 bit value for the Nanosynth DAC, so we bitshift it 4 bits the right. You following?
    Then delete:
    void dacOutput(byte v)
    {
    PORTB = (PORTB & B11100000) | (v >> 3);
    PORTD = (PORTD & B00011111) | ((v & B00000111) << 5);
    }
    from the bottom of the code, so that it doesn’t mess with pin 4, which your DAC needs.

    What you have done is changed the clock pin to pin 2, initialised the nanosynth DAC, told it what gain to output at, and changed the DAC output by four bits to match the scaling of the Nanosynth.

    My favourite patches with the nano so far are the Quantizer, the shaped LFO, the envelope generator, the shift register, and the 101 sequencer. Some of them might need tweaking to work if you feel you are up to it. If in doubt take a look at the Nanosynth code on the soundmachines github and make sure you have all the DAC related code right.
    Let me know how it goes!
    Thanks
    Liam

    • This reply was modified 2 years, 7 months ago by  liamfpower.
    #1531

    dirk
    Participant

    thanks a lot for this! i find here some really nice functionality that i was actually looking for.

    greetings;
    dirk

    #1532

    rssutcliffe
    Participant

    Hi Liam,

    thanks a lot for the zip file and the guide+explanation of how to modify the code: Awesome!

    I’m going to modify one to get the process down : )

    Thanks again Liam, I needed something to get me into the NS1 programming; this has been it!

    Appreciate your help! Hopefully I can share something with you soon : )

    #1536

    rssutcliffe
    Participant

    Having a go with the shaped LFO, it has a slower rate but an expressive range, nice.

    I find the 101Seq quite confusing with the inputs. I can get a voltage in to record, and trigger out from pin 6 to envelope gate. Clock in eems to work from LFO square wave. However the analog inputs and knob inputs are not clear to me, record on switch is also a bit odd. It sort of records the same note and then rises in pitch playing the same note. I’m sure it’s meant to be more interesting than this! : )

    I looked in the code to see where I might attach C1, 2 for the loop in/out points but I think it checks all 5 analog ins. I see it will look at A3 for the record change:

    // deal with possible change of record/play mode
    i = analogRead(A3)

    Still trying to get my head around the pins in general on the board, as they can be different in different sketches this might take me some time!

    RSS

    #1537

    liamfpower
    Participant

    Hi Rss,
    I had a but of trouble with the 101 seq as well, i think A3 expects a gate instead of an analog voltage. Also if you attach an led to the digital pins 3 and 4 you can see whethee you are recording or not. But it never really worked that great for me. Next thing I wanna do is combine some of the sketches and make a multi purpose one tailored to the ones I use most and integrate it with the base nanosynth midi cv/osc code. Not too ambitious. Pretty sure you can change the speed of the LFO in code to bring its range up but it locks up the arduino if it gets too fast so warch your back.
    Cheers
    Liam

    #1538

    rssutcliffe
    Participant

    Hi Liam, I replied to this just now but then he decided I wasn’t logged in! The quantizer is also much fun, it’s not reacting to the clock input on 2, but it’s deffo quantizing and sounds good. Trigger and gate out and quantize out work great. How does the clock input affect the quantize anyway?

    I have a nice piano sound with it, Pinano I mean ; ) It would be great to have the cool sketches withmidi usb, especially midi clock for the sequencer/ clock input. Would be very useful for recording to DAW with a locked seq.

    Cheers!

    #1540

    rssutcliffe
    Participant

    After some perseverance with the Sh101 seq I got it to work properly, you were correct about it expecting the gate signal. I took gate also from the ribbon (as well as voltage to record). Which meant it went into record each time you press the ribbon but it sounds cool, the loop points are a great feature!

    I got the LED working on pin 3, attached led leg to 0v somewhere else on the NS1 and it will light when gate input to A3 is high. Pin 4 is gate out (to envelope gate in) My LED was way too bright but it was cool to see it working as meant to.

    Bouncing ball next, love that! Do you know Eric Archer.net? He makes circuits like these only using Op Amps and through hole perfboard components, 70s tech. No ucontrollers. Impressive, steep learning curve however : )

    #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)));
    }

    #1610

    werle.kyle
    Participant

    Sweet thread. Ive never used interrupts before. Gonna add it to my sequencer clock.

    I made a toggle button for record in my sequencer, but just putting the gate from the ribbon to activate recording is way simpler and better!

Viewing 15 posts - 16 through 30 (of 35 total)

You must be logged in to reply to this topic.