UNTZtrument Sequencer with changeable notes?

For other supported Arduino products from Adafruit: Shields, accessories, etc.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
pranks
 
Posts: 11
Joined: Thu Mar 28, 2013 4:30 pm

UNTZtrument Sequencer with changeable notes?

Post by pranks »

I have been attempting to make the notes changeable with an encoder. my encoder works perfectly but I cannot seem to change the value &note[row]. I have attempted making

//in global
uint8_t note[8] = {
midiNote[0], midiNote[1], midiNote[2], midiNote[3],
midiNote[4], midiNote[5], midiNote[6], midiNote[7] }
;

instead of:

static const uint8_t note[8] PROGMEM = {
midiNote[0], midiNote[1], midiNote[2], midiNote[3],
midiNote[4], midiNote[5], midiNote[6], midiNote[7] }
;

//in loop
//in midi message

&note[row]

intead of

pgm_read_byte (&note[row])

I am going about this the wrong way. If anyone has any tips, that would be awesome.

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: UNTZtrument Sequencer with changeable notes?

Post by pburgess »

For a normal RAM-based array lookup, drop the & and just use:

Code: Select all

n = note[row];
pgm_read_byte expects the address of a value, hence the additional & operator there.

As long as your tables aren't huge, having them RAM-resident shouldn't be a problem. If it grows into the hundreds though, that's when PROGMEM really becomes vital, and the demo code is just trying to set a good example there.

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: UNTZtrument Sequencer with changeable notes?

Post by pburgess »

*squint* Sorry, just realized you're doing something slightly different...which code is this based on? Is it Collin's arpeggiator, or something else?

What you have here:

Code: Select all

uint8_t note[8] = { 
midiNote[0], midiNote[1], midiNote[2], midiNote[3], 
midiNote[4], midiNote[5], midiNote[6], midiNote[7] };
Is actually an array of arrays. Which is cool and fine, we just need to go about declaring and reading it in special ways. I'm not sure it'll actually compile as written that way.

I started to go off half-cocked on an answer here, but deleted it because it might not be answering the right thing. If you can post more/all of the sketch, or link to the example you're working from, then I can provide a better thought-out response.

User avatar
pranks
 
Posts: 11
Joined: Thu Mar 28, 2013 4:30 pm

Re: UNTZtrument Sequencer with changeable notes?

Post by pranks »

http://BANNED.com/sbgbXirD

Maybe this will clarify my question.
Thanks again for your help. It is very appreciated.

User avatar
adafruit2
 
Posts: 22148
Joined: Fri Mar 11, 2005 7:36 pm

Re: UNTZtrument Sequencer with changeable notes?

Post by adafruit2 »

hmm, you're not using a Leonardo like the UNTZ tutorial uses, so its not really USB MIDI - so much of the sketch has been rewritten :/ its going to be tougher to have us help you if you have a significantly different setup.

User avatar
pranks
 
Posts: 11
Joined: Thu Mar 28, 2013 4:30 pm

Re: UNTZtrument Sequencer with changeable notes?

Post by pranks »

The midi note structure is the same. The only difference is I am outputting through standard midi. The midi note byte is 0-127 just as the midi USB value is. I am only looking to figure out how to make the note variable with your library. If it will be easier on your end I can change the code back to the Leonardo, but it should make little difference as the code was working fine with standard midi out prior to changing the note to be variable.

User avatar
pranks
 
Posts: 11
Joined: Thu Mar 28, 2013 4:30 pm

Re: UNTZtrument Sequencer with changeable notes?

Post by pranks »

Here is the code with usbMIDI on the leo.
Notes and questions in //comments

Code: Select all

// Simple UNTZtrument MIDI step sequencer.
// Requires an Arduino Leonardo w/TeeOnArdu config (or a PJRC Teensy),
// software on host computer for synth or to route to other devices.

#include <Wire.h>
#include <Adafruit_Trellis.h>
#include <Adafruit_UNTZtrument.h>

#define LED 13 // Pin for heartbeat LED (shows code is working)

#ifndef HELLA
// A standard UNTZtrument has four Trellises in a 2x2 arrangement
// (8x8 buttons total).  addr[] is the I2C address of the upper left,
// upper right, lower left and lower right matrices, respectively,
// assuming an upright orientation, i.e. labels on board are in the
// normal reading direction.
Adafruit_Trellis     T[4];
Adafruit_UNTZtrument untztrument(&T[0], &T[1], &T[2], &T[3]);
const uint8_t        addr[] = { 
  0x70, 0x71, 0x72, 0x73 };
#else
// A HELLA UNTZtrument has eight Trellis boards...
Adafruit_Trellis     T[8];
Adafruit_UNTZtrument untztrument(&T[0], &T[1], &T[2], &T[3],
&T[4], &T[5], &T[6], &T[7]);
const uint8_t        addr[] = { 
  0x70, 0x71, 0x72, 0x73,
  0x74, 0x75, 0x76, 0x77 };
#endif // HELLA

#define WIDTH     ((sizeof(T) / sizeof(T[0])) * 2)
#define N_BUTTONS ((sizeof(T) / sizeof(T[0])) * 16)

// Encoder on pins 4,5 sets tempo.  Optional, not present in
// standard UNTZtrument, but won't affect things if unconnected.
enc e[] = { 
  enc(4, 5), enc(8, 9) };

uint8_t       grid[WIDTH];                 // Sequencer state
uint8_t       heart        = 0,            // Heartbeat LED counter
col          = WIDTH-1;      // Current column
unsigned int  bpm          = 240;          // Tempo
unsigned long beatInterval = 60000L / bpm, // ms/beat
prevBeatTime = 0L,           // Column step timer
prevReadTime = 0L;           // Keypad polling timer

//Below used for encoder function
//and changing midiNote[] 
uint8_t
encNoteValueStore[8] = {
  0, 0, 0, 0, 0, 0, 0, 0 }
,  
encNoteValue[8] = {
  0, 0, 0, 0, 0, 0, 0, 0 }
,
encValue[8] = {
  24, 24, 24, 24, 24, 24, 24, 24  }
  ,
newNote[8] = {
  24, 24, 24, 24, 24, 24, 24, 24 }
,
midiNote[8] = {
  24, 24, 24, 24, 24, 24, 24, 24 }
,
noteEncoderValue[8] = {
  0, 0, 0, 0, 0, 0, 0, 0 } 
,
noteButtonPin[] = {
  A0, A1, A2, A3}
;

// The note[] and channel[] tables are the MIDI note and channel numbers
// for to each row (top to bottom); they're specific to this application.
// bitmask[] is for efficient reading/writing bits to the grid[] array.

//***There is now a selection type conflict***
//UNTZtrument_Step_Seq:84: error: bitmask causes a section type conflict
//UNTZtrument_Step_Seq:81: error: channel causes a section type conflict
static const uint8_t PROGMEM
note[8]    = {                                        
  midiNote[0], midiNote[1], midiNote[2], midiNote[3],         
  midiNote[4], midiNote[5], midiNote[6],  midiNote[7]}
,
channel[8] = {   
  1,  1,  1,  1,  1,  1,  1,   1 }
,
bitmask[8] = {   
  1,  2,  4,  8, 16, 32, 64, 128 };




void setup() {
  pinMode(LED, OUTPUT);
#ifndef HELLA
  untztrument.begin(addr[0], addr[1], addr[2], addr[3]);
#else
  untztrument.begin(addr[0], addr[1], addr[2], addr[3],
  addr[4], addr[5], addr[6], addr[7]);
#endif // HELLA
#ifdef __AVR__
  // Default Arduino I2C speed is 100 KHz, but the HT16K33 supports
  // 400 KHz.  We can force this for faster read & refresh, but may
  // break compatibility with other I2C devices...so be prepared to
  // comment this out, or save & restore value as needed.
  TWBR = 12;
#endif
  untztrument.clear();
  untztrument.writeDisplay();
  memset(grid, 0, sizeof(grid));
  e[0].setBounds(60 * 4, 480 * 4 + 3); // Set tempo limits
  e[0].setValue(bpm * 4);              // *4's for encoder detents
}

// Turn on (or off) one column of the display
void line(uint8_t x, boolean set) {
  for(uint8_t mask=1, y=0; y<8; y++, mask <<= 1) {
    uint8_t i = untztrument.xy2i(x, y);
    if(set || (grid[x] & mask)) untztrument.setLED(i);
    else                        untztrument.clrLED(i);
  }
pinMode(noteButtonPin[0], INPUT);  //assigns button for midiNote[0]
}


void loop() {
  uint8_t       mask;
  boolean       refresh = false;
  unsigned long t       = millis();

  enc::poll(); // Read encoder(s)
//-------------------------------------------------------------------------------------
  // Changes midiNote[] when corresponding button is pressed 
  // and ecoder is turned. This is only for midiNote[0]
  //which is replacing what was previously note[0]
  //I did not add the other encoder/button functions for simplicity 
  int noteButtonState0 = digitalRead(noteButtonPin[0]); //Read if button pressed
  encNoteValue[0] = (e[1].getValue() / 4);           
  if(encNoteValue[0] > encNoteValueStore[0]) {       
    if(noteButtonState0 == LOW) {                      //Note encoder changes if button 1 pressed
      encValue[0]++;
      encNoteValueStore[0] = encNoteValue[0];
    }
  }
  if(encNoteValue[0] < encNoteValueStore[0]) {
    if(noteButtonState0 == LOW) {                  //Note encoder changes if button 1 pressed
      encValue[0]--;
      encNoteValueStore[0] = encNoteValue[0];
    }    
  }
  newNote[0] = encValue[0];
  midiNote[0] = newNote[0] ;              //Set midiNote[]
//-------------------------------------------------------------------------------------

    if((t - prevReadTime) >= 20L) { // 20ms = min Trellis poll time
    if(untztrument.readSwitches()) { // Button state change?
      for(uint8_t i=0; i<N_BUTTONS; i++) { // For each button...
        uint8_t x, y;
        untztrument.i2xy(i, &x, &y);
        mask = pgm_read_byte(&bitmask[y]);
        if(untztrument.justPressed(i)) {
          if(grid[x] & mask) { // Already set?  Turn off...
            grid[x] &= ~mask;
            untztrument.clrLED(i);
            usbMIDI.sendNoteOff(pgm_read_byte(&note[y]),
            127, pgm_read_byte(&channel[y]));
          } 
          else { // Turn on
            grid[x] |= mask;
            untztrument.setLED(i);
          }
          refresh = true;
        }
      }
    }
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
  }

  if((t - prevBeatTime) >= beatInterval) { // Next beat?
    // Turn off old column
    line(col, false);
    for(uint8_t row=0, mask=1; row<8; row++, mask <<= 1) {
      if(grid[col] & mask) {
        usbMIDI.sendNoteOff(pgm_read_byte(&note[row]), 127,
        pgm_read_byte(&channel[row]));
      }
    }
    // Advance column counter, wrap around
    if(++col >= WIDTH) col = 0;
    // Turn on new column
    line(col, true);
    for(uint8_t row=0, mask=1; row<8; row++, mask <<= 1) {
      if(grid[col] & mask) {
        usbMIDI.sendNoteOn(pgm_read_byte(&note[row]), 127,       //I want to make &note[row] Variable
        pgm_read_byte(&channel[row]));                           //with the turn of encoder knob
      }                                                          //while button pushed code for that function
    }                                                            //works, pgm_read_byte(&note[row]
    prevBeatTime = t;                                            //does not for this application
    refresh      = true;                                         //please help. THANKS PRANKS
    bpm          = e[0].getValue() / 4; // Div for encoder detents
    beatInterval = 60000L / bpm;
  }

  if(refresh) untztrument.writeDisplay();

  while(usbMIDI.read()); // Discard incoming MIDI messages
}

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: UNTZtrument Sequencer with changeable notes?

Post by pburgess »

Problem begins here, though the compiler has a crappy way of reporting it:

Code: Select all

static const uint8_t PROGMEM
note[8]    = {                                        
  midiNote[0], midiNote[1], midiNote[2], midiNote[3],         
  midiNote[4], midiNote[5], midiNote[6],  midiNote[7]};
note[], as declared here, is a PROGMEM (flash-resident) array. But inside you're referencing elements of the midiNote[] array, which is RAM-resident and thus non-constant and can't be PROGMEM'd.

Need to determine, first of all, what values and tables are completely invariant during the program's entire run and PROGMEM only those (and remember to always use pgm_read_byte (or word, etc.) when accessing elements of those tables -- using their address, e.g. foo = pgm_read_byte(&array[n])). Changeable things need to be declared non-const, non-PROGMEM, and are read directly, e.g. foo = array[n].

Since it looks like the note[] array is a direct copy of the midiNote[] array, I'd just do away with one or the other and access the singular copy...maybe keep just the midiNote[] array (as a non-const, non-PROGMEM variable array), remove note[] entirely and make sure any references to it just read the array directly, no pgm_read stuff on that.

User avatar
pranks
 
Posts: 11
Joined: Thu Mar 28, 2013 4:30 pm

Re: UNTZtrument Sequencer with changeable notes?

Post by pranks »

You guys are the best. Thanks again pburgess.

I <3 adafruit!

Locked
Please be positive and constructive with your questions and comments.

Return to “Other Arduino products from Adafruit”