Go Down

Topic: Reaper DAW triggering Arduinos (Read 826 times) previous topic - next topic

slipstick

Since there are many audio interfaces with MIDI already incorporated and even if you don't have one of those the USB to MIDI converters are so cheap I was going with "proper" MIDI to make wiring simpler. There's no need to use bulky DIN plugs at the Arduino end as it's a custom build.

For me using MIDI over USB would be more of a pain. As the OP wants to send the signal in parallel to a number of different Arduinos you'd probably need a powered hub and you'd have USB cables trailing around the place when there's otherwise no need for a USB connection. But my way does mean building a little opto-isolator MIDI IN circuit for each Arduino.

As always there are many different ways to achieve something similar.

Steve

slipstick

It shouldn't be nearly as complicated at the Reaper end as that video suggests. But since I've never actually done it I'd better test my theories myself before I tell you how easy it is;).  Give me a few days, I don't have a lot of spare time just now.
Sorry, that took longer than I expected. I couldn't find the optocoupler I thought I had so had to order more and then my laptop died so I had to fix that. Fortunately, when I did get to it, it was fairly simple, as I thought.

In Reaper you just need a single MIDI track then choose a different note for each function you need to control. You then add long notes to that track with the start of the note switching on the function and the end switching it off. That gives you up to 128 different functions you can control on each of 16 channels.

Each Arduino needs the standard MIDI IN circuit (a 6N138 optocoupler a diode and two resistors). I set mine up so the MIDI used software serial on pins 2/3 so I still had the hardware serial for debugging.

If you use the MIDI.h library the Arduino code is simple and fairly neat. The following example looks on channel 16 for middle C (MIDI 60) and E (64) and switches different LEDs on and off and still has serial.prints in to check what notes are arriving. Obviously you could also run motors or do whatever else you need in those NoteHandler routines. Just don't use any blocking code like delay()...leave all the timing to the MIDI NoteOn/NoteOff.
Code: [Select]

#include <SoftwareSerial.h>
#include <MIDI.h>

byte greenLED = 10;
byte redLED = 11;

SoftwareSerial softSerial (2, 3);
MIDI_CREATE_INSTANCE(SoftwareSerial, softSerial, MIDI);

void handleNoteOn(byte channel, byte pitch, byte velocity)
{
  Serial.print("Channel: ");
  Serial.print(channel);

  if (channel == 16) // check channel first
  {
    // Do functions based on note received
    // Simple case - switch appropriate LED on depending on note 60=green, 64=red
    if (pitch == 60) {
      digitalWrite(greenLED, HIGH);
    }
    if (pitch == 64) {
      digitalWrite(redLED, HIGH);
    }
    Serial.print("NoteOn: ");
    Serial.println(pitch);
  }
}
void handleNoteOff(byte channel, byte pitch, byte velocity)
{
  Serial.print("Channel: ");
  Serial.print(channel);

  if (channel == 16) // check channel first if listening on all channels
  {
    // Do functions based on note received
    // Switch appropriate note's LED off
    if (pitch == 60) {
      digitalWrite(greenLED, LOW);
    }
    if (pitch == 64) {
      digitalWrite(redLED, LOW);
    }

    Serial.print(" NoteOff: ");
    Serial.println(pitch);
  }
}
void setup() {
  Serial.begin (57600);
  // Connect the handleNoteOn function to the library,
  // so it is called upon reception of a NoteOn.
  MIDI.setHandleNoteOn(handleNoteOn);  // Put only the name of the function

  // Do the same for NoteOffs
  MIDI.setHandleNoteOff(handleNoteOff);

  // Initiate MIDI communications, listen to all channels
  // change to MIDI.begin(16); to listen to just ch16 then you won't need tests in the notehandlers.
  MIDI.begin(MIDI_CHANNEL_OMNI);
}

void loop() {
  // Call MIDI.read the fastest you can for real-time performance.
  MIDI.read();
  // Note: you could add callback handlers for other messages but
  // currently only NoteOn and NoteOff are set up so
  // any othe MIDI messages will be ignored
}


I'm certain it could be neater but it's so simple and effective that I'm now thinking of interesting projects I can use the technique for.

Steve

Saynomore

Steve,

thanks a lot for taking the time to do this test despite your computer problems.

I feel ashamed because I've made some tests on my side as well and I completely forgot to let you know...

I get some results but not exactly with your technique. I don't understand why you had to used an optocoupler though.

What I've done is use Hairless MIDI to "connect" Reaper with my Arduino. It was a bit arduous but I finally make it works with the built in LED and a servo. But when I've tried with my solenoids it was an another story.
I used a lot of libraries because I'm lame and because I tried to have a non-blocking code. But I think I got timer conflicts between librairies.

So, for my solenoids only I tried this alone (without MIDI triggering), for having them periodically hitting(while the note is ON when it's in the next code, lower in this comment):

Code: [Select]

#include "Timer.h"

Timer t;

int soleEvent1 = 10;
int soleEvent2 = 11;


void setup()
{
  Serial.begin(9600);
 
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);

  int tickEvent1 = t.every(1, doSomething1);
  int tickEvent2 = t.every(1, doSomething2);

}

void loop()
{
  t.update();
  doSomething1();
  doSomething2();
}


void doSomething1() {
    soleEvent1 = t.oscillate(soleEvent1, 50, LOW, 5);
}

void doSomething2() {
    soleEvent2 = t.oscillate(soleEvent2, 50, LOW, 40);
}


And my test code for MIDI triggering with LED and servo working but not the solenoids because I don't know how to include that in my switch/case part(I know it's a super ugly code, sorry...):

Code: [Select]


#include <MIDI.h>  // Add Midi Library

#include "Timer.h"

Timer t;

int soleEvent1 = 10;
int soleEvent2 = 11;


#define LED 13    // Arduino Board LED is on Pin 13

#include <VarSpeedServo.h>
 
VarSpeedServo myservo;    // create servo object to control a servo


//Create an instance of the library with default name, serial port and settings
MIDI_CREATE_DEFAULT_INSTANCE();

void setup() {
  pinMode (LED, OUTPUT); // Set Arduino board pin 13 to output
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);

  int tickEvent1 = t.every(1, doSomething1);
  int tickEvent2 = t.every(1, doSomething2);

  myservo.attach(9);  // attaches the servo on pin 9 to the servo object
  MIDI.begin(MIDI_CHANNEL_OMNI); // Initialize the Midi Library.
  // OMNI sets it to listen to all channels.. MIDI.begin(2) would set it
  // to respond to notes on channel 2 only.
  MIDI.setHandleNoteOn(MyHandleNoteOn); // This is important!! This command
  // tells the Midi Library which function you want to call when a NOTE ON command
  // is received. In this case it's "MyHandleNoteOn".
  MIDI.setHandleNoteOff(MyHandleNoteOff); // This command tells the Midi Library
  // to call "MyHandleNoteOff" when a NOTE OFF command is received.
  Serial.begin(115200);
}

void loop() { // Main loop
  MIDI.read(); // Continuously check if Midi data has been received.
}

// MyHandleNoteON is the function that will be called by the Midi Library
// when a MIDI NOTE ON message is received.
// It will be passed bytes for Channel, Pitch, and Velocity
void MyHandleNoteOn(byte channel, byte pitch, byte velocity) {
  switch (pitch) {
    case 42:
    myservo.write(150, 200, false);        // move to 180 degrees, use a speed of 30, wait until move is complete
    digitalWrite(LED,HIGH);  //Turn LED on
      return;
    case 44:
    myservo.write(30, 100, false);         // move to 0 degrees, use a speed of 30, wait until move is complete
      return;
      case 46:
    myservo.write(60, 30, false);         // move to 0 degrees, use a speed of 30, wait until move is complete
      return;
      case 48: {
      t.update();
      doSomething1();
      }
      return;
      case 50: {
      t.update();
      doSomething2();
      }
      return;
    default:
    myservo.stop();
    digitalWrite(LED,LOW);
    t.stop(soleEvent1);
    t.stop(soleEvent2);
      // if nothing else matches, do the default
      // default is optional
    return;
  }
}

// MyHandleNoteOFF is the function that will be called by the Midi Library
// when a MIDI NOTE OFF message is received.
// * A NOTE ON message with Velocity = 0 will be treated as a NOTE OFF message *
// It will be passed bytes for Channel, Pitch, and Velocity
void MyHandleNoteOff(byte channel, byte pitch, byte velocity) {
 //switch (pitch) {
    //case 42:
      //do something when var equals 1
      //break;
    //case 44:
      //do something when var equals 2
      //break;
    //default:
      // if nothing else matches, do the default
      // default is optional
    //break;
    myservo.stop();
    digitalWrite(LED,LOW);
    t.stop(soleEvent1);
    t.stop(soleEvent2);

 }

void doSomething1() {
    soleEvent1 = t.oscillate(soleEvent1, 10, LOW);
}

void doSomething2() {
    soleEvent2 = t.oscillate(soleEvent2, 10, LOW);
}


Do you know how I could improve that and make it works as intended. I try to have a code simple to edit to have various parameters easily adjustable for each note.
Also, I didn't try with stepper motor yet... And with several Arduinos either.
But I feel like I slowly move forward (with baby steps but still).

Again, thanks a lot for your help, and again, sorry to have forgotten to let you know about my progress...

Anyway I'm very glad you find this inspiring for your own projects! :)

Have a good day.

Cyril_

slipstick

No apology needed I'm enjoying myself. I'm glad you're getting it worked out.

I wanted to just use Reaper's MIDI routing without needing extra software like Hairless and LoopMIDI. I already had MIDI outs on my Audio interface (though I tested it with a cheap USB to MIDI converter too) so I'm using plain MIDI and at the Arduino end the standard MIDI in circuit (which uses an optocoupler) https://www.midi.org/specifications/item/midi-din-electrical-specification

Your way should also work and has the advantage of not needing you to build the input circuits.

I'll have a look at your code...but what exactly do you want the solenoids doing? For as long as their specific notes (48, 50?) are on should the pin really be switching on and off every 10ms ? Will your solenoids switch 50 times a second, that's very fast?

I think some problems are likely to be because both MIDI and Timer are using independent callbacks and getting those integrated is tricky. I haven't needed to use that Timer library before so I'll have a play with it.

But presumably one possibility is to drop the timer and instead send a series of short notes to drive the solenoids directly.Though I guess that's not really practical if it really does need to run so fast.

I don't know anything about stepper motors so I'd be even less use there ;).

Steve


Saynomore

Thanks for your reply Steve.

Hairless MIDI doesn't seem to use much processor and memory resources so I find this way more simple, at least for now. But I don't know how I will manage that with several Arduinos...

About the solenoids part, you're exactly right. I want them to switch on and off as long as the note is on. 50ms was a value I put for my test. I figured out it was the fastest I needed (and this speed seems to be supported by my tiny solenoids). But if I could adjust their "hitting speed" with the note velocity it would be perfect. I don't know if it's feasible with switch/case though...

You must be right about callbacks issues. And that's where I was struggling. The problem with the "short notes" is that you can't have notes this short in MIDI afaik.

No problem for the stepper motors. I'm about to make them work I think (hope?).

Cyril_

Grumpy_Mike

Quote
But if I could adjust their "hitting speed" with the note velocity it would be perfect.
Instead of using a digitalWrite to fire the solenoids use an analogWrite, and use PWM capable pins. The hitting speed can be controlled to a certain extent by the PWM value you use. You might not get a great range of values that will be enough to trigger them but you can always use a map function to spread the 1 to 127 velocity values into the range of values that will work.

Do you have diodes across those solenoids? You need to.   

Saynomore

#21
Oct 10, 2017, 06:00 pm Last Edit: Oct 10, 2017, 06:05 pm by Saynomore
Hi Mike! Thanks for your input.

I have no idea it was possible. Do you happen to have a code example by any chance? I must admit I have some difficulty to visualize how this could be set up...
Also, do you think I could do that to control servo speed (with VarSpeedServo.h) and stepper speed (with AccelStepper.h) as well?

My electronic circuit has diodes of course, and it works fine since the first time I make/used it (months ago).

Grumpy_Mike

#22
Oct 10, 2017, 09:19 pm Last Edit: Oct 10, 2017, 09:20 pm by Grumpy_Mike
Quote
Do you happen to have a code example by any chance?
Well no but it is only one line, just replace digitalWrite with analogWrite, I am not sure what you don't get.

You will have to experiment to see how low you can go with the value you put in analogWrite and still have the solenoid pull in.

Quote
Also, do you think I could do that to control servo speed (with VarSpeedServo.h) and stepper speed (with AccelStepper.h) as well?
Can't see why you can't.

Saynomore

Mike, sorry, I was tired and I misunderstood you.

So I tried but it doesn't seem to work. The only effect I get is that the solenoid shaft is fully extended all the time when I hit value between 240 and 255 approximately... Under this threshold it extends lazily and around 200 and under it doesn't do anything.

Grumpy_Mike

Quote
So I tried but it doesn't seem to work.
Are you still pulsing them on and off at the same time? It might help if you either don't or you make the pulse time longer.

Many people here have reported that applying PWM to solenoids does affect the striking force while others have been skeptical. I have never tried it myself. But I would not expect to see much difference in the stroke as it is hard to differentiate speeds of that order. I would however expect to hear a difference in the sound it makes thumping into its target.

Also the magnetic field to overcome the inertia of the solenoid is much greater than that required to keep it moving. Also the closer the solenoids core gets to the magnet the greater the force.

Therefore you might have a bit more luck giving a short blast of full power before cutting back to a smaller power for the rest of the strike, or keep on cutting it back through the time of the strike. 

Go Up