Tone Osc implemented: CC Pots Firmware

Home Forums Products NS1nanosynth Tone Osc implemented: CC Pots Firmware

This topic contains 2 replies, has 1 voice, and was last updated by  liamfpower 1 year, 7 months ago.

Viewing 3 posts - 1 through 3 (of 3 total)
  • Author
    Posts
  • #1421

    liamfpower
    Participant

    Hi, I have modified the Nanosynth CC pot firmware to add a tone() oscillator on pin 10 of the Nanosynth as mentioned in the code comments. This adds some great functionality if we can’t have mozzi. To my ears this sounds better than the mozzi DCO anyway, being pure square and a bit louder, try patching one osc into each side of sum/sub and play them via midi. Fatness!
    Code follows:
    Cheers.
    Liam

    
    //NS1nanosynth firmware NS1NANOSYNTH_CC_NO_MOZZI_01
    //
    //This is the basic software with CC# mapped to the digipot.
    //In this version MOZZI is not installed, there are problems with I2C compatibility.
    //To supply anyway a simple second oscillator with MIDI we use the tone output on the D9 pin.
    //A solution could be either to use twi_non_block functions OR implement a software I2C (more CPU-time consuming)
    //MIDI is OK on channel 1, pitch bend is managed (+/- 2 semitones) ,modwheel is on DAC1.
    //CC numbers are from 30 to 33 mapped to digipot A to D. Mapping is a simple multiplication by two.
    //remember that DIGIPOTS are 'floating' and they should be connected as the user wants!
    //to setup the main timebase that checks USB midi events, we use the Timer1 functions.
    //See Fritzing sketches for reference
    
    //modification also includes: 
    //                          CODE CLEANING
    //
    //
    //insert instruction on arcore and lins installation here:
    // 11/02/16: L.P added Tone() Osc on D10
    //........................................................
    /*************************************************
     * Public Constants
     *************************************************/
    #include <inttypes.h>
    
    #define NOTE_B0  31
    #define NOTE_C1  33
    #define NOTE_CS1 35
    #define NOTE_D1  37
    #define NOTE_DS1 39
    #define NOTE_E1  41
    #define NOTE_F1  44
    #define NOTE_FS1 46
    #define NOTE_G1  49
    #define NOTE_GS1 52
    #define NOTE_A1  55
    #define NOTE_AS1 58
    #define NOTE_B1  62
    #define NOTE_C2  65
    #define NOTE_CS2 69
    #define NOTE_D2  73
    #define NOTE_DS2 78
    #define NOTE_E2  82
    #define NOTE_F2  87
    #define NOTE_FS2 93
    #define NOTE_G2  98
    #define NOTE_GS2 104
    #define NOTE_A2  110
    #define NOTE_AS2 117
    #define NOTE_B2  123
    #define NOTE_C3  131
    #define NOTE_CS3 139
    #define NOTE_D3  147
    #define NOTE_DS3 156
    #define NOTE_E3  165
    #define NOTE_F3  175
    #define NOTE_FS3 185
    #define NOTE_G3  196
    #define NOTE_GS3 208
    #define NOTE_A3  220
    #define NOTE_AS3 233
    #define NOTE_B3  247
    #define NOTE_C4  262
    #define NOTE_CS4 277
    #define NOTE_D4  294
    #define NOTE_DS4 311
    #define NOTE_E4  330
    #define NOTE_F4  349
    #define NOTE_FS4 370
    #define NOTE_G4  392
    #define NOTE_GS4 415
    #define NOTE_A4  440
    #define NOTE_AS4 466
    #define NOTE_B4  494
    #define NOTE_C5  523
    #define NOTE_CS5 554
    #define NOTE_D5  587
    #define NOTE_DS5 622
    #define NOTE_E5  659
    #define NOTE_F5  698
    #define NOTE_FS5 740
    #define NOTE_G5  784
    #define NOTE_GS5 831
    #define NOTE_A5  880
    #define NOTE_AS5 932
    #define NOTE_B5  988
    #define NOTE_C6  1047
    #define NOTE_CS6 1109
    #define NOTE_D6  1175
    #define NOTE_DS6 1245
    #define NOTE_E6  1319
    #define NOTE_F6  1397
    #define NOTE_FS6 1480
    #define NOTE_G6  1568
    #define NOTE_GS6 1661
    #define NOTE_A6  1760
    #define NOTE_AS6 1865
    #define NOTE_B6  1976
    #define NOTE_C7  2093
    #define NOTE_CS7 2217
    #define NOTE_D7  2349
    #define NOTE_DS7 2489
    #define NOTE_E7  2637
    #define NOTE_F7  2794
    #define NOTE_FS7 2960
    #define NOTE_G7  3136
    #define NOTE_GS7 3322
    #define NOTE_A7  3520
    #define NOTE_AS7 3729
    #define NOTE_B7  3951
    #define NOTE_C8  4186
    #define NOTE_CS8 4435
    #define NOTE_D8  4699
    #define NOTE_DS8 4978
    
     const  uint16_t  sNotePitches[178] = {
        NOTE_B0, NOTE_C1, NOTE_CS1, NOTE_D1, NOTE_DS1, NOTE_E1, NOTE_F1, NOTE_FS1,
        NOTE_G1, NOTE_GS1, NOTE_A1, NOTE_AS1, NOTE_B1, NOTE_C2, NOTE_CS2, NOTE_D2,
        NOTE_DS2, NOTE_E2, NOTE_F2, NOTE_FS2, NOTE_G2, NOTE_GS2, NOTE_A2, NOTE_AS2,
        NOTE_B2, NOTE_C3, NOTE_CS3, NOTE_D3, NOTE_DS3, NOTE_E3, NOTE_F3, NOTE_FS3,
        NOTE_G3, NOTE_GS3, NOTE_A3, NOTE_AS3, NOTE_B3, NOTE_C4, NOTE_CS4, NOTE_D4,
        NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4,
        NOTE_B4, NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5,
        NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5, NOTE_C6, NOTE_CS6, NOTE_D6,
        NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6,
        NOTE_B6, NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7,
        NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7, NOTE_C8, NOTE_CS8, NOTE_D8, NOTE_DS8,
    };
    
    #ifdef ARDUINO_SAM_DUE // Due has no tone function (yet), overriden to prevent build errors.
    #define tone(...)
    #define noTone(...)
    #endif
    
    // This example shows how to make a simple synth out of an Arduino, using the
    // tone() function. It also outputs a gate signal for controlling external
    // analog synth components (like envelopes).
    
    static const unsigned sAudioOutPin = 10;
    static const unsigned sMaxNumNotes = 16;
    
    #include "Wire.h"       //i2c lib to drive the quad digipot chip
    #include <TimerOne.h>   //Timer lib to generate interrupt callback (used for MIDI USB checking)
    
    #include <SPI.h>         // Remember this line!
    #include <DAC_MCP49xx.h> // DAC libs (remember we use an external ref voltage of 2.5V)
    
    DAC_MCP49xx dac(DAC_MCP49xx::MCP4922, 4, -1);   //NS1nanosynth has DAC SS on pin D4
    
    #define MIN_NOTE 36
    #define MAX_NOTE MIN_NOTE+61
    #define TRIGGER_PIN 5 // default GATE pin on NS1nanosynth.
    #define NOTES_BUFFER 127
    
    const byte NOTEON = 0x09;
    const byte NOTEOFF = 0x08;
    const byte CC = 0x0B;
    const byte PB = 0x0E;
    const int TONE_NOTE = 37;
    
    //////////////////////////////////////////////////////////////
    // start of variables that you are likely to want to change
    //////////////////////////////////////////////////////////////
    byte MIDI_CHANNEL = 0; // Initial MIDI channel (0=1, 1=2, etc...), can be adjusted with notes 12-23
    //////////////////////////////////////////////////////////////
    // end of variables that you are likely to want to change
    //////////////////////////////////////////////////////////////
    
    // Starting here are things that probably shouldn't be adjusted unless you're prepared to fix/enhance the code.
    unsigned short notePointer = 0;
    int notes[NOTES_BUFFER];
    int noteNeeded=0;
    float currentNote=0;
    byte analogVal = 0;
    float glide=0;
    int mod=0;
    float currentMod=0;
    int bend=0;
    
    int DacVal[] = {0, 68, 137, 205, 273, 341, 410, 478, 546, 614, 683, 751, 819, 887, 956, 1024, 1092, 1160, 1229, 1297, 1365, 1433, 1502, 1570, 1638, 1706, 1775, 1843, 1911, 1979, 2048, 2116, 2184, 2252, 2321, 2389, 2457, 2525, 2594, 2662, 2730, 2798, 2867, 2935, 3003, 3071, 3140, 3208, 3276, 3344, 3413, 3481, 3549, 3617, 3686, 3754, 3822, 3890, 3959, 4027, 4095};
    
    byte addresses[4] = { 0x00, 0x10, 0x60, 0x70 }; //digipot address
    byte digipot_addr= 0x2C;  //i2c bus digipot IC addr
    byte valorepot=0; //only for debug routine... delete.
    byte ccpot0_ready=0;
    byte ccpot1_ready=0;
    byte ccpot2_ready=0;
    byte ccpot3_ready=0;
    byte pot0=0;
    byte pot1=0;
    byte pot2=0;
    byte pot3=0;
    int tunePin = A1;
    
    unsigned short DacOutA=0;
    unsigned short DacOutB=0;
    
    void setup(){
      pinMode( TRIGGER_PIN, OUTPUT ); // set GATE pin to output mode
      analogWrite( TRIGGER_PIN, 0);  //GATE down
     // Serial.begin(9600);
     dac.setGain(2);
      Wire.begin();
      Timer1.initialize(8000);          //check MIDI packets each XXX ms
      Timer1.attachInterrupt(updateNS1);
      
     
      
      pinMode (tunePin, INPUT_PULLUP);// blinkLED to run every 0.15 seconds  
    
    }
    
    void i2c_send(byte addr, byte a, byte b)      //wrapper for I2C routines
    {
        Wire.beginTransmission(addr);
        Wire.write(a);
        Wire.write(b);
        Wire.endTransmission();
    //    Wire.send(address);             // send register address
    //    Wire.send(val);                 // send value to write
    //    Wire.endTransmission();         // end transmission
    }
    
    void DigipotWrite(byte pot,byte val)        //write a value on one of the four digipots in the IC
    {
              i2c_send( digipot_addr, 0x40, 0xff );
              i2c_send( digipot_addr, 0xA0, 0xff );
              i2c_send( digipot_addr, addresses[pot], val);  
    }
    
    void updateNS1(){
    
     
    
      while(MIDIUSB.available() > 0) { 
          // Repeat while notes are available to read.
          MIDIEvent e;
          e = MIDIUSB.read();
          if(e.type == NOTEON) {
            if(e.m1 == (0x90 + MIDI_CHANNEL)){
              if(e.m2 >= MIN_NOTE && e.m2 <= MAX_NOTE){
                if(e.m3==0)         //Note in the right range, if velocity=0, remove note
                  removeNote(e.m2);
                else                //Note in right range and velocity>0, add note
                  addNote(e.m2);              
              } else if (e.m2 < MIN_NOTE) {
                //out of lower range hook      
              } else if (e.m2 > MAX_NOTE) {
                //out of upper range hook
              }
            }
          }
          
          if(e.type == NOTEOFF) {
            if(e.m1 == 0x80 + MIDI_CHANNEL){
              removeNote(e.m2);
               
            }
          }
          
          // set modulation wheel
          if (e.type == CC && e.m2 == 1)
          {
            if (e.m3 <= 3)
            {
              // set mod to zero
             mod=0;
             dac.outputB(0);
            } 
            else 
            {
              mod=e.m3;
              DacOutB=mod*32;
              dac.outputB(DacOutB);
            }
          }
    
          //set digipots A to D with CC from 30 to 33
          if (e.type == CC && e.m2 == 30){
            ccpot0_ready=1;
            pot0=e.m3<<1;
          }
          if (e.type == CC && e.m2 == 31){
            ccpot1_ready=1;
            pot1=e.m3<<1;
          }
          if (e.type == CC && e.m2 == 32){
            ccpot2_ready=1;
            pot2=e.m3<<1;
          }
          if (e.type == CC && e.m2 == 33){
            ccpot3_ready=1;
            pot3=e.m3<<1;
          }
    
          
          // set pitch bend
          if (e.type == PB){
           if(e.m1 == (0xE0 + MIDI_CHANNEL)){
              // map bend somewhere between -127 and 127, depending on pitch wheel
              // allow for a slight amount of slack in the middle (63-65)
              // with the following mapping pitch bend is +/- two semitones
              if (e.m3 > 65){
                bend=map(e.m3, 64, 127, 0, 136);
              } else if (e.m3 < 63){
                bend=map(e.m3, 0, 64, -136, 0);
              } else {
                bend=0;
              }
              
              if (currentNote>0){
                playNote (currentNote, 0);
              }
            }
          }
          
          MIDIUSB.flush();
       }
       
      if (noteNeeded>0){
        // on our way to another note
        if (currentNote==0){
          // play the note, no glide needed
          playNote (noteNeeded, 0);
          
          // set last note and current note, clear out noteNeeded becase we are there
          currentNote=noteNeeded;
          noteNeeded=0;
        } else {
          if (glide>0){
            // glide is needed on our way to the note
            if (noteNeeded>int(currentNote)) {
              currentNote=currentNote+glide;
              if (int(currentNote)>noteNeeded) currentNote=noteNeeded;     
            } else if (noteNeeded<int(currentNote)) {
              currentNote=currentNote-glide;
              if (int(currentNote)<noteNeeded) currentNote=noteNeeded;
            } else {
              currentNote=noteNeeded;
            }
          } else {
            currentNote=noteNeeded;
          }
          playNote (int(currentNote), 0);
          if (int(currentNote)==noteNeeded){
            noteNeeded=0;
          }
        }
      } else {
        if (currentNote>0){
        }
      }
    }
    
     void loop(){
    
      //it is necessary to move the i2c routines out of the callback. probably due to some interrupt handling!
     if(ccpot0_ready){
        DigipotWrite(0,pot0);
        Serial.println(pot0);
        ccpot0_ready=0;
        }
     if(ccpot1_ready){
        DigipotWrite(1,pot1);
        ccpot1_ready=0;
        }
     if(ccpot2_ready){
        DigipotWrite(2,pot2);
        ccpot2_ready=0;
        }
     if(ccpot3_ready){
        DigipotWrite(3,pot3);
        ccpot3_ready=0;
        }
     }
    
    void playNote(byte noteVal, float myMod)
      {
    
        analogVal = map(noteVal, MIN_NOTE, MAX_NOTE, 0, 2550)/10;  //  analogVal = map(noteVal, MIN_NOTE, MAX_NOTE, 0, 2550+oscAdjust)/10;
      if (analogVal > 255)
      {
      analogVal=255;
      }
      if (myMod != 0)
      {
        //analogVal=myMod+int(1.0*analogVal+(1.0*myMod*(mod/127)/40));
      }
    //  DacOutB=DacOutB+myMod;  //attenzione!! non volendo suono MOLTO PARTRICOLARE su dacB !!!!!
      // see if this note needs pitch bend
        if (bend != 0)
        {
        analogVal=analogVal+bend;
        }
    //  analogWrite(NOTE_PIN1, analogVal);
          int DacOutA=DacVal[noteVal-MIN_NOTE];
          if (bend != 0)
          {
           DacOutA=DacOutA+bend;
          }
          dac.outputA(DacOutA);
    toneOut(noteVal);
        
        
          analogWrite(TRIGGER_PIN, 255); //GATE ON
          
          //add here tone update !!!!!!!!!!!!!!!!!!!
          //
          //
          //
          
    }
    
    void addNote(byte note){
      boolean found=false;
      // a note was just played
      
      // see if it was already being played
      if (notePointer>0){
        for (int i=notePointer; i>0; i--){
          if (notes[i]==note){
            // this note is already being played
            found=true;
            
            // step forward through the remaining notes, shifting each backward one
            for (int j=i; j<notePointer; j++){
              notes[j]=notes[j+1];
            }
            
            // set the last note in the buffer to this note
            notes[notePointer]=note;
            
            // done adding note
            break;
          }
        }
      }
      
      if (found==false){
        // the note wasn't already being played, add it to the buffer
        notePointer=(notePointer+1) % NOTES_BUFFER;
        notes[notePointer]=note; 
      }
      
      noteNeeded=note;
    }
    
    void removeNote(byte note){
      boolean complete=false;
      
      // handle most likely scenario
      if (notePointer==1 && notes[1]==note){
        // only one note played, and it was this note
        //analogWrite(NOTE_PIN1, 0);
        notePointer=0;
        currentNote=0;
        
        // turn light off
        analogWrite(TRIGGER_PIN, 0);
     
      } else {
        // a note was just released, but it was one of many
        for (int i=notePointer; i>0; i--){
          if (notes[i]==note){
            // this is the note that was being played, was it the last note?
            if (i==notePointer){
              // this was the last note that was being played, remove it from the buffer
              notes[i]=0;
              notePointer=notePointer-1;
              
              // see if there is another note still being held
              if (i>1){
                // there are other that are still being held, sound the most recently played one
                addNote(notes[i-1]);
                complete=true;
              }
            } 
            else{
              // this was not the last note that was being played, just remove it from the buffer and shift all other notes
              for (int j=i; j<notePointer; j++){
                notes[j]=notes[j+1];
              }
              
              // set the last note in the buffer to this note
              notes[notePointer]=note;
              Serial.println(notes[notePointer]);
              notePointer=notePointer-1;
              complete=true;
            }
            
            if (complete==false){
              // we need to stop all sound
              //analogWrite(NOTE_PIN1, 0);
              
              // make sure notePointer is cleared back to zero
              notePointer=0;
              currentNote=0;
              noteNeeded=0;
              break;
            }
            
            // finished processing the release of the note that was released, just quit
            break;
            //F<3
          }
        }
      }
    }
    
    void toneOut(int noteVal)
    {
    for (int notePointer; notePointer < 10; notePointer++)
    {
       tone(sAudioOutPin, sNotePitches[noteVal - TONE_NOTE]);  }
    }
    
    
    • This topic was modified 1 year, 7 months ago by  liamfpower.
    #1423

    liamfpower
    Participant

    By the way, the tone osc() cuts out sometimes, which is probably due to it being run in the interrupt which polls midi. It is possible it is getting confused when noteoff messages are received. Its not incredibly noticable but if you play a lot of notes fast you can miss a few. If someone wanted to take a look at the code, it could most likely be optimized.
    Liam

    #1429

    liamfpower
    Participant

    Ok, figured out that using the toneAC library instead of the standard tone Library fixes the problem with notes cutting out, now we have a rock solid digital square wave on pin 10, and also the use of the digiPots! HAZZAHH!!!

Viewing 3 posts - 1 through 3 (of 3 total)

You must be logged in to reply to this topic.