diff --git a/CMakeLists.txt b/CMakeLists.txt index 3283814..af3133d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.13.0 FATAL_ERROR) -SET(VERSION 0.3.4) +SET(VERSION 0.3.5) project(ChucKDesigner VERSION ${VERSION}) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ChucKDesignerCHOP) diff --git a/README.md b/README.md index de99c67..d41ecb8 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,11 @@ Install Python 3 Install CMake and confirm that it's installed by running cmake --version in a command prompt.
Then in this repository, -
cmake . -DCMAKE_BUILD_TYPE=Release -Bbuild -DPYTHONVER="3.11"
-Finally, open build/ChucKDesignerCHOP.sln and compile. +Then, cmake --build build --config Release to compile. ### MacOS @@ -70,7 +69,17 @@ If you'd like to build the ChucKDesigner plugins yourself, these are the instruc ### Python interface to ChucK Audio CHOP -The ChucK Audio CHOP's functions: +The ChucK Audio CHOP's getter methods: + +* `.get_float(name: str) -> float` +* `.get_int(name: str) -> int` +* `.get_string(name: str) -> str` +* `.get_float_array(name: str) -> List[float]` +* `.get_int_array(name: str) -> List[int]` + +Note that these getters return results with a one-frame delay. They will return `None` the first time they're called. If `None` is returned on later calls, it means that the requested global variable wasn't found. + +The ChucK Audio CHOP's setter methods: * `.set_float(name: str, val: float)` * `.set_int(name: str, val: int)` @@ -84,6 +93,8 @@ The ChucK Audio CHOP's functions: * `.broadcast_event(name: str)` * `.set_log_level(level: int)` **0 is None and 10 is "Crazy"** +### Example + Suppose the ChucK Audio CHOP has compiled this code: ```chuck diff --git a/src/ChucKDesignerCHOP.cpp b/src/ChucKDesignerCHOP.cpp index 76e7217..c6e29ac 100644 --- a/src/ChucKDesignerCHOP.cpp +++ b/src/ChucKDesignerCHOP.cpp @@ -32,8 +32,7 @@ pySetGlobalFloat(PyObject* self, PyObject* args, void*) PY_Struct* me = (PY_Struct*)self; PY_GetInfo info; - // We don't want to cook the node before we set this, since it doesn't depend on its current state - info.autoCook = false; // todo: which value to use? + info.autoCook = false; ChucKDesignerCHOP* inst = (ChucKDesignerCHOP*)me->context->getNodeInstance(info); // It's possible the instance will be nullptr, such as if the node has been deleted // while the Python class is still being held on and used elsewhere. @@ -53,7 +52,6 @@ pySetGlobalFloat(PyObject* self, PyObject* args, void*) t_CKFLOAT castVal = PyFloat_AsDouble(val); inst->setGlobalFloat(castName, castVal); - // Make the node dirty so it will cook an output a newly reset filter when asked next me->context->makeNodeDirty(); } @@ -67,8 +65,7 @@ pySetGlobalInt(PyObject* self, PyObject* args, void*) PY_Struct* me = (PY_Struct*)self; PY_GetInfo info; - // We don't want to cook the node before we set this, since it doesn't depend on its current state - info.autoCook = false; // todo: which value to use? + info.autoCook = false; ChucKDesignerCHOP* inst = (ChucKDesignerCHOP*)me->context->getNodeInstance(info); // It's possible the instance will be nullptr, such as if the node has been deleted // while the Python class is still being held on and used elsewhere. @@ -88,7 +85,6 @@ pySetGlobalInt(PyObject* self, PyObject* args, void*) t_CKINT castVal = _PyLong_AsInt(val); inst->setGlobalInt(castName, castVal); - // Make the node dirty so it will cook an output a newly reset filter when asked next me->context->makeNodeDirty(); } @@ -102,8 +98,7 @@ pySetGlobalString(PyObject* self, PyObject* args, void*) PY_Struct* me = (PY_Struct*)self; PY_GetInfo info; - // We don't want to cook the node before we set this, since it doesn't depend on its current state - info.autoCook = false; // todo: which value to use? + info.autoCook = false; ChucKDesignerCHOP* inst = (ChucKDesignerCHOP*)me->context->getNodeInstance(info); // It's possible the instance will be nullptr, such as if the node has been deleted // while the Python class is still being held on and used elsewhere. @@ -121,7 +116,6 @@ pySetGlobalString(PyObject* self, PyObject* args, void*) const char* castVal = PyBytes_AsString(PyUnicode_AsASCIIString(val)); inst->setGlobalString(castName, castVal); - // Make the node dirty so it will cook an output a newly reset filter when asked next me->context->makeNodeDirty(); } @@ -135,8 +129,7 @@ pySetGlobalFloatArray(PyObject* self, PyObject* args, void*) PY_Struct* me = (PY_Struct*)self; PY_GetInfo info; - // We don't want to cook the node before we set this, since it doesn't depend on its current state - info.autoCook = false; // todo: which value to use? + info.autoCook = false; ChucKDesignerCHOP* inst = (ChucKDesignerCHOP*)me->context->getNodeInstance(info); // It's possible the instance will be nullptr, such as if the node has been deleted // while the Python class is still being held on and used elsewhere. @@ -163,7 +156,6 @@ pySetGlobalFloatArray(PyObject* self, PyObject* args, void*) inst->setGlobalFloatArray(castName, nums, numValues); delete[] nums; - // Make the node dirty so it will cook an output a newly reset filter when asked next me->context->makeNodeDirty(); } @@ -177,8 +169,7 @@ pySetGlobalIntArray(PyObject* self, PyObject* args, void*) PY_Struct* me = (PY_Struct*)self; PY_GetInfo info; - // We don't want to cook the node before we set this, since it doesn't depend on its current state - info.autoCook = false; // todo: which value to use? + info.autoCook = false; ChucKDesignerCHOP* inst = (ChucKDesignerCHOP*)me->context->getNodeInstance(info); // It's possible the instance will be nullptr, such as if the node has been deleted // while the Python class is still being held on and used elsewhere. @@ -205,7 +196,6 @@ pySetGlobalIntArray(PyObject* self, PyObject* args, void*) inst->setGlobalIntArray(castName, nums, numValues); delete[] nums; - // Make the node dirty so it will cook an output a newly reset filter when asked next me->context->makeNodeDirty(); } @@ -219,8 +209,7 @@ pySetGlobalFloatArrayValue(PyObject* self, PyObject* args, void*) PY_Struct* me = (PY_Struct*)self; PY_GetInfo info; - // We don't want to cook the node before we set this, since it doesn't depend on its current state - info.autoCook = false; // todo: which value to use? + info.autoCook = false; ChucKDesignerCHOP* inst = (ChucKDesignerCHOP*)me->context->getNodeInstance(info); // It's possible the instance will be nullptr, such as if the node has been deleted // while the Python class is still being held on and used elsewhere. @@ -241,7 +230,6 @@ pySetGlobalFloatArrayValue(PyObject* self, PyObject* args, void*) t_CKFLOAT castValue = PyFloat_AsDouble(value); inst->setGlobalFloatArrayValue(castName, castIndex, castValue); - // Make the node dirty so it will cook an output a newly reset filter when asked next me->context->makeNodeDirty(); } @@ -255,8 +243,7 @@ pySetGlobalIntArrayValue(PyObject* self, PyObject* args, void*) PY_Struct* me = (PY_Struct*)self; PY_GetInfo info; - // We don't want to cook the node before we set this, since it doesn't depend on its current state - info.autoCook = false; // todo: which value to use? + info.autoCook = false; ChucKDesignerCHOP* inst = (ChucKDesignerCHOP*)me->context->getNodeInstance(info); // It's possible the instance will be nullptr, such as if the node has been deleted // while the Python class is still being held on and used elsewhere. @@ -277,7 +264,6 @@ pySetGlobalIntArrayValue(PyObject* self, PyObject* args, void*) t_CKINT castValue = PyLong_AsLongLong(value); inst->setGlobalIntArrayValue(castName, castIndex, castValue); - // Make the node dirty so it will cook an output a newly reset filter when asked next me->context->makeNodeDirty(); } @@ -291,8 +277,7 @@ pySetGlobalAssociativeFloatArrayValue(PyObject* self, PyObject* args, void*) PY_Struct* me = (PY_Struct*)self; PY_GetInfo info; - // We don't want to cook the node before we set this, since it doesn't depend on its current state - info.autoCook = false; // todo: which value to use? + info.autoCook = false; ChucKDesignerCHOP* inst = (ChucKDesignerCHOP*)me->context->getNodeInstance(info); // It's possible the instance will be nullptr, such as if the node has been deleted // while the Python class is still being held on and used elsewhere. @@ -314,7 +299,6 @@ pySetGlobalAssociativeFloatArrayValue(PyObject* self, PyObject* args, void*) t_CKFLOAT castValue = PyFloat_AsDouble(value); inst->setGlobalAssociativeFloatArrayValue(castName, castKey, castValue); - // Make the node dirty so it will cook an output a newly reset filter when asked next me->context->makeNodeDirty(); } @@ -328,8 +312,7 @@ pySetGlobalAssociativeIntArrayValue(PyObject* self, PyObject* args, void*) PY_Struct* me = (PY_Struct*)self; PY_GetInfo info; - // We don't want to cook the node before we set this, since it doesn't depend on its current state - info.autoCook = false; // todo: which value to use? + info.autoCook = false; ChucKDesignerCHOP* inst = (ChucKDesignerCHOP*)me->context->getNodeInstance(info); // It's possible the instance will be nullptr, such as if the node has been deleted // while the Python class is still being held on and used elsewhere. @@ -351,7 +334,6 @@ pySetGlobalAssociativeIntArrayValue(PyObject* self, PyObject* args, void*) t_CKINT castValue = PyLong_AsLongLong(value); inst->setGlobalAssociativeIntArrayValue(castName, castKey, castValue); - // Make the node dirty so it will cook an output a newly reset filter when asked next me->context->makeNodeDirty(); } @@ -359,14 +341,170 @@ pySetGlobalAssociativeIntArrayValue(PyObject* self, PyObject* args, void*) FAIL_IN_CUSTOM_OPERATOR_METHOD } +static PyObject* pyGetGlobalFloat(PyObject* self, PyObject* args, void*) { + PY_Struct* me = (PY_Struct*)self; + + PY_GetInfo info; + info.autoCook = false; + ChucKDesignerCHOP* inst = + (ChucKDesignerCHOP*)me->context->getNodeInstance(info); + // It's possible the instance will be nullptr, such as if the node has been + // deleted while the Python class is still being held on and used elsewhere. + if (inst) { + PyObject* name; + + if (!PyArg_UnpackTuple(args, "ref", 1, 1, &name)) { + // error + FAIL_IN_CUSTOM_OPERATOR_METHOD + } + + const char* castName = PyBytes_AsString(PyUnicode_AsASCIIString(name)); + + t_CKFLOAT val; + if (inst->getGlobalFloat(castName, val)) { + return PyFloat_FromDouble(val); + } + } + + // We need to inc-ref the None object if we are going to return it. + FAIL_IN_CUSTOM_OPERATOR_METHOD +} + +static PyObject* pyGetGlobalInt(PyObject* self, PyObject* args, void*) { + PY_Struct* me = (PY_Struct*)self; + + PY_GetInfo info; + info.autoCook = false; + ChucKDesignerCHOP* inst = + (ChucKDesignerCHOP*)me->context->getNodeInstance(info); + // It's possible the instance will be nullptr, such as if the node has been + // deleted while the Python class is still being held on and used elsewhere. + if (inst) { + PyObject* name; + + if (!PyArg_UnpackTuple(args, "ref", 1, 1, &name)) { + // error + FAIL_IN_CUSTOM_OPERATOR_METHOD + } + + const char* castName = PyBytes_AsString(PyUnicode_AsASCIIString(name)); + + t_CKINT val; + if (inst->getGlobalInt(castName, val)) { + return PyLong_FromLongLong(val); + } + } + + // We need to inc-ref the None object if we are going to return it. + FAIL_IN_CUSTOM_OPERATOR_METHOD +} + +static PyObject* pyGetGlobalString(PyObject* self, PyObject* args, void*) { + PY_Struct* me = (PY_Struct*)self; + + PY_GetInfo info; + info.autoCook = false; + ChucKDesignerCHOP* inst = + (ChucKDesignerCHOP*)me->context->getNodeInstance(info); + // It's possible the instance will be nullptr, such as if the node has been + // deleted while the Python class is still being held on and used elsewhere. + if (inst) { + PyObject* name; + + if (!PyArg_UnpackTuple(args, "ref", 1, 1, &name)) { + // error + FAIL_IN_CUSTOM_OPERATOR_METHOD + } + + const char* castName = PyBytes_AsString(PyUnicode_AsASCIIString(name)); + + std::string val; + if (inst->getGlobalString(castName, val)) { + return PyUnicode_FromString(val.c_str()); + } + } + + // We need to inc-ref the None object if we are going to return it. + FAIL_IN_CUSTOM_OPERATOR_METHOD +} + +static PyObject* pyGetGlobalFloatArray(PyObject* self, PyObject* args, void*) { + PY_Struct* me = (PY_Struct*)self; + + PY_GetInfo info; + info.autoCook = false; + ChucKDesignerCHOP* inst = + (ChucKDesignerCHOP*)me->context->getNodeInstance(info); + // It's possible the instance will be nullptr, such as if the node has been + // deleted while the Python class is still being held on and used elsewhere. + if (inst) { + PyObject* name; + + if (!PyArg_UnpackTuple(args, "ref", 1, 1, &name)) { + // error + FAIL_IN_CUSTOM_OPERATOR_METHOD + } + + const char* castName = PyBytes_AsString(PyUnicode_AsASCIIString(name)); + + t_CKFLOAT* vec = nullptr; + int numItems = 0; + if (inst->getGlobalFloatArray(castName, &vec, numItems)) { + // todo: return a numpy array + PyObject* lst = PyList_New(numItems); + for (int i = 0; i < numItems; i++) { + PyList_SET_ITEM(lst, i, PyFloat_FromDouble(*(vec++))); + } + return lst; + } + } + + // We need to inc-ref the None object if we are going to return it. + FAIL_IN_CUSTOM_OPERATOR_METHOD +} + +static PyObject* pyGetGlobalIntArray(PyObject* self, PyObject* args, void*) { + PY_Struct* me = (PY_Struct*)self; + + PY_GetInfo info; + info.autoCook = false; + ChucKDesignerCHOP* inst = + (ChucKDesignerCHOP*)me->context->getNodeInstance(info); + // It's possible the instance will be nullptr, such as if the node has been + // deleted while the Python class is still being held on and used elsewhere. + if (inst) { + PyObject* name; + + if (!PyArg_UnpackTuple(args, "ref", 1, 1, &name)) { + // error + FAIL_IN_CUSTOM_OPERATOR_METHOD + } + + const char* castName = PyBytes_AsString(PyUnicode_AsASCIIString(name)); + + t_CKINT* vec = nullptr; + int numItems = 0; + if (inst->getGlobalIntArray(castName, &vec, numItems)) { + // todo: return a numpy array + PyObject* lst = PyList_New(numItems); + for (int i = 0; i < numItems; i++) { + PyList_SET_ITEM(lst, i, PyLong_FromLongLong(*(vec++))); + } + return lst; + } + } + + // We need to inc-ref the None object if we are going to return it. + FAIL_IN_CUSTOM_OPERATOR_METHOD +} + static PyObject* pyBroadcastChuckEvent(PyObject* self, PyObject* args, void*) { PY_Struct* me = (PY_Struct*)self; PY_GetInfo info; - // We don't want to cook the node before we set this, since it doesn't depend on its current state - info.autoCook = false; // todo: which value to use? + info.autoCook = false; ChucKDesignerCHOP* inst = (ChucKDesignerCHOP*)me->context->getNodeInstance(info); // It's possible the instance will be nullptr, such as if the node has been deleted // while the Python class is still being held on and used elsewhere. @@ -382,7 +520,6 @@ pyBroadcastChuckEvent(PyObject* self, PyObject* args, void*) const char* castName = PyBytes_AsString(PyUnicode_AsASCIIString(name)); inst->broadcastChuckEvent(castName); - // Make the node dirty so it will cook an output a newly reset filter when asked next me->context->makeNodeDirty(); } @@ -396,8 +533,7 @@ pySetLogLevel(PyObject* self, PyObject* args, void*) PY_Struct* me = (PY_Struct*)self; PY_GetInfo info; - // We don't want to cook the node before we set this, since it doesn't depend on its current state - info.autoCook = false; // todo: which value to use? + info.autoCook = false; ChucKDesignerCHOP* inst = (ChucKDesignerCHOP*)me->context->getNodeInstance(info); // It's possible the instance will be nullptr, such as if the node has been deleted // while the Python class is still being held on and used elsewhere. @@ -411,7 +547,6 @@ pySetLogLevel(PyObject* self, PyObject* args, void*) } inst->setLogLevel(_PyLong_AsInt(level)); - // Make the node dirty so it will cook an output a newly reset filter when asked next me->context->makeNodeDirty(); } @@ -436,6 +571,12 @@ static PyMethodDef methods[] = {"broadcast_event", (PyCFunction)pyBroadcastChuckEvent, METH_VARARGS, "Broadcast an event to ChucK."}, + {"get_float", (PyCFunction)pyGetGlobalFloat, METH_VARARGS, "Get a ChucK global float variable."}, + {"get_int", (PyCFunction)pyGetGlobalInt, METH_VARARGS, "Get a ChucK global int variable."}, + {"get_string", (PyCFunction)pyGetGlobalString, METH_VARARGS, "Get a ChucK global string variable."}, + {"get_float_array", (PyCFunction)pyGetGlobalFloatArray, METH_VARARGS, "Get a ChucK global float array variable."}, + {"get_int_array", (PyCFunction)pyGetGlobalIntArray, METH_VARARGS, "Get a ChucK global int array variable."}, + {"set_log_level", (PyCFunction)pySetLogLevel, METH_VARARGS, "Set ChucK's log level."}, {0} @@ -592,8 +733,6 @@ ChucKDesignerCHOP::execute(CHOP_Output* output, memset(inChucKBuffer, 0.f, sizeof(float) * CHUCKDESIGNERCHOP_BUFFER_SIZE * m_inChannels); memset(outChucKBuffer, 0.f, sizeof(float) * CHUCKDESIGNERCHOP_BUFFER_SIZE * m_outChannels); - //m_chuckID = ChucK_For_TouchDesigner::getNextValidID(myNodeInfo->opId); - ChucK_For_TouchDesigner::initChuckInstance(m_chuckID, sample_rate, m_inChannels, m_outChannels, globalDir); const OP_DATInput* codeInput = inputs->getParDAT("Code"); diff --git a/src/ChucKDesignerCHOP.h b/src/ChucKDesignerCHOP.h index a9cb0ef..0e5b42b 100644 --- a/src/ChucKDesignerCHOP.h +++ b/src/ChucKDesignerCHOP.h @@ -108,6 +108,36 @@ class ChucKDesignerCHOP : public CHOP_CPlusPlusBase ChucK_For_TouchDesigner::setGlobalAssociativeIntArrayValue(m_chuckID, name, key, value); } + bool getGlobalFloat(const char* name, t_CKFLOAT &val) + { + ChucK_For_TouchDesigner::getNamedChuckFloat(m_chuckID, name, ChucK_For_TouchDesigner::sharedFloatCallback); + return ChucK_For_TouchDesigner::getFloat(name, val); + } + + bool getGlobalInt(const char* name, t_CKINT& val) + { + ChucK_For_TouchDesigner::getNamedChuckInt(m_chuckID, name, ChucK_For_TouchDesigner::sharedIntCallback); + return ChucK_For_TouchDesigner::getInt(name, val); + } + + bool getGlobalString(const char* name, std::string& val) + { + ChucK_For_TouchDesigner::getNamedChuckString(m_chuckID, name, ChucK_For_TouchDesigner::sharedStringCallback); + return ChucK_For_TouchDesigner::getString(name, val); + } + + bool getGlobalIntArray(const char* name, t_CKINT** vec, int& numValues) + { + ChucK_For_TouchDesigner::getNamedGlobalIntArray(m_chuckID, name, ChucK_For_TouchDesigner::sharedIntArrayCallback); + return ChucK_For_TouchDesigner::getIntArray(name, vec, numValues); + } + + bool getGlobalFloatArray(const char* name, t_CKFLOAT** vec, int& numValues) + { + ChucK_For_TouchDesigner::getNamedGlobalFloatArray(m_chuckID, name, ChucK_For_TouchDesigner::sharedFloatArrayCallback); + return ChucK_For_TouchDesigner::getFloatArray(name, vec, numValues); + } + void broadcastChuckEvent(const char* name) { diff --git a/src/ChucKListenerCHOP.cpp b/src/ChucKListenerCHOP.cpp index 4d3f8e9..d562553 100644 --- a/src/ChucKListenerCHOP.cpp +++ b/src/ChucKListenerCHOP.cpp @@ -272,7 +272,7 @@ ChucKListenerCHOP::execute(CHOP_Output* output, ChucK_For_TouchDesigner::getNamedGlobalIntArray(chuck_id, varName.c_str(), ChucK_For_TouchDesigner::sharedIntArrayCallback); } - // << event stuff +#pragma region Events std::string Eventvars = inputs->getParString("Eventvars"); auto Eventvarstrings = split(Eventvars, ' '); @@ -301,13 +301,17 @@ ChucKListenerCHOP::execute(CHOP_Output* output, myEventVarNames.insert(varName); } } - // end event stuff >> +#pragma endregion Events int i = 0; for (const std::string varName : myFloatVarNames) { auto name = varName.c_str(); - t_CKFLOAT val = ChucK_For_TouchDesigner::getFloat(name); + t_CKFLOAT val = 0; + if (!ChucK_For_TouchDesigner::getFloat(name, val)) + { + continue; + } if (i < output->numChannels) { output->channels[i][0] = val; } @@ -331,8 +335,11 @@ ChucKListenerCHOP::execute(CHOP_Output* output, for (const std::string varName : myIntVarNames) { - auto name = varName.c_str(); - t_CKINT val = ChucK_For_TouchDesigner::getInt(name); + auto name = varName.c_str(); + t_CKINT val = 0; + if (!ChucK_For_TouchDesigner::getInt(name, val)) { + continue; + } if (i < output->numChannels) { output->channels[i][0] = val; } @@ -356,13 +363,16 @@ ChucKListenerCHOP::execute(CHOP_Output* output, for (const std::string varName : myStringVarNames) { auto name = varName.c_str(); - auto str = ChucK_For_TouchDesigner::getString(name); + std::string str = ""; + if (!ChucK_For_TouchDesigner::getString(name, str)) { + continue; + } // We'll only be adding one extra argument PyObject* args = myNodeInfo->context->createArgumentsTuple(2, nullptr); // The first argument is already set to the 'op' variable, so we set the second argument to our speed value PyTuple_SET_ITEM(args, 1, PyUnicode_FromString(name)); - PyTuple_SET_ITEM(args, 2, PyUnicode_FromString(str)); + PyTuple_SET_ITEM(args, 2, PyUnicode_FromString(str.c_str())); PyObject* result = myNodeInfo->context->callPythonCallback("getString", args, nullptr, nullptr); // callPythonCallback doesn't take ownership of the argts @@ -376,9 +386,8 @@ ChucKListenerCHOP::execute(CHOP_Output* output, { auto name = varName.c_str(); int numItems = 0; - auto vec = ChucK_For_TouchDesigner::getFloatArray(name, numItems); - - if (!numItems || !vec) { + t_CKFLOAT* vec = nullptr; + if (!ChucK_For_TouchDesigner::getFloatArray(name, &vec, numItems)) { continue; } @@ -408,9 +417,9 @@ ChucKListenerCHOP::execute(CHOP_Output* output, { auto name = varName.c_str(); int numItems = 0; - auto vec = ChucK_For_TouchDesigner::getIntArray(name, numItems); + t_CKINT* vec = nullptr; - if (!numItems || !vec) { + if (!ChucK_For_TouchDesigner::getIntArray(name, &vec, numItems)) { continue; } @@ -453,7 +462,6 @@ ChucKListenerCHOP::execute(CHOP_Output* output, // We own result now, so we need to Py_DECREF it unless we want to hold onto it if (result) { Py_DECREF(result); } } - } } diff --git a/src/Plugin_ChucK.cpp b/src/Plugin_ChucK.cpp index 1987bf2..70dfba1 100644 --- a/src/Plugin_ChucK.cpp +++ b/src/Plugin_ChucK.cpp @@ -11,8 +11,12 @@ #include "Plugin_ChucK.h" #include "chuck_globals.h" +#include "chuck_vm.h" +#include "util_platforms.h" // for ck_usleep + #include #include + #ifndef WIN32 #include #endif @@ -545,7 +549,9 @@ namespace ChucK_For_TouchDesigner } - CHUCKDESIGNERSHARED_API bool getGlobalAssociativeIntArrayValueWithID(unsigned int chuckID, t_CKINT callbackID, const char* name, char* key, void(*callback)(t_CKINT, t_CKINT)) + CHUCKDESIGNERSHARED_API bool getGlobalAssociativeIntArrayValueWithID( + unsigned int chuckID, t_CKINT callbackID, const char* name, + char* key, void(*callback)(t_CKINT, t_CKINT)) { if (chuck_instances.count(chuckID) == 0) { return false; } Chuck_Globals_Manager* gm = chuck_instances[chuckID]->globals(); @@ -555,6 +561,27 @@ namespace ChucK_For_TouchDesigner name, callbackID, key, callback); } + // internal/audio-thread-friendly global array setter + CHUCKDESIGNERSHARED_API bool setGlobalIntArray_AT( + unsigned int chuckID,const char* name, t_CKINT arrayValues[], unsigned int numValues) + { + if (chuck_instances.count(chuckID) == 0) { return false; } + Chuck_Globals_Manager* gm = chuck_instances[chuckID]->globals(); + if (gm == NULL) { return false; } + + return gm->set_global_int_array(name, arrayValues, numValues); + } + + // internal/audio-thread-friendly + CHUCKDESIGNERSHARED_API bool setGlobalIntArrayValue_AT( + unsigned int chuckID, const char* name, unsigned int index, t_CKINT value) + { + if (chuck_instances.count(chuckID) == 0) { return false; } + Chuck_Globals_Manager* gm = chuck_instances[chuckID]->globals(); + if (gm == NULL) { return false; } + + return gm->set_global_float_array_value(name, index, value); + } // float array methods CHUCKDESIGNERSHARED_API bool setGlobalFloatArray(unsigned int chuckID, @@ -700,6 +727,28 @@ namespace ChucK_For_TouchDesigner } + // internal/audio-thread-friendly global array setter + CHUCKDESIGNERSHARED_API bool setGlobalFloatArray_AT( + unsigned int chuckID, const char* name, t_CKFLOAT arrayValues[], unsigned int numValues) + { + if (chuck_instances.count(chuckID) == 0) { return false; } + Chuck_Globals_Manager* gm = chuck_instances[chuckID]->globals(); + if (gm == NULL) { return false; } + + return gm->set_global_float_array(name, arrayValues, numValues); + } + + + CHUCKDESIGNERSHARED_API bool setGlobalFloatArrayValue_AT( + unsigned int chuckID, const char* name, unsigned int index, t_CKFLOAT value) { + if (chuck_instances.count(chuckID) == 0) { return false; } + Chuck_Globals_Manager* gm = chuck_instances[chuckID]->globals(); + if (gm == NULL) { return false; } + + return gm->set_global_float_array_value(name, index, value); + } + + CHUCKDESIGNERSHARED_API bool setChoutCallback(unsigned int chuckID, void (*callback)(const char*)) { return chuck_instances[chuckID]->setChoutCallback(callback); @@ -886,49 +935,72 @@ namespace ChucK_For_TouchDesigner return count; } - CHUCKDESIGNERSHARED_API t_CKFLOAT getFloat(const char* varName) { - if (myFloatVars.find(varName) != myFloatVars.end()) { - return myFloatVars[varName]; - } - return 0.f; + CHUCKDESIGNERSHARED_API bool getFloat(const char* varName, t_CKFLOAT &val) { + if (myFloatVars.find(varName) != myFloatVars.end()) { + val = myFloatVars[varName]; + return true; + } + return false; } - CHUCKDESIGNERSHARED_API t_CKINT getInt(const char* varName) { + CHUCKDESIGNERSHARED_API bool getInt(const char* varName, t_CKINT& val) { if (myIntVars.find(varName) != myIntVars.end()) { - return myIntVars[varName]; + val = myIntVars[varName]; + return true; } - return 0; + return false; } - CHUCKDESIGNERSHARED_API const char* getString(const char* varName) { + CHUCKDESIGNERSHARED_API bool getString(const char* varName, std::string& val) { if (myStringVars.find(varName) != myStringVars.end()) { - return myStringVars[varName].c_str(); + val = myStringVars[varName]; + return true; } - return ""; + return false; } - CHUCKDESIGNERSHARED_API t_CKFLOAT* getFloatArray(const char* varName, int& numItems) { + CHUCKDESIGNERSHARED_API bool getFloatArray(const char* varName, t_CKFLOAT** vec, int& numItems) { if ( (myFloatArrayVars.find(varName) != myFloatArrayVars.end()) && (myFloatArrayVarSizes.find(varName) != myFloatArrayVarSizes.end()) ) { numItems = myFloatArrayVarSizes[varName]; - return myFloatArrayVars[varName]; + *vec = myFloatArrayVars[varName]; + return true; } numItems = 0; - return nullptr; + return false; } - CHUCKDESIGNERSHARED_API t_CKINT* getIntArray(const char* varName, int& numItems) { + CHUCKDESIGNERSHARED_API bool getIntArray(const char* varName, t_CKINT** vec, int& numItems) { if ( (myIntArrayVars.find(varName) != myIntArrayVars.end()) && (myIntArrayVarSizes.find(varName) != myIntArrayVarSizes.end()) ) { numItems = myIntArrayVarSizes[varName]; - return myIntArrayVars[varName]; + *vec = myIntArrayVars[varName]; + return true; } numItems = 0; - return nullptr; + return false; + } + + CHUCKDESIGNERSHARED_API bool getFloatArrayValue(const char* varName, unsigned int index, t_CKFLOAT& val) { + if ((myFloatArrayVars.find(varName) != myFloatArrayVars.end()) && + (myFloatArrayVarSizes.find(varName) != myFloatArrayVarSizes.end())) { + val = myFloatArrayVars[varName][index]; + return true; + } + return false; + } + + CHUCKDESIGNERSHARED_API bool getIntArrayValue(const char* varName, unsigned int index, t_CKINT& val) { + if ((myIntArrayVars.find(varName) != myIntArrayVars.end()) && + (myIntArrayVars.find(varName) != myIntArrayVars.end())) { + val = myIntArrayVars[varName][index]; + return true; + } + return false; } CHUCKDESIGNERSHARED_API bool initChuckInstance( unsigned int chuckID, unsigned int sampleRate, unsigned int numInChannels, unsigned int numOutChannels, string globalDir ) @@ -1028,6 +1100,9 @@ namespace ChucK_For_TouchDesigner data_instances.erase( chuckID ); } + // wait a bit + ck_usleep(30000); + op_ids_to_chuck_ids.erase(opId); // todo: is this dangerous? @@ -1077,13 +1152,15 @@ namespace ChucK_For_TouchDesigner CHUCKDESIGNERSHARED_API void cleanRegisteredChucks() { // first, invalidate all callbacks' references to chucks - for( std::map< unsigned int, EffectData::Data * >::iterator it = - data_instances.begin(); it != data_instances.end(); it++ ) + for (std::map::iterator it = data_instances.begin(); it != data_instances.end(); it++) { - EffectData::Data * data = it->second; + EffectData::Data* data = it->second; data->myId = -1; } + // wait for callbacks to finish their current run + ck_usleep(30000); + // next, delete chucks for( std::map< unsigned int, ChucK * >::iterator it = chuck_instances.begin(); it != chuck_instances.end(); it++ ) diff --git a/src/Plugin_ChucK.h b/src/Plugin_ChucK.h index 81950fa..d981c9e 100644 --- a/src/Plugin_ChucK.h +++ b/src/Plugin_ChucK.h @@ -41,7 +41,6 @@ extern "C" { CHUCKDESIGNERSHARED_API bool getNamedChuckInt(unsigned int chuckID, const char* name, void (*callback)(const char*, t_CKINT)); CHUCKDESIGNERSHARED_API bool getChuckIntWithID(unsigned int chuckID, t_CKINT callbackID, const char* name, void (*callback)(t_CKINT, t_CKINT)); - CHUCKDESIGNERSHARED_API bool setChuckFloat(unsigned int chuckID, const char* name, t_CKFLOAT val); CHUCKDESIGNERSHARED_API bool getChuckFloat(unsigned int chuckID, const char* name, void (*callback)(t_CKFLOAT)); CHUCKDESIGNERSHARED_API bool getNamedChuckFloat(unsigned int chuckID, const char* name, void (*callback)(const char*, t_CKFLOAT)); @@ -79,6 +78,8 @@ extern "C" { CHUCKDESIGNERSHARED_API bool getGlobalAssociativeIntArrayValue(unsigned int chuckID, const char* name, char* key, void (*callback)(t_CKINT)); CHUCKDESIGNERSHARED_API bool getNamedGlobalAssociativeIntArrayValue(unsigned int chuckID, const char* name, char* key, void (*callback)(const char*, t_CKINT)); CHUCKDESIGNERSHARED_API bool getGlobalAssociativeIntArrayValueWithID(unsigned int chuckID, t_CKINT callbackID, const char* name, char* key, void (*callback)(t_CKINT, t_CKINT)); + CHUCKDESIGNERSHARED_API bool setGlobalIntArray_AT(unsigned int chuckID, const char* name, t_CKINT arrayValues[], unsigned int numValues); // internal/audio-thread-friendly global array setter + CHUCKDESIGNERSHARED_API bool setGlobalIntArrayValue_AT(unsigned int chuckID, const char* name, unsigned int index, t_CKINT value); // internal/audio-thread-friendly // TODO: set entire dict, add to dict in batch; get entire dict // float array methods @@ -94,7 +95,8 @@ extern "C" { CHUCKDESIGNERSHARED_API bool getGlobalAssociativeFloatArrayValue(unsigned int chuckID, const char* name, char* key, void (*callback)(t_CKFLOAT)); CHUCKDESIGNERSHARED_API bool getNamedGlobalAssociativeFloatArrayValue(unsigned int chuckID, const char* name, char* key, void (*callback)(const char*, t_CKFLOAT)); CHUCKDESIGNERSHARED_API bool getGlobalAssociativeFloatArrayValueWithID(unsigned int chuckID, t_CKINT callbackID, const char* name, char* key, void (*callback)(t_CKINT, t_CKFLOAT)); - + CHUCKDESIGNERSHARED_API bool setGlobalFloatArray_AT(unsigned int chuckID, const char* name, t_CKFLOAT arrayValues[], unsigned int numValues); // internal/audio-thread-friendly global array setter + CHUCKDESIGNERSHARED_API bool setGlobalFloatArrayValue_AT(unsigned int chuckID, const char* name, unsigned int index, t_CKFLOAT value); // internal/audio-thread-friendly CHUCKDESIGNERSHARED_API bool initChuckInstance(unsigned int chuckID, unsigned int sampleRate, unsigned int numInChannels, unsigned int numOutChannels, string globalDir); CHUCKDESIGNERSHARED_API bool clearChuckInstance(unsigned int chuckID); @@ -118,11 +120,16 @@ extern "C" { CHUCKDESIGNERSHARED_API bool processBlock(unsigned int chuckID, const float** inBuffer, int inBufferNumChannels, int inBufferNumSamples, float* inChucKBuffer, float* outChucKBuffer, float** outBuffer, int numOutSamples, int numOutChannels); - CHUCKDESIGNERSHARED_API t_CKFLOAT getFloat(const char* varStr); - CHUCKDESIGNERSHARED_API t_CKINT getInt(const char* varStr); - CHUCKDESIGNERSHARED_API const char* getString(const char* varStr); - CHUCKDESIGNERSHARED_API t_CKFLOAT* getFloatArray(const char* varName, int& numItems); - CHUCKDESIGNERSHARED_API t_CKINT* getIntArray(const char* varName, int& numItems); + CHUCKDESIGNERSHARED_API bool getFloat(const char* varName, t_CKFLOAT& val); + CHUCKDESIGNERSHARED_API bool getInt(const char* varStr, t_CKINT& val); + CHUCKDESIGNERSHARED_API bool getString(const char* varStr, std::string& val); + CHUCKDESIGNERSHARED_API bool getFloatArray(const char* varName, t_CKFLOAT** vec, int& numItems); + CHUCKDESIGNERSHARED_API bool getIntArray(const char* varName, t_CKINT** vec, int& numItems); + + CHUCKDESIGNERSHARED_API bool getFloatArrayValue(const char* varName, unsigned int index, t_CKFLOAT& val); + CHUCKDESIGNERSHARED_API bool getIntArrayValue(const char* varName, unsigned int index, t_CKINT& val); + //CHUCKDESIGNERSHARED_API bool getAssociativeFloatArrayValue(const char* varName, char* key, unsigned int index, t_CKFLOAT& val); + //CHUCKDESIGNERSHARED_API bool getAssociativeIntArrayValue(const char* varName, char* key, unsigned int index, t_CKINT& val); CHUCKDESIGNERSHARED_API void sharedFloatCallback(const char* varName, t_CKFLOAT val); CHUCKDESIGNERSHARED_API void sharedIntCallback(const char* varName, t_CKINT val); @@ -131,6 +138,9 @@ extern "C" { CHUCKDESIGNERSHARED_API void sharedFloatArrayCallback(const char* varName, t_CKFLOAT vals[], t_CKUINT numItems); CHUCKDESIGNERSHARED_API void sharedIntArrayCallback(const char* varName, t_CKINT vals[], t_CKUINT numItems); + //CHUCKDESIGNERSHARED_API void sharedAssociativeFloatArrayCallback(const char* varName, t_CKFLOAT val); + //CHUCKDESIGNERSHARED_API void sharedAssociativeIntArrayCallback(const char* varName, t_CKUINT val); + CHUCKDESIGNERSHARED_API void sharedEventCallback(const char* varName); CHUCKDESIGNERSHARED_API void sharedEventNonCallback(const char* varName); diff --git a/thirdparty/chuck b/thirdparty/chuck index 2b886d7..c63d71c 160000 --- a/thirdparty/chuck +++ b/thirdparty/chuck @@ -1 +1 @@ -Subproject commit 2b886d7f190d3e9a82be0b0588656105e4666274 +Subproject commit c63d71c1c7082f8c865bd29540368518d5a71960