0

So I have been working on an Arduino program that sends MIDI data over USB, and I can't seem to figure out how to pass the function that handles sending of midi data to my main class. It worked at some point but for some reason, it doesn't, and I feel like I have tried every combination of things.

Here is my main file, simplified down to just output the MIDI data over serial:

#include "XiaoPiezoDrum.h"

void sendNoteOn(int note, int velocity, int chan) {
    Serial.printf("WORKS! %d %d %d\n", note, velocity, chan);
};

XiaoPiezoDrum drum(9, 2, 3, 4);

void setup() {
    drum.setNoteComFunctions(sendNoteOn);
}

Here is what that setNoteComFunctions method looks like:

void XiaoPiezoDrum::setNoteComFunctions(const std::function<void(int, int, int)>& onFunc) {
    sendNote = [onFunc](auto && PH1, auto && PH2, auto && PH3) { onFunc(PH1, PH2, PH3); };
}

And here is the relevant parts of the main class definition:

class XiaoPiezoDrum {
public:
    XiaoPiezoDrum();
    XiaoPiezoDrum(int piezoPin, int rPin, int gPin, int bPin);
    void setNoteComFunctions(const function<void(int, int, int)>& onFunc); 
    void RunCycle();

private:
    std::function<void(int, int, int)> sendNote;
}

So everything runs just fine until I call sendNote from within RunCycle, which is just called every loop, and then serial communication just stops suddenly. What am I doing wrong? I also tried using bind instead of a lambda, and the same thing happens. When I just copy the same print statement that is in sendNoteOn in the main file into the lambda in setNoteComFunctions, it prints just fine, so I know the issue is all about linking the function from the main file.

Things I have tried:

  • passing the function in setNoteComFunctions both as a reference and not
  • using bind rather than lambda
  • changing the body of sendNoteOn
  • making the lambda arguments references
  • making the lambda arguments not references
  • returning onFunc in the lambda

I have been banging my head against the keyboard for hours with this. If anybody knows what I'm doing wrong, I'd love an explanation!

EDIT Here is the full code of RunCycle. Many of these variables are instance variables that I left out of the provided class definition, but I know the issue is with the call of sendNote, because if I comment it out, things continue running normally. I have also tried replacing the velocity variable with a fixed integer, and the same thing happened.

void XiaoPiezoDrum::RunCycle() {
    double val = sensor.read();
    val = (val > 0) ? val : 0;
    Serial.println(val);
    unsigned long timeElapsed;
    int velocity;
    MaxVal = (val > MaxVal) ? val : MaxVal;
    trigger = val > THRESHOLD && !triggering;
    if (val > THRESHOLD && !triggering) trigger = true;
    if (trigger) {
        triggerTime = millis();
        trigger = false;
        triggering = true;
        triggerBuffer.clear();
    }
    if (triggering) {
        timeElapsed = millis() - triggerTime;
        if (timeElapsed < SAMPLE_TIME) {
            loopCounter++;
            triggerBuffer.addValue(val);
            Serial.println(val);
        }
        else {
            velocity = map(round(triggerBuffer.getMax()), 0, 300, THRESHOLD, 127);
            Serial.printf("Velocity: %d\n", velocity);
            if (comFunctionsSet) sendNote(40, velocity, 1);
            noteEndTime = millis();
            triggerTime = 0;
            triggerBuffer.clear();
            triggering = false;
            resting = true;
        }
    }
}
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
TheDitis
  • 1
  • 1
  • 1
    I don't see anything obviously wrong in the code shown. To the extent there is a problem, it likely likes in the code not shown. For further assistance, prepare a [mcve] – Igor Tandetnik Aug 22 '20 at 18:58
  • Where is the source of `RunCycle`? Where is `void loop()`? Why do you need function pointers at all, why can't you use a `vector` of notes? Again. please provide a [mcve]. – rustyx Aug 22 '20 at 19:07
  • You could simplify `setNoteComFunctions()` a bit: `sendNote = onFunc;` But, I don't think that will fix your issue. – jignatius Aug 22 '20 at 19:28
  • "setNoteComFunctions(const function& onFunc);" You take a reference to a temporary object? If you pass the function address in that call, you generate a temporary object of std::function and take that ref for later use. But the temporary has gone... – Klaus Dec 27 '20 at 17:27

1 Answers1

0

jignatius' answer did it! I should have known I could do that!! Thanks!

I just had to replace

void XiaoPiezoDrum::setNoteComFunctions(const std::function<void(int, int, int)>& onFunc) {
    sendNote = [onFunc](auto && PH1, auto && PH2, auto && PH3) { onFunc(PH1, PH2, PH3); };
}

with:

void XiaoPiezoDrum::setNoteComFunctions(const std::function<void(int, int, int)>& onFunc) {
    sendNote = onFunc;
}

So glad to have that solved! Thanks!

TheDitis
  • 1
  • 1
  • That fixes the issue but did not fix the reason :-) See my comment above! Your code now works because you create a copy from the referenced object now instead of using the reference on later calls. only for your understanding what was really wrong! Try it out to use your old version with lambda and simply remove the "&" from your function argument. – Klaus Dec 27 '20 at 17:30