Creating Python callbacks in Custom OPs

Hi guys,

got a few questions. I have never done much Python-C or Python integration with C++, so, I thought I would ask these questions here.

I am trying to achieve the following things:

  1. I would like to create a Python callback function, so, when data comes into my custom OP. I can call the Python callback and let the user process the data in Python. What’s Derivative’s guideline on doing that? The solutions available out there are Python.h, Boost.Python and pybind11. But before I invest a lot of time on any of them, I would like to know if there is an official way of implementing this?

  2. I would like to create a function in my C++ code that the user can call via Python.

  3. I would like to create a docked text DAT that contains a sample template function that the user can edit and implement their own code. In my head, I imagine I can try and get a Python object that represents my op’s COMP parent, and then call create, dock, and probably inject the sample code into the text dat.

However, that would require me to get a hold of the Python object associated with my op. How would I go about doing that? The most obvious way I can find is creating a custom python param, put in a default value of “me,” and then call getParPython. But that just seems really round about. Is there a way to do it without doing this?

Thank you!

Da.

4 Likes

Major upvote to this! I’m looking for both 1 and 2. I’ve used boost python and pybind11 moderately recently and pybind11 is waaaay better.

// inside CPlusPlusExample::execute(…) {

PyObject *p = something;
p = py::make_tuple("collisionStart", 42.);
// if a certain condition is met then
plugin_callback(p); // all custom plugins would have plugin_callback, which can't be modified

Inside a ParExecuteDAT or something new maybe

# this becomes a default function that the user can customize
# a lot like def onPulse(par):
def plugin_callback(obj):
    # do something with obj.
    # obj could be whatever the user determines via c++.
    # A good choice might be a tuple where the first item is a str identifying a broadcast event
    # and the second item is some value: ("collisionStart", 42.)
    pass

In c++ implement as many functions as we want that we would expose with bindings

void CPlusPlusExample::doCustomThing(std::string s) {
    // do something
}

PyObject* CPlusPlusExample::doCustomGet(std::string s, bool otherArgsArePossible) {
    PyObject *p = something;
    return p;
}

In c++ create pybind11 binding somehow

PYBIND11_MODULE(my_module, m)
{
    py::class_<CPlusPlusExample>(m, "CPlusPlusExample")
   .def("DoCustomThing", &CPlusPlusExample::doCustomThing)
   .def("DoCustomGet", &CPlusPlusExample::doCustomGet)
}

In python:

op('my_plugin').DoCustomThing("hello")
result = op('my_plugin').DoCustomGet("bark", True)
op('my_plugin').DoNotImplementedThing("fail") # throws error
1 Like

Moved over to Wishlist and RFEs for this addition.

I want to add some more context. For TD-FAUST in its polyphonic mode, I’d like to do op("faust_chop").KeyOn(60, 127) to play MIDI note 60 at velocity 127. I could probably hack around it using CHOP inputs or more custom parameters, but being able to use custom methods like this would be really clean and helpful.

1 Like

The new Python C++ stuff is working great. Could we get some boilerplate for using numpy data in the new CHOPWithPythonClass example? I’m interested in both sending data like
op('my_op').send_numpy(np.zeros(4,4))
and callbacks like

PyObject *result = myNodeInfo->context->callPythonCallback("getNumpyData", args, nullptr, nullptr);

Can we also get a C++ function equivalent of
tdu.expand('A[1-3] B[xyz]')?

https://docs.derivative.ca/Tdu_Module