material is only an angle. custom UIs are very much in flutter’s wheelhouse. if you want some examples feel free to send me a message.

(full disclosure: i work on dart/flutter… best you know that up front, lest you think i’m just a shill!)

so i built my own little usb midi footswitch. it’s based on a teensy lc and i’m using a bit of code i found and have pasted below. i’m wondering if it’s possible to program the teensy as such that the three switches on the footswitch will correspond with the three buttons on my fates. my end goal is to use the footswitch to start/stop recording/overdubbing in cranes. i’m not sure if there’s a way to tell the norns software to interpret each footswitch press as a key press, or if my best bet is to expose the rec on/off params in cranes and then use midi learn to assign those to the switches. any help would be hugely appreciated!

p.s. if there’s a better place for this question, please feel free to move this post to a more appropriate thread.

/* Simple Teensy DIY USB-MIDI controller.
  Created by Liam Lacey, based on the Teensy USB-MIDI Buttons example code.

   Contains 8 push buttons for sending MIDI messages,
   and a toggle switch for setting whether the buttons
   send note messages or CC messages.

   The toggle switch is connected to input pin 0,
   and the push buttons are connected to input pins 1 - 8.

   You must select MIDI from the "Tools > USB Type" menu for this code to compile.

   To change the name of the USB-MIDI device, edit the STR_PRODUCT define
   in the /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/usb_midi/usb_private.h
   file. You may need to clear your computers cache of MIDI devices for the name change to be applied.

   See https://www.pjrc.com/teensy/td_midi.html for the Teensy MIDI library documentation.

*/

#include <Bounce.h>

//The number of push buttons
const int NUM_OF_BUTTONS = 8;

// the MIDI channel number to send messages
const int MIDI_CHAN = 1;

// Create Bounce objects for each button and switch. The Bounce object
// automatically deals with contact chatter or "bounce", and
// it makes detecting changes very simple.
// 5 = 5 ms debounce time which is appropriate for good quality mechanical push buttons.
// If a button is too "sensitive" to rapid touch, you can increase this time.

//button debounce time
const int DEBOUNCE_TIME = 5;

Bounce buttons[NUM_OF_BUTTONS + 1] =
{
  Bounce (0, DEBOUNCE_TIME),
  Bounce (1, DEBOUNCE_TIME),
  Bounce (2, DEBOUNCE_TIME),
  Bounce (3, DEBOUNCE_TIME),
  Bounce (4, DEBOUNCE_TIME),
  Bounce (5, DEBOUNCE_TIME),
  Bounce (6, DEBOUNCE_TIME),
  Bounce (7, DEBOUNCE_TIME),
  Bounce (8, DEBOUNCE_TIME)
};

const int MIDI_MODE_NOTES = 0;
const int MIDI_MODE_CCS = 1;

//Variable that stores the current MIDI mode of the device (what type of messages the push buttons send).
int midiMode = MIDI_MODE_NOTES;

//Arrays the store the exact note and CC messages each push button will send.
const int MIDI_NOTE_NUMS[NUM_OF_BUTTONS] = {40, 41, 42, 43, 36, 37, 38, 39};
const int MIDI_NOTE_VELS[NUM_OF_BUTTONS] = {110, 110, 110, 110, 110, 110, 110, 110};
const int MIDI_CC_NUMS[NUM_OF_BUTTONS] = {24, 25, 26, 27, 20, 21, 22, 23};
const int MIDI_CC_VALS[NUM_OF_BUTTONS] = {127, 127, 127, 127, 127, 127, 127, 127};

//==============================================================================
//==============================================================================
//==============================================================================
//The setup function. Called once when the Teensy is turned on or restarted

void setup()
{
  // Configure the pins for input mode with pullup resistors.
  // The buttons/switch connect from each pin to ground.  When
  // the button is pressed/on, the pin reads LOW because the button
  // shorts it to ground.  When released/off, the pin reads HIGH
  // because the pullup resistor connects to +5 volts inside
  // the chip.  LOW for "on", and HIGH for "off" may seem
  // backwards, but using the on-chip pullup resistors is very
  // convenient.  The scheme is called "active low", and it's
  // very commonly used in electronics... so much that the chip
  // has built-in pullup resistors!

  for (int i = 0; i < NUM_OF_BUTTONS + 1; i++)
  {
    pinMode (i, INPUT_PULLUP);
  }

}

//==============================================================================
//==============================================================================
//==============================================================================
//The loop function. Called over-and-over once the setup function has been called.

void loop()
{
  //==============================================================================
  // Update all the buttons/switch. There should not be any long
  // delays in loop(), so this runs repetitively at a rate
  // faster than the buttons could be pressed and released.
  for (int i = 0; i < NUM_OF_BUTTONS + 1; i++)
  {
    buttons[i].update();
  }

  //==============================================================================
  // Check the status of each push button

  for (int i = 0; i < NUM_OF_BUTTONS; i++)
  {

    //========================================
    // Check each button for "falling" edge.
    // Falling = high (not pressed - voltage from pullup resistor) to low (pressed - button connects pin to ground)

    if (buttons[i + 1].fallingEdge())
    {
      //If in note mode send a MIDI note-on message.
      //Else send a CC message.
      if (midiMode == MIDI_MODE_NOTES)
        usbMIDI.sendNoteOn (MIDI_NOTE_NUMS[i], MIDI_NOTE_VELS[i], MIDI_CHAN);
      else
        usbMIDI.sendControlChange (MIDI_CC_NUMS[i], MIDI_CC_VALS[i], MIDI_CHAN);
    }

    //========================================
    // Check each button for "rising" edge
    // Rising = low (pressed - button connects pin to ground) to high (not pressed - voltage from pullup resistor)

    else if (buttons[i + 1].risingEdge())
    {
      //If in note mode send a MIDI note-off message.
      //Else send a CC message with a value of 0.
      if (midiMode == MIDI_MODE_NOTES)
        usbMIDI.sendNoteOff (MIDI_NOTE_NUMS[i], 0, MIDI_CHAN);
      else
        usbMIDI.sendControlChange (MIDI_CC_NUMS[i], 0, MIDI_CHAN);
    }

  } //for (int i = 0; i < NUM_OF_BUTTONS; i++)

  //==============================================================================
  // Check the status of the toggle switch, and set the MIDI mode based on this.
  if (buttons[0].fallingEdge())
  {
    midiMode = MIDI_MODE_NOTES;
  }
  else if (buttons[0].risingEdge())
  {
    midiMode = MIDI_MODE_CCS;
  }

  //==============================================================================
  // MIDI Controllers should discard incoming MIDI messages.
  // http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash
  while (usbMIDI.read())
  {
    // ignoring incoming messages, so don't do anything here.
  }
  
}
6 Likes

So, I’m no expect in midi abd teensy programming, but, I don’t believe you can replicate the K1-3 button presses with midi. But, if this already sends out midi information with each button press, you should be able to midi learn the buttons to the functions you wish in the params of cranes. Plus, if you can’t find that as a midi function, it shouldn’t be TOO difficult to either alter the code or ask the creator for some help.

To possibly getting it to do what you want to without replicating the keys or altering the code, check to see if record and play are available on the params page of cranes.

Then, if those are available, plug in the pedal, choose it in the midi listing, open cranes, go to the params page and hold K1 to pull up a menu that will allow you to turn on midi learn. Scroll to the param you want the button to control, select that param and press your pedal button. If all goes well, you’ll see the CC that the button sent out next to it!

Sorry if this was an over explanation and you knew all of this, but it seems like the pedal is sending out midi channel 1 and is ready to go.

1 Like

hey! you are spot on; the teensy is on channel 1 and is sending midi information. unfortunately play and record are not available on the params page. so i was wondering if i should edit the cranes code to expose those params (and how to do that, tho i should probably take that q to the cranes thread,) or if there was a way to make norns interpret the presses of each footswitch as a press of one of the three norns keys. hopefully that makes sense! :grin:

Yeah, I’d post in that thread about making those params midi accessible. Honestly, the creator of the script @dan_derks is insanely helpful and responsive!

1 Like

this is a script-level-only sorta thing, but @okyeron did some great work on this already :slight_smile:

modified script: https://gist.github.com/okyeron/fe902b191ba4e39188da43373f39edd1

i’ll be able to get back to cranes after I finish up some residual cheat codes work, apologies for the delay!

6 Likes

dan, i wanna give ya a hug or buy ya a drink or both! thanks so much for your help! @kasselvania is right: you’re always insanely helpful and responsive :grin: :bouquet: :beers:

2 Likes

sounds like @okyeron saved the day with that mod! happy to help connect dots / folks :slight_smile:

3 Likes

i just wanted to circle back and let ya guys know that after looking over the code a bunch, and figuring out what @okyeron did to add the midi record trigger, i modded the code some more to include a midi trigger to clear the buffer. this is the first coding i’ve done on my own so that felt pretty cool! thank you both for facilitating that! :grin:

i was trying to figure out how to add a trigger to reverse the selected buffer, but i couldn’t get it for some reason.

3 Likes

oh, so so exciting! so glad you were able to navigate it! i really can’t wait to get back to it

I think you’d just need to call the rev_speed() function, same as @okyeron did with record() :slight_smile:

elseif d.type=="note_on" and d.note == midi_record_trigger then
        record() 

lmk how it goes!

2 Likes

aw man… dan beat me to it!

was gonna post rev_speed()

so you could add…
local midi_rev_trigger = 10 -- note number chosen at random
then

elseif d.type=="note_on" and d.note == midi_rev_trigger then
        rev_speed() 
3 Likes

sure you can, just call the key handler function directly from the midi handler
(e.g.)

midi.connect(1).event = function(data) 
  msg = midi.to_msg(data)
  if msg.type = 'note_on' then 
     if msg.note == 1 then 
        key(1, 1)
     end
  elseif msg.type= 'note_off' then
     if msg.note == 1 then 
        key(1, 0)
     end
  end
end
10 Likes

20 characters of thanks for correcting my ignorance!!

well you’re quite right that it does require extra programming on the norns side, i just meant its fortunately not too difficult

whoa! thanks for this!

I have a couple of Lemur touch screen controller laying around and was thinking if anybody uses it with Norns via the Ethernet port?
So that direct control of all options is possible?

If you add a few lines of code, apples magic trackpad works :slight_smile:

19 Likes

Nice! And what are the lines…??? :slight_smile:

2 Likes

I used @okyeron’s HID test script to figure out what was being sent from my trackpad. In my case, the X and Y axis was

  • typ == 2
  • X = code == 1
  • Y = code == 0

Then took note of what parameters I wanted to alter with the x/y pad. Here is the code I added

local xy_pad = hid.connect()

-- HID xy trackpad
function xy_pad.event(typ, code, val)
  if typ == 2 then
    if code == 1 then
      params:delta("res", -val * .25)
    elseif code == 0 then
      params:delta("freq", val * .05)
    end
  end
end

The typ and code's will likely differ with other hardware.

8 Likes

That trackpad probably emits multi-touch events too right? It should be able to have at least 5 touch events. Could be fun for granular

2 Likes